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