summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe')
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/ReadMe.txt5
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxFswParam.h89
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxHfs.inf95
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxIso9660.inf95
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_base.h192
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.c961
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.h554
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.c1142
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.h133
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_base.h115
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_lib.c179
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.c1451
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.h177
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.c679
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.h235
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_lib.c564
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_strfunc.h491
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/Makefile32
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/README2
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.c483
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.h120
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix_base.h90
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lslr.c140
-rw-r--r--src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lsroot.c79
24 files changed, 8103 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/ReadMe.txt b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/ReadMe.txt
new file mode 100644
index 00000000..871ac64f
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/ReadMe.txt
@@ -0,0 +1,5 @@
+FSW code (all fsw_* files) are based on rEFIt's FSW (filesystem wrapper)
+code, revison 448 taken from
+
+ https://refit.svn.sourceforge.net/svnroot/refit/trunk/refit
+
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxFswParam.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxFswParam.h
new file mode 100644
index 00000000..6904ad6d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxFswParam.h
@@ -0,0 +1,89 @@
+/* $Id: VBoxFswParam.h $ */
+/** @file
+ * VBoxFswParam.h
+ */
+
+/*
+ * Copyright (C) 2010-2022 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
+ */
+
+#ifndef VBOXFSPARAM_H
+#define VBOXFSPARAM_H
+/*
+ * Here is common declarations for EDK<->EDK2 compatibility
+ */
+# include <Uefi.h>
+# include <Library/DebugLib.h>
+# include <Library/BaseLib.h>
+# include <Protocol/DriverBinding.h>
+# include <Library/BaseMemoryLib.h>
+# include <Library/UefiRuntimeServicesTableLib.h>
+# include <Library/UefiDriverEntryPoint.h>
+# include <Library/UefiBootServicesTableLib.h>
+# include <Library/MemoryAllocationLib.h>
+# include <Library/DevicePathLib.h>
+# include <Protocol/DevicePathFromText.h>
+# include <Protocol/DevicePathToText.h>
+# include <Protocol/DebugPort.h>
+# include <Protocol/DebugSupport.h>
+# include <Library/PrintLib.h>
+# include <Library/UefiLib.h>
+# include <Protocol/SimpleFileSystem.h>
+# include <Protocol/BlockIo.h>
+# include <Protocol/DiskIo.h>
+# include <Guid/FileSystemInfo.h>
+# include <Guid/FileInfo.h>
+# include <Guid/FileSystemVolumeLabelInfo.h>
+# include <Protocol/ComponentName.h>
+
+# include <Guid/VBoxFsBlessedFileInfo.h> /* For HFS blessed file support. */
+
+# define BS gBS
+# define PROTO_NAME(x) gEfi ## x ## Guid
+# define GUID_NAME(x) gEfi ## x ## Guid
+
+# define EFI_FILE_HANDLE_REVISION EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION
+# define SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL
+# define EFI_FILE_SYSTEM_VOLUME_LABEL_INFO EFI_FILE_SYSTEM_VOLUME_LABEL
+# define EFI_SIGNATURE_32(a, b, c, d) SIGNATURE_32(a, b, c, d)
+# define DivU64x32(x,y,z) DivU64x32((x),(y))
+
+
+INTN CompareGuidEdk1(
+ IN EFI_GUID *Guid1,
+ IN EFI_GUID *Guid2
+ );
+
+//#define CompareGuid(x, y) CompareGuidEdk1((x),(y))
+# define HOST_EFI 1
+//# define FSW_DEBUG_LEVEL 3
+
+int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len);
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxHfs.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxHfs.inf
new file mode 100644
index 00000000..96f11337
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxHfs.inf
@@ -0,0 +1,95 @@
+# $Id: VBoxHfs.inf $
+## @file
+# VBoxHfs - VBox HFS FS driver.
+#
+
+#
+# Copyright (C) 2010-2022 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
+#
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VBoxHfs
+ FILE_GUID = 6E506CC2-65E0-4814-994A-08ECDA046987
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+
+ ENTRY_POINT = fsw_efi_main
+
+[Sources.common]
+ fsw_hfs.c
+ fsw_core.c
+ fsw_efi_lib.c
+ fsw_efi.c
+ fsw_lib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ VBoxPkg/VBoxPkg.dec
+
+[LibraryClasses]
+ UefiRuntimeServicesTableLib
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ BaseLib
+ UefiLib
+ UefiDriverEntryPoint
+ DebugLib
+ PcdLib
+ DevicePathLib
+
+[Guids]
+ gEfiFileInfoGuid
+ gEfiFileSystemInfoGuid
+ gEfiFileSystemVolumeLabelInfoIdGuid
+ gVBoxFsBlessedFileInfoGuid
+
+[Protocols]
+ gEfiDiskIoProtocolGuid
+ gEfiBlockIoProtocolGuid
+ gEfiSimpleFileSystemProtocolGuid
+ gEfiUnicodeCollationProtocolGuid
+ gEfiUnicodeCollation2ProtocolGuid
+ gEfiDevicePathToTextProtocolGuid ## CONSUMED
+
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang
+ gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang
+
+[BuildOptions.common]
+ GCC:*_*_*_CC_FLAGS = -DFSTYPE=hfs -DEFI_LOG_ENABLED=1
+# -DFSW_DEBUG_LEVEL=3
+ INTEL:*_*_*_CC_FLAGS = -DFSTYPE=hfs -DEFI_LOG_ENABLED=1
+ MSFT:*_*_*_CC_FLAGS = -DFSTYPE=hfs -DEFI_LOG_ENABLED=1
+
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxIso9660.inf b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxIso9660.inf
new file mode 100644
index 00000000..2f3875ad
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/VBoxIso9660.inf
@@ -0,0 +1,95 @@
+# $Id: VBoxIso9660.inf $
+## @file
+# VBox ISO9660 FS driver
+#
+
+#
+# Copyright (C) 2010-2022 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
+#
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VBoxIso9600
+ FILE_GUID = B34E57EE-2E02-4DAF-867F-7F40BE6FC33D
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ EDK_RELEASE_VERSION = 0x00020000
+ EFI_SPECIFICATION_VERSION = 0x00020000
+ ENTRY_POINT = fsw_efi_main
+
+[Sources.common]
+ fsw_iso9660.c
+ fsw_core.c
+ fsw_efi_lib.c
+ fsw_efi.c
+ fsw_lib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ VBoxPkg/VBoxPkg.dec
+
+[LibraryClasses]
+ UefiRuntimeServicesTableLib
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ BaseLib
+ UefiLib
+ UefiDriverEntryPoint
+ DebugLib
+ PcdLib
+ DevicePathLib
+
+[Guids]
+ gEfiFileInfoGuid
+ gEfiFileSystemInfoGuid
+ gEfiFileSystemVolumeLabelInfoIdGuid
+
+
+[Protocols]
+ gEfiDiskIoProtocolGuid
+ gEfiBlockIoProtocolGuid
+ gEfiSimpleFileSystemProtocolGuid
+ gEfiUnicodeCollationProtocolGuid
+ gEfiUnicodeCollation2ProtocolGuid
+ gEfiDevicePathToTextProtocolGuid ## CONSUMED
+
+[Pcd]
+ gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang
+ gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang
+
+[BuildOptions.common]
+ GCC:*_*_*_CC_FLAGS = -DFSTYPE=iso9660
+
+ INTEL:*_*_*_CC_FLAGS = -DFSTYPE=iso9660
+
+ MSFT:*_*_*_CC_FLAGS = -DFSTYPE=iso9660
+
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_base.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_base.h
new file mode 100644
index 00000000..7770b009
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_base.h
@@ -0,0 +1,192 @@
+/* $Id: fsw_base.h $ */
+/** @file
+ * fsw_base.h - Base definitions switch.
+ */
+
+/*
+ * Copyright (C) 2010-2022 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.
+ */
+
+#ifndef _FSW_BASE_H_
+#define _FSW_BASE_H_
+
+#ifdef VBOX
+#include "VBoxFswParam.h"
+#endif
+
+#ifndef FSW_DEBUG_LEVEL
+/**
+ * Global debugging level. Can be set locally for the scope of a single
+ * file by defining the macro before fsw_base.h is included.
+ */
+#define FSW_DEBUG_LEVEL 1
+#endif
+
+
+#ifdef HOST_EFI
+#include "fsw_efi_base.h"
+#endif
+
+#ifdef HOST_POSIX
+#include "fsw_posix_base.h"
+#endif
+
+// message printing
+
+#if FSW_DEBUG_LEVEL >= 1
+#define FSW_MSG_ASSERT(params) FSW_MSGFUNC params
+#else
+#define FSW_MSG_ASSERT(params)
+#endif
+
+#if FSW_DEBUG_LEVEL >= 2
+#define FSW_MSG_DEBUG(params) FSW_MSGFUNC params
+#else
+#define FSW_MSG_DEBUG(params)
+#endif
+
+#if FSW_DEBUG_LEVEL >= 3
+#define FSW_MSG_DEBUGV(params) FSW_MSGFUNC params
+#else
+#define FSW_MSG_DEBUGV(params)
+#endif
+
+
+// Documentation for system-dependent defines
+
+/**
+ * \typedef fsw_s8
+ * Signed 8-bit integer.
+ */
+
+/**
+ * \typedef fsw_u8
+ * Unsigned 8-bit integer.
+ */
+
+/**
+ * \typedef fsw_s16
+ * Signed 16-bit integer.
+ */
+
+/**
+ * \typedef fsw_u16
+ * Unsigned 16-bit integer.
+ */
+
+/**
+ * \typedef fsw_s32
+ * Signed 32-bit integer.
+ */
+
+/**
+ * \typedef fsw_u32
+ * Unsigned 32-bit integer.
+ */
+
+/**
+ * \typedef fsw_s64
+ * Signed 64-bit integer.
+ */
+
+/**
+ * \typedef fsw_u64
+ * Unsigned 64-bit integer.
+ */
+
+
+/**
+ * \def fsw_alloc(size,ptrptr)
+ * Allocate memory on the heap. This function or macro allocates \a size
+ * bytes of memory using host-specific methods. The address of the
+ * allocated memory block is stored into the pointer variable pointed
+ * to by \a ptrptr. A status code is returned; FSW_SUCCESS if the block
+ * was allocated or FSW_OUT_OF_MEMORY if there is not enough memory
+ * to allocated the requested block.
+ */
+
+/**
+ * \def fsw_free(ptr)
+ * Release allocated memory. This function or macro returns an allocated
+ * memory block to the heap for reuse. Does not return a status.
+ */
+
+/**
+ * \def fsw_memcpy(dest,src,size)
+ * Copies a block of memory from \a src to \a dest. The two memory blocks
+ * must not overlap, or the result of the operation will be undefined.
+ * Does not return a status.
+ */
+
+/**
+ * \def fsw_memeq(dest,src,size)
+ * Compares two blocks of memory for equality. Returns boolean true if the
+ * memory blocks are equal, boolean false if they are different.
+ */
+
+/**
+ * \def fsw_memzero(dest,size)
+ * Initializes a block of memory with zeros. Does not return a status.
+ */
+
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.c
new file mode 100644
index 00000000..6c39a814
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.c
@@ -0,0 +1,961 @@
+/* $Id: fsw_core.c $ */
+/** @file
+ * fsw_core.c - Core file system wrapper abstraction layer code.
+ */
+
+/*
+ * Copyright (C) 2010-2022 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_core.h"
+
+
+// functions
+
+static void fsw_blockcache_free(struct fsw_volume *vol);
+
+#define MAX_CACHE_LEVEL (5)
+
+
+/**
+ * Mount a volume with a given file system driver. This function is called by the
+ * host driver to make a volume accessible. The file system driver to use is specified
+ * by a pointer to its dispatch table. The file system driver will look at the
+ * data on the volume to determine if it can read the format. If the volume is found
+ * unsuitable, FSW_UNSUPPORTED is returned.
+ *
+ * If this function returns FSW_SUCCESS, *vol_out points at a valid volume data
+ * structure. The caller must release it later by calling fsw_unmount.
+ *
+ * If this function returns an error status, the caller only needs to clean up its
+ * own buffers that may have been allocated through the read_block interface.
+ */
+
+fsw_status_t fsw_mount(void *host_data,
+ struct fsw_host_table *host_table,
+ struct fsw_fstype_table *fstype_table,
+ struct fsw_volume **vol_out)
+{
+ fsw_status_t status;
+ struct fsw_volume *vol;
+
+ // allocate memory for the structure
+ status = fsw_alloc_zero(fstype_table->volume_struct_size, (void **)&vol);
+ if (status)
+ return status;
+
+ // initialize fields
+ vol->phys_blocksize = 512;
+ vol->log_blocksize = 512;
+ vol->label.type = FSW_STRING_TYPE_EMPTY;
+ vol->host_data = host_data;
+ vol->host_table = host_table;
+ vol->fstype_table = fstype_table;
+ vol->host_string_type = host_table->native_string_type;
+
+ // let the fs driver mount the file system
+ status = vol->fstype_table->volume_mount(vol);
+ if (status)
+ goto errorexit;
+
+ /// @todo anything else?
+
+ *vol_out = vol;
+ return FSW_SUCCESS;
+
+errorexit:
+ fsw_unmount(vol);
+ return status;
+}
+
+/**
+ * Unmount a volume by releasing all memory associated with it. This function is
+ * called by the host driver when a volume is no longer needed. It is also called
+ * by the core after a failed mount to clean up any allocated memory.
+ *
+ * Note that all dnodes must have been released before calling this function.
+ */
+
+void fsw_unmount(struct fsw_volume *vol)
+{
+ if (vol->root)
+ fsw_dnode_release(vol->root);
+ /// @todo check that no other dnodes are still around
+
+ vol->fstype_table->volume_free(vol);
+
+ fsw_blockcache_free(vol);
+ fsw_strfree(&vol->label);
+ fsw_free(vol);
+}
+
+/**
+ * Get in-depth information on the volume. This function can be called by the host
+ * driver to get additional information on the volume.
+ */
+
+fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb)
+{
+ return vol->fstype_table->volume_stat(vol, sb);
+}
+
+/**
+ * Set the physical and logical block sizes of the volume. This functions is called by
+ * the file system driver to announce the block sizes it wants to use for accessing
+ * the disk (physical) and for addressing file contents (logical).
+ * Usually both sizes will be the same but there may be file systems that need to access
+ * metadata at a smaller block size than the allocation unit for files.
+ *
+ * Calling this function causes the block cache to be dropped. All pointers returned
+ * from fsw_block_get become invalid. This function should only be called while
+ * mounting the file system, not as a part of file access operations.
+ *
+ * Both sizes are measured in bytes, must be powers of 2, and must not be smaller
+ * than 512 bytes. The logical block size cannot be smaller than the physical block size.
+ */
+
+void fsw_set_blocksize(struct fsw_volume *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize)
+{
+ /// @todo Check the sizes. Both must be powers of 2. log_blocksize must not be smaller than
+ // phys_blocksize.
+
+ // drop core block cache if present
+ fsw_blockcache_free(vol);
+
+ // signal host driver to drop caches etc.
+ vol->host_table->change_blocksize(vol,
+ vol->phys_blocksize, vol->log_blocksize,
+ phys_blocksize, log_blocksize);
+
+ vol->phys_blocksize = phys_blocksize;
+ vol->log_blocksize = log_blocksize;
+}
+
+/**
+ * Get a block of data from the disk. This function is called by the file system driver
+ * or by core functions. It calls through to the host driver's device access routine.
+ * Given a physical block number, it reads the block into memory (or fetches it from the
+ * block cache) and returns the address of the memory buffer. The caller should provide
+ * an indication of how important the block is in the cache_level parameter. Blocks with
+ * a low level are purged first. Some suggestions for cache levels:
+ *
+ * - 0: File data
+ * - 1: Directory data, symlink data
+ * - 2: File system metadata
+ * - 3..5: File system metadata with a high rate of access
+ *
+ * If this function returns successfully, the returned data pointer is valid until the
+ * caller calls fsw_block_release.
+ */
+
+fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out)
+{
+ fsw_status_t status;
+ fsw_u32 i, discard_level, new_bcache_size;
+ struct fsw_blockcache *new_bcache;
+
+ /// @todo allow the host driver to do its own caching; just call through if
+ // the appropriate function pointers are set
+
+ if (cache_level > MAX_CACHE_LEVEL)
+ cache_level = MAX_CACHE_LEVEL;
+
+ // check block cache
+ for (i = 0; i < vol->bcache_size; i++) {
+ if (vol->bcache[i].phys_bno == phys_bno) {
+ // cache hit!
+ if (vol->bcache[i].cache_level < cache_level)
+ vol->bcache[i].cache_level = cache_level; // promote the entry
+ vol->bcache[i].refcount++;
+ *buffer_out = vol->bcache[i].data;
+ return FSW_SUCCESS;
+ }
+ }
+
+ // find a free entry in the cache table
+ for (i = 0; i < vol->bcache_size; i++) {
+ if (vol->bcache[i].phys_bno == FSW_INVALID_BNO)
+ break;
+ }
+ if (i >= vol->bcache_size) {
+ for (discard_level = 0; discard_level <= MAX_CACHE_LEVEL; discard_level++) {
+ for (i = 0; i < vol->bcache_size; i++) {
+ if (vol->bcache[i].refcount == 0 && vol->bcache[i].cache_level <= discard_level)
+ break;
+ }
+ if (i < vol->bcache_size)
+ break;
+ }
+ }
+ if (i >= vol->bcache_size) {
+ // enlarge / create the cache
+ if (vol->bcache_size < 16)
+ new_bcache_size = 16;
+ else
+ new_bcache_size = vol->bcache_size << 1;
+ status = fsw_alloc(new_bcache_size * sizeof(struct fsw_blockcache), &new_bcache);
+ if (status)
+ return status;
+ if (vol->bcache_size > 0)
+ fsw_memcpy(new_bcache, vol->bcache, vol->bcache_size * sizeof(struct fsw_blockcache));
+ for (i = vol->bcache_size; i < new_bcache_size; i++) {
+ new_bcache[i].refcount = 0;
+ new_bcache[i].cache_level = 0;
+ new_bcache[i].phys_bno = FSW_INVALID_BNO;
+ new_bcache[i].data = NULL;
+ }
+ i = vol->bcache_size;
+
+ // switch caches
+ if (vol->bcache != NULL)
+ fsw_free(vol->bcache);
+ vol->bcache = new_bcache;
+ vol->bcache_size = new_bcache_size;
+ }
+ vol->bcache[i].phys_bno = FSW_INVALID_BNO;
+
+ // read the data
+ if (vol->bcache[i].data == NULL) {
+ status = fsw_alloc(vol->phys_blocksize, &vol->bcache[i].data);
+ if (status)
+ return status;
+ }
+ status = vol->host_table->read_block(vol, phys_bno, vol->bcache[i].data);
+ if (status)
+ return status;
+
+ vol->bcache[i].phys_bno = phys_bno;
+ vol->bcache[i].cache_level = cache_level;
+ vol->bcache[i].refcount = 1;
+ *buffer_out = vol->bcache[i].data;
+ return FSW_SUCCESS;
+}
+
+/**
+ * Releases a disk block. This function must be called to release disk blocks returned
+ * from fsw_block_get.
+ */
+
+void fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer)
+{
+ fsw_u32 i;
+
+ /// @todo allow the host driver to do its own caching; just call through if
+ // the appropriate function pointers are set
+
+ // update block cache
+ for (i = 0; i < vol->bcache_size; i++) {
+ if (vol->bcache[i].phys_bno == phys_bno && vol->bcache[i].refcount > 0)
+ vol->bcache[i].refcount--;
+ }
+}
+
+/**
+ * Release the block cache. Called internally when changing block sizes and when
+ * unmounting the volume. It frees all data occupied by the generic block cache.
+ */
+
+static void fsw_blockcache_free(struct fsw_volume *vol)
+{
+ fsw_u32 i;
+
+ for (i = 0; i < vol->bcache_size; i++) {
+ if (vol->bcache[i].data != NULL)
+ fsw_free(vol->bcache[i].data);
+ }
+ if (vol->bcache != NULL) {
+ fsw_free(vol->bcache);
+ vol->bcache = NULL;
+ }
+ vol->bcache_size = 0;
+}
+
+/**
+ * Add a new dnode to the list of known dnodes. This internal function is used when a
+ * dnode is created to add it to the dnode list that is used to search for existing
+ * dnodes by id.
+ */
+
+static void fsw_dnode_register(struct fsw_volume *vol, struct fsw_dnode *dno)
+{
+ dno->next = vol->dnode_head;
+ if (vol->dnode_head != NULL)
+ vol->dnode_head->prev = dno;
+ dno->prev = NULL;
+ vol->dnode_head = dno;
+}
+
+/**
+ * Create a dnode representing the root directory. This function is called by the file system
+ * driver while mounting the file system. The root directory is special because it has no parent
+ * dnode, its name is defined to be empty, and its type is also fixed. Otherwise, this functions
+ * behaves in the same way as fsw_dnode_create.
+ */
+
+fsw_status_t fsw_dnode_create_root(struct fsw_volume *vol, fsw_u32 dnode_id, struct fsw_dnode **dno_out)
+{
+ fsw_status_t status;
+ struct fsw_dnode *dno;
+
+ // allocate memory for the structure
+ status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno);
+ if (status)
+ return status;
+
+ // fill the structure
+ dno->vol = vol;
+ dno->parent = NULL;
+ dno->dnode_id = dnode_id;
+ dno->type = FSW_DNODE_TYPE_DIR;
+ dno->refcount = 1;
+ dno->name.type = FSW_STRING_TYPE_EMPTY;
+ /// @todo instead, call a function to create an empty string in the native string type
+
+ fsw_dnode_register(vol, dno);
+
+ *dno_out = dno;
+ return FSW_SUCCESS;
+}
+
+/**
+ * Create a new dnode representing a file system object. This function is called by
+ * the file system driver in response to directory lookup or read requests. Note that
+ * if there already is a dnode with the given dnode_id on record, then no new object
+ * is created. Instead, the existing dnode is returned and its reference count
+ * increased. All other parameters are ignored in this case.
+ *
+ * The type passed into this function may be FSW_DNODE_TYPE_UNKNOWN. It is sufficient
+ * to fill the type field during the dnode_fill call.
+ *
+ * The name parameter must describe a string with the object's name. A copy will be
+ * stored in the dnode structure for future reference. The name will not be used to
+ * shortcut directory lookups, but may be used to reconstruct paths.
+ *
+ * If the function returns successfully, *dno_out contains a pointer to the dnode
+ * that must be released by the caller with fsw_dnode_release.
+ */
+
+fsw_status_t fsw_dnode_create(struct fsw_dnode *parent_dno, fsw_u32 dnode_id, int type,
+ struct fsw_string *name, struct fsw_dnode **dno_out)
+{
+ fsw_status_t status;
+ struct fsw_volume *vol = parent_dno->vol;
+ struct fsw_dnode *dno;
+
+ // check if we already have a dnode with the same id
+ for (dno = vol->dnode_head; dno; dno = dno->next) {
+ if (dno->dnode_id == dnode_id) {
+ fsw_dnode_retain(dno);
+ *dno_out = dno;
+ return FSW_SUCCESS;
+ }
+ }
+
+ // allocate memory for the structure
+ status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno);
+ if (status)
+ return status;
+
+ // fill the structure
+ dno->vol = vol;
+ dno->parent = parent_dno;
+ fsw_dnode_retain(dno->parent);
+ dno->dnode_id = dnode_id;
+ dno->type = type;
+ dno->refcount = 1;
+ status = fsw_strdup_coerce(&dno->name, vol->host_table->native_string_type, name);
+ if (status) {
+ fsw_free(dno);
+ return status;
+ }
+
+ fsw_dnode_register(vol, dno);
+
+ *dno_out = dno;
+ return FSW_SUCCESS;
+}
+
+/**
+ * Increases the reference count of a dnode. This must be balanced with
+ * fsw_dnode_release calls. Note that some dnode functions return a retained
+ * dnode pointer to their caller.
+ */
+
+void fsw_dnode_retain(struct fsw_dnode *dno)
+{
+ dno->refcount++;
+}
+
+/**
+ * Release a dnode pointer, deallocating it if this was the last reference.
+ * This function decrements the reference counter of the dnode. If the counter
+ * reaches zero, the dnode is freed. Since the parent dnode is released
+ * during that process, this function may cause it to be freed, too.
+ */
+
+void fsw_dnode_release(struct fsw_dnode *dno)
+{
+ struct fsw_volume *vol = dno->vol;
+ struct fsw_dnode *parent_dno;
+
+ dno->refcount--;
+
+ if (dno->refcount == 0) {
+ parent_dno = dno->parent;
+
+ // de-register from volume's list
+ if (dno->next)
+ dno->next->prev = dno->prev;
+ if (dno->prev)
+ dno->prev->next = dno->next;
+ if (vol->dnode_head == dno)
+ vol->dnode_head = dno->next;
+
+ // run fstype-specific cleanup
+ vol->fstype_table->dnode_free(vol, dno);
+
+ fsw_strfree(&dno->name);
+ fsw_free(dno);
+
+ // release our pointer to the parent, possibly deallocating it, too
+ if (parent_dno)
+ fsw_dnode_release(parent_dno);
+ }
+}
+
+/**
+ * Get full information about a dnode from disk. This function is called by the host
+ * driver as well as by the core functions. Some file systems defer reading full
+ * information on a dnode until it is actually needed (i.e. separation between
+ * directory and inode information). This function makes sure that all information
+ * is available in the dnode structure. The following fields may not have a correct
+ * value until fsw_dnode_fill has been called:
+ *
+ * type, size
+ */
+
+fsw_status_t fsw_dnode_fill(struct fsw_dnode *dno)
+{
+ /// @todo check a flag right here, call fstype's dnode_fill only once per dnode
+
+ return dno->vol->fstype_table->dnode_fill(dno->vol, dno);
+}
+
+/**
+ * Get extended information about a dnode. This function can be called by the host
+ * driver to get a full compliment of information about a dnode in addition to the
+ * fields of the fsw_dnode structure itself.
+ *
+ * Some data requires host-specific conversion to be useful (i.e. timestamps) and
+ * will be passed to callback functions instead of being written into the structure.
+ * These callbacks must be filled in by the caller.
+ */
+
+fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb)
+{
+ fsw_status_t status;
+
+ status = fsw_dnode_fill(dno);
+ if (status)
+ return status;
+
+ sb->used_bytes = 0;
+ status = dno->vol->fstype_table->dnode_stat(dno->vol, dno, sb);
+ if (!status && !sb->used_bytes)
+ sb->used_bytes = FSW_U64_DIV(dno->size + dno->vol->log_blocksize - 1, dno->vol->log_blocksize);
+ return status;
+}
+
+/**
+ * Lookup a directory entry by name. This function is called by the host driver.
+ * Given a directory dnode and a file name, it looks up the named entry in the
+ * directory.
+ *
+ * If the dnode is not a directory, the call will fail. The caller is responsible for
+ * resolving symbolic links before calling this function.
+ *
+ * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory
+ * entry. The caller must call fsw_dnode_release on it.
+ */
+
+fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno,
+ struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out)
+{
+ fsw_status_t status;
+
+ status = fsw_dnode_fill(dno);
+ if (status)
+ return status;
+ if (dno->type != FSW_DNODE_TYPE_DIR)
+ return FSW_UNSUPPORTED;
+
+ return dno->vol->fstype_table->dir_lookup(dno->vol, dno, lookup_name, child_dno_out);
+}
+
+/**
+ * Find a file system object by path. This function is called by the host driver.
+ * Given a directory dnode and a relative or absolute path, it walks the directory
+ * tree until it finds the target dnode. If an intermediate node turns out to be
+ * a symlink, it is resolved automatically. If the target node is a symlink, it
+ * is not resolved.
+ *
+ * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory
+ * entry. The caller must call fsw_dnode_release on it.
+ */
+
+fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno,
+ struct fsw_string *lookup_path, char separator,
+ struct fsw_dnode **child_dno_out)
+{
+ fsw_status_t status;
+ struct fsw_volume *vol = dno->vol;
+ struct fsw_dnode *child_dno = NULL;
+ struct fsw_string lookup_name;
+ struct fsw_string remaining_path;
+ int root_if_empty;
+
+ remaining_path = *lookup_path;
+ fsw_dnode_retain(dno);
+
+ // loop over the path
+ for (root_if_empty = 1; fsw_strlen(&remaining_path) > 0; root_if_empty = 0) {
+ // parse next path component
+ fsw_strsplit(&lookup_name, &remaining_path, separator);
+
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: split into %d '%s' and %d '%s'\n"),
+ lookup_name.len, lookup_name.data,
+ remaining_path.len, remaining_path.data));
+
+ if (fsw_strlen(&lookup_name) == 0) { // empty path component
+ if (root_if_empty)
+ child_dno = vol->root;
+ else
+ child_dno = dno;
+ fsw_dnode_retain(child_dno);
+
+ } else {
+ // do an actual directory lookup
+
+ // ensure we have full information
+ status = fsw_dnode_fill(dno);
+ if (status)
+ goto errorexit;
+
+ // resolve symlink if necessary
+ if (dno->type == FSW_DNODE_TYPE_SYMLINK) {
+ status = fsw_dnode_resolve(dno, &child_dno);
+ if (status)
+ goto errorexit;
+
+ // symlink target becomes the new dno
+ fsw_dnode_release(dno);
+ dno = child_dno; // is already retained
+ child_dno = NULL;
+
+ // ensure we have full information
+ status = fsw_dnode_fill(dno);
+ if (status)
+ goto errorexit;
+ }
+
+ // make sure we operate on a directory
+ if (dno->type != FSW_DNODE_TYPE_DIR) {
+ return FSW_UNSUPPORTED;
+ goto errorexit;
+ }
+
+ // check special paths
+ if (fsw_streq_cstr(&lookup_name, ".")) { // self directory
+ child_dno = dno;
+ fsw_dnode_retain(child_dno);
+
+ } else if (fsw_streq_cstr(&lookup_name, "..")) { // parent directory
+ if (dno->parent == NULL) {
+ // We cannot go up from the root directory. Caution: Certain apps like the EFI shell
+ // rely on this behaviour!
+ status = FSW_NOT_FOUND;
+ goto errorexit;
+ }
+ child_dno = dno->parent;
+ fsw_dnode_retain(child_dno);
+
+ } else {
+ // do an actual lookup
+ status = vol->fstype_table->dir_lookup(vol, dno, &lookup_name, &child_dno);
+ if (status)
+ goto errorexit;
+ }
+ }
+
+ // child_dno becomes the new dno
+ fsw_dnode_release(dno);
+ dno = child_dno; // is already retained
+ child_dno = NULL;
+
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: now at inode %d\n"), dno->dnode_id));
+ }
+
+ *child_dno_out = dno;
+ return FSW_SUCCESS;
+
+errorexit:
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: leaving with error %d\n"), status));
+ fsw_dnode_release(dno);
+ if (child_dno != NULL)
+ fsw_dnode_release(child_dno);
+ return status;
+}
+
+/**
+ * Get the next directory item in sequential order. This function is called by the
+ * host driver to read the complete contents of a directory in sequential (file system
+ * defined) order. Calling this function returns the next entry. Iteration state is
+ * kept by a shandle on the directory's dnode. The caller must set up the shandle
+ * when starting the iteration.
+ *
+ * When the end of the directory is reached, this function returns FSW_NOT_FOUND.
+ * If the function returns FSW_SUCCESS, *child_dno_out points to the next directory
+ * entry. The caller must call fsw_dnode_release on it.
+ */
+
+fsw_status_t fsw_dnode_dir_read(struct fsw_shandle *shand, struct fsw_dnode **child_dno_out)
+{
+ fsw_status_t status;
+ struct fsw_dnode *dno = shand->dnode;
+ fsw_u64 saved_pos;
+
+ if (dno->type != FSW_DNODE_TYPE_DIR)
+ return FSW_UNSUPPORTED;
+
+ saved_pos = shand->pos;
+ status = dno->vol->fstype_table->dir_read(dno->vol, dno, shand, child_dno_out);
+ if (status)
+ shand->pos = saved_pos;
+ return status;
+}
+
+/**
+ * Read the target path of a symbolic link. This function is called by the host driver
+ * to read the "content" of a symbolic link, that is the relative or absolute path
+ * it points to.
+ *
+ * If the function returns FSW_SUCCESS, the string handle provided by the caller is
+ * filled with a string in the host's preferred encoding. The caller is responsible
+ * for calling fsw_strfree on the string.
+ */
+
+fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *target_name)
+{
+ fsw_status_t status;
+
+ status = fsw_dnode_fill(dno);
+ if (status)
+ return status;
+ if (dno->type != FSW_DNODE_TYPE_SYMLINK)
+ return FSW_UNSUPPORTED;
+
+ return dno->vol->fstype_table->readlink(dno->vol, dno, target_name);
+}
+
+/**
+ * Read the target path of a symbolic link by accessing file data. This function can
+ * be called by the file system driver if the file system stores the target path
+ * as normal file data. This function will open an shandle, read the whole content
+ * of the file into a buffer, and build a string from that. Currently the encoding
+ * for the string is fixed as FSW_STRING_TYPE_ISO88591.
+ *
+ * If the function returns FSW_SUCCESS, the string handle provided by the caller is
+ * filled with a string in the host's preferred encoding. The caller is responsible
+ * for calling fsw_strfree on the string.
+ */
+
+fsw_status_t fsw_dnode_readlink_data(struct fsw_dnode *dno, struct fsw_string *link_target)
+{
+ fsw_status_t status;
+ struct fsw_shandle shand;
+ fsw_u32 buffer_size;
+ char buffer[FSW_PATH_MAX];
+
+ struct fsw_string s;
+
+ if (dno->size > FSW_PATH_MAX)
+ return FSW_VOLUME_CORRUPTED;
+
+ s.type = FSW_STRING_TYPE_ISO88591;
+ s.size = s.len = (int)dno->size;
+ s.data = buffer;
+
+ // open shandle and read the data
+ status = fsw_shandle_open(dno, &shand);
+ if (status)
+ return status;
+ buffer_size = (fsw_u32)s.size;
+ status = fsw_shandle_read(&shand, &buffer_size, buffer);
+ fsw_shandle_close(&shand);
+ if (status)
+ return status;
+ if ((int)buffer_size < s.size)
+ return FSW_VOLUME_CORRUPTED;
+
+ status = fsw_strdup_coerce(link_target, dno->vol->host_string_type, &s);
+ return status;
+}
+
+/**
+ * Resolve a symbolic link. This function can be called by the host driver to make
+ * sure the a dnode is fully resolved instead of pointing at a symlink. If the dnode
+ * passed in is not a symlink, it is returned unmodified.
+ *
+ * Note that absolute paths will be resolved relative to the root directory of the
+ * volume. If the host is an operating system with its own VFS layer, it should
+ * resolve symlinks on its own.
+ *
+ * If the function returns FSW_SUCCESS, *target_dno_out points at a dnode that is
+ * not a symlink. The caller is responsible for calling fsw_dnode_release on it.
+ */
+
+fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out)
+{
+ fsw_status_t status;
+ struct fsw_string target_name;
+ struct fsw_dnode *target_dno;
+
+ fsw_dnode_retain(dno);
+
+ while (1) {
+ // get full information
+ status = fsw_dnode_fill(dno);
+ if (status)
+ goto errorexit;
+ if (dno->type != FSW_DNODE_TYPE_SYMLINK) {
+ // found a non-symlink target, return it
+ *target_dno_out = dno;
+ return FSW_SUCCESS;
+ }
+ if (dno->parent == NULL) { // safety measure, cannot happen in theory
+ status = FSW_NOT_FOUND;
+ goto errorexit;
+ }
+
+ // read the link's target
+ status = fsw_dnode_readlink(dno, &target_name);
+ if (status)
+ goto errorexit;
+
+ // resolve it
+ status = fsw_dnode_lookup_path(dno->parent, &target_name, '/', &target_dno);
+ fsw_strfree(&target_name);
+ if (status)
+ goto errorexit;
+
+ // target_dno becomes the new dno
+ fsw_dnode_release(dno);
+ dno = target_dno; // is already retained
+ }
+
+errorexit:
+ fsw_dnode_release(dno);
+ return status;
+}
+
+/**
+ * Set up a shandle (storage handle) to access a file's data. This function is called
+ * by the host driver and by the core when they need to access a file's data. It is also
+ * used in accessing the raw data of directories and symlinks if the file system uses
+ * the same mechanisms for storing the data of those items.
+ *
+ * The storage for the fsw_shandle structure is provided by the caller. The dnode and pos
+ * fields may be accessed, pos may also be written to to set the file pointer. The file's
+ * data size is available as shand->dnode->size.
+ *
+ * If this function returns FSW_SUCCESS, the caller must call fsw_shandle_close to release
+ * the dnode reference held by the shandle.
+ */
+
+fsw_status_t fsw_shandle_open(struct fsw_dnode *dno, struct fsw_shandle *shand)
+{
+ fsw_status_t status;
+ struct fsw_volume *vol = dno->vol;
+
+ // read full dnode information into memory
+ status = vol->fstype_table->dnode_fill(vol, dno);
+ if (status)
+ return status;
+
+ // setup shandle
+ fsw_dnode_retain(dno);
+
+ shand->dnode = dno;
+ shand->pos = 0;
+ shand->extent.type = FSW_EXTENT_TYPE_INVALID;
+
+ return FSW_SUCCESS;
+}
+
+/**
+ * Close a shandle after accessing the dnode's data. This function is called by the host
+ * driver or core functions when they are finished with accessing a file's data. It
+ * releases the dnode reference and frees any buffers associated with the shandle itself.
+ * The dnode is only released if this was the last reference using it.
+ */
+
+void fsw_shandle_close(struct fsw_shandle *shand)
+{
+ if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER)
+ fsw_free(shand->extent.buffer);
+ fsw_dnode_release(shand->dnode);
+}
+
+/**
+ * Read data from a shandle (storage handle for a dnode). This function is called by the
+ * host driver or internally when data is read from a file. TODO: more
+ */
+
+fsw_status_t fsw_shandle_read(struct fsw_shandle *shand, fsw_u32 *buffer_size_inout, void *buffer_in)
+{
+ fsw_status_t status;
+ struct fsw_dnode *dno = shand->dnode;
+ struct fsw_volume *vol = dno->vol;
+ fsw_u8 *buffer, *block_buffer;
+ fsw_u32 buflen, copylen, pos;
+ fsw_u32 log_bno, pos_in_extent, phys_bno, pos_in_physblock;
+ fsw_u32 cache_level;
+
+ if (shand->pos >= dno->size) { // already at EOF
+ *buffer_size_inout = 0;
+ return FSW_SUCCESS;
+ }
+
+ // initialize vars
+ buffer = buffer_in;
+ buflen = *buffer_size_inout;
+ pos = (fsw_u32)shand->pos;
+ cache_level = (dno->type != FSW_DNODE_TYPE_FILE) ? 1 : 0;
+ // restrict read to file size
+ if (buflen > dno->size - pos)
+ buflen = (fsw_u32)(dno->size - pos);
+
+ while (buflen > 0) {
+ // get extent for the current logical block
+ log_bno = pos / vol->log_blocksize;
+ if (shand->extent.type == FSW_EXTENT_TYPE_INVALID ||
+ log_bno < shand->extent.log_start ||
+ log_bno >= shand->extent.log_start + shand->extent.log_count) {
+
+ if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER)
+ fsw_free(shand->extent.buffer);
+
+ // ask the file system for the proper extent
+ shand->extent.log_start = log_bno;
+ status = vol->fstype_table->get_extent(vol, dno, &shand->extent);
+ if (status) {
+ shand->extent.type = FSW_EXTENT_TYPE_INVALID;
+ return status;
+ }
+ }
+
+ pos_in_extent = pos - shand->extent.log_start * vol->log_blocksize;
+
+ // dispatch by extent type
+ if (shand->extent.type == FSW_EXTENT_TYPE_PHYSBLOCK) {
+ // convert to physical block number and offset
+ phys_bno = shand->extent.phys_start + pos_in_extent / vol->phys_blocksize;
+ pos_in_physblock = pos_in_extent & (vol->phys_blocksize - 1);
+ copylen = vol->phys_blocksize - pos_in_physblock;
+ if (copylen > buflen)
+ copylen = buflen;
+
+ // get one physical block
+ status = fsw_block_get(vol, phys_bno, cache_level, (void **)&block_buffer);
+ if (status)
+ return status;
+
+ // copy data from it
+ fsw_memcpy(buffer, block_buffer + pos_in_physblock, copylen);
+ fsw_block_release(vol, phys_bno, block_buffer);
+
+ } else if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER) {
+ copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent;
+ if (copylen > buflen)
+ copylen = buflen;
+ fsw_memcpy(buffer, (fsw_u8 *)shand->extent.buffer + pos_in_extent, copylen);
+
+ } else { // _SPARSE or _INVALID
+ copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent;
+ if (copylen > buflen)
+ copylen = buflen;
+ fsw_memzero(buffer, copylen);
+
+ }
+
+ buffer += copylen;
+ buflen -= copylen;
+ pos += copylen;
+ }
+
+ *buffer_size_inout = (fsw_u32)(pos - shand->pos);
+ shand->pos = pos;
+
+ return FSW_SUCCESS;
+}
+
+// EOF
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.h
new file mode 100644
index 00000000..4bcb1416
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.h
@@ -0,0 +1,554 @@
+/* $Id: fsw_core.h $ */
+/** @file
+ * fsw_core.h - Core file system wrapper abstraction layer header.
+ */
+
+/*
+ * Copyright (C) 2010-2022 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
+ * Portions Copyright (c) The Regents of the University of California.
+ * Portions Copyright (c) UNIX System Laboratories, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _FSW_CORE_H_
+#define _FSW_CORE_H_
+
+#include "fsw_base.h"
+
+
+/** Maximum size for a path, specifically symlink target paths. */
+#ifndef VBOX
+#define FSW_PATH_MAX (4096)
+#else
+/* Too big allocations are handled with alloca() */
+#define FSW_PATH_MAX (2048)
+#endif
+
+/** Helper macro for token concatenation. */
+#define FSW_CONCAT3(a,b,c) a##b##c
+/** Expands to the name of a fstype dispatch table (fsw_fstype_table) for a named file system type. */
+#define FSW_FSTYPE_TABLE_NAME(t) FSW_CONCAT3(fsw_,t,_table)
+
+/** Indicates that the block cache entry is empty. */
+#define FSW_INVALID_BNO (~0U)
+
+
+//
+// Byte-swapping macros
+//
+
+
+/**
+ * \name Byte Order Macros
+ * Implements big endian vs. little endian awareness and conversion.
+ */
+/*@{*/
+
+typedef fsw_u16 fsw_u16_le;
+typedef fsw_u16 fsw_u16_be;
+typedef fsw_u32 fsw_u32_le;
+typedef fsw_u32 fsw_u32_be;
+typedef fsw_u64 fsw_u64_le;
+typedef fsw_u64 fsw_u64_be;
+
+#define FSW_SWAPVALUE_U16(v) ((((fsw_u16)(v) & 0xff00) >> 8) | \
+ (((fsw_u16)(v) & 0x00ff) << 8))
+#define FSW_SWAPVALUE_U32(v) ((((fsw_u32)(v) & 0xff000000UL) >> 24) | \
+ (((fsw_u32)(v) & 0x00ff0000UL) >> 8) | \
+ (((fsw_u32)(v) & 0x0000ff00UL) << 8) | \
+ (((fsw_u32)(v) & 0x000000ffUL) << 24))
+#define FSW_SWAPVALUE_U64(v) ((((fsw_u64)(v) & 0xff00000000000000ULL) >> 56) | \
+ (((fsw_u64)(v) & 0x00ff000000000000ULL) >> 40) | \
+ (((fsw_u64)(v) & 0x0000ff0000000000ULL) >> 24) | \
+ (((fsw_u64)(v) & 0x000000ff00000000ULL) >> 8) | \
+ (((fsw_u64)(v) & 0x00000000ff000000ULL) << 8) | \
+ (((fsw_u64)(v) & 0x0000000000ff0000ULL) << 24) | \
+ (((fsw_u64)(v) & 0x000000000000ff00ULL) << 40) | \
+ (((fsw_u64)(v) & 0x00000000000000ffULL) << 56))
+
+#ifdef FSW_LITTLE_ENDIAN
+
+#define fsw_u16_le_swap(v) (v)
+#define fsw_u16_be_swap(v) FSW_SWAPVALUE_U16(v)
+#define fsw_u32_le_swap(v) (v)
+#define fsw_u32_be_swap(v) FSW_SWAPVALUE_U32(v)
+#define fsw_u64_le_swap(v) (v)
+#define fsw_u64_be_swap(v) FSW_SWAPVALUE_U64(v)
+
+#define fsw_u16_le_sip(var)
+#define fsw_u16_be_sip(var) (var = FSW_SWAPVALUE_U16(var))
+#define fsw_u32_le_sip(var)
+#define fsw_u32_be_sip(var) (var = FSW_SWAPVALUE_U32(var))
+#define fsw_u64_le_sip(var)
+#define fsw_u64_be_sip(var) (var = FSW_SWAPVALUE_U64(var))
+
+#else
+#ifdef FSW_BIG_ENDIAN
+
+#define fsw_u16_le_swap(v) FSW_SWAPVALUE_U16(v)
+#define fsw_u16_be_swap(v) (v)
+#define fsw_u32_le_swap(v) FSW_SWAPVALUE_U32(v)
+#define fsw_u32_be_swap(v) (v)
+#define fsw_u64_le_swap(v) FSW_SWAPVALUE_U64(v)
+#define fsw_u64_be_swap(v) (v)
+
+#define fsw_u16_le_sip(var) (var = FSW_SWAPVALUE_U16(var))
+#define fsw_u16_be_sip(var)
+#define fsw_u32_le_sip(var) (var = FSW_SWAPVALUE_U32(var))
+#define fsw_u32_be_sip(var)
+#define fsw_u64_le_sip(var) (var = FSW_SWAPVALUE_U64(var))
+#define fsw_u64_be_sip(var)
+
+#else
+#fail Neither FSW_BIG_ENDIAN nor FSW_LITTLE_ENDIAN are defined
+#endif
+#endif
+
+/*@}*/
+
+
+//
+// The following evil hack avoids a lot of casts between generic and fstype-specific
+// structures.
+//
+
+#ifndef VOLSTRUCTNAME
+#define VOLSTRUCTNAME fsw_volume
+#else
+struct VOLSTRUCTNAME;
+#endif
+#ifndef DNODESTRUCTNAME
+#define DNODESTRUCTNAME fsw_dnode
+#else
+struct DNODESTRUCTNAME;
+#endif
+
+
+/**
+ * Status code type, returned from all functions that can fail.
+ */
+typedef int fsw_status_t;
+
+/**
+ * Possible status codes.
+ */
+enum {
+ FSW_SUCCESS,
+ FSW_OUT_OF_MEMORY,
+ FSW_IO_ERROR,
+ FSW_UNSUPPORTED,
+ FSW_NOT_FOUND,
+ FSW_VOLUME_CORRUPTED,
+ FSW_UNKNOWN_ERROR
+};
+
+
+/**
+ * Core: A string with explicit length and encoding information.
+ */
+
+struct fsw_string {
+ int type; //!< Encoding of the string - empty, ISO-8859-1, UTF8, UTF16
+ int len; //!< Length in characters
+ int size; //!< Total data size in bytes
+ void *data; //!< Data pointer (may be NULL if type is EMPTY or len is zero)
+};
+
+/**
+ * Possible string types / encodings. In the case of FSW_STRING_TYPE_EMPTY,
+ * all other members of the fsw_string structure may be invalid.
+ */
+enum {
+ FSW_STRING_TYPE_EMPTY,
+ FSW_STRING_TYPE_ISO88591,
+ FSW_STRING_TYPE_UTF8,
+ FSW_STRING_TYPE_UTF16,
+ FSW_STRING_TYPE_UTF16_SWAPPED
+};
+
+#ifdef FSW_LITTLE_ENDIAN
+#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16
+#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16_SWAPPED
+#else
+#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16_SWAPPED
+#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16
+#endif
+
+/** Static initializer for an empty string. */
+#define FSW_STRING_INIT { FSW_STRING_TYPE_EMPTY, 0, 0, NULL }
+
+
+/* forward declarations */
+
+struct fsw_dnode;
+struct fsw_host_table;
+struct fsw_fstype_table;
+
+struct fsw_blockcache {
+ fsw_u32 refcount; //!< Reference count
+ fsw_u32 cache_level; //!< Level of importance of this block
+ fsw_u32 phys_bno; //!< Physical block number
+ void *data; //!< Block data buffer
+};
+
+/**
+ * Core: Represents a mounted volume.
+ */
+
+struct fsw_volume {
+ fsw_u32 phys_blocksize; //!< Block size for disk access / file system structures
+ fsw_u32 log_blocksize; //!< Block size for logical file data
+
+ struct DNODESTRUCTNAME *root; //!< Root directory dnode
+ struct fsw_string label; //!< Volume label
+
+ struct fsw_dnode *dnode_head; //!< List of all dnodes allocated for this volume
+
+ struct fsw_blockcache *bcache; //!< Array of block cache entries
+ fsw_u32 bcache_size; //!< Number of entries in the block cache array
+
+ void *host_data; //!< Hook for a host-specific data structure
+ struct fsw_host_table *host_table; //!< Dispatch table for host-specific functions
+ struct fsw_fstype_table *fstype_table; //!< Dispatch table for file system specific functions
+ int host_string_type; //!< String type used by the host environment
+};
+
+/**
+ * Core: Represents a "directory node" - a file, directory, symlink, whatever.
+ */
+
+struct fsw_dnode {
+ fsw_u32 refcount; //!< Reference count
+
+ struct VOLSTRUCTNAME *vol; //!< The volume this dnode belongs to
+ struct DNODESTRUCTNAME *parent; //!< Parent directory dnode
+ struct fsw_string name; //!< Name of this item in the parent directory
+
+ fsw_u32 dnode_id; //!< Unique id number (usually the inode number)
+ int type; //!< Type of the dnode - file, dir, symlink, special
+ fsw_u64 size; //!< Data size in bytes
+
+ struct fsw_dnode *next; //!< Doubly-linked list of all dnodes: previous dnode
+ struct fsw_dnode *prev; //!< Doubly-linked list of all dnodes: next dnode
+};
+
+/**
+ * Possible dnode types. FSW_DNODE_TYPE_UNKNOWN may only be used before
+ * fsw_dnode_fill has been called on the dnode.
+ */
+enum {
+ FSW_DNODE_TYPE_UNKNOWN,
+ FSW_DNODE_TYPE_FILE,
+ FSW_DNODE_TYPE_DIR,
+ FSW_DNODE_TYPE_SYMLINK,
+ FSW_DNODE_TYPE_SPECIAL
+};
+
+/**
+ * Core: Stores the mapping of a region of a file to the data on disk.
+ */
+
+struct fsw_extent {
+ int type; //!< Type of extent specification
+ fsw_u32 log_start; //!< Starting logical block number
+ fsw_u32 log_count; //!< Logical block count
+ fsw_u32 phys_start; //!< Starting physical block number (for FSW_EXTENT_TYPE_PHYSBLOCK only)
+ void *buffer; //!< Allocated buffer pointer (for FSW_EXTENT_TYPE_BUFFER only)
+};
+
+/**
+ * Possible extent representation types. FSW_EXTENT_TYPE_INVALID is for shandle's
+ * internal use only, it must not be returned from a get_extent function.
+ */
+enum {
+ FSW_EXTENT_TYPE_INVALID,
+ FSW_EXTENT_TYPE_SPARSE,
+ FSW_EXTENT_TYPE_PHYSBLOCK,
+ FSW_EXTENT_TYPE_BUFFER
+};
+
+/**
+ * Core: An access structure to a dnode's raw data. There can be multiple
+ * shandles per dnode, each of them has its own position pointer.
+ */
+
+struct fsw_shandle {
+ struct fsw_dnode *dnode; //!< The dnode this handle reads data from
+
+ fsw_u64 pos; //!< Current file pointer in bytes
+ struct fsw_extent extent; //!< Current extent
+};
+
+/**
+ * Core: Used in gathering detailed information on a volume.
+ */
+
+struct fsw_volume_stat {
+ fsw_u64 total_bytes; //!< Total size of data area size in bytes
+ fsw_u64 free_bytes; //!< Bytes still available for storing file data
+};
+
+/**
+ * Core: Used in gathering detailed information on a dnode.
+ */
+
+struct fsw_dnode_stat {
+ fsw_u64 used_bytes; //!< Bytes actually used by the file on disk
+ void (*store_time_posix)(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time); //!< Callback for storing a Posix-style timestamp
+ void (*store_attr_posix)(struct fsw_dnode_stat *sb, fsw_u16 posix_mode); //!< Callback for storing a Posix-style file mode
+ void *host_data; //!< Hook for a host-specific data structure
+};
+
+/**
+ * Type of the timestamp passed into store_time_posix.
+ */
+enum {
+ FSW_DNODE_STAT_CTIME,
+ FSW_DNODE_STAT_MTIME,
+ FSW_DNODE_STAT_ATIME
+};
+
+/**
+ * Core: Function table for a host environment.
+ */
+
+struct fsw_host_table
+{
+ int native_string_type; //!< String type used by the host environment
+
+ void (*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 (*read_block)(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer);
+};
+
+/**
+ * Core: Function table for a file system driver.
+ */
+
+struct fsw_fstype_table
+{
+ struct fsw_string name; //!< String giving the name of the file system
+ fsw_u32 volume_struct_size; //!< Size for allocating the fsw_volume structure
+ fsw_u32 dnode_struct_size; //!< Size for allocating the fsw_dnode structure
+
+ fsw_status_t (*volume_mount)(struct VOLSTRUCTNAME *vol);
+ void (*volume_free)(struct VOLSTRUCTNAME *vol);
+ fsw_status_t (*volume_stat)(struct VOLSTRUCTNAME *vol, struct fsw_volume_stat *sb);
+
+ fsw_status_t (*dnode_fill)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno);
+ void (*dnode_free)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno);
+ fsw_status_t (*dnode_stat)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+ struct fsw_dnode_stat *sb);
+ fsw_status_t (*get_extent)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+ struct fsw_extent *extent);
+
+ fsw_status_t (*dir_lookup)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+ struct fsw_string *lookup_name, struct DNODESTRUCTNAME **child_dno);
+ fsw_status_t (*dir_read)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+ struct fsw_shandle *shand, struct DNODESTRUCTNAME **child_dno);
+ fsw_status_t (*readlink)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno,
+ struct fsw_string *link_target);
+};
+
+
+/**
+ * \name Volume Functions
+ */
+/*@{*/
+
+fsw_status_t fsw_mount(void *host_data,
+ struct fsw_host_table *host_table,
+ struct fsw_fstype_table *fstype_table,
+ struct fsw_volume **vol_out);
+void fsw_unmount(struct fsw_volume *vol);
+fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb);
+
+void fsw_set_blocksize(struct VOLSTRUCTNAME *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize);
+fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out);
+void fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer);
+
+/*@}*/
+
+
+/**
+ * \name dnode Functions
+ */
+/*@{*/
+
+fsw_status_t fsw_dnode_create_root(struct VOLSTRUCTNAME *vol, fsw_u32 dnode_id, struct DNODESTRUCTNAME **dno_out);
+fsw_status_t fsw_dnode_create(struct DNODESTRUCTNAME *parent_dno, fsw_u32 dnode_id, int type,
+ struct fsw_string *name, struct DNODESTRUCTNAME **dno_out);
+void fsw_dnode_retain(struct fsw_dnode *dno);
+void fsw_dnode_release(struct fsw_dnode *dno);
+
+fsw_status_t fsw_dnode_fill(struct fsw_dnode *dno);
+fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb);
+
+fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno,
+ struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out);
+fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno,
+ struct fsw_string *lookup_path, char separator,
+ struct fsw_dnode **child_dno_out);
+fsw_status_t fsw_dnode_dir_read(struct fsw_shandle *shand, struct fsw_dnode **child_dno_out);
+fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *link_target);
+fsw_status_t fsw_dnode_readlink_data(struct DNODESTRUCTNAME *dno, struct fsw_string *link_target);
+fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out);
+
+/*@}*/
+
+
+/**
+ * \name shandle Functions
+ */
+/*@{*/
+
+fsw_status_t fsw_shandle_open(struct DNODESTRUCTNAME *dno, struct fsw_shandle *shand);
+void fsw_shandle_close(struct fsw_shandle *shand);
+fsw_status_t fsw_shandle_read(struct fsw_shandle *shand, fsw_u32 *buffer_size_inout, void *buffer);
+
+/*@}*/
+
+
+/**
+ * \name Memory Functions
+ */
+/*@{*/
+
+fsw_status_t fsw_alloc_zero(int len, void **ptr_out);
+fsw_status_t fsw_memdup(void **dest_out, void *src, int len);
+
+/*@}*/
+
+
+/**
+ * \name String Functions
+ */
+/*@{*/
+
+int fsw_strlen(struct fsw_string *s);
+int fsw_streq(struct fsw_string *s1, struct fsw_string *s2);
+int fsw_streq_cstr(struct fsw_string *s1, const char *s2);
+fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src);
+void fsw_strsplit(struct fsw_string *lookup_name, struct fsw_string *buffer, char separator);
+
+void fsw_strfree(struct fsw_string *s);
+fsw_u16 fsw_to_lower(fsw_u16 ch);
+
+/*@}*/
+
+
+/**
+ * \name Posix Mode Macros
+ * These macros can be used globally to test fields and bits in
+ * Posix-style modes.
+ *
+ * Taken from FreeBSD sys/stat.h.
+ */
+/*@{*/
+#ifndef S_IRWXU
+
+#define S_ISUID 0004000 /* set user id on execution */
+#define S_ISGID 0002000 /* set group id on execution */
+#define S_ISTXT 0001000 /* sticky bit */
+
+#define S_IRWXU 0000700 /* RWX mask for owner */
+#define S_IRUSR 0000400 /* R for owner */
+#define S_IWUSR 0000200 /* W for owner */
+#define S_IXUSR 0000100 /* X for owner */
+
+#define S_IRWXG 0000070 /* RWX mask for group */
+#define S_IRGRP 0000040 /* R for group */
+#define S_IWGRP 0000020 /* W for group */
+#define S_IXGRP 0000010 /* X for group */
+
+#define S_IRWXO 0000007 /* RWX mask for other */
+#define S_IROTH 0000004 /* R for other */
+#define S_IWOTH 0000002 /* W for other */
+#define S_IXOTH 0000001 /* X for other */
+
+#define S_IFMT 0170000 /* type of file mask */
+#define S_IFIFO 0010000 /* named pipe (fifo) */
+#define S_IFCHR 0020000 /* character special */
+#define S_IFDIR 0040000 /* directory */
+#define S_IFBLK 0060000 /* block special */
+#define S_IFREG 0100000 /* regular */
+#define S_IFLNK 0120000 /* symbolic link */
+#define S_IFSOCK 0140000 /* socket */
+#define S_ISVTX 0001000 /* save swapped text even after use */
+#define S_IFWHT 0160000 /* whiteout */
+
+#define S_ISDIR(m) (((m) & 0170000) == 0040000) /* directory */
+#define S_ISCHR(m) (((m) & 0170000) == 0020000) /* char special */
+#define S_ISBLK(m) (((m) & 0170000) == 0060000) /* block special */
+#define S_ISREG(m) (((m) & 0170000) == 0100000) /* regular file */
+#define S_ISFIFO(m) (((m) & 0170000) == 0010000) /* fifo or socket */
+#define S_ISLNK(m) (((m) & 0170000) == 0120000) /* symbolic link */
+#define S_ISSOCK(m) (((m) & 0170000) == 0140000) /* socket */
+#define S_ISWHT(m) (((m) & 0170000) == 0160000) /* whiteout */
+
+#define S_BLKSIZE 512 /* block size used in the stat struct */
+
+#endif
+/*@}*/
+
+
+#endif
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..6468aa0c
--- /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-2022 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
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.h
new file mode 100644
index 00000000..d081552b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi.h
@@ -0,0 +1,133 @@
+/* $Id: fsw_efi.h $ */
+/** @file
+ * fsw_efi.h - EFI host environment header.
+ */
+
+/*
+ * Copyright (C) 2010-2022 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.
+ */
+
+#ifndef _FSW_EFI_H_
+#define _FSW_EFI_H_
+
+#include "fsw_core.h"
+
+
+/**
+ * EFI Host: Private per-volume structure.
+ */
+
+typedef struct {
+ UINT64 Signature; //!< Used to identify this structure
+
+ EFI_FILE_IO_INTERFACE FileSystem; //!< Published EFI protocol interface structure
+
+ EFI_HANDLE Handle; //!< The device handle the protocol is attached to
+ EFI_DISK_IO *DiskIo; //!< The Disk I/O protocol we use for disk access
+ UINT32 MediaId; //!< The media ID from the Block I/O protocol
+ EFI_STATUS LastIOStatus; //!< Last status from Disk I/O
+
+ struct fsw_volume *vol; //!< FSW volume structure
+
+} FSW_VOLUME_DATA;
+
+/** Signature for the volume structure. */
+#define FSW_VOLUME_DATA_SIGNATURE EFI_SIGNATURE_32 ('f', 's', 'w', 'V')
+/** Access macro for the volume structure. */
+#define FSW_VOLUME_FROM_FILE_SYSTEM(a) CR (a, FSW_VOLUME_DATA, FileSystem, FSW_VOLUME_DATA_SIGNATURE)
+
+/**
+ * EFI Host: Private structure for a EFI_FILE interface.
+ */
+
+typedef struct {
+ UINT64 Signature; //!< Used to identify this structure
+
+ EFI_FILE FileHandle; //!< Published EFI protocol interface structure
+
+ UINTN Type; //!< File type used for dispatching
+ struct fsw_shandle shand; //!< FSW handle for this file
+
+} FSW_FILE_DATA;
+
+/** File type: regular file. */
+#define FSW_EFI_FILE_TYPE_FILE (0)
+/** File type: directory. */
+#define FSW_EFI_FILE_TYPE_DIR (1)
+
+/** Signature for the file handle structure. */
+#define FSW_FILE_DATA_SIGNATURE EFI_SIGNATURE_32 ('f', 's', 'w', 'F')
+/** Access macro for the file handle structure. */
+#define FSW_FILE_FROM_FILE_HANDLE(a) CR (a, FSW_FILE_DATA, FileHandle, FSW_FILE_DATA_SIGNATURE)
+
+
+//
+// Library functions
+//
+
+VOID fsw_efi_decode_time(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime);
+
+UINTN fsw_efi_strsize(struct fsw_string *s);
+VOID fsw_efi_strcpy(CHAR16 *Dest, struct fsw_string *src);
+
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_base.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_base.h
new file mode 100644
index 00000000..758f11b4
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_base.h
@@ -0,0 +1,115 @@
+/* $Id: fsw_efi_base.h $ */
+/** @file
+ * fsw_efi_base.h - Base definitions for the EFI host environment.
+ */
+
+/*
+ * Copyright (C) 2010-2022 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.
+ */
+
+#ifndef _FSW_EFI_BASE_H_
+#define _FSW_EFI_BASE_H_
+
+#ifndef VBOX
+#include <efi.h>
+#include <efilib.h>
+#define PROTO_NAME(x) x
+#endif
+
+#define FSW_LITTLE_ENDIAN (1)
+
+
+// types, reuse EFI types
+
+typedef INT8 fsw_s8;
+typedef UINT8 fsw_u8;
+typedef INT16 fsw_s16;
+typedef UINT16 fsw_u16;
+typedef INT32 fsw_s32;
+typedef UINT32 fsw_u32;
+typedef INT64 fsw_s64;
+typedef UINT64 fsw_u64;
+
+
+// allocation functions
+
+#define fsw_alloc(size, ptrptr) (((*(ptrptr) = AllocatePool(size)) == NULL) ? FSW_OUT_OF_MEMORY : FSW_SUCCESS)
+#define fsw_free(ptr) FreePool(ptr)
+
+// memory functions
+
+#define fsw_memzero(dest,size) ZeroMem(dest,size)
+#define fsw_memcpy(dest,src,size) CopyMem(dest,src,size)
+#define fsw_memeq(p1,p2,size) (CompareMem(p1,p2,size) == 0)
+
+// message printing
+
+#define FSW_MSGSTR(s) DEBUG_INFO, s
+#define FSW_MSGFUNC DebugPrint
+
+// 64-bit hooks
+
+#define FSW_U64_SHR(val,shiftbits) RShiftU64((val), (shiftbits))
+#define FSW_U64_DIV(val,divisor) DivU64x32((val), (divisor), NULL)
+
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_lib.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_lib.c
new file mode 100644
index 00000000..25eeef3e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_efi_lib.c
@@ -0,0 +1,179 @@
+/* $Id: fsw_efi_lib.c $ */
+/** @file
+ * fsw_efi_lib.c - EFI host environment library functions.
+ */
+
+/*
+ * Copyright (C) 2010-2022 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"
+
+
+//
+// time conversion
+//
+// Adopted from public domain code in FreeBSD libc.
+//
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+VOID fsw_efi_decode_time(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime)
+{
+ long days, rem;
+ int y, newy, yleap;
+ const int *ip;
+
+ ZeroMem(EfiTime, sizeof(EFI_TIME));
+
+ days = UnixTime / SECSPERDAY;
+ rem = UnixTime % SECSPERDAY;
+
+ EfiTime->Hour = (UINT8) (rem / SECSPERHOUR);
+ rem = rem % SECSPERHOUR;
+ EfiTime->Minute = (UINT8) (rem / SECSPERMIN);
+ EfiTime->Second = (UINT8) (rem % SECSPERMIN);
+
+ y = EPOCH_YEAR;
+ while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
+ newy = y + days / DAYSPERNYEAR;
+ if (days < 0)
+ --newy;
+ days -= (newy - y) * DAYSPERNYEAR +
+ LEAPS_THRU_END_OF(newy - 1) -
+ LEAPS_THRU_END_OF(y - 1);
+ y = newy;
+ }
+ EfiTime->Year = (UINT16)y;
+ ip = mon_lengths[yleap];
+ for (EfiTime->Month = 0; days >= (long) ip[EfiTime->Month]; ++(EfiTime->Month))
+ days = days - (long) ip[EfiTime->Month];
+ EfiTime->Month++; // adjust range to EFI conventions
+ EfiTime->Day = (UINT8) (days + 1);
+}
+
+//
+// String functions, used for file and volume info
+//
+
+UINTN fsw_efi_strsize(struct fsw_string *s)
+{
+ if (s->type == FSW_STRING_TYPE_EMPTY)
+ return sizeof(CHAR16);
+ return (s->len + 1) * sizeof(CHAR16);
+}
+
+VOID fsw_efi_strcpy(CHAR16 *Dest, struct fsw_string *src)
+{
+ if (src->type == FSW_STRING_TYPE_EMPTY) {
+ Dest[0] = 0;
+ } else if (src->type == FSW_STRING_TYPE_UTF16) {
+ CopyMem(Dest, src->data, src->size);
+ Dest[src->len] = 0;
+ } else {
+ /// @todo coerce, recurse
+ Dest[0] = 0;
+ }
+}
+
+#ifdef VBOX
+int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len)
+{
+ int i;
+ fsw_u8 *p1 = (fsw_u8 *)s1data;
+ fsw_u16 *p2 = (fsw_u16 *)s2data;
+
+ for (i = 0; i<len; i++)
+ {
+ if (fsw_to_lower(p1[i]) != fsw_to_lower(p2[i]))
+ {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+#endif
+
+// EOF
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.c
new file mode 100644
index 00000000..ba961214
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.c
@@ -0,0 +1,1451 @@
+/* $Id: fsw_hfs.c $ */
+/** @file
+ * fsw_hfs.c - HFS file system driver code, see
+ *
+ * https://developer.apple.com/legacy/library/technotes/tn/tn1150.html
+ * (formerly http://developer.apple.com/technotes/tn/tn1150.html)
+ *
+ * Current limitations:
+ * - Doesn't support permissions
+ * - Complete Unicode case-insensitiveness disabled (large tables)
+ * - No links
+ * - Only supports pure HFS+ (i.e. no HFS, or HFS+ embedded to HFS)
+ */
+
+/*
+ * Copyright (C) 2010-2022 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
+ */
+
+#include "fsw_hfs.h"
+
+#ifdef HOST_POSIX
+#include <assert.h>
+#define DPRINT(x) printf(x)
+#define DPRINT2(x,y) printf(x,y)
+#define BP(msg) do { printf("ERROR: %s", msg); assert(0); } while (0)
+#elif defined DEBUG_LEVEL
+#define CONCAT(x,y) x##y
+#define DPRINT(x) Print(CONCAT(L,x))
+#define DPRINT2(x,y) Print(CONCAT(L,x), y)
+#define BP(msg) DPRINT(msg)
+#else
+#include <Library/PrintLib.h>
+#define DPRINT(x) do { } while (0)
+#define DPRINT2(x,y) do { } while (0)
+#define BP(msg) do { } while (0)
+#endif
+
+// functions
+#if 0
+void dump_str(fsw_u16* p, fsw_u32 len, int swap)
+{
+ int i;
+
+ for (i=0; i<len; i++)
+ {
+ fprintf(stderr, "%c", swap ? be16_to_cpu(p[i]) : p[i]);
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
+static fsw_status_t fsw_hfs_volume_mount(struct fsw_hfs_volume *vol);
+static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol);
+static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb);
+
+static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno);
+static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno);
+static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
+ struct fsw_dnode_stat *sb);
+static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
+ struct fsw_extent *extent);
+
+static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
+ struct fsw_string *lookup_name, struct fsw_hfs_dnode **child_dno);
+static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
+ struct fsw_shandle *shand, struct fsw_hfs_dnode **child_dno);
+#if 0
+static fsw_status_t fsw_hfs_read_dirrec(struct fsw_shandle *shand, struct hfs_dirrec_buffer *dirrec_buffer);
+#endif
+
+static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
+ struct fsw_string *link);
+
+//
+// Dispatch Table
+//
+
+struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfs) = {
+ { FSW_STRING_TYPE_ISO88591, 4, 4, "hfs" },
+ sizeof(struct fsw_hfs_volume),
+ sizeof(struct fsw_hfs_dnode),
+
+ fsw_hfs_volume_mount,
+ fsw_hfs_volume_free,
+ fsw_hfs_volume_stat,
+ fsw_hfs_dnode_fill,
+ fsw_hfs_dnode_free,
+ fsw_hfs_dnode_stat,
+ fsw_hfs_get_extent,
+ fsw_hfs_dir_lookup,
+ fsw_hfs_dir_read,
+ fsw_hfs_readlink,
+};
+
+static fsw_s32
+fsw_hfs_read_block (struct fsw_hfs_dnode * dno,
+ fsw_u32 log_bno,
+ fsw_u32 off,
+ fsw_s32 len,
+ fsw_u8 * buf)
+{
+ fsw_status_t status;
+ struct fsw_extent extent;
+ fsw_u32 phys_bno;
+ fsw_u8* buffer;
+
+ extent.log_start = log_bno;
+ status = fsw_hfs_get_extent(dno->g.vol, dno, &extent);
+ if (status)
+ return status;
+
+ phys_bno = extent.phys_start;
+ status = fsw_block_get(dno->g.vol, phys_bno, 0, (void **)&buffer);
+ if (status)
+ return status;
+
+ fsw_memcpy(buf, buffer + off, len);
+
+ fsw_block_release(dno->g.vol, phys_bno, buffer);
+
+ return FSW_SUCCESS;
+
+}
+
+/* Read data from HFS file. */
+static fsw_s32
+fsw_hfs_read_file (struct fsw_hfs_dnode * dno,
+ fsw_u64 pos,
+ fsw_s32 len,
+ fsw_u8 * buf)
+{
+
+ fsw_status_t status;
+ fsw_u32 log_bno;
+ fsw_u32 block_size_bits = dno->g.vol->block_size_shift;
+ fsw_u32 block_size = (1 << block_size_bits);
+ fsw_u32 block_size_mask = block_size - 1;
+ fsw_s32 read = 0;
+
+ while (len > 0)
+ {
+ fsw_u32 off = (fsw_u32)(pos & block_size_mask);
+ fsw_s32 next_len = len;
+
+ log_bno = (fsw_u32)RShiftU64(pos, block_size_bits);
+
+ if ( next_len >= 0
+ && (fsw_u32)next_len > block_size)
+ next_len = block_size;
+ status = fsw_hfs_read_block(dno, log_bno, off, next_len, buf);
+ if (status)
+ return -1;
+ buf += next_len;
+ pos += next_len;
+ len -= next_len;
+ read += next_len;
+ }
+
+ return read;
+}
+
+
+static fsw_s32
+fsw_hfs_compute_shift(fsw_u32 size)
+{
+ fsw_s32 i;
+
+ for (i=0; i<32; i++)
+ {
+ if ((size >> i) == 0)
+ return i - 1;
+ }
+
+ BP("BUG\n");
+ return 0;
+}
+
+/**
+ * Mount an HFS+ volume. Reads the superblock and constructs the
+ * root directory dnode.
+ */
+
+static fsw_status_t fsw_hfs_volume_mount(struct fsw_hfs_volume *vol)
+{
+ fsw_status_t status, rv;
+ void *buffer = NULL;
+ HFSPlusVolumeHeader *voldesc;
+ fsw_u32 blockno;
+ struct fsw_string s;
+
+ rv = FSW_UNSUPPORTED;
+
+ vol->primary_voldesc = NULL;
+ fsw_set_blocksize(vol, HFS_BLOCKSIZE, HFS_BLOCKSIZE);
+ blockno = HFS_SUPERBLOCK_BLOCKNO;
+
+#define CHECK(s) \
+ if (status) { \
+ rv = status; \
+ break; \
+ }
+
+ vol->emb_block_off = 0;
+ vol->hfs_kind = 0;
+ do {
+ fsw_u16 signature;
+ BTHeaderRec tree_header;
+ fsw_s32 r;
+ fsw_u32 block_size;
+
+ status = fsw_block_get(vol, blockno, 0, &buffer);
+ CHECK(status);
+ voldesc = (HFSPlusVolumeHeader *)buffer;
+ signature = be16_to_cpu(voldesc->signature);
+
+ if ((signature == kHFSPlusSigWord) || (signature == kHFSXSigWord))
+ {
+ if (vol->hfs_kind == 0)
+ {
+ DPRINT("found HFS+\n");
+ vol->hfs_kind = FSW_HFS_PLUS;
+ }
+ }
+ else if (signature == kHFSSigWord)
+ {
+ HFSMasterDirectoryBlock* mdb = (HFSMasterDirectoryBlock*)buffer;
+
+ if (be16_to_cpu(mdb->drEmbedSigWord) == kHFSPlusSigWord)
+ {
+ DPRINT("found HFS+ inside HFS, untested\n");
+ vol->hfs_kind = FSW_HFS_PLUS_EMB;
+ vol->emb_block_off = be32_to_cpu(mdb->drEmbedExtent.startBlock);
+ blockno += vol->emb_block_off;
+ /* retry */
+ continue;
+ }
+ else
+ {
+ DPRINT("found plain HFS, unsupported\n");
+ vol->hfs_kind = FSW_HFS_PLAIN;
+ }
+ rv = FSW_UNSUPPORTED;
+ break;
+ }
+ else
+ {
+ rv = FSW_UNSUPPORTED;
+ break;
+ }
+
+ status = fsw_memdup((void **)&vol->primary_voldesc, voldesc,
+ sizeof(*voldesc));
+ CHECK(status);
+
+
+ block_size = be32_to_cpu(voldesc->blockSize);
+ vol->block_size_shift = fsw_hfs_compute_shift(block_size);
+
+ fsw_block_release(vol, blockno, buffer);
+ buffer = NULL;
+ voldesc = NULL;
+ fsw_set_blocksize(vol, block_size, block_size);
+
+ /* get volume name */
+ s.type = FSW_STRING_TYPE_ISO88591;
+ s.size = s.len = kHFSMaxVolumeNameChars;
+ s.data = "HFS+ volume\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; /* Otherwise buffer overflow reading beyond the end of the buffer. */
+ status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s);
+ CHECK(status);
+
+ /* Setup catalog dnode */
+ status = fsw_dnode_create_root(vol, kHFSCatalogFileID, &vol->catalog_tree.file);
+ CHECK(status);
+ fsw_memcpy (vol->catalog_tree.file->extents,
+ vol->primary_voldesc->catalogFile.extents,
+ sizeof vol->catalog_tree.file->extents);
+ vol->catalog_tree.file->g.size =
+ be64_to_cpu(vol->primary_voldesc->catalogFile.logicalSize);
+
+ /* Setup extents overflow file */
+ status = fsw_dnode_create_root(vol, kHFSExtentsFileID, &vol->extents_tree.file);
+ fsw_memcpy (vol->extents_tree.file->extents,
+ vol->primary_voldesc->extentsFile.extents,
+ sizeof vol->extents_tree.file->extents);
+ vol->extents_tree.file->g.size =
+ be64_to_cpu(vol->primary_voldesc->extentsFile.logicalSize);
+
+ /* Setup the root dnode */
+ status = fsw_dnode_create_root(vol, kHFSRootFolderID, &vol->g.root);
+ CHECK(status);
+
+ /*
+ * Read catalog file, we know that first record is in the first node, right after
+ * the node descriptor.
+ */
+ r = fsw_hfs_read_file(vol->catalog_tree.file,
+ sizeof (BTNodeDescriptor),
+ sizeof (BTHeaderRec), (fsw_u8 *) &tree_header);
+ if (r <= 0)
+ {
+ status = FSW_VOLUME_CORRUPTED;
+ break;
+ }
+ vol->case_sensitive =
+ (signature == kHFSXSigWord) &&
+ (tree_header.keyCompareType == kHFSBinaryCompare);
+ vol->catalog_tree.root_node = be32_to_cpu (tree_header.rootNode);
+ vol->catalog_tree.node_size = be16_to_cpu (tree_header.nodeSize);
+
+ /* Read extents overflow file */
+ r = fsw_hfs_read_file(vol->extents_tree.file,
+ sizeof (BTNodeDescriptor),
+ sizeof (BTHeaderRec), (fsw_u8 *) &tree_header);
+ if (r <= 0)
+ {
+ status = FSW_VOLUME_CORRUPTED;
+ break;
+ }
+
+ vol->extents_tree.root_node = be32_to_cpu (tree_header.rootNode);
+ vol->extents_tree.node_size = be16_to_cpu (tree_header.nodeSize);
+
+ rv = FSW_SUCCESS;
+ } while (0);
+
+#undef CHECK
+
+
+ if (buffer != NULL)
+ fsw_block_release(vol, blockno, buffer);
+
+ return rv;
+}
+
+/**
+ * Free the volume data structure. Called by the core after an unmount or after
+ * an unsuccessful mount to release the memory used by the file system type specific
+ * part of the volume structure.
+ */
+
+static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol)
+{
+ if (vol->primary_voldesc)
+ {
+ fsw_free(vol->primary_voldesc);
+ vol->primary_voldesc = NULL;
+ }
+}
+
+/**
+ * Get in-depth information on a volume.
+ */
+
+static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb)
+{
+ sb->total_bytes = be32_to_cpu(vol->primary_voldesc->totalBlocks) << vol->block_size_shift;
+ sb->free_bytes = be32_to_cpu(vol->primary_voldesc->freeBlocks) << vol->block_size_shift;
+ return FSW_SUCCESS;
+}
+
+/**
+ * Get full information on a dnode from disk. This function is called by the core
+ * whenever it needs to access fields in the dnode structure that may not
+ * be filled immediately upon creation of the dnode.
+ */
+
+static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno)
+{
+ return FSW_SUCCESS;
+}
+
+/**
+ * Free the dnode data structure. Called by the core when deallocating a dnode
+ * structure to release the memory used by the file system type specific part
+ * of the dnode structure.
+ */
+
+static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno)
+{
+}
+
+static fsw_u32 mac_to_posix(fsw_u32 mac_time)
+{
+ /* Mac time is 1904 year based */
+ return mac_time ? mac_time - 2082844800 : 0;
+}
+
+/**
+ * Get in-depth information on a dnode. The core makes sure that fsw_hfs_dnode_fill
+ * has been called on the dnode before this function is called. Note that some
+ * data is not directly stored into the structure, but passed to a host-specific
+ * callback that converts it to the host-specific format.
+ */
+
+static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol,
+ struct fsw_hfs_dnode *dno,
+ struct fsw_dnode_stat *sb)
+{
+ sb->used_bytes = dno->used_bytes;
+ sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, mac_to_posix(dno->ctime));
+ sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, mac_to_posix(dno->mtime));
+ sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, 0);
+ sb->store_attr_posix(sb, 0700);
+
+ return FSW_SUCCESS;
+}
+
+static int
+fsw_hfs_find_block(HFSPlusExtentRecord * exts,
+ fsw_u32 * lbno,
+ fsw_u32 * pbno)
+{
+ int i;
+ fsw_u32 cur_lbno = *lbno;
+
+ for (i = 0; i < 8; i++)
+ {
+ fsw_u32 start = be32_to_cpu ((*exts)[i].startBlock);
+ fsw_u32 count = be32_to_cpu ((*exts)[i].blockCount);
+
+ if (cur_lbno < count)
+ {
+ *pbno = start + cur_lbno;
+ return 1;
+ }
+
+ cur_lbno -= count;
+ }
+
+ *lbno = cur_lbno;
+
+ return 0;
+}
+
+/* Find record offset, numbering starts from the end */
+static fsw_u32
+fsw_hfs_btree_recoffset (struct fsw_hfs_btree * btree,
+ BTNodeDescriptor * node,
+ fsw_u32 index)
+{
+ fsw_u8 *cnode = (fsw_u8 *) node;
+ fsw_u16 *recptr;
+ recptr = (fsw_u16 *) (cnode+btree->node_size - index * 2 - 2);
+ return be16_to_cpu(*recptr);
+}
+
+/* Pointer to the key inside node */
+static BTreeKey *
+fsw_hfs_btree_rec (struct fsw_hfs_btree * btree,
+ BTNodeDescriptor * node,
+ fsw_u32 index)
+{
+ fsw_u8 *cnode = (fsw_u8 *) node;
+ fsw_u32 offset;
+ offset = fsw_hfs_btree_recoffset (btree, node, index);
+ return (BTreeKey *) (cnode + offset);
+}
+
+
+static fsw_status_t
+fsw_hfs_btree_search (struct fsw_hfs_btree * btree,
+ BTreeKey * key,
+ int (*compare_keys) (BTreeKey* key1, BTreeKey* key2),
+ BTNodeDescriptor ** result,
+ fsw_u32 * key_offset)
+{
+ BTNodeDescriptor* node;
+ fsw_u32 currnode;
+ fsw_u32 rec;
+ fsw_status_t status;
+ fsw_u8* buffer = NULL;
+
+ currnode = btree->root_node;
+ status = fsw_alloc(btree->node_size, &buffer);
+ if (status)
+ return status;
+ node = (BTNodeDescriptor*)buffer;
+
+ while (1)
+ {
+ int cmp = 0;
+ int match;
+ fsw_u32 count;
+
+ readnode:
+ match = 0;
+ /* Read a node. */
+ if (fsw_hfs_read_file (btree->file,
+ (fsw_u64)currnode * btree->node_size,
+ btree->node_size, buffer) <= 0)
+ {
+ status = FSW_VOLUME_CORRUPTED;
+ break;
+ }
+
+ if (be16_to_cpu(*(fsw_u16*)(buffer + btree->node_size - 2)) != sizeof(BTNodeDescriptor))
+ BP("corrupted node\n");
+
+ count = be16_to_cpu (node->numRecords);
+
+#if 1
+ for (rec = 0; rec < count; rec++)
+ {
+ BTreeKey *currkey;
+
+ currkey = fsw_hfs_btree_rec (btree, node, rec);
+ cmp = compare_keys (currkey, key);
+ //fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind);
+
+ /* Leaf node. */
+ if (node->kind == kBTLeafNode)
+ {
+ if (cmp == 0)
+ {
+ /* Found! */
+ *result = node;
+ *key_offset = rec;
+
+ status = FSW_SUCCESS;
+ goto done;
+ }
+ }
+ else if (node->kind == kBTIndexNode)
+ {
+ fsw_u32 *pointer;
+
+ if (cmp > 0)
+ break;
+
+ pointer = (fsw_u32 *) ((char *) currkey
+ + be16_to_cpu (currkey->length16)
+ + 2);
+ currnode = be32_to_cpu (*pointer);
+ match = 1;
+ }
+ }
+
+ if (node->kind == kBTLeafNode && cmp < 0 && node->fLink)
+ {
+ currnode = be32_to_cpu(node->fLink);
+ goto readnode;
+ }
+ else if (!match)
+ {
+ status = FSW_NOT_FOUND;
+ break;
+ }
+#else
+ /* Perform binary search */
+ fsw_u32 lower = 0;
+ fsw_u32 upper = count - 1;
+ fsw_s32 cmp = -1;
+ BTreeKey *currkey = NULL;
+
+ if (count == 0)
+ {
+ status = FSW_NOT_FOUND;
+ goto done;
+ }
+
+ while (lower <= upper)
+ {
+ fsw_u32 index = (lower + upper) / 2;
+
+ currkey = fsw_hfs_btree_rec (btree, node, index);
+
+ cmp = compare_keys (currkey, key);
+ if (cmp < 0) upper = index - 1;
+ if (cmp > 0) lower = index + 1;
+ if (cmp == 0)
+ {
+ /* Found! */
+ *result = node;
+ *key_offset = rec;
+
+ status = FSW_SUCCESS;
+ goto done;
+ }
+ }
+
+ if (cmp < 0)
+ currkey = fsw_hfs_btree_rec (btree, node, upper);
+
+ if (node->kind == kBTIndexNode && currkey)
+ {
+ fsw_u32 *pointer;
+
+ pointer = (fsw_u32 *) ((char *) currkey
+ + be16_to_cpu (currkey->length16)
+ + 2);
+ currnode = be32_to_cpu (*pointer);
+ }
+ else
+ {
+ status = FSW_NOT_FOUND;
+ break;
+ }
+#endif
+ }
+
+
+ done:
+ if (buffer != NULL && status != FSW_SUCCESS)
+ fsw_free(buffer);
+
+ return status;
+}
+
+typedef struct
+{
+ fsw_u32 id;
+ fsw_u32 type;
+ struct fsw_string * name;
+ fsw_u64 size;
+ fsw_u64 used;
+ fsw_u32 ctime;
+ fsw_u32 mtime;
+ fsw_u32 node_num;
+ HFSPlusExtentRecord extents;
+} file_info_t;
+
+typedef struct
+{
+ fsw_u32 cur_pos; /* current position */
+ fsw_u32 parent;
+ struct fsw_hfs_volume * vol;
+
+ struct fsw_shandle * shandle; /* this one track iterator's state */
+ file_info_t file_info;
+} visitor_parameter_t;
+
+static void hfs_fill_info(struct fsw_hfs_volume *vol, HFSPlusCatalogKey *file_key, file_info_t *file_info)
+{
+ fsw_u8 * base;
+ fsw_u16 rec_type;
+
+ /* for plain HFS "-(keySize & 1)" would be needed */
+ base = (fsw_u8*)file_key + be16_to_cpu(file_key->keyLength) + 2;
+ rec_type = be16_to_cpu(*(fsw_u16*)base);
+
+ /** @todo read additional info */
+ switch (rec_type)
+ {
+ case kHFSPlusFolderRecord:
+ {
+ HFSPlusCatalogFolder* info = (HFSPlusCatalogFolder*)base;
+
+ file_info->id = be32_to_cpu(info->folderID);
+ file_info->type = FSW_DNODE_TYPE_DIR;
+ /** @todo return number of elements, maybe use smth else */
+ file_info->size = be32_to_cpu(info->valence);
+ file_info->used = be32_to_cpu(info->valence);
+ file_info->ctime = be32_to_cpu(info->createDate);
+ file_info->mtime = be32_to_cpu(info->contentModDate);
+ break;
+ }
+ case kHFSPlusFileRecord:
+ {
+ HFSPlusCatalogFile* info = (HFSPlusCatalogFile*)base;
+ uint32_t creator = be32_to_cpu(info->userInfo.fdCreator);
+ uint32_t crtype = be32_to_cpu(info->userInfo.fdType);
+
+ file_info->id = be32_to_cpu(info->fileID);
+ file_info->type = FSW_DNODE_TYPE_FILE;
+ file_info->size = be64_to_cpu(info->dataFork.logicalSize);
+ file_info->used = LShiftU64(be32_to_cpu(info->dataFork.totalBlocks), vol->block_size_shift);
+ file_info->ctime = be32_to_cpu(info->createDate);
+ file_info->mtime = be32_to_cpu(info->contentModDate);
+ fsw_memcpy(&file_info->extents, &info->dataFork.extents,
+ sizeof file_info->extents);
+ if (creator == kHFSPlusCreator && crtype == kHardLinkFileType)
+ {
+ /* Only hard links currently supported. */
+ file_info->type = FSW_DNODE_TYPE_SYMLINK;
+ file_info->node_num = be32_to_cpu(info->bsdInfo.special.iNodeNum);
+ }
+ break;
+ }
+ case kHFSPlusFolderThreadRecord:
+ case kHFSPlusFileThreadRecord:
+ {
+ /* Do nothing. */
+ break;
+ }
+ default:
+ BP("unknown file type\n");
+ file_info->type = FSW_DNODE_TYPE_UNKNOWN;
+
+ break;
+ }
+}
+
+static int
+fsw_hfs_btree_visit_node(BTreeKey *record, void* param)
+{
+ visitor_parameter_t* vp = (visitor_parameter_t*)param;
+ fsw_u8* base = (fsw_u8*)record->rawData + be16_to_cpu(record->length16) + 2;
+ fsw_u16 rec_type = be16_to_cpu(*(fsw_u16*)base);
+ struct HFSPlusCatalogKey* cat_key = (HFSPlusCatalogKey*)record;
+ fsw_u16 name_len;
+ fsw_u16 *name_ptr;
+ fsw_u32 i;
+ struct fsw_string * file_name;
+
+ if (be32_to_cpu(cat_key->parentID) != vp->parent)
+ return -1;
+
+ /* not smth we care about */
+ if (vp->shandle->pos != vp->cur_pos++)
+ return 0;
+
+ if (rec_type == kHFSPlusFolderThreadRecord || rec_type == kHFSPlusFileThreadRecord)
+ {
+ vp->shandle->pos++;
+ return 0;
+ }
+
+ hfs_fill_info(vp->vol, cat_key, &vp->file_info);
+
+ name_len = be16_to_cpu(cat_key->nodeName.length);
+
+ file_name = vp->file_info.name;
+ file_name->len = name_len;
+ fsw_memdup(&file_name->data, &cat_key->nodeName.unicode[0], 2*name_len);
+ file_name->size = 2*name_len;
+ file_name->type = FSW_STRING_TYPE_UTF16;
+ name_ptr = (fsw_u16*)file_name->data;
+ for (i=0; i<name_len; i++)
+ {
+ name_ptr[i] = be16_to_cpu(name_ptr[i]);
+ }
+ vp->shandle->pos++;
+
+ return 1;
+}
+
+static fsw_status_t
+fsw_hfs_btree_iterate_node (struct fsw_hfs_btree * btree,
+ BTNodeDescriptor * first_node,
+ fsw_u32 first_rec,
+ int (*callback) (BTreeKey *record, void* param),
+ void * param)
+{
+ fsw_status_t status;
+ /* We modify node, so make a copy */
+ BTNodeDescriptor* node = first_node;
+ fsw_u8* buffer = NULL;
+
+ status = fsw_alloc(btree->node_size, &buffer);
+ if (status)
+ return status;
+
+ while (1)
+ {
+ fsw_u32 i;
+ fsw_u32 count = be16_to_cpu(node->numRecords);
+ fsw_u32 next_node;
+
+ /* Iterate over all records in this node. */
+ for (i = first_rec; i < count; i++)
+ {
+ int rv = callback(fsw_hfs_btree_rec (btree, node, i), param);
+
+ switch (rv)
+ {
+ case 1:
+ status = FSW_SUCCESS;
+ goto done;
+ case -1:
+ status = FSW_NOT_FOUND;
+ goto done;
+ }
+ /* if callback returned 0 - continue */
+ }
+
+ next_node = be32_to_cpu(node->fLink);
+
+ if (!next_node)
+ {
+ status = FSW_NOT_FOUND;
+ break;
+ }
+
+ if (fsw_hfs_read_file (btree->file,
+ next_node * btree->node_size,
+ btree->node_size, buffer) <= 0)
+ {
+ status = FSW_VOLUME_CORRUPTED;
+ return 1;
+ }
+
+ node = (BTNodeDescriptor*)buffer;
+ first_rec = 0;
+ }
+ done:
+ if (buffer)
+ fsw_free(buffer);
+
+ return status;
+}
+
+#if 0
+void deb(fsw_u16* p, int len, int swap)
+{
+ int i;
+ for (i=0; i<len; i++)
+ {
+ printf("%c", swap ? be16_to_cpu(p[i]) : p[i]);
+ }
+ printf("\n");
+}
+#endif
+
+static int
+fsw_hfs_cmp_extkey(BTreeKey* key1, BTreeKey* key2)
+{
+ HFSPlusExtentKey* ekey1 = (HFSPlusExtentKey*)key1;
+ HFSPlusExtentKey* ekey2 = (HFSPlusExtentKey*)key2;
+ int result;
+
+ /* First key is read from the FS data, second is in-memory in CPU endianess */
+ result = be32_to_cpu(ekey1->fileID) - ekey2->fileID;
+
+ if (result)
+ return result;
+
+ result = ekey1->forkType - ekey2->forkType;
+
+ if (result)
+ return result;
+
+ result = be32_to_cpu(ekey1->startBlock) - ekey2->startBlock;
+ return result;
+}
+
+static int
+fsw_hfs_cmp_catkey (BTreeKey *key1, BTreeKey *key2)
+{
+ HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1;
+ HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2;
+
+ int apos, bpos, lc;
+ fsw_u16 ac, bc;
+ fsw_u32 parentId1;
+ int key1Len;
+ fsw_u16 *p1;
+ fsw_u16 *p2;
+
+ parentId1 = be32_to_cpu(ckey1->parentID);
+
+ if (parentId1 > ckey2->parentID)
+ return 1;
+ if (parentId1 < ckey2->parentID)
+ return -1;
+
+ p1 = &ckey1->nodeName.unicode[0];
+ p2 = &ckey2->nodeName.unicode[0];
+ key1Len = be16_to_cpu (ckey1->nodeName.length);
+ apos = bpos = 0;
+
+ while(1)
+ {
+ /* get next valid character from ckey1 */
+ for (lc = 0; lc == 0 && apos < key1Len; apos++) {
+ ac = be16_to_cpu(p1[apos]);
+ lc = ac;
+ };
+ ac = (fsw_u16)lc;
+
+ /* get next valid character from ckey2 */
+ for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) {
+ bc = p2[bpos];
+ lc = bc;
+ };
+ bc = (fsw_u16)lc;
+
+ if (ac != bc || (ac == 0 && bc == 0))
+ return ac - bc;
+ }
+}
+
+static int
+fsw_hfs_cmpi_catkey (BTreeKey *key1, BTreeKey *key2)
+{
+ HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1;
+ HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2;
+
+ int apos, bpos, lc;
+ fsw_u16 ac, bc;
+ fsw_u32 parentId1;
+ int key1Len;
+ fsw_u16 *p1;
+ fsw_u16 *p2;
+
+ parentId1 = be32_to_cpu(ckey1->parentID);
+
+ if (parentId1 > ckey2->parentID)
+ return 1;
+ if (parentId1 < ckey2->parentID)
+ return -1;
+
+ key1Len = be16_to_cpu (ckey1->nodeName.length);
+
+ if (key1Len == 0 && ckey2->nodeName.length == 0)
+ return 0;
+
+ p1 = &ckey1->nodeName.unicode[0];
+ p2 = &ckey2->nodeName.unicode[0];
+
+ apos = bpos = 0;
+
+ while(1)
+ {
+ /* get next valid (non-zero) character from ckey1 */
+ for (lc = 0; lc == 0 && apos < key1Len; apos++) {
+ ac = be16_to_cpu(p1[apos]);
+ lc = fsw_to_lower(ac); /* NB: 0x0000 is translated to 0xffff */
+ };
+ ac = (fsw_u16)lc;
+
+ /* get next valid (non-zero) character from ckey2 */
+ for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) {
+ bc = p2[bpos];
+ lc = fsw_to_lower(bc); /* NB: 0x0000 is translated to 0xffff */
+ };
+ bc = (fsw_u16)lc;
+
+ if (ac != bc || (ac == 0 && bc == 0))
+ return ac - bc;
+ }
+}
+
+/**
+ * Retrieve file data mapping information. This function is called by the core when
+ * fsw_shandle_read needs to know where on the disk the required piece of the file's
+ * data can be found. The core makes sure that fsw_hfs_dnode_fill has been called
+ * on the dnode before. Our task here is to get the physical disk block number for
+ * the requested logical block number.
+ */
+
+static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume * vol,
+ struct fsw_hfs_dnode * dno,
+ struct fsw_extent * extent)
+{
+ fsw_status_t status;
+ fsw_u32 lbno;
+ HFSPlusExtentRecord *exts;
+ BTNodeDescriptor *node = NULL;
+
+ extent->type = FSW_EXTENT_TYPE_PHYSBLOCK;
+ extent->log_count = 1;
+ lbno = extent->log_start;
+
+ /* we only care about data forks atm, do we? */
+ exts = &dno->extents;
+
+ while (1)
+ {
+ struct HFSPlusExtentKey* key;
+ struct HFSPlusExtentKey overflowkey;
+ fsw_u32 ptr;
+ fsw_u32 phys_bno;
+
+ if (fsw_hfs_find_block(exts, &lbno, &phys_bno))
+ {
+ extent->phys_start = phys_bno + vol->emb_block_off;
+ status = FSW_SUCCESS;
+ break;
+ }
+
+
+ /* Find appropriate overflow record */
+ overflowkey.fileID = dno->g.dnode_id;
+ overflowkey.startBlock = extent->log_start - lbno;
+
+ if (node != NULL)
+ {
+ fsw_free(node);
+ node = NULL;
+ }
+
+ status = fsw_hfs_btree_search (&vol->extents_tree,
+ (BTreeKey*)&overflowkey,
+ fsw_hfs_cmp_extkey,
+ &node, &ptr);
+ if (status)
+ break;
+
+ key = (struct HFSPlusExtentKey *)
+ fsw_hfs_btree_rec (&vol->extents_tree, node, ptr);
+ exts = (HFSPlusExtentRecord*) (key + 1);
+ }
+
+ if (node != NULL)
+ fsw_free(node);
+
+ return status;
+}
+
+static const fsw_u16* g_blacklist[] =
+{
+ //L"AppleIntelCPUPowerManagement.kext",
+ NULL
+};
+
+
+//#define HFS_FILE_INJECTION
+
+#ifdef HFS_FILE_INJECTION
+static struct
+{
+ const fsw_u16* path;
+ const fsw_u16* name;
+} g_injectList[] =
+{
+ {
+ L"/System/Library/Extensions",
+ L"ApplePS2Controller.kext"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+#endif
+
+static fsw_status_t
+create_hfs_dnode(struct fsw_hfs_dnode * dno,
+ file_info_t * file_info,
+ struct fsw_hfs_dnode ** child_dno_out)
+{
+ fsw_status_t status;
+ struct fsw_hfs_dnode * baby;
+
+ status = fsw_dnode_create(dno, file_info->id, file_info->type,
+ file_info->name, &baby);
+ if (status)
+ return status;
+
+ baby->g.size = file_info->size;
+ baby->used_bytes = file_info->used;
+ baby->ctime = file_info->ctime;
+ baby->mtime = file_info->mtime;
+ baby->node_num = file_info->node_num;
+
+
+ /* Fill-in extents info */
+ if (file_info->type == FSW_DNODE_TYPE_FILE)
+ {
+ fsw_memcpy(baby->extents, &file_info->extents, sizeof file_info->extents);
+ }
+
+ *child_dno_out = baby;
+
+ return FSW_SUCCESS;
+}
+
+
+/**
+ * Lookup a directory's child dnode by name. This function is called on a directory
+ * to retrieve the directory entry with the given name. A dnode is constructed for
+ * this entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
+ * and the dnode is actually a directory.
+ */
+
+static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume * vol,
+ struct fsw_hfs_dnode * dno,
+ struct fsw_string * lookup_name,
+ struct fsw_hfs_dnode ** child_dno_out)
+{
+ fsw_status_t status;
+ struct HFSPlusCatalogKey catkey;
+ fsw_u32 ptr;
+ BTNodeDescriptor * node = NULL;
+ struct fsw_string rec_name;
+ int free_data = 0, i;
+ HFSPlusCatalogKey* file_key;
+ file_info_t file_info;
+
+
+ fsw_memzero(&file_info, sizeof file_info);
+ file_info.name = &rec_name;
+
+ catkey.parentID = dno->g.dnode_id;
+ catkey.nodeName.length = (fsw_u16)lookup_name->len;
+
+ /* no need to allocate anything */
+ if (lookup_name->type == FSW_STRING_TYPE_UTF16)
+ {
+ fsw_memcpy(catkey.nodeName.unicode, lookup_name->data, lookup_name->size);
+ rec_name = *lookup_name;
+ } else
+ {
+ status = fsw_strdup_coerce(&rec_name, FSW_STRING_TYPE_UTF16, lookup_name);
+ /* nothing allocated so far */
+ if (status)
+ goto done;
+ free_data = 1;
+ fsw_memcpy(catkey.nodeName.unicode, rec_name.data, rec_name.size);
+ }
+
+ /* Dirty hack: blacklisting of certain files on FS driver level */
+ for (i = 0; g_blacklist[i]; i++)
+ {
+ if (fsw_memeq(g_blacklist[i], catkey.nodeName.unicode, catkey.nodeName.length*2))
+ {
+ DPRINT2("Blacklisted %s\n", g_blacklist[i]);
+ status = FSW_NOT_FOUND;
+ goto done;
+ }
+ }
+
+#ifdef HFS_FILE_INJECTION
+ if (fsw_hfs_inject(vol,
+ dno,
+ catkey.nodeName.unicode,
+ catkey.nodeName.length,
+ &file_info))
+ {
+ status = FSW_SUCCESS;
+ goto create;
+ }
+#endif
+
+ catkey.keyLength = (fsw_u16)(6 + rec_name.len);
+
+ status = fsw_hfs_btree_search (&vol->catalog_tree,
+ (BTreeKey*)&catkey,
+ vol->case_sensitive ?
+ fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey,
+ &node, &ptr);
+ if (status)
+ goto done;
+
+ file_key = (HFSPlusCatalogKey *)fsw_hfs_btree_rec (&vol->catalog_tree, node, ptr);
+ hfs_fill_info(vol, file_key, &file_info);
+
+#ifdef HFS_FILE_INJECTION
+create:
+#endif
+ status = create_hfs_dnode(dno, &file_info, child_dno_out);
+ if (status)
+ goto done;
+
+done:
+
+ if (node != NULL)
+ fsw_free(node);
+
+ if (free_data)
+ fsw_strfree(&rec_name);
+
+ return status;
+}
+
+/**
+ * Get the next directory entry when reading a directory. This function is called during
+ * directory iteration to retrieve the next directory entry. A dnode is constructed for
+ * the entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
+ * and the dnode is actually a directory. The shandle provided by the caller is used to
+ * record the position in the directory between calls.
+ */
+
+static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol,
+ struct fsw_hfs_dnode *dno,
+ struct fsw_shandle *shand,
+ struct fsw_hfs_dnode **child_dno_out)
+{
+ fsw_status_t status;
+ struct HFSPlusCatalogKey catkey;
+ fsw_u32 ptr;
+ BTNodeDescriptor * node = NULL;
+
+ visitor_parameter_t param;
+ struct fsw_string rec_name;
+
+ catkey.parentID = dno->g.dnode_id;
+ catkey.nodeName.length = 0;
+
+ fsw_memzero(&param, sizeof(param));
+
+ rec_name.type = FSW_STRING_TYPE_EMPTY;
+ param.file_info.name = &rec_name;
+
+ status = fsw_hfs_btree_search (&vol->catalog_tree,
+ (BTreeKey*)&catkey,
+ vol->case_sensitive ?
+ fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey,
+ &node, &ptr);
+ if (status)
+ goto done;
+
+ /* Iterator updates shand state */
+ param.vol = vol;
+ param.shandle = shand;
+ param.parent = dno->g.dnode_id;
+ param.cur_pos = 0;
+ status = fsw_hfs_btree_iterate_node (&vol->catalog_tree,
+ node,
+ ptr,
+ fsw_hfs_btree_visit_node,
+ &param);
+ if (status)
+ goto done;
+
+ status = create_hfs_dnode(dno, &param.file_info, child_dno_out);
+
+ if (status)
+ goto done;
+
+ done:
+ fsw_strfree(&rec_name);
+
+ return status;
+}
+
+static const char hfs_priv_prefix[] = "/\0\0\0\0HFS+ Private Data/" HFS_INODE_PREFIX;
+
+/**
+ * Get the target path of a symbolic link. This function is called when a symbolic
+ * link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been
+ * called on the dnode and that it really is a symlink.
+ *
+ */
+static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
+ struct fsw_string *link_target)
+{
+ fsw_status_t status;
+
+ if (dno->node_num)
+ {
+ struct fsw_string tgt;
+
+ DPRINT2("hfs_readlink: %d\n", dno->node_num);
+ tgt.type = FSW_STRING_TYPE_ISO88591;
+ tgt.size = sizeof(hfs_priv_prefix) + 10;
+ tgt.len = tgt.size - 1;
+ status = fsw_alloc(tgt.size, &tgt.data);
+ if (!status)
+ {
+ char *str = tgt.data;
+ fsw_memcpy(tgt.data, hfs_priv_prefix, sizeof(hfs_priv_prefix)); // null chars here!
+#ifdef HOST_POSIX
+ tgt.len = sprintf(&str[sizeof(hfs_priv_prefix) - 1], "%d", dno->node_num);
+#else
+ tgt.len = (int)AsciiSPrint(&str[sizeof(hfs_priv_prefix) - 1], tgt.len, "%d", dno->node_num);
+#endif
+ tgt.len += sizeof(hfs_priv_prefix) - 1;
+ status = fsw_strdup_coerce(link_target, vol->g.host_string_type, &tgt);
+ fsw_strfree(&tgt);
+ }
+ return status;
+ }
+
+ return FSW_UNSUPPORTED;
+}
+
+static int fsw_hfs_btree_find_id(BTreeKey *record, void* param)
+{
+ visitor_parameter_t *vp = (visitor_parameter_t*)param;
+ fsw_u8 *base = (fsw_u8*)record->rawData + be16_to_cpu(record->length16) + 2;
+ fsw_u16 rec_type = be16_to_cpu(*(fsw_u16*)base);
+ struct HFSPlusCatalogKey *cat_key = (HFSPlusCatalogKey*)record;
+ fsw_u16 name_len;
+ fsw_u16 *name_ptr;
+ fsw_u16 *old_ptr;
+ int i;
+ struct fsw_string *file_name;
+ struct fsw_string new_name;
+
+ if (be32_to_cpu(cat_key->parentID) != vp->parent)
+ return -1;
+
+ if (!vp->cur_pos)
+ vp->cur_pos = be32_to_cpu(cat_key->parentID);
+
+ /* Not what we're looking for. */
+ if (vp->file_info.id != vp->cur_pos++)
+ return 0;
+
+ if (rec_type == kHFSPlusFolderThreadRecord || rec_type == kHFSPlusFileThreadRecord)
+ {
+ HFSPlusCatalogThread *thread;
+
+ thread = (HFSPlusCatalogThread *)base;
+ vp->file_info.id = be32_to_cpu(thread->parentID);
+
+ name_len = be16_to_cpu(thread->nodeName.length);
+
+ file_name = vp->file_info.name;
+
+ new_name.len = name_len + 1 + file_name->len;
+ new_name.size = sizeof(fsw_u16) * new_name.len;
+ fsw_alloc(new_name.size, &new_name.data);
+ name_ptr = (fsw_u16*)new_name.data;
+ /* Tack on path separator. */
+#ifdef HOST_POSIX
+ name_ptr[0] = L'/';
+#else
+ name_ptr[0] = L'\\';
+#endif
+ /* Copy over + swap the new path component. */
+ for (i = 0; i < name_len; i++)
+ name_ptr[i + 1] = be16_to_cpu(thread->nodeName.unicode[i]);
+ if (file_name->len) {
+ /* Tack on the previous path. */
+ old_ptr = (fsw_u16*)file_name->data;
+ for (++i; i < new_name.len; i++ )
+ name_ptr[i] = *old_ptr++;
+ }
+
+ fsw_free(file_name->data);
+ file_name->len = new_name.len;
+ file_name->size = new_name.size;
+ file_name->data = new_name.data;
+ file_name->type = FSW_STRING_TYPE_UTF16;
+
+ /* This was it, stop iterating. */
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Obtain the full path of a file given its CNID (Catalog Node ID), i.e.
+ * file or folder ID.
+ *
+ */
+static fsw_status_t fsw_hfs_get_path_from_cnid(struct fsw_hfs_volume *vol, fsw_u32 cnid, struct fsw_string *path)
+{
+ fsw_status_t status = FSW_UNSUPPORTED;
+ fsw_u32 ptr;
+ BTNodeDescriptor *node = NULL;
+ struct HFSPlusCatalogKey catkey;
+ visitor_parameter_t param;
+ struct fsw_string rec_name;
+
+ /* The CNID must be a valid user node ID. */
+ if (cnid < kHFSFirstUserCatalogNodeID)
+ goto done;
+
+ fsw_memzero(&param, sizeof(param));
+ fsw_memzero(&rec_name, sizeof(rec_name));
+
+ catkey.parentID = cnid;
+ catkey.nodeName.length = 0;
+
+ param.vol = vol;
+ param.shandle = NULL;
+ param.file_info.id = cnid;
+ param.parent = cnid;
+ param.cur_pos = 0;
+
+ do {
+ rec_name.type = FSW_STRING_TYPE_EMPTY;
+ param.file_info.name = &rec_name;
+
+ status = fsw_hfs_btree_search(&vol->catalog_tree, (BTreeKey*)&catkey,
+ vol->case_sensitive ? fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey,
+ &node, &ptr);
+ if (status)
+ goto done;
+
+ status = fsw_hfs_btree_iterate_node(&vol->catalog_tree, node, ptr,
+ fsw_hfs_btree_find_id, &param);
+ if (status)
+ goto done;
+
+ param.parent = param.file_info.id;
+ param.cur_pos = 0;
+
+ catkey.parentID = param.file_info.id;
+ catkey.nodeName.length = 0;
+ } while (catkey.parentID >= kHFSFirstUserCatalogNodeID);
+
+ /* If everything worked out , the final parent ID will be the root folder ID. */
+ if (catkey.parentID == kHFSRootFolderID)
+ {
+ *path = *param.file_info.name;
+ status = FSW_SUCCESS;
+ }
+ else
+ status = FSW_NOT_FOUND;
+
+done:
+ return status;
+}
+
+/**
+ * Get the path of the HFS+ blessed file, if any.
+ *
+ */
+/*static*/ fsw_status_t fsw_hfs_get_blessed_file(struct fsw_hfs_volume *vol, struct fsw_string *path)
+{
+ fsw_status_t status = FSW_UNSUPPORTED;
+ fsw_u32 bfile_id;
+ fsw_u32 *finderinfo;
+
+ finderinfo = (fsw_u32 *)&vol->primary_voldesc->finderInfo;
+ bfile_id = finderinfo[1];
+ bfile_id = be32_to_cpu(bfile_id);
+
+ DPRINT2("Blessed file ID: %u\n", bfile_id);
+
+ status = fsw_hfs_get_path_from_cnid(vol, bfile_id, path);
+#ifdef HOST_POSIX
+ if (!status)
+ {
+ fsw_u16 *name_ptr;
+ int i;
+
+ printf("Blessed file: ");
+ name_ptr = (fsw_u16*)path->data;
+ for (i = 0; i < path->len; i++)
+ printf("%c", name_ptr[i]);
+ printf("\n");
+ }
+#endif
+
+ return status;
+}
+
+// EOF
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.h
new file mode 100644
index 00000000..ecb1cfeb
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.h
@@ -0,0 +1,177 @@
+/* $Id: fsw_hfs.h $ */
+/** @file
+ * fsw_hfs.h - HFS file system driver header.
+ */
+
+/*
+ * Copyright (C) 2010-2022 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
+ */
+
+#ifndef _FSW_HFS_H_
+#define _FSW_HFS_H_
+
+#define VOLSTRUCTNAME fsw_hfs_volume
+#define DNODESTRUCTNAME fsw_hfs_dnode
+
+#include "fsw_core.h"
+
+#define IN_RING0
+#if !defined(ARCH_BITS) || !defined(HC_ARCH_BITS)
+# error "please add right bitness"
+#endif
+#include "iprt/formats/hfs.h"
+#include "iprt/asm.h" /* endian conversion */
+
+#ifndef HOST_POSIX
+#include <Library/BaseLib.h>
+#endif
+
+//! Block size for HFS volumes.
+#define HFS_BLOCKSIZE 512
+
+//! Block number where the HFS superblock resides.
+#define HFS_SUPERBLOCK_BLOCKNO 2
+
+#ifdef _MSC_VER
+/* vasily: disable warning for non-standard anonymous struct/union
+ * declarations
+ */
+# pragma warning (disable:4201)
+#endif
+
+struct hfs_dirrec {
+ fsw_u8 _dummy;
+};
+
+#pragma pack(1)
+struct fsw_hfs_key
+{
+ union
+ {
+ struct HFSPlusExtentKey ext_key;
+ struct HFSPlusCatalogKey cat_key;
+ fsw_u16 key_len; /* Length is at the beginning of all keys */
+ };
+};
+#pragma pack()
+
+typedef enum {
+ /* Regular HFS */
+ FSW_HFS_PLAIN = 0,
+ /* HFS+ */
+ FSW_HFS_PLUS,
+ /* HFS+ embedded to HFS */
+ FSW_HFS_PLUS_EMB
+} fsw_hfs_kind;
+
+/**
+ * HFS: Dnode structure with HFS-specific data.
+ */
+struct fsw_hfs_dnode
+{
+ struct fsw_dnode g; //!< Generic dnode structure
+ HFSPlusExtentRecord extents;
+ fsw_u32 ctime;
+ fsw_u32 mtime;
+ fsw_u64 used_bytes;
+ fsw_u32 node_num;
+};
+
+/**
+ * HFS: In-memory B-tree structure.
+ */
+struct fsw_hfs_btree
+{
+ fsw_u32 root_node;
+ fsw_u32 node_size;
+ struct fsw_hfs_dnode* file;
+};
+
+
+/**
+ * HFS: In-memory volume structure with HFS-specific data.
+ */
+
+struct fsw_hfs_volume
+{
+ struct fsw_volume g; //!< Generic volume structure
+
+ struct HFSPlusVolumeHeader *primary_voldesc; //!< Volume Descriptor
+ struct fsw_hfs_btree catalog_tree; // Catalog tree
+ struct fsw_hfs_btree extents_tree; // Extents overflow tree
+ struct fsw_hfs_dnode root_file;
+ int case_sensitive;
+ fsw_u32 block_size_shift;
+ fsw_hfs_kind hfs_kind;
+ fsw_u32 emb_block_off;
+};
+
+/* Endianess swappers. */
+DECLINLINE(fsw_u16)
+be16_to_cpu(fsw_u16 x)
+{
+ return RT_BE2H_U16(x);
+}
+
+DECLINLINE(fsw_u16)
+cpu_to_be16(fsw_u16 x)
+{
+ return RT_H2BE_U16(x);
+}
+
+
+DECLINLINE(fsw_u32)
+cpu_to_be32(fsw_u32 x)
+{
+ return RT_H2BE_U32(x);
+}
+
+DECLINLINE(fsw_u32)
+be32_to_cpu(fsw_u32 x)
+{
+ return RT_BE2H_U32(x);
+}
+
+DECLINLINE(fsw_u64)
+be64_to_cpu(fsw_u64 x)
+{
+#ifdef RT_LITTLE_ENDIAN
+#ifdef HOST_POSIX
+ return RT_BE2H_U64(x);
+#else
+ return SwapBytes64(x);
+#endif
+#else
+ return x;
+#endif
+}
+
+#endif
+
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.c
new file mode 100644
index 00000000..3dc8c1db
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.c
@@ -0,0 +1,679 @@
+/* $Id: fsw_iso9660.c $ */
+/** @file
+ * fsw_iso9660.c - ISO9660 file system driver code.
+ *
+ * Current limitations:
+ * - Files must be in one extent (i.e. Level 2)
+ * - No Joliet or Rock Ridge extensions
+ * - No interleaving
+ * - inode number generation strategy fails on volumes > 2 GB
+ * - No blocksizes != 2048
+ * - No High Sierra or anything else != 'CD001'
+ * - No volume sets with directories pointing at other volumes
+ * - No extended attribute records
+ */
+
+/*
+ * Copyright (C) 2010-2022 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_iso9660.h"
+
+
+// functions
+
+static fsw_status_t fsw_iso9660_volume_mount(struct fsw_iso9660_volume *vol);
+static void fsw_iso9660_volume_free(struct fsw_iso9660_volume *vol);
+static fsw_status_t fsw_iso9660_volume_stat(struct fsw_iso9660_volume *vol, struct fsw_volume_stat *sb);
+
+static fsw_status_t fsw_iso9660_dnode_fill(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno);
+static void fsw_iso9660_dnode_free(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno);
+static fsw_status_t fsw_iso9660_dnode_stat(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_dnode_stat *sb);
+static fsw_status_t fsw_iso9660_get_extent(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_extent *extent);
+
+static fsw_status_t fsw_iso9660_dir_lookup(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_string *lookup_name, struct fsw_iso9660_dnode **child_dno);
+static fsw_status_t fsw_iso9660_dir_read(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_shandle *shand, struct fsw_iso9660_dnode **child_dno);
+static fsw_status_t fsw_iso9660_read_dirrec(struct fsw_iso9660_volume *vol, struct fsw_shandle *shand, struct iso9660_dirrec_buffer *dirrec_buffer);
+
+static fsw_status_t fsw_iso9660_readlink(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_string *link);
+
+static fsw_status_t rr_find_sp(struct iso9660_dirrec *dirrec, struct fsw_rock_ridge_susp_sp **psp);
+static fsw_status_t rr_find_nm(struct fsw_iso9660_volume *vol, struct iso9660_dirrec *dirrec, int off, struct fsw_string *str);
+static fsw_status_t rr_read_ce(struct fsw_iso9660_volume *vol, union fsw_rock_ridge_susp_ce *ce, fsw_u8 *begin);
+//
+// Dispatch Table
+//
+
+struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(iso9660) = {
+ { FSW_STRING_TYPE_ISO88591, 4, 4, "iso9660" },
+ sizeof(struct fsw_iso9660_volume),
+ sizeof(struct fsw_iso9660_dnode),
+
+ fsw_iso9660_volume_mount,
+ fsw_iso9660_volume_free,
+ fsw_iso9660_volume_stat,
+ fsw_iso9660_dnode_fill,
+ fsw_iso9660_dnode_free,
+ fsw_iso9660_dnode_stat,
+ fsw_iso9660_get_extent,
+ fsw_iso9660_dir_lookup,
+ fsw_iso9660_dir_read,
+ fsw_iso9660_readlink,
+};
+
+static fsw_status_t rr_find_sp(struct iso9660_dirrec *dirrec, struct fsw_rock_ridge_susp_sp **psp)
+{
+ fsw_u8 *r;
+ int off = 0;
+ struct fsw_rock_ridge_susp_sp *sp;
+ r = (fsw_u8 *)((fsw_u8 *)dirrec + sizeof(*dirrec) + dirrec->file_identifier_length);
+ off = (int)(r - (fsw_u8 *)dirrec);
+ while(off < dirrec->dirrec_length)
+ {
+ if (*r == 'S')
+ {
+ sp = (struct fsw_rock_ridge_susp_sp *)r;
+ if( sp->e.sig[0] == 'S'
+ && sp->e.sig[1] == 'P'
+ && sp->magic[0] == 0xbe
+ && sp->magic[1] == 0xef)
+ {
+ *psp = sp;
+ return FSW_SUCCESS;
+ }
+ }
+ r++;
+ off = (int)(r - (fsw_u8 *)dirrec);
+ }
+ *psp = NULL;
+ return FSW_NOT_FOUND;
+}
+
+static fsw_status_t rr_find_nm(struct fsw_iso9660_volume *vol, struct iso9660_dirrec *dirrec, int off, struct fsw_string *str)
+{
+ fsw_u8 *r, *begin;
+ int fCe = 0;
+ struct fsw_rock_ridge_susp_nm *nm;
+ int limit = dirrec->dirrec_length;
+ begin = (fsw_u8 *)dirrec;
+ r = (fsw_u8 *)dirrec + off;
+ str->data = NULL;
+ str->len = 0;
+ str->size = 0;
+ str->type = 0;
+ while(off < limit)
+ {
+ if (r[0] == 'C' && r[1] == 'E' && r[2] == 28)
+ {
+ int rc;
+ int ce_off;
+ union fsw_rock_ridge_susp_ce *ce;
+ if (fCe == 0)
+ fsw_alloc_zero(ISO9660_BLOCKSIZE, (void *)&begin);
+ fCe = 1;
+ DEBUG((DEBUG_WARN, "%a:%d we found CE before NM or its continuation\n", __FILE__, __LINE__));
+ ce = (union fsw_rock_ridge_susp_ce *)r;
+ limit = ISOINT(ce->X.len);
+ ce_off = ISOINT(ce->X.offset);
+ rc = rr_read_ce(vol, ce, begin);
+ if (rc != FSW_SUCCESS)
+ {
+ fsw_free(begin);
+ return rc;
+ }
+ begin += ce_off;
+ r = begin;
+ }
+ if (r[0] == 'N' && r[1] == 'M')
+ {
+ nm = (struct fsw_rock_ridge_susp_nm *)r;
+ if( nm->e.sig[0] == 'N'
+ && nm->e.sig[1] == 'M')
+ {
+ int len = 0;
+ fsw_u8 *tmp = NULL;
+ if (nm->flags & RR_NM_CURR)
+ {
+ fsw_memdup(str->data, ".", 1);
+ str->len = 1;
+ goto done;
+ }
+ if (nm->flags & RR_NM_PARE)
+ {
+ fsw_memdup(str->data, "..", 2);
+ str->len = 2;
+ goto done;
+ }
+ len = nm->e.len - sizeof(struct fsw_rock_ridge_susp_nm) + 1;
+ fsw_alloc_zero(str->len + len, (void **)&tmp);
+ if (str->data != NULL)
+ {
+ fsw_memcpy(tmp, str->data, str->len);
+ fsw_free(str->data);
+ }
+ DEBUG((DEBUG_INFO, "dst:%p src:%p len:%d\n", tmp + str->len, &nm->name[0], len));
+ fsw_memcpy(tmp + str->len, &nm->name[0], len);
+ str->data = tmp;
+ str->len += len;
+
+ if ((nm->flags & RR_NM_CONT) == 0)
+ goto done;
+ }
+ }
+ r++;
+ off = (int)(r - (fsw_u8 *)begin);
+ }
+ if(fCe == 1)
+ fsw_free(begin);
+ return FSW_NOT_FOUND;
+done:
+ str->type = FSW_STRING_TYPE_ISO88591;
+ str->size = str->len;
+ if(fCe == 1)
+ fsw_free(begin);
+ return FSW_SUCCESS;
+}
+
+static fsw_status_t rr_read_ce(struct fsw_iso9660_volume *vol, union fsw_rock_ridge_susp_ce *ce, fsw_u8 *begin)
+{
+ int rc;
+ rc = vol->g.host_table->read_block(&vol->g, ISOINT(ce->X.block_loc), begin);
+ if (rc != FSW_SUCCESS)
+ return rc;
+ return FSW_SUCCESS;
+}
+/**
+ * Mount an ISO9660 volume. Reads the superblock and constructs the
+ * root directory dnode.
+ */
+
+static fsw_status_t fsw_iso9660_volume_mount(struct fsw_iso9660_volume *vol)
+{
+ fsw_status_t status;
+ void *buffer;
+ fsw_u32 blockno;
+ struct iso9660_volume_descriptor *voldesc;
+ struct iso9660_primary_volume_descriptor *pvoldesc;
+ fsw_u32 voldesc_type;
+ int i;
+ struct fsw_string s;
+ struct iso9660_dirrec rootdir;
+ int sua_pos;
+ char *sig;
+ int skip;
+ struct fsw_rock_ridge_susp_entry *entry;
+
+ // read through the Volume Descriptor Set
+ fsw_set_blocksize(vol, ISO9660_BLOCKSIZE, ISO9660_BLOCKSIZE);
+ blockno = ISO9660_SUPERBLOCK_BLOCKNO;
+
+ do {
+ status = fsw_block_get(vol, blockno, 0, &buffer);
+ if (status)
+ return status;
+
+ voldesc = (struct iso9660_volume_descriptor *)buffer;
+ voldesc_type = voldesc->volume_descriptor_type;
+ if (fsw_memeq(voldesc->standard_identifier, "CD001", 5)) {
+ // descriptor follows ISO 9660 standard
+ if (voldesc_type == 1 && voldesc->volume_descriptor_version == 1) {
+ // suitable Primary Volume Descriptor found
+ if (vol->primary_voldesc) {
+ fsw_free(vol->primary_voldesc);
+ vol->primary_voldesc = NULL;
+ }
+ status = fsw_memdup((void **)&vol->primary_voldesc, voldesc, ISO9660_BLOCKSIZE);
+ }
+ } else if (!fsw_memeq(voldesc->standard_identifier, "CD", 2)) {
+ // completely alien standard identifier, stop reading
+ voldesc_type = 255;
+ }
+
+ fsw_block_release(vol, blockno, buffer);
+ blockno++;
+ } while (!status && voldesc_type != 255);
+ if (status)
+ return status;
+
+ // get information from Primary Volume Descriptor
+ if (vol->primary_voldesc == NULL)
+ return FSW_UNSUPPORTED;
+ pvoldesc = vol->primary_voldesc;
+ if (ISOINT(pvoldesc->logical_block_size) != 2048)
+ return FSW_UNSUPPORTED;
+
+ // get volume name
+ for (i = 32; i > 0; i--)
+ if (pvoldesc->volume_identifier[i-1] != ' ')
+ break;
+ s.type = FSW_STRING_TYPE_ISO88591;
+ s.size = s.len = i;
+ s.data = pvoldesc->volume_identifier;
+ status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s);
+ if (status)
+ return status;
+
+ // setup the root dnode
+ status = fsw_dnode_create_root(vol, ISO9660_SUPERBLOCK_BLOCKNO << ISO9660_BLOCKSIZE_BITS, &vol->g.root);
+ if (status)
+ return status;
+ fsw_memcpy(&vol->g.root->dirrec, &pvoldesc->root_directory, sizeof(struct iso9660_dirrec));
+
+ if ( pvoldesc->escape[0] == 0x25
+ && pvoldesc->escape[1] == 0x2f
+ && ( pvoldesc->escape[2] == 0x40
+ || pvoldesc->escape[2] == 0x43
+ || pvoldesc->escape[2] == 0x45))
+ {
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: success (joliet!!!)\n")));
+ vol->fJoliet = 1;
+ }
+
+
+ fsw_memcpy(&rootdir, &pvoldesc->root_directory, sizeof(rootdir));
+ sua_pos = (sizeof(struct iso9660_dirrec)) + rootdir.file_identifier_length + (rootdir.file_identifier_length % 2) - 2;
+ //int sua_size = rootdir.dirrec_length - rootdir.file_identifier_length;
+ //FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: success (SUA(pos:%x, sz:%d)!!!)\n"), sua_pos, sua_size));
+
+#if 1
+ status = fsw_block_get(vol, ISOINT(rootdir.extent_location), 0, &buffer);
+ sig = (char *)buffer + sua_pos;
+ skip = 0;
+ entry = (struct fsw_rock_ridge_susp_entry *)sig;
+ if ( entry->sig[0] == 'S'
+ && entry->sig[1] == 'P')
+ {
+ struct fsw_rock_ridge_susp_sp *sp = (struct fsw_rock_ridge_susp_sp *)entry;
+ if (sp->magic[0] == 0xbe && sp->magic[1] == 0xef)
+ {
+ vol->fRockRidge = 1;
+ } else {
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: SP magic isn't valid\n")));
+ }
+ skip = sp->skip;
+ }
+#endif
+ // release volume descriptors
+ fsw_free(vol->primary_voldesc);
+ vol->primary_voldesc = NULL;
+
+
+ FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: success\n")));
+
+ return FSW_SUCCESS;
+}
+
+/**
+ * Free the volume data structure. Called by the core after an unmount or after
+ * an unsuccessful mount to release the memory used by the file system type specific
+ * part of the volume structure.
+ */
+
+static void fsw_iso9660_volume_free(struct fsw_iso9660_volume *vol)
+{
+ if (vol->primary_voldesc)
+ fsw_free(vol->primary_voldesc);
+}
+
+/**
+ * Get in-depth information on a volume.
+ */
+
+static fsw_status_t fsw_iso9660_volume_stat(struct fsw_iso9660_volume *vol, struct fsw_volume_stat *sb)
+{
+ sb->total_bytes = 0; //(fsw_u64)vol->sb->s_blocks_count * vol->g.log_blocksize;
+ sb->free_bytes = 0;
+ return FSW_SUCCESS;
+}
+
+/**
+ * Get full information on a dnode from disk. This function is called by the core
+ * whenever it needs to access fields in the dnode structure that may not
+ * be filled immediately upon creation of the dnode. In the case of iso9660, we
+ * delay fetching of the inode structure until dnode_fill is called. The size and
+ * type fields are invalid until this function has been called.
+ */
+
+static fsw_status_t fsw_iso9660_dnode_fill(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno)
+{
+ // get info from the directory record
+ dno->g.size = ISOINT(dno->dirrec.data_length);
+ if (dno->dirrec.file_flags & 0x02)
+ dno->g.type = FSW_DNODE_TYPE_DIR;
+ else
+ dno->g.type = FSW_DNODE_TYPE_FILE;
+
+ return FSW_SUCCESS;
+}
+
+/**
+ * Free the dnode data structure. Called by the core when deallocating a dnode
+ * structure to release the memory used by the file system type specific part
+ * of the dnode structure.
+ */
+
+static void fsw_iso9660_dnode_free(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno)
+{
+}
+
+/**
+ * Get in-depth information on a dnode. The core makes sure that fsw_iso9660_dnode_fill
+ * has been called on the dnode before this function is called. Note that some
+ * data is not directly stored into the structure, but passed to a host-specific
+ * callback that converts it to the host-specific format.
+ */
+
+static fsw_status_t fsw_iso9660_dnode_stat(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_dnode_stat *sb)
+{
+ sb->used_bytes = (dno->g.size + (ISO9660_BLOCKSIZE-1)) & ~(ISO9660_BLOCKSIZE-1);
+ /*
+ sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, dno->raw->i_ctime);
+ sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, dno->raw->i_atime);
+ sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, dno->raw->i_mtime);
+ sb->store_attr_posix(sb, dno->raw->i_mode);
+ */
+
+ return FSW_SUCCESS;
+}
+
+/**
+ * Retrieve file data mapping information. This function is called by the core when
+ * fsw_shandle_read needs to know where on the disk the required piece of the file's
+ * data can be found. The core makes sure that fsw_iso9660_dnode_fill has been called
+ * on the dnode before. Our task here is to get the physical disk block number for
+ * the requested logical block number.
+ */
+
+static fsw_status_t fsw_iso9660_get_extent(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_extent *extent)
+{
+ // Preconditions: The caller has checked that the requested logical block
+ // is within the file's size. The dnode has complete information, i.e.
+ // fsw_iso9660_dnode_read_info was called successfully on it.
+
+ extent->type = FSW_EXTENT_TYPE_PHYSBLOCK;
+ extent->phys_start = ISOINT(dno->dirrec.extent_location);
+ extent->log_start = 0;
+ extent->log_count = (ISOINT(dno->dirrec.data_length) + (ISO9660_BLOCKSIZE-1)) >> ISO9660_BLOCKSIZE_BITS;
+ return FSW_SUCCESS;
+}
+
+/**
+ * Lookup a directory's child dnode by name. This function is called on a directory
+ * to retrieve the directory entry with the given name. A dnode is constructed for
+ * this entry and returned. The core makes sure that fsw_iso9660_dnode_fill has been called
+ * and the dnode is actually a directory.
+ */
+
+static fsw_status_t fsw_iso9660_dir_lookup(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_string *lookup_name, struct fsw_iso9660_dnode **child_dno_out)
+{
+ fsw_status_t status;
+ struct fsw_shandle shand;
+ struct iso9660_dirrec_buffer dirrec_buffer;
+ struct iso9660_dirrec *dirrec = &dirrec_buffer.dirrec;
+
+ // Preconditions: The caller has checked that dno is a directory node.
+
+ // setup handle to read the directory
+ status = fsw_shandle_open(dno, &shand);
+ if (status)
+ return status;
+
+ // scan the directory for the file
+ while (1) {
+ // read next entry
+ status = fsw_iso9660_read_dirrec(vol, &shand, &dirrec_buffer);
+ if (status)
+ goto errorexit;
+ if (dirrec->dirrec_length == 0) {
+ // end of directory reached
+ status = FSW_NOT_FOUND;
+ goto errorexit;
+ }
+
+ // skip . and ..
+ if (dirrec->file_identifier_length == 1 &&
+ (dirrec->file_identifier[0] == 0 || dirrec->file_identifier[0] == 1))
+ continue;
+
+ // compare name
+ if (fsw_streq(lookup_name, &dirrec_buffer.name)) /// @todo compare case-insensitively
+ break;
+ }
+
+ // setup a dnode for the child item
+ status = fsw_dnode_create(dno, dirrec_buffer.ino, FSW_DNODE_TYPE_UNKNOWN, &dirrec_buffer.name, child_dno_out);
+ if (status == FSW_SUCCESS)
+ fsw_memcpy(&(*child_dno_out)->dirrec, dirrec, sizeof(struct iso9660_dirrec));
+
+errorexit:
+ fsw_shandle_close(&shand);
+ return status;
+}
+
+/**
+ * Get the next directory entry when reading a directory. This function is called during
+ * directory iteration to retrieve the next directory entry. A dnode is constructed for
+ * the entry and returned. The core makes sure that fsw_iso9660_dnode_fill has been called
+ * and the dnode is actually a directory. The shandle provided by the caller is used to
+ * record the position in the directory between calls.
+ */
+
+static fsw_status_t fsw_iso9660_dir_read(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_shandle *shand, struct fsw_iso9660_dnode **child_dno_out)
+{
+ fsw_status_t status;
+ struct iso9660_dirrec_buffer dirrec_buffer;
+ struct iso9660_dirrec *dirrec = &dirrec_buffer.dirrec;
+
+ // Preconditions: The caller has checked that dno is a directory node. The caller
+ // has opened a storage handle to the directory's storage and keeps it around between
+ // calls.
+ /* (vasily) directory nodes are 4096 bytes that is two logical blocks so read dir operation
+ * should read both blocks.
+ */
+
+ while (1) {
+ // read next entry
+ if (shand->pos >= dno->g.size)
+ return FSW_NOT_FOUND; // end of directory
+ status = fsw_iso9660_read_dirrec(vol, shand, &dirrec_buffer);
+ if (status)
+ return status;
+ if (dirrec->dirrec_length == 0)
+ {
+ // try the next block
+ shand->pos =(shand->pos & ~(vol->g.log_blocksize - 1)) + vol->g.log_blocksize;
+ continue;
+ }
+
+ // skip . and ..
+ if (dirrec->file_identifier_length == 1 &&
+ (dirrec->file_identifier[0] == 0 || dirrec->file_identifier[0] == 1))
+ continue;
+ break;
+ }
+
+ // setup a dnode for the child item
+ status = fsw_dnode_create(dno, dirrec_buffer.ino, FSW_DNODE_TYPE_UNKNOWN, &dirrec_buffer.name, child_dno_out);
+ if (status == FSW_SUCCESS)
+ fsw_memcpy(&(*child_dno_out)->dirrec, dirrec, sizeof(struct iso9660_dirrec));
+
+ return status;
+}
+
+/**
+ * Read a directory entry from the directory's raw data. This internal function is used
+ * to read a raw iso9660 directory entry into memory. The shandle's position pointer is adjusted
+ * to point to the next entry.
+ */
+
+static fsw_status_t fsw_iso9660_read_dirrec(struct fsw_iso9660_volume *vol, struct fsw_shandle *shand, struct iso9660_dirrec_buffer *dirrec_buffer)
+{
+ fsw_status_t status;
+ fsw_u32 i, buffer_size, remaining_size, name_len;
+ struct fsw_rock_ridge_susp_sp *sp = NULL;
+ struct iso9660_dirrec *dirrec = &dirrec_buffer->dirrec;
+ int rc;
+
+ dirrec_buffer->ino = (ISOINT(((struct fsw_iso9660_dnode *)shand->dnode)->dirrec.extent_location)
+ << ISO9660_BLOCKSIZE_BITS)
+ + (fsw_u32)shand->pos;
+
+ // read fixed size part of directory record
+ buffer_size = 33;
+ status = fsw_shandle_read(shand, &buffer_size, dirrec);
+ if (status)
+ {
+ DEBUG((DEBUG_INFO, "%a:%d \n", __FILE__, __LINE__));
+ return status;
+ }
+
+ if (buffer_size < 33 || dirrec->dirrec_length == 0) {
+ // end of directory reached
+ fsw_u8 *r;
+ r = (fsw_u8 *)dirrec;
+ DEBUG((DEBUG_INFO, "%a:%d bs:%d dl:%d\n", __FILE__, __LINE__, buffer_size, dirrec->dirrec_length));
+ for(i = 0; i < buffer_size; ++i)
+ {
+ DEBUG((DEBUG_INFO, "r[%d]:%c", i, r[i]));
+ }
+ dirrec->dirrec_length = 0;
+ return FSW_SUCCESS;
+ }
+ if (dirrec->dirrec_length < 33 ||
+ dirrec->dirrec_length < 33 + dirrec->file_identifier_length)
+ return FSW_VOLUME_CORRUPTED;
+
+ DEBUG((DEBUG_INFO, "%a:%d, dirrec_length: %d\n", __FILE__, __LINE__, dirrec->dirrec_length));
+
+ // read variable size part of directory record
+ buffer_size = remaining_size = dirrec->dirrec_length - 33;
+ status = fsw_shandle_read(shand, &buffer_size, dirrec->file_identifier);
+ if (status)
+ return status;
+ if (buffer_size < remaining_size)
+ return FSW_VOLUME_CORRUPTED;
+
+ if (vol->fRockRidge)
+ {
+ UINTN sp_off = sizeof(*dirrec) + dirrec->file_identifier_length;
+ rc = rr_find_sp(dirrec, &sp);
+ if ( rc == FSW_SUCCESS
+ && sp != NULL)
+ {
+ sp_off = (fsw_u8 *)&sp[1] - (fsw_u8 *)dirrec + sp->skip;
+ }
+ rc = rr_find_nm(vol, dirrec, (int)sp_off, &dirrec_buffer->name);
+ if (rc == FSW_SUCCESS)
+ return FSW_SUCCESS;
+ }
+
+ // setup name
+ name_len = dirrec->file_identifier_length;
+ for (i = name_len - 1; i > 0; i--) {
+ if (dirrec->file_identifier[i] == ';') {
+ name_len = i; // cut the ISO9660 version number off
+ break;
+ }
+ }
+ if (name_len > 0 && dirrec->file_identifier[name_len-1] == '.')
+ name_len--; // also cut the extension separator if the extension is empty
+ dirrec_buffer->name.type = FSW_STRING_TYPE_ISO88591;
+ dirrec_buffer->name.len = dirrec_buffer->name.size = name_len;
+ dirrec_buffer->name.data = dirrec->file_identifier;
+ DEBUG((DEBUG_INFO, "%a:%d: dirrec_buffer->name.data:%a\n", __FILE__, __LINE__, dirrec_buffer->name.data));
+ return FSW_SUCCESS;
+}
+
+/**
+ * Get the target path of a symbolic link. This function is called when a symbolic
+ * link needs to be resolved. The core makes sure that the fsw_iso9660_dnode_fill has been
+ * called on the dnode and that it really is a symlink.
+ *
+ * For iso9660, the target path can be stored inline in the inode structure (in the space
+ * otherwise occupied by the block pointers) or in the inode's data. There is no flag
+ * indicating this, only the number of blocks entry (i_blocks) can be used as an
+ * indication. The check used here comes from the Linux kernel.
+ */
+
+static fsw_status_t fsw_iso9660_readlink(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno,
+ struct fsw_string *link_target)
+{
+ fsw_status_t status;
+
+ if (dno->g.size > FSW_PATH_MAX)
+ return FSW_VOLUME_CORRUPTED;
+
+ status = fsw_dnode_readlink_data(dno, link_target);
+
+ return status;
+}
+
+// EOF
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.h
new file mode 100644
index 00000000..0779ab45
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_iso9660.h
@@ -0,0 +1,235 @@
+/* $Id: fsw_iso9660.h $ */
+/** @file
+ * fsw_iso9660.h - ISO9660 file system driver header.
+ */
+
+/*
+ * Copyright (C) 2010-2022 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.
+ */
+
+#ifndef _FSW_ISO9660_H_
+#define _FSW_ISO9660_H_
+
+#define VOLSTRUCTNAME fsw_iso9660_volume
+#define DNODESTRUCTNAME fsw_iso9660_dnode
+#include "fsw_core.h"
+
+
+//! Block size for ISO9660 volumes.
+#define ISO9660_BLOCKSIZE 2048
+#define ISO9660_BLOCKSIZE_BITS 11
+//! Block number where the ISO9660 superblock resides.
+#define ISO9660_SUPERBLOCK_BLOCKNO 16
+
+
+#pragma pack(1)
+
+typedef struct {
+ fsw_u16 lsb;
+ fsw_u16 msb;
+} iso9660_u16;
+
+typedef struct {
+ fsw_u32 lsb;
+ fsw_u32 msb;
+} iso9660_u32;
+
+#define ISOINT(lsbmsbvalue) ((lsbmsbvalue).lsb)
+
+struct iso9660_dirrec {
+ fsw_u8 dirrec_length;
+ fsw_u8 ear_length;
+ iso9660_u32 extent_location;
+ iso9660_u32 data_length;
+ fsw_u8 recording_datetime[7];
+ fsw_u8 file_flags;
+ fsw_u8 file_unit_size;
+ fsw_u8 interleave_gap_size;
+ iso9660_u16 volume_sequence_number;
+ fsw_u8 file_identifier_length;
+ char file_identifier[1];
+};
+//#if sizeof(struct fsw_iso9660_dirrec) != 34
+//#fail Structure fsw_iso9660_dirrec has wrong size
+//#endif
+
+struct iso9660_volume_descriptor {
+ fsw_u8 volume_descriptor_type;
+ char standard_identifier[5];
+ fsw_u8 volume_descriptor_version;
+};
+
+struct iso9660_primary_volume_descriptor {
+ fsw_u8 volume_descriptor_type;
+ char standard_identifier[5];
+ fsw_u8 volume_descriptor_version;
+ fsw_u8 unused1;
+ char system_identifier[32];
+ char volume_identifier[32];
+ fsw_u8 unused2[8];
+ iso9660_u32 volume_space_size;
+ fsw_u8 unused3[4];
+ fsw_u8 escape[3];
+ fsw_u8 unused4[25];
+ iso9660_u16 volume_set_size;
+ iso9660_u16 volume_sequence_number;
+ iso9660_u16 logical_block_size;
+ iso9660_u32 path_table_size;
+ fsw_u32 location_type_l_path_table;
+ fsw_u32 location_optional_type_l_path_table;
+ fsw_u32 location_type_m_path_table;
+ fsw_u32 location_optional_type_m_path_table;
+ struct iso9660_dirrec root_directory;
+ char volume_set_identifier[128];
+ char publisher_identifier[128];
+ char data_preparer_identifier[128];
+ char application_identifier[128];
+ char copyright_file_identifier[37];
+ char abstract_file_identifier[37];
+ char bibliographic_file_identifier[37];
+ char volume_creation_datetime[17];
+ char volume_modification_datetime[17];
+ char volume_expiration_datetime[17];
+ char volume_effective_datetime[17];
+ fsw_u8 file_structure_version;
+ fsw_u8 reserved1;
+ fsw_u8 application_use[512];
+ fsw_u8 reserved2[653];
+};
+//#if sizeof(struct fsw_iso9660_volume_descriptor) != 2048
+//#fail Structure fsw_iso9660_volume_descriptor has wrong size
+//#endif
+
+#pragma pack()
+
+struct iso9660_dirrec_buffer {
+ fsw_u32 ino;
+ struct fsw_string name;
+ struct iso9660_dirrec dirrec;
+ char dirrec_buffer[222];
+};
+
+
+/**
+ * ISO9660: Volume structure with ISO9660-specific data.
+ */
+
+struct fsw_iso9660_volume {
+ struct fsw_volume g; //!< Generic volume structure
+ /*Note: don't move g!*/
+ int fJoliet;
+ /*Joliet specific fields*/
+ int fRockRidge;
+ /*Rock Ridge specific fields*/
+ int rr_susp_skip;
+
+ struct iso9660_primary_volume_descriptor *primary_voldesc; //!< Full Primary Volume Descriptor
+};
+
+/**
+ * ISO9660: Dnode structure with ISO9660-specific data.
+ */
+
+struct fsw_iso9660_dnode {
+ struct fsw_dnode g; //!< Generic dnode structure
+
+ struct iso9660_dirrec dirrec; //!< Fixed part of the directory record (i.e. w/o name)
+};
+
+
+struct fsw_rock_ridge_susp_entry
+{
+ fsw_u8 sig[2];
+ fsw_u8 len;
+ fsw_u8 ver;
+};
+
+struct fsw_rock_ridge_susp_sp
+{
+ struct fsw_rock_ridge_susp_entry e;
+ fsw_u8 magic[2];
+ fsw_u8 skip;
+};
+
+struct fsw_rock_ridge_susp_nm
+{
+ struct fsw_rock_ridge_susp_entry e;
+ fsw_u8 flags;
+ fsw_u8 name[1];
+};
+
+#define RR_NM_CONT (1<<0)
+#define RR_NM_CURR (1<<1)
+#define RR_NM_PARE (1<<2)
+
+union fsw_rock_ridge_susp_ce
+{
+ struct X{
+ struct fsw_rock_ridge_susp_entry e;
+ iso9660_u32 block_loc;
+ iso9660_u32 offset;
+ iso9660_u32 len;
+ } X;
+ fsw_u8 raw[28];
+};
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_lib.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_lib.c
new file mode 100644
index 00000000..64a0a5cf
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_lib.c
@@ -0,0 +1,564 @@
+/* $Id: fsw_lib.c $ */
+/** @file
+ * fsw_lib.c - Core file system wrapper library functions.
+ */
+
+/*
+ * Copyright (C) 2010-2022 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_core.h"
+
+/* Include generated string encoding specific functions */
+#include "fsw_strfunc.h"
+
+
+/**
+ * Allocate memory and clear it.
+ */
+
+fsw_status_t fsw_alloc_zero(int len, void **ptr_out)
+{
+ fsw_status_t status;
+
+ status = fsw_alloc(len, ptr_out);
+ if (status)
+ return status;
+ fsw_memzero(*ptr_out, len);
+ return FSW_SUCCESS;
+}
+
+/**
+ * Duplicate a piece of data.
+ */
+
+fsw_status_t fsw_memdup(void **dest_out, void *src, int len)
+{
+ fsw_status_t status;
+
+ status = fsw_alloc(len, dest_out);
+ if (status)
+ return status;
+ fsw_memcpy(*dest_out, src, len);
+ return FSW_SUCCESS;
+}
+
+/**
+ * Get the length of a string. Returns the number of characters in the string.
+ */
+
+int fsw_strlen(struct fsw_string *s)
+{
+ if (s->type == FSW_STRING_TYPE_EMPTY)
+ return 0;
+ return s->len;
+}
+
+#if 0
+static const fsw_u16
+fsw_lower_case_table[] =
+{
+
+ /* High-byte indices ( == 0 iff no case mapping and no ignorables ) */
+
+ /* 0 */ 0x0000, 0x0100, 0x0000, 0x0200, 0x0300, 0x0400, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 1 */ 0x0500, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 2 */ 0x0600, 0x0700, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x0900,
+
+ /* Table 1 (for high byte 0x01) */
+
+ /* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F,
+ /* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F,
+ /* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127, 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F,
+ /* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140,
+ /* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147, 0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F,
+ /* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F,
+ /* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167, 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F,
+ /* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F,
+ /* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259,
+ /* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275,
+ /* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8, 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF,
+ /* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292, 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF,
+ /* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9, 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF,
+ /* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF,
+ /* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7, 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF,
+ /* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF,
+
+ /* Table 2 (for high byte 0x03) */
+
+ /* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
+ /* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
+ /* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
+ /* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
+ /* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
+ /* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
+ /* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
+ /* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F,
+ /* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F,
+ /* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ /* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+ /* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ /* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF,
+ /* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7, 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF,
+ /* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7, 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF,
+ /* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7, 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF,
+
+ /* Table 3 (for high byte 0x04) */
+
+ /* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407, 0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F,
+ /* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ /* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ /* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ /* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ /* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,
+ /* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, 0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F,
+ /* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477, 0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F,
+ /* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F,
+ /* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, 0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F,
+ /* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7, 0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF,
+ /* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7, 0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF,
+ /* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8, 0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF,
+ /* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7, 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF,
+ /* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7, 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF,
+ /* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7, 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF,
+
+ /* Table 4 (for high byte 0x05) */
+
+ /* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F,
+ /* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F,
+ /* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F,
+ /* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
+ /* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
+ /* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557, 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F,
+ /* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
+ /* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
+ /* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F,
+ /* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F,
+ /* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF,
+ /* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
+ /* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7, 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF,
+ /* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+ /* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF,
+ /* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7, 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF,
+
+ /* Table 5 (for high byte 0x10) */
+
+ /* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F,
+ /* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F,
+ /* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F,
+ /* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F,
+ /* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F,
+ /* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F,
+ /* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F,
+ /* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077, 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F,
+ /* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F,
+ /* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F,
+ /* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
+ /* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
+ /* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7, 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF,
+ /* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
+ /* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
+ /* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7, 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF,
+
+ /* Table 6 (for high byte 0x20) */
+
+ /* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017, 0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F,
+ /* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027, 0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F,
+ /* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037, 0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F,
+ /* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047, 0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F,
+ /* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F,
+ /* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067, 0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F,
+ /* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F,
+ /* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097, 0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F,
+ /* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7, 0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF,
+ /* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7, 0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF,
+ /* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7, 0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF,
+ /* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF,
+ /* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7, 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF,
+ /* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7, 0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF,
+
+ /* Table 7 (for high byte 0x21) */
+
+ /* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F,
+ /* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F,
+ /* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F,
+ /* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F,
+ /* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F,
+ /* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F,
+ /* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
+ /* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
+ /* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187, 0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F,
+ /* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F,
+ /* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7, 0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF,
+ /* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7, 0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF,
+ /* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7, 0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF,
+ /* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7, 0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF,
+ /* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7, 0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF,
+ /* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7, 0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF,
+
+ /* Table 8 (for high byte 0xFE) */
+
+ /* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07, 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F,
+ /* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17, 0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F,
+ /* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27, 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F,
+ /* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37, 0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F,
+ /* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47, 0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F,
+ /* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57, 0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F,
+ /* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67, 0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F,
+ /* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77, 0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F,
+ /* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87, 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F,
+ /* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97, 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F,
+ /* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF,
+ /* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF,
+ /* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF,
+ /* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7, 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF,
+ /* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF,
+ /* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000,
+
+ /* Table 9 (for high byte 0xFF) */
+
+ /* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07, 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F,
+ /* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F,
+ /* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
+ /* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F,
+ /* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
+ /* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F,
+ /* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F,
+ /* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F,
+ /* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F,
+ /* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F,
+ /* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7, 0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF,
+ /* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7, 0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF,
+ /* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7, 0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF,
+ /* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7, 0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF,
+ /* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7, 0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF,
+ /* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7, 0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF,
+};
+#endif
+
+static const fsw_u16 fsw_latin_case_fold[] =
+{
+ /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF,
+ /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF,
+};
+
+
+fsw_u16 fsw_to_lower(fsw_u16 ch)
+{
+#if 0
+ fsw_u16 temp = temp;
+#endif
+
+ if (ch < 0x0100)
+ return fsw_latin_case_fold[ch];
+#if 0
+ /*
+ * Uncomment this along with above huge table (fsw_lower_case_table)
+ * for full UTF-16 case insensitivity
+ */
+ temp = fsw_lower_case_table[ch>>8];
+ if (temp != 0)
+ return fsw_lower_case_table[temp + (ch & 0x00FF)];
+#endif
+
+ return ch;
+}
+
+/**
+ * Compare two strings for equality. The two strings are compared, taking their
+ * encoding into account. If they are considered equal, boolean true is returned.
+ * Otherwise, boolean false is returned.
+ */
+
+int fsw_streq(struct fsw_string *s1, struct fsw_string *s2)
+{
+ struct fsw_string temp_s;
+
+ // handle empty strings
+ if (s1->type == FSW_STRING_TYPE_EMPTY) {
+ temp_s.type = FSW_STRING_TYPE_ISO88591;
+ temp_s.size = temp_s.len = 0;
+ temp_s.data = NULL;
+ return fsw_streq(&temp_s, s2);
+ }
+ if (s2->type == FSW_STRING_TYPE_EMPTY) {
+ temp_s.type = FSW_STRING_TYPE_ISO88591;
+ temp_s.size = temp_s.len = 0;
+ temp_s.data = NULL;
+ return fsw_streq(s1, &temp_s);
+ }
+
+ // check length (count of chars)
+ if (s1->len != s2->len)
+ return 0;
+ if (s1->len == 0) // both strings are empty
+ return 1;
+
+ if (s1->type == s2->type) {
+ // same type, do a dumb memory compare
+ if (s1->size != s2->size)
+ return 0;
+ return fsw_memeq(s1->data, s2->data, s1->size);
+ }
+
+ // dispatch to type-specific functions
+ #define STREQ_DISPATCH(type1, type2) \
+ if (s1->type == FSW_STRING_TYPE_##type1 && s2->type == FSW_STRING_TYPE_##type2) \
+ return fsw_streq_##type1##_##type2(s1->data, s2->data, s1->len); \
+ if (s2->type == FSW_STRING_TYPE_##type1 && s1->type == FSW_STRING_TYPE_##type2) \
+ return fsw_streq_##type1##_##type2(s2->data, s1->data, s1->len);
+ STREQ_DISPATCH(ISO88591, UTF8);
+ STREQ_DISPATCH(ISO88591, UTF16);
+ STREQ_DISPATCH(ISO88591, UTF16_SWAPPED);
+ STREQ_DISPATCH(UTF8, UTF16);
+ STREQ_DISPATCH(UTF8, UTF16_SWAPPED);
+ STREQ_DISPATCH(UTF16, UTF16_SWAPPED);
+
+ // final fallback
+ return 0;
+}
+
+/**
+ * Compare a string with a C string constant. This sets up a string descriptor
+ * for the string constant (second argument) and runs fsw_streq on the two
+ * strings. Currently the C string is interpreted as ISO 8859-1.
+ * Returns boolean true if the strings are considered equal, boolean false otherwise.
+ */
+
+int fsw_streq_cstr(struct fsw_string *s1, const char *s2)
+{
+ struct fsw_string temp_s;
+ int i;
+
+ for (i = 0; s2[i]; i++)
+ ;
+
+ temp_s.type = FSW_STRING_TYPE_ISO88591;
+ temp_s.size = temp_s.len = i;
+ temp_s.data = (char *)s2;
+
+ return fsw_streq(s1, &temp_s);
+}
+
+/**
+ * Creates a duplicate of a string, converting it to the given encoding during the copy.
+ * If the function returns FSW_SUCCESS, the caller must free the string later with
+ * fsw_strfree.
+ */
+
+fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src)
+{
+ fsw_status_t status;
+
+ if (src->type == FSW_STRING_TYPE_EMPTY || src->len == 0) {
+ dest->type = type;
+ dest->size = dest->len = 0;
+ dest->data = NULL;
+ return FSW_SUCCESS;
+ }
+
+ if (src->type == type) {
+ dest->type = type;
+ dest->len = src->len;
+ dest->size = src->size;
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ fsw_memcpy(dest->data, src->data, dest->size);
+ return FSW_SUCCESS;
+ }
+
+ // dispatch to type-specific functions
+ #define STRCOERCE_DISPATCH(type1, type2) \
+ if (src->type == FSW_STRING_TYPE_##type1 && type == FSW_STRING_TYPE_##type2) \
+ return fsw_strcoerce_##type1##_##type2(src->data, src->len, dest);
+ STRCOERCE_DISPATCH(UTF8, ISO88591);
+ STRCOERCE_DISPATCH(UTF16, ISO88591);
+ STRCOERCE_DISPATCH(UTF16_SWAPPED, ISO88591);
+ STRCOERCE_DISPATCH(ISO88591, UTF8);
+ STRCOERCE_DISPATCH(UTF16, UTF8);
+ STRCOERCE_DISPATCH(UTF16_SWAPPED, UTF8);
+ STRCOERCE_DISPATCH(ISO88591, UTF16);
+ STRCOERCE_DISPATCH(UTF8, UTF16);
+ STRCOERCE_DISPATCH(UTF16_SWAPPED, UTF16);
+
+ return FSW_UNSUPPORTED;
+}
+
+/**
+ * Splits a string at the first occurrence of the separator character.
+ * The buffer string is searched for the separator character. If it is found, the
+ * element string descriptor is filled to point at the part of the buffer string
+ * before the separator. The buffer string itself is adjusted to point at the
+ * remaining part of the string (without the separator).
+ *
+ * If the separator is not found in the buffer string, then element is changed to
+ * point at the whole buffer string, and the buffer string itself is changed into
+ * an empty string.
+ *
+ * This function only manipulates the pointers and lengths in the two string descriptors,
+ * it does not change the actual string. If the buffer string is dynamically allocated,
+ * you must make a copy of it so that you can release it later.
+ */
+
+void fsw_strsplit(struct fsw_string *element, struct fsw_string *buffer, char separator)
+{
+ int i, maxlen;
+
+ if (buffer->type == FSW_STRING_TYPE_EMPTY || buffer->len == 0) {
+ element->type = FSW_STRING_TYPE_EMPTY;
+ return;
+ }
+
+ maxlen = buffer->len;
+ *element = *buffer;
+
+ if (buffer->type == FSW_STRING_TYPE_ISO88591) {
+ fsw_u8 *p;
+
+ p = (fsw_u8 *)element->data;
+ for (i = 0; i < maxlen; i++, p++) {
+ if (*p == separator) {
+ buffer->data = p + 1;
+ buffer->len -= i + 1;
+ break;
+ }
+ }
+ element->len = i;
+ if (i == maxlen) {
+ buffer->data = p;
+ buffer->len -= i;
+ }
+
+ element->size = element->len;
+ buffer->size = buffer->len;
+
+ } else if (buffer->type == FSW_STRING_TYPE_UTF16) {
+ fsw_u16 *p;
+
+ p = (fsw_u16 *)element->data;
+ for (i = 0; i < maxlen; i++, p++) {
+ if (*p == separator) {
+ buffer->data = p + 1;
+ buffer->len -= i + 1;
+ break;
+ }
+ }
+ element->len = i;
+ if (i == maxlen) {
+ buffer->data = p;
+ buffer->len -= i;
+ }
+
+ element->size = element->len * sizeof(fsw_u16);
+ buffer->size = buffer->len * sizeof(fsw_u16);
+
+ } else {
+ // fallback
+ buffer->type = FSW_STRING_TYPE_EMPTY;
+ }
+
+ /// @todo support UTF8 and UTF16_SWAPPED
+}
+
+/**
+ * Frees the memory used by a string returned from fsw_strdup_coerce.
+ */
+
+void fsw_strfree(struct fsw_string *s)
+{
+ if (s->type != FSW_STRING_TYPE_EMPTY && s->data)
+ fsw_free(s->data);
+ s->type = FSW_STRING_TYPE_EMPTY;
+}
+
+// EOF
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_strfunc.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_strfunc.h
new file mode 100644
index 00000000..22e7b120
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_strfunc.h
@@ -0,0 +1,491 @@
+/* $Id: fsw_strfunc.h $ */
+/** @file
+ * fsw_strfunc.h
+ */
+
+/*
+ * Copyright (C) 2010-2022 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
+ */
+
+/* fsw_strfunc.h generated by mk_fsw_strfunc.py */
+
+static int fsw_streq_ISO88591_UTF8(void *s1data, void *s2data, int len)
+{
+ int i;
+ fsw_u8 *p1 = (fsw_u8 *)s1data;
+ fsw_u8 *p2 = (fsw_u8 *)s2data;
+ fsw_u32 c1, c2;
+
+ for (i = 0; i < len; i++) {
+ c1 = *p1++;
+ c2 = *p2++;
+ if ((c2 & 0xe0) == 0xc0) {
+ c2 = ((c2 & 0x1f) << 6) | (*p2++ & 0x3f);
+ } else if ((c2 & 0xf0) == 0xe0) {
+ c2 = ((c2 & 0x0f) << 12) | ((*p2++ & 0x3f) << 6);
+ c2 |= (*p2++ & 0x3f);
+ } else if ((c2 & 0xf8) == 0xf0) {
+ c2 = ((c2 & 0x07) << 18) | ((*p2++ & 0x3f) << 12);
+ c2 |= ((*p2++ & 0x3f) << 6);
+ c2 |= (*p2++ & 0x3f);
+ }
+ if (c1 != c2)
+ return 0;
+ }
+ return 1;
+}
+
+#ifndef HOST_EFI
+static int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len)
+{
+ int i;
+ fsw_u8 *p1 = (fsw_u8 *)s1data;
+ fsw_u16 *p2 = (fsw_u16 *)s2data;
+ fsw_u32 c1, c2;
+
+ for (i = 0; i < len; i++) {
+ c1 = *p1++;
+ c2 = *p2++;
+ if (c1 != c2)
+ return 0;
+ }
+ return 1;
+}
+#endif
+
+static int fsw_streq_ISO88591_UTF16_SWAPPED(void *s1data, void *s2data, int len)
+{
+ int i;
+ fsw_u8 *p1 = (fsw_u8 *)s1data;
+ fsw_u16 *p2 = (fsw_u16 *)s2data;
+ fsw_u32 c1, c2;
+
+ for (i = 0; i < len; i++) {
+ c1 = *p1++;
+ c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2);
+ if (c1 != c2)
+ return 0;
+ }
+ return 1;
+}
+
+static int fsw_streq_UTF8_UTF16(void *s1data, void *s2data, int len)
+{
+ int i;
+ fsw_u8 *p1 = (fsw_u8 *)s1data;
+ fsw_u16 *p2 = (fsw_u16 *)s2data;
+ fsw_u32 c1, c2;
+
+ for (i = 0; i < len; i++) {
+ c1 = *p1++;
+ if ((c1 & 0xe0) == 0xc0) {
+ c1 = ((c1 & 0x1f) << 6) | (*p1++ & 0x3f);
+ } else if ((c1 & 0xf0) == 0xe0) {
+ c1 = ((c1 & 0x0f) << 12) | ((*p1++ & 0x3f) << 6);
+ c1 |= (*p1++ & 0x3f);
+ } else if ((c1 & 0xf8) == 0xf0) {
+ c1 = ((c1 & 0x07) << 18) | ((*p1++ & 0x3f) << 12);
+ c1 |= ((*p1++ & 0x3f) << 6);
+ c1 |= (*p1++ & 0x3f);
+ }
+ c2 = *p2++;
+ if (c1 != c2)
+ return 0;
+ }
+ return 1;
+}
+
+static int fsw_streq_UTF8_UTF16_SWAPPED(void *s1data, void *s2data, int len)
+{
+ int i;
+ fsw_u8 *p1 = (fsw_u8 *)s1data;
+ fsw_u16 *p2 = (fsw_u16 *)s2data;
+ fsw_u32 c1, c2;
+
+ for (i = 0; i < len; i++) {
+ c1 = *p1++;
+ if ((c1 & 0xe0) == 0xc0) {
+ c1 = ((c1 & 0x1f) << 6) | (*p1++ & 0x3f);
+ } else if ((c1 & 0xf0) == 0xe0) {
+ c1 = ((c1 & 0x0f) << 12) | ((*p1++ & 0x3f) << 6);
+ c1 |= (*p1++ & 0x3f);
+ } else if ((c1 & 0xf8) == 0xf0) {
+ c1 = ((c1 & 0x07) << 18) | ((*p1++ & 0x3f) << 12);
+ c1 |= ((*p1++ & 0x3f) << 6);
+ c1 |= (*p1++ & 0x3f);
+ }
+ c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2);
+ if (c1 != c2)
+ return 0;
+ }
+ return 1;
+}
+
+static int fsw_streq_UTF16_UTF16_SWAPPED(void *s1data, void *s2data, int len)
+{
+ int i;
+ fsw_u16 *p1 = (fsw_u16 *)s1data;
+ fsw_u16 *p2 = (fsw_u16 *)s2data;
+ fsw_u32 c1, c2;
+
+ for (i = 0; i < len; i++) {
+ c1 = *p1++;
+ c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2);
+ if (c1 != c2)
+ return 0;
+ }
+ return 1;
+}
+
+static fsw_status_t fsw_strcoerce_UTF8_ISO88591(void *srcdata, int srclen, struct fsw_string *dest)
+{
+ fsw_status_t status;
+ int i;
+ fsw_u8 *sp;
+ fsw_u8 *dp;
+ fsw_u32 c;
+
+ dest->type = FSW_STRING_TYPE_ISO88591;
+ dest->len = srclen;
+ dest->size = srclen * sizeof(fsw_u8);
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ sp = (fsw_u8 *)srcdata;
+ dp = (fsw_u8 *)dest->data;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++;
+ if ((c & 0xe0) == 0xc0) {
+ c = ((c & 0x1f) << 6) | (*sp++ & 0x3f);
+ } else if ((c & 0xf0) == 0xe0) {
+ c = ((c & 0x0f) << 12) | ((*sp++ & 0x3f) << 6);
+ c |= (*sp++ & 0x3f);
+ } else if ((c & 0xf8) == 0xf0) {
+ c = ((c & 0x07) << 18) | ((*sp++ & 0x3f) << 12);
+ c |= ((*sp++ & 0x3f) << 6);
+ c |= (*sp++ & 0x3f);
+ }
+ *dp++ = (fsw_u8)c;
+ }
+ return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_ISO88591(void *srcdata, int srclen, struct fsw_string *dest)
+{
+ fsw_status_t status;
+ int i;
+ fsw_u16 *sp;
+ fsw_u8 *dp;
+ fsw_u32 c;
+
+ dest->type = FSW_STRING_TYPE_ISO88591;
+ dest->len = srclen;
+ dest->size = srclen * sizeof(fsw_u8);
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ sp = (fsw_u16 *)srcdata;
+ dp = (fsw_u8 *)dest->data;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++;
+ *dp++ = (fsw_u8)c;
+ }
+ return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_ISO88591(void *srcdata, int srclen, struct fsw_string *dest)
+{
+ fsw_status_t status;
+ int i;
+ fsw_u16 *sp;
+ fsw_u8 *dp;
+ fsw_u32 c;
+
+ dest->type = FSW_STRING_TYPE_ISO88591;
+ dest->len = srclen;
+ dest->size = srclen * sizeof(fsw_u8);
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ sp = (fsw_u16 *)srcdata;
+ dp = (fsw_u8 *)dest->data;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++; c = FSW_SWAPVALUE_U16(c);
+ *dp++ = (fsw_u8)c;
+ }
+ return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_ISO88591_UTF16(void *srcdata, int srclen, struct fsw_string *dest)
+{
+ fsw_status_t status;
+ int i;
+ fsw_u8 *sp;
+ fsw_u16 *dp;
+ fsw_u32 c;
+
+ dest->type = FSW_STRING_TYPE_UTF16;
+ dest->len = srclen;
+ dest->size = srclen * sizeof(fsw_u16);
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ sp = (fsw_u8 *)srcdata;
+ dp = (fsw_u16 *)dest->data;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++;
+ *dp++ = (fsw_u16)c;
+ }
+ return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF8_UTF16(void *srcdata, int srclen, struct fsw_string *dest)
+{
+ fsw_status_t status;
+ int i;
+ fsw_u8 *sp;
+ fsw_u16 *dp;
+ fsw_u32 c;
+
+ dest->type = FSW_STRING_TYPE_UTF16;
+ dest->len = srclen;
+ dest->size = srclen * sizeof(fsw_u16);
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ sp = (fsw_u8 *)srcdata;
+ dp = (fsw_u16 *)dest->data;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++;
+ if ((c & 0xe0) == 0xc0) {
+ c = ((c & 0x1f) << 6) | (*sp++ & 0x3f);
+ } else if ((c & 0xf0) == 0xe0) {
+ c = ((c & 0x0f) << 12) | ((*sp++ & 0x3f) << 6);
+ c |= (*sp++ & 0x3f);
+ } else if ((c & 0xf8) == 0xf0) {
+ c = ((c & 0x07) << 18) | ((*sp++ & 0x3f) << 12);
+ c |= ((*sp++ & 0x3f) << 6);
+ c |= (*sp++ & 0x3f);
+ }
+ *dp++ = (fsw_u16)c;
+ }
+ return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_UTF16(void *srcdata, int srclen, struct fsw_string *dest)
+{
+ fsw_status_t status;
+ int i;
+ fsw_u16 *sp;
+ fsw_u16 *dp;
+ fsw_u32 c;
+
+ dest->type = FSW_STRING_TYPE_UTF16;
+ dest->len = srclen;
+ dest->size = srclen * sizeof(fsw_u16);
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ sp = (fsw_u16 *)srcdata;
+ dp = (fsw_u16 *)dest->data;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++; c = FSW_SWAPVALUE_U16(c);
+ *dp++ = (fsw_u16)c;
+ }
+ return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_ISO88591_UTF8(void *srcdata, int srclen, struct fsw_string *dest)
+{
+ fsw_status_t status;
+ int i, destsize;
+ fsw_u8 *sp;
+ fsw_u8 *dp;
+ fsw_u32 c;
+
+ sp = (fsw_u8 *)srcdata;
+ destsize = 0;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++;
+
+ if (c < 0x000080)
+ destsize++;
+ else if (c < 0x000800)
+ destsize += 2;
+ else if (c < 0x010000)
+ destsize += 3;
+ else
+ destsize += 4;
+ }
+
+ dest->type = FSW_STRING_TYPE_UTF8;
+ dest->len = srclen;
+ dest->size = destsize;
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ sp = (fsw_u8 *)srcdata;
+ dp = (fsw_u8 *)dest->data;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++;
+
+ if (c < 0x000080) {
+ *dp++ = (fsw_u8)c;
+ } else if (c < 0x000800) {
+ *dp++ = (fsw_u8)(0xc0 | ((c >> 6) & 0x1f));
+ *dp++ = (fsw_u8)(0x80 | (c & 0x3f));
+ } else if (c < 0x010000) {
+ *dp++ = (fsw_u8)(0xe0 | ((c >> 12) & 0x0f));
+ *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f));
+ *dp++ = (fsw_u8)(0x80 | (c & 0x3f));
+ } else {
+ *dp++ = (fsw_u8)(0xf0 | ((c >> 18) & 0x07));
+ *dp++ = (fsw_u8)(0x80 | ((c >> 12) & 0x3f));
+ *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f));
+ *dp++ = (fsw_u8)(0x80 | (c & 0x3f));
+ }
+ }
+ return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_UTF8(void *srcdata, int srclen, struct fsw_string *dest)
+{
+ fsw_status_t status;
+ int i, destsize;
+ fsw_u16 *sp;
+ fsw_u8 *dp;
+ fsw_u32 c;
+
+ sp = (fsw_u16 *)srcdata;
+ destsize = 0;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++;
+
+ if (c < 0x000080)
+ destsize++;
+ else if (c < 0x000800)
+ destsize += 2;
+ else if (c < 0x010000)
+ destsize += 3;
+ else
+ destsize += 4;
+ }
+
+ dest->type = FSW_STRING_TYPE_UTF8;
+ dest->len = srclen;
+ dest->size = destsize;
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ sp = (fsw_u16 *)srcdata;
+ dp = (fsw_u8 *)dest->data;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++;
+
+ if (c < 0x000080) {
+ *dp++ = (fsw_u8)c;
+ } else if (c < 0x000800) {
+ *dp++ = (fsw_u8)(0xc0 | ((c >> 6) & 0x1f));
+ *dp++ = (fsw_u8)(0x80 | (c & 0x3f));
+ } else if (c < 0x010000) {
+ *dp++ = (fsw_u8)(0xe0 | ((c >> 12) & 0x0f));
+ *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f));
+ *dp++ = (fsw_u8)(0x80 | (c & 0x3f));
+ } else {
+ *dp++ = (fsw_u8)(0xf0 | ((c >> 18) & 0x07));
+ *dp++ = (fsw_u8)(0x80 | ((c >> 12) & 0x3f));
+ *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f));
+ *dp++ = (fsw_u8)(0x80 | (c & 0x3f));
+ }
+ }
+ return FSW_SUCCESS;
+}
+
+static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_UTF8(void *srcdata, int srclen, struct fsw_string *dest)
+{
+ fsw_status_t status;
+ int i, destsize;
+ fsw_u16 *sp;
+ fsw_u8 *dp;
+ fsw_u32 c;
+
+ sp = (fsw_u16 *)srcdata;
+ destsize = 0;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++; c = FSW_SWAPVALUE_U16(c);
+
+ if (c < 0x000080)
+ destsize++;
+ else if (c < 0x000800)
+ destsize += 2;
+ else if (c < 0x010000)
+ destsize += 3;
+ else
+ destsize += 4;
+ }
+
+ dest->type = FSW_STRING_TYPE_UTF8;
+ dest->len = srclen;
+ dest->size = destsize;
+ status = fsw_alloc(dest->size, &dest->data);
+ if (status)
+ return status;
+
+ sp = (fsw_u16 *)srcdata;
+ dp = (fsw_u8 *)dest->data;
+ for (i = 0; i < srclen; i++) {
+ c = *sp++; c = FSW_SWAPVALUE_U16(c);
+
+ if (c < 0x000080) {
+ *dp++ = (fsw_u8)c;
+ } else if (c < 0x000800) {
+ *dp++ = (fsw_u8)(0xc0 | ((c >> 6) & 0x1f));
+ *dp++ = (fsw_u8)(0x80 | (c & 0x3f));
+ } else if (c < 0x010000) {
+ *dp++ = (fsw_u8)(0xe0 | ((c >> 12) & 0x0f));
+ *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f));
+ *dp++ = (fsw_u8)(0x80 | (c & 0x3f));
+ } else {
+ *dp++ = (fsw_u8)(0xf0 | ((c >> 18) & 0x07));
+ *dp++ = (fsw_u8)(0x80 | ((c >> 12) & 0x3f));
+ *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f));
+ *dp++ = (fsw_u8)(0x80 | (c & 0x3f));
+ }
+ }
+ return FSW_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/Makefile b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/Makefile
new file mode 100644
index 00000000..376bbba7
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/Makefile
@@ -0,0 +1,32 @@
+
+FS=hfs
+
+SOURCES += ../fsw_core.c
+SOURCES += ../fsw_$(FS).c
+SOURCES += ../fsw_lib.c
+SOURCES += fsw_posix.c
+
+OBJ = ${foreach src,$(SOURCES),$(addsuffix .o, $(basename $(src)))}
+
+#lslr.c
+#lsroot.c
+CPPFLAGS += -DHOST_POSIX
+CPPFLAGS += -DFSTYPE=$(FS)
+ASFLAGS += -include asm.h
+CPPFLAGS += -I..
+CPPFLAGS += -I.
+CFLAGS += -g -gdwarf-2
+CFLAGS += -m32
+
+.PHONY: all clear
+all:lslr lsroot
+clean:
+ $(RM) $(OBJ) lslr lsroot lslr.o lsroot.o
+
+lslr: OBJ += lslr.o
+lslr: $(OBJ)
+
+lsroot: OBJ += lsroot.o
+lsroot: $(OBJ)
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/README b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/README
new file mode 100644
index 00000000..c7c34e4d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/README
@@ -0,0 +1,2 @@
+This folder contains tests for VBoxFsDxe module, allowing up
+and test filesystems without EFI environment and launching whole VBox.
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.c
new file mode 100644
index 00000000..6e8255c6
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.c
@@ -0,0 +1,483 @@
+/**
+ * \file fsw_posix.c
+ * POSIX user space host environment code.
+ */
+
+/*-
+ * 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_posix.h"
+
+
+#ifndef FSTYPE
+/** The file system type name to use. */
+#define FSTYPE ext2
+#endif
+
+
+// function prototypes
+
+fsw_status_t fsw_posix_open_dno(struct fsw_posix_volume *pvol, const char *path, int required_type,
+ struct fsw_shandle *shand);
+
+void fsw_posix_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_posix_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer);
+
+/**
+ * Dispatch table for our FSW host driver.
+ */
+
+struct fsw_host_table fsw_posix_host_table = {
+ FSW_STRING_TYPE_ISO88591,
+
+ fsw_posix_change_blocksize,
+ fsw_posix_read_block
+};
+
+extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE);
+
+
+/**
+ * Mount function.
+ */
+
+struct fsw_posix_volume * fsw_posix_mount(const char *path, struct fsw_fstype_table *fstype_table)
+{
+ fsw_status_t status;
+ struct fsw_posix_volume *pvol;
+
+ // allocate volume structure
+ status = fsw_alloc_zero(sizeof(struct fsw_posix_volume), (void **)&pvol);
+ if (status)
+ return NULL;
+ pvol->fd = -1;
+
+ // open underlying file/device
+ pvol->fd = open(path, O_RDONLY | O_BINARY, 0);
+ if (pvol->fd < 0) {
+ fprintf(stderr, "fsw_posix_mount: %s: %s\n", path, strerror(errno));
+ fsw_free(pvol);
+ return NULL;
+ }
+
+ // mount the filesystem
+ if (fstype_table == NULL)
+ fstype_table = &FSW_FSTYPE_TABLE_NAME(FSTYPE);
+ status = fsw_mount(pvol, &fsw_posix_host_table, fstype_table, &pvol->vol);
+ if (status) {
+ fprintf(stderr, "fsw_posix_mount: fsw_mount returned %d\n", status);
+ fsw_free(pvol);
+ return NULL;
+ }
+
+ return pvol;
+}
+
+/**
+ * Unmount function.
+ */
+
+int fsw_posix_unmount(struct fsw_posix_volume *pvol)
+{
+ if (pvol->vol != NULL)
+ fsw_unmount(pvol->vol);
+ fsw_free(pvol);
+ return 0;
+}
+
+/**
+ * Open a named regular file.
+ */
+
+struct fsw_posix_file * fsw_posix_open(struct fsw_posix_volume *pvol, const char *path, int flags, mode_t mode)
+{
+ fsw_status_t status;
+ struct fsw_posix_file *file;
+
+ // TODO: check flags for unwanted values
+
+ // allocate file structure
+ status = fsw_alloc(sizeof(struct fsw_posix_file), &file);
+ if (status)
+ return NULL;
+ file->pvol = pvol;
+
+ // open the file
+ status = fsw_posix_open_dno(pvol, path, FSW_DNODE_TYPE_FILE, &file->shand);
+ if (status) {
+ fprintf(stderr, "fsw_posix_open: open_dno returned %d\n", status);
+ fsw_free(file);
+ return NULL;
+ }
+
+ return file;
+}
+
+/**
+ * Read data from a regular file.
+ */
+
+ssize_t fsw_posix_read(struct fsw_posix_file *file, void *buf, size_t nbytes)
+{
+ fsw_status_t status;
+ fsw_u32 buffer_size;
+
+ buffer_size = nbytes;
+ status = fsw_shandle_read(&file->shand, &buffer_size, buf);
+ if (status)
+ return -1;
+ return buffer_size;
+}
+
+/**
+ * Change position within a regular file.
+ */
+
+off_t fsw_posix_lseek(struct fsw_posix_file *file, off_t offset, int whence)
+{
+ fsw_u64 base_offset = 0;
+
+ // get base offset
+ base_offset = 0;
+ if (whence == SEEK_CUR)
+ base_offset = file->shand.pos;
+ else if (whence == SEEK_END)
+ base_offset = file->shand.dnode->size;
+
+ // calculate new offset, prevent seeks before the start of the file
+ if (offset < 0 && -offset > base_offset)
+ file->shand.pos = 0;
+ else
+ file->shand.pos = base_offset + offset;
+
+ return file->shand.pos;
+}
+
+/**
+ * Close a regular file.
+ */
+
+int fsw_posix_close(struct fsw_posix_file *file)
+{
+ fsw_shandle_close(&file->shand);
+ fsw_free(file);
+ return 0;
+}
+
+/**
+ * Open a directory for iteration.
+ */
+
+struct fsw_posix_dir * fsw_posix_opendir(struct fsw_posix_volume *pvol, const char *path)
+{
+ fsw_status_t status;
+ struct fsw_posix_dir *dir;
+
+ // allocate file structure
+ status = fsw_alloc(sizeof(struct fsw_posix_dir), &dir);
+ if (status)
+ return NULL;
+ dir->pvol = pvol;
+
+ // open the directory
+ status = fsw_posix_open_dno(pvol, path, FSW_DNODE_TYPE_DIR, &dir->shand);
+ if (status) {
+ fprintf(stderr, "fsw_posix_opendir: open_dno returned %d\n", status);
+ fsw_free(dir);
+ return NULL;
+ }
+
+ return dir;
+}
+
+/**
+ * Read the next entry from a directory.
+ */
+
+struct fsw_posix_dirent * fsw_posix_readdir(struct fsw_posix_dir *dir)
+{
+ fsw_status_t status;
+ struct fsw_dnode *dno;
+ static struct fsw_posix_dirent dent;
+
+ // get next entry from file system
+ status = fsw_dnode_dir_read(&dir->shand, &dno);
+ if (status) {
+ if (status != 4)
+ fprintf(stderr, "fsw_posix_readdir: fsw_dnode_dir_read returned %d\n", status);
+ return NULL;
+ }
+ status = fsw_dnode_fill(dno);
+ if (status) {
+ fprintf(stderr, "fsw_posix_readdir: fsw_dnode_fill returned %d\n", status);
+ fsw_dnode_release(dno);
+ return NULL;
+ }
+
+ // fill dirent structure
+ dent.d_fileno = dno->dnode_id;
+ //dent.d_reclen = 8 + dno->name.size + 1;
+ switch (dno->type) {
+ case FSW_DNODE_TYPE_FILE:
+ dent.d_type = DT_REG;
+ break;
+ case FSW_DNODE_TYPE_DIR:
+ dent.d_type = DT_DIR;
+ break;
+ case FSW_DNODE_TYPE_SYMLINK:
+ dent.d_type = DT_LNK;
+ break;
+ default:
+ dent.d_type = DT_UNKNOWN;
+ break;
+ }
+#if 0
+ dent.d_namlen = dno->name.size;
+#endif
+ memcpy(dent.d_name, dno->name.data, dno->name.size);
+ dent.d_name[dno->name.size] = 0;
+
+ return &dent;
+}
+
+/**
+ * Rewind a directory to the start.
+ */
+
+void fsw_posix_rewinddir(struct fsw_posix_dir *dir)
+{
+ dir->shand.pos = 0;
+}
+
+/**
+ * Close a directory.
+ */
+
+int fsw_posix_closedir(struct fsw_posix_dir *dir)
+{
+ fsw_shandle_close(&dir->shand);
+ fsw_free(dir);
+ return 0;
+}
+
+/**
+ * Open a shand of a required type by path.
+ */
+
+fsw_status_t fsw_posix_open_dno(struct fsw_posix_volume *pvol, const char *path, int required_type, struct fsw_shandle *shand)
+{
+ fsw_status_t status;
+ struct fsw_dnode *dno;
+ struct fsw_dnode *target_dno;
+ struct fsw_string lookup_path;
+
+ lookup_path.type = FSW_STRING_TYPE_ISO88591;
+ lookup_path.len = strlen(path);
+ lookup_path.size = lookup_path.len;
+ lookup_path.data = (void *)path;
+
+ // resolve the path (symlinks along the way are automatically resolved)
+ status = fsw_dnode_lookup_path(pvol->vol->root, &lookup_path, '/', &dno);
+ if (status) {
+ fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_lookup_path returned %d\n", status);
+ return status;
+ }
+
+ // if the final node is a symlink, also resolve it
+ status = fsw_dnode_resolve(dno, &target_dno);
+ fsw_dnode_release(dno);
+ if (status) {
+ fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_resolve returned %d\n", status);
+ return status;
+ }
+ dno = target_dno;
+
+ // check that it is a regular file
+ status = fsw_dnode_fill(dno);
+ if (status) {
+ fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_fill returned %d\n", status);
+ fsw_dnode_release(dno);
+ return status;
+ }
+ if (dno->type != required_type) {
+ fprintf(stderr, "fsw_posix_open_dno: dnode is not of the requested type\n");
+ fsw_dnode_release(dno);
+ return FSW_UNSUPPORTED;
+ }
+
+ // open shandle
+ status = fsw_shandle_open(dno, shand);
+ if (status) {
+ fprintf(stderr, "fsw_posix_open_dno: fsw_shandle_open returned %d\n", status);
+ }
+ fsw_dnode_release(dno);
+ return status;
+}
+
+/**
+ * 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_posix_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_posix_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer)
+{
+ struct fsw_posix_volume *pvol = (struct fsw_posix_volume *)vol->host_data;
+ off_t block_offset, seek_result;
+ ssize_t read_result;
+
+ FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_posix_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize));
+
+ // read from disk
+ block_offset = (off_t)phys_bno * vol->phys_blocksize;
+ seek_result = lseek(pvol->fd, block_offset, SEEK_SET);
+ if (seek_result != block_offset) {
+ fprintf(stderr, "fsw_posix_read_block: failed to seek to block %u (offset %u)\n", phys_bno, block_offset);
+ return FSW_IO_ERROR;
+ }
+ read_result = read(pvol->fd, buffer, vol->phys_blocksize);
+ if (read_result != vol->phys_blocksize) {
+ fprintf(stderr, "fsw_posix_read_block: failed to read %u bytes at %u\n", vol->phys_blocksize, block_offset);
+ return FSW_IO_ERROR;
+ }
+
+ return FSW_SUCCESS;
+}
+
+
+/**
+ * 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_posix_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_posix_decode_time(&FileInfo->CreateTime, posix_time);
+ else if (which == FSW_DNODE_STAT_MTIME)
+ fsw_posix_decode_time(&FileInfo->ModificationTime, posix_time);
+ else if (which == FSW_DNODE_STAT_ATIME)
+ fsw_posix_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_posix_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_posix_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_stat sb;
+
+ // make sure the dnode has complete info
+ Status = fsw_posix_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_posix_strsize(&dno->name);
+ if (*BufferSize < RequiredSize) {
+ // TODO: wind back the directory in this case
+
+ *BufferSize = RequiredSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ // fill structure
+ ZeroMem(Buffer, RequiredSize);
+ FileInfo = (EFI_FILE_INFO *)Buffer;
+ FileInfo->Size = RequiredSize;
+ FileInfo->FileSize = dno->size;
+ FileInfo->Attribute = 0;
+ if (dno->type == FSW_DNODE_TYPE_DIR)
+ FileInfo->Attribute |= EFI_FILE_DIRECTORY;
+ fsw_posix_strcpy(FileInfo->FileName, &dno->name);
+
+ // get the missing info from the fs driver
+ ZeroMem(&sb, sizeof(struct fsw_dnode_stat));
+ sb.store_time_posix = fsw_posix_store_time_posix;
+ sb.store_attr_posix = fsw_posix_store_attr_posix;
+ sb.host_data = FileInfo;
+ Status = fsw_posix_map_status(fsw_dnode_stat(dno, &sb), Volume);
+ if (EFI_ERROR(Status))
+ return Status;
+ FileInfo->PhysicalSize = sb.used_bytes;
+
+ // prepare for return
+ *BufferSize = RequiredSize;
+ return EFI_SUCCESS;
+}
+*/
+
+// EOF
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.h
new file mode 100644
index 00000000..4df2bbca
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix.h
@@ -0,0 +1,120 @@
+/**
+ * \file fsw_posix.h
+ * POSIX user space host environment header.
+ */
+
+/*-
+ * 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.
+ */
+
+#ifndef _FSW_POSIX_H_
+#define _FSW_POSIX_H_
+
+#include "fsw_core.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+
+
+/**
+ * POSIX Host: Private per-volume structure.
+ */
+
+struct fsw_posix_volume {
+ struct fsw_volume *vol; //!< FSW volume structure
+
+ int fd; //!< System file descriptor for data access
+
+};
+
+/**
+ * POSIX Host: Private structure for an open file.
+ */
+
+struct fsw_posix_file {
+ struct fsw_posix_volume *pvol; //!< POSIX host volume structure
+
+ struct fsw_shandle shand; //!< FSW handle for this file
+
+};
+
+/**
+ * POSIX Host: Private structure for an open directory.
+ */
+
+struct fsw_posix_dir {
+ struct fsw_posix_volume *pvol; //!< POSIX host volume structure
+
+ struct fsw_shandle shand; //!< FSW handle for this file
+
+};
+
+
+#define NAME_MAX 4096
+
+#define DT_UNKNOWN 'u'
+#define DT_REG 'r'
+#define DT_DIR 'd'
+#define DT_LNK 'l'
+
+
+/**
+ * POSIX Host: Private structure for an open directory.
+ */
+
+struct fsw_posix_dirent {
+ char d_attr; /* file's attribute */
+ unsigned d_type;
+ unsigned short int d_time; /* file's time */
+ unsigned short int d_date; /* file's date */
+ long d_size; /* file's size */
+ char d_name[NAME_MAX+1]; /* file's name */
+ unsigned d_fileno; /* file number/inode */
+};
+typedef struct fsw_posix_dirent DIR;
+
+/* functions */
+
+struct fsw_posix_volume * fsw_posix_mount(const char *path, struct fsw_fstype_table *fstype_table);
+int fsw_posix_unmount(struct fsw_posix_volume *pvol);
+
+struct fsw_posix_file * fsw_posix_open(struct fsw_posix_volume *pvol, const char *path, int flags, mode_t mode);
+ssize_t fsw_posix_read(struct fsw_posix_file *file, void *buf, size_t nbytes);
+off_t fsw_posix_lseek(struct fsw_posix_file *file, off_t offset, int whence);
+int fsw_posix_close(struct fsw_posix_file *file);
+
+struct fsw_posix_dir * fsw_posix_opendir(struct fsw_posix_volume *pvol, const char *path);
+struct fsw_posix_dirent * fsw_posix_readdir(struct fsw_posix_dir *dir);
+void fsw_posix_rewinddir(struct fsw_posix_dir *dir);
+int fsw_posix_closedir(struct fsw_posix_dir *dir);
+
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix_base.h b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix_base.h
new file mode 100644
index 00000000..114e21fd
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/fsw_posix_base.h
@@ -0,0 +1,90 @@
+/**
+ * \file fsw_posix_base.h
+ * Base definitions for the POSIX user space host environment.
+ */
+
+/*-
+ * 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.
+ */
+
+#ifndef _FSW_POSIX_BASE_H_
+#define _FSW_POSIX_BASE_H_
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+
+#define FSW_LITTLE_ENDIAN (1)
+// TODO: use info from the headers to define FSW_LITTLE_ENDIAN or FSW_BIG_ENDIAN
+
+
+// types
+
+typedef int8_t fsw_s8;
+typedef uint8_t fsw_u8;
+typedef int16_t fsw_s16;
+typedef uint16_t fsw_u16;
+typedef int32_t fsw_s32;
+typedef uint32_t fsw_u32;
+typedef int64_t fsw_s64;
+typedef uint64_t fsw_u64;
+
+
+// allocation functions
+
+#define fsw_alloc(size, ptrptr) (((*(ptrptr) = malloc(size)) == NULL) ? FSW_OUT_OF_MEMORY : FSW_SUCCESS)
+#define fsw_free(ptr) free(ptr)
+
+// memory functions
+
+#define fsw_memzero(dest,size) memset(dest,0,size)
+#define fsw_memcpy(dest,src,size) memcpy(dest,src,size)
+#define fsw_memeq(p1,p2,size) (memcmp(p1,p2,size) == 0)
+
+// message printing
+
+#define FSW_MSGSTR(s) s
+#define FSW_MSGFUNC printf
+
+// 64-bit hooks
+
+#define FSW_U64_SHR(val,shiftbits) ((val) >> (shiftbits))
+#define FSW_U64_DIV(val,divisor) ((val) / (divisor))
+#define DEBUG(x)
+
+#define RShiftU64(val, shift) ((val) >> (shift))
+#define LShiftU64(val, shift) ((val) << (shift))
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lslr.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lslr.c
new file mode 100644
index 00000000..19442916
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lslr.c
@@ -0,0 +1,140 @@
+/**
+ * \file lslr.c
+ * Test program for the POSIX user space environment.
+ */
+
+/*-
+ * 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_posix.h"
+
+
+//extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext2);
+//extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(reiserfs);
+extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE);
+
+static struct fsw_fstype_table *fstypes[] = {
+ //&FSW_FSTYPE_TABLE_NAME(ext2),
+ //&FSW_FSTYPE_TABLE_NAME(reiserfs),
+ &FSW_FSTYPE_TABLE_NAME(FSTYPE ),
+ NULL
+};
+
+static int listdir(struct fsw_posix_volume *vol, char *path, int level)
+{
+ struct fsw_posix_dir *dir;
+ struct fsw_posix_dirent *dent;
+ int i;
+ char subpath[4096];
+
+ dir = fsw_posix_opendir(vol, path);
+ if (dir == NULL) {
+ printf("opendir(%s) call failed.\n", path);
+ return 1;
+ }
+ while ((dent = fsw_posix_readdir(dir)) != NULL) {
+ for (i = 0; i < level*2; i++)
+ fputc(' ', stdout);
+ printf("%c %s\n", dent->d_type, dent->d_name);
+
+ if (dent->d_type == DT_DIR) {
+ snprintf(subpath, 4095, "%s%s/", path, dent->d_name);
+ listdir(vol, subpath, level + 1);
+ }
+ }
+ fsw_posix_closedir(dir);
+
+ return 0;
+}
+
+static int catfile(struct fsw_posix_volume *vol, char *path)
+{
+ struct fsw_posix_file *file;
+ int r;
+ char buf[256];
+
+ file = fsw_posix_open(vol, path, 0, 0);
+ if (file == NULL) {
+ printf("open(%s) call failed.\n", path);
+ return 1;
+ }
+ while ((r=fsw_posix_read(file, buf, sizeof(buf))) > 0)
+ {
+ int i;
+ for (i=0; i<r; i++)
+ {
+ printf("%c", buf[i]);
+ }
+ }
+ fsw_posix_close(file);
+
+ return 0;
+}
+
+extern fsw_status_t fsw_hfs_get_blessed_file(struct fsw_hfs_volume *vol, struct fsw_string *link_target);
+
+int main(int argc, char **argv)
+{
+ struct fsw_posix_volume *vol;
+ struct fsw_string blessed;
+ int i;
+
+ if (argc != 2) {
+ printf("Usage: lslr <file/device>\n");
+ return 1;
+ }
+
+ for (i = 0; fstypes[i]; i++) {
+ vol = fsw_posix_mount(argv[1], fstypes[i]);
+ if (vol != NULL) {
+ printf("Mounted as '%s'.\n", fstypes[i]->name.data);
+ break;
+ }
+ }
+ if (vol == NULL) {
+ printf("Mounting failed.\n");
+ return 1;
+ }
+
+ listdir(vol, "/System/Library/CoreServices", 0);
+ //listdir(vol, "/System/Library/Extensions/AppleACPIPlatform.kext/", 0);
+ //listdir(vol, "/System/Library/Extensions/", 0);
+ catfile(vol, "/System/Library/CoreServices/SystemVersion.plist");
+ //listdir(vol, "/", 0);
+ fsw_hfs_get_blessed_file((struct fsw_hfs_volume *)vol->vol, &blessed);
+
+ fsw_posix_unmount(vol);
+
+ return 0;
+}
+
+// EOF
diff --git a/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lsroot.c b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lsroot.c
new file mode 100644
index 00000000..741ae4c2
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/test/lsroot.c
@@ -0,0 +1,79 @@
+/**
+ * \file lsroot.c
+ * Example program for the POSIX user space environment.
+ */
+
+/*-
+ * 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_posix.h"
+
+
+extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext2);
+extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(reiserfs);
+extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(iso9660);
+extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfs);
+
+int main(int argc, char **argv)
+{
+ struct fsw_posix_volume *vol;
+ struct fsw_posix_dir *dir;
+ struct fsw_posix_dirent *dent;
+
+ if (argc != 3) {
+ printf("Usage: lsroot <file/device> <directory>\n");
+ return 1;
+ }
+
+ //vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(ext2));
+ //vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(reiserfs));
+ vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(FSTYPE));
+ if (vol == NULL) {
+ printf("Mounting failed.\n");
+ return 1;
+ }
+ //dir = fsw_posix_opendir(vol, "/drivers/net/");
+ dir = fsw_posix_opendir(vol, argv[2]);
+ if (dir == NULL) {
+ printf("opendir call failed.\n");
+ return 1;
+ }
+ while ((dent = fsw_posix_readdir(dir)) != NULL) {
+ printf("%c %s %u\n", dent->d_type, dent->d_name, dent->d_fileno);
+ }
+ fsw_posix_closedir(dir);
+ fsw_posix_unmount(vol);
+
+ return 0;
+}
+
+// EOF