xref: /optee_os/core/tee/tee_ree_fs.c (revision 19a31ec40245ae01a9adcd206eec2a4bb4479fc9)
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/nv_counter.h>
10 #include <kernel/panic.h>
11 #include <kernel/thread.h>
12 #include <kernel/user_access.h>
13 #include <mempool.h>
14 #include <mm/core_memprot.h>
15 #include <mm/tee_pager.h>
16 #include <optee_rpc_cmd.h>
17 #include <stdlib.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_core,
66 				     const void *buf_user, size_t len)
67 {
68 	TEE_Result res;
69 	size_t start_block_num = pos_to_block_num(pos);
70 	size_t end_block_num = pos_to_block_num(pos + len - 1);
71 	size_t remain_bytes = len;
72 	uint8_t *data_core_ptr = (uint8_t *)buf_core;
73 	uint8_t *data_user_ptr = (uint8_t *)buf_user;
74 	uint8_t *block;
75 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
76 
77 	/*
78 	 * It doesn't make sense to call this function if nothing is to be
79 	 * written. This also guards against end_block_num getting an
80 	 * unexpected value when pos == 0 and len == 0.
81 	 */
82 	if (!len)
83 		return TEE_ERROR_BAD_PARAMETERS;
84 
85 	block = get_tmp_block();
86 	if (!block)
87 		return TEE_ERROR_OUT_OF_MEMORY;
88 
89 	while (start_block_num <= end_block_num) {
90 		size_t offset = pos % BLOCK_SIZE;
91 		size_t size_to_write = MIN(remain_bytes, (size_t)BLOCK_SIZE);
92 
93 		if (size_to_write + offset > BLOCK_SIZE)
94 			size_to_write = BLOCK_SIZE - offset;
95 
96 		if (start_block_num * BLOCK_SIZE <
97 		    ROUNDUP(meta->length, BLOCK_SIZE)) {
98 			res = tee_fs_htree_read_block(&fdp->ht,
99 						      start_block_num, block);
100 			if (res != TEE_SUCCESS)
101 				goto exit;
102 		} else {
103 			memset(block, 0, BLOCK_SIZE);
104 		}
105 
106 		if (data_core_ptr) {
107 			memcpy(block + offset, data_core_ptr, size_to_write);
108 		} else if (data_user_ptr) {
109 			res = copy_from_user(block + offset, data_user_ptr,
110 					     size_to_write);
111 			if (res)
112 				return res;
113 		} else {
114 			memset(block + offset, 0, size_to_write);
115 		}
116 
117 		res = tee_fs_htree_write_block(&fdp->ht, start_block_num,
118 					       block);
119 		if (res != TEE_SUCCESS)
120 			goto exit;
121 
122 		if (data_core_ptr)
123 			data_core_ptr += size_to_write;
124 		if (data_user_ptr)
125 			data_user_ptr += size_to_write;
126 		remain_bytes -= size_to_write;
127 		start_block_num++;
128 		pos += size_to_write;
129 	}
130 
131 	if (pos > meta->length) {
132 		meta->length = pos;
133 		tee_fs_htree_meta_set_dirty(fdp->ht);
134 	}
135 
136 exit:
137 	if (block)
138 		put_tmp_block(block);
139 	return res;
140 }
141 
142 static TEE_Result get_offs_size(enum tee_fs_htree_type type, size_t idx,
143 				uint8_t vers, size_t *offs, size_t *size)
144 {
145 	const size_t node_size = sizeof(struct tee_fs_htree_node_image);
146 	const size_t block_nodes = BLOCK_SIZE / (node_size * 2);
147 	size_t pbn;
148 	size_t bidx;
149 
150 	assert(vers == 0 || vers == 1);
151 
152 	/*
153 	 * File layout
154 	 * [demo with input:
155 	 * BLOCK_SIZE = 4096,
156 	 * node_size = 66,
157 	 * block_nodes = 4096/(66*2) = 31 ]
158 	 *
159 	 * phys block 0:
160 	 * tee_fs_htree_image vers 0 @ offs = 0
161 	 * tee_fs_htree_image vers 1 @ offs = sizeof(tee_fs_htree_image)
162 	 *
163 	 * phys block 1:
164 	 * tee_fs_htree_node_image 0  vers 0 @ offs = 0
165 	 * tee_fs_htree_node_image 0  vers 1 @ offs = node_size
166 	 * tee_fs_htree_node_image 1  vers 0 @ offs = node_size * 2
167 	 * tee_fs_htree_node_image 1  vers 1 @ offs = node_size * 3
168 	 * ...
169 	 * tee_fs_htree_node_image 30 vers 0 @ offs = node_size * 60
170 	 * tee_fs_htree_node_image 30 vers 1 @ offs = node_size * 61
171 	 *
172 	 * phys block 2:
173 	 * data block 0 vers 0
174 	 *
175 	 * phys block 3:
176 	 * data block 0 vers 1
177 	 *
178 	 * ...
179 	 * phys block 62:
180 	 * data block 30 vers 0
181 	 *
182 	 * phys block 63:
183 	 * data block 30 vers 1
184 	 *
185 	 * phys block 64:
186 	 * tee_fs_htree_node_image 31  vers 0 @ offs = 0
187 	 * tee_fs_htree_node_image 31  vers 1 @ offs = node_size
188 	 * tee_fs_htree_node_image 32  vers 0 @ offs = node_size * 2
189 	 * tee_fs_htree_node_image 32  vers 1 @ offs = node_size * 3
190 	 * ...
191 	 * tee_fs_htree_node_image 61 vers 0 @ offs = node_size * 60
192 	 * tee_fs_htree_node_image 61 vers 1 @ offs = node_size * 61
193 	 *
194 	 * phys block 65:
195 	 * data block 31 vers 0
196 	 *
197 	 * phys block 66:
198 	 * data block 31 vers 1
199 	 * ...
200 	 */
201 
202 	switch (type) {
203 	case TEE_FS_HTREE_TYPE_HEAD:
204 		*offs = sizeof(struct tee_fs_htree_image) * vers;
205 		*size = sizeof(struct tee_fs_htree_image);
206 		return TEE_SUCCESS;
207 	case TEE_FS_HTREE_TYPE_NODE:
208 		pbn = 1 + ((idx / block_nodes) * block_nodes * 2);
209 		*offs = pbn * BLOCK_SIZE +
210 			2 * node_size * (idx % block_nodes) +
211 			node_size * vers;
212 		*size = node_size;
213 		return TEE_SUCCESS;
214 	case TEE_FS_HTREE_TYPE_BLOCK:
215 		bidx = 2 * idx + vers;
216 		pbn = 2 + bidx + bidx / (block_nodes * 2 - 1);
217 		*offs = pbn * BLOCK_SIZE;
218 		*size = BLOCK_SIZE;
219 		return TEE_SUCCESS;
220 	default:
221 		return TEE_ERROR_GENERIC;
222 	}
223 }
224 
225 static TEE_Result ree_fs_rpc_read_init(void *aux,
226 				       struct tee_fs_rpc_operation *op,
227 				       enum tee_fs_htree_type type, size_t idx,
228 				       uint8_t vers, void **data)
229 {
230 	struct tee_fs_fd *fdp = aux;
231 	TEE_Result res;
232 	size_t offs;
233 	size_t size;
234 
235 	res = get_offs_size(type, idx, vers, &offs, &size);
236 	if (res != TEE_SUCCESS)
237 		return res;
238 
239 	return tee_fs_rpc_read_init(op, OPTEE_RPC_CMD_FS, fdp->fd,
240 				    offs, size, data);
241 }
242 
243 static TEE_Result ree_fs_rpc_write_init(void *aux,
244 					struct tee_fs_rpc_operation *op,
245 					enum tee_fs_htree_type type, size_t idx,
246 					uint8_t vers, void **data)
247 {
248 	struct tee_fs_fd *fdp = aux;
249 	TEE_Result res;
250 	size_t offs;
251 	size_t size;
252 
253 	res = get_offs_size(type, idx, vers, &offs, &size);
254 	if (res != TEE_SUCCESS)
255 		return res;
256 
257 	return tee_fs_rpc_write_init(op, OPTEE_RPC_CMD_FS, fdp->fd,
258 				     offs, size, data);
259 }
260 
261 static const struct tee_fs_htree_storage ree_fs_storage_ops = {
262 	.block_size = BLOCK_SIZE,
263 	.rpc_read_init = ree_fs_rpc_read_init,
264 	.rpc_read_final = tee_fs_rpc_read_final,
265 	.rpc_write_init = ree_fs_rpc_write_init,
266 	.rpc_write_final = tee_fs_rpc_write_final,
267 };
268 
269 static TEE_Result ree_fs_ftruncate_internal(struct tee_fs_fd *fdp,
270 					    tee_fs_off_t new_file_len)
271 {
272 	TEE_Result res;
273 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
274 
275 	if ((size_t)new_file_len > meta->length) {
276 		size_t ext_len = new_file_len - meta->length;
277 
278 		res = out_of_place_write(fdp, meta->length, NULL, NULL,
279 					 ext_len);
280 		if (res != TEE_SUCCESS)
281 			return res;
282 	} else {
283 		size_t offs;
284 		size_t sz;
285 
286 		res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK,
287 				    ROUNDUP(new_file_len, BLOCK_SIZE) /
288 					BLOCK_SIZE, 1, &offs, &sz);
289 		if (res != TEE_SUCCESS)
290 			return res;
291 
292 		res = tee_fs_htree_truncate(&fdp->ht,
293 					    new_file_len / BLOCK_SIZE);
294 		if (res != TEE_SUCCESS)
295 			return res;
296 
297 		res = tee_fs_rpc_truncate(OPTEE_RPC_CMD_FS, fdp->fd,
298 					  offs + sz);
299 		if (res != TEE_SUCCESS)
300 			return res;
301 
302 		meta->length = new_file_len;
303 		tee_fs_htree_meta_set_dirty(fdp->ht);
304 	}
305 
306 	return TEE_SUCCESS;
307 }
308 
309 static TEE_Result ree_fs_read_primitive(struct tee_file_handle *fh, size_t pos,
310 					void *buf_core, void *buf_user,
311 					size_t *len)
312 {
313 	TEE_Result res;
314 	int start_block_num;
315 	int end_block_num;
316 	size_t remain_bytes;
317 	uint8_t *data_core_ptr = buf_core;
318 	uint8_t *data_user_ptr = buf_user;
319 	uint8_t *block = NULL;
320 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
321 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
322 
323 	/* One of buf_core and buf_user must be NULL */
324 	assert(!buf_core || !buf_user);
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 		if (data_core_ptr) {
360 			memcpy(data_core_ptr, block + offset, size_to_read);
361 			data_core_ptr += size_to_read;
362 		} else if (data_user_ptr) {
363 			res = copy_to_user(data_user_ptr, block + offset,
364 					   size_to_read);
365 			if (res)
366 				goto exit;
367 			data_user_ptr += size_to_read;
368 		}
369 
370 		remain_bytes -= size_to_read;
371 		pos += size_to_read;
372 
373 		start_block_num++;
374 	}
375 	res = TEE_SUCCESS;
376 exit:
377 	if (block)
378 		put_tmp_block(block);
379 	return res;
380 }
381 
382 static TEE_Result ree_fs_read(struct tee_file_handle *fh, size_t pos,
383 			      void *buf_core, void *buf_user, size_t *len)
384 {
385 	TEE_Result res;
386 
387 	mutex_lock(&ree_fs_mutex);
388 	res = ree_fs_read_primitive(fh, pos, buf_core, buf_user, len);
389 	mutex_unlock(&ree_fs_mutex);
390 
391 	return res;
392 }
393 
394 static TEE_Result ree_fs_write_primitive(struct tee_file_handle *fh, size_t pos,
395 					 const void *buf_core,
396 					 const void *buf_user, size_t len)
397 {
398 	TEE_Result res;
399 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
400 	size_t file_size;
401 
402 	/* One of buf_core and buf_user must be NULL */
403 	assert(!buf_core || !buf_user);
404 
405 	if (!len)
406 		return TEE_SUCCESS;
407 
408 	file_size = tee_fs_htree_get_meta(fdp->ht)->length;
409 
410 	if ((pos + len) < len)
411 		return TEE_ERROR_BAD_PARAMETERS;
412 
413 	if (file_size < pos) {
414 		res = ree_fs_ftruncate_internal(fdp, pos);
415 		if (res != TEE_SUCCESS)
416 			return res;
417 	}
418 
419 	return out_of_place_write(fdp, pos, buf_core, buf_user, len);
420 }
421 
422 static TEE_Result ree_fs_open_primitive(bool create, uint8_t *hash,
423 					uint32_t min_counter,
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, min_counter, uuid,
447 				&ree_fs_storage_ops, 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, uint32_t *counter)
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, counter);
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, 0, &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, 0, &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, NULL);
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 = TEE_SUCCESS;
592 	uint32_t min_counter = 0;
593 
594 	res = nv_counter_get_ree_fs(&min_counter);
595 	if (res) {
596 		static bool once;
597 
598 		if (res != TEE_ERROR_NOT_IMPLEMENTED ||
599 		    !IS_ENABLED(CFG_INSECURE))
600 			return res;
601 
602 		if (!once) {
603 			IMSG("WARNING (insecure configuration): Failed to get monotonic counter for REE FS, using 0");
604 			once = true;
605 		}
606 		min_counter = 0;
607 	}
608 	res = tee_fs_dirfile_open(false, NULL, min_counter, &ree_dirf_ops,
609 				  dirh);
610 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
611 		if (min_counter) {
612 			if (!IS_ENABLED(CFG_REE_FS_ALLOW_RESET)) {
613 				DMSG("dirf.db file not found");
614 				return TEE_ERROR_SECURITY;
615 			}
616 			DMSG("dirf.db not found, initializing with a non-zero monotonic counter");
617 		}
618 		return tee_fs_dirfile_open(true, NULL, min_counter,
619 					   &ree_dirf_ops, dirh);
620 	}
621 
622 	return res;
623 }
624 
625 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
626 {
627 	TEE_Result res = TEE_SUCCESS;
628 	uint32_t counter = 0;
629 
630 	res = tee_fs_dirfile_commit_writes(dirh, NULL, &counter);
631 	if (res)
632 		return res;
633 	res = nv_counter_incr_ree_fs_to(counter);
634 	if (res == TEE_ERROR_NOT_IMPLEMENTED && IS_ENABLED(CFG_INSECURE)) {
635 		static bool once;
636 
637 		if (!once) {
638 			IMSG("WARNING (insecure configuration): Failed to commit dirh counter %"PRIu32, counter);
639 			once = true;
640 		}
641 		return TEE_SUCCESS;
642 	}
643 	return res;
644 }
645 
646 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
647 {
648 	tee_fs_dirfile_close(*dirh);
649 	*dirh = NULL;
650 }
651 #endif /*!CFG_REE_FS_INTEGRITY_RPMB*/
652 
653 static TEE_Result get_dirh(struct tee_fs_dirfile_dirh **dirh)
654 {
655 	if (!ree_fs_dirh) {
656 		TEE_Result res = open_dirh(&ree_fs_dirh);
657 
658 		if (res) {
659 			*dirh = NULL;
660 			return res;
661 		}
662 	}
663 	ree_fs_dirh_refcount++;
664 	assert(ree_fs_dirh);
665 	assert(ree_fs_dirh_refcount);
666 	*dirh = ree_fs_dirh;
667 	return TEE_SUCCESS;
668 }
669 
670 static void put_dirh_primitive(bool close)
671 {
672 	assert(ree_fs_dirh_refcount);
673 
674 	/*
675 	 * During the execution of one of the ree_fs_ops ree_fs_dirh is
676 	 * guareteed to be a valid pointer. But when the fop has returned
677 	 * another thread may get an error or something causing that fop
678 	 * to do a put with close=1.
679 	 *
680 	 * For all fops but ree_fs_close() there's a call to get_dirh() to
681 	 * get a new dirh which will open it again if it was closed before.
682 	 * But in the ree_fs_close() case there's no call to get_dirh()
683 	 * only to this function, put_dirh_primitive(), and in this case
684 	 * ree_fs_dirh may actually be NULL.
685 	 */
686 	ree_fs_dirh_refcount--;
687 	if (ree_fs_dirh && (!ree_fs_dirh_refcount || close))
688 		close_dirh(&ree_fs_dirh);
689 }
690 
691 static void put_dirh(struct tee_fs_dirfile_dirh *dirh, bool close)
692 {
693 	if (dirh) {
694 		assert(dirh == ree_fs_dirh);
695 		put_dirh_primitive(close);
696 	}
697 }
698 
699 static TEE_Result ree_fs_open(struct tee_pobj *po, size_t *size,
700 			      struct tee_file_handle **fh)
701 {
702 	TEE_Result res;
703 	struct tee_fs_dirfile_dirh *dirh = NULL;
704 	struct tee_fs_dirfile_fileh dfh;
705 
706 	mutex_lock(&ree_fs_mutex);
707 
708 	res = get_dirh(&dirh);
709 	if (res != TEE_SUCCESS)
710 		goto out;
711 
712 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
713 				  &dfh);
714 	if (res != TEE_SUCCESS)
715 		goto out;
716 
717 	res = ree_fs_open_primitive(false, dfh.hash, 0, &po->uuid, &dfh, fh);
718 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
719 		/*
720 		 * If the object isn't found someone has tampered with it,
721 		 * treat it as corrupt.
722 		 */
723 		res = TEE_ERROR_CORRUPT_OBJECT;
724 	} else if (!res && size) {
725 		struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh;
726 
727 		*size = tee_fs_htree_get_meta(fdp->ht)->length;
728 	}
729 
730 out:
731 	if (res)
732 		put_dirh(dirh, true);
733 	mutex_unlock(&ree_fs_mutex);
734 
735 	return res;
736 }
737 
738 static TEE_Result set_name(struct tee_fs_dirfile_dirh *dirh,
739 			   struct tee_fs_fd *fdp, struct tee_pobj *po,
740 			   bool overwrite)
741 {
742 	TEE_Result res;
743 	bool have_old_dfh = false;
744 	struct tee_fs_dirfile_fileh old_dfh = { .idx = -1 };
745 
746 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
747 				  &old_dfh);
748 	if (!overwrite && !res)
749 		return TEE_ERROR_ACCESS_CONFLICT;
750 
751 	if (!res)
752 		have_old_dfh = true;
753 
754 	/*
755 	 * If old_dfh wasn't found, the idx will be -1 and
756 	 * tee_fs_dirfile_rename() will allocate a new index.
757 	 */
758 	fdp->dfh.idx = old_dfh.idx;
759 	old_dfh.idx = -1;
760 	res = tee_fs_dirfile_rename(dirh, &po->uuid, &fdp->dfh,
761 				    po->obj_id, po->obj_id_len);
762 	if (res)
763 		return res;
764 
765 	res = commit_dirh_writes(dirh);
766 	if (res)
767 		return res;
768 
769 	if (have_old_dfh)
770 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &old_dfh);
771 
772 	return TEE_SUCCESS;
773 }
774 
775 static void ree_fs_close(struct tee_file_handle **fh)
776 {
777 	if (*fh) {
778 		mutex_lock(&ree_fs_mutex);
779 		put_dirh_primitive(false);
780 		ree_fs_close_primitive(*fh);
781 		*fh = NULL;
782 		mutex_unlock(&ree_fs_mutex);
783 
784 	}
785 }
786 
787 static TEE_Result ree_fs_create(struct tee_pobj *po, bool overwrite,
788 				const void *head, size_t head_size,
789 				const void *attr, size_t attr_size,
790 				const void *data_core, const void *data_user,
791 				size_t data_size, struct tee_file_handle **fh)
792 {
793 	struct tee_fs_fd *fdp;
794 	struct tee_fs_dirfile_dirh *dirh = NULL;
795 	struct tee_fs_dirfile_fileh dfh;
796 	TEE_Result res;
797 	size_t pos = 0;
798 
799 	/* One of data_core and data_user must be NULL */
800 	assert(!data_core || !data_user);
801 
802 	*fh = NULL;
803 	mutex_lock(&ree_fs_mutex);
804 
805 	res = get_dirh(&dirh);
806 	if (res)
807 		goto out;
808 
809 	res = tee_fs_dirfile_get_tmp(dirh, &dfh);
810 	if (res)
811 		goto out;
812 
813 	res = ree_fs_open_primitive(true, dfh.hash, 0, &po->uuid, &dfh, fh);
814 	if (res)
815 		goto out;
816 
817 	if (head && head_size) {
818 		res = ree_fs_write_primitive(*fh, pos, head, NULL, head_size);
819 		if (res)
820 			goto out;
821 		pos += head_size;
822 	}
823 
824 	if (attr && attr_size) {
825 		res = ree_fs_write_primitive(*fh, pos, attr, NULL, attr_size);
826 		if (res)
827 			goto out;
828 		pos += attr_size;
829 	}
830 
831 	if ((data_core || data_user) && data_size) {
832 		res = ree_fs_write_primitive(*fh, pos, data_core, data_user,
833 					     data_size);
834 		if (res)
835 			goto out;
836 	}
837 
838 	fdp = (struct tee_fs_fd *)*fh;
839 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL);
840 	if (res)
841 		goto out;
842 
843 	res = set_name(dirh, fdp, po, overwrite);
844 out:
845 	if (res) {
846 		put_dirh(dirh, true);
847 		if (*fh) {
848 			ree_fs_close_primitive(*fh);
849 			*fh = NULL;
850 			tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
851 		}
852 	}
853 	mutex_unlock(&ree_fs_mutex);
854 
855 	return res;
856 }
857 
858 static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos,
859 			       const void *buf_core, const void *buf_user,
860 			       size_t len)
861 {
862 	TEE_Result res;
863 	struct tee_fs_dirfile_dirh *dirh = NULL;
864 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
865 
866 	/* One of buf_core and buf_user must be NULL */
867 	assert(!buf_core || !buf_user);
868 
869 	mutex_lock(&ree_fs_mutex);
870 
871 	res = get_dirh(&dirh);
872 	if (res)
873 		goto out;
874 
875 	res = ree_fs_write_primitive(fh, pos, buf_core, buf_user, len);
876 	if (res)
877 		goto out;
878 
879 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL);
880 	if (res)
881 		goto out;
882 
883 	res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
884 	if (res)
885 		goto out;
886 	res = commit_dirh_writes(dirh);
887 out:
888 	put_dirh(dirh, res);
889 	mutex_unlock(&ree_fs_mutex);
890 
891 	return res;
892 }
893 
894 static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new,
895 				bool overwrite)
896 {
897 	TEE_Result res;
898 	struct tee_fs_dirfile_dirh *dirh = NULL;
899 	struct tee_fs_dirfile_fileh dfh;
900 	struct tee_fs_dirfile_fileh remove_dfh = { .idx = -1 };
901 
902 	if (!new)
903 		return TEE_ERROR_BAD_PARAMETERS;
904 
905 	mutex_lock(&ree_fs_mutex);
906 	res = get_dirh(&dirh);
907 	if (res)
908 		goto out;
909 
910 	res = tee_fs_dirfile_find(dirh, &new->uuid, new->obj_id,
911 				  new->obj_id_len, &remove_dfh);
912 	if (!res && !overwrite) {
913 		res = TEE_ERROR_ACCESS_CONFLICT;
914 		goto out;
915 	}
916 
917 	res = tee_fs_dirfile_find(dirh, &old->uuid, old->obj_id,
918 				  old->obj_id_len, &dfh);
919 	if (res)
920 		goto out;
921 
922 	res = tee_fs_dirfile_rename(dirh, &new->uuid, &dfh, new->obj_id,
923 				    new->obj_id_len);
924 	if (res)
925 		goto out;
926 
927 	if (remove_dfh.idx != -1) {
928 		res = tee_fs_dirfile_remove(dirh, &remove_dfh);
929 		if (res)
930 			goto out;
931 	}
932 
933 	res = commit_dirh_writes(dirh);
934 	if (res)
935 		goto out;
936 
937 	if (remove_dfh.idx != -1)
938 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &remove_dfh);
939 
940 out:
941 	put_dirh(dirh, res);
942 	mutex_unlock(&ree_fs_mutex);
943 
944 	return res;
945 
946 }
947 
948 static TEE_Result ree_fs_remove(struct tee_pobj *po)
949 {
950 	TEE_Result res;
951 	struct tee_fs_dirfile_dirh *dirh = NULL;
952 	struct tee_fs_dirfile_fileh dfh;
953 
954 	mutex_lock(&ree_fs_mutex);
955 	res = get_dirh(&dirh);
956 	if (res)
957 		goto out;
958 
959 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
960 				  &dfh);
961 	if (res)
962 		goto out;
963 
964 	res = tee_fs_dirfile_remove(dirh, &dfh);
965 	if (res)
966 		goto out;
967 
968 	res = commit_dirh_writes(dirh);
969 	if (res)
970 		goto out;
971 
972 	tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
973 
974 	assert(tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
975 				   &dfh));
976 out:
977 	put_dirh(dirh, res);
978 	mutex_unlock(&ree_fs_mutex);
979 
980 	return res;
981 }
982 
983 static TEE_Result ree_fs_truncate(struct tee_file_handle *fh, size_t len)
984 {
985 	TEE_Result res;
986 	struct tee_fs_dirfile_dirh *dirh = NULL;
987 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
988 
989 	mutex_lock(&ree_fs_mutex);
990 
991 	res = get_dirh(&dirh);
992 	if (res)
993 		goto out;
994 
995 	res = ree_fs_ftruncate_internal(fdp, len);
996 	if (res)
997 		goto out;
998 
999 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL);
1000 	if (res)
1001 		goto out;
1002 
1003 	res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
1004 	if (res)
1005 		goto out;
1006 	res = commit_dirh_writes(dirh);
1007 out:
1008 	put_dirh(dirh, res);
1009 	mutex_unlock(&ree_fs_mutex);
1010 
1011 	return res;
1012 }
1013 
1014 static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid,
1015 				     struct tee_fs_dir **dir)
1016 
1017 {
1018 	TEE_Result res;
1019 	struct tee_fs_dir *d = calloc(1, sizeof(*d));
1020 
1021 	if (!d)
1022 		return TEE_ERROR_OUT_OF_MEMORY;
1023 
1024 	d->uuid = uuid;
1025 
1026 	mutex_lock(&ree_fs_mutex);
1027 
1028 	res = get_dirh(&d->dirh);
1029 	if (res)
1030 		goto out;
1031 
1032 	/* See that there's at least one file */
1033 	d->idx = -1;
1034 	d->d.oidlen = sizeof(d->d.oid);
1035 	res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
1036 				      &d->d.oidlen);
1037 	d->idx = -1;
1038 
1039 out:
1040 	if (!res) {
1041 		*dir = d;
1042 	} else {
1043 		if (d)
1044 			put_dirh(d->dirh, false);
1045 		free(d);
1046 	}
1047 	mutex_unlock(&ree_fs_mutex);
1048 
1049 	return res;
1050 }
1051 
1052 static void ree_fs_closedir_rpc(struct tee_fs_dir *d)
1053 {
1054 	if (d) {
1055 		mutex_lock(&ree_fs_mutex);
1056 
1057 		put_dirh(d->dirh, false);
1058 		free(d);
1059 
1060 		mutex_unlock(&ree_fs_mutex);
1061 	}
1062 }
1063 
1064 static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d,
1065 				     struct tee_fs_dirent **ent)
1066 {
1067 	TEE_Result res;
1068 
1069 	mutex_lock(&ree_fs_mutex);
1070 
1071 	d->d.oidlen = sizeof(d->d.oid);
1072 	res = tee_fs_dirfile_get_next(d->dirh, d->uuid, &d->idx, d->d.oid,
1073 				      &d->d.oidlen);
1074 	if (res == TEE_SUCCESS)
1075 		*ent = &d->d;
1076 
1077 	mutex_unlock(&ree_fs_mutex);
1078 
1079 	return res;
1080 }
1081 
1082 const struct tee_file_operations ree_fs_ops = {
1083 	.open = ree_fs_open,
1084 	.create = ree_fs_create,
1085 	.close = ree_fs_close,
1086 	.read = ree_fs_read,
1087 	.write = ree_fs_write,
1088 	.truncate = ree_fs_truncate,
1089 	.rename = ree_fs_rename,
1090 	.remove = ree_fs_remove,
1091 	.opendir = ree_fs_opendir_rpc,
1092 	.closedir = ree_fs_closedir_rpc,
1093 	.readdir = ree_fs_readdir_rpc,
1094 };
1095