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
pos_to_block_num(int position)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
get_tmp_block(void)54 static void *get_tmp_block(void)
55 {
56 return mempool_alloc(mempool_default, BLOCK_SIZE);
57 }
58
put_tmp_block(void * tmp_block)59 static void put_tmp_block(void *tmp_block)
60 {
61 mempool_free(mempool_default, tmp_block);
62 }
63
out_of_place_write(struct tee_fs_fd * fdp,size_t pos,const void * buf_core,const void * buf_user,size_t len)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
get_offs_size(enum tee_fs_htree_type type,size_t idx,uint8_t vers,size_t * offs,size_t * size)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
ree_fs_rpc_read_init(void * aux,struct tee_fs_rpc_operation * op,enum tee_fs_htree_type type,size_t idx,uint8_t vers,void ** data)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
ree_fs_rpc_write_init(void * aux,struct tee_fs_rpc_operation * op,enum tee_fs_htree_type type,size_t idx,uint8_t vers,void ** data)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
ree_fs_ftruncate_internal(struct tee_fs_fd * fdp,tee_fs_off_t new_file_len)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_DIV(new_file_len, BLOCK_SIZE), 1,
288 &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
ree_fs_read_primitive(struct tee_file_handle * fh,size_t pos,void * buf_core,void * buf_user,size_t * len)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
ree_fs_read(struct tee_file_handle * fh,size_t pos,void * buf_core,void * buf_user,size_t * len)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
ree_fs_write_primitive(struct tee_file_handle * fh,size_t pos,const void * buf_core,const void * buf_user,size_t len)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
ree_fs_open_primitive(bool create,uint8_t * hash,uint32_t min_counter,const TEE_UUID * uuid,struct tee_fs_dirfile_fileh * dfh,struct tee_file_handle ** fh)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
ree_fs_close_primitive(struct tee_file_handle * fh)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
ree_dirf_commit_writes(struct tee_file_handle * fh,uint8_t * hash,uint32_t * counter)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
dirf_read(struct tee_file_handle * fh,size_t pos,void * buf,size_t * len)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
dirf_write(struct tee_file_handle * fh,size_t pos,const void * buf,size_t len)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 /*
514 * ree_fs_dirh is caching the dirfile handle to avoid frequent opening and
515 * closing of that handle. When ree_fs_dirh_refcount reaches 0, ree_fs_dirh
516 * will be freed. However, ree_fs_dirh_refcount > 0 is not a guarantee that
517 * ree_fs_dirh will not be freed, it may very well be freed earlier in an
518 * error path. get_dirh() must be used to get the ree_fs_dirh pointer each
519 * time it's needed if ree_fs_mutex has been unlocked in between.
520 */
521 static struct tee_fs_dirfile_dirh *ree_fs_dirh;
522 static size_t ree_fs_dirh_refcount;
523
524 #ifdef CFG_REE_FS_INTEGRITY_RPMB
525 static struct tee_file_handle *ree_fs_rpmb_fh;
526
open_dirh(struct tee_fs_dirfile_dirh ** dirh)527 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
528 {
529 TEE_Result res;
530 uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
531 uint8_t *hashp = NULL;
532 const char fname[] = "dirfile.db.hash";
533
534 res = tee_rpmb_fs_raw_open(fname, false, &ree_fs_rpmb_fh);
535 if (!res) {
536 size_t l = sizeof(hash);
537
538 res = rpmb_fs_ops.read(ree_fs_rpmb_fh, 0, hash, NULL, &l);
539 if (res)
540 return res;
541 if (l == sizeof(hash))
542 hashp = hash;
543 } else if (res == TEE_ERROR_ITEM_NOT_FOUND) {
544 res = tee_rpmb_fs_raw_open(fname, true, &ree_fs_rpmb_fh);
545 }
546 if (res)
547 return res;
548
549 res = tee_fs_dirfile_open(false, hashp, 0, &ree_dirf_ops, dirh);
550
551 if (res == TEE_ERROR_ITEM_NOT_FOUND) {
552 if (hashp) {
553 if (IS_ENABLED(CFG_REE_FS_ALLOW_RESET)) {
554 DMSG("dirf.db not found, clear hash in RPMB");
555 res = rpmb_fs_ops.truncate(ree_fs_rpmb_fh, 0);
556 if (res) {
557 DMSG("Can't clear hash: %#"PRIx32, res);
558 res = TEE_ERROR_SECURITY;
559 goto out;
560 }
561 } else {
562 DMSG("dirf.db file not found");
563 res = TEE_ERROR_SECURITY;
564 goto out;
565 }
566 }
567
568 res = tee_fs_dirfile_open(true, NULL, 0, &ree_dirf_ops, dirh);
569 }
570
571 out:
572 if (res)
573 rpmb_fs_ops.close(&ree_fs_rpmb_fh);
574
575 return res;
576 }
577
commit_dirh_writes(struct tee_fs_dirfile_dirh * dirh)578 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
579 {
580 TEE_Result res;
581 uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
582
583 res = tee_fs_dirfile_commit_writes(dirh, hash, NULL);
584 if (res)
585 return res;
586 return rpmb_fs_ops.write(ree_fs_rpmb_fh, 0, hash, NULL, sizeof(hash));
587 }
588
close_dirh(struct tee_fs_dirfile_dirh ** dirh)589 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
590 {
591 tee_fs_dirfile_close(*dirh);
592 *dirh = NULL;
593 rpmb_fs_ops.close(&ree_fs_rpmb_fh);
594 }
595
596 #else /*!CFG_REE_FS_INTEGRITY_RPMB*/
open_dirh(struct tee_fs_dirfile_dirh ** dirh)597 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
598 {
599 TEE_Result res = TEE_SUCCESS;
600 uint32_t min_counter = 0;
601
602 res = nv_counter_get_ree_fs(&min_counter);
603 if (res) {
604 static bool once;
605
606 if (res != TEE_ERROR_NOT_IMPLEMENTED ||
607 !IS_ENABLED(CFG_INSECURE))
608 return res;
609
610 if (!once) {
611 IMSG("WARNING (insecure configuration): Failed to get monotonic counter for REE FS, using 0");
612 once = true;
613 }
614 min_counter = 0;
615 }
616 res = tee_fs_dirfile_open(false, NULL, min_counter, &ree_dirf_ops,
617 dirh);
618 if (res == TEE_ERROR_ITEM_NOT_FOUND) {
619 if (min_counter) {
620 if (!IS_ENABLED(CFG_REE_FS_ALLOW_RESET)) {
621 DMSG("dirf.db file not found");
622 return TEE_ERROR_SECURITY;
623 }
624 DMSG("dirf.db not found, initializing with a non-zero monotonic counter");
625 }
626 return tee_fs_dirfile_open(true, NULL, min_counter,
627 &ree_dirf_ops, dirh);
628 }
629
630 return res;
631 }
632
commit_dirh_writes(struct tee_fs_dirfile_dirh * dirh)633 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
634 {
635 TEE_Result res = TEE_SUCCESS;
636 uint32_t counter = 0;
637
638 res = tee_fs_dirfile_commit_writes(dirh, NULL, &counter);
639 if (res)
640 return res;
641 res = nv_counter_incr_ree_fs_to(counter);
642 if (res == TEE_ERROR_NOT_IMPLEMENTED && IS_ENABLED(CFG_INSECURE)) {
643 static bool once;
644
645 if (!once) {
646 IMSG("WARNING (insecure configuration): Failed to commit dirh counter %"PRIu32, counter);
647 once = true;
648 }
649 return TEE_SUCCESS;
650 }
651 return res;
652 }
653
close_dirh(struct tee_fs_dirfile_dirh ** dirh)654 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
655 {
656 tee_fs_dirfile_close(*dirh);
657 *dirh = NULL;
658 }
659 #endif /*!CFG_REE_FS_INTEGRITY_RPMB*/
660
get_dirh(struct tee_fs_dirfile_dirh ** dirh)661 static TEE_Result get_dirh(struct tee_fs_dirfile_dirh **dirh)
662 {
663 if (!ree_fs_dirh) {
664 TEE_Result res = open_dirh(&ree_fs_dirh);
665
666 if (res) {
667 *dirh = NULL;
668 return res;
669 }
670 }
671 ree_fs_dirh_refcount++;
672 assert(ree_fs_dirh);
673 assert(ree_fs_dirh_refcount);
674 *dirh = ree_fs_dirh;
675 return TEE_SUCCESS;
676 }
677
put_dirh_primitive(bool close)678 static void put_dirh_primitive(bool close)
679 {
680 assert(ree_fs_dirh_refcount);
681
682 /*
683 * During the execution of one of the ree_fs_ops ree_fs_dirh is
684 * guareteed to be a valid pointer. But when the fop has returned
685 * another thread may get an error or something causing that fop
686 * to do a put with close=1.
687 *
688 * For all fops but ree_fs_close() there's a call to get_dirh() to
689 * get a new dirh which will open it again if it was closed before.
690 * But in the ree_fs_close() case there's no call to get_dirh()
691 * only to this function, put_dirh_primitive(), and in this case
692 * ree_fs_dirh may actually be NULL.
693 */
694 ree_fs_dirh_refcount--;
695 if (ree_fs_dirh && (!ree_fs_dirh_refcount || close))
696 close_dirh(&ree_fs_dirh);
697 }
698
put_dirh(struct tee_fs_dirfile_dirh * dirh,bool close)699 static void put_dirh(struct tee_fs_dirfile_dirh *dirh, bool close)
700 {
701 if (dirh) {
702 assert(dirh == ree_fs_dirh);
703 put_dirh_primitive(close);
704 }
705 }
706
ree_fs_open(struct tee_pobj * po,size_t * size,struct tee_file_handle ** fh)707 static TEE_Result ree_fs_open(struct tee_pobj *po, size_t *size,
708 struct tee_file_handle **fh)
709 {
710 TEE_Result res;
711 struct tee_fs_dirfile_dirh *dirh = NULL;
712 struct tee_fs_dirfile_fileh dfh;
713
714 mutex_lock(&ree_fs_mutex);
715
716 res = get_dirh(&dirh);
717 if (res != TEE_SUCCESS)
718 goto out;
719
720 res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
721 &dfh);
722 if (res != TEE_SUCCESS)
723 goto out;
724
725 res = ree_fs_open_primitive(false, dfh.hash, 0, &po->uuid, &dfh, fh);
726 if (res == TEE_ERROR_ITEM_NOT_FOUND) {
727 /*
728 * If the object isn't found someone has tampered with it,
729 * treat it as corrupt.
730 */
731 res = TEE_ERROR_CORRUPT_OBJECT;
732 } else if (!res && size) {
733 struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh;
734
735 *size = tee_fs_htree_get_meta(fdp->ht)->length;
736 }
737
738 out:
739 if (res)
740 put_dirh(dirh, true);
741 mutex_unlock(&ree_fs_mutex);
742
743 return res;
744 }
745
set_name(struct tee_fs_dirfile_dirh * dirh,struct tee_fs_fd * fdp,struct tee_pobj * po,bool overwrite)746 static TEE_Result set_name(struct tee_fs_dirfile_dirh *dirh,
747 struct tee_fs_fd *fdp, struct tee_pobj *po,
748 bool overwrite)
749 {
750 TEE_Result res;
751 bool have_old_dfh = false;
752 struct tee_fs_dirfile_fileh old_dfh = { .idx = -1 };
753
754 res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
755 &old_dfh);
756 if (!overwrite && !res)
757 return TEE_ERROR_ACCESS_CONFLICT;
758
759 if (!res)
760 have_old_dfh = true;
761
762 /*
763 * If old_dfh wasn't found, the idx will be -1 and
764 * tee_fs_dirfile_rename() will allocate a new index.
765 */
766 fdp->dfh.idx = old_dfh.idx;
767 old_dfh.idx = -1;
768 res = tee_fs_dirfile_rename(dirh, &po->uuid, &fdp->dfh,
769 po->obj_id, po->obj_id_len);
770 if (res)
771 return res;
772
773 res = commit_dirh_writes(dirh);
774 if (res)
775 return res;
776
777 if (have_old_dfh)
778 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &old_dfh);
779
780 return TEE_SUCCESS;
781 }
782
ree_fs_close(struct tee_file_handle ** fh)783 static void ree_fs_close(struct tee_file_handle **fh)
784 {
785 if (*fh) {
786 mutex_lock(&ree_fs_mutex);
787 put_dirh_primitive(false);
788 ree_fs_close_primitive(*fh);
789 *fh = NULL;
790 mutex_unlock(&ree_fs_mutex);
791
792 }
793 }
794
ree_fs_create(struct tee_pobj * po,bool overwrite,const void * head,size_t head_size,const void * attr,size_t attr_size,const void * data_core,const void * data_user,size_t data_size,struct tee_file_handle ** fh)795 static TEE_Result ree_fs_create(struct tee_pobj *po, bool overwrite,
796 const void *head, size_t head_size,
797 const void *attr, size_t attr_size,
798 const void *data_core, const void *data_user,
799 size_t data_size, struct tee_file_handle **fh)
800 {
801 struct tee_fs_fd *fdp;
802 struct tee_fs_dirfile_dirh *dirh = NULL;
803 struct tee_fs_dirfile_fileh dfh;
804 TEE_Result res;
805 size_t pos = 0;
806
807 /* One of data_core and data_user must be NULL */
808 assert(!data_core || !data_user);
809
810 *fh = NULL;
811 mutex_lock(&ree_fs_mutex);
812
813 res = get_dirh(&dirh);
814 if (res)
815 goto out;
816
817 res = tee_fs_dirfile_get_tmp(dirh, &dfh);
818 if (res)
819 goto out;
820
821 res = ree_fs_open_primitive(true, dfh.hash, 0, &po->uuid, &dfh, fh);
822 if (res)
823 goto out;
824
825 if (head && head_size) {
826 res = ree_fs_write_primitive(*fh, pos, head, NULL, head_size);
827 if (res)
828 goto out;
829 pos += head_size;
830 }
831
832 if (attr && attr_size) {
833 res = ree_fs_write_primitive(*fh, pos, attr, NULL, attr_size);
834 if (res)
835 goto out;
836 pos += attr_size;
837 }
838
839 if ((data_core || data_user) && data_size) {
840 res = ree_fs_write_primitive(*fh, pos, data_core, data_user,
841 data_size);
842 if (res)
843 goto out;
844 }
845
846 fdp = (struct tee_fs_fd *)*fh;
847 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL);
848 if (res)
849 goto out;
850
851 res = set_name(dirh, fdp, po, overwrite);
852 out:
853 if (res) {
854 put_dirh(dirh, true);
855 if (*fh) {
856 ree_fs_close_primitive(*fh);
857 *fh = NULL;
858 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
859 }
860 }
861 mutex_unlock(&ree_fs_mutex);
862
863 return res;
864 }
865
ree_fs_write(struct tee_file_handle * fh,size_t pos,const void * buf_core,const void * buf_user,size_t len)866 static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos,
867 const void *buf_core, const void *buf_user,
868 size_t len)
869 {
870 TEE_Result res;
871 struct tee_fs_dirfile_dirh *dirh = NULL;
872 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
873
874 /* One of buf_core and buf_user must be NULL */
875 assert(!buf_core || !buf_user);
876
877 mutex_lock(&ree_fs_mutex);
878
879 res = get_dirh(&dirh);
880 if (res)
881 goto out;
882
883 res = ree_fs_write_primitive(fh, pos, buf_core, buf_user, len);
884 if (res)
885 goto out;
886
887 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL);
888 if (res)
889 goto out;
890
891 res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
892 if (res)
893 goto out;
894 res = commit_dirh_writes(dirh);
895 out:
896 put_dirh(dirh, res);
897 mutex_unlock(&ree_fs_mutex);
898
899 return res;
900 }
901
ree_fs_rename(struct tee_pobj * old,struct tee_pobj * new,bool overwrite)902 static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new,
903 bool overwrite)
904 {
905 TEE_Result res;
906 struct tee_fs_dirfile_dirh *dirh = NULL;
907 struct tee_fs_dirfile_fileh dfh;
908 struct tee_fs_dirfile_fileh remove_dfh = { .idx = -1 };
909
910 if (!new)
911 return TEE_ERROR_BAD_PARAMETERS;
912
913 mutex_lock(&ree_fs_mutex);
914 res = get_dirh(&dirh);
915 if (res)
916 goto out;
917
918 res = tee_fs_dirfile_find(dirh, &new->uuid, new->obj_id,
919 new->obj_id_len, &remove_dfh);
920 if (!res && !overwrite) {
921 res = TEE_ERROR_ACCESS_CONFLICT;
922 goto out;
923 }
924
925 res = tee_fs_dirfile_find(dirh, &old->uuid, old->obj_id,
926 old->obj_id_len, &dfh);
927 if (res)
928 goto out;
929
930 res = tee_fs_dirfile_rename(dirh, &new->uuid, &dfh, new->obj_id,
931 new->obj_id_len);
932 if (res)
933 goto out;
934
935 if (remove_dfh.idx != -1) {
936 res = tee_fs_dirfile_remove(dirh, &remove_dfh);
937 if (res)
938 goto out;
939 }
940
941 res = commit_dirh_writes(dirh);
942 if (res)
943 goto out;
944
945 if (remove_dfh.idx != -1)
946 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &remove_dfh);
947
948 out:
949 put_dirh(dirh, res);
950 mutex_unlock(&ree_fs_mutex);
951
952 return res;
953
954 }
955
ree_fs_remove(struct tee_pobj * po)956 static TEE_Result ree_fs_remove(struct tee_pobj *po)
957 {
958 TEE_Result res;
959 struct tee_fs_dirfile_dirh *dirh = NULL;
960 struct tee_fs_dirfile_fileh dfh;
961
962 mutex_lock(&ree_fs_mutex);
963 res = get_dirh(&dirh);
964 if (res)
965 goto out;
966
967 res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
968 &dfh);
969 if (res)
970 goto out;
971
972 res = tee_fs_dirfile_remove(dirh, &dfh);
973 if (res)
974 goto out;
975
976 res = commit_dirh_writes(dirh);
977 if (res)
978 goto out;
979
980 tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
981
982 assert(tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
983 &dfh));
984 out:
985 put_dirh(dirh, res);
986 mutex_unlock(&ree_fs_mutex);
987
988 return res;
989 }
990
ree_fs_truncate(struct tee_file_handle * fh,size_t len)991 static TEE_Result ree_fs_truncate(struct tee_file_handle *fh, size_t len)
992 {
993 TEE_Result res;
994 struct tee_fs_dirfile_dirh *dirh = NULL;
995 struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
996
997 mutex_lock(&ree_fs_mutex);
998
999 res = get_dirh(&dirh);
1000 if (res)
1001 goto out;
1002
1003 res = ree_fs_ftruncate_internal(fdp, len);
1004 if (res)
1005 goto out;
1006
1007 res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL);
1008 if (res)
1009 goto out;
1010
1011 res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
1012 if (res)
1013 goto out;
1014 res = commit_dirh_writes(dirh);
1015 out:
1016 put_dirh(dirh, res);
1017 mutex_unlock(&ree_fs_mutex);
1018
1019 return res;
1020 }
1021
ree_fs_opendir_rpc(const TEE_UUID * uuid,struct tee_fs_dir ** dir)1022 static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid,
1023 struct tee_fs_dir **dir)
1024
1025 {
1026 TEE_Result res = TEE_SUCCESS;
1027 struct tee_fs_dirfile_dirh *dirh = NULL;
1028 struct tee_fs_dir *d = calloc(1, sizeof(*d));
1029
1030 if (!d)
1031 return TEE_ERROR_OUT_OF_MEMORY;
1032
1033 d->uuid = uuid;
1034
1035 mutex_lock(&ree_fs_mutex);
1036
1037 res = get_dirh(&dirh);
1038 if (res)
1039 goto out;
1040
1041 /* See that there's at least one file */
1042 d->idx = -1;
1043 d->d.oidlen = sizeof(d->d.oid);
1044 res = tee_fs_dirfile_get_next(dirh, d->uuid, &d->idx, d->d.oid,
1045 &d->d.oidlen);
1046 d->idx = -1;
1047
1048 out:
1049 if (!res) {
1050 *dir = d;
1051 } else {
1052 if (d)
1053 put_dirh(dirh, false);
1054 free(d);
1055 }
1056 mutex_unlock(&ree_fs_mutex);
1057
1058 return res;
1059 }
1060
ree_fs_closedir_rpc(struct tee_fs_dir * d)1061 static void ree_fs_closedir_rpc(struct tee_fs_dir *d)
1062 {
1063 if (d) {
1064 mutex_lock(&ree_fs_mutex);
1065
1066 put_dirh(ree_fs_dirh, false);
1067 free(d);
1068
1069 mutex_unlock(&ree_fs_mutex);
1070 }
1071 }
1072
ree_fs_readdir_rpc(struct tee_fs_dir * d,struct tee_fs_dirent ** ent)1073 static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d,
1074 struct tee_fs_dirent **ent)
1075 {
1076 struct tee_fs_dirfile_dirh *dirh = NULL;
1077 TEE_Result res = TEE_SUCCESS;
1078
1079 mutex_lock(&ree_fs_mutex);
1080
1081 res = get_dirh(&dirh);
1082 if (res)
1083 goto out;
1084
1085 d->d.oidlen = sizeof(d->d.oid);
1086 res = tee_fs_dirfile_get_next(dirh, d->uuid, &d->idx, d->d.oid,
1087 &d->d.oidlen);
1088 if (res == TEE_SUCCESS)
1089 *ent = &d->d;
1090
1091 put_dirh(dirh, res);
1092 out:
1093 mutex_unlock(&ree_fs_mutex);
1094
1095 return res;
1096 }
1097
1098 const struct tee_file_operations ree_fs_ops = {
1099 .open = ree_fs_open,
1100 .create = ree_fs_create,
1101 .close = ree_fs_close,
1102 .read = ree_fs_read,
1103 .write = ree_fs_write,
1104 .truncate = ree_fs_truncate,
1105 .rename = ree_fs_rename,
1106 .remove = ree_fs_remove,
1107 .opendir = ree_fs_opendir_rpc,
1108 .closedir = ree_fs_closedir_rpc,
1109 .readdir = ree_fs_readdir_rpc,
1110 };
1111