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