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 <stdio.h> 18 #include <stdlib.h> 19 #include <string_ext.h> 20 #include <string.h> 21 #include <sys/queue.h> 22 #include <tee/fs_dirfile.h> 23 #include <tee/fs_htree.h> 24 #include <tee/tee_fs.h> 25 #include <tee/tee_fs_rpc.h> 26 #include <tee/tee_pobj.h> 27 #include <trace.h> 28 #include <utee_defines.h> 29 #include <util.h> 30 31 #define BLOCK_SHIFT 12 32 33 #define BLOCK_SIZE (1 << BLOCK_SHIFT) 34 35 struct tee_fs_fd { 36 struct tee_fs_htree *ht; 37 int fd; 38 struct tee_fs_dirfile_fileh dfh; 39 const TEE_UUID *uuid; 40 }; 41 42 struct tee_fs_dir { 43 struct tee_fs_dirfile_dirh *dirh; 44 int idx; 45 struct tee_fs_dirent d; 46 const TEE_UUID *uuid; 47 }; 48 49 static int pos_to_block_num(int position) 50 { 51 return position >> BLOCK_SHIFT; 52 } 53 54 static struct mutex ree_fs_mutex = MUTEX_INITIALIZER; 55 56 static void *get_tmp_block(void) 57 { 58 return mempool_alloc(mempool_default, BLOCK_SIZE); 59 } 60 61 static void put_tmp_block(void *tmp_block) 62 { 63 mempool_free(mempool_default, tmp_block); 64 } 65 66 static TEE_Result out_of_place_write(struct tee_fs_fd *fdp, size_t pos, 67 const void *buf_core, 68 const void *buf_user, size_t len) 69 { 70 TEE_Result res; 71 size_t start_block_num = pos_to_block_num(pos); 72 size_t end_block_num = pos_to_block_num(pos + len - 1); 73 size_t remain_bytes = len; 74 uint8_t *data_core_ptr = (uint8_t *)buf_core; 75 uint8_t *data_user_ptr = (uint8_t *)buf_user; 76 uint8_t *block; 77 struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht); 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 return res; 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 = tee_fs_htree_get_meta(fdp->ht); 276 277 if ((size_t)new_file_len > meta->length) { 278 size_t ext_len = new_file_len - meta->length; 279 280 res = out_of_place_write(fdp, meta->length, NULL, NULL, 281 ext_len); 282 if (res != TEE_SUCCESS) 283 return res; 284 } else { 285 size_t offs; 286 size_t sz; 287 288 res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK, 289 ROUNDUP(new_file_len, BLOCK_SIZE) / 290 BLOCK_SIZE, 1, &offs, &sz); 291 if (res != TEE_SUCCESS) 292 return res; 293 294 res = tee_fs_htree_truncate(&fdp->ht, 295 new_file_len / BLOCK_SIZE); 296 if (res != TEE_SUCCESS) 297 return res; 298 299 res = tee_fs_rpc_truncate(OPTEE_RPC_CMD_FS, fdp->fd, 300 offs + sz); 301 if (res != TEE_SUCCESS) 302 return res; 303 304 meta->length = new_file_len; 305 tee_fs_htree_meta_set_dirty(fdp->ht); 306 } 307 308 return TEE_SUCCESS; 309 } 310 311 static TEE_Result ree_fs_read_primitive(struct tee_file_handle *fh, size_t pos, 312 void *buf_core, void *buf_user, 313 size_t *len) 314 { 315 TEE_Result res; 316 int start_block_num; 317 int end_block_num; 318 size_t remain_bytes; 319 uint8_t *data_core_ptr = buf_core; 320 uint8_t *data_user_ptr = buf_user; 321 uint8_t *block = NULL; 322 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 323 struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht); 324 325 /* One of buf_core and buf_user must be NULL */ 326 assert(!buf_core || !buf_user); 327 328 remain_bytes = *len; 329 if ((pos + remain_bytes) < remain_bytes || pos > meta->length) 330 remain_bytes = 0; 331 else if (pos + remain_bytes > meta->length) 332 remain_bytes = meta->length - pos; 333 334 *len = remain_bytes; 335 336 if (!remain_bytes) { 337 res = TEE_SUCCESS; 338 goto exit; 339 } 340 341 start_block_num = pos_to_block_num(pos); 342 end_block_num = pos_to_block_num(pos + remain_bytes - 1); 343 344 block = get_tmp_block(); 345 if (!block) { 346 res = TEE_ERROR_OUT_OF_MEMORY; 347 goto exit; 348 } 349 350 while (start_block_num <= end_block_num) { 351 size_t offset = pos % BLOCK_SIZE; 352 size_t size_to_read = MIN(remain_bytes, (size_t)BLOCK_SIZE); 353 354 if (size_to_read + offset > BLOCK_SIZE) 355 size_to_read = BLOCK_SIZE - offset; 356 357 res = tee_fs_htree_read_block(&fdp->ht, start_block_num, block); 358 if (res != TEE_SUCCESS) 359 goto exit; 360 361 if (data_core_ptr) { 362 memcpy(data_core_ptr, block + offset, size_to_read); 363 data_core_ptr += size_to_read; 364 } else if (data_user_ptr) { 365 res = copy_to_user(data_user_ptr, block + offset, 366 size_to_read); 367 if (res) 368 goto exit; 369 data_user_ptr += size_to_read; 370 } 371 372 remain_bytes -= size_to_read; 373 pos += size_to_read; 374 375 start_block_num++; 376 } 377 res = TEE_SUCCESS; 378 exit: 379 if (block) 380 put_tmp_block(block); 381 return res; 382 } 383 384 static TEE_Result ree_fs_read(struct tee_file_handle *fh, size_t pos, 385 void *buf_core, void *buf_user, size_t *len) 386 { 387 TEE_Result res; 388 389 mutex_lock(&ree_fs_mutex); 390 res = ree_fs_read_primitive(fh, pos, buf_core, buf_user, len); 391 mutex_unlock(&ree_fs_mutex); 392 393 return res; 394 } 395 396 static TEE_Result ree_fs_write_primitive(struct tee_file_handle *fh, size_t pos, 397 const void *buf_core, 398 const void *buf_user, size_t len) 399 { 400 TEE_Result res; 401 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 402 size_t file_size; 403 404 /* One of buf_core and buf_user must be NULL */ 405 assert(!buf_core || !buf_user); 406 407 if (!len) 408 return TEE_SUCCESS; 409 410 file_size = tee_fs_htree_get_meta(fdp->ht)->length; 411 412 if ((pos + len) < len) 413 return TEE_ERROR_BAD_PARAMETERS; 414 415 if (file_size < pos) { 416 res = ree_fs_ftruncate_internal(fdp, pos); 417 if (res != TEE_SUCCESS) 418 return res; 419 } 420 421 return out_of_place_write(fdp, pos, buf_core, buf_user, len); 422 } 423 424 static TEE_Result ree_fs_open_primitive(bool create, uint8_t *hash, 425 uint32_t min_counter, 426 const TEE_UUID *uuid, 427 struct tee_fs_dirfile_fileh *dfh, 428 struct tee_file_handle **fh) 429 { 430 TEE_Result res; 431 struct tee_fs_fd *fdp; 432 433 fdp = calloc(1, sizeof(struct tee_fs_fd)); 434 if (!fdp) 435 return TEE_ERROR_OUT_OF_MEMORY; 436 fdp->fd = -1; 437 fdp->uuid = uuid; 438 439 if (create) 440 res = tee_fs_rpc_create_dfh(OPTEE_RPC_CMD_FS, 441 dfh, &fdp->fd); 442 else 443 res = tee_fs_rpc_open_dfh(OPTEE_RPC_CMD_FS, dfh, &fdp->fd); 444 445 if (res != TEE_SUCCESS) 446 goto out; 447 448 res = tee_fs_htree_open(create, hash, min_counter, uuid, 449 &ree_fs_storage_ops, fdp, &fdp->ht); 450 out: 451 if (res == TEE_SUCCESS) { 452 if (dfh) 453 fdp->dfh = *dfh; 454 else 455 fdp->dfh.idx = -1; 456 *fh = (struct tee_file_handle *)fdp; 457 } else { 458 if (res == TEE_ERROR_SECURITY) 459 DMSG("Secure storage corruption detected"); 460 if (fdp->fd != -1) 461 tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd); 462 if (create) 463 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, dfh); 464 free(fdp); 465 } 466 467 return res; 468 } 469 470 static void ree_fs_close_primitive(struct tee_file_handle *fh) 471 { 472 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 473 474 if (fdp) { 475 tee_fs_htree_close(&fdp->ht); 476 tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd); 477 free(fdp); 478 } 479 } 480 481 static TEE_Result ree_dirf_commit_writes(struct tee_file_handle *fh, 482 uint8_t *hash, uint32_t *counter) 483 { 484 TEE_Result res; 485 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 486 487 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, counter); 488 489 if (!res && hash) 490 memcpy(hash, fdp->dfh.hash, sizeof(fdp->dfh.hash)); 491 492 return res; 493 } 494 495 static TEE_Result dirf_read(struct tee_file_handle *fh, size_t pos, void *buf, 496 size_t *len) 497 { 498 return ree_fs_read_primitive(fh, pos, buf, NULL, len); 499 } 500 501 static TEE_Result dirf_write(struct tee_file_handle *fh, size_t pos, 502 const void *buf, size_t len) 503 { 504 return ree_fs_write_primitive(fh, pos, buf, NULL, len); 505 } 506 507 static const struct tee_fs_dirfile_operations ree_dirf_ops = { 508 .open = ree_fs_open_primitive, 509 .close = ree_fs_close_primitive, 510 .read = dirf_read, 511 .write = dirf_write, 512 .commit_writes = ree_dirf_commit_writes, 513 }; 514 515 static struct tee_fs_dirfile_dirh *ree_fs_dirh; 516 static size_t ree_fs_dirh_refcount; 517 518 #ifdef CFG_REE_FS_INTEGRITY_RPMB 519 static struct tee_file_handle *ree_fs_rpmb_fh; 520 521 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh) 522 { 523 TEE_Result res; 524 uint8_t hash[TEE_FS_HTREE_HASH_SIZE]; 525 uint8_t *hashp = NULL; 526 const char fname[] = "dirfile.db.hash"; 527 528 res = tee_rpmb_fs_raw_open(fname, false, &ree_fs_rpmb_fh); 529 if (!res) { 530 size_t l = sizeof(hash); 531 532 res = rpmb_fs_ops.read(ree_fs_rpmb_fh, 0, hash, NULL, &l); 533 if (res) 534 return res; 535 if (l == sizeof(hash)) 536 hashp = hash; 537 } else if (res == TEE_ERROR_ITEM_NOT_FOUND) { 538 res = tee_rpmb_fs_raw_open(fname, true, &ree_fs_rpmb_fh); 539 } 540 if (res) 541 return res; 542 543 res = tee_fs_dirfile_open(false, hashp, 0, &ree_dirf_ops, dirh); 544 545 if (res == TEE_ERROR_ITEM_NOT_FOUND) { 546 if (hashp) { 547 if (IS_ENABLED(CFG_REE_FS_ALLOW_RESET)) { 548 DMSG("dirf.db not found, clear hash in RPMB"); 549 res = rpmb_fs_ops.truncate(ree_fs_rpmb_fh, 0); 550 if (res) { 551 DMSG("Can't clear hash: %#"PRIx32, res); 552 res = TEE_ERROR_SECURITY; 553 goto out; 554 } 555 } else { 556 DMSG("dirf.db file not found"); 557 res = TEE_ERROR_SECURITY; 558 goto out; 559 } 560 } 561 562 res = tee_fs_dirfile_open(true, NULL, 0, &ree_dirf_ops, dirh); 563 } 564 565 out: 566 if (res) 567 rpmb_fs_ops.close(&ree_fs_rpmb_fh); 568 569 return res; 570 } 571 572 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh) 573 { 574 TEE_Result res; 575 uint8_t hash[TEE_FS_HTREE_HASH_SIZE]; 576 577 res = tee_fs_dirfile_commit_writes(dirh, hash, NULL); 578 if (res) 579 return res; 580 return rpmb_fs_ops.write(ree_fs_rpmb_fh, 0, hash, NULL, sizeof(hash)); 581 } 582 583 static void close_dirh(struct tee_fs_dirfile_dirh **dirh) 584 { 585 tee_fs_dirfile_close(*dirh); 586 *dirh = NULL; 587 rpmb_fs_ops.close(&ree_fs_rpmb_fh); 588 } 589 590 #else /*!CFG_REE_FS_INTEGRITY_RPMB*/ 591 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh) 592 { 593 TEE_Result res = TEE_SUCCESS; 594 uint32_t min_counter = 0; 595 596 res = nv_counter_get_ree_fs(&min_counter); 597 if (res) { 598 static bool once; 599 600 if (res != TEE_ERROR_NOT_IMPLEMENTED || 601 !IS_ENABLED(CFG_INSECURE)) 602 return res; 603 604 if (!once) { 605 IMSG("WARNING (insecure configuration): Failed to get monotonic counter for REE FS, using 0"); 606 once = true; 607 } 608 min_counter = 0; 609 } 610 res = tee_fs_dirfile_open(false, NULL, min_counter, &ree_dirf_ops, 611 dirh); 612 if (res == TEE_ERROR_ITEM_NOT_FOUND) { 613 if (min_counter) { 614 if (!IS_ENABLED(CFG_REE_FS_ALLOW_RESET)) { 615 DMSG("dirf.db file not found"); 616 return TEE_ERROR_SECURITY; 617 } 618 DMSG("dirf.db not found, initializing with a non-zero monotonic counter"); 619 } 620 return tee_fs_dirfile_open(true, NULL, min_counter, 621 &ree_dirf_ops, dirh); 622 } 623 624 return res; 625 } 626 627 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh) 628 { 629 TEE_Result res = TEE_SUCCESS; 630 uint32_t counter = 0; 631 632 res = tee_fs_dirfile_commit_writes(dirh, NULL, &counter); 633 if (res) 634 return res; 635 res = nv_counter_incr_ree_fs_to(counter); 636 if (res == TEE_ERROR_NOT_IMPLEMENTED && IS_ENABLED(CFG_INSECURE)) { 637 static bool once; 638 639 if (!once) { 640 IMSG("WARNING (insecure configuration): Failed to commit dirh counter %"PRIu32, counter); 641 once = true; 642 } 643 return TEE_SUCCESS; 644 } 645 return res; 646 } 647 648 static void close_dirh(struct tee_fs_dirfile_dirh **dirh) 649 { 650 tee_fs_dirfile_close(*dirh); 651 *dirh = NULL; 652 } 653 #endif /*!CFG_REE_FS_INTEGRITY_RPMB*/ 654 655 static TEE_Result get_dirh(struct tee_fs_dirfile_dirh **dirh) 656 { 657 if (!ree_fs_dirh) { 658 TEE_Result res = open_dirh(&ree_fs_dirh); 659 660 if (res) { 661 *dirh = NULL; 662 return res; 663 } 664 } 665 ree_fs_dirh_refcount++; 666 assert(ree_fs_dirh); 667 assert(ree_fs_dirh_refcount); 668 *dirh = ree_fs_dirh; 669 return TEE_SUCCESS; 670 } 671 672 static void put_dirh_primitive(bool close) 673 { 674 assert(ree_fs_dirh_refcount); 675 676 /* 677 * During the execution of one of the ree_fs_ops ree_fs_dirh is 678 * guareteed to be a valid pointer. But when the fop has returned 679 * another thread may get an error or something causing that fop 680 * to do a put with close=1. 681 * 682 * For all fops but ree_fs_close() there's a call to get_dirh() to 683 * get a new dirh which will open it again if it was closed before. 684 * But in the ree_fs_close() case there's no call to get_dirh() 685 * only to this function, put_dirh_primitive(), and in this case 686 * ree_fs_dirh may actually be NULL. 687 */ 688 ree_fs_dirh_refcount--; 689 if (ree_fs_dirh && (!ree_fs_dirh_refcount || close)) 690 close_dirh(&ree_fs_dirh); 691 } 692 693 static void put_dirh(struct tee_fs_dirfile_dirh *dirh, bool close) 694 { 695 if (dirh) { 696 assert(dirh == ree_fs_dirh); 697 put_dirh_primitive(close); 698 } 699 } 700 701 static TEE_Result ree_fs_open(struct tee_pobj *po, size_t *size, 702 struct tee_file_handle **fh) 703 { 704 TEE_Result res; 705 struct tee_fs_dirfile_dirh *dirh = NULL; 706 struct tee_fs_dirfile_fileh dfh; 707 708 mutex_lock(&ree_fs_mutex); 709 710 res = get_dirh(&dirh); 711 if (res != TEE_SUCCESS) 712 goto out; 713 714 res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len, 715 &dfh); 716 if (res != TEE_SUCCESS) 717 goto out; 718 719 res = ree_fs_open_primitive(false, dfh.hash, 0, &po->uuid, &dfh, fh); 720 if (res == TEE_ERROR_ITEM_NOT_FOUND) { 721 /* 722 * If the object isn't found someone has tampered with it, 723 * treat it as corrupt. 724 */ 725 res = TEE_ERROR_CORRUPT_OBJECT; 726 } else if (!res && size) { 727 struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh; 728 729 *size = tee_fs_htree_get_meta(fdp->ht)->length; 730 } 731 732 out: 733 if (res) 734 put_dirh(dirh, true); 735 mutex_unlock(&ree_fs_mutex); 736 737 return res; 738 } 739 740 static TEE_Result set_name(struct tee_fs_dirfile_dirh *dirh, 741 struct tee_fs_fd *fdp, struct tee_pobj *po, 742 bool overwrite) 743 { 744 TEE_Result res; 745 bool have_old_dfh = false; 746 struct tee_fs_dirfile_fileh old_dfh = { .idx = -1 }; 747 748 res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len, 749 &old_dfh); 750 if (!overwrite && !res) 751 return TEE_ERROR_ACCESS_CONFLICT; 752 753 if (!res) 754 have_old_dfh = true; 755 756 /* 757 * If old_dfh wasn't found, the idx will be -1 and 758 * tee_fs_dirfile_rename() will allocate a new index. 759 */ 760 fdp->dfh.idx = old_dfh.idx; 761 old_dfh.idx = -1; 762 res = tee_fs_dirfile_rename(dirh, &po->uuid, &fdp->dfh, 763 po->obj_id, po->obj_id_len); 764 if (res) 765 return res; 766 767 res = commit_dirh_writes(dirh); 768 if (res) 769 return res; 770 771 if (have_old_dfh) 772 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &old_dfh); 773 774 return TEE_SUCCESS; 775 } 776 777 static void ree_fs_close(struct tee_file_handle **fh) 778 { 779 if (*fh) { 780 mutex_lock(&ree_fs_mutex); 781 put_dirh_primitive(false); 782 ree_fs_close_primitive(*fh); 783 *fh = NULL; 784 mutex_unlock(&ree_fs_mutex); 785 786 } 787 } 788 789 static TEE_Result ree_fs_create(struct tee_pobj *po, bool overwrite, 790 const void *head, size_t head_size, 791 const void *attr, size_t attr_size, 792 const void *data_core, const void *data_user, 793 size_t data_size, struct tee_file_handle **fh) 794 { 795 struct tee_fs_fd *fdp; 796 struct tee_fs_dirfile_dirh *dirh = NULL; 797 struct tee_fs_dirfile_fileh dfh; 798 TEE_Result res; 799 size_t pos = 0; 800 801 /* One of data_core and data_user must be NULL */ 802 assert(!data_core || !data_user); 803 804 *fh = NULL; 805 mutex_lock(&ree_fs_mutex); 806 807 res = get_dirh(&dirh); 808 if (res) 809 goto out; 810 811 res = tee_fs_dirfile_get_tmp(dirh, &dfh); 812 if (res) 813 goto out; 814 815 res = ree_fs_open_primitive(true, dfh.hash, 0, &po->uuid, &dfh, fh); 816 if (res) 817 goto out; 818 819 if (head && head_size) { 820 res = ree_fs_write_primitive(*fh, pos, head, NULL, head_size); 821 if (res) 822 goto out; 823 pos += head_size; 824 } 825 826 if (attr && attr_size) { 827 res = ree_fs_write_primitive(*fh, pos, attr, NULL, attr_size); 828 if (res) 829 goto out; 830 pos += attr_size; 831 } 832 833 if ((data_core || data_user) && data_size) { 834 res = ree_fs_write_primitive(*fh, pos, data_core, data_user, 835 data_size); 836 if (res) 837 goto out; 838 } 839 840 fdp = (struct tee_fs_fd *)*fh; 841 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL); 842 if (res) 843 goto out; 844 845 res = set_name(dirh, fdp, po, overwrite); 846 out: 847 if (res) { 848 put_dirh(dirh, true); 849 if (*fh) { 850 ree_fs_close_primitive(*fh); 851 *fh = NULL; 852 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh); 853 } 854 } 855 mutex_unlock(&ree_fs_mutex); 856 857 return res; 858 } 859 860 static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos, 861 const void *buf_core, const void *buf_user, 862 size_t len) 863 { 864 TEE_Result res; 865 struct tee_fs_dirfile_dirh *dirh = NULL; 866 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 867 868 /* One of buf_core and buf_user must be NULL */ 869 assert(!buf_core || !buf_user); 870 871 mutex_lock(&ree_fs_mutex); 872 873 res = get_dirh(&dirh); 874 if (res) 875 goto out; 876 877 res = ree_fs_write_primitive(fh, pos, buf_core, buf_user, len); 878 if (res) 879 goto out; 880 881 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL); 882 if (res) 883 goto out; 884 885 res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh); 886 if (res) 887 goto out; 888 res = commit_dirh_writes(dirh); 889 out: 890 put_dirh(dirh, res); 891 mutex_unlock(&ree_fs_mutex); 892 893 return res; 894 } 895 896 static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new, 897 bool overwrite) 898 { 899 TEE_Result res; 900 struct tee_fs_dirfile_dirh *dirh = NULL; 901 struct tee_fs_dirfile_fileh dfh; 902 struct tee_fs_dirfile_fileh remove_dfh = { .idx = -1 }; 903 904 if (!new) 905 return TEE_ERROR_BAD_PARAMETERS; 906 907 mutex_lock(&ree_fs_mutex); 908 res = get_dirh(&dirh); 909 if (res) 910 goto out; 911 912 res = tee_fs_dirfile_find(dirh, &new->uuid, new->obj_id, 913 new->obj_id_len, &remove_dfh); 914 if (!res && !overwrite) { 915 res = TEE_ERROR_ACCESS_CONFLICT; 916 goto out; 917 } 918 919 res = tee_fs_dirfile_find(dirh, &old->uuid, old->obj_id, 920 old->obj_id_len, &dfh); 921 if (res) 922 goto out; 923 924 res = tee_fs_dirfile_rename(dirh, &new->uuid, &dfh, new->obj_id, 925 new->obj_id_len); 926 if (res) 927 goto out; 928 929 if (remove_dfh.idx != -1) { 930 res = tee_fs_dirfile_remove(dirh, &remove_dfh); 931 if (res) 932 goto out; 933 } 934 935 res = commit_dirh_writes(dirh); 936 if (res) 937 goto out; 938 939 if (remove_dfh.idx != -1) 940 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &remove_dfh); 941 942 out: 943 put_dirh(dirh, res); 944 mutex_unlock(&ree_fs_mutex); 945 946 return res; 947 948 } 949 950 static TEE_Result ree_fs_remove(struct tee_pobj *po) 951 { 952 TEE_Result res; 953 struct tee_fs_dirfile_dirh *dirh = NULL; 954 struct tee_fs_dirfile_fileh dfh; 955 956 mutex_lock(&ree_fs_mutex); 957 res = get_dirh(&dirh); 958 if (res) 959 goto out; 960 961 res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len, 962 &dfh); 963 if (res) 964 goto out; 965 966 res = tee_fs_dirfile_remove(dirh, &dfh); 967 if (res) 968 goto out; 969 970 res = commit_dirh_writes(dirh); 971 if (res) 972 goto out; 973 974 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh); 975 976 assert(tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len, 977 &dfh)); 978 out: 979 put_dirh(dirh, res); 980 mutex_unlock(&ree_fs_mutex); 981 982 return res; 983 } 984 985 static TEE_Result ree_fs_truncate(struct tee_file_handle *fh, size_t len) 986 { 987 TEE_Result res; 988 struct tee_fs_dirfile_dirh *dirh = NULL; 989 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; 990 991 mutex_lock(&ree_fs_mutex); 992 993 res = get_dirh(&dirh); 994 if (res) 995 goto out; 996 997 res = ree_fs_ftruncate_internal(fdp, len); 998 if (res) 999 goto out; 1000 1001 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL); 1002 if (res) 1003 goto out; 1004 1005 res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh); 1006 if (res) 1007 goto out; 1008 res = commit_dirh_writes(dirh); 1009 out: 1010 put_dirh(dirh, res); 1011 mutex_unlock(&ree_fs_mutex); 1012 1013 return res; 1014 } 1015 1016 static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid, 1017 struct tee_fs_dir **dir) 1018 1019 { 1020 TEE_Result res; 1021 struct tee_fs_dir *d = calloc(1, sizeof(*d)); 1022 1023 if (!d) 1024 return TEE_ERROR_OUT_OF_MEMORY; 1025 1026 d->uuid = uuid; 1027 1028 mutex_lock(&ree_fs_mutex); 1029 1030 res = get_dirh(&d->dirh); 1031 if (res) 1032 goto out; 1033 1034 /* See that there's at least one file */ 1035 d->idx = -1; 1036 d->d.oidlen = sizeof(d->d.oid); 1037 res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid, 1038 &d->d.oidlen); 1039 d->idx = -1; 1040 1041 out: 1042 if (!res) { 1043 *dir = d; 1044 } else { 1045 if (d) 1046 put_dirh(d->dirh, false); 1047 free(d); 1048 } 1049 mutex_unlock(&ree_fs_mutex); 1050 1051 return res; 1052 } 1053 1054 static void ree_fs_closedir_rpc(struct tee_fs_dir *d) 1055 { 1056 if (d) { 1057 mutex_lock(&ree_fs_mutex); 1058 1059 put_dirh(d->dirh, false); 1060 free(d); 1061 1062 mutex_unlock(&ree_fs_mutex); 1063 } 1064 } 1065 1066 static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d, 1067 struct tee_fs_dirent **ent) 1068 { 1069 TEE_Result res; 1070 1071 mutex_lock(&ree_fs_mutex); 1072 1073 d->d.oidlen = sizeof(d->d.oid); 1074 res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid, 1075 &d->d.oidlen); 1076 if (res == TEE_SUCCESS) 1077 *ent = &d->d; 1078 1079 mutex_unlock(&ree_fs_mutex); 1080 1081 return res; 1082 } 1083 1084 const struct tee_file_operations ree_fs_ops = { 1085 .open = ree_fs_open, 1086 .create = ree_fs_create, 1087 .close = ree_fs_close, 1088 .read = ree_fs_read, 1089 .write = ree_fs_write, 1090 .truncate = ree_fs_truncate, 1091 .rename = ree_fs_rename, 1092 .remove = ree_fs_remove, 1093 .opendir = ree_fs_opendir_rpc, 1094 .closedir = ree_fs_closedir_rpc, 1095 .readdir = ree_fs_readdir_rpc, 1096 }; 1097