blob: 3c56cebf96530353192b45156e5211b49563729a [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
8#include <common.h>
9#include <charset.h>
10#include <efi_loader.h>
Simon Glass0f2af882020-05-10 11:40:05 -060011#include <log.h>
Rob Clark53142032017-09-13 18:05:34 -040012#include <malloc.h>
Alexander Grafab133c72018-08-08 03:54:32 -060013#include <mapmem.h>
Rob Clark53142032017-09-13 18:05:34 -040014#include <fs.h>
Simon Glass655306c2020-05-10 11:39:58 -060015#include <part.h>
Rob Clark53142032017-09-13 18:05:34 -040016
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +020017/* GUID for file system information */
18const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
19
Heinrich Schuchardt0c236c42019-09-08 10:32:54 +020020/* GUID to obtain the volume label */
21const efi_guid_t efi_system_volume_label_id = EFI_FILE_SYSTEM_VOLUME_LABEL_ID;
22
Rob Clark53142032017-09-13 18:05:34 -040023struct file_system {
24 struct efi_simple_file_system_protocol base;
25 struct efi_device_path *dp;
26 struct blk_desc *desc;
27 int part;
28};
29#define to_fs(x) container_of(x, struct file_system, base)
30
31struct file_handle {
32 struct efi_file_handle base;
33 struct file_system *fs;
34 loff_t offset; /* current file position/cursor */
35 int isdir;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +020036 u64 open_mode;
Rob Clark53142032017-09-13 18:05:34 -040037
38 /* for reading a directory: */
39 struct fs_dir_stream *dirs;
40 struct fs_dirent *dent;
41
42 char path[0];
43};
44#define to_fh(x) container_of(x, struct file_handle, base)
45
46static const struct efi_file_handle efi_file_handle_protocol;
47
48static char *basename(struct file_handle *fh)
49{
50 char *s = strrchr(fh->path, '/');
51 if (s)
52 return s + 1;
53 return fh->path;
54}
55
56static int set_blk_dev(struct file_handle *fh)
57{
58 return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
59}
60
Heinrich Schuchardt80f834d2018-10-02 05:57:32 +020061/**
62 * is_dir() - check if file handle points to directory
63 *
64 * We assume that set_blk_dev(fh) has been called already.
65 *
66 * @fh: file handle
67 * Return: true if file handle points to a directory
68 */
Rob Clark53142032017-09-13 18:05:34 -040069static int is_dir(struct file_handle *fh)
70{
71 struct fs_dir_stream *dirs;
72
Rob Clark53142032017-09-13 18:05:34 -040073 dirs = fs_opendir(fh->path);
74 if (!dirs)
75 return 0;
76
77 fs_closedir(dirs);
78
79 return 1;
80}
81
82/*
83 * Normalize a path which may include either back or fwd slashes,
84 * double slashes, . or .. entries in the path, etc.
85 */
86static int sanitize_path(char *path)
87{
88 char *p;
89
90 /* backslash to slash: */
91 p = path;
92 while ((p = strchr(p, '\\')))
93 *p++ = '/';
94
95 /* handle double-slashes: */
96 p = path;
97 while ((p = strstr(p, "//"))) {
98 char *src = p + 1;
99 memmove(p, src, strlen(src) + 1);
100 }
101
102 /* handle extra /.'s */
103 p = path;
104 while ((p = strstr(p, "/."))) {
105 /*
106 * You'd be tempted to do this *after* handling ".."s
107 * below to avoid having to check if "/." is start of
108 * a "/..", but that won't have the correct results..
109 * for example, "/foo/./../bar" would get resolved to
110 * "/foo/bar" if you did these two passes in the other
111 * order
112 */
113 if (p[2] == '.') {
114 p += 2;
115 continue;
116 }
117 char *src = p + 2;
118 memmove(p, src, strlen(src) + 1);
119 }
120
121 /* handle extra /..'s: */
122 p = path;
123 while ((p = strstr(p, "/.."))) {
124 char *src = p + 3;
125
126 p--;
127
128 /* find beginning of previous path entry: */
129 while (true) {
130 if (p < path)
131 return -1;
132 if (*p == '/')
133 break;
134 p--;
135 }
136
137 memmove(p, src, strlen(src) + 1);
138 }
139
140 return 0;
141}
142
Heinrich Schuchardt1af423b2018-09-12 19:00:02 +0200143/**
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200144 * efi_create_file() - create file or directory
145 *
146 * @fh: file handle
147 * @attributes: attributes for newly created file
148 * Returns: 0 for success
149 */
150static int efi_create_file(struct file_handle *fh, u64 attributes)
151{
152 loff_t actwrite;
153 void *buffer = &actwrite;
154
155 if (attributes & EFI_FILE_DIRECTORY)
156 return fs_mkdir(fh->path);
157 else
158 return fs_write(fh->path, map_to_sysmem(buffer), 0, 0,
159 &actwrite);
160}
161
162/**
Heinrich Schuchardt1af423b2018-09-12 19:00:02 +0200163 * file_open() - open a file handle
164 *
165 * @fs: file system
166 * @parent: directory relative to which the file is to be opened
167 * @file_name: path of the file to be opened. '\', '.', or '..' may
168 * be used as modifiers. A leading backslash indicates an
169 * absolute path.
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200170 * @open_mode: bit mask indicating the access mode (read, write,
Heinrich Schuchardt1af423b2018-09-12 19:00:02 +0200171 * create)
172 * @attributes: attributes for newly created file
173 * Returns: handle to the opened file or NULL
Rob Clark53142032017-09-13 18:05:34 -0400174 */
175static struct efi_file_handle *file_open(struct file_system *fs,
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200176 struct file_handle *parent, u16 *file_name, u64 open_mode,
AKASHI Takahiro0c672b02018-09-11 15:59:12 +0900177 u64 attributes)
Rob Clark53142032017-09-13 18:05:34 -0400178{
179 struct file_handle *fh;
180 char f0[MAX_UTF8_PER_UTF16] = {0};
181 int plen = 0;
182 int flen = 0;
183
184 if (file_name) {
Heinrich Schuchardtdb47c342019-01-12 12:02:33 +0100185 utf16_to_utf8((u8 *)f0, file_name, 1);
186 flen = u16_strlen(file_name);
Rob Clark53142032017-09-13 18:05:34 -0400187 }
188
189 /* we could have a parent, but also an absolute path: */
190 if (f0[0] == '\\') {
191 plen = 0;
192 } else if (parent) {
193 plen = strlen(parent->path) + 1;
194 }
195
196 /* +2 is for null and '/' */
197 fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
Heinrich Schuchardtf407af82023-07-30 14:06:06 +0200198 if (!fh)
199 return NULL;
Rob Clark53142032017-09-13 18:05:34 -0400200
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200201 fh->open_mode = open_mode;
Rob Clark53142032017-09-13 18:05:34 -0400202 fh->base = efi_file_handle_protocol;
203 fh->fs = fs;
204
205 if (parent) {
206 char *p = fh->path;
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200207 int exists;
Rob Clark53142032017-09-13 18:05:34 -0400208
209 if (plen > 0) {
210 strcpy(p, parent->path);
211 p += plen - 1;
212 *p++ = '/';
213 }
214
Heinrich Schuchardtdb47c342019-01-12 12:02:33 +0100215 utf16_to_utf8((u8 *)p, file_name, flen);
Rob Clark53142032017-09-13 18:05:34 -0400216
217 if (sanitize_path(fh->path))
218 goto error;
219
220 /* check if file exists: */
221 if (set_blk_dev(fh))
222 goto error;
223
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200224 exists = fs_exists(fh->path);
Heinrich Schuchardt3ab87ac2019-02-09 22:23:48 +0100225 /* fs_exists() calls fs_close(), so open file system again */
226 if (set_blk_dev(fh))
227 goto error;
228
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200229 if (!exists) {
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200230 if (!(open_mode & EFI_FILE_MODE_CREATE) ||
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200231 efi_create_file(fh, attributes))
232 goto error;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200233 if (set_blk_dev(fh))
234 goto error;
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200235 }
236
Rob Clark53142032017-09-13 18:05:34 -0400237 /* figure out if file is a directory: */
238 fh->isdir = is_dir(fh);
239 } else {
240 fh->isdir = 1;
241 strcpy(fh->path, "");
242 }
243
244 return &fh->base;
245
246error:
247 free(fh);
248 return NULL;
249}
250
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900251efi_status_t efi_file_open_int(struct efi_file_handle *this,
252 struct efi_file_handle **new_handle,
253 u16 *file_name, u64 open_mode,
254 u64 attributes)
Rob Clark53142032017-09-13 18:05:34 -0400255{
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100256 struct file_handle *fh = to_fh(this);
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200257 efi_status_t ret;
Rob Clark53142032017-09-13 18:05:34 -0400258
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200259 /* Check parameters */
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100260 if (!this || !new_handle || !file_name) {
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200261 ret = EFI_INVALID_PARAMETER;
262 goto out;
263 }
264 if (open_mode != EFI_FILE_MODE_READ &&
265 open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
266 open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
267 EFI_FILE_MODE_CREATE)) {
268 ret = EFI_INVALID_PARAMETER;
269 goto out;
270 }
Heinrich Schuchardt662d0be2018-09-13 21:31:49 +0200271 /*
272 * The UEFI spec requires that attributes are only set in create mode.
273 * The SCT does not care about this and sets EFI_FILE_DIRECTORY in
274 * read mode. EDK2 does not check that attributes are zero if not in
275 * create mode.
276 *
277 * So here we only check attributes in create mode and do not check
278 * that they are zero otherwise.
279 */
280 if ((open_mode & EFI_FILE_MODE_CREATE) &&
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200281 (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
282 ret = EFI_INVALID_PARAMETER;
283 goto out;
284 }
Rob Clark53142032017-09-13 18:05:34 -0400285
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200286 /* Open file */
287 *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
Heinrich Schuchardtd4fe5282019-04-06 16:36:16 +0200288 if (*new_handle) {
289 EFI_PRINT("file handle %p\n", *new_handle);
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200290 ret = EFI_SUCCESS;
Heinrich Schuchardtd4fe5282019-04-06 16:36:16 +0200291 } else {
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200292 ret = EFI_NOT_FOUND;
Heinrich Schuchardtd4fe5282019-04-06 16:36:16 +0200293 }
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100294out:
295 return ret;
296}
297
298/**
299 * efi_file_open_()
300 *
301 * This function implements the Open service of the File Protocol.
302 * See the UEFI spec for details.
303 *
304 * @this: EFI_FILE_PROTOCOL instance
305 * @new_handle: on return pointer to file handle
306 * @file_name: file name
307 * @open_mode: mode to open the file (read, read/write, create/read/write)
308 * @attributes: attributes for newly created file
309 */
310static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *this,
311 struct efi_file_handle **new_handle,
312 u16 *file_name, u64 open_mode,
313 u64 attributes)
314{
315 efi_status_t ret;
316
317 EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", this, new_handle,
318 file_name, open_mode, attributes);
319
320 ret = efi_file_open_int(this, new_handle, file_name, open_mode,
321 attributes);
322
323 return EFI_EXIT(ret);
324}
325
326/**
327 * efi_file_open_ex() - open file asynchronously
328 *
329 * This function implements the OpenEx service of the File Protocol.
330 * See the UEFI spec for details.
331 *
332 * @this: EFI_FILE_PROTOCOL instance
333 * @new_handle: on return pointer to file handle
334 * @file_name: file name
335 * @open_mode: mode to open the file (read, read/write, create/read/write)
336 * @attributes: attributes for newly created file
337 * @token: transaction token
338 */
339static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *this,
340 struct efi_file_handle **new_handle,
341 u16 *file_name, u64 open_mode,
342 u64 attributes,
343 struct efi_file_io_token *token)
344{
345 efi_status_t ret;
346
347 EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu, %p", this, new_handle,
348 file_name, open_mode, attributes, token);
349
350 if (!token) {
351 ret = EFI_INVALID_PARAMETER;
352 goto out;
353 }
354
355 ret = efi_file_open_int(this, new_handle, file_name, open_mode,
356 attributes);
357
358 if (ret == EFI_SUCCESS && token->event) {
359 token->status = EFI_SUCCESS;
360 efi_signal_event(token->event);
361 }
362
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200363out:
364 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400365}
366
367static efi_status_t file_close(struct file_handle *fh)
368{
369 fs_closedir(fh->dirs);
370 free(fh);
371 return EFI_SUCCESS;
372}
373
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900374efi_status_t efi_file_close_int(struct efi_file_handle *file)
Rob Clark53142032017-09-13 18:05:34 -0400375{
376 struct file_handle *fh = to_fh(file);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900377
378 return file_close(fh);
379}
380
381static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
382{
Rob Clark53142032017-09-13 18:05:34 -0400383 EFI_ENTRY("%p", file);
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900384 return EFI_EXIT(efi_file_close_int(file));
Rob Clark53142032017-09-13 18:05:34 -0400385}
386
387static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
388{
389 struct file_handle *fh = to_fh(file);
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900390 efi_status_t ret = EFI_SUCCESS;
391
Rob Clark53142032017-09-13 18:05:34 -0400392 EFI_ENTRY("%p", file);
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900393
Heinrich Schuchardt646faf32019-06-17 22:00:13 +0200394 if (set_blk_dev(fh) || fs_unlink(fh->path))
395 ret = EFI_WARN_DELETE_FAILURE;
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900396
Rob Clark53142032017-09-13 18:05:34 -0400397 file_close(fh);
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900398 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400399}
400
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200401/**
402 * efi_get_file_size() - determine the size of a file
403 *
404 * @fh: file handle
405 * @file_size: pointer to receive file size
406 * Return: status code
407 */
408static efi_status_t efi_get_file_size(struct file_handle *fh,
409 loff_t *file_size)
410{
411 if (set_blk_dev(fh))
412 return EFI_DEVICE_ERROR;
413
414 if (fs_size(fh->path, file_size))
415 return EFI_DEVICE_ERROR;
416
417 return EFI_SUCCESS;
418}
419
Ilias Apalodimasaa0f7552021-03-17 21:54:59 +0200420/**
421 * efi_file_size() - Get the size of a file using an EFI file handle
422 *
423 * @fh: EFI file handle
424 * @size: buffer to fill in the discovered size
425 *
426 * Return: size of the file
427 */
428efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size)
429{
430 struct efi_file_info *info = NULL;
431 efi_uintn_t bs = 0;
432 efi_status_t ret;
433
434 *size = 0;
435 ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
436 info));
437 if (ret != EFI_BUFFER_TOO_SMALL) {
438 ret = EFI_DEVICE_ERROR;
439 goto out;
440 }
441
442 info = malloc(bs);
443 if (!info) {
444 ret = EFI_OUT_OF_RESOURCES;
445 goto out;
446 }
447 ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
448 info));
449 if (ret != EFI_SUCCESS)
450 goto out;
451
452 *size = info->file_size;
453
454out:
455 free(info);
456 return ret;
457}
458
Rob Clark53142032017-09-13 18:05:34 -0400459static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
460 void *buffer)
461{
462 loff_t actread;
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200463 efi_status_t ret;
464 loff_t file_size;
Rob Clark53142032017-09-13 18:05:34 -0400465
Stefan Sørensen6b398d02020-07-22 09:43:31 +0200466 if (!buffer) {
467 ret = EFI_INVALID_PARAMETER;
468 return ret;
469 }
470
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200471 ret = efi_get_file_size(fh, &file_size);
472 if (ret != EFI_SUCCESS)
473 return ret;
474 if (file_size < fh->offset) {
475 ret = EFI_DEVICE_ERROR;
476 return ret;
477 }
478
479 if (set_blk_dev(fh))
480 return EFI_DEVICE_ERROR;
Simon Glass849688f2018-09-15 00:51:00 -0600481 if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
Rob Clark53142032017-09-13 18:05:34 -0400482 *buffer_size, &actread))
483 return EFI_DEVICE_ERROR;
484
485 *buffer_size = actread;
486 fh->offset += actread;
487
488 return EFI_SUCCESS;
489}
490
Heinrich Schuchardt69f722b2021-05-15 22:41:26 +0200491static void rtc2efi(struct efi_time *time, struct rtc_time *tm)
492{
493 memset(time, 0, sizeof(struct efi_time));
494 time->year = tm->tm_year;
495 time->month = tm->tm_mon;
496 time->day = tm->tm_mday;
497 time->hour = tm->tm_hour;
498 time->minute = tm->tm_min;
499 time->second = tm->tm_sec;
500}
501
Rob Clark53142032017-09-13 18:05:34 -0400502static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
503 void *buffer)
504{
505 struct efi_file_info *info = buffer;
506 struct fs_dirent *dent;
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200507 u64 required_size;
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200508 u16 *dst;
Rob Clark53142032017-09-13 18:05:34 -0400509
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200510 if (set_blk_dev(fh))
511 return EFI_DEVICE_ERROR;
512
Rob Clark53142032017-09-13 18:05:34 -0400513 if (!fh->dirs) {
514 assert(fh->offset == 0);
515 fh->dirs = fs_opendir(fh->path);
516 if (!fh->dirs)
517 return EFI_DEVICE_ERROR;
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200518 fh->dent = NULL;
Rob Clark53142032017-09-13 18:05:34 -0400519 }
520
521 /*
522 * So this is a bit awkward. Since fs layer is stateful and we
523 * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
524 * we might have to return without consuming the dent.. so we
525 * have to stash it for next call.
526 */
527 if (fh->dent) {
528 dent = fh->dent;
Rob Clark53142032017-09-13 18:05:34 -0400529 } else {
530 dent = fs_readdir(fh->dirs);
531 }
532
Rob Clark53142032017-09-13 18:05:34 -0400533 if (!dent) {
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200534 /* no more files in directory */
535 *buffer_size = 0;
Rob Clark53142032017-09-13 18:05:34 -0400536 return EFI_SUCCESS;
537 }
538
539 /* check buffer size: */
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200540 required_size = sizeof(*info) +
541 2 * (utf8_utf16_strlen(dent->name) + 1);
Rob Clark53142032017-09-13 18:05:34 -0400542 if (*buffer_size < required_size) {
543 *buffer_size = required_size;
544 fh->dent = dent;
545 return EFI_BUFFER_TOO_SMALL;
546 }
Stefan Sørensen6b398d02020-07-22 09:43:31 +0200547 if (!buffer)
548 return EFI_INVALID_PARAMETER;
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200549 fh->dent = NULL;
Rob Clark53142032017-09-13 18:05:34 -0400550
551 *buffer_size = required_size;
552 memset(info, 0, required_size);
553
554 info->size = required_size;
555 info->file_size = dent->size;
556 info->physical_size = dent->size;
Heinrich Schuchardt69f722b2021-05-15 22:41:26 +0200557 info->attribute = dent->attr;
558 rtc2efi(&info->create_time, &dent->create_time);
559 rtc2efi(&info->modification_time, &dent->change_time);
560 rtc2efi(&info->last_access_time, &dent->access_time);
Rob Clark53142032017-09-13 18:05:34 -0400561
562 if (dent->type == FS_DT_DIR)
563 info->attribute |= EFI_FILE_DIRECTORY;
564
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200565 dst = info->file_name;
566 utf8_utf16_strcpy(&dst, dent->name);
Rob Clark53142032017-09-13 18:05:34 -0400567
568 fh->offset++;
569
570 return EFI_SUCCESS;
571}
572
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900573efi_status_t efi_file_read_int(struct efi_file_handle *this,
574 efi_uintn_t *buffer_size, void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400575{
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100576 struct file_handle *fh = to_fh(this);
Rob Clark53142032017-09-13 18:05:34 -0400577 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200578 u64 bs;
Rob Clark53142032017-09-13 18:05:34 -0400579
Peng Fanca02db22021-04-28 21:54:01 +0800580 if (!this || !buffer_size)
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100581 return EFI_INVALID_PARAMETER;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200582
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200583 bs = *buffer_size;
Rob Clark53142032017-09-13 18:05:34 -0400584 if (fh->isdir)
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200585 ret = dir_read(fh, &bs, buffer);
Rob Clark53142032017-09-13 18:05:34 -0400586 else
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200587 ret = file_read(fh, &bs, buffer);
588 if (bs <= SIZE_MAX)
589 *buffer_size = bs;
590 else
591 *buffer_size = SIZE_MAX;
Rob Clark53142032017-09-13 18:05:34 -0400592
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100593 return ret;
594}
595
596/**
597 * efi_file_read() - read file
598 *
599 * This function implements the Read() service of the EFI_FILE_PROTOCOL.
600 *
601 * See the Unified Extensible Firmware Interface (UEFI) specification for
602 * details.
603 *
604 * @this: file protocol instance
605 * @buffer_size: number of bytes to read
606 * @buffer: read buffer
607 * Return: status code
608 */
609static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *this,
610 efi_uintn_t *buffer_size, void *buffer)
611{
612 efi_status_t ret;
613
614 EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer);
615
616 ret = efi_file_read_int(this, buffer_size, buffer);
617
Rob Clark53142032017-09-13 18:05:34 -0400618 return EFI_EXIT(ret);
619}
620
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200621/**
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100622 * efi_file_read_ex() - read file asynchonously
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200623 *
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100624 * This function implements the ReadEx() service of the EFI_FILE_PROTOCOL.
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200625 *
626 * See the Unified Extensible Firmware Interface (UEFI) specification for
627 * details.
628 *
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100629 * @this: file protocol instance
630 * @token: transaction token
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200631 * Return: status code
632 */
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100633static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *this,
634 struct efi_file_io_token *token)
Rob Clark53142032017-09-13 18:05:34 -0400635{
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100636 efi_status_t ret;
637
638 EFI_ENTRY("%p, %p", this, token);
639
640 if (!token) {
641 ret = EFI_INVALID_PARAMETER;
642 goto out;
643 }
644
645 ret = efi_file_read_int(this, &token->buffer_size, token->buffer);
646
647 if (ret == EFI_SUCCESS && token->event) {
648 token->status = EFI_SUCCESS;
649 efi_signal_event(token->event);
650 }
651
652out:
653 return EFI_EXIT(ret);
654}
655
656static efi_status_t efi_file_write_int(struct efi_file_handle *this,
657 efi_uintn_t *buffer_size, void *buffer)
658{
659 struct file_handle *fh = to_fh(this);
Rob Clark53142032017-09-13 18:05:34 -0400660 efi_status_t ret = EFI_SUCCESS;
661 loff_t actwrite;
662
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100663 if (!this || !buffer_size || !buffer) {
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200664 ret = EFI_INVALID_PARAMETER;
665 goto out;
666 }
667 if (fh->isdir) {
668 ret = EFI_UNSUPPORTED;
669 goto out;
670 }
671 if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) {
672 ret = EFI_ACCESS_DENIED;
673 goto out;
674 }
675
676 if (!*buffer_size)
677 goto out;
678
Rob Clark53142032017-09-13 18:05:34 -0400679 if (set_blk_dev(fh)) {
680 ret = EFI_DEVICE_ERROR;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200681 goto out;
Rob Clark53142032017-09-13 18:05:34 -0400682 }
Simon Glass849688f2018-09-15 00:51:00 -0600683 if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
Rob Clark53142032017-09-13 18:05:34 -0400684 &actwrite)) {
685 ret = EFI_DEVICE_ERROR;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200686 goto out;
Rob Clark53142032017-09-13 18:05:34 -0400687 }
Rob Clark53142032017-09-13 18:05:34 -0400688 *buffer_size = actwrite;
689 fh->offset += actwrite;
690
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200691out:
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100692 return ret;
693}
694
695/**
696 * efi_file_write() - write to file
697 *
698 * This function implements the Write() service of the EFI_FILE_PROTOCOL.
699 *
700 * See the Unified Extensible Firmware Interface (UEFI) specification for
701 * details.
702 *
703 * @this: file protocol instance
704 * @buffer_size: number of bytes to write
705 * @buffer: buffer with the bytes to write
706 * Return: status code
707 */
708static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *this,
709 efi_uintn_t *buffer_size,
710 void *buffer)
711{
712 efi_status_t ret;
713
714 EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer);
715
716 ret = efi_file_write_int(this, buffer_size, buffer);
717
Rob Clark53142032017-09-13 18:05:34 -0400718 return EFI_EXIT(ret);
719}
720
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200721/**
Heinrich Schuchardte759b572021-01-01 08:39:43 +0100722 * efi_file_write_ex() - write to file
723 *
724 * This function implements the WriteEx() service of the EFI_FILE_PROTOCOL.
725 *
726 * See the Unified Extensible Firmware Interface (UEFI) specification for
727 * details.
728 *
729 * @this: file protocol instance
730 * @token: transaction token
731 * Return: status code
732 */
733static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *this,
734 struct efi_file_io_token *token)
735{
736 efi_status_t ret;
737
738 EFI_ENTRY("%p, %p", this, token);
739
740 if (!token) {
741 ret = EFI_INVALID_PARAMETER;
742 goto out;
743 }
744
745 ret = efi_file_write_int(this, &token->buffer_size, token->buffer);
746
747 if (ret == EFI_SUCCESS && token->event) {
748 token->status = EFI_SUCCESS;
749 efi_signal_event(token->event);
750 }
751
752out:
753 return EFI_EXIT(ret);
754}
755
756/**
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200757 * efi_file_getpos() - get current position in file
758 *
759 * This function implements the GetPosition service of the EFI file protocol.
760 * See the UEFI spec for details.
761 *
762 * @file: file handle
763 * @pos: pointer to file position
764 * Return: status code
765 */
Rob Clark53142032017-09-13 18:05:34 -0400766static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200767 u64 *pos)
Rob Clark53142032017-09-13 18:05:34 -0400768{
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200769 efi_status_t ret = EFI_SUCCESS;
Rob Clark53142032017-09-13 18:05:34 -0400770 struct file_handle *fh = to_fh(file);
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200771
Rob Clark53142032017-09-13 18:05:34 -0400772 EFI_ENTRY("%p, %p", file, pos);
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200773
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200774 if (fh->isdir) {
775 ret = EFI_UNSUPPORTED;
776 goto out;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200777 }
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200778
779 *pos = fh->offset;
780out:
781 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400782}
783
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900784efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos)
Rob Clark53142032017-09-13 18:05:34 -0400785{
786 struct file_handle *fh = to_fh(file);
787 efi_status_t ret = EFI_SUCCESS;
788
Rob Clark53142032017-09-13 18:05:34 -0400789 if (fh->isdir) {
790 if (pos != 0) {
791 ret = EFI_UNSUPPORTED;
792 goto error;
793 }
794 fs_closedir(fh->dirs);
795 fh->dirs = NULL;
796 }
797
798 if (pos == ~0ULL) {
799 loff_t file_size;
800
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200801 ret = efi_get_file_size(fh, &file_size);
802 if (ret != EFI_SUCCESS)
Rob Clark53142032017-09-13 18:05:34 -0400803 goto error;
Rob Clark53142032017-09-13 18:05:34 -0400804 pos = file_size;
805 }
806
807 fh->offset = pos;
808
809error:
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +0900810 return ret;
811}
812
813/**
814 * efi_file_setpos() - set current position in file
815 *
816 * This function implements the SetPosition service of the EFI file protocol.
817 * See the UEFI spec for details.
818 *
819 * @file: file handle
820 * @pos: new file position
821 * Return: status code
822 */
823static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
824 u64 pos)
825{
826 efi_status_t ret = EFI_SUCCESS;
827
828 EFI_ENTRY("%p, %llu", file, pos);
829
830 ret = efi_file_setpos_int(file, pos);
831
Rob Clark53142032017-09-13 18:05:34 -0400832 return EFI_EXIT(ret);
833}
834
835static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
Heinrich Schuchardte86380d2018-04-04 15:42:09 +0200836 const efi_guid_t *info_type,
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200837 efi_uintn_t *buffer_size,
838 void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400839{
840 struct file_handle *fh = to_fh(file);
841 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200842 u16 *dst;
Rob Clark53142032017-09-13 18:05:34 -0400843
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100844 EFI_ENTRY("%p, %pUs, %p, %p", file, info_type, buffer_size, buffer);
Rob Clark53142032017-09-13 18:05:34 -0400845
Heinrich Schuchardtc540a0e2019-09-08 10:45:31 +0200846 if (!file || !info_type || !buffer_size ||
847 (*buffer_size && !buffer)) {
848 ret = EFI_INVALID_PARAMETER;
849 goto error;
850 }
851
Rob Clark53142032017-09-13 18:05:34 -0400852 if (!guidcmp(info_type, &efi_file_info_guid)) {
853 struct efi_file_info *info = buffer;
854 char *filename = basename(fh);
855 unsigned int required_size;
856 loff_t file_size;
857
858 /* check buffer size: */
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200859 required_size = sizeof(*info) +
860 2 * (utf8_utf16_strlen(filename) + 1);
Rob Clark53142032017-09-13 18:05:34 -0400861 if (*buffer_size < required_size) {
862 *buffer_size = required_size;
863 ret = EFI_BUFFER_TOO_SMALL;
864 goto error;
865 }
866
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200867 ret = efi_get_file_size(fh, &file_size);
868 if (ret != EFI_SUCCESS)
Rob Clark53142032017-09-13 18:05:34 -0400869 goto error;
Rob Clark53142032017-09-13 18:05:34 -0400870
871 memset(info, 0, required_size);
872
873 info->size = required_size;
874 info->file_size = file_size;
875 info->physical_size = file_size;
876
877 if (fh->isdir)
878 info->attribute |= EFI_FILE_DIRECTORY;
879
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200880 dst = info->file_name;
881 utf8_utf16_strcpy(&dst, filename);
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200882 } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
883 struct efi_file_system_info *info = buffer;
Simon Glassc1c4a8f2020-05-10 11:39:57 -0600884 struct disk_partition part;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200885 efi_uintn_t required_size;
886 int r;
887
888 if (fh->fs->part >= 1)
889 r = part_get_info(fh->fs->desc, fh->fs->part, &part);
890 else
891 r = part_get_info_whole_disk(fh->fs->desc, &part);
892 if (r < 0) {
893 ret = EFI_DEVICE_ERROR;
894 goto error;
895 }
Heinrich Schuchardt0c236c42019-09-08 10:32:54 +0200896 required_size = sizeof(*info) + 2;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200897 if (*buffer_size < required_size) {
898 *buffer_size = required_size;
899 ret = EFI_BUFFER_TOO_SMALL;
900 goto error;
901 }
902
903 memset(info, 0, required_size);
904
905 info->size = required_size;
Heinrich Schuchardta821b0c2019-12-08 10:02:37 +0100906 /*
907 * TODO: We cannot determine if the volume can be written to.
908 */
909 info->read_only = false;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200910 info->volume_size = part.size * part.blksz;
Heinrich Schuchardta821b0c2019-12-08 10:02:37 +0100911 /*
912 * TODO: We currently have no function to determine the free
913 * space. The volume size is the best upper bound we have.
914 */
915 info->free_space = info->volume_size;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200916 info->block_size = part.blksz;
917 /*
918 * TODO: The volume label is not available in U-Boot.
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200919 */
Heinrich Schuchardt0c236c42019-09-08 10:32:54 +0200920 info->volume_label[0] = 0;
921 } else if (!guidcmp(info_type, &efi_system_volume_label_id)) {
922 if (*buffer_size < 2) {
923 *buffer_size = 2;
924 ret = EFI_BUFFER_TOO_SMALL;
925 goto error;
926 }
927 *(u16 *)buffer = 0;
Rob Clark53142032017-09-13 18:05:34 -0400928 } else {
929 ret = EFI_UNSUPPORTED;
930 }
931
932error:
933 return EFI_EXIT(ret);
934}
935
936static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
Heinrich Schuchardte86380d2018-04-04 15:42:09 +0200937 const efi_guid_t *info_type,
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200938 efi_uintn_t buffer_size,
939 void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400940{
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200941 struct file_handle *fh = to_fh(file);
942 efi_status_t ret = EFI_UNSUPPORTED;
943
Heinrich Schuchardt282249d2022-01-16 14:15:31 +0100944 EFI_ENTRY("%p, %pUs, %zu, %p", file, info_type, buffer_size, buffer);
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200945
946 if (!guidcmp(info_type, &efi_file_info_guid)) {
947 struct efi_file_info *info = (struct efi_file_info *)buffer;
948 char *filename = basename(fh);
949 char *new_file_name, *pos;
950 loff_t file_size;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200951
Heinrich Schuchardtdd106d02019-09-08 11:37:07 +0200952 /* The buffer will always contain a file name. */
953 if (buffer_size < sizeof(struct efi_file_info) + 2 ||
954 buffer_size < info->size) {
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200955 ret = EFI_BAD_BUFFER_SIZE;
956 goto out;
957 }
958 /* We cannot change the directory attribute */
959 if (!fh->isdir != !(info->attribute & EFI_FILE_DIRECTORY)) {
960 ret = EFI_ACCESS_DENIED;
961 goto out;
962 }
963 /* Check for renaming */
Heinrich Schuchardt344bc072020-11-10 07:24:16 +0100964 new_file_name = malloc(utf16_utf8_strlen(info->file_name) + 1);
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200965 if (!new_file_name) {
966 ret = EFI_OUT_OF_RESOURCES;
967 goto out;
968 }
969 pos = new_file_name;
970 utf16_utf8_strcpy(&pos, info->file_name);
971 if (strcmp(new_file_name, filename)) {
972 /* TODO: we do not support renaming */
973 EFI_PRINT("Renaming not supported\n");
974 free(new_file_name);
975 ret = EFI_ACCESS_DENIED;
976 goto out;
977 }
978 free(new_file_name);
979 /* Check for truncation */
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200980 ret = efi_get_file_size(fh, &file_size);
981 if (ret != EFI_SUCCESS)
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200982 goto out;
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200983 if (file_size != info->file_size) {
984 /* TODO: we do not support truncation */
985 EFI_PRINT("Truncation not supported\n");
986 ret = EFI_ACCESS_DENIED;
987 goto out;
988 }
989 /*
990 * We do not care for the other attributes
991 * TODO: Support read only
992 */
993 ret = EFI_SUCCESS;
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200994 } else {
Heinrich Schuchardtdd106d02019-09-08 11:37:07 +0200995 /* TODO: We do not support changing the volume label */
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200996 ret = EFI_UNSUPPORTED;
997 }
998out:
999 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -04001000}
1001
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001002/**
1003 * efi_file_flush_int() - flush file
1004 *
1005 * This is the internal implementation of the Flush() and FlushEx() services of
1006 * the EFI_FILE_PROTOCOL.
1007 *
1008 * @this: file protocol instance
1009 * Return: status code
1010 */
1011static efi_status_t efi_file_flush_int(struct efi_file_handle *this)
Rob Clark53142032017-09-13 18:05:34 -04001012{
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001013 struct file_handle *fh = to_fh(this);
Rob Clark53142032017-09-13 18:05:34 -04001014
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001015 if (!this)
1016 return EFI_INVALID_PARAMETER;
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001017
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001018 if (!(fh->open_mode & EFI_FILE_MODE_WRITE))
1019 return EFI_ACCESS_DENIED;
1020
1021 /* TODO: flush for file position after end of file */
1022 return EFI_SUCCESS;
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001023}
1024
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001025/**
1026 * efi_file_flush() - flush file
1027 *
1028 * This function implements the Flush() service of the EFI_FILE_PROTOCOL.
1029 *
1030 * See the Unified Extensible Firmware Interface (UEFI) specification for
1031 * details.
1032 *
1033 * @this: file protocol instance
1034 * Return: status code
1035 */
1036static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *this)
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001037{
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001038 efi_status_t ret;
1039
1040 EFI_ENTRY("%p", this);
1041
1042 ret = efi_file_flush_int(this);
1043
1044 return EFI_EXIT(ret);
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001045}
1046
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001047/**
1048 * efi_file_flush_ex() - flush file
1049 *
1050 * This function implements the FlushEx() service of the EFI_FILE_PROTOCOL.
1051 *
1052 * See the Unified Extensible Firmware Interface (UEFI) specification for
1053 * details.
1054 *
1055 * @this: file protocol instance
1056 * @token: transaction token
1057 * Return: status code
1058 */
1059static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *this,
1060 struct efi_file_io_token *token)
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001061{
Heinrich Schuchardte759b572021-01-01 08:39:43 +01001062 efi_status_t ret;
1063
1064 EFI_ENTRY("%p, %p", this, token);
1065
1066 if (!token) {
1067 ret = EFI_INVALID_PARAMETER;
1068 goto out;
1069 }
1070
1071 ret = efi_file_flush_int(this);
1072
1073 if (ret == EFI_SUCCESS && token->event) {
1074 token->status = EFI_SUCCESS;
1075 efi_signal_event(token->event);
1076 }
1077
1078out:
1079 return EFI_EXIT(ret);
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001080}
1081
Rob Clark53142032017-09-13 18:05:34 -04001082static const struct efi_file_handle efi_file_handle_protocol = {
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001083 .rev = EFI_FILE_PROTOCOL_REVISION2,
Rob Clark53142032017-09-13 18:05:34 -04001084 .open = efi_file_open,
1085 .close = efi_file_close,
1086 .delete = efi_file_delete,
1087 .read = efi_file_read,
1088 .write = efi_file_write,
1089 .getpos = efi_file_getpos,
1090 .setpos = efi_file_setpos,
1091 .getinfo = efi_file_getinfo,
1092 .setinfo = efi_file_setinfo,
1093 .flush = efi_file_flush,
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +02001094 .open_ex = efi_file_open_ex,
1095 .read_ex = efi_file_read_ex,
1096 .write_ex = efi_file_write_ex,
1097 .flush_ex = efi_file_flush_ex,
Rob Clark53142032017-09-13 18:05:34 -04001098};
1099
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001100/**
1101 * efi_file_from_path() - open file via device path
1102 *
Heinrich Schuchardt4395adb2022-11-10 14:14:06 +01001103 * The device path @fp consists of the device path of the handle with the
1104 * simple file system protocol and one or more file path device path nodes.
1105 * The concatenation of all file path names provides the total file path.
1106 *
1107 * The code starts at the first file path node and tries to open that file or
1108 * directory. If there is a succeding file path node, the code opens it relative
1109 * to this directory and continues iterating until reaching the last file path
1110 * node.
1111 *
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001112 * @fp: device path
Heinrich Schuchardtfbe90212022-01-20 19:48:20 +01001113 * Return: EFI_FILE_PROTOCOL for the file or NULL
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001114 */
Rob Clark53142032017-09-13 18:05:34 -04001115struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
1116{
1117 struct efi_simple_file_system_protocol *v;
1118 struct efi_file_handle *f;
1119 efi_status_t ret;
1120
1121 v = efi_fs_from_path(fp);
1122 if (!v)
1123 return NULL;
1124
1125 EFI_CALL(ret = v->open_volume(v, &f));
1126 if (ret != EFI_SUCCESS)
1127 return NULL;
1128
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001129 /* Skip over device-path nodes before the file path. */
Rob Clark53142032017-09-13 18:05:34 -04001130 while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
1131 fp = efi_dp_next(fp);
1132
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +01001133 /*
1134 * Step through the nodes of the directory path until the actual file
1135 * node is reached which is the final node in the device path.
1136 */
Rob Clark53142032017-09-13 18:05:34 -04001137 while (fp) {
1138 struct efi_device_path_file_path *fdp =
1139 container_of(fp, struct efi_device_path_file_path, dp);
1140 struct efi_file_handle *f2;
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +02001141 u16 *filename;
Ilias Apalodimas50279dc2022-11-11 20:04:31 +02001142 size_t filename_sz;
Rob Clark53142032017-09-13 18:05:34 -04001143
1144 if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
1145 printf("bad file path!\n");
Ilias Apalodimas08d1af22022-11-11 18:20:37 +02001146 EFI_CALL(f->close(f));
Rob Clark53142032017-09-13 18:05:34 -04001147 return NULL;
1148 }
1149
Ilias Apalodimas9124bc62022-11-10 15:31:30 +02001150 /*
1151 * UEFI specification requires pointers that are passed to
1152 * protocol member functions to be aligned. So memcpy it
1153 * unconditionally
1154 */
Ilias Apalodimas50279dc2022-11-11 20:04:31 +02001155 if (fdp->dp.length <= offsetof(struct efi_device_path_file_path, str))
1156 return NULL;
1157 filename_sz = fdp->dp.length -
1158 offsetof(struct efi_device_path_file_path, str);
1159 filename = malloc(filename_sz);
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +02001160 if (!filename)
1161 return NULL;
Ilias Apalodimas50279dc2022-11-11 20:04:31 +02001162 memcpy(filename, fdp->str, filename_sz);
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +02001163 EFI_CALL(ret = f->open(f, &f2, filename,
Rob Clark53142032017-09-13 18:05:34 -04001164 EFI_FILE_MODE_READ, 0));
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +02001165 free(filename);
Rob Clark53142032017-09-13 18:05:34 -04001166 if (ret != EFI_SUCCESS)
1167 return NULL;
1168
1169 fp = efi_dp_next(fp);
1170
1171 EFI_CALL(f->close(f));
1172 f = f2;
1173 }
1174
1175 return f;
1176}
1177
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001178efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
1179 struct efi_file_handle **root)
1180{
1181 struct file_system *fs = to_fs(this);
1182
1183 *root = file_open(fs, NULL, NULL, 0, 0);
1184
1185 return EFI_SUCCESS;
1186}
1187
Rob Clark53142032017-09-13 18:05:34 -04001188static efi_status_t EFIAPI
1189efi_open_volume(struct efi_simple_file_system_protocol *this,
1190 struct efi_file_handle **root)
1191{
Rob Clark53142032017-09-13 18:05:34 -04001192 EFI_ENTRY("%p, %p", this, root);
1193
Masahisa Kojimac5ff0a02022-09-12 17:33:50 +09001194 return EFI_EXIT(efi_open_volume_int(this, root));
Rob Clark53142032017-09-13 18:05:34 -04001195}
1196
Heinrich Schuchardtf9e8ada2023-07-30 14:03:53 +02001197efi_status_t
1198efi_create_simple_file_system(struct blk_desc *desc, int part,
1199 struct efi_device_path *dp,
1200 struct efi_simple_file_system_protocol **fsp)
Rob Clark53142032017-09-13 18:05:34 -04001201{
1202 struct file_system *fs;
1203
1204 fs = calloc(1, sizeof(*fs));
Heinrich Schuchardtf9e8ada2023-07-30 14:03:53 +02001205 if (!fs)
1206 return EFI_OUT_OF_RESOURCES;
Rob Clark53142032017-09-13 18:05:34 -04001207 fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
1208 fs->base.open_volume = efi_open_volume;
1209 fs->desc = desc;
1210 fs->part = part;
1211 fs->dp = dp;
Heinrich Schuchardtf9e8ada2023-07-30 14:03:53 +02001212 *fsp = &fs->base;
Rob Clark53142032017-09-13 18:05:34 -04001213
Heinrich Schuchardtf9e8ada2023-07-30 14:03:53 +02001214 return EFI_SUCCESS;
Rob Clark53142032017-09-13 18:05:34 -04001215}