1 /* 2 * Copyright (c) 2014, STMicroelectronics International N.V. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <kernel/tee_ta_manager.h> 29 #include <kernel/tee_misc.h> 30 #include <mm/tee_mmu.h> 31 #include <tee/tee_fs.h> 32 #include <tee/tee_fs_defs.h> 33 #include <tee/tee_obj.h> 34 #include <tee/tee_svc.h> 35 #include <tee/tee_pobj.h> 36 #include <tee/tee_svc_storage.h> 37 #include <tee/tee_svc_cryp.h> 38 #include <tee_api_defines.h> 39 #include <tee_api_defines_extensions.h> 40 #include <trace.h> 41 42 /* 43 * Returns the appropriate tee_file_operations for the specified storage ID. 44 * The value TEE_STORAGE_PRIVATE will select the REE FS if available, otherwise 45 * RPMB. 46 */ 47 static const struct tee_file_operations *file_ops(uint32_t storage_id) 48 { 49 50 switch (storage_id) { 51 case TEE_STORAGE_PRIVATE: 52 #if defined(CFG_REE_FS) 53 return &ree_fs_ops; 54 #elif defined(CFG_RPMB_FS) 55 return &rpmb_fs_ops; 56 #else 57 #error At least one filesystem must be enabled. 58 #endif 59 #ifdef CFG_REE_FS 60 case TEE_STORAGE_PRIVATE_REE: 61 return &ree_fs_ops; 62 #endif 63 #ifdef CFG_RPMB_FS 64 case TEE_STORAGE_PRIVATE_RPMB: 65 return &rpmb_fs_ops; 66 #endif 67 default: 68 return NULL; 69 } 70 } 71 72 /* SSF (Secure Storage File version 00 */ 73 #define TEE_SVC_STORAGE_MAGIC 0x53534600; 74 75 /* Header of GP formated secure storage files */ 76 struct tee_svc_storage_head { 77 uint32_t magic; 78 uint32_t head_size; 79 uint32_t meta_size; 80 uint32_t ds_size; 81 uint32_t keySize; 82 uint32_t maxKeySize; 83 uint32_t objectUsage; 84 uint32_t objectType; 85 uint32_t have_attrs; 86 }; 87 88 struct tee_storage_enum { 89 TAILQ_ENTRY(tee_storage_enum) link; 90 struct tee_fs_dir *dir; 91 const struct tee_file_operations *fops; 92 }; 93 94 static TEE_Result tee_svc_storage_get_enum(struct user_ta_ctx *utc, 95 uint32_t enum_id, 96 struct tee_storage_enum **e_out) 97 { 98 struct tee_storage_enum *e; 99 100 TAILQ_FOREACH(e, &utc->storage_enums, link) { 101 if (enum_id == (vaddr_t)e) { 102 *e_out = e; 103 return TEE_SUCCESS; 104 } 105 } 106 return TEE_ERROR_BAD_PARAMETERS; 107 } 108 109 static TEE_Result tee_svc_close_enum(struct user_ta_ctx *utc, 110 struct tee_storage_enum *e) 111 { 112 int ret; 113 114 if (e == NULL || utc == NULL) 115 return TEE_ERROR_BAD_PARAMETERS; 116 117 TAILQ_REMOVE(&utc->storage_enums, e, link); 118 119 if (!e->fops) 120 return TEE_ERROR_ITEM_NOT_FOUND; 121 122 ret = e->fops->closedir(e->dir); 123 e->dir = NULL; 124 e->fops = NULL; 125 126 free(e); 127 128 if (ret != 0) 129 return TEE_ERROR_ITEM_NOT_FOUND; 130 131 return TEE_SUCCESS; 132 } 133 134 /* "/TA_uuid/object_id" or "/TA_uuid/.object_id" */ 135 char *tee_svc_storage_create_filename(struct tee_ta_session *sess, 136 void *object_id, 137 uint32_t object_id_len, 138 bool transient) 139 { 140 uint8_t *file; 141 uint32_t pos = 0; 142 uint32_t hslen = 1 /* Leading slash */ 143 + TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID) + object_id_len) 144 + 1; /* Intermediate slash */ 145 146 /* +1 for the '.' (temporary persistent object) */ 147 if (transient) 148 hslen++; 149 150 file = malloc(hslen); 151 if (!file) 152 return NULL; 153 154 file[pos++] = '/'; 155 pos += tee_b2hs((uint8_t *)&sess->ctx->uuid, &file[pos], 156 sizeof(TEE_UUID), hslen); 157 file[pos++] = '/'; 158 159 if (transient) 160 file[pos++] = '.'; 161 162 tee_b2hs(object_id, file + pos, object_id_len, hslen - pos); 163 164 return (char *)file; 165 } 166 167 /* "/TA_uuid" */ 168 char *tee_svc_storage_create_dirname(struct tee_ta_session *sess) 169 { 170 uint8_t *dir; 171 uint32_t hslen = TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID)) + 1; 172 173 dir = malloc(hslen); 174 if (!dir) 175 return NULL; 176 177 dir[0] = '/'; 178 tee_b2hs((uint8_t *)&sess->ctx->uuid, dir + 1, sizeof(TEE_UUID), 179 hslen); 180 181 return (char *)dir; 182 } 183 184 static TEE_Result tee_svc_storage_remove_corrupt_obj( 185 struct tee_ta_session *sess, 186 struct tee_obj *o) 187 { 188 TEE_Result res; 189 char *file = NULL; 190 char *dir = NULL; 191 const struct tee_file_operations *fops = o->pobj->fops; 192 193 file = tee_svc_storage_create_filename(sess, 194 o->pobj->obj_id, 195 o->pobj->obj_id_len, 196 false); 197 if (file == NULL) { 198 res = TEE_ERROR_OUT_OF_MEMORY; 199 goto exit; 200 } 201 202 tee_obj_close(to_user_ta_ctx(sess->ctx), o); 203 fops->unlink(file); 204 free(file); 205 dir = tee_svc_storage_create_dirname(sess); 206 if (dir != NULL) { 207 fops->rmdir(dir); 208 free(dir); 209 } 210 211 res = TEE_SUCCESS; 212 213 exit: 214 return res; 215 } 216 217 static uint32_t tee_svc_storage_conv_oflags(uint32_t flags) 218 { 219 uint32_t out = 0; 220 221 if (flags & (TEE_DATA_FLAG_ACCESS_READ | TEE_DATA_FLAG_SHARE_READ)) { 222 if (flags & (TEE_DATA_FLAG_ACCESS_WRITE | 223 TEE_DATA_FLAG_ACCESS_WRITE_META | 224 TEE_DATA_FLAG_SHARE_WRITE)) 225 out |= TEE_FS_O_RDWR; 226 else 227 out |= TEE_FS_O_RDONLY; 228 } else { 229 if (flags & (TEE_DATA_FLAG_ACCESS_WRITE | 230 TEE_DATA_FLAG_ACCESS_WRITE_META | 231 TEE_DATA_FLAG_SHARE_WRITE)) 232 out |= TEE_FS_O_WRONLY; 233 } 234 235 return out; 236 } 237 238 static int tee_svc_storage_conv_whence(TEE_Whence whence) 239 { 240 switch (whence) { 241 case TEE_DATA_SEEK_SET: 242 return TEE_FS_SEEK_SET; 243 case TEE_DATA_SEEK_CUR: 244 return TEE_FS_SEEK_CUR; 245 case TEE_DATA_SEEK_END: 246 return TEE_FS_SEEK_END; 247 default: 248 return -1; 249 } 250 } 251 252 static TEE_Result tee_svc_storage_create_file(struct tee_ta_session *sess, 253 char *file, 254 const struct tee_file_operations *fops, 255 int *fd) 256 { 257 TEE_Result res = TEE_SUCCESS; 258 char *dir = NULL; 259 int tmp; 260 int err; 261 uint32_t cflags = TEE_FS_O_WRONLY | 262 TEE_FS_O_CREATE | TEE_FS_O_TRUNC; 263 264 dir = tee_svc_storage_create_dirname(sess); 265 if (dir == NULL) { 266 res = TEE_ERROR_OUT_OF_MEMORY; 267 goto exit; 268 } 269 270 /* try and make directory */ 271 err = fops->access(dir, TEE_FS_F_OK); 272 if (err) { 273 /* directory does not exists */ 274 tmp = fops->mkdir(dir, TEE_FS_S_IRUSR | TEE_FS_S_IWUSR); 275 276 if (tmp < 0) { 277 /* error codes needs better granularity */ 278 res = TEE_ERROR_GENERIC; 279 goto exit; 280 } 281 } 282 283 /* try and open again */ 284 *fd = fops->open(&res, file, cflags); 285 286 exit: 287 free(dir); 288 289 return res; 290 } 291 292 static TEE_Result tee_svc_storage_read_head(struct tee_ta_session *sess, 293 struct tee_obj *o) 294 { 295 TEE_Result res = TEE_SUCCESS; 296 int fd = -1; 297 int err; 298 struct tee_svc_storage_head head; 299 char *file = NULL; 300 const struct tee_file_operations *fops; 301 void *attr = NULL; 302 303 if (o == NULL || o->pobj == NULL) 304 return TEE_ERROR_BAD_PARAMETERS; 305 306 fops = o->pobj->fops; 307 308 file = tee_svc_storage_create_filename(sess, 309 o->pobj->obj_id, 310 o->pobj->obj_id_len, 311 false); 312 if (file == NULL) { 313 res = TEE_ERROR_OUT_OF_MEMORY; 314 goto exit; 315 } 316 317 fd = fops->open(&res, file, TEE_FS_O_RDONLY); 318 free(file); 319 if (fd < 0) 320 goto exit; 321 322 /* read head */ 323 err = fops->read(&res, fd, &head, 324 sizeof(struct tee_svc_storage_head)); 325 if (err < 0) { 326 if (res == TEE_ERROR_CORRUPT_OBJECT) 327 EMSG("Head corrupt\n"); 328 goto exit; 329 } 330 331 if (err != sizeof(struct tee_svc_storage_head)) { 332 res = TEE_ERROR_BAD_FORMAT; 333 goto exit; 334 } 335 336 res = tee_obj_set_type(o, head.objectType, head.maxKeySize); 337 if (res != TEE_SUCCESS) 338 goto exit; 339 340 if (head.meta_size) { 341 attr = malloc(head.meta_size); 342 if (!attr) { 343 res = TEE_ERROR_OUT_OF_MEMORY; 344 goto exit; 345 } 346 347 /* read meta */ 348 err = fops->read(&res, fd, attr, head.meta_size); 349 if (err != (int)head.meta_size) { 350 res = TEE_ERROR_CORRUPT_OBJECT; 351 goto exit; 352 } 353 } 354 355 res = tee_obj_attr_from_binary(o, attr, head.meta_size); 356 if (res != TEE_SUCCESS) 357 goto exit; 358 359 o->info.dataSize = head.ds_size; 360 o->info.keySize = head.keySize; 361 o->info.objectUsage = head.objectUsage; 362 o->info.objectType = head.objectType; 363 o->have_attrs = head.have_attrs; 364 365 exit: 366 free(attr); 367 if (fd >= 0) 368 fops->close(fd); 369 370 return res; 371 } 372 373 static TEE_Result tee_svc_storage_init_file(struct tee_ta_session *sess, 374 struct tee_obj *o, 375 struct tee_obj *attr_o, void *data, 376 uint32_t len) 377 { 378 TEE_Result res = TEE_SUCCESS; 379 int fd = -1; 380 int err = -1; 381 struct tee_svc_storage_head head; 382 char *tmpfile = NULL; 383 const struct tee_file_operations *fops; 384 void *attr = NULL; 385 size_t attr_size = 0; 386 387 if (o == NULL || o->pobj == NULL) 388 return TEE_ERROR_BAD_PARAMETERS; 389 390 fops = o->pobj->fops; 391 392 /* create temporary persistent object filename */ 393 tmpfile = tee_svc_storage_create_filename(sess, 394 o->pobj->obj_id, 395 o->pobj->obj_id_len, 396 true); 397 398 if (tmpfile == NULL) { 399 res = TEE_ERROR_OUT_OF_MEMORY; 400 goto exit; 401 } 402 403 res = tee_svc_storage_create_file(sess, tmpfile, fops, &fd); 404 if (res != TEE_SUCCESS) 405 goto exit; 406 407 if (attr_o) { 408 res = tee_obj_set_type(o, attr_o->info.objectType, 409 attr_o->info.maxKeySize); 410 if (res != TEE_SUCCESS) 411 goto exit; 412 res = tee_obj_attr_copy_from(o, attr_o); 413 if (res != TEE_SUCCESS) 414 goto exit; 415 o->have_attrs = attr_o->have_attrs; 416 o->info.objectUsage = attr_o->info.objectUsage; 417 o->info.keySize = attr_o->info.keySize; 418 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 419 if (res != TEE_SUCCESS) 420 goto exit; 421 if (attr_size) { 422 attr = malloc(attr_size); 423 if (!attr) { 424 res = TEE_ERROR_OUT_OF_MEMORY; 425 goto exit; 426 } 427 res = tee_obj_attr_to_binary(o, attr, &attr_size); 428 if (res != TEE_SUCCESS) 429 goto exit; 430 } 431 } else { 432 res = tee_obj_set_type(o, TEE_TYPE_DATA, 0); 433 if (res != TEE_SUCCESS) 434 goto exit; 435 } 436 437 /* write head */ 438 head.magic = TEE_SVC_STORAGE_MAGIC; 439 head.head_size = sizeof(struct tee_svc_storage_head); 440 head.meta_size = attr_size; 441 head.ds_size = len; 442 head.keySize = o->info.keySize; 443 head.maxKeySize = o->info.maxKeySize; 444 head.objectUsage = o->info.objectUsage; 445 head.objectType = o->info.objectType; 446 head.have_attrs = o->have_attrs; 447 448 /* write head */ 449 err = fops->write(&res, fd, &head, 450 sizeof(struct tee_svc_storage_head)); 451 /* error codes needs better granularity */ 452 if (err != sizeof(struct tee_svc_storage_head)) 453 goto exit; 454 455 /* write meta */ 456 err = fops->write(&res, fd, attr, attr_size); 457 if (err != (int)attr_size) 458 goto exit; 459 460 /* write init data */ 461 o->info.dataSize = len; 462 463 /* write data to fs if needed */ 464 if (data && len) { 465 err = fops->write(&res, fd, data, len); 466 if (err != (int)len) 467 goto exit; 468 } 469 470 exit: 471 free(attr); 472 free(tmpfile); 473 tmpfile = NULL; 474 if (fd != -1) 475 fops->close(fd); 476 477 return res; 478 } 479 480 TEE_Result syscall_storage_obj_open(unsigned long storage_id, void *object_id, 481 size_t object_id_len, unsigned long flags, 482 uint32_t *obj) 483 { 484 TEE_Result res; 485 struct tee_ta_session *sess; 486 struct tee_obj *o; 487 char *file = NULL; 488 int fs_flags; 489 int fd = -1; 490 tee_fs_off_t off; 491 tee_fs_off_t e_off; 492 struct tee_pobj *po = NULL; 493 int err = -1; 494 struct user_ta_ctx *utc; 495 const struct tee_file_operations *fops = file_ops(storage_id); 496 size_t attr_size; 497 498 if (!fops) { 499 res = TEE_ERROR_ITEM_NOT_FOUND; 500 goto exit; 501 } 502 503 if (object_id_len > TEE_OBJECT_ID_MAX_LEN) { 504 res = TEE_ERROR_BAD_PARAMETERS; 505 goto exit; 506 } 507 508 res = tee_ta_get_current_session(&sess); 509 if (res != TEE_SUCCESS) 510 goto err; 511 utc = to_user_ta_ctx(sess->ctx); 512 513 res = tee_mmu_check_access_rights(utc, 514 TEE_MEMORY_ACCESS_READ | 515 TEE_MEMORY_ACCESS_ANY_OWNER, 516 (tee_uaddr_t) object_id, 517 object_id_len); 518 if (res != TEE_SUCCESS) 519 goto err; 520 521 res = tee_pobj_get((void *)&sess->ctx->uuid, object_id, 522 object_id_len, flags, fops, &po); 523 if (res != TEE_SUCCESS) 524 goto err; 525 526 fs_flags = tee_svc_storage_conv_oflags(flags); 527 528 o = tee_obj_alloc(); 529 if (o == NULL) { 530 res = TEE_ERROR_OUT_OF_MEMORY; 531 goto err; 532 } 533 534 o->info.handleFlags = 535 TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED; 536 o->flags = flags; 537 o->pobj = po; 538 539 res = tee_svc_storage_read_head(sess, o); 540 if (res != TEE_SUCCESS) { 541 tee_obj_add(utc, o); 542 if (res == TEE_ERROR_CORRUPT_OBJECT) { 543 EMSG("Object corrupt\n"); 544 res = tee_svc_storage_remove_corrupt_obj(sess, o); 545 if (res != TEE_SUCCESS) 546 goto exit; 547 res = TEE_ERROR_CORRUPT_OBJECT; 548 goto exit; 549 } 550 goto oclose; 551 } 552 553 file = tee_svc_storage_create_filename(sess, object_id, 554 object_id_len, false); 555 if (file == NULL) { 556 res = TEE_ERROR_OUT_OF_MEMORY; 557 goto err; 558 } 559 560 err = fops->access(file, TEE_FS_F_OK); 561 if (err) { 562 /* file not found */ 563 res = TEE_ERROR_STORAGE_NOT_AVAILABLE; 564 goto err; 565 } 566 567 fd = fops->open(&res, file, fs_flags); 568 if (fd < 0) { 569 goto err; 570 } 571 o->fd = fd; 572 573 tee_obj_add(utc, o); 574 575 res = tee_svc_copy_kaddr_to_uref(obj, o); 576 if (res != TEE_SUCCESS) 577 goto oclose; 578 579 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 580 if (res != TEE_SUCCESS && res) 581 goto oclose; 582 583 e_off = sizeof(struct tee_svc_storage_head) + attr_size; 584 off = fops->lseek(&res, fd, e_off, TEE_FS_SEEK_SET); 585 if (off != e_off) { 586 res = TEE_ERROR_NO_DATA; 587 goto oclose; 588 } 589 590 goto exit; 591 592 oclose: 593 tee_obj_close(utc, o); 594 595 err: 596 if (res == TEE_ERROR_NO_DATA || res == TEE_ERROR_BAD_FORMAT) 597 res = TEE_ERROR_CORRUPT_OBJECT; 598 if (res == TEE_ERROR_CORRUPT_OBJECT) 599 fops->unlink(file); 600 if (fd >= 0) 601 fops->close(fd); 602 if (po) 603 tee_pobj_release(po); 604 605 exit: 606 free(file); 607 file = NULL; 608 return res; 609 } 610 611 TEE_Result syscall_storage_obj_create(unsigned long storage_id, void *object_id, 612 size_t object_id_len, unsigned long flags, 613 unsigned long attr, void *data, size_t len, 614 uint32_t *obj) 615 { 616 TEE_Result res; 617 struct tee_ta_session *sess; 618 struct tee_obj *o = NULL; 619 struct tee_obj *attr_o = NULL; 620 char *file = NULL; 621 int fd = -1; 622 int fs_flags; 623 tee_fs_off_t off; 624 tee_fs_off_t e_off; 625 struct tee_pobj *po = NULL; 626 char *tmpfile = NULL; 627 int err = -1; 628 int filedoesnotexist; 629 struct user_ta_ctx *utc; 630 const struct tee_file_operations *fops = file_ops(storage_id); 631 size_t attr_size; 632 633 if (!fops) 634 return TEE_ERROR_ITEM_NOT_FOUND; 635 636 if (object_id_len > TEE_OBJECT_ID_MAX_LEN) 637 return TEE_ERROR_BAD_PARAMETERS; 638 639 res = tee_ta_get_current_session(&sess); 640 if (res != TEE_SUCCESS) 641 return res; 642 utc = to_user_ta_ctx(sess->ctx); 643 644 res = tee_mmu_check_access_rights(utc, 645 TEE_MEMORY_ACCESS_READ | 646 TEE_MEMORY_ACCESS_ANY_OWNER, 647 (tee_uaddr_t) object_id, 648 object_id_len); 649 if (res != TEE_SUCCESS) 650 goto err; 651 652 res = tee_pobj_get((void *)&sess->ctx->uuid, object_id, 653 object_id_len, flags, fops, &po); 654 if (res != TEE_SUCCESS) 655 goto err; 656 657 /* check rights of the provided buffer */ 658 if (data && len) { 659 res = tee_mmu_check_access_rights(utc, 660 TEE_MEMORY_ACCESS_READ | 661 TEE_MEMORY_ACCESS_ANY_OWNER, 662 (tee_uaddr_t) data, len); 663 664 if (res != TEE_SUCCESS) 665 goto err; 666 } 667 668 o = tee_obj_alloc(); 669 if (o == NULL) { 670 res = TEE_ERROR_OUT_OF_MEMORY; 671 goto err; 672 } 673 674 o->info.handleFlags = 675 TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED; 676 o->flags = flags; 677 o->pobj = po; 678 679 if (attr != TEE_HANDLE_NULL) { 680 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(attr), 681 &attr_o); 682 if (res != TEE_SUCCESS) 683 goto err; 684 } 685 686 res = tee_svc_storage_init_file(sess, o, attr_o, data, len); 687 if (res != TEE_SUCCESS) 688 goto err; 689 690 /* create persistent object filename */ 691 file = tee_svc_storage_create_filename(sess, object_id, 692 object_id_len, false); 693 if (file == NULL) { 694 res = TEE_ERROR_OUT_OF_MEMORY; 695 goto err; 696 } 697 698 filedoesnotexist = fops->access(file, TEE_FS_F_OK); 699 if (!filedoesnotexist) { 700 /* file exists */ 701 if (!(flags & TEE_DATA_FLAG_OVERWRITE)) { 702 res = TEE_ERROR_ACCESS_CONFLICT; 703 goto err; 704 } 705 } 706 707 /* create temporary persistent object filename */ 708 tmpfile = tee_svc_storage_create_filename(sess, object_id, 709 object_id_len, 710 true); 711 if (tmpfile == NULL) { 712 res = TEE_ERROR_OUT_OF_MEMORY; 713 goto err; 714 } 715 716 /* 717 * remove the file if it exists, because rename does not perform 718 * this operation. Note that it delete and rename should be atomic, 719 * which is not the case currently. 720 * Fixme: unlink must be removed once rename() support prior deletion 721 * of the new file name when it already exists. 722 */ 723 if (!filedoesnotexist) 724 fops->unlink(file); 725 /* rename temporary persistent object filename */ 726 err = fops->rename(tmpfile, file); 727 if (err) { 728 /* error codes needs better granularity */ 729 res = TEE_ERROR_GENERIC; 730 goto rmfile; 731 } 732 733 fs_flags = tee_svc_storage_conv_oflags(flags); 734 735 fd = fops->open(&res, file, fs_flags); 736 if (fd < 0) { 737 goto err; 738 } 739 o->fd = fd; 740 741 tee_obj_add(utc, o); 742 743 res = tee_svc_copy_kaddr_to_uref(obj, o); 744 if (res != TEE_SUCCESS) 745 goto oclose; 746 747 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 748 if (res != TEE_SUCCESS) 749 goto oclose; 750 751 e_off = sizeof(struct tee_svc_storage_head) + attr_size; 752 off = fops->lseek(&res, fd, e_off, TEE_FS_SEEK_SET); 753 if (off != e_off) { 754 res = TEE_ERROR_NO_DATA; 755 goto oclose; 756 } 757 758 goto exit; 759 760 oclose: 761 tee_obj_close(utc, o); 762 goto exit; 763 764 rmfile: 765 fops->unlink(tmpfile); 766 767 err: 768 if (res == TEE_ERROR_NO_DATA || res == TEE_ERROR_BAD_FORMAT) 769 res = TEE_ERROR_CORRUPT_OBJECT; 770 if (res == TEE_ERROR_CORRUPT_OBJECT) 771 fops->unlink(file); 772 if (fd >= 0) 773 fops->close(fd); 774 if (po) 775 tee_pobj_release(po); 776 if (o) 777 free(o); 778 779 exit: 780 free(file); 781 file = NULL; 782 free(tmpfile); 783 tmpfile = NULL; 784 785 return res; 786 } 787 788 TEE_Result syscall_storage_obj_del(unsigned long obj) 789 { 790 TEE_Result res; 791 struct tee_ta_session *sess; 792 struct tee_obj *o; 793 int err; 794 char *file; 795 char *dir; 796 struct user_ta_ctx *utc; 797 const struct tee_file_operations *fops; 798 799 res = tee_ta_get_current_session(&sess); 800 if (res != TEE_SUCCESS) 801 return res; 802 utc = to_user_ta_ctx(sess->ctx); 803 804 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o); 805 if (res != TEE_SUCCESS) 806 return res; 807 808 if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE_META)) 809 return TEE_ERROR_ACCESS_CONFLICT; 810 811 if (o->pobj == NULL || o->pobj->obj_id == NULL) 812 return TEE_ERROR_BAD_STATE; 813 814 file = tee_svc_storage_create_filename(sess, o->pobj->obj_id, 815 o->pobj->obj_id_len, false); 816 if (file == NULL) 817 return TEE_ERROR_OUT_OF_MEMORY; 818 819 fops = o->pobj->fops; 820 tee_obj_close(utc, o); 821 822 err = fops->access(file, TEE_FS_F_OK); 823 if (err) 824 /* file not found */ 825 return TEE_ERROR_STORAGE_NOT_AVAILABLE; 826 827 err = fops->unlink(file); 828 free(file); 829 if (err) 830 /* error codes needs better granularity */ 831 return TEE_ERROR_GENERIC; 832 833 /* try and remove dir */ 834 dir = tee_svc_storage_create_dirname(sess); 835 if (dir == NULL) 836 return TEE_ERROR_OUT_OF_MEMORY; 837 /* ignore result */ 838 fops->rmdir(dir); 839 free(dir); 840 841 return TEE_SUCCESS; 842 } 843 844 TEE_Result syscall_storage_obj_rename(unsigned long obj, void *object_id, 845 size_t object_id_len) 846 { 847 TEE_Result res; 848 struct tee_ta_session *sess; 849 struct tee_obj *o; 850 struct tee_pobj *po = NULL; 851 char *new_file = NULL; 852 char *old_file = NULL; 853 int err = -1; 854 struct user_ta_ctx *utc; 855 const struct tee_file_operations *fops; 856 857 if (object_id_len > TEE_OBJECT_ID_MAX_LEN) 858 return TEE_ERROR_BAD_PARAMETERS; 859 860 res = tee_ta_get_current_session(&sess); 861 if (res != TEE_SUCCESS) 862 return res; 863 utc = to_user_ta_ctx(sess->ctx); 864 865 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o); 866 if (res != TEE_SUCCESS) 867 return res; 868 869 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 870 res = TEE_ERROR_BAD_STATE; 871 goto exit; 872 } 873 874 if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE_META)) { 875 res = TEE_ERROR_BAD_STATE; 876 goto exit; 877 } 878 879 if (o->pobj == NULL || o->pobj->obj_id == NULL) { 880 res = TEE_ERROR_BAD_STATE; 881 goto exit; 882 } 883 884 res = tee_mmu_check_access_rights(utc, 885 TEE_MEMORY_ACCESS_READ | 886 TEE_MEMORY_ACCESS_ANY_OWNER, 887 (tee_uaddr_t) object_id, object_id_len); 888 if (res != TEE_SUCCESS) 889 goto exit; 890 891 res = tee_obj_verify(sess, o); 892 if (res != TEE_SUCCESS) 893 goto exit; 894 895 /* get new ds name */ 896 new_file = tee_svc_storage_create_filename(sess, object_id, 897 object_id_len, false); 898 if (new_file == NULL) { 899 res = TEE_ERROR_OUT_OF_MEMORY; 900 goto exit; 901 } 902 903 old_file = tee_svc_storage_create_filename(sess, o->pobj->obj_id, 904 o->pobj->obj_id_len, false); 905 if (old_file == NULL) { 906 res = TEE_ERROR_OUT_OF_MEMORY; 907 goto exit; 908 } 909 910 /* reserve dest name */ 911 fops = o->pobj->fops; 912 res = tee_pobj_get((void *)&sess->ctx->uuid, object_id, 913 object_id_len, TEE_DATA_FLAG_ACCESS_WRITE_META, 914 fops, &po); 915 if (res != TEE_SUCCESS) 916 goto exit; 917 918 err = fops->access(new_file, TEE_FS_F_OK); 919 if (err == 0) { 920 /* file exists */ 921 res = TEE_ERROR_ACCESS_CONFLICT; 922 goto exit; 923 } 924 925 /* move */ 926 err = fops->rename(old_file, new_file); 927 if (err) { 928 /* error codes needs better granularity */ 929 res = TEE_ERROR_GENERIC; 930 goto exit; 931 } 932 933 res = tee_pobj_rename(o->pobj, object_id, object_id_len); 934 935 exit: 936 tee_pobj_release(po); 937 938 free(new_file); 939 free(old_file); 940 941 return res; 942 } 943 944 TEE_Result syscall_storage_alloc_enum(uint32_t *obj_enum) 945 { 946 struct tee_storage_enum *e; 947 struct tee_ta_session *sess; 948 TEE_Result res; 949 struct user_ta_ctx *utc; 950 951 if (obj_enum == NULL) 952 return TEE_ERROR_BAD_PARAMETERS; 953 954 res = tee_ta_get_current_session(&sess); 955 if (res != TEE_SUCCESS) 956 return res; 957 utc = to_user_ta_ctx(sess->ctx); 958 959 e = malloc(sizeof(struct tee_storage_enum)); 960 if (e == NULL) 961 return TEE_ERROR_OUT_OF_MEMORY; 962 963 e->dir = NULL; 964 e->fops = NULL; 965 TAILQ_INSERT_TAIL(&utc->storage_enums, e, link); 966 967 return tee_svc_copy_kaddr_to_uref(obj_enum, e); 968 } 969 970 TEE_Result syscall_storage_free_enum(unsigned long obj_enum) 971 { 972 struct tee_storage_enum *e; 973 TEE_Result res; 974 struct tee_ta_session *sess; 975 struct user_ta_ctx *utc; 976 977 res = tee_ta_get_current_session(&sess); 978 if (res != TEE_SUCCESS) 979 return res; 980 utc = to_user_ta_ctx(sess->ctx); 981 982 res = tee_svc_storage_get_enum(utc, 983 tee_svc_uref_to_vaddr(obj_enum), &e); 984 if (res != TEE_SUCCESS) 985 return res; 986 987 return tee_svc_close_enum(utc, e); 988 } 989 990 TEE_Result syscall_storage_reset_enum(unsigned long obj_enum) 991 { 992 struct tee_storage_enum *e; 993 TEE_Result res; 994 struct tee_ta_session *sess; 995 996 res = tee_ta_get_current_session(&sess); 997 if (res != TEE_SUCCESS) 998 return res; 999 1000 res = tee_svc_storage_get_enum(to_user_ta_ctx(sess->ctx), 1001 tee_svc_uref_to_vaddr(obj_enum), &e); 1002 if (res != TEE_SUCCESS) 1003 return res; 1004 1005 res = e->fops->closedir(e->dir); 1006 e->dir = NULL; 1007 if (res != 0) 1008 return TEE_ERROR_GENERIC; 1009 1010 return TEE_SUCCESS; 1011 } 1012 1013 static TEE_Result tee_svc_storage_set_enum(char *d_name, 1014 const struct tee_file_operations *fops, 1015 struct tee_obj *o) 1016 { 1017 TEE_Result res; 1018 uint32_t blen; 1019 uint32_t hslen; 1020 1021 o->info.handleFlags = 1022 TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED; 1023 o->info.objectUsage = TEE_USAGE_DEFAULT; 1024 1025 hslen = strlen(d_name); 1026 blen = TEE_HS2B_BBUF_SIZE(hslen); 1027 o->pobj->obj_id = malloc(blen); 1028 if (!o->pobj->obj_id) { 1029 res = TEE_ERROR_OUT_OF_MEMORY; 1030 goto exit; 1031 } 1032 tee_hs2b((uint8_t *)d_name, o->pobj->obj_id, hslen, blen); 1033 o->pobj->obj_id_len = blen; 1034 o->pobj->fops = fops; 1035 1036 res = TEE_SUCCESS; 1037 1038 exit: 1039 return res; 1040 1041 } 1042 1043 TEE_Result syscall_storage_start_enum(unsigned long obj_enum, 1044 unsigned long storage_id) 1045 { 1046 struct tee_storage_enum *e; 1047 char *dir; 1048 TEE_Result res; 1049 struct tee_ta_session *sess; 1050 struct tee_fs_dirent *d = NULL; 1051 struct tee_obj *o = NULL; 1052 const struct tee_file_operations *fops = file_ops(storage_id); 1053 1054 res = tee_ta_get_current_session(&sess); 1055 if (res != TEE_SUCCESS) 1056 return res; 1057 1058 res = tee_svc_storage_get_enum(to_user_ta_ctx(sess->ctx), 1059 tee_svc_uref_to_vaddr(obj_enum), &e); 1060 if (res != TEE_SUCCESS) 1061 return res; 1062 1063 if (!fops) 1064 return TEE_ERROR_ITEM_NOT_FOUND; 1065 1066 dir = tee_svc_storage_create_dirname(sess); 1067 if (dir == NULL) 1068 return TEE_ERROR_OUT_OF_MEMORY; 1069 1070 e->fops = fops; 1071 e->dir = fops->opendir(dir); 1072 free(dir); 1073 if (e->dir == NULL) 1074 /* error codes needs better granularity */ 1075 return TEE_ERROR_ITEM_NOT_FOUND; 1076 1077 /* verify object */ 1078 o = tee_obj_alloc(); 1079 if (o == NULL) { 1080 res = TEE_ERROR_OUT_OF_MEMORY; 1081 goto exit; 1082 } 1083 1084 o->pobj = calloc(1, sizeof(struct tee_pobj)); 1085 if (!o->pobj) { 1086 res = TEE_ERROR_OUT_OF_MEMORY; 1087 goto exit; 1088 } 1089 1090 /* object enumeration loop */ 1091 do { 1092 d = fops->readdir(e->dir); 1093 if (d) { 1094 /* allocate obj_id and set object */ 1095 res = tee_svc_storage_set_enum(d->d_name, fops, o); 1096 if (res != TEE_SUCCESS) 1097 goto exit; 1098 res = tee_obj_verify(sess, o); 1099 if (res != TEE_SUCCESS) 1100 goto exit; 1101 /* free obj_id for each iteration */ 1102 free(o->pobj->obj_id); 1103 /* force obj_id to skip freeing at exit statement */ 1104 o->pobj->obj_id = NULL; 1105 } 1106 } while (d); 1107 1108 /* re-start */ 1109 res = fops->closedir(e->dir); 1110 e->dir = NULL; 1111 if (res != 0) { 1112 res = TEE_ERROR_GENERIC; 1113 goto exit; 1114 } 1115 1116 dir = tee_svc_storage_create_dirname(sess); 1117 if (dir == NULL) { 1118 res = TEE_ERROR_OUT_OF_MEMORY; 1119 goto exit; 1120 } 1121 1122 e->dir = fops->opendir(dir); 1123 free(dir); 1124 1125 exit: 1126 if (o) { 1127 if (o->pobj) 1128 free(o->pobj->obj_id); 1129 free(o->pobj); 1130 tee_obj_free(o); 1131 } 1132 1133 return res; 1134 } 1135 1136 TEE_Result syscall_storage_next_enum(unsigned long obj_enum, 1137 TEE_ObjectInfo *info, void *obj_id, uint64_t *len) 1138 { 1139 struct tee_storage_enum *e; 1140 struct tee_fs_dirent *d; 1141 TEE_Result res = TEE_SUCCESS; 1142 struct tee_ta_session *sess; 1143 struct tee_obj *o = NULL; 1144 uint64_t l; 1145 struct user_ta_ctx *utc; 1146 1147 res = tee_ta_get_current_session(&sess); 1148 if (res != TEE_SUCCESS) 1149 goto exit; 1150 utc = to_user_ta_ctx(sess->ctx); 1151 1152 res = tee_svc_storage_get_enum(utc, 1153 tee_svc_uref_to_vaddr(obj_enum), &e); 1154 if (res != TEE_SUCCESS) 1155 goto exit; 1156 1157 /* check rights of the provided buffers */ 1158 res = tee_mmu_check_access_rights(utc, 1159 TEE_MEMORY_ACCESS_WRITE | 1160 TEE_MEMORY_ACCESS_ANY_OWNER, 1161 (tee_uaddr_t) info, 1162 sizeof(TEE_ObjectInfo)); 1163 if (res != TEE_SUCCESS) 1164 goto exit; 1165 1166 res = tee_mmu_check_access_rights(utc, 1167 TEE_MEMORY_ACCESS_WRITE | 1168 TEE_MEMORY_ACCESS_ANY_OWNER, 1169 (tee_uaddr_t) obj_id, 1170 TEE_OBJECT_ID_MAX_LEN); 1171 if (res != TEE_SUCCESS) 1172 goto exit; 1173 1174 if (!e->fops) { 1175 res = TEE_ERROR_ITEM_NOT_FOUND; 1176 goto exit; 1177 } 1178 1179 d = e->fops->readdir(e->dir); 1180 if (d == NULL) { 1181 res = TEE_ERROR_ITEM_NOT_FOUND; 1182 goto exit; 1183 } 1184 1185 o = tee_obj_alloc(); 1186 if (o == NULL) { 1187 res = TEE_ERROR_OUT_OF_MEMORY; 1188 goto exit; 1189 } 1190 1191 o->pobj = calloc(1, sizeof(struct tee_pobj)); 1192 if (!o->pobj) { 1193 res = TEE_ERROR_OUT_OF_MEMORY; 1194 goto exit; 1195 } 1196 1197 res = tee_svc_storage_set_enum(d->d_name, e->fops, o); 1198 if (res != TEE_SUCCESS) 1199 goto exit; 1200 1201 res = tee_obj_verify(sess, o); 1202 if (res != TEE_SUCCESS) 1203 goto exit; 1204 1205 res = tee_svc_storage_read_head(sess, o); 1206 if (res != TEE_SUCCESS) 1207 goto exit; 1208 1209 memcpy(info, &o->info, sizeof(TEE_ObjectInfo)); 1210 memcpy(obj_id, o->pobj->obj_id, o->pobj->obj_id_len); 1211 1212 l = o->pobj->obj_id_len; 1213 res = tee_svc_copy_to_user(len, &l, sizeof(*len)); 1214 1215 exit: 1216 if (o) { 1217 if (o->pobj) 1218 free(o->pobj->obj_id); 1219 free(o->pobj); 1220 tee_obj_free(o); 1221 } 1222 1223 return res; 1224 } 1225 1226 TEE_Result syscall_storage_obj_read(unsigned long obj, void *data, size_t len, 1227 uint64_t *count) 1228 { 1229 TEE_Result res; 1230 struct tee_ta_session *sess; 1231 struct tee_obj *o; 1232 int n_count; 1233 uint64_t u_count; 1234 struct user_ta_ctx *utc; 1235 1236 res = tee_ta_get_current_session(&sess); 1237 if (res != TEE_SUCCESS) 1238 goto exit; 1239 utc = to_user_ta_ctx(sess->ctx); 1240 1241 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o); 1242 if (res != TEE_SUCCESS) 1243 goto exit; 1244 1245 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 1246 res = TEE_ERROR_BAD_STATE; 1247 goto exit; 1248 } 1249 1250 if (!(o->flags & TEE_DATA_FLAG_ACCESS_READ)) { 1251 res = TEE_ERROR_ACCESS_CONFLICT; 1252 goto exit; 1253 } 1254 1255 /* check rights of the provided buffer */ 1256 res = tee_mmu_check_access_rights(utc, 1257 TEE_MEMORY_ACCESS_WRITE | 1258 TEE_MEMORY_ACCESS_ANY_OWNER, 1259 (tee_uaddr_t) data, len); 1260 if (res != TEE_SUCCESS) 1261 goto exit; 1262 1263 n_count = o->pobj->fops->read(&res, o->fd, data, len); 1264 if (n_count < 0) { 1265 EMSG("Error code=%x\n", (uint32_t)res); 1266 if (res == TEE_ERROR_CORRUPT_OBJECT) { 1267 EMSG("Object corrupt\n"); 1268 tee_svc_storage_remove_corrupt_obj(sess, o); 1269 } 1270 goto exit; 1271 } 1272 u_count = (uint64_t)((n_count < 0) ? 0 : n_count); 1273 1274 res = tee_svc_copy_to_user(count, &u_count, sizeof(*count)); 1275 1276 o->info.dataPosition += u_count; 1277 1278 res = TEE_SUCCESS; 1279 1280 exit: 1281 return res; 1282 } 1283 1284 TEE_Result syscall_storage_obj_write(unsigned long obj, void *data, size_t len) 1285 { 1286 TEE_Result res; 1287 struct tee_ta_session *sess; 1288 struct tee_obj *o; 1289 int err; 1290 struct user_ta_ctx *utc; 1291 1292 res = tee_ta_get_current_session(&sess); 1293 if (res != TEE_SUCCESS) 1294 goto exit; 1295 utc = to_user_ta_ctx(sess->ctx); 1296 1297 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o); 1298 if (res != TEE_SUCCESS) 1299 goto exit; 1300 1301 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 1302 res = TEE_ERROR_BAD_STATE; 1303 goto exit; 1304 } 1305 1306 if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE)) { 1307 res = TEE_ERROR_ACCESS_CONFLICT; 1308 goto exit; 1309 } 1310 1311 /* check rights of the provided buffer */ 1312 res = tee_mmu_check_access_rights(utc, 1313 TEE_MEMORY_ACCESS_READ | 1314 TEE_MEMORY_ACCESS_ANY_OWNER, 1315 (tee_uaddr_t) data, len); 1316 1317 err = o->pobj->fops->write(&res, o->fd, data, len); 1318 1319 if (err != (int)len) 1320 goto exit; 1321 1322 o->info.dataPosition += len; 1323 if (o->info.dataPosition > o->info.dataSize) 1324 o->info.dataSize = o->info.dataPosition; 1325 1326 res = TEE_SUCCESS; 1327 exit: 1328 return res; 1329 } 1330 1331 TEE_Result syscall_storage_obj_trunc(unsigned long obj, size_t len) 1332 { 1333 TEE_Result res; 1334 struct tee_ta_session *sess; 1335 struct tee_obj *o; 1336 int err; 1337 tee_fs_off_t off; 1338 size_t attr_size; 1339 1340 res = tee_ta_get_current_session(&sess); 1341 if (res != TEE_SUCCESS) 1342 goto exit; 1343 1344 res = tee_obj_get(to_user_ta_ctx(sess->ctx), 1345 tee_svc_uref_to_vaddr(obj), &o); 1346 if (res != TEE_SUCCESS) 1347 goto exit; 1348 1349 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 1350 res = TEE_ERROR_BAD_STATE; 1351 goto exit; 1352 } 1353 1354 if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE)) { 1355 res = TEE_ERROR_ACCESS_CONFLICT; 1356 goto exit; 1357 } 1358 1359 res = tee_obj_verify(sess, o); 1360 if (res != TEE_SUCCESS) 1361 goto exit; 1362 1363 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 1364 if (res != TEE_SUCCESS) 1365 goto exit; 1366 1367 off = sizeof(struct tee_svc_storage_head) + attr_size; 1368 err = o->pobj->fops->ftruncate(&res, o->fd, len + off); 1369 if (err) { 1370 if (res == TEE_ERROR_CORRUPT_OBJECT) { 1371 EMSG("Object corrupt\n"); 1372 res = tee_svc_storage_remove_corrupt_obj(sess, o); 1373 if (res != TEE_SUCCESS) 1374 goto exit; 1375 res = TEE_ERROR_CORRUPT_OBJECT; 1376 goto exit; 1377 } else 1378 res = TEE_ERROR_GENERIC; 1379 } 1380 1381 exit: 1382 return res; 1383 } 1384 1385 TEE_Result syscall_storage_obj_seek(unsigned long obj, long offset, 1386 unsigned long whence) 1387 { 1388 TEE_Result res; 1389 struct tee_ta_session *sess; 1390 struct tee_obj *o; 1391 int fw; 1392 tee_fs_off_t off; 1393 tee_fs_off_t e_off = 0; 1394 size_t attr_size; 1395 1396 res = tee_ta_get_current_session(&sess); 1397 if (res != TEE_SUCCESS) 1398 goto exit; 1399 1400 res = tee_obj_get(to_user_ta_ctx(sess->ctx), 1401 tee_svc_uref_to_vaddr(obj), &o); 1402 if (res != TEE_SUCCESS) 1403 goto exit; 1404 1405 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 1406 res = TEE_ERROR_BAD_STATE; 1407 goto exit; 1408 } 1409 1410 res = tee_obj_verify(sess, o); 1411 if (res != TEE_SUCCESS) 1412 goto exit; 1413 1414 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 1415 if (res != TEE_SUCCESS) 1416 goto exit; 1417 1418 fw = tee_svc_storage_conv_whence(whence); 1419 1420 if (whence == TEE_DATA_SEEK_SET) 1421 e_off = sizeof(struct tee_svc_storage_head) + attr_size; 1422 1423 off = o->pobj->fops->lseek(&res, o->fd, e_off + offset, fw); 1424 if (off > -1 && off >= e_off) 1425 o->info.dataPosition = off - 1426 (sizeof(struct tee_svc_storage_head) + attr_size); 1427 else { 1428 res = TEE_ERROR_GENERIC; 1429 goto exit; 1430 } 1431 1432 res = TEE_SUCCESS; 1433 1434 exit: 1435 return res; 1436 } 1437 1438 void tee_svc_storage_close_all_enum(struct user_ta_ctx *utc) 1439 { 1440 struct tee_storage_enum_head *eh = &utc->storage_enums; 1441 1442 /* disregard return value */ 1443 while (!TAILQ_EMPTY(eh)) 1444 tee_svc_close_enum(utc, TAILQ_FIRST(eh)); 1445 } 1446