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