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