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