1 /* 2 * Copyright (c) 2017, Linaro Limited 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <assert.h> 29 #include <bitstring.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <tee/fs_dirfile.h> 34 #include <types_ext.h> 35 36 struct tee_fs_dirfile_dirh { 37 const struct tee_fs_dirfile_operations *fops; 38 struct tee_file_handle *fh; 39 int nbits; 40 bitstr_t *files; 41 size_t ndents; 42 }; 43 44 struct dirfile_entry { 45 TEE_UUID uuid; 46 uint8_t oid[TEE_OBJECT_ID_MAX_LEN]; 47 uint32_t oidlen; 48 uint8_t hash[TEE_FS_HTREE_HASH_SIZE]; 49 uint32_t file_number; 50 }; 51 52 /* 53 * File layout 54 * 55 * dirfile_entry.0 56 * ... 57 * dirfile_entry.n 58 * 59 * where n the index is disconnected from file_number in struct dirfile_entry 60 */ 61 62 static TEE_Result maybe_grow_files(struct tee_fs_dirfile_dirh *dirh, int idx) 63 { 64 void *p; 65 66 if (idx < dirh->nbits) 67 return TEE_SUCCESS; 68 69 p = realloc(dirh->files, bitstr_size(idx + 1)); 70 if (!p) 71 return TEE_ERROR_OUT_OF_MEMORY; 72 dirh->files = p; 73 74 bit_nclear(dirh->files, dirh->nbits, idx); 75 dirh->nbits = idx + 1; 76 77 return TEE_SUCCESS; 78 } 79 80 static TEE_Result set_file(struct tee_fs_dirfile_dirh *dirh, int idx) 81 { 82 TEE_Result res = maybe_grow_files(dirh, idx); 83 84 if (!res) 85 bit_set(dirh->files, idx); 86 87 return res; 88 } 89 90 static void clear_file(struct tee_fs_dirfile_dirh *dirh, int idx) 91 { 92 if (idx < dirh->nbits) 93 bit_clear(dirh->files, idx); 94 } 95 96 static bool test_file(struct tee_fs_dirfile_dirh *dirh, int idx) 97 { 98 if (idx < dirh->nbits) 99 return bit_test(dirh->files, idx); 100 101 return false; 102 } 103 104 static TEE_Result read_dent(struct tee_fs_dirfile_dirh *dirh, int idx, 105 struct dirfile_entry *dent) 106 { 107 TEE_Result res; 108 size_t l; 109 110 l = sizeof(*dent); 111 res = dirh->fops->read(dirh->fh, sizeof(struct dirfile_entry) * idx, 112 dent, &l); 113 if (!res && l != sizeof(*dent)) 114 res = TEE_ERROR_ITEM_NOT_FOUND; 115 116 return res; 117 } 118 119 static TEE_Result write_dent(struct tee_fs_dirfile_dirh *dirh, size_t n, 120 struct dirfile_entry *dent) 121 { 122 TEE_Result res; 123 124 res = dirh->fops->write(dirh->fh, sizeof(*dent) * n, 125 dent, sizeof(*dent)); 126 if (!res && n >= dirh->ndents) 127 dirh->ndents = n + 1; 128 129 return res; 130 } 131 132 TEE_Result tee_fs_dirfile_open(const struct tee_fs_dirfile_operations *fops, 133 struct tee_fs_dirfile_dirh **dirh_ret) 134 { 135 TEE_Result res; 136 struct tee_fs_dirfile_dirh *dirh = calloc(1, sizeof(*dirh)); 137 size_t n; 138 139 if (!dirh) 140 return TEE_ERROR_OUT_OF_MEMORY; 141 142 dirh->fops = fops; 143 res = fops->open(false, NULL, NULL, &dirh->fh); 144 if (res) { 145 res = fops->open(true, NULL, NULL, &dirh->fh); 146 if (res) 147 goto out; 148 } 149 150 for (n = 0;; n++) { 151 struct dirfile_entry dent; 152 153 res = read_dent(dirh, n, &dent); 154 if (res) { 155 if (res == TEE_ERROR_ITEM_NOT_FOUND) 156 res = TEE_SUCCESS; 157 goto out; 158 } 159 160 if (!dent.oidlen) 161 continue; 162 163 if (test_file(dirh, dent.file_number)) { 164 DMSG("clearing duplicate file number %" PRIu32, 165 dent.file_number); 166 memset(&dent, 0, sizeof(dent)); 167 res = write_dent(dirh, n, &dent); 168 if (res) 169 goto out; 170 continue; 171 } 172 173 res = set_file(dirh, dent.file_number); 174 if (res != TEE_SUCCESS) 175 goto out; 176 } 177 out: 178 if (!res) { 179 dirh->ndents = n; 180 *dirh_ret = dirh; 181 } else { 182 tee_fs_dirfile_close(dirh); 183 } 184 return res; 185 } 186 187 void tee_fs_dirfile_close(struct tee_fs_dirfile_dirh *dirh) 188 { 189 if (dirh) { 190 dirh->fops->close(dirh->fh); 191 free(dirh->files); 192 free(dirh); 193 } 194 } 195 196 TEE_Result tee_fs_dirfile_commit_writes(struct tee_fs_dirfile_dirh *dirh) 197 { 198 return dirh->fops->commit_writes(dirh->fh, NULL); 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