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 */ 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 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 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 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 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 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 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, dent, 117 sizeof(*dent)); 118 if (!res && n >= dirh->ndents) 119 dirh->ndents = n + 1; 120 121 return res; 122 } 123 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 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 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 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 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 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 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 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 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 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 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