]> git.sur5r.net Git - u-boot/blob - lib/efi_loader/efi_file.c
net: sun8i-emac: fix printing NULL character
[u-boot] / lib / efi_loader / efi_file.c
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 }