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