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