new file mode 100644
@@ -0,0 +1,5 @@
+[Defines]
+ DEC_SPECIFICATION = 0x00010005
+ PACKAGE_NAME = Ext2Pkg
+ PACKAGE_GUID = 8EA68A2C-99CB-4332-85C6-DD5864EAA674
+ PACKAGE_VERSION = 0.1
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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;
+}