xref: /optee_os/core/tee/tee_ree_fs.c (revision a1cbb728630308fcf902a8953a32cc972d14757e)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2015, Linaro Limited
4  */
5 
6 #include <assert.h>
7 #include <kernel/mutex.h>
8 #include <kernel/panic.h>
9 #include <kernel/thread.h>
10 #include <mm/core_memprot.h>
11 #include <mm/tee_pager.h>
12 #include <optee_rpc_cmd.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string_ext.h>
16 #include <string.h>
17 #include <sys/queue.h>
18 #include <tee/fs_dirfile.h>
19 #include <tee/fs_htree.h>
20 #include <tee/tee_fs.h>
21 #include <tee/tee_fs_rpc.h>
22 #include <tee/tee_pobj.h>
23 #include <trace.h>
24 #include <utee_defines.h>
25 #include <util.h>
26 
27 #define BLOCK_SHIFT	12
28 
29 #define BLOCK_SIZE	(1 << BLOCK_SHIFT)
30 
31 struct tee_fs_fd {
32 	struct tee_fs_htree *ht;
33 	int fd;
34 	struct tee_fs_dirfile_fileh dfh;
35 	const TEE_UUID *uuid;
36 };
37 
38 struct tee_fs_dir {
39 	struct tee_fs_dirfile_dirh *dirh;
40 	int idx;
41 	struct tee_fs_dirent d;
42 	const TEE_UUID *uuid;
43 };
44 
45 static int pos_to_block_num(int position)
46 {
47 	return position >> BLOCK_SHIFT;
48 }
49 
50 static struct mutex ree_fs_mutex = MUTEX_INITIALIZER;
51 
52 #ifdef CFG_WITH_PAGER
53 static void *ree_fs_tmp_block;
54 static bool ree_fs_tmp_block_busy;
55 
56 static void *get_tmp_block(void)
57 {
58 	assert(!ree_fs_tmp_block_busy);
59 	if (!ree_fs_tmp_block)
60 		ree_fs_tmp_block = tee_pager_alloc(BLOCK_SIZE,
61 						   TEE_MATTR_LOCKED);
62 
63 	if (ree_fs_tmp_block)
64 		ree_fs_tmp_block_busy = true;
65 
66 	return ree_fs_tmp_block;
67 }
68 
69 static void put_tmp_block(void *tmp_block)
70 {
71 	assert(ree_fs_tmp_block_busy);
72 	assert(tmp_block == ree_fs_tmp_block);
73 	tee_pager_release_phys(tmp_block, BLOCK_SIZE);
74 	ree_fs_tmp_block_busy = false;
75 }
76 #else
77 static void *get_tmp_block(void)
78 {
79 	return malloc(BLOCK_SIZE);
80 }
81 
82 static void put_tmp_block(void *tmp_block)
83 {
84 	free(tmp_block);
85 }
86 #endif
87 
88 static TEE_Result out_of_place_write(struct tee_fs_fd *fdp, size_t pos,
89 				     const void *buf, size_t len)
90 {
91 	TEE_Result res;
92 	size_t start_block_num = pos_to_block_num(pos);
93 	size_t end_block_num = pos_to_block_num(pos + len - 1);
94 	size_t remain_bytes = len;
95 	uint8_t *data_ptr = (uint8_t *)buf;
96 	uint8_t *block;
97 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
98 
99 	/*
100 	 * It doesn't make sense to call this function if nothing is to be
101 	 * written. This also guards against end_block_num getting an
102 	 * unexpected value when pos == 0 and len == 0.
103 	 */
104 	if (!len)
105 		return TEE_ERROR_BAD_PARAMETERS;
106 
107 	block = get_tmp_block();
108 	if (!block)
109 		return TEE_ERROR_OUT_OF_MEMORY;
110 
111 	while (start_block_num <= end_block_num) {
112 		size_t offset = pos % BLOCK_SIZE;
113 		size_t size_to_write = MIN(remain_bytes, (size_t)BLOCK_SIZE);
114 
115 		if (size_to_write + offset > BLOCK_SIZE)
116 			size_to_write = BLOCK_SIZE - offset;
117 
118 		if (start_block_num * BLOCK_SIZE <
119 		    ROUNDUP(meta->length, BLOCK_SIZE)) {
120 			res = tee_fs_htree_read_block(&fdp->ht,
121 						      start_block_num, block);
122 			if (res != TEE_SUCCESS)
123 				goto exit;
124 		} else {
125 			memset(block, 0, BLOCK_SIZE);
126 		}
127 
128 		if (data_ptr)
129 			memcpy(block + offset, data_ptr, size_to_write);
130 		else
131 			memset(block + offset, 0, size_to_write);
132 
133 		res = tee_fs_htree_write_block(&fdp->ht, start_block_num,
134 					       block);
135 		if (res != TEE_SUCCESS)
136 			goto exit;
137 
138 		if (data_ptr)
139 			data_ptr += size_to_write;
140 		remain_bytes -= size_to_write;
141 		start_block_num++;
142 		pos += size_to_write;
143 	}
144 
145 	if (pos > meta->length) {
146 		meta->length = pos;
147 		tee_fs_htree_meta_set_dirty(fdp->ht);
148 	}
149 
150 exit:
151 	if (block)
152 		put_tmp_block(block);
153 	return res;
154 }
155 
156 static TEE_Result get_offs_size(enum tee_fs_htree_type type, size_t idx,
157 				uint8_t vers, size_t *offs, size_t *size)
158 {
159 	const size_t node_size = sizeof(struct tee_fs_htree_node_image);
160 	const size_t block_nodes = BLOCK_SIZE / (node_size * 2);
161 	size_t pbn;
162 	size_t bidx;
163 
164 	assert(vers == 0 || vers == 1);
165 
166 	/*
167 	 * File layout
168 	 * [demo with input:
169 	 * BLOCK_SIZE = 4096,
170 	 * node_size = 66,
171 	 * block_nodes = 4096/(66*2) = 31 ]
172 	 *
173 	 * phys block 0:
174 	 * tee_fs_htree_image vers 0 @ offs = 0
175 	 * tee_fs_htree_image vers 1 @ offs = sizeof(tee_fs_htree_image)
176 	 *
177 	 * phys block 1:
178 	 * tee_fs_htree_node_image 0  vers 0 @ offs = 0
179 	 * tee_fs_htree_node_image 0  vers 1 @ offs = node_size
180 	 * tee_fs_htree_node_image 1  vers 0 @ offs = node_size * 2
181 	 * tee_fs_htree_node_image 1  vers 1 @ offs = node_size * 3
182 	 * ...
183 	 * tee_fs_htree_node_image 30 vers 0 @ offs = node_size * 60
184 	 * tee_fs_htree_node_image 30 vers 1 @ offs = node_size * 61
185 	 *
186 	 * phys block 2:
187 	 * data block 0 vers 0
188 	 *
189 	 * phys block 3:
190 	 * data block 0 vers 1
191 	 *
192 	 * ...
193 	 * phys block 62:
194 	 * data block 30 vers 0
195 	 *
196 	 * phys block 63:
197 	 * data block 30 vers 1
198 	 *
199 	 * phys block 64:
200 	 * tee_fs_htree_node_image 31  vers 0 @ offs = 0
201 	 * tee_fs_htree_node_image 31  vers 1 @ offs = node_size
202 	 * tee_fs_htree_node_image 32  vers 0 @ offs = node_size * 2
203 	 * tee_fs_htree_node_image 32  vers 1 @ offs = node_size * 3
204 	 * ...
205 	 * tee_fs_htree_node_image 61 vers 0 @ offs = node_size * 60
206 	 * tee_fs_htree_node_image 61 vers 1 @ offs = node_size * 61
207 	 *
208 	 * phys block 65:
209 	 * data block 31 vers 0
210 	 *
211 	 * phys block 66:
212 	 * data block 31 vers 1
213 	 * ...
214 	 */
215 
216 	switch (type) {
217 	case TEE_FS_HTREE_TYPE_HEAD:
218 		*offs = sizeof(struct tee_fs_htree_image) * vers;
219 		*size = sizeof(struct tee_fs_htree_image);
220 		return TEE_SUCCESS;
221 	case TEE_FS_HTREE_TYPE_NODE:
222 		pbn = 1 + ((idx / block_nodes) * block_nodes * 2);
223 		*offs = pbn * BLOCK_SIZE +
224 			2 * node_size * (idx % block_nodes) +
225 			node_size * vers;
226 		*size = node_size;
227 		return TEE_SUCCESS;
228 	case TEE_FS_HTREE_TYPE_BLOCK:
229 		bidx = 2 * idx + vers;
230 		pbn = 2 + bidx + bidx / (block_nodes * 2 - 1);
231 		*offs = pbn * BLOCK_SIZE;
232 		*size = BLOCK_SIZE;
233 		return TEE_SUCCESS;
234 	default:
235 		return TEE_ERROR_GENERIC;
236 	}
237 }
238 
239 static TEE_Result ree_fs_rpc_read_init(void *aux,
240 				       struct tee_fs_rpc_operation *op,
241 				       enum tee_fs_htree_type type, size_t idx,
242 				       uint8_t vers, void **data)
243 {
244 	struct tee_fs_fd *fdp = aux;
245 	TEE_Result res;
246 	size_t offs;
247 	size_t size;
248 
249 	res = get_offs_size(type, idx, vers, &offs, &size);
250 	if (res != TEE_SUCCESS)
251 		return res;
252 
253 	return tee_fs_rpc_read_init(op, OPTEE_RPC_CMD_FS, fdp->fd,
254 				    offs, size, data);
255 }
256 
257 static TEE_Result ree_fs_rpc_write_init(void *aux,
258 					struct tee_fs_rpc_operation *op,
259 					enum tee_fs_htree_type type, size_t idx,
260 					uint8_t vers, void **data)
261 {
262 	struct tee_fs_fd *fdp = aux;
263 	TEE_Result res;
264 	size_t offs;
265 	size_t size;
266 
267 	res = get_offs_size(type, idx, vers, &offs, &size);
268 	if (res != TEE_SUCCESS)
269 		return res;
270 
271 	return tee_fs_rpc_write_init(op, OPTEE_RPC_CMD_FS, fdp->fd,
272 				     offs, size, data);
273 }
274 
275 static const struct tee_fs_htree_storage ree_fs_storage_ops = {
276 	.block_size = BLOCK_SIZE,
277 	.rpc_read_init = ree_fs_rpc_read_init,
278 	.rpc_read_final = tee_fs_rpc_read_final,
279 	.rpc_write_init = ree_fs_rpc_write_init,
280 	.rpc_write_final = tee_fs_rpc_write_final,
281 };
282 
283 static TEE_Result ree_fs_ftruncate_internal(struct tee_fs_fd *fdp,
284 					    tee_fs_off_t new_file_len)
285 {
286 	TEE_Result res;
287 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
288 
289 	if ((size_t)new_file_len > meta->length) {
290 		size_t ext_len = new_file_len - meta->length;
291 
292 		res = out_of_place_write(fdp, meta->length, NULL, ext_len);
293 		if (res != TEE_SUCCESS)
294 			return res;
295 	} else {
296 		size_t offs;
297 		size_t sz;
298 
299 		res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK,
300 				    ROUNDUP(new_file_len, BLOCK_SIZE) /
301 					BLOCK_SIZE, 1, &offs, &sz);
302 		if (res != TEE_SUCCESS)
303 			return res;
304 
305 		res = tee_fs_htree_truncate(&fdp->ht,
306 					    new_file_len / BLOCK_SIZE);
307 		if (res != TEE_SUCCESS)
308 			return res;
309 
310 		res = tee_fs_rpc_truncate(OPTEE_RPC_CMD_FS, fdp->fd,
311 					  offs + sz);
312 		if (res != TEE_SUCCESS)
313 			return res;
314 
315 		meta->length = new_file_len;
316 		tee_fs_htree_meta_set_dirty(fdp->ht);
317 	}
318 
319 	return TEE_SUCCESS;
320 }
321 
322 static TEE_Result ree_fs_read_primitive(struct tee_file_handle *fh, size_t pos,
323 					void *buf, size_t *len)
324 {
325 	TEE_Result res;
326 	int start_block_num;
327 	int end_block_num;
328 	size_t remain_bytes;
329 	uint8_t *data_ptr = buf;
330 	uint8_t *block = NULL;
331 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
332 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
333 
334 	remain_bytes = *len;
335 	if ((pos + remain_bytes) < remain_bytes || pos > meta->length)
336 		remain_bytes = 0;
337 	else if (pos + remain_bytes > meta->length)
338 		remain_bytes = meta->length - pos;
339 
340 	*len = remain_bytes;
341 
342 	if (!remain_bytes) {
343 		res = TEE_SUCCESS;
344 		goto exit;
345 	}
346 
347 	start_block_num = pos_to_block_num(pos);
348 	end_block_num = pos_to_block_num(pos + remain_bytes - 1);
349 
350 	block = get_tmp_block();
351 	if (!block) {
352 		res = TEE_ERROR_OUT_OF_MEMORY;
353 		goto exit;
354 	}
355 
356 	while (start_block_num <= end_block_num) {
357 		size_t offset = pos % BLOCK_SIZE;
358 		size_t size_to_read = MIN(remain_bytes, (size_t)BLOCK_SIZE);
359 
360 		if (size_to_read + offset > BLOCK_SIZE)
361 			size_to_read = BLOCK_SIZE - offset;
362 
363 		res = tee_fs_htree_read_block(&fdp->ht, start_block_num, block);
364 		if (res != TEE_SUCCESS)
365 			goto exit;
366 
367 		memcpy(data_ptr, block + offset, size_to_read);
368 
369 		data_ptr += size_to_read;
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, size_t *len)
384 {
385 	TEE_Result res;
386 
387 	mutex_lock(&ree_fs_mutex);
388 	res = ree_fs_read_primitive(fh, pos, buf, 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, size_t len)
396 {
397 	TEE_Result res;
398 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
399 	size_t file_size;
400 
401 	if (!len)
402 		return TEE_SUCCESS;
403 
404 	file_size = tee_fs_htree_get_meta(fdp->ht)->length;
405 
406 	if ((pos + len) < len)
407 		return TEE_ERROR_BAD_PARAMETERS;
408 
409 	if (file_size < pos) {
410 		res = ree_fs_ftruncate_internal(fdp, pos);
411 		if (res != TEE_SUCCESS)
412 			return res;
413 	}
414 
415 	return out_of_place_write(fdp, pos, buf, len);
416 }
417 
418 static TEE_Result ree_fs_open_primitive(bool create, uint8_t *hash,
419 					const TEE_UUID *uuid,
420 					struct tee_fs_dirfile_fileh *dfh,
421 					struct tee_file_handle **fh)
422 {
423 	TEE_Result res;
424 	struct tee_fs_fd *fdp;
425 
426 	fdp = calloc(1, sizeof(struct tee_fs_fd));
427 	if (!fdp)
428 		return TEE_ERROR_OUT_OF_MEMORY;
429 	fdp->fd = -1;
430 	fdp->uuid = uuid;
431 
432 	if (create)
433 		res = tee_fs_rpc_create_dfh(OPTEE_RPC_CMD_FS,
434 					    dfh, &fdp->fd);
435 	else
436 		res = tee_fs_rpc_open_dfh(OPTEE_RPC_CMD_FS, dfh, &fdp->fd);
437 
438 	if (res != TEE_SUCCESS)
439 		goto out;
440 
441 	res = tee_fs_htree_open(create, hash, uuid, &ree_fs_storage_ops,
442 				fdp, &fdp->ht);
443 out:
444 	if (res == TEE_SUCCESS) {
445 		if (dfh)
446 			fdp->dfh = *dfh;
447 		else
448 			fdp->dfh.idx = -1;
449 		*fh = (struct tee_file_handle *)fdp;
450 	} else {
451 		if (fdp->fd != -1)
452 			tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd);
453 		if (create)
454 			tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, dfh);
455 		free(fdp);
456 	}
457 
458 	return res;
459 }
460 
461 static void ree_fs_close_primitive(struct tee_file_handle *fh)
462 {
463 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
464 
465 	if (fdp) {
466 		tee_fs_htree_close(&fdp->ht);
467 		tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd);
468 		free(fdp);
469 	}
470 }
471 
472 static TEE_Result ree_dirf_commit_writes(struct tee_file_handle *fh,
473 					 uint8_t *hash)
474 {
475 	TEE_Result res;
476 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
477 
478 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
479 
480 	if (!res && hash)
481 		memcpy(hash, fdp->dfh.hash, sizeof(fdp->dfh.hash));
482 
483 	return res;
484 }
485 
486 static const struct tee_fs_dirfile_operations ree_dirf_ops = {
487 	.open = ree_fs_open_primitive,
488 	.close = ree_fs_close_primitive,
489 	.read = ree_fs_read_primitive,
490 	.write = ree_fs_write_primitive,
491 	.commit_writes = ree_dirf_commit_writes,
492 };
493 
494 static struct tee_fs_dirfile_dirh *ree_fs_dirh;
495 static size_t ree_fs_dirh_refcount;
496 
497 #ifdef CFG_RPMB_FS
498 static struct tee_file_handle *ree_fs_rpmb_fh;
499 
500 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
501 {
502 	TEE_Result res;
503 	uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
504 	uint8_t *hashp = NULL;
505 	const char fname[] = "dirfile.db.hash";
506 
507 	res = tee_rpmb_fs_raw_open(fname, false, &ree_fs_rpmb_fh);
508 	if (!res) {
509 		size_t l = sizeof(hash);
510 
511 		res = rpmb_fs_ops.read(ree_fs_rpmb_fh, 0, hash, &l);
512 		if (res)
513 			return res;
514 		if (l == sizeof(hash))
515 			hashp = hash;
516 	} else if (res == TEE_ERROR_ITEM_NOT_FOUND) {
517 		res = tee_rpmb_fs_raw_open(fname, true, &ree_fs_rpmb_fh);
518 	}
519 	if (res)
520 		return res;
521 
522 	if (!tee_fs_dirfile_open(false, hashp, &ree_dirf_ops, dirh))
523 		return TEE_SUCCESS;
524 
525 	res = tee_fs_dirfile_open(true, NULL, &ree_dirf_ops, dirh);
526 	if (res)
527 		rpmb_fs_ops.close(&ree_fs_rpmb_fh);
528 	return res;
529 }
530 
531 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
532 {
533 	TEE_Result res;
534 	uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
535 
536 	res = tee_fs_dirfile_commit_writes(dirh, hash);
537 	if (res)
538 		return res;
539 	return rpmb_fs_ops.write(ree_fs_rpmb_fh, 0, hash, sizeof(hash));
540 }
541 
542 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
543 {
544 	tee_fs_dirfile_close(*dirh);
545 	*dirh = NULL;
546 	rpmb_fs_ops.close(&ree_fs_rpmb_fh);
547 }
548 
549 #else /*!CFG_RPMB_FS*/
550 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
551 {
552 	if (!tee_fs_dirfile_open(false, NULL, &ree_dirf_ops, dirh))
553 		return TEE_SUCCESS;
554 	return tee_fs_dirfile_open(true, NULL, &ree_dirf_ops, dirh);
555 }
556 
557 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
558 {
559 	return tee_fs_dirfile_commit_writes(dirh, NULL);
560 }
561 
562 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
563 {
564 	tee_fs_dirfile_close(*dirh);
565 	*dirh = NULL;
566 }
567 #endif /*!CFG_RPMB_FS*/
568 
569 static TEE_Result get_dirh(struct tee_fs_dirfile_dirh **dirh)
570 {
571 	if (!ree_fs_dirh) {
572 		TEE_Result res = open_dirh(&ree_fs_dirh);
573 
574 		if (res) {
575 			*dirh = NULL;
576 			return res;
577 		}
578 	}
579 	ree_fs_dirh_refcount++;
580 	assert(ree_fs_dirh);
581 	assert(ree_fs_dirh_refcount);
582 	*dirh = ree_fs_dirh;
583 	return TEE_SUCCESS;
584 }
585 
586 static void put_dirh_primitive(bool close)
587 {
588 	assert(ree_fs_dirh_refcount);
589 
590 	/*
591 	 * During the execution of one of the ree_fs_ops ree_fs_dirh is
592 	 * guareteed to be a valid pointer. But when the fop has returned
593 	 * another thread may get an error or something causing that fop
594 	 * to do a put with close=1.
595 	 *
596 	 * For all fops but ree_fs_close() there's a call to get_dirh() to
597 	 * get a new dirh which will open it again if it was closed before.
598 	 * But in the ree_fs_close() case there's no call to get_dirh()
599 	 * only to this function, put_dirh_primitive(), and in this case
600 	 * ree_fs_dirh may actually be NULL.
601 	 */
602 	ree_fs_dirh_refcount--;
603 	if (ree_fs_dirh && (!ree_fs_dirh_refcount || close))
604 		close_dirh(&ree_fs_dirh);
605 }
606 
607 static void put_dirh(struct tee_fs_dirfile_dirh *dirh, bool close)
608 {
609 	if (dirh) {
610 		assert(dirh == ree_fs_dirh);
611 		put_dirh_primitive(close);
612 	}
613 }
614 
615 static TEE_Result ree_fs_open(struct tee_pobj *po, size_t *size,
616 			      struct tee_file_handle **fh)
617 {
618 	TEE_Result res;
619 	struct tee_fs_dirfile_dirh *dirh = NULL;
620 	struct tee_fs_dirfile_fileh dfh;
621 
622 	mutex_lock(&ree_fs_mutex);
623 
624 	res = get_dirh(&dirh);
625 	if (res != TEE_SUCCESS)
626 		goto out;
627 
628 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
629 				  &dfh);
630 	if (res != TEE_SUCCESS)
631 		goto out;
632 
633 	res = ree_fs_open_primitive(false, dfh.hash, &po->uuid, &dfh, fh);
634 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
635 		/*
636 		 * If the object isn't found someone has tampered with it,
637 		 * treat it as corrupt.
638 		 */
639 		res = TEE_ERROR_CORRUPT_OBJECT;
640 	} else if (!res && size) {
641 		struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh;
642 
643 		*size = tee_fs_htree_get_meta(fdp->ht)->length;
644 	}
645 
646 out:
647 	if (res)
648 		put_dirh(dirh, false);
649 	mutex_unlock(&ree_fs_mutex);
650 
651 	return res;
652 }
653 
654 static TEE_Result set_name(struct tee_fs_dirfile_dirh *dirh,
655 			   struct tee_fs_fd *fdp, struct tee_pobj *po,
656 			   bool overwrite)
657 {
658 	TEE_Result res;
659 	bool have_old_dfh = false;
660 	struct tee_fs_dirfile_fileh old_dfh = { .idx = -1 };
661 
662 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
663 				  &old_dfh);
664 	if (!overwrite && !res)
665 		return TEE_ERROR_ACCESS_CONFLICT;
666 
667 	if (!res)
668 		have_old_dfh = true;
669 
670 	/*
671 	 * If old_dfh wasn't found, the idx will be -1 and
672 	 * tee_fs_dirfile_rename() will allocate a new index.
673 	 */
674 	fdp->dfh.idx = old_dfh.idx;
675 	old_dfh.idx = -1;
676 	res = tee_fs_dirfile_rename(dirh, &po->uuid, &fdp->dfh,
677 				    po->obj_id, po->obj_id_len);
678 	if (res)
679 		return res;
680 
681 	res = commit_dirh_writes(dirh);
682 	if (res)
683 		return res;
684 
685 	if (have_old_dfh)
686 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &old_dfh);
687 
688 	return TEE_SUCCESS;
689 }
690 
691 static void ree_fs_close(struct tee_file_handle **fh)
692 {
693 	if (*fh) {
694 		mutex_lock(&ree_fs_mutex);
695 		put_dirh_primitive(false);
696 		ree_fs_close_primitive(*fh);
697 		*fh = NULL;
698 		mutex_unlock(&ree_fs_mutex);
699 
700 	}
701 }
702 
703 static TEE_Result ree_fs_create(struct tee_pobj *po, bool overwrite,
704 				const void *head, size_t head_size,
705 				const void *attr, size_t attr_size,
706 				const void *data, size_t data_size,
707 				struct tee_file_handle **fh)
708 {
709 	struct tee_fs_fd *fdp;
710 	struct tee_fs_dirfile_dirh *dirh = NULL;
711 	struct tee_fs_dirfile_fileh dfh;
712 	TEE_Result res;
713 	size_t pos = 0;
714 
715 	*fh = NULL;
716 	mutex_lock(&ree_fs_mutex);
717 
718 	res = get_dirh(&dirh);
719 	if (res)
720 		goto out;
721 
722 	res = tee_fs_dirfile_get_tmp(dirh, &dfh);
723 	if (res)
724 		goto out;
725 
726 	res = ree_fs_open_primitive(true, dfh.hash, &po->uuid, &dfh, fh);
727 	if (res)
728 		goto out;
729 
730 	if (head && head_size) {
731 		res = ree_fs_write_primitive(*fh, pos, head, head_size);
732 		if (res)
733 			goto out;
734 		pos += head_size;
735 	}
736 
737 	if (attr && attr_size) {
738 		res = ree_fs_write_primitive(*fh, pos, attr, attr_size);
739 		if (res)
740 			goto out;
741 		pos += attr_size;
742 	}
743 
744 	if (data && data_size) {
745 		res = ree_fs_write_primitive(*fh, pos, data, data_size);
746 		if (res)
747 			goto out;
748 	}
749 
750 	fdp = (struct tee_fs_fd *)*fh;
751 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
752 	if (res)
753 		goto out;
754 
755 	res = set_name(dirh, fdp, po, overwrite);
756 out:
757 	if (res) {
758 		put_dirh(dirh, true);
759 		if (*fh) {
760 			ree_fs_close_primitive(*fh);
761 			*fh = NULL;
762 			tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
763 		}
764 	}
765 	mutex_unlock(&ree_fs_mutex);
766 
767 	return res;
768 }
769 
770 static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos,
771 			       const void *buf, size_t len)
772 {
773 	TEE_Result res;
774 	struct tee_fs_dirfile_dirh *dirh = NULL;
775 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
776 
777 	mutex_lock(&ree_fs_mutex);
778 
779 	res = get_dirh(&dirh);
780 	if (res)
781 		goto out;
782 
783 	res = ree_fs_write_primitive(fh, pos, buf, len);
784 	if (res)
785 		goto out;
786 
787 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
788 	if (res)
789 		goto out;
790 
791 	res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
792 	if (res)
793 		goto out;
794 	res = commit_dirh_writes(dirh);
795 out:
796 	put_dirh(dirh, res);
797 	mutex_unlock(&ree_fs_mutex);
798 
799 	return res;
800 }
801 
802 static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new,
803 				bool overwrite)
804 {
805 	TEE_Result res;
806 	struct tee_fs_dirfile_dirh *dirh = NULL;
807 	struct tee_fs_dirfile_fileh dfh;
808 	struct tee_fs_dirfile_fileh remove_dfh = { .idx = -1 };
809 
810 	if (!new)
811 		return TEE_ERROR_BAD_PARAMETERS;
812 
813 	mutex_lock(&ree_fs_mutex);
814 	res = get_dirh(&dirh);
815 	if (res)
816 		goto out;
817 
818 	res = tee_fs_dirfile_find(dirh, &new->uuid, new->obj_id,
819 				  new->obj_id_len, &remove_dfh);
820 	if (!res && !overwrite) {
821 		res = TEE_ERROR_ACCESS_CONFLICT;
822 		goto out;
823 	}
824 
825 	res = tee_fs_dirfile_find(dirh, &old->uuid, old->obj_id,
826 				  old->obj_id_len, &dfh);
827 	if (res)
828 		goto out;
829 
830 	res = tee_fs_dirfile_rename(dirh, &new->uuid, &dfh, new->obj_id,
831 				    new->obj_id_len);
832 	if (res)
833 		goto out;
834 
835 	if (remove_dfh.idx != -1) {
836 		res = tee_fs_dirfile_remove(dirh, &remove_dfh);
837 		if (res)
838 			goto out;
839 	}
840 
841 	res = commit_dirh_writes(dirh);
842 	if (res)
843 		goto out;
844 
845 	if (remove_dfh.idx != -1)
846 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &remove_dfh);
847 
848 out:
849 	put_dirh(dirh, res);
850 	mutex_unlock(&ree_fs_mutex);
851 
852 	return res;
853 
854 }
855 
856 static TEE_Result ree_fs_remove(struct tee_pobj *po)
857 {
858 	TEE_Result res;
859 	struct tee_fs_dirfile_dirh *dirh = NULL;
860 	struct tee_fs_dirfile_fileh dfh;
861 
862 	mutex_lock(&ree_fs_mutex);
863 	res = get_dirh(&dirh);
864 	if (res)
865 		goto out;
866 
867 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
868 				  &dfh);
869 	if (res)
870 		goto out;
871 
872 	res = tee_fs_dirfile_remove(dirh, &dfh);
873 	if (res)
874 		goto out;
875 
876 	res = commit_dirh_writes(dirh);
877 	if (res)
878 		goto out;
879 
880 	tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
881 
882 	assert(tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
883 				   &dfh));
884 out:
885 	put_dirh(dirh, res);
886 	mutex_unlock(&ree_fs_mutex);
887 
888 	return res;
889 }
890 
891 static TEE_Result ree_fs_truncate(struct tee_file_handle *fh, size_t len)
892 {
893 	TEE_Result res;
894 	struct tee_fs_dirfile_dirh *dirh = NULL;
895 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
896 
897 	mutex_lock(&ree_fs_mutex);
898 
899 	res = get_dirh(&dirh);
900 	if (res)
901 		goto out;
902 
903 	res = ree_fs_ftruncate_internal(fdp, len);
904 	if (res)
905 		goto out;
906 
907 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
908 	if (res)
909 		goto out;
910 
911 	res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
912 	if (res)
913 		goto out;
914 	res = commit_dirh_writes(dirh);
915 out:
916 	put_dirh(dirh, res);
917 	mutex_unlock(&ree_fs_mutex);
918 
919 	return res;
920 }
921 
922 static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid,
923 				     struct tee_fs_dir **dir)
924 
925 {
926 	TEE_Result res;
927 	struct tee_fs_dir *d = calloc(1, sizeof(*d));
928 
929 	if (!d)
930 		return TEE_ERROR_OUT_OF_MEMORY;
931 
932 	d->uuid = uuid;
933 
934 	mutex_lock(&ree_fs_mutex);
935 
936 	res = get_dirh(&d->dirh);
937 	if (res)
938 		goto out;
939 
940 	/* See that there's at least one file */
941 	d->idx = -1;
942 	d->d.oidlen = sizeof(d->d.oid);
943 	res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
944 				      &d->d.oidlen);
945 	d->idx = -1;
946 
947 out:
948 	if (!res) {
949 		*dir = d;
950 	} else {
951 		if (d)
952 			put_dirh(d->dirh, false);
953 		free(d);
954 	}
955 	mutex_unlock(&ree_fs_mutex);
956 
957 	return res;
958 }
959 
960 static void ree_fs_closedir_rpc(struct tee_fs_dir *d)
961 {
962 	if (d) {
963 		mutex_lock(&ree_fs_mutex);
964 
965 		put_dirh(d->dirh, false);
966 		free(d);
967 
968 		mutex_unlock(&ree_fs_mutex);
969 	}
970 }
971 
972 static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d,
973 				     struct tee_fs_dirent **ent)
974 {
975 	TEE_Result res;
976 
977 	mutex_lock(&ree_fs_mutex);
978 
979 	d->d.oidlen = sizeof(d->d.oid);
980 	res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
981 				      &d->d.oidlen);
982 	if (res == TEE_SUCCESS)
983 		*ent = &d->d;
984 
985 	mutex_unlock(&ree_fs_mutex);
986 
987 	return res;
988 }
989 
990 const struct tee_file_operations ree_fs_ops = {
991 	.open = ree_fs_open,
992 	.create = ree_fs_create,
993 	.close = ree_fs_close,
994 	.read = ree_fs_read,
995 	.write = ree_fs_write,
996 	.truncate = ree_fs_truncate,
997 	.rename = ree_fs_rename,
998 	.remove = ree_fs_remove,
999 	.opendir = ree_fs_opendir_rpc,
1000 	.closedir = ree_fs_closedir_rpc,
1001 	.readdir = ree_fs_readdir_rpc,
1002 };
1003