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