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