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