1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * EFI utils 4 * 5 * Copyright (c) 2017 Rob Clark 6 */ 7 8 #include <common.h> 9 #include <charset.h> 10 #include <efi_loader.h> 11 #include <malloc.h> 12 #include <fs.h> 13 14 /* GUID for file system information */ 15 const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID; 16 17 struct file_system { 18 struct efi_simple_file_system_protocol base; 19 struct efi_device_path *dp; 20 struct blk_desc *desc; 21 int part; 22 }; 23 #define to_fs(x) container_of(x, struct file_system, base) 24 25 struct file_handle { 26 struct efi_file_handle base; 27 struct file_system *fs; 28 loff_t offset; /* current file position/cursor */ 29 int isdir; 30 31 /* for reading a directory: */ 32 struct fs_dir_stream *dirs; 33 struct fs_dirent *dent; 34 35 char path[0]; 36 }; 37 #define to_fh(x) container_of(x, struct file_handle, base) 38 39 static const struct efi_file_handle efi_file_handle_protocol; 40 41 static char *basename(struct file_handle *fh) 42 { 43 char *s = strrchr(fh->path, '/'); 44 if (s) 45 return s + 1; 46 return fh->path; 47 } 48 49 static int set_blk_dev(struct file_handle *fh) 50 { 51 return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part); 52 } 53 54 static int is_dir(struct file_handle *fh) 55 { 56 struct fs_dir_stream *dirs; 57 58 set_blk_dev(fh); 59 dirs = fs_opendir(fh->path); 60 if (!dirs) 61 return 0; 62 63 fs_closedir(dirs); 64 65 return 1; 66 } 67 68 /* 69 * Normalize a path which may include either back or fwd slashes, 70 * double slashes, . or .. entries in the path, etc. 71 */ 72 static int sanitize_path(char *path) 73 { 74 char *p; 75 76 /* backslash to slash: */ 77 p = path; 78 while ((p = strchr(p, '\\'))) 79 *p++ = '/'; 80 81 /* handle double-slashes: */ 82 p = path; 83 while ((p = strstr(p, "//"))) { 84 char *src = p + 1; 85 memmove(p, src, strlen(src) + 1); 86 } 87 88 /* handle extra /.'s */ 89 p = path; 90 while ((p = strstr(p, "/."))) { 91 /* 92 * You'd be tempted to do this *after* handling ".."s 93 * below to avoid having to check if "/." is start of 94 * a "/..", but that won't have the correct results.. 95 * for example, "/foo/./../bar" would get resolved to 96 * "/foo/bar" if you did these two passes in the other 97 * order 98 */ 99 if (p[2] == '.') { 100 p += 2; 101 continue; 102 } 103 char *src = p + 2; 104 memmove(p, src, strlen(src) + 1); 105 } 106 107 /* handle extra /..'s: */ 108 p = path; 109 while ((p = strstr(p, "/.."))) { 110 char *src = p + 3; 111 112 p--; 113 114 /* find beginning of previous path entry: */ 115 while (true) { 116 if (p < path) 117 return -1; 118 if (*p == '/') 119 break; 120 p--; 121 } 122 123 memmove(p, src, strlen(src) + 1); 124 } 125 126 return 0; 127 } 128 129 /* NOTE: despite what you would expect, 'file_name' is actually a path. 130 * With windoze style backlashes, ofc. 131 */ 132 static struct efi_file_handle *file_open(struct file_system *fs, 133 struct file_handle *parent, s16 *file_name, u64 mode) 134 { 135 struct file_handle *fh; 136 char f0[MAX_UTF8_PER_UTF16] = {0}; 137 int plen = 0; 138 int flen = 0; 139 140 if (file_name) { 141 utf16_to_utf8((u8 *)f0, (u16 *)file_name, 1); 142 flen = utf16_strlen((u16 *)file_name); 143 } 144 145 /* we could have a parent, but also an absolute path: */ 146 if (f0[0] == '\\') { 147 plen = 0; 148 } else if (parent) { 149 plen = strlen(parent->path) + 1; 150 } 151 152 /* +2 is for null and '/' */ 153 fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2); 154 155 fh->base = efi_file_handle_protocol; 156 fh->fs = fs; 157 158 if (parent) { 159 char *p = fh->path; 160 161 if (plen > 0) { 162 strcpy(p, parent->path); 163 p += plen - 1; 164 *p++ = '/'; 165 } 166 167 utf16_to_utf8((u8 *)p, (u16 *)file_name, flen); 168 169 if (sanitize_path(fh->path)) 170 goto error; 171 172 /* check if file exists: */ 173 if (set_blk_dev(fh)) 174 goto error; 175 176 if (!((mode & EFI_FILE_MODE_CREATE) || fs_exists(fh->path))) 177 goto error; 178 179 /* figure out if file is a directory: */ 180 fh->isdir = is_dir(fh); 181 } else { 182 fh->isdir = 1; 183 strcpy(fh->path, ""); 184 } 185 186 return &fh->base; 187 188 error: 189 free(fh); 190 return NULL; 191 } 192 193 static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file, 194 struct efi_file_handle **new_handle, 195 s16 *file_name, u64 open_mode, u64 attributes) 196 { 197 struct file_handle *fh = to_fh(file); 198 199 EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, file_name, 200 open_mode, attributes); 201 202 *new_handle = file_open(fh->fs, fh, file_name, open_mode); 203 if (!*new_handle) 204 return EFI_EXIT(EFI_NOT_FOUND); 205 206 return EFI_EXIT(EFI_SUCCESS); 207 } 208 209 static efi_status_t file_close(struct file_handle *fh) 210 { 211 fs_closedir(fh->dirs); 212 free(fh); 213 return EFI_SUCCESS; 214 } 215 216 static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file) 217 { 218 struct file_handle *fh = to_fh(file); 219 EFI_ENTRY("%p", file); 220 return EFI_EXIT(file_close(fh)); 221 } 222 223 static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file) 224 { 225 struct file_handle *fh = to_fh(file); 226 EFI_ENTRY("%p", file); 227 file_close(fh); 228 return EFI_EXIT(EFI_WARN_DELETE_FAILURE); 229 } 230 231 static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size, 232 void *buffer) 233 { 234 loff_t actread; 235 236 if (fs_read(fh->path, (ulong)buffer, fh->offset, 237 *buffer_size, &actread)) 238 return EFI_DEVICE_ERROR; 239 240 *buffer_size = actread; 241 fh->offset += actread; 242 243 return EFI_SUCCESS; 244 } 245 246 static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size, 247 void *buffer) 248 { 249 struct efi_file_info *info = buffer; 250 struct fs_dirent *dent; 251 unsigned int required_size; 252 253 if (!fh->dirs) { 254 assert(fh->offset == 0); 255 fh->dirs = fs_opendir(fh->path); 256 if (!fh->dirs) 257 return EFI_DEVICE_ERROR; 258 } 259 260 /* 261 * So this is a bit awkward. Since fs layer is stateful and we 262 * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below 263 * we might have to return without consuming the dent.. so we 264 * have to stash it for next call. 265 */ 266 if (fh->dent) { 267 dent = fh->dent; 268 fh->dent = NULL; 269 } else { 270 dent = fs_readdir(fh->dirs); 271 } 272 273 274 if (!dent) { 275 /* no more files in directory: */ 276 /* workaround shim.efi bug/quirk.. as find_boot_csv() 277 * loops through directory contents, it initially calls 278 * read w/ zero length buffer to find out how much mem 279 * to allocate for the EFI_FILE_INFO, then allocates, 280 * and then calls a 2nd time. If we return size of 281 * zero the first time, it happily passes that to 282 * AllocateZeroPool(), and when that returns NULL it 283 * thinks it is EFI_OUT_OF_RESOURCES. So on first 284 * call return a non-zero size: 285 */ 286 if (*buffer_size == 0) 287 *buffer_size = sizeof(*info); 288 else 289 *buffer_size = 0; 290 return EFI_SUCCESS; 291 } 292 293 /* check buffer size: */ 294 required_size = sizeof(*info) + 2 * (strlen(dent->name) + 1); 295 if (*buffer_size < required_size) { 296 *buffer_size = required_size; 297 fh->dent = dent; 298 return EFI_BUFFER_TOO_SMALL; 299 } 300 301 *buffer_size = required_size; 302 memset(info, 0, required_size); 303 304 info->size = required_size; 305 info->file_size = dent->size; 306 info->physical_size = dent->size; 307 308 if (dent->type == FS_DT_DIR) 309 info->attribute |= EFI_FILE_DIRECTORY; 310 311 ascii2unicode((u16 *)info->file_name, dent->name); 312 313 fh->offset++; 314 315 return EFI_SUCCESS; 316 } 317 318 static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file, 319 efi_uintn_t *buffer_size, void *buffer) 320 { 321 struct file_handle *fh = to_fh(file); 322 efi_status_t ret = EFI_SUCCESS; 323 u64 bs; 324 325 EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer); 326 327 if (!buffer_size || !buffer) { 328 ret = EFI_INVALID_PARAMETER; 329 goto error; 330 } 331 332 if (set_blk_dev(fh)) { 333 ret = EFI_DEVICE_ERROR; 334 goto error; 335 } 336 337 bs = *buffer_size; 338 if (fh->isdir) 339 ret = dir_read(fh, &bs, buffer); 340 else 341 ret = file_read(fh, &bs, buffer); 342 if (bs <= SIZE_MAX) 343 *buffer_size = bs; 344 else 345 *buffer_size = SIZE_MAX; 346 347 error: 348 return EFI_EXIT(ret); 349 } 350 351 static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file, 352 efi_uintn_t *buffer_size, 353 void *buffer) 354 { 355 struct file_handle *fh = to_fh(file); 356 efi_status_t ret = EFI_SUCCESS; 357 loff_t actwrite; 358 359 EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer); 360 361 if (set_blk_dev(fh)) { 362 ret = EFI_DEVICE_ERROR; 363 goto error; 364 } 365 366 if (fs_write(fh->path, (ulong)buffer, fh->offset, *buffer_size, 367 &actwrite)) { 368 ret = EFI_DEVICE_ERROR; 369 goto error; 370 } 371 372 *buffer_size = actwrite; 373 fh->offset += actwrite; 374 375 error: 376 return EFI_EXIT(ret); 377 } 378 379 static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file, 380 efi_uintn_t *pos) 381 { 382 struct file_handle *fh = to_fh(file); 383 384 EFI_ENTRY("%p, %p", file, pos); 385 386 if (fh->offset <= SIZE_MAX) { 387 *pos = fh->offset; 388 return EFI_EXIT(EFI_SUCCESS); 389 } else { 390 return EFI_EXIT(EFI_DEVICE_ERROR); 391 } 392 } 393 394 static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file, 395 efi_uintn_t pos) 396 { 397 struct file_handle *fh = to_fh(file); 398 efi_status_t ret = EFI_SUCCESS; 399 400 EFI_ENTRY("%p, %zu", file, pos); 401 402 if (fh->isdir) { 403 if (pos != 0) { 404 ret = EFI_UNSUPPORTED; 405 goto error; 406 } 407 fs_closedir(fh->dirs); 408 fh->dirs = NULL; 409 } 410 411 if (pos == ~0ULL) { 412 loff_t file_size; 413 414 if (set_blk_dev(fh)) { 415 ret = EFI_DEVICE_ERROR; 416 goto error; 417 } 418 419 if (fs_size(fh->path, &file_size)) { 420 ret = EFI_DEVICE_ERROR; 421 goto error; 422 } 423 424 pos = file_size; 425 } 426 427 fh->offset = pos; 428 429 error: 430 return EFI_EXIT(ret); 431 } 432 433 static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file, 434 const efi_guid_t *info_type, 435 efi_uintn_t *buffer_size, 436 void *buffer) 437 { 438 struct file_handle *fh = to_fh(file); 439 efi_status_t ret = EFI_SUCCESS; 440 441 EFI_ENTRY("%p, %p, %p, %p", file, info_type, buffer_size, buffer); 442 443 if (!guidcmp(info_type, &efi_file_info_guid)) { 444 struct efi_file_info *info = buffer; 445 char *filename = basename(fh); 446 unsigned int required_size; 447 loff_t file_size; 448 449 /* check buffer size: */ 450 required_size = sizeof(*info) + 2 * (strlen(filename) + 1); 451 if (*buffer_size < required_size) { 452 *buffer_size = required_size; 453 ret = EFI_BUFFER_TOO_SMALL; 454 goto error; 455 } 456 457 if (set_blk_dev(fh)) { 458 ret = EFI_DEVICE_ERROR; 459 goto error; 460 } 461 462 if (fs_size(fh->path, &file_size)) { 463 ret = EFI_DEVICE_ERROR; 464 goto error; 465 } 466 467 memset(info, 0, required_size); 468 469 info->size = required_size; 470 info->file_size = file_size; 471 info->physical_size = file_size; 472 473 if (fh->isdir) 474 info->attribute |= EFI_FILE_DIRECTORY; 475 476 ascii2unicode((u16 *)info->file_name, filename); 477 } else if (!guidcmp(info_type, &efi_file_system_info_guid)) { 478 struct efi_file_system_info *info = buffer; 479 disk_partition_t part; 480 efi_uintn_t required_size; 481 int r; 482 483 if (fh->fs->part >= 1) 484 r = part_get_info(fh->fs->desc, fh->fs->part, &part); 485 else 486 r = part_get_info_whole_disk(fh->fs->desc, &part); 487 if (r < 0) { 488 ret = EFI_DEVICE_ERROR; 489 goto error; 490 } 491 required_size = sizeof(info) + 2 * 492 (strlen((const char *)part.name) + 1); 493 if (*buffer_size < required_size) { 494 *buffer_size = required_size; 495 ret = EFI_BUFFER_TOO_SMALL; 496 goto error; 497 } 498 499 memset(info, 0, required_size); 500 501 info->size = required_size; 502 info->read_only = true; 503 info->volume_size = part.size * part.blksz; 504 info->free_space = 0; 505 info->block_size = part.blksz; 506 /* 507 * TODO: The volume label is not available in U-Boot. 508 * Use the partition name as substitute. 509 */ 510 ascii2unicode((u16 *)info->volume_label, 511 (const char *)part.name); 512 } else { 513 ret = EFI_UNSUPPORTED; 514 } 515 516 error: 517 return EFI_EXIT(ret); 518 } 519 520 static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file, 521 const efi_guid_t *info_type, 522 efi_uintn_t buffer_size, 523 void *buffer) 524 { 525 EFI_ENTRY("%p, %p, %zu, %p", file, info_type, buffer_size, buffer); 526 527 return EFI_EXIT(EFI_UNSUPPORTED); 528 } 529 530 static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file) 531 { 532 EFI_ENTRY("%p", file); 533 return EFI_EXIT(EFI_SUCCESS); 534 } 535 536 static const struct efi_file_handle efi_file_handle_protocol = { 537 .rev = EFI_FILE_PROTOCOL_REVISION, 538 .open = efi_file_open, 539 .close = efi_file_close, 540 .delete = efi_file_delete, 541 .read = efi_file_read, 542 .write = efi_file_write, 543 .getpos = efi_file_getpos, 544 .setpos = efi_file_setpos, 545 .getinfo = efi_file_getinfo, 546 .setinfo = efi_file_setinfo, 547 .flush = efi_file_flush, 548 }; 549 550 struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp) 551 { 552 struct efi_simple_file_system_protocol *v; 553 struct efi_file_handle *f; 554 efi_status_t ret; 555 556 v = efi_fs_from_path(fp); 557 if (!v) 558 return NULL; 559 560 EFI_CALL(ret = v->open_volume(v, &f)); 561 if (ret != EFI_SUCCESS) 562 return NULL; 563 564 /* skip over device-path nodes before the file path: */ 565 while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) 566 fp = efi_dp_next(fp); 567 568 while (fp) { 569 struct efi_device_path_file_path *fdp = 570 container_of(fp, struct efi_device_path_file_path, dp); 571 struct efi_file_handle *f2; 572 573 if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) { 574 printf("bad file path!\n"); 575 f->close(f); 576 return NULL; 577 } 578 579 EFI_CALL(ret = f->open(f, &f2, (s16 *)fdp->str, 580 EFI_FILE_MODE_READ, 0)); 581 if (ret != EFI_SUCCESS) 582 return NULL; 583 584 fp = efi_dp_next(fp); 585 586 EFI_CALL(f->close(f)); 587 f = f2; 588 } 589 590 return f; 591 } 592 593 static efi_status_t EFIAPI 594 efi_open_volume(struct efi_simple_file_system_protocol *this, 595 struct efi_file_handle **root) 596 { 597 struct file_system *fs = to_fs(this); 598 599 EFI_ENTRY("%p, %p", this, root); 600 601 *root = file_open(fs, NULL, NULL, 0); 602 603 return EFI_EXIT(EFI_SUCCESS); 604 } 605 606 struct efi_simple_file_system_protocol * 607 efi_simple_file_system(struct blk_desc *desc, int part, 608 struct efi_device_path *dp) 609 { 610 struct file_system *fs; 611 612 fs = calloc(1, sizeof(*fs)); 613 fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION; 614 fs->base.open_volume = efi_open_volume; 615 fs->desc = desc; 616 fs->part = part; 617 fs->dp = dp; 618 619 return &fs->base; 620 } 621