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