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