summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c1142
1 files changed, 1142 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c
new file mode 100644
index 00000000..9a1168c1
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c
@@ -0,0 +1,1142 @@
+/* $Id: fsw_efi.c $ */
+/** @file
+ * fsw_efi.c - EFI host environment code.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ * ---------------------------------------------------------------------------
+ * This code is based on:
+ *
+ * Copyright (c) 2006 Christoph Pfisterer
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of Christoph Pfisterer nor the names of the
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fsw_efi.h"
+
+#define DEBUG_LEVEL 0
+
+#ifndef FSTYPE
+#ifdef VBOX
+#error FSTYPE must be defined!
+#else
+#define FSTYPE ext2
+#endif
+#endif
+
+/** Helper macro for stringification. */
+#define FSW_EFI_STRINGIFY(x) L#x
+/** Expands to the EFI driver name given the file system type name. */
+#define FSW_EFI_DRIVER_NAME(t) L"Fsw " FSW_EFI_STRINGIFY(t) L" File System Driver"
+
+
+// function prototypes
+
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath);
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath);
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer);
+
+EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName);
+EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName);
+
+void fsw_efi_change_blocksize(struct fsw_volume *vol,
+ fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
+ fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize);
+fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer);
+
+EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume);
+
+EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
+ OUT EFI_FILE **Root);
+EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
+ OUT EFI_FILE **NewFileHandle);
+
+EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer);
+EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
+ OUT UINT64 *Position);
+EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File,
+ IN UINT64 Position);
+
+EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
+ OUT EFI_FILE **NewHandle,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes);
+EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer);
+EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File,
+ IN UINT64 Position);
+
+EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
+ IN EFI_GUID *InformationType,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer);
+EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
+ IN struct fsw_dnode *dno,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer);
+
+#if defined(VBOX) && (FSTYPE == hfs)
+extern fsw_status_t fsw_hfs_get_blessed_file(void *vol, struct fsw_string *path);
+#endif
+
+/**
+ * Interface structure for the EFI Driver Binding protocol.
+ */
+
+EFI_DRIVER_BINDING_PROTOCOL fsw_efi_DriverBinding_table = {
+ fsw_efi_DriverBinding_Supported,
+ fsw_efi_DriverBinding_Start,
+ fsw_efi_DriverBinding_Stop,
+ 0x10,
+ NULL,
+ NULL
+};
+
+/**
+ * Interface structure for the EFI Component Name protocol.
+ */
+
+EFI_COMPONENT_NAME_PROTOCOL fsw_efi_ComponentName_table = {
+ fsw_efi_ComponentName_GetDriverName,
+ fsw_efi_ComponentName_GetControllerName,
+ "eng"
+};
+
+/**
+ * Dispatch table for our FSW host driver.
+ */
+
+struct fsw_host_table fsw_efi_host_table = {
+ FSW_STRING_TYPE_UTF16,
+
+ fsw_efi_change_blocksize,
+ fsw_efi_read_block
+};
+
+extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE);
+
+
+
+/**
+ * Image entry point. Installs the Driver Binding and Component Name protocols
+ * on the image's handle. Actually mounting a file system is initiated through
+ * the Driver Binding protocol at the firmware's request.
+ */
+EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable)
+{
+ EFI_STATUS Status;
+
+#ifndef VBOX
+ InitializeLib(ImageHandle, SystemTable);
+#endif
+
+ // complete Driver Binding protocol instance
+ fsw_efi_DriverBinding_table.ImageHandle = ImageHandle;
+ fsw_efi_DriverBinding_table.DriverBindingHandle = ImageHandle;
+ // install Driver Binding protocol
+ Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle,
+ &PROTO_NAME(DriverBindingProtocol),
+ EFI_NATIVE_INTERFACE,
+ &fsw_efi_DriverBinding_table);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ // install Component Name protocol
+ Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle,
+ &PROTO_NAME(ComponentNameProtocol),
+ EFI_NATIVE_INTERFACE,
+ &fsw_efi_ComponentName_table);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * Driver Binding EFI protocol, Supported function. This function is called by EFI
+ * to test if this driver can handle a certain device. Our implementation only checks
+ * if the device is a disk (i.e. that it supports the Block I/O and Disk I/O protocols)
+ * and implicitly checks if the disk is already in use by another driver.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
+{
+ EFI_STATUS Status;
+ EFI_DISK_IO *DiskIo;
+
+ // we check for both DiskIO and BlockIO protocols
+
+ // first, open DiskIO
+ Status = BS->OpenProtocol(ControllerHandle,
+ &PROTO_NAME(DiskIoProtocol),
+ (VOID **) &DiskIo,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(Status))
+ {
+ return Status;
+ }
+
+ // we were just checking, close it again
+ BS->CloseProtocol(ControllerHandle,
+ &PROTO_NAME(DiskIoProtocol),
+ This->DriverBindingHandle,
+ ControllerHandle);
+
+ // next, check BlockIO without actually opening it
+ Status = BS->OpenProtocol(ControllerHandle,
+ &PROTO_NAME(BlockIoProtocol),
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL);
+ return Status;
+}
+
+static EFI_STATUS fsw_efi_ReMount(IN FSW_VOLUME_DATA *pVolume,
+ IN EFI_HANDLE ControllerHandle,
+ EFI_DISK_IO *pDiskIo,
+ EFI_BLOCK_IO *pBlockIo)
+{
+ EFI_STATUS Status;
+ pVolume->Signature = FSW_VOLUME_DATA_SIGNATURE;
+ pVolume->Handle = ControllerHandle;
+ pVolume->DiskIo = pDiskIo;
+ pVolume->MediaId = pBlockIo->Media->MediaId;
+ pVolume->LastIOStatus = EFI_SUCCESS;
+
+ // mount the filesystem
+ Status = fsw_efi_map_status(fsw_mount(pVolume, &fsw_efi_host_table,
+ &FSW_FSTYPE_TABLE_NAME(FSTYPE), &pVolume->vol),
+ pVolume);
+
+ if (!EFI_ERROR(Status)) {
+ // register the SimpleFileSystem protocol
+ pVolume->FileSystem.Revision = EFI_FILE_IO_INTERFACE_REVISION;
+ pVolume->FileSystem.OpenVolume = fsw_efi_FileSystem_OpenVolume;
+ Status = BS->InstallMultipleProtocolInterfaces(&ControllerHandle,
+ &PROTO_NAME(SimpleFileSystemProtocol), &pVolume->FileSystem,
+ NULL);
+#if DEBUG_LEVEL /* This error is always printed and destroys the boot logo. */
+ if (EFI_ERROR(Status))
+ Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status);
+#endif
+ }
+ return Status;
+}
+
+/**
+ * Driver Binding EFI protocol, Start function. This function is called by EFI
+ * to start driving the given device. It is still possible at this point to
+ * return EFI_UNSUPPORTED, and in fact we will do so if the file system driver
+ * cannot find the superblock signature (or equivalent) that it expects.
+ *
+ * This function allocates memory for a per-volume structure, opens the
+ * required protocols (just Disk I/O in our case, Block I/O is only looked
+ * at to get the MediaId field), and lets the FSW core mount the file system.
+ * If successful, an EFI Simple File System protocol is exported on the
+ * device handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath)
+{
+ EFI_STATUS Status;
+ EFI_BLOCK_IO *BlockIo;
+ EFI_DISK_IO *DiskIo;
+ FSW_VOLUME_DATA *Volume;
+
+ // open consumed protocols
+ Status = BS->OpenProtocol(ControllerHandle,
+ &PROTO_NAME(BlockIoProtocol),
+ (VOID **) &BlockIo,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL); // NOTE: we only want to look at the MediaId
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ Status = BS->OpenProtocol(ControllerHandle,
+ &PROTO_NAME(DiskIoProtocol),
+ (VOID **) &DiskIo,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ // allocate volume structure
+ Volume = AllocateZeroPool(sizeof(FSW_VOLUME_DATA));
+ Status = fsw_efi_ReMount(Volume, ControllerHandle, DiskIo, BlockIo);
+
+ // on errors, close the opened protocols
+ if (EFI_ERROR(Status)) {
+ if (Volume->vol != NULL)
+ fsw_unmount(Volume->vol);
+ FreePool(Volume);
+
+#if 0
+ if (Status == EFI_MEDIA_CHANGED)
+ Status = fsw_efi_ReMount(Volume, ControllerHandle, DiskIo, BlockIo);
+ else
+#endif
+ BS->CloseProtocol(ControllerHandle,
+ &PROTO_NAME(DiskIoProtocol),
+ This->DriverBindingHandle,
+ ControllerHandle);
+ }
+
+ return Status;
+}
+
+/**
+ * Driver Binding EFI protocol, Stop function. This function is called by EFI
+ * to stop the driver on the given device. This translates to an unmount
+ * call for the FSW core.
+ *
+ * We assume that all file handles on the volume have been closed before
+ * the driver is stopped. At least with the EFI shell, that is actually the
+ * case; it closes all file handles between commands.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer)
+{
+ EFI_STATUS Status;
+ EFI_FILE_IO_INTERFACE *FileSystem;
+ FSW_VOLUME_DATA *Volume;
+
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_DriverBinding_Stop\n");
+#endif
+
+ // get the installed SimpleFileSystem interface
+ Status = BS->OpenProtocol(ControllerHandle,
+ &PROTO_NAME(SimpleFileSystemProtocol),
+ (VOID **) &FileSystem,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (EFI_ERROR(Status))
+ return EFI_UNSUPPORTED;
+
+ // get private data structure
+ Volume = FSW_VOLUME_FROM_FILE_SYSTEM(FileSystem);
+
+ // uninstall Simple File System protocol
+ Status = BS->UninstallMultipleProtocolInterfaces(ControllerHandle,
+ &PROTO_NAME(SimpleFileSystemProtocol), &Volume->FileSystem,
+ NULL);
+ if (EFI_ERROR(Status)) {
+ Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status);
+ return Status;
+ }
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_DriverBinding_Stop: protocol uninstalled successfully\n");
+#endif
+
+ // release private data structure
+ if (Volume->vol != NULL)
+ fsw_unmount(Volume->vol);
+ FreePool(Volume);
+
+ // close the consumed protocols
+ Status = BS->CloseProtocol(ControllerHandle,
+ &PROTO_NAME(DiskIoProtocol),
+ This->DriverBindingHandle,
+ ControllerHandle);
+
+ return Status;
+}
+
+/**
+ * Component Name EFI protocol, GetDriverName function. Used by the EFI
+ * environment to inquire the name of this driver. The name returned is
+ * based on the file system type actually used in compilation.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName)
+{
+ if (Language == NULL || DriverName == NULL)
+ return EFI_INVALID_PARAMETER;
+#if 0
+
+ if (Language[0] == 'e' && Language[1] == 'n' && Language[2] == 'g' && Language[3] == 0) {
+ *DriverName = FSW_EFI_DRIVER_NAME(FSTYPE);
+ return EFI_SUCCESS;
+ }
+#endif
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * Component Name EFI protocol, GetControllerName function. Not implemented
+ * because this is not a "bus" driver in the sense of the EFI Driver Model.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName)
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * FSW interface function for block size changes. This function is called by the FSW core
+ * when the file system driver changes the block sizes for the volume.
+ */
+
+void fsw_efi_change_blocksize(struct fsw_volume *vol,
+ fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize,
+ fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize)
+{
+ // nothing to do
+}
+
+/**
+ * FSW interface function to read data blocks. This function is called by the FSW core
+ * to read a block of data from the device. The buffer is allocated by the core code.
+ */
+
+fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer)
+{
+ EFI_STATUS Status;
+ FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)vol->host_data;
+
+ FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_efi_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize));
+
+ // read from disk
+ Status = Volume->DiskIo->ReadDisk(Volume->DiskIo, Volume->MediaId,
+ (UINT64)phys_bno * vol->phys_blocksize,
+ vol->phys_blocksize,
+ buffer);
+ Volume->LastIOStatus = Status;
+ if (EFI_ERROR(Status))
+ return FSW_IO_ERROR;
+ return FSW_SUCCESS;
+}
+
+/**
+ * Map FSW status codes to EFI status codes. The FSW_IO_ERROR code is only produced
+ * by fsw_efi_read_block, so we map it back to the EFI status code remembered from
+ * the last I/O operation.
+ */
+
+EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume)
+{
+ switch (fsw_status) {
+ case FSW_SUCCESS:
+ return EFI_SUCCESS;
+ case FSW_OUT_OF_MEMORY:
+ return EFI_VOLUME_CORRUPTED;
+ case FSW_IO_ERROR:
+ return Volume->LastIOStatus;
+ case FSW_UNSUPPORTED:
+ return EFI_UNSUPPORTED;
+ case FSW_NOT_FOUND:
+ return EFI_NOT_FOUND;
+ case FSW_VOLUME_CORRUPTED:
+ return EFI_VOLUME_CORRUPTED;
+ default:
+ return EFI_DEVICE_ERROR;
+ }
+}
+
+/**
+ * File System EFI protocol, OpenVolume function. Creates a file handle for
+ * the root directory and returns it. Note that this function may be called
+ * multiple times and returns a new file handle each time. Each returned
+ * handle is closed by the client using it.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This,
+ OUT EFI_FILE **Root)
+{
+ EFI_STATUS Status;
+ FSW_VOLUME_DATA *Volume = FSW_VOLUME_FROM_FILE_SYSTEM(This);
+
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_FileSystem_OpenVolume\n");
+#endif
+
+ Status = fsw_efi_dnode_to_FileHandle(Volume->vol->root, Root);
+
+ return Status;
+}
+
+/**
+ * File Handle EFI protocol, Open function. Dispatches the call
+ * based on the kind of file handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Open(IN EFI_FILE *This,
+ OUT EFI_FILE **NewHandle,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes)
+{
+ FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
+
+ if (File->Type == FSW_EFI_FILE_TYPE_DIR)
+ return fsw_efi_dir_open(File, NewHandle, FileName, OpenMode, Attributes);
+ // not supported for regular files
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * File Handle EFI protocol, Close function. Closes the FSW shandle
+ * and frees the memory used for the structure.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Close(IN EFI_FILE *This)
+{
+ FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
+
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_FileHandle_Close\n");
+#endif
+
+ fsw_shandle_close(&File->shand);
+ FreePool(File);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ * File Handle EFI protocol, Delete function. Calls through to Close
+ * and returns a warning because this driver is read-only.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Delete(IN EFI_FILE *This)
+{
+ EFI_STATUS Status;
+
+ Status = This->Close(This);
+ if (Status == EFI_SUCCESS) {
+ // this driver is read-only
+ Status = EFI_WARN_DELETE_FAILURE;
+ }
+
+ return Status;
+}
+
+/**
+ * File Handle EFI protocol, Read function. Dispatches the call
+ * based on the kind of file handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Read(IN EFI_FILE *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer)
+{
+ FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
+
+ if (File->Type == FSW_EFI_FILE_TYPE_FILE)
+ return fsw_efi_file_read(File, BufferSize, Buffer);
+ else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
+ return fsw_efi_dir_read(File, BufferSize, Buffer);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * File Handle EFI protocol, Write function. Returns unsupported status
+ * because this driver is read-only.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Write(IN EFI_FILE *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer)
+{
+ // this driver is read-only
+ return EFI_WRITE_PROTECTED;
+}
+
+/**
+ * File Handle EFI protocol, GetPosition function. Dispatches the call
+ * based on the kind of file handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_GetPosition(IN EFI_FILE *This,
+ OUT UINT64 *Position)
+{
+ FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
+
+ if (File->Type == FSW_EFI_FILE_TYPE_FILE)
+ return fsw_efi_file_getpos(File, Position);
+ // not defined for directories
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * File Handle EFI protocol, SetPosition function. Dispatches the call
+ * based on the kind of file handle.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_SetPosition(IN EFI_FILE *This,
+ IN UINT64 Position)
+{
+ FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
+
+ if (File->Type == FSW_EFI_FILE_TYPE_FILE)
+ return fsw_efi_file_setpos(File, Position);
+ else if (File->Type == FSW_EFI_FILE_TYPE_DIR)
+ return fsw_efi_dir_setpos(File, Position);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * File Handle EFI protocol, GetInfo function. Dispatches to the common
+ * function implementing this.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_GetInfo(IN EFI_FILE *This,
+ IN EFI_GUID *InformationType,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer)
+{
+ FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This);
+
+ return fsw_efi_dnode_getinfo(File, InformationType, BufferSize, Buffer);
+}
+
+/**
+ * File Handle EFI protocol, SetInfo function. Returns unsupported status
+ * because this driver is read-only.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_SetInfo(IN EFI_FILE *This,
+ IN EFI_GUID *InformationType,
+ IN UINTN BufferSize,
+ IN VOID *Buffer)
+{
+ // this driver is read-only
+ return EFI_WRITE_PROTECTED;
+}
+
+/**
+ * File Handle EFI protocol, Flush function. Returns unsupported status
+ * because this driver is read-only.
+ */
+
+EFI_STATUS EFIAPI fsw_efi_FileHandle_Flush(IN EFI_FILE *This)
+{
+ // this driver is read-only
+ return EFI_WRITE_PROTECTED;
+}
+
+/**
+ * Set up a file handle for a dnode. This function allocates a data structure
+ * for a file handle, opens a FSW shandle and populates the EFI_FILE structure
+ * with the interface functions.
+ */
+
+EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno,
+ OUT EFI_FILE **NewFileHandle)
+{
+ EFI_STATUS Status;
+ FSW_FILE_DATA *File;
+
+ // make sure the dnode has complete info
+ Status = fsw_efi_map_status(fsw_dnode_fill(dno), (FSW_VOLUME_DATA *)dno->vol->host_data);
+ if (EFI_ERROR(Status))
+ return Status;
+
+ // check type
+ if (dno->type != FSW_DNODE_TYPE_FILE && dno->type != FSW_DNODE_TYPE_DIR)
+ return EFI_UNSUPPORTED;
+
+ // allocate file structure
+ File = AllocateZeroPool(sizeof(FSW_FILE_DATA));
+ File->Signature = FSW_FILE_DATA_SIGNATURE;
+ if (dno->type == FSW_DNODE_TYPE_FILE)
+ File->Type = FSW_EFI_FILE_TYPE_FILE;
+ else if (dno->type == FSW_DNODE_TYPE_DIR)
+ File->Type = FSW_EFI_FILE_TYPE_DIR;
+
+ // open shandle
+ Status = fsw_efi_map_status(fsw_shandle_open(dno, &File->shand),
+ (FSW_VOLUME_DATA *)dno->vol->host_data);
+ if (EFI_ERROR(Status)) {
+ FreePool(File);
+ return Status;
+ }
+
+ // populate the file handle
+ File->FileHandle.Revision = EFI_FILE_HANDLE_REVISION;
+ File->FileHandle.Open = fsw_efi_FileHandle_Open;
+ File->FileHandle.Close = fsw_efi_FileHandle_Close;
+ File->FileHandle.Delete = fsw_efi_FileHandle_Delete;
+ File->FileHandle.Read = fsw_efi_FileHandle_Read;
+ File->FileHandle.Write = fsw_efi_FileHandle_Write;
+ File->FileHandle.GetPosition = fsw_efi_FileHandle_GetPosition;
+ File->FileHandle.SetPosition = fsw_efi_FileHandle_SetPosition;
+ File->FileHandle.GetInfo = fsw_efi_FileHandle_GetInfo;
+ File->FileHandle.SetInfo = fsw_efi_FileHandle_SetInfo;
+ File->FileHandle.Flush = fsw_efi_FileHandle_Flush;
+
+ *NewFileHandle = &File->FileHandle;
+ return EFI_SUCCESS;
+}
+
+/**
+ * Data read function for regular files. Calls through to fsw_shandle_read.
+ */
+
+EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer)
+{
+ EFI_STATUS Status;
+ fsw_u32 buffer_size;
+
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_file_read %d bytes\n", *BufferSize);
+#endif
+
+ buffer_size = (fsw_u32)*BufferSize;
+ if (buffer_size != *BufferSize)
+ buffer_size = ~(fsw_u32)0;
+ Status = fsw_efi_map_status(fsw_shandle_read(&File->shand, &buffer_size, Buffer),
+ (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data);
+ *BufferSize = buffer_size;
+
+ return Status;
+}
+
+/**
+ * Get file position for regular files.
+ */
+
+EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File,
+ OUT UINT64 *Position)
+{
+ *Position = File->shand.pos;
+ return EFI_SUCCESS;
+}
+
+/**
+ * Set file position for regular files. EFI specifies the all-ones value
+ * to be a special value for the end of the file.
+ */
+
+EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File,
+ IN UINT64 Position)
+{
+ if (Position == 0xFFFFFFFFFFFFFFFFULL)
+ File->shand.pos = File->shand.dnode->size;
+ else
+ File->shand.pos = Position;
+ return EFI_SUCCESS;
+}
+
+/**
+ * Open function used to open new file handles relative to a directory.
+ * In EFI, the "open file" function is implemented by directory file handles
+ * and is passed a relative or volume-absolute path to the file or directory
+ * to open. We use fsw_dnode_lookup_path to find the node plus an additional
+ * call to fsw_dnode_resolve because EFI has no concept of symbolic links.
+ */
+
+EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File,
+ OUT EFI_FILE **NewHandle,
+ IN CHAR16 *FileName,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes)
+{
+ EFI_STATUS Status;
+ FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
+ struct fsw_dnode *dno;
+ struct fsw_dnode *target_dno;
+ struct fsw_string lookup_path;
+
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_dir_open: '%s'\n", FileName);
+#endif
+
+ if (OpenMode != EFI_FILE_MODE_READ)
+ return EFI_WRITE_PROTECTED;
+
+ lookup_path.type = FSW_STRING_TYPE_UTF16;
+ lookup_path.len = (int)StrLen(FileName);
+ lookup_path.size = lookup_path.len * sizeof(fsw_u16);
+ lookup_path.data = FileName;
+
+ // resolve the path (symlinks along the way are automatically resolved)
+ Status = fsw_efi_map_status(fsw_dnode_lookup_path(File->shand.dnode, &lookup_path, '\\', &dno),
+ Volume);
+ if (EFI_ERROR(Status))
+ return Status;
+
+ // if the final node is a symlink, also resolve it
+ Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno),
+ Volume);
+ fsw_dnode_release(dno);
+ if (EFI_ERROR(Status))
+ return Status;
+ dno = target_dno;
+
+ // make a new EFI handle for the target dnode
+ Status = fsw_efi_dnode_to_FileHandle(dno, NewHandle);
+ fsw_dnode_release(dno);
+ return Status;
+}
+
+/**
+ * Read function for directories. A file handle read on a directory retrieves
+ * the next directory entry.
+ */
+
+EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer)
+{
+ EFI_STATUS Status;
+ FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
+ struct fsw_dnode *dno;
+
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_dir_read...\n");
+#endif
+
+ // read the next entry
+ Status = fsw_efi_map_status(fsw_dnode_dir_read(&File->shand, &dno),
+ Volume);
+ if (Status == EFI_NOT_FOUND) {
+ // end of directory
+ *BufferSize = 0;
+#if DEBUG_LEVEL
+ Print(L"...no more entries\n");
+#endif
+ return EFI_SUCCESS;
+ }
+ if (EFI_ERROR(Status))
+ return Status;
+
+ // get info into buffer
+ Status = fsw_efi_dnode_fill_FileInfo(Volume, dno, BufferSize, Buffer);
+ fsw_dnode_release(dno);
+ return Status;
+}
+
+/**
+ * Set file position for directories. The only allowed set position operation
+ * for directories is to rewind the directory completely by setting the
+ * position to zero.
+ */
+
+EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File,
+ IN UINT64 Position)
+{
+ if (Position == 0) {
+ File->shand.pos = 0;
+ return EFI_SUCCESS;
+ } else {
+ // directories can only rewind to the start
+ return EFI_UNSUPPORTED;
+ }
+}
+
+/**
+ * Get file or volume information. This function implements the GetInfo call
+ * for all file handles. Control is dispatched according to the type of information
+ * requested by the caller.
+ */
+
+EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File,
+ IN EFI_GUID *InformationType,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer)
+{
+ EFI_STATUS Status;
+ FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data;
+ EFI_FILE_SYSTEM_INFO *FSInfo;
+ UINTN RequiredSize;
+ struct fsw_volume_stat vsb;
+
+ if (CompareGuid(InformationType, &GUID_NAME(FileInfo))) {
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_dnode_getinfo: FILE_INFO\n");
+#endif
+
+ Status = fsw_efi_dnode_fill_FileInfo(Volume, File->shand.dnode, BufferSize, Buffer);
+
+ } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemInfo))) {
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_INFO\n");
+#endif
+
+ // check buffer size
+ RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + fsw_efi_strsize(&Volume->vol->label);
+ if (*BufferSize < RequiredSize) {
+ *BufferSize = RequiredSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ // fill structure
+ FSInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;
+ FSInfo->Size = RequiredSize;
+ FSInfo->ReadOnly = TRUE;
+ FSInfo->BlockSize = Volume->vol->log_blocksize;
+ fsw_efi_strcpy(FSInfo->VolumeLabel, &Volume->vol->label);
+
+ // get the missing info from the fs driver
+ ZeroMem(&vsb, sizeof(struct fsw_volume_stat));
+ Status = fsw_efi_map_status(fsw_volume_stat(Volume->vol, &vsb), Volume);
+ if (EFI_ERROR(Status))
+ return Status;
+ FSInfo->VolumeSize = vsb.total_bytes;
+ FSInfo->FreeSpace = vsb.free_bytes;
+
+ // prepare for return
+ *BufferSize = RequiredSize;
+ Status = EFI_SUCCESS;
+
+ } else if (CompareGuid(InformationType, &GUID_NAME(FileSystemVolumeLabelInfoId))) {
+#if DEBUG_LEVEL
+ Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_VOLUME_LABEL\n");
+#endif
+
+ // check buffer size
+ RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO + fsw_efi_strsize(&Volume->vol->label);
+ if (*BufferSize < RequiredSize) {
+ *BufferSize = RequiredSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ // copy volume label
+ fsw_efi_strcpy(((EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Buffer)->VolumeLabel, &Volume->vol->label);
+
+ // prepare for return
+ *BufferSize = RequiredSize;
+ Status = EFI_SUCCESS;
+
+#ifdef VBOX
+ } else if (CompareGuid(InformationType, &gVBoxFsBlessedFileInfoGuid)) {
+
+# if FSTYPE == hfs
+ struct fsw_string StrBlessedFile;
+
+ fsw_status_t rc = fsw_hfs_get_blessed_file(Volume->vol, &StrBlessedFile);
+ if (!rc)
+ {
+ // check buffer size
+ RequiredSize = SIZE_OF_VBOX_FS_BLESSED_FILE + fsw_efi_strsize(&StrBlessedFile);
+ if (*BufferSize < RequiredSize) {
+ *BufferSize = RequiredSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ // copy volume label
+ fsw_efi_strcpy(((VBOX_FS_BLESSED_FILE *)Buffer)->BlessedFile, &StrBlessedFile);
+
+ // prepare for return
+ *BufferSize = RequiredSize;
+ Status = EFI_SUCCESS;
+ }
+ else
+# endif
+ Status = EFI_UNSUPPORTED;
+#endif
+
+ } else {
+ Status = EFI_UNSUPPORTED;
+ }
+
+ return Status;
+}
+
+/**
+ * Time mapping callback for the fsw_dnode_stat call. This function converts
+ * a Posix style timestamp into an EFI_TIME structure and writes it to the
+ * appropriate member of the EFI_FILE_INFO structure that we're filling.
+ */
+
+static void fsw_efi_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time)
+{
+ EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data;
+
+ if (which == FSW_DNODE_STAT_CTIME)
+ fsw_efi_decode_time(&FileInfo->CreateTime, posix_time);
+ else if (which == FSW_DNODE_STAT_MTIME)
+ fsw_efi_decode_time(&FileInfo->ModificationTime, posix_time);
+ else if (which == FSW_DNODE_STAT_ATIME)
+ fsw_efi_decode_time(&FileInfo->LastAccessTime, posix_time);
+}
+
+/**
+ * Mode mapping callback for the fsw_dnode_stat call. This function looks at
+ * the Posix mode passed by the file system driver and makes appropriate
+ * adjustments to the EFI_FILE_INFO structure that we're filling.
+ */
+
+static void fsw_efi_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode)
+{
+ EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data;
+
+ if ((posix_mode & S_IWUSR) == 0)
+ FileInfo->Attribute |= EFI_FILE_READ_ONLY;
+}
+
+/**
+ * Common function to fill an EFI_FILE_INFO with information about a dnode.
+ */
+
+EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume,
+ IN struct fsw_dnode *dno,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer)
+{
+ EFI_STATUS Status;
+ EFI_FILE_INFO *FileInfo;
+ UINTN RequiredSize;
+ struct fsw_dnode *target_dno;
+ struct fsw_dnode_stat sb;
+
+ // make sure the dnode has complete info
+ Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume);
+ if (EFI_ERROR(Status))
+ return Status;
+
+ /// @todo check/assert that the dno's name is in UTF16
+
+ // check buffer size
+ RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_efi_strsize(&dno->name);
+ if (*BufferSize < RequiredSize) {
+ /// @todo wind back the directory in this case
+
+#if DEBUG_LEVEL
+ Print(L"...BUFFER TOO SMALL\n");
+#endif
+ *BufferSize = RequiredSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ // fill structure
+ ZeroMem(Buffer, RequiredSize);
+ FileInfo = (EFI_FILE_INFO *)Buffer;
+
+ // must preserve the original file name
+ fsw_efi_strcpy(FileInfo->FileName, &dno->name);
+
+ // if the node is a symlink, also resolve it
+ Status = fsw_efi_map_status(fsw_dnode_resolve(dno, &target_dno), Volume);
+ fsw_dnode_release(dno);
+ if (EFI_ERROR(Status))
+ return Status;
+ dno = target_dno;
+ // make sure the dnode has complete info again
+ Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume);
+ if (EFI_ERROR(Status))
+ return Status;
+
+ FileInfo->Size = RequiredSize;
+ FileInfo->FileSize = dno->size;
+ FileInfo->Attribute = 0;
+ if (dno->type == FSW_DNODE_TYPE_DIR)
+ FileInfo->Attribute |= EFI_FILE_DIRECTORY;
+
+ // get the missing info from the fs driver
+ ZeroMem(&sb, sizeof(struct fsw_dnode_stat));
+ sb.store_time_posix = fsw_efi_store_time_posix;
+ sb.store_attr_posix = fsw_efi_store_attr_posix;
+ sb.host_data = FileInfo;
+ Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume);
+ if (EFI_ERROR(Status))
+ return Status;
+ FileInfo->PhysicalSize = sb.used_bytes;
+
+ // prepare for return
+ *BufferSize = RequiredSize;
+#if DEBUG_LEVEL
+ Print(L"...returning '%s'\n", FileInfo->FileName);
+#endif
+ return EFI_SUCCESS;
+}
+
+// EOF