xref: /optee_os/core/tee/tee_ree_fs.c (revision 300faa62b09a97b2d6ef4397b21d327bf9bb2f6b)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2015, Linaro Limited
4  */
5 
6 #include <assert.h>
7 #include <config.h>
8 #include <kernel/mutex.h>
9 #include <kernel/panic.h>
10 #include <kernel/thread.h>
11 #include <mempool.h>
12 #include <mm/core_memprot.h>
13 #include <mm/tee_pager.h>
14 #include <optee_rpc_cmd.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string_ext.h>
18 #include <string.h>
19 #include <sys/queue.h>
20 #include <tee/fs_dirfile.h>
21 #include <tee/fs_htree.h>
22 #include <tee/tee_fs.h>
23 #include <tee/tee_fs_rpc.h>
24 #include <tee/tee_pobj.h>
25 #include <trace.h>
26 #include <utee_defines.h>
27 #include <util.h>
28 
29 #define BLOCK_SHIFT	12
30 
31 #define BLOCK_SIZE	(1 << BLOCK_SHIFT)
32 
33 struct tee_fs_fd {
34 	struct tee_fs_htree *ht;
35 	int fd;
36 	struct tee_fs_dirfile_fileh dfh;
37 	const TEE_UUID *uuid;
38 };
39 
40 struct tee_fs_dir {
41 	struct tee_fs_dirfile_dirh *dirh;
42 	int idx;
43 	struct tee_fs_dirent d;
44 	const TEE_UUID *uuid;
45 };
46 
47 static int pos_to_block_num(int position)
48 {
49 	return position >> BLOCK_SHIFT;
50 }
51 
52 static struct mutex ree_fs_mutex = MUTEX_INITIALIZER;
53 
54 static void *get_tmp_block(void)
55 {
56 	return mempool_alloc(mempool_default, BLOCK_SIZE);
57 }
58 
59 static void put_tmp_block(void *tmp_block)
60 {
61 	mempool_free(mempool_default, tmp_block);
62 }
63 
64 static TEE_Result out_of_place_write(struct tee_fs_fd *fdp, size_t pos,
65 				     const void *buf, size_t len)
66 {
67 	TEE_Result res;
68 	size_t start_block_num = pos_to_block_num(pos);
69 	size_t end_block_num = pos_to_block_num(pos + len - 1);
70 	size_t remain_bytes = len;
71 	uint8_t *data_ptr = (uint8_t *)buf;
72 	uint8_t *block;
73 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
74 
75 	/*
76 	 * It doesn't make sense to call this function if nothing is to be
77 	 * written. This also guards against end_block_num getting an
78 	 * unexpected value when pos == 0 and len == 0.
79 	 */
80 	if (!len)
81 		return TEE_ERROR_BAD_PARAMETERS;
82 
83 	block = get_tmp_block();
84 	if (!block)
85 		return TEE_ERROR_OUT_OF_MEMORY;
86 
87 	while (start_block_num <= end_block_num) {
88 		size_t offset = pos % BLOCK_SIZE;
89 		size_t size_to_write = MIN(remain_bytes, (size_t)BLOCK_SIZE);
90 
91 		if (size_to_write + offset > BLOCK_SIZE)
92 			size_to_write = BLOCK_SIZE - offset;
93 
94 		if (start_block_num * BLOCK_SIZE <
95 		    ROUNDUP(meta->length, BLOCK_SIZE)) {
96 			res = tee_fs_htree_read_block(&fdp->ht,
97 						      start_block_num, block);
98 			if (res != TEE_SUCCESS)
99 				goto exit;
100 		} else {
101 			memset(block, 0, BLOCK_SIZE);
102 		}
103 
104 		if (data_ptr)
105 			memcpy(block + offset, data_ptr, size_to_write);
106 		else
107 			memset(block + offset, 0, size_to_write);
108 
109 		res = tee_fs_htree_write_block(&fdp->ht, start_block_num,
110 					       block);
111 		if (res != TEE_SUCCESS)
112 			goto exit;
113 
114 		if (data_ptr)
115 			data_ptr += size_to_write;
116 		remain_bytes -= size_to_write;
117 		start_block_num++;
118 		pos += size_to_write;
119 	}
120 
121 	if (pos > meta->length) {
122 		meta->length = pos;
123 		tee_fs_htree_meta_set_dirty(fdp->ht);
124 	}
125 
126 exit:
127 	if (block)
128 		put_tmp_block(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_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_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_RPC_CMD_FS, fdp->fd,
287 					  offs + sz);
288 		if (res != TEE_SUCCESS)
289 			return res;
290 
291 		meta->length = new_file_len;
292 		tee_fs_htree_meta_set_dirty(fdp->ht);
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 = get_tmp_block();
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 	if (block)
354 		put_tmp_block(block);
355 	return res;
356 }
357 
358 static TEE_Result ree_fs_read(struct tee_file_handle *fh, size_t pos,
359 			      void *buf, size_t *len)
360 {
361 	TEE_Result res;
362 
363 	mutex_lock(&ree_fs_mutex);
364 	res = ree_fs_read_primitive(fh, pos, buf, len);
365 	mutex_unlock(&ree_fs_mutex);
366 
367 	return res;
368 }
369 
370 static TEE_Result ree_fs_write_primitive(struct tee_file_handle *fh, size_t pos,
371 					 const void *buf, size_t len)
372 {
373 	TEE_Result res;
374 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
375 	size_t file_size;
376 
377 	if (!len)
378 		return TEE_SUCCESS;
379 
380 	file_size = tee_fs_htree_get_meta(fdp->ht)->length;
381 
382 	if ((pos + len) < len)
383 		return TEE_ERROR_BAD_PARAMETERS;
384 
385 	if (file_size < pos) {
386 		res = ree_fs_ftruncate_internal(fdp, pos);
387 		if (res != TEE_SUCCESS)
388 			return res;
389 	}
390 
391 	return out_of_place_write(fdp, pos, buf, len);
392 }
393 
394 static TEE_Result ree_fs_open_primitive(bool create, uint8_t *hash,
395 					const TEE_UUID *uuid,
396 					struct tee_fs_dirfile_fileh *dfh,
397 					struct tee_file_handle **fh)
398 {
399 	TEE_Result res;
400 	struct tee_fs_fd *fdp;
401 
402 	fdp = calloc(1, sizeof(struct tee_fs_fd));
403 	if (!fdp)
404 		return TEE_ERROR_OUT_OF_MEMORY;
405 	fdp->fd = -1;
406 	fdp->uuid = uuid;
407 
408 	if (create)
409 		res = tee_fs_rpc_create_dfh(OPTEE_RPC_CMD_FS,
410 					    dfh, &fdp->fd);
411 	else
412 		res = tee_fs_rpc_open_dfh(OPTEE_RPC_CMD_FS, dfh, &fdp->fd);
413 
414 	if (res != TEE_SUCCESS)
415 		goto out;
416 
417 	res = tee_fs_htree_open(create, hash, uuid, &ree_fs_storage_ops,
418 				fdp, &fdp->ht);
419 out:
420 	if (res == TEE_SUCCESS) {
421 		if (dfh)
422 			fdp->dfh = *dfh;
423 		else
424 			fdp->dfh.idx = -1;
425 		*fh = (struct tee_file_handle *)fdp;
426 	} else {
427 		if (res == TEE_ERROR_SECURITY)
428 			DMSG("Secure storage corruption detected");
429 		if (fdp->fd != -1)
430 			tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd);
431 		if (create)
432 			tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, dfh);
433 		free(fdp);
434 	}
435 
436 	return res;
437 }
438 
439 static void ree_fs_close_primitive(struct tee_file_handle *fh)
440 {
441 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
442 
443 	if (fdp) {
444 		tee_fs_htree_close(&fdp->ht);
445 		tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd);
446 		free(fdp);
447 	}
448 }
449 
450 static TEE_Result ree_dirf_commit_writes(struct tee_file_handle *fh,
451 					 uint8_t *hash)
452 {
453 	TEE_Result res;
454 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
455 
456 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
457 
458 	if (!res && hash)
459 		memcpy(hash, fdp->dfh.hash, sizeof(fdp->dfh.hash));
460 
461 	return res;
462 }
463 
464 static const struct tee_fs_dirfile_operations ree_dirf_ops = {
465 	.open = ree_fs_open_primitive,
466 	.close = ree_fs_close_primitive,
467 	.read = ree_fs_read_primitive,
468 	.write = ree_fs_write_primitive,
469 	.commit_writes = ree_dirf_commit_writes,
470 };
471 
472 static struct tee_fs_dirfile_dirh *ree_fs_dirh;
473 static size_t ree_fs_dirh_refcount;
474 
475 #ifdef CFG_RPMB_FS
476 static struct tee_file_handle *ree_fs_rpmb_fh;
477 
478 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
479 {
480 	TEE_Result res;
481 	uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
482 	uint8_t *hashp = NULL;
483 	const char fname[] = "dirfile.db.hash";
484 
485 	res = tee_rpmb_fs_raw_open(fname, false, &ree_fs_rpmb_fh);
486 	if (!res) {
487 		size_t l = sizeof(hash);
488 
489 		res = rpmb_fs_ops.read(ree_fs_rpmb_fh, 0, hash, &l);
490 		if (res)
491 			return res;
492 		if (l == sizeof(hash))
493 			hashp = hash;
494 	} else if (res == TEE_ERROR_ITEM_NOT_FOUND) {
495 		res = tee_rpmb_fs_raw_open(fname, true, &ree_fs_rpmb_fh);
496 	}
497 	if (res)
498 		return res;
499 
500 	res = tee_fs_dirfile_open(false, hashp, &ree_dirf_ops, dirh);
501 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
502 		if (IS_ENABLED(CFG_REE_FS_ALLOW_RESET) && hashp) {
503 			DMSG("Clear REE FS hash in RPMB");
504 			res = rpmb_fs_ops.truncate(ree_fs_rpmb_fh, 0);
505 			if (res) {
506 				DMSG("Clear REE FS hash failed: %#"PRIx32, res);
507 				res = TEE_ERROR_SECURITY;
508 				goto out;
509 			}
510 		}
511 
512 		res = tee_fs_dirfile_open(true, NULL, &ree_dirf_ops, dirh);
513 	}
514 
515 out:
516 	if (res)
517 		rpmb_fs_ops.close(&ree_fs_rpmb_fh);
518 
519 	return res;
520 }
521 
522 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
523 {
524 	TEE_Result res;
525 	uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
526 
527 	res = tee_fs_dirfile_commit_writes(dirh, hash);
528 	if (res)
529 		return res;
530 	return rpmb_fs_ops.write(ree_fs_rpmb_fh, 0, hash, sizeof(hash));
531 }
532 
533 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
534 {
535 	tee_fs_dirfile_close(*dirh);
536 	*dirh = NULL;
537 	rpmb_fs_ops.close(&ree_fs_rpmb_fh);
538 }
539 
540 #else /*!CFG_RPMB_FS*/
541 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
542 {
543 	TEE_Result res;
544 
545 	res = tee_fs_dirfile_open(false, NULL, &ree_dirf_ops, dirh);
546 	if (res == TEE_ERROR_ITEM_NOT_FOUND)
547 		return tee_fs_dirfile_open(true, NULL, &ree_dirf_ops, dirh);
548 
549 	return res;
550 }
551 
552 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
553 {
554 	return tee_fs_dirfile_commit_writes(dirh, NULL);
555 }
556 
557 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
558 {
559 	tee_fs_dirfile_close(*dirh);
560 	*dirh = NULL;
561 }
562 #endif /*!CFG_RPMB_FS*/
563 
564 static TEE_Result get_dirh(struct tee_fs_dirfile_dirh **dirh)
565 {
566 	if (!ree_fs_dirh) {
567 		TEE_Result res = open_dirh(&ree_fs_dirh);
568 
569 		if (res) {
570 			*dirh = NULL;
571 			return res;
572 		}
573 	}
574 	ree_fs_dirh_refcount++;
575 	assert(ree_fs_dirh);
576 	assert(ree_fs_dirh_refcount);
577 	*dirh = ree_fs_dirh;
578 	return TEE_SUCCESS;
579 }
580 
581 static void put_dirh_primitive(bool close)
582 {
583 	assert(ree_fs_dirh_refcount);
584 
585 	/*
586 	 * During the execution of one of the ree_fs_ops ree_fs_dirh is
587 	 * guareteed to be a valid pointer. But when the fop has returned
588 	 * another thread may get an error or something causing that fop
589 	 * to do a put with close=1.
590 	 *
591 	 * For all fops but ree_fs_close() there's a call to get_dirh() to
592 	 * get a new dirh which will open it again if it was closed before.
593 	 * But in the ree_fs_close() case there's no call to get_dirh()
594 	 * only to this function, put_dirh_primitive(), and in this case
595 	 * ree_fs_dirh may actually be NULL.
596 	 */
597 	ree_fs_dirh_refcount--;
598 	if (ree_fs_dirh && (!ree_fs_dirh_refcount || close))
599 		close_dirh(&ree_fs_dirh);
600 }
601 
602 static void put_dirh(struct tee_fs_dirfile_dirh *dirh, bool close)
603 {
604 	if (dirh) {
605 		assert(dirh == ree_fs_dirh);
606 		put_dirh_primitive(close);
607 	}
608 }
609 
610 static TEE_Result ree_fs_open(struct tee_pobj *po, size_t *size,
611 			      struct tee_file_handle **fh)
612 {
613 	TEE_Result res;
614 	struct tee_fs_dirfile_dirh *dirh = NULL;
615 	struct tee_fs_dirfile_fileh dfh;
616 
617 	mutex_lock(&ree_fs_mutex);
618 
619 	res = get_dirh(&dirh);
620 	if (res != TEE_SUCCESS)
621 		goto out;
622 
623 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
624 				  &dfh);
625 	if (res != TEE_SUCCESS)
626 		goto out;
627 
628 	res = ree_fs_open_primitive(false, dfh.hash, &po->uuid, &dfh, fh);
629 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
630 		/*
631 		 * If the object isn't found someone has tampered with it,
632 		 * treat it as corrupt.
633 		 */
634 		res = TEE_ERROR_CORRUPT_OBJECT;
635 	} else if (!res && size) {
636 		struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh;
637 
638 		*size = tee_fs_htree_get_meta(fdp->ht)->length;
639 	}
640 
641 out:
642 	if (res)
643 		put_dirh(dirh, false);
644 	mutex_unlock(&ree_fs_mutex);
645 
646 	return res;
647 }
648 
649 static TEE_Result set_name(struct tee_fs_dirfile_dirh *dirh,
650 			   struct tee_fs_fd *fdp, struct tee_pobj *po,
651 			   bool overwrite)
652 {
653 	TEE_Result res;
654 	bool have_old_dfh = false;
655 	struct tee_fs_dirfile_fileh old_dfh = { .idx = -1 };
656 
657 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
658 				  &old_dfh);
659 	if (!overwrite && !res)
660 		return TEE_ERROR_ACCESS_CONFLICT;
661 
662 	if (!res)
663 		have_old_dfh = true;
664 
665 	/*
666 	 * If old_dfh wasn't found, the idx will be -1 and
667 	 * tee_fs_dirfile_rename() will allocate a new index.
668 	 */
669 	fdp->dfh.idx = old_dfh.idx;
670 	old_dfh.idx = -1;
671 	res = tee_fs_dirfile_rename(dirh, &po->uuid, &fdp->dfh,
672 				    po->obj_id, po->obj_id_len);
673 	if (res)
674 		return res;
675 
676 	res = commit_dirh_writes(dirh);
677 	if (res)
678 		return res;
679 
680 	if (have_old_dfh)
681 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &old_dfh);
682 
683 	return TEE_SUCCESS;
684 }
685 
686 static void ree_fs_close(struct tee_file_handle **fh)
687 {
688 	if (*fh) {
689 		mutex_lock(&ree_fs_mutex);
690 		put_dirh_primitive(false);
691 		ree_fs_close_primitive(*fh);
692 		*fh = NULL;
693 		mutex_unlock(&ree_fs_mutex);
694 
695 	}
696 }
697 
698 static TEE_Result ree_fs_create(struct tee_pobj *po, bool overwrite,
699 				const void *head, size_t head_size,
700 				const void *attr, size_t attr_size,
701 				const void *data, size_t data_size,
702 				struct tee_file_handle **fh)
703 {
704 	struct tee_fs_fd *fdp;
705 	struct tee_fs_dirfile_dirh *dirh = NULL;
706 	struct tee_fs_dirfile_fileh dfh;
707 	TEE_Result res;
708 	size_t pos = 0;
709 
710 	*fh = NULL;
711 	mutex_lock(&ree_fs_mutex);
712 
713 	res = get_dirh(&dirh);
714 	if (res)
715 		goto out;
716 
717 	res = tee_fs_dirfile_get_tmp(dirh, &dfh);
718 	if (res)
719 		goto out;
720 
721 	res = ree_fs_open_primitive(true, dfh.hash, &po->uuid, &dfh, fh);
722 	if (res)
723 		goto out;
724 
725 	if (head && head_size) {
726 		res = ree_fs_write_primitive(*fh, pos, head, head_size);
727 		if (res)
728 			goto out;
729 		pos += head_size;
730 	}
731 
732 	if (attr && attr_size) {
733 		res = ree_fs_write_primitive(*fh, pos, attr, attr_size);
734 		if (res)
735 			goto out;
736 		pos += attr_size;
737 	}
738 
739 	if (data && data_size) {
740 		res = ree_fs_write_primitive(*fh, pos, data, data_size);
741 		if (res)
742 			goto out;
743 	}
744 
745 	fdp = (struct tee_fs_fd *)*fh;
746 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
747 	if (res)
748 		goto out;
749 
750 	res = set_name(dirh, fdp, po, overwrite);
751 out:
752 	if (res) {
753 		put_dirh(dirh, true);
754 		if (*fh) {
755 			ree_fs_close_primitive(*fh);
756 			*fh = NULL;
757 			tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
758 		}
759 	}
760 	mutex_unlock(&ree_fs_mutex);
761 
762 	return res;
763 }
764 
765 static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos,
766 			       const void *buf, size_t len)
767 {
768 	TEE_Result res;
769 	struct tee_fs_dirfile_dirh *dirh = NULL;
770 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
771 
772 	mutex_lock(&ree_fs_mutex);
773 
774 	res = get_dirh(&dirh);
775 	if (res)
776 		goto out;
777 
778 	res = ree_fs_write_primitive(fh, pos, buf, len);
779 	if (res)
780 		goto out;
781 
782 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
783 	if (res)
784 		goto out;
785 
786 	res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
787 	if (res)
788 		goto out;
789 	res = commit_dirh_writes(dirh);
790 out:
791 	put_dirh(dirh, res);
792 	mutex_unlock(&ree_fs_mutex);
793 
794 	return res;
795 }
796 
797 static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new,
798 				bool overwrite)
799 {
800 	TEE_Result res;
801 	struct tee_fs_dirfile_dirh *dirh = NULL;
802 	struct tee_fs_dirfile_fileh dfh;
803 	struct tee_fs_dirfile_fileh remove_dfh = { .idx = -1 };
804 
805 	if (!new)
806 		return TEE_ERROR_BAD_PARAMETERS;
807 
808 	mutex_lock(&ree_fs_mutex);
809 	res = get_dirh(&dirh);
810 	if (res)
811 		goto out;
812 
813 	res = tee_fs_dirfile_find(dirh, &new->uuid, new->obj_id,
814 				  new->obj_id_len, &remove_dfh);
815 	if (!res && !overwrite) {
816 		res = TEE_ERROR_ACCESS_CONFLICT;
817 		goto out;
818 	}
819 
820 	res = tee_fs_dirfile_find(dirh, &old->uuid, old->obj_id,
821 				  old->obj_id_len, &dfh);
822 	if (res)
823 		goto out;
824 
825 	res = tee_fs_dirfile_rename(dirh, &new->uuid, &dfh, new->obj_id,
826 				    new->obj_id_len);
827 	if (res)
828 		goto out;
829 
830 	if (remove_dfh.idx != -1) {
831 		res = tee_fs_dirfile_remove(dirh, &remove_dfh);
832 		if (res)
833 			goto out;
834 	}
835 
836 	res = commit_dirh_writes(dirh);
837 	if (res)
838 		goto out;
839 
840 	if (remove_dfh.idx != -1)
841 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &remove_dfh);
842 
843 out:
844 	put_dirh(dirh, res);
845 	mutex_unlock(&ree_fs_mutex);
846 
847 	return res;
848 
849 }
850 
851 static TEE_Result ree_fs_remove(struct tee_pobj *po)
852 {
853 	TEE_Result res;
854 	struct tee_fs_dirfile_dirh *dirh = NULL;
855 	struct tee_fs_dirfile_fileh dfh;
856 
857 	mutex_lock(&ree_fs_mutex);
858 	res = get_dirh(&dirh);
859 	if (res)
860 		goto out;
861 
862 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
863 				  &dfh);
864 	if (res)
865 		goto out;
866 
867 	res = tee_fs_dirfile_remove(dirh, &dfh);
868 	if (res)
869 		goto out;
870 
871 	res = commit_dirh_writes(dirh);
872 	if (res)
873 		goto out;
874 
875 	tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
876 
877 	assert(tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
878 				   &dfh));
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_truncate(struct tee_file_handle *fh, size_t len)
887 {
888 	TEE_Result res;
889 	struct tee_fs_dirfile_dirh *dirh = NULL;
890 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
891 
892 	mutex_lock(&ree_fs_mutex);
893 
894 	res = get_dirh(&dirh);
895 	if (res)
896 		goto out;
897 
898 	res = ree_fs_ftruncate_internal(fdp, len);
899 	if (res)
900 		goto out;
901 
902 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash);
903 	if (res)
904 		goto out;
905 
906 	res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
907 	if (res)
908 		goto out;
909 	res = commit_dirh_writes(dirh);
910 out:
911 	put_dirh(dirh, res);
912 	mutex_unlock(&ree_fs_mutex);
913 
914 	return res;
915 }
916 
917 static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid,
918 				     struct tee_fs_dir **dir)
919 
920 {
921 	TEE_Result res;
922 	struct tee_fs_dir *d = calloc(1, sizeof(*d));
923 
924 	if (!d)
925 		return TEE_ERROR_OUT_OF_MEMORY;
926 
927 	d->uuid = uuid;
928 
929 	mutex_lock(&ree_fs_mutex);
930 
931 	res = get_dirh(&d->dirh);
932 	if (res)
933 		goto out;
934 
935 	/* See that there's at least one file */
936 	d->idx = -1;
937 	d->d.oidlen = sizeof(d->d.oid);
938 	res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
939 				      &d->d.oidlen);
940 	d->idx = -1;
941 
942 out:
943 	if (!res) {
944 		*dir = d;
945 	} else {
946 		if (d)
947 			put_dirh(d->dirh, false);
948 		free(d);
949 	}
950 	mutex_unlock(&ree_fs_mutex);
951 
952 	return res;
953 }
954 
955 static void ree_fs_closedir_rpc(struct tee_fs_dir *d)
956 {
957 	if (d) {
958 		mutex_lock(&ree_fs_mutex);
959 
960 		put_dirh(d->dirh, false);
961 		free(d);
962 
963 		mutex_unlock(&ree_fs_mutex);
964 	}
965 }
966 
967 static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d,
968 				     struct tee_fs_dirent **ent)
969 {
970 	TEE_Result res;
971 
972 	mutex_lock(&ree_fs_mutex);
973 
974 	d->d.oidlen = sizeof(d->d.oid);
975 	res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
976 				      &d->d.oidlen);
977 	if (res == TEE_SUCCESS)
978 		*ent = &d->d;
979 
980 	mutex_unlock(&ree_fs_mutex);
981 
982 	return res;
983 }
984 
985 const struct tee_file_operations ree_fs_ops = {
986 	.open = ree_fs_open,
987 	.create = ree_fs_create,
988 	.close = ree_fs_close,
989 	.read = ree_fs_read,
990 	.write = ree_fs_write,
991 	.truncate = ree_fs_truncate,
992 	.rename = ree_fs_rename,
993 	.remove = ree_fs_remove,
994 	.opendir = ree_fs_opendir_rpc,
995 	.closedir = ree_fs_closedir_rpc,
996 	.readdir = ree_fs_readdir_rpc,
997 };
998