1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2015, Linaro Limited 4 */ 5 6 #include <assert.h> 7 #include <config.h> 8 #include <kernel/mutex.h> 9 #include <kernel/nv_counter.h> 10 #include <kernel/panic.h> 11 #include <kernel/thread.h> 12 #include <kernel/user_access.h> 13 #include <mempool.h> 14 #include <mm/core_memprot.h> 15 #include <mm/tee_pager.h> 16 #include <optee_rpc_cmd.h> 17 #include <stdlib.h> 18 #include <string.h> 19 #include <sys/queue.h> 20 #include <tee/fs_dirfile.h> 21 #include <tee/fs_htree.h> 22 #include <tee/tee_fs.h> 23 #include <tee/tee_fs_rpc.h> 24 #include <tee/tee_pobj.h> 25 #include <trace.h> 26 #include <utee_defines.h> 27 #include <util.h> 28 29 #define BLOCK_SHIFT 12 30 31 #define BLOCK_SIZE (1 << BLOCK_SHIFT) 32 33 struct tee_fs_fd { 34 struct tee_fs_htree *ht; 35 int fd; 36 struct tee_fs_dirfile_fileh dfh; 37 const TEE_UUID *uuid; 38 }; 39 40 struct tee_fs_dir { 41 struct tee_fs_dirfile_dirh *dirh; 42 int idx; 43 struct tee_fs_dirent d; 44 const TEE_UUID *uuid; 45 }; 46 47 static int pos_to_block_num(int position) 48 { 49 return position >> BLOCK_SHIFT; 50 } 51 52 static struct mutex ree_fs_mutex = MUTEX_INITIALIZER; 53 54 static void *get_tmp_block(void) 55 { 56 return mempool_alloc(mempool_default, BLOCK_SIZE); 57 } 58 59 static void put_tmp_block(void *tmp_block) 60 { 61 mempool_free(mempool_default, tmp_block); 62 } 63 64 static TEE_Result out_of_place_write(struct tee_fs_fd *fdp, size_t pos, 65 const void *buf_core, 66 const void *buf_user, size_t len) 67 { 68 TEE_Result res; 69 size_t start_block_num = pos_to_block_num(pos); 70 size_t end_block_num = pos_to_block_num(pos + len - 1); 71 size_t remain_bytes = len; 72 uint8_t *data_core_ptr = (uint8_t *)buf_core; 73 uint8_t *data_user_ptr = (uint8_t *)buf_user; 74 uint8_t *block; 75 struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht); 76 77 assert(meta); 78 79 /* 80 * It doesn't make sense to call this function if nothing is to be 81 * written. This also guards against end_block_num getting an 82 * unexpected value when pos == 0 and len == 0. 83 */ 84 if (!len) 85 return TEE_ERROR_BAD_PARAMETERS; 86 87 block = get_tmp_block(); 88 if (!block) 89 return TEE_ERROR_OUT_OF_MEMORY; 90 91 while (start_block_num <= end_block_num) { 92 size_t offset = pos % BLOCK_SIZE; 93 size_t size_to_write = MIN(remain_bytes, (size_t)BLOCK_SIZE); 94 95 if (size_to_write + offset > BLOCK_SIZE) 96 size_to_write = BLOCK_SIZE - offset; 97 98 if (start_block_num * BLOCK_SIZE < 99 ROUNDUP(meta->length, BLOCK_SIZE)) { 100 res = tee_fs_htree_read_block(&fdp->ht, 101 start_block_num, block); 102 if (res != TEE_SUCCESS) 103 goto exit; 104 } else { 105 memset(block, 0, BLOCK_SIZE); 106 } 107 108 if (data_core_ptr) { 109 memcpy(block + offset, data_core_ptr, size_to_write); 110 } else if (data_user_ptr) { 111 res = copy_from_user(block + offset, data_user_ptr, 112 size_to_write); 113 if (res) 114 goto exit; 115 } else { 116 memset(block + offset, 0, size_to_write); 117 } 118 119 res = tee_fs_htree_write_block(&fdp->ht, start_block_num, 120 block); 121 if (res != TEE_SUCCESS) 122 goto exit; 123 124 if (data_core_ptr) 125 data_core_ptr += size_to_write; 126 if (data_user_ptr) 127 data_user_ptr += size_to_write; 128 remain_bytes -= size_to_write; 129 start_block_num++; 130 pos += size_to_write; 131 } 132 133 if (pos > meta->length) { 134 meta->length = pos; 135 tee_fs_htree_meta_set_dirty(fdp->ht); 136 } 137 138 exit: 139 if (block) 140 put_tmp_block(block); 141 return res; 142 } 143 144 static TEE_Result get_offs_size(enum tee_fs_htree_type type, size_t idx, 145 uint8_t vers, size_t *offs, size_t *size) 146 { 147 const size_t node_size = sizeof(struct tee_fs_htree_node_image); 148 const size_t block_nodes = BLOCK_SIZE / (node_size * 2); 149 size_t pbn; 150 size_t bidx; 151 152 assert(vers == 0 || vers == 1); 153 154 /* 155 * File layout 156 * [demo with input: 157 * BLOCK_SIZE = 4096, 158 * node_size = 66, 159 * block_nodes = 4096/(66*2) = 31 ] 160 * 161 * phys block 0: 162 * tee_fs_htree_image vers 0 @ offs = 0 163 * tee_fs_htree_image vers 1 @ offs = sizeof(tee_fs_htree_image) 164 * 165 * phys block 1: 166 * tee_fs_htree_node_image 0 vers 0 @ offs = 0 167 * tee_fs_htree_node_image 0 vers 1 @ offs = node_size 168 * tee_fs_htree_node_image 1 vers 0 @ offs = node_size * 2 169 * tee_fs_htree_node_image 1 vers 1 @ offs = node_size * 3 170 * ... 171 * tee_fs_htree_node_image 30 vers 0 @ offs = node_size * 60 172 * tee_fs_htree_node_image 30 vers 1 @ offs = node_size * 61 173 * 174 * phys block 2: 175 * data block 0 vers 0 176 * 177 * phys block 3: 178 * data block 0 vers 1 179 * 180 * ... 181 * phys block 62: 182 * data block 30 vers 0 183 * 184 * phys block 63: 185 * data block 30 vers 1 186 * 187 * phys block 64: 188 * tee_fs_htree_node_image 31 vers 0 @ offs = 0 189 * tee_fs_htree_node_image 31 vers 1 @ offs = node_size 190 * tee_fs_htree_node_image 32 vers 0 @ offs = node_size * 2 191 * tee_fs_htree_node_image 32 vers 1 @ offs = node_size * 3 192 * ... 193 * tee_fs_htree_node_image 61 vers 0 @ offs = node_size * 60 194 * tee_fs_htree_node_image 61 vers 1 @ offs = node_size * 61 195 * 196 * phys block 65: 197 * data block 31 vers 0 198 * 199 * phys block 66: 200 * data block 31 vers 1 201 * ... 202 */ 203 204 switch (type) { 205 case TEE_FS_HTREE_TYPE_HEAD: 206 *offs = sizeof(struct tee_fs_htree_image) * vers; 207 *size = sizeof(struct tee_fs_htree_image); 208 return TEE_SUCCESS; 209 case TEE_FS_HTREE_TYPE_NODE: 210 pbn = 1 + ((idx / block_nodes) * block_nodes * 2); 211 *offs = pbn * BLOCK_SIZE + 212 2 * node_size * (idx % block_nodes) + 213 node_size * vers; 214 *size = node_size; 215 return TEE_SUCCESS; 216 case TEE_FS_HTREE_TYPE_BLOCK: 217 bidx = 2 * idx + vers; 218 pbn = 2 + bidx + bidx / (block_nodes * 2 - 1); 219 *offs = pbn * BLOCK_SIZE; 220 *size = BLOCK_SIZE; 221 return TEE_SUCCESS; 222 default: 223 return TEE_ERROR_GENERIC; 224 } 225 } 226 227 static TEE_Result ree_fs_rpc_read_init(void *aux, 228 struct tee_fs_rpc_operation *op, 229 enum tee_fs_htree_type type, size_t idx, 230 uint8_t vers, void **data) 231 { 232 struct tee_fs_fd *fdp = aux; 233 TEE_Result res; 234 size_t offs; 235 size_t size; 236 237 res = get_offs_size(type, idx, vers, &offs, &size); 238 if (res != TEE_SUCCESS) 239 return res; 240 241 return tee_fs_rpc_read_init(op, OPTEE_RPC_CMD_FS, fdp->fd, 242 offs, size, data); 243 } 244 245 static TEE_Result ree_fs_rpc_write_init(void *aux, 246 struct tee_fs_rpc_operation *op, 247 enum tee_fs_htree_type type, size_t idx, 248 uint8_t vers, void **data) 249 { 250 struct tee_fs_fd *fdp = aux; 251 TEE_Result res; 252 size_t offs; 253 size_t size; 254 255 res = get_offs_size(type, idx, vers, &offs, &size); 256 if (res != TEE_SUCCESS) 257 return res; 258 259 return tee_fs_rpc_write_init(op, OPTEE_RPC_CMD_FS, fdp->fd, 260 offs, size, data); 261 } 262 263 static const struct tee_fs_htree_storage ree_fs_storage_ops = { 264 .block_size = BLOCK_SIZE, 265 .rpc_read_init = ree_fs_rpc_read_init, 266 .rpc_read_final = tee_fs_rpc_read_final, 267 .rpc_write_init = ree_fs_rpc_write_init, 268 .rpc_write_final = tee_fs_rpc_write_final, 269 }; 270 271 static TEE_Result ree_fs_ftruncate_internal(struct tee_fs_fd *fdp, 272 tee_fs_off_t new_file_len) 273 { 274 TEE_Result res; 275 struct tee_fs_htree_meta *meta = NULL; 276 277 assert(fdp); 278 meta = tee_fs_htree_get_meta(fdp->ht); 279 if (!meta) 280 return TEE_ERROR_CORRUPT_OBJECT; 281 282 if ((size_t)new_file_len > meta->length) { 283 size_t ext_len = new_file_len - meta->length; 284 285 res = out_of_place_write(fdp, meta->length, NULL, NULL, 286 ext_len); 287 if (res != TEE_SUCCESS) 288 return res; 289 } else { 290 size_t offs; 291 size_t sz; 292 293 res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK, 294 ROUNDUP_DIV(new_file_len, BLOCK_SIZE), 1, 295 &offs, &sz); 296 if (res != TEE_SUCCESS) 297 return res; 298 299 res = tee_fs_htree_truncate(&fdp->ht, 300 new_file_len / BLOCK_SIZE); 301 if (res != TEE_SUCCESS) 302 return res; 303 304 res = tee_fs_rpc_truncate(OPTEE_RPC_CMD_FS, fdp->fd, 305 offs + sz); 306 if (res != TEE_SUCCESS) 307 return res; 308 309 meta->length = new_file_len; 310 tee_fs_htree_meta_set_dirty(fdp->ht); 311 } 312 313 return TEE_SUCCESS; 314 } 315 316 static TEE_Result ree_fs_read_primitive(struct tee_file_handle *fh, size_t pos, 317 void *buf_core, void *buf_user, 318 size_t *len) 319 { 320 TEE_Result res; 321 int start_block_num; 322 int end_block_num; 323 size_t remain_bytes; 324 uint8_t *data_core_ptr = buf_core; 325 uint8_t *data_user_ptr = buf_user; 326 uint8_t *block = NULL; 327 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 328 struct tee_fs_htree_meta *meta = NULL; 329 330 /* One of buf_core and buf_user must be NULL */ 331 assert(!buf_core || !buf_user); 332 333 assert(fdp); 334 meta = tee_fs_htree_get_meta(fdp->ht); 335 if (!meta) 336 return TEE_ERROR_CORRUPT_OBJECT; 337 338 remain_bytes = *len; 339 if ((pos + remain_bytes) < remain_bytes || pos > meta->length) 340 remain_bytes = 0; 341 else if (pos + remain_bytes > meta->length) 342 remain_bytes = meta->length - pos; 343 344 *len = remain_bytes; 345 346 if (!remain_bytes) { 347 res = TEE_SUCCESS; 348 goto exit; 349 } 350 351 start_block_num = pos_to_block_num(pos); 352 end_block_num = pos_to_block_num(pos + remain_bytes - 1); 353 354 block = get_tmp_block(); 355 if (!block) { 356 res = TEE_ERROR_OUT_OF_MEMORY; 357 goto exit; 358 } 359 360 while (start_block_num <= end_block_num) { 361 size_t offset = pos % BLOCK_SIZE; 362 size_t size_to_read = MIN(remain_bytes, (size_t)BLOCK_SIZE); 363 364 if (size_to_read + offset > BLOCK_SIZE) 365 size_to_read = BLOCK_SIZE - offset; 366 367 res = tee_fs_htree_read_block(&fdp->ht, start_block_num, block); 368 if (res != TEE_SUCCESS) 369 goto exit; 370 371 if (data_core_ptr) { 372 memcpy(data_core_ptr, block + offset, size_to_read); 373 data_core_ptr += size_to_read; 374 } else if (data_user_ptr) { 375 res = copy_to_user(data_user_ptr, block + offset, 376 size_to_read); 377 if (res) 378 goto exit; 379 data_user_ptr += size_to_read; 380 } 381 382 remain_bytes -= size_to_read; 383 pos += size_to_read; 384 385 start_block_num++; 386 } 387 res = TEE_SUCCESS; 388 exit: 389 if (block) 390 put_tmp_block(block); 391 return res; 392 } 393 394 static TEE_Result ree_fs_read(struct tee_file_handle *fh, size_t pos, 395 void *buf_core, void *buf_user, size_t *len) 396 { 397 TEE_Result res; 398 399 mutex_lock(&ree_fs_mutex); 400 res = ree_fs_read_primitive(fh, pos, buf_core, buf_user, len); 401 mutex_unlock(&ree_fs_mutex); 402 403 return res; 404 } 405 406 static TEE_Result ree_fs_write_primitive(struct tee_file_handle *fh, size_t pos, 407 const void *buf_core, 408 const void *buf_user, size_t len) 409 { 410 TEE_Result res; 411 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 412 struct tee_fs_htree_meta *meta = NULL; 413 size_t file_size; 414 415 /* One of buf_core and buf_user must be NULL */ 416 assert(!buf_core || !buf_user); 417 418 if (!len) 419 return TEE_SUCCESS; 420 421 assert(fdp); 422 meta = tee_fs_htree_get_meta(fdp->ht); 423 if (!meta) 424 return TEE_ERROR_CORRUPT_OBJECT; 425 426 file_size = meta->length; 427 428 if ((pos + len) < len) 429 return TEE_ERROR_BAD_PARAMETERS; 430 431 if (file_size < pos) { 432 res = ree_fs_ftruncate_internal(fdp, pos); 433 if (res != TEE_SUCCESS) 434 return res; 435 } 436 437 return out_of_place_write(fdp, pos, buf_core, buf_user, len); 438 } 439 440 static TEE_Result ree_fs_open_primitive(bool create, uint8_t *hash, 441 uint32_t min_counter, 442 const TEE_UUID *uuid, 443 struct tee_fs_dirfile_fileh *dfh, 444 struct tee_file_handle **fh) 445 { 446 TEE_Result res; 447 struct tee_fs_fd *fdp; 448 449 fdp = calloc(1, sizeof(struct tee_fs_fd)); 450 if (!fdp) 451 return TEE_ERROR_OUT_OF_MEMORY; 452 fdp->fd = -1; 453 fdp->uuid = uuid; 454 455 if (create) 456 res = tee_fs_rpc_create_dfh(OPTEE_RPC_CMD_FS, 457 dfh, &fdp->fd); 458 else 459 res = tee_fs_rpc_open_dfh(OPTEE_RPC_CMD_FS, dfh, &fdp->fd); 460 461 if (res != TEE_SUCCESS) 462 goto out; 463 464 res = tee_fs_htree_open(create, hash, min_counter, uuid, 465 &ree_fs_storage_ops, fdp, &fdp->ht); 466 out: 467 if (res == TEE_SUCCESS) { 468 if (dfh) 469 fdp->dfh = *dfh; 470 else 471 fdp->dfh.idx = -1; 472 *fh = (struct tee_file_handle *)fdp; 473 } else { 474 if (res == TEE_ERROR_SECURITY) 475 DMSG("Secure storage corruption detected"); 476 if (fdp->fd != -1) 477 tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd); 478 /* 479 * Remove the file if hash is NULL and min_counter is 0, 480 * as it is not yet rollback-protected 481 */ 482 if (create || (!hash && !min_counter)) { 483 DMSG("Remove corrupt file"); 484 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, dfh); 485 } 486 free(fdp); 487 } 488 489 return res; 490 } 491 492 static void ree_fs_close_primitive(struct tee_file_handle *fh) 493 { 494 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 495 496 if (fdp) { 497 tee_fs_htree_close(&fdp->ht); 498 tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd); 499 free(fdp); 500 } 501 } 502 503 static TEE_Result ree_dirf_commit_writes(struct tee_file_handle *fh, 504 uint8_t *hash, uint32_t *counter) 505 { 506 TEE_Result res; 507 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 508 509 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, counter); 510 511 if (!res && hash) 512 memcpy(hash, fdp->dfh.hash, sizeof(fdp->dfh.hash)); 513 514 return res; 515 } 516 517 static TEE_Result dirf_read(struct tee_file_handle *fh, size_t pos, void *buf, 518 size_t *len) 519 { 520 return ree_fs_read_primitive(fh, pos, buf, NULL, len); 521 } 522 523 static TEE_Result dirf_write(struct tee_file_handle *fh, size_t pos, 524 const void *buf, size_t len) 525 { 526 return ree_fs_write_primitive(fh, pos, buf, NULL, len); 527 } 528 529 static const struct tee_fs_dirfile_operations ree_dirf_ops = { 530 .open = ree_fs_open_primitive, 531 .close = ree_fs_close_primitive, 532 .read = dirf_read, 533 .write = dirf_write, 534 .commit_writes = ree_dirf_commit_writes, 535 }; 536 537 /* 538 * ree_fs_dirh is caching the dirfile handle to avoid frequent opening and 539 * closing of that handle. When ree_fs_dirh_refcount reaches 0, ree_fs_dirh 540 * will be freed. However, ree_fs_dirh_refcount > 0 is not a guarantee that 541 * ree_fs_dirh will not be freed, it may very well be freed earlier in an 542 * error path. get_dirh() must be used to get the ree_fs_dirh pointer each 543 * time it's needed if ree_fs_mutex has been unlocked in between. 544 */ 545 static struct tee_fs_dirfile_dirh *ree_fs_dirh; 546 static size_t ree_fs_dirh_refcount; 547 548 #ifdef CFG_REE_FS_INTEGRITY_RPMB 549 static struct tee_file_handle *ree_fs_rpmb_fh; 550 551 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh) 552 { 553 TEE_Result res; 554 uint8_t hash[TEE_FS_HTREE_HASH_SIZE]; 555 uint8_t *hashp = NULL; 556 const char fname[] = "dirfile.db.hash"; 557 558 res = tee_rpmb_fs_raw_open(fname, false, &ree_fs_rpmb_fh); 559 if (!res) { 560 size_t l = sizeof(hash); 561 562 res = rpmb_fs_ops.read(ree_fs_rpmb_fh, 0, hash, NULL, &l); 563 if (res) 564 return res; 565 if (l == sizeof(hash)) 566 hashp = hash; 567 } else if (res == TEE_ERROR_ITEM_NOT_FOUND) { 568 res = tee_rpmb_fs_raw_open(fname, true, &ree_fs_rpmb_fh); 569 } 570 if (res) 571 return res; 572 573 res = tee_fs_dirfile_open(false, hashp, 0, &ree_dirf_ops, dirh); 574 575 if (res == TEE_ERROR_ITEM_NOT_FOUND) { 576 if (hashp) { 577 if (IS_ENABLED(CFG_REE_FS_ALLOW_RESET)) { 578 DMSG("dirf.db not found, clear hash in RPMB"); 579 res = rpmb_fs_ops.truncate(ree_fs_rpmb_fh, 0); 580 if (res) { 581 DMSG("Can't clear hash: %#"PRIx32, res); 582 res = TEE_ERROR_SECURITY; 583 goto out; 584 } 585 } else { 586 DMSG("dirf.db file not found"); 587 res = TEE_ERROR_SECURITY; 588 goto out; 589 } 590 } 591 592 DMSG("Create dirf.db"); 593 res = tee_fs_dirfile_open(true, NULL, 0, &ree_dirf_ops, dirh); 594 } 595 596 out: 597 if (res) 598 rpmb_fs_ops.close(&ree_fs_rpmb_fh); 599 600 return res; 601 } 602 603 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh) 604 { 605 TEE_Result res; 606 uint8_t hash[TEE_FS_HTREE_HASH_SIZE]; 607 608 res = tee_fs_dirfile_commit_writes(dirh, hash, NULL); 609 if (res) 610 return res; 611 return rpmb_fs_ops.write(ree_fs_rpmb_fh, 0, hash, NULL, sizeof(hash)); 612 } 613 614 static void close_dirh(struct tee_fs_dirfile_dirh **dirh) 615 { 616 tee_fs_dirfile_close(*dirh); 617 *dirh = NULL; 618 rpmb_fs_ops.close(&ree_fs_rpmb_fh); 619 } 620 621 #else /*!CFG_REE_FS_INTEGRITY_RPMB*/ 622 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh) 623 { 624 TEE_Result res = TEE_SUCCESS; 625 uint32_t min_counter = 0; 626 627 res = nv_counter_get_ree_fs(&min_counter); 628 if (res) { 629 static bool once; 630 631 if (res != TEE_ERROR_NOT_IMPLEMENTED || 632 !IS_ENABLED(CFG_INSECURE)) 633 return res; 634 635 if (!once) { 636 IMSG("WARNING (insecure configuration): Failed to get monotonic counter for REE FS, using 0"); 637 once = true; 638 } 639 min_counter = 0; 640 } 641 res = tee_fs_dirfile_open(false, NULL, min_counter, &ree_dirf_ops, 642 dirh); 643 if (res == TEE_ERROR_ITEM_NOT_FOUND) { 644 if (min_counter) { 645 if (!IS_ENABLED(CFG_REE_FS_ALLOW_RESET)) { 646 DMSG("dirf.db file not found"); 647 return TEE_ERROR_SECURITY; 648 } 649 DMSG("dirf.db not found, initializing with a non-zero monotonic counter"); 650 } 651 return tee_fs_dirfile_open(true, NULL, min_counter, 652 &ree_dirf_ops, dirh); 653 } 654 655 return res; 656 } 657 658 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh) 659 { 660 TEE_Result res = TEE_SUCCESS; 661 uint32_t counter = 0; 662 663 res = tee_fs_dirfile_commit_writes(dirh, NULL, &counter); 664 if (res) 665 return res; 666 res = nv_counter_incr_ree_fs_to(counter); 667 if (res == TEE_ERROR_NOT_IMPLEMENTED && IS_ENABLED(CFG_INSECURE)) { 668 static bool once; 669 670 if (!once) { 671 IMSG("WARNING (insecure configuration): Failed to commit dirh counter %"PRIu32, counter); 672 once = true; 673 } 674 return TEE_SUCCESS; 675 } 676 return res; 677 } 678 679 static void close_dirh(struct tee_fs_dirfile_dirh **dirh) 680 { 681 tee_fs_dirfile_close(*dirh); 682 *dirh = NULL; 683 } 684 #endif /*!CFG_REE_FS_INTEGRITY_RPMB*/ 685 686 static TEE_Result get_dirh(struct tee_fs_dirfile_dirh **dirh) 687 { 688 if (!ree_fs_dirh) { 689 TEE_Result res = open_dirh(&ree_fs_dirh); 690 691 if (res) { 692 *dirh = NULL; 693 return res; 694 } 695 } 696 ree_fs_dirh_refcount++; 697 assert(ree_fs_dirh); 698 assert(ree_fs_dirh_refcount); 699 *dirh = ree_fs_dirh; 700 return TEE_SUCCESS; 701 } 702 703 static void put_dirh_primitive(bool close) 704 { 705 assert(ree_fs_dirh_refcount); 706 707 /* 708 * During the execution of one of the ree_fs_ops ree_fs_dirh is 709 * guareteed to be a valid pointer. But when the fop has returned 710 * another thread may get an error or something causing that fop 711 * to do a put with close=1. 712 * 713 * For all fops but ree_fs_close() there's a call to get_dirh() to 714 * get a new dirh which will open it again if it was closed before. 715 * But in the ree_fs_close() case there's no call to get_dirh() 716 * only to this function, put_dirh_primitive(), and in this case 717 * ree_fs_dirh may actually be NULL. 718 */ 719 ree_fs_dirh_refcount--; 720 if (ree_fs_dirh && (!ree_fs_dirh_refcount || close)) 721 close_dirh(&ree_fs_dirh); 722 } 723 724 static void put_dirh(struct tee_fs_dirfile_dirh *dirh, bool close) 725 { 726 if (dirh) { 727 assert(dirh == ree_fs_dirh); 728 put_dirh_primitive(close); 729 } 730 } 731 732 static TEE_Result ree_fs_open(struct tee_pobj *po, size_t *size, 733 struct tee_file_handle **fh) 734 { 735 TEE_Result res; 736 struct tee_fs_dirfile_dirh *dirh = NULL; 737 struct tee_fs_dirfile_fileh dfh; 738 739 mutex_lock(&ree_fs_mutex); 740 741 res = get_dirh(&dirh); 742 if (res != TEE_SUCCESS) 743 goto out; 744 745 res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len, 746 &dfh); 747 if (res != TEE_SUCCESS) 748 goto out; 749 750 res = ree_fs_open_primitive(false, dfh.hash, 0, &po->uuid, &dfh, fh); 751 if (res == TEE_ERROR_ITEM_NOT_FOUND) { 752 /* 753 * If the object isn't found someone has tampered with it, 754 * treat it as corrupt. 755 */ 756 res = TEE_ERROR_CORRUPT_OBJECT; 757 } else if (!res && size) { 758 struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh; 759 760 assert(fdp->ht); 761 *size = tee_fs_htree_get_meta(fdp->ht)->length; 762 } 763 764 out: 765 if (res) 766 put_dirh(dirh, true); 767 mutex_unlock(&ree_fs_mutex); 768 769 return res; 770 } 771 772 static TEE_Result set_name(struct tee_fs_dirfile_dirh *dirh, 773 struct tee_fs_fd *fdp, struct tee_pobj *po, 774 bool overwrite) 775 { 776 TEE_Result res; 777 bool have_old_dfh = false; 778 struct tee_fs_dirfile_fileh old_dfh = { .idx = -1 }; 779 780 res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len, 781 &old_dfh); 782 if (!overwrite && !res) 783 return TEE_ERROR_ACCESS_CONFLICT; 784 785 if (!res) 786 have_old_dfh = true; 787 788 /* 789 * If old_dfh wasn't found, the idx will be -1 and 790 * tee_fs_dirfile_rename() will allocate a new index. 791 */ 792 fdp->dfh.idx = old_dfh.idx; 793 old_dfh.idx = -1; 794 res = tee_fs_dirfile_rename(dirh, &po->uuid, &fdp->dfh, 795 po->obj_id, po->obj_id_len); 796 if (res) 797 return res; 798 799 res = commit_dirh_writes(dirh); 800 if (res) 801 return res; 802 803 if (have_old_dfh) 804 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &old_dfh); 805 806 return TEE_SUCCESS; 807 } 808 809 static void ree_fs_close(struct tee_file_handle **fh) 810 { 811 if (*fh) { 812 mutex_lock(&ree_fs_mutex); 813 put_dirh_primitive(false); 814 ree_fs_close_primitive(*fh); 815 *fh = NULL; 816 mutex_unlock(&ree_fs_mutex); 817 818 } 819 } 820 821 static TEE_Result ree_fs_create(struct tee_pobj *po, bool overwrite, 822 const void *head, size_t head_size, 823 const void *attr, size_t attr_size, 824 const void *data_core, const void *data_user, 825 size_t data_size, struct tee_file_handle **fh) 826 { 827 struct tee_fs_fd *fdp; 828 struct tee_fs_dirfile_dirh *dirh = NULL; 829 struct tee_fs_dirfile_fileh dfh; 830 TEE_Result res; 831 size_t pos = 0; 832 833 /* One of data_core and data_user must be NULL */ 834 assert(!data_core || !data_user); 835 836 *fh = NULL; 837 mutex_lock(&ree_fs_mutex); 838 839 res = get_dirh(&dirh); 840 if (res) 841 goto out; 842 843 res = tee_fs_dirfile_get_tmp(dirh, &dfh); 844 if (res) 845 goto out; 846 847 res = ree_fs_open_primitive(true, dfh.hash, 0, &po->uuid, &dfh, fh); 848 if (res) 849 goto out; 850 851 if (head && head_size) { 852 res = ree_fs_write_primitive(*fh, pos, head, NULL, head_size); 853 if (res) 854 goto out; 855 pos += head_size; 856 } 857 858 if (attr && attr_size) { 859 res = ree_fs_write_primitive(*fh, pos, attr, NULL, attr_size); 860 if (res) 861 goto out; 862 pos += attr_size; 863 } 864 865 if ((data_core || data_user) && data_size) { 866 res = ree_fs_write_primitive(*fh, pos, data_core, data_user, 867 data_size); 868 if (res) 869 goto out; 870 } 871 872 fdp = (struct tee_fs_fd *)*fh; 873 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL); 874 if (res) 875 goto out; 876 877 res = set_name(dirh, fdp, po, overwrite); 878 out: 879 if (res) { 880 put_dirh(dirh, true); 881 if (*fh) { 882 ree_fs_close_primitive(*fh); 883 *fh = NULL; 884 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh); 885 } 886 } 887 mutex_unlock(&ree_fs_mutex); 888 889 return res; 890 } 891 892 static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos, 893 const void *buf_core, const void *buf_user, 894 size_t len) 895 { 896 TEE_Result res; 897 struct tee_fs_dirfile_dirh *dirh = NULL; 898 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 899 900 /* One of buf_core and buf_user must be NULL */ 901 assert(!buf_core || !buf_user); 902 903 mutex_lock(&ree_fs_mutex); 904 905 res = get_dirh(&dirh); 906 if (res) 907 goto out; 908 909 res = ree_fs_write_primitive(fh, pos, buf_core, buf_user, len); 910 if (res) 911 goto out; 912 913 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL); 914 if (res) 915 goto out; 916 917 res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh); 918 if (res) 919 goto out; 920 res = commit_dirh_writes(dirh); 921 out: 922 put_dirh(dirh, res); 923 mutex_unlock(&ree_fs_mutex); 924 925 return res; 926 } 927 928 static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new, 929 bool overwrite) 930 { 931 TEE_Result res; 932 struct tee_fs_dirfile_dirh *dirh = NULL; 933 struct tee_fs_dirfile_fileh dfh; 934 struct tee_fs_dirfile_fileh remove_dfh = { .idx = -1 }; 935 936 if (!new) 937 return TEE_ERROR_BAD_PARAMETERS; 938 939 mutex_lock(&ree_fs_mutex); 940 res = get_dirh(&dirh); 941 if (res) 942 goto out; 943 944 res = tee_fs_dirfile_find(dirh, &new->uuid, new->obj_id, 945 new->obj_id_len, &remove_dfh); 946 if (!res && !overwrite) { 947 res = TEE_ERROR_ACCESS_CONFLICT; 948 goto out; 949 } 950 951 res = tee_fs_dirfile_find(dirh, &old->uuid, old->obj_id, 952 old->obj_id_len, &dfh); 953 if (res) 954 goto out; 955 956 res = tee_fs_dirfile_rename(dirh, &new->uuid, &dfh, new->obj_id, 957 new->obj_id_len); 958 if (res) 959 goto out; 960 961 if (remove_dfh.idx != -1) { 962 res = tee_fs_dirfile_remove(dirh, &remove_dfh); 963 if (res) 964 goto out; 965 } 966 967 res = commit_dirh_writes(dirh); 968 if (res) 969 goto out; 970 971 if (remove_dfh.idx != -1) 972 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &remove_dfh); 973 974 out: 975 put_dirh(dirh, res); 976 mutex_unlock(&ree_fs_mutex); 977 978 return res; 979 980 } 981 982 static TEE_Result ree_fs_remove(struct tee_pobj *po) 983 { 984 TEE_Result res; 985 struct tee_fs_dirfile_dirh *dirh = NULL; 986 struct tee_fs_dirfile_fileh dfh; 987 988 mutex_lock(&ree_fs_mutex); 989 res = get_dirh(&dirh); 990 if (res) 991 goto out; 992 993 res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len, 994 &dfh); 995 if (res) 996 goto out; 997 998 res = tee_fs_dirfile_remove(dirh, &dfh); 999 if (res) 1000 goto out; 1001 1002 res = commit_dirh_writes(dirh); 1003 if (res) 1004 goto out; 1005 1006 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh); 1007 1008 assert(tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len, 1009 &dfh)); 1010 out: 1011 put_dirh(dirh, res); 1012 mutex_unlock(&ree_fs_mutex); 1013 1014 return res; 1015 } 1016 1017 static TEE_Result ree_fs_truncate(struct tee_file_handle *fh, size_t len) 1018 { 1019 TEE_Result res; 1020 struct tee_fs_dirfile_dirh *dirh = NULL; 1021 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 1022 1023 mutex_lock(&ree_fs_mutex); 1024 1025 res = get_dirh(&dirh); 1026 if (res) 1027 goto out; 1028 1029 res = ree_fs_ftruncate_internal(fdp, len); 1030 if (res) 1031 goto out; 1032 1033 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL); 1034 if (res) 1035 goto out; 1036 1037 res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh); 1038 if (res) 1039 goto out; 1040 res = commit_dirh_writes(dirh); 1041 out: 1042 put_dirh(dirh, res); 1043 mutex_unlock(&ree_fs_mutex); 1044 1045 return res; 1046 } 1047 1048 static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid, 1049 struct tee_fs_dir **dir) 1050 1051 { 1052 TEE_Result res = TEE_SUCCESS; 1053 struct tee_fs_dirfile_dirh *dirh = NULL; 1054 struct tee_fs_dir *d = calloc(1, sizeof(*d)); 1055 1056 if (!d) 1057 return TEE_ERROR_OUT_OF_MEMORY; 1058 1059 d->uuid = uuid; 1060 1061 mutex_lock(&ree_fs_mutex); 1062 1063 res = get_dirh(&dirh); 1064 if (res) 1065 goto out; 1066 1067 /* See that there's at least one file */ 1068 d->idx = -1; 1069 d->d.oidlen = sizeof(d->d.oid); 1070 res = tee_fs_dirfile_get_next(dirh, d->uuid, &d->idx, d->d.oid, 1071 &d->d.oidlen); 1072 d->idx = -1; 1073 1074 out: 1075 if (!res) { 1076 *dir = d; 1077 } else { 1078 if (d) 1079 put_dirh(dirh, false); 1080 free(d); 1081 } 1082 mutex_unlock(&ree_fs_mutex); 1083 1084 return res; 1085 } 1086 1087 static void ree_fs_closedir_rpc(struct tee_fs_dir *d) 1088 { 1089 if (d) { 1090 mutex_lock(&ree_fs_mutex); 1091 1092 put_dirh(ree_fs_dirh, false); 1093 free(d); 1094 1095 mutex_unlock(&ree_fs_mutex); 1096 } 1097 } 1098 1099 static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d, 1100 struct tee_fs_dirent **ent) 1101 { 1102 struct tee_fs_dirfile_dirh *dirh = NULL; 1103 TEE_Result res = TEE_SUCCESS; 1104 1105 mutex_lock(&ree_fs_mutex); 1106 1107 res = get_dirh(&dirh); 1108 if (res) 1109 goto out; 1110 1111 d->d.oidlen = sizeof(d->d.oid); 1112 res = tee_fs_dirfile_get_next(dirh, d->uuid, &d->idx, d->d.oid, 1113 &d->d.oidlen); 1114 if (res == TEE_SUCCESS) 1115 *ent = &d->d; 1116 1117 put_dirh(dirh, res); 1118 out: 1119 mutex_unlock(&ree_fs_mutex); 1120 1121 return res; 1122 } 1123 1124 const struct tee_file_operations ree_fs_ops = { 1125 .open = ree_fs_open, 1126 .create = ree_fs_create, 1127 .close = ree_fs_close, 1128 .read = ree_fs_read, 1129 .write = ree_fs_write, 1130 .truncate = ree_fs_truncate, 1131 .rename = ree_fs_rename, 1132 .remove = ree_fs_remove, 1133 .opendir = ree_fs_opendir_rpc, 1134 .closedir = ree_fs_closedir_rpc, 1135 .readdir = ree_fs_readdir_rpc, 1136 }; 1137