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