blob: 140116ddc4ace7ccd2e1916aad62b9a1bb47c498 [file] [log] [blame]
Tom Rinif739fcd2018-05-07 17:02:21 -04001// SPDX-License-Identifier: GPL-2.0+
Rob Clark2a920802017-09-13 18:05:34 -04002/*
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +02003 * EFI_FILE_PROTOCOL
Rob Clark2a920802017-09-13 18:05:34 -04004 *
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +02005 * Copyright (c) 2017 Rob Clark
Rob Clark2a920802017-09-13 18:05:34 -04006 */
7
8#include <common.h>
9#include <charset.h>
10#include <efi_loader.h>
11#include <malloc.h>
Alexander Grafd5a5a5a2018-08-08 03:54:32 -060012#include <mapmem.h>
Rob Clark2a920802017-09-13 18:05:34 -040013#include <fs.h>
14
Heinrich Schuchardt9e6835e2018-04-04 15:42:11 +020015/* GUID for file system information */
16const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
17
Heinrich Schuchardt632834c2019-09-08 10:32:54 +020018/* GUID to obtain the volume label */
19const efi_guid_t efi_system_volume_label_id = EFI_FILE_SYSTEM_VOLUME_LABEL_ID;
20
Rob Clark2a920802017-09-13 18:05:34 -040021struct file_system {
22 struct efi_simple_file_system_protocol base;
23 struct efi_device_path *dp;
24 struct blk_desc *desc;
25 int part;
26};
27#define to_fs(x) container_of(x, struct file_system, base)
28
29struct file_handle {
30 struct efi_file_handle base;
31 struct file_system *fs;
32 loff_t offset; /* current file position/cursor */
33 int isdir;
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +020034 u64 open_mode;
Rob Clark2a920802017-09-13 18:05:34 -040035
36 /* for reading a directory: */
37 struct fs_dir_stream *dirs;
38 struct fs_dirent *dent;
39
40 char path[0];
41};
42#define to_fh(x) container_of(x, struct file_handle, base)
43
44static const struct efi_file_handle efi_file_handle_protocol;
45
46static char *basename(struct file_handle *fh)
47{
48 char *s = strrchr(fh->path, '/');
49 if (s)
50 return s + 1;
51 return fh->path;
52}
53
54static int set_blk_dev(struct file_handle *fh)
55{
56 return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
57}
58
Heinrich Schuchardt2c61e0c2018-10-02 05:57:32 +020059/**
60 * is_dir() - check if file handle points to directory
61 *
62 * We assume that set_blk_dev(fh) has been called already.
63 *
64 * @fh: file handle
65 * Return: true if file handle points to a directory
66 */
Rob Clark2a920802017-09-13 18:05:34 -040067static int is_dir(struct file_handle *fh)
68{
69 struct fs_dir_stream *dirs;
70
Rob Clark2a920802017-09-13 18:05:34 -040071 dirs = fs_opendir(fh->path);
72 if (!dirs)
73 return 0;
74
75 fs_closedir(dirs);
76
77 return 1;
78}
79
80/*
81 * Normalize a path which may include either back or fwd slashes,
82 * double slashes, . or .. entries in the path, etc.
83 */
84static int sanitize_path(char *path)
85{
86 char *p;
87
88 /* backslash to slash: */
89 p = path;
90 while ((p = strchr(p, '\\')))
91 *p++ = '/';
92
93 /* handle double-slashes: */
94 p = path;
95 while ((p = strstr(p, "//"))) {
96 char *src = p + 1;
97 memmove(p, src, strlen(src) + 1);
98 }
99
100 /* handle extra /.'s */
101 p = path;
102 while ((p = strstr(p, "/."))) {
103 /*
104 * You'd be tempted to do this *after* handling ".."s
105 * below to avoid having to check if "/." is start of
106 * a "/..", but that won't have the correct results..
107 * for example, "/foo/./../bar" would get resolved to
108 * "/foo/bar" if you did these two passes in the other
109 * order
110 */
111 if (p[2] == '.') {
112 p += 2;
113 continue;
114 }
115 char *src = p + 2;
116 memmove(p, src, strlen(src) + 1);
117 }
118
119 /* handle extra /..'s: */
120 p = path;
121 while ((p = strstr(p, "/.."))) {
122 char *src = p + 3;
123
124 p--;
125
126 /* find beginning of previous path entry: */
127 while (true) {
128 if (p < path)
129 return -1;
130 if (*p == '/')
131 break;
132 p--;
133 }
134
135 memmove(p, src, strlen(src) + 1);
136 }
137
138 return 0;
139}
140
Heinrich Schuchardt050cea72018-09-12 19:00:02 +0200141/**
Heinrich Schuchardtcb0c2a72019-04-06 16:27:34 +0200142 * efi_create_file() - create file or directory
143 *
144 * @fh: file handle
145 * @attributes: attributes for newly created file
146 * Returns: 0 for success
147 */
148static int efi_create_file(struct file_handle *fh, u64 attributes)
149{
150 loff_t actwrite;
151 void *buffer = &actwrite;
152
153 if (attributes & EFI_FILE_DIRECTORY)
154 return fs_mkdir(fh->path);
155 else
156 return fs_write(fh->path, map_to_sysmem(buffer), 0, 0,
157 &actwrite);
158}
159
160/**
Heinrich Schuchardt050cea72018-09-12 19:00:02 +0200161 * file_open() - open a file handle
162 *
163 * @fs: file system
164 * @parent: directory relative to which the file is to be opened
165 * @file_name: path of the file to be opened. '\', '.', or '..' may
166 * be used as modifiers. A leading backslash indicates an
167 * absolute path.
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200168 * @open_mode: bit mask indicating the access mode (read, write,
Heinrich Schuchardt050cea72018-09-12 19:00:02 +0200169 * create)
170 * @attributes: attributes for newly created file
171 * Returns: handle to the opened file or NULL
Rob Clark2a920802017-09-13 18:05:34 -0400172 */
173static struct efi_file_handle *file_open(struct file_system *fs,
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200174 struct file_handle *parent, u16 *file_name, u64 open_mode,
AKASHI Takahiro5bc84a12018-09-11 15:59:12 +0900175 u64 attributes)
Rob Clark2a920802017-09-13 18:05:34 -0400176{
177 struct file_handle *fh;
178 char f0[MAX_UTF8_PER_UTF16] = {0};
179 int plen = 0;
180 int flen = 0;
181
182 if (file_name) {
Heinrich Schuchardtc82f8f62019-01-12 12:02:33 +0100183 utf16_to_utf8((u8 *)f0, file_name, 1);
184 flen = u16_strlen(file_name);
Rob Clark2a920802017-09-13 18:05:34 -0400185 }
186
187 /* we could have a parent, but also an absolute path: */
188 if (f0[0] == '\\') {
189 plen = 0;
190 } else if (parent) {
191 plen = strlen(parent->path) + 1;
192 }
193
194 /* +2 is for null and '/' */
195 fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
196
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200197 fh->open_mode = open_mode;
Rob Clark2a920802017-09-13 18:05:34 -0400198 fh->base = efi_file_handle_protocol;
199 fh->fs = fs;
200
201 if (parent) {
202 char *p = fh->path;
Heinrich Schuchardtcb0c2a72019-04-06 16:27:34 +0200203 int exists;
Rob Clark2a920802017-09-13 18:05:34 -0400204
205 if (plen > 0) {
206 strcpy(p, parent->path);
207 p += plen - 1;
208 *p++ = '/';
209 }
210
Heinrich Schuchardtc82f8f62019-01-12 12:02:33 +0100211 utf16_to_utf8((u8 *)p, file_name, flen);
Rob Clark2a920802017-09-13 18:05:34 -0400212
213 if (sanitize_path(fh->path))
214 goto error;
215
216 /* check if file exists: */
217 if (set_blk_dev(fh))
218 goto error;
219
Heinrich Schuchardtcb0c2a72019-04-06 16:27:34 +0200220 exists = fs_exists(fh->path);
Heinrich Schuchardt823c2332019-02-09 22:23:48 +0100221 /* fs_exists() calls fs_close(), so open file system again */
222 if (set_blk_dev(fh))
223 goto error;
224
Heinrich Schuchardtcb0c2a72019-04-06 16:27:34 +0200225 if (!exists) {
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200226 if (!(open_mode & EFI_FILE_MODE_CREATE) ||
Heinrich Schuchardtcb0c2a72019-04-06 16:27:34 +0200227 efi_create_file(fh, attributes))
228 goto error;
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200229 if (set_blk_dev(fh))
230 goto error;
Heinrich Schuchardtcb0c2a72019-04-06 16:27:34 +0200231 }
232
Rob Clark2a920802017-09-13 18:05:34 -0400233 /* figure out if file is a directory: */
234 fh->isdir = is_dir(fh);
235 } else {
236 fh->isdir = 1;
237 strcpy(fh->path, "");
238 }
239
240 return &fh->base;
241
242error:
243 free(fh);
244 return NULL;
245}
246
247static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *file,
248 struct efi_file_handle **new_handle,
Heinrich Schuchardtc82f8f62019-01-12 12:02:33 +0100249 u16 *file_name, u64 open_mode, u64 attributes)
Rob Clark2a920802017-09-13 18:05:34 -0400250{
251 struct file_handle *fh = to_fh(file);
Heinrich Schuchardt143acd12018-09-12 18:43:58 +0200252 efi_status_t ret;
Rob Clark2a920802017-09-13 18:05:34 -0400253
Simon Glass0c89a312019-01-07 16:44:18 -0700254 EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", file, new_handle,
Heinrich Schuchardt1646e092019-03-19 19:16:23 +0100255 file_name, open_mode, attributes);
Rob Clark2a920802017-09-13 18:05:34 -0400256
Heinrich Schuchardt143acd12018-09-12 18:43:58 +0200257 /* Check parameters */
Heinrich Schuchardtd3dce352018-09-15 20:43:46 +0200258 if (!file || !new_handle || !file_name) {
Heinrich Schuchardt143acd12018-09-12 18:43:58 +0200259 ret = EFI_INVALID_PARAMETER;
260 goto out;
261 }
262 if (open_mode != EFI_FILE_MODE_READ &&
263 open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
264 open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
265 EFI_FILE_MODE_CREATE)) {
266 ret = EFI_INVALID_PARAMETER;
267 goto out;
268 }
Heinrich Schuchardtbd665882018-09-13 21:31:49 +0200269 /*
270 * The UEFI spec requires that attributes are only set in create mode.
271 * The SCT does not care about this and sets EFI_FILE_DIRECTORY in
272 * read mode. EDK2 does not check that attributes are zero if not in
273 * create mode.
274 *
275 * So here we only check attributes in create mode and do not check
276 * that they are zero otherwise.
277 */
278 if ((open_mode & EFI_FILE_MODE_CREATE) &&
Heinrich Schuchardt143acd12018-09-12 18:43:58 +0200279 (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
280 ret = EFI_INVALID_PARAMETER;
281 goto out;
282 }
Rob Clark2a920802017-09-13 18:05:34 -0400283
Heinrich Schuchardt143acd12018-09-12 18:43:58 +0200284 /* Open file */
285 *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
Heinrich Schuchardt19b2d892019-04-06 16:36:16 +0200286 if (*new_handle) {
287 EFI_PRINT("file handle %p\n", *new_handle);
Heinrich Schuchardt143acd12018-09-12 18:43:58 +0200288 ret = EFI_SUCCESS;
Heinrich Schuchardt19b2d892019-04-06 16:36:16 +0200289 } else {
Heinrich Schuchardt143acd12018-09-12 18:43:58 +0200290 ret = EFI_NOT_FOUND;
Heinrich Schuchardt19b2d892019-04-06 16:36:16 +0200291 }
Heinrich Schuchardt143acd12018-09-12 18:43:58 +0200292out:
293 return EFI_EXIT(ret);
Rob Clark2a920802017-09-13 18:05:34 -0400294}
295
296static efi_status_t file_close(struct file_handle *fh)
297{
298 fs_closedir(fh->dirs);
299 free(fh);
300 return EFI_SUCCESS;
301}
302
303static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
304{
305 struct file_handle *fh = to_fh(file);
306 EFI_ENTRY("%p", file);
307 return EFI_EXIT(file_close(fh));
308}
309
310static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
311{
312 struct file_handle *fh = to_fh(file);
AKASHI Takahirod39f6a62018-09-11 15:59:16 +0900313 efi_status_t ret = EFI_SUCCESS;
314
Rob Clark2a920802017-09-13 18:05:34 -0400315 EFI_ENTRY("%p", file);
AKASHI Takahirod39f6a62018-09-11 15:59:16 +0900316
Heinrich Schuchardtfa390812019-06-17 22:00:13 +0200317 if (set_blk_dev(fh) || fs_unlink(fh->path))
318 ret = EFI_WARN_DELETE_FAILURE;
AKASHI Takahirod39f6a62018-09-11 15:59:16 +0900319
Rob Clark2a920802017-09-13 18:05:34 -0400320 file_close(fh);
AKASHI Takahirod39f6a62018-09-11 15:59:16 +0900321 return EFI_EXIT(ret);
Rob Clark2a920802017-09-13 18:05:34 -0400322}
323
Heinrich Schuchardt9bb62fa2019-09-07 23:28:04 +0200324/**
325 * efi_get_file_size() - determine the size of a file
326 *
327 * @fh: file handle
328 * @file_size: pointer to receive file size
329 * Return: status code
330 */
331static efi_status_t efi_get_file_size(struct file_handle *fh,
332 loff_t *file_size)
333{
334 if (set_blk_dev(fh))
335 return EFI_DEVICE_ERROR;
336
337 if (fs_size(fh->path, file_size))
338 return EFI_DEVICE_ERROR;
339
340 return EFI_SUCCESS;
341}
342
Rob Clark2a920802017-09-13 18:05:34 -0400343static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
344 void *buffer)
345{
346 loff_t actread;
Heinrich Schuchardt9bb62fa2019-09-07 23:28:04 +0200347 efi_status_t ret;
348 loff_t file_size;
Rob Clark2a920802017-09-13 18:05:34 -0400349
Heinrich Schuchardt9bb62fa2019-09-07 23:28:04 +0200350 ret = efi_get_file_size(fh, &file_size);
351 if (ret != EFI_SUCCESS)
352 return ret;
353 if (file_size < fh->offset) {
354 ret = EFI_DEVICE_ERROR;
355 return ret;
356 }
357
358 if (set_blk_dev(fh))
359 return EFI_DEVICE_ERROR;
Simon Glass2ae843f2018-09-15 00:51:00 -0600360 if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
Rob Clark2a920802017-09-13 18:05:34 -0400361 *buffer_size, &actread))
362 return EFI_DEVICE_ERROR;
363
364 *buffer_size = actread;
365 fh->offset += actread;
366
367 return EFI_SUCCESS;
368}
369
370static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
371 void *buffer)
372{
373 struct efi_file_info *info = buffer;
374 struct fs_dirent *dent;
Heinrich Schuchardt83a74ad2019-09-07 22:34:07 +0200375 u64 required_size;
Heinrich Schuchardt87c48402019-09-07 21:05:45 +0200376 u16 *dst;
Rob Clark2a920802017-09-13 18:05:34 -0400377
Heinrich Schuchardt9bb62fa2019-09-07 23:28:04 +0200378 if (set_blk_dev(fh))
379 return EFI_DEVICE_ERROR;
380
Rob Clark2a920802017-09-13 18:05:34 -0400381 if (!fh->dirs) {
382 assert(fh->offset == 0);
383 fh->dirs = fs_opendir(fh->path);
384 if (!fh->dirs)
385 return EFI_DEVICE_ERROR;
Heinrich Schuchardt83a74ad2019-09-07 22:34:07 +0200386 fh->dent = NULL;
Rob Clark2a920802017-09-13 18:05:34 -0400387 }
388
389 /*
390 * So this is a bit awkward. Since fs layer is stateful and we
391 * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
392 * we might have to return without consuming the dent.. so we
393 * have to stash it for next call.
394 */
395 if (fh->dent) {
396 dent = fh->dent;
Rob Clark2a920802017-09-13 18:05:34 -0400397 } else {
398 dent = fs_readdir(fh->dirs);
399 }
400
Rob Clark2a920802017-09-13 18:05:34 -0400401 if (!dent) {
Heinrich Schuchardt83a74ad2019-09-07 22:34:07 +0200402 /* no more files in directory */
403 *buffer_size = 0;
Rob Clark2a920802017-09-13 18:05:34 -0400404 return EFI_SUCCESS;
405 }
406
407 /* check buffer size: */
Heinrich Schuchardt87c48402019-09-07 21:05:45 +0200408 required_size = sizeof(*info) +
409 2 * (utf8_utf16_strlen(dent->name) + 1);
Rob Clark2a920802017-09-13 18:05:34 -0400410 if (*buffer_size < required_size) {
411 *buffer_size = required_size;
412 fh->dent = dent;
413 return EFI_BUFFER_TOO_SMALL;
414 }
Heinrich Schuchardt83a74ad2019-09-07 22:34:07 +0200415 fh->dent = NULL;
Rob Clark2a920802017-09-13 18:05:34 -0400416
417 *buffer_size = required_size;
418 memset(info, 0, required_size);
419
420 info->size = required_size;
421 info->file_size = dent->size;
422 info->physical_size = dent->size;
423
424 if (dent->type == FS_DT_DIR)
425 info->attribute |= EFI_FILE_DIRECTORY;
426
Heinrich Schuchardt87c48402019-09-07 21:05:45 +0200427 dst = info->file_name;
428 utf8_utf16_strcpy(&dst, dent->name);
Rob Clark2a920802017-09-13 18:05:34 -0400429
430 fh->offset++;
431
432 return EFI_SUCCESS;
433}
434
435static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *file,
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200436 efi_uintn_t *buffer_size, void *buffer)
Rob Clark2a920802017-09-13 18:05:34 -0400437{
438 struct file_handle *fh = to_fh(file);
439 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200440 u64 bs;
Rob Clark2a920802017-09-13 18:05:34 -0400441
442 EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
443
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200444 if (!buffer_size || !buffer) {
445 ret = EFI_INVALID_PARAMETER;
446 goto error;
447 }
448
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200449 bs = *buffer_size;
Rob Clark2a920802017-09-13 18:05:34 -0400450 if (fh->isdir)
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200451 ret = dir_read(fh, &bs, buffer);
Rob Clark2a920802017-09-13 18:05:34 -0400452 else
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200453 ret = file_read(fh, &bs, buffer);
454 if (bs <= SIZE_MAX)
455 *buffer_size = bs;
456 else
457 *buffer_size = SIZE_MAX;
Rob Clark2a920802017-09-13 18:05:34 -0400458
459error:
460 return EFI_EXIT(ret);
461}
462
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200463/**
464 * efi_file_write() - write to file
465 *
466 * This function implements the Write() service of the EFI_FILE_PROTOCOL.
467 *
468 * See the Unified Extensible Firmware Interface (UEFI) specification for
469 * details.
470 *
471 * @file: file handle
472 * @buffer_size: number of bytes to write
473 * @buffer: buffer with the bytes to write
474 * Return: status code
475 */
Rob Clark2a920802017-09-13 18:05:34 -0400476static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *file,
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200477 efi_uintn_t *buffer_size,
478 void *buffer)
Rob Clark2a920802017-09-13 18:05:34 -0400479{
480 struct file_handle *fh = to_fh(file);
481 efi_status_t ret = EFI_SUCCESS;
482 loff_t actwrite;
483
484 EFI_ENTRY("%p, %p, %p", file, buffer_size, buffer);
485
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200486 if (!file || !buffer_size || !buffer) {
487 ret = EFI_INVALID_PARAMETER;
488 goto out;
489 }
490 if (fh->isdir) {
491 ret = EFI_UNSUPPORTED;
492 goto out;
493 }
494 if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) {
495 ret = EFI_ACCESS_DENIED;
496 goto out;
Rob Clark2a920802017-09-13 18:05:34 -0400497 }
498
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200499 if (!*buffer_size)
500 goto out;
501
502 if (set_blk_dev(fh)) {
503 ret = EFI_DEVICE_ERROR;
504 goto out;
505 }
Simon Glass2ae843f2018-09-15 00:51:00 -0600506 if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
Rob Clark2a920802017-09-13 18:05:34 -0400507 &actwrite)) {
508 ret = EFI_DEVICE_ERROR;
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200509 goto out;
Rob Clark2a920802017-09-13 18:05:34 -0400510 }
Rob Clark2a920802017-09-13 18:05:34 -0400511 *buffer_size = actwrite;
512 fh->offset += actwrite;
513
Heinrich Schuchardtb0f1c722019-09-06 21:37:21 +0200514out:
Rob Clark2a920802017-09-13 18:05:34 -0400515 return EFI_EXIT(ret);
516}
517
Heinrich Schuchardt0801d4d2018-10-07 05:26:26 +0200518/**
519 * efi_file_getpos() - get current position in file
520 *
521 * This function implements the GetPosition service of the EFI file protocol.
522 * See the UEFI spec for details.
523 *
524 * @file: file handle
525 * @pos: pointer to file position
526 * Return: status code
527 */
Rob Clark2a920802017-09-13 18:05:34 -0400528static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
Heinrich Schuchardt0801d4d2018-10-07 05:26:26 +0200529 u64 *pos)
Rob Clark2a920802017-09-13 18:05:34 -0400530{
Heinrich Schuchardt0801d4d2018-10-07 05:26:26 +0200531 efi_status_t ret = EFI_SUCCESS;
Rob Clark2a920802017-09-13 18:05:34 -0400532 struct file_handle *fh = to_fh(file);
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200533
Rob Clark2a920802017-09-13 18:05:34 -0400534 EFI_ENTRY("%p, %p", file, pos);
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200535
Heinrich Schuchardt0801d4d2018-10-07 05:26:26 +0200536 if (fh->isdir) {
537 ret = EFI_UNSUPPORTED;
538 goto out;
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200539 }
Heinrich Schuchardt0801d4d2018-10-07 05:26:26 +0200540
541 *pos = fh->offset;
542out:
543 return EFI_EXIT(ret);
Rob Clark2a920802017-09-13 18:05:34 -0400544}
545
Heinrich Schuchardt0801d4d2018-10-07 05:26:26 +0200546/**
547 * efi_file_setpos() - set current position in file
548 *
549 * This function implements the SetPosition service of the EFI file protocol.
550 * See the UEFI spec for details.
551 *
552 * @file: file handle
553 * @pos: new file position
554 * Return: status code
555 */
Rob Clark2a920802017-09-13 18:05:34 -0400556static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
Heinrich Schuchardt0801d4d2018-10-07 05:26:26 +0200557 u64 pos)
Rob Clark2a920802017-09-13 18:05:34 -0400558{
559 struct file_handle *fh = to_fh(file);
560 efi_status_t ret = EFI_SUCCESS;
561
Heinrich Schuchardt0801d4d2018-10-07 05:26:26 +0200562 EFI_ENTRY("%p, %llu", file, pos);
Rob Clark2a920802017-09-13 18:05:34 -0400563
564 if (fh->isdir) {
565 if (pos != 0) {
566 ret = EFI_UNSUPPORTED;
567 goto error;
568 }
569 fs_closedir(fh->dirs);
570 fh->dirs = NULL;
571 }
572
573 if (pos == ~0ULL) {
574 loff_t file_size;
575
Heinrich Schuchardt9bb62fa2019-09-07 23:28:04 +0200576 ret = efi_get_file_size(fh, &file_size);
577 if (ret != EFI_SUCCESS)
Rob Clark2a920802017-09-13 18:05:34 -0400578 goto error;
Rob Clark2a920802017-09-13 18:05:34 -0400579 pos = file_size;
580 }
581
582 fh->offset = pos;
583
584error:
585 return EFI_EXIT(ret);
586}
587
588static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
Heinrich Schuchardt9c9021e2018-04-04 15:42:09 +0200589 const efi_guid_t *info_type,
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200590 efi_uintn_t *buffer_size,
591 void *buffer)
Rob Clark2a920802017-09-13 18:05:34 -0400592{
593 struct file_handle *fh = to_fh(file);
594 efi_status_t ret = EFI_SUCCESS;
Heinrich Schuchardt87c48402019-09-07 21:05:45 +0200595 u16 *dst;
Rob Clark2a920802017-09-13 18:05:34 -0400596
Heinrich Schuchardt008fb482018-09-14 22:46:34 +0200597 EFI_ENTRY("%p, %pUl, %p, %p", file, info_type, buffer_size, buffer);
Rob Clark2a920802017-09-13 18:05:34 -0400598
Heinrich Schuchardt11335c02019-09-08 10:45:31 +0200599 if (!file || !info_type || !buffer_size ||
600 (*buffer_size && !buffer)) {
601 ret = EFI_INVALID_PARAMETER;
602 goto error;
603 }
604
Rob Clark2a920802017-09-13 18:05:34 -0400605 if (!guidcmp(info_type, &efi_file_info_guid)) {
606 struct efi_file_info *info = buffer;
607 char *filename = basename(fh);
608 unsigned int required_size;
609 loff_t file_size;
610
611 /* check buffer size: */
Heinrich Schuchardt87c48402019-09-07 21:05:45 +0200612 required_size = sizeof(*info) +
613 2 * (utf8_utf16_strlen(filename) + 1);
Rob Clark2a920802017-09-13 18:05:34 -0400614 if (*buffer_size < required_size) {
615 *buffer_size = required_size;
616 ret = EFI_BUFFER_TOO_SMALL;
617 goto error;
618 }
619
Heinrich Schuchardt9bb62fa2019-09-07 23:28:04 +0200620 ret = efi_get_file_size(fh, &file_size);
621 if (ret != EFI_SUCCESS)
Rob Clark2a920802017-09-13 18:05:34 -0400622 goto error;
Rob Clark2a920802017-09-13 18:05:34 -0400623
624 memset(info, 0, required_size);
625
626 info->size = required_size;
627 info->file_size = file_size;
628 info->physical_size = file_size;
629
630 if (fh->isdir)
631 info->attribute |= EFI_FILE_DIRECTORY;
632
Heinrich Schuchardt87c48402019-09-07 21:05:45 +0200633 dst = info->file_name;
634 utf8_utf16_strcpy(&dst, filename);
Heinrich Schuchardt9e6835e2018-04-04 15:42:11 +0200635 } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
636 struct efi_file_system_info *info = buffer;
637 disk_partition_t part;
638 efi_uintn_t required_size;
639 int r;
640
641 if (fh->fs->part >= 1)
642 r = part_get_info(fh->fs->desc, fh->fs->part, &part);
643 else
644 r = part_get_info_whole_disk(fh->fs->desc, &part);
645 if (r < 0) {
646 ret = EFI_DEVICE_ERROR;
647 goto error;
648 }
Heinrich Schuchardt632834c2019-09-08 10:32:54 +0200649 required_size = sizeof(*info) + 2;
Heinrich Schuchardt9e6835e2018-04-04 15:42:11 +0200650 if (*buffer_size < required_size) {
651 *buffer_size = required_size;
652 ret = EFI_BUFFER_TOO_SMALL;
653 goto error;
654 }
655
656 memset(info, 0, required_size);
657
658 info->size = required_size;
Heinrich Schuchardt57014722019-12-08 10:02:37 +0100659 /*
660 * TODO: We cannot determine if the volume can be written to.
661 */
662 info->read_only = false;
Heinrich Schuchardt9e6835e2018-04-04 15:42:11 +0200663 info->volume_size = part.size * part.blksz;
Heinrich Schuchardt57014722019-12-08 10:02:37 +0100664 /*
665 * TODO: We currently have no function to determine the free
666 * space. The volume size is the best upper bound we have.
667 */
668 info->free_space = info->volume_size;
Heinrich Schuchardt9e6835e2018-04-04 15:42:11 +0200669 info->block_size = part.blksz;
670 /*
671 * TODO: The volume label is not available in U-Boot.
Heinrich Schuchardt9e6835e2018-04-04 15:42:11 +0200672 */
Heinrich Schuchardt632834c2019-09-08 10:32:54 +0200673 info->volume_label[0] = 0;
674 } else if (!guidcmp(info_type, &efi_system_volume_label_id)) {
675 if (*buffer_size < 2) {
676 *buffer_size = 2;
677 ret = EFI_BUFFER_TOO_SMALL;
678 goto error;
679 }
680 *(u16 *)buffer = 0;
Rob Clark2a920802017-09-13 18:05:34 -0400681 } else {
682 ret = EFI_UNSUPPORTED;
683 }
684
685error:
686 return EFI_EXIT(ret);
687}
688
689static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
Heinrich Schuchardt9c9021e2018-04-04 15:42:09 +0200690 const efi_guid_t *info_type,
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200691 efi_uintn_t buffer_size,
692 void *buffer)
Rob Clark2a920802017-09-13 18:05:34 -0400693{
Heinrich Schuchardtfbe4c7d2019-04-06 18:17:39 +0200694 struct file_handle *fh = to_fh(file);
695 efi_status_t ret = EFI_UNSUPPORTED;
Heinrich Schuchardtb6dd5772018-04-03 22:37:11 +0200696
Heinrich Schuchardtfbe4c7d2019-04-06 18:17:39 +0200697 EFI_ENTRY("%p, %pUl, %zu, %p", file, info_type, buffer_size, buffer);
698
699 if (!guidcmp(info_type, &efi_file_info_guid)) {
700 struct efi_file_info *info = (struct efi_file_info *)buffer;
701 char *filename = basename(fh);
702 char *new_file_name, *pos;
703 loff_t file_size;
704
Heinrich Schuchardt82625782019-09-08 11:37:07 +0200705 /* The buffer will always contain a file name. */
706 if (buffer_size < sizeof(struct efi_file_info) + 2 ||
707 buffer_size < info->size) {
Heinrich Schuchardtfbe4c7d2019-04-06 18:17:39 +0200708 ret = EFI_BAD_BUFFER_SIZE;
709 goto out;
710 }
711 /* We cannot change the directory attribute */
712 if (!fh->isdir != !(info->attribute & EFI_FILE_DIRECTORY)) {
713 ret = EFI_ACCESS_DENIED;
714 goto out;
715 }
716 /* Check for renaming */
717 new_file_name = malloc(utf16_utf8_strlen(info->file_name));
718 if (!new_file_name) {
719 ret = EFI_OUT_OF_RESOURCES;
720 goto out;
721 }
722 pos = new_file_name;
723 utf16_utf8_strcpy(&pos, info->file_name);
724 if (strcmp(new_file_name, filename)) {
725 /* TODO: we do not support renaming */
726 EFI_PRINT("Renaming not supported\n");
727 free(new_file_name);
728 ret = EFI_ACCESS_DENIED;
729 goto out;
730 }
731 free(new_file_name);
732 /* Check for truncation */
Heinrich Schuchardt9bb62fa2019-09-07 23:28:04 +0200733 ret = efi_get_file_size(fh, &file_size);
734 if (ret != EFI_SUCCESS)
Heinrich Schuchardtfbe4c7d2019-04-06 18:17:39 +0200735 goto out;
Heinrich Schuchardtfbe4c7d2019-04-06 18:17:39 +0200736 if (file_size != info->file_size) {
737 /* TODO: we do not support truncation */
738 EFI_PRINT("Truncation not supported\n");
739 ret = EFI_ACCESS_DENIED;
740 goto out;
741 }
742 /*
743 * We do not care for the other attributes
744 * TODO: Support read only
745 */
746 ret = EFI_SUCCESS;
Heinrich Schuchardtfbe4c7d2019-04-06 18:17:39 +0200747 } else {
Heinrich Schuchardt82625782019-09-08 11:37:07 +0200748 /* TODO: We do not support changing the volume label */
Heinrich Schuchardtfbe4c7d2019-04-06 18:17:39 +0200749 ret = EFI_UNSUPPORTED;
750 }
751out:
752 return EFI_EXIT(ret);
Rob Clark2a920802017-09-13 18:05:34 -0400753}
754
755static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *file)
756{
757 EFI_ENTRY("%p", file);
758 return EFI_EXIT(EFI_SUCCESS);
759}
760
Heinrich Schuchardte692ed12019-09-08 09:35:32 +0200761static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *file,
762 struct efi_file_handle **new_handle,
763 u16 *file_name, u64 open_mode, u64 attributes,
764 struct efi_file_io_token *token)
765{
766 return EFI_UNSUPPORTED;
767}
768
769static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *file,
770 struct efi_file_io_token *token)
771{
772 return EFI_UNSUPPORTED;
773}
774
775static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *file,
776 struct efi_file_io_token *token)
777{
778 return EFI_UNSUPPORTED;
779}
780
781static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *file,
782 struct efi_file_io_token *token)
783{
784 return EFI_UNSUPPORTED;
785}
786
Rob Clark2a920802017-09-13 18:05:34 -0400787static const struct efi_file_handle efi_file_handle_protocol = {
Heinrich Schuchardte692ed12019-09-08 09:35:32 +0200788 .rev = EFI_FILE_PROTOCOL_REVISION2,
Rob Clark2a920802017-09-13 18:05:34 -0400789 .open = efi_file_open,
790 .close = efi_file_close,
791 .delete = efi_file_delete,
792 .read = efi_file_read,
793 .write = efi_file_write,
794 .getpos = efi_file_getpos,
795 .setpos = efi_file_setpos,
796 .getinfo = efi_file_getinfo,
797 .setinfo = efi_file_setinfo,
798 .flush = efi_file_flush,
Heinrich Schuchardte692ed12019-09-08 09:35:32 +0200799 .open_ex = efi_file_open_ex,
800 .read_ex = efi_file_read_ex,
801 .write_ex = efi_file_write_ex,
802 .flush_ex = efi_file_flush_ex,
Rob Clark2a920802017-09-13 18:05:34 -0400803};
804
Heinrich Schuchardt95288b12019-02-04 21:24:35 +0100805/**
806 * efi_file_from_path() - open file via device path
807 *
808 * @fp: device path
809 * @return: EFI_FILE_PROTOCOL for the file or NULL
810 */
Rob Clark2a920802017-09-13 18:05:34 -0400811struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
812{
813 struct efi_simple_file_system_protocol *v;
814 struct efi_file_handle *f;
815 efi_status_t ret;
816
817 v = efi_fs_from_path(fp);
818 if (!v)
819 return NULL;
820
821 EFI_CALL(ret = v->open_volume(v, &f));
822 if (ret != EFI_SUCCESS)
823 return NULL;
824
Heinrich Schuchardt95288b12019-02-04 21:24:35 +0100825 /* Skip over device-path nodes before the file path. */
Rob Clark2a920802017-09-13 18:05:34 -0400826 while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
827 fp = efi_dp_next(fp);
828
Heinrich Schuchardt95288b12019-02-04 21:24:35 +0100829 /*
830 * Step through the nodes of the directory path until the actual file
831 * node is reached which is the final node in the device path.
832 */
Rob Clark2a920802017-09-13 18:05:34 -0400833 while (fp) {
834 struct efi_device_path_file_path *fdp =
835 container_of(fp, struct efi_device_path_file_path, dp);
836 struct efi_file_handle *f2;
Heinrich Schuchardtf62be162019-07-14 20:14:46 +0200837 u16 *filename;
Rob Clark2a920802017-09-13 18:05:34 -0400838
839 if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
840 printf("bad file path!\n");
841 f->close(f);
842 return NULL;
843 }
844
Heinrich Schuchardtf62be162019-07-14 20:14:46 +0200845 filename = u16_strdup(fdp->str);
846 if (!filename)
847 return NULL;
848 EFI_CALL(ret = f->open(f, &f2, filename,
Rob Clark2a920802017-09-13 18:05:34 -0400849 EFI_FILE_MODE_READ, 0));
Heinrich Schuchardtf62be162019-07-14 20:14:46 +0200850 free(filename);
Rob Clark2a920802017-09-13 18:05:34 -0400851 if (ret != EFI_SUCCESS)
852 return NULL;
853
854 fp = efi_dp_next(fp);
855
856 EFI_CALL(f->close(f));
857 f = f2;
858 }
859
860 return f;
861}
862
863static efi_status_t EFIAPI
864efi_open_volume(struct efi_simple_file_system_protocol *this,
865 struct efi_file_handle **root)
866{
867 struct file_system *fs = to_fs(this);
868
869 EFI_ENTRY("%p, %p", this, root);
870
AKASHI Takahiro5bc84a12018-09-11 15:59:12 +0900871 *root = file_open(fs, NULL, NULL, 0, 0);
Rob Clark2a920802017-09-13 18:05:34 -0400872
873 return EFI_EXIT(EFI_SUCCESS);
874}
875
876struct efi_simple_file_system_protocol *
877efi_simple_file_system(struct blk_desc *desc, int part,
878 struct efi_device_path *dp)
879{
880 struct file_system *fs;
881
882 fs = calloc(1, sizeof(*fs));
883 fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
884 fs->base.open_volume = efi_open_volume;
885 fs->desc = desc;
886 fs->part = part;
887 fs->dp = dp;
888
889 return &fs->base;
890}