diff --git a/OpenCorePkg.dsc b/OpenCorePkg.dsc index 465b86ef..0789631c 100755 --- a/OpenCorePkg.dsc +++ b/OpenCorePkg.dsc @@ -237,7 +237,7 @@ OpenCorePkg/Platform/OpenRuntime/OpenRuntime.inf OpenCorePkg/Platform/OpenUsbKbDxe/UsbKbDxe.inf OpenCorePkg/Staging/AudioDxe/AudioDxe.inf - OpenCorePkg/Staging/VBoxHfs/VBoxHfs.inf + OpenCorePkg/Staging/FswHfsPlus/FswHfsPlus.inf OpenCorePkg/Tests/AcpiTest/AcpiTest.inf OpenCorePkg/Tests/AcpiTest/AcpiTestApp.inf OpenCorePkg/Tests/CryptoTest/CryptoTest.inf diff --git a/Staging/FswHfsPlus/FswHfsPlus.inf b/Staging/FswHfsPlus/FswHfsPlus.inf new file mode 100644 index 00000000..ab901a90 --- /dev/null +++ b/Staging/FswHfsPlus/FswHfsPlus.inf @@ -0,0 +1,64 @@ +## @file +# File System Wrapper (FSW) based HFS+ driver. +# +# Copyright (C) 2020, Vladislav Yaroshchuk +# Copyright (C) 2017, Gabriel L. Somlo +# +# This program and the accompanying materials are licensed and made +# available under the terms and conditions of the BSD License which +# accompanies this distribution. The full text of the license may +# be found at http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" +# BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER +# EXPRESS OR IMPLIED. +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = FswHfsPlus + FILE_GUID = F474802F-26BB-4E4F-A923-2FC568E94125 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = fsw_efi_main + +[Sources] + fsw_base.h + fsw_efi_base.h + fsw_efi_edk2_base.h + fsw_strfunc.h + fsw_core.h + fsw_core.c + fsw_efi.h + fsw_efi.c + fsw_lib.c + fsw_efi_lib.c + fsw_hfsplus.h + fsw_hfsplus.c + +[BuildOptions] + GCC:*_*_*_CC_FLAGS = -DHOST_EFI -DFSTYPE=hfsplus + INTEL:*_*_*_CC_FLAGS = -DHOST_EFI -DFSTYPE=hfsplus + MSFT:*_*_*_CC_FLAGS = -DHOST_EFI -DFSTYPE=hfsplus + XCODE:*_*_*_CC_FLAGS = -DHOST_EFI -DFSTYPE=hfsplus + +[Packages] + MdePkg/MdePkg.dec + OpenCorePkg/OpenCorePkg.dec + +[LibraryClasses] + UefiLib + UefiDriverEntryPoint + +[Guids] + gEfiFileSystemInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + gEfiFileInfoGuid + gAppleBlessedOsxFolderInfoGuid ## SOMETIMES_CONSUMES + gAppleBlessedSystemFileInfoGuid ## SOMETIMES_CONSUMES + gAppleBlessedSystemFolderInfoGuid ## SOMETIMES_CONSUMES + +[Protocols] + gEfiBlockIoProtocolGuid + gEfiDiskIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid diff --git a/Staging/FswHfsPlus/fsw_base.h b/Staging/FswHfsPlus/fsw_base.h new file mode 100644 index 00000000..1c084e2a --- /dev/null +++ b/Staging/FswHfsPlus/fsw_base.h @@ -0,0 +1,158 @@ +/** + * \file fsw_base.h + * Base definitions switch. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_BASE_H_ +#define _FSW_BASE_H_ + + +#ifndef FSW_DEBUG_LEVEL +/** + * Global debugging level. Can be set locally for the scope of a single + * file by defining the macro before fsw_base.h is included. + */ +#define FSW_DEBUG_LEVEL 1 +#endif + + +#ifdef HOST_EFI +#include "fsw_efi_base.h" +#endif + +#ifdef HOST_POSIX +#include "fsw_posix_base.h" +#endif + +// message printing + +#if FSW_DEBUG_LEVEL >= 1 +#define FSW_MSG_ASSERT(params) FSW_MSGFUNC(params) +#else +#define FSW_MSG_ASSERT(params) +#endif + +#if FSW_DEBUG_LEVEL >= 2 +#define FSW_MSG_DEBUG(params) FSW_MSGFUNC(params) +#else +#define FSW_MSG_DEBUG(params) +#endif + +#if FSW_DEBUG_LEVEL >= 3 +#define FSW_MSG_DEBUGV(params) FSW_MSGFUNC(params) +#else +#define FSW_MSG_DEBUGV(params) +#endif + + +// Documentation for system-dependent defines + +/** + * \typedef fsw_s8 + * Signed 8-bit integer. + */ + +/** + * \typedef fsw_u8 + * Unsigned 8-bit integer. + */ + +/** + * \typedef fsw_s16 + * Signed 16-bit integer. + */ + +/** + * \typedef fsw_u16 + * Unsigned 16-bit integer. + */ + +/** + * \typedef fsw_s32 + * Signed 32-bit integer. + */ + +/** + * \typedef fsw_u32 + * Unsigned 32-bit integer. + */ + +/** + * \typedef fsw_s64 + * Signed 64-bit integer. + */ + +/** + * \typedef fsw_u64 + * Unsigned 64-bit integer. + */ + + +/** + * \def fsw_alloc(size,ptrptr) + * Allocate memory on the heap. This function or macro allocates \a size + * bytes of memory using host-specific methods. The address of the + * allocated memory block is stored into the pointer variable pointed + * to by \a ptrptr. A status code is returned; FSW_SUCCESS if the block + * was allocated or FSW_OUT_OF_MEMORY if there is not enough memory + * to allocated the requested block. + */ + +/** + * \def fsw_free(ptr) + * Release allocated memory. This function or macro returns an allocated + * memory block to the heap for reuse. Does not return a status. + */ + +/** + * \def fsw_memcpy(dest,src,size) + * Copies a block of memory from \a src to \a dest. The two memory blocks + * must not overlap, or the result of the operation will be undefined. + * Does not return a status. + */ + +/** + * \def fsw_memeq(dest,src,size) + * Compares two blocks of memory for equality. Returns boolean true if the + * memory blocks are equal, boolean false if they are different. + */ + +/** + * \def fsw_memzero(dest,size) + * Initializes a block of memory with zeros. Does not return a status. + */ + + +#endif diff --git a/Staging/FswHfsPlus/fsw_core.c b/Staging/FswHfsPlus/fsw_core.c new file mode 100644 index 00000000..8dc68c79 --- /dev/null +++ b/Staging/FswHfsPlus/fsw_core.c @@ -0,0 +1,1052 @@ +/** + * \file fsw_core.c + * Core file system wrapper abstraction layer code. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_core.h" + + +// functions + +static void fsw_blockcache_free(struct fsw_volume *vol); + +#define MAX_CACHE_LEVEL (5) + + +/** + * Mount a volume with a given file system driver. This function is called by the + * host driver to make a volume accessible. The file system driver to use is specified + * by a pointer to its dispatch table. The file system driver will look at the + * data on the volume to determine if it can read the format. If the volume is found + * unsuitable, FSW_UNSUPPORTED is returned. + * + * If this function returns FSW_SUCCESS, *vol_out points at a valid volume data + * structure. The caller must release it later by calling fsw_unmount. + * + * If this function returns an error status, the caller only needs to clean up its + * own buffers that may have been allocated through the read_block interface. + */ + +fsw_status_t fsw_mount(void *host_data, + struct fsw_host_table *host_table, + struct fsw_fstype_table *fstype_table, + struct fsw_volume **vol_out) +{ + fsw_status_t status; + struct fsw_volume *vol; + + // allocate memory for the structure + status = fsw_alloc_zero(fstype_table->volume_struct_size, (void **)&vol); + if (status) + return status; + + // initialize fields + vol->phys_blocksize = 512; + vol->log_blocksize = 512; + vol->label.type = FSW_STRING_TYPE_EMPTY; + vol->host_data = host_data; + vol->host_table = host_table; + vol->fstype_table = fstype_table; + vol->host_string_type = host_table->native_string_type; + + // let the fs driver mount the file system + status = vol->fstype_table->volume_mount(vol); + if (status) + goto errorexit; + + // TODO: anything else? + + *vol_out = vol; + return FSW_SUCCESS; + +errorexit: + fsw_unmount(vol); + return status; +} + +/** + * Unmount a volume by releasing all memory associated with it. This function is + * called by the host driver when a volume is no longer needed. It is also called + * by the core after a failed mount to clean up any allocated memory. + * + * Note that all dnodes must have been released before calling this function. + */ + +void fsw_unmount(struct fsw_volume *vol) +{ + if (vol->root) + fsw_dnode_release(vol->root); + // TODO: check that no other dnodes are still around + + vol->fstype_table->volume_free(vol); + + fsw_blockcache_free(vol); + fsw_strfree(&vol->label); + fsw_free(vol); +} + +/** + * Get in-depth information on the volume. This function can be called by the host + * driver to get additional information on the volume. + */ + +fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb) +{ + return vol->fstype_table->volume_stat(vol, sb); +} + +/** + * Set the physical and logical block sizes of the volume. This functions is called by + * the file system driver to announce the block sizes it wants to use for accessing + * the disk (physical) and for addressing file contents (logical). + * Usually both sizes will be the same but there may be file systems that need to access + * metadata at a smaller block size than the allocation unit for files. + * + * Calling this function causes the block cache to be dropped. All pointers returned + * from fsw_block_get become invalid. This function should only be called while + * mounting the file system, not as a part of file access operations. + * + * Both sizes are measured in bytes, must be powers of 2, and must not be smaller + * than 512 bytes. The logical block size cannot be smaller than the physical block size. + */ + +void fsw_set_blocksize(struct fsw_volume *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize) +{ + // TODO: Check the sizes. Both must be powers of 2. log_blocksize must not be smaller than + // phys_blocksize. + + // drop core block cache if present + fsw_blockcache_free(vol); + + // signal host driver to drop caches etc. + vol->host_table->change_blocksize(vol, + vol->phys_blocksize, vol->log_blocksize, + phys_blocksize, log_blocksize); + + vol->phys_blocksize = phys_blocksize; + vol->log_blocksize = log_blocksize; +} + +/** + * Get a block of data from the disk. This function is called by the file system driver + * or by core functions. It calls through to the host driver's device access routine. + * Given a physical block number, it reads the block into memory (or fetches it from the + * block cache) and returns the address of the memory buffer. The caller should provide + * an indication of how important the block is in the cache_level parameter. Blocks with + * a low level are purged first. Some suggestions for cache levels: + * + * - 0: File data + * - 1: Directory data, symlink data + * - 2: File system metadata + * - 3..5: File system metadata with a high rate of access + * + * If this function returns successfully, the returned data pointer is valid until the + * caller calls fsw_block_release. + */ + +fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out) +{ + fsw_status_t status; + fsw_u32 i, discard_level, new_bcache_size; + struct fsw_blockcache *new_bcache; + + // TODO: allow the host driver to do its own caching; just call through if + // the appropriate function pointers are set + + if (cache_level > MAX_CACHE_LEVEL) + cache_level = MAX_CACHE_LEVEL; + + // check block cache + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].phys_bno == phys_bno) { + // cache hit! + if (vol->bcache[i].cache_level < cache_level) + vol->bcache[i].cache_level = cache_level; // promote the entry + vol->bcache[i].refcount++; + *buffer_out = vol->bcache[i].data; + return FSW_SUCCESS; + } + } + + // find a free entry in the cache table + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].phys_bno == FSW_INVALID_BNO) + break; + } + if (i >= vol->bcache_size) { + for (discard_level = 0; discard_level <= MAX_CACHE_LEVEL; discard_level++) { + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].refcount == 0 && vol->bcache[i].cache_level <= discard_level) + break; + } + if (i < vol->bcache_size) + break; + } + } + if (i >= vol->bcache_size) { + // enlarge / create the cache + if (vol->bcache_size < 16) + new_bcache_size = 16; + else + new_bcache_size = vol->bcache_size << 1; + status = fsw_alloc(new_bcache_size * sizeof(struct fsw_blockcache), &new_bcache); + if (status) + return status; + if (vol->bcache_size > 0) + fsw_memcpy(new_bcache, vol->bcache, vol->bcache_size * sizeof(struct fsw_blockcache)); + for (i = vol->bcache_size; i < new_bcache_size; i++) { + new_bcache[i].refcount = 0; + new_bcache[i].cache_level = 0; + new_bcache[i].phys_bno = FSW_INVALID_BNO; + new_bcache[i].data = NULL; + } + i = vol->bcache_size; + + // switch caches + if (vol->bcache != NULL) + fsw_free(vol->bcache); + vol->bcache = new_bcache; + vol->bcache_size = new_bcache_size; + } + vol->bcache[i].phys_bno = FSW_INVALID_BNO; + + // read the data + if (vol->bcache[i].data == NULL) { + status = fsw_alloc(vol->phys_blocksize, &vol->bcache[i].data); + if (status) + return status; + } + status = vol->host_table->read_block(vol, phys_bno, vol->bcache[i].data); + if (status) + return status; + + vol->bcache[i].phys_bno = phys_bno; + vol->bcache[i].cache_level = cache_level; + vol->bcache[i].refcount = 1; + *buffer_out = vol->bcache[i].data; + return FSW_SUCCESS; +} + +/** + * Releases a disk block. This function must be called to release disk blocks returned + * from fsw_block_get. + */ + +void fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer) +{ + fsw_u32 i; + + // TODO: allow the host driver to do its own caching; just call through if + // the appropriate function pointers are set + + // update block cache + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].phys_bno == phys_bno && vol->bcache[i].refcount > 0) + vol->bcache[i].refcount--; + } +} + +/** + * Release the block cache. Called internally when changing block sizes and when + * unmounting the volume. It frees all data occupied by the generic block cache. + */ + +static void fsw_blockcache_free(struct fsw_volume *vol) +{ + fsw_u32 i; + + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].data != NULL) + fsw_free(vol->bcache[i].data); + } + if (vol->bcache != NULL) { + fsw_free(vol->bcache); + vol->bcache = NULL; + } + vol->bcache_size = 0; +} + +/** + * Add a new dnode to the list of known dnodes. This internal function is used when a + * dnode is created to add it to the dnode list that is used to search for existing + * dnodes by id. + */ + +static void fsw_dnode_register(struct fsw_volume *vol, struct fsw_dnode *dno) +{ + dno->next = vol->dnode_head; + if (vol->dnode_head != NULL) + vol->dnode_head->prev = dno; + dno->prev = NULL; + vol->dnode_head = dno; +} + +/** + * Create a dnode representing the root directory. This function is called by the file system + * driver while mounting the file system. The root directory is special because it has no parent + * dnode, its name is defined to be empty, and its type is also fixed. Otherwise, this functions + * behaves in the same way as fsw_dnode_create. + */ + +fsw_status_t fsw_dnode_create_root(struct fsw_volume *vol, fsw_u32 dnode_id, struct fsw_dnode **dno_out) +{ + fsw_status_t status; + struct fsw_dnode *dno; + + // allocate memory for the structure + status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno); + if (status) + return status; + + // fill the structure + dno->vol = vol; + dno->parent = NULL; + dno->dnode_id = dnode_id; + dno->type = FSW_DNODE_TYPE_DIR; + dno->refcount = 1; + dno->complete = 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); + fsw_dnode_mkcomplete(dno); + + *dno_out = dno; + return FSW_SUCCESS; +} + +int fsw_dnode_is_root(struct fsw_dnode *dno) { + return dno->parent == NULL && dno->type == FSW_DNODE_TYPE_DIR && fsw_strlen(&dno->name) == 0; +} + +/** + * 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_volume *vol, 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_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; + + // Flag must be set by fsw_dnode_mkcomplete() call + dno->complete = 0; + + // fill the structure + dno->vol = vol; + dno->parent = parent_dno; + + // Parent may be null and filled in future by fsw_dnode_fill() call + if (parent_dno != NULL) { + 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; +} + +/** + * Mark dnode as 'complete' one + */ +void fsw_dnode_mkcomplete(struct fsw_dnode *dno) { + dno->complete = 1; +} + +/** + * 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 + if (dno->complete) { + return FSW_SUCCESS; + } + 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 = DivU64x32(dno->size + dno->vol->log_blocksize - 1, dno->vol->log_blocksize, NULL); + 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; + + // read full dnode information into memory + status = fsw_dnode_fill(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; +} + +fsw_status_t fsw_dnode_get_path(struct fsw_volume *vol, struct fsw_dnode *dno, struct fsw_string *out_path) { + fsw_status_t status; + struct fsw_string *array, *temp_arr_ptr; + fsw_u32 array_capacity, array_len; + fsw_s64 i; + fsw_u16 *temp_char_ptr; + + // Simple growable array +#define START_BUF_SIZE 5 + array_capacity = START_BUF_SIZE; + array_len = 0; + temp_arr_ptr = NULL; + status = fsw_alloc(array_capacity * sizeof(struct fsw_string), &array); + if (status) { + return status; + } +#undef START_BUF_SIZE + + out_path->len = 0; + out_path->size = 0; + + while (status == FSW_SUCCESS && !fsw_dnode_is_root(dno)) { + // dnode should be filled! + fsw_dnode_fill(dno); + + // Extend array capacity if needed + if (array_len == array_capacity) { + temp_arr_ptr = array; + array_capacity *= 2; + status = fsw_alloc(array_capacity * sizeof(struct fsw_string), &array); + + if (!status) { + // Copy array contents + for (i = 0; i < (fsw_s64) array_len; ++i) + array[i] = temp_arr_ptr[i]; + + // Free the old pool + fsw_free(temp_arr_ptr); + temp_arr_ptr = NULL; + } else + // Store actual pointer into 'array' + array = temp_arr_ptr; + } + + // If no errors - copy dnode name + if (status == FSW_SUCCESS) + status = fsw_strdup_coerce(&array[array_len], FSW_STRING_TYPE_UTF16, &dno->name); + + // On any error free all allocated memory + if (status) + goto done; + + out_path->len += array[array_len].len + 1; + + ++array_len; + dno = dno->parent; + } + +#define SEP_CHAR L'\\' + out_path->type = FSW_STRING_TYPE_UTF16; + + if (out_path->len == 0) { + out_path->len = 1; + } + + out_path->size = out_path->len * sizeof(fsw_u16); + status = fsw_alloc(out_path->size, &out_path->data); + if (status) + goto done; + + temp_char_ptr = (fsw_u16 *) out_path->data; + if (array_len > 0) { + for (i = (fsw_s64) array_len - 1; i >= 0; --i) { + *(temp_char_ptr++) = SEP_CHAR; + fsw_memcpy(temp_char_ptr, array[i].data, array[i].size); + temp_char_ptr += array[i].len; + } + } else { + *temp_char_ptr = SEP_CHAR; + } + +#undef SEP_CHAR + +done: + for (i = 0; i < (fsw_s64) array_len; ++i) + fsw_strfree(&array[i]); + fsw_free(array); + + return status; +} + +fsw_status_t fsw_get_bless_info(struct fsw_volume *vol, int type, struct fsw_string *out_path) { + fsw_status_t status; + struct fsw_dnode *dnode; + + status = vol->fstype_table->get_bless_info(vol, type, &dnode); + if (status) { + return status; + } + + status = fsw_dnode_get_path(vol, dnode, out_path); + return status; +} + +// EOF diff --git a/Staging/FswHfsPlus/fsw_core.h b/Staging/FswHfsPlus/fsw_core.h new file mode 100644 index 00000000..1e223d59 --- /dev/null +++ b/Staging/FswHfsPlus/fsw_core.h @@ -0,0 +1,533 @@ +/** + * \file fsw_core.h + * Core file system wrapper abstraction layer header. + */ + +/*- + * Copyright (c) 2020 Vladislav Yaroshchuk + * Copyright (c) 2006 Christoph Pfisterer + * Portions Copyright (c) The Regents of the University of California. + * Portions Copyright (c) UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_CORE_H_ +#define _FSW_CORE_H_ + +#include "fsw_base.h" + + +/** Maximum size for a path, specifically symlink target paths. */ +#define FSW_PATH_MAX (4096) + +/** Helper macro for token concatenation. */ +#define FSW_CONCAT3(a,b,c) a##b##c +/** Expands to the name of a fstype dispatch table (fsw_fstype_table) for a named file system type. */ +#define FSW_FSTYPE_TABLE_NAME(t) FSW_CONCAT3(fsw_,t,_table) + +/** Indicates that the block cache entry is empty. */ +#define FSW_INVALID_BNO (~0U) + + +// +// Byte-swapping macros +// + + +/** + * \name Byte Order Macros + * Implements big endian vs. little endian awareness and conversion. + */ +/*@{*/ + +typedef fsw_u16 fsw_u16_le; +typedef fsw_u16 fsw_u16_be; +typedef fsw_u32 fsw_u32_le; +typedef fsw_u32 fsw_u32_be; +typedef fsw_u64 fsw_u64_le; +typedef fsw_u64 fsw_u64_be; + +#define FSW_SWAPVALUE_U16(v) ((((fsw_u16)(v) & 0xff00) >> 8) | \ + (((fsw_u16)(v) & 0x00ff) << 8)) +#define FSW_SWAPVALUE_U32(v) ((((fsw_u32)(v) & 0xff000000UL) >> 24) | \ + (((fsw_u32)(v) & 0x00ff0000UL) >> 8) | \ + (((fsw_u32)(v) & 0x0000ff00UL) << 8) | \ + (((fsw_u32)(v) & 0x000000ffUL) << 24)) +#define FSW_SWAPVALUE_U64(v) ((((fsw_u64)(v) & 0xff00000000000000ULL) >> 56) | \ + (((fsw_u64)(v) & 0x00ff000000000000ULL) >> 40) | \ + (((fsw_u64)(v) & 0x0000ff0000000000ULL) >> 24) | \ + (((fsw_u64)(v) & 0x000000ff00000000ULL) >> 8) | \ + (((fsw_u64)(v) & 0x00000000ff000000ULL) << 8) | \ + (((fsw_u64)(v) & 0x0000000000ff0000ULL) << 24) | \ + (((fsw_u64)(v) & 0x000000000000ff00ULL) << 40) | \ + (((fsw_u64)(v) & 0x00000000000000ffULL) << 56)) + +#ifdef FSW_LITTLE_ENDIAN + +#define fsw_u16_le_swap(v) (v) +#define fsw_u16_be_swap(v) FSW_SWAPVALUE_U16(v) +#define fsw_u32_le_swap(v) (v) +#define fsw_u32_be_swap(v) FSW_SWAPVALUE_U32(v) +#define fsw_u64_le_swap(v) (v) +#define fsw_u64_be_swap(v) FSW_SWAPVALUE_U64(v) + +#define fsw_u16_le_sip(var) +#define fsw_u16_be_sip(var) (var = FSW_SWAPVALUE_U16(var)) +#define fsw_u32_le_sip(var) +#define fsw_u32_be_sip(var) (var = FSW_SWAPVALUE_U32(var)) +#define fsw_u64_le_sip(var) +#define fsw_u64_be_sip(var) (var = FSW_SWAPVALUE_U64(var)) + +#else +#ifdef FSW_BIG_ENDIAN + +#define fsw_u16_le_swap(v) FSW_SWAPVALUE_U16(v) +#define fsw_u16_be_swap(v) (v) +#define fsw_u32_le_swap(v) FSW_SWAPVALUE_U32(v) +#define fsw_u32_be_swap(v) (v) +#define fsw_u64_le_swap(v) FSW_SWAPVALUE_U64(v) +#define fsw_u64_be_swap(v) (v) + +#define fsw_u16_le_sip(var) (var = FSW_SWAPVALUE_U16(var)) +#define fsw_u16_be_sip(var) +#define fsw_u32_le_sip(var) (var = FSW_SWAPVALUE_U32(var)) +#define fsw_u32_be_sip(var) +#define fsw_u64_le_sip(var) (var = FSW_SWAPVALUE_U64(var)) +#define fsw_u64_be_sip(var) + +#else +#fail Neither FSW_BIG_ENDIAN nor FSW_LITTLE_ENDIAN are defined +#endif +#endif + +/*@}*/ + + +// +// The following evil hack avoids a lot of casts between generic and fstype-specific +// structures. +// + +#ifndef VOLSTRUCTNAME +#define VOLSTRUCTNAME fsw_volume +#else +struct VOLSTRUCTNAME; +#endif +#ifndef DNODESTRUCTNAME +#define DNODESTRUCTNAME fsw_dnode +#else +struct DNODESTRUCTNAME; +#endif + + +/** + * Status code type, returned from all functions that can fail. + */ +typedef int fsw_status_t; + +/** + * Possible status codes. + */ +enum { + FSW_SUCCESS, + FSW_OUT_OF_MEMORY, + FSW_IO_ERROR, + FSW_UNSUPPORTED, + FSW_NOT_FOUND, + FSW_VOLUME_CORRUPTED, + FSW_UNKNOWN_ERROR +}; + + +/** + * Core: A string with explicit length and encoding information. + */ + +struct fsw_string { + int type; //!< Encoding of the string - empty, ISO-8859-1, UTF8, UTF16 + int len; //!< Length in characters + int size; //!< Total data size in bytes + void *data; //!< Data pointer (may be NULL if type is EMPTY or len is zero) +}; + +/** + * Possible string types / encodings. In the case of FSW_STRING_TYPE_EMPTY, + * all other members of the fsw_string structure may be invalid. + */ +enum { + FSW_STRING_TYPE_EMPTY, + FSW_STRING_TYPE_ISO88591, + FSW_STRING_TYPE_UTF8, + FSW_STRING_TYPE_UTF16, + FSW_STRING_TYPE_UTF16_SWAPPED +}; + +#ifdef FSW_LITTLE_ENDIAN +#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16 +#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16_SWAPPED +#else +#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16_SWAPPED +#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16 +#endif + +/** Static initializer for an empty string. */ +#define FSW_STRING_INIT { FSW_STRING_TYPE_EMPTY, 0, 0, NULL } + + +/* forward declarations */ + +struct fsw_dnode; +struct fsw_host_table; +struct fsw_fstype_table; + +struct fsw_blockcache { + fsw_u32 refcount; //!< Reference count + fsw_u32 cache_level; //!< Level of importance of this block + fsw_u32 phys_bno; //!< Physical block number + void *data; //!< Block data buffer +}; + +/** + * Core: Represents a mounted volume. + */ + +struct fsw_volume { + fsw_u32 phys_blocksize; //!< Block size for disk access / file system structures + fsw_u32 log_blocksize; //!< Block size for logical file data + + struct DNODESTRUCTNAME *root; //!< Root directory dnode + struct fsw_string label; //!< Volume label + + struct fsw_dnode *dnode_head; //!< List of all dnodes allocated for this volume + + struct fsw_blockcache *bcache; //!< Array of block cache entries + fsw_u32 bcache_size; //!< Number of entries in the block cache array + + void *host_data; //!< Hook for a host-specific data structure + struct fsw_host_table *host_table; //!< Dispatch table for host-specific functions + struct fsw_fstype_table *fstype_table; //!< Dispatch table for file system specific functions + int host_string_type; //!< String type used by the host environment +}; + +/** + * Core: Represents a "directory node" - a file, directory, symlink, whatever. + */ + +struct fsw_dnode { + fsw_u32 refcount; //!< Reference count + fsw_u32 complete; //!< Flag to be set on all dnode info was filled + + 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 +}; + +enum { + BLESSED_TYPE_SYSTEM_FILE, + BLESSED_TYPE_SYSTEM_FOLDER, + BLESSED_TYPE_OSX_FOLDER +}; + +/** + * Core: Stores the mapping of a region of a file to the data on disk. + */ + +struct fsw_extent { + int type; //!< Type of extent specification + fsw_u32 log_start; //!< Starting logical block number + fsw_u32 log_count; //!< Logical block count + fsw_u32 phys_start; //!< Starting physical block number (for FSW_EXTENT_TYPE_PHYSBLOCK only) + void *buffer; //!< Allocated buffer pointer (for FSW_EXTENT_TYPE_BUFFER only) +}; + +/** + * Possible extent representation types. FSW_EXTENT_TYPE_INVALID is for shandle's + * internal use only, it must not be returned from a get_extent function. + */ +enum { + FSW_EXTENT_TYPE_INVALID, + FSW_EXTENT_TYPE_SPARSE, + FSW_EXTENT_TYPE_PHYSBLOCK, + FSW_EXTENT_TYPE_BUFFER +}; + +/** + * Core: An access structure to a dnode's raw data. There can be multiple + * shandles per dnode, each of them has its own position pointer. + */ + +struct fsw_shandle { + struct fsw_dnode *dnode; //!< The dnode this handle reads data from + + fsw_u64 pos; //!< Current file pointer in bytes + struct fsw_extent extent; //!< Current extent +}; + +/** + * Core: Used in gathering detailed information on a volume. + */ + +struct fsw_volume_stat { + fsw_u64 total_bytes; //!< Total size of data area size in bytes + fsw_u64 free_bytes; //!< Bytes still available for storing file data +}; + +/** + * Core: Used in gathering detailed information on a dnode. + */ + +struct fsw_dnode_stat { + fsw_u64 used_bytes; //!< Bytes actually used by the file on disk + void (*store_time_posix)(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time); //!< Callback for storing a Posix-style timestamp + void (*store_attr_posix)(struct fsw_dnode_stat *sb, fsw_u16 posix_mode); //!< Callbock 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); + fsw_status_t (*get_bless_info)(struct VOLSTRUCTNAME *vol, fsw_u32 type, struct DNODESTRUCTNAME **dno_out); +}; + + +/** + * \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 VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *parent_dno, fsw_u32 dnode_id, int type, + struct fsw_string *name, struct DNODESTRUCTNAME **dno_out); +void fsw_dnode_mkcomplete(struct DNODESTRUCTNAME *dno); +int fsw_dnode_is_root(struct fsw_dnode *dno); +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); + +fsw_status_t fsw_dnode_get_path(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, struct fsw_string *out_path); +fsw_status_t fsw_get_bless_info(struct VOLSTRUCTNAME *vol, int type, struct fsw_string *out_path); + +/*@}*/ + + +/** + * \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_strsize(struct fsw_string *s); +void *fsw_strdata(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); + +/*@}*/ + + +/** + * \name Posix Mode Macros + * These macros can be used globally to test fields and bits in + * Posix-style modes. + * + * Taken from FreeBSD sys/stat.h. + */ +/*@{*/ +#ifndef S_IRWXU + +#define S_ISUID 0004000 /* set user id on execution */ +#define S_ISGID 0002000 /* set group id on execution */ +#define S_ISTXT 0001000 /* sticky bit */ + +#define S_IRWXU 0000700 /* RWX mask for owner */ +#define S_IRUSR 0000400 /* R for owner */ +#define S_IWUSR 0000200 /* W for owner */ +#define S_IXUSR 0000100 /* X for owner */ + +#define S_IRWXG 0000070 /* RWX mask for group */ +#define S_IRGRP 0000040 /* R for group */ +#define S_IWGRP 0000020 /* W for group */ +#define S_IXGRP 0000010 /* X for group */ + +#define S_IRWXO 0000007 /* RWX mask for other */ +#define S_IROTH 0000004 /* R for other */ +#define S_IWOTH 0000002 /* W for other */ +#define S_IXOTH 0000001 /* X for other */ + +#define S_IFMT 0170000 /* type of file mask */ +#define S_IFIFO 0010000 /* named pipe (fifo) */ +#define S_IFCHR 0020000 /* character special */ +#define S_IFDIR 0040000 /* directory */ +#define S_IFBLK 0060000 /* block special */ +#define S_IFREG 0100000 /* regular */ +#define S_IFLNK 0120000 /* symbolic link */ +#define S_IFSOCK 0140000 /* socket */ +#define S_ISVTX 0001000 /* save swapped text even after use */ +#define S_IFWHT 0160000 /* whiteout */ + +#define S_ISDIR(m) (((m) & 0170000) == 0040000) /* directory */ +#define S_ISCHR(m) (((m) & 0170000) == 0020000) /* char special */ +#define S_ISBLK(m) (((m) & 0170000) == 0060000) /* block special */ +#define S_ISREG(m) (((m) & 0170000) == 0100000) /* regular file */ +#define S_ISFIFO(m) (((m) & 0170000) == 0010000) /* fifo or socket */ +#define S_ISLNK(m) (((m) & 0170000) == 0120000) /* symbolic link */ +#define S_ISSOCK(m) (((m) & 0170000) == 0140000) /* socket */ +#define S_ISWHT(m) (((m) & 0170000) == 0160000) /* whiteout */ + +#define S_BLKSIZE 512 /* block size used in the stat struct */ + +#endif +/*@}*/ + + +#endif diff --git a/Staging/FswHfsPlus/fsw_efi.c b/Staging/FswHfsPlus/fsw_efi.c new file mode 100644 index 00000000..df75f08e --- /dev/null +++ b/Staging/FswHfsPlus/fsw_efi.c @@ -0,0 +1,1120 @@ +/** + * \file fsw_efi.c + * EFI host environment code. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_efi.h" + +#define DEBUG_LEVEL 0 + + +#ifndef FSTYPE +/** The file system type name to use. */ +#define FSTYPE ext2 +#endif + +/** Helper macro for stringification. */ +#define FSW_EFI_STRINGIFY(x) #x +/** Expands to the EFI driver name given the file system type name. */ +#define FSW_EFI_DRIVER_NAME(t) L"Fsw " FSW_EFI_STRINGIFY(t) L" File System Driver" + +// function prototypes + +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath); +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Start(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath); +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer); + +EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName); +EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName); + +void fsw_efi_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize); +fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer); + +EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume); + +EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This, + OUT EFI_FILE **Root); +EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno, + OUT EFI_FILE **NewFileHandle); + +EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer); +EFI_STATUS fsw_efi_file_getpos(IN FSW_FILE_DATA *File, + OUT UINT64 *Position); +EFI_STATUS fsw_efi_file_setpos(IN FSW_FILE_DATA *File, + IN UINT64 Position); + +EFI_STATUS fsw_efi_dir_open(IN FSW_FILE_DATA *File, + OUT EFI_FILE **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes); +EFI_STATUS fsw_efi_dir_read(IN FSW_FILE_DATA *File, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer); +EFI_STATUS fsw_efi_dir_setpos(IN FSW_FILE_DATA *File, + IN UINT64 Position); + +EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer); +EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, + IN struct fsw_dnode *dno, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer); + +EFI_STATUS fsw_efi_bless_info(IN FSW_VOLUME_DATA *Volume, + IN UINT32 type, + OUT VOID *Buffer, + IN OUT UINTN *BufferSize); +/** + * Interface structure for the EFI Driver Binding protocol. + */ + +EFI_DRIVER_BINDING_PROTOCOL fsw_efi_DriverBinding_table = { + fsw_efi_DriverBinding_Supported, + fsw_efi_DriverBinding_Start, + fsw_efi_DriverBinding_Stop, + 0x10, + NULL, + NULL +}; + +/** + * Interface structure for the EFI Component Name protocol. + */ + +EFI_COMPONENT_NAME_PROTOCOL fsw_efi_ComponentName_table = { + fsw_efi_ComponentName_GetDriverName, + fsw_efi_ComponentName_GetControllerName, + "eng" +}; + +/** + * Dispatch table for our FSW host driver. + */ + +struct fsw_host_table fsw_efi_host_table = { + FSW_STRING_TYPE_UTF16, + + fsw_efi_change_blocksize, + fsw_efi_read_block +}; + +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE); + + +/** + * Image entry point. Installs the Driver Binding and Component Name protocols + * on the image's handle. Actually mounting a file system is initiated through + * the Driver Binding protocol at the firmware's request. + */ + +EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS Status; + + // complete Driver Binding protocol instance + fsw_efi_DriverBinding_table.ImageHandle = ImageHandle; + fsw_efi_DriverBinding_table.DriverBindingHandle = ImageHandle; + // install Driver Binding protocol + Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle, + &gEfiDriverBindingProtocolGuid, + EFI_NATIVE_INTERFACE, + &fsw_efi_DriverBinding_table); + if (EFI_ERROR (Status)) { + return Status; + } + + // install Component Name protocol + Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle, + &gEfiComponentNameProtocolGuid, + EFI_NATIVE_INTERFACE, + &fsw_efi_ComponentName_table); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + * Driver Binding EFI protocol, Supported function. This function is called by EFI + * to test if this driver can handle a certain device. Our implementation only checks + * if the device is a disk (i.e. that it supports the Block I/O and Disk I/O protocols) + * and implicitly checks if the disk is already in use by another driver. + */ + +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath) +{ + EFI_STATUS Status; + EFI_DISK_IO *DiskIo; + + // we check for both DiskIO and BlockIO protocols + + // first, open DiskIO + Status = BS->OpenProtocol(ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER); + if (EFI_ERROR(Status)) + return Status; + + // we were just checking, close it again + BS->CloseProtocol(ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle); + + // next, check BlockIO without actually opening it + Status = BS->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 = BS->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)) { + Print(L"Fsw ERROR: OpenProtocol(BlockIo) returned %x\n", Status); + return Status; + } + + Status = BS->OpenProtocol(ControllerHandle, + &gEfiDiskIoProtocolGuid, + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER); + if (EFI_ERROR(Status)) { + Print(L"Fsw ERROR: OpenProtocol(DiskIo) returned %x\n", Status); + 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)) { + // register the SimpleFileSystem protocol + Volume->FileSystem.Revision = EFI_FILE_IO_INTERFACE_REVISION; + Volume->FileSystem.OpenVolume = fsw_efi_FileSystem_OpenVolume; + Status = BS->InstallMultipleProtocolInterfaces(&ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, &Volume->FileSystem, + NULL); + if (EFI_ERROR(Status)) + Print(L"Fsw ERROR: InstallMultipleProtocolInterfaces returned %x\n", Status); + } + + // on errors, close the opened protocols + if (EFI_ERROR(Status)) { + if (Volume->vol != NULL) + fsw_unmount(Volume->vol); + FreePool(Volume); + + BS->CloseProtocol(ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle); + } + + return Status; +} + +/** + * Driver Binding EFI protocol, Stop function. This function is called by EFI + * to stop the driver on the given device. This translates to an unmount + * call for the FSW core. + * + * We assume that all file handles on the volume have been closed before + * the driver is stopped. At least with the EFI shell, that is actually the + * case; it closes all file handles between commands. + */ + +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer) +{ + EFI_STATUS Status; + EFI_FILE_IO_INTERFACE *FileSystem; + FSW_VOLUME_DATA *Volume; + +#if DEBUG_LEVEL + Print(L"fsw_efi_DriverBinding_Stop\n"); +#endif + + // get the installed SimpleFileSystem interface + Status = BS->OpenProtocol(ControllerHandle, + &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 = BS->UninstallMultipleProtocolInterfaces(ControllerHandle, + &gEfiSimpleFileSystemProtocolGuid, &Volume->FileSystem, + NULL); + if (EFI_ERROR(Status)) { + Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status); + return Status; + } +#if DEBUG_LEVEL + Print(L"fsw_efi_DriverBinding_Stop: protocol uninstalled successfully\n"); +#endif + + // release private data structure + if (Volume->vol != NULL) + fsw_unmount(Volume->vol); + FreePool(Volume); + + // close the consumed protocols + Status = BS->CloseProtocol(ControllerHandle, + &gEfiDiskIoProtocolGuid, + This->DriverBindingHandle, + ControllerHandle); + + return Status; +} + +/** + * Component Name EFI protocol, GetDriverName function. Used by the EFI + * environment to inquire the name of this driver. The name returned is + * based on the file system type actually used in compilation. + */ + +EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName) +{ + if (Language == NULL || DriverName == NULL) + return EFI_INVALID_PARAMETER; + + if (Language[0] == 'e' && Language[1] == 'n' && Language[2] == 'g' && Language[3] == 0) { + *DriverName = FSW_EFI_DRIVER_NAME(FSTYPE); + return EFI_SUCCESS; + } + return EFI_UNSUPPORTED; +} + +/** + * Component Name EFI protocol, GetControllerName function. Not implemented + * because this is not a "bus" driver in the sense of the EFI Driver Model. + */ + +EFI_STATUS EFIAPI fsw_efi_ComponentName_GetControllerName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName) +{ + return EFI_UNSUPPORTED; +} + +/** + * FSW interface function for block size changes. This function is called by the FSW core + * when the file system driver changes the block sizes for the volume. + */ + +void fsw_efi_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize) +{ + // nothing to do +} + +/** + * FSW interface function to read data blocks. This function is called by the FSW core + * to read a block of data from the device. The buffer is allocated by the core code. + */ + +fsw_status_t fsw_efi_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer) +{ + EFI_STATUS Status; + FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)vol->host_data; + + FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_efi_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize)); + + // read from disk + Status = Volume->DiskIo->ReadDisk(Volume->DiskIo, Volume->MediaId, + (UINT64)phys_bno * vol->phys_blocksize, + vol->phys_blocksize, + buffer); + Volume->LastIOStatus = Status; + if (EFI_ERROR(Status)) + return FSW_IO_ERROR; + return FSW_SUCCESS; +} + +/** + * Map FSW status codes to EFI status codes. The FSW_IO_ERROR code is only produced + * by fsw_efi_read_block, so we map it back to the EFI status code remembered from + * the last I/O operation. + */ + +EFI_STATUS fsw_efi_map_status(fsw_status_t fsw_status, FSW_VOLUME_DATA *Volume) +{ + switch (fsw_status) { + case FSW_SUCCESS: + return EFI_SUCCESS; + case FSW_OUT_OF_MEMORY: + return EFI_VOLUME_CORRUPTED; + case FSW_IO_ERROR: + return Volume->LastIOStatus; + case FSW_UNSUPPORTED: + return EFI_UNSUPPORTED; + case FSW_NOT_FOUND: + return EFI_NOT_FOUND; + case FSW_VOLUME_CORRUPTED: + return EFI_VOLUME_CORRUPTED; + default: + return EFI_DEVICE_ERROR; + } +} + +/** + * File System EFI protocol, OpenVolume function. Creates a file handle for + * the root directory and returns it. Note that this function may be called + * multiple times and returns a new file handle each time. Each returned + * handle is closed by the client using it. + */ + +EFI_STATUS EFIAPI fsw_efi_FileSystem_OpenVolume(IN EFI_FILE_IO_INTERFACE *This, + OUT EFI_FILE **Root) +{ + EFI_STATUS Status; + FSW_VOLUME_DATA *Volume = FSW_VOLUME_FROM_FILE_SYSTEM(This); + +#if DEBUG_LEVEL + Print(L"fsw_efi_FileSystem_OpenVolume\n"); +#endif + + Status = fsw_efi_dnode_to_FileHandle(Volume->vol->root, Root); + + return Status; +} + +/** + * File Handle EFI protocol, Open function. Dispatches the call + * based on the kind of file handle. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Open(IN EFI_FILE *This, + OUT EFI_FILE **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + if (File->Type == FSW_EFI_FILE_TYPE_DIR) + return fsw_efi_dir_open(File, NewHandle, FileName, OpenMode, Attributes); + // not supported for regular files + return EFI_UNSUPPORTED; +} + +/** + * File Handle EFI protocol, Close function. Closes the FSW shandle + * and frees the memory used for the structure. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Close(IN EFI_FILE *This) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + +#if DEBUG_LEVEL + Print(L"fsw_efi_FileHandle_Close\n"); +#endif + + fsw_shandle_close(&File->shand); + FreePool(File); + + return EFI_SUCCESS; +} + +/** + * File Handle EFI protocol, Delete function. Calls through to Close + * and returns a warning because this driver is read-only. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Delete(IN EFI_FILE *This) +{ + EFI_STATUS Status; + + Status = This->Close(This); + if (Status == EFI_SUCCESS) { + // this driver is read-only + Status = EFI_WARN_DELETE_FAILURE; + } + + return Status; +} + +/** + * File Handle EFI protocol, Read function. Dispatches the call + * based on the kind of file handle. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Read(IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + if (File->Type == FSW_EFI_FILE_TYPE_FILE) + return fsw_efi_file_read(File, BufferSize, Buffer); + else if (File->Type == FSW_EFI_FILE_TYPE_DIR) + return fsw_efi_dir_read(File, BufferSize, Buffer); + return EFI_UNSUPPORTED; +} + +/** + * File Handle EFI protocol, Write function. Returns unsupported status + * because this driver is read-only. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Write(IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer) +{ + // this driver is read-only + return EFI_WRITE_PROTECTED; +} + +/** + * File Handle EFI protocol, GetPosition function. Dispatches the call + * based on the kind of file handle. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_GetPosition(IN EFI_FILE *This, + OUT UINT64 *Position) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + if (File->Type == FSW_EFI_FILE_TYPE_FILE) + return fsw_efi_file_getpos(File, Position); + // not defined for directories + return EFI_UNSUPPORTED; +} + +/** + * File Handle EFI protocol, SetPosition function. Dispatches the call + * based on the kind of file handle. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_SetPosition(IN EFI_FILE *This, + IN UINT64 Position) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + if (File->Type == FSW_EFI_FILE_TYPE_FILE) + return fsw_efi_file_setpos(File, Position); + else if (File->Type == FSW_EFI_FILE_TYPE_DIR) + return fsw_efi_dir_setpos(File, Position); + return EFI_UNSUPPORTED; +} + +/** + * File Handle EFI protocol, GetInfo function. Dispatches to the common + * function implementing this. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_GetInfo(IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + FSW_FILE_DATA *File = FSW_FILE_FROM_FILE_HANDLE(This); + + return fsw_efi_dnode_getinfo(File, InformationType, BufferSize, Buffer); +} + +/** + * File Handle EFI protocol, SetInfo function. Returns unsupported status + * because this driver is read-only. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_SetInfo(IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer) +{ + // this driver is read-only + return EFI_WRITE_PROTECTED; +} + +/** + * File Handle EFI protocol, Flush function. Returns unsupported status + * because this driver is read-only. + */ + +EFI_STATUS EFIAPI fsw_efi_FileHandle_Flush(IN EFI_FILE *This) +{ + // this driver is read-only + return EFI_WRITE_PROTECTED; +} + +/** + * Set up a file handle for a dnode. This function allocates a data structure + * for a file handle, opens a FSW shandle and populates the EFI_FILE structure + * with the interface functions. + */ + +EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno, + OUT EFI_FILE **NewFileHandle) +{ + EFI_STATUS Status; + FSW_FILE_DATA *File; + + // make sure the dnode has complete info + Status = fsw_efi_map_status(fsw_dnode_fill(dno), (FSW_VOLUME_DATA *)dno->vol->host_data); + if (EFI_ERROR(Status)) + return Status; + + // check type + if (dno->type != FSW_DNODE_TYPE_FILE && dno->type != FSW_DNODE_TYPE_DIR) + return EFI_UNSUPPORTED; + + // allocate file structure + File = AllocateZeroPool(sizeof(FSW_FILE_DATA)); + File->Signature = FSW_FILE_DATA_SIGNATURE; + if (dno->type == FSW_DNODE_TYPE_FILE) + File->Type = FSW_EFI_FILE_TYPE_FILE; + else if (dno->type == FSW_DNODE_TYPE_DIR) + File->Type = FSW_EFI_FILE_TYPE_DIR; + + // open shandle + Status = fsw_efi_map_status(fsw_shandle_open(dno, &File->shand), + (FSW_VOLUME_DATA *)dno->vol->host_data); + if (EFI_ERROR(Status)) { + FreePool(File); + return Status; + } + + // populate the file handle + File->FileHandle.Revision = EFI_FILE_HANDLE_REVISION; + File->FileHandle.Open = fsw_efi_FileHandle_Open; + File->FileHandle.Close = fsw_efi_FileHandle_Close; + File->FileHandle.Delete = fsw_efi_FileHandle_Delete; + File->FileHandle.Read = fsw_efi_FileHandle_Read; + File->FileHandle.Write = fsw_efi_FileHandle_Write; + File->FileHandle.GetPosition = fsw_efi_FileHandle_GetPosition; + File->FileHandle.SetPosition = fsw_efi_FileHandle_SetPosition; + File->FileHandle.GetInfo = fsw_efi_FileHandle_GetInfo; + File->FileHandle.SetInfo = fsw_efi_FileHandle_SetInfo; + File->FileHandle.Flush = fsw_efi_FileHandle_Flush; + + *NewFileHandle = &File->FileHandle; + return EFI_SUCCESS; +} + +/** + * Data read function for regular files. Calls through to fsw_shandle_read. + */ + +EFI_STATUS fsw_efi_file_read(IN FSW_FILE_DATA *File, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + fsw_u32 buffer_size; + +#if DEBUG_LEVEL + Print(L"fsw_efi_file_read %d bytes\n", *BufferSize); +#endif + + buffer_size = (fsw_u32) *BufferSize; + 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; + } +} + +EFI_STATUS fsw_efi_bless_info(IN FSW_VOLUME_DATA *Volume, + IN UINT32 BlessType, + OUT VOID *Buffer, + IN OUT UINTN *BufferSize) { + EFI_STATUS Status; + UINTN RequiredSize; + EFI_DEVICE_PATH_PROTOCOL *devicePathProtocol; + struct fsw_string PathStr; + UINT16 *PathChars; + +#if DEBUG_LEVEL + Print(L"fsw_efi_bless_info.1\n"); +#endif + + Status = fsw_efi_map_status(fsw_get_bless_info(Volume->vol, BlessType, &PathStr), Volume); + if (Status) + return EFI_NOT_FOUND; + + // We assume that PathStr is in UTF16 + // Reserve one symbol for '\0' + RequiredSize = (PathStr.len + 1) * sizeof(UINT16); + if ((PathChars = AllocatePool(RequiredSize)) == NULL) { + fsw_strfree (&PathStr); + return EFI_OUT_OF_RESOURCES; + } + + CopyMem(PathChars, PathStr.data, PathStr.size); + PathChars[PathStr.len] = 0; + fsw_strfree(&PathStr); + +#if DEBUG_LEVEL + Print(L"fsw_efi_bless_info.2 type=%d, res=%s\n", BlessType, PathChars); +#endif + + devicePathProtocol = FileDevicePath(Volume->Handle, PathChars); + FreePool(PathChars); + + RequiredSize = GetDevicePathSize(devicePathProtocol); + if (*BufferSize < RequiredSize) + Status = EFI_BUFFER_TOO_SMALL; + else { + CopyMem(Buffer, devicePathProtocol, RequiredSize); + Status = EFI_SUCCESS; + } + + *BufferSize = RequiredSize; + FreePool(devicePathProtocol); + + return Status; +} + +/** + * 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 if (CompareGuid(InformationType, &gAppleBlessedSystemFileInfoGuid)) { + Status = fsw_efi_bless_info(Volume, BLESSED_TYPE_SYSTEM_FILE, Buffer, BufferSize); + } else if (CompareGuid(InformationType, &gAppleBlessedSystemFolderInfoGuid)) { + Status = fsw_efi_bless_info(Volume, BLESSED_TYPE_SYSTEM_FOLDER, Buffer, BufferSize); + } else if (CompareGuid(InformationType, &gAppleBlessedOsxFolderInfoGuid)) { + Status = fsw_efi_bless_info(Volume, BLESSED_TYPE_OSX_FOLDER, Buffer, BufferSize); + } 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; + struct fsw_dnode *target_dno; + + // 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; + + // copy name before symlink resolving + fsw_efi_strcpy(FileInfo->FileName, &dno->name); + + // resolve symlink if needed + Status = fsw_efi_map_status (fsw_dnode_resolve (dno, &target_dno), Volume); + if (EFI_ERROR (Status)) + return Status; + + // make sure the dnode has complete info + Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume); + if (EFI_ERROR(Status)) { + fsw_dnode_release (target_dno); + return Status; + } + + fsw_dnode_release(dno); + dno = target_dno; + + FileInfo->Size = RequiredSize; + FileInfo->FileSize = dno->size; + FileInfo->Attribute = 0; + if (dno->type == FSW_DNODE_TYPE_DIR) + FileInfo->Attribute |= EFI_FILE_DIRECTORY; + fsw_efi_strcpy(FileInfo->FileName, &dno->name); + + // get the missing info from the fs driver + ZeroMem(&sb, sizeof(struct fsw_dnode_stat)); + sb.store_time_posix = fsw_efi_store_time_posix; + sb.store_attr_posix = fsw_efi_store_attr_posix; + sb.host_data = FileInfo; + Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume); + if (EFI_ERROR(Status)) + return Status; + FileInfo->PhysicalSize = sb.used_bytes; + + // prepare for return + *BufferSize = RequiredSize; +#if DEBUG_LEVEL + Print(L"...returning '%s'\n", FileInfo->FileName); +#endif + return EFI_SUCCESS; +} + +// EOF diff --git a/Staging/FswHfsPlus/fsw_efi.h b/Staging/FswHfsPlus/fsw_efi.h new file mode 100644 index 00000000..91751067 --- /dev/null +++ b/Staging/FswHfsPlus/fsw_efi.h @@ -0,0 +1,103 @@ +/** + * \file fsw_efi.h + * EFI host environment header. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_EFI_H_ +#define _FSW_EFI_H_ + +#include +#include "fsw_core.h" + + +/** + * EFI Host: Private per-volume structure. + */ + +typedef struct { + UINT64 Signature; //!< Used to identify this structure + + EFI_FILE_IO_INTERFACE FileSystem; //!< Published EFI protocol interface structure + + EFI_HANDLE Handle; //!< The device handle the protocol is attached to + EFI_DISK_IO *DiskIo; //!< The Disk I/O protocol we use for disk access + UINT32 MediaId; //!< The media ID from the Block I/O protocol + EFI_STATUS LastIOStatus; //!< Last status from Disk I/O + + struct fsw_volume *vol; //!< FSW volume structure + +} FSW_VOLUME_DATA; + +/** Signature for the volume structure. */ +#define FSW_VOLUME_DATA_SIGNATURE EFI_SIGNATURE_32 ('f', 's', 'w', 'V') +/** Access macro for the volume structure. */ +#define FSW_VOLUME_FROM_FILE_SYSTEM(a) CR (a, FSW_VOLUME_DATA, FileSystem, FSW_VOLUME_DATA_SIGNATURE) + +/** + * EFI Host: Private structure for a EFI_FILE interface. + */ + +typedef struct { + UINT64 Signature; //!< Used to identify this structure + + EFI_FILE FileHandle; //!< Published EFI protocol interface structure + + UINTN Type; //!< File type used for dispatchinng + struct fsw_shandle shand; //!< FSW handle for this file + +} FSW_FILE_DATA; + +/** File type: regular file. */ +#define FSW_EFI_FILE_TYPE_FILE (0) +/** File type: directory. */ +#define FSW_EFI_FILE_TYPE_DIR (1) + +/** Signature for the file handle structure. */ +#define FSW_FILE_DATA_SIGNATURE EFI_SIGNATURE_32 ('f', 's', 'w', 'F') +/** Access macro for the file handle structure. */ +#define FSW_FILE_FROM_FILE_HANDLE(a) CR (a, FSW_FILE_DATA, FileHandle, FSW_FILE_DATA_SIGNATURE) + + +// +// Library functions +// + +VOID fsw_efi_decode_time(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime); + +UINTN fsw_efi_strsize(struct fsw_string *s); +VOID fsw_efi_strcpy(CHAR16 *Dest, struct fsw_string *src); + + +#endif diff --git a/Staging/FswHfsPlus/fsw_efi_base.h b/Staging/FswHfsPlus/fsw_efi_base.h new file mode 100644 index 00000000..6a1c1acf --- /dev/null +++ b/Staging/FswHfsPlus/fsw_efi_base.h @@ -0,0 +1,81 @@ +/** + * \file fsw_efi_base.h + * Base definitions for the EFI host environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_EFI_BASE_H_ +#define _FSW_EFI_BASE_H_ + + +#include "fsw_efi_edk2_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) DEBUG_INFO, s +#define FSW_MSGFUNC(params) DEBUG(params) + +// 64-bit hooks + +#define FSW_U64_SHR(val,shiftbits) RShiftU64((val), (shiftbits)) +#define FSW_U64_DIV(val,divisor) DivU64x32((val), (divisor), NULL) + + +#endif diff --git a/Staging/FswHfsPlus/fsw_efi_edk2_base.h b/Staging/FswHfsPlus/fsw_efi_edk2_base.h new file mode 100644 index 00000000..ca14eb25 --- /dev/null +++ b/Staging/FswHfsPlus/fsw_efi_edk2_base.h @@ -0,0 +1,76 @@ +/** + * \file fsw_efi_edk2_base.h + * Base definitions for the EDK EFI Toolkit environment. + */ +/* + * Copyright (c) 2012 Stefan Agner + * + * 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_EDK2_BASE_H_ +#define _FSW_EFI_EDK2_BASE_H_ +/* + * Here is common declarations for EDK<->EDK2 compatibility + */ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +# define BS gBS + +# 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)) + + +#endif diff --git a/Staging/FswHfsPlus/fsw_efi_lib.c b/Staging/FswHfsPlus/fsw_efi_lib.c new file mode 100644 index 00000000..b079a3d7 --- /dev/null +++ b/Staging/FswHfsPlus/fsw_efi_lib.c @@ -0,0 +1,129 @@ +/** + * \file fsw_efi_lib.c + * EFI host environment library functions. + */ + +/*- + * 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; + } +} + +// EOF diff --git a/Staging/FswHfsPlus/fsw_hfsplus.c b/Staging/FswHfsPlus/fsw_hfsplus.c new file mode 100644 index 00000000..45f64cb4 --- /dev/null +++ b/Staging/FswHfsPlus/fsw_hfsplus.c @@ -0,0 +1,1363 @@ +/** @file + HFS+ file system driver. + + Copyright (c) 2020, Vladislav Yaroshchuk + Copyright (C) 2017, Gabriel L. Somlo + + This program and the accompanying materials are licensed and made + available under the terms and conditions of the BSD License which + accompanies this distribution. The full text of the license may + be found at http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" + BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER + EXPRESS OR IMPLIED. +**/ + +#include "fsw_hfsplus.h" + +// -------------------------------------------------------- +// HFS+ FSW Methods declaration +// + +/* Mount an HFS+ volume. Read volume header (equivalent of superblock), + * and set up dnodes for the root folder and B-Tree file(s). + * Return FSW_SUCCESS or error code. + */ +static fsw_status_t +fsw_hfsplus_vol_mount(struct fsw_hfsplus_volume *v); + +/* Free the volume data structure. Called by the core after an unmount or + * unsuccessful mount, to release the memory used by the file system type + * specific part of the volume structure. + */ +static void +fsw_hfsplus_vol_free(struct fsw_hfsplus_volume *v); + +/* Get in-depth information on a volume. + */ +static fsw_status_t +fsw_hfsplus_vol_stat(struct fsw_hfsplus_volume *v, struct fsw_volume_stat *s); + +/* Get full information on a dnode from disk. This function is called by + * the core whenever it needs to access fields in the dnode structure that + * may not be filled immediately upon creation of the dnode. + */ +static fsw_status_t +fsw_hfsplus_dno_fill(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d); + +/* 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_hfsplus_dno_free(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d); + +/* Get in-depth dnode information. The core ensures fsw_hfsplus_dno_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_hfsplus_dno_stat(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_dnode_stat *s); + +/* 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_hfsplus_dno_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. + * NOTE: logical and physical block sizes are the same (see mount method). + */ +static fsw_status_t +fsw_hfsplus_get_ext(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_extent *e); + + +/* 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_hfsplus_dno_fill has been called and the dnode is actually a directory. + */ +static fsw_status_t +fsw_hfsplus_dir_get(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_string *name, struct fsw_hfsplus_dnode **d_out); + +/* 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_hfsplus_dno_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_hfsplus_dir_read(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_shandle *sh, struct fsw_hfsplus_dnode **d_out); + +/* 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_hfsplus_dno_fill has been called on the dnode and that it really is a + * symlink. + */ +static fsw_status_t +fsw_hfsplus_readlink(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_string *lnk_tgt); +/** + * Get dnode associated with the given bless type + * @param vol volume + * @param bless_type bless type + * @param dno_out resulting dnode + * @return FSW_SUCCESS on success + */ +static fsw_status_t +fsw_hfsplus_get_bless_info(struct fsw_hfsplus_volume *vol, /* in */ + fsw_u32 bless_type, /* in */ + struct fsw_hfsplus_dnode **dno_out /* out */); + +/** + * HFS+ FSW Method Dispatch Table + */ +struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfsplus) = { + { FSW_STRING_TYPE_ISO88591, 4, 4, "hfsplus" }, + sizeof(struct fsw_hfsplus_volume), sizeof(struct fsw_hfsplus_dnode), + fsw_hfsplus_vol_mount, fsw_hfsplus_vol_free, fsw_hfsplus_vol_stat, + fsw_hfsplus_dno_fill, fsw_hfsplus_dno_free, fsw_hfsplus_dno_stat, + fsw_hfsplus_get_ext, fsw_hfsplus_dir_get, fsw_hfsplus_dir_read, + fsw_hfsplus_readlink, fsw_hfsplus_get_bless_info +}; + +// ------------------------------------------------------------------- +// Other declarations + +/* Given dnode 'd' and offset 'pos' into the data file it represents, + * retrieve 'len' bytes of data into buffer 'buf'; + * Return FSW_SUCCESS or error code. + */ +static fsw_status_t +fsw_hfsplus_read(struct fsw_hfsplus_dnode *d, fsw_u64 pos, + fsw_u32 len, void *buf); + +/* Given the volume being mounted ('v'), and the ID & fork data of the B-Tree + * file being set up ('dn_id' and 'f', respectively), populate a cached, + * in-memory record of the B-Tree file at the location pointed to by 'btp'; + * Return FSW_SUCCESS or error code. + */ +static fsw_status_t +fsw_hfsplus_btf_setup(struct fsw_hfsplus_volume *v, + fsw_u32 dn_id, HFSPlusForkData *f, + struct fsw_hfsplus_dnode **btp); + +/* HFS+ to Posix timestamp conversion + */ +static fsw_u32 +fsw_hfsplus_posix_time(fsw_u32 t); + +/* Given a B-Tree node pointed to by 'btnode', with node size 'size', + * locate the record given by its record number 'rnum'; + * Return a pointer to the B-Tree key at the beginning of the record. + */ +static HFSPlusBTKey * +fsw_hfsplus_btnode_get_rec(BTNodeDescriptor* btnode, fsw_u16 size, fsw_u32 rnum); + +/* Given a B-Tree record pointer 'k', return a pointer to the data + * immediately following the key record; IOW, skip the key record which + * prefixes the record data payload. + */ +static void * +fsw_hfsplus_bt_rec_skip_key(HFSPlusBTKey *k); + +/* Return the child node number immediately following the key record 'k' of + * an index node + */ +static fsw_u32 +fsw_hfsplus_bt_idx_get_child(HFSPlusBTKey *k); + +/** + * Create minimal dnode that will be complete in future with fsw_hfsplus_dnode_complete() call. + * fsw_hfsplus_dnode_fill() also makes dnode complete + * @param parent_id parent dnode id + * @param dnode_id dnode id + * @param name dnode name + * @param d_out created dnode + * @return FSW_SUCCESS on success + */ +static fsw_status_t +fsw_hfsplus_dnode_create_minimal(struct fsw_hfsplus_volume *v /* in */, + fsw_u32 parent_id /* in */, + fsw_u32 dnode_id /* in */, + struct fsw_string *name /* in */, + struct fsw_hfsplus_dnode **d_out /* out */); + +/** + * Complete dnode from HFSPlusCatalogRecord information. + * We assume that d_out's name, id and parent_id already filled + * @param rec catalog record + * @param parent parent dnode + * @param d_out dnode to complete + */ +static void +fsw_hfsplus_dnode_complete(HFSPlusCatalogRecord *rec /* in */, + struct fsw_hfsplus_dnode *parent /* in */, + struct fsw_hfsplus_dnode *d_out /* out */); + +/** + * Create dnode and complete its data with 'rec' info + * @param rec catalog record + * @param parent parent dnode + * @param name dnode name + * @param d_out created dnode + * @return FSW_SUCCESS on success + */ +static fsw_status_t +fsw_hfsplus_dnode_create_full(HFSPlusCatalogRecord *rec /* in */, + struct fsw_hfsplus_dnode *parent /* in */, + struct fsw_string *name /* in */, + struct fsw_hfsplus_dnode **d_out /* out */); + +/** Compare an on-disk catalog B-Tree trial key ('tk') with an in-memory + * search key ('sk'). Precedence is parentID (keyLength does not + * factor into the comparison). Allows only empty 'tk' nodeName + * @param sk search key + * @param on-disk catalog B-Tree trial key + * @return -1/0/1 if 'tk'is smaller/equal/larger than 'sk', respectively. + */ +static int +fsw_hfsplus_thread_cmp(HFSPlusBTKey *tk /* in */, HFSPlusBTKey *sk /* in */); + +/** Compare an on-disk catalog B-Tree trial key ('tk') with an in-memory + * search key ('sk'). Precedence is parentID, nodeName (keyLength does not + * factor into the comparison). + * @param sk search key + * @param on-disk catalog B-Tree trial key + * @return -1/0/1 if 'tk'is smaller/equal/larger than 'sk', respectively. + */ +static int +fsw_hfsplus_cat_cmp(HFSPlusBTKey *tk /* in */, HFSPlusBTKey *sk /* in */); + + +/** + * Convert fsw string to HFSUniStr255 (FSW_STRING_TYPE_UTF16 content type). + * Used to prepare names for fsw_hfsplus_bt_search() search key + * @param t target string + * @param src source + * @return FSW_SUCCESS on success + */ +static fsw_status_t +fsw_hfsplus_fswstr2unistr(HFSUniStr255* t /* out */, + struct fsw_string* src /* in */); + +/* Search an HFS+ special file's B-Tree (given by 'bt'), for a search key + * matching 'sk', using comparison procedure 'k_cmp' to determine when a key + * match occurs; + * Finish by filling a caller-provided B-Tree node buffer ('btnode'), and returning + * record number of the matching record inside 'btnode', via 'rec_num'; + * On error, set fsw_status_t return code acoordingly. + * + * NOTE: A HFS+ volume has a few "special" files, linked directly from the + * volume header. For the purpose of this driver, we mainly care about + * two of them: the "catalog" and "extents" files. All of these files + * are organized as B-Tree structures. This means that, overlaid on + * top of the linear span of each file there is an array of nodes of + * a given size (node_size), internally cross-linked with "pointers" + * to parent/child/sibling nodes, which are essentially the "index" + * (or 'node-number') of the target node in this overlaid array. + * Ultimately, (node-number * node-size) is a byte offset into the + * file, to the location where the referenced node's data begins. + * Each B-Tree file's "dnode" information is available in the HFS+ + * volume header. The node at the very beginning of each file (at + * index or node-number == 0) contains a "header node", which provides + * the 'node-number' of the B-Tree's "root" node, as well as the + * 'node-size' of all nodes in that B-Tree file. + */ +static fsw_status_t +fsw_hfsplus_bt_search(struct fsw_hfsplus_dnode *bt, /* in */ + HFSPlusBTKey *sk, /* in */ + k_cmp_t k_cmp, /* in */ + BTNodeDescriptor *btnode, /* out */ + fsw_u32 *rec_num /* out */); + +/* Compare unsigned integers 'a' and 'b'; + * Return -1/0/1 if 'a' is less/equal/greater than 'b'. + */ +static int +fsw_hfsplus_int_cmp(fsw_u32 a, fsw_u32 b); + +/* Basic latin unicode lowercase + */ +static fsw_u16 +fsw_hfsplus_ucblatin_tolower(fsw_u16 c); + +/** + * Iterate B-Tree records searching for `rec_skip` node starting from `rec_num` record of `btnode` + * @param bt B-Tree root + * @param parent_id parent dnode id (used as stop criteria: finished on unequal parent ids) + * @param btnode BTNode to start iterate from. On finish filled with actual value + * @param rec_num BTNode record number to start iterate from. On finish filled with actual value + * @param rec_skip Number of records to skip + * @return FSW_SUCCESS on success, else FSW_NOT_FOUND + */ +static fsw_status_t +fsw_hfsplus_btree_get_rec(struct fsw_hfsplus_dnode *bt, /* in */ + fsw_u32 parent_id, /* in */ + BTNodeDescriptor *btnode, /* in */ /* out */ + fsw_u32 *rec_num, /* in */ /* out */ + fsw_u64 *rec_skip /* in */ /* out */); + +/** + * Find thread record by the existing dnid (CNID in HFS+ specs) + * @param v volume + * @param dnid given dnid + * @param btnode pre-allocated btnode used for b-tree search + * @param thread_out found thread record + * @return FSW_SUCCESS on success + */ +static fsw_status_t +fsw_hfsplus_dnid2thread(struct fsw_hfsplus_volume *v /* in */, + fsw_u32 dnid /* in */, + BTNodeDescriptor *btnode /* in */ /* out */, + HFSPlusCatalogThread **thread_out /* out */); + +/** + * Create d_node from existing thread record. + * Created dnode is not complete and should be filled by fsw_dnode_fill() call + * @param v volume + * @param thread known thread record + * @param btnode pre-allocated btnode used for b-tree search + * @param d_out created dnode + * @return FSW_SUCCESS on success + */ +static fsw_status_t +fsw_hfsplus_thread2dnode(struct fsw_hfsplus_volume *v /* in */, + HFSPlusCatalogThread *thread /* in */, + BTNodeDescriptor *btnode /* in */ /* out */, + struct fsw_hfsplus_dnode **d_out /* out */); + +/** + * Create dnode from known dnid (CNID in HFS+ specs). + * Created dnode is not complete and should be filled by fsw_dnode_fill() call + * @param v volume + * @param dnid known dnid + * @param d_out created dnode + * @return FSW_SUCCESS on success + */ +static fsw_status_t +fsw_hfsplus_dnid2dnode(struct fsw_hfsplus_volume *v /* in */, + fsw_u32 dnid /* in */, + struct fsw_hfsplus_dnode **d_out /* out */); +/** +* Read requested Bless info type from the volume header, returns according dnid (CNID in HFS+ specs) +* @param vol volume +* @param bless_type bless info type +* @param dnid readed dnid +* @return FSW_SUCCESS if found +*/ +static fsw_status_t +fsw_hfsplus_bless_type2dnid(struct fsw_hfsplus_volume *vol, /* in */ + fsw_u32 bless_type, /* in */ + fsw_u32 *dnid /* out */); + +/** + * Convert fsw_u32 to string (char[]). Memory will be allocated depending on the string length required + * @param num Source number + * @param dest Destination array + * @param len Destination string length + * @return FSW_OUT_OF_MEMORY on fsw_alloc() errored, else FSW_SUCCESS + */ +static fsw_status_t +fsw_hfsplus_itoa(fsw_u32 num, char** dest, int* len); + +/** + * Concatenate two strings + * @param s1 First string + * @param len1 First string length + * @param s2 Second string + * @param len2 Second string length + * @param dest Destination buffer + * @return FSW_OUT_OF_MEMORY on fsw_alloc() errored, else FSW_SUCCESS + */ +static fsw_status_t +fsw_hfsplus_sconcat(const char *const s1, int len1, const char *const s2, int len2, char **dest); + +// --------------------------------------------------------------------- + + +static fsw_status_t +fsw_hfsplus_read(struct fsw_hfsplus_dnode *d, fsw_u64 pos, + fsw_u32 len, void *buf) +{ + struct fsw_shandle sh; + fsw_u32 buflen; + fsw_status_t status; + + status = fsw_shandle_open(d, &sh); + if (status) + return status; + + sh.pos = pos; + buflen = len; + status = fsw_shandle_read(&sh, &buflen, buf); + if (!status && buflen != len) + status = FSW_IO_ERROR; + + fsw_shandle_close(&sh); + return status; +} + +static fsw_status_t +fsw_hfsplus_btf_setup(struct fsw_hfsplus_volume *v, + fsw_u32 dn_id, HFSPlusForkData *f, + struct fsw_hfsplus_dnode **btp) +{ + BTHeaderRec hdr_rec; + fsw_status_t status; + + status = fsw_dnode_create_root(v, dn_id, btp); + if (status) + return status; + + (*btp)->g.size = fsw_u64_be_swap(f->logicalSize); + fsw_memcpy((*btp)->extents, f->extents, sizeof(HFSPlusExtentRecord)); + + // read header record (from node 0, immediately past the node descriptor) + status = fsw_hfsplus_read(*btp, sizeof(BTNodeDescriptor), + sizeof(BTHeaderRec), &hdr_rec); + if (status) + return status; + + // grab root node index and node size from header record + (*btp)->bt_root = fsw_u32_be_swap(hdr_rec.rootNode); + (*btp)->bt_ndsz = fsw_u16_be_swap(hdr_rec.nodeSize); + + return FSW_SUCCESS; +} + +static fsw_status_t +fsw_hfsplus_vol_mount(struct fsw_hfsplus_volume *v) +{ + void *buf; + fsw_u32 bs; + BTNodeDescriptor *btnode; + HFSPlusCatalogThread *thread; + struct fsw_string label; + fsw_status_t status; + + // allocate memory for vol. header + status = fsw_alloc(sizeof(HFSPlusVolumeHeader), &v->vh); + if (status) + return status; + + // read vol. header into buffer + fsw_set_blocksize(v, kHFSBlockSize, kHFSBlockSize); + status = fsw_block_get(v, kMasterDirectoryBlock, 0, &buf); + if (status) + return status; + fsw_memcpy(v->vh, buf, sizeof(HFSPlusVolumeHeader)); + fsw_block_release(v, kMasterDirectoryBlock, buf); + + // check vol. header + if (fsw_u16_be_swap(v->vh->signature) != kHFSPlusSigWord) + return FSW_UNSUPPORTED; + + // use block size specified by vol. header + bs = fsw_u32_be_swap(v->vh->blockSize); + fsw_set_blocksize(v, bs, bs); + + // set up catalog B-Tree file: + status = fsw_hfsplus_btf_setup(v, kHFSCatalogFileID, &v->vh->catalogFile, + &v->catf); + if (status) + return status; + + // set up root folder: + status = fsw_dnode_create_root(v, kHFSRootFolderID, &v->g.root); + if (status) + return status; + + // Get volume label from kHFSRootFolderID thread record + // Not-readed volume label is not fatal error, + // we can proceed without it leaving label empty + status = fsw_alloc(v->catf->bt_ndsz, &btnode); + if (!status) { + status = fsw_hfsplus_dnid2thread(v, kHFSRootFolderID, btnode, &thread); + if (status) + goto label_read_fail; + + label.len = fsw_u16_be_swap(thread->nodeName.length); + label.size = sizeof(fsw_u16) * label.len; + label.data = thread->nodeName.unicode; + label.type = FSW_STRING_TYPE_UTF16_BE; + + status = fsw_strdup_coerce(&v->g.label, FSW_STRING_TYPE_UTF16, &label); + if (status) + goto label_read_fail; + + fsw_free(btnode); + } + + return FSW_SUCCESS; + +// If volume label reading failed we leave it empty and return SUCCESS status +label_read_fail: + fsw_free(btnode); + v->g.label.type = FSW_STRING_TYPE_EMPTY; + return FSW_SUCCESS; + +} + +static void +fsw_hfsplus_vol_free(struct fsw_hfsplus_volume *v) +{ + if (v->vh) + fsw_free(v->vh); + if (v->catf) + fsw_dnode_release((struct fsw_dnode *)v->catf); +} + +static fsw_status_t +fsw_hfsplus_vol_stat(struct fsw_hfsplus_volume *v, struct fsw_volume_stat *s) +{ + fsw_u32 bs = fsw_u32_be_swap(v->vh->blockSize); + + s->total_bytes = bs * fsw_u32_be_swap(v->vh->totalBlocks); + s->free_bytes = bs * fsw_u32_be_swap(v->vh->freeBlocks); + + return FSW_SUCCESS; +} + +static fsw_status_t +fsw_hfsplus_dno_fill(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d) +{ + fsw_status_t status; + struct fsw_hfsplus_dnode *parent; + HFSPlusCatalogKey sk, *tk; + HFSPlusCatalogRecord *rec; + fsw_u32 rec_num; + BTNodeDescriptor *btnode; + + // pre-allocate bt-node buffer for use by search function: + status = fsw_alloc(v->catf->bt_ndsz, &btnode); + if (status) { + return status; + } + + sk.parentID = d->parent_id; + status = fsw_hfsplus_fswstr2unistr(&(sk.nodeName), &(d->g.name)); + if (status) { + goto done; + } + + status = fsw_hfsplus_bt_search(v->catf, + (HFSPlusBTKey *)&sk, + fsw_hfsplus_cat_cmp, + btnode, &rec_num); + + tk = (HFSPlusCatalogKey *) fsw_hfsplus_btnode_get_rec(btnode, v->catf->bt_ndsz, rec_num); + rec = (HFSPlusCatalogRecord *) fsw_hfsplus_bt_rec_skip_key((HFSPlusBTKey *)tk); + + status = fsw_hfsplus_dnid2dnode(v, d->parent_id, &parent); + if (status) { + goto done; + } + + fsw_hfsplus_dnode_complete(rec, parent, d); + +done: + fsw_free(btnode); + + return status; +} + +static void +fsw_hfsplus_dno_free(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d) +{ + // NOTE: not applicable to HFS+ dnodes! + return; +} + +static fsw_u32 +fsw_hfsplus_posix_time(fsw_u32 t) +{ + // t is in seconds since midnight, January 1, 1904, GMT + return (t > 2082844800) ? t - 2082844800 : 0; +} + +static fsw_status_t +fsw_hfsplus_dno_stat(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_dnode_stat *s) +{ + s->used_bytes = d->g.size; + s->store_attr_posix(s, 0500); + s->store_time_posix(s, FSW_DNODE_STAT_CTIME, fsw_hfsplus_posix_time(d->ct)); + s->store_time_posix(s, FSW_DNODE_STAT_MTIME, fsw_hfsplus_posix_time(d->mt)); + s->store_time_posix(s, FSW_DNODE_STAT_ATIME, fsw_hfsplus_posix_time(d->at)); + + return FSW_SUCCESS; +} + +static HFSPlusBTKey * +fsw_hfsplus_btnode_get_rec(BTNodeDescriptor* btnode, fsw_u16 size, fsw_u32 rnum) +{ + fsw_u16 *off = (fsw_u16 *) ((fsw_u8 *) btnode + size) - 1 - rnum; + return (HFSPlusBTKey *)((fsw_u8 *)btnode + fsw_u16_be_swap(*off)); +} + +static void * +fsw_hfsplus_bt_rec_skip_key(HFSPlusBTKey *k) +{ + return (fsw_u8 *)k + sizeof(k->keyLength) + fsw_u16_be_swap(k->keyLength); +} + +static fsw_u32 +fsw_hfsplus_bt_idx_get_child(HFSPlusBTKey *k) +{ + fsw_u32 *child; + child = (fsw_u32 *)fsw_hfsplus_bt_rec_skip_key(k); + return fsw_u32_be_swap(*child); +} + +static fsw_status_t +fsw_hfsplus_bt_search(struct fsw_hfsplus_dnode *bt, + HFSPlusBTKey *sk, + k_cmp_t k_cmp, + BTNodeDescriptor *btnode, + fsw_u32 *rec_num) +{ + fsw_u32 node; + fsw_u16 rec, lo, hi; + HFSPlusBTKey *tk; // trial key + int cmp; + fsw_status_t status; + + // start searching from the B-Tree root node: + node = bt->bt_root; + + for (;;) { + // load data for current node into caller-provided buffer 'btnode' + status = fsw_hfsplus_read(bt, (fsw_u64)node * bt->bt_ndsz, + bt->bt_ndsz, btnode); + if (status) + return status; + + // sanity check: record 0 located immediately after node descriptor + if ((fsw_u8 *)btnode + sizeof(BTNodeDescriptor) != + (fsw_u8 *)fsw_hfsplus_btnode_get_rec(btnode, bt->bt_ndsz, 0)) + return FSW_VOLUME_CORRUPTED; + + // search records within current node + lo = 0; + hi = fsw_u16_be_swap(btnode->numRecords) - 1; + while (lo <= hi) { + // access record data, then compare to search key 'sk' + rec = (lo + hi) >> 1; + tk = fsw_hfsplus_btnode_get_rec(btnode, bt->bt_ndsz, rec); + cmp = k_cmp(tk, sk); + + if (cmp < 0) // (tk < sk) + lo = rec + 1; + else if (cmp > 0) // (tk > sk) + hi = rec - 1; + else { // (tk == sk) + if (btnode->kind != kBTLeafNode) { + hi = rec; + break; + } + + *rec_num = rec; + return FSW_SUCCESS; + } + } + + // NOTE: following the binary search, 'hi' now points at the + // record with the largest 'tk' for which (tk <= sk) + + if (btnode->kind != kBTIndexNode) + break; + + // on an index node, so descend to child + tk = fsw_hfsplus_btnode_get_rec(btnode, bt->bt_ndsz, hi); + node = fsw_hfsplus_bt_idx_get_child(tk); + } + + // search key 'sk' not found + return FSW_NOT_FOUND; +} + + +static int +fsw_hfsplus_int_cmp(fsw_u32 a, fsw_u32 b) +{ + return (a < b) ? -1 : (a > b) ? 1 : 0; +} + +static fsw_u16 +fsw_hfsplus_ucblatin_tolower(fsw_u16 c) +{ + if (c == 0) + return 0xFFFF; + if (c == 0x00C6 || c == 0x00D0 || c == 0x00D8 || c == 0x00DE || + (c >= 0x0041 && c <= 0x005A)) + return c + 0x0020; + return c; +} + +static int +fsw_hfsplus_thread_cmp(HFSPlusBTKey *tk, HFSPlusBTKey *sk) +{ + int ret; + // NOTE: all 'tk' fields are stored as big-endian values and must be + // converted to CPU endianness before any comparison to corresponding + // fields in 'sk'. + + // Here is enough to compare parent's IDs and + // check if the name is an empty string + ret = fsw_hfsplus_int_cmp(fsw_u32_be_swap(tk->catKey.parentID), sk->catKey.parentID); + if (ret) + return ret; + + // NOTE: + // When failing name length check there's no reason of continuing B-Tree traverse + // because we can have only one child of 'thread' type (with an empty name), and it + // should be the 1st record. But let the search traverse be stopped + // later on parents comparison for simplicity + return tk->catKey.nodeName.length > 0; +} + +static int +fsw_hfsplus_cat_cmp(HFSPlusBTKey *tk, HFSPlusBTKey *sk) +{ + fsw_u16 *t_str, *s_str; + fsw_u16 t_len, s_len; + fsw_u16 t_char, s_char; + int ret; + + // NOTE: all 'tk' fields are stored as big-endian values and must be + // converted to CPU endianness before any comparison to corresponding + // fields in 'sk'. + + // compare parent IDs: if unequal, we're done! + ret = fsw_hfsplus_int_cmp(fsw_u32_be_swap(tk->catKey.parentID), + sk->catKey.parentID); + if (ret) + return ret; + + // unicode string pointers and lengths: + t_len = fsw_u16_be_swap(tk->catKey.nodeName.length); + t_str = tk->catKey.nodeName.unicode; + s_len = sk->catKey.nodeName.length; + s_str = sk->catKey.nodeName.unicode; + + for (;;) { + // start by assuming strings are empty: + t_char = s_char = 0; + + // find next valid char from on-disk key string: + while (t_char == 0 && t_len > 0) { + t_char = fsw_hfsplus_ucblatin_tolower(fsw_u16_be_swap(*t_str)); + t_len--; + t_str++; + } + + // find next valid char from memory key string: + while (s_char == 0 && s_len > 0) { + s_char = fsw_hfsplus_ucblatin_tolower(*s_str); + s_len--; + s_str++; + } + + // stop if difference or both strings exhausted: + ret = fsw_hfsplus_int_cmp(t_char, s_char); + if (ret || s_char == 0) + break; + } + + return ret; +} + +static fsw_status_t +fsw_hfsplus_get_ext(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_extent *e) +{ + fsw_u32 off, bc; + HFSPlusExtentRecord *er; + int i; + + // set initial offset to provided starting logical block number: + off = e->log_start; + + // start with dnode's initial extent record: + er = &d->extents; + + // search extent record: + for (i = 0; i < kHFSPlusExtentDensity; i++) { + // get block count for current extent descriptor: + bc = fsw_u32_be_swap((*er)[i].blockCount); + + // have we exhausted all available extents? + if (bc == 0) + return FSW_NOT_FOUND; + + // offset is relative to current extent's physical startBlock: + if (off < bc) { + e->type = FSW_EXTENT_TYPE_PHYSBLOCK; + e->phys_start = fsw_u32_be_swap((*er)[i].startBlock) + off; + e->log_count = bc - off; + return FSW_SUCCESS; + } + + // update offset to NEXT extent descriptor: + off -= bc; + } + + // FIXME: more than 8 fragments not yet supported! + FSW_MSG_ASSERT((FSW_MSGSTR("FswHfsPlus: get_ext: got_here\n"))); + + return FSW_UNSUPPORTED; +} + +static fsw_status_t +fsw_hfsplus_fswstr2unistr(HFSUniStr255* t, struct fsw_string* src) +{ + fsw_status_t status; + struct fsw_string ws; + + status = fsw_strdup_coerce(&ws, FSW_STRING_TYPE_UTF16, src); + if (status == FSW_SUCCESS) { + t->length = (fsw_u16) fsw_strlen(&ws); + if (t->length > 0) { + fsw_memcpy(t->unicode, fsw_strdata(&ws), fsw_strsize(&ws)); + } + } + fsw_strfree (&ws); + return status; +} + +static fsw_status_t +fsw_hfsplus_btree_get_rec(struct fsw_hfsplus_dnode *bt, + fsw_u32 parent_id, + BTNodeDescriptor *btnode, + fsw_u32 *rec_num, + fsw_u64 *rec_skip) +{ + fsw_status_t status; + fsw_u32 btnode_next; + fsw_u32 num_records; + fsw_u32 counter; + HFSPlusBTKey *tk; + HFSPlusCatalogRecord *rec; + fsw_u32 i; + + counter = 0; + for (;;) { + status = FSW_NOT_FOUND; + num_records = fsw_u16_be_swap(btnode->numRecords); + + for (i = *rec_num; i < num_records; ++i) { + tk = fsw_hfsplus_btnode_get_rec(btnode, bt->bt_ndsz, i); + + if (fsw_u32_be_swap(((HFSPlusCatalogKey*)(tk))->parentID) != parent_id) { + return status; + } + + if (counter++ != *rec_skip) { + continue; + } + + rec = fsw_hfsplus_bt_rec_skip_key(tk); + ++(*rec_skip); + switch (fsw_u16_be_swap(rec->recordType)) { + case kHFSPlusFileRecord: + case kHFSPlusFolderRecord: + *rec_num = i; + return FSW_SUCCESS; + break; + + case kHFSPlusFolderThreadRecord: + case kHFSPlusFileThreadRecord: + default: + break; + } + } + + btnode_next = fsw_u32_be_swap(btnode->fLink); + if (btnode_next == 0) { + return status; + } + + status = fsw_hfsplus_read(bt, (fsw_u64) btnode_next * bt->bt_ndsz, + bt->bt_ndsz, btnode); + *rec_num = 0; + if (status) { + return status; + } + } + +} + +static fsw_status_t +fsw_hfsplus_dnode_create_minimal(struct fsw_hfsplus_volume *v, + fsw_u32 parent_id, + fsw_u32 dnode_id, + struct fsw_string *name, + struct fsw_hfsplus_dnode **d_out) +{ + fsw_status_t status; + + status = fsw_dnode_create(v, NULL, dnode_id, FSW_DNODE_TYPE_UNKNOWN, name, d_out); + if (!status) { + (*d_out)->parent_id = parent_id; + } + return status; +} + +static void +fsw_hfsplus_dnode_complete(HFSPlusCatalogRecord *rec, + struct fsw_hfsplus_dnode *parent, + struct fsw_hfsplus_dnode *d_out) +{ + + // figure out type of child dnode: + switch (fsw_u16_be_swap(rec->recordType)) { + case kHFSPlusFolderRecord: + d_out->g.type = FSW_DNODE_TYPE_DIR; + + d_out->ct = fsw_u32_be_swap(rec->folderRecord.createDate); + d_out->mt = fsw_u32_be_swap(rec->folderRecord.contentModDate); + d_out->at = fsw_u32_be_swap(rec->folderRecord.accessDate); + break; + + case kHFSPlusFileRecord: + d_out->g.type = FSW_DNODE_TYPE_FILE; + d_out->g.size = fsw_u64_be_swap(rec->fileRecord.dataFork.logicalSize); + + fsw_memcpy(d_out->extents, &rec->fileRecord.dataFork.extents, sizeof(HFSPlusExtentRecord)); + + d_out->ct = fsw_u32_be_swap(rec->fileRecord.createDate); + d_out->mt = fsw_u32_be_swap(rec->fileRecord.contentModDate); + d_out->at = fsw_u32_be_swap(rec->fileRecord.accessDate); + + d_out->fd_type = fsw_u32_be_swap(rec->fileRecord.userInfo.fdType); + d_out->fd_creator = fsw_u32_be_swap(rec->fileRecord.userInfo.fdCreator); + + if ((d_out->fd_creator == kHFSPlusSymlinkCreator && d_out->fd_type == kHFSPlusSymlinkType) || + (d_out->fd_creator == kHFSPlusHFSPlusCreator && d_out->fd_type == kHFSPlusHardlinkType)) { + + d_out->g.type = FSW_DNODE_TYPE_SYMLINK; + d_out->inode_num = fsw_u32_be_swap (rec->fileRecord.permissions.special.iNodeNum); + } + break; + + default: + d_out->g.type = FSW_DNODE_TYPE_UNKNOWN; + + d_out->ct = 0; + d_out->mt = 0; + d_out->at = 0; + break; + } + + d_out->g.parent = parent; + fsw_dnode_retain(&parent->g); + + // mark dnode as complete + fsw_dnode_mkcomplete(d_out); +} + +static fsw_status_t +fsw_hfsplus_dnode_create_full(HFSPlusCatalogRecord *rec, + struct fsw_hfsplus_dnode *parent, + struct fsw_string *name, + struct fsw_hfsplus_dnode **d_out) +{ + fsw_status_t status; + fsw_u32 dnid; + + switch (fsw_u16_be_swap(rec->recordType)) { + case kHFSPlusFolderRecord: + dnid = fsw_u32_be_swap(rec->folderRecord.folderID); + break; + case kHFSPlusFileRecord: + dnid = fsw_u32_be_swap(rec->fileRecord.fileID); + break; + default: + dnid = 0; + break; + } + + status = fsw_hfsplus_dnode_create_minimal(parent->g.vol, parent->g.dnode_id, dnid, name, d_out); + + if (!status) { + fsw_hfsplus_dnode_complete(rec, parent, *d_out); + } + return status; +} + +static fsw_status_t +fsw_hfsplus_dir_get(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_string *name, struct fsw_hfsplus_dnode **d_out) +{ + BTNodeDescriptor *btnode; + HFSPlusCatalogKey sk, *tk; + HFSPlusCatalogRecord *rec; + fsw_status_t status; + fsw_u32 rec_num; + fsw_u32 i; + + // search catalog file for child named by 'name': + sk.parentID = d->g.dnode_id; + status = fsw_hfsplus_fswstr2unistr(&(sk.nodeName), name); + + if (status) + return status; + + FSW_MSG_DEBUG((FSW_MSGSTR("FswHfsPlus: dir_get: parent=%d name: "), d->g.dnode_id)); + for (i = 0; i < sk.nodeName.length; i++) + FSW_MSG_DEBUG((FSW_MSGSTR("%c"), ((fsw_u16 *)sk.nodeName.unicode)[i])); + FSW_MSG_DEBUG((FSW_MSGSTR("\n"))); + + // pre-allocate bt-node buffer for use by search function: + status = fsw_alloc(v->catf->bt_ndsz, &btnode); + if (status) { + return status; + } + + status = fsw_hfsplus_bt_search(v->catf, + (HFSPlusBTKey *)&sk, + fsw_hfsplus_cat_cmp, + btnode, + &rec_num); + if (status) { + goto done; + } + + tk = (HFSPlusCatalogKey *)fsw_hfsplus_btnode_get_rec(btnode, v->catf->bt_ndsz, rec_num); + rec = fsw_hfsplus_bt_rec_skip_key((HFSPlusBTKey *)tk); + status = fsw_hfsplus_dnode_create_full(rec, d, name, d_out); + +done: + fsw_free(btnode); + return status; +} + +static fsw_status_t +fsw_hfsplus_dir_read(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_shandle *sh, struct fsw_hfsplus_dnode **d_out) +{ + BTNodeDescriptor *btnode; + HFSPlusCatalogKey sk, *tk; + HFSPlusCatalogRecord *rec; + struct fsw_string name; + fsw_u32 rec_num; + int i; + fsw_status_t status; + + FSW_MSG_DEBUG((FSW_MSGSTR("FswHfsPlus: dir_read.1: parent=%ld, sh->pos=%ld\n"), d->g.dnode_id, sh->pos)); + + // pre-allocate bt-node buffer for use by search function: + status = fsw_alloc(v->catf->bt_ndsz, &btnode); + if (status) { + return status; + } + + // search catalog file for first child of 'd' (a.k.a. "./"): + sk.parentID = d->g.dnode_id; + sk.nodeName.length = 0; + // NOTE: keyLength not used in search, setting only for completeness: + + sk.keyLength = sizeof(sk.parentID) + sizeof(sk.nodeName.length); + status = fsw_hfsplus_bt_search(v->catf, + (HFSPlusBTKey *)&sk, + fsw_hfsplus_cat_cmp, + btnode, &rec_num); + if (status) { + goto done; + } + + status = fsw_hfsplus_btree_get_rec(v->catf, + sk.parentID, + btnode, + &rec_num, + &sh->pos); + if (status) { + goto done; + } + + tk = (HFSPlusCatalogKey *)fsw_hfsplus_btnode_get_rec(btnode, v->catf->bt_ndsz, rec_num); + rec = fsw_hfsplus_bt_rec_skip_key((HFSPlusBTKey *)tk); + + // Fill child name + name.type = FSW_STRING_TYPE_UTF16; + name.len = fsw_u16_be_swap(tk->nodeName.length); + name.size = name.len * 2; + status = fsw_alloc(name.size, &name.data); + if (status) { + goto done; + } + + FSW_MSG_DEBUG((FSW_MSGSTR("FswHfsPlus: dir_read.2: child name: "))); + for (i = 0; i < name.len; i++) { + ((fsw_u16 *) name.data)[i] = fsw_u16_be_swap(tk->nodeName.unicode[i]); + FSW_MSG_DEBUG((FSW_MSGSTR("%c"), ((fsw_u16 *) name.data)[i])); + } + FSW_MSG_DEBUG((FSW_MSGSTR("\n"))); + + status = fsw_hfsplus_dnode_create_full(rec, d, &name, d_out); + fsw_strfree(&name); + +done: + fsw_free(btnode); + return status; +} + +static fsw_status_t +fsw_hfsplus_itoa(fsw_u32 num, char** dest, int* len) +{ + fsw_status_t status; + int i, j; + int num_copy; + char c; + + (*len) = 0; + + // Pre-calculating string length + num_copy = num; + do { + ++(*len); + } while ((num_copy /= 10) > 0); + + // Allocating memory for the string + status = fsw_alloc_zero(sizeof(char) * (*len), (void **) dest); + if (status) { + return status; + } + + // Converting + i = 0; + do { + (*dest)[i++] = num % 10 + '0'; + } while ((num /= 10) > 0); + + // Reversing the string + for (i = 0, j = *len - 1; i < j; i++, j--) { + c = (*dest)[i]; + (*dest)[i] = (*dest)[j]; + (*dest)[j] = c; + } + return status; +} + +static fsw_status_t +fsw_hfsplus_sconcat(const char *const s1, int len1, const char *const s2, int len2, char **dest) +{ + int i; + fsw_status_t status; + + status = fsw_alloc_zero(sizeof(char) * (len1 + len2), (void **) dest); + if (status) { + return status; + } + + for (i = 0; i < len1; ++i) { + (*dest)[i] = s1[i]; + } + + for (i = 0; i < len2; ++i) { + (*dest)[i + len1] = s2[i]; + } + return status; +} + +static fsw_status_t +fsw_hfsplus_readlink(struct fsw_hfsplus_volume *v, struct fsw_hfsplus_dnode *d, + struct fsw_string *lnk_tgt) +{ + // Note: + // We assume the size = length * sizeof(char) + // And NO strings are null-terminated + + static const char hlink_prefix[] = "/\0\0\0\0HFS+ Private Data/iNode"; + static const int hlink_prefix_len = 28; + + fsw_status_t status; + char *buf; + int buf_len; + + if(d->fd_creator == kHFSPlusHFSPlusCreator && d->fd_type == kHFSPlusHardlinkType) { + status = fsw_hfsplus_itoa(d->inode_num, &buf, &buf_len); + if (status) { + return status; + } + + status = fsw_hfsplus_sconcat(hlink_prefix, hlink_prefix_len, buf, buf_len, (char**) &lnk_tgt->data); + if (status) { + return status; + } + + lnk_tgt->type = FSW_STRING_TYPE_ISO88591; + lnk_tgt->size = (hlink_prefix_len + buf_len) * sizeof(char); + lnk_tgt->len = hlink_prefix_len + buf_len; + fsw_free(buf); + + FSW_MSG_DEBUG((FSW_MSGSTR("FswHfsPlus: readlink: inode no: %d\n"), d->g.dnode_id)); + return FSW_SUCCESS; + } else if (d->fd_creator == kHFSPlusSymlinkCreator && d->fd_type == kHFSPlusSymlinkType) { + return fsw_dnode_readlink_data(d, lnk_tgt); + } + return FSW_UNSUPPORTED; +} + +static fsw_status_t +fsw_hfsplus_dnid2thread(struct fsw_hfsplus_volume *v, fsw_u32 dnid, + BTNodeDescriptor *btnode, HFSPlusCatalogThread **thread_out) +{ + fsw_status_t status; + HFSPlusCatalogKey sk, *tk; + fsw_u32 rec_num; + + // Known parent id is enough to find a thread + sk.parentID = dnid; + status = fsw_hfsplus_bt_search(v->catf, + (HFSPlusBTKey *)&sk, + fsw_hfsplus_thread_cmp, + btnode, &rec_num); + if (status) { + return status; + } + + tk = (HFSPlusCatalogKey *) fsw_hfsplus_btnode_get_rec(btnode, v->catf->bt_ndsz, rec_num); + *thread_out = (HFSPlusCatalogThread *) fsw_hfsplus_bt_rec_skip_key((HFSPlusBTKey *)tk); + return FSW_SUCCESS; +} + +static fsw_status_t +fsw_hfsplus_thread2dnode(struct fsw_hfsplus_volume *v, HFSPlusCatalogThread *thread, + BTNodeDescriptor *btnode, struct fsw_hfsplus_dnode **d_out) +{ + fsw_status_t status; + struct fsw_string name; + fsw_u32 rec_num; + HFSPlusCatalogKey sk, *tk; + HFSPlusCatalogRecord *rec; + + // Prepare search key + sk.parentID = fsw_u32_be_swap(thread->parentID); + + name.len = fsw_u16_be_swap(thread->nodeName.length); + name.size = sizeof(fsw_u16) * name.len; + name.data = thread->nodeName.unicode; + name.type = FSW_STRING_TYPE_UTF16_BE; + + status = fsw_hfsplus_fswstr2unistr(&(sk.nodeName), &name); + if (status) { + return status; + } + + // Try to find btree node by its parent id and name + status = fsw_hfsplus_bt_search(v->catf, + (HFSPlusBTKey *)&sk, + fsw_hfsplus_cat_cmp, + btnode, &rec_num); + if (status) { + return status; + } + + tk = (HFSPlusCatalogKey *) fsw_hfsplus_btnode_get_rec(btnode, v->catf->bt_ndsz, rec_num); + rec = (HFSPlusCatalogRecord *) fsw_hfsplus_bt_rec_skip_key((HFSPlusBTKey *)tk); + + // Just a sanity check + switch (fsw_u16_be_swap(rec->recordType)) { + case kHFSPlusFolderRecord: + case kHFSPlusFileRecord: + break; + default: + return FSW_NOT_FOUND; + } + + // We have same dnode id field position + // for file (rec->fileRecord.fileID) + // and folder (rec->folderRecord.folderID), so use one of them + status = fsw_hfsplus_dnode_create_minimal(v, + fsw_u32_be_swap(thread->parentID), + fsw_u32_be_swap(rec->folderRecord.folderID), + &name, d_out); + if (status) { + return status; + } + + return status; +} + +static fsw_status_t +fsw_hfsplus_dnid2dnode(struct fsw_hfsplus_volume *v, fsw_u32 dnid, struct fsw_hfsplus_dnode **d_out) +{ + fsw_status_t status; + BTNodeDescriptor *btnode_thread, *btnode_dnode; + HFSPlusCatalogThread *thread; + + status = fsw_alloc(v->catf->bt_ndsz, &btnode_thread); + if (status) { + return status; + } + + status = fsw_alloc(v->catf->bt_ndsz, &btnode_dnode); + if (status) { + goto free_btnode_thread; + } + + status = fsw_hfsplus_dnid2thread(v, dnid, btnode_thread, &thread); + if (status) { + goto free_all; + } + + status = fsw_hfsplus_thread2dnode(v, thread, btnode_dnode, d_out); + +free_all: + fsw_free(btnode_dnode); + +free_btnode_thread: + fsw_free(btnode_thread); + + return status; +} + +static fsw_status_t +fsw_hfsplus_bless_type2dnid(struct fsw_hfsplus_volume *vol, + fsw_u32 bless_type, + fsw_u32 *dnid) +{ + HFSPlusVolumeFinderInfo *finderInfo; + + finderInfo = (HFSPlusVolumeFinderInfo*) &vol->vh->finderInfo; + switch (bless_type) { + case BLESSED_TYPE_SYSTEM_FILE: + *dnid = fsw_u32_be_swap(finderInfo->blessedSystemFileID); + break; + case BLESSED_TYPE_SYSTEM_FOLDER: + *dnid = fsw_u32_be_swap(finderInfo->blessedSystemFolderID); + break; + case BLESSED_TYPE_OSX_FOLDER: + *dnid = fsw_u32_be_swap(finderInfo->blessedOSXFolderID); + break; + default: + return FSW_NOT_FOUND; + break; + } + + return FSW_SUCCESS; +} + +static fsw_status_t +fsw_hfsplus_get_bless_info(struct fsw_hfsplus_volume *vol, + fsw_u32 bless_type, + struct fsw_hfsplus_dnode **dno_out) { + fsw_status_t status; + fsw_u32 dnid; + + status = fsw_hfsplus_bless_type2dnid(vol, bless_type, &dnid); + if (status) + return status; + + status = fsw_hfsplus_dnid2dnode(vol, dnid, dno_out); + return status; +} + diff --git a/Staging/FswHfsPlus/fsw_hfsplus.h b/Staging/FswHfsPlus/fsw_hfsplus.h new file mode 100644 index 00000000..bd498b6b --- /dev/null +++ b/Staging/FswHfsPlus/fsw_hfsplus.h @@ -0,0 +1,314 @@ +/** @file + HFS+ file system driver header. + + Copyright (c) 2020, Vladislav Yaroshchuk + Copyright (C) 2017, Gabriel L. Somlo + + This program and the accompanying materials are licensed and made + available under the terms and conditions of the BSD License which + accompanies this distribution. The full text of the license may + be found at http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" + BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER + EXPRESS OR IMPLIED. +**/ + +#ifndef _FSW_HFSPLUS_H_ +#define _FSW_HFSPLUS_H_ + +#define VOLSTRUCTNAME fsw_hfsplus_volume +#define DNODESTRUCTNAME fsw_hfsplus_dnode + +/* Let debug messages be printed */ +//#define FSW_DEBUG_LEVEL 2 + +#include "fsw_core.h" + +/*============= HFS+ constants and data types from Apple TN1150 =============*/ +#pragma pack(1) + +#define kHFSBlockSize 512 // Minimum block size to transfer Vol.Hdr. +#define kMasterDirectoryBlock 2 // Vol.Hdr. disk offset (x kHFSBlockSize) + +#define kHFSPlusSigWord 0x482B // HFS+ volume signature (ASCII for 'H+') + +#define kHFSPlusMaxFileNameChars 255 // Max. length of HFS+ folder or filename + +#define kHFSPlusExtentDensity 8 // Number of extent descriptors per record + +#define kHFSPlusDataFork 0x00 // data fork type +#define kHFSPlusResourceFork 0xFF // resource fork type + +#define kHFSRootParentID 1 // Parent ID of the root folder +#define kHFSRootFolderID 2 // ID of the root folder +#define kHFSExtentsFileID 3 // ID of the extent overflow file +#define kHFSCatalogFileID 4 // ID of the catalog file +#define kHFSBadBlockFileID 5 // ID of the bad block file +#define kHFSAllocationFileID 6 // ID of the allocation file +#define kHFSStartupFileID 7 // ID of the startup file +#define kHFSAttributesFileID 8 // ID of the attributes file +#define kHFSRepairCatalogFileID 14 // Used temporarily when rebuilding Catalog B-tree +#define kHFSBogusExtentFileID 15 // Used temporarily during ExchangeFiles operations +#define kHFSFirstUserCatalogNodeID 16 // First CNID available for use by user files and folders + +#define kHFSPlusFolderRecord 1 // catalog folder record type +#define kHFSPlusFileRecord 2 // catalog file record type +#define kHFSPlusFolderThreadRecord 3 // catalog folder thread record type +#define kHFSPlusFileThreadRecord 4 // catalog file thread record type + +#define kHFSPlusHFSPlusCreator 0x6866732B // 'hfs+' +#define kHFSPlusSymlinkCreator 0x72686170 //'rhap' + +#define kHFSPlusHardlinkType 0x686C6E6B // 'hlnk' +#define kHFSPlusSymlinkType 0x736C6E6B // 'slnk' + +#define kBTLeafNode -1 // B-Tree leaf node type +#define kBTIndexNode 0 // B-Tree index node type +#define kBTHeaderNode 1 // B-Tree header node type + +// file/folder name unicode string type +typedef struct { + fsw_u16 length; // character count + fsw_u16 unicode[kHFSPlusMaxFileNameChars]; // character string +} HFSUniStr255; + +// extent descriptor type +typedef struct { + fsw_u32 startBlock; // first allocation block in the extent + fsw_u32 blockCount; // extent length, in allocation blocks +} HFSPlusExtentDescriptor; + +// extent record type +typedef HFSPlusExtentDescriptor HFSPlusExtentRecord[kHFSPlusExtentDensity]; + +// fork-data information type +typedef struct { + fsw_u64 logicalSize; // size of valid fork data, in bytes + fsw_u32 clumpSize; // fork-specific clump size + fsw_u32 totalBlocks; // total blocks across all extents in fork + HFSPlusExtentRecord extents; // initial extents for the fork data +} HFSPlusForkData; + +// volume header type +typedef struct { + fsw_u16 signature; // should be kHFSPlusSigWord + fsw_u16 version; // should be kHFSPlusVersion + fsw_u32 attributes; // volume attributes + fsw_u32 lastMountedVersion; // unique ID of software that last wrote volume + fsw_u32 journalInfoBlock; // block no. of journal info + fsw_u32 createDate; // volume creation timestamp (date and time) + fsw_u32 modifyDate; // volume last modification timestamp + fsw_u32 backupDate; // timestamp of last backup + fsw_u32 checkedDate; // timestamp of last consistency check + fsw_u32 fileCount; // total number of files on volume + fsw_u32 folderCount; // total number of folders on volume + fsw_u32 blockSize; // allocation block size, in bytes + fsw_u32 totalBlocks; // total number of allocation blocks + fsw_u32 freeBlocks; // total number of unused allocation blocks + fsw_u32 nextAllocation; // block number to start next allocation search + fsw_u32 rsrcClumpSize; // dflt. resource fork clump size (in bytes) + fsw_u32 dataClumpSize; // dflt. data fork clump size (in bytes) + fsw_u32 nextCatalogID; // next unused catalog ID + fsw_u32 writeCount; // incr. each time volume is write-mounted + fsw_u64 encodingsBitmap; // keep track of text encodings used in names + fsw_u8 finderInfo[32]; // information used by the OS X Finder + HFSPlusForkData allocationFile; // info re. size & location of alloc. file + HFSPlusForkData extentsFile; // info re. size & location of extents file + HFSPlusForkData catalogFile; // info re. size & location of catalog file + HFSPlusForkData attributesFile; // info re. size & location of attr. file + HFSPlusForkData startupFile; // info re. size & location of startup file +} HFSPlusVolumeHeader; + +// file permissions type +typedef struct { + fsw_u32 ownerID; // owner UID (or hard-link previous-link) + fsw_u32 groupID; // owner GID (or hard-link next-link) + fsw_u8 adminFlags; // flags changeable by root only + fsw_u8 ownerFlags; // flags changeable by owner + fsw_u16 fileMode; // BSD file type and mode bits + union { + fsw_u32 iNodeNum; // link reference number, if hard-link + fsw_u32 linkCount; // ref-count of hard-links, if indirect node file + fsw_u32 rawDevice; // device number, if block/char special dev. file + } special; // reserved for directories and most files +} HFSPlusBSDInfo; + +// finder info types +typedef struct { + fsw_u32 fdType; // file type + fsw_u32 fdCreator; // file creator + fsw_u16 fdFlags; // Finder flags + struct { + fsw_u16 v; // file's location + fsw_u16 h; + } fdLocation; + fsw_u16 opaque; +} FndrFileInfo; + +typedef struct { + struct { // folder's window rectangle + fsw_u16 top; + fsw_u16 left; + fsw_u16 bottom; + fsw_u16 right; + } frRect; + unsigned short frFlags; // Finder flags + struct { + fsw_u16 v; // folder's location + fsw_u16 h; + } frLocation; + fsw_u16 opaque; +} FndrDirInfo; + +typedef struct { + fsw_u8 opaque[16]; +} FndrOpaqueInfo; + +// catalog folder record type +typedef struct { + fsw_s16 recordType; // should be kHFSPlusFolderRecord + fsw_u16 flags; // bit flags about the folder (reserved) + fsw_u32 valence; // items directly contained by this folder + fsw_u32 folderID; // CNID of this folder + fsw_u32 createDate; // folder creation timestamp (date and time) + fsw_u32 contentModDate; // folder content last modification timestamp + fsw_u32 attributeModDate; // ctime (last change to a catalog record field) + fsw_u32 accessDate; // atime (last access timestamp) + fsw_u32 backupDate; // timestamp of last backup + HFSPlusBSDInfo permissions; // folder permissions + FndrDirInfo userInfo; // information used by the OS X Finder + FndrOpaqueInfo finderInfo; // additional information for the OS X Finder + fsw_u32 textEncoding; // hint re. folder name text encoding + fsw_u32 reserved; // reserved field +} HFSPlusCatalogFolder; + +// catalog file record type +typedef struct { + fsw_s16 recordType; // should be kHFSPlusFileRecord + fsw_u16 flags; // bit flags about the file (reserved) + fsw_u32 reserved1; // reserved field + fsw_u32 fileID; // CNID of this file + fsw_u32 createDate; // file creation timestamp (date and time) + fsw_u32 contentModDate; // file content last modification timestamp + fsw_u32 attributeModDate; // ctime (last change to a catalog record field) + fsw_u32 accessDate; // atime (last access timestamp) + fsw_u32 backupDate; // timestamp of last backup + HFSPlusBSDInfo permissions; // file permissions + FndrFileInfo userInfo; // information used by the OS X Finder + FndrOpaqueInfo finderInfo; // additional information for the OS X Finder + fsw_u32 textEncoding; // hint re. file name text encoding + fsw_u32 reserved2; // reserved field + HFSPlusForkData dataFork; // info re. size & location of data fork + HFSPlusForkData resourceFork; // info re. size & location of resource fork +} HFSPlusCatalogFile; + +// catalog thread record type +typedef struct { + fsw_s16 recordType; // should be kHFSPlus[Folder|File]ThreadRecord + fsw_u16 reserved; // reserved field + fsw_u32 parentID; // CNID of this record's parent + HFSUniStr255 nodeName; // basename of file or folder +} HFSPlusCatalogThread; + +// generic (union) catalog record type +typedef union { + fsw_s16 recordType; // kHFSPlus[Folder|File][Thread]Record + HFSPlusCatalogFolder folderRecord; // catalog folder record fields + HFSPlusCatalogFile fileRecord; // catalog file record fields + HFSPlusCatalogThread threadRecord; // catalog thread record fields +} HFSPlusCatalogRecord; + +// B-Tree node descriptor found at the start of each node +typedef struct { + fsw_u32 fLink; // number of next node of this type (0 if we're last) + fsw_u32 bLink; // number of prev. node of this type (0 if we're first) + fsw_s8 kind; // node type (leaf, index, header, map) + fsw_u8 height; // node depth in B-Tree hierarchy (0 for header) + fsw_u16 numRecords; // number of records contained in this node + fsw_u16 reserved; // reserved field +} BTNodeDescriptor; + +// B-Tree header record type (first record of a B-Tree header node) +typedef struct { + fsw_u16 treeDepth; // current B-Tree depth (always == rootNode.height) + fsw_u32 rootNode; // node number of B-Tree root node + fsw_u32 leafRecords; // total number of records across all leaf nodes + fsw_u32 firstLeafNode; // node number of first leaf node + fsw_u32 lastLeafNode; // node number of last leaf node + fsw_u16 nodeSize; // node size (in bytes) + fsw_u16 maxKeyLength; // max. length of a key in index/leaf node + fsw_u32 totalNodes; // total number of nodes in the B-Tree + fsw_u32 freeNodes; // number of unused nodes in the B-Tree + fsw_u16 reserved1; // reserved field + fsw_u32 clumpSize; // reserved field (deprecated) + fsw_u8 btreeType; // reserved (0 for catalog, extents, attrib. file) + fsw_u8 keyCompareType; // case-sensitive string comparison (HFSX only) + fsw_u32 attributes; // B-Tree attributes + fsw_u32 reserved3[16]; // reserved field +} BTHeaderRec; + +// extent overflow file key type +typedef struct { + fsw_u16 keyLength; // key length (excluding this field) + fsw_u8 forkType; // data or resource fork + fsw_u8 pad; // ensure 32-bit alignment for subsequent fields + fsw_u32 fileID; // CNID of file to which this extent record applies + fsw_u32 startBlock; // start block of first extent described by this record +} HFSPlusExtentKey; + +// catalog file key type +typedef struct { + fsw_u16 keyLength; // key length (excluding this field) + fsw_u32 parentID; // ID of parent folder (or CNID if thread record) + HFSUniStr255 nodeName; // basename of file or folder +} HFSPlusCatalogKey; + +// generic (union) B-Tree record key type +typedef union { + fsw_u16 keyLength; // key length (excluding this field) + HFSPlusExtentKey extKey; // extent key fields + HFSPlusCatalogKey catKey; // catalog key fields +} HFSPlusBTKey; + +typedef struct { + fsw_u32 blessedSystemFolderID; // for OpenFirmware systems + fsw_u32 blessedSystemFileID; // for EFI systems + fsw_u32 openWindowFolderID; // deprecated, first link in linked list of folders to open at mount + fsw_u32 blessedAlternateOSID; // currently used for FV2 recovery, inaccessible from UEFI + fsw_u32 unused; // formerly PowerTalk Inbox + fsw_u32 blessedOSXFolderID; // currently used for normal recovery + fsw_u64 volumeID; +} HFSPlusVolumeFinderInfo; + +#pragma pack() +/*========= end HFS+ constants and data types from Apple TN1150 =============*/ + +/* FSW: key comparison procedure type */ +typedef int (*k_cmp_t)(HFSPlusBTKey*, HFSPlusBTKey*); + +// FSW: HFS+ specific dnode +struct fsw_hfsplus_dnode { + struct fsw_dnode g; // Generic (parent) dnode structure + fsw_u32 ct, mt, at; // HFS+ create/modify/access timestamps + HFSPlusExtentRecord extents; // HFS+ initial extent record + fsw_u32 bt_root; // root node index (if B-Tree file) + fsw_u16 bt_ndsz; // node size (if B-Tree file) + + // Links stuff + fsw_u32 fd_creator; + fsw_u32 fd_type; + fsw_u32 inode_num; + + fsw_u32 parent_id; // parent id used by dnode_fill() +}; + + +// FSW: HFS+ specific volume +struct fsw_hfsplus_volume { + struct fsw_volume g; // Generic (parent) volume structure + HFSPlusVolumeHeader *vh; // Raw HFS+ Volume Header + struct fsw_hfsplus_dnode *catf; // Catalog file dnode +}; + +#endif // _FSW_HFSPLUS_H_ diff --git a/Staging/FswHfsPlus/fsw_lib.c b/Staging/FswHfsPlus/fsw_lib.c new file mode 100644 index 00000000..57beff3d --- /dev/null +++ b/Staging/FswHfsPlus/fsw_lib.c @@ -0,0 +1,317 @@ +/** + * \file fsw_lib.c + * Core file system wrapper library functions. + */ + +/*- + * Copyright (c) 2020 Vladislav Yaroshchuk + * 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 == NULL || s->type == FSW_STRING_TYPE_EMPTY) + return 0; + return s->len; +} + +/** + * Get the size of a string in bytes. + */ +int fsw_strsize(struct fsw_string *s) +{ + if (s == NULL || s->type == FSW_STRING_TYPE_EMPTY) + return 0; + + return s->size; +} + +/** + * Get the data of a string. + */ +void *fsw_strdata(struct fsw_string *s) +{ + if (s == NULL || s->type == FSW_STRING_TYPE_EMPTY) + return NULL; + + return (void *) s->data; +} + +/** + * 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 occurence of the separator character. + * The buffer string is searched for the separator character. If it is found, the + * element string descriptor is filled to point at the part of the buffer string + * before the separator. The buffer string itself is adjusted to point at the + * remaining part of the string (without the separator). + * + * If the separator is not found in the buffer string, then element is changed to + * point at the whole buffer string, and the buffer string itself is changed into + * an empty string. + * + * This function only manipulates the pointers and lengths in the two string descriptors, + * it does not change the actual string. If the buffer string is dynamically allocated, + * you must make a copy of it so that you can release it later. + */ + +void fsw_strsplit(struct fsw_string *element, struct fsw_string *buffer, char separator) +{ + int i, maxlen; + + if (buffer->type == FSW_STRING_TYPE_EMPTY || buffer->len == 0) { + element->type = FSW_STRING_TYPE_EMPTY; + return; + } + + maxlen = buffer->len; + *element = *buffer; + + if (buffer->type == FSW_STRING_TYPE_ISO88591) { + fsw_u8 *p; + + p = (fsw_u8 *)element->data; + for (i = 0; i < maxlen; i++, p++) { + if (*p == separator) { + buffer->data = p + 1; + buffer->len -= i + 1; + break; + } + } + element->len = i; + if (i == maxlen) { + buffer->data = p; + buffer->len -= i; + } + + element->size = element->len; + buffer->size = buffer->len; + + } else if (buffer->type == FSW_STRING_TYPE_UTF16) { + fsw_u16 *p; + + p = (fsw_u16 *)element->data; + for (i = 0; i < maxlen; i++, p++) { + if (*p == separator) { + buffer->data = p + 1; + buffer->len -= i + 1; + break; + } + } + element->len = i; + if (i == maxlen) { + buffer->data = p; + buffer->len -= i; + } + + element->size = element->len * sizeof(fsw_u16); + buffer->size = buffer->len * sizeof(fsw_u16); + + } else { + // fallback + buffer->type = FSW_STRING_TYPE_EMPTY; + } + + // TODO: support UTF8 and UTF16_SWAPPED +} + +/** + * Frees the memory used by a string returned from fsw_strdup_coerce. + */ + +void fsw_strfree(struct fsw_string *s) +{ + if (s->type != FSW_STRING_TYPE_EMPTY && s->data) + fsw_free(s->data); + s->type = FSW_STRING_TYPE_EMPTY; +} + +// EOF diff --git a/Staging/FswHfsPlus/fsw_strfunc.h b/Staging/FswHfsPlus/fsw_strfunc.h new file mode 100644 index 00000000..f0399232 --- /dev/null +++ b/Staging/FswHfsPlus/fsw_strfunc.h @@ -0,0 +1,453 @@ +/* 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; +} + +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; +} + +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++ = 0xc0 | ((c >> 6) & 0x1f); + *dp++ = 0x80 | (c & 0x3f); + } else if (c < 0x010000) { + *dp++ = 0xe0 | ((c >> 12) & 0x0f); + *dp++ = 0x80 | ((c >> 6) & 0x3f); + *dp++ = 0x80 | (c & 0x3f); + } else { + *dp++ = 0xf0 | ((c >> 18) & 0x07); + *dp++ = 0x80 | ((c >> 12) & 0x3f); + *dp++ = 0x80 | ((c >> 6) & 0x3f); + *dp++ = 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++ = 0xc0 | ((c >> 6) & 0x1f); + *dp++ = 0x80 | (c & 0x3f); + } else if (c < 0x010000) { + *dp++ = 0xe0 | ((c >> 12) & 0x0f); + *dp++ = 0x80 | ((c >> 6) & 0x3f); + *dp++ = 0x80 | (c & 0x3f); + } else { + *dp++ = 0xf0 | ((c >> 18) & 0x07); + *dp++ = 0x80 | ((c >> 12) & 0x3f); + *dp++ = 0x80 | ((c >> 6) & 0x3f); + *dp++ = 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++ = 0xc0 | ((c >> 6) & 0x1f); + *dp++ = 0x80 | (c & 0x3f); + } else if (c < 0x010000) { + *dp++ = 0xe0 | ((c >> 12) & 0x0f); + *dp++ = 0x80 | ((c >> 6) & 0x3f); + *dp++ = 0x80 | (c & 0x3f); + } else { + *dp++ = 0xf0 | ((c >> 18) & 0x07); + *dp++ = 0x80 | ((c >> 12) & 0x3f); + *dp++ = 0x80 | ((c >> 6) & 0x3f); + *dp++ = 0x80 | (c & 0x3f); + } + } + return FSW_SUCCESS; +} diff --git a/build_oc.tool b/build_oc.tool index 01af174d..843b37ac 100755 --- a/build_oc.tool +++ b/build_oc.tool @@ -151,7 +151,7 @@ package() { "Ps2MouseDxe.efi" "Ps2KeyboardDxe.efi" "UsbMouseDxe.efi" - "VBoxHfs.efi" + "FswHfsPlus.efi" "XhciDxe.efi" ) for efiDriver in "${efiDrivers[@]}"; do