xref: /optee_os/core/tee/tee_ree_fs.c (revision 756aea59d99741416e83f788d9788194c13422d2)
1 /*
2  * Copyright (c) 2015, Linaro Limited
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <assert.h>
29 #include <kernel/mutex.h>
30 #include <kernel/panic.h>
31 #include <kernel/thread.h>
32 #include <mm/core_memprot.h>
33 #include <optee_msg_supplicant.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string_ext.h>
37 #include <string.h>
38 #include <sys/queue.h>
39 #include <tee/fs_htree.h>
40 #include <tee/tee_cryp_provider.h>
41 #include <tee/tee_fs.h>
42 #include <tee/tee_fs_rpc.h>
43 #include <trace.h>
44 #include <utee_defines.h>
45 #include <util.h>
46 
47 #define BLOCK_SHIFT	12
48 
49 #define BLOCK_SIZE	(1 << BLOCK_SHIFT)
50 
51 struct tee_fs_fd {
52 	struct tee_fs_htree *ht;
53 	int fd;
54 };
55 
56 static int pos_to_block_num(int position)
57 {
58 	return position >> BLOCK_SHIFT;
59 }
60 
61 static struct mutex ree_fs_mutex = MUTEX_INITIALIZER;
62 
63 static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid,
64 				     struct tee_fs_dir **d)
65 
66 {
67 	return tee_fs_rpc_opendir(OPTEE_MSG_RPC_CMD_FS, uuid, d);
68 }
69 
70 static void ree_fs_closedir_rpc(struct tee_fs_dir *d)
71 {
72 	if (d)
73 		tee_fs_rpc_closedir(OPTEE_MSG_RPC_CMD_FS, d);
74 }
75 
76 static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d,
77 				     struct tee_fs_dirent **ent)
78 {
79 	return tee_fs_rpc_readdir(OPTEE_MSG_RPC_CMD_FS, d, ent);
80 }
81 
82 static TEE_Result out_of_place_write(struct tee_fs_fd *fdp, size_t pos,
83 				     const void *buf, size_t len)
84 {
85 	TEE_Result res;
86 	size_t start_block_num = pos_to_block_num(pos);
87 	size_t end_block_num = pos_to_block_num(pos + len - 1);
88 	size_t remain_bytes = len;
89 	uint8_t *data_ptr = (uint8_t *)buf;
90 	uint8_t *block;
91 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
92 
93 	block = malloc(BLOCK_SIZE);
94 	if (!block)
95 		return TEE_ERROR_OUT_OF_MEMORY;
96 
97 	while (start_block_num <= end_block_num) {
98 		size_t offset = pos % BLOCK_SIZE;
99 		size_t size_to_write = MIN(remain_bytes, (size_t)BLOCK_SIZE);
100 
101 		if (size_to_write + offset > BLOCK_SIZE)
102 			size_to_write = BLOCK_SIZE - offset;
103 
104 		if (start_block_num * BLOCK_SIZE <
105 		    ROUNDUP(meta->length, BLOCK_SIZE)) {
106 			res = tee_fs_htree_read_block(&fdp->ht,
107 						      start_block_num, block);
108 			if (res != TEE_SUCCESS)
109 				goto exit;
110 		} else {
111 			memset(block, 0, BLOCK_SIZE);
112 		}
113 
114 		if (data_ptr)
115 			memcpy(block + offset, data_ptr, size_to_write);
116 		else
117 			memset(block + offset, 0, size_to_write);
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_ptr)
125 			data_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 
134 exit:
135 	free(block);
136 	return res;
137 }
138 
139 static TEE_Result get_offs_size(enum tee_fs_htree_type type, size_t idx,
140 				uint8_t vers, size_t *offs, size_t *size)
141 {
142 	const size_t node_size = sizeof(struct tee_fs_htree_node_image);
143 	const size_t block_nodes = BLOCK_SIZE / (node_size * 2);
144 	size_t pbn;
145 	size_t bidx;
146 
147 	assert(vers == 0 || vers == 1);
148 
149 	/*
150 	 * File layout
151 	 *
152 	 * phys block 0:
153 	 * tee_fs_htree_image vers 0 @ offs = 0
154 	 * tee_fs_htree_image vers 1 @ offs = sizeof(tee_fs_htree_image)
155 	 *
156 	 * phys block 1:
157 	 * tee_fs_htree_node_image 0  vers 0 @ offs = 0
158 	 * tee_fs_htree_node_image 0  vers 1 @ offs = node_size
159 	 * tee_fs_htree_node_image 1  vers 0 @ offs = node_size * 2
160 	 * tee_fs_htree_node_image 1  vers 1 @ offs = node_size * 3
161 	 * ...
162 	 * tee_fs_htree_node_image 61 vers 0 @ offs = node_size * 122
163 	 * tee_fs_htree_node_image 61 vers 1 @ offs = node_size * 123
164 	 *
165 	 * phys block 2:
166 	 * data block 0 vers 0
167 	 *
168 	 * phys block 3:
169 	 * data block 0 vers 1
170 	 *
171 	 * ...
172 	 * phys block 63:
173 	 * data block 61 vers 0
174 	 *
175 	 * phys block 64:
176 	 * data block 61 vers 1
177 	 *
178 	 * phys block 65:
179 	 * tee_fs_htree_node_image 62  vers 0 @ offs = 0
180 	 * tee_fs_htree_node_image 62  vers 1 @ offs = node_size
181 	 * tee_fs_htree_node_image 63  vers 0 @ offs = node_size * 2
182 	 * tee_fs_htree_node_image 63  vers 1 @ offs = node_size * 3
183 	 * ...
184 	 * tee_fs_htree_node_image 121 vers 0 @ offs = node_size * 122
185 	 * tee_fs_htree_node_image 121 vers 1 @ offs = node_size * 123
186 	 *
187 	 * ...
188 	 */
189 
190 	switch (type) {
191 	case TEE_FS_HTREE_TYPE_HEAD:
192 		*offs = sizeof(struct tee_fs_htree_image) * vers;
193 		*size = sizeof(struct tee_fs_htree_image);
194 		return TEE_SUCCESS;
195 	case TEE_FS_HTREE_TYPE_NODE:
196 		pbn = 1 + ((idx / block_nodes) * block_nodes * 2);
197 		*offs = pbn * BLOCK_SIZE +
198 			2 * node_size * (idx % block_nodes) +
199 			node_size * vers;
200 		*size = node_size;
201 		return TEE_SUCCESS;
202 	case TEE_FS_HTREE_TYPE_BLOCK:
203 		bidx = 2 * idx + vers;
204 		pbn = 2 + bidx + bidx / (block_nodes * 2 - 1);
205 		*offs = pbn * BLOCK_SIZE;
206 		*size = BLOCK_SIZE;
207 		return TEE_SUCCESS;
208 	default:
209 		return TEE_ERROR_GENERIC;
210 	}
211 }
212 
213 static TEE_Result ree_fs_rpc_read_init(void *aux,
214 				       struct tee_fs_rpc_operation *op,
215 				       enum tee_fs_htree_type type, size_t idx,
216 				       uint8_t vers, void **data)
217 {
218 	struct tee_fs_fd *fdp = aux;
219 	TEE_Result res;
220 	size_t offs;
221 	size_t size;
222 
223 	res = get_offs_size(type, idx, vers, &offs, &size);
224 	if (res != TEE_SUCCESS)
225 		return res;
226 
227 	return tee_fs_rpc_read_init(op, OPTEE_MSG_RPC_CMD_FS, fdp->fd,
228 				    offs, size, data);
229 }
230 
231 static TEE_Result ree_fs_rpc_write_init(void *aux,
232 					struct tee_fs_rpc_operation *op,
233 					enum tee_fs_htree_type type, size_t idx,
234 					uint8_t vers, void **data)
235 {
236 	struct tee_fs_fd *fdp = aux;
237 	TEE_Result res;
238 	size_t offs;
239 	size_t size;
240 
241 	res = get_offs_size(type, idx, vers, &offs, &size);
242 	if (res != TEE_SUCCESS)
243 		return res;
244 
245 	return tee_fs_rpc_write_init(op, OPTEE_MSG_RPC_CMD_FS, fdp->fd,
246 				     offs, size, data);
247 }
248 
249 static const struct tee_fs_htree_storage ree_fs_storage_ops = {
250 	.block_size = BLOCK_SIZE,
251 	.rpc_read_init = ree_fs_rpc_read_init,
252 	.rpc_write_init = ree_fs_rpc_write_init,
253 };
254 
255 static TEE_Result open_internal(struct tee_pobj *po, bool create,
256 				struct tee_file_handle **fh)
257 {
258 	TEE_Result res;
259 	struct tee_fs_fd *fdp = NULL;
260 
261 	fdp = calloc(1, sizeof(struct tee_fs_fd));
262 	if (!fdp)
263 		return TEE_ERROR_OUT_OF_MEMORY;
264 	fdp->fd = -1;
265 
266 	mutex_lock(&ree_fs_mutex);
267 
268 	if (create)
269 		res = tee_fs_rpc_create(OPTEE_MSG_RPC_CMD_FS, po, &fdp->fd);
270 	else
271 		res = tee_fs_rpc_open(OPTEE_MSG_RPC_CMD_FS, po, &fdp->fd);
272 
273 	if (res != TEE_SUCCESS)
274 		goto out;
275 
276 	res = tee_fs_htree_open(create, &ree_fs_storage_ops, fdp, &fdp->ht);
277 out:
278 	if (res == TEE_SUCCESS) {
279 		*fh = (struct tee_file_handle *)fdp;
280 	} else {
281 		if (fdp->fd != -1)
282 			tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_FS, fdp->fd);
283 		if (create)
284 			tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_FS, po);
285 		free(fdp);
286 	}
287 
288 	mutex_unlock(&ree_fs_mutex);
289 	return res;
290 }
291 
292 static TEE_Result ree_fs_open(struct tee_pobj *po, struct tee_file_handle **fh)
293 {
294 	return open_internal(po, false, fh);
295 }
296 
297 static TEE_Result ree_fs_create(struct tee_pobj *po,
298 				struct tee_file_handle **fh)
299 {
300 	return open_internal(po, true, fh);
301 }
302 
303 static void ree_fs_close(struct tee_file_handle **fh)
304 {
305 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh;
306 
307 	if (fdp) {
308 		tee_fs_htree_close(&fdp->ht);
309 		tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_FS, fdp->fd);
310 		free(fdp);
311 		*fh = NULL;
312 	}
313 }
314 
315 static TEE_Result ree_fs_ftruncate_internal(struct tee_fs_fd *fdp,
316 					    tee_fs_off_t new_file_len)
317 {
318 	TEE_Result res;
319 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
320 
321 	if ((size_t)new_file_len > meta->length) {
322 		size_t ext_len = new_file_len - meta->length;
323 
324 		res = out_of_place_write(fdp, meta->length, NULL, ext_len);
325 		if (res != TEE_SUCCESS)
326 			return res;
327 	} else {
328 		size_t offs;
329 		size_t sz;
330 
331 		res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK,
332 				    ROUNDUP(new_file_len, BLOCK_SIZE) /
333 					BLOCK_SIZE, 1, &offs, &sz);
334 		if (res != TEE_SUCCESS)
335 			return res;
336 
337 		res = tee_fs_htree_truncate(&fdp->ht,
338 					    new_file_len / BLOCK_SIZE);
339 		if (res != TEE_SUCCESS)
340 			return res;
341 
342 		res = tee_fs_rpc_truncate(OPTEE_MSG_RPC_CMD_FS, fdp->fd,
343 					  offs + sz);
344 		if (res != TEE_SUCCESS)
345 			return res;
346 
347 		meta->length = new_file_len;
348 	}
349 
350 	return tee_fs_htree_sync_to_storage(&fdp->ht);
351 }
352 
353 static TEE_Result ree_fs_read(struct tee_file_handle *fh, size_t pos,
354 			      void *buf, size_t *len)
355 {
356 	TEE_Result res;
357 	int start_block_num;
358 	int end_block_num;
359 	size_t remain_bytes;
360 	uint8_t *data_ptr = buf;
361 	uint8_t *block = NULL;
362 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
363 	struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht);
364 
365 	mutex_lock(&ree_fs_mutex);
366 
367 	remain_bytes = *len;
368 	if ((pos + remain_bytes) < remain_bytes || pos > meta->length)
369 		remain_bytes = 0;
370 	else if (pos + remain_bytes > meta->length)
371 		remain_bytes = meta->length - pos;
372 
373 	*len = remain_bytes;
374 
375 	if (!remain_bytes) {
376 		res = TEE_SUCCESS;
377 		goto exit;
378 	}
379 
380 	start_block_num = pos_to_block_num(pos);
381 	end_block_num = pos_to_block_num(pos + remain_bytes - 1);
382 
383 	block = malloc(BLOCK_SIZE);
384 	if (!block) {
385 		res = TEE_ERROR_OUT_OF_MEMORY;
386 		goto exit;
387 	}
388 
389 	while (start_block_num <= end_block_num) {
390 		size_t offset = pos % BLOCK_SIZE;
391 		size_t size_to_read = MIN(remain_bytes, (size_t)BLOCK_SIZE);
392 
393 		if (size_to_read + offset > BLOCK_SIZE)
394 			size_to_read = BLOCK_SIZE - offset;
395 
396 		res = tee_fs_htree_read_block(&fdp->ht, start_block_num, block);
397 		if (res != TEE_SUCCESS)
398 			goto exit;
399 
400 		memcpy(data_ptr, block + offset, size_to_read);
401 
402 		data_ptr += size_to_read;
403 		remain_bytes -= size_to_read;
404 		pos += size_to_read;
405 
406 		start_block_num++;
407 	}
408 	res = TEE_SUCCESS;
409 exit:
410 	mutex_unlock(&ree_fs_mutex);
411 	free(block);
412 	return res;
413 }
414 
415 static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos,
416 			       const void *buf, size_t len)
417 {
418 	TEE_Result res;
419 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
420 	size_t file_size;
421 
422 	if (!len)
423 		return TEE_SUCCESS;
424 
425 	mutex_lock(&ree_fs_mutex);
426 
427 	file_size = tee_fs_htree_get_meta(fdp->ht)->length;
428 
429 	if ((pos + len) < len) {
430 		res = TEE_ERROR_BAD_PARAMETERS;
431 		goto exit;
432 	}
433 
434 	if (file_size < pos) {
435 		res = ree_fs_ftruncate_internal(fdp, pos);
436 		if (res != TEE_SUCCESS)
437 			goto exit;
438 	}
439 
440 	res = out_of_place_write(fdp, pos, buf, len);
441 	if (res != TEE_SUCCESS)
442 		goto exit;
443 
444 exit:
445 	if (res == TEE_SUCCESS)
446 		res = tee_fs_htree_sync_to_storage(&fdp->ht);
447 	mutex_unlock(&ree_fs_mutex);
448 	return res;
449 }
450 
451 static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new,
452 				bool overwrite)
453 {
454 	TEE_Result res;
455 
456 	mutex_lock(&ree_fs_mutex);
457 	res = tee_fs_rpc_rename(OPTEE_MSG_RPC_CMD_FS, old, new, overwrite);
458 	mutex_unlock(&ree_fs_mutex);
459 
460 	return res;
461 }
462 
463 static TEE_Result ree_fs_remove(struct tee_pobj *po)
464 {
465 	TEE_Result res;
466 
467 	mutex_lock(&ree_fs_mutex);
468 	res = tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_FS, po);
469 	mutex_unlock(&ree_fs_mutex);
470 
471 	return res;
472 }
473 
474 static TEE_Result ree_fs_truncate(struct tee_file_handle *fh, size_t len)
475 {
476 	TEE_Result res;
477 	struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh;
478 
479 	mutex_lock(&ree_fs_mutex);
480 	res = ree_fs_ftruncate_internal(fdp, len);
481 	mutex_unlock(&ree_fs_mutex);
482 
483 	return res;
484 }
485 
486 const struct tee_file_operations ree_fs_ops = {
487 	.open = ree_fs_open,
488 	.create = ree_fs_create,
489 	.close = ree_fs_close,
490 	.read = ree_fs_read,
491 	.write = ree_fs_write,
492 	.truncate = ree_fs_truncate,
493 	.rename = ree_fs_rename,
494 	.remove = ree_fs_remove,
495 	.opendir = ree_fs_opendir_rpc,
496 	.closedir = ree_fs_closedir_rpc,
497 	.readdir = ree_fs_readdir_rpc,
498 };
499