blob: 72b7ec1e63c1ab9d9a9d13c931a84a3aa595fa7a [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);
198
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200199 fh->open_mode = open_mode;
Rob Clark53142032017-09-13 18:05:34 -0400200 fh->base = efi_file_handle_protocol;
201 fh->fs = fs;
202
203 if (parent) {
204 char *p = fh->path;
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200205 int exists;
Rob Clark53142032017-09-13 18:05:34 -0400206
207 if (plen > 0) {
208 strcpy(p, parent->path);
209 p += plen - 1;
210 *p++ = '/';
211 }
212
Heinrich Schuchardtdb47c342019-01-12 12:02:33 +0100213 utf16_to_utf8((u8 *)p, file_name, flen);
Rob Clark53142032017-09-13 18:05:34 -0400214
215 if (sanitize_path(fh->path))
216 goto error;
217
218 /* check if file exists: */
219 if (set_blk_dev(fh))
220 goto error;
221
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200222 exists = fs_exists(fh->path);
Heinrich Schuchardt3ab87ac2019-02-09 22:23:48 +0100223 /* fs_exists() calls fs_close(), so open file system again */
224 if (set_blk_dev(fh))
225 goto error;
226
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200227 if (!exists) {
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200228 if (!(open_mode & EFI_FILE_MODE_CREATE) ||
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200229 efi_create_file(fh, attributes))
230 goto error;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200231 if (set_blk_dev(fh))
232 goto error;
Heinrich Schuchardt34c303b2019-04-06 16:27:34 +0200233 }
234
Rob Clark53142032017-09-13 18:05:34 -0400235 /* figure out if file is a directory: */
236 fh->isdir = is_dir(fh);
237 } else {
238 fh->isdir = 1;
239 strcpy(fh->path, "");
240 }
241
242 return &fh->base;
243
244error:
245 free(fh);
246 return NULL;
247}
248
249static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
250 struct efi_file_handle **new_handle,
Heinrich Schuchardtdb47c342019-01-12 12:02:33 +0100251 u16 *file_name, u64 open_mode, u64 attributes)
Rob Clark53142032017-09-13 18:05:34 -0400252{
253 struct file_handle *fh = to_fh(file);
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200254 efi_status_t ret;
Rob Clark53142032017-09-13 18:05:34 -0400255
Simon Glass60209e62019-01-07 16:44:18 -0700256 EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle,
Heinrich Schuchardt9de5c4c2019-03-19 19:16:23 +0100257 file_name, open_mode, attributes);
Rob Clark53142032017-09-13 18:05:34 -0400258
Heinrich Schuchardtd6932c02018-09-12 18:43:58 +0200259 /* Check parameters */
Heinrich Schuchardtb63f9fc2018-09-15 20:43:46 +0200260 if (!file || !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 Schuchardtd6932c02018-09-12 18:43:58 +0200294out:
295 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400296}
297
298static efi_status_t file_close(struct file_handle *fh)
299{
300 fs_closedir(fh->dirs);
301 free(fh);
302 return EFI_SUCCESS;
303}
304
305static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
306{
307 struct file_handle *fh = to_fh(file);
308 EFI_ENTRY("%p", file);
309 return EFI_EXIT(file_close(fh));
310}
311
312static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
313{
314 struct file_handle *fh = to_fh(file);
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900315 efi_status_t ret = EFI_SUCCESS;
316
Rob Clark53142032017-09-13 18:05:34 -0400317 EFI_ENTRY("%p", file);
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900318
Heinrich Schuchardt646faf32019-06-17 22:00:13 +0200319 if (set_blk_dev(fh) || fs_unlink(fh->path))
320 ret = EFI_WARN_DELETE_FAILURE;
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900321
Rob Clark53142032017-09-13 18:05:34 -0400322 file_close(fh);
AKASHI Takahiro4dd5b9c2018-09-11 15:59:16 +0900323 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400324}
325
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200326/**
327 * efi_get_file_size() - determine the size of a file
328 *
329 * @fh: file handle
330 * @file_size: pointer to receive file size
331 * Return: status code
332 */
333static efi_status_t efi_get_file_size(struct file_handle *fh,
334 loff_t *file_size)
335{
336 if (set_blk_dev(fh))
337 return EFI_DEVICE_ERROR;
338
339 if (fs_size(fh->path, file_size))
340 return EFI_DEVICE_ERROR;
341
342 return EFI_SUCCESS;
343}
344
Rob Clark53142032017-09-13 18:05:34 -0400345static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
346 void *buffer)
347{
348 loff_t actread;
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200349 efi_status_t ret;
350 loff_t file_size;
Rob Clark53142032017-09-13 18:05:34 -0400351
Stefan Sørensen6b398d02020-07-22 09:43:31 +0200352 if (!buffer) {
353 ret = EFI_INVALID_PARAMETER;
354 return ret;
355 }
356
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200357 ret = efi_get_file_size(fh, &file_size);
358 if (ret != EFI_SUCCESS)
359 return ret;
360 if (file_size < fh->offset) {
361 ret = EFI_DEVICE_ERROR;
362 return ret;
363 }
364
365 if (set_blk_dev(fh))
366 return EFI_DEVICE_ERROR;
Simon Glass849688f2018-09-15 00:51:00 -0600367 if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
Rob Clark53142032017-09-13 18:05:34 -0400368 *buffer_size, &actread))
369 return EFI_DEVICE_ERROR;
370
371 *buffer_size = actread;
372 fh->offset += actread;
373
374 return EFI_SUCCESS;
375}
376
377static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
378 void *buffer)
379{
380 struct efi_file_info *info = buffer;
381 struct fs_dirent *dent;
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200382 u64 required_size;
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200383 u16 *dst;
Rob Clark53142032017-09-13 18:05:34 -0400384
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200385 if (set_blk_dev(fh))
386 return EFI_DEVICE_ERROR;
387
Rob Clark53142032017-09-13 18:05:34 -0400388 if (!fh->dirs) {
389 assert(fh->offset == 0);
390 fh->dirs = fs_opendir(fh->path);
391 if (!fh->dirs)
392 return EFI_DEVICE_ERROR;
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200393 fh->dent = NULL;
Rob Clark53142032017-09-13 18:05:34 -0400394 }
395
396 /*
397 * So this is a bit awkward. Since fs layer is stateful and we
398 * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
399 * we might have to return without consuming the dent.. so we
400 * have to stash it for next call.
401 */
402 if (fh->dent) {
403 dent = fh->dent;
Rob Clark53142032017-09-13 18:05:34 -0400404 } else {
405 dent = fs_readdir(fh->dirs);
406 }
407
Rob Clark53142032017-09-13 18:05:34 -0400408 if (!dent) {
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200409 /* no more files in directory */
410 *buffer_size = 0;
Rob Clark53142032017-09-13 18:05:34 -0400411 return EFI_SUCCESS;
412 }
413
414 /* check buffer size: */
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200415 required_size = sizeof(*info) +
416 2 * (utf8_utf16_strlen(dent->name) + 1);
Rob Clark53142032017-09-13 18:05:34 -0400417 if (*buffer_size < required_size) {
418 *buffer_size = required_size;
419 fh->dent = dent;
420 return EFI_BUFFER_TOO_SMALL;
421 }
Stefan Sørensen6b398d02020-07-22 09:43:31 +0200422 if (!buffer)
423 return EFI_INVALID_PARAMETER;
Heinrich Schuchardtda062602019-09-07 22:34:07 +0200424 fh->dent = NULL;
Rob Clark53142032017-09-13 18:05:34 -0400425
426 *buffer_size = required_size;
427 memset(info, 0, required_size);
428
429 info->size = required_size;
430 info->file_size = dent->size;
431 info->physical_size = dent->size;
432
433 if (dent->type == FS_DT_DIR)
434 info->attribute |= EFI_FILE_DIRECTORY;
435
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200436 dst = info->file_name;
437 utf8_utf16_strcpy(&dst, dent->name);
Rob Clark53142032017-09-13 18:05:34 -0400438
439 fh->offset++;
440
441 return EFI_SUCCESS;
442}
443
444static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200445 efi_uintn_t *buffer_size, void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400446{
447 struct file_handle *fh = to_fh(file);
448 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200449 u64 bs;
Rob Clark53142032017-09-13 18:05:34 -0400450
451 EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
452
Stefan Sørensen6b398d02020-07-22 09:43:31 +0200453 if (!buffer_size) {
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200454 ret = EFI_INVALID_PARAMETER;
455 goto error;
456 }
457
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200458 bs = *buffer_size;
Rob Clark53142032017-09-13 18:05:34 -0400459 if (fh->isdir)
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200460 ret = dir_read(fh, &bs, buffer);
Rob Clark53142032017-09-13 18:05:34 -0400461 else
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200462 ret = file_read(fh, &bs, buffer);
463 if (bs <= SIZE_MAX)
464 *buffer_size = bs;
465 else
466 *buffer_size = SIZE_MAX;
Rob Clark53142032017-09-13 18:05:34 -0400467
468error:
469 return EFI_EXIT(ret);
470}
471
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200472/**
473 * efi_file_write() - write to file
474 *
475 * This function implements the Write() service of the EFI_FILE_PROTOCOL.
476 *
477 * See the Unified Extensible Firmware Interface (UEFI) specification for
478 * details.
479 *
480 * @file: file handle
481 * @buffer_size: number of bytes to write
482 * @buffer: buffer with the bytes to write
483 * Return: status code
484 */
Rob Clark53142032017-09-13 18:05:34 -0400485static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200486 efi_uintn_t *buffer_size,
487 void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400488{
489 struct file_handle *fh = to_fh(file);
490 efi_status_t ret = EFI_SUCCESS;
491 loff_t actwrite;
492
493 EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
494
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200495 if (!file || !buffer_size || !buffer) {
496 ret = EFI_INVALID_PARAMETER;
497 goto out;
498 }
499 if (fh->isdir) {
500 ret = EFI_UNSUPPORTED;
501 goto out;
502 }
503 if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) {
504 ret = EFI_ACCESS_DENIED;
505 goto out;
506 }
507
508 if (!*buffer_size)
509 goto out;
510
Rob Clark53142032017-09-13 18:05:34 -0400511 if (set_blk_dev(fh)) {
512 ret = EFI_DEVICE_ERROR;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200513 goto out;
Rob Clark53142032017-09-13 18:05:34 -0400514 }
Simon Glass849688f2018-09-15 00:51:00 -0600515 if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
Rob Clark53142032017-09-13 18:05:34 -0400516 &actwrite)) {
517 ret = EFI_DEVICE_ERROR;
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200518 goto out;
Rob Clark53142032017-09-13 18:05:34 -0400519 }
Rob Clark53142032017-09-13 18:05:34 -0400520 *buffer_size = actwrite;
521 fh->offset += actwrite;
522
Heinrich Schuchardtb7e87a22019-09-06 21:37:21 +0200523out:
Rob Clark53142032017-09-13 18:05:34 -0400524 return EFI_EXIT(ret);
525}
526
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200527/**
528 * efi_file_getpos() - get current position in file
529 *
530 * This function implements the GetPosition service of the EFI file protocol.
531 * See the UEFI spec for details.
532 *
533 * @file: file handle
534 * @pos: pointer to file position
535 * Return: status code
536 */
Rob Clark53142032017-09-13 18:05:34 -0400537static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200538 u64 *pos)
Rob Clark53142032017-09-13 18:05:34 -0400539{
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200540 efi_status_t ret = EFI_SUCCESS;
Rob Clark53142032017-09-13 18:05:34 -0400541 struct file_handle *fh = to_fh(file);
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200542
Rob Clark53142032017-09-13 18:05:34 -0400543 EFI_ENTRY("%p, %p", file, pos);
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200544
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200545 if (fh->isdir) {
546 ret = EFI_UNSUPPORTED;
547 goto out;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200548 }
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200549
550 *pos = fh->offset;
551out:
552 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400553}
554
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200555/**
556 * efi_file_setpos() - set current position in file
557 *
558 * This function implements the SetPosition service of the EFI file protocol.
559 * See the UEFI spec for details.
560 *
561 * @file: file handle
562 * @pos: new file position
563 * Return: status code
564 */
Rob Clark53142032017-09-13 18:05:34 -0400565static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200566 u64 pos)
Rob Clark53142032017-09-13 18:05:34 -0400567{
568 struct file_handle *fh = to_fh(file);
569 efi_status_t ret = EFI_SUCCESS;
570
Heinrich Schuchardt21ae0572018-10-07 05:26:26 +0200571 EFI_ENTRY("%p, %llu", file, pos);
Rob Clark53142032017-09-13 18:05:34 -0400572
573 if (fh->isdir) {
574 if (pos != 0) {
575 ret = EFI_UNSUPPORTED;
576 goto error;
577 }
578 fs_closedir(fh->dirs);
579 fh->dirs = NULL;
580 }
581
582 if (pos == ~0ULL) {
583 loff_t file_size;
584
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200585 ret = efi_get_file_size(fh, &file_size);
586 if (ret != EFI_SUCCESS)
Rob Clark53142032017-09-13 18:05:34 -0400587 goto error;
Rob Clark53142032017-09-13 18:05:34 -0400588 pos = file_size;
589 }
590
591 fh->offset = pos;
592
593error:
594 return EFI_EXIT(ret);
595}
596
597static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
Heinrich Schuchardte86380d2018-04-04 15:42:09 +0200598 const efi_guid_t *info_type,
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200599 efi_uintn_t *buffer_size,
600 void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400601{
602 struct file_handle *fh = to_fh(file);
603 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200604 u16 *dst;
Rob Clark53142032017-09-13 18:05:34 -0400605
Heinrich Schuchardte8c99b02018-09-14 22:46:34 +0200606 EFI_ENTRY("%p, %pUl, %p, %p", file, info_type, buffer_size, buffer);
Rob Clark53142032017-09-13 18:05:34 -0400607
Heinrich Schuchardtc540a0e2019-09-08 10:45:31 +0200608 if (!file || !info_type || !buffer_size ||
609 (*buffer_size && !buffer)) {
610 ret = EFI_INVALID_PARAMETER;
611 goto error;
612 }
613
Rob Clark53142032017-09-13 18:05:34 -0400614 if (!guidcmp(info_type, &efi_file_info_guid)) {
615 struct efi_file_info *info = buffer;
616 char *filename = basename(fh);
617 unsigned int required_size;
618 loff_t file_size;
619
620 /* check buffer size: */
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200621 required_size = sizeof(*info) +
622 2 * (utf8_utf16_strlen(filename) + 1);
Rob Clark53142032017-09-13 18:05:34 -0400623 if (*buffer_size < required_size) {
624 *buffer_size = required_size;
625 ret = EFI_BUFFER_TOO_SMALL;
626 goto error;
627 }
628
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200629 ret = efi_get_file_size(fh, &file_size);
630 if (ret != EFI_SUCCESS)
Rob Clark53142032017-09-13 18:05:34 -0400631 goto error;
Rob Clark53142032017-09-13 18:05:34 -0400632
633 memset(info, 0, required_size);
634
635 info->size = required_size;
636 info->file_size = file_size;
637 info->physical_size = file_size;
638
639 if (fh->isdir)
640 info->attribute |= EFI_FILE_DIRECTORY;
641
Heinrich Schuchardt2ad17f92019-09-07 21:05:45 +0200642 dst = info->file_name;
643 utf8_utf16_strcpy(&dst, filename);
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200644 } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
645 struct efi_file_system_info *info = buffer;
Simon Glassc1c4a8f2020-05-10 11:39:57 -0600646 struct disk_partition part;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200647 efi_uintn_t required_size;
648 int r;
649
650 if (fh->fs->part >= 1)
651 r = part_get_info(fh->fs->desc, fh->fs->part, &part);
652 else
653 r = part_get_info_whole_disk(fh->fs->desc, &part);
654 if (r < 0) {
655 ret = EFI_DEVICE_ERROR;
656 goto error;
657 }
Heinrich Schuchardt0c236c42019-09-08 10:32:54 +0200658 required_size = sizeof(*info) + 2;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200659 if (*buffer_size < required_size) {
660 *buffer_size = required_size;
661 ret = EFI_BUFFER_TOO_SMALL;
662 goto error;
663 }
664
665 memset(info, 0, required_size);
666
667 info->size = required_size;
Heinrich Schuchardta821b0c2019-12-08 10:02:37 +0100668 /*
669 * TODO: We cannot determine if the volume can be written to.
670 */
671 info->read_only = false;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200672 info->volume_size = part.size * part.blksz;
Heinrich Schuchardta821b0c2019-12-08 10:02:37 +0100673 /*
674 * TODO: We currently have no function to determine the free
675 * space. The volume size is the best upper bound we have.
676 */
677 info->free_space = info->volume_size;
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200678 info->block_size = part.blksz;
679 /*
680 * TODO: The volume label is not available in U-Boot.
Heinrich Schuchardt37fb4b92018-04-04 15:42:11 +0200681 */
Heinrich Schuchardt0c236c42019-09-08 10:32:54 +0200682 info->volume_label[0] = 0;
683 } else if (!guidcmp(info_type, &efi_system_volume_label_id)) {
684 if (*buffer_size < 2) {
685 *buffer_size = 2;
686 ret = EFI_BUFFER_TOO_SMALL;
687 goto error;
688 }
689 *(u16 *)buffer = 0;
Rob Clark53142032017-09-13 18:05:34 -0400690 } else {
691 ret = EFI_UNSUPPORTED;
692 }
693
694error:
695 return EFI_EXIT(ret);
696}
697
698static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
Heinrich Schuchardte86380d2018-04-04 15:42:09 +0200699 const efi_guid_t *info_type,
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200700 efi_uintn_t buffer_size,
701 void *buffer)
Rob Clark53142032017-09-13 18:05:34 -0400702{
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200703 struct file_handle *fh = to_fh(file);
704 efi_status_t ret = EFI_UNSUPPORTED;
705
706 EFI_ENTRY("%p, %pUl, %zu, %p", file, info_type, buffer_size, buffer);
707
708 if (!guidcmp(info_type, &efi_file_info_guid)) {
709 struct efi_file_info *info = (struct efi_file_info *)buffer;
710 char *filename = basename(fh);
711 char *new_file_name, *pos;
712 loff_t file_size;
Heinrich Schuchardt6b06e0d2018-04-03 22:37:11 +0200713
Heinrich Schuchardtdd106d02019-09-08 11:37:07 +0200714 /* The buffer will always contain a file name. */
715 if (buffer_size < sizeof(struct efi_file_info) + 2 ||
716 buffer_size < info->size) {
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200717 ret = EFI_BAD_BUFFER_SIZE;
718 goto out;
719 }
720 /* We cannot change the directory attribute */
721 if (!fh->isdir != !(info->attribute & EFI_FILE_DIRECTORY)) {
722 ret = EFI_ACCESS_DENIED;
723 goto out;
724 }
725 /* Check for renaming */
Heinrich Schuchardt344bc072020-11-10 07:24:16 +0100726 new_file_name = malloc(utf16_utf8_strlen(info->file_name) + 1);
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200727 if (!new_file_name) {
728 ret = EFI_OUT_OF_RESOURCES;
729 goto out;
730 }
731 pos = new_file_name;
732 utf16_utf8_strcpy(&pos, info->file_name);
733 if (strcmp(new_file_name, filename)) {
734 /* TODO: we do not support renaming */
735 EFI_PRINT("Renaming not supported\n");
736 free(new_file_name);
737 ret = EFI_ACCESS_DENIED;
738 goto out;
739 }
740 free(new_file_name);
741 /* Check for truncation */
Heinrich Schuchardtdf862792019-09-07 23:28:04 +0200742 ret = efi_get_file_size(fh, &file_size);
743 if (ret != EFI_SUCCESS)
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200744 goto out;
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200745 if (file_size != info->file_size) {
746 /* TODO: we do not support truncation */
747 EFI_PRINT("Truncation not supported\n");
748 ret = EFI_ACCESS_DENIED;
749 goto out;
750 }
751 /*
752 * We do not care for the other attributes
753 * TODO: Support read only
754 */
755 ret = EFI_SUCCESS;
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200756 } else {
Heinrich Schuchardtdd106d02019-09-08 11:37:07 +0200757 /* TODO: We do not support changing the volume label */
Heinrich Schuchardtf1fe17e2019-04-06 18:17:39 +0200758 ret = EFI_UNSUPPORTED;
759 }
760out:
761 return EFI_EXIT(ret);
Rob Clark53142032017-09-13 18:05:34 -0400762}
763
764static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
765{
766 EFI_ENTRY("%p", file);
767 return EFI_EXIT(EFI_SUCCESS);
768}
769
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +0200770static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *file,
771 struct efi_file_handle **new_handle,
772 u16 *file_name, u64 open_mode, u64 attributes,
773 struct efi_file_io_token *token)
774{
775 return EFI_UNSUPPORTED;
776}
777
778static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *file,
779 struct efi_file_io_token *token)
780{
781 return EFI_UNSUPPORTED;
782}
783
784static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *file,
785 struct efi_file_io_token *token)
786{
787 return EFI_UNSUPPORTED;
788}
789
790static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *file,
791 struct efi_file_io_token *token)
792{
793 return EFI_UNSUPPORTED;
794}
795
Rob Clark53142032017-09-13 18:05:34 -0400796static const struct efi_file_handle efi_file_handle_protocol = {
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +0200797 .rev = EFI_FILE_PROTOCOL_REVISION2,
Rob Clark53142032017-09-13 18:05:34 -0400798 .open = efi_file_open,
799 .close = efi_file_close,
800 .delete = efi_file_delete,
801 .read = efi_file_read,
802 .write = efi_file_write,
803 .getpos = efi_file_getpos,
804 .setpos = efi_file_setpos,
805 .getinfo = efi_file_getinfo,
806 .setinfo = efi_file_setinfo,
807 .flush = efi_file_flush,
Heinrich Schuchardtf2dcbac2019-09-08 09:35:32 +0200808 .open_ex = efi_file_open_ex,
809 .read_ex = efi_file_read_ex,
810 .write_ex = efi_file_write_ex,
811 .flush_ex = efi_file_flush_ex,
Rob Clark53142032017-09-13 18:05:34 -0400812};
813
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +0100814/**
815 * efi_file_from_path() - open file via device path
816 *
817 * @fp: device path
818 * @return: EFI_FILE_PROTOCOL for the file or NULL
819 */
Rob Clark53142032017-09-13 18:05:34 -0400820struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
821{
822 struct efi_simple_file_system_protocol *v;
823 struct efi_file_handle *f;
824 efi_status_t ret;
825
826 v = efi_fs_from_path(fp);
827 if (!v)
828 return NULL;
829
830 EFI_CALL(ret = v->open_volume(v, &f));
831 if (ret != EFI_SUCCESS)
832 return NULL;
833
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +0100834 /* Skip over device-path nodes before the file path. */
Rob Clark53142032017-09-13 18:05:34 -0400835 while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
836 fp = efi_dp_next(fp);
837
Heinrich Schuchardtc1476c22019-02-04 21:24:35 +0100838 /*
839 * Step through the nodes of the directory path until the actual file
840 * node is reached which is the final node in the device path.
841 */
Rob Clark53142032017-09-13 18:05:34 -0400842 while (fp) {
843 struct efi_device_path_file_path *fdp =
844 container_of(fp, struct efi_device_path_file_path, dp);
845 struct efi_file_handle *f2;
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +0200846 u16 *filename;
Rob Clark53142032017-09-13 18:05:34 -0400847
848 if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
849 printf("bad file path!\n");
850 f->close(f);
851 return NULL;
852 }
853
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +0200854 filename = u16_strdup(fdp->str);
855 if (!filename)
856 return NULL;
857 EFI_CALL(ret = f->open(f, &f2, filename,
Rob Clark53142032017-09-13 18:05:34 -0400858 EFI_FILE_MODE_READ, 0));
Heinrich Schuchardt2cfcd992019-07-14 20:14:46 +0200859 free(filename);
Rob Clark53142032017-09-13 18:05:34 -0400860 if (ret != EFI_SUCCESS)
861 return NULL;
862
863 fp = efi_dp_next(fp);
864
865 EFI_CALL(f->close(f));
866 f = f2;
867 }
868
869 return f;
870}
871
872static efi_status_t EFIAPI
873efi_open_volume(struct efi_simple_file_system_protocol *this,
874 struct efi_file_handle **root)
875{
876 struct file_system *fs = to_fs(this);
877
878 EFI_ENTRY("%p, %p", this, root);
879
AKASHI Takahiro0c672b02018-09-11 15:59:12 +0900880 *root = file_open(fs, NULL, NULL, 0, 0);
Rob Clark53142032017-09-13 18:05:34 -0400881
882 return EFI_EXIT(EFI_SUCCESS);
883}
884
885struct efi_simple_file_system_protocol *
886efi_simple_file_system(struct blk_desc *desc, int part,
887 struct efi_device_path *dp)
888{
889 struct file_system *fs;
890
891 fs = calloc(1, sizeof(*fs));
892 fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
893 fs->base.open_volume = efi_open_volume;
894 fs->desc = desc;
895 fs->part = part;
896 fs->dp = dp;
897
898 return &fs->base;
899}