1
2
3
4
5
6
7
8#include <common.h>
9#include <charset.h>
10#include <efi_loader.h>
11#include <log.h>
12#include <malloc.h>
13#include <mapmem.h>
14#include <fs.h>
15#include <part.h>
16
17
18const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
19
20
21const efi_guid_t efi_system_volume_label_id = EFI_FILE_SYSTEM_VOLUME_LABEL_ID;
22
23struct file_system {
24 struct efi_simple_file_system_protocol base;
25 struct efi_device_path *dp;
26 struct blk_desc *desc;
27 int part;
28};
29#define to_fs(x) container_of(x, struct file_system, base)
30
31struct file_handle {
32 struct efi_file_handle base;
33 struct file_system *fs;
34 loff_t offset;
35 int isdir;
36 u64 open_mode;
37
38
39 struct fs_dir_stream *dirs;
40 struct fs_dirent *dent;
41
42 char path[0];
43};
44#define to_fh(x) container_of(x, struct file_handle, base)
45
46static const struct efi_file_handle efi_file_handle_protocol;
47
48static char *basename(struct file_handle *fh)
49{
50 char *s = strrchr(fh->path, '/');
51 if (s)
52 return s + 1;
53 return fh->path;
54}
55
56static int set_blk_dev(struct file_handle *fh)
57{
58 return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
59}
60
61
62
63
64
65
66
67
68
69static int is_dir(struct file_handle *fh)
70{
71 struct fs_dir_stream *dirs;
72
73 dirs = fs_opendir(fh->path);
74 if (!dirs)
75 return 0;
76
77 fs_closedir(dirs);
78
79 return 1;
80}
81
82
83
84
85
86static int sanitize_path(char *path)
87{
88 char *p;
89
90
91 p = path;
92 while ((p = strchr(p, '\\')))
93 *p++ = '/';
94
95
96 p = path;
97 while ((p = strstr(p, "//"))) {
98 char *src = p + 1;
99 memmove(p, src, strlen(src) + 1);
100 }
101
102
103 p = path;
104 while ((p = strstr(p, "/."))) {
105
106
107
108
109
110
111
112
113 if (p[2] == '.') {
114 p += 2;
115 continue;
116 }
117 char *src = p + 2;
118 memmove(p, src, strlen(src) + 1);
119 }
120
121
122 p = path;
123 while ((p = strstr(p, "/.."))) {
124 char *src = p + 3;
125
126 p--;
127
128
129 while (true) {
130 if (p < path)
131 return -1;
132 if (*p == '/')
133 break;
134 p--;
135 }
136
137 memmove(p, src, strlen(src) + 1);
138 }
139
140 return 0;
141}
142
143
144
145
146
147
148
149
150static int efi_create_file(struct file_handle *fh, u64 attributes)
151{
152 loff_t actwrite;
153 void *buffer = &actwrite;
154
155 if (attributes & EFI_FILE_DIRECTORY)
156 return fs_mkdir(fh->path);
157 else
158 return fs_write(fh->path, map_to_sysmem(buffer), 0, 0,
159 &actwrite);
160}
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175static struct efi_file_handle *file_open(struct file_system *fs,
176 struct file_handle *parent, u16 *file_name, u64 open_mode,
177 u64 attributes)
178{
179 struct file_handle *fh;
180 char f0[MAX_UTF8_PER_UTF16] = {0};
181 int plen = 0;
182 int flen = 0;
183
184 if (file_name) {
185 utf16_to_utf8((u8 *)f0, file_name, 1);
186 flen = u16_strlen(file_name);
187 }
188
189
190 if (f0[0] == '\\') {
191 plen = 0;
192 } else if (parent) {
193 plen = strlen(parent->path) + 1;
194 }
195
196
197 fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
198
199 fh->open_mode = open_mode;
200 fh->base = efi_file_handle_protocol;
201 fh->fs = fs;
202
203 if (parent) {
204 char *p = fh->path;
205 int exists;
206
207 if (plen > 0) {
208 strcpy(p, parent->path);
209 p += plen - 1;
210 *p++ = '/';
211 }
212
213 utf16_to_utf8((u8 *)p, file_name, flen);
214
215 if (sanitize_path(fh->path))
216 goto error;
217
218
219 if (set_blk_dev(fh))
220 goto error;
221
222 exists = fs_exists(fh->path);
223
224 if (set_blk_dev(fh))
225 goto error;
226
227 if (!exists) {
228 if (!(open_mode & EFI_FILE_MODE_CREATE) ||
229 efi_create_file(fh, attributes))
230 goto error;
231 if (set_blk_dev(fh))
232 goto error;
233 }
234
235
236 fh->isdir = is_dir(fh);
237 } else {
238 fh->isdir = 1;
239 strcpy(fh->path, "");
240 }
241
242 return &fh->base;
243
244error:
245 free(fh);
246 return NULL;
247}
248
249static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
250 struct efi_file_handle **new_handle,
251 u16 *file_name, u64 open_mode, u64 attributes)
252{
253 struct file_handle *fh = to_fh(file);
254 efi_status_t ret;
255
256 EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle,
257 file_name, open_mode, attributes);
258
259
260 if (!file || !new_handle || !file_name) {
261 ret = EFI_INVALID_PARAMETER;
262 goto out;
263 }
264 if (open_mode != EFI_FILE_MODE_READ &&
265 open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
266 open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
267 EFI_FILE_MODE_CREATE)) {
268 ret = EFI_INVALID_PARAMETER;
269 goto out;
270 }
271
272
273
274
275
276
277
278
279
280 if ((open_mode & EFI_FILE_MODE_CREATE) &&
281 (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
282 ret = EFI_INVALID_PARAMETER;
283 goto out;
284 }
285
286
287 *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
288 if (*new_handle) {
289 EFI_PRINT("file handle %p\n", *new_handle);
290 ret = EFI_SUCCESS;
291 } else {
292 ret = EFI_NOT_FOUND;
293 }
294out:
295 return EFI_EXIT(ret);
296}
297
298static efi_status_t file_close(struct file_handle *fh)
299{
300 fs_closedir(fh->dirs);
301 free(fh);
302 return EFI_SUCCESS;
303}
304
305static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
306{
307 struct file_handle *fh = to_fh(file);
308 EFI_ENTRY("%p", file);
309 return EFI_EXIT(file_close(fh));
310}
311
312static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
313{
314 struct file_handle *fh = to_fh(file);
315 efi_status_t ret = EFI_SUCCESS;
316
317 EFI_ENTRY("%p", file);
318
319 if (set_blk_dev(fh) || fs_unlink(fh->path))
320 ret = EFI_WARN_DELETE_FAILURE;
321
322 file_close(fh);
323 return EFI_EXIT(ret);
324}
325
326
327
328
329
330
331
332
333static efi_status_t efi_get_file_size(struct file_handle *fh,
334 loff_t *file_size)
335{
336 if (set_blk_dev(fh))
337 return EFI_DEVICE_ERROR;
338
339 if (fs_size(fh->path, file_size))
340 return EFI_DEVICE_ERROR;
341
342 return EFI_SUCCESS;
343}
344
345static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
346 void *buffer)
347{
348 loff_t actread;
349 efi_status_t ret;
350 loff_t file_size;
351
352 if (!buffer) {
353 ret = EFI_INVALID_PARAMETER;
354 return ret;
355 }
356
357 ret = efi_get_file_size(fh, &file_size);
358 if (ret != EFI_SUCCESS)
359 return ret;
360 if (file_size < fh->offset) {
361 ret = EFI_DEVICE_ERROR;
362 return ret;
363 }
364
365 if (set_blk_dev(fh))
366 return EFI_DEVICE_ERROR;
367 if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
368 *buffer_size, &actread))
369 return EFI_DEVICE_ERROR;
370
371 *buffer_size = actread;
372 fh->offset += actread;
373
374 return EFI_SUCCESS;
375}
376
377static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
378 void *buffer)
379{
380 struct efi_file_info *info = buffer;
381 struct fs_dirent *dent;
382 u64 required_size;
383 u16 *dst;
384
385 if (set_blk_dev(fh))
386 return EFI_DEVICE_ERROR;
387
388 if (!fh->dirs) {
389 assert(fh->offset == 0);
390 fh->dirs = fs_opendir(fh->path);
391 if (!fh->dirs)
392 return EFI_DEVICE_ERROR;
393 fh->dent = NULL;
394 }
395
396
397
398
399
400
401
402 if (fh->dent) {
403 dent = fh->dent;
404 } else {
405 dent = fs_readdir(fh->dirs);
406 }
407
408 if (!dent) {
409
410 *buffer_size = 0;
411 return EFI_SUCCESS;
412 }
413
414
415 required_size = sizeof(*info) +
416 2 * (utf8_utf16_strlen(dent->name) + 1);
417 if (*buffer_size < required_size) {
418 *buffer_size = required_size;
419 fh->dent = dent;
420 return EFI_BUFFER_TOO_SMALL;
421 }
422 if (!buffer)
423 return EFI_INVALID_PARAMETER;
424 fh->dent = NULL;
425
426 *buffer_size = required_size;
427 memset(info, 0, required_size);
428
429 info->size = required_size;
430 info->file_size = dent->size;
431 info->physical_size = dent->size;
432
433 if (dent->type == FS_DT_DIR)
434 info->attribute |= EFI_FILE_DIRECTORY;
435
436 dst = info->file_name;
437 utf8_utf16_strcpy(&dst, dent->name);
438
439 fh->offset++;
440
441 return EFI_SUCCESS;
442}
443
444static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
445 efi_uintn_t *buffer_size, void *buffer)
446{
447 struct file_handle *fh = to_fh(file);
448 efi_status_t ret = EFI_SUCCESS;
449 u64 bs;
450
451 EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
452
453 if (!buffer_size) {
454 ret = EFI_INVALID_PARAMETER;
455 goto error;
456 }
457
458 bs = *buffer_size;
459 if (fh->isdir)
460 ret = dir_read(fh, &bs, buffer);
461 else
462 ret = file_read(fh, &bs, buffer);
463 if (bs <= SIZE_MAX)
464 *buffer_size = bs;
465 else
466 *buffer_size = SIZE_MAX;
467
468error:
469 return EFI_EXIT(ret);
470}
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
486 efi_uintn_t *buffer_size,
487 void *buffer)
488{
489 struct file_handle *fh = to_fh(file);
490 efi_status_t ret = EFI_SUCCESS;
491 loff_t actwrite;
492
493 EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
494
495 if (!file || !buffer_size || !buffer) {
496 ret = EFI_INVALID_PARAMETER;
497 goto out;
498 }
499 if (fh->isdir) {
500 ret = EFI_UNSUPPORTED;
501 goto out;
502 }
503 if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) {
504 ret = EFI_ACCESS_DENIED;
505 goto out;
506 }
507
508 if (!*buffer_size)
509 goto out;
510
511 if (set_blk_dev(fh)) {
512 ret = EFI_DEVICE_ERROR;
513 goto out;
514 }
515 if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
516 &actwrite)) {
517 ret = EFI_DEVICE_ERROR;
518 goto out;
519 }
520 *buffer_size = actwrite;
521 fh->offset += actwrite;
522
523out:
524 return EFI_EXIT(ret);
525}
526
527
528
529
530
531
532
533
534
535
536
537static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
538 u64 *pos)
539{
540 efi_status_t ret = EFI_SUCCESS;
541 struct file_handle *fh = to_fh(file);
542
543 EFI_ENTRY("%p, %p", file, pos);
544
545 if (fh->isdir) {
546 ret = EFI_UNSUPPORTED;
547 goto out;
548 }
549
550 *pos = fh->offset;
551out:
552 return EFI_EXIT(ret);
553}
554
555
556
557
558
559
560
561
562
563
564
565static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
566 u64 pos)
567{
568 struct file_handle *fh = to_fh(file);
569 efi_status_t ret = EFI_SUCCESS;
570
571 EFI_ENTRY("%p, %llu", file, pos);
572
573 if (fh->isdir) {
574 if (pos != 0) {
575 ret = EFI_UNSUPPORTED;
576 goto error;
577 }
578 fs_closedir(fh->dirs);
579 fh->dirs = NULL;
580 }
581
582 if (pos == ~0ULL) {
583 loff_t file_size;
584
585 ret = efi_get_file_size(fh, &file_size);
586 if (ret != EFI_SUCCESS)
587 goto error;
588 pos = file_size;
589 }
590
591 fh->offset = pos;
592
593error:
594 return EFI_EXIT(ret);
595}
596
597static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
598 const efi_guid_t *info_type,
599 efi_uintn_t *buffer_size,
600 void *buffer)
601{
602 struct file_handle *fh = to_fh(file);
603 efi_status_t ret = EFI_SUCCESS;
604 u16 *dst;
605
606 EFI_ENTRY("%p, %pUl, %p, %p", file, info_type, buffer_size, buffer);
607
608 if (!file || !info_type || !buffer_size ||
609 (*buffer_size && !buffer)) {
610 ret = EFI_INVALID_PARAMETER;
611 goto error;
612 }
613
614 if (!guidcmp(info_type, &efi_file_info_guid)) {
615 struct efi_file_info *info = buffer;
616 char *filename = basename(fh);
617 unsigned int required_size;
618 loff_t file_size;
619
620
621 required_size = sizeof(*info) +
622 2 * (utf8_utf16_strlen(filename) + 1);
623 if (*buffer_size < required_size) {
624 *buffer_size = required_size;
625 ret = EFI_BUFFER_TOO_SMALL;
626 goto error;
627 }
628
629 ret = efi_get_file_size(fh, &file_size);
630 if (ret != EFI_SUCCESS)
631 goto error;
632
633 memset(info, 0, required_size);
634
635 info->size = required_size;
636 info->file_size = file_size;
637 info->physical_size = file_size;
638
639 if (fh->isdir)
640 info->attribute |= EFI_FILE_DIRECTORY;
641
642 dst = info->file_name;
643 utf8_utf16_strcpy(&dst, filename);
644 } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
645 struct efi_file_system_info *info = buffer;
646 struct disk_partition part;
647 efi_uintn_t required_size;
648 int r;
649
650 if (fh->fs->part >= 1)
651 r = part_get_info(fh->fs->desc, fh->fs->part, &part);
652 else
653 r = part_get_info_whole_disk(fh->fs->desc, &part);
654 if (r < 0) {
655 ret = EFI_DEVICE_ERROR;
656 goto error;
657 }
658 required_size = sizeof(*info) + 2;
659 if (*buffer_size < required_size) {
660 *buffer_size = required_size;
661 ret = EFI_BUFFER_TOO_SMALL;
662 goto error;
663 }
664
665 memset(info, 0, required_size);
666
667 info->size = required_size;
668
669
670
671 info->read_only = false;
672 info->volume_size = part.size * part.blksz;
673
674
675
676
677 info->free_space = info->volume_size;
678 info->block_size = part.blksz;
679
680
681
682 info->volume_label[0] = 0;
683 } else if (!guidcmp(info_type, &efi_system_volume_label_id)) {
684 if (*buffer_size < 2) {
685 *buffer_size = 2;
686 ret = EFI_BUFFER_TOO_SMALL;
687 goto error;
688 }
689 *(u16 *)buffer = 0;
690 } else {
691 ret = EFI_UNSUPPORTED;
692 }
693
694error:
695 return EFI_EXIT(ret);
696}
697
698static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
699 const efi_guid_t *info_type,
700 efi_uintn_t buffer_size,
701 void *buffer)
702{
703 struct file_handle *fh = to_fh(file);
704 efi_status_t ret = EFI_UNSUPPORTED;
705
706 EFI_ENTRY("%p, %pUl, %zu, %p", file, info_type, buffer_size, buffer);
707
708 if (!guidcmp(info_type, &efi_file_info_guid)) {
709 struct efi_file_info *info = (struct efi_file_info *)buffer;
710 char *filename = basename(fh);
711 char *new_file_name, *pos;
712 loff_t file_size;
713
714
715 if (buffer_size < sizeof(struct efi_file_info) + 2 ||
716 buffer_size < info->size) {
717 ret = EFI_BAD_BUFFER_SIZE;
718 goto out;
719 }
720
721 if (!fh->isdir != !(info->attribute & EFI_FILE_DIRECTORY)) {
722 ret = EFI_ACCESS_DENIED;
723 goto out;
724 }
725
726 new_file_name = malloc(utf16_utf8_strlen(info->file_name) + 1);
727 if (!new_file_name) {
728 ret = EFI_OUT_OF_RESOURCES;
729 goto out;
730 }
731 pos = new_file_name;
732 utf16_utf8_strcpy(&pos, info->file_name);
733 if (strcmp(new_file_name, filename)) {
734
735 EFI_PRINT("Renaming not supported\n");
736 free(new_file_name);
737 ret = EFI_ACCESS_DENIED;
738 goto out;
739 }
740 free(new_file_name);
741
742 ret = efi_get_file_size(fh, &file_size);
743 if (ret != EFI_SUCCESS)
744 goto out;
745 if (file_size != info->file_size) {
746
747 EFI_PRINT("Truncation not supported\n");
748 ret = EFI_ACCESS_DENIED;
749 goto out;
750 }
751
752
753
754
755 ret = EFI_SUCCESS;
756 } else {
757
758 ret = EFI_UNSUPPORTED;
759 }
760out:
761 return EFI_EXIT(ret);
762}
763
764static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
765{
766 EFI_ENTRY("%p", file);
767 return EFI_EXIT(EFI_SUCCESS);
768}
769
770static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *file,
771 struct efi_file_handle **new_handle,
772 u16 *file_name, u64 open_mode, u64 attributes,
773 struct efi_file_io_token *token)
774{
775 return EFI_UNSUPPORTED;
776}
777
778static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *file,
779 struct efi_file_io_token *token)
780{
781 return EFI_UNSUPPORTED;
782}
783
784static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *file,
785 struct efi_file_io_token *token)
786{
787 return EFI_UNSUPPORTED;
788}
789
790static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *file,
791 struct efi_file_io_token *token)
792{
793 return EFI_UNSUPPORTED;
794}
795
796static const struct efi_file_handle efi_file_handle_protocol = {
797 .rev = EFI_FILE_PROTOCOL_REVISION2,
798 .open = efi_file_open,
799 .close = efi_file_close,
800 .delete = efi_file_delete,
801 .read = efi_file_read,
802 .write = efi_file_write,
803 .getpos = efi_file_getpos,
804 .setpos = efi_file_setpos,
805 .getinfo = efi_file_getinfo,
806 .setinfo = efi_file_setinfo,
807 .flush = efi_file_flush,
808 .open_ex = efi_file_open_ex,
809 .read_ex = efi_file_read_ex,
810 .write_ex = efi_file_write_ex,
811 .flush_ex = efi_file_flush_ex,
812};
813
814
815
816
817
818
819
820struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
821{
822 struct efi_simple_file_system_protocol *v;
823 struct efi_file_handle *f;
824 efi_status_t ret;
825
826 v = efi_fs_from_path(fp);
827 if (!v)
828 return NULL;
829
830 EFI_CALL(ret = v->open_volume(v, &f));
831 if (ret != EFI_SUCCESS)
832 return NULL;
833
834
835 while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
836 fp = efi_dp_next(fp);
837
838
839
840
841
842 while (fp) {
843 struct efi_device_path_file_path *fdp =
844 container_of(fp, struct efi_device_path_file_path, dp);
845 struct efi_file_handle *f2;
846 u16 *filename;
847
848 if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
849 printf("bad file path!\n");
850 f->close(f);
851 return NULL;
852 }
853
854 filename = u16_strdup(fdp->str);
855 if (!filename)
856 return NULL;
857 EFI_CALL(ret = f->open(f, &f2, filename,
858 EFI_FILE_MODE_READ, 0));
859 free(filename);
860 if (ret != EFI_SUCCESS)
861 return NULL;
862
863 fp = efi_dp_next(fp);
864
865 EFI_CALL(f->close(f));
866 f = f2;
867 }
868
869 return f;
870}
871
872static efi_status_t EFIAPI
873efi_open_volume(struct efi_simple_file_system_protocol *this,
874 struct efi_file_handle **root)
875{
876 struct file_system *fs = to_fs(this);
877
878 EFI_ENTRY("%p, %p", this, root);
879
880 *root = file_open(fs, NULL, NULL, 0, 0);
881
882 return EFI_EXIT(EFI_SUCCESS);
883}
884
885struct efi_simple_file_system_protocol *
886efi_simple_file_system(struct blk_desc *desc, int part,
887 struct efi_device_path *dp)
888{
889 struct file_system *fs;
890
891 fs = calloc(1, sizeof(*fs));
892 fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
893 fs->base.open_volume = efi_open_volume;
894 fs->desc = desc;
895 fs->part = part;
896 fs->dp = dp;
897
898 return &fs->base;
899}
900