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