xref: /optee_os/core/tee/tee_ree_fs.c (revision ea3a3b9e030c2f59bf872670de82df154e78f45b)
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 
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_core,const void * buf_user,size_t len)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 	assert(meta);
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 				goto exit;
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 
get_offs_size(enum tee_fs_htree_type type,size_t idx,uint8_t vers,size_t * offs,size_t * size)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 
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)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 
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)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 
ree_fs_ftruncate_internal(struct tee_fs_fd * fdp,tee_fs_off_t new_file_len)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 = NULL;
276 
277 	assert(fdp);
278 	meta = tee_fs_htree_get_meta(fdp->ht);
279 	if (!meta)
280 		return TEE_ERROR_CORRUPT_OBJECT;
281 
282 	if ((size_t)new_file_len > meta->length) {
283 		size_t ext_len = new_file_len - meta->length;
284 
285 		res = out_of_place_write(fdp, meta->length, NULL, NULL,
286 					 ext_len);
287 		if (res != TEE_SUCCESS)
288 			return res;
289 	} else {
290 		size_t offs;
291 		size_t sz;
292 
293 		res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK,
294 				    ROUNDUP_DIV(new_file_len, BLOCK_SIZE), 1,
295 				    &offs, &sz);
296 		if (res != TEE_SUCCESS)
297 			return res;
298 
299 		res = tee_fs_htree_truncate(&fdp->ht,
300 					    new_file_len / BLOCK_SIZE);
301 		if (res != TEE_SUCCESS)
302 			return res;
303 
304 		res = tee_fs_rpc_truncate(OPTEE_RPC_CMD_FS, fdp->fd,
305 					  offs + sz);
306 		if (res != TEE_SUCCESS)
307 			return res;
308 
309 		meta->length = new_file_len;
310 		tee_fs_htree_meta_set_dirty(fdp->ht);
311 	}
312 
313 	return TEE_SUCCESS;
314 }
315 
ree_fs_read_primitive(struct tee_file_handle * fh,size_t pos,void * buf_core,void * buf_user,size_t * len)316 static TEE_Result ree_fs_read_primitive(struct tee_file_handle *fh, size_t pos,
317 					void *buf_core, void *buf_user,
318 					size_t *len)
319 {
320 	TEE_Result res;
321 	int start_block_num;
322 	int end_block_num;
323 	size_t remain_bytes;
324 	uint8_t *data_core_ptr = buf_core;
325 	uint8_t *data_user_ptr = buf_user;
326 	uint8_t *block = NULL;
327 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
328 	struct tee_fs_htree_meta *meta = NULL;
329 
330 	/* One of buf_core and buf_user must be NULL */
331 	assert(!buf_core || !buf_user);
332 
333 	assert(fdp);
334 	meta = tee_fs_htree_get_meta(fdp->ht);
335 	if (!meta)
336 		return TEE_ERROR_CORRUPT_OBJECT;
337 
338 	remain_bytes = *len;
339 	if ((pos + remain_bytes) < remain_bytes || pos > meta->length)
340 		remain_bytes = 0;
341 	else if (pos + remain_bytes > meta->length)
342 		remain_bytes = meta->length - pos;
343 
344 	*len = remain_bytes;
345 
346 	if (!remain_bytes) {
347 		res = TEE_SUCCESS;
348 		goto exit;
349 	}
350 
351 	start_block_num = pos_to_block_num(pos);
352 	end_block_num = pos_to_block_num(pos + remain_bytes - 1);
353 
354 	block = get_tmp_block();
355 	if (!block) {
356 		res = TEE_ERROR_OUT_OF_MEMORY;
357 		goto exit;
358 	}
359 
360 	while (start_block_num <= end_block_num) {
361 		size_t offset = pos % BLOCK_SIZE;
362 		size_t size_to_read = MIN(remain_bytes, (size_t)BLOCK_SIZE);
363 
364 		if (size_to_read + offset > BLOCK_SIZE)
365 			size_to_read = BLOCK_SIZE - offset;
366 
367 		res = tee_fs_htree_read_block(&fdp->ht, start_block_num, block);
368 		if (res != TEE_SUCCESS)
369 			goto exit;
370 
371 		if (data_core_ptr) {
372 			memcpy(data_core_ptr, block + offset, size_to_read);
373 			data_core_ptr += size_to_read;
374 		} else if (data_user_ptr) {
375 			res = copy_to_user(data_user_ptr, block + offset,
376 					   size_to_read);
377 			if (res)
378 				goto exit;
379 			data_user_ptr += size_to_read;
380 		}
381 
382 		remain_bytes -= size_to_read;
383 		pos += size_to_read;
384 
385 		start_block_num++;
386 	}
387 	res = TEE_SUCCESS;
388 exit:
389 	if (block)
390 		put_tmp_block(block);
391 	return res;
392 }
393 
ree_fs_read(struct tee_file_handle * fh,size_t pos,void * buf_core,void * buf_user,size_t * len)394 static TEE_Result ree_fs_read(struct tee_file_handle *fh, size_t pos,
395 			      void *buf_core, void *buf_user, size_t *len)
396 {
397 	TEE_Result res;
398 
399 	mutex_lock(&ree_fs_mutex);
400 	res = ree_fs_read_primitive(fh, pos, buf_core, buf_user, len);
401 	mutex_unlock(&ree_fs_mutex);
402 
403 	return res;
404 }
405 
ree_fs_write_primitive(struct tee_file_handle * fh,size_t pos,const void * buf_core,const void * buf_user,size_t len)406 static TEE_Result ree_fs_write_primitive(struct tee_file_handle *fh, size_t pos,
407 					 const void *buf_core,
408 					 const void *buf_user, size_t len)
409 {
410 	TEE_Result res;
411 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
412 	struct tee_fs_htree_meta *meta = NULL;
413 	size_t file_size;
414 
415 	/* One of buf_core and buf_user must be NULL */
416 	assert(!buf_core || !buf_user);
417 
418 	if (!len)
419 		return TEE_SUCCESS;
420 
421 	assert(fdp);
422 	meta = tee_fs_htree_get_meta(fdp->ht);
423 	if (!meta)
424 		return TEE_ERROR_CORRUPT_OBJECT;
425 
426 	file_size = meta->length;
427 
428 	if ((pos + len) < len)
429 		return TEE_ERROR_BAD_PARAMETERS;
430 
431 	if (file_size < pos) {
432 		res = ree_fs_ftruncate_internal(fdp, pos);
433 		if (res != TEE_SUCCESS)
434 			return res;
435 	}
436 
437 	return out_of_place_write(fdp, pos, buf_core, buf_user, len);
438 }
439 
ree_fs_open_primitive(bool create,uint8_t * hash,uint32_t min_counter,const TEE_UUID * uuid,struct tee_fs_dirfile_fileh * dfh,struct tee_file_handle ** fh)440 static TEE_Result ree_fs_open_primitive(bool create, uint8_t *hash,
441 					uint32_t min_counter,
442 					const TEE_UUID *uuid,
443 					struct tee_fs_dirfile_fileh *dfh,
444 					struct tee_file_handle **fh)
445 {
446 	TEE_Result res;
447 	struct tee_fs_fd *fdp;
448 
449 	fdp = calloc(1, sizeof(struct tee_fs_fd));
450 	if (!fdp)
451 		return TEE_ERROR_OUT_OF_MEMORY;
452 	fdp->fd = -1;
453 	fdp->uuid = uuid;
454 
455 	if (create)
456 		res = tee_fs_rpc_create_dfh(OPTEE_RPC_CMD_FS,
457 					    dfh, &fdp->fd);
458 	else
459 		res = tee_fs_rpc_open_dfh(OPTEE_RPC_CMD_FS, dfh, &fdp->fd);
460 
461 	if (res != TEE_SUCCESS)
462 		goto out;
463 
464 	res = tee_fs_htree_open(create, hash, min_counter, uuid,
465 				&ree_fs_storage_ops, fdp, &fdp->ht);
466 out:
467 	if (res == TEE_SUCCESS) {
468 		if (dfh)
469 			fdp->dfh = *dfh;
470 		else
471 			fdp->dfh.idx = -1;
472 		*fh = (struct tee_file_handle *)fdp;
473 	} else {
474 		if (res == TEE_ERROR_SECURITY)
475 			DMSG("Secure storage corruption detected");
476 		if (fdp->fd != -1)
477 			tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd);
478 		/*
479 		 * Remove the file if hash is NULL and min_counter is 0,
480 		 * as it is not yet rollback-protected
481 		 */
482 		if (create || (!hash && !min_counter)) {
483 			DMSG("Remove corrupt file");
484 			tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, dfh);
485 		}
486 		free(fdp);
487 	}
488 
489 	return res;
490 }
491 
ree_fs_close_primitive(struct tee_file_handle * fh)492 static void ree_fs_close_primitive(struct tee_file_handle *fh)
493 {
494 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
495 
496 	if (fdp) {
497 		tee_fs_htree_close(&fdp->ht);
498 		tee_fs_rpc_close(OPTEE_RPC_CMD_FS, fdp->fd);
499 		free(fdp);
500 	}
501 }
502 
ree_dirf_commit_writes(struct tee_file_handle * fh,uint8_t * hash,uint32_t * counter)503 static TEE_Result ree_dirf_commit_writes(struct tee_file_handle *fh,
504 					 uint8_t *hash, uint32_t *counter)
505 {
506 	TEE_Result res;
507 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
508 
509 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, counter);
510 
511 	if (!res && hash)
512 		memcpy(hash, fdp->dfh.hash, sizeof(fdp->dfh.hash));
513 
514 	return res;
515 }
516 
dirf_read(struct tee_file_handle * fh,size_t pos,void * buf,size_t * len)517 static TEE_Result dirf_read(struct tee_file_handle *fh, size_t pos, void *buf,
518 			    size_t *len)
519 {
520 	return ree_fs_read_primitive(fh, pos, buf, NULL, len);
521 }
522 
dirf_write(struct tee_file_handle * fh,size_t pos,const void * buf,size_t len)523 static TEE_Result dirf_write(struct tee_file_handle *fh, size_t pos,
524 			     const void *buf, size_t len)
525 {
526 	return ree_fs_write_primitive(fh, pos, buf, NULL, len);
527 }
528 
529 static const struct tee_fs_dirfile_operations ree_dirf_ops = {
530 	.open = ree_fs_open_primitive,
531 	.close = ree_fs_close_primitive,
532 	.read = dirf_read,
533 	.write = dirf_write,
534 	.commit_writes = ree_dirf_commit_writes,
535 };
536 
537 /*
538  * ree_fs_dirh is caching the dirfile handle to avoid frequent opening and
539  * closing of that handle. When ree_fs_dirh_refcount reaches 0, ree_fs_dirh
540  * will be freed. However, ree_fs_dirh_refcount > 0 is not a guarantee that
541  * ree_fs_dirh will not be freed, it may very well be freed earlier in an
542  * error path. get_dirh() must be used to get the ree_fs_dirh pointer each
543  * time it's needed if ree_fs_mutex has been unlocked in between.
544  */
545 static struct tee_fs_dirfile_dirh *ree_fs_dirh;
546 static size_t ree_fs_dirh_refcount;
547 
548 #ifdef CFG_REE_FS_INTEGRITY_RPMB
549 static struct tee_file_handle *ree_fs_rpmb_fh;
550 
open_dirh(struct tee_fs_dirfile_dirh ** dirh)551 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
552 {
553 	TEE_Result res;
554 	uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
555 	uint8_t *hashp = NULL;
556 	const char fname[] = "dirfile.db.hash";
557 
558 	res = tee_rpmb_fs_raw_open(fname, false, &ree_fs_rpmb_fh);
559 	if (!res) {
560 		size_t l = sizeof(hash);
561 
562 		res = rpmb_fs_ops.read(ree_fs_rpmb_fh, 0, hash, NULL, &l);
563 		if (res)
564 			return res;
565 		if (l == sizeof(hash))
566 			hashp = hash;
567 	} else if (res == TEE_ERROR_ITEM_NOT_FOUND) {
568 		res = tee_rpmb_fs_raw_open(fname, true, &ree_fs_rpmb_fh);
569 	}
570 	if (res)
571 		return res;
572 
573 	res = tee_fs_dirfile_open(false, hashp, 0, &ree_dirf_ops, dirh);
574 
575 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
576 		if (hashp) {
577 			if (IS_ENABLED(CFG_REE_FS_ALLOW_RESET)) {
578 				DMSG("dirf.db not found, clear hash in RPMB");
579 				res = rpmb_fs_ops.truncate(ree_fs_rpmb_fh, 0);
580 				if (res) {
581 					DMSG("Can't clear hash: %#"PRIx32, res);
582 					res = TEE_ERROR_SECURITY;
583 					goto out;
584 				}
585 			} else {
586 				DMSG("dirf.db file not found");
587 				res = TEE_ERROR_SECURITY;
588 				goto out;
589 			}
590 		}
591 
592 		DMSG("Create dirf.db");
593 		res = tee_fs_dirfile_open(true, NULL, 0, &ree_dirf_ops, dirh);
594 	}
595 
596 out:
597 	if (res)
598 		rpmb_fs_ops.close(&ree_fs_rpmb_fh);
599 
600 	return res;
601 }
602 
commit_dirh_writes(struct tee_fs_dirfile_dirh * dirh)603 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
604 {
605 	TEE_Result res;
606 	uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
607 
608 	res = tee_fs_dirfile_commit_writes(dirh, hash, NULL);
609 	if (res)
610 		return res;
611 	return rpmb_fs_ops.write(ree_fs_rpmb_fh, 0, hash, NULL, sizeof(hash));
612 }
613 
close_dirh(struct tee_fs_dirfile_dirh ** dirh)614 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
615 {
616 	tee_fs_dirfile_close(*dirh);
617 	*dirh = NULL;
618 	rpmb_fs_ops.close(&ree_fs_rpmb_fh);
619 }
620 
621 #else /*!CFG_REE_FS_INTEGRITY_RPMB*/
open_dirh(struct tee_fs_dirfile_dirh ** dirh)622 static TEE_Result open_dirh(struct tee_fs_dirfile_dirh **dirh)
623 {
624 	TEE_Result res = TEE_SUCCESS;
625 	uint32_t min_counter = 0;
626 
627 	res = nv_counter_get_ree_fs(&min_counter);
628 	if (res) {
629 		static bool once;
630 
631 		if (res != TEE_ERROR_NOT_IMPLEMENTED ||
632 		    !IS_ENABLED(CFG_INSECURE))
633 			return res;
634 
635 		if (!once) {
636 			IMSG("WARNING (insecure configuration): Failed to get monotonic counter for REE FS, using 0");
637 			once = true;
638 		}
639 		min_counter = 0;
640 	}
641 	res = tee_fs_dirfile_open(false, NULL, min_counter, &ree_dirf_ops,
642 				  dirh);
643 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
644 		if (min_counter) {
645 			if (!IS_ENABLED(CFG_REE_FS_ALLOW_RESET)) {
646 				DMSG("dirf.db file not found");
647 				return TEE_ERROR_SECURITY;
648 			}
649 			DMSG("dirf.db not found, initializing with a non-zero monotonic counter");
650 		}
651 		return tee_fs_dirfile_open(true, NULL, min_counter,
652 					   &ree_dirf_ops, dirh);
653 	}
654 
655 	return res;
656 }
657 
commit_dirh_writes(struct tee_fs_dirfile_dirh * dirh)658 static TEE_Result commit_dirh_writes(struct tee_fs_dirfile_dirh *dirh)
659 {
660 	TEE_Result res = TEE_SUCCESS;
661 	uint32_t counter = 0;
662 
663 	res = tee_fs_dirfile_commit_writes(dirh, NULL, &counter);
664 	if (res)
665 		return res;
666 	res = nv_counter_incr_ree_fs_to(counter);
667 	if (res == TEE_ERROR_NOT_IMPLEMENTED && IS_ENABLED(CFG_INSECURE)) {
668 		static bool once;
669 
670 		if (!once) {
671 			IMSG("WARNING (insecure configuration): Failed to commit dirh counter %"PRIu32, counter);
672 			once = true;
673 		}
674 		return TEE_SUCCESS;
675 	}
676 	return res;
677 }
678 
close_dirh(struct tee_fs_dirfile_dirh ** dirh)679 static void close_dirh(struct tee_fs_dirfile_dirh **dirh)
680 {
681 	tee_fs_dirfile_close(*dirh);
682 	*dirh = NULL;
683 }
684 #endif /*!CFG_REE_FS_INTEGRITY_RPMB*/
685 
get_dirh(struct tee_fs_dirfile_dirh ** dirh)686 static TEE_Result get_dirh(struct tee_fs_dirfile_dirh **dirh)
687 {
688 	if (!ree_fs_dirh) {
689 		TEE_Result res = open_dirh(&ree_fs_dirh);
690 
691 		if (res) {
692 			*dirh = NULL;
693 			return res;
694 		}
695 	}
696 	ree_fs_dirh_refcount++;
697 	assert(ree_fs_dirh);
698 	assert(ree_fs_dirh_refcount);
699 	*dirh = ree_fs_dirh;
700 	return TEE_SUCCESS;
701 }
702 
put_dirh_primitive(bool close)703 static void put_dirh_primitive(bool close)
704 {
705 	assert(ree_fs_dirh_refcount);
706 
707 	/*
708 	 * During the execution of one of the ree_fs_ops ree_fs_dirh is
709 	 * guareteed to be a valid pointer. But when the fop has returned
710 	 * another thread may get an error or something causing that fop
711 	 * to do a put with close=1.
712 	 *
713 	 * For all fops but ree_fs_close() there's a call to get_dirh() to
714 	 * get a new dirh which will open it again if it was closed before.
715 	 * But in the ree_fs_close() case there's no call to get_dirh()
716 	 * only to this function, put_dirh_primitive(), and in this case
717 	 * ree_fs_dirh may actually be NULL.
718 	 */
719 	ree_fs_dirh_refcount--;
720 	if (ree_fs_dirh && (!ree_fs_dirh_refcount || close))
721 		close_dirh(&ree_fs_dirh);
722 }
723 
put_dirh(struct tee_fs_dirfile_dirh * dirh,bool close)724 static void put_dirh(struct tee_fs_dirfile_dirh *dirh, bool close)
725 {
726 	if (dirh) {
727 		assert(dirh == ree_fs_dirh);
728 		put_dirh_primitive(close);
729 	}
730 }
731 
ree_fs_open(struct tee_pobj * po,size_t * size,struct tee_file_handle ** fh)732 static TEE_Result ree_fs_open(struct tee_pobj *po, size_t *size,
733 			      struct tee_file_handle **fh)
734 {
735 	TEE_Result res;
736 	struct tee_fs_dirfile_dirh *dirh = NULL;
737 	struct tee_fs_dirfile_fileh dfh;
738 
739 	mutex_lock(&ree_fs_mutex);
740 
741 	res = get_dirh(&dirh);
742 	if (res != TEE_SUCCESS)
743 		goto out;
744 
745 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
746 				  &dfh);
747 	if (res != TEE_SUCCESS)
748 		goto out;
749 
750 	res = ree_fs_open_primitive(false, dfh.hash, 0, &po->uuid, &dfh, fh);
751 	if (res == TEE_ERROR_ITEM_NOT_FOUND) {
752 		/*
753 		 * If the object isn't found someone has tampered with it,
754 		 * treat it as corrupt.
755 		 */
756 		res = TEE_ERROR_CORRUPT_OBJECT;
757 	} else if (!res && size) {
758 		struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh;
759 
760 		assert(fdp->ht);
761 		*size = tee_fs_htree_get_meta(fdp->ht)->length;
762 	}
763 
764 out:
765 	if (res)
766 		put_dirh(dirh, true);
767 	mutex_unlock(&ree_fs_mutex);
768 
769 	return res;
770 }
771 
set_name(struct tee_fs_dirfile_dirh * dirh,struct tee_fs_fd * fdp,struct tee_pobj * po,bool overwrite)772 static TEE_Result set_name(struct tee_fs_dirfile_dirh *dirh,
773 			   struct tee_fs_fd *fdp, struct tee_pobj *po,
774 			   bool overwrite)
775 {
776 	TEE_Result res;
777 	bool have_old_dfh = false;
778 	struct tee_fs_dirfile_fileh old_dfh = { .idx = -1 };
779 
780 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
781 				  &old_dfh);
782 	if (!overwrite && !res)
783 		return TEE_ERROR_ACCESS_CONFLICT;
784 
785 	if (!res)
786 		have_old_dfh = true;
787 
788 	/*
789 	 * If old_dfh wasn't found, the idx will be -1 and
790 	 * tee_fs_dirfile_rename() will allocate a new index.
791 	 */
792 	fdp->dfh.idx = old_dfh.idx;
793 	old_dfh.idx = -1;
794 	res = tee_fs_dirfile_rename(dirh, &po->uuid, &fdp->dfh,
795 				    po->obj_id, po->obj_id_len);
796 	if (res)
797 		return res;
798 
799 	res = commit_dirh_writes(dirh);
800 	if (res)
801 		return res;
802 
803 	if (have_old_dfh)
804 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &old_dfh);
805 
806 	return TEE_SUCCESS;
807 }
808 
ree_fs_close(struct tee_file_handle ** fh)809 static void ree_fs_close(struct tee_file_handle **fh)
810 {
811 	if (*fh) {
812 		mutex_lock(&ree_fs_mutex);
813 		put_dirh_primitive(false);
814 		ree_fs_close_primitive(*fh);
815 		*fh = NULL;
816 		mutex_unlock(&ree_fs_mutex);
817 
818 	}
819 }
820 
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_core,const void * data_user,size_t data_size,struct tee_file_handle ** fh)821 static TEE_Result ree_fs_create(struct tee_pobj *po, bool overwrite,
822 				const void *head, size_t head_size,
823 				const void *attr, size_t attr_size,
824 				const void *data_core, const void *data_user,
825 				size_t data_size, struct tee_file_handle **fh)
826 {
827 	struct tee_fs_fd *fdp;
828 	struct tee_fs_dirfile_dirh *dirh = NULL;
829 	struct tee_fs_dirfile_fileh dfh;
830 	TEE_Result res;
831 	size_t pos = 0;
832 
833 	/* One of data_core and data_user must be NULL */
834 	assert(!data_core || !data_user);
835 
836 	*fh = NULL;
837 	mutex_lock(&ree_fs_mutex);
838 
839 	res = get_dirh(&dirh);
840 	if (res)
841 		goto out;
842 
843 	res = tee_fs_dirfile_get_tmp(dirh, &dfh);
844 	if (res)
845 		goto out;
846 
847 	res = ree_fs_open_primitive(true, dfh.hash, 0, &po->uuid, &dfh, fh);
848 	if (res)
849 		goto out;
850 
851 	if (head && head_size) {
852 		res = ree_fs_write_primitive(*fh, pos, head, NULL, head_size);
853 		if (res)
854 			goto out;
855 		pos += head_size;
856 	}
857 
858 	if (attr && attr_size) {
859 		res = ree_fs_write_primitive(*fh, pos, attr, NULL, attr_size);
860 		if (res)
861 			goto out;
862 		pos += attr_size;
863 	}
864 
865 	if ((data_core || data_user) && data_size) {
866 		res = ree_fs_write_primitive(*fh, pos, data_core, data_user,
867 					     data_size);
868 		if (res)
869 			goto out;
870 	}
871 
872 	fdp = (struct tee_fs_fd *)*fh;
873 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL);
874 	if (res)
875 		goto out;
876 
877 	res = set_name(dirh, fdp, po, overwrite);
878 out:
879 	if (res) {
880 		put_dirh(dirh, true);
881 		if (*fh) {
882 			ree_fs_close_primitive(*fh);
883 			*fh = NULL;
884 			tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
885 		}
886 	}
887 	mutex_unlock(&ree_fs_mutex);
888 
889 	return res;
890 }
891 
ree_fs_write(struct tee_file_handle * fh,size_t pos,const void * buf_core,const void * buf_user,size_t len)892 static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos,
893 			       const void *buf_core, const void *buf_user,
894 			       size_t len)
895 {
896 	TEE_Result res;
897 	struct tee_fs_dirfile_dirh *dirh = NULL;
898 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
899 
900 	/* One of buf_core and buf_user must be NULL */
901 	assert(!buf_core || !buf_user);
902 
903 	mutex_lock(&ree_fs_mutex);
904 
905 	res = get_dirh(&dirh);
906 	if (res)
907 		goto out;
908 
909 	res = ree_fs_write_primitive(fh, pos, buf_core, buf_user, len);
910 	if (res)
911 		goto out;
912 
913 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL);
914 	if (res)
915 		goto out;
916 
917 	res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
918 	if (res)
919 		goto out;
920 	res = commit_dirh_writes(dirh);
921 out:
922 	put_dirh(dirh, res);
923 	mutex_unlock(&ree_fs_mutex);
924 
925 	return res;
926 }
927 
ree_fs_rename(struct tee_pobj * old,struct tee_pobj * new,bool overwrite)928 static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new,
929 				bool overwrite)
930 {
931 	TEE_Result res;
932 	struct tee_fs_dirfile_dirh *dirh = NULL;
933 	struct tee_fs_dirfile_fileh dfh;
934 	struct tee_fs_dirfile_fileh remove_dfh = { .idx = -1 };
935 
936 	if (!new)
937 		return TEE_ERROR_BAD_PARAMETERS;
938 
939 	mutex_lock(&ree_fs_mutex);
940 	res = get_dirh(&dirh);
941 	if (res)
942 		goto out;
943 
944 	res = tee_fs_dirfile_find(dirh, &new->uuid, new->obj_id,
945 				  new->obj_id_len, &remove_dfh);
946 	if (!res && !overwrite) {
947 		res = TEE_ERROR_ACCESS_CONFLICT;
948 		goto out;
949 	}
950 
951 	res = tee_fs_dirfile_find(dirh, &old->uuid, old->obj_id,
952 				  old->obj_id_len, &dfh);
953 	if (res)
954 		goto out;
955 
956 	res = tee_fs_dirfile_rename(dirh, &new->uuid, &dfh, new->obj_id,
957 				    new->obj_id_len);
958 	if (res)
959 		goto out;
960 
961 	if (remove_dfh.idx != -1) {
962 		res = tee_fs_dirfile_remove(dirh, &remove_dfh);
963 		if (res)
964 			goto out;
965 	}
966 
967 	res = commit_dirh_writes(dirh);
968 	if (res)
969 		goto out;
970 
971 	if (remove_dfh.idx != -1)
972 		tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &remove_dfh);
973 
974 out:
975 	put_dirh(dirh, res);
976 	mutex_unlock(&ree_fs_mutex);
977 
978 	return res;
979 
980 }
981 
ree_fs_remove(struct tee_pobj * po)982 static TEE_Result ree_fs_remove(struct tee_pobj *po)
983 {
984 	TEE_Result res;
985 	struct tee_fs_dirfile_dirh *dirh = NULL;
986 	struct tee_fs_dirfile_fileh dfh;
987 
988 	mutex_lock(&ree_fs_mutex);
989 	res = get_dirh(&dirh);
990 	if (res)
991 		goto out;
992 
993 	res = tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
994 				  &dfh);
995 	if (res)
996 		goto out;
997 
998 	res = tee_fs_dirfile_remove(dirh, &dfh);
999 	if (res)
1000 		goto out;
1001 
1002 	res = commit_dirh_writes(dirh);
1003 	if (res)
1004 		goto out;
1005 
1006 	tee_fs_rpc_remove_dfh(OPTEE_RPC_CMD_FS, &dfh);
1007 
1008 	assert(tee_fs_dirfile_find(dirh, &po->uuid, po->obj_id, po->obj_id_len,
1009 				   &dfh));
1010 out:
1011 	put_dirh(dirh, res);
1012 	mutex_unlock(&ree_fs_mutex);
1013 
1014 	return res;
1015 }
1016 
ree_fs_truncate(struct tee_file_handle * fh,size_t len)1017 static TEE_Result ree_fs_truncate(struct tee_file_handle *fh, size_t len)
1018 {
1019 	TEE_Result res;
1020 	struct tee_fs_dirfile_dirh *dirh = NULL;
1021 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
1022 
1023 	mutex_lock(&ree_fs_mutex);
1024 
1025 	res = get_dirh(&dirh);
1026 	if (res)
1027 		goto out;
1028 
1029 	res = ree_fs_ftruncate_internal(fdp, len);
1030 	if (res)
1031 		goto out;
1032 
1033 	res = tee_fs_htree_sync_to_storage(&fdp->ht, fdp->dfh.hash, NULL);
1034 	if (res)
1035 		goto out;
1036 
1037 	res = tee_fs_dirfile_update_hash(dirh, &fdp->dfh);
1038 	if (res)
1039 		goto out;
1040 	res = commit_dirh_writes(dirh);
1041 out:
1042 	put_dirh(dirh, res);
1043 	mutex_unlock(&ree_fs_mutex);
1044 
1045 	return res;
1046 }
1047 
ree_fs_opendir_rpc(const TEE_UUID * uuid,struct tee_fs_dir ** dir)1048 static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid,
1049 				     struct tee_fs_dir **dir)
1050 
1051 {
1052 	TEE_Result res = TEE_SUCCESS;
1053 	struct tee_fs_dirfile_dirh *dirh = NULL;
1054 	struct tee_fs_dir *d = calloc(1, sizeof(*d));
1055 
1056 	if (!d)
1057 		return TEE_ERROR_OUT_OF_MEMORY;
1058 
1059 	d->uuid = uuid;
1060 
1061 	mutex_lock(&ree_fs_mutex);
1062 
1063 	res = get_dirh(&dirh);
1064 	if (res)
1065 		goto out;
1066 
1067 	/* See that there's at least one file */
1068 	d->idx = -1;
1069 	d->d.oidlen = sizeof(d->d.oid);
1070 	res = tee_fs_dirfile_get_next(dirh, d->uuid, &d->idx, d->d.oid,
1071 				      &d->d.oidlen);
1072 	d->idx = -1;
1073 
1074 out:
1075 	if (!res) {
1076 		*dir = d;
1077 	} else {
1078 		if (d)
1079 			put_dirh(dirh, false);
1080 		free(d);
1081 	}
1082 	mutex_unlock(&ree_fs_mutex);
1083 
1084 	return res;
1085 }
1086 
ree_fs_closedir_rpc(struct tee_fs_dir * d)1087 static void ree_fs_closedir_rpc(struct tee_fs_dir *d)
1088 {
1089 	if (d) {
1090 		mutex_lock(&ree_fs_mutex);
1091 
1092 		put_dirh(ree_fs_dirh, false);
1093 		free(d);
1094 
1095 		mutex_unlock(&ree_fs_mutex);
1096 	}
1097 }
1098 
ree_fs_readdir_rpc(struct tee_fs_dir * d,struct tee_fs_dirent ** ent)1099 static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d,
1100 				     struct tee_fs_dirent **ent)
1101 {
1102 	struct tee_fs_dirfile_dirh *dirh = NULL;
1103 	TEE_Result res = TEE_SUCCESS;
1104 
1105 	mutex_lock(&ree_fs_mutex);
1106 
1107 	res = get_dirh(&dirh);
1108 	if (res)
1109 		goto out;
1110 
1111 	d->d.oidlen = sizeof(d->d.oid);
1112 	res = tee_fs_dirfile_get_next(dirh, d->uuid, &d->idx, d->d.oid,
1113 				      &d->d.oidlen);
1114 	if (res == TEE_SUCCESS)
1115 		*ent = &d->d;
1116 
1117 	put_dirh(dirh, res);
1118 out:
1119 	mutex_unlock(&ree_fs_mutex);
1120 
1121 	return res;
1122 }
1123 
1124 const struct tee_file_operations ree_fs_ops = {
1125 	.open = ree_fs_open,
1126 	.create = ree_fs_create,
1127 	.close = ree_fs_close,
1128 	.read = ree_fs_read,
1129 	.write = ree_fs_write,
1130 	.truncate = ree_fs_truncate,
1131 	.rename = ree_fs_rename,
1132 	.remove = ree_fs_remove,
1133 	.opendir = ree_fs_opendir_rpc,
1134 	.closedir = ree_fs_closedir_rpc,
1135 	.readdir = ree_fs_readdir_rpc,
1136 };
1137