]>
Commit | Line | Data |
---|---|---|
f739fcd8 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2a92080d RC |
2 | /* |
3 | * EFI utils | |
4 | * | |
5 | * Copyright (c) 2017 Rob Clark | |
2a92080d RC |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <charset.h> | |
10 | #include <efi_loader.h> | |
11 | #include <malloc.h> | |
d5a5a5a7 | 12 | #include <mapmem.h> |
2a92080d RC |
13 | #include <fs.h> |
14 | ||
9e6835e6 HS |
15 | /* GUID for file system information */ |
16 | const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID; | |
17 | ||
2a92080d RC |
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 | ||
2c61e0cc HS |
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 | */ | |
2a92080d RC |
63 | static int is_dir(struct file_handle *fh) |
64 | { | |
65 | struct fs_dir_stream *dirs; | |
66 | ||
2a92080d RC |
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 | ||
050cea77 HS |
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 | |
2a92080d RC |
149 | */ |
150 | static struct efi_file_handle *file_open(struct file_system *fs, | |
c82f8f60 | 151 | struct file_handle *parent, u16 *file_name, u64 mode, |
5bc84a13 | 152 | u64 attributes) |
2a92080d RC |
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) { | |
c82f8f60 HS |
160 | utf16_to_utf8((u8 *)f0, file_name, 1); |
161 | flen = u16_strlen(file_name); | |
2a92080d RC |
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 | ||
c82f8f60 | 186 | utf16_to_utf8((u8 *)p, file_name, flen); |
2a92080d RC |
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 | ||
5bc84a13 AT |
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))) | |
2a92080d RC |
201 | goto error; |
202 | ||
823c233b HS |
203 | /* fs_exists() calls fs_close(), so open file system again */ |
204 | if (set_blk_dev(fh)) | |
205 | goto error; | |
206 | ||
2a92080d RC |
207 | /* figure out if file is a directory: */ |
208 | fh->isdir = is_dir(fh); | |
209 | } else { | |
210 | fh->isdir = 1; | |
211 | strcpy(fh->path, ""); | |
212 | } | |
213 | ||
214 | return &fh->base; | |
215 | ||
216 | error: | |
217 | free(fh); | |
218 | return NULL; | |
219 | } | |
220 | ||
221 | static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file, | |
222 | struct efi_file_handle **new_handle, | |
c82f8f60 | 223 | u16 *file_name, u64 open_mode, u64 attributes) |
2a92080d RC |
224 | { |
225 | struct file_handle *fh = to_fh(file); | |
143acd1e | 226 | efi_status_t ret; |
2a92080d | 227 | |
0c89a318 SG |
228 | EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle, |
229 | (wchar_t *)file_name, open_mode, attributes); | |
2a92080d | 230 | |
143acd1e | 231 | /* Check parameters */ |
d3dce35a | 232 | if (!file || !new_handle || !file_name) { |
143acd1e HS |
233 | ret = EFI_INVALID_PARAMETER; |
234 | goto out; | |
235 | } | |
236 | if (open_mode != EFI_FILE_MODE_READ && | |
237 | open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) && | |
238 | open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | | |
239 | EFI_FILE_MODE_CREATE)) { | |
240 | ret = EFI_INVALID_PARAMETER; | |
241 | goto out; | |
242 | } | |
bd665886 HS |
243 | /* |
244 | * The UEFI spec requires that attributes are only set in create mode. | |
245 | * The SCT does not care about this and sets EFI_FILE_DIRECTORY in | |
246 | * read mode. EDK2 does not check that attributes are zero if not in | |
247 | * create mode. | |
248 | * | |
249 | * So here we only check attributes in create mode and do not check | |
250 | * that they are zero otherwise. | |
251 | */ | |
252 | if ((open_mode & EFI_FILE_MODE_CREATE) && | |
143acd1e HS |
253 | (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) { |
254 | ret = EFI_INVALID_PARAMETER; | |
255 | goto out; | |
256 | } | |
2a92080d | 257 | |
143acd1e HS |
258 | /* Open file */ |
259 | *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes); | |
260 | if (*new_handle) | |
261 | ret = EFI_SUCCESS; | |
262 | else | |
263 | ret = EFI_NOT_FOUND; | |
264 | out: | |
265 | return EFI_EXIT(ret); | |
2a92080d RC |
266 | } |
267 | ||
268 | static efi_status_t file_close(struct file_handle *fh) | |
269 | { | |
270 | fs_closedir(fh->dirs); | |
271 | free(fh); | |
272 | return EFI_SUCCESS; | |
273 | } | |
274 | ||
275 | static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file) | |
276 | { | |
277 | struct file_handle *fh = to_fh(file); | |
278 | EFI_ENTRY("%p", file); | |
279 | return EFI_EXIT(file_close(fh)); | |
280 | } | |
281 | ||
282 | static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file) | |
283 | { | |
284 | struct file_handle *fh = to_fh(file); | |
d39f6a61 AT |
285 | efi_status_t ret = EFI_SUCCESS; |
286 | ||
2a92080d | 287 | EFI_ENTRY("%p", file); |
d39f6a61 AT |
288 | |
289 | if (set_blk_dev(fh)) { | |
290 | ret = EFI_DEVICE_ERROR; | |
291 | goto error; | |
292 | } | |
293 | ||
294 | if (fs_unlink(fh->path)) | |
295 | ret = EFI_DEVICE_ERROR; | |
2a92080d | 296 | file_close(fh); |
d39f6a61 AT |
297 | |
298 | error: | |
299 | return EFI_EXIT(ret); | |
2a92080d RC |
300 | } |
301 | ||
302 | static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size, | |
303 | void *buffer) | |
304 | { | |
305 | loff_t actread; | |
306 | ||
2ae843f7 | 307 | if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset, |
2a92080d RC |
308 | *buffer_size, &actread)) |
309 | return EFI_DEVICE_ERROR; | |
310 | ||
311 | *buffer_size = actread; | |
312 | fh->offset += actread; | |
313 | ||
314 | return EFI_SUCCESS; | |
315 | } | |
316 | ||
317 | static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size, | |
318 | void *buffer) | |
319 | { | |
320 | struct efi_file_info *info = buffer; | |
321 | struct fs_dirent *dent; | |
322 | unsigned int required_size; | |
323 | ||
324 | if (!fh->dirs) { | |
325 | assert(fh->offset == 0); | |
326 | fh->dirs = fs_opendir(fh->path); | |
327 | if (!fh->dirs) | |
328 | return EFI_DEVICE_ERROR; | |
329 | } | |
330 | ||
331 | /* | |
332 | * So this is a bit awkward. Since fs layer is stateful and we | |
333 | * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below | |
334 | * we might have to return without consuming the dent.. so we | |
335 | * have to stash it for next call. | |
336 | */ | |
337 | if (fh->dent) { | |
338 | dent = fh->dent; | |
339 | fh->dent = NULL; | |
340 | } else { | |
341 | dent = fs_readdir(fh->dirs); | |
342 | } | |
343 | ||
344 | ||
345 | if (!dent) { | |
346 | /* no more files in directory: */ | |
347 | /* workaround shim.efi bug/quirk.. as find_boot_csv() | |
348 | * loops through directory contents, it initially calls | |
349 | * read w/ zero length buffer to find out how much mem | |
350 | * to allocate for the EFI_FILE_INFO, then allocates, | |
351 | * and then calls a 2nd time. If we return size of | |
352 | * zero the first time, it happily passes that to | |
353 | * AllocateZeroPool(), and when that returns NULL it | |
354 | * thinks it is EFI_OUT_OF_RESOURCES. So on first | |
355 | * call return a non-zero size: | |
356 | */ | |
357 | if (*buffer_size == 0) | |
358 | *buffer_size = sizeof(*info); | |
359 | else | |
360 | *buffer_size = 0; | |
361 | return EFI_SUCCESS; | |
362 | } | |
363 | ||
364 | /* check buffer size: */ | |
365 | required_size = sizeof(*info) + 2 * (strlen(dent->name) + 1); | |
366 | if (*buffer_size < required_size) { | |
367 | *buffer_size = required_size; | |
368 | fh->dent = dent; | |
369 | return EFI_BUFFER_TOO_SMALL; | |
370 | } | |
371 | ||
372 | *buffer_size = required_size; | |
373 | memset(info, 0, required_size); | |
374 | ||
375 | info->size = required_size; | |
376 | info->file_size = dent->size; | |
377 | info->physical_size = dent->size; | |
378 | ||
379 | if (dent->type == FS_DT_DIR) | |
380 | info->attribute |= EFI_FILE_DIRECTORY; | |
381 | ||
c82f8f60 | 382 | ascii2unicode(info->file_name, dent->name); |
2a92080d RC |
383 | |
384 | fh->offset++; | |
385 | ||
386 | return EFI_SUCCESS; | |
387 | } | |
388 | ||
389 | static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file, | |
b6dd5777 | 390 | efi_uintn_t *buffer_size, void *buffer) |
2a92080d RC |
391 | { |
392 | struct file_handle *fh = to_fh(file); | |
393 | efi_status_t ret = EFI_SUCCESS; | |
b6dd5777 | 394 | u64 bs; |
2a92080d RC |
395 | |
396 | EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer); | |
397 | ||
b6dd5777 HS |
398 | if (!buffer_size || !buffer) { |
399 | ret = EFI_INVALID_PARAMETER; | |
400 | goto error; | |
401 | } | |
402 | ||
2a92080d RC |
403 | if (set_blk_dev(fh)) { |
404 | ret = EFI_DEVICE_ERROR; | |
405 | goto error; | |
406 | } | |
407 | ||
b6dd5777 | 408 | bs = *buffer_size; |
2a92080d | 409 | if (fh->isdir) |
b6dd5777 | 410 | ret = dir_read(fh, &bs, buffer); |
2a92080d | 411 | else |
b6dd5777 HS |
412 | ret = file_read(fh, &bs, buffer); |
413 | if (bs <= SIZE_MAX) | |
414 | *buffer_size = bs; | |
415 | else | |
416 | *buffer_size = SIZE_MAX; | |
2a92080d RC |
417 | |
418 | error: | |
419 | return EFI_EXIT(ret); | |
420 | } | |
421 | ||
422 | static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file, | |
b6dd5777 HS |
423 | efi_uintn_t *buffer_size, |
424 | void *buffer) | |
2a92080d RC |
425 | { |
426 | struct file_handle *fh = to_fh(file); | |
427 | efi_status_t ret = EFI_SUCCESS; | |
428 | loff_t actwrite; | |
429 | ||
430 | EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer); | |
431 | ||
432 | if (set_blk_dev(fh)) { | |
433 | ret = EFI_DEVICE_ERROR; | |
434 | goto error; | |
435 | } | |
436 | ||
2ae843f7 | 437 | if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size, |
2a92080d RC |
438 | &actwrite)) { |
439 | ret = EFI_DEVICE_ERROR; | |
440 | goto error; | |
441 | } | |
442 | ||
443 | *buffer_size = actwrite; | |
444 | fh->offset += actwrite; | |
445 | ||
446 | error: | |
447 | return EFI_EXIT(ret); | |
448 | } | |
449 | ||
0801d4d2 HS |
450 | /** |
451 | * efi_file_getpos() - get current position in file | |
452 | * | |
453 | * This function implements the GetPosition service of the EFI file protocol. | |
454 | * See the UEFI spec for details. | |
455 | * | |
456 | * @file: file handle | |
457 | * @pos: pointer to file position | |
458 | * Return: status code | |
459 | */ | |
2a92080d | 460 | static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file, |
0801d4d2 | 461 | u64 *pos) |
2a92080d | 462 | { |
0801d4d2 | 463 | efi_status_t ret = EFI_SUCCESS; |
2a92080d | 464 | struct file_handle *fh = to_fh(file); |
b6dd5777 | 465 | |
2a92080d | 466 | EFI_ENTRY("%p, %p", file, pos); |
b6dd5777 | 467 | |
0801d4d2 HS |
468 | if (fh->isdir) { |
469 | ret = EFI_UNSUPPORTED; | |
470 | goto out; | |
b6dd5777 | 471 | } |
0801d4d2 HS |
472 | |
473 | *pos = fh->offset; | |
474 | out: | |
475 | return EFI_EXIT(ret); | |
2a92080d RC |
476 | } |
477 | ||
0801d4d2 HS |
478 | /** |
479 | * efi_file_setpos() - set current position in file | |
480 | * | |
481 | * This function implements the SetPosition service of the EFI file protocol. | |
482 | * See the UEFI spec for details. | |
483 | * | |
484 | * @file: file handle | |
485 | * @pos: new file position | |
486 | * Return: status code | |
487 | */ | |
2a92080d | 488 | static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file, |
0801d4d2 | 489 | u64 pos) |
2a92080d RC |
490 | { |
491 | struct file_handle *fh = to_fh(file); | |
492 | efi_status_t ret = EFI_SUCCESS; | |
493 | ||
0801d4d2 | 494 | EFI_ENTRY("%p, %llu", file, pos); |
2a92080d RC |
495 | |
496 | if (fh->isdir) { | |
497 | if (pos != 0) { | |
498 | ret = EFI_UNSUPPORTED; | |
499 | goto error; | |
500 | } | |
501 | fs_closedir(fh->dirs); | |
502 | fh->dirs = NULL; | |
503 | } | |
504 | ||
505 | if (pos == ~0ULL) { | |
506 | loff_t file_size; | |
507 | ||
508 | if (set_blk_dev(fh)) { | |
509 | ret = EFI_DEVICE_ERROR; | |
510 | goto error; | |
511 | } | |
512 | ||
513 | if (fs_size(fh->path, &file_size)) { | |
514 | ret = EFI_DEVICE_ERROR; | |
515 | goto error; | |
516 | } | |
517 | ||
518 | pos = file_size; | |
519 | } | |
520 | ||
521 | fh->offset = pos; | |
522 | ||
523 | error: | |
524 | return EFI_EXIT(ret); | |
525 | } | |
526 | ||
527 | static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file, | |
9c9021e2 | 528 | const efi_guid_t *info_type, |
b6dd5777 HS |
529 | efi_uintn_t *buffer_size, |
530 | void *buffer) | |
2a92080d RC |
531 | { |
532 | struct file_handle *fh = to_fh(file); | |
533 | efi_status_t ret = EFI_SUCCESS; | |
534 | ||
008fb489 | 535 | EFI_ENTRY("%p, %pUl, %p, %p", file, info_type, buffer_size, buffer); |
2a92080d RC |
536 | |
537 | if (!guidcmp(info_type, &efi_file_info_guid)) { | |
538 | struct efi_file_info *info = buffer; | |
539 | char *filename = basename(fh); | |
540 | unsigned int required_size; | |
541 | loff_t file_size; | |
542 | ||
543 | /* check buffer size: */ | |
544 | required_size = sizeof(*info) + 2 * (strlen(filename) + 1); | |
545 | if (*buffer_size < required_size) { | |
546 | *buffer_size = required_size; | |
547 | ret = EFI_BUFFER_TOO_SMALL; | |
548 | goto error; | |
549 | } | |
550 | ||
551 | if (set_blk_dev(fh)) { | |
552 | ret = EFI_DEVICE_ERROR; | |
553 | goto error; | |
554 | } | |
555 | ||
556 | if (fs_size(fh->path, &file_size)) { | |
557 | ret = EFI_DEVICE_ERROR; | |
558 | goto error; | |
559 | } | |
560 | ||
561 | memset(info, 0, required_size); | |
562 | ||
563 | info->size = required_size; | |
564 | info->file_size = file_size; | |
565 | info->physical_size = file_size; | |
566 | ||
567 | if (fh->isdir) | |
568 | info->attribute |= EFI_FILE_DIRECTORY; | |
569 | ||
02c2f029 | 570 | ascii2unicode(info->file_name, filename); |
9e6835e6 HS |
571 | } else if (!guidcmp(info_type, &efi_file_system_info_guid)) { |
572 | struct efi_file_system_info *info = buffer; | |
573 | disk_partition_t part; | |
574 | efi_uintn_t required_size; | |
575 | int r; | |
576 | ||
577 | if (fh->fs->part >= 1) | |
578 | r = part_get_info(fh->fs->desc, fh->fs->part, &part); | |
579 | else | |
580 | r = part_get_info_whole_disk(fh->fs->desc, &part); | |
581 | if (r < 0) { | |
582 | ret = EFI_DEVICE_ERROR; | |
583 | goto error; | |
584 | } | |
585 | required_size = sizeof(info) + 2 * | |
586 | (strlen((const char *)part.name) + 1); | |
587 | if (*buffer_size < required_size) { | |
588 | *buffer_size = required_size; | |
589 | ret = EFI_BUFFER_TOO_SMALL; | |
590 | goto error; | |
591 | } | |
592 | ||
593 | memset(info, 0, required_size); | |
594 | ||
595 | info->size = required_size; | |
596 | info->read_only = true; | |
597 | info->volume_size = part.size * part.blksz; | |
598 | info->free_space = 0; | |
599 | info->block_size = part.blksz; | |
600 | /* | |
601 | * TODO: The volume label is not available in U-Boot. | |
602 | * Use the partition name as substitute. | |
603 | */ | |
604 | ascii2unicode((u16 *)info->volume_label, | |
605 | (const char *)part.name); | |
2a92080d RC |
606 | } else { |
607 | ret = EFI_UNSUPPORTED; | |
608 | } | |
609 | ||
610 | error: | |
611 | return EFI_EXIT(ret); | |
612 | } | |
613 | ||
614 | static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file, | |
9c9021e2 | 615 | const efi_guid_t *info_type, |
b6dd5777 HS |
616 | efi_uintn_t buffer_size, |
617 | void *buffer) | |
2a92080d | 618 | { |
b6dd5777 HS |
619 | EFI_ENTRY("%p, %p, %zu, %p", file, info_type, buffer_size, buffer); |
620 | ||
2a92080d RC |
621 | return EFI_EXIT(EFI_UNSUPPORTED); |
622 | } | |
623 | ||
624 | static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file) | |
625 | { | |
626 | EFI_ENTRY("%p", file); | |
627 | return EFI_EXIT(EFI_SUCCESS); | |
628 | } | |
629 | ||
630 | static const struct efi_file_handle efi_file_handle_protocol = { | |
631 | .rev = EFI_FILE_PROTOCOL_REVISION, | |
632 | .open = efi_file_open, | |
633 | .close = efi_file_close, | |
634 | .delete = efi_file_delete, | |
635 | .read = efi_file_read, | |
636 | .write = efi_file_write, | |
637 | .getpos = efi_file_getpos, | |
638 | .setpos = efi_file_setpos, | |
639 | .getinfo = efi_file_getinfo, | |
640 | .setinfo = efi_file_setinfo, | |
641 | .flush = efi_file_flush, | |
642 | }; | |
643 | ||
644 | struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp) | |
645 | { | |
646 | struct efi_simple_file_system_protocol *v; | |
647 | struct efi_file_handle *f; | |
648 | efi_status_t ret; | |
649 | ||
650 | v = efi_fs_from_path(fp); | |
651 | if (!v) | |
652 | return NULL; | |
653 | ||
654 | EFI_CALL(ret = v->open_volume(v, &f)); | |
655 | if (ret != EFI_SUCCESS) | |
656 | return NULL; | |
657 | ||
658 | /* skip over device-path nodes before the file path: */ | |
659 | while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) | |
660 | fp = efi_dp_next(fp); | |
661 | ||
662 | while (fp) { | |
663 | struct efi_device_path_file_path *fdp = | |
664 | container_of(fp, struct efi_device_path_file_path, dp); | |
665 | struct efi_file_handle *f2; | |
666 | ||
667 | if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) { | |
668 | printf("bad file path!\n"); | |
669 | f->close(f); | |
670 | return NULL; | |
671 | } | |
672 | ||
c82f8f60 | 673 | EFI_CALL(ret = f->open(f, &f2, fdp->str, |
2a92080d RC |
674 | EFI_FILE_MODE_READ, 0)); |
675 | if (ret != EFI_SUCCESS) | |
676 | return NULL; | |
677 | ||
678 | fp = efi_dp_next(fp); | |
679 | ||
680 | EFI_CALL(f->close(f)); | |
681 | f = f2; | |
682 | } | |
683 | ||
684 | return f; | |
685 | } | |
686 | ||
687 | static efi_status_t EFIAPI | |
688 | efi_open_volume(struct efi_simple_file_system_protocol *this, | |
689 | struct efi_file_handle **root) | |
690 | { | |
691 | struct file_system *fs = to_fs(this); | |
692 | ||
693 | EFI_ENTRY("%p, %p", this, root); | |
694 | ||
5bc84a13 | 695 | *root = file_open(fs, NULL, NULL, 0, 0); |
2a92080d RC |
696 | |
697 | return EFI_EXIT(EFI_SUCCESS); | |
698 | } | |
699 | ||
700 | struct efi_simple_file_system_protocol * | |
701 | efi_simple_file_system(struct blk_desc *desc, int part, | |
702 | struct efi_device_path *dp) | |
703 | { | |
704 | struct file_system *fs; | |
705 | ||
706 | fs = calloc(1, sizeof(*fs)); | |
707 | fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION; | |
708 | fs->base.open_volume = efi_open_volume; | |
709 | fs->desc = desc; | |
710 | fs->part = part; | |
711 | fs->dp = dp; | |
712 | ||
713 | return &fs->base; | |
714 | } |