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