diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe')
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(¶m, 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, + ¶m); + if (status) + goto done; + + status = create_hfs_dnode(dno, ¶m.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(¶m, 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, ¶m); + 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 |