VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.1 KB
Line 
1/* $Id: fsw_efi.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * fsw_efi.c - EFI host environment code.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 * ---------------------------------------------------------------------------
36 * This code is based on:
37 *
38 * Copyright (c) 2006 Christoph Pfisterer
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions are
42 * met:
43 *
44 * * Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 *
47 * * Redistributions in binary form must reproduce the above copyright
48 * notice, this list of conditions and the following disclaimer in the
49 * documentation and/or other materials provided with the
50 * distribution.
51 *
52 * * Neither the name of Christoph Pfisterer nor the names of the
53 * contributors may be used to endorse or promote products derived
54 * from this software without specific prior written permission.
55 *
56 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
57 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
58 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
59 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
60 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
61 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
62 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
63 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
64 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
65 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
66 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67 */
68
69#include "fsw_efi.h"
70
71#define DEBUG_LEVEL 0
72
73#ifndef FSTYPE
74#ifdef VBOX
75#error FSTYPE must be defined!
76#else
77#define FSTYPE ext2
78#endif
79#endif
80
81/** Helper macro for stringification. */
82#define FSW_EFI_STRINGIFY(x) L#x
83/** Expands to the EFI driver name given the file system type name. */
84#define FSW_EFI_DRIVER_NAME(t) L"Fsw " FSW_EFI_STRINGIFY(t) L" File System Driver"
85
86
87// function prototypes
88
89EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This,
90 IN EFI_HANDLE ControllerHandle,
91 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath);
92EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This,
93 IN EFI_HANDLE ControllerHandle,
94 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath);
95EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This,
96 IN EFI_HANDLE ControllerHandle,
97 IN UINTN NumberOfChildren,
98 IN EFI_HANDLE *ChildHandleBuffer);
99
100EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
101 IN CHAR8 *Language,
102 OUT CHAR16 **DriverName);
103EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
104 IN EFI_HANDLE ControllerHandle,
105 IN EFI_HANDLE ChildHandle OPTIONAL,
106 IN CHAR8 *Language,
107 OUT CHAR16 **ControllerName);
108
109void fsw_efi_change_blocksize(struct fsw_volume *vol,
110 fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
111 fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize);
112fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer);
113
114EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume);
115
116EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
117 OUT EFI_FILE **Root);
118EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
119 OUT EFI_FILE **NewFileHandle);
120
121EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
122 IN OUT UINTN *BufferSize,
123 OUT VOID *Buffer);
124EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
125 OUT UINT64 *Position);
126EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File,
127 IN UINT64 Position);
128
129EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
130 OUT EFI_FILE **NewHandle,
131 IN CHAR16 *FileName,
132 IN UINT64 OpenMode,
133 IN UINT64 Attributes);
134EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
135 IN OUT UINTN *BufferSize,
136 OUT VOID *Buffer);
137EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File,
138 IN UINT64 Position);
139
140EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
141 IN EFI_GUID *InformationType,
142 IN OUT UINTN *BufferSize,
143 OUT VOID *Buffer);
144EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
145 IN struct fsw_dnode *dno,
146 IN OUT UINTN *BufferSize,
147 OUT VOID *Buffer);
148
149#if defined(VBOX) && (FSTYPE == hfs)
150extern fsw_status_t fsw_hfs_get_blessed_file(void *vol, struct fsw_string *path);
151#endif
152
153/**
154 * Interface structure for the EFI Driver Binding protocol.
155 */
156
157EFI_DRIVER_BINDING_PROTOCOL fsw_efi_DriverBinding_table = {
158 fsw_efi_DriverBinding_Supported,
159 fsw_efi_DriverBinding_Start,
160 fsw_efi_DriverBinding_Stop,
161 0x10,
162 NULL,
163 NULL
164};
165
166/**
167 * Interface structure for the EFI Component Name protocol.
168 */
169
170EFI_COMPONENT_NAME_PROTOCOL fsw_efi_ComponentName_table = {
171 fsw_efi_ComponentName_GetDriverName,
172 fsw_efi_ComponentName_GetControllerName,
173 "eng"
174};
175
176/**
177 * Dispatch table for our FSW host driver.
178 */
179
180struct fsw_host_table fsw_efi_host_table = {
181 FSW_STRING_TYPE_UTF16,
182
183 fsw_efi_change_blocksize,
184 fsw_efi_read_block
185};
186
187extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE);
188
189
190
191/**
192 * Image entry point. Installs the Driver Binding and Component Name protocols
193 * on the image's handle. Actually mounting a file system is initiated through
194 * the Driver Binding protocol at the firmware's request.
195 */
196EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE ImageHandle,
197 IN EFI_SYSTEM_TABLE *SystemTable)
198{
199 EFI_STATUS Status;
200
201#ifndef VBOX
202 InitializeLib(ImageHandle, SystemTable);
203#endif
204
205 // complete Driver Binding protocol instance
206 fsw_efi_DriverBinding_table.ImageHandle = ImageHandle;
207 fsw_efi_DriverBinding_table.DriverBindingHandle = ImageHandle;
208 // install Driver Binding protocol
209 Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle,
210 &PROTO_NAME(DriverBindingProtocol),
211 EFI_NATIVE_INTERFACE,
212 &fsw_efi_DriverBinding_table);
213 if (EFI_ERROR (Status)) {
214 return Status;
215 }
216
217 // install Component Name protocol
218 Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle,
219 &PROTO_NAME(ComponentNameProtocol),
220 EFI_NATIVE_INTERFACE,
221 &fsw_efi_ComponentName_table);
222 if (EFI_ERROR (Status)) {
223 return Status;
224 }
225
226 return EFI_SUCCESS;
227}
228
229/**
230 * Driver Binding EFI protocol, Supported function. This function is called by EFI
231 * to test if this driver can handle a certain device. Our implementation only checks
232 * if the device is a disk (i.e. that it supports the Block I/O and Disk I/O protocols)
233 * and implicitly checks if the disk is already in use by another driver.
234 */
235
236EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This,
237 IN EFI_HANDLE ControllerHandle,
238 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
239{
240 EFI_STATUS Status;
241 EFI_DISK_IO *DiskIo;
242
243 // we check for both DiskIO and BlockIO protocols
244
245 // first, open DiskIO
246 Status = BS->OpenProtocol(ControllerHandle,
247 &PROTO_NAME(DiskIoProtocol),
248 (VOID **) &DiskIo,
249 This->DriverBindingHandle,
250 ControllerHandle,
251 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
252 if (EFI_ERROR(Status))
253 {
254 return Status;
255 }
256
257 // we were just checking, close it again
258 BS->CloseProtocol(ControllerHandle,
259 &PROTO_NAME(DiskIoProtocol),
260 This->DriverBindingHandle,
261 ControllerHandle);
262
263 // next, check BlockIO without actually opening it
264 Status = BS->OpenProtocol(ControllerHandle,
265 &PROTO_NAME(BlockIoProtocol),
266 NULL,
267 This->DriverBindingHandle,
268 ControllerHandle,
269 EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
270 return Status;
271}
272
273static EFI_STATUS fsw_efi_ReMount(IN FSW_VOLUME_DATA *pVolume,
274 IN EFI_HANDLE ControllerHandle,
275 EFI_DISK_IO *pDiskIo,
276 EFI_BLOCK_IO *pBlockIo)
277{
278 EFI_STATUS Status;
279 pVolume->Signature = FSW_VOLUME_DATA_SIGNATURE;
280 pVolume->Handle = ControllerHandle;
281 pVolume->DiskIo = pDiskIo;
282 pVolume->MediaId = pBlockIo->Media->MediaId;
283 pVolume->LastIOStatus = EFI_SUCCESS;
284
285 // mount the filesystem
286 Status = fsw_efi_map_status(fsw_mount(pVolume, &fsw_efi_host_table,
287 &FSW_FSTYPE_TABLE_NAME(FSTYPE), &pVolume->vol),
288 pVolume);
289
290 if (!EFI_ERROR(Status)) {
291 // register the SimpleFileSystem protocol
292 pVolume->FileSystem.Revision = EFI_FILE_IO_INTERFACE_REVISION;
293 pVolume->FileSystem.OpenVolume = fsw_efi_FileSystem_OpenVolume;
294 Status = BS->InstallMultipleProtocolInterfaces(&ControllerHandle,
295 &PROTO_NAME(SimpleFileSystemProtocol), &pVolume->FileSystem,
296 NULL);
297#if DEBUG_LEVEL /* This error is always printed and destroys the boot logo. */
298 if (EFI_ERROR(Status))
299 Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status);
300#endif
301 }
302 return Status;
303}
304
305/**
306 * Driver Binding EFI protocol, Start function. This function is called by EFI
307 * to start driving the given device. It is still possible at this point to
308 * return EFI_UNSUPPORTED, and in fact we will do so if the file system driver
309 * cannot find the superblock signature (or equivalent) that it expects.
310 *
311 * This function allocates memory for a per-volume structure, opens the
312 * required protocols (just Disk I/O in our case, Block I/O is only looked
313 * at to get the MediaId field), and lets the FSW core mount the file system.
314 * If successful, an EFI Simple File System protocol is exported on the
315 * device handle.
316 */
317
318EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This,
319 IN EFI_HANDLE ControllerHandle,
320 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
321{
322 EFI_STATUS Status;
323 EFI_BLOCK_IO *BlockIo;
324 EFI_DISK_IO *DiskIo;
325 FSW_VOLUME_DATA *Volume;
326
327 // open consumed protocols
328 Status = BS->OpenProtocol(ControllerHandle,
329 &PROTO_NAME(BlockIoProtocol),
330 (VOID **) &BlockIo,
331 This->DriverBindingHandle,
332 ControllerHandle,
333 EFI_OPEN_PROTOCOL_GET_PROTOCOL); // NOTE: we only want to look at the MediaId
334 if (EFI_ERROR(Status)) {
335 return Status;
336 }
337
338 Status = BS->OpenProtocol(ControllerHandle,
339 &PROTO_NAME(DiskIoProtocol),
340 (VOID **) &DiskIo,
341 This->DriverBindingHandle,
342 ControllerHandle,
343 EFI_OPEN_PROTOCOL_BY_DRIVER);
344 if (EFI_ERROR(Status)) {
345 return Status;
346 }
347
348 // allocate volume structure
349 Volume = AllocateZeroPool(sizeof(FSW_VOLUME_DATA));
350 Status = fsw_efi_ReMount(Volume, ControllerHandle, DiskIo, BlockIo);
351
352 // on errors, close the opened protocols
353 if (EFI_ERROR(Status)) {
354 if (Volume->vol != NULL)
355 fsw_unmount(Volume->vol);
356 FreePool(Volume);
357
358#if 0
359 if (Status == EFI_MEDIA_CHANGED)
360 Status = fsw_efi_ReMount(Volume, ControllerHandle, DiskIo, BlockIo);
361 else
362#endif
363 BS->CloseProtocol(ControllerHandle,
364 &PROTO_NAME(DiskIoProtocol),
365 This->DriverBindingHandle,
366 ControllerHandle);
367 }
368
369 return Status;
370}
371
372/**
373 * Driver Binding EFI protocol, Stop function. This function is called by EFI
374 * to stop the driver on the given device. This translates to an unmount
375 * call for the FSW core.
376 *
377 * We assume that all file handles on the volume have been closed before
378 * the driver is stopped. At least with the EFI shell, that is actually the
379 * case; it closes all file handles between commands.
380 */
381
382EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This,
383 IN EFI_HANDLE ControllerHandle,
384 IN UINTN NumberOfChildren,
385 IN EFI_HANDLE *ChildHandleBuffer)
386{
387 EFI_STATUS Status;
388 EFI_FILE_IO_INTERFACE *FileSystem;
389 FSW_VOLUME_DATA *Volume;
390
391#if DEBUG_LEVEL
392 Print(L"fsw_efi_DriverBinding_Stop\n");
393#endif
394
395 // get the installed SimpleFileSystem interface
396 Status = BS->OpenProtocol(ControllerHandle,
397 &PROTO_NAME(SimpleFileSystemProtocol),
398 (VOID **) &FileSystem,
399 This->DriverBindingHandle,
400 ControllerHandle,
401 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
402 if (EFI_ERROR(Status))
403 return EFI_UNSUPPORTED;
404
405 // get private data structure
406 Volume = FSW_VOLUME_FROM_FILE_SYSTEM(FileSystem);
407
408 // uninstall Simple File System protocol
409 Status = BS->UninstallMultipleProtocolInterfaces(ControllerHandle,
410 &PROTO_NAME(SimpleFileSystemProtocol), &Volume->FileSystem,
411 NULL);
412 if (EFI_ERROR(Status)) {
413 Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status);
414 return Status;
415 }
416#if DEBUG_LEVEL
417 Print(L"fsw_efi_DriverBinding_Stop: protocol uninstalled successfully\n");
418#endif
419
420 // release private data structure
421 if (Volume->vol != NULL)
422 fsw_unmount(Volume->vol);
423 FreePool(Volume);
424
425 // close the consumed protocols
426 Status = BS->CloseProtocol(ControllerHandle,
427 &PROTO_NAME(DiskIoProtocol),
428 This->DriverBindingHandle,
429 ControllerHandle);
430
431 return Status;
432}
433
434/**
435 * Component Name EFI protocol, GetDriverName function. Used by the EFI
436 * environment to inquire the name of this driver. The name returned is
437 * based on the file system type actually used in compilation.
438 */
439
440EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
441 IN CHAR8 *Language,
442 OUT CHAR16 **DriverName)
443{
444 if (Language == NULL || DriverName == NULL)
445 return EFI_INVALID_PARAMETER;
446#if 0
447
448 if (Language[0] == 'e' && Language[1] == 'n' && Language[2] == 'g' && Language[3] == 0) {
449 *DriverName = FSW_EFI_DRIVER_NAME(FSTYPE);
450 return EFI_SUCCESS;
451 }
452#endif
453 return EFI_UNSUPPORTED;
454}
455
456/**
457 * Component Name EFI protocol, GetControllerName function. Not implemented
458 * because this is not a "bus" driver in the sense of the EFI Driver Model.
459 */
460
461EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
462 IN EFI_HANDLE ControllerHandle,
463 IN EFI_HANDLE ChildHandle OPTIONAL,
464 IN CHAR8 *Language,
465 OUT CHAR16 **ControllerName)
466{
467 return EFI_UNSUPPORTED;
468}
469
470/**
471 * FSW interface function for block size changes. This function is called by the FSW core
472 * when the file system driver changes the block sizes for the volume.
473 */
474
475void fsw_efi_change_blocksize(struct fsw_volume *vol,
476 fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
477 fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize)
478{
479 // nothing to do
480}
481
482/**
483 * FSW interface function to read data blocks. This function is called by the FSW core
484 * to read a block of data from the device. The buffer is allocated by the core code.
485 */
486
487fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer)
488{
489 EFI_STATUS Status;
490 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)vol->host_data;
491
492 FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_efi_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize));
493
494 // read from disk
495 Status = Volume->DiskIo->ReadDisk(Volume->DiskIo, Volume->MediaId,
496 (UINT64)phys_bno * vol->phys_blocksize,
497 vol->phys_blocksize,
498 buffer);
499 Volume->LastIOStatus = Status;
500 if (EFI_ERROR(Status))
501 return FSW_IO_ERROR;
502 return FSW_SUCCESS;
503}
504
505/**
506 * Map FSW status codes to EFI status codes. The FSW_IO_ERROR code is only produced
507 * by fsw_efi_read_block, so we map it back to the EFI status code remembered from
508 * the last I/O operation.
509 */
510
511EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume)
512{
513 switch (fsw_status) {
514 case FSW_SUCCESS:
515 return EFI_SUCCESS;
516 case FSW_OUT_OF_MEMORY:
517 return EFI_VOLUME_CORRUPTED;
518 case FSW_IO_ERROR:
519 return Volume->LastIOStatus;
520 case FSW_UNSUPPORTED:
521 return EFI_UNSUPPORTED;
522 case FSW_NOT_FOUND:
523 return EFI_NOT_FOUND;
524 case FSW_VOLUME_CORRUPTED:
525 return EFI_VOLUME_CORRUPTED;
526 default:
527 return EFI_DEVICE_ERROR;
528 }
529}
530
531/**
532 * File System EFI protocol, OpenVolume function. Creates a file handle for
533 * the root directory and returns it. Note that this function may be called
534 * multiple times and returns a new file handle each time. Each returned
535 * handle is closed by the client using it.
536 */
537
538EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
539 OUT EFI_FILE **Root)
540{
541 EFI_STATUS Status;
542 FSW_VOLUME_DATA *Volume = FSW_VOLUME_FROM_FILE_SYSTEM(This);
543
544#if DEBUG_LEVEL
545 Print(L"fsw_efi_FileSystem_OpenVolume\n");
546#endif
547
548 Status = fsw_efi_dnode_to_FileHandle(Volume->vol->root, Root);
549
550 return Status;
551}
552
553/**
554 * File Handle EFI protocol, Open function. Dispatches the call
555 * based on the kind of file handle.
556 */
557
558EFI_STATUS EFIAPI fsw_efi_FileHandle_Open(IN EFI_FILE *This,
559 OUT EFI_FILE **NewHandle,
560 IN CHAR16 *FileName,
561 IN UINT64 OpenMode,
562 IN UINT64 Attributes)
563{
564 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
565
566 if (File->Type == FSW_EFI_FILE_TYPE_DIR)
567 return fsw_efi_dir_open(File, NewHandle, FileName, OpenMode, Attributes);
568 // not supported for regular files
569 return EFI_UNSUPPORTED;
570}
571
572/**
573 * File Handle EFI protocol, Close function. Closes the FSW shandle
574 * and frees the memory used for the structure.
575 */
576
577EFI_STATUS EFIAPI fsw_efi_FileHandle_Close(IN EFI_FILE *This)
578{
579 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
580
581#if DEBUG_LEVEL
582 Print(L"fsw_efi_FileHandle_Close\n");
583#endif
584
585 fsw_shandle_close(&File->shand);
586 FreePool(File);
587
588 return EFI_SUCCESS;
589}
590
591/**
592 * File Handle EFI protocol, Delete function. Calls through to Close
593 * and returns a warning because this driver is read-only.
594 */
595
596EFI_STATUS EFIAPI fsw_efi_FileHandle_Delete(IN EFI_FILE *This)
597{
598 EFI_STATUS Status;
599
600 Status = This->Close(This);
601 if (Status == EFI_SUCCESS) {
602 // this driver is read-only
603 Status = EFI_WARN_DELETE_FAILURE;
604 }
605
606 return Status;
607}
608
609/**
610 * File Handle EFI protocol, Read function. Dispatches the call
611 * based on the kind of file handle.
612 */
613
614EFI_STATUS EFIAPI fsw_efi_FileHandle_Read(IN EFI_FILE *This,
615 IN OUT UINTN *BufferSize,
616 OUT VOID *Buffer)
617{
618 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
619
620 if (File->Type == FSW_EFI_FILE_TYPE_FILE)
621 return fsw_efi_file_read(File, BufferSize, Buffer);
622 else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
623 return fsw_efi_dir_read(File, BufferSize, Buffer);
624 return EFI_UNSUPPORTED;
625}
626
627/**
628 * File Handle EFI protocol, Write function. Returns unsupported status
629 * because this driver is read-only.
630 */
631
632EFI_STATUS EFIAPI fsw_efi_FileHandle_Write(IN EFI_FILE *This,
633 IN OUT UINTN *BufferSize,
634 IN VOID *Buffer)
635{
636 // this driver is read-only
637 return EFI_WRITE_PROTECTED;
638}
639
640/**
641 * File Handle EFI protocol, GetPosition function. Dispatches the call
642 * based on the kind of file handle.
643 */
644
645EFI_STATUS EFIAPI fsw_efi_FileHandle_GetPosition(IN EFI_FILE *This,
646 OUT UINT64 *Position)
647{
648 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
649
650 if (File->Type == FSW_EFI_FILE_TYPE_FILE)
651 return fsw_efi_file_getpos(File, Position);
652 // not defined for directories
653 return EFI_UNSUPPORTED;
654}
655
656/**
657 * File Handle EFI protocol, SetPosition function. Dispatches the call
658 * based on the kind of file handle.
659 */
660
661EFI_STATUS EFIAPI fsw_efi_FileHandle_SetPosition(IN EFI_FILE *This,
662 IN UINT64 Position)
663{
664 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
665
666 if (File->Type == FSW_EFI_FILE_TYPE_FILE)
667 return fsw_efi_file_setpos(File, Position);
668 else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
669 return fsw_efi_dir_setpos(File, Position);
670 return EFI_UNSUPPORTED;
671}
672
673/**
674 * File Handle EFI protocol, GetInfo function. Dispatches to the common
675 * function implementing this.
676 */
677
678EFI_STATUS EFIAPI fsw_efi_FileHandle_GetInfo(IN EFI_FILE *This,
679 IN EFI_GUID *InformationType,
680 IN OUT UINTN *BufferSize,
681 OUT VOID *Buffer)
682{
683 FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
684
685 return fsw_efi_dnode_getinfo(File, InformationType, BufferSize, Buffer);
686}
687
688/**
689 * File Handle EFI protocol, SetInfo function. Returns unsupported status
690 * because this driver is read-only.
691 */
692
693EFI_STATUS EFIAPI fsw_efi_FileHandle_SetInfo(IN EFI_FILE *This,
694 IN EFI_GUID *InformationType,
695 IN UINTN BufferSize,
696 IN VOID *Buffer)
697{
698 // this driver is read-only
699 return EFI_WRITE_PROTECTED;
700}
701
702/**
703 * File Handle EFI protocol, Flush function. Returns unsupported status
704 * because this driver is read-only.
705 */
706
707EFI_STATUS EFIAPI fsw_efi_FileHandle_Flush(IN EFI_FILE *This)
708{
709 // this driver is read-only
710 return EFI_WRITE_PROTECTED;
711}
712
713/**
714 * Set up a file handle for a dnode. This function allocates a data structure
715 * for a file handle, opens a FSW shandle and populates the EFI_FILE structure
716 * with the interface functions.
717 */
718
719EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
720 OUT EFI_FILE **NewFileHandle)
721{
722 EFI_STATUS Status;
723 FSW_FILE_DATA *File;
724
725 // make sure the dnode has complete info
726 Status = fsw_efi_map_status(fsw_dnode_fill(dno), (FSW_VOLUME_DATA *)dno->vol->host_data);
727 if (EFI_ERROR(Status))
728 return Status;
729
730 // check type
731 if (dno->type != FSW_DNODE_TYPE_FILE && dno->type != FSW_DNODE_TYPE_DIR)
732 return EFI_UNSUPPORTED;
733
734 // allocate file structure
735 File = AllocateZeroPool(sizeof(FSW_FILE_DATA));
736 File->Signature = FSW_FILE_DATA_SIGNATURE;
737 if (dno->type == FSW_DNODE_TYPE_FILE)
738 File->Type = FSW_EFI_FILE_TYPE_FILE;
739 else if (dno->type == FSW_DNODE_TYPE_DIR)
740 File->Type = FSW_EFI_FILE_TYPE_DIR;
741
742 // open shandle
743 Status = fsw_efi_map_status(fsw_shandle_open(dno, &File->shand),
744 (FSW_VOLUME_DATA *)dno->vol->host_data);
745 if (EFI_ERROR(Status)) {
746 FreePool(File);
747 return Status;
748 }
749
750 // populate the file handle
751 File->FileHandle.Revision = EFI_FILE_HANDLE_REVISION;
752 File->FileHandle.Open = fsw_efi_FileHandle_Open;
753 File->FileHandle.Close = fsw_efi_FileHandle_Close;
754 File->FileHandle.Delete = fsw_efi_FileHandle_Delete;
755 File->FileHandle.Read = fsw_efi_FileHandle_Read;
756 File->FileHandle.Write = fsw_efi_FileHandle_Write;
757 File->FileHandle.GetPosition = fsw_efi_FileHandle_GetPosition;
758 File->FileHandle.SetPosition = fsw_efi_FileHandle_SetPosition;
759 File->FileHandle.GetInfo = fsw_efi_FileHandle_GetInfo;
760 File->FileHandle.SetInfo = fsw_efi_FileHandle_SetInfo;
761 File->FileHandle.Flush = fsw_efi_FileHandle_Flush;
762
763 *NewFileHandle = &File->FileHandle;
764 return EFI_SUCCESS;
765}
766
767/**
768 * Data read function for regular files. Calls through to fsw_shandle_read.
769 */
770
771EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
772 IN OUT UINTN *BufferSize,
773 OUT VOID *Buffer)
774{
775 EFI_STATUS Status;
776 fsw_u32 buffer_size;
777
778#if DEBUG_LEVEL
779 Print(L"fsw_efi_file_read %d bytes\n", *BufferSize);
780#endif
781
782 buffer_size = (fsw_u32)*BufferSize;
783 if (buffer_size != *BufferSize)
784 buffer_size = ~(fsw_u32)0;
785 Status = fsw_efi_map_status(fsw_shandle_read(&File->shand, &buffer_size, Buffer),
786 (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data);
787 *BufferSize = buffer_size;
788
789 return Status;
790}
791
792/**
793 * Get file position for regular files.
794 */
795
796EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
797 OUT UINT64 *Position)
798{
799 *Position = File->shand.pos;
800 return EFI_SUCCESS;
801}
802
803/**
804 * Set file position for regular files. EFI specifies the all-ones value
805 * to be a special value for the end of the file.
806 */
807
808EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File,
809 IN UINT64 Position)
810{
811 if (Position == 0xFFFFFFFFFFFFFFFFULL)
812 File->shand.pos = File->shand.dnode->size;
813 else
814 File->shand.pos = Position;
815 return EFI_SUCCESS;
816}
817
818/**
819 * Open function used to open new file handles relative to a directory.
820 * In EFI, the "open file" function is implemented by directory file handles
821 * and is passed a relative or volume-absolute path to the file or directory
822 * to open. We use fsw_dnode_lookup_path to find the node plus an additional
823 * call to fsw_dnode_resolve because EFI has no concept of symbolic links.
824 */
825
826EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
827 OUT EFI_FILE **NewHandle,
828 IN CHAR16 *FileName,
829 IN UINT64 OpenMode,
830 IN UINT64 Attributes)
831{
832 EFI_STATUS Status;
833 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
834 struct fsw_dnode *dno;
835 struct fsw_dnode *target_dno;
836 struct fsw_string lookup_path;
837
838#if DEBUG_LEVEL
839 Print(L"fsw_efi_dir_open: '%s'\n", FileName);
840#endif
841
842 if (OpenMode != EFI_FILE_MODE_READ)
843 return EFI_WRITE_PROTECTED;
844
845 lookup_path.type = FSW_STRING_TYPE_UTF16;
846 lookup_path.len = (int)StrLen(FileName);
847 lookup_path.size = lookup_path.len * sizeof(fsw_u16);
848 lookup_path.data = FileName;
849
850 // resolve the path (symlinks along the way are automatically resolved)
851 Status = fsw_efi_map_status(fsw_dnode_lookup_path(File->shand.dnode, &lookup_path, '\\', &dno),
852 Volume);
853 if (EFI_ERROR(Status))
854 return Status;
855
856 // if the final node is a symlink, also resolve it
857 Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno),
858 Volume);
859 fsw_dnode_release(dno);
860 if (EFI_ERROR(Status))
861 return Status;
862 dno = target_dno;
863
864 // make a new EFI handle for the target dnode
865 Status = fsw_efi_dnode_to_FileHandle(dno, NewHandle);
866 fsw_dnode_release(dno);
867 return Status;
868}
869
870/**
871 * Read function for directories. A file handle read on a directory retrieves
872 * the next directory entry.
873 */
874
875EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
876 IN OUT UINTN *BufferSize,
877 OUT VOID *Buffer)
878{
879 EFI_STATUS Status;
880 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
881 struct fsw_dnode *dno;
882
883#if DEBUG_LEVEL
884 Print(L"fsw_efi_dir_read...\n");
885#endif
886
887 // read the next entry
888 Status = fsw_efi_map_status(fsw_dnode_dir_read(&File->shand, &dno),
889 Volume);
890 if (Status == EFI_NOT_FOUND) {
891 // end of directory
892 *BufferSize = 0;
893#if DEBUG_LEVEL
894 Print(L"...no more entries\n");
895#endif
896 return EFI_SUCCESS;
897 }
898 if (EFI_ERROR(Status))
899 return Status;
900
901 // get info into buffer
902 Status = fsw_efi_dnode_fill_FileInfo(Volume, dno, BufferSize, Buffer);
903 fsw_dnode_release(dno);
904 return Status;
905}
906
907/**
908 * Set file position for directories. The only allowed set position operation
909 * for directories is to rewind the directory completely by setting the
910 * position to zero.
911 */
912
913EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File,
914 IN UINT64 Position)
915{
916 if (Position == 0) {
917 File->shand.pos = 0;
918 return EFI_SUCCESS;
919 } else {
920 // directories can only rewind to the start
921 return EFI_UNSUPPORTED;
922 }
923}
924
925/**
926 * Get file or volume information. This function implements the GetInfo call
927 * for all file handles. Control is dispatched according to the type of information
928 * requested by the caller.
929 */
930
931EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
932 IN EFI_GUID *InformationType,
933 IN OUT UINTN *BufferSize,
934 OUT VOID *Buffer)
935{
936 EFI_STATUS Status;
937 FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
938 EFI_FILE_SYSTEM_INFO *FSInfo;
939 UINTN RequiredSize;
940 struct fsw_volume_stat vsb;
941
942 if (CompareGuid(InformationType, &GUID_NAME(FileInfo))) {
943#if DEBUG_LEVEL
944 Print(L"fsw_efi_dnode_getinfo: FILE_INFO\n");
945#endif
946
947 Status = fsw_efi_dnode_fill_FileInfo(Volume, File->shand.dnode, BufferSize, Buffer);
948
949 } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemInfo))) {
950#if DEBUG_LEVEL
951 Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_INFO\n");
952#endif
953
954 // check buffer size
955 RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + fsw_efi_strsize(&Volume->vol->label);
956 if (*BufferSize < RequiredSize) {
957 *BufferSize = RequiredSize;
958 return EFI_BUFFER_TOO_SMALL;
959 }
960
961 // fill structure
962 FSInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;
963 FSInfo->Size = RequiredSize;
964 FSInfo->ReadOnly = TRUE;
965 FSInfo->BlockSize = Volume->vol->log_blocksize;
966 fsw_efi_strcpy(FSInfo->VolumeLabel, &Volume->vol->label);
967
968 // get the missing info from the fs driver
969 ZeroMem(&vsb, sizeof(struct fsw_volume_stat));
970 Status = fsw_efi_map_status(fsw_volume_stat(Volume->vol, &vsb), Volume);
971 if (EFI_ERROR(Status))
972 return Status;
973 FSInfo->VolumeSize = vsb.total_bytes;
974 FSInfo->FreeSpace = vsb.free_bytes;
975
976 // prepare for return
977 *BufferSize = RequiredSize;
978 Status = EFI_SUCCESS;
979
980 } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemVolumeLabelInfoId))) {
981#if DEBUG_LEVEL
982 Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_VOLUME_LABEL\n");
983#endif
984
985 // check buffer size
986 RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO + fsw_efi_strsize(&Volume->vol->label);
987 if (*BufferSize < RequiredSize) {
988 *BufferSize = RequiredSize;
989 return EFI_BUFFER_TOO_SMALL;
990 }
991
992 // copy volume label
993 fsw_efi_strcpy(((EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Buffer)->VolumeLabel, &Volume->vol->label);
994
995 // prepare for return
996 *BufferSize = RequiredSize;
997 Status = EFI_SUCCESS;
998
999#ifdef VBOX
1000 } else if (CompareGuid(InformationType, &gVBoxFsBlessedFileInfoGuid)) {
1001
1002# if FSTYPE == hfs
1003 struct fsw_string StrBlessedFile;
1004
1005 fsw_status_t rc = fsw_hfs_get_blessed_file(Volume->vol, &StrBlessedFile);
1006 if (!rc)
1007 {
1008 // check buffer size
1009 RequiredSize = SIZE_OF_VBOX_FS_BLESSED_FILE + fsw_efi_strsize(&StrBlessedFile);
1010 if (*BufferSize < RequiredSize) {
1011 *BufferSize = RequiredSize;
1012 return EFI_BUFFER_TOO_SMALL;
1013 }
1014
1015 // copy volume label
1016 fsw_efi_strcpy(((VBOX_FS_BLESSED_FILE *)Buffer)->BlessedFile, &StrBlessedFile);
1017
1018 // prepare for return
1019 *BufferSize = RequiredSize;
1020 Status = EFI_SUCCESS;
1021 }
1022 else
1023# endif
1024 Status = EFI_UNSUPPORTED;
1025#endif
1026
1027 } else {
1028 Status = EFI_UNSUPPORTED;
1029 }
1030
1031 return Status;
1032}
1033
1034/**
1035 * Time mapping callback for the fsw_dnode_stat call. This function converts
1036 * a Posix style timestamp into an EFI_TIME structure and writes it to the
1037 * appropriate member of the EFI_FILE_INFO structure that we're filling.
1038 */
1039
1040static void fsw_efi_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time)
1041{
1042 EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data;
1043
1044 if (which == FSW_DNODE_STAT_CTIME)
1045 fsw_efi_decode_time(&FileInfo->CreateTime, posix_time);
1046 else if (which == FSW_DNODE_STAT_MTIME)
1047 fsw_efi_decode_time(&FileInfo->ModificationTime, posix_time);
1048 else if (which == FSW_DNODE_STAT_ATIME)
1049 fsw_efi_decode_time(&FileInfo->LastAccessTime, posix_time);
1050}
1051
1052/**
1053 * Mode mapping callback for the fsw_dnode_stat call. This function looks at
1054 * the Posix mode passed by the file system driver and makes appropriate
1055 * adjustments to the EFI_FILE_INFO structure that we're filling.
1056 */
1057
1058static void fsw_efi_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode)
1059{
1060 EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data;
1061
1062 if ((posix_mode & S_IWUSR) == 0)
1063 FileInfo->Attribute |= EFI_FILE_READ_ONLY;
1064}
1065
1066/**
1067 * Common function to fill an EFI_FILE_INFO with information about a dnode.
1068 */
1069
1070EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
1071 IN struct fsw_dnode *dno,
1072 IN OUT UINTN *BufferSize,
1073 OUT VOID *Buffer)
1074{
1075 EFI_STATUS Status;
1076 EFI_FILE_INFO *FileInfo;
1077 UINTN RequiredSize;
1078 struct fsw_dnode *target_dno;
1079 struct fsw_dnode_stat sb;
1080
1081 // make sure the dnode has complete info
1082 Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume);
1083 if (EFI_ERROR(Status))
1084 return Status;
1085
1086 /// @todo check/assert that the dno's name is in UTF16
1087
1088 // check buffer size
1089 RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_efi_strsize(&dno->name);
1090 if (*BufferSize < RequiredSize) {
1091 /// @todo wind back the directory in this case
1092
1093#if DEBUG_LEVEL
1094 Print(L"...BUFFER TOO SMALL\n");
1095#endif
1096 *BufferSize = RequiredSize;
1097 return EFI_BUFFER_TOO_SMALL;
1098 }
1099
1100 // fill structure
1101 ZeroMem(Buffer, RequiredSize);
1102 FileInfo = (EFI_FILE_INFO *)Buffer;
1103
1104 // must preserve the original file name
1105 fsw_efi_strcpy(FileInfo->FileName, &dno->name);
1106
1107 // if the node is a symlink, also resolve it
1108 Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno), Volume);
1109 fsw_dnode_release(dno);
1110 if (EFI_ERROR(Status))
1111 return Status;
1112 dno = target_dno;
1113 // make sure the dnode has complete info again
1114 Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume);
1115 if (EFI_ERROR(Status))
1116 return Status;
1117
1118 FileInfo->Size = RequiredSize;
1119 FileInfo->FileSize = dno->size;
1120 FileInfo->Attribute = 0;
1121 if (dno->type == FSW_DNODE_TYPE_DIR)
1122 FileInfo->Attribute |= EFI_FILE_DIRECTORY;
1123
1124 // get the missing info from the fs driver
1125 ZeroMem(&sb, sizeof(struct fsw_dnode_stat));
1126 sb.store_time_posix = fsw_efi_store_time_posix;
1127 sb.store_attr_posix = fsw_efi_store_attr_posix;
1128 sb.host_data = FileInfo;
1129 Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume);
1130 if (EFI_ERROR(Status))
1131 return Status;
1132 FileInfo->PhysicalSize = sb.used_bytes;
1133
1134 // prepare for return
1135 *BufferSize = RequiredSize;
1136#if DEBUG_LEVEL
1137 Print(L"...returning '%s'\n", FileInfo->FileName);
1138#endif
1139 return EFI_SUCCESS;
1140}
1141
1142// EOF
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use