blob: 201fa5f8f3cafd4b85f7dd2b6dd50725333ac511 [file] [log] [blame]
Tom Rini70df9d62018-05-07 17:02:21 -04001// SPDX-License-Identifier: GPL-2.0+
Rob Clark53142032017-09-13 18:05:34 -04002/*
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +02003 * EFI_FILE_PROTOCOL
Rob Clark53142032017-09-13 18:05:34 -04004 *
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +02005 * Copyright (c) 2017 Rob Clark
Rob Clark53142032017-09-13 18:05:34 -04006 */
7
Heinrich Schuchardt955a3212025-01-16 20:26:59 +01008#define LOG_CATEGORY LOGC_EFI
9
Rob Clark53142032017-09-13 18:05:34 -040010#include <charset.h>
11#include <efi_loader.h>
Simon Glass0f2af882020-05-10 11:40:05 -060012#include <log.h>
Rob Clark53142032017-09-13 18:05:34 -040013#include <malloc.h>
Alexander Grafab133c72018-08-08 03:54:32 -060014#include <mapmem.h>
Rob Clark53142032017-09-13 18:05:34 -040015#include <fs.h>
Simon Glass655306c2020-05-10 11:39:58 -060016#include <part.h>
Rob Clark53142032017-09-13 18:05:34 -040017
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +020018/* GUID for file system information */
19const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
20
Heinrich Schuchardt0c236c42019-09-08 10:32:54 +020021/* GUID to obtain the volume label */
22const efi_guid_t efi_system_volume_label_id = EFI_FILE_SYSTEM_VOLUME_LABEL_ID;
23
Rob Clark53142032017-09-13 18:05:34 -040024struct file_system {
25 struct efi_simple_file_system_protocol base;
26 struct efi_device_path *dp;
27 struct blk_desc *desc;
28 int part;
29};
30#define to_fs(x) container_of(x, struct file_system, base)
31
32struct file_handle {
33 struct efi_file_handle base;
34 struct file_system *fs;
35 loff_t offset; /* current file position/cursor */
36 int isdir;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +020037 u64 open_mode;
Rob Clark53142032017-09-13 18:05:34 -040038
39 /* for reading a directory: */
40 struct fs_dir_stream *dirs;
41 struct fs_dirent *dent;
42
43 char path[0];
44};
45#define to_fh(x) container_of(x, struct file_handle, base)
46
47static const struct efi_file_handle efi_file_handle_protocol;
48
49static char *basename(struct file_handle *fh)
50{
51 char *s = strrchr(fh->path, '/');
52 if (s)
53 return s + 1;
54 return fh->path;
55}
56
57static int set_blk_dev(struct file_handle *fh)
58{
59 return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
60}
61
Heinrich Schuchardt80f834d2018-10-02 05:57:32 +020062/**
63 * is_dir() - check if file handle points to directory
64 *
65 * We assume that set_blk_dev(fh) has been called already.
66 *
67 * @fh: file handle
68 * Return: true if file handle points to a directory
69 */
Rob Clark53142032017-09-13 18:05:34 -040070static int is_dir(struct file_handle *fh)
71{
72 struct fs_dir_stream *dirs;
73
Rob Clark53142032017-09-13 18:05:34 -040074 dirs = fs_opendir(fh->path);
75 if (!dirs)
76 return 0;
77
78 fs_closedir(dirs);
79
80 return 1;
81}
82
83/*
84 * Normalize a path which may include either back or fwd slashes,
85 * double slashes, . or .. entries in the path, etc.
86 */
87static int sanitize_path(char *path)
88{
89 char *p;
90
91 /* backslash to slash: */
92 p = path;
93 while ((p = strchr(p, '\\')))
94 *p++ = '/';
95
96 /* handle double-slashes: */
97 p = path;
98 while ((p = strstr(p, "//"))) {
99 char *src = p + 1;
100 memmove(p, src, strlen(src) + 1);
101 }
102
103 /* handle extra /.'s */
104 p = path;
105 while ((p = strstr(p, "/."))) {
106 /*
107 * You'd be tempted to do this *after* handling ".."s
108 * below to avoid having to check if "/." is start of
109 * a "/..", but that won't have the correct results..
110 * for example, "/foo/./../bar" would get resolved to
111 * "/foo/bar" if you did these two passes in the other
112 * order
113 */
114 if (p[2] == '.') {
115 p += 2;
116 continue;
117 }
118 char *src = p + 2;
119 memmove(p, src, strlen(src) + 1);
120 }
121
122 /* handle extra /..'s: */
123 p = path;
124 while ((p = strstr(p, "/.."))) {
125 char *src = p + 3;
126
127 p--;
128
129 /* find beginning of previous path entry: */
130 while (true) {
131 if (p < path)
132 return -1;
133 if (*p == '/')
134 break;
135 p--;
136 }
137
138 memmove(p, src, strlen(src) + 1);
139 }
140
141 return 0;
142}
143
Heinrich Schuchardt1af423b2018-09-12 19:00:02 +0200144/**
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200145 * efi_create_file() - create file or directory
146 *
147 * @fh: file handle
148 * @attributes: attributes for newly created file
149 * Returns: 0 for success
150 */
151static int efi_create_file(struct file_handle *fh, u64 attributes)
152{
153 loff_t actwrite;
154 void *buffer = &actwrite;
155
156 if (attributes & EFI_FILE_DIRECTORY)
157 return fs_mkdir(fh->path);
158 else
159 return fs_write(fh->path, map_to_sysmem(buffer), 0, 0,
160 &actwrite);
161}
162
163/**
Heinrich Schuchardt1af423b2018-09-12 19:00:02 +0200164 * file_open() - open a file handle
165 *
166 * @fs: file system
167 * @parent: directory relative to which the file is to be opened
168 * @file_name: path of the file to be opened. '\', '.', or '..' may
169 * be used as modifiers. A leading backslash indicates an
170 * absolute path.
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200171 * @open_mode: bit mask indicating the access mode (read, write,
Heinrich Schuchardt1af423b2018-09-12 19:00:02 +0200172 * create)
173 * @attributes: attributes for newly created file
174 * Returns: handle to the opened file or NULL
Rob Clark53142032017-09-13 18:05:34 -0400175 */
176static struct efi_file_handle *file_open(struct file_system *fs,
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200177 struct file_handle *parent, u16 *file_name, u64 open_mode,
AKASHI Takahiro0c672b02018-09-11 15:59:12 +0900178 u64 attributes)
Rob Clark53142032017-09-13 18:05:34 -0400179{
180 struct file_handle *fh;
181 char f0[MAX_UTF8_PER_UTF16] = {0};
182 int plen = 0;
183 int flen = 0;
184
185 if (file_name) {
Heinrich Schuchardtdb47c342019-01-12 12:02:33 +0100186 utf16_to_utf8((u8 *)f0, file_name, 1);
187 flen = u16_strlen(file_name);
Rob Clark53142032017-09-13 18:05:34 -0400188 }
189
190 /* we could have a parent, but also an absolute path: */
191 if (f0[0] == '\\') {
192 plen = 0;
193 } else if (parent) {
194 plen = strlen(parent->path) + 1;
195 }
196
197 /* +2 is for null and '/' */
198 fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
Heinrich Schuchardtf407af82023-07-30 14:06:06 +0200199 if (!fh)
200 return NULL;
Rob Clark53142032017-09-13 18:05:34 -0400201
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200202 fh->open_mode = open_mode;
Rob Clark53142032017-09-13 18:05:34 -0400203 fh->base = efi_file_handle_protocol;
204 fh->fs = fs;
205
206 if (parent) {
207 char *p = fh->path;
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200208 int exists;
Rob Clark53142032017-09-13 18:05:34 -0400209
210 if (plen > 0) {
211 strcpy(p, parent->path);
212 p += plen - 1;
213 *p++ = '/';
214 }
215
Heinrich Schuchardtdb47c342019-01-12 12:02:33 +0100216 utf16_to_utf8((u8 *)p, file_name, flen);
Rob Clark53142032017-09-13 18:05:34 -0400217
218 if (sanitize_path(fh->path))
219 goto error;
220
221 /* check if file exists: */
222 if (set_blk_dev(fh))
223 goto error;
224
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200225 exists = fs_exists(fh->path);
Heinrich Schuchardt3ab87ac2019-02-09 22:23:48 +0100226 /* fs_exists() calls fs_close(), so open file system again */
227 if (set_blk_dev(fh))
228 goto error;
229
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200230 if (!exists) {
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200231 if (!(open_mode & EFI_FILE_MODE_CREATE) ||
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200232 efi_create_file(fh, attributes))
233 goto error;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200234 if (set_blk_dev(fh))
235 goto error;
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200236 }
237
Rob Clark53142032017-09-13 18:05:34 -0400238 /* figure out if file is a directory: */
239 fh->isdir = is_dir(fh);
240 } else {
241 fh->isdir = 1;
242 strcpy(fh->path, "");
243 }
244
245 return &fh->base;
246
247error:
248 free(fh);
249 return NULL;
250}
251
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900252efi_status_t efi_file_open_int(struct efi_file_handle *this,
253 struct efi_file_handle **new_handle,
254 u16 *file_name, u64 open_mode,
255 u64 attributes)
Rob Clark53142032017-09-13 18:05:34 -0400256{
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100257 struct file_handle *fh = to_fh(this);
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200258 efi_status_t ret;
Rob Clark53142032017-09-13 18:05:34 -0400259
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200260 /* Check parameters */
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100261 if (!this || !new_handle || !file_name) {
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200262 ret = EFI_INVALID_PARAMETER;
263 goto out;
264 }
265 if (open_mode != EFI_FILE_MODE_READ &&
266 open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
267 open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
268 EFI_FILE_MODE_CREATE)) {
269 ret = EFI_INVALID_PARAMETER;
270 goto out;
271 }
Heinrich Schuchardt662d0be2018-09-13 21:31:49 +0200272 /*
273 * The UEFI spec requires that attributes are only set in create mode.
274 * The SCT does not care about this and sets EFI_FILE_DIRECTORY in
275 * read mode. EDK2 does not check that attributes are zero if not in
276 * create mode.
277 *
278 * So here we only check attributes in create mode and do not check
279 * that they are zero otherwise.
280 */
281 if ((open_mode & EFI_FILE_MODE_CREATE) &&
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200282 (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
283 ret = EFI_INVALID_PARAMETER;
284 goto out;
285 }
Rob Clark53142032017-09-13 18:05:34 -0400286
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200287 /* Open file */
288 *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
Heinrich Schuchardtd4fe5282019-04-06 16:36:16 +0200289 if (*new_handle) {
290 EFI_PRINT("file handle %p\n", *new_handle);
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200291 ret = EFI_SUCCESS;
Heinrich Schuchardtd4fe5282019-04-06 16:36:16 +0200292 } else {
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200293 ret = EFI_NOT_FOUND;
Heinrich Schuchardtd4fe5282019-04-06 16:36:16 +0200294 }
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100295out:
296 return ret;
297}
298
299/**
Heinrich Schuchardtb0102502024-09-18 23:37:28 +0200300 * efi_file_open() - open file synchronously
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100301 *
302 * This function implements the Open service of the File Protocol.
303 * See the UEFI spec for details.
304 *
305 * @this: EFI_FILE_PROTOCOL instance
306 * @new_handle: on return pointer to file handle
307 * @file_name: file name
308 * @open_mode: mode to open the file (read, read/write, create/read/write)
309 * @attributes: attributes for newly created file
310 */
311static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *this,
312 struct efi_file_handle **new_handle,
313 u16 *file_name, u64 open_mode,
314 u64 attributes)
315{
316 efi_status_t ret;
317
318 EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", this, new_handle,
319 file_name, open_mode, attributes);
320
321 ret = efi_file_open_int(this, new_handle, file_name, open_mode,
322 attributes);
323
324 return EFI_EXIT(ret);
325}
326
327/**
328 * efi_file_open_ex() - open file asynchronously
329 *
330 * This function implements the OpenEx service of the File Protocol.
331 * See the UEFI spec for details.
332 *
333 * @this: EFI_FILE_PROTOCOL instance
334 * @new_handle: on return pointer to file handle
335 * @file_name: file name
336 * @open_mode: mode to open the file (read, read/write, create/read/write)
337 * @attributes: attributes for newly created file
338 * @token: transaction token
339 */
340static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *this,
341 struct efi_file_handle **new_handle,
342 u16 *file_name, u64 open_mode,
343 u64 attributes,
344 struct efi_file_io_token *token)
345{
346 efi_status_t ret;
347
348 EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu, %p", this, new_handle,
349 file_name, open_mode, attributes, token);
350
351 if (!token) {
352 ret = EFI_INVALID_PARAMETER;
353 goto out;
354 }
355
356 ret = efi_file_open_int(this, new_handle, file_name, open_mode,
357 attributes);
358
359 if (ret == EFI_SUCCESS && token->event) {
360 token->status = EFI_SUCCESS;
361 efi_signal_event(token->event);
362 }
363
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200364out:
365 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400366}
367
368static efi_status_t file_close(struct file_handle *fh)
369{
370 fs_closedir(fh->dirs);
371 free(fh);
372 return EFI_SUCCESS;
373}
374
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900375efi_status_t efi_file_close_int(struct efi_file_handle *file)
Rob Clark53142032017-09-13 18:05:34 -0400376{
377 struct file_handle *fh = to_fh(file);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900378
379 return file_close(fh);
380}
381
382static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
383{
Rob Clark53142032017-09-13 18:05:34 -0400384 EFI_ENTRY("%p", file);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900385 return EFI_EXIT(efi_file_close_int(file));
Rob Clark53142032017-09-13 18:05:34 -0400386}
387
388static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
389{
390 struct file_handle *fh = to_fh(file);
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900391 efi_status_t ret = EFI_SUCCESS;
392
Rob Clark53142032017-09-13 18:05:34 -0400393 EFI_ENTRY("%p", file);
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900394
Heinrich Schuchardt646faf32019-06-17 22:00:13 +0200395 if (set_blk_dev(fh) || fs_unlink(fh->path))
396 ret = EFI_WARN_DELETE_FAILURE;
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900397
Rob Clark53142032017-09-13 18:05:34 -0400398 file_close(fh);
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900399 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400400}
401
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200402/**
403 * efi_get_file_size() - determine the size of a file
404 *
405 * @fh: file handle
406 * @file_size: pointer to receive file size
407 * Return: status code
408 */
409static efi_status_t efi_get_file_size(struct file_handle *fh,
410 loff_t *file_size)
411{
412 if (set_blk_dev(fh))
413 return EFI_DEVICE_ERROR;
414
415 if (fs_size(fh->path, file_size))
416 return EFI_DEVICE_ERROR;
417
418 return EFI_SUCCESS;
419}
420
Ilias Apalodimasaa0f7552021-03-17 21:54:59 +0200421/**
422 * efi_file_size() - Get the size of a file using an EFI file handle
423 *
424 * @fh: EFI file handle
425 * @size: buffer to fill in the discovered size
426 *
427 * Return: size of the file
428 */
429efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size)
430{
431 struct efi_file_info *info = NULL;
432 efi_uintn_t bs = 0;
433 efi_status_t ret;
434
435 *size = 0;
436 ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
437 info));
438 if (ret != EFI_BUFFER_TOO_SMALL) {
439 ret = EFI_DEVICE_ERROR;
440 goto out;
441 }
442
443 info = malloc(bs);
444 if (!info) {
445 ret = EFI_OUT_OF_RESOURCES;
446 goto out;
447 }
448 ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
449 info));
450 if (ret != EFI_SUCCESS)
451 goto out;
452
453 *size = info->file_size;
454
455out:
456 free(info);
457 return ret;
458}
459
Rob Clark53142032017-09-13 18:05:34 -0400460static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
461 void *buffer)
462{
463 loff_t actread;
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200464 efi_status_t ret;
465 loff_t file_size;
Rob Clark53142032017-09-13 18:05:34 -0400466
Stefan Sørensen6b398d02020-07-22 09:43:31 +0200467 if (!buffer) {
468 ret = EFI_INVALID_PARAMETER;
469 return ret;
470 }
471
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200472 ret = efi_get_file_size(fh, &file_size);
473 if (ret != EFI_SUCCESS)
474 return ret;
475 if (file_size < fh->offset) {
476 ret = EFI_DEVICE_ERROR;
477 return ret;
478 }
479
480 if (set_blk_dev(fh))
481 return EFI_DEVICE_ERROR;
Simon Glass849688f2018-09-15 00:51:00 -0600482 if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
Rob Clark53142032017-09-13 18:05:34 -0400483 *buffer_size, &actread))
484 return EFI_DEVICE_ERROR;
485
486 *buffer_size = actread;
487 fh->offset += actread;
488
489 return EFI_SUCCESS;
490}
491
Heinrich Schuchardt69f722b2021-05-15 22:41:26 +0200492static void rtc2efi(struct efi_time *time, struct rtc_time *tm)
493{
494 memset(time, 0, sizeof(struct efi_time));
495 time->year = tm->tm_year;
496 time->month = tm->tm_mon;
497 time->day = tm->tm_mday;
498 time->hour = tm->tm_hour;
499 time->minute = tm->tm_min;
500 time->second = tm->tm_sec;
501}
502
Rob Clark53142032017-09-13 18:05:34 -0400503static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
504 void *buffer)
505{
506 struct efi_file_info *info = buffer;
507 struct fs_dirent *dent;
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200508 u64 required_size;
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200509 u16 *dst;
Rob Clark53142032017-09-13 18:05:34 -0400510
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200511 if (set_blk_dev(fh))
512 return EFI_DEVICE_ERROR;
513
Rob Clark53142032017-09-13 18:05:34 -0400514 if (!fh->dirs) {
515 assert(fh->offset == 0);
516 fh->dirs = fs_opendir(fh->path);
517 if (!fh->dirs)
518 return EFI_DEVICE_ERROR;
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200519 fh->dent = NULL;
Rob Clark53142032017-09-13 18:05:34 -0400520 }
521
522 /*
523 * So this is a bit awkward. Since fs layer is stateful and we
524 * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
525 * we might have to return without consuming the dent.. so we
526 * have to stash it for next call.
527 */
528 if (fh->dent) {
529 dent = fh->dent;
Rob Clark53142032017-09-13 18:05:34 -0400530 } else {
531 dent = fs_readdir(fh->dirs);
532 }
533
Rob Clark53142032017-09-13 18:05:34 -0400534 if (!dent) {
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200535 /* no more files in directory */
536 *buffer_size = 0;
Rob Clark53142032017-09-13 18:05:34 -0400537 return EFI_SUCCESS;
538 }
539
540 /* check buffer size: */
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200541 required_size = sizeof(*info) +
542 2 * (utf8_utf16_strlen(dent->name) + 1);
Rob Clark53142032017-09-13 18:05:34 -0400543 if (*buffer_size < required_size) {
544 *buffer_size = required_size;
545 fh->dent = dent;
546 return EFI_BUFFER_TOO_SMALL;
547 }
Stefan Sørensen6b398d02020-07-22 09:43:31 +0200548 if (!buffer)
549 return EFI_INVALID_PARAMETER;
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200550 fh->dent = NULL;
Rob Clark53142032017-09-13 18:05:34 -0400551
552 *buffer_size = required_size;
553 memset(info, 0, required_size);
554
555 info->size = required_size;
556 info->file_size = dent->size;
557 info->physical_size = dent->size;
Heinrich Schuchardt69f722b2021-05-15 22:41:26 +0200558 info->attribute = dent->attr;
559 rtc2efi(&info->create_time, &dent->create_time);
560 rtc2efi(&info->modification_time, &dent->change_time);
561 rtc2efi(&info->last_access_time, &dent->access_time);
Rob Clark53142032017-09-13 18:05:34 -0400562
563 if (dent->type == FS_DT_DIR)
564 info->attribute |= EFI_FILE_DIRECTORY;
565
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200566 dst = info->file_name;
567 utf8_utf16_strcpy(&dst, dent->name);
Rob Clark53142032017-09-13 18:05:34 -0400568
569 fh->offset++;
570
571 return EFI_SUCCESS;
572}
573
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900574efi_status_t efi_file_read_int(struct efi_file_handle *this,
575 efi_uintn_t *buffer_size, void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400576{
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100577 struct file_handle *fh = to_fh(this);
Rob Clark53142032017-09-13 18:05:34 -0400578 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200579 u64 bs;
Rob Clark53142032017-09-13 18:05:34 -0400580
Peng Fanca02db22021-04-28 21:54:01 +0800581 if (!this || !buffer_size)
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100582 return EFI_INVALID_PARAMETER;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200583
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200584 bs = *buffer_size;
Rob Clark53142032017-09-13 18:05:34 -0400585 if (fh->isdir)
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200586 ret = dir_read(fh, &bs, buffer);
Rob Clark53142032017-09-13 18:05:34 -0400587 else
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200588 ret = file_read(fh, &bs, buffer);
589 if (bs <= SIZE_MAX)
590 *buffer_size = bs;
591 else
592 *buffer_size = SIZE_MAX;
Rob Clark53142032017-09-13 18:05:34 -0400593
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100594 return ret;
595}
596
597/**
598 * efi_file_read() - read file
599 *
600 * This function implements the Read() service of the EFI_FILE_PROTOCOL.
601 *
602 * See the Unified Extensible Firmware Interface (UEFI) specification for
603 * details.
604 *
605 * @this: file protocol instance
606 * @buffer_size: number of bytes to read
607 * @buffer: read buffer
608 * Return: status code
609 */
610static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *this,
611 efi_uintn_t *buffer_size, void *buffer)
612{
613 efi_status_t ret;
614
615 EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer);
616
617 ret = efi_file_read_int(this, buffer_size, buffer);
618
Rob Clark53142032017-09-13 18:05:34 -0400619 return EFI_EXIT(ret);
620}
621
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200622/**
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100623 * efi_file_read_ex() - read file asynchonously
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200624 *
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100625 * This function implements the ReadEx() service of the EFI_FILE_PROTOCOL.
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200626 *
627 * See the Unified Extensible Firmware Interface (UEFI) specification for
628 * details.
629 *
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100630 * @this: file protocol instance
631 * @token: transaction token
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200632 * Return: status code
633 */
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100634static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *this,
635 struct efi_file_io_token *token)
Rob Clark53142032017-09-13 18:05:34 -0400636{
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100637 efi_status_t ret;
638
639 EFI_ENTRY("%p, %p", this, token);
640
641 if (!token) {
642 ret = EFI_INVALID_PARAMETER;
643 goto out;
644 }
645
646 ret = efi_file_read_int(this, &token->buffer_size, token->buffer);
647
648 if (ret == EFI_SUCCESS && token->event) {
649 token->status = EFI_SUCCESS;
650 efi_signal_event(token->event);
651 }
652
653out:
654 return EFI_EXIT(ret);
655}
656
657static efi_status_t efi_file_write_int(struct efi_file_handle *this,
658 efi_uintn_t *buffer_size, void *buffer)
659{
660 struct file_handle *fh = to_fh(this);
Rob Clark53142032017-09-13 18:05:34 -0400661 efi_status_t ret = EFI_SUCCESS;
662 loff_t actwrite;
663
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100664 if (!this || !buffer_size || !buffer) {
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200665 ret = EFI_INVALID_PARAMETER;
666 goto out;
667 }
668 if (fh->isdir) {
669 ret = EFI_UNSUPPORTED;
670 goto out;
671 }
672 if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) {
673 ret = EFI_ACCESS_DENIED;
674 goto out;
675 }
676
677 if (!*buffer_size)
678 goto out;
679
Rob Clark53142032017-09-13 18:05:34 -0400680 if (set_blk_dev(fh)) {
681 ret = EFI_DEVICE_ERROR;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200682 goto out;
Rob Clark53142032017-09-13 18:05:34 -0400683 }
Simon Glass849688f2018-09-15 00:51:00 -0600684 if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
Rob Clark53142032017-09-13 18:05:34 -0400685 &actwrite)) {
686 ret = EFI_DEVICE_ERROR;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200687 goto out;
Rob Clark53142032017-09-13 18:05:34 -0400688 }
Rob Clark53142032017-09-13 18:05:34 -0400689 *buffer_size = actwrite;
690 fh->offset += actwrite;
691
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200692out:
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100693 return ret;
694}
695
696/**
697 * efi_file_write() - write to file
698 *
699 * This function implements the Write() service of the EFI_FILE_PROTOCOL.
700 *
701 * See the Unified Extensible Firmware Interface (UEFI) specification for
702 * details.
703 *
704 * @this: file protocol instance
705 * @buffer_size: number of bytes to write
706 * @buffer: buffer with the bytes to write
707 * Return: status code
708 */
709static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *this,
710 efi_uintn_t *buffer_size,
711 void *buffer)
712{
713 efi_status_t ret;
714
715 EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer);
716
717 ret = efi_file_write_int(this, buffer_size, buffer);
718
Rob Clark53142032017-09-13 18:05:34 -0400719 return EFI_EXIT(ret);
720}
721
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200722/**
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100723 * efi_file_write_ex() - write to file
724 *
725 * This function implements the WriteEx() service of the EFI_FILE_PROTOCOL.
726 *
727 * See the Unified Extensible Firmware Interface (UEFI) specification for
728 * details.
729 *
730 * @this: file protocol instance
731 * @token: transaction token
732 * Return: status code
733 */
734static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *this,
735 struct efi_file_io_token *token)
736{
737 efi_status_t ret;
738
739 EFI_ENTRY("%p, %p", this, token);
740
741 if (!token) {
742 ret = EFI_INVALID_PARAMETER;
743 goto out;
744 }
745
746 ret = efi_file_write_int(this, &token->buffer_size, token->buffer);
747
748 if (ret == EFI_SUCCESS && token->event) {
749 token->status = EFI_SUCCESS;
750 efi_signal_event(token->event);
751 }
752
753out:
754 return EFI_EXIT(ret);
755}
756
757/**
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200758 * efi_file_getpos() - get current position in file
759 *
760 * This function implements the GetPosition service of the EFI file protocol.
761 * See the UEFI spec for details.
762 *
763 * @file: file handle
764 * @pos: pointer to file position
765 * Return: status code
766 */
Rob Clark53142032017-09-13 18:05:34 -0400767static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200768 u64 *pos)
Rob Clark53142032017-09-13 18:05:34 -0400769{
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200770 efi_status_t ret = EFI_SUCCESS;
Rob Clark53142032017-09-13 18:05:34 -0400771 struct file_handle *fh = to_fh(file);
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200772
Rob Clark53142032017-09-13 18:05:34 -0400773 EFI_ENTRY("%p, %p", file, pos);
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200774
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200775 if (fh->isdir) {
776 ret = EFI_UNSUPPORTED;
777 goto out;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200778 }
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200779
780 *pos = fh->offset;
781out:
782 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400783}
784
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900785efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos)
Rob Clark53142032017-09-13 18:05:34 -0400786{
787 struct file_handle *fh = to_fh(file);
788 efi_status_t ret = EFI_SUCCESS;
789
Rob Clark53142032017-09-13 18:05:34 -0400790 if (fh->isdir) {
791 if (pos != 0) {
792 ret = EFI_UNSUPPORTED;
793 goto error;
794 }
795 fs_closedir(fh->dirs);
796 fh->dirs = NULL;
797 }
798
799 if (pos == ~0ULL) {
800 loff_t file_size;
801
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200802 ret = efi_get_file_size(fh, &file_size);
803 if (ret != EFI_SUCCESS)
Rob Clark53142032017-09-13 18:05:34 -0400804 goto error;
Rob Clark53142032017-09-13 18:05:34 -0400805 pos = file_size;
806 }
807
808 fh->offset = pos;
809
810error:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900811 return ret;
812}
813
814/**
815 * efi_file_setpos() - set current position in file
816 *
817 * This function implements the SetPosition service of the EFI file protocol.
818 * See the UEFI spec for details.
819 *
820 * @file: file handle
821 * @pos: new file position
822 * Return: status code
823 */
824static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
825 u64 pos)
826{
827 efi_status_t ret = EFI_SUCCESS;
828
829 EFI_ENTRY("%p, %llu", file, pos);
830
831 ret = efi_file_setpos_int(file, pos);
832
Rob Clark53142032017-09-13 18:05:34 -0400833 return EFI_EXIT(ret);
834}
835
836static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
Heinrich Schuchardte86380d2018-04-04 15:42:09 +0200837 const efi_guid_t *info_type,
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200838 efi_uintn_t *buffer_size,
839 void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400840{
841 struct file_handle *fh = to_fh(file);
842 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200843 u16 *dst;
Rob Clark53142032017-09-13 18:05:34 -0400844
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100845 EFI_ENTRY("%p, %pUs, %p, %p", file, info_type, buffer_size, buffer);
Rob Clark53142032017-09-13 18:05:34 -0400846
Heinrich Schuchardtc540a0e2019-09-08 10:45:31 +0200847 if (!file || !info_type || !buffer_size ||
848 (*buffer_size && !buffer)) {
849 ret = EFI_INVALID_PARAMETER;
850 goto error;
851 }
852
Rob Clark53142032017-09-13 18:05:34 -0400853 if (!guidcmp(info_type, &efi_file_info_guid)) {
854 struct efi_file_info *info = buffer;
855 char *filename = basename(fh);
856 unsigned int required_size;
857 loff_t file_size;
858
859 /* check buffer size: */
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200860 required_size = sizeof(*info) +
861 2 * (utf8_utf16_strlen(filename) + 1);
Rob Clark53142032017-09-13 18:05:34 -0400862 if (*buffer_size < required_size) {
863 *buffer_size = required_size;
864 ret = EFI_BUFFER_TOO_SMALL;
865 goto error;
866 }
867
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200868 ret = efi_get_file_size(fh, &file_size);
Heinrich Schuchardt6e3f70b2024-10-26 08:40:47 +0200869 if (ret != EFI_SUCCESS) {
870 if (!fh->isdir)
871 goto error;
872 /*
873 * Some file drivers don't implement fs_size() for
874 * directories. Use a dummy non-zero value.
875 */
876 file_size = 4096;
877 ret = EFI_SUCCESS;
878 }
Rob Clark53142032017-09-13 18:05:34 -0400879
880 memset(info, 0, required_size);
881
882 info->size = required_size;
883 info->file_size = file_size;
884 info->physical_size = file_size;
885
886 if (fh->isdir)
887 info->attribute |= EFI_FILE_DIRECTORY;
888
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200889 dst = info->file_name;
890 utf8_utf16_strcpy(&dst, filename);
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200891 } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
892 struct efi_file_system_info *info = buffer;
Simon Glassc1c4a8f2020-05-10 11:39:57 -0600893 struct disk_partition part;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200894 efi_uintn_t required_size;
895 int r;
896
897 if (fh->fs->part >= 1)
898 r = part_get_info(fh->fs->desc, fh->fs->part, &part);
899 else
900 r = part_get_info_whole_disk(fh->fs->desc, &part);
901 if (r < 0) {
902 ret = EFI_DEVICE_ERROR;
903 goto error;
904 }
Heinrich Schuchardt0c236c42019-09-08 10:32:54 +0200905 required_size = sizeof(*info) + 2;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200906 if (*buffer_size < required_size) {
907 *buffer_size = required_size;
908 ret = EFI_BUFFER_TOO_SMALL;
909 goto error;
910 }
911
912 memset(info, 0, required_size);
913
914 info->size = required_size;
Heinrich Schuchardta821b0c2019-12-08 10:02:37 +0100915 /*
916 * TODO: We cannot determine if the volume can be written to.
917 */
918 info->read_only = false;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200919 info->volume_size = part.size * part.blksz;
Heinrich Schuchardta821b0c2019-12-08 10:02:37 +0100920 /*
921 * TODO: We currently have no function to determine the free
922 * space. The volume size is the best upper bound we have.
923 */
924 info->free_space = info->volume_size;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200925 info->block_size = part.blksz;
926 /*
927 * TODO: The volume label is not available in U-Boot.
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200928 */
Heinrich Schuchardt0c236c42019-09-08 10:32:54 +0200929 info->volume_label[0] = 0;
930 } else if (!guidcmp(info_type, &efi_system_volume_label_id)) {
931 if (*buffer_size < 2) {
932 *buffer_size = 2;
933 ret = EFI_BUFFER_TOO_SMALL;
934 goto error;
935 }
936 *(u16 *)buffer = 0;
Rob Clark53142032017-09-13 18:05:34 -0400937 } else {
938 ret = EFI_UNSUPPORTED;
939 }
940
941error:
942 return EFI_EXIT(ret);
943}
944
945static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
Heinrich Schuchardte86380d2018-04-04 15:42:09 +0200946 const efi_guid_t *info_type,
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200947 efi_uintn_t buffer_size,
948 void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400949{
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200950 struct file_handle *fh = to_fh(file);
951 efi_status_t ret = EFI_UNSUPPORTED;
952
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100953 EFI_ENTRY("%p, %pUs, %zu, %p", file, info_type, buffer_size, buffer);
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200954
955 if (!guidcmp(info_type, &efi_file_info_guid)) {
956 struct efi_file_info *info = (struct efi_file_info *)buffer;
957 char *filename = basename(fh);
958 char *new_file_name, *pos;
959 loff_t file_size;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200960
Heinrich Schuchardtdd106d02019-09-08 11:37:07 +0200961 /* The buffer will always contain a file name. */
962 if (buffer_size < sizeof(struct efi_file_info) + 2 ||
963 buffer_size < info->size) {
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200964 ret = EFI_BAD_BUFFER_SIZE;
965 goto out;
966 }
967 /* We cannot change the directory attribute */
968 if (!fh->isdir != !(info->attribute & EFI_FILE_DIRECTORY)) {
969 ret = EFI_ACCESS_DENIED;
970 goto out;
971 }
972 /* Check for renaming */
Heinrich Schuchardt344bc072020-11-10 07:24:16 +0100973 new_file_name = malloc(utf16_utf8_strlen(info->file_name) + 1);
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200974 if (!new_file_name) {
975 ret = EFI_OUT_OF_RESOURCES;
976 goto out;
977 }
978 pos = new_file_name;
979 utf16_utf8_strcpy(&pos, info->file_name);
980 if (strcmp(new_file_name, filename)) {
981 /* TODO: we do not support renaming */
982 EFI_PRINT("Renaming not supported\n");
983 free(new_file_name);
984 ret = EFI_ACCESS_DENIED;
985 goto out;
986 }
987 free(new_file_name);
988 /* Check for truncation */
Heinrich Schuchardt6e3f70b2024-10-26 08:40:47 +0200989 if (!fh->isdir) {
990 ret = efi_get_file_size(fh, &file_size);
991 if (ret != EFI_SUCCESS)
992 goto out;
993 if (file_size != info->file_size) {
994 /* TODO: we do not support truncation */
995 EFI_PRINT("Truncation not supported\n");
996 ret = EFI_ACCESS_DENIED;
997 goto out;
998 }
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200999 }
1000 /*
1001 * We do not care for the other attributes
1002 * TODO: Support read only
1003 */
1004 ret = EFI_SUCCESS;
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +02001005 } else {
Heinrich Schuchardtdd106d02019-09-08 11:37:07 +02001006 /* TODO: We do not support changing the volume label */
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +02001007 ret = EFI_UNSUPPORTED;
1008 }
1009out:
1010 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -04001011}
1012
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001013/**
1014 * efi_file_flush_int() - flush file
1015 *
1016 * This is the internal implementation of the Flush() and FlushEx() services of
1017 * the EFI_FILE_PROTOCOL.
1018 *
1019 * @this: file protocol instance
1020 * Return: status code
1021 */
1022static efi_status_t efi_file_flush_int(struct efi_file_handle *this)
Rob Clark53142032017-09-13 18:05:34 -04001023{
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001024 struct file_handle *fh = to_fh(this);
Rob Clark53142032017-09-13 18:05:34 -04001025
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001026 if (!this)
1027 return EFI_INVALID_PARAMETER;
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001028
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001029 if (!(fh->open_mode & EFI_FILE_MODE_WRITE))
1030 return EFI_ACCESS_DENIED;
1031
1032 /* TODO: flush for file position after end of file */
1033 return EFI_SUCCESS;
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001034}
1035
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001036/**
1037 * efi_file_flush() - flush file
1038 *
1039 * This function implements the Flush() service of the EFI_FILE_PROTOCOL.
1040 *
1041 * See the Unified Extensible Firmware Interface (UEFI) specification for
1042 * details.
1043 *
1044 * @this: file protocol instance
1045 * Return: status code
1046 */
1047static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *this)
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001048{
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001049 efi_status_t ret;
1050
1051 EFI_ENTRY("%p", this);
1052
1053 ret = efi_file_flush_int(this);
1054
1055 return EFI_EXIT(ret);
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001056}
1057
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001058/**
1059 * efi_file_flush_ex() - flush file
1060 *
1061 * This function implements the FlushEx() service of the EFI_FILE_PROTOCOL.
1062 *
1063 * See the Unified Extensible Firmware Interface (UEFI) specification for
1064 * details.
1065 *
1066 * @this: file protocol instance
1067 * @token: transaction token
1068 * Return: status code
1069 */
1070static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *this,
1071 struct efi_file_io_token *token)
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001072{
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001073 efi_status_t ret;
1074
1075 EFI_ENTRY("%p, %p", this, token);
1076
1077 if (!token) {
1078 ret = EFI_INVALID_PARAMETER;
1079 goto out;
1080 }
1081
1082 ret = efi_file_flush_int(this);
1083
1084 if (ret == EFI_SUCCESS && token->event) {
1085 token->status = EFI_SUCCESS;
1086 efi_signal_event(token->event);
1087 }
1088
1089out:
1090 return EFI_EXIT(ret);
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001091}
1092
Rob Clark53142032017-09-13 18:05:34 -04001093static const struct efi_file_handle efi_file_handle_protocol = {
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001094 .rev = EFI_FILE_PROTOCOL_REVISION2,
Rob Clark53142032017-09-13 18:05:34 -04001095 .open = efi_file_open,
1096 .close = efi_file_close,
1097 .delete = efi_file_delete,
1098 .read = efi_file_read,
1099 .write = efi_file_write,
1100 .getpos = efi_file_getpos,
1101 .setpos = efi_file_setpos,
1102 .getinfo = efi_file_getinfo,
1103 .setinfo = efi_file_setinfo,
1104 .flush = efi_file_flush,
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001105 .open_ex = efi_file_open_ex,
1106 .read_ex = efi_file_read_ex,
1107 .write_ex = efi_file_write_ex,
1108 .flush_ex = efi_file_flush_ex,
Rob Clark53142032017-09-13 18:05:34 -04001109};
1110
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001111/**
1112 * efi_file_from_path() - open file via device path
1113 *
Heinrich Schuchardt4395adb2022-11-10 14:14:06 +01001114 * The device path @fp consists of the device path of the handle with the
1115 * simple file system protocol and one or more file path device path nodes.
1116 * The concatenation of all file path names provides the total file path.
1117 *
1118 * The code starts at the first file path node and tries to open that file or
1119 * directory. If there is a succeding file path node, the code opens it relative
1120 * to this directory and continues iterating until reaching the last file path
1121 * node.
1122 *
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001123 * @fp: device path
Heinrich Schuchardtfbe90212022-01-20 19:48:20 +01001124 * Return: EFI_FILE_PROTOCOL for the file or NULL
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001125 */
Rob Clark53142032017-09-13 18:05:34 -04001126struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
1127{
1128 struct efi_simple_file_system_protocol *v;
1129 struct efi_file_handle *f;
1130 efi_status_t ret;
1131
1132 v = efi_fs_from_path(fp);
1133 if (!v)
1134 return NULL;
1135
1136 EFI_CALL(ret = v->open_volume(v, &f));
1137 if (ret != EFI_SUCCESS)
1138 return NULL;
1139
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001140 /* Skip over device-path nodes before the file path. */
Rob Clark53142032017-09-13 18:05:34 -04001141 while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
1142 fp = efi_dp_next(fp);
1143
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001144 /*
1145 * Step through the nodes of the directory path until the actual file
1146 * node is reached which is the final node in the device path.
1147 */
Rob Clark53142032017-09-13 18:05:34 -04001148 while (fp) {
1149 struct efi_device_path_file_path *fdp =
1150 container_of(fp, struct efi_device_path_file_path, dp);
1151 struct efi_file_handle *f2;
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +02001152 u16 *filename;
Ilias Apalodimas50279dc2022-11-11 20:04:31 +02001153 size_t filename_sz;
Rob Clark53142032017-09-13 18:05:34 -04001154
1155 if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
1156 printf("bad file path!\n");
Ilias Apalodimas08d1af22022-11-11 18:20:37 +02001157 EFI_CALL(f->close(f));
Rob Clark53142032017-09-13 18:05:34 -04001158 return NULL;
1159 }
1160
Ilias Apalodimas9124bc62022-11-10 15:31:30 +02001161 /*
1162 * UEFI specification requires pointers that are passed to
1163 * protocol member functions to be aligned. So memcpy it
1164 * unconditionally
1165 */
Ilias Apalodimas50279dc2022-11-11 20:04:31 +02001166 if (fdp->dp.length <= offsetof(struct efi_device_path_file_path, str))
1167 return NULL;
1168 filename_sz = fdp->dp.length -
1169 offsetof(struct efi_device_path_file_path, str);
1170 filename = malloc(filename_sz);
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +02001171 if (!filename)
1172 return NULL;
Ilias Apalodimas50279dc2022-11-11 20:04:31 +02001173 memcpy(filename, fdp->str, filename_sz);
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +02001174 EFI_CALL(ret = f->open(f, &f2, filename,
Rob Clark53142032017-09-13 18:05:34 -04001175 EFI_FILE_MODE_READ, 0));
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +02001176 free(filename);
Rob Clark53142032017-09-13 18:05:34 -04001177 if (ret != EFI_SUCCESS)
1178 return NULL;
1179
1180 fp = efi_dp_next(fp);
1181
1182 EFI_CALL(f->close(f));
1183 f = f2;
1184 }
1185
1186 return f;
1187}
1188
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001189efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
1190 struct efi_file_handle **root)
1191{
1192 struct file_system *fs = to_fs(this);
1193
1194 *root = file_open(fs, NULL, NULL, 0, 0);
1195
1196 return EFI_SUCCESS;
1197}
1198
Rob Clark53142032017-09-13 18:05:34 -04001199static efi_status_t EFIAPI
1200efi_open_volume(struct efi_simple_file_system_protocol *this,
1201 struct efi_file_handle **root)
1202{
Rob Clark53142032017-09-13 18:05:34 -04001203 EFI_ENTRY("%p, %p", this, root);
1204
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001205 return EFI_EXIT(efi_open_volume_int(this, root));
Rob Clark53142032017-09-13 18:05:34 -04001206}
1207
Heinrich Schuchardtf9e8ada2023-07-30 14:03:53 +02001208efi_status_t
1209efi_create_simple_file_system(struct blk_desc *desc, int part,
1210 struct efi_device_path *dp,
1211 struct efi_simple_file_system_protocol **fsp)
Rob Clark53142032017-09-13 18:05:34 -04001212{
1213 struct file_system *fs;
1214
1215 fs = calloc(1, sizeof(*fs));
Heinrich Schuchardtf9e8ada2023-07-30 14:03:53 +02001216 if (!fs)
1217 return EFI_OUT_OF_RESOURCES;
Rob Clark53142032017-09-13 18:05:34 -04001218 fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
1219 fs->base.open_volume = efi_open_volume;
1220 fs->desc = desc;
1221 fs->part = part;
1222 fs->dp = dp;
Heinrich Schuchardtf9e8ada2023-07-30 14:03:53 +02001223 *fsp = &fs->base;
Rob Clark53142032017-09-13 18:05:34 -04001224
Heinrich Schuchardtf9e8ada2023-07-30 14:03:53 +02001225 return EFI_SUCCESS;
Rob Clark53142032017-09-13 18:05:34 -04001226}