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