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