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