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