Signed-off-by: Aditya Pratap Sharma aditya.ps@samsung.com Signed-off-by: Rony Nandy rony.nandy@linaro.org
--- ExtPkg/ExtPkg.dec | 5 + ExtPkg/ExtPkg.dsc | 67 +++ ExtPkg/ExtPkgDxe/Ext.h | 43 ++ ExtPkg/ExtPkgDxe/Ext.inf | 57 ++ ExtPkg/ExtPkgDxe/fsw_core.c | 932 ++++++++++++++++++++++++++++++ ExtPkg/ExtPkgDxe/fsw_core.h | 570 +++++++++++++++++++ ExtPkg/ExtPkgDxe/fsw_efi.c | 1167 ++++++++++++++++++++++++++++++++++++++ ExtPkg/ExtPkgDxe/fsw_efi.h | 132 +++++ ExtPkg/ExtPkgDxe/fsw_efi_base.h | 89 +++ ExtPkg/ExtPkgDxe/fsw_efi_lib.c | 159 ++++++ ExtPkg/ExtPkgDxe/fsw_ext2.c | 553 ++++++++++++++++++ ExtPkg/ExtPkgDxe/fsw_ext2.h | 65 +++ ExtPkg/ExtPkgDxe/fsw_ext2_disk.h | 356 ++++++++++++ ExtPkg/ExtPkgDxe/fsw_ext4.c | 731 ++++++++++++++++++++++++ ExtPkg/ExtPkgDxe/fsw_ext4.h | 66 +++ ExtPkg/ExtPkgDxe/fsw_ext4_disk.h | 500 ++++++++++++++++ ExtPkg/ExtPkgDxe/fsw_lib.c | 336 +++++++++++ ExtPkg/ExtPkgDxe/fsw_strfunc.h | 472 +++++++++++++++ 18 files changed, 6300 insertions(+) create mode 100644 ExtPkg/ExtPkg.dec create mode 100644 ExtPkg/ExtPkg.dsc create mode 100644 ExtPkg/ExtPkgDxe/Ext.h create mode 100644 ExtPkg/ExtPkgDxe/Ext.inf create mode 100644 ExtPkg/ExtPkgDxe/fsw_core.c create mode 100644 ExtPkg/ExtPkgDxe/fsw_core.h create mode 100644 ExtPkg/ExtPkgDxe/fsw_efi.c create mode 100644 ExtPkg/ExtPkgDxe/fsw_efi.h create mode 100644 ExtPkg/ExtPkgDxe/fsw_efi_base.h create mode 100644 ExtPkg/ExtPkgDxe/fsw_efi_lib.c create mode 100644 ExtPkg/ExtPkgDxe/fsw_ext2.c create mode 100644 ExtPkg/ExtPkgDxe/fsw_ext2.h create mode 100644 ExtPkg/ExtPkgDxe/fsw_ext2_disk.h create mode 100644 ExtPkg/ExtPkgDxe/fsw_ext4.c create mode 100644 ExtPkg/ExtPkgDxe/fsw_ext4.h create mode 100644 ExtPkg/ExtPkgDxe/fsw_ext4_disk.h create mode 100644 ExtPkg/ExtPkgDxe/fsw_lib.c create mode 100644 ExtPkg/ExtPkgDxe/fsw_strfunc.h
diff --git a/ExtPkg/ExtPkg.dec b/ExtPkg/ExtPkg.dec new file mode 100644 index 0000000..2a7935a --- /dev/null +++ b/ExtPkg/ExtPkg.dec @@ -0,0 +1,5 @@ +[Defines] + DEC_SPECIFICATION = 0x00010005 + PACKAGE_NAME = Ext2Pkg + PACKAGE_GUID = 8EA68A2C-99CB-4332-85C6-DD5864EAA674 + PACKAGE_VERSION = 0.1 diff --git a/ExtPkg/ExtPkg.dsc b/ExtPkg/ExtPkg.dsc new file mode 100644 index 0000000..28e16a5 --- /dev/null +++ b/ExtPkg/ExtPkg.dsc @@ -0,0 +1,67 @@ +[Defines] + PLATFORM_NAME = Ext2 + PLATFORM_GUID = 25b55dbc-9d0b-4a32-80da-46e1273d623c + PLATFORM_VERSION = 0.1 + DSC_SPECIFICATION = 0x00010005 + SUPPORTED_ARCHITECTURES = ARM + OUTPUT_DIRECTORY = Build/Ext2 + BUILD_TARGETS = DEBUG|RELEASE + SKUID_IDENTIFIER = DEFAULT + +[BuildOptions] + GCC:RELEASE_*_*_CC_FLAGS = -DMDEPKG_NDEBUG + INTEL:RELEASE_*_*_CC_FLAGS = /D MDEPKG_NDEBUG + MSFT:RELEASE_*_*_CC_FLAGS = /D MDEPKG_NDEBUG + RVCT:RELEASE_*_*_CC_FLAGS = -DMDEPKG_NDEBUG + +[LibraryClasses] + # + # Entry Point Libraries + # + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + # + # Common Libraries + # + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + +[LibraryClasses.common.PEIM] + PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf + PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf + PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf + HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf + MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf + +[LibraryClasses.ARM] + NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf + +################################################################################################### +# +# Components Section - list of the modules and components that will be processed by compilation +# tools and the EDK II tools to generate PE32/PE32+/Coff image files. +# +# Note: The EDK II DSC file is not used to specify how compiled binary images get placed +# into firmware volume images. This section is just a list of modules to compile from +# source into UEFI-compliant binaries. +# It is the FDF file that contains information on combining binary files into firmware +# volume images, whose concept is beyond UEFI and is described in PI specification. +# Binary modules do not need to be listed in this section, as they should be +# specified in the FDF file. For example: Shell binary (Shell_Full.efi), EXT2 binary (Ext2.efi), +# Logo (Logo.bmp), and etc. +# There may also be modules listed in this section that are not required in the FDF file, +# When a module listed here is excluded from FDF file, then UEFI-compliant binary will be +# generated for it, but the binary will not be put into any firmware volume. +# +################################################################################################### + +[Components] + Ext2Pkg/Ext2PkgDxe/Ext2.inf diff --git a/ExtPkg/ExtPkgDxe/Ext.h b/ExtPkg/ExtPkgDxe/Ext.h new file mode 100644 index 0000000..76c6e8c --- /dev/null +++ b/ExtPkg/ExtPkgDxe/Ext.h @@ -0,0 +1,43 @@ +#ifndef _EXT2_H_ +#define _EXT2_H_ + +#include <Uefi.h> + +#include <Guid/FileInfo.h> +#include <Guid/FileSystemInfo.h> +#include <Guid/FileSystemVolumeLabelInfo.h> +#include <Protocol/BlockIo.h> +#include <Protocol/DiskIo.h> +#include <Protocol/SimpleFileSystem.h> +#include <Protocol/UnicodeCollation.h> + +#include <Library/PcdLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> + +// +#define IS_LEAP_YEAR(a) (((a) % 4 == 0) && (((a) % 100 != 0) || ((a) % 400 == 0))) + +#define EFI_PATH_STRING_LENGTH 260 +#define EFI_FILE_STRING_LENGTH 255 + +#define CACHE_ENABLED(a) ((a) >= 2) +#define RAW_ACCESS(a) ((IO_MODE)((a) & 0x1)) +#define CACHE_TYPE(a) ((CACHE_DATA_TYPE)((a) >> 2)) + +// +// Global Variables +// +extern EFI_DRIVER_BINDING_PROTOCOL gExtDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gExtComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gExtComponentName2; +extern EFI_LOCK ExtFsLock; +extern EFI_FILE_PROTOCOL ExtFileInterface; + +#endif diff --git a/ExtPkg/ExtPkgDxe/Ext.inf b/ExtPkg/ExtPkgDxe/Ext.inf new file mode 100644 index 0000000..3bf61bb --- /dev/null +++ b/ExtPkg/ExtPkgDxe/Ext.inf @@ -0,0 +1,57 @@ +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Ext2 + FILE_GUID = 961578FE-B6B7-44c3-AF35-6BC705CD2B2F + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = fsw_efi_main + UNLOAD_IMAGE = ExtUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = ARM +# +# DRIVER_BINDING = gExt2DriverBinding +# COMPONENT_NAME = gExt2ComponentName +# COMPONENT_NAME2 = gExt2ComponentName2 +# + +[Sources] + fsw_core.c + fsw_ext2.c + fsw_ext4.c + fsw_efi.c + fsw_efi_lib.c + fsw_lib.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + BaseLib + UefiLib + UefiDriverEntryPoint + DebugLib + PcdLib + +[Guids] + gEfiFileInfoGuid + gEfiFileSystemInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + +[Protocols] + gEfiDiskIoProtocolGuid + gEfiBlockIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiUnicodeCollationProtocolGuid + gEfiUnicodeCollation2ProtocolGuid + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultLang + gEfiMdePkgTokenSpaceGuid.PcdUefiVariableDefaultPlatformLang diff --git a/ExtPkg/ExtPkgDxe/fsw_core.c b/ExtPkg/ExtPkgDxe/fsw_core.c new file mode 100644 index 0000000..85f606b --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_core.c @@ -0,0 +1,932 @@ +/* $Id: fsw_core.c 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_core.c - Core file system wrapper abstraction layer code. + */ + +/*- + * 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_u32)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_u32)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_u32)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 = (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/ExtPkg/ExtPkgDxe/fsw_core.h b/ExtPkg/ExtPkgDxe/fsw_core.h new file mode 100644 index 0000000..b32808e --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_core.h @@ -0,0 +1,570 @@ +/* $Id: fsw_core.h 33540 2010-10-28 09:27:05Z vboxsync $ */ +/** @file + * fsw_core.h - Core file system wrapper abstraction layer header. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * 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 <Library/PcdLib.h> +#include <Library/DebugLib.h> +#include <Library/UefiLib.h> +#include <Library/BaseLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include "fsw_efi_base.h" + +#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 0 +#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 + +/** Maximum size for a path, specifically symlink target paths. */ +#ifndef HOST_EFI_EDK2 +#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 (~0UL) + + +// +// 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) + +#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 { + fsw_u32 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/ExtPkg/ExtPkgDxe/fsw_efi.c b/ExtPkg/ExtPkgDxe/fsw_efi.c new file mode 100644 index 0000000..9a4a739 --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_efi.c @@ -0,0 +1,1167 @@ +/* $Id: fsw_efi.c 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_efi.c - EFI host environment code. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * 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 "Ext.h" +#include "fsw_efi.h" +#include "fsw_core.h" + +#define DEBUG_LEVEL 0 +#define FSTYPE ext4 +#ifndef FSTYPE +#error FSTYPE must be defined! +#endif + +#define DEBUG_VBFS 0 + +#if DEBUG_VBFS==2 +#define DBG(x...) AsciiPrint(x) +#elif DEBUG_VBFS==1 +#define DBG(x...) BootLog(x) +#else +#define DBG(x...) +#endif + +/** Helper macro for stringification. */ +#define FSW_EFI_STRINGIFY(x) #x + +// 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); + +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); + +/** + * 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, + 0xa, + NULL, + NULL +}; + +//forward declaration +EFI_STATUS +EFIAPI +ExtComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +ExtComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gExtComponentName = { + ExtComponentNameGetDriverName, + ExtComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gExtComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) ExtComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) ExtComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mExtDriverNameTable[] = { + { + "eng;en", + L"Ext File System Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mExtControllerNameTable[] = { + { + "eng;en", + L"Ext File System" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +ExtComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ +#if DEBUG_LEVEL + Print(L" ExtComponentNameGetDriverName called\n"); +#endif + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mExtDriverNameTable, + DriverName, + (BOOLEAN)(This == &gExtComponentName) + ); +} + +EFI_STATUS +EFIAPI +ExtComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + EFI_STATUS Status; +#if DEBUG_LEVEL + Print(L"ExtComponentNameGetControllerName called\n"); +#endif + // + // This is a device driver, so ChildHandle must be NULL. + // + if (ChildHandle != NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver is currently managing ControllHandle + // + Status = EfiTestManagedDevice ( + ControllerHandle, + fsw_efi_DriverBinding_table.DriverBindingHandle, + &gEfiDiskIoProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mExtControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gExtComponentName) + ); +} + +/** + * 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; + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &fsw_efi_DriverBinding_table, + ImageHandle, + &gExtComponentName, + &gExtComponentName2 + ); + ASSERT_EFI_ERROR (Status); + return EFI_SUCCESS; +} + +#ifdef __MAKEWITH_GNUEFI +EFI_DRIVER_ENTRY_POINT(fsw_efi_main) +#endif + +EFI_STATUS +EFIAPI +ExtUnload ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE *DeviceHandleBuffer; + UINTN DeviceHandleCount; + UINTN Index; + + Status = gBS->LocateHandleBuffer ( + AllHandles, + NULL, + NULL, + &DeviceHandleCount, + &DeviceHandleBuffer + ); + if (!EFI_ERROR (Status)) { + for (Index = 0; Index < DeviceHandleCount; Index++) { + Status = gBS->DisconnectController ( + DeviceHandleBuffer[Index], + ImageHandle, + NULL + ); + } + + if (DeviceHandleBuffer != NULL) { + FreePool (DeviceHandleBuffer); + } + } + return Status; +} + +/** + * 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 = gBS->OpenProtocol ( + ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL + ); + if (EFI_ERROR(Status)) + return Status; + + // we were just checking, close it again + gBS->CloseProtocol(ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle); + + // next, check BlockIO without actually opening it + Status = gBS->OpenProtocol(ControllerHandle, + &gEfiBlockIoProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + 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; + +#if DEBUG_LEVEL + Print(L"fsw_efi_DriverBinding_Start\n"); +#endif + + // open consumed protocols + Status = gBS->OpenProtocol(ControllerHandle, + &gEfiBlockIoProtocolGuid, + (VOID **) &BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); // NOTE: we only want to look at the MediaId + if (EFI_ERROR(Status)) { +#if DEBUG_LEVEL + Print(L"Fsw ERROR: OpenProtocol(BlockIo) returned %x\n", Status); +#endif + return Status; + } + + Status = gBS->OpenProtocol( ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); + if (EFI_ERROR(Status)) { +#if DEBUG_LEVEL + Print(L"Fsw ERROR: OpenProtocol(DiskIo) returned %r\n", Status); +#endif + return Status; + } + + // allocate volume structure + Volume = AllocateZeroPool(sizeof(FSW_VOLUME_DATA)); + Volume->Signature = FSW_VOLUME_DATA_SIGNATURE; + Volume->Handle = ControllerHandle; + Volume->DiskIo = DiskIo; + Volume->MediaId = BlockIo->Media->MediaId; + Volume->LastIOStatus = EFI_SUCCESS; + + // mount the filesystem + Status = fsw_efi_map_status(fsw_mount(Volume, &fsw_efi_host_table, + &FSW_FSTYPE_TABLE_NAME(FSTYPE), &Volume->vol), + Volume); + + if (!EFI_ERROR(Status)) { +#if DEBUG_LEVEL + Print(L"Fsw success register the SimpleFileSystem protocol %x\n", Status); +#endif + // register the SimpleFileSystem protocol + Volume->FileSystem.Revision = EFI_FILE_IO_INTERFACE_REVISION; + Volume->FileSystem.OpenVolume = fsw_efi_FileSystem_OpenVolume; + Status = gBS->InstallMultipleProtocolInterfaces( &ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, + &Volume->FileSystem, + NULL); + if (EFI_ERROR(Status)) { +#if DEBUG_LEVEL + Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status); +#endif + } + } + + // on errors, close the opened protocols + if (EFI_ERROR(Status)) { + if (Volume->vol != NULL) + fsw_unmount(Volume->vol); + FreePool(Volume); + + gBS->CloseProtocol( ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle); + } +#if DEBUG_LEVEL + Print(L"fsw_efi_DriverBinding_Start returned %x\n", Status); +#endif + 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 = gBS->OpenProtocol( ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, + (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 = gBS->UninstallMultipleProtocolInterfaces( ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, &Volume->FileSystem, + NULL); + if (EFI_ERROR(Status)) { +#if DEBUG_LEVEL + Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status); +#endif + 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 = gBS->CloseProtocol( ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle); + + 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_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; + 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, &gEfiFileInfoGuid)) { +#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, &gEfiFileSystemInfoGuid)) { +#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, &gEfiFileSystemVolumeLabelInfoIdGuid)) { +#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; + + } 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_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; + FileInfo->Size = RequiredSize; + FileInfo->FileSize = dno->size; + FileInfo->Attribute = 0; + if (dno->type == FSW_DNODE_TYPE_DIR) + FileInfo->Attribute |= EFI_FILE_DIRECTORY; + fsw_efi_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_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/ExtPkg/ExtPkgDxe/fsw_efi.h b/ExtPkg/ExtPkgDxe/fsw_efi.h new file mode 100644 index 0000000..31e72da --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_efi.h @@ -0,0 +1,132 @@ +/* $Id: fsw_efi.h 33540 2010-10-28 09:27:05Z vboxsync $ */ +/** @file + * fsw_efi.h - EFI host environment header. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * 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" +#include "Protocol/SimpleFileSystem.h" +#include "Protocol/DiskIo.h" +#ifdef __MAKEWITH_GNUEFI +#define CompareGuid(a, b) CompareGuid(a, b)==0 +#endif + +/** + * EFI Host: Private per-volume structure. + */ + +# 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)) + +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; + + +#define SIGNATURE_16(A, B) ((A) | (B << 8)) +#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) +# define EFI_SIGNATURE_32(a, b, c, d) SIGNATURE_32(a, b, c, d) +/** 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 + + UINT64 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/ExtPkg/ExtPkgDxe/fsw_efi_base.h b/ExtPkg/ExtPkgDxe/fsw_efi_base.h new file mode 100644 index 0000000..f2968a6 --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_efi_base.h @@ -0,0 +1,89 @@ +/* $Id: fsw_efi_base.h 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_efi_base.h - Base definitions for the EFI host environment. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * 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_ + +#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) L##s +#define FSW_MSGFUNC Print + +// 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/ExtPkg/ExtPkgDxe/fsw_efi_lib.c b/ExtPkg/ExtPkgDxe/fsw_efi_lib.c new file mode 100644 index 0000000..9c97843 --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_efi_lib.c @@ -0,0 +1,159 @@ +/* $Id: fsw_efi_lib.c 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_efi_lib.c - EFI host environment library functions. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * 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; + } +} + +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; +} + +// EOF diff --git a/ExtPkg/ExtPkgDxe/fsw_ext2.c b/ExtPkg/ExtPkgDxe/fsw_ext2.c new file mode 100644 index 0000000..6f882c2 --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_ext2.c @@ -0,0 +1,553 @@ +/** + * \file fsw_ext2.c + * ext2 file system driver code. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "fsw_ext2.h" + + +// functions + +static fsw_status_t fsw_ext2_volume_mount(struct fsw_ext2_volume *vol); +static void fsw_ext2_volume_free(struct fsw_ext2_volume *vol); +static fsw_status_t fsw_ext2_volume_stat(struct fsw_ext2_volume *vol, struct fsw_volume_stat *sb); + +static fsw_status_t fsw_ext2_dnode_fill(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno); +static void fsw_ext2_dnode_free(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno); +static fsw_status_t fsw_ext2_dnode_stat(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_dnode_stat *sb); +static fsw_status_t fsw_ext2_get_extent(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_extent *extent); + +static fsw_status_t fsw_ext2_dir_lookup(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_string *lookup_name, struct fsw_ext2_dnode **child_dno); +static fsw_status_t fsw_ext2_dir_read(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_shandle *shand, struct fsw_ext2_dnode **child_dno); +static fsw_status_t fsw_ext2_read_dentry(struct fsw_shandle *shand, struct ext2_dir_entry *entry); + +static fsw_status_t fsw_ext2_readlink(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_string *link); + +// +// Dispatch Table +// + +struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext2) = { + { FSW_STRING_TYPE_ISO88591, 4, 4, "ext2" }, + sizeof(struct fsw_ext2_volume), + sizeof(struct fsw_ext2_dnode), + + fsw_ext2_volume_mount, + fsw_ext2_volume_free, + fsw_ext2_volume_stat, + fsw_ext2_dnode_fill, + fsw_ext2_dnode_free, + fsw_ext2_dnode_stat, + fsw_ext2_get_extent, + fsw_ext2_dir_lookup, + fsw_ext2_dir_read, + fsw_ext2_readlink, +}; + +/** + * Mount an ext2 volume. Reads the superblock and constructs the + * root directory dnode. + */ + +static fsw_status_t fsw_ext2_volume_mount(struct fsw_ext2_volume *vol) +{ + fsw_status_t status; + void *buffer; + fsw_u32 blocksize; + fsw_u32 groupcnt, groupno, gdesc_per_block, gdesc_bno, gdesc_index; + struct ext2_group_desc *gdesc; + int i; + struct fsw_string s; + + // allocate memory to keep the superblock around + status = fsw_alloc(sizeof(struct ext2_super_block), &vol->sb); + if (status) + return status; + + // read the superblock into its buffer + fsw_set_blocksize(vol, EXT2_SUPERBLOCK_BLOCKSIZE, EXT2_SUPERBLOCK_BLOCKSIZE); + status = fsw_block_get(vol, EXT2_SUPERBLOCK_BLOCKNO, 0, &buffer); + if (status) + return status; + fsw_memcpy(vol->sb, buffer, sizeof(struct ext2_super_block)); + fsw_block_release(vol, EXT2_SUPERBLOCK_BLOCKNO, buffer); + + // check the superblock + if (vol->sb->s_magic != EXT2_SUPER_MAGIC) + return FSW_UNSUPPORTED; + if (vol->sb->s_rev_level != EXT2_GOOD_OLD_REV && + vol->sb->s_rev_level != EXT2_DYNAMIC_REV) + return FSW_UNSUPPORTED; + if (vol->sb->s_rev_level == EXT2_DYNAMIC_REV && + (vol->sb->s_feature_incompat & ~(EXT2_FEATURE_INCOMPAT_FILETYPE | EXT3_FEATURE_INCOMPAT_RECOVER))) + return FSW_UNSUPPORTED; + + // set real blocksize + blocksize = EXT2_BLOCK_SIZE(vol->sb); + fsw_set_blocksize(vol, blocksize, blocksize); + + // get other info from superblock + vol->ind_bcnt = EXT2_ADDR_PER_BLOCK(vol->sb); + vol->dind_bcnt = vol->ind_bcnt * vol->ind_bcnt; + vol->inode_size = EXT2_INODE_SIZE(vol->sb); + + for (i = 0; i < 16; i++) + if (vol->sb->s_volume_name[i] == 0) + break; + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = i; + s.data = vol->sb->s_volume_name; + status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); + if (status) + return status; + + // read the group descriptors to get inode table offsets + groupcnt = ((vol->sb->s_inodes_count - 2) / vol->sb->s_inodes_per_group) + 1; + gdesc_per_block = (vol->g.phys_blocksize / sizeof(struct ext2_group_desc)); + + status = fsw_alloc(sizeof(fsw_u32) * groupcnt, &vol->inotab_bno); + if (status) + return status; + for (groupno = 0; groupno < groupcnt; groupno++) { + // get the block group descriptor + gdesc_bno = (vol->sb->s_first_data_block + 1) + groupno / gdesc_per_block; + gdesc_index = groupno % gdesc_per_block; + status = fsw_block_get(vol, gdesc_bno, 1, (void **)&buffer); + if (status) + return status; + gdesc = ((struct ext2_group_desc *)(buffer)) + gdesc_index; + vol->inotab_bno[groupno] = gdesc->bg_inode_table; + fsw_block_release(vol, gdesc_bno, buffer); + } + + // setup the root dnode + status = fsw_dnode_create_root(vol, EXT2_ROOT_INO, &vol->g.root); + if (status) + return status; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext2_volume_mount: success, blocksize %d\n"), blocksize)); + AsciiPrint("fsw_ext2_volume_mount: success, blocksize %d\n", blocksize); + + 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_ext2_volume_free(struct fsw_ext2_volume *vol) +{ + if (vol->sb) + fsw_free(vol->sb); + if (vol->inotab_bno) + fsw_free(vol->inotab_bno); +} + +/** + * Get in-depth information on a volume. + */ + +static fsw_status_t fsw_ext2_volume_stat(struct fsw_ext2_volume *vol, struct fsw_volume_stat *sb) +{ + sb->total_bytes = (fsw_u64)vol->sb->s_blocks_count * vol->g.log_blocksize; + sb->free_bytes = (fsw_u64)vol->sb->s_free_blocks_count * vol->g.log_blocksize; + 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 ext2, 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_ext2_dnode_fill(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno) +{ + fsw_status_t status; + fsw_u32 groupno, ino_in_group, ino_bno, ino_index; + fsw_u8 *buffer; + + if (dno->raw) + return FSW_SUCCESS; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext2_dnode_fill: inode %d\n"), dno->g.dnode_id)); + + // read the inode block + groupno = (dno->g.dnode_id - 1) / vol->sb->s_inodes_per_group; + ino_in_group = (dno->g.dnode_id - 1) % vol->sb->s_inodes_per_group; + ino_bno = vol->inotab_bno[groupno] + + ino_in_group / (vol->g.phys_blocksize / vol->inode_size); + ino_index = ino_in_group % (vol->g.phys_blocksize / vol->inode_size); + status = fsw_block_get(vol, ino_bno, 2, (void **)&buffer); + if (status) + return status; + + // keep our inode around + status = fsw_memdup((void **)&dno->raw, buffer + ino_index * vol->inode_size, vol->inode_size); + fsw_block_release(vol, ino_bno, buffer); + if (status) + return status; + + // get info from the inode + dno->g.size = dno->raw->i_size; + // TODO: check docs for 64-bit sized files + if (S_ISREG(dno->raw->i_mode)) + dno->g.type = FSW_DNODE_TYPE_FILE; + else if (S_ISDIR(dno->raw->i_mode)) + dno->g.type = FSW_DNODE_TYPE_DIR; + else if (S_ISLNK(dno->raw->i_mode)) + dno->g.type = FSW_DNODE_TYPE_SYMLINK; + else + dno->g.type = FSW_DNODE_TYPE_SPECIAL; + + 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_ext2_dnode_free(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno) +{ + if (dno->raw) + fsw_free(dno->raw); +} + +/** + * Get in-depth information on a dnode. The core makes sure that fsw_ext2_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_ext2_dnode_stat(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_dnode_stat *sb) +{ + sb->used_bytes = dno->raw->i_blocks * 512; // very, very strange... + 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_ext2_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. + * + * The ext2 file system does not use extents, but stores a list of block numbers + * using the usual direct, indirect, double-indirect, triple-indirect scheme. To + * optimize access, this function checks if the following file blocks are mapped + * to consecutive disk blocks and returns a combined extent if possible. + */ + +static fsw_status_t fsw_ext2_get_extent(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_extent *extent) +{ + fsw_status_t status; + fsw_u32 bno, release_bno, buf_bcnt, file_bcnt; + fsw_u32 *buffer; + int path[5], i; + + // Preconditions: The caller has checked that the requested logical block + // is within the file's size. The dnode has complete information, i.e. + // fsw_ext2_dnode_read_info was called successfully on it. + + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + extent->log_count = 1; + bno = extent->log_start; + + // try direct block pointers in the inode + if (bno < EXT2_NDIR_BLOCKS) { + path[0] = bno; + path[1] = -1; + } else { + bno -= EXT2_NDIR_BLOCKS; + + // try indirect block + if (bno < vol->ind_bcnt) { + path[0] = EXT2_IND_BLOCK; + path[1] = bno; + path[2] = -1; + } else { + bno -= vol->ind_bcnt; + + // try double-indirect block + if (bno < vol->dind_bcnt) { + path[0] = EXT2_DIND_BLOCK; + path[1] = bno / vol->ind_bcnt; + path[2] = bno % vol->ind_bcnt; + path[3] = -1; + } else { + bno -= vol->dind_bcnt; + + // use the triple-indirect block + path[0] = EXT2_TIND_BLOCK; + path[1] = bno / vol->dind_bcnt; + path[2] = (bno / vol->ind_bcnt) % vol->ind_bcnt; + path[3] = bno % vol->ind_bcnt; + path[4] = -1; + } + } + } + + // follow the indirection path + buffer = dno->raw->i_block; + buf_bcnt = EXT2_NDIR_BLOCKS; + release_bno = 0; + for (i = 0; ; i++) { + bno = buffer[path[i]]; + if (bno == 0) { + extent->type = FSW_EXTENT_TYPE_SPARSE; + if (release_bno) + fsw_block_release(vol, release_bno, buffer); + return FSW_SUCCESS; + } + if (path[i+1] < 0) + break; + + if (release_bno) + fsw_block_release(vol, release_bno, buffer); + status = fsw_block_get(vol, bno, 1, (void **)&buffer); + if (status) + return status; + release_bno = bno; + buf_bcnt = vol->ind_bcnt; + } + extent->phys_start = bno; + + // check if the following blocks can be aggregated into one extent + file_bcnt = (fsw_u32)((dno->g.size + vol->g.log_blocksize - 1) & (vol->g.log_blocksize - 1)); + while (path[i] + extent->log_count < buf_bcnt && // indirect block has more block pointers + extent->log_start + extent->log_count < file_bcnt) { // file has more blocks + if (buffer[path[i] + extent->log_count] == buffer[path[i] + extent->log_count - 1] + 1) + extent->log_count++; + else + break; + } + + if (release_bno) + fsw_block_release(vol, release_bno, buffer); + 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_ext2_dnode_fill has been called + * and the dnode is actually a directory. + */ + +static fsw_status_t fsw_ext2_dir_lookup(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_string *lookup_name, struct fsw_ext2_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_shandle shand; + fsw_u32 child_ino; + struct ext2_dir_entry entry; + struct fsw_string entry_name; + + // Preconditions: The caller has checked that dno is a directory node. + + entry_name.type = FSW_STRING_TYPE_ISO88591; + + // setup handle to read the directory + status = fsw_shandle_open(dno, &shand); + if (status) + return status; + + // scan the directory for the file + child_ino = 0; + while (child_ino == 0) { + // read next entry + status = fsw_ext2_read_dentry(&shand, &entry); + if (status) + goto errorexit; + if (entry.inode == 0) { + // end of directory reached + status = FSW_NOT_FOUND; + goto errorexit; + } + + // compare name + entry_name.len = entry_name.size = entry.name_len; + entry_name.data = entry.name; + if (fsw_streq(lookup_name, &entry_name)) { + child_ino = entry.inode; + break; + } + } + + // setup a dnode for the child item + status = fsw_dnode_create(dno, child_ino, FSW_DNODE_TYPE_UNKNOWN, &entry_name, child_dno_out); + +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_ext2_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_ext2_dir_read(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_shandle *shand, struct fsw_ext2_dnode **child_dno_out) +{ + fsw_status_t status; + struct ext2_dir_entry entry; + struct fsw_string entry_name; + + // 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. + + while (1) { + // read next entry + status = fsw_ext2_read_dentry(shand, &entry); + if (status) + return status; + if (entry.inode == 0) // end of directory + return FSW_NOT_FOUND; + + // skip . and .. + if ((entry.name_len == 1 && entry.name[0] == '.') || + (entry.name_len == 2 && entry.name[0] == '.' && entry.name[1] == '.')) + continue; + break; + } + + // setup name + entry_name.type = FSW_STRING_TYPE_ISO88591; + entry_name.len = entry_name.size = entry.name_len; + entry_name.data = entry.name; + + // setup a dnode for the child item + status = fsw_dnode_create(dno, entry.inode, FSW_DNODE_TYPE_UNKNOWN, &entry_name, child_dno_out); + + return status; +} + +/** + * Read a directory entry from the directory's raw data. This internal function is used + * to read a raw ext2 directory entry into memory. The shandle's position pointer is adjusted + * to point to the next entry. + */ + +static fsw_status_t fsw_ext2_read_dentry(struct fsw_shandle *shand, struct ext2_dir_entry *entry) +{ + fsw_status_t status; + fsw_u32 buffer_size; + + while (1) { + // read dir_entry header (fixed length) + buffer_size = 8; + status = fsw_shandle_read(shand, &buffer_size, entry); + if (status) + return status; + + if (buffer_size < 8 || entry->rec_len == 0) { + // end of directory reached + entry->inode = 0; + return FSW_SUCCESS; + } + if (entry->rec_len < 8) + return FSW_VOLUME_CORRUPTED; + if (entry->inode != 0) { + // this entry is used + if (entry->rec_len < 8 + entry->name_len) + return FSW_VOLUME_CORRUPTED; + break; + } + + // valid, but unused entry, skip it + shand->pos += entry->rec_len - 8; + } + + // read file name (variable length) + buffer_size = entry->name_len; + status = fsw_shandle_read(shand, &buffer_size, entry->name); + if (status) + return status; + if (buffer_size < entry->name_len) + return FSW_VOLUME_CORRUPTED; + + // skip any remaining padding + shand->pos += entry->rec_len - (8 + entry->name_len); + + 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_ext2_dnode_fill has been + * called on the dnode and that it really is a symlink. + * + * For ext2, 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_ext2_readlink(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_string *link_target) +{ + fsw_status_t status; + int ea_blocks; + struct fsw_string s; + + if (dno->g.size > FSW_PATH_MAX) + return FSW_VOLUME_CORRUPTED; + + ea_blocks = dno->raw->i_file_acl ? (vol->g.log_blocksize >> 9) : 0; + + if (dno->raw->i_blocks - ea_blocks == 0) { + // "fast" symlink, path is stored inside the inode + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = (int)dno->g.size; + s.data = dno->raw->i_block; + status = fsw_strdup_coerce(link_target, vol->g.host_string_type, &s); + } else { + // "slow" symlink, path is stored in normal inode data + status = fsw_dnode_readlink_data(dno, link_target); + } + + return status; +} + +// EOF diff --git a/ExtPkg/ExtPkgDxe/fsw_ext2.h b/ExtPkg/ExtPkgDxe/fsw_ext2.h new file mode 100644 index 0000000..13b4caa --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_ext2.h @@ -0,0 +1,65 @@ +/** + * \file fsw_ext2.h + * ext2 file system driver header. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _FSW_EXT2_H_ +#define _FSW_EXT2_H_ + +#define VOLSTRUCTNAME fsw_ext2_volume +#define DNODESTRUCTNAME fsw_ext2_dnode +#include "fsw_core.h" + +#include "fsw_ext2_disk.h" + + +//! Block size to be used when reading the ext2 superblock. +#define EXT2_SUPERBLOCK_BLOCKSIZE 1024 +//! Block number where the (master copy of the) ext2 superblock resides. +#define EXT2_SUPERBLOCK_BLOCKNO 1 + + +/** + * ext2: Volume structure with ext2-specific data. + */ + +struct fsw_ext2_volume { + struct fsw_volume g; //!< Generic volume structure + + struct ext2_super_block *sb; //!< Full raw ext2 superblock structure + fsw_u32 *inotab_bno; //!< Block numbers of the inode tables + fsw_u32 ind_bcnt; //!< Number of blocks addressable through an indirect block + fsw_u32 dind_bcnt; //!< Number of blocks addressable through a double-indirect block + fsw_u32 inode_size; //!< Size of inode structure in bytes +}; + +/** + * ext2: Dnode structure with ext2-specific data. + */ + +struct fsw_ext2_dnode { + struct fsw_dnode g; //!< Generic dnode structure + + struct ext2_inode *raw; //!< Full raw inode structure +}; + + +#endif diff --git a/ExtPkg/ExtPkgDxe/fsw_ext2_disk.h b/ExtPkg/ExtPkgDxe/fsw_ext2_disk.h new file mode 100644 index 0000000..c89020b --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_ext2_disk.h @@ -0,0 +1,356 @@ +/** + * \file fsw_ext2_disk.h + * ext2 file system on-disk structures. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * Portions Copyright (c) 1991-2006 by various Linux kernel contributors + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _FSW_EXT2_DISK_H_ +#define _FSW_EXT2_DISK_H_ + +// types + +typedef fsw_s8 __s8; +typedef fsw_u8 __u8; +typedef fsw_s16 __s16; +typedef fsw_u16 __u16; +typedef fsw_s32 __s32; +typedef fsw_u32 __u32; +typedef fsw_s64 __s64; +typedef fsw_u64 __u64; + +typedef __u16 __le16; +typedef __u32 __le32; +typedef __u64 __le64; + +// +// from Linux kernel, include/linux/ext2_fs.h +// + +/* + * Special inode numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_SIZE 1024 +#define EXT2_MAX_BLOCK_SIZE 4096 +#define EXT2_MIN_BLOCK_LOG_SIZE 10 +#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : \ + (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : \ + (s)->s_first_ino) + +/* + * Structure of a blocks group descriptor + */ +struct ext2_group_desc +{ + __le32 bg_block_bitmap; /* Blocks bitmap block */ + __le32 bg_inode_bitmap; /* Inodes bitmap block */ + __le32 bg_inode_table; /* Inodes table block */ + __le16 bg_free_blocks_count; /* Free blocks count */ + __le16 bg_free_inodes_count; /* Free inodes count */ + __le16 bg_used_dirs_count; /* Directories count */ + __le16 bg_pad; + __le32 bg_reserved[3]; +}; + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT2_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group) +#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) +#define EXT2_INODES_PER_GROUP(s) ((s)->s_inodes_per_group) + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x00000002 /* Undelete */ +#define EXT2_COMPR_FL 0x00000004 /* Compress file */ +#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMP_FL 0x00000400 /* Don't compress */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT2_IMAGIC_FL 0x00002000 /* AFS directory */ +#define EXT2_JOURNAL_DATA_FL 0x00004000 /* Reserved for ext3 */ +#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT2_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ +#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x000380FF /* User modifiable flags */ + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + __le16 i_mode; /* 0: File mode */ + __le16 i_uid; /* 2: Low 16 bits of Owner Uid */ + __le32 i_size; /* 4: Size in bytes */ + __le32 i_atime; /* 8: Access time */ + __le32 i_ctime; /* 12: Creation time */ + __le32 i_mtime; /* 16: Modification time */ + __le32 i_dtime; /* 20: Deletion Time */ + __le16 i_gid; /* 24: Low 16 bits of Group Id */ + __le16 i_links_count; /* 26: Links count */ + __le32 i_blocks; /* 28: Blocks count */ + __le32 i_flags; /* 32: File flags */ + union { + struct { + __le32 l_i_reserved1; + } linux1; + struct { + __le32 h_i_translator; + } hurd1; + struct { + __le32 m_i_reserved1; + } masix1; + } osd1; /* 36: OS dependent 1 */ + __le32 i_block[EXT2_N_BLOCKS];/* 40: Pointers to blocks */ + __le32 i_generation; /* 100: File version (for NFS) */ + __le32 i_file_acl; /* 104: File ACL */ + __le32 i_dir_acl; /* 108: Directory ACL */ + __le32 i_faddr; /* 112: Fragment address */ + union { + struct { + __u8 l_i_frag; /* 116: Fragment number */ + __u8 l_i_fsize; /* 117: Fragment size */ + __u16 i_pad1; + __le16 l_i_uid_high; /* 120: these 2 fields */ + __le16 l_i_gid_high; /* 122: were reserved2[0] */ + __u32 l_i_reserved2; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __le16 h_i_mode_high; + __le16 h_i_uid_high; + __le16 h_i_gid_high; + __le32 h_i_author; + } hurd2; + struct { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ +}; + +#define i_size_high i_dir_acl + +/* + * Structure of the super block + */ +struct ext2_super_block { + __le32 s_inodes_count; /* Inodes count */ + __le32 s_blocks_count; /* Blocks count */ + __le32 s_r_blocks_count; /* Reserved blocks count */ + __le32 s_free_blocks_count; /* Free blocks count */ + __le32 s_free_inodes_count; /* Free inodes count */ + __le32 s_first_data_block; /* First Data Block */ + __le32 s_log_block_size; /* Block size */ + __le32 s_log_frag_size; /* Fragment size */ + __le32 s_blocks_per_group; /* # Blocks per group */ + __le32 s_frags_per_group; /* # Fragments per group */ + __le32 s_inodes_per_group; /* # Inodes per group */ + __le32 s_mtime; /* Mount time */ + __le32 s_wtime; /* Write time */ + __le16 s_mnt_count; /* Mount count */ + __le16 s_max_mnt_count; /* Maximal mount count */ + __le16 s_magic; /* Magic signature */ + __le16 s_state; /* File system state */ + __le16 s_errors; /* Behaviour when detecting errors */ + __le16 s_minor_rev_level; /* minor revision level */ + __le32 s_lastcheck; /* time of last check */ + __le32 s_checkinterval; /* max. time between checks */ + __le32 s_creator_os; /* OS */ + __le32 s_rev_level; /* Revision level */ + __le16 s_def_resuid; /* Default uid for reserved blocks */ + __le16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __le32 s_first_ino; /* First non-reserved inode */ + __le16 s_inode_size; /* size of inode structure */ + __le16 s_block_group_nr; /* block group # of this superblock */ + __le32 s_feature_compat; /* compatible feature set */ + __le32 s_feature_incompat; /* incompatible feature set */ + __le32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __le32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_padding1; + /* + * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_reserved_char_pad; + __u16 s_reserved_word_pad; + __le32 s_default_mount_opts; + __le32 s_first_meta_bg; /* First metablock block group */ + __u32 s_reserved[190]; /* Padding to the end of the block */ +}; + +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Feature set definitions + */ + +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask) ) +#define EXT2_SET_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_compat |= cpu_to_le32(mask) +#define EXT2_SET_RO_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_ro_compat |= cpu_to_le32(mask) +#define EXT2_SET_INCOMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_incompat |= cpu_to_le32(mask) +#define EXT2_CLEAR_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_compat &= ~cpu_to_le32(mask) +#define EXT2_CLEAR_RO_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_ro_compat &= ~cpu_to_le32(mask) +#define EXT2_CLEAR_INCOMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_incompat &= ~cpu_to_le32(mask) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010 +#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 +#define EXT2_FEATURE_COMPAT_ANY 0xffffffff + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#define EXT2_FEATURE_RO_COMPAT_ANY 0xffffffff + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + __le32 inode; /* Inode number */ + __le16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; +// NOTE: The original Linux kernel header defines ext2_dir_entry with the original +// layout and ext2_dir_entry_2 with the revised layout. We simply use the revised one. + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +enum { + EXT2_FT_UNKNOWN, + EXT2_FT_REG_FILE, + EXT2_FT_DIR, + EXT2_FT_CHRDEV, + EXT2_FT_BLKDEV, + EXT2_FT_FIFO, + EXT2_FT_SOCK, + EXT2_FT_SYMLINK, + EXT2_FT_MAX +}; + + +#endif diff --git a/ExtPkg/ExtPkgDxe/fsw_ext4.c b/ExtPkg/ExtPkgDxe/fsw_ext4.c new file mode 100644 index 0000000..879cd57 --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_ext4.c @@ -0,0 +1,731 @@ +/** + * \file fsw_ext4.c + * ext4 file system driver code. + */ + +/*- + * Copyright (c) 2012 Stefan Agner + * Portions Copyright (c) 2006 Christoph Pfisterer + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "fsw_ext4.h" + + +// functions + +static fsw_status_t fsw_ext4_volume_mount(struct fsw_ext4_volume *vol); +static void fsw_ext4_volume_free(struct fsw_ext4_volume *vol); +static fsw_status_t fsw_ext4_volume_stat(struct fsw_ext4_volume *vol, struct fsw_volume_stat *sb); + +static fsw_status_t fsw_ext4_dnode_fill(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno); +static void fsw_ext4_dnode_free(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno); +static fsw_status_t fsw_ext4_dnode_stat(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_dnode_stat *sb); +static fsw_status_t fsw_ext4_get_extent(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_extent *extent); +static fsw_status_t fsw_ext4_get_by_blkaddr(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_extent *extent); +static fsw_status_t fsw_ext4_get_by_extent(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_extent *extent); + +static fsw_status_t fsw_ext4_dir_lookup(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_string *lookup_name, struct fsw_ext4_dnode **child_dno); +static fsw_status_t fsw_ext4_dir_read(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_shandle *shand, struct fsw_ext4_dnode **child_dno); +static fsw_status_t fsw_ext4_read_dentry(struct fsw_shandle *shand, struct ext4_dir_entry *entry); + +static fsw_status_t fsw_ext4_readlink(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_string *link); + +// +// Dispatch Table +// + +struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext4) = { + { FSW_STRING_TYPE_ISO88591, 4, 4, "ext4" }, + sizeof(struct fsw_ext4_volume), + sizeof(struct fsw_ext4_dnode), + + fsw_ext4_volume_mount, + fsw_ext4_volume_free, + fsw_ext4_volume_stat, + fsw_ext4_dnode_fill, + fsw_ext4_dnode_free, + fsw_ext4_dnode_stat, + fsw_ext4_get_extent, + fsw_ext4_dir_lookup, + fsw_ext4_dir_read, + fsw_ext4_readlink, +}; + + +static inline int test_root(fsw_u32 a, int b) +{ + int num = b; + + while (a > num) + num *= b; + return num == a; +} + +static int fsw_ext4_group_sparse(fsw_u32 group) +{ + if (group <= 1) + return 1; + if (!(group & 1)) + return 0; + return (test_root(group, 7) || test_root(group, 5) || + test_root(group, 3)); +} + +/* calculate the first block number of the group */ +static inline fsw_u32 +fsw_ext4_group_first_block_no(struct ext4_super_block *sb, fsw_u32 group_no) +{ + return group_no * (fsw_u32)EXT4_BLOCKS_PER_GROUP(sb) + + sb->s_first_data_block; +} + +/** + * Mount an ext4 volume. Reads the superblock and constructs the + * root directory dnode. + */ + +static fsw_status_t fsw_ext4_volume_mount(struct fsw_ext4_volume *vol) +{ + fsw_status_t status; + void *buffer; + fsw_u32 blocksize; + fsw_u32 groupcnt, groupno, gdesc_per_block, gdesc_bno, gdesc_index, metabg_of_gdesc; + struct ext4_group_desc *gdesc; + int i; + struct fsw_string s; + + // allocate memory to keep the superblock around + status = fsw_alloc(sizeof(struct ext4_super_block), &vol->sb); + if (status) + return status; + + // read the superblock into its buffer + fsw_set_blocksize(vol, EXT4_SUPERBLOCK_BLOCKSIZE, EXT4_SUPERBLOCK_BLOCKSIZE); + status = fsw_block_get(vol, EXT4_SUPERBLOCK_BLOCKNO, 0, &buffer); + if (status) + return status; + fsw_memcpy(vol->sb, buffer, sizeof(struct ext4_super_block)); + fsw_block_release(vol, EXT4_SUPERBLOCK_BLOCKNO, buffer); + + // check the superblock + if (vol->sb->s_magic != EXT4_SUPER_MAGIC) + return FSW_UNSUPPORTED; + if (vol->sb->s_rev_level != EXT4_GOOD_OLD_REV && + vol->sb->s_rev_level != EXT4_DYNAMIC_REV) + return FSW_UNSUPPORTED; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_volume_mount: Incompat flag %x\n"), vol->sb->s_feature_incompat)); + + if (vol->sb->s_rev_level == EXT4_DYNAMIC_REV && + (vol->sb->s_feature_incompat & ~(EXT4_FEATURE_INCOMPAT_FILETYPE | EXT4_FEATURE_INCOMPAT_RECOVER | + EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_FLEX_BG | + EXT4_FEATURE_INCOMPAT_META_BG))) + return FSW_UNSUPPORTED; + + + if (vol->sb->s_rev_level == EXT4_DYNAMIC_REV && + (vol->sb->s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER)) + { + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_volume_mount: This ext3 file system needs recovery\n"))); + // Print(L"Ext4 WARNING: This file system needs recovery, trying to use it anyway.\n"); + } + + blocksize = EXT4_BLOCK_SIZE(vol->sb); + if (blocksize < EXT4_MIN_BLOCK_SIZE || blocksize > EXT4_MAX_BLOCK_SIZE) + return FSW_UNSUPPORTED; + + // set real blocksize + fsw_set_blocksize(vol, blocksize, blocksize); + + // get other info from superblock + vol->ind_bcnt = EXT4_ADDR_PER_BLOCK(vol->sb); + vol->dind_bcnt = vol->ind_bcnt * vol->ind_bcnt; + vol->inode_size = vol->sb->s_inode_size;//EXT4_INODE_SIZE(vol->sb); + + for (i = 0; i < 16; i++) + if (vol->sb->s_volume_name[i] == 0) + break; + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = i; + s.data = vol->sb->s_volume_name; + status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); + if (status) + return status; + + // size of group descriptor depends on feature.... + if (!(vol->sb->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT)) { + // Default minimal group descriptor size... (this might not be set in old ext2 filesystems, therefor set it!) + vol->sb->s_desc_size = EXT4_MIN_DESC_SIZE; + } + + // Calculate group descriptor count the way the kernel does it... + groupcnt = (vol->sb->s_blocks_count_lo - vol->sb->s_first_data_block + + vol->sb->s_blocks_per_group - 1) / vol->sb->s_blocks_per_group; + + // Descriptors in one block... s_desc_size needs to be set! (Usually 128 since normal block + // descriptors are 32 byte and block size is 4096) + gdesc_per_block = EXT4_DESC_PER_BLOCK(vol->sb); + + // Read the group descriptors to get inode table offsets + status = fsw_alloc(sizeof(fsw_u32) * groupcnt, &vol->inotab_bno); + if (status) + return status; + + // Loop through all block group descriptors in order to get inode table locations + for (groupno = 0; groupno < groupcnt; groupno++) { + + // Calculate the block number which contains the block group descriptor we look for + if(vol->sb->s_feature_incompat & EXT4_FEATURE_INCOMPAT_META_BG && groupno >= vol->sb->s_first_meta_bg) + { + // If option meta_bg is set, the block group descriptor is in meta block group... + metabg_of_gdesc = (fsw_u32)(groupno / gdesc_per_block) * gdesc_per_block; + gdesc_bno = fsw_ext4_group_first_block_no(vol->sb, metabg_of_gdesc); + // We need to know if the block group in questition has a super block, if yes, the + // block group descriptors are in the next block number + if(!(vol->sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER) || fsw_ext4_group_sparse(metabg_of_gdesc)) + gdesc_bno += 1; + } + else + { + // All group descriptors follow the super block (+1) + gdesc_bno = (vol->sb->s_first_data_block + 1) + groupno / gdesc_per_block; + } + gdesc_index = groupno % gdesc_per_block; + + // Get block if necessary... + status = fsw_block_get(vol, gdesc_bno, 1, (void **)&buffer); + if (status) + return status; + + // Get group descriptor table and block number of inode table... + gdesc = (struct ext4_group_desc *)(buffer + gdesc_index * vol->sb->s_desc_size); + vol->inotab_bno[groupno] = gdesc->bg_inode_table_lo; + + fsw_block_release(vol, gdesc_bno, buffer); + } + + // setup the root dnode + status = fsw_dnode_create_root(vol, EXT4_ROOT_INO, &vol->g.root); + if (status) + return status; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_volume_mount: success, blocksize %d\n"), blocksize)); + AsciiPrint("fsw_ext4_volume_mount: success, blocksize %d\n", blocksize); + + 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_ext4_volume_free(struct fsw_ext4_volume *vol) +{ + if (vol->sb) + fsw_free(vol->sb); + if (vol->inotab_bno) + fsw_free(vol->inotab_bno); +} + +/** + * Get in-depth information on a volume. + */ + +static fsw_status_t fsw_ext4_volume_stat(struct fsw_ext4_volume *vol, struct fsw_volume_stat *sb) +{ + sb->total_bytes = (fsw_u64)vol->sb->s_blocks_count_lo * vol->g.log_blocksize; + sb->free_bytes = (fsw_u64)vol->sb->s_free_blocks_count_lo * vol->g.log_blocksize; + 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 ext4, 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_ext4_dnode_fill(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno) +{ + fsw_status_t status; + fsw_u32 groupno, ino_in_group, ino_bno, ino_index; + fsw_u8 *buffer; + + if (dno->raw) + return FSW_SUCCESS; + + + // read the inode block + groupno = (dno->g.dnode_id - 1) / vol->sb->s_inodes_per_group; + ino_in_group = (dno->g.dnode_id - 1) % vol->sb->s_inodes_per_group; + ino_bno = vol->inotab_bno[groupno] + + ino_in_group / (vol->g.phys_blocksize / vol->inode_size); + ino_index = ino_in_group % (vol->g.phys_blocksize / vol->inode_size); + status = fsw_block_get(vol, ino_bno, 2, (void **)&buffer); + + if (status) + return status; + + // keep our inode around + status = fsw_memdup((void **)&dno->raw, buffer + ino_index * vol->inode_size, vol->inode_size); + fsw_block_release(vol, ino_bno, buffer); + if (status) + return status; + + // get info from the inode + dno->g.size = dno->raw->i_size_lo; // TODO: check docs for 64-bit sized files + + if (S_ISREG(dno->raw->i_mode)) + dno->g.type = FSW_DNODE_TYPE_FILE; + else if (S_ISDIR(dno->raw->i_mode)) + dno->g.type = FSW_DNODE_TYPE_DIR; + else if (S_ISLNK(dno->raw->i_mode)) + dno->g.type = FSW_DNODE_TYPE_SYMLINK; + else + dno->g.type = FSW_DNODE_TYPE_SPECIAL; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_dnode_fill: inode flags %x\n"), dno->raw->i_flags)); + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_dnode_fill: i_mode %x\n"), dno->raw->i_mode)); + 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_ext4_dnode_free(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno) +{ + if (dno->raw) + fsw_free(dno->raw); +} + +/** + * Get in-depth information on a dnode. The core makes sure that fsw_ext4_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_ext4_dnode_stat(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_dnode_stat *sb) +{ + sb->used_bytes = dno->raw->i_blocks_lo * EXT4_BLOCK_SIZE(vol->sb); // very, very strange... + 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_ext4_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. + * + * The ext4 file system usually uses extents do to store those disk block numbers. + * However, since ext4 is backward compatible, depending on inode flags the old direct + * and indirect addressing scheme can still be in place... + */ + +static fsw_status_t fsw_ext4_get_extent(struct fsw_ext4_volume *vol, struct fsw_ext4_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_ext4_dnode_read_info was called successfully on it. + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d, block %d\n"), dno->g.dnode_id, extent->log_start)); + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + extent->log_count = 1; + + if(dno->raw->i_flags & 1 << EXT4_INODE_EXTENTS) + { + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d uses extents\n"), dno->g.dnode_id)); + return fsw_ext4_get_by_extent(vol, dno, extent); + } + else + { + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d uses direct/indirect block addressing\n"), + dno->g.dnode_id)); + return fsw_ext4_get_by_blkaddr(vol, dno, extent); + } +} + +/** + * New ext4 extents... + */ +static fsw_status_t fsw_ext4_get_by_extent(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_extent *extent) +{ + fsw_status_t status; + fsw_u32 bno, buf_offset; + int ext_cnt; + void *buffer; + + struct ext4_extent_header *ext4_extent_header; + struct ext4_extent_idx *ext4_extent_idx; + struct ext4_extent *ext4_extent; + + // Logical block requested by core... + bno = extent->log_start; + + // First buffer is the i_block field from inode... + buffer = (void *)dno->raw->i_block; + buf_offset = 0; + while(1) { + ext4_extent_header = (struct ext4_extent_header *)buffer + buf_offset; + buf_offset += sizeof(struct ext4_extent_header); + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent header with %d entries\n"), + ext4_extent_header->eh_entries)); + if(ext4_extent_header->eh_magic != EXT4_EXT_MAGIC) + return FSW_VOLUME_CORRUPTED; + + for(ext_cnt = 0;ext_cnt < ext4_extent_header->eh_entries;ext_cnt++) + { + if(ext4_extent_header->eh_depth == 0) + { + // Leaf node, the header follows actual extents + ext4_extent = (struct ext4_extent *)(buffer + buf_offset); + buf_offset += sizeof(struct ext4_extent); + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent node cover %d...\n"), ext4_extent->ee_block)); + + // Is the requested block in this extent? + if(bno >= ext4_extent->ee_block && bno < ext4_extent->ee_block + ext4_extent->ee_len) + { + extent->phys_start = ext4_extent->ee_start_lo + (bno - ext4_extent->ee_block); + extent->log_count = ext4_extent->ee_len - (bno - ext4_extent->ee_block); + return FSW_SUCCESS; + } + } + else + { + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index extents, depth %d\n"), + ext4_extent_header->eh_depth)); + ext4_extent_idx = (struct ext4_extent_idx *)(buffer + buf_offset); + buf_offset += sizeof(struct ext4_extent_idx); + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index node covers block %d...\n"), + ext4_extent_idx->ei_block)); + if(bno >= ext4_extent_idx->ei_block) + { + // Follow extent tree... + status = fsw_block_get(vol, ext4_extent_idx->ei_leaf_lo, 1, (void **)&buffer); + if (status) + return status; + buf_offset = 0; + break; + } + } + } + } + + return FSW_NOT_FOUND; +} + +/** + * The ext2/ext3 file system does not use extents, but stores a list of block numbers + * using the usual direct, indirect, double-indirect, triple-indirect scheme. To + * optimize access, this function checks if the following file blocks are mapped + * to consecutive disk blocks and returns a combined extent if possible. + */ +static fsw_status_t fsw_ext4_get_by_blkaddr(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_extent *extent) +{ + fsw_status_t status; + fsw_u32 bno, release_bno, buf_bcnt, file_bcnt; + int path[5], i; + fsw_u32 *buffer; + bno = extent->log_start; + + // try direct block pointers in the inode + if (bno < EXT4_NDIR_BLOCKS) { + path[0] = bno; + path[1] = -1; + } else { + bno -= EXT4_NDIR_BLOCKS; + + // try indirect block + if (bno < vol->ind_bcnt) { + path[0] = EXT4_IND_BLOCK; + path[1] = bno; + path[2] = -1; + } else { + bno -= vol->ind_bcnt; + + // try double-indirect block + if (bno < vol->dind_bcnt) { + path[0] = EXT4_DIND_BLOCK; + path[1] = bno / vol->ind_bcnt; + path[2] = bno % vol->ind_bcnt; + path[3] = -1; + } else { + bno -= vol->dind_bcnt; + + // use the triple-indirect block + path[0] = EXT4_TIND_BLOCK; + path[1] = bno / vol->dind_bcnt; + path[2] = (bno / vol->ind_bcnt) % vol->ind_bcnt; + path[3] = bno % vol->ind_bcnt; + path[4] = -1; + } + } + } + + // follow the indirection path + buffer = dno->raw->i_block; + buf_bcnt = EXT4_NDIR_BLOCKS; + release_bno = 0; + for (i = 0; ; i++) { + bno = buffer[path[i]]; + if (bno == 0) { + extent->type = FSW_EXTENT_TYPE_SPARSE; + if (release_bno) + fsw_block_release(vol, release_bno, buffer); + return FSW_SUCCESS; + } + if (path[i+1] < 0) + break; + + if (release_bno) + fsw_block_release(vol, release_bno, buffer); + status = fsw_block_get(vol, bno, 1, (void **)&buffer); + if (status) + return status; + release_bno = bno; + buf_bcnt = vol->ind_bcnt; + } + extent->phys_start = bno; + + // check if the following blocks can be aggregated into one extent + file_bcnt = (fsw_u32)((dno->g.size + vol->g.log_blocksize - 1) & (vol->g.log_blocksize - 1)); + while (path[i] + extent->log_count < buf_bcnt && // indirect block has more block pointers + extent->log_start + extent->log_count < file_bcnt) { // file has more blocks + if (buffer[path[i] + extent->log_count] == buffer[path[i] + extent->log_count - 1] + 1) + extent->log_count++; + else + break; + } + + if (release_bno) + fsw_block_release(vol, release_bno, buffer); + 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_ext4_dnode_fill has been called + * and the dnode is actually a directory. + */ + +static fsw_status_t fsw_ext4_dir_lookup(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_string *lookup_name, struct fsw_ext4_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_shandle shand; + fsw_u32 child_ino; + struct ext4_dir_entry entry; + struct fsw_string entry_name; + + // Preconditions: The caller has checked that dno is a directory node. + + entry_name.type = FSW_STRING_TYPE_ISO88591; + + // setup handle to read the directory + status = fsw_shandle_open(dno, &shand); + if (status) + return status; + + // scan the directory for the file + child_ino = 0; + while (child_ino == 0) { + // read next entry + status = fsw_ext4_read_dentry(&shand, &entry); + if (status) + goto errorexit; + if (entry.inode == 0) { + // end of directory reached + status = FSW_NOT_FOUND; + goto errorexit; + } + + // compare name + entry_name.len = entry_name.size = entry.name_len; + entry_name.data = entry.name; + if (fsw_streq(lookup_name, &entry_name)) { + child_ino = entry.inode; + break; + } + } + + // setup a dnode for the child item + status = fsw_dnode_create(dno, child_ino, FSW_DNODE_TYPE_UNKNOWN, &entry_name, child_dno_out); + +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_ext4_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_ext4_dir_read(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_shandle *shand, struct fsw_ext4_dnode **child_dno_out) +{ + fsw_status_t status; + struct ext4_dir_entry entry; + struct fsw_string entry_name; + + // 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. + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_dir_read: started reading dir\n"))); + + while (1) { + // read next entry + status = fsw_ext4_read_dentry(shand, &entry); + if (status) + return status; + if (entry.inode == 0) // end of directory + return FSW_NOT_FOUND; + + // skip . and .. + if ((entry.name_len == 1 && entry.name[0] == '.') || + (entry.name_len == 2 && entry.name[0] == '.' && entry.name[1] == '.')) + continue; + break; + } + + // setup name + entry_name.type = FSW_STRING_TYPE_ISO88591; + entry_name.len = entry_name.size = entry.name_len; + entry_name.data = entry.name; + + // setup a dnode for the child item + status = fsw_dnode_create(dno, entry.inode, FSW_DNODE_TYPE_UNKNOWN, &entry_name, child_dno_out); + + return status; +} + +/** + * Read a directory entry from the directory's raw data. This internal function is used + * to read a raw ext2 directory entry into memory. The shandle's position pointer is adjusted + * to point to the next entry. + */ + +static fsw_status_t fsw_ext4_read_dentry(struct fsw_shandle *shand, struct ext4_dir_entry *entry) +{ + fsw_status_t status; + fsw_u32 buffer_size; + + while (1) { + // read dir_entry header (fixed length) + buffer_size = 8; + status = fsw_shandle_read(shand, &buffer_size, entry); + if (status) + return status; + + if (buffer_size < 8 || entry->rec_len == 0) { + // end of directory reached + entry->inode = 0; + return FSW_SUCCESS; + } + if (entry->rec_len < 8) + return FSW_VOLUME_CORRUPTED; + if (entry->inode != 0) { + // this entry is used + if (entry->rec_len < 8 + entry->name_len) + return FSW_VOLUME_CORRUPTED; + break; + } + + // valid, but unused entry, skip it + shand->pos += entry->rec_len - 8; + } + + // read file name (variable length) + buffer_size = entry->name_len; + status = fsw_shandle_read(shand, &buffer_size, entry->name); + if (status) + return status; + if (buffer_size < entry->name_len) + return FSW_VOLUME_CORRUPTED; + + // skip any remaining padding + shand->pos += entry->rec_len - (8 + entry->name_len); + + 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_ext4_dnode_fill has been + * called on the dnode and that it really is a symlink. + * + * For ext4, 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_ext4_readlink(struct fsw_ext4_volume *vol, struct fsw_ext4_dnode *dno, + struct fsw_string *link_target) +{ + fsw_status_t status; + int ea_blocks; + struct fsw_string s; + + if (dno->g.size > FSW_PATH_MAX) + return FSW_VOLUME_CORRUPTED; + + /* Linux kernels ext4_inode_is_fast_symlink... */ + ea_blocks = dno->raw->i_file_acl_lo ? (vol->g.log_blocksize >> 9) : 0; + + if (dno->raw->i_blocks_lo - ea_blocks == 0) { + // "fast" symlink, path is stored inside the inode + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = (int)dno->g.size; + s.data = dno->raw->i_block; + status = fsw_strdup_coerce(link_target, vol->g.host_string_type, &s); + } else { + // "slow" symlink, path is stored in normal inode data + status = fsw_dnode_readlink_data(dno, link_target); + } + + return status; +} + +// EOF diff --git a/ExtPkg/ExtPkgDxe/fsw_ext4.h b/ExtPkg/ExtPkgDxe/fsw_ext4.h new file mode 100644 index 0000000..df7ad35 --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_ext4.h @@ -0,0 +1,66 @@ +/** + * \file fsw_ext4.h + * ext4 file system driver header. + */ + +/*- + * Copyright (c) 2012 Stefan Agner + * Portions Copyright (c) 2006 Christoph Pfisterer + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _FSW_EXT4_H_ +#define _FSW_EXT4_H_ + +#define VOLSTRUCTNAME fsw_ext4_volume +#define DNODESTRUCTNAME fsw_ext4_dnode +#include "fsw_core.h" + +#include "fsw_ext4_disk.h" + + +//! Block size to be used when reading the ext4 superblock. +#define EXT4_SUPERBLOCK_BLOCKSIZE 1024 +//! Block number where the (master copy of the) ext4 superblock resides. +#define EXT4_SUPERBLOCK_BLOCKNO 1 + + +/** + * ext4: Volume structure with ext2-specific data. + */ + +struct fsw_ext4_volume { + struct fsw_volume g; //!< Generic volume structure + + struct ext4_super_block *sb; //!< Full raw ext2 superblock structure + fsw_u32 *inotab_bno; //!< Block numbers of the inode tables + fsw_u32 ind_bcnt; //!< Number of blocks addressable through an indirect block + fsw_u32 dind_bcnt; //!< Number of blocks addressable through a double-indirect block + fsw_u32 inode_size; //!< Size of inode structure in bytes +}; + +/** + * ext2: Dnode structure with ext2-specific data. + */ + +struct fsw_ext4_dnode { + struct fsw_dnode g; //!< Generic dnode structure + + struct ext4_inode *raw; //!< Full raw inode structure +}; + + +#endif diff --git a/ExtPkg/ExtPkgDxe/fsw_ext4_disk.h b/ExtPkg/ExtPkgDxe/fsw_ext4_disk.h new file mode 100644 index 0000000..2bf3a4e --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_ext4_disk.h @@ -0,0 +1,500 @@ +/** + * \file fsw_ext4_disk.h + * ext4 file system on-disk structures. + */ + +/*- + * Copyright (c) 2012 Stefan Agner + * Portions Copyright (c) 2006 Christoph Pfisterer + * Portions Copyright (c) 1991-2012 by various Linux kernel contributors + * + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _FSW_EXT4_DISK_H_ +#define _FSW_EXT4_DISK_H_ + +// types + +typedef fsw_s8 __s8; +typedef fsw_u8 __u8; +typedef fsw_s16 __s16; +typedef fsw_u16 __u16; +typedef fsw_s32 __s32; +typedef fsw_u32 __u32; +typedef fsw_s64 __s64; +typedef fsw_u64 __u64; + +typedef __u16 __le16; +typedef __u32 __le32; +typedef __u64 __le64; + +// +// from Linux kernel, fs/ext4/ext4.h +// + +/* + * Special inode numbers + */ +#define EXT4_BAD_INO 1 /* Bad blocks inode */ +#define EXT4_ROOT_INO 2 /* Root inode */ +#define EXT4_USR_QUOTA_INO 3 /* User quota inode */ +#define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */ +#define EXT4_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT4_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT4_RESIZE_INO 7 /* Reserved group descriptors inode */ +#define EXT4_JOURNAL_INO 8 /* Journal inode */ + +/* + * The second extended file system magic number + */ +#define EXT4_SUPER_MAGIC 0xEF53 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT4_MIN_BLOCK_SIZE 1024 +#define EXT4_MAX_BLOCK_SIZE 4096 +#define EXT4_MIN_BLOCK_LOG_SIZE 10 +#define EXT4_BLOCK_SIZE(s) (EXT4_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT4_ADDR_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / sizeof (__u32)) +#define EXT4_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT4_INODE_SIZE(s) (((s)->s_rev_level == EXT4_GOOD_OLD_REV) ? \ + EXT4_GOOD_OLD_INODE_SIZE : \ + (s)->s_inode_size) + +/* + * Structure of a blocks group descriptor + */ +struct ext4_group_desc +{ + __le32 bg_block_bitmap_lo; /* Blocks bitmap block */ + __le32 bg_inode_bitmap_lo; /* Inodes bitmap block */ + __le32 bg_inode_table_lo; /* Inodes table block */ + __le16 bg_free_blocks_count_lo;/* Free blocks count */ + __le16 bg_free_inodes_count_lo;/* Free inodes count */ + __le16 bg_used_dirs_count_lo; /* Directories count */ + __le16 bg_flags; /* EXT4_BG_flags (INODE_UNINIT, etc) */ + __le32 bg_exclude_bitmap_lo; /* Exclude bitmap for snapshots */ + __le16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bbitmap) LE */ + __le16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+ibitmap) LE */ + __le16 bg_itable_unused_lo; /* Unused inodes count */ + __le16 bg_checksum; /* crc16(sb_uuid+group+desc) */ + __le32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */ + __le32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */ + __le32 bg_inode_table_hi; /* Inodes table block MSB */ + __le16 bg_free_blocks_count_hi;/* Free blocks count MSB */ + __le16 bg_free_inodes_count_hi;/* Free inodes count MSB */ + __le16 bg_used_dirs_count_hi; /* Directories count MSB */ + __le16 bg_itable_unused_hi; /* Unused inodes count MSB */ + __le32 bg_exclude_bitmap_hi; /* Exclude bitmap block MSB */ + __le16 bg_block_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bbitmap) BE */ + __le16 bg_inode_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+ibitmap) BE */ + __u32 bg_reserved; +}; + + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT4_MIN_DESC_SIZE 32 +#define EXT4_MIN_DESC_SIZE_64BIT 64 +#define EXT4_MAX_DESC_SIZE EXT4_MIN_BLOCK_SIZE +#define EXT4_DESC_SIZE(s) ((s)->s_desc_size) +#define EXT4_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group) +#define EXT4_DESC_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / EXT4_DESC_SIZE(s)) +#define EXT4_INODES_PER_GROUP(s) ((s)->s_inodes_per_group) + +/* + * Constants relative to the data blocks + */ +#define EXT4_NDIR_BLOCKS 12 +#define EXT4_IND_BLOCK EXT4_NDIR_BLOCKS +#define EXT4_DIND_BLOCK (EXT4_IND_BLOCK + 1) +#define EXT4_TIND_BLOCK (EXT4_DIND_BLOCK + 1) +#define EXT4_N_BLOCKS (EXT4_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT4_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT4_UNRM_FL 0x00000002 /* Undelete */ +#define EXT4_COMPR_FL 0x00000004 /* Compress file */ +#define EXT4_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT4_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT4_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT4_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT4_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT4_DIRTY_FL 0x00000100 +#define EXT4_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT4_NOCOMP_FL 0x00000400 /* Don't compress */ +#define EXT4_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT4_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT4_IMAGIC_FL 0x00002000 /* AFS directory */ +#define EXT4_JOURNAL_DATA_FL 0x00004000 /* Reserved for ext3 */ +#define EXT4_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT4_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ +#define EXT4_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ +#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ +#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ +#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ + +#define EXT4_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ +#define EXT4_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ + + +/* + * Structure of an inode on the disk + */ +struct ext4_inode { + __le16 i_mode; /* File mode */ + __le16 i_uid; /* Low 16 bits of Owner Uid */ + __le32 i_size_lo; /* Size in bytes */ + __le32 i_atime; /* Access time */ + __le32 i_ctime; /* Inode Change time */ + __le32 i_mtime; /* Modification time */ + __le32 i_dtime; /* Deletion Time */ + __le16 i_gid; /* Low 16 bits of Group Id */ + __le16 i_links_count; /* Links count */ + __le32 i_blocks_lo; /* Blocks count */ + __le32 i_flags; /* File flags */ + union { + struct { + __le32 l_i_version; + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + struct { + __u32 m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + __le32 i_block[EXT4_N_BLOCKS];/* Pointers to blocks */ + __le32 i_generation; /* File version (for NFS) */ + __le32 i_file_acl_lo; /* File ACL */ + __le32 i_size_high; + __le32 i_obso_faddr; /* Obsoleted fragment address */ + union { + struct { + __le16 l_i_blocks_high; /* were l_i_reserved1 */ + __le16 l_i_file_acl_high; + __le16 l_i_uid_high; /* these 2 fields */ + __le16 l_i_gid_high; /* were reserved2[0] */ + __le16 l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */ + __le16 l_i_reserved; + } linux2; + struct { + __le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + struct { + __le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */ + __le16 m_i_file_acl_high; + __u32 m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ + __le16 i_extra_isize; + __le16 i_checksum_hi; /* crc32c(uuid+inum+inode) BE */ + __le32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ + __le32 i_mtime_extra; /* extra Modification time(nsec << 2 | epoch) */ + __le32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ + __le32 i_crtime; /* File Creation time */ + __le32 i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */ + __le32 i_version_hi; /* high 32 bits for 64-bit version */ +}; + + +/* + * Inode flags used for atomic set/get + */ +enum { + EXT4_INODE_SECRM = 0, /* Secure deletion */ + EXT4_INODE_UNRM = 1, /* Undelete */ + EXT4_INODE_COMPR = 2, /* Compress file */ + EXT4_INODE_SYNC = 3, /* Synchronous updates */ + EXT4_INODE_IMMUTABLE = 4, /* Immutable file */ + EXT4_INODE_APPEND = 5, /* writes to file may only append */ + EXT4_INODE_NODUMP = 6, /* do not dump file */ + EXT4_INODE_NOATIME = 7, /* do not update atime */ +/* Reserved for compression usage... */ + EXT4_INODE_DIRTY = 8, + EXT4_INODE_COMPRBLK = 9, /* One or more compressed clusters */ + EXT4_INODE_NOCOMPR = 10, /* Don't compress */ + EXT4_INODE_ECOMPR = 11, /* Compression error */ +/* End compression flags --- maybe not all used */ + EXT4_INODE_INDEX = 12, /* hash-indexed directory */ + EXT4_INODE_IMAGIC = 13, /* AFS directory */ + EXT4_INODE_JOURNAL_DATA = 14, /* file data should be journaled */ + EXT4_INODE_NOTAIL = 15, /* file tail should not be merged */ + EXT4_INODE_DIRSYNC = 16, /* dirsync behaviour (directories only) */ + EXT4_INODE_TOPDIR = 17, /* Top of directory hierarchies*/ + EXT4_INODE_HUGE_FILE = 18, /* Set to each huge file */ + EXT4_INODE_EXTENTS = 19, /* Inode uses extents */ + EXT4_INODE_EA_INODE = 21, /* Inode used for large EA */ + EXT4_INODE_EOFBLOCKS = 22, /* Blocks allocated beyond EOF */ + EXT4_INODE_RESERVED = 31, /* reserved for ext4 lib */ +}; + +/* + * Structure of the super block + */ +struct ext4_super_block { +/*00*/ __le32 s_inodes_count; /* Inodes count */ + __le32 s_blocks_count_lo; /* Blocks count */ + __le32 s_r_blocks_count_lo; /* Reserved blocks count */ + __le32 s_free_blocks_count_lo; /* Free blocks count */ +/*10*/ __le32 s_free_inodes_count; /* Free inodes count */ + __le32 s_first_data_block; /* First Data Block */ + __le32 s_log_block_size; /* Block size */ + __le32 s_log_cluster_size; /* Allocation cluster size */ +/*20*/ __le32 s_blocks_per_group; /* # Blocks per group */ + __le32 s_clusters_per_group; /* # Clusters per group */ + __le32 s_inodes_per_group; /* # Inodes per group */ + __le32 s_mtime; /* Mount time */ +/*30*/ __le32 s_wtime; /* Write time */ + __le16 s_mnt_count; /* Mount count */ + __le16 s_max_mnt_count; /* Maximal mount count */ + __le16 s_magic; /* Magic signature */ + __le16 s_state; /* File system state */ + __le16 s_errors; /* Behaviour when detecting errors */ + __le16 s_minor_rev_level; /* minor revision level */ +/*40*/ __le32 s_lastcheck; /* time of last check */ + __le32 s_checkinterval; /* max. time between checks */ + __le32 s_creator_os; /* OS */ + __le32 s_rev_level; /* Revision level */ +/*50*/ __le16 s_def_resuid; /* Default uid for reserved blocks */ + __le16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT4_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __le32 s_first_ino; /* First non-reserved inode */ + __le16 s_inode_size; /* size of inode structure */ + __le16 s_block_group_nr; /* block group # of this superblock */ + __le32 s_feature_compat; /* compatible feature set */ +/*60*/ __le32 s_feature_incompat; /* incompatible feature set */ + __le32 s_feature_ro_compat; /* readonly-compatible feature set */ +/*68*/ __u8 s_uuid[16]; /* 128-bit uuid for volume */ +/*78*/ char s_volume_name[16]; /* volume name */ +/*88*/ char s_last_mounted[64]; /* directory where last mounted */ +/*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __le16 s_reserved_gdt_blocks; /* Per group desc for online growth */ + /* + * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set. + */ +/*D0*/ __u8 s_journal_uuid[16]; /* uuid of journal superblock */ +/*E0*/ __le32 s_journal_inum; /* inode number of journal file */ + __le32 s_journal_dev; /* device number of journal file */ + __le32 s_last_orphan; /* start of list of inodes to delete */ + __le32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_jnl_backup_type; + __le16 s_desc_size; /* size of group descriptor */ +/*100*/ __le32 s_default_mount_opts; + __le32 s_first_meta_bg; /* First metablock block group */ + __le32 s_mkfs_time; /* When the filesystem was created */ + __le32 s_jnl_blocks[17]; /* Backup of the journal inode */ + /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */ +/*150*/ __le32 s_blocks_count_hi; /* Blocks count */ + __le32 s_r_blocks_count_hi; /* Reserved blocks count */ + __le32 s_free_blocks_count_hi; /* Free blocks count */ + __le16 s_min_extra_isize; /* All inodes have at least # bytes */ + __le16 s_want_extra_isize; /* New inodes should reserve # bytes */ + __le32 s_flags; /* Miscellaneous flags */ + __le16 s_raid_stride; /* RAID stride */ + __le16 s_mmp_update_interval; /* # seconds to wait in MMP checking */ + __le64 s_mmp_block; /* Block for multi-mount protection */ + __le32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ + __u8 s_log_groups_per_flex; /* FLEX_BG group size */ + __u8 s_checksum_type; /* metadata checksum algorithm used */ + __le16 s_reserved_pad; + __le64 s_kbytes_written; /* nr of lifetime kilobytes written */ + __le32 s_snapshot_inum; /* Inode number of active snapshot */ + __le32 s_snapshot_id; /* sequential ID of active snapshot */ + __le64 s_snapshot_r_blocks_count; /* reserved blocks for active + snapshot's future use */ + __le32 s_snapshot_list; /* inode number of the head of the + on-disk snapshot list */ +#define EXT4_S_ERR_START offsetof(struct ext4_super_block, s_error_count) + __le32 s_error_count; /* number of fs errors */ + __le32 s_first_error_time; /* first time an error happened */ + __le32 s_first_error_ino; /* inode involved in first error */ + __le64 s_first_error_block; /* block involved of first error */ + __u8 s_first_error_func[32]; /* function where the error happened */ + __le32 s_first_error_line; /* line number where error happened */ + __le32 s_last_error_time; /* most recent time of an error */ + __le32 s_last_error_ino; /* inode involved in last error */ + __le32 s_last_error_line; /* line number where error happened */ + __le64 s_last_error_block; /* block involved of last error */ + __u8 s_last_error_func[32]; /* function where the error happened */ +#define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts) + __u8 s_mount_opts[64]; + __le32 s_usr_quota_inum; /* inode for tracking user quota */ + __le32 s_grp_quota_inum; /* inode for tracking group quota */ + __le32 s_overhead_clusters; /* overhead blocks/clusters in fs */ + __le32 s_reserved[108]; /* Padding to the end of the block */ + __le32 s_checksum; /* crc32c(superblock) */ +}; + +/* + * Revision levels + */ +#define EXT4_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT4_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT4_CURRENT_REV EXT4_GOOD_OLD_REV +#define EXT4_MAX_SUPP_REV EXT4_DYNAMIC_REV + +#define EXT4_GOOD_OLD_INODE_SIZE 128 + +/* + * Feature set definitions (only the once we need for read support) + */ +#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 + +#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT4_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ +#define EXT4_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ +#define EXT4_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */ +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 /* EA in inode */ +#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 /* data in dirent */ +#define EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM 0x2000 /* use crc32c for bg */ +#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */ +#define EXT4_FEATURE_INCOMPAT_INLINEDATA 0x8000 /* data in inode */ + +#define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ + EXT4_FEATURE_INCOMPAT_RECOVER| \ + EXT4_FEATURE_INCOMPAT_META_BG| \ + EXT4_FEATURE_INCOMPAT_EXTENTS| \ + EXT4_FEATURE_INCOMPAT_64BIT| \ + EXT4_FEATURE_INCOMPAT_FLEX_BG| \ + EXT4_FEATURE_INCOMPAT_MMP) + +/* + * Structure of a directory entry + */ +#define EXT4_NAME_LEN 255 + +struct ext4_dir_entry { + __le32 inode; /* Inode number */ + __le16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT4_NAME_LEN]; /* File name */ +}; +// NOTE: The original Linux kernel header defines ext4_dir_entry with the original +// layout and ext4_dir_entry_2 with the revised layout. We simply use the revised one. + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +enum { + EXT4_FT_UNKNOWN, + EXT4_FT_REG_FILE, + EXT4_FT_DIR, + EXT4_FT_CHRDEV, + EXT4_FT_BLKDEV, + EXT4_FT_FIFO, + EXT4_FT_SOCK, + EXT4_FT_SYMLINK, + EXT4_FT_MAX +}; + +/* + * ext4_inode has i_block array (60 bytes total). + * The first 12 bytes store ext4_extent_header; + * the remainder stores an array of ext4_extent. + * For non-inode extent blocks, ext4_extent_tail + * follows the array. + */ + +/* + * This is the extent tail on-disk structure. + * All other extent structures are 12 bytes long. It turns out that + * block_size % 12 >= 4 for at least all powers of 2 greater than 512, which + * covers all valid ext4 block sizes. Therefore, this tail structure can be + * crammed into the end of the block without having to rebalance the tree. + */ +struct ext4_extent_tail { + __le32 et_checksum; /* crc32c(uuid+inum+extent_block) */ +}; + +/* + * This is the extent on-disk structure. + * It's used at the bottom of the tree. + */ +struct ext4_extent { + __le32 ee_block; /* first logical block extent covers */ + __le16 ee_len; /* number of blocks covered by extent */ + __le16 ee_start_hi; /* high 16 bits of physical block */ + __le32 ee_start_lo; /* low 32 bits of physical block */ +}; + +/* + * This is index on-disk structure. + * It's used at all the levels except the bottom. + */ +struct ext4_extent_idx { + __le32 ei_block; /* index covers logical blocks from 'block' */ + __le32 ei_leaf_lo; /* pointer to the physical block of the next * + * level. leaf or next index could be there */ + __le16 ei_leaf_hi; /* high 16 bits of physical block */ + __u16 ei_unused; +}; + + +/* + * Each block (leaves and indexes), even inode-stored has header. + */ +struct ext4_extent_header { + __le16 eh_magic; /* probably will support different formats */ + __le16 eh_entries; /* number of valid entries */ + __le16 eh_max; /* capacity of store in entries */ + __le16 eh_depth; /* has tree real underlying blocks? */ + __le32 eh_generation; /* generation of the tree */ +}; + +#define EXT4_EXT_MAGIC (0xf30a) + + +#endif diff --git a/ExtPkg/ExtPkgDxe/fsw_lib.c b/ExtPkg/ExtPkgDxe/fsw_lib.c new file mode 100644 index 0000000..e0af0d3 --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_lib.c @@ -0,0 +1,336 @@ +/* $Id: fsw_lib.c 33540 2010-10-28 09:27:05Z vboxsync $ */ +/** @file + * fsw_lib.c - Core file system wrapper library functions. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * 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; +} + +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 (ch < 0x0100) + return fsw_latin_case_fold[ch]; + 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/ExtPkg/ExtPkgDxe/fsw_strfunc.h b/ExtPkg/ExtPkgDxe/fsw_strfunc.h new file mode 100644 index 0000000..61cf25d --- /dev/null +++ b/ExtPkg/ExtPkgDxe/fsw_strfunc.h @@ -0,0 +1,472 @@ +/* $Id: fsw_strfunc.h 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_strfunc.h + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* 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; +}