xref: /openbmc/u-boot/lib/efi_loader/efi_file.c (revision 9b23c73d)
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 }
621