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