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