1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2017, Linaro Limited 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <assert.h> 30 #include <bitstring.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <tee/fs_dirfile.h> 35 #include <types_ext.h> 36 37 struct tee_fs_dirfile_dirh { 38 const struct tee_fs_dirfile_operations *fops; 39 struct tee_file_handle *fh; 40 int nbits; 41 bitstr_t *files; 42 size_t ndents; 43 }; 44 45 struct dirfile_entry { 46 TEE_UUID uuid; 47 uint8_t oid[TEE_OBJECT_ID_MAX_LEN]; 48 uint32_t oidlen; 49 uint8_t hash[TEE_FS_HTREE_HASH_SIZE]; 50 uint32_t file_number; 51 }; 52 53 /* 54 * File layout 55 * 56 * dirfile_entry.0 57 * ... 58 * dirfile_entry.n 59 * 60 * where n the index is disconnected from file_number in struct dirfile_entry 61 */ 62 63 static TEE_Result maybe_grow_files(struct tee_fs_dirfile_dirh *dirh, int idx) 64 { 65 void *p; 66 67 if (idx < dirh->nbits) 68 return TEE_SUCCESS; 69 70 p = realloc(dirh->files, bitstr_size(idx + 1)); 71 if (!p) 72 return TEE_ERROR_OUT_OF_MEMORY; 73 dirh->files = p; 74 75 bit_nclear(dirh->files, dirh->nbits, idx); 76 dirh->nbits = idx + 1; 77 78 return TEE_SUCCESS; 79 } 80 81 static TEE_Result set_file(struct tee_fs_dirfile_dirh *dirh, int idx) 82 { 83 TEE_Result res = maybe_grow_files(dirh, idx); 84 85 if (!res) 86 bit_set(dirh->files, idx); 87 88 return res; 89 } 90 91 static void clear_file(struct tee_fs_dirfile_dirh *dirh, int idx) 92 { 93 if (idx < dirh->nbits) 94 bit_clear(dirh->files, idx); 95 } 96 97 static bool test_file(struct tee_fs_dirfile_dirh *dirh, int idx) 98 { 99 if (idx < dirh->nbits) 100 return bit_test(dirh->files, idx); 101 102 return false; 103 } 104 105 static TEE_Result read_dent(struct tee_fs_dirfile_dirh *dirh, int idx, 106 struct dirfile_entry *dent) 107 { 108 TEE_Result res; 109 size_t l; 110 111 l = sizeof(*dent); 112 res = dirh->fops->read(dirh->fh, sizeof(struct dirfile_entry) * idx, 113 dent, &l); 114 if (!res && l != sizeof(*dent)) 115 res = TEE_ERROR_ITEM_NOT_FOUND; 116 117 return res; 118 } 119 120 static TEE_Result write_dent(struct tee_fs_dirfile_dirh *dirh, size_t n, 121 struct dirfile_entry *dent) 122 { 123 TEE_Result res; 124 125 res = dirh->fops->write(dirh->fh, sizeof(*dent) * n, 126 dent, sizeof(*dent)); 127 if (!res && n >= dirh->ndents) 128 dirh->ndents = n + 1; 129 130 return res; 131 } 132 133 TEE_Result tee_fs_dirfile_open(bool create, uint8_t *hash, 134 const struct tee_fs_dirfile_operations *fops, 135 struct tee_fs_dirfile_dirh **dirh_ret) 136 { 137 TEE_Result res; 138 struct tee_fs_dirfile_dirh *dirh = calloc(1, sizeof(*dirh)); 139 size_t n; 140 141 if (!dirh) 142 return TEE_ERROR_OUT_OF_MEMORY; 143 144 dirh->fops = fops; 145 res = fops->open(create, hash, NULL, NULL, &dirh->fh); 146 if (res) 147 goto out; 148 149 for (n = 0;; n++) { 150 struct dirfile_entry dent; 151 152 res = read_dent(dirh, n, &dent); 153 if (res) { 154 if (res == TEE_ERROR_ITEM_NOT_FOUND) 155 res = TEE_SUCCESS; 156 goto out; 157 } 158 159 if (!dent.oidlen) 160 continue; 161 162 if (test_file(dirh, dent.file_number)) { 163 DMSG("clearing duplicate file number %" PRIu32, 164 dent.file_number); 165 memset(&dent, 0, sizeof(dent)); 166 res = write_dent(dirh, n, &dent); 167 if (res) 168 goto out; 169 continue; 170 } 171 172 res = set_file(dirh, dent.file_number); 173 if (res != TEE_SUCCESS) 174 goto out; 175 } 176 out: 177 if (!res) { 178 dirh->ndents = n; 179 *dirh_ret = dirh; 180 } else { 181 tee_fs_dirfile_close(dirh); 182 } 183 return res; 184 } 185 186 void tee_fs_dirfile_close(struct tee_fs_dirfile_dirh *dirh) 187 { 188 if (dirh) { 189 dirh->fops->close(dirh->fh); 190 free(dirh->files); 191 free(dirh); 192 } 193 } 194 195 TEE_Result tee_fs_dirfile_commit_writes(struct tee_fs_dirfile_dirh *dirh, 196 uint8_t *hash) 197 { 198 return dirh->fops->commit_writes(dirh->fh, hash); 199 } 200 201 TEE_Result tee_fs_dirfile_get_tmp(struct tee_fs_dirfile_dirh *dirh, 202 struct tee_fs_dirfile_fileh *dfh) 203 { 204 TEE_Result res; 205 int i = 0; 206 207 if (dirh->files) { 208 bit_ffc(dirh->files, dirh->nbits, &i); 209 if (i == -1) 210 i = dirh->nbits; 211 } 212 213 res = set_file(dirh, i); 214 if (!res) 215 dfh->file_number = i; 216 217 return res; 218 } 219 220 TEE_Result tee_fs_dirfile_find(struct tee_fs_dirfile_dirh *dirh, 221 const TEE_UUID *uuid, const void *oid, 222 size_t oidlen, struct tee_fs_dirfile_fileh *dfh) 223 { 224 TEE_Result res; 225 struct dirfile_entry dent; 226 int n; 227 int first_free = -1; 228 229 for (n = 0;; n++) { 230 res = read_dent(dirh, n, &dent); 231 if (res == TEE_ERROR_ITEM_NOT_FOUND && !oidlen) { 232 memset(&dent, 0, sizeof(dent)); 233 if (first_free != -1) 234 n = first_free; 235 break; 236 } 237 if (res) 238 return res; 239 240 /* TODO check this loop when oidlen == 0 */ 241 242 if (!dent.oidlen && first_free == -1) 243 first_free = n; 244 if (dent.oidlen != oidlen) 245 continue; 246 247 assert(!oidlen || !dent.oidlen || 248 test_file(dirh, dent.file_number)); 249 250 if (!memcmp(&dent.uuid, uuid, sizeof(dent.uuid)) && 251 !memcmp(&dent.oid, oid, oidlen)) 252 break; 253 } 254 255 if (dfh) { 256 dfh->idx = n; 257 dfh->file_number = dent.file_number; 258 memcpy(dfh->hash, dent.hash, sizeof(dent.hash)); 259 } 260 261 return TEE_SUCCESS; 262 } 263 264 TEE_Result tee_fs_dirfile_fileh_to_fname(const struct tee_fs_dirfile_fileh *dfh, 265 char *fname, size_t *fnlen) 266 { 267 int r; 268 size_t l = *fnlen; 269 270 if (dfh) 271 r = snprintf(fname, l, "%" PRIx32, dfh->file_number); 272 else 273 r = snprintf(fname, l, "dirf.db"); 274 275 if (r < 0) 276 return TEE_ERROR_GENERIC; 277 278 *fnlen = r + 1; 279 if ((size_t)r >= l) 280 return TEE_ERROR_SHORT_BUFFER; 281 282 return TEE_SUCCESS; 283 } 284 285 TEE_Result tee_fs_dirfile_rename(struct tee_fs_dirfile_dirh *dirh, 286 const TEE_UUID *uuid, 287 struct tee_fs_dirfile_fileh *dfh, 288 const void *oid, size_t oidlen) 289 { 290 TEE_Result res; 291 struct dirfile_entry dent; 292 293 if (!oidlen || oidlen > sizeof(dent.oid)) 294 return TEE_ERROR_BAD_PARAMETERS; 295 memset(&dent, 0, sizeof(dent)); 296 dent.uuid = *uuid; 297 memcpy(dent.oid, oid, oidlen); 298 dent.oidlen = oidlen; 299 memcpy(dent.hash, dfh->hash, sizeof(dent.hash)); 300 dent.file_number = dfh->file_number; 301 302 if (dfh->idx < 0) { 303 struct tee_fs_dirfile_fileh dfh2; 304 305 res = tee_fs_dirfile_find(dirh, uuid, oid, oidlen, &dfh2); 306 if (res) { 307 if (res == TEE_ERROR_ITEM_NOT_FOUND) 308 res = tee_fs_dirfile_find(dirh, uuid, NULL, 0, 309 &dfh2); 310 if (res) 311 return res; 312 } 313 dfh->idx = dfh2.idx; 314 } 315 316 return write_dent(dirh, dfh->idx, &dent); 317 } 318 319 TEE_Result tee_fs_dirfile_remove(struct tee_fs_dirfile_dirh *dirh, 320 const struct tee_fs_dirfile_fileh *dfh) 321 { 322 TEE_Result res; 323 struct dirfile_entry dent; 324 uint32_t file_number; 325 326 res = read_dent(dirh, dfh->idx, &dent); 327 if (res) 328 return res; 329 330 if (!dent.oidlen) 331 return TEE_SUCCESS; 332 333 file_number = dent.file_number; 334 assert(dfh->file_number == file_number); 335 assert(test_file(dirh, file_number)); 336 337 memset(&dent, 0, sizeof(dent)); 338 res = write_dent(dirh, dfh->idx, &dent); 339 if (!res) 340 clear_file(dirh, file_number); 341 342 return res; 343 } 344 345 TEE_Result tee_fs_dirfile_update_hash(struct tee_fs_dirfile_dirh *dirh, 346 const struct tee_fs_dirfile_fileh *dfh) 347 { 348 TEE_Result res; 349 struct dirfile_entry dent; 350 351 res = read_dent(dirh, dfh->idx, &dent); 352 if (res) 353 return res; 354 assert(dent.file_number == dfh->file_number); 355 assert(test_file(dirh, dent.file_number)); 356 357 memcpy(&dent.hash, dfh->hash, sizeof(dent.hash)); 358 359 return write_dent(dirh, dfh->idx, &dent); 360 } 361 362 TEE_Result tee_fs_dirfile_get_next(struct tee_fs_dirfile_dirh *dirh, 363 const TEE_UUID *uuid, int *idx, void *oid, 364 size_t *oidlen) 365 { 366 TEE_Result res; 367 int i = *idx + 1; 368 struct dirfile_entry dent; 369 370 if (i < 0) 371 i = 0; 372 373 for (;; i++) { 374 res = read_dent(dirh, i, &dent); 375 if (res) 376 return res; 377 if (!memcmp(&dent.uuid, uuid, sizeof(dent.uuid)) && 378 dent.oidlen) 379 break; 380 } 381 382 if (*oidlen < dent.oidlen) 383 return TEE_ERROR_SHORT_BUFFER; 384 385 memcpy(oid, dent.oid, dent.oidlen); 386 *oidlen = dent.oidlen; 387 *idx = i; 388 389 return TEE_SUCCESS; 390 } 391