xref: /openbmc/u-boot/lib/efi_loader/efi_file.c (revision 30754ef7)
1 /*
2  *  EFI utils
3  *
4  *  Copyright (c) 2017 Rob Clark
5  *
6  *  SPDX-License-Identifier:     GPL-2.0+
7  */
8 
9 #include <common.h>
10 #include <charset.h>
11 #include <efi_loader.h>
12 #include <malloc.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 static int is_dir(struct file_handle *fh)
56 {
57 	struct fs_dir_stream *dirs;
58 
59 	set_blk_dev(fh);
60 	dirs = fs_opendir(fh->path);
61 	if (!dirs)
62 		return 0;
63 
64 	fs_closedir(dirs);
65 
66 	return 1;
67 }
68 
69 /*
70  * Normalize a path which may include either back or fwd slashes,
71  * double slashes, . or .. entries in the path, etc.
72  */
73 static int sanitize_path(char *path)
74 {
75 	char *p;
76 
77 	/* backslash to slash: */
78 	p = path;
79 	while ((p = strchr(p, '\\')))
80 		*p++ = '/';
81 
82 	/* handle double-slashes: */
83 	p = path;
84 	while ((p = strstr(p, "//"))) {
85 		char *src = p + 1;
86 		memmove(p, src, strlen(src) + 1);
87 	}
88 
89 	/* handle extra /.'s */
90 	p = path;
91 	while ((p = strstr(p, "/."))) {
92 		/*
93 		 * You'd be tempted to do this *after* handling ".."s
94 		 * below to avoid having to check if "/." is start of
95 		 * a "/..", but that won't have the correct results..
96 		 * for example, "/foo/./../bar" would get resolved to
97 		 * "/foo/bar" if you did these two passes in the other
98 		 * order
99 		 */
100 		if (p[2] == '.') {
101 			p += 2;
102 			continue;
103 		}
104 		char *src = p + 2;
105 		memmove(p, src, strlen(src) + 1);
106 	}
107 
108 	/* handle extra /..'s: */
109 	p = path;
110 	while ((p = strstr(p, "/.."))) {
111 		char *src = p + 3;
112 
113 		p--;
114 
115 		/* find beginning of previous path entry: */
116 		while (true) {
117 			if (p < path)
118 				return -1;
119 			if (*p == '/')
120 				break;
121 			p--;
122 		}
123 
124 		memmove(p, src, strlen(src) + 1);
125 	}
126 
127 	return 0;
128 }
129 
130 /* NOTE: despite what you would expect, 'file_name' is actually a path.
131  * With windoze style backlashes, ofc.
132  */
133 static struct efi_file_handle *file_open(struct file_system *fs,
134 		struct file_handle *parent, s16 *file_name, u64 mode)
135 {
136 	struct file_handle *fh;
137 	char f0[MAX_UTF8_PER_UTF16] = {0};
138 	int plen = 0;
139 	int flen = 0;
140 
141 	if (file_name) {
142 		utf16_to_utf8((u8 *)f0, (u16 *)file_name, 1);
143 		flen = utf16_strlen((u16 *)file_name);
144 	}
145 
146 	/* we could have a parent, but also an absolute path: */
147 	if (f0[0] == '\\') {
148 		plen = 0;
149 	} else if (parent) {
150 		plen = strlen(parent->path) + 1;
151 	}
152 
153 	/* +2 is for null and '/' */
154 	fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
155 
156 	fh->base = efi_file_handle_protocol;
157 	fh->fs = fs;
158 
159 	if (parent) {
160 		char *p = fh->path;
161 
162 		if (plen > 0) {
163 			strcpy(p, parent->path);
164 			p += plen - 1;
165 			*p++ = '/';
166 		}
167 
168 		utf16_to_utf8((u8 *)p, (u16 *)file_name, flen);
169 
170 		if (sanitize_path(fh->path))
171 			goto error;
172 
173 		/* check if file exists: */
174 		if (set_blk_dev(fh))
175 			goto error;
176 
177 		if (!((mode & EFI_FILE_MODE_CREATE) || fs_exists(fh->path)))
178 			goto error;
179 
180 		/* figure out if file is a directory: */
181 		fh->isdir = is_dir(fh);
182 	} else {
183 		fh->isdir = 1;
184 		strcpy(fh->path, "");
185 	}
186 
187 	return &fh->base;
188 
189 error:
190 	free(fh);
191 	return NULL;
192 }
193 
194 static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
195 		struct efi_file_handle **new_handle,
196 		s16 *file_name, u64 open_mode, u64 attributes)
197 {
198 	struct file_handle *fh = to_fh(file);
199 
200 	EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, file_name,
201 		  open_mode, attributes);
202 
203 	*new_handle = file_open(fh->fs, fh, file_name, open_mode);
204 	if (!*new_handle)
205 		return EFI_EXIT(EFI_NOT_FOUND);
206 
207 	return EFI_EXIT(EFI_SUCCESS);
208 }
209 
210 static efi_status_t file_close(struct file_handle *fh)
211 {
212 	fs_closedir(fh->dirs);
213 	free(fh);
214 	return EFI_SUCCESS;
215 }
216 
217 static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
218 {
219 	struct file_handle *fh = to_fh(file);
220 	EFI_ENTRY("%p", file);
221 	return EFI_EXIT(file_close(fh));
222 }
223 
224 static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
225 {
226 	struct file_handle *fh = to_fh(file);
227 	EFI_ENTRY("%p", file);
228 	file_close(fh);
229 	return EFI_EXIT(EFI_WARN_DELETE_FAILURE);
230 }
231 
232 static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
233 		void *buffer)
234 {
235 	loff_t actread;
236 
237 	if (fs_read(fh->path, (ulong)buffer, fh->offset,
238 		    *buffer_size, &actread))
239 		return EFI_DEVICE_ERROR;
240 
241 	*buffer_size = actread;
242 	fh->offset += actread;
243 
244 	return EFI_SUCCESS;
245 }
246 
247 static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
248 		void *buffer)
249 {
250 	struct efi_file_info *info = buffer;
251 	struct fs_dirent *dent;
252 	unsigned int required_size;
253 
254 	if (!fh->dirs) {
255 		assert(fh->offset == 0);
256 		fh->dirs = fs_opendir(fh->path);
257 		if (!fh->dirs)
258 			return EFI_DEVICE_ERROR;
259 	}
260 
261 	/*
262 	 * So this is a bit awkward.  Since fs layer is stateful and we
263 	 * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
264 	 * we might have to return without consuming the dent.. so we
265 	 * have to stash it for next call.
266 	 */
267 	if (fh->dent) {
268 		dent = fh->dent;
269 		fh->dent = NULL;
270 	} else {
271 		dent = fs_readdir(fh->dirs);
272 	}
273 
274 
275 	if (!dent) {
276 		/* no more files in directory: */
277 		/* workaround shim.efi bug/quirk.. as find_boot_csv()
278 		 * loops through directory contents, it initially calls
279 		 * read w/ zero length buffer to find out how much mem
280 		 * to allocate for the EFI_FILE_INFO, then allocates,
281 		 * and then calls a 2nd time.  If we return size of
282 		 * zero the first time, it happily passes that to
283 		 * AllocateZeroPool(), and when that returns NULL it
284 		 * thinks it is EFI_OUT_OF_RESOURCES.  So on first
285 		 * call return a non-zero size:
286 		 */
287 		if (*buffer_size == 0)
288 			*buffer_size = sizeof(*info);
289 		else
290 			*buffer_size = 0;
291 		return EFI_SUCCESS;
292 	}
293 
294 	/* check buffer size: */
295 	required_size = sizeof(*info) + 2 * (strlen(dent->name) + 1);
296 	if (*buffer_size < required_size) {
297 		*buffer_size = required_size;
298 		fh->dent = dent;
299 		return EFI_BUFFER_TOO_SMALL;
300 	}
301 
302 	*buffer_size = required_size;
303 	memset(info, 0, required_size);
304 
305 	info->size = required_size;
306 	info->file_size = dent->size;
307 	info->physical_size = dent->size;
308 
309 	if (dent->type == FS_DT_DIR)
310 		info->attribute |= EFI_FILE_DIRECTORY;
311 
312 	ascii2unicode((u16 *)info->file_name, dent->name);
313 
314 	fh->offset++;
315 
316 	return EFI_SUCCESS;
317 }
318 
319 static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
320 					 efi_uintn_t *buffer_size, void *buffer)
321 {
322 	struct file_handle *fh = to_fh(file);
323 	efi_status_t ret = EFI_SUCCESS;
324 	u64 bs;
325 
326 	EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
327 
328 	if (!buffer_size || !buffer) {
329 		ret = EFI_INVALID_PARAMETER;
330 		goto error;
331 	}
332 
333 	if (set_blk_dev(fh)) {
334 		ret = EFI_DEVICE_ERROR;
335 		goto error;
336 	}
337 
338 	bs = *buffer_size;
339 	if (fh->isdir)
340 		ret = dir_read(fh, &bs, buffer);
341 	else
342 		ret = file_read(fh, &bs, buffer);
343 	if (bs <= SIZE_MAX)
344 		*buffer_size = bs;
345 	else
346 		*buffer_size = SIZE_MAX;
347 
348 error:
349 	return EFI_EXIT(ret);
350 }
351 
352 static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
353 					  efi_uintn_t *buffer_size,
354 					  void *buffer)
355 {
356 	struct file_handle *fh = to_fh(file);
357 	efi_status_t ret = EFI_SUCCESS;
358 	loff_t actwrite;
359 
360 	EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
361 
362 	if (set_blk_dev(fh)) {
363 		ret = EFI_DEVICE_ERROR;
364 		goto error;
365 	}
366 
367 	if (fs_write(fh->path, (ulong)buffer, fh->offset, *buffer_size,
368 		     &actwrite)) {
369 		ret = EFI_DEVICE_ERROR;
370 		goto error;
371 	}
372 
373 	*buffer_size = actwrite;
374 	fh->offset += actwrite;
375 
376 error:
377 	return EFI_EXIT(ret);
378 }
379 
380 static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
381 					   efi_uintn_t *pos)
382 {
383 	struct file_handle *fh = to_fh(file);
384 
385 	EFI_ENTRY("%p, %p", file, pos);
386 
387 	if (fh->offset <= SIZE_MAX) {
388 		*pos = fh->offset;
389 		return EFI_EXIT(EFI_SUCCESS);
390 	} else {
391 		return EFI_EXIT(EFI_DEVICE_ERROR);
392 	}
393 }
394 
395 static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
396 		efi_uintn_t pos)
397 {
398 	struct file_handle *fh = to_fh(file);
399 	efi_status_t ret = EFI_SUCCESS;
400 
401 	EFI_ENTRY("%p, %zu", file, pos);
402 
403 	if (fh->isdir) {
404 		if (pos != 0) {
405 			ret = EFI_UNSUPPORTED;
406 			goto error;
407 		}
408 		fs_closedir(fh->dirs);
409 		fh->dirs = NULL;
410 	}
411 
412 	if (pos == ~0ULL) {
413 		loff_t file_size;
414 
415 		if (set_blk_dev(fh)) {
416 			ret = EFI_DEVICE_ERROR;
417 			goto error;
418 		}
419 
420 		if (fs_size(fh->path, &file_size)) {
421 			ret = EFI_DEVICE_ERROR;
422 			goto error;
423 		}
424 
425 		pos = file_size;
426 	}
427 
428 	fh->offset = pos;
429 
430 error:
431 	return EFI_EXIT(ret);
432 }
433 
434 static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
435 					    const efi_guid_t *info_type,
436 					    efi_uintn_t *buffer_size,
437 					    void *buffer)
438 {
439 	struct file_handle *fh = to_fh(file);
440 	efi_status_t ret = EFI_SUCCESS;
441 
442 	EFI_ENTRY("%p, %p, %p, %p", file, info_type, buffer_size, buffer);
443 
444 	if (!guidcmp(info_type, &efi_file_info_guid)) {
445 		struct efi_file_info *info = buffer;
446 		char *filename = basename(fh);
447 		unsigned int required_size;
448 		loff_t file_size;
449 
450 		/* check buffer size: */
451 		required_size = sizeof(*info) + 2 * (strlen(filename) + 1);
452 		if (*buffer_size < required_size) {
453 			*buffer_size = required_size;
454 			ret = EFI_BUFFER_TOO_SMALL;
455 			goto error;
456 		}
457 
458 		if (set_blk_dev(fh)) {
459 			ret = EFI_DEVICE_ERROR;
460 			goto error;
461 		}
462 
463 		if (fs_size(fh->path, &file_size)) {
464 			ret = EFI_DEVICE_ERROR;
465 			goto error;
466 		}
467 
468 		memset(info, 0, required_size);
469 
470 		info->size = required_size;
471 		info->file_size = file_size;
472 		info->physical_size = file_size;
473 
474 		if (fh->isdir)
475 			info->attribute |= EFI_FILE_DIRECTORY;
476 
477 		ascii2unicode((u16 *)info->file_name, filename);
478 	} else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
479 		struct efi_file_system_info *info = buffer;
480 		disk_partition_t part;
481 		efi_uintn_t required_size;
482 		int r;
483 
484 		if (fh->fs->part >= 1)
485 			r = part_get_info(fh->fs->desc, fh->fs->part, &part);
486 		else
487 			r = part_get_info_whole_disk(fh->fs->desc, &part);
488 		if (r < 0) {
489 			ret = EFI_DEVICE_ERROR;
490 			goto error;
491 		}
492 		required_size = sizeof(info) + 2 *
493 				(strlen((const char *)part.name) + 1);
494 		if (*buffer_size < required_size) {
495 			*buffer_size = required_size;
496 			ret = EFI_BUFFER_TOO_SMALL;
497 			goto error;
498 		}
499 
500 		memset(info, 0, required_size);
501 
502 		info->size = required_size;
503 		info->read_only = true;
504 		info->volume_size = part.size * part.blksz;
505 		info->free_space = 0;
506 		info->block_size = part.blksz;
507 		/*
508 		 * TODO: The volume label is not available in U-Boot.
509 		 * Use the partition name as substitute.
510 		 */
511 		ascii2unicode((u16 *)info->volume_label,
512 			      (const char *)part.name);
513 	} else {
514 		ret = EFI_UNSUPPORTED;
515 	}
516 
517 error:
518 	return EFI_EXIT(ret);
519 }
520 
521 static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
522 					    const efi_guid_t *info_type,
523 					    efi_uintn_t buffer_size,
524 					    void *buffer)
525 {
526 	EFI_ENTRY("%p, %p, %zu, %p", file, info_type, buffer_size, buffer);
527 
528 	return EFI_EXIT(EFI_UNSUPPORTED);
529 }
530 
531 static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
532 {
533 	EFI_ENTRY("%p", file);
534 	return EFI_EXIT(EFI_SUCCESS);
535 }
536 
537 static const struct efi_file_handle efi_file_handle_protocol = {
538 	.rev = EFI_FILE_PROTOCOL_REVISION,
539 	.open = efi_file_open,
540 	.close = efi_file_close,
541 	.delete = efi_file_delete,
542 	.read = efi_file_read,
543 	.write = efi_file_write,
544 	.getpos = efi_file_getpos,
545 	.setpos = efi_file_setpos,
546 	.getinfo = efi_file_getinfo,
547 	.setinfo = efi_file_setinfo,
548 	.flush = efi_file_flush,
549 };
550 
551 struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
552 {
553 	struct efi_simple_file_system_protocol *v;
554 	struct efi_file_handle *f;
555 	efi_status_t ret;
556 
557 	v = efi_fs_from_path(fp);
558 	if (!v)
559 		return NULL;
560 
561 	EFI_CALL(ret = v->open_volume(v, &f));
562 	if (ret != EFI_SUCCESS)
563 		return NULL;
564 
565 	/* skip over device-path nodes before the file path: */
566 	while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
567 		fp = efi_dp_next(fp);
568 
569 	while (fp) {
570 		struct efi_device_path_file_path *fdp =
571 			container_of(fp, struct efi_device_path_file_path, dp);
572 		struct efi_file_handle *f2;
573 
574 		if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
575 			printf("bad file path!\n");
576 			f->close(f);
577 			return NULL;
578 		}
579 
580 		EFI_CALL(ret = f->open(f, &f2, (s16 *)fdp->str,
581 				       EFI_FILE_MODE_READ, 0));
582 		if (ret != EFI_SUCCESS)
583 			return NULL;
584 
585 		fp = efi_dp_next(fp);
586 
587 		EFI_CALL(f->close(f));
588 		f = f2;
589 	}
590 
591 	return f;
592 }
593 
594 static efi_status_t EFIAPI
595 efi_open_volume(struct efi_simple_file_system_protocol *this,
596 		struct efi_file_handle **root)
597 {
598 	struct file_system *fs = to_fs(this);
599 
600 	EFI_ENTRY("%p, %p", this, root);
601 
602 	*root = file_open(fs, NULL, NULL, 0);
603 
604 	return EFI_EXIT(EFI_SUCCESS);
605 }
606 
607 struct efi_simple_file_system_protocol *
608 efi_simple_file_system(struct blk_desc *desc, int part,
609 		       struct efi_device_path *dp)
610 {
611 	struct file_system *fs;
612 
613 	fs = calloc(1, sizeof(*fs));
614 	fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
615 	fs->base.open_volume = efi_open_volume;
616 	fs->desc = desc;
617 	fs->part = part;
618 	fs->dp = dp;
619 
620 	return &fs->base;
621 }
622