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