xref: /optee_os/core/tee/fs_dirfile.c (revision 78b7c7c7653f8bff42fe44d31a79d7f6bbfd4d47)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2017, Linaro Limited
4  * All rights reserved.
5  */
6 
7 #include <assert.h>
8 #include <bitstring.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <tee/fs_dirfile.h>
13 #include <types_ext.h>
14 
15 struct tee_fs_dirfile_dirh {
16 	const struct tee_fs_dirfile_operations *fops;
17 	struct tee_file_handle *fh;
18 	int nbits;
19 	bitstr_t *files;
20 	size_t ndents;
21 };
22 
23 struct dirfile_entry {
24 	TEE_UUID uuid;
25 	uint8_t oid[TEE_OBJECT_ID_MAX_LEN];
26 	uint32_t oidlen;
27 	uint8_t hash[TEE_FS_HTREE_HASH_SIZE];
28 	uint32_t file_number;
29 };
30 
31 /*
32  * File layout
33  *
34  * dirfile_entry.0
35  * ...
36  * dirfile_entry.n
37  *
38  * where n the index is disconnected from file_number in struct dirfile_entry
39  */
40 
41 static TEE_Result maybe_grow_files(struct tee_fs_dirfile_dirh *dirh, int idx)
42 {
43 	void *p;
44 
45 	if (idx < dirh->nbits)
46 		return TEE_SUCCESS;
47 
48 	p = realloc(dirh->files, bitstr_size(idx + 1));
49 	if (!p)
50 		return TEE_ERROR_OUT_OF_MEMORY;
51 	dirh->files = p;
52 
53 	bit_nclear(dirh->files, dirh->nbits, idx);
54 	dirh->nbits = idx + 1;
55 
56 	return TEE_SUCCESS;
57 }
58 
59 static TEE_Result set_file(struct tee_fs_dirfile_dirh *dirh, int idx)
60 {
61 	TEE_Result res = maybe_grow_files(dirh, idx);
62 
63 	if (!res)
64 		bit_set(dirh->files, idx);
65 
66 	return res;
67 }
68 
69 static void clear_file(struct tee_fs_dirfile_dirh *dirh, int idx)
70 {
71 	if (idx < dirh->nbits)
72 		bit_clear(dirh->files, idx);
73 }
74 
75 static bool test_file(struct tee_fs_dirfile_dirh *dirh, int idx)
76 {
77 	if (idx < dirh->nbits)
78 		return bit_test(dirh->files, idx);
79 
80 	return false;
81 }
82 
83 static TEE_Result read_dent(struct tee_fs_dirfile_dirh *dirh, int idx,
84 			    struct dirfile_entry *dent)
85 {
86 	TEE_Result res;
87 	size_t l;
88 
89 	l = sizeof(*dent);
90 	res = dirh->fops->read(dirh->fh, sizeof(struct dirfile_entry) * idx,
91 			       dent, &l);
92 	if (!res && l != sizeof(*dent))
93 		res = TEE_ERROR_ITEM_NOT_FOUND;
94 
95 	return res;
96 }
97 
98 static TEE_Result write_dent(struct tee_fs_dirfile_dirh *dirh, size_t n,
99 			     struct dirfile_entry *dent)
100 {
101 	TEE_Result res;
102 
103 	res = dirh->fops->write(dirh->fh, sizeof(*dent) * n,
104 				dent, sizeof(*dent));
105 	if (!res && n >= dirh->ndents)
106 		dirh->ndents = n + 1;
107 
108 	return res;
109 }
110 
111 TEE_Result tee_fs_dirfile_open(bool create, uint8_t *hash,
112 			       const struct tee_fs_dirfile_operations *fops,
113 			       struct tee_fs_dirfile_dirh **dirh_ret)
114 {
115 	TEE_Result res;
116 	struct tee_fs_dirfile_dirh *dirh = calloc(1, sizeof(*dirh));
117 	size_t n;
118 
119 	if (!dirh)
120 		return TEE_ERROR_OUT_OF_MEMORY;
121 
122 	dirh->fops = fops;
123 	res = fops->open(create, hash, NULL, NULL, &dirh->fh);
124 	if (res)
125 		goto out;
126 
127 	for (n = 0;; n++) {
128 		struct dirfile_entry dent;
129 
130 		res = read_dent(dirh, n, &dent);
131 		if (res) {
132 			if (res == TEE_ERROR_ITEM_NOT_FOUND)
133 				res = TEE_SUCCESS;
134 			goto out;
135 		}
136 
137 		if (!dent.oidlen)
138 			continue;
139 
140 		if (test_file(dirh, dent.file_number)) {
141 			DMSG("clearing duplicate file number %" PRIu32,
142 			     dent.file_number);
143 			memset(&dent, 0, sizeof(dent));
144 			res = write_dent(dirh, n, &dent);
145 			if (res)
146 				goto out;
147 			continue;
148 		}
149 
150 		res = set_file(dirh, dent.file_number);
151 		if (res != TEE_SUCCESS)
152 			goto out;
153 	}
154 out:
155 	if (!res) {
156 		dirh->ndents = n;
157 		*dirh_ret = dirh;
158 	} else {
159 		tee_fs_dirfile_close(dirh);
160 	}
161 	return res;
162 }
163 
164 void tee_fs_dirfile_close(struct tee_fs_dirfile_dirh *dirh)
165 {
166 	if (dirh) {
167 		dirh->fops->close(dirh->fh);
168 		free(dirh->files);
169 		free(dirh);
170 	}
171 }
172 
173 TEE_Result tee_fs_dirfile_commit_writes(struct tee_fs_dirfile_dirh *dirh,
174 					uint8_t *hash)
175 {
176 	return dirh->fops->commit_writes(dirh->fh, hash);
177 }
178 
179 TEE_Result tee_fs_dirfile_get_tmp(struct tee_fs_dirfile_dirh *dirh,
180 				  struct tee_fs_dirfile_fileh *dfh)
181 {
182 	TEE_Result res;
183 	int i = 0;
184 
185 	if (dirh->files) {
186 		bit_ffc(dirh->files, dirh->nbits, &i);
187 		if (i == -1)
188 			i = dirh->nbits;
189 	}
190 
191 	res = set_file(dirh, i);
192 	if (!res)
193 		dfh->file_number = i;
194 
195 	return res;
196 }
197 
198 TEE_Result tee_fs_dirfile_find(struct tee_fs_dirfile_dirh *dirh,
199 			       const TEE_UUID *uuid, const void *oid,
200 			       size_t oidlen, struct tee_fs_dirfile_fileh *dfh)
201 {
202 	TEE_Result res;
203 	struct dirfile_entry dent;
204 	int n;
205 	int first_free = -1;
206 
207 	for (n = 0;; n++) {
208 		res = read_dent(dirh, n, &dent);
209 		if (res == TEE_ERROR_ITEM_NOT_FOUND && !oidlen) {
210 			memset(&dent, 0, sizeof(dent));
211 			if (first_free != -1)
212 				n = first_free;
213 			break;
214 		}
215 		if (res)
216 			return res;
217 
218 		/* TODO check this loop when oidlen == 0 */
219 
220 		if (!dent.oidlen && first_free == -1)
221 			first_free = n;
222 		if (dent.oidlen != oidlen)
223 			continue;
224 
225 		assert(!oidlen || !dent.oidlen ||
226 		       test_file(dirh, dent.file_number));
227 
228 		if (!memcmp(&dent.uuid, uuid, sizeof(dent.uuid)) &&
229 		    !memcmp(&dent.oid, oid, oidlen))
230 			break;
231 	}
232 
233 	if (dfh) {
234 		dfh->idx = n;
235 		dfh->file_number = dent.file_number;
236 		memcpy(dfh->hash, dent.hash, sizeof(dent.hash));
237 	}
238 
239 	return TEE_SUCCESS;
240 }
241 
242 TEE_Result tee_fs_dirfile_fileh_to_fname(const struct tee_fs_dirfile_fileh *dfh,
243 					 char *fname, size_t *fnlen)
244 {
245 	int r;
246 	size_t l = *fnlen;
247 
248 	if (dfh)
249 		r = snprintf(fname, l, "%" PRIx32, dfh->file_number);
250 	else
251 		r = snprintf(fname, l, "dirf.db");
252 
253 	if (r < 0)
254 		return TEE_ERROR_GENERIC;
255 
256 	*fnlen = r + 1;
257 	if ((size_t)r >= l)
258 		return TEE_ERROR_SHORT_BUFFER;
259 
260 	return TEE_SUCCESS;
261 }
262 
263 TEE_Result tee_fs_dirfile_rename(struct tee_fs_dirfile_dirh *dirh,
264 				 const TEE_UUID *uuid,
265 				 struct tee_fs_dirfile_fileh *dfh,
266 				 const void *oid, size_t oidlen)
267 {
268 	TEE_Result res;
269 	struct dirfile_entry dent;
270 
271 	if (!oidlen || oidlen > sizeof(dent.oid))
272 		return TEE_ERROR_BAD_PARAMETERS;
273 	memset(&dent, 0, sizeof(dent));
274 	dent.uuid = *uuid;
275 	memcpy(dent.oid, oid, oidlen);
276 	dent.oidlen = oidlen;
277 	memcpy(dent.hash, dfh->hash, sizeof(dent.hash));
278 	dent.file_number = dfh->file_number;
279 
280 	if (dfh->idx < 0) {
281 		struct tee_fs_dirfile_fileh dfh2;
282 
283 		res = tee_fs_dirfile_find(dirh, uuid, oid, oidlen, &dfh2);
284 		if (res) {
285 			if (res == TEE_ERROR_ITEM_NOT_FOUND)
286 				res = tee_fs_dirfile_find(dirh, uuid, NULL, 0,
287 							  &dfh2);
288 			if (res)
289 				return res;
290 		}
291 		dfh->idx = dfh2.idx;
292 	}
293 
294 	return write_dent(dirh, dfh->idx, &dent);
295 }
296 
297 TEE_Result tee_fs_dirfile_remove(struct tee_fs_dirfile_dirh *dirh,
298 				 const struct tee_fs_dirfile_fileh *dfh)
299 {
300 	TEE_Result res;
301 	struct dirfile_entry dent;
302 	uint32_t file_number;
303 
304 	res = read_dent(dirh, dfh->idx, &dent);
305 	if (res)
306 		return res;
307 
308 	if (!dent.oidlen)
309 		return TEE_SUCCESS;
310 
311 	file_number = dent.file_number;
312 	assert(dfh->file_number == file_number);
313 	assert(test_file(dirh, file_number));
314 
315 	memset(&dent, 0, sizeof(dent));
316 	res = write_dent(dirh, dfh->idx, &dent);
317 	if (!res)
318 		clear_file(dirh, file_number);
319 
320 	return res;
321 }
322 
323 TEE_Result tee_fs_dirfile_update_hash(struct tee_fs_dirfile_dirh *dirh,
324 				      const struct tee_fs_dirfile_fileh *dfh)
325 {
326 	TEE_Result res;
327 	struct dirfile_entry dent;
328 
329 	res = read_dent(dirh, dfh->idx, &dent);
330 	if (res)
331 		return res;
332 	assert(dent.file_number == dfh->file_number);
333 	assert(test_file(dirh, dent.file_number));
334 
335 	memcpy(&dent.hash, dfh->hash, sizeof(dent.hash));
336 
337 	return write_dent(dirh, dfh->idx, &dent);
338 }
339 
340 TEE_Result tee_fs_dirfile_get_next(struct tee_fs_dirfile_dirh *dirh,
341 				   const TEE_UUID *uuid, int *idx, void *oid,
342 				   size_t *oidlen)
343 {
344 	TEE_Result res;
345 	int i = *idx + 1;
346 	struct dirfile_entry dent;
347 
348 	if (i < 0)
349 		i = 0;
350 
351 	for (;; i++) {
352 		res = read_dent(dirh, i, &dent);
353 		if (res)
354 			return res;
355 		if (!memcmp(&dent.uuid, uuid, sizeof(dent.uuid)) &&
356 		    dent.oidlen)
357 			break;
358 	}
359 
360 	if (*oidlen < dent.oidlen)
361 		return TEE_ERROR_SHORT_BUFFER;
362 
363 	memcpy(oid, dent.oid, dent.oidlen);
364 	*oidlen = dent.oidlen;
365 	*idx = i;
366 
367 	return TEE_SUCCESS;
368 }
369