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