xref: /optee_os/core/tee/tee_ree_fs.c (revision 1868eb206733e931b6c6c2d85d55e646bc8a2496)
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 
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 
54 static void *get_tmp_block(void)
55 {
56 	return mempool_alloc(mempool_default, BLOCK_SIZE);
57 }
58 
59 static void put_tmp_block(void *tmp_block)
60 {
61 	mempool_free(mempool_default, tmp_block);
62 }
63 
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 
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 
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 
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 
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(new_file_len, BLOCK_SIZE) /
288 					BLOCK_SIZE, 1, &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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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*/
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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