xref: /optee_os/core/tee/tee_ree_fs.c (revision 0d77037f5943c86560dd7c8f473fbc6a55d60a34)
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 	res = tee_fs_dirfile_open(false, hashp, &ree_dirf_ops, dirh);
498 	if (res == TEE_ERROR_ITEM_NOT_FOUND)
499 		res = tee_fs_dirfile_open(true, NULL, &ree_dirf_ops, dirh);
500 
501 	if (res)
502 		rpmb_fs_ops.close(&ree_fs_rpmb_fh);
503 
504 	return res;
505 }
506 
507 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
508 {
509 	TEE_Result res;
510 	uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
511 
512 	res = tee_fs_dirfile_commit_writes(dirh, hash);
513 	if (res)
514 		return res;
515 	return rpmb_fs_ops.write(ree_fs_rpmb_fh, 0, hash, sizeof(hash));
516 }
517 
518 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
519 {
520 	tee_fs_dirfile_close(*dirh);
521 	*dirh = NULL;
522 	rpmb_fs_ops.close(&ree_fs_rpmb_fh);
523 }
524 
525 #else /*!CFG_RPMB_FS*/
526 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
527 {
528 	TEE_Result res;
529 
530 	res = tee_fs_dirfile_open(false, NULL, &ree_dirf_ops, dirh);
531 	if (res == TEE_ERROR_ITEM_NOT_FOUND)
532 		return tee_fs_dirfile_open(true, NULL, &ree_dirf_ops, dirh);
533 
534 	return res;
535 }
536 
537 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
538 {
539 	return tee_fs_dirfile_commit_writes(dirh, NULL);
540 }
541 
542 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
543 {
544 	tee_fs_dirfile_close(*dirh);
545 	*dirh = NULL;
546 }
547 #endif /*!CFG_RPMB_FS*/
548 
549 static TEE_Result get_dirh(struct tee_fs_dirfile_dirh **dirh)
550 {
551 	if (!ree_fs_dirh) {
552 		TEE_Result res = open_dirh(&ree_fs_dirh);
553 
554 		if (res) {
555 			*dirh = NULL;
556 			return res;
557 		}
558 	}
559 	ree_fs_dirh_refcount++;
560 	assert(ree_fs_dirh);
561 	assert(ree_fs_dirh_refcount);
562 	*dirh = ree_fs_dirh;
563 	return TEE_SUCCESS;
564 }
565 
566 static void put_dirh_primitive(bool close)
567 {
568 	assert(ree_fs_dirh_refcount);
569 
570 	/*
571 	 * During the execution of one of the ree_fs_ops ree_fs_dirh is
572 	 * guareteed to be a valid pointer. But when the fop has returned
573 	 * another thread may get an error or something causing that fop
574 	 * to do a put with close=1.
575 	 *
576 	 * For all fops but ree_fs_close() there's a call to get_dirh() to
577 	 * get a new dirh which will open it again if it was closed before.
578 	 * But in the ree_fs_close() case there's no call to get_dirh()
579 	 * only to this function, put_dirh_primitive(), and in this case
580 	 * ree_fs_dirh may actually be NULL.
581 	 */
582 	ree_fs_dirh_refcount--;
583 	if (ree_fs_dirh && (!ree_fs_dirh_refcount || close))
584 		close_dirh(&ree_fs_dirh);
585 }
586 
587 static void put_dirh(struct tee_fs_dirfile_dirh *dirh, bool close)
588 {
589 	if (dirh) {
590 		assert(dirh == ree_fs_dirh);
591 		put_dirh_primitive(close);
592 	}
593 }
594 
595 static TEE_Result ree_fs_open(struct tee_pobj *po, size_t *size,
596 			      struct tee_file_handle **fh)
597 {
598 	TEE_Result res;
599 	struct tee_fs_dirfile_dirh *dirh = NULL;
600 	struct tee_fs_dirfile_fileh dfh;
601 
602 	mutex_lock(&ree_fs_mutex);
603 
604 	res = get_dirh(&dirh);
605 	if (res != TEE_SUCCESS)
606 		goto out;
607 
608 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
609 				  &dfh);
610 	if (res != TEE_SUCCESS)
611 		goto out;
612 
613 	res = ree_fs_open_primitive(false, dfh.hash, &po->uuid, &dfh, fh);
614 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
615 		/*
616 		 * If the object isn't found someone has tampered with it,
617 		 * treat it as corrupt.
618 		 */
619 		res = TEE_ERROR_CORRUPT_OBJECT;
620 	} else if (!res && size) {
621 		struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh;
622 
623 		*size = tee_fs_htree_get_meta(fdp->ht)->length;
624 	}
625 
626 out:
627 	if (res)
628 		put_dirh(dirh, false);
629 	mutex_unlock(&ree_fs_mutex);
630 
631 	return res;
632 }
633 
634 static TEE_Result set_name(struct tee_fs_dirfile_dirh *dirh,
635 			   struct tee_fs_fd *fdp, struct tee_pobj *po,
636 			   bool overwrite)
637 {
638 	TEE_Result res;
639 	bool have_old_dfh = false;
640 	struct tee_fs_dirfile_fileh old_dfh = { .idx = -1 };
641 
642 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
643 				  &old_dfh);
644 	if (!overwrite && !res)
645 		return TEE_ERROR_ACCESS_CONFLICT;
646 
647 	if (!res)
648 		have_old_dfh = true;
649 
650 	/*
651 	 * If old_dfh wasn't found, the idx will be -1 and
652 	 * tee_fs_dirfile_rename() will allocate a new index.
653 	 */
654 	fdp->dfh.idx = old_dfh.idx;
655 	old_dfh.idx = -1;
656 	res = tee_fs_dirfile_rename(dirh, &po->uuid, &fdp->dfh,
657 				    po->obj_id, po->obj_id_len);
658 	if (res)
659 		return res;
660 
661 	res = commit_dirh_writes(dirh);
662 	if (res)
663 		return res;
664 
665 	if (have_old_dfh)
666 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &old_dfh);
667 
668 	return TEE_SUCCESS;
669 }
670 
671 static void ree_fs_close(struct tee_file_handle **fh)
672 {
673 	if (*fh) {
674 		mutex_lock(&ree_fs_mutex);
675 		put_dirh_primitive(false);
676 		ree_fs_close_primitive(*fh);
677 		*fh = NULL;
678 		mutex_unlock(&ree_fs_mutex);
679 
680 	}
681 }
682 
683 static TEE_Result ree_fs_create(struct tee_pobj *po, bool overwrite,
684 				const void *head, size_t head_size,
685 				const void *attr, size_t attr_size,
686 				const void *data, size_t data_size,
687 				struct tee_file_handle **fh)
688 {
689 	struct tee_fs_fd *fdp;
690 	struct tee_fs_dirfile_dirh *dirh = NULL;
691 	struct tee_fs_dirfile_fileh dfh;
692 	TEE_Result res;
693 	size_t pos = 0;
694 
695 	*fh = NULL;
696 	mutex_lock(&ree_fs_mutex);
697 
698 	res = get_dirh(&dirh);
699 	if (res)
700 		goto out;
701 
702 	res = tee_fs_dirfile_get_tmp(dirh, &dfh);
703 	if (res)
704 		goto out;
705 
706 	res = ree_fs_open_primitive(true, dfh.hash, &po->uuid, &dfh, fh);
707 	if (res)
708 		goto out;
709 
710 	if (head && head_size) {
711 		res = ree_fs_write_primitive(*fh, pos, head, head_size);
712 		if (res)
713 			goto out;
714 		pos += head_size;
715 	}
716 
717 	if (attr && attr_size) {
718 		res = ree_fs_write_primitive(*fh, pos, attr, attr_size);
719 		if (res)
720 			goto out;
721 		pos += attr_size;
722 	}
723 
724 	if (data && data_size) {
725 		res = ree_fs_write_primitive(*fh, pos, data, data_size);
726 		if (res)
727 			goto out;
728 	}
729 
730 	fdp = (struct tee_fs_fd *)*fh;
731 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
732 	if (res)
733 		goto out;
734 
735 	res = set_name(dirh, fdp, po, overwrite);
736 out:
737 	if (res) {
738 		put_dirh(dirh, true);
739 		if (*fh) {
740 			ree_fs_close_primitive(*fh);
741 			*fh = NULL;
742 			tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
743 		}
744 	}
745 	mutex_unlock(&ree_fs_mutex);
746 
747 	return res;
748 }
749 
750 static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos,
751 			       const void *buf, size_t len)
752 {
753 	TEE_Result res;
754 	struct tee_fs_dirfile_dirh *dirh = NULL;
755 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
756 
757 	mutex_lock(&ree_fs_mutex);
758 
759 	res = get_dirh(&dirh);
760 	if (res)
761 		goto out;
762 
763 	res = ree_fs_write_primitive(fh, pos, buf, len);
764 	if (res)
765 		goto out;
766 
767 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
768 	if (res)
769 		goto out;
770 
771 	res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
772 	if (res)
773 		goto out;
774 	res = commit_dirh_writes(dirh);
775 out:
776 	put_dirh(dirh, res);
777 	mutex_unlock(&ree_fs_mutex);
778 
779 	return res;
780 }
781 
782 static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new,
783 				bool overwrite)
784 {
785 	TEE_Result res;
786 	struct tee_fs_dirfile_dirh *dirh = NULL;
787 	struct tee_fs_dirfile_fileh dfh;
788 	struct tee_fs_dirfile_fileh remove_dfh = { .idx = -1 };
789 
790 	if (!new)
791 		return TEE_ERROR_BAD_PARAMETERS;
792 
793 	mutex_lock(&ree_fs_mutex);
794 	res = get_dirh(&dirh);
795 	if (res)
796 		goto out;
797 
798 	res = tee_fs_dirfile_find(dirh, &new->uuid, new->obj_id,
799 				  new->obj_id_len, &remove_dfh);
800 	if (!res && !overwrite) {
801 		res = TEE_ERROR_ACCESS_CONFLICT;
802 		goto out;
803 	}
804 
805 	res = tee_fs_dirfile_find(dirh, &old->uuid, old->obj_id,
806 				  old->obj_id_len, &dfh);
807 	if (res)
808 		goto out;
809 
810 	res = tee_fs_dirfile_rename(dirh, &new->uuid, &dfh, new->obj_id,
811 				    new->obj_id_len);
812 	if (res)
813 		goto out;
814 
815 	if (remove_dfh.idx != -1) {
816 		res = tee_fs_dirfile_remove(dirh, &remove_dfh);
817 		if (res)
818 			goto out;
819 	}
820 
821 	res = commit_dirh_writes(dirh);
822 	if (res)
823 		goto out;
824 
825 	if (remove_dfh.idx != -1)
826 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &remove_dfh);
827 
828 out:
829 	put_dirh(dirh, res);
830 	mutex_unlock(&ree_fs_mutex);
831 
832 	return res;
833 
834 }
835 
836 static TEE_Result ree_fs_remove(struct tee_pobj *po)
837 {
838 	TEE_Result res;
839 	struct tee_fs_dirfile_dirh *dirh = NULL;
840 	struct tee_fs_dirfile_fileh dfh;
841 
842 	mutex_lock(&ree_fs_mutex);
843 	res = get_dirh(&dirh);
844 	if (res)
845 		goto out;
846 
847 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
848 				  &dfh);
849 	if (res)
850 		goto out;
851 
852 	res = tee_fs_dirfile_remove(dirh, &dfh);
853 	if (res)
854 		goto out;
855 
856 	res = commit_dirh_writes(dirh);
857 	if (res)
858 		goto out;
859 
860 	tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
861 
862 	assert(tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
863 				   &dfh));
864 out:
865 	put_dirh(dirh, res);
866 	mutex_unlock(&ree_fs_mutex);
867 
868 	return res;
869 }
870 
871 static TEE_Result ree_fs_truncate(struct tee_file_handle *fh, size_t len)
872 {
873 	TEE_Result res;
874 	struct tee_fs_dirfile_dirh *dirh = NULL;
875 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
876 
877 	mutex_lock(&ree_fs_mutex);
878 
879 	res = get_dirh(&dirh);
880 	if (res)
881 		goto out;
882 
883 	res = ree_fs_ftruncate_internal(fdp, len);
884 	if (res)
885 		goto out;
886 
887 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
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_opendir_rpc(const TEE_UUID *uuid,
903 				     struct tee_fs_dir **dir)
904 
905 {
906 	TEE_Result res;
907 	struct tee_fs_dir *d = calloc(1, sizeof(*d));
908 
909 	if (!d)
910 		return TEE_ERROR_OUT_OF_MEMORY;
911 
912 	d->uuid = uuid;
913 
914 	mutex_lock(&ree_fs_mutex);
915 
916 	res = get_dirh(&d->dirh);
917 	if (res)
918 		goto out;
919 
920 	/* See that there's at least one file */
921 	d->idx = -1;
922 	d->d.oidlen = sizeof(d->d.oid);
923 	res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
924 				      &d->d.oidlen);
925 	d->idx = -1;
926 
927 out:
928 	if (!res) {
929 		*dir = d;
930 	} else {
931 		if (d)
932 			put_dirh(d->dirh, false);
933 		free(d);
934 	}
935 	mutex_unlock(&ree_fs_mutex);
936 
937 	return res;
938 }
939 
940 static void ree_fs_closedir_rpc(struct tee_fs_dir *d)
941 {
942 	if (d) {
943 		mutex_lock(&ree_fs_mutex);
944 
945 		put_dirh(d->dirh, false);
946 		free(d);
947 
948 		mutex_unlock(&ree_fs_mutex);
949 	}
950 }
951 
952 static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d,
953 				     struct tee_fs_dirent **ent)
954 {
955 	TEE_Result res;
956 
957 	mutex_lock(&ree_fs_mutex);
958 
959 	d->d.oidlen = sizeof(d->d.oid);
960 	res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
961 				      &d->d.oidlen);
962 	if (res == TEE_SUCCESS)
963 		*ent = &d->d;
964 
965 	mutex_unlock(&ree_fs_mutex);
966 
967 	return res;
968 }
969 
970 const struct tee_file_operations ree_fs_ops = {
971 	.open = ree_fs_open,
972 	.create = ree_fs_create,
973 	.close = ree_fs_close,
974 	.read = ree_fs_read,
975 	.write = ree_fs_write,
976 	.truncate = ree_fs_truncate,
977 	.rename = ree_fs_rename,
978 	.remove = ree_fs_remove,
979 	.opendir = ree_fs_opendir_rpc,
980 	.closedir = ree_fs_closedir_rpc,
981 	.readdir = ree_fs_readdir_rpc,
982 };
983