From: srs5694 Date: Sun, 20 May 2012 17:35:21 +0000 (-0400) Subject: Added filesystem drivers. X-Git-Url: https://code.delx.au/refind/commitdiff_plain/38c626aab2a451ca669576e4c57e5fbf5da987ad Added filesystem drivers. --- diff --git a/filesystems/AutoGen.c b/filesystems/AutoGen.c new file mode 100644 index 0000000..cc01832 --- /dev/null +++ b/filesystems/AutoGen.c @@ -0,0 +1,232 @@ +/** + DO NOT EDIT + FILE auto-generated + Module name: + AutoGen.c + Abstract: Auto-generated AutoGen.c for building module or library. +**/ +#include +#include +#include +#include +#include + +// Guids +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiFileInfoGuid = { 0x09576E92, 0x6D3F, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiFileSystemInfoGuid = { 0x09576E93, 0x6D3F, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiFileSystemVolumeLabelInfoIdGuid = { 0xDB47D7D3, 0xFE81, 0x11D3, { 0x9A, 0x35, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiMdePkgTokenSpaceGuid = { 0x914AEBE7, 0x4635, 0x459b, { 0xAA, 0x1C, 0x11, 0xE2, 0x19, 0xB0, 0x3A, 0x10 }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiEventReadyToBootGuid = { 0x7CE88FB3, 0x4BD7, 0x4679, { 0x87, 0xA8, 0xA8, 0xD8, 0xDE, 0xE5, 0x0D, 0x2B }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiEventLegacyBootGuid = { 0x2A571201, 0x4966, 0x47F6, { 0x8B, 0x86, 0xF3, 0x1E, 0x41, 0xF3, 0x2F, 0x10 }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiGlobalVariableGuid = { 0x8BE4DF61, 0x93CA, 0x11D2, { 0xAA, 0x0D, 0x00, 0xE0, 0x98, 0x03, 0x2B, 0x8C }}; + +// Protocols +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiDiskIoProtocolGuid = { 0xCE345171, 0xBA0B, 0x11D2, { 0x8E, 0x4F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiBlockIoProtocolGuid = { 0x964E5B21, 0x6459, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiSimpleFileSystemProtocolGuid = { 0x964E5B22, 0x6459, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiUnicodeCollationProtocolGuid = { 0x1D85CD7F, 0xF43D, 0x11D2, { 0x9A, 0x0C, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiUnicodeCollation2ProtocolGuid = {0xa4c751fc, 0x23ae, 0x4c3e, { 0x92, 0xe9, 0x49, 0x64, 0xcf, 0x63, 0xf3, 0x49 }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gMsgLogProtocolGuid = {0x511CE018, 0x0018, 0x4002, {0x20, 0x12, 0x17, 0x38, 0x05, 0x01, 0x02, 0x03 }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiDevicePathProtocolGuid = { 0x09576E91, 0x6D3F, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiDriverBindingProtocolGuid = { 0x18A031AB, 0xB443, 0x4D1A, { 0xA5, 0xC0, 0x0C, 0x09, 0x26, 0x1E, 0x9F, 0x71 }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiSimpleTextOutProtocolGuid = { 0x387477C2, 0x69C7, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiGraphicsOutputProtocolGuid = { 0x9042A9DE, 0x23DC, 0x4A38, { 0x96, 0xFB, 0x7A, 0xDE, 0xD0, 0x80, 0x51, 0x6A }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiHiiFontProtocolGuid = {0xe9ca4775, 0x8657, 0x47fc, {0x97, 0xe7, 0x7e, 0xd6, 0x5a, 0x08, 0x43, 0x24}}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiUgaDrawProtocolGuid = { 0x982C298B, 0xF4FA, 0x41CB, { 0xB8, 0x38, 0x77, 0xAA, 0x68, 0x8F, 0xB8, 0x39 }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiComponentNameProtocolGuid = { 0x107A772C, 0xD5E1, 0x11D4, { 0x9A, 0x46, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiComponentName2ProtocolGuid = { 0x6A7A5CFF, 0xE8D9, 0x4F70, { 0xBA, 0xDA, 0x75, 0xAB, 0x30, 0x25, 0xCE, 0x14 }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiDriverConfigurationProtocolGuid = { 0x107A772B, 0xD5E1, 0x11D4, { 0x9A, 0x46, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiDriverConfiguration2ProtocolGuid = { 0xBFD7DC1D, 0x24F1, 0x40D9, { 0x82, 0xE7, 0x2E, 0x09, 0xBB, 0x6B, 0x4E, 0xBE }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiDriverDiagnosticsProtocolGuid = { 0x0784924F, 0xE296, 0x11D4, { 0x9A, 0x49, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiDriverDiagnostics2ProtocolGuid = { 0x4D330321, 0x025F, 0x4AAC, { 0x90, 0xD8, 0x5E, 0xD9, 0x00, 0x17, 0x3B, 0x63 }}; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_GUID gEfiLoadedImageProtocolGuid = { 0x5B1B31A1, 0x9562, 0x11D2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }}; + +// Definition of PCDs used in this module +GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdUefiVariableDefaultLang[5] = {101, 110, 103, 0 }; +GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdUefiVariableDefaultPlatformLang[7] = {101, 110, 45, 85, 83, 0 }; + +// Definition of PCDs used in libraries + +#define _PCD_TOKEN_PcdDebugPrintErrorLevel 5U +#define _PCD_VALUE_PcdDebugPrintErrorLevel 0x80000000U +GLOBAL_REMOVE_IF_UNREFERENCED const UINT32 _gPcd_FixedAtBuild_PcdDebugPrintErrorLevel = _PCD_VALUE_PcdDebugPrintErrorLevel; +extern const UINT32 _gPcd_FixedAtBuild_PcdDebugPrintErrorLevel; +#define _PCD_GET_MODE_32_PcdDebugPrintErrorLevel _gPcd_FixedAtBuild_PcdDebugPrintErrorLevel +//#define _PCD_SET_MODE_32_PcdDebugPrintErrorLevel ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdDebugClearMemoryValue 10U +#define _PCD_VALUE_PcdDebugClearMemoryValue 0xAFU +GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdDebugClearMemoryValue = _PCD_VALUE_PcdDebugClearMemoryValue; +extern const UINT8 _gPcd_FixedAtBuild_PcdDebugClearMemoryValue; +#define _PCD_GET_MODE_8_PcdDebugClearMemoryValue _gPcd_FixedAtBuild_PcdDebugClearMemoryValue +//#define _PCD_SET_MODE_8_PcdDebugClearMemoryValue ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdDebugPropertyMask 11U +#define _PCD_VALUE_PcdDebugPropertyMask 0x0fU +GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gPcd_FixedAtBuild_PcdDebugPropertyMask = _PCD_VALUE_PcdDebugPropertyMask; +extern const UINT8 _gPcd_FixedAtBuild_PcdDebugPropertyMask; +#define _PCD_GET_MODE_8_PcdDebugPropertyMask _gPcd_FixedAtBuild_PcdDebugPropertyMask +//#define _PCD_SET_MODE_8_PcdDebugPropertyMask ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdMaximumLinkedListLength 6U +#define _PCD_VALUE_PcdMaximumLinkedListLength 1000000U +GLOBAL_REMOVE_IF_UNREFERENCED const UINT32 _gPcd_FixedAtBuild_PcdMaximumLinkedListLength = _PCD_VALUE_PcdMaximumLinkedListLength; +extern const UINT32 _gPcd_FixedAtBuild_PcdMaximumLinkedListLength; +#define _PCD_GET_MODE_32_PcdMaximumLinkedListLength _gPcd_FixedAtBuild_PcdMaximumLinkedListLength +//#define _PCD_SET_MODE_32_PcdMaximumLinkedListLength ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdMaximumAsciiStringLength 7U +#define _PCD_VALUE_PcdMaximumAsciiStringLength 1000000U +GLOBAL_REMOVE_IF_UNREFERENCED const UINT32 _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength = _PCD_VALUE_PcdMaximumAsciiStringLength; +extern const UINT32 _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength; +#define _PCD_GET_MODE_32_PcdMaximumAsciiStringLength _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength +//#define _PCD_SET_MODE_32_PcdMaximumAsciiStringLength ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdMaximumUnicodeStringLength 8U +#define _PCD_VALUE_PcdMaximumUnicodeStringLength 1000000U +GLOBAL_REMOVE_IF_UNREFERENCED const UINT32 _gPcd_FixedAtBuild_PcdMaximumUnicodeStringLength = _PCD_VALUE_PcdMaximumUnicodeStringLength; +extern const UINT32 _gPcd_FixedAtBuild_PcdMaximumUnicodeStringLength; +#define _PCD_GET_MODE_32_PcdMaximumUnicodeStringLength _gPcd_FixedAtBuild_PcdMaximumUnicodeStringLength +//#define _PCD_SET_MODE_32_PcdMaximumUnicodeStringLength ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdVerifyNodeInList 9U +#define _PCD_VALUE_PcdVerifyNodeInList ((BOOLEAN)0U) +GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdVerifyNodeInList = _PCD_VALUE_PcdVerifyNodeInList; +extern const BOOLEAN _gPcd_FixedAtBuild_PcdVerifyNodeInList; +#define _PCD_GET_MODE_BOOL_PcdVerifyNodeInList _gPcd_FixedAtBuild_PcdVerifyNodeInList +//#define _PCD_SET_MODE_BOOL_PcdVerifyNodeInList ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdDriverDiagnosticsDisable 12U +#define _PCD_VALUE_PcdDriverDiagnosticsDisable ((BOOLEAN)0U) +GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdDriverDiagnosticsDisable = _PCD_VALUE_PcdDriverDiagnosticsDisable; +extern const BOOLEAN _gPcd_FixedAtBuild_PcdDriverDiagnosticsDisable; +#define _PCD_GET_MODE_BOOL_PcdDriverDiagnosticsDisable _gPcd_FixedAtBuild_PcdDriverDiagnosticsDisable +//#define _PCD_SET_MODE_BOOL_PcdDriverDiagnosticsDisable ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdComponentNameDisable 13U +#define _PCD_VALUE_PcdComponentNameDisable ((BOOLEAN)0U) +GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdComponentNameDisable = _PCD_VALUE_PcdComponentNameDisable; +extern const BOOLEAN _gPcd_FixedAtBuild_PcdComponentNameDisable; +#define _PCD_GET_MODE_BOOL_PcdComponentNameDisable _gPcd_FixedAtBuild_PcdComponentNameDisable +//#define _PCD_SET_MODE_BOOL_PcdComponentNameDisable ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdDriverDiagnostics2Disable 14U +#define _PCD_VALUE_PcdDriverDiagnostics2Disable ((BOOLEAN)1U) +GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdDriverDiagnostics2Disable = _PCD_VALUE_PcdDriverDiagnostics2Disable; +extern const BOOLEAN _gPcd_FixedAtBuild_PcdDriverDiagnostics2Disable; +#define _PCD_GET_MODE_BOOL_PcdDriverDiagnostics2Disable _gPcd_FixedAtBuild_PcdDriverDiagnostics2Disable +//#define _PCD_SET_MODE_BOOL_PcdDriverDiagnostics2Disable ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdComponentName2Disable 15U +#define _PCD_VALUE_PcdComponentName2Disable ((BOOLEAN)1U) +GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdComponentName2Disable = _PCD_VALUE_PcdComponentName2Disable; +extern const BOOLEAN _gPcd_FixedAtBuild_PcdComponentName2Disable; +#define _PCD_GET_MODE_BOOL_PcdComponentName2Disable _gPcd_FixedAtBuild_PcdComponentName2Disable +//#define _PCD_SET_MODE_BOOL_PcdComponentName2Disable ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdUgaConsumeSupport 16U +#define _PCD_VALUE_PcdUgaConsumeSupport ((BOOLEAN)1U) +GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdUgaConsumeSupport = _PCD_VALUE_PcdUgaConsumeSupport; +extern const BOOLEAN _gPcd_FixedAtBuild_PcdUgaConsumeSupport; +#define _PCD_GET_MODE_BOOL_PcdUgaConsumeSupport _gPcd_FixedAtBuild_PcdUgaConsumeSupport +//#define _PCD_SET_MODE_BOOL_PcdUgaConsumeSupport ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdUefiLibMaxPrintBufferSize 17U +#define _PCD_VALUE_PcdUefiLibMaxPrintBufferSize 320U +GLOBAL_REMOVE_IF_UNREFERENCED const UINT32 _gPcd_FixedAtBuild_PcdUefiLibMaxPrintBufferSize = _PCD_VALUE_PcdUefiLibMaxPrintBufferSize; +extern const UINT32 _gPcd_FixedAtBuild_PcdUefiLibMaxPrintBufferSize; +#define _PCD_GET_MODE_32_PcdUefiLibMaxPrintBufferSize _gPcd_FixedAtBuild_PcdUefiLibMaxPrintBufferSize +//#define _PCD_SET_MODE_32_PcdUefiLibMaxPrintBufferSize ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + + +EFI_STATUS +EFIAPI +UefiBootServicesTableLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +EFI_STATUS +EFIAPI +UefiRuntimeServicesTableLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +EFI_STATUS +EFIAPI +UefiLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + + +VOID +EFIAPI +ProcessLibraryConstructorList ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = UefiBootServicesTableLibConstructor (ImageHandle, SystemTable); + ASSERT_EFI_ERROR (Status); + + Status = UefiRuntimeServicesTableLibConstructor (ImageHandle, SystemTable); + ASSERT_EFI_ERROR (Status); + + Status = UefiLibConstructor (ImageHandle, SystemTable); + ASSERT_EFI_ERROR (Status); + +} + + + +VOID +EFIAPI +ProcessLibraryDestructorList ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + +} + +const UINT32 _gUefiDriverRevision = 0x00020000U; +const UINT32 _gDxeRevision = 0x00000000U; + + +EFI_STATUS +EFIAPI +ProcessModuleEntryPointList ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) + +{ + return fsw_efi_main (ImageHandle, SystemTable); +} + +VOID +EFIAPI +ExitDriver ( + IN EFI_STATUS Status + ) +{ + if (EFI_ERROR (Status)) { + ProcessLibraryDestructorList (gImageHandle, gST); + } + gBS->Exit (gImageHandle, Status, 0, NULL); +} + +GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 _gDriverUnloadImageCount = 0U; + +EFI_STATUS +EFIAPI +ProcessModuleUnloadList ( + IN EFI_HANDLE ImageHandle + ) +{ + return EFI_SUCCESS; +} diff --git a/filesystems/AutoGen.h b/filesystems/AutoGen.h new file mode 100644 index 0000000..28080cf --- /dev/null +++ b/filesystems/AutoGen.h @@ -0,0 +1,54 @@ +/** + DO NOT EDIT + FILE auto-generated + Module name: + AutoGen.h + Abstract: Auto-generated AutoGen.h for building module or library. +**/ + +#ifndef _AUTOGENH_B34E5765_2E02_4DAF_867F_7F40BE6FC33D +#define _AUTOGENH_B34E5765_2E02_4DAF_867F_7F40BE6FC33D + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +// Definition of PCDs used in this module + +#define _PCD_TOKEN_PcdUefiVariableDefaultLang 18U +#define _PCD_PATCHABLE_PcdUefiVariableDefaultLang_SIZE 5 +#define _PCD_VALUE_PcdUefiVariableDefaultLang _gPcd_FixedAtBuild_PcdUefiVariableDefaultLang +extern const UINT8 _gPcd_FixedAtBuild_PcdUefiVariableDefaultLang[5]; +#define _PCD_GET_MODE_PTR_PcdUefiVariableDefaultLang _gPcd_FixedAtBuild_PcdUefiVariableDefaultLang +//#define _PCD_SET_MODE_PTR_PcdUefiVariableDefaultLang ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +#define _PCD_TOKEN_PcdUefiVariableDefaultPlatformLang 19U +#define _PCD_PATCHABLE_PcdUefiVariableDefaultPlatformLang_SIZE 7 +#define _PCD_VALUE_PcdUefiVariableDefaultPlatformLang _gPcd_FixedAtBuild_PcdUefiVariableDefaultPlatformLang +extern const UINT8 _gPcd_FixedAtBuild_PcdUefiVariableDefaultPlatformLang[7]; +#define _PCD_GET_MODE_PTR_PcdUefiVariableDefaultPlatformLang _gPcd_FixedAtBuild_PcdUefiVariableDefaultPlatformLang +//#define _PCD_SET_MODE_PTR_PcdUefiVariableDefaultPlatformLang ASSERT(FALSE) // It is not allowed to set value for a FIXED_AT_BUILD PCD + +// Definition of PCDs used in libraries is in AutoGen.c + + +EFI_STATUS +EFIAPI +fsw_efi_main ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + + + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/filesystems/Doxyfile b/filesystems/Doxyfile new file mode 100644 index 0000000..15f4a3f --- /dev/null +++ b/filesystems/Doxyfile @@ -0,0 +1,277 @@ +# Doxyfile 1.4.7 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = "File System Wrapper" +PROJECT_NUMBER = 0.1 +OUTPUT_DIRECTORY = doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +BUILTIN_STL_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +#INPUT = /Users/chrisp/efi_toolkit/refit/fsw +INPUT = . +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.PY +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = HOST_EFI HOST_POSIX +EXPAND_AS_DEFINED = VOLSTRUCTNAME \ + DNODESTRUCTNAME +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = /Applications/Graphviz.app/Contents/MacOS +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = YES +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/filesystems/LICENSE.txt b/filesystems/LICENSE.txt new file mode 100644 index 0000000..68b38fd --- /dev/null +++ b/filesystems/LICENSE.txt @@ -0,0 +1,26 @@ +The below was written by Christoph Phisterer with respect to his original +code. Since then, Oracle and the Clover team have modified the original +files, and added the HFS+ driver, which bears Oracle and Apple copyrights +and is released under terms of the GNU GPL. + + File System Wrapper License +============================= + +The various parts of the File System Wrapper source code come from +different sources and may carry different licenses. Here's a quick +account of the situation: + + * The core code was written from scratch and is covered by a + BSD-style license. + + * The EFI host driver was written from scratch, possibly using code + from the TianoCore project and Intel's EFI Application Toolkit. It + is covered by a BSD-style license. + + * The ext2 and reiserfs file system drivers use definitions from the + Linux kernel source. The actual code was written from scratch, + using multiple sources for reference. These drivers are covered by + the GNU GPL. + +For more details, see each file's boilerplate comment. The full text +of the GNU GPL is in the file LICENSE_GPL.txt. diff --git a/filesystems/LICENSE_GPL.txt b/filesystems/LICENSE_GPL.txt new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/filesystems/LICENSE_GPL.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/filesystems/Make.common b/filesystems/Make.common new file mode 100644 index 0000000..99b165a --- /dev/null +++ b/filesystems/Make.common @@ -0,0 +1,141 @@ +# +# filesystems/Make.common +# Build control file for rEFInd's EFI filesystem drivers +# + +SRCDIR = . + +VPATH = $(SRCDIR) + +HOSTARCH = $(shell uname -m | sed s,i[3456789]86,ia32,) +ARCH := $(HOSTARCH) + +ifeq ($(ARCH),ia32) + ARCHDIR = Ia32 + FILENAME_CODE = ia32 +endif + +ifeq ($(ARCH),x86_64) + ARCH_C_FLAGS = "-DEFIAPI=__attribute__((ms_abi))" -mcmodel=large + ARCHDIR = X64 + FILENAME_CODE = x64 +endif + +EDK2BASE = /usr/local/UDK2010/MyWorkSpace + +# Below file defines TARGET (RELEASE or DEBUG), TARGET_ARCH (X64 or IA32), and TOOL_CHAIN_TAG (GCC45, GCC46, or GCC47) +include $(EDK2BASE)/Conf/target.txt + +EFIINC = $(EDK2BASE)/MdePkg/Include/ +EFILIB = $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library +ALL_EFILIBS = $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib/OUTPUT/BaseDebugPrintErrorLevelLib.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/BasePrintLib/BasePrintLib/OUTPUT/BasePrintLib.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/BasePcdLibNull/BasePcdLibNull/OUTPUT/BasePcdLibNull.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut/OUTPUT/UefiDebugLibConOut.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/BaseLib/BaseLib/OUTPUT/BaseLib.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/BaseMemoryLib/BaseMemoryLib/OUTPUT/BaseMemoryLib.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib/OUTPUT/UefiBootServicesTableLib.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib/OUTPUT/UefiMemoryAllocationLib.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib/OUTPUT/UefiDevicePathLib.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib/OUTPUT/UefiRuntimeServicesTableLib.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/UefiLib/UefiLib/OUTPUT/UefiLib.lib \ + $(EDK2BASE)/Build/MdeModule/$(TARGET)_$(TOOL_CHAIN_TAG)/$(TARGET_ARCH)/MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint/OUTPUT/UefiDriverEntryPoint.lib + +OS = $(shell uname -s) +CPPFLAGS = -I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/Protocol -I$(EFIINC)/$(ARCHDIR) -DNO_BUILTIN_VA_FUNCS + +INCLUDE_DIRS = -I $(EDK2BASE)/MdePkg \ + -I $(EDK2BASE)/MdePkg/Include \ + -I $(EDK2BASE)/MdePkg/Include/$(ARCHDIR) \ + -I $(EDK2BASE)/IntelFrameworkModulePkg \ + -I $(EDK2BASE)/IntelFrameworkModulePkg/Include \ + +FSW_NAMES = fsw_efi fsw_core fsw_efi_lib fsw_lib AutoGen +OBJS = $(FSW_NAMES:=.o) +#DRIVERNAME = ext2 +BUILDME = $(DRIVERNAME)_$(FILENAME_CODE).efi + +OPTIMFLAGS = -fno-strict-aliasing -mno-red-zone -Wno-address -Os +#OPTIMFLAGS = -fno-strict-aliasing -mno-red-zone -Wno-address -mcmodel=large -Os +DEBUGFLAGS = -Wall -Werror -Wno-missing-braces -Wno-array-bounds -ffunction-sections -fdata-sections -c -include AutoGen.h +#CFLAGS = $(ARCH3264) $(OPTIMFLAGS) -fpic -fshort-wchar $(DEBUGFLAGS) +CFLAGS = $(ARCH3264) $(OPTIMFLAGS) -g -fshort-wchar -fno-stack-protector $(DEBUGFLAGS) +ASFLAGS = $(ARCH3264) +LDFLAGS = -nostdlib -znocombreloc -dp --entry=fsw_efi_main + +prefix = /usr/bin/ +CC = $(prefix)gcc +AS = $(prefix)as +LD = $(prefix)ld +AR = $(prefix)ar +RANLIB = $(prefix)ranlib +OBJCOPY = $(prefix)objcopy +GENFW = $(EDK2BASE)/BaseTools/BinWrappers/PosixLike/GenFw + +ifeq ($(ARCH),ia64) + # EFI specs allows only lower floating point partition to be used + CFLAGS += -frename-registers -mfixed-range=f32-f127 +endif + +ifeq ($(ARCH),x86_64) +# CFLAGS += -DEFI_FUNCTION_WRAPPER +# CPPFLAGS += -DEFIX64 + + ARCH3264 = -m64 +endif + +ifeq ($(ARCH),ia32) +# CPPFLAGS += -DEFI32 + + ARCH3264 = -m32 + +endif + + +LDSCRIPT = $(EDK2BASE)/BaseTools/Scripts/gcc4.4-ld-script + +LDFLAGS = -nostdlib -n -q --gc-sections --script=$(EDK2BASE)/BaseTools/Scripts/gcc4.4-ld-script \ + --entry _ModuleEntryPoint -u _ModuleEntryPoint +LIBS = $(shell $(CC) $(ARCH3264) -print-libgcc-file-name) + +%.o: %.c + $(CC) $(CPPFLAGS) $(ARCH_C_FLAGS) $(CFLAGS) $(INCLUDE_DIRS) -DFSTYPE=$(DRIVERNAME) -c $< -o $@ + +FORMAT = efi-bsdrv-$(ARCH) + + +ifneq (,$(filter %.efi,$(BUILDME))) + +SHLIB_TARGET = $(subst .efi,.lib,$(BUILDME)) +DLL_TARGET = $(subst .efi,.dll,$(BUILDME)) + +all: $(BUILDME) + +$(DLL_TARGET): $(OBJS) fsw_$(DRIVERNAME).o + $(LD) -o $(DRIVERNAME)_$(FILENAME_CODE).dll $(LDFLAGS) --start-group $(ALL_EFILIBS) $(OBJS) fsw_$(DRIVERNAME).o --end-group + +$(BUILDME): $(DLL_TARGET) + $(OBJCOPY) --strip-unneeded $(DLL_TARGET) + $(OBJCOPY) $(DLL_TARGET) + $(GENFW) -e UEFI_DRIVER -o $(BUILDME) $(DLL_TARGET) + mkdir -p ../fs-drivers + cp $(BUILDME) ../fs-drivers +# $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \ +# -j .rela -j .reloc --target=$(FORMAT) $< $@ + +endif + +# rules for libraries + +ifneq (,$(filter %.a,$(BUILDME))) + +endif + +# utility rules + +clean: + rm -f $(BUILDME) *~ *.so $(OBJS) *.efi *.lib *.dll + + + +# EOF diff --git a/filesystems/Makefile b/filesystems/Makefile new file mode 100644 index 0000000..d4f0b6c --- /dev/null +++ b/filesystems/Makefile @@ -0,0 +1,39 @@ +# meta-Makefile for rEFInd filesystem drivers +# +# Most of the functionality is in Make.common; this Makefile merely +# deletes critical temporary files and calls Make.common with the +# name of the driver to be built. This is done because of a dependency +# in the fsw_efi.c file on the filesystem type; this file must be +# recompiled for each new filesystem built. + +INSTALL_DIR = /boot/efi/EFI/refind/drivers + +all: ext2fs reiserfs iso9660 hfs + +ext2fs: + rm -f fsw_efi.o + make DRIVERNAME=ext2 -f Make.common + +reiserfs: + rm -f fsw_efi.o + make DRIVERNAME=reiserfs -f Make.common + +iso9660: + rm -f fsw_efi.o + make DRIVERNAME=iso9660 -f Make.common + +hfs: + rm -f fsw_efi.o + make DRIVERNAME=hfs -f Make.common + +# utility rules + +clean: + rm -f *~ *.so *.o *.efi *.dll err.txt ext2*.txt hfs*.txt iso9660*.txt reiserfs*.txt + + +install: + mkdir -p $(INSTALL_DIR) + cp *.efi $(INSTALL_DIR) + +# DO NOT DELETE diff --git a/filesystems/VBoxFswParam.h b/filesystems/VBoxFswParam.h new file mode 100644 index 0000000..0f20c9e --- /dev/null +++ b/filesystems/VBoxFswParam.h @@ -0,0 +1,69 @@ +/* $Id: VBoxFswParam.h 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * VBoxFswParam.h + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef VBOXFSPARAM_H +#define VBOXFSPARAM_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 PROTO_NAME(x) gEfi ## x ## Guid +# define GUID_NAME(x) gEfi ## x ## Guid + +# define EFI_FILE_HANDLE_REVISION EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION +# define SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL +# define EFI_FILE_SYSTEM_VOLUME_LABEL_INFO EFI_FILE_SYSTEM_VOLUME_LABEL +# define EFI_SIGNATURE_32(a, b, c, d) SIGNATURE_32(a, b, c, d) +# define DivU64x32(x,y,z) DivU64x32((x),(y)) + + +INTN CompareGuidEdk1( + IN EFI_GUID *Guid1, + IN EFI_GUID *Guid2 + ); + +//#define CompareGuid(x, y) CompareGuidEdk1((x),(y)) +# define HOST_EFI 1 +//# define FSW_DEBUG_LEVEL 3 + +int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len); +#endif diff --git a/filesystems/design.dox b/filesystems/design.dox new file mode 100644 index 0000000..df7baf1 --- /dev/null +++ b/filesystems/design.dox @@ -0,0 +1,106 @@ +/* + * \file design.dox + * Documentation title page with design information + */ + + +/** +\mainpage File System Wrapper Documentation + +Welcome to the documentation for FSW. This doxygen-generated documentation +is intended for developers that either want to integrate FSW into their +own project or want to write additional file system drivers. + +\section goals Design Goals + +File System Wrapper wants to provide reusable, read-only file system drivers +for a range of common file systems. Reusability is achieved through a common +abstraction layer (called the "core"), which serves as the API for the host +environment driver. Once the required glue code is written for a host +environment, that host has access to all file system types implemented by FSW, +with no code to be written per file system type. + +Why read-only? There are a range of reasons why FSW only provides read-only +access: + +- Read-only drivers are much easier to write than read-write drivers. +- Write access isn't easily abstracted in an OS-independent way because + of more delicate buffer I/O handling requirements and features like + journalling. +- There is no risk of destroying data on the disk. +- Having read access is much better than having no access at all. + (Read-only drivers for several file systems can be written in the time + it would take to develop a read-write driver for just one file system.) +- Boot loaders only need read access in most cases. + +\section model Object and Data Model + +\subsection main_objects Main Objects + +There are three main "objects" that FSW works with: volume, dnode, and shandle. + +The fsw_volume structure keeps the information that applies to a file +system volume as a whole. Most importantly, it keeps pointers to the host driver +and file system driver dispatch tables, which are used by the core to call +the appropriate functions. + +The fsw_dnode structure represents a "directory node", that is any file-like +object in the file system: regular files, directories, symbolic links as well +as special files like device nodes. When compared with Unix-style file systems, +a dnode is very similar to an inode, but it retains some essential information +from the directory: the canonical name and the parent directory. FSW requires that +a dnode is uniquely identified by an integer number (currently 32 bit in size). +Inode numbers can be used for this purpose, non-Unix file systems will have to +come up with a unique number in some way. + +The fsw_shandle structure is used to access file data ("storage handle"). +A dnode only represents the file as such and doesn't offer access to its data. +An shandle is linked to a certain dnode, but there may be several shandles per +dnode. The shandle stores a file position pointer (byte offset) that can be changed +at will. With the current design, an shandle is also used for directory iteration, +even if the file system stores directory information in a central tree structure. + +\subsection disk_access Disk Data Access + +Data on the disk is accessed in blocks, addressed by a sequential number starting +at zero. The block size to use can be set by the file system driver. FSW supports +two separate block sizes: the "physical block size" is used when accessing the disk, +the "logical block size" is used when accessing a file's data. For most file +systems, these two are identical, but there may be some where the file allocation +block size is larger than the sector or block size used to store metadata. (FAT +comes to mind as an example.) + +For accessing the actual file data, the file system driver doesn't handle the +disk I/O, but merely returns information about the mapping from file logical +blocks to disk physical blocks in the fsw_extent structure. This allows host OS +buffer mechanisms to be used for file data. In special cases, like tail-packing, +fragments or compressed file systems, the file system driver can return file data +in an allocated buffer. + +\subsection data_hooks Data Extension Hooks + +File system specific data can be stored by extending the fsw_volume and fsw_dnode +structures. The core code uses the structure sizes stored in the fsw_fstype_table +to allocate memory for these structures. The fsw_shandle and fsw_extent structures +are not designed to be extended. + +Host specific data must be stored in separate structures private to the host +environment driver. The fsw_volume structure provides a host_data variable to +store a pointer. The fsw_dnode structure has no such field, because it is assumed +that all actions regarding dnodes are initiated on the host side and so the +host-specific private structure is known. + +\section callconv Calling Conventions + +All functions that can fail return a status code in a fsw_status_t. This type is an +integer. A boolean test yields true if there was an error and false if the function +executed successfully, i.e. success is signalled by a 0 return value. + +Functions that return data do so either by filling a structure passed in by the caller, +or by allocating a structure on the heap and returning a pointer through a +double-indirect parameter. A returned object pointer is the last parameter in the +parameter list. + +(More to be written about specific conventions for dnodes, shandles, strings.) + +*/ diff --git a/filesystems/fsw_base.h b/filesystems/fsw_base.h new file mode 100644 index 0000000..0d5e7ba --- /dev/null +++ b/filesystems/fsw_base.h @@ -0,0 +1,179 @@ +/* $Id: fsw_base.h 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_base.h - Base definitions switch. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_BASE_H_ +#define _FSW_BASE_H_ +//#define HOST_EFI 1 +#define VBOX + +#ifdef VBOX +#include "VBoxFswParam.h" +#endif + +//#include + +#ifndef FSW_DEBUG_LEVEL +/** + * Global debugging level. Can be set locally for the scope of a single + * file by defining the macro before fsw_base.h is included. + */ +#define FSW_DEBUG_LEVEL 0 +#endif + + +#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/filesystems/fsw_core.c b/filesystems/fsw_core.c new file mode 100644 index 0000000..64d70d7 --- /dev/null +++ b/filesystems/fsw_core.c @@ -0,0 +1,932 @@ +/* $Id: fsw_core.c 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_core.c - Core file system wrapper abstraction layer code. + */ + +/*- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_core.h" + + +// functions + +static void fsw_blockcache_free(struct fsw_volume *vol); + +#define MAX_CACHE_LEVEL (5) + + +/** + * Mount a volume with a given file system driver. This function is called by the + * host driver to make a volume accessible. The file system driver to use is specified + * by a pointer to its dispatch table. The file system driver will look at the + * data on the volume to determine if it can read the format. If the volume is found + * unsuitable, FSW_UNSUPPORTED is returned. + * + * If this function returns FSW_SUCCESS, *vol_out points at a valid volume data + * structure. The caller must release it later by calling fsw_unmount. + * + * If this function returns an error status, the caller only needs to clean up its + * own buffers that may have been allocated through the read_block interface. + */ + +fsw_status_t fsw_mount(void *host_data, + struct fsw_host_table *host_table, + struct fsw_fstype_table *fstype_table, + struct fsw_volume **vol_out) +{ + fsw_status_t status; + struct fsw_volume *vol; + + // allocate memory for the structure + status = fsw_alloc_zero(fstype_table->volume_struct_size, (void **)&vol); + if (status) + return status; + + // initialize fields + vol->phys_blocksize = 512; + vol->log_blocksize = 512; + vol->label.type = FSW_STRING_TYPE_EMPTY; + vol->host_data = host_data; + vol->host_table = host_table; + vol->fstype_table = fstype_table; + vol->host_string_type = host_table->native_string_type; + + // let the fs driver mount the file system + status = vol->fstype_table->volume_mount(vol); + if (status) + goto errorexit; + + // TODO: anything else? + + *vol_out = vol; + return FSW_SUCCESS; + +errorexit: + fsw_unmount(vol); + return status; +} + +/** + * Unmount a volume by releasing all memory associated with it. This function is + * called by the host driver when a volume is no longer needed. It is also called + * by the core after a failed mount to clean up any allocated memory. + * + * Note that all dnodes must have been released before calling this function. + */ + +void fsw_unmount(struct fsw_volume *vol) +{ + if (vol->root) + fsw_dnode_release(vol->root); + // TODO: check that no other dnodes are still around + + vol->fstype_table->volume_free(vol); + + fsw_blockcache_free(vol); + fsw_strfree(&vol->label); + fsw_free(vol); +} + +/** + * Get in-depth information on the volume. This function can be called by the host + * driver to get additional information on the volume. + */ + +fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb) +{ + return vol->fstype_table->volume_stat(vol, sb); +} + +/** + * Set the physical and logical block sizes of the volume. This functions is called by + * the file system driver to announce the block sizes it wants to use for accessing + * the disk (physical) and for addressing file contents (logical). + * Usually both sizes will be the same but there may be file systems that need to access + * metadata at a smaller block size than the allocation unit for files. + * + * Calling this function causes the block cache to be dropped. All pointers returned + * from fsw_block_get become invalid. This function should only be called while + * mounting the file system, not as a part of file access operations. + * + * Both sizes are measured in bytes, must be powers of 2, and must not be smaller + * than 512 bytes. The logical block size cannot be smaller than the physical block size. + */ + +void fsw_set_blocksize(struct fsw_volume *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize) +{ + // TODO: Check the sizes. Both must be powers of 2. log_blocksize must not be smaller than + // phys_blocksize. + + // drop core block cache if present + fsw_blockcache_free(vol); + + // signal host driver to drop caches etc. + vol->host_table->change_blocksize(vol, + vol->phys_blocksize, vol->log_blocksize, + phys_blocksize, log_blocksize); + + vol->phys_blocksize = phys_blocksize; + vol->log_blocksize = log_blocksize; +} + +/** + * Get a block of data from the disk. This function is called by the file system driver + * or by core functions. It calls through to the host driver's device access routine. + * Given a physical block number, it reads the block into memory (or fetches it from the + * block cache) and returns the address of the memory buffer. The caller should provide + * an indication of how important the block is in the cache_level parameter. Blocks with + * a low level are purged first. Some suggestions for cache levels: + * + * - 0: File data + * - 1: Directory data, symlink data + * - 2: File system metadata + * - 3..5: File system metadata with a high rate of access + * + * If this function returns successfully, the returned data pointer is valid until the + * caller calls fsw_block_release. + */ + +fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out) +{ + fsw_status_t status; + fsw_u32 i, discard_level, new_bcache_size; + struct fsw_blockcache *new_bcache; + + // TODO: allow the host driver to do its own caching; just call through if + // the appropriate function pointers are set + + if (cache_level > MAX_CACHE_LEVEL) + cache_level = MAX_CACHE_LEVEL; + + // check block cache + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].phys_bno == phys_bno) { + // cache hit! + if (vol->bcache[i].cache_level < cache_level) + vol->bcache[i].cache_level = cache_level; // promote the entry + vol->bcache[i].refcount++; + *buffer_out = vol->bcache[i].data; + return FSW_SUCCESS; + } + } + + // find a free entry in the cache table + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].phys_bno == (fsw_u32)FSW_INVALID_BNO) + break; + } + if (i >= vol->bcache_size) { + for (discard_level = 0; discard_level <= MAX_CACHE_LEVEL; discard_level++) { + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].refcount == 0 && vol->bcache[i].cache_level <= discard_level) + break; + } + if (i < vol->bcache_size) + break; + } + } + if (i >= vol->bcache_size) { + // enlarge / create the cache + if (vol->bcache_size < 16) + new_bcache_size = 16; + else + new_bcache_size = vol->bcache_size << 1; + status = fsw_alloc(new_bcache_size * sizeof(struct fsw_blockcache), &new_bcache); + if (status) + return status; + if (vol->bcache_size > 0) + fsw_memcpy(new_bcache, vol->bcache, vol->bcache_size * sizeof(struct fsw_blockcache)); + for (i = vol->bcache_size; i < new_bcache_size; i++) { + new_bcache[i].refcount = 0; + new_bcache[i].cache_level = 0; + new_bcache[i].phys_bno = (fsw_u32)FSW_INVALID_BNO; + new_bcache[i].data = NULL; + } + i = vol->bcache_size; + + // switch caches + if (vol->bcache != NULL) + fsw_free(vol->bcache); + vol->bcache = new_bcache; + vol->bcache_size = new_bcache_size; + } + vol->bcache[i].phys_bno = (fsw_u32)FSW_INVALID_BNO; + + // read the data + if (vol->bcache[i].data == NULL) { + status = fsw_alloc(vol->phys_blocksize, &vol->bcache[i].data); + if (status) + return status; + } + status = vol->host_table->read_block(vol, phys_bno, vol->bcache[i].data); + if (status) + return status; + + vol->bcache[i].phys_bno = phys_bno; + vol->bcache[i].cache_level = cache_level; + vol->bcache[i].refcount = 1; + *buffer_out = vol->bcache[i].data; + return FSW_SUCCESS; +} + +/** + * Releases a disk block. This function must be called to release disk blocks returned + * from fsw_block_get. + */ + +void fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer) +{ + fsw_u32 i; + + // TODO: allow the host driver to do its own caching; just call through if + // the appropriate function pointers are set + + // update block cache + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].phys_bno == phys_bno && vol->bcache[i].refcount > 0) + vol->bcache[i].refcount--; + } +} + +/** + * Release the block cache. Called internally when changing block sizes and when + * unmounting the volume. It frees all data occupied by the generic block cache. + */ + +static void fsw_blockcache_free(struct fsw_volume *vol) +{ + fsw_u32 i; + + for (i = 0; i < vol->bcache_size; i++) { + if (vol->bcache[i].data != NULL) + fsw_free(vol->bcache[i].data); + } + if (vol->bcache != NULL) { + fsw_free(vol->bcache); + vol->bcache = NULL; + } + vol->bcache_size = 0; +} + +/** + * Add a new dnode to the list of known dnodes. This internal function is used when a + * dnode is created to add it to the dnode list that is used to search for existing + * dnodes by id. + */ + +static void fsw_dnode_register(struct fsw_volume *vol, struct fsw_dnode *dno) +{ + dno->next = vol->dnode_head; + if (vol->dnode_head != NULL) + vol->dnode_head->prev = dno; + dno->prev = NULL; + vol->dnode_head = dno; +} + +/** + * Create a dnode representing the root directory. This function is called by the file system + * driver while mounting the file system. The root directory is special because it has no parent + * dnode, its name is defined to be empty, and its type is also fixed. Otherwise, this functions + * behaves in the same way as fsw_dnode_create. + */ + +fsw_status_t fsw_dnode_create_root(struct fsw_volume *vol, fsw_u32 dnode_id, struct fsw_dnode **dno_out) +{ + fsw_status_t status; + struct fsw_dnode *dno; + + // allocate memory for the structure + status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno); + if (status) + return status; + + // fill the structure + dno->vol = vol; + dno->parent = NULL; + dno->dnode_id = dnode_id; + dno->type = FSW_DNODE_TYPE_DIR; + dno->refcount = 1; + dno->name.type = FSW_STRING_TYPE_EMPTY; + // TODO: instead, call a function to create an empty string in the native string type + + fsw_dnode_register(vol, dno); + + *dno_out = dno; + return FSW_SUCCESS; +} + +/** + * Create a new dnode representing a file system object. This function is called by + * the file system driver in response to directory lookup or read requests. Note that + * if there already is a dnode with the given dnode_id on record, then no new object + * is created. Instead, the existing dnode is returned and its reference count + * increased. All other parameters are ignored in this case. + * + * The type passed into this function may be FSW_DNODE_TYPE_UNKNOWN. It is sufficient + * to fill the type field during the dnode_fill call. + * + * The name parameter must describe a string with the object's name. A copy will be + * stored in the dnode structure for future reference. The name will not be used to + * shortcut directory lookups, but may be used to reconstruct paths. + * + * If the function returns successfully, *dno_out contains a pointer to the dnode + * that must be released by the caller with fsw_dnode_release. + */ + +fsw_status_t fsw_dnode_create(struct fsw_dnode *parent_dno, fsw_u32 dnode_id, int type, + struct fsw_string *name, struct fsw_dnode **dno_out) +{ + fsw_status_t status; + struct fsw_volume *vol = parent_dno->vol; + struct fsw_dnode *dno; + + // check if we already have a dnode with the same id + for (dno = vol->dnode_head; dno; dno = dno->next) { + if (dno->dnode_id == dnode_id) { + fsw_dnode_retain(dno); + *dno_out = dno; + return FSW_SUCCESS; + } + } + + // allocate memory for the structure + status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno); + if (status) + return status; + + // fill the structure + dno->vol = vol; + dno->parent = parent_dno; + fsw_dnode_retain(dno->parent); + dno->dnode_id = dnode_id; + dno->type = type; + dno->refcount = 1; + status = fsw_strdup_coerce(&dno->name, vol->host_table->native_string_type, name); + if (status) { + fsw_free(dno); + return status; + } + + fsw_dnode_register(vol, dno); + + *dno_out = dno; + return FSW_SUCCESS; +} + +/** + * Increases the reference count of a dnode. This must be balanced with + * fsw_dnode_release calls. Note that some dnode functions return a retained + * dnode pointer to their caller. + */ + +void fsw_dnode_retain(struct fsw_dnode *dno) +{ + dno->refcount++; +} + +/** + * Release a dnode pointer, deallocating it if this was the last reference. + * This function decrements the reference counter of the dnode. If the counter + * reaches zero, the dnode is freed. Since the parent dnode is released + * during that process, this function may cause it to be freed, too. + */ + +void fsw_dnode_release(struct fsw_dnode *dno) +{ + struct fsw_volume *vol = dno->vol; + struct fsw_dnode *parent_dno; + + dno->refcount--; + + if (dno->refcount == 0) { + parent_dno = dno->parent; + + // de-register from volume's list + if (dno->next) + dno->next->prev = dno->prev; + if (dno->prev) + dno->prev->next = dno->next; + if (vol->dnode_head == dno) + vol->dnode_head = dno->next; + + // run fstype-specific cleanup + vol->fstype_table->dnode_free(vol, dno); + + fsw_strfree(&dno->name); + fsw_free(dno); + + // release our pointer to the parent, possibly deallocating it, too + if (parent_dno) + fsw_dnode_release(parent_dno); + } +} + +/** + * Get full information about a dnode from disk. This function is called by the host + * driver as well as by the core functions. Some file systems defer reading full + * information on a dnode until it is actually needed (i.e. separation between + * directory and inode information). This function makes sure that all information + * is available in the dnode structure. The following fields may not have a correct + * value until fsw_dnode_fill has been called: + * + * type, size + */ + +fsw_status_t fsw_dnode_fill(struct fsw_dnode *dno) +{ + // TODO: check a flag right here, call fstype's dnode_fill only once per dnode + + return dno->vol->fstype_table->dnode_fill(dno->vol, dno); +} + +/** + * Get extended information about a dnode. This function can be called by the host + * driver to get a full compliment of information about a dnode in addition to the + * fields of the fsw_dnode structure itself. + * + * Some data requires host-specific conversion to be useful (i.e. timestamps) and + * will be passed to callback functions instead of being written into the structure. + * These callbacks must be filled in by the caller. + */ + +fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb) +{ + fsw_status_t status; + + status = fsw_dnode_fill(dno); + if (status) + return status; + + sb->used_bytes = 0; + status = dno->vol->fstype_table->dnode_stat(dno->vol, dno, sb); + if (!status && !sb->used_bytes) + sb->used_bytes = FSW_U64_DIV(dno->size + dno->vol->log_blocksize - 1, dno->vol->log_blocksize); + return status; +} + +/** + * Lookup a directory entry by name. This function is called by the host driver. + * Given a directory dnode and a file name, it looks up the named entry in the + * directory. + * + * If the dnode is not a directory, the call will fail. The caller is responsible for + * resolving symbolic links before calling this function. + * + * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory + * entry. The caller must call fsw_dnode_release on it. + */ + +fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno, + struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out) +{ + fsw_status_t status; + + status = fsw_dnode_fill(dno); + if (status) + return status; + if (dno->type != FSW_DNODE_TYPE_DIR) + return FSW_UNSUPPORTED; + + return dno->vol->fstype_table->dir_lookup(dno->vol, dno, lookup_name, child_dno_out); +} + +/** + * Find a file system object by path. This function is called by the host driver. + * Given a directory dnode and a relative or absolute path, it walks the directory + * tree until it finds the target dnode. If an intermediate node turns out to be + * a symlink, it is resolved automatically. If the target node is a symlink, it + * is not resolved. + * + * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory + * entry. The caller must call fsw_dnode_release on it. + */ + +fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno, + struct fsw_string *lookup_path, char separator, + struct fsw_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_volume *vol = dno->vol; + struct fsw_dnode *child_dno = NULL; + struct fsw_string lookup_name; + struct fsw_string remaining_path; + int root_if_empty; + + remaining_path = *lookup_path; + fsw_dnode_retain(dno); + + // loop over the path + for (root_if_empty = 1; fsw_strlen(&remaining_path) > 0; root_if_empty = 0) { + // parse next path component + fsw_strsplit(&lookup_name, &remaining_path, separator); + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: split into %d '%s' and %d '%s'\n"), + lookup_name.len, lookup_name.data, + remaining_path.len, remaining_path.data)); + + if (fsw_strlen(&lookup_name) == 0) { // empty path component + if (root_if_empty) + child_dno = vol->root; + else + child_dno = dno; + fsw_dnode_retain(child_dno); + + } else { + // do an actual directory lookup + + // ensure we have full information + status = fsw_dnode_fill(dno); + if (status) + goto errorexit; + + // resolve symlink if necessary + if (dno->type == FSW_DNODE_TYPE_SYMLINK) { + status = fsw_dnode_resolve(dno, &child_dno); + if (status) + goto errorexit; + + // symlink target becomes the new dno + fsw_dnode_release(dno); + dno = child_dno; // is already retained + child_dno = NULL; + + // ensure we have full information + status = fsw_dnode_fill(dno); + if (status) + goto errorexit; + } + + // make sure we operate on a directory + if (dno->type != FSW_DNODE_TYPE_DIR) { + return FSW_UNSUPPORTED; + goto errorexit; + } + + // check special paths + if (fsw_streq_cstr(&lookup_name, ".")) { // self directory + child_dno = dno; + fsw_dnode_retain(child_dno); + + } else if (fsw_streq_cstr(&lookup_name, "..")) { // parent directory + if (dno->parent == NULL) { + // We cannot go up from the root directory. Caution: Certain apps like the EFI shell + // rely on this behaviour! + status = FSW_NOT_FOUND; + goto errorexit; + } + child_dno = dno->parent; + fsw_dnode_retain(child_dno); + + } else { + // do an actual lookup + status = vol->fstype_table->dir_lookup(vol, dno, &lookup_name, &child_dno); + if (status) + goto errorexit; + } + } + + // child_dno becomes the new dno + fsw_dnode_release(dno); + dno = child_dno; // is already retained + child_dno = NULL; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: now at inode %d\n"), dno->dnode_id)); + } + + *child_dno_out = dno; + return FSW_SUCCESS; + +errorexit: + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: leaving with error %d\n"), status)); + fsw_dnode_release(dno); + if (child_dno != NULL) + fsw_dnode_release(child_dno); + return status; +} + +/** + * Get the next directory item in sequential order. This function is called by the + * host driver to read the complete contents of a directory in sequential (file system + * defined) order. Calling this function returns the next entry. Iteration state is + * kept by a shandle on the directory's dnode. The caller must set up the shandle + * when starting the iteration. + * + * When the end of the directory is reached, this function returns FSW_NOT_FOUND. + * If the function returns FSW_SUCCESS, *child_dno_out points to the next directory + * entry. The caller must call fsw_dnode_release on it. + */ + +fsw_status_t fsw_dnode_dir_read(struct fsw_shandle *shand, struct fsw_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_dnode *dno = shand->dnode; + fsw_u64 saved_pos; + + if (dno->type != FSW_DNODE_TYPE_DIR) + return FSW_UNSUPPORTED; + + saved_pos = shand->pos; + status = dno->vol->fstype_table->dir_read(dno->vol, dno, shand, child_dno_out); + if (status) + shand->pos = saved_pos; + return status; +} + +/** + * Read the target path of a symbolic link. This function is called by the host driver + * to read the "content" of a symbolic link, that is the relative or absolute path + * it points to. + * + * If the function returns FSW_SUCCESS, the string handle provided by the caller is + * filled with a string in the host's preferred encoding. The caller is responsible + * for calling fsw_strfree on the string. + */ + +fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *target_name) +{ + fsw_status_t status; + + status = fsw_dnode_fill(dno); + if (status) + return status; + if (dno->type != FSW_DNODE_TYPE_SYMLINK) + return FSW_UNSUPPORTED; + + return dno->vol->fstype_table->readlink(dno->vol, dno, target_name); +} + +/** + * Read the target path of a symbolic link by accessing file data. This function can + * be called by the file system driver if the file system stores the target path + * as normal file data. This function will open an shandle, read the whole content + * of the file into a buffer, and build a string from that. Currently the encoding + * for the string is fixed as FSW_STRING_TYPE_ISO88591. + * + * If the function returns FSW_SUCCESS, the string handle provided by the caller is + * filled with a string in the host's preferred encoding. The caller is responsible + * for calling fsw_strfree on the string. + */ + +fsw_status_t fsw_dnode_readlink_data(struct fsw_dnode *dno, struct fsw_string *link_target) +{ + fsw_status_t status; + struct fsw_shandle shand; + fsw_u32 buffer_size; + char buffer[FSW_PATH_MAX]; + + struct fsw_string s; + + if (dno->size > FSW_PATH_MAX) + return FSW_VOLUME_CORRUPTED; + + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = (int)dno->size; + s.data = buffer; + + // open shandle and read the data + status = fsw_shandle_open(dno, &shand); + if (status) + return status; + buffer_size = (fsw_u32)s.size; + status = fsw_shandle_read(&shand, &buffer_size, buffer); + fsw_shandle_close(&shand); + if (status) + return status; + if ((int)buffer_size < s.size) + return FSW_VOLUME_CORRUPTED; + + status = fsw_strdup_coerce(link_target, dno->vol->host_string_type, &s); + return status; +} + +/** + * Resolve a symbolic link. This function can be called by the host driver to make + * sure the a dnode is fully resolved instead of pointing at a symlink. If the dnode + * passed in is not a symlink, it is returned unmodified. + * + * Note that absolute paths will be resolved relative to the root directory of the + * volume. If the host is an operating system with its own VFS layer, it should + * resolve symlinks on its own. + * + * If the function returns FSW_SUCCESS, *target_dno_out points at a dnode that is + * not a symlink. The caller is responsible for calling fsw_dnode_release on it. + */ + +fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out) +{ + fsw_status_t status; + struct fsw_string target_name; + struct fsw_dnode *target_dno; + + fsw_dnode_retain(dno); + + while (1) { + // get full information + status = fsw_dnode_fill(dno); + if (status) + goto errorexit; + if (dno->type != FSW_DNODE_TYPE_SYMLINK) { + // found a non-symlink target, return it + *target_dno_out = dno; + return FSW_SUCCESS; + } + if (dno->parent == NULL) { // safety measure, cannot happen in theory + status = FSW_NOT_FOUND; + goto errorexit; + } + + // read the link's target + status = fsw_dnode_readlink(dno, &target_name); + if (status) + goto errorexit; + + // resolve it + status = fsw_dnode_lookup_path(dno->parent, &target_name, '/', &target_dno); + fsw_strfree(&target_name); + if (status) + goto errorexit; + + // target_dno becomes the new dno + fsw_dnode_release(dno); + dno = target_dno; // is already retained + } + +errorexit: + fsw_dnode_release(dno); + return status; +} + +/** + * Set up a shandle (storage handle) to access a file's data. This function is called + * by the host driver and by the core when they need to access a file's data. It is also + * used in accessing the raw data of directories and symlinks if the file system uses + * the same mechanisms for storing the data of those items. + * + * The storage for the fsw_shandle structure is provided by the caller. The dnode and pos + * fields may be accessed, pos may also be written to to set the file pointer. The file's + * data size is available as shand->dnode->size. + * + * If this function returns FSW_SUCCESS, the caller must call fsw_shandle_close to release + * the dnode reference held by the shandle. + */ + +fsw_status_t fsw_shandle_open(struct fsw_dnode *dno, struct fsw_shandle *shand) +{ + fsw_status_t status; + struct fsw_volume *vol = dno->vol; + + // read full dnode information into memory + status = vol->fstype_table->dnode_fill(vol, dno); + if (status) + return status; + + // setup shandle + fsw_dnode_retain(dno); + + shand->dnode = dno; + shand->pos = 0; + shand->extent.type = FSW_EXTENT_TYPE_INVALID; + + return FSW_SUCCESS; +} + +/** + * Close a shandle after accessing the dnode's data. This function is called by the host + * driver or core functions when they are finished with accessing a file's data. It + * releases the dnode reference and frees any buffers associated with the shandle itself. + * The dnode is only released if this was the last reference using it. + */ + +void fsw_shandle_close(struct fsw_shandle *shand) +{ + if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER) + fsw_free(shand->extent.buffer); + fsw_dnode_release(shand->dnode); +} + +/** + * Read data from a shandle (storage handle for a dnode). This function is called by the + * host driver or internally when data is read from a file. TODO: more + */ + +fsw_status_t fsw_shandle_read(struct fsw_shandle *shand, fsw_u32 *buffer_size_inout, void *buffer_in) +{ + fsw_status_t status; + struct fsw_dnode *dno = shand->dnode; + struct fsw_volume *vol = dno->vol; + fsw_u8 *buffer, *block_buffer; + fsw_u32 buflen, copylen, pos; + fsw_u32 log_bno, pos_in_extent, phys_bno, pos_in_physblock; + fsw_u32 cache_level; + + if (shand->pos >= dno->size) { // already at EOF + *buffer_size_inout = 0; + return FSW_SUCCESS; + } + + // initialize vars + buffer = buffer_in; + buflen = *buffer_size_inout; + pos = (fsw_u32)shand->pos; + cache_level = (dno->type != FSW_DNODE_TYPE_FILE) ? 1 : 0; + // restrict read to file size + if (buflen > dno->size - pos) + buflen = (fsw_u32)(dno->size - pos); + + while (buflen > 0) { + // get extent for the current logical block + log_bno = pos / vol->log_blocksize; + if (shand->extent.type == FSW_EXTENT_TYPE_INVALID || + log_bno < shand->extent.log_start || + log_bno >= shand->extent.log_start + shand->extent.log_count) { + + if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER) + fsw_free(shand->extent.buffer); + + // ask the file system for the proper extent + shand->extent.log_start = log_bno; + status = vol->fstype_table->get_extent(vol, dno, &shand->extent); + if (status) { + shand->extent.type = FSW_EXTENT_TYPE_INVALID; + return status; + } + } + + pos_in_extent = pos - shand->extent.log_start * vol->log_blocksize; + + // dispatch by extent type + if (shand->extent.type == FSW_EXTENT_TYPE_PHYSBLOCK) { + // convert to physical block number and offset + phys_bno = shand->extent.phys_start + pos_in_extent / vol->phys_blocksize; + pos_in_physblock = pos_in_extent & (vol->phys_blocksize - 1); + copylen = vol->phys_blocksize - pos_in_physblock; + if (copylen > buflen) + copylen = buflen; + + // get one physical block + status = fsw_block_get(vol, phys_bno, cache_level, (void **)&block_buffer); + if (status) + return status; + + // copy data from it + fsw_memcpy(buffer, block_buffer + pos_in_physblock, copylen); + fsw_block_release(vol, phys_bno, block_buffer); + + } else if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER) { + copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent; + if (copylen > buflen) + copylen = buflen; + fsw_memcpy(buffer, (fsw_u8 *)shand->extent.buffer + pos_in_extent, copylen); + + } else { // _SPARSE or _INVALID + copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent; + if (copylen > buflen) + copylen = buflen; + fsw_memzero(buffer, copylen); + + } + + buffer += copylen; + buflen -= copylen; + pos += copylen; + } + + *buffer_size_inout = (fsw_u32)(pos - shand->pos); + shand->pos = pos; + + return FSW_SUCCESS; +} + +// EOF diff --git a/filesystems/fsw_core.h b/filesystems/fsw_core.h new file mode 100644 index 0000000..284913c --- /dev/null +++ b/filesystems/fsw_core.h @@ -0,0 +1,537 @@ +/* $Id: fsw_core.h 33540 2010-10-28 09:27:05Z vboxsync $ */ +/** @file + * fsw_core.h - Core file system wrapper abstraction layer header. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * Portions Copyright (c) The Regents of the University of California. + * Portions Copyright (c) UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_CORE_H_ +#define _FSW_CORE_H_ + +#include "fsw_base.h" + + +/** Maximum size for a path, specifically symlink target paths. */ +#ifndef VBOX +#define FSW_PATH_MAX (4096) +#else +/* Too big allocations are handled with alloca() */ +#define FSW_PATH_MAX (2048) +#endif + +/** Helper macro for token concatenation. */ +#define FSW_CONCAT3(a,b,c) a##b##c +/** Expands to the name of a fstype dispatch table (fsw_fstype_table) for a named file system type. */ +#define FSW_FSTYPE_TABLE_NAME(t) FSW_CONCAT3(fsw_,t,_table) + +/** Indicates that the block cache entry is empty. */ +#define FSW_INVALID_BNO (~0UL) + + +// +// Byte-swapping macros +// + + +/** + * \name Byte Order Macros + * Implements big endian vs. little endian awareness and conversion. + */ +/*@{*/ + +typedef fsw_u16 fsw_u16_le; +typedef fsw_u16 fsw_u16_be; +typedef fsw_u32 fsw_u32_le; +typedef fsw_u32 fsw_u32_be; +typedef fsw_u64 fsw_u64_le; +typedef fsw_u64 fsw_u64_be; + +#define FSW_SWAPVALUE_U16(v) ((((fsw_u16)(v) & 0xff00) >> 8) | \ + (((fsw_u16)(v) & 0x00ff) << 8)) +#define FSW_SWAPVALUE_U32(v) ((((fsw_u32)(v) & 0xff000000UL) >> 24) | \ + (((fsw_u32)(v) & 0x00ff0000UL) >> 8) | \ + (((fsw_u32)(v) & 0x0000ff00UL) << 8) | \ + (((fsw_u32)(v) & 0x000000ffUL) << 24)) +#define FSW_SWAPVALUE_U64(v) ((((fsw_u64)(v) & 0xff00000000000000ULL) >> 56) | \ + (((fsw_u64)(v) & 0x00ff000000000000ULL) >> 40) | \ + (((fsw_u64)(v) & 0x0000ff0000000000ULL) >> 24) | \ + (((fsw_u64)(v) & 0x000000ff00000000ULL) >> 8) | \ + (((fsw_u64)(v) & 0x00000000ff000000ULL) << 8) | \ + (((fsw_u64)(v) & 0x0000000000ff0000ULL) << 24) | \ + (((fsw_u64)(v) & 0x000000000000ff00ULL) << 40) | \ + (((fsw_u64)(v) & 0x00000000000000ffULL) << 56)) + +#ifdef FSW_LITTLE_ENDIAN + +#define fsw_u16_le_swap(v) (v) +#define fsw_u16_be_swap(v) FSW_SWAPVALUE_U16(v) +#define fsw_u32_le_swap(v) (v) +#define fsw_u32_be_swap(v) FSW_SWAPVALUE_U32(v) +#define fsw_u64_le_swap(v) (v) +#define fsw_u64_be_swap(v) FSW_SWAPVALUE_U64(v) + +#define fsw_u16_le_sip(var) +#define fsw_u16_be_sip(var) (var = FSW_SWAPVALUE_U16(var)) +#define fsw_u32_le_sip(var) +#define fsw_u32_be_sip(var) (var = FSW_SWAPVALUE_U32(var)) +#define fsw_u64_le_sip(var) +#define fsw_u64_be_sip(var) (var = FSW_SWAPVALUE_U64(var)) + +#else +#ifdef FSW_BIG_ENDIAN + +#define fsw_u16_le_swap(v) FSW_SWAPVALUE_U16(v) +#define fsw_u16_be_swap(v) (v) +#define fsw_u32_le_swap(v) FSW_SWAPVALUE_U32(v) +#define fsw_u32_be_swap(v) (v) +#define fsw_u64_le_swap(v) FSW_SWAPVALUE_U64(v) +#define fsw_u64_be_swap(v) (v) + +#define fsw_u16_le_sip(var) (var = FSW_SWAPVALUE_U16(var)) +#define fsw_u16_be_sip(var) +#define fsw_u32_le_sip(var) (var = FSW_SWAPVALUE_U32(var)) +#define fsw_u32_be_sip(var) +#define fsw_u64_le_sip(var) (var = FSW_SWAPVALUE_U64(var)) +#define fsw_u64_be_sip(var) + +#else +#fail Neither FSW_BIG_ENDIAN nor FSW_LITTLE_ENDIAN are defined +#endif +#endif + +/*@}*/ + + +// +// The following evil hack avoids a lot of casts between generic and fstype-specific +// structures. +// + +#ifndef VOLSTRUCTNAME +#define VOLSTRUCTNAME fsw_volume +#else +struct VOLSTRUCTNAME; +#endif +#ifndef DNODESTRUCTNAME +#define DNODESTRUCTNAME fsw_dnode +#else +struct DNODESTRUCTNAME; +#endif + + +/** + * Status code type, returned from all functions that can fail. + */ +typedef int fsw_status_t; + +/** + * Possible status codes. + */ +enum { + FSW_SUCCESS, + FSW_OUT_OF_MEMORY, + FSW_IO_ERROR, + FSW_UNSUPPORTED, + FSW_NOT_FOUND, + FSW_VOLUME_CORRUPTED, + FSW_UNKNOWN_ERROR +}; + + +/** + * Core: A string with explicit length and encoding information. + */ + +struct fsw_string { + int type; //!< Encoding of the string - empty, ISO-8859-1, UTF8, UTF16 + int len; //!< Length in characters + int size; //!< Total data size in bytes + void *data; //!< Data pointer (may be NULL if type is EMPTY or len is zero) +}; + +/** + * Possible string types / encodings. In the case of FSW_STRING_TYPE_EMPTY, + * all other members of the fsw_string structure may be invalid. + */ +enum { + FSW_STRING_TYPE_EMPTY, + FSW_STRING_TYPE_ISO88591, + FSW_STRING_TYPE_UTF8, + FSW_STRING_TYPE_UTF16, + FSW_STRING_TYPE_UTF16_SWAPPED +}; + +#ifdef FSW_LITTLE_ENDIAN +#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16 +#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16_SWAPPED +#else +#define FSW_STRING_TYPE_UTF16_LE FSW_STRING_TYPE_UTF16_SWAPPED +#define FSW_STRING_TYPE_UTF16_BE FSW_STRING_TYPE_UTF16 +#endif + +/** Static initializer for an empty string. */ +#define FSW_STRING_INIT { FSW_STRING_TYPE_EMPTY, 0, 0, NULL } + + +/* forward declarations */ + +struct fsw_dnode; +struct fsw_host_table; +struct fsw_fstype_table; + +struct fsw_blockcache { + fsw_u32 refcount; //!< Reference count + fsw_u32 cache_level; //!< Level of importance of this block + fsw_u32 phys_bno; //!< Physical block number + void *data; //!< Block data buffer +}; + +/** + * Core: Represents a mounted volume. + */ + +struct fsw_volume { + fsw_u32 phys_blocksize; //!< Block size for disk access / file system structures + fsw_u32 log_blocksize; //!< Block size for logical file data + + struct DNODESTRUCTNAME *root; //!< Root directory dnode + struct fsw_string label; //!< Volume label + + struct fsw_dnode *dnode_head; //!< List of all dnodes allocated for this volume + + struct fsw_blockcache *bcache; //!< Array of block cache entries + fsw_u32 bcache_size; //!< Number of entries in the block cache array + + void *host_data; //!< Hook for a host-specific data structure + struct fsw_host_table *host_table; //!< Dispatch table for host-specific functions + struct fsw_fstype_table *fstype_table; //!< Dispatch table for file system specific functions + int host_string_type; //!< String type used by the host environment +}; + +/** + * Core: Represents a "directory node" - a file, directory, symlink, whatever. + */ + +struct fsw_dnode { + fsw_u32 refcount; //!< Reference count + + struct VOLSTRUCTNAME *vol; //!< The volume this dnode belongs to + struct DNODESTRUCTNAME *parent; //!< Parent directory dnode + struct fsw_string name; //!< Name of this item in the parent directory + + fsw_u32 dnode_id; //!< Unique id number (usually the inode number) + int type; //!< Type of the dnode - file, dir, symlink, special + fsw_u64 size; //!< Data size in bytes + + struct fsw_dnode *next; //!< Doubly-linked list of all dnodes: previous dnode + struct fsw_dnode *prev; //!< Doubly-linked list of all dnodes: next dnode +}; + +/** + * Possible dnode types. FSW_DNODE_TYPE_UNKNOWN may only be used before + * fsw_dnode_fill has been called on the dnode. + */ +enum { + FSW_DNODE_TYPE_UNKNOWN, + FSW_DNODE_TYPE_FILE, + FSW_DNODE_TYPE_DIR, + FSW_DNODE_TYPE_SYMLINK, + FSW_DNODE_TYPE_SPECIAL +}; + +/** + * Core: Stores the mapping of a region of a file to the data on disk. + */ + +struct fsw_extent { + fsw_u32 type; //!< Type of extent specification + fsw_u32 log_start; //!< Starting logical block number + fsw_u32 log_count; //!< Logical block count + fsw_u32 phys_start; //!< Starting physical block number (for FSW_EXTENT_TYPE_PHYSBLOCK only) + void *buffer; //!< Allocated buffer pointer (for FSW_EXTENT_TYPE_BUFFER only) +}; + +/** + * Possible extent representation types. FSW_EXTENT_TYPE_INVALID is for shandle's + * internal use only, it must not be returned from a get_extent function. + */ +enum { + FSW_EXTENT_TYPE_INVALID, + FSW_EXTENT_TYPE_SPARSE, + FSW_EXTENT_TYPE_PHYSBLOCK, + FSW_EXTENT_TYPE_BUFFER +}; + +/** + * Core: An access structure to a dnode's raw data. There can be multiple + * shandles per dnode, each of them has its own position pointer. + */ + +struct fsw_shandle { + struct fsw_dnode *dnode; //!< The dnode this handle reads data from + + fsw_u64 pos; //!< Current file pointer in bytes + struct fsw_extent extent; //!< Current extent +}; + +/** + * Core: Used in gathering detailed information on a volume. + */ + +struct fsw_volume_stat { + fsw_u64 total_bytes; //!< Total size of data area size in bytes + fsw_u64 free_bytes; //!< Bytes still available for storing file data +}; + +/** + * Core: Used in gathering detailed information on a dnode. + */ + +struct fsw_dnode_stat { + fsw_u64 used_bytes; //!< Bytes actually used by the file on disk + void (*store_time_posix)(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time); //!< Callback for storing a Posix-style timestamp + void (*store_attr_posix)(struct fsw_dnode_stat *sb, fsw_u16 posix_mode); //!< Callback for storing a Posix-style file mode + void *host_data; //!< Hook for a host-specific data structure +}; + +/** + * Type of the timestamp passed into store_time_posix. + */ +enum { + FSW_DNODE_STAT_CTIME, + FSW_DNODE_STAT_MTIME, + FSW_DNODE_STAT_ATIME +}; + +/** + * Core: Function table for a host environment. + */ + +struct fsw_host_table +{ + int native_string_type; //!< String type used by the host environment + + void (*change_blocksize)(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize); + fsw_status_t (*read_block)(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer); +}; + +/** + * Core: Function table for a file system driver. + */ + +struct fsw_fstype_table +{ + struct fsw_string name; //!< String giving the name of the file system + fsw_u32 volume_struct_size; //!< Size for allocating the fsw_volume structure + fsw_u32 dnode_struct_size; //!< Size for allocating the fsw_dnode structure + + fsw_status_t (*volume_mount)(struct VOLSTRUCTNAME *vol); + void (*volume_free)(struct VOLSTRUCTNAME *vol); + fsw_status_t (*volume_stat)(struct VOLSTRUCTNAME *vol, struct fsw_volume_stat *sb); + + fsw_status_t (*dnode_fill)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno); + void (*dnode_free)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno); + fsw_status_t (*dnode_stat)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_dnode_stat *sb); + fsw_status_t (*get_extent)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_extent *extent); + + fsw_status_t (*dir_lookup)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_string *lookup_name, struct DNODESTRUCTNAME **child_dno); + fsw_status_t (*dir_read)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_shandle *shand, struct DNODESTRUCTNAME **child_dno); + fsw_status_t (*readlink)(struct VOLSTRUCTNAME *vol, struct DNODESTRUCTNAME *dno, + struct fsw_string *link_target); +}; + + +/** + * \name Volume Functions + */ +/*@{*/ + +fsw_status_t fsw_mount(void *host_data, + struct fsw_host_table *host_table, + struct fsw_fstype_table *fstype_table, + struct fsw_volume **vol_out); +void fsw_unmount(struct fsw_volume *vol); +fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb); + +void fsw_set_blocksize(struct VOLSTRUCTNAME *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize); +fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out); +void fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer); + +/*@}*/ + + +/** + * \name dnode Functions + */ +/*@{*/ + +fsw_status_t fsw_dnode_create_root(struct VOLSTRUCTNAME *vol, fsw_u32 dnode_id, struct DNODESTRUCTNAME **dno_out); +fsw_status_t fsw_dnode_create(struct DNODESTRUCTNAME *parent_dno, fsw_u32 dnode_id, int type, + struct fsw_string *name, struct DNODESTRUCTNAME **dno_out); +void fsw_dnode_retain(struct fsw_dnode *dno); +void fsw_dnode_release(struct fsw_dnode *dno); + +fsw_status_t fsw_dnode_fill(struct fsw_dnode *dno); +fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb); + +fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno, + struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out); +fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno, + struct fsw_string *lookup_path, char separator, + struct fsw_dnode **child_dno_out); +fsw_status_t fsw_dnode_dir_read(struct fsw_shandle *shand, struct fsw_dnode **child_dno_out); +fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *link_target); +fsw_status_t fsw_dnode_readlink_data(struct DNODESTRUCTNAME *dno, struct fsw_string *link_target); +fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out); + +/*@}*/ + + +/** + * \name shandle Functions + */ +/*@{*/ + +fsw_status_t fsw_shandle_open(struct DNODESTRUCTNAME *dno, struct fsw_shandle *shand); +void fsw_shandle_close(struct fsw_shandle *shand); +fsw_status_t fsw_shandle_read(struct fsw_shandle *shand, fsw_u32 *buffer_size_inout, void *buffer); + +/*@}*/ + + +/** + * \name Memory Functions + */ +/*@{*/ + +fsw_status_t fsw_alloc_zero(int len, void **ptr_out); +fsw_status_t fsw_memdup(void **dest_out, void *src, int len); + +/*@}*/ + + +/** + * \name String Functions + */ +/*@{*/ + +int fsw_strlen(struct fsw_string *s); +int fsw_streq(struct fsw_string *s1, struct fsw_string *s2); +int fsw_streq_cstr(struct fsw_string *s1, const char *s2); +fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src); +void fsw_strsplit(struct fsw_string *lookup_name, struct fsw_string *buffer, char separator); + +void fsw_strfree(struct fsw_string *s); +fsw_u16 fsw_to_lower(fsw_u16 ch); + +/*@}*/ + + +/** + * \name Posix Mode Macros + * These macros can be used globally to test fields and bits in + * Posix-style modes. + * + * Taken from FreeBSD sys/stat.h. + */ +/*@{*/ +#ifndef S_IRWXU + +#define S_ISUID 0004000 /* set user id on execution */ +#define S_ISGID 0002000 /* set group id on execution */ +#define S_ISTXT 0001000 /* sticky bit */ + +#define S_IRWXU 0000700 /* RWX mask for owner */ +#define S_IRUSR 0000400 /* R for owner */ +#define S_IWUSR 0000200 /* W for owner */ +#define S_IXUSR 0000100 /* X for owner */ + +#define S_IRWXG 0000070 /* RWX mask for group */ +#define S_IRGRP 0000040 /* R for group */ +#define S_IWGRP 0000020 /* W for group */ +#define S_IXGRP 0000010 /* X for group */ + +#define S_IRWXO 0000007 /* RWX mask for other */ +#define S_IROTH 0000004 /* R for other */ +#define S_IWOTH 0000002 /* W for other */ +#define S_IXOTH 0000001 /* X for other */ + +#define S_IFMT 0170000 /* type of file mask */ +#define S_IFIFO 0010000 /* named pipe (fifo) */ +#define S_IFCHR 0020000 /* character special */ +#define S_IFDIR 0040000 /* directory */ +#define S_IFBLK 0060000 /* block special */ +#define S_IFREG 0100000 /* regular */ +#define S_IFLNK 0120000 /* symbolic link */ +#define S_IFSOCK 0140000 /* socket */ +#define S_ISVTX 0001000 /* save swapped text even after use */ +#define S_IFWHT 0160000 /* whiteout */ + +#define S_ISDIR(m) (((m) & 0170000) == 0040000) /* directory */ +#define S_ISCHR(m) (((m) & 0170000) == 0020000) /* char special */ +#define S_ISBLK(m) (((m) & 0170000) == 0060000) /* block special */ +#define S_ISREG(m) (((m) & 0170000) == 0100000) /* regular file */ +#define S_ISFIFO(m) (((m) & 0170000) == 0010000) /* fifo or socket */ +#define S_ISLNK(m) (((m) & 0170000) == 0120000) /* symbolic link */ +#define S_ISSOCK(m) (((m) & 0170000) == 0140000) /* socket */ +#define S_ISWHT(m) (((m) & 0170000) == 0160000) /* whiteout */ + +#define S_BLKSIZE 512 /* block size used in the stat struct */ + +#endif +/*@}*/ + + +#endif diff --git a/filesystems/fsw_efi.c b/filesystems/fsw_efi.c new file mode 100644 index 0000000..e7d2400 --- /dev/null +++ b/filesystems/fsw_efi.c @@ -0,0 +1,1085 @@ +/* $Id: fsw_efi.c 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_efi.c - EFI host environment code. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_efi.h" + +#define DEBUG_LEVEL 0 + +#ifndef FSTYPE +#ifdef VBOX +#error FSTYPE must be defined! +#else +#define FSTYPE ext2 +#endif +#endif + +#define DEBUG_VBFS 1 +// CHAR8 *msgCursor; +// MESSAGE_LOG_PROTOCOL *Msg = NULL; + +#if DEBUG_VBFS==2 +#define DBG(x...) AsciiPrint(x) +#elif DEBUG_VBFS==1 +#define DBG(x...) BootLog(x) +#else +#define DBG(x...) +#endif + + +/** Helper macro for stringification. */ +#define FSW_EFI_STRINGIFY(x) #x +/** 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); + +/** + * 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); + +//#include "OverrideFunctions-kabyl.edk2.c.include" + +/** + * Image entry point. Installs the Driver Binding and Component Name protocols + * on the image's handle. Actually mounting a file system is initiated through + * the Driver Binding protocol at the firmware's request. + */ +EFI_STATUS EFIAPI fsw_efi_main(IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS Status; + +#ifndef VBOX + InitializeLib(ImageHandle, SystemTable); +#endif + + // complete Driver Binding protocol instance + fsw_efi_DriverBinding_table.ImageHandle = ImageHandle; + fsw_efi_DriverBinding_table.DriverBindingHandle = ImageHandle; + // install Driver Binding protocol + Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle, + &PROTO_NAME(DriverBindingProtocol), + EFI_NATIVE_INTERFACE, + &fsw_efi_DriverBinding_table); + if (EFI_ERROR (Status)) { + return Status; + } + + // install Component Name protocol + Status = BS->InstallProtocolInterface(&fsw_efi_DriverBinding_table.DriverBindingHandle, + &PROTO_NAME(ComponentNameProtocol), + EFI_NATIVE_INTERFACE, + &fsw_efi_ComponentName_table); + if (EFI_ERROR (Status)) { + return Status; + } + +// OverrideFunctions(); +// Msg = NULL; +// msgCursor = NULL; +// Status = BS->LocateProtocol(&gMsgLogProtocolGuid, NULL, (VOID **) &Msg); +// if (!EFI_ERROR(Status) && (Msg != NULL)) { +// msgCursor = Msg->Cursor; +// BootLog("MsgLog installed into VBoxFs\n"); +// } + + return EFI_SUCCESS; +} + +/** + * Driver Binding EFI protocol, Supported function. This function is called by EFI + * to test if this driver can handle a certain device. Our implementation only checks + * if the device is a disk (i.e. that it supports the Block I/O and Disk I/O protocols) + * and implicitly checks if the disk is already in use by another driver. + */ + +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Supported(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath) +{ + EFI_STATUS Status; + EFI_DISK_IO *DiskIo; + + // we check for both DiskIO and BlockIO protocols + + // first, open DiskIO + Status = BS->OpenProtocol(ControllerHandle, + &PROTO_NAME(DiskIoProtocol), + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER); + if (EFI_ERROR(Status)) + return Status; + + // we were just checking, close it again + BS->CloseProtocol(ControllerHandle, + &PROTO_NAME(DiskIoProtocol), + This->DriverBindingHandle, + ControllerHandle); + + // next, check BlockIO without actually opening it + Status = BS->OpenProtocol(ControllerHandle, + &PROTO_NAME(BlockIoProtocol), + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL); + return Status; +} + +/** + * 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, + &PROTO_NAME(BlockIoProtocol), + (VOID **) &BlockIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); // NOTE: we only want to look at the MediaId + if (EFI_ERROR(Status)) { +// Print(L"Fsw ERROR: OpenProtocol(BlockIo) returned %x\n", Status); + return Status; + } + + Status = BS->OpenProtocol(ControllerHandle, + &PROTO_NAME(DiskIoProtocol), + (VOID **) &DiskIo, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER); + if (EFI_ERROR(Status)) { + Print(L"Fsw ERROR: OpenProtocol(DiskIo) returned %r\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, + &PROTO_NAME(SimpleFileSystemProtocol), + &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, + &PROTO_NAME(DiskIoProtocol), + This->DriverBindingHandle, + ControllerHandle); + } + + return Status; +} + +/** + * Driver Binding EFI protocol, Stop function. This function is called by EFI + * to stop the driver on the given device. This translates to an unmount + * call for the FSW core. + * + * We assume that all file handles on the volume have been closed before + * the driver is stopped. At least with the EFI shell, that is actually the + * case; it closes all file handles between commands. + */ + +EFI_STATUS EFIAPI fsw_efi_DriverBinding_Stop(IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer) +{ + EFI_STATUS Status; + EFI_FILE_IO_INTERFACE *FileSystem; + FSW_VOLUME_DATA *Volume; + +#if DEBUG_LEVEL + Print(L"fsw_efi_DriverBinding_Stop\n"); +#endif + + // get the installed SimpleFileSystem interface + Status = BS->OpenProtocol(ControllerHandle, + &PROTO_NAME(SimpleFileSystemProtocol), + (VOID **) &FileSystem, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(Status)) + return EFI_UNSUPPORTED; + + // get private data structure + Volume = FSW_VOLUME_FROM_FILE_SYSTEM(FileSystem); + + // uninstall Simple File System protocol + Status = BS->UninstallMultipleProtocolInterfaces(ControllerHandle, + &PROTO_NAME(SimpleFileSystemProtocol), &Volume->FileSystem, + NULL); + if (EFI_ERROR(Status)) { + // Print(L"Fsw ERROR: UninstallMultipleProtocolInterfaces returned %x\n", Status); + return Status; + } +#if DEBUG_LEVEL + Print(L"fsw_efi_DriverBinding_Stop: protocol uninstalled successfully\n"); +#endif + + // release private data structure + if (Volume->vol != NULL) + fsw_unmount(Volume->vol); + FreePool(Volume); + + // close the consumed protocols + Status = BS->CloseProtocol(ControllerHandle, + &PROTO_NAME(DiskIoProtocol), + This->DriverBindingHandle, + ControllerHandle); + + return Status; +} + +/** + * Component Name EFI protocol, GetDriverName function. Used by the EFI + * environment to inquire the name of this driver. The name returned is + * based on the file system type actually used in compilation. + */ + +EFI_STATUS EFIAPI fsw_efi_ComponentName_GetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName) +{ + if (Language == NULL || DriverName == NULL) + return EFI_INVALID_PARAMETER; + + if (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; + } +} + +/** + * Get file or volume information. This function implements the GetInfo call + * for all file handles. Control is dispatched according to the type of information + * requested by the caller. + */ + +EFI_STATUS fsw_efi_dnode_getinfo(IN FSW_FILE_DATA *File, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + FSW_VOLUME_DATA *Volume = (FSW_VOLUME_DATA *)File->shand.dnode->vol->host_data; + EFI_FILE_SYSTEM_INFO *FSInfo; + UINTN RequiredSize; + struct fsw_volume_stat vsb; + + + if (CompareGuid(InformationType, &gEfiFileInfoGuid)) { +#if DEBUG_LEVEL + Print(L"fsw_efi_dnode_getinfo: FILE_INFO\n"); +#endif + + Status = fsw_efi_dnode_fill_FileInfo(Volume, File->shand.dnode, BufferSize, Buffer); + + } else if (CompareGuid(InformationType, &gEfiFileSystemInfoGuid)) { +#if DEBUG_LEVEL + Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_INFO\n"); +#endif + + // check buffer size + RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + fsw_efi_strsize(&Volume->vol->label); + if (*BufferSize < RequiredSize) { + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // fill structure + FSInfo = (EFI_FILE_SYSTEM_INFO *)Buffer; + FSInfo->Size = RequiredSize; + FSInfo->ReadOnly = TRUE; + FSInfo->BlockSize = Volume->vol->log_blocksize; + fsw_efi_strcpy(FSInfo->VolumeLabel, &Volume->vol->label); + + // get the missing info from the fs driver + ZeroMem(&vsb, sizeof(struct fsw_volume_stat)); + Status = fsw_efi_map_status(fsw_volume_stat(Volume->vol, &vsb), Volume); + if (EFI_ERROR(Status)) + return Status; + FSInfo->VolumeSize = vsb.total_bytes; + FSInfo->FreeSpace = vsb.free_bytes; + + // prepare for return + *BufferSize = RequiredSize; + Status = EFI_SUCCESS; + + } else if (CompareGuid(InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { +#if DEBUG_LEVEL + Print(L"fsw_efi_dnode_getinfo: FILE_SYSTEM_VOLUME_LABEL\n"); +#endif + + // check buffer size + RequiredSize = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL_INFO + fsw_efi_strsize(&Volume->vol->label); + if (*BufferSize < RequiredSize) { + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // copy volume label + fsw_efi_strcpy(((EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Buffer)->VolumeLabel, &Volume->vol->label); + + // prepare for return + *BufferSize = RequiredSize; + Status = EFI_SUCCESS; + + } else { + Status = EFI_UNSUPPORTED; + } + + return Status; +} + +/** + * Time mapping callback for the fsw_dnode_stat call. This function converts + * a Posix style timestamp into an EFI_TIME structure and writes it to the + * appropriate member of the EFI_FILE_INFO structure that we're filling. + */ + +static void fsw_efi_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if (which == FSW_DNODE_STAT_CTIME) + fsw_efi_decode_time(&FileInfo->CreateTime, posix_time); + else if (which == FSW_DNODE_STAT_MTIME) + fsw_efi_decode_time(&FileInfo->ModificationTime, posix_time); + else if (which == FSW_DNODE_STAT_ATIME) + fsw_efi_decode_time(&FileInfo->LastAccessTime, posix_time); +} + +/** + * Mode mapping callback for the fsw_dnode_stat call. This function looks at + * the Posix mode passed by the file system driver and makes appropriate + * adjustments to the EFI_FILE_INFO structure that we're filling. + */ + +static void fsw_efi_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if ((posix_mode & S_IWUSR) == 0) + FileInfo->Attribute |= EFI_FILE_READ_ONLY; +} + +/** + * Common function to fill an EFI_FILE_INFO with information about a dnode. + */ + +EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, + IN struct fsw_dnode *dno, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + EFI_FILE_INFO *FileInfo; + UINTN RequiredSize; + struct fsw_dnode_stat sb; + + // make sure the dnode has complete info + Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume); + if (EFI_ERROR(Status)) + return Status; + + // TODO: check/assert that the dno's name is in UTF16 + + // check buffer size + RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_efi_strsize(&dno->name); + if (*BufferSize < RequiredSize) { + // TODO: wind back the directory in this case + +#if DEBUG_LEVEL + Print(L"...BUFFER TOO SMALL\n"); +#endif + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // fill structure + ZeroMem(Buffer, RequiredSize); + FileInfo = (EFI_FILE_INFO *)Buffer; + FileInfo->Size = RequiredSize; + FileInfo->FileSize = dno->size; + FileInfo->Attribute = 0; + if (dno->type == FSW_DNODE_TYPE_DIR) + FileInfo->Attribute |= EFI_FILE_DIRECTORY; + fsw_efi_strcpy(FileInfo->FileName, &dno->name); + + // get the missing info from the fs driver + ZeroMem(&sb, sizeof(struct fsw_dnode_stat)); + sb.store_time_posix = fsw_efi_store_time_posix; + sb.store_attr_posix = fsw_efi_store_attr_posix; + sb.host_data = FileInfo; + Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume); + if (EFI_ERROR(Status)) + return Status; + FileInfo->PhysicalSize = sb.used_bytes; + + // prepare for return + *BufferSize = RequiredSize; +#if DEBUG_LEVEL + Print(L"...returning '%s'\n", FileInfo->FileName); +#endif + return EFI_SUCCESS; +} + +// EOF diff --git a/filesystems/fsw_efi.h b/filesystems/fsw_efi.h new file mode 100644 index 0000000..6ca5a86 --- /dev/null +++ b/filesystems/fsw_efi.h @@ -0,0 +1,118 @@ +/* $Id: fsw_efi.h 33540 2010-10-28 09:27:05Z vboxsync $ */ +/** @file + * fsw_efi.h - EFI host environment header. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_EFI_H_ +#define _FSW_EFI_H_ + +#include "fsw_core.h" + +// extern CHAR8 *msgCursor; +// extern MESSAGE_LOG_PROTOCOL *Msg; + +/** + * 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 + + UINT64 Type; //!< File type used for dispatching + struct fsw_shandle shand; //!< FSW handle for this file + +} FSW_FILE_DATA; + +/** File type: regular file. */ +#define FSW_EFI_FILE_TYPE_FILE (0) +/** File type: directory. */ +#define FSW_EFI_FILE_TYPE_DIR (1) + +/** Signature for the file handle structure. */ +#define FSW_FILE_DATA_SIGNATURE EFI_SIGNATURE_32 ('f', 's', 'w', 'F') +/** Access macro for the file handle structure. */ +#define FSW_FILE_FROM_FILE_HANDLE(a) CR (a, FSW_FILE_DATA, FileHandle, FSW_FILE_DATA_SIGNATURE) + + +// +// Library functions +// + +VOID fsw_efi_decode_time(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime); + +UINTN fsw_efi_strsize(struct fsw_string *s); +VOID fsw_efi_strcpy(CHAR16 *Dest, struct fsw_string *src); + + +#endif diff --git a/filesystems/fsw_efi_base.h b/filesystems/fsw_efi_base.h new file mode 100644 index 0000000..7c7af75 --- /dev/null +++ b/filesystems/fsw_efi_base.h @@ -0,0 +1,98 @@ +/* $Id: fsw_efi_base.h 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_efi_base.h - Base definitions for the EFI host environment. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_EFI_BASE_H_ +#define _FSW_EFI_BASE_H_ + +#ifndef VBOX +#include +#include +#define PROTO_NAME(x) x +#endif + +#define FSW_LITTLE_ENDIAN (1) + + +// types, reuse EFI types + +typedef INT8 fsw_s8; +typedef UINT8 fsw_u8; +typedef INT16 fsw_s16; +typedef UINT16 fsw_u16; +typedef INT32 fsw_s32; +typedef UINT32 fsw_u32; +typedef INT64 fsw_s64; +typedef UINT64 fsw_u64; + + +// allocation functions + +#define fsw_alloc(size, ptrptr) (((*(ptrptr) = AllocatePool(size)) == NULL) ? FSW_OUT_OF_MEMORY : FSW_SUCCESS) +#define fsw_free(ptr) FreePool(ptr) + +// memory functions + +#define fsw_memzero(dest,size) ZeroMem(dest,size) +#define fsw_memcpy(dest,src,size) CopyMem(dest,src,size) +#define fsw_memeq(p1,p2,size) (CompareMem(p1,p2,size) == 0) + +// message printing + +#define FSW_MSGSTR(s) L##s +#define FSW_MSGFUNC Print + +// 64-bit hooks + +#define FSW_U64_SHR(val,shiftbits) RShiftU64((val), (shiftbits)) +#define FSW_U64_DIV(val,divisor) DivU64x32((val), (divisor), NULL) + + +#endif diff --git a/filesystems/fsw_efi_lib.c b/filesystems/fsw_efi_lib.c new file mode 100644 index 0000000..956e9ee --- /dev/null +++ b/filesystems/fsw_efi_lib.c @@ -0,0 +1,162 @@ +/* $Id: fsw_efi_lib.c 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_efi_lib.c - EFI host environment library functions. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_efi.h" + + +// +// time conversion +// +// Adopted from public domain code in FreeBSD libc. +// + +#define SECSPERMIN 60 +#define MINSPERHOUR 60 +#define HOURSPERDAY 24 +#define DAYSPERWEEK 7 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY) +#define MONSPERYEAR 12 + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY TM_THURSDAY + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) + +static const int mon_lengths[2][MONSPERYEAR] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; +static const int year_lengths[2] = { + DAYSPERNYEAR, DAYSPERLYEAR +}; + +VOID fsw_efi_decode_time(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime) +{ + long days, rem; + int y, newy, yleap; + const int *ip; + + ZeroMem(EfiTime, sizeof(EFI_TIME)); + + days = UnixTime / SECSPERDAY; + rem = UnixTime % SECSPERDAY; + + EfiTime->Hour = (UINT8) (rem / SECSPERHOUR); + rem = rem % SECSPERHOUR; + EfiTime->Minute = (UINT8) (rem / SECSPERMIN); + EfiTime->Second = (UINT8) (rem % SECSPERMIN); + + y = EPOCH_YEAR; + while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) { + newy = y + days / DAYSPERNYEAR; + if (days < 0) + --newy; + days -= (newy - y) * DAYSPERNYEAR + + LEAPS_THRU_END_OF(newy - 1) - + LEAPS_THRU_END_OF(y - 1); + y = newy; + } + EfiTime->Year = (UINT16)y; + ip = mon_lengths[yleap]; + for (EfiTime->Month = 0; days >= (long) ip[EfiTime->Month]; ++(EfiTime->Month)) + days = days - (long) ip[EfiTime->Month]; + EfiTime->Month++; // adjust range to EFI conventions + EfiTime->Day = (UINT8) (days + 1); +} + +// +// String functions, used for file and volume info +// + +UINTN fsw_efi_strsize(struct fsw_string *s) +{ + if (s->type == FSW_STRING_TYPE_EMPTY) + return sizeof(CHAR16); + return (s->len + 1) * sizeof(CHAR16); +} + +VOID fsw_efi_strcpy(CHAR16 *Dest, struct fsw_string *src) +{ + if (src->type == FSW_STRING_TYPE_EMPTY) { + Dest[0] = 0; + } else if (src->type == FSW_STRING_TYPE_UTF16) { + CopyMem(Dest, src->data, src->size); + Dest[src->len] = 0; + } else { + // TODO: coerce, recurse + Dest[0] = 0; + } +} + +#ifdef VBOX +int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + + for (i = 0; isb); + if (status) + return status; + + // read the superblock into its buffer + fsw_set_blocksize(vol, EXT2_SUPERBLOCK_BLOCKSIZE, EXT2_SUPERBLOCK_BLOCKSIZE); + status = fsw_block_get(vol, EXT2_SUPERBLOCK_BLOCKNO, 0, &buffer); + if (status) + return status; + fsw_memcpy(vol->sb, buffer, sizeof(struct ext2_super_block)); + fsw_block_release(vol, EXT2_SUPERBLOCK_BLOCKNO, buffer); + + // check the superblock + if (vol->sb->s_magic != EXT2_SUPER_MAGIC) + return FSW_UNSUPPORTED; + if (vol->sb->s_rev_level != EXT2_GOOD_OLD_REV && + vol->sb->s_rev_level != EXT2_DYNAMIC_REV) + return FSW_UNSUPPORTED; + if (vol->sb->s_rev_level == EXT2_DYNAMIC_REV && + (vol->sb->s_feature_incompat & ~(EXT2_FEATURE_INCOMPAT_FILETYPE | EXT3_FEATURE_INCOMPAT_RECOVER))) + return FSW_UNSUPPORTED; + + /* + if (vol->sb->s_rev_level == EXT2_DYNAMIC_REV && + (vol->sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER)) + Print(L"Ext2 WARNING: This ext3 file system needs recovery, trying to use it anyway.\n"); + */ + + // set real blocksize + blocksize = EXT2_BLOCK_SIZE(vol->sb); + fsw_set_blocksize(vol, blocksize, blocksize); + + // get other info from superblock + vol->ind_bcnt = EXT2_ADDR_PER_BLOCK(vol->sb); + vol->dind_bcnt = vol->ind_bcnt * vol->ind_bcnt; + vol->inode_size = EXT2_INODE_SIZE(vol->sb); + + for (i = 0; i < 16; i++) + if (vol->sb->s_volume_name[i] == 0) + break; + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = i; + s.data = vol->sb->s_volume_name; + status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); + if (status) + return status; + + // read the group descriptors to get inode table offsets + groupcnt = ((vol->sb->s_inodes_count - 2) / vol->sb->s_inodes_per_group) + 1; + gdesc_per_block = (vol->g.phys_blocksize / sizeof(struct ext2_group_desc)); + + status = fsw_alloc(sizeof(fsw_u32) * groupcnt, &vol->inotab_bno); + if (status) + return status; + for (groupno = 0; groupno < groupcnt; groupno++) { + // get the block group descriptor + gdesc_bno = (vol->sb->s_first_data_block + 1) + groupno / gdesc_per_block; + gdesc_index = groupno % gdesc_per_block; + status = fsw_block_get(vol, gdesc_bno, 1, (void **)&buffer); + if (status) + return status; + gdesc = ((struct ext2_group_desc *)(buffer)) + gdesc_index; + vol->inotab_bno[groupno] = gdesc->bg_inode_table; + fsw_block_release(vol, gdesc_bno, buffer); + } + + // setup the root dnode + status = fsw_dnode_create_root(vol, EXT2_ROOT_INO, &vol->g.root); + if (status) + return status; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext2_volume_mount: success, blocksize %d\n"), blocksize)); + + return FSW_SUCCESS; +} + +/** + * Free the volume data structure. Called by the core after an unmount or after + * an unsuccessful mount to release the memory used by the file system type specific + * part of the volume structure. + */ + +static void fsw_ext2_volume_free(struct fsw_ext2_volume *vol) +{ + if (vol->sb) + fsw_free(vol->sb); + if (vol->inotab_bno) + fsw_free(vol->inotab_bno); +} + +/** + * Get in-depth information on a volume. + */ + +static fsw_status_t fsw_ext2_volume_stat(struct fsw_ext2_volume *vol, struct fsw_volume_stat *sb) +{ + sb->total_bytes = (fsw_u64)vol->sb->s_blocks_count * vol->g.log_blocksize; + sb->free_bytes = (fsw_u64)vol->sb->s_free_blocks_count * vol->g.log_blocksize; + return FSW_SUCCESS; +} + +/** + * Get full information on a dnode from disk. This function is called by the core + * whenever it needs to access fields in the dnode structure that may not + * be filled immediately upon creation of the dnode. In the case of ext2, we + * delay fetching of the inode structure until dnode_fill is called. The size and + * type fields are invalid until this function has been called. + */ + +static fsw_status_t fsw_ext2_dnode_fill(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno) +{ + fsw_status_t status; + fsw_u32 groupno, ino_in_group, ino_bno, ino_index; + fsw_u8 *buffer; + + if (dno->raw) + return FSW_SUCCESS; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext2_dnode_fill: inode %d\n"), dno->g.dnode_id)); + + // read the inode block + groupno = (dno->g.dnode_id - 1) / vol->sb->s_inodes_per_group; + ino_in_group = (dno->g.dnode_id - 1) % vol->sb->s_inodes_per_group; + ino_bno = vol->inotab_bno[groupno] + + ino_in_group / (vol->g.phys_blocksize / vol->inode_size); + ino_index = ino_in_group % (vol->g.phys_blocksize / vol->inode_size); + status = fsw_block_get(vol, ino_bno, 2, (void **)&buffer); + if (status) + return status; + + // keep our inode around + status = fsw_memdup((void **)&dno->raw, buffer + ino_index * vol->inode_size, vol->inode_size); + fsw_block_release(vol, ino_bno, buffer); + if (status) + return status; + + // get info from the inode + dno->g.size = dno->raw->i_size; + // TODO: check docs for 64-bit sized files + if (S_ISREG(dno->raw->i_mode)) + dno->g.type = FSW_DNODE_TYPE_FILE; + else if (S_ISDIR(dno->raw->i_mode)) + dno->g.type = FSW_DNODE_TYPE_DIR; + else if (S_ISLNK(dno->raw->i_mode)) + dno->g.type = FSW_DNODE_TYPE_SYMLINK; + else + dno->g.type = FSW_DNODE_TYPE_SPECIAL; + + return FSW_SUCCESS; +} + +/** + * Free the dnode data structure. Called by the core when deallocating a dnode + * structure to release the memory used by the file system type specific part + * of the dnode structure. + */ + +static void fsw_ext2_dnode_free(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno) +{ + if (dno->raw) + fsw_free(dno->raw); +} + +/** + * Get in-depth information on a dnode. The core makes sure that fsw_ext2_dnode_fill + * has been called on the dnode before this function is called. Note that some + * data is not directly stored into the structure, but passed to a host-specific + * callback that converts it to the host-specific format. + */ + +static fsw_status_t fsw_ext2_dnode_stat(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_dnode_stat *sb) +{ + sb->used_bytes = dno->raw->i_blocks * 512; // very, very strange... + sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, dno->raw->i_ctime); + sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, dno->raw->i_atime); + sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, dno->raw->i_mtime); + sb->store_attr_posix(sb, dno->raw->i_mode); + + return FSW_SUCCESS; +} + +/** + * Retrieve file data mapping information. This function is called by the core when + * fsw_shandle_read needs to know where on the disk the required piece of the file's + * data can be found. The core makes sure that fsw_ext2_dnode_fill has been called + * on the dnode before. Our task here is to get the physical disk block number for + * the requested logical block number. + * + * The ext2 file system does not use extents, but stores a list of block numbers + * using the usual direct, indirect, double-indirect, triple-indirect scheme. To + * optimize access, this function checks if the following file blocks are mapped + * to consecutive disk blocks and returns a combined extent if possible. + */ + +static fsw_status_t fsw_ext2_get_extent(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_extent *extent) +{ + fsw_status_t status; + fsw_u32 bno, release_bno, buf_bcnt, file_bcnt; + fsw_u32 *buffer; + int path[5], i; + + // Preconditions: The caller has checked that the requested logical block + // is within the file's size. The dnode has complete information, i.e. + // fsw_ext2_dnode_read_info was called successfully on it. + + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + extent->log_count = 1; + bno = extent->log_start; + + // try direct block pointers in the inode + if (bno < EXT2_NDIR_BLOCKS) { + path[0] = bno; + path[1] = -1; + } else { + bno -= EXT2_NDIR_BLOCKS; + + // try indirect block + if (bno < vol->ind_bcnt) { + path[0] = EXT2_IND_BLOCK; + path[1] = bno; + path[2] = -1; + } else { + bno -= vol->ind_bcnt; + + // try double-indirect block + if (bno < vol->dind_bcnt) { + path[0] = EXT2_DIND_BLOCK; + path[1] = bno / vol->ind_bcnt; + path[2] = bno % vol->ind_bcnt; + path[3] = -1; + } else { + bno -= vol->dind_bcnt; + + // use the triple-indirect block + path[0] = EXT2_TIND_BLOCK; + path[1] = bno / vol->dind_bcnt; + path[2] = (bno / vol->ind_bcnt) % vol->ind_bcnt; + path[3] = bno % vol->ind_bcnt; + path[4] = -1; + } + } + } + + // follow the indirection path + buffer = dno->raw->i_block; + buf_bcnt = EXT2_NDIR_BLOCKS; + release_bno = 0; + for (i = 0; ; i++) { + bno = buffer[path[i]]; + if (bno == 0) { + extent->type = FSW_EXTENT_TYPE_SPARSE; + if (release_bno) + fsw_block_release(vol, release_bno, buffer); + return FSW_SUCCESS; + } + if (path[i+1] < 0) + break; + + if (release_bno) + fsw_block_release(vol, release_bno, buffer); + status = fsw_block_get(vol, bno, 1, (void **)&buffer); + if (status) + return status; + release_bno = bno; + buf_bcnt = vol->ind_bcnt; + } + extent->phys_start = bno; + + // check if the following blocks can be aggregated into one extent + file_bcnt = (fsw_u32)((dno->g.size + vol->g.log_blocksize - 1) & (vol->g.log_blocksize - 1)); + while (path[i] + extent->log_count < buf_bcnt && // indirect block has more block pointers + extent->log_start + extent->log_count < file_bcnt) { // file has more blocks + if (buffer[path[i] + extent->log_count] == buffer[path[i] + extent->log_count - 1] + 1) + extent->log_count++; + else + break; + } + + if (release_bno) + fsw_block_release(vol, release_bno, buffer); + return FSW_SUCCESS; +} + +/** + * Lookup a directory's child dnode by name. This function is called on a directory + * to retrieve the directory entry with the given name. A dnode is constructed for + * this entry and returned. The core makes sure that fsw_ext2_dnode_fill has been called + * and the dnode is actually a directory. + */ + +static fsw_status_t fsw_ext2_dir_lookup(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_string *lookup_name, struct fsw_ext2_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_shandle shand; + fsw_u32 child_ino; + struct ext2_dir_entry entry; + struct fsw_string entry_name; + + // Preconditions: The caller has checked that dno is a directory node. + + entry_name.type = FSW_STRING_TYPE_ISO88591; + + // setup handle to read the directory + status = fsw_shandle_open(dno, &shand); + if (status) + return status; + + // scan the directory for the file + child_ino = 0; + while (child_ino == 0) { + // read next entry + status = fsw_ext2_read_dentry(&shand, &entry); + if (status) + goto errorexit; + if (entry.inode == 0) { + // end of directory reached + status = FSW_NOT_FOUND; + goto errorexit; + } + + // compare name + entry_name.len = entry_name.size = entry.name_len; + entry_name.data = entry.name; + if (fsw_streq(lookup_name, &entry_name)) { + child_ino = entry.inode; + break; + } + } + + // setup a dnode for the child item + status = fsw_dnode_create(dno, child_ino, FSW_DNODE_TYPE_UNKNOWN, &entry_name, child_dno_out); + +errorexit: + fsw_shandle_close(&shand); + return status; +} + +/** + * Get the next directory entry when reading a directory. This function is called during + * directory iteration to retrieve the next directory entry. A dnode is constructed for + * the entry and returned. The core makes sure that fsw_ext2_dnode_fill has been called + * and the dnode is actually a directory. The shandle provided by the caller is used to + * record the position in the directory between calls. + */ + +static fsw_status_t fsw_ext2_dir_read(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_shandle *shand, struct fsw_ext2_dnode **child_dno_out) +{ + fsw_status_t status; + struct ext2_dir_entry entry; + struct fsw_string entry_name; + + // Preconditions: The caller has checked that dno is a directory node. The caller + // has opened a storage handle to the directory's storage and keeps it around between + // calls. + + while (1) { + // read next entry + status = fsw_ext2_read_dentry(shand, &entry); + if (status) + return status; + if (entry.inode == 0) // end of directory + return FSW_NOT_FOUND; + + // skip . and .. + if ((entry.name_len == 1 && entry.name[0] == '.') || + (entry.name_len == 2 && entry.name[0] == '.' && entry.name[1] == '.')) + continue; + break; + } + + // setup name + entry_name.type = FSW_STRING_TYPE_ISO88591; + entry_name.len = entry_name.size = entry.name_len; + entry_name.data = entry.name; + + // setup a dnode for the child item + status = fsw_dnode_create(dno, entry.inode, FSW_DNODE_TYPE_UNKNOWN, &entry_name, child_dno_out); + + return status; +} + +/** + * Read a directory entry from the directory's raw data. This internal function is used + * to read a raw ext2 directory entry into memory. The shandle's position pointer is adjusted + * to point to the next entry. + */ + +static fsw_status_t fsw_ext2_read_dentry(struct fsw_shandle *shand, struct ext2_dir_entry *entry) +{ + fsw_status_t status; + fsw_u32 buffer_size; + + while (1) { + // read dir_entry header (fixed length) + buffer_size = 8; + status = fsw_shandle_read(shand, &buffer_size, entry); + if (status) + return status; + + if (buffer_size < 8 || entry->rec_len == 0) { + // end of directory reached + entry->inode = 0; + return FSW_SUCCESS; + } + if (entry->rec_len < 8) + return FSW_VOLUME_CORRUPTED; + if (entry->inode != 0) { + // this entry is used + if (entry->rec_len < 8 + entry->name_len) + return FSW_VOLUME_CORRUPTED; + break; + } + + // valid, but unused entry, skip it + shand->pos += entry->rec_len - 8; + } + + // read file name (variable length) + buffer_size = entry->name_len; + status = fsw_shandle_read(shand, &buffer_size, entry->name); + if (status) + return status; + if (buffer_size < entry->name_len) + return FSW_VOLUME_CORRUPTED; + + // skip any remaining padding + shand->pos += entry->rec_len - (8 + entry->name_len); + + return FSW_SUCCESS; +} + +/** + * Get the target path of a symbolic link. This function is called when a symbolic + * link needs to be resolved. The core makes sure that the fsw_ext2_dnode_fill has been + * called on the dnode and that it really is a symlink. + * + * For ext2, the target path can be stored inline in the inode structure (in the space + * otherwise occupied by the block pointers) or in the inode's data. There is no flag + * indicating this, only the number of blocks entry (i_blocks) can be used as an + * indication. The check used here comes from the Linux kernel. + */ + +static fsw_status_t fsw_ext2_readlink(struct fsw_ext2_volume *vol, struct fsw_ext2_dnode *dno, + struct fsw_string *link_target) +{ + fsw_status_t status; + int ea_blocks; + struct fsw_string s; + + if (dno->g.size > FSW_PATH_MAX) + return FSW_VOLUME_CORRUPTED; + + ea_blocks = dno->raw->i_file_acl ? (vol->g.log_blocksize >> 9) : 0; + + if (dno->raw->i_blocks - ea_blocks == 0) { + // "fast" symlink, path is stored inside the inode + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = (int)dno->g.size; + s.data = dno->raw->i_block; + status = fsw_strdup_coerce(link_target, vol->g.host_string_type, &s); + } else { + // "slow" symlink, path is stored in normal inode data + status = fsw_dnode_readlink_data(dno, link_target); + } + + return status; +} + +// EOF diff --git a/filesystems/fsw_ext2.h b/filesystems/fsw_ext2.h new file mode 100644 index 0000000..13b4caa --- /dev/null +++ b/filesystems/fsw_ext2.h @@ -0,0 +1,65 @@ +/** + * \file fsw_ext2.h + * ext2 file system driver header. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _FSW_EXT2_H_ +#define _FSW_EXT2_H_ + +#define VOLSTRUCTNAME fsw_ext2_volume +#define DNODESTRUCTNAME fsw_ext2_dnode +#include "fsw_core.h" + +#include "fsw_ext2_disk.h" + + +//! Block size to be used when reading the ext2 superblock. +#define EXT2_SUPERBLOCK_BLOCKSIZE 1024 +//! Block number where the (master copy of the) ext2 superblock resides. +#define EXT2_SUPERBLOCK_BLOCKNO 1 + + +/** + * ext2: Volume structure with ext2-specific data. + */ + +struct fsw_ext2_volume { + struct fsw_volume g; //!< Generic volume structure + + struct ext2_super_block *sb; //!< Full raw ext2 superblock structure + fsw_u32 *inotab_bno; //!< Block numbers of the inode tables + fsw_u32 ind_bcnt; //!< Number of blocks addressable through an indirect block + fsw_u32 dind_bcnt; //!< Number of blocks addressable through a double-indirect block + fsw_u32 inode_size; //!< Size of inode structure in bytes +}; + +/** + * ext2: Dnode structure with ext2-specific data. + */ + +struct fsw_ext2_dnode { + struct fsw_dnode g; //!< Generic dnode structure + + struct ext2_inode *raw; //!< Full raw inode structure +}; + + +#endif diff --git a/filesystems/fsw_ext2_disk.h b/filesystems/fsw_ext2_disk.h new file mode 100644 index 0000000..759c9bc --- /dev/null +++ b/filesystems/fsw_ext2_disk.h @@ -0,0 +1,367 @@ +/** + * \file fsw_ext2_disk.h + * ext2 file system on-disk structures. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * Portions Copyright (c) 1991-2006 by various Linux kernel contributors + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _FSW_EXT2_DISK_H_ +#define _FSW_EXT2_DISK_H_ + +// types + +typedef fsw_s8 __s8; +typedef fsw_u8 __u8; +typedef fsw_s16 __s16; +typedef fsw_u16 __u16; +typedef fsw_s32 __s32; +typedef fsw_u32 __u32; +typedef fsw_s64 __s64; +typedef fsw_u64 __u64; + +typedef __u16 __le16; +typedef __u32 __le32; +typedef __u64 __le64; + +// +// from Linux kernel, include/linux/ext2_fs.h +// + +/* + * Special inode numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_SIZE 1024 +#define EXT2_MAX_BLOCK_SIZE 4096 +#define EXT2_MIN_BLOCK_LOG_SIZE 10 +#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : \ + (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : \ + (s)->s_first_ino) + +/* + * Structure of a blocks group descriptor + */ +struct ext2_group_desc +{ + __le32 bg_block_bitmap; /* Blocks bitmap block */ + __le32 bg_inode_bitmap; /* Inodes bitmap block */ + __le32 bg_inode_table; /* Inodes table block */ + __le16 bg_free_blocks_count; /* Free blocks count */ + __le16 bg_free_inodes_count; /* Free inodes count */ + __le16 bg_used_dirs_count; /* Directories count */ + __le16 bg_pad; + __le32 bg_reserved[3]; +}; + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT2_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group) +#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) +#define EXT2_INODES_PER_GROUP(s) ((s)->s_inodes_per_group) + +/* + * Constants relative to the data blocks + */ +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x00000002 /* Undelete */ +#define EXT2_COMPR_FL 0x00000004 /* Compress file */ +#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMP_FL 0x00000400 /* Don't compress */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT2_IMAGIC_FL 0x00002000 /* AFS directory */ +#define EXT2_JOURNAL_DATA_FL 0x00004000 /* Reserved for ext3 */ +#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT2_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ +#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x000380FF /* User modifiable flags */ + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + __le16 i_mode; /* 0: File mode */ + __le16 i_uid; /* 2: Low 16 bits of Owner Uid */ + __le32 i_size; /* 4: Size in bytes */ + __le32 i_atime; /* 8: Access time */ + __le32 i_ctime; /* 12: Creation time */ + __le32 i_mtime; /* 16: Modification time */ + __le32 i_dtime; /* 20: Deletion Time */ + __le16 i_gid; /* 24: Low 16 bits of Group Id */ + __le16 i_links_count; /* 26: Links count */ + __le32 i_blocks; /* 28: Blocks count */ + __le32 i_flags; /* 32: File flags */ + union { + struct { + __le32 l_i_reserved1; + } linux1; + struct { + __le32 h_i_translator; + } hurd1; + struct { + __le32 m_i_reserved1; + } masix1; + } osd1; /* 36: OS dependent 1 */ + __le32 i_block[EXT2_N_BLOCKS];/* 40: Pointers to blocks */ + __le32 i_generation; /* 100: File version (for NFS) */ + __le32 i_file_acl; /* 104: File ACL */ + __le32 i_dir_acl; /* 108: Directory ACL */ + __le32 i_faddr; /* 112: Fragment address */ + union { + struct { + __u8 l_i_frag; /* 116: Fragment number */ + __u8 l_i_fsize; /* 117: Fragment size */ + __u16 i_pad1; + __le16 l_i_uid_high; /* 120: these 2 fields */ + __le16 l_i_gid_high; /* 122: were reserved2[0] */ + __u32 l_i_reserved2; + } linux2; + struct { + __u8 h_i_frag; /* Fragment number */ + __u8 h_i_fsize; /* Fragment size */ + __le16 h_i_mode_high; + __le16 h_i_uid_high; + __le16 h_i_gid_high; + __le32 h_i_author; + } hurd2; + struct { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ +}; + +#define i_size_high i_dir_acl + +/* + * Structure of the super block + */ +struct ext2_super_block { + __le32 s_inodes_count; /* Inodes count */ + __le32 s_blocks_count; /* Blocks count */ + __le32 s_r_blocks_count; /* Reserved blocks count */ + __le32 s_free_blocks_count; /* Free blocks count */ + __le32 s_free_inodes_count; /* Free inodes count */ + __le32 s_first_data_block; /* First Data Block */ + __le32 s_log_block_size; /* Block size */ + __le32 s_log_frag_size; /* Fragment size */ + __le32 s_blocks_per_group; /* # Blocks per group */ + __le32 s_frags_per_group; /* # Fragments per group */ + __le32 s_inodes_per_group; /* # Inodes per group */ + __le32 s_mtime; /* Mount time */ + __le32 s_wtime; /* Write time */ + __le16 s_mnt_count; /* Mount count */ + __le16 s_max_mnt_count; /* Maximal mount count */ + __le16 s_magic; /* Magic signature */ + __le16 s_state; /* File system state */ + __le16 s_errors; /* Behaviour when detecting errors */ + __le16 s_minor_rev_level; /* minor revision level */ + __le32 s_lastcheck; /* time of last check */ + __le32 s_checkinterval; /* max. time between checks */ + __le32 s_creator_os; /* OS */ + __le32 s_rev_level; /* Revision level */ + __le16 s_def_resuid; /* Default uid for reserved blocks */ + __le16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __le32 s_first_ino; /* First non-reserved inode */ + __le16 s_inode_size; /* size of inode structure */ + __le16 s_block_group_nr; /* block group # of this superblock */ + __le32 s_feature_compat; /* compatible feature set */ + __le32 s_feature_incompat; /* incompatible feature set */ + __le32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __le32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_padding1; + /* + * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set. + */ + __u8 s_journal_uuid[16]; /* uuid of journal superblock */ + __u32 s_journal_inum; /* inode number of journal file */ + __u32 s_journal_dev; /* device number of journal file */ + __u32 s_last_orphan; /* start of list of inodes to delete */ + __u32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_reserved_char_pad; + __u16 s_reserved_word_pad; + __le32 s_default_mount_opts; + __le32 s_first_meta_bg; /* First metablock block group */ + __u32 s_reserved[190]; /* Padding to the end of the block */ +}; + +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Feature set definitions + */ + +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask) ) +#define EXT2_SET_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_compat |= cpu_to_le32(mask) +#define EXT2_SET_RO_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_ro_compat |= cpu_to_le32(mask) +#define EXT2_SET_INCOMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_incompat |= cpu_to_le32(mask) +#define EXT2_CLEAR_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_compat &= ~cpu_to_le32(mask) +#define EXT2_CLEAR_RO_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_ro_compat &= ~cpu_to_le32(mask) +#define EXT2_CLEAR_INCOMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_incompat &= ~cpu_to_le32(mask) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010 +#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 +#define EXT2_FEATURE_COMPAT_ANY 0xffffffff + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#define EXT2_FEATURE_RO_COMPAT_ANY 0xffffffff + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 +#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff + +/* +#define EXT2_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ + EXT2_FEATURE_INCOMPAT_META_BG) +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP +#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP +*/ + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + __le32 inode; /* Inode number */ + __le16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT2_NAME_LEN]; /* File name */ +}; +// NOTE: The original Linux kernel header defines ext2_dir_entry with the original +// layout and ext2_dir_entry_2 with the revised layout. We simply use the revised one. + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +enum { + EXT2_FT_UNKNOWN, + EXT2_FT_REG_FILE, + EXT2_FT_DIR, + EXT2_FT_CHRDEV, + EXT2_FT_BLKDEV, + EXT2_FT_FIFO, + EXT2_FT_SOCK, + EXT2_FT_SYMLINK, + EXT2_FT_MAX +}; + + +#endif diff --git a/filesystems/fsw_hfs.c b/filesystems/fsw_hfs.c new file mode 100644 index 0000000..59823c6 --- /dev/null +++ b/filesystems/fsw_hfs.c @@ -0,0 +1,1320 @@ +/* $Id: fsw_hfs.c 33540 2010-10-28 09:27:05Z vboxsync $ */ +/** @file + * fsw_hfs.c - HFS file system driver code, see + * + * http://developer.apple.com/technotes/tn/tn1150.html + * + * Current limitations: + * - Doesn't support permissions + * - Complete Unicode case-insensitiveness disabled (large tables) + * - No links + * - Only supports pure HFS+ (i.e. no HFS, or HFS+ embedded to HFS) + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include "fsw_hfs.h" + +#ifdef HOST_POSIX +#define DPRINT(x) printf(x) +#define DPRINT2(x,y) printf(x,y) +#define BP(msg) do { printf("ERROR: %s", msg); asm("int3"); } while (0) +#else +#define CONCAT(x,y) x##y +#define DPRINT(x) Print(CONCAT(L,x)) +#define DPRINT2(x,y) Print(CONCAT(L,x), y) +#define BP(msg) DPRINT(msg) +#endif + +// functions +#if 0 +void dump_str(fsw_u16* p, fsw_u32 len, int swap) +{ + int i; + + for (i=0; ig.vol, dno, &extent); + if (status) + return status; + + phys_bno = extent.phys_start; + //Slice - increase cache level from 0 to 3 + status = fsw_block_get(dno->g.vol, phys_bno, 3, (void **)&buffer); + if (status) + return status; + + fsw_memcpy(buf, buffer + off, len); + + fsw_block_release(dno->g.vol, phys_bno, buffer); + + return FSW_SUCCESS; + +} + +/* Read data from HFS file. */ +static fsw_s32 +fsw_hfs_read_file (struct fsw_hfs_dnode * dno, + fsw_u64 pos, + fsw_s32 len, + fsw_u8 * buf) +{ + + fsw_status_t status; + fsw_u32 log_bno; + fsw_u32 block_size_bits = dno->g.vol->block_size_shift; + fsw_u32 block_size = (1 << block_size_bits); + fsw_u32 block_size_mask = block_size - 1; + fsw_s32 read = 0; + + while (len > 0) + { + fsw_u32 off = (fsw_u32)(pos & block_size_mask); + fsw_s32 next_len = len; + + log_bno = (fsw_u32)RShiftU64(pos, block_size_bits); + + if ( next_len >= 0 + && (fsw_u32)next_len > block_size) + next_len = block_size; + status = fsw_hfs_read_block(dno, log_bno, off, next_len, buf); + if (status) + return -1; + buf += next_len; + pos += next_len; + len -= next_len; + read += next_len; + } + + return read; +} + + +static fsw_s32 +fsw_hfs_compute_shift(fsw_u32 size) +{ + fsw_s32 i; + + for (i=0; i<32; i++) + { + if ((size >> i) == 0) + return i - 1; + } + +// BP("BUG\n"); + return 0; +} + +/** + * Mount an HFS+ volume. Reads the superblock and constructs the + * root directory dnode. + */ +//algo from Chameleon +/* +void +HFSGetDescription(CICell ih, char *str, long strMaxLen) +{ + + UInt16 nodeSize; + UInt32 firstLeafNode; + long long dirIndex; + char *name; + long flags, time; + + if (HFSInitPartition(ih) == -1) { return; } + + // Fill some crucial data structures by side effect. + dirIndex = 0; + HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0); + + // Now we can loook up the volume name node. + nodeSize = be16_to_cpu(gBTHeaders[kBTreeCatalog]->nodeSize); + firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode); + + dirIndex = (long long) firstLeafNode * nodeSize; + + GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0); + + strncpy(str, name, strMaxLen); + str[strMaxLen] = '\0'; +} +*/ + + +static fsw_status_t fsw_hfs_volume_mount(struct fsw_hfs_volume *vol) +{ + fsw_status_t status, rv; + void *buffer = NULL; + HFSPlusVolumeHeader *voldesc; + fsw_u32 blockno; + struct fsw_string s; + HFSMasterDirectoryBlock* mdb; + UINTN i; + + rv = FSW_UNSUPPORTED; + + vol->primary_voldesc = NULL; + fsw_set_blocksize(vol, HFS_BLOCKSIZE, HFS_BLOCKSIZE); + blockno = HFS_SUPERBLOCK_BLOCKNO; + +#define CHECK(s) \ + if (status) { \ + rv = status; \ + break; \ + } + + vol->emb_block_off = 0; + vol->hfs_kind = 0; + do { + fsw_u16 signature; + BTHeaderRec tree_header; + fsw_s32 r; + fsw_u32 block_size; + + status = fsw_block_get(vol, blockno, 0, &buffer); + CHECK(status); + voldesc = (HFSPlusVolumeHeader *)buffer; + mdb = (HFSMasterDirectoryBlock*)buffer; + signature = be16_to_cpu(voldesc->signature); + + if ((signature == kHFSPlusSigWord) || (signature == kHFSXSigWord)) //H+ or HX + { + if (vol->hfs_kind == 0) + { + // DPRINT("found HFS+\n"); + vol->hfs_kind = FSW_HFS_PLUS; + } + } + else if (signature == kHFSSigWord) // 'BD' + { +// HFSMasterDirectoryBlock* mdb = (HFSMasterDirectoryBlock*)buffer; +//VolumeName = mdb->drVN 28bytes + if (be16_to_cpu(mdb->drEmbedSigWord) == kHFSPlusSigWord) + { + DPRINT("found HFS+ inside HFS, untested\n"); + vol->hfs_kind = FSW_HFS_PLUS_EMB; + vol->emb_block_off = be32_to_cpu(mdb->drEmbedExtent.startBlock); + blockno += vol->emb_block_off; + /* retry */ + continue; + } + else + { + DPRINT("found plain HFS, unsupported\n"); + vol->hfs_kind = FSW_HFS_PLAIN; + } + rv = FSW_UNSUPPORTED; + break; + } + else + { + rv = FSW_UNSUPPORTED; + break; + } + + status = fsw_memdup((void **)&vol->primary_voldesc, voldesc, + sizeof(*voldesc)); + CHECK(status); + + + block_size = be32_to_cpu(voldesc->blockSize); + vol->block_size_shift = fsw_hfs_compute_shift(block_size); + + fsw_block_release(vol, blockno, buffer); + buffer = NULL; + voldesc = NULL; + fsw_set_blocksize(vol, block_size, block_size); + + /* get volume name */ + for (i = kHFSMaxVolumeNameChars; i > 0; i--) + if (mdb->drVN[i-1] != ' ') + break; + + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = 0; + s.data = NULL; //&mdb->drVN; //"HFS+ volume"; + + //fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src) + status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); + CHECK(status); + + /* Setup catalog dnode */ + status = fsw_dnode_create_root(vol, kHFSCatalogFileID, &vol->catalog_tree.file); + CHECK(status); + fsw_memcpy (vol->catalog_tree.file->extents, + vol->primary_voldesc->catalogFile.extents, + sizeof vol->catalog_tree.file->extents); + vol->catalog_tree.file->g.size = + be64_to_cpu(vol->primary_voldesc->catalogFile.logicalSize); + + /* Setup extents overflow file */ + status = fsw_dnode_create_root(vol, kHFSExtentsFileID, &vol->extents_tree.file); + fsw_memcpy (vol->extents_tree.file->extents, + vol->primary_voldesc->extentsFile.extents, + sizeof vol->extents_tree.file->extents); + vol->extents_tree.file->g.size = + be64_to_cpu(vol->primary_voldesc->extentsFile.logicalSize); + + /* Setup the root dnode */ + status = fsw_dnode_create_root(vol, kHFSRootFolderID, &vol->g.root); + CHECK(status); + + /* + * Read catalog file, we know that first record is in the first node, right after + * the node descriptor. + */ + r = fsw_hfs_read_file(vol->catalog_tree.file, + sizeof (BTNodeDescriptor), + sizeof (BTHeaderRec), (fsw_u8 *) &tree_header); + if (r <= 0) + { + status = FSW_VOLUME_CORRUPTED; + break; + } + vol->case_sensitive = + (signature == kHFSXSigWord) && + (tree_header.keyCompareType == kHFSBinaryCompare); + vol->catalog_tree.root_node = be32_to_cpu (tree_header.rootNode); + vol->catalog_tree.node_size = be16_to_cpu (tree_header.nodeSize); + + /* Read extents overflow file */ + r = fsw_hfs_read_file(vol->extents_tree.file, + sizeof (BTNodeDescriptor), + sizeof (BTHeaderRec), (fsw_u8 *) &tree_header); + if (r <= 0) + { + status = FSW_VOLUME_CORRUPTED; + break; + } + + vol->extents_tree.root_node = be32_to_cpu (tree_header.rootNode); + vol->extents_tree.node_size = be16_to_cpu (tree_header.nodeSize); + + rv = FSW_SUCCESS; + } while (0); + +#undef CHECK + + + if (buffer != NULL) + fsw_block_release(vol, blockno, buffer); + + return rv; +} +//Here is a method to obtain Volume label from Apple +//how to implement it? +/* +UInt16 nodeSize; +UInt32 firstLeafNode; +long long dirIndex; +char *name; +long flags, time; + char *nodeBuf, *testKey, *entry; + + +if (HFSInitPartition(ih) == -1) { return; } + +// Fill some crucial data structures by side effect. +dirIndex = 0; +HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0); + +// Now we can loook up the volume name node. +nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize); +firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode); + +dirIndex = (long long) firstLeafNode * nodeSize; + index = (long) (*dirIndex % nodeSize); == 0 + curNode = (long) (*dirIndex / nodeSize); == firstLeafNode + +//GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0); + // Read the BTree node and get the record for index. + ReadExtent(extent, extentSize, kHFSCatalogFileID, + (long long) curNode * nodeSize, nodeSize, nodeBuf, 1); + GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry); + + utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode, + SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length), + (u_int8_t *)gTempStr, 256, OSBigEndian); + + *name = gTempStr; + +strncpy(str, name, strMaxLen); +str[strMaxLen] = '\0'; +*/ + +/** + * Free the volume data structure. Called by the core after an unmount or after + * an unsuccessful mount to release the memory used by the file system type specific + * part of the volume structure. + */ + +static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol) +{ + if (vol->primary_voldesc) + { + fsw_free(vol->primary_voldesc); + vol->primary_voldesc = NULL; + } +} + +/** + * Get in-depth information on a volume. + */ + +static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb) +{ + sb->total_bytes = be32_to_cpu(vol->primary_voldesc->totalBlocks) << vol->block_size_shift; + sb->free_bytes = be32_to_cpu(vol->primary_voldesc->freeBlocks) << vol->block_size_shift; + return FSW_SUCCESS; +} + +/** + * Get full information on a dnode from disk. This function is called by the core + * whenever it needs to access fields in the dnode structure that may not + * be filled immediately upon creation of the dnode. + */ + +static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno) +{ + return FSW_SUCCESS; +} + +/** + * Free the dnode data structure. Called by the core when deallocating a dnode + * structure to release the memory used by the file system type specific part + * of the dnode structure. + */ + +static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno) +{ +} + +static fsw_u32 mac_to_posix(fsw_u32 mac_time) +{ + /* Mac time is 1904 year based */ + return mac_time ? mac_time - 2082844800 : 0; +} + +/** + * Get in-depth information on a dnode. The core makes sure that fsw_hfs_dnode_fill + * has been called on the dnode before this function is called. Note that some + * data is not directly stored into the structure, but passed to a host-specific + * callback that converts it to the host-specific format. + */ + +static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol, + struct fsw_hfs_dnode *dno, + struct fsw_dnode_stat *sb) +{ + sb->used_bytes = dno->used_bytes; + sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, mac_to_posix(dno->ctime)); + sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, mac_to_posix(dno->mtime)); + sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, 0); + sb->store_attr_posix(sb, 0700); + + return FSW_SUCCESS; +} + +static int +fsw_hfs_find_block(HFSPlusExtentRecord * exts, + fsw_u32 * lbno, + fsw_u32 * pbno) +{ + int i; + fsw_u32 cur_lbno = *lbno; + + for (i = 0; i < 8; i++) + { + fsw_u32 start = be32_to_cpu ((*exts)[i].startBlock); + fsw_u32 count = be32_to_cpu ((*exts)[i].blockCount); + + if (cur_lbno < count) + { + *pbno = start + cur_lbno; + return 1; + } + + cur_lbno -= count; + } + + *lbno = cur_lbno; + + return 0; +} + +/* Find record offset, numbering starts from the end */ +static fsw_u32 +fsw_hfs_btree_recoffset (struct fsw_hfs_btree * btree, + BTNodeDescriptor * node, + fsw_u32 index) +{ + fsw_u8 *cnode = (fsw_u8 *) node; + fsw_u16 *recptr; + recptr = (fsw_u16 *) (cnode+btree->node_size - index * 2 - 2); + return be16_to_cpu(*recptr); +} + +/* Pointer to the key inside node */ +static BTreeKey * +fsw_hfs_btree_rec (struct fsw_hfs_btree * btree, + BTNodeDescriptor * node, + fsw_u32 index) +{ + fsw_u8 *cnode = (fsw_u8 *) node; + fsw_u32 offset; + offset = fsw_hfs_btree_recoffset (btree, node, index); + return (BTreeKey *) (cnode + offset); +} + + +static fsw_status_t +fsw_hfs_btree_search (struct fsw_hfs_btree * btree, + BTreeKey * key, + int (*compare_keys) (BTreeKey* key1, BTreeKey* key2), + BTNodeDescriptor ** result, + fsw_u32 * key_offset) +{ + BTNodeDescriptor* node; + fsw_u32 currnode; + fsw_u32 rec; + fsw_status_t status; + fsw_u8* buffer = NULL; + + currnode = btree->root_node; + status = fsw_alloc(btree->node_size, &buffer); + if (status) + return status; + node = (BTNodeDescriptor*)buffer; + + while (1) + { + int cmp = 0; + int match; + fsw_u32 count; + + readnode: + match = 0; + /* Read a node. */ + if (fsw_hfs_read_file (btree->file, + (fsw_u64)currnode * btree->node_size, + btree->node_size, buffer) <= 0) + { + status = FSW_VOLUME_CORRUPTED; + break; + } + + if (be16_to_cpu(*(fsw_u16*)(buffer + btree->node_size - 2)) != sizeof(BTNodeDescriptor)) + BP("corrupted node\n"); + + count = be16_to_cpu (node->numRecords); + +#if 1 + for (rec = 0; rec < count; rec++) + { + BTreeKey *currkey; + + currkey = fsw_hfs_btree_rec (btree, node, rec); + cmp = compare_keys (currkey, key); + //fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind); + + /* Leaf node. */ + if (node->kind == kBTLeafNode) + { + if (cmp == 0) + { + /* Found! */ + *result = node; + *key_offset = rec; + + status = FSW_SUCCESS; + goto done; + } + } + else if (node->kind == kBTIndexNode) + { + fsw_u32 *pointer; + + if (cmp > 0) + break; + + pointer = (fsw_u32 *) ((char *) currkey + + be16_to_cpu (currkey->length16) + + 2); + currnode = be32_to_cpu (*pointer); + match = 1; + } + } + + if (node->kind == kBTLeafNode && cmp < 0 && node->fLink) + { + currnode = be32_to_cpu(node->fLink); + goto readnode; + } + else if (!match) + { + status = FSW_NOT_FOUND; + break; + } +#else + /* Perform binary search */ + fsw_u32 lower = 0; + fsw_u32 upper = count - 1; + fsw_s32 cmp = -1; + BTreeKey *currkey = NULL; + + if (count == 0) + { + status = FSW_NOT_FOUND; + goto done; + } + + while (lower <= upper) + { + fsw_u32 index = (lower + upper) / 2; + + currkey = fsw_hfs_btree_rec (btree, node, index); + + cmp = compare_keys (currkey, key); + if (cmp < 0) upper = index - 1; + if (cmp > 0) lower = index + 1; + if (cmp == 0) + { + /* Found! */ + *result = node; + *key_offset = rec; + + status = FSW_SUCCESS; + goto done; + } + } + + if (cmp < 0) + currkey = fsw_hfs_btree_rec (btree, node, upper); + + if (node->kind == kBTIndexNode && currkey) + { + fsw_u32 *pointer; + + pointer = (fsw_u32 *) ((char *) currkey + + be16_to_cpu (currkey->length16) + + 2); + currnode = be32_to_cpu (*pointer); + } + else + { + status = FSW_NOT_FOUND; + break; + } +#endif + } + + + done: + if (buffer != NULL && status != FSW_SUCCESS) + fsw_free(buffer); + + return status; +} +typedef struct +{ + fsw_u32 id; + fsw_u32 type; + struct fsw_string * name; + fsw_u64 size; + fsw_u64 used; + fsw_u32 ctime; + fsw_u32 mtime; + HFSPlusExtentRecord extents; +} file_info_t; + +typedef struct +{ + fsw_u32 cur_pos; /* current position */ + fsw_u32 parent; + struct fsw_hfs_volume * vol; + + struct fsw_shandle * shandle; /* this one track iterator's state */ + file_info_t file_info; +} visitor_parameter_t; + +static int +fsw_hfs_btree_visit_node(BTreeKey *record, void* param) +{ + visitor_parameter_t* vp = (visitor_parameter_t*)param; + fsw_u8* base = (fsw_u8*)record->rawData + be16_to_cpu(record->length16) + 2; + fsw_u16 rec_type = be16_to_cpu(*(fsw_u16*)base); + struct HFSPlusCatalogKey* cat_key = (HFSPlusCatalogKey*)record; + fsw_u16 name_len; + fsw_u16 *name_ptr; + fsw_u32 i; + struct fsw_string * file_name; + + if (be32_to_cpu(cat_key->parentID) != vp->parent) + return -1; + + /* not smth we care about */ + if (vp->shandle->pos != vp->cur_pos++) + return 0; + + switch (rec_type) + { + case kHFSPlusFolderRecord: + { + HFSPlusCatalogFolder* folder_info = (HFSPlusCatalogFolder*)base; + + vp->file_info.id = be32_to_cpu(folder_info->folderID); + vp->file_info.type = FSW_DNODE_TYPE_DIR; + vp->file_info.size = be32_to_cpu(folder_info->valence); + vp->file_info.used = be32_to_cpu(folder_info->valence); + vp->file_info.ctime = be32_to_cpu(folder_info->createDate); + vp->file_info.mtime = be32_to_cpu(folder_info->contentModDate); + break; + } + case kHFSPlusFileRecord: + { + HFSPlusCatalogFile* file_info = (HFSPlusCatalogFile*)base; + + vp->file_info.id = be32_to_cpu(file_info->fileID); + vp->file_info.type = FSW_DNODE_TYPE_FILE; + vp->file_info.size = be64_to_cpu(file_info->dataFork.logicalSize); + vp->file_info.used = LShiftU64(be32_to_cpu(file_info->dataFork.totalBlocks), + vp->vol->block_size_shift); + vp->file_info.ctime = be32_to_cpu(file_info->createDate); + vp->file_info.mtime = be32_to_cpu(file_info->contentModDate); + fsw_memcpy(&vp->file_info.extents, &file_info->dataFork.extents, + sizeof vp->file_info.extents); + break; + } + case kHFSPlusFolderThreadRecord: + case kHFSPlusFileThreadRecord: + { + vp->shandle->pos++; + return 0; + } + default: + BP("unknown file type\n"); + vp->file_info.type = FSW_DNODE_TYPE_UNKNOWN; + break; + } + + name_len = be16_to_cpu(cat_key->nodeName.length); + + file_name = vp->file_info.name; + file_name->len = name_len; + fsw_memdup(&file_name->data, &cat_key->nodeName.unicode[0], 2*name_len); + file_name->size = 2*name_len; + file_name->type = FSW_STRING_TYPE_UTF16; + name_ptr = (fsw_u16*)file_name->data; + for (i=0; ishandle->pos++; + + return 1; +} + +static fsw_status_t +fsw_hfs_btree_iterate_node (struct fsw_hfs_btree * btree, + BTNodeDescriptor * first_node, + fsw_u32 first_rec, + int (*callback) (BTreeKey *record, void* param), + void * param) +{ + fsw_status_t status; + /* We modify node, so make a copy */ + BTNodeDescriptor* node = first_node; + fsw_u8* buffer = NULL; + + status = fsw_alloc(btree->node_size, &buffer); + if (status) + return status; + + while (1) + { + fsw_u32 i; + fsw_u32 count = be16_to_cpu(node->numRecords); + fsw_u32 next_node; + + /* Iterate over all records in this node. */ + for (i = first_rec; i < count; i++) + { + int rv = callback(fsw_hfs_btree_rec (btree, node, i), param); + + switch (rv) + { + case 1: + status = FSW_SUCCESS; + goto done; + case -1: + status = FSW_NOT_FOUND; + goto done; + } + /* if callback returned 0 - continue */ + } + + next_node = be32_to_cpu(node->fLink); + + if (!next_node) + { + status = FSW_NOT_FOUND; + break; + } + + if (fsw_hfs_read_file (btree->file, + next_node * btree->node_size, + btree->node_size, buffer) <= 0) + { + status = FSW_VOLUME_CORRUPTED; + return 1; + } + + node = (BTNodeDescriptor*)buffer; + first_rec = 0; + } + done: + if (buffer) + fsw_free(buffer); + + return status; +} + +#if 0 +void deb(fsw_u16* p, int len, int swap) +{ + int i; + for (i=0; ifileID) - ekey2->fileID; + + if (result) + return result; + + result = ekey1->forkType - ekey2->forkType; + + if (result) + return result; + + result = be32_to_cpu(ekey1->startBlock) - ekey2->startBlock; + return result; +} + +static int +fsw_hfs_cmp_catkey (BTreeKey *key1, BTreeKey *key2) +{ + HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1; + HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2; + + int apos, bpos, lc; + fsw_u16 ac, bc; + fsw_u32 parentId1; + int key1Len; + fsw_u16 *p1; + fsw_u16 *p2; + + parentId1 = be32_to_cpu(ckey1->parentID); + + if (parentId1 > ckey2->parentID) + return 1; + if (parentId1 < ckey2->parentID) + return -1; + + p1 = &ckey1->nodeName.unicode[0]; + p2 = &ckey2->nodeName.unicode[0]; + key1Len = be16_to_cpu (ckey1->nodeName.length); + apos = bpos = 0; + + while(1) + { + /* get next valid character from ckey1 */ + for (lc = 0; lc == 0 && apos < key1Len; apos++) { + ac = be16_to_cpu(p1[apos]); + lc = ac; + }; + ac = (fsw_u16)lc; + + /* get next valid character from ckey2 */ + for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) { + bc = p2[bpos]; + lc = bc; + }; + bc = (fsw_u16)lc; + + if (ac != bc || (ac == 0 && bc == 0)) + return ac - bc; + } +} + +static int +fsw_hfs_cmpi_catkey (BTreeKey *key1, BTreeKey *key2) +{ + HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1; + HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2; + + int apos, bpos, lc; + fsw_u16 ac, bc; + fsw_u32 parentId1; + int key1Len; + fsw_u16 *p1; + fsw_u16 *p2; + + parentId1 = be32_to_cpu(ckey1->parentID); + + if (parentId1 > ckey2->parentID) + return 1; + if (parentId1 < ckey2->parentID) + return -1; + + key1Len = be16_to_cpu (ckey1->nodeName.length); + + if (key1Len == 0 && ckey2->nodeName.length == 0) + return 0; + + p1 = &ckey1->nodeName.unicode[0]; + p2 = &ckey2->nodeName.unicode[0]; + + apos = bpos = 0; + + while(1) + { + /* get next valid character from ckey1 */ + for (lc = 0; lc == 0 && apos < key1Len; apos++) { + ac = be16_to_cpu(p1[apos]); + lc = ac ? fsw_to_lower(ac) : 0; + }; + ac = (fsw_u16)lc; + + /* get next valid character from ckey2 */ + for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) { + bc = p2[bpos]; + lc = bc ? fsw_to_lower(bc) : 0; + }; + bc = (fsw_u16)lc; + + if (ac != bc || (ac == 0 && bc == 0)) + return ac - bc; + } +} + +/** + * Retrieve file data mapping information. This function is called by the core when + * fsw_shandle_read needs to know where on the disk the required piece of the file's + * data can be found. The core makes sure that fsw_hfs_dnode_fill has been called + * on the dnode before. Our task here is to get the physical disk block number for + * the requested logical block number. + */ + +static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume * vol, + struct fsw_hfs_dnode * dno, + struct fsw_extent * extent) +{ + fsw_status_t status; + fsw_u32 lbno; + HFSPlusExtentRecord *exts; + BTNodeDescriptor *node = NULL; + + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + extent->log_count = 1; + lbno = extent->log_start; + + /* we only care about data forks atm, do we? */ + exts = &dno->extents; + + while (1) + { + struct HFSPlusExtentKey* key; + struct HFSPlusExtentKey overflowkey; + fsw_u32 ptr; + fsw_u32 phys_bno; + + if (fsw_hfs_find_block(exts, &lbno, &phys_bno)) + { + extent->phys_start = phys_bno + vol->emb_block_off; + status = FSW_SUCCESS; + break; + } + + + /* Find appropriate overflow record */ + overflowkey.fileID = dno->g.dnode_id; + overflowkey.startBlock = extent->log_start - lbno; + + if (node != NULL) + { + fsw_free(node); + node = NULL; + } + + status = fsw_hfs_btree_search (&vol->extents_tree, + (BTreeKey*)&overflowkey, + fsw_hfs_cmp_extkey, + &node, &ptr); + if (status) + break; + + key = (struct HFSPlusExtentKey *) + fsw_hfs_btree_rec (&vol->extents_tree, node, ptr); + exts = (HFSPlusExtentRecord*) (key + 1); + } + + if (node != NULL) + fsw_free(node); + + return status; +} + +static const fsw_u16* g_blacklist[] = +{ + //L"AppleIntelCPUPowerManagement.kext", + NULL +}; + + +//#define HFS_FILE_INJECTION + +#ifdef HFS_FILE_INJECTION +static struct +{ + const fsw_u16* path; + const fsw_u16* name; +} g_injectList[] = +{ + { + L"/System/Library/Extensions", + L"ApplePS2Controller.kext" + }, + { + NULL, + NULL + } +}; +#endif + +static fsw_status_t +create_hfs_dnode(struct fsw_hfs_dnode * dno, + file_info_t * file_info, + struct fsw_hfs_dnode ** child_dno_out) +{ + fsw_status_t status; + struct fsw_hfs_dnode * baby; + + status = fsw_dnode_create(dno, file_info->id, file_info->type, + file_info->name, &baby); + if (status) + return status; + + baby->g.size = file_info->size; + baby->used_bytes = file_info->used; + baby->ctime = file_info->ctime; + baby->mtime = file_info->mtime; + + + /* Fill-in extents info */ + if (file_info->type == FSW_DNODE_TYPE_FILE) + { + fsw_memcpy(baby->extents, &file_info->extents, sizeof file_info->extents); + } + + *child_dno_out = baby; + + return FSW_SUCCESS; +} + + +/** + * Lookup a directory's child dnode by name. This function is called on a directory + * to retrieve the directory entry with the given name. A dnode is constructed for + * this entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called + * and the dnode is actually a directory. + */ + +static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume * vol, + struct fsw_hfs_dnode * dno, + struct fsw_string * lookup_name, + struct fsw_hfs_dnode ** child_dno_out) +{ + fsw_status_t status; + struct HFSPlusCatalogKey catkey; + fsw_u32 ptr; + fsw_u16 rec_type; + BTNodeDescriptor * node = NULL; + struct fsw_string rec_name; + int free_data = 0, i; + HFSPlusCatalogKey* file_key; + file_info_t file_info; + fsw_u8* base; + + + fsw_memzero(&file_info, sizeof file_info); + file_info.name = &rec_name; + + catkey.parentID = dno->g.dnode_id; + catkey.nodeName.length = (fsw_u16)lookup_name->len; + + /* no need to allocate anything */ + if (lookup_name->type == FSW_STRING_TYPE_UTF16) + { + fsw_memcpy(catkey.nodeName.unicode, lookup_name->data, lookup_name->size); + rec_name = *lookup_name; + } else + { + status = fsw_strdup_coerce(&rec_name, FSW_STRING_TYPE_UTF16, lookup_name); + /* nothing allocated so far */ + if (status) + goto done; + free_data = 1; + fsw_memcpy(catkey.nodeName.unicode, rec_name.data, rec_name.size); + } + + /* Dirty hack: blacklisting of certain files on FS driver level */ + for (i = 0; g_blacklist[i]; i++) + { + if (fsw_memeq(g_blacklist[i], catkey.nodeName.unicode, catkey.nodeName.length*2)) + { + DPRINT2("Blacklisted %s\n", g_blacklist[i]); + status = FSW_NOT_FOUND; + goto done; + } + } + +#ifdef HFS_FILE_INJECTION + if (fsw_hfs_inject(vol, + dno, + catkey.nodeName.unicode, + catkey.nodeName.length, + &file_info)) + { + status = FSW_SUCCESS; + goto create; + } +#endif + + catkey.keyLength = (fsw_u16)(5 + rec_name.size); + + status = fsw_hfs_btree_search (&vol->catalog_tree, + (BTreeKey*)&catkey, + vol->case_sensitive ? + fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey, + &node, &ptr); + if (status) + goto done; + + file_key = (HFSPlusCatalogKey *)fsw_hfs_btree_rec (&vol->catalog_tree, node, ptr); + /* for plain HFS "-(keySize & 1)" would be needed */ + base = (fsw_u8*)file_key + be16_to_cpu(file_key->keyLength) + 2; + rec_type = be16_to_cpu(*(fsw_u16*)base); + + /** @todo: read additional info */ + switch (rec_type) + { + case kHFSPlusFolderRecord: + { + HFSPlusCatalogFolder* info = (HFSPlusCatalogFolder*)base; + + file_info.id = be32_to_cpu(info->folderID); + file_info.type = FSW_DNODE_TYPE_DIR; + /* @todo: return number of elements, maybe use smth else */ + file_info.size = be32_to_cpu(info->valence); + file_info.used = be32_to_cpu(info->valence); + file_info.ctime = be32_to_cpu(info->createDate); + file_info.mtime = be32_to_cpu(info->contentModDate); + break; + } + case kHFSPlusFileRecord: + { + HFSPlusCatalogFile* info = (HFSPlusCatalogFile*)base; + + file_info.id = be32_to_cpu(info->fileID); + file_info.type = FSW_DNODE_TYPE_FILE; + file_info.size = be64_to_cpu(info->dataFork.logicalSize); + file_info.used = LShiftU64(be32_to_cpu(info->dataFork.totalBlocks), vol->block_size_shift); + file_info.ctime = be32_to_cpu(info->createDate); + file_info.mtime = be32_to_cpu(info->contentModDate); + fsw_memcpy(&file_info.extents, &info->dataFork.extents, + sizeof file_info.extents); + break; + } + default: + BP("unknown file type\n"); + file_info.type = FSW_DNODE_TYPE_UNKNOWN; + + break; + } +#ifdef HFS_FILE_INJECTION +create: +#endif + status = create_hfs_dnode(dno, &file_info, child_dno_out); + if (status) + goto done; + +done: + + if (node != NULL) + fsw_free(node); + + if (free_data) + fsw_strfree(&rec_name); + + return status; +} + +/** + * Get the next directory entry when reading a directory. This function is called during + * directory iteration to retrieve the next directory entry. A dnode is constructed for + * the entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called + * and the dnode is actually a directory. The shandle provided by the caller is used to + * record the position in the directory between calls. + */ + +static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol, + struct fsw_hfs_dnode *dno, + struct fsw_shandle *shand, + struct fsw_hfs_dnode **child_dno_out) +{ + fsw_status_t status; + struct HFSPlusCatalogKey catkey; + fsw_u32 ptr; + BTNodeDescriptor * node = NULL; + + visitor_parameter_t param; + struct fsw_string rec_name; + + catkey.parentID = dno->g.dnode_id; + catkey.nodeName.length = 0; + + fsw_memzero(¶m, sizeof(param)); + + rec_name.type = FSW_STRING_TYPE_EMPTY; + param.file_info.name = &rec_name; + + status = fsw_hfs_btree_search (&vol->catalog_tree, + (BTreeKey*)&catkey, + vol->case_sensitive ? + fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey, + &node, &ptr); + if (status) + goto done; + + /* Iterator updates shand state */ + param.vol = vol; + param.shandle = shand; + param.parent = dno->g.dnode_id; + param.cur_pos = 0; + status = fsw_hfs_btree_iterate_node (&vol->catalog_tree, + node, + ptr, + fsw_hfs_btree_visit_node, + ¶m); + if (status) + goto done; + + status = create_hfs_dnode(dno, ¶m.file_info, child_dno_out); + + if (status) + goto done; + + done: + fsw_strfree(&rec_name); + + return status; +} + +/** + * Get the target path of a symbolic link. This function is called when a symbolic + * link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been + * called on the dnode and that it really is a symlink. + * + */ +static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno, + struct fsw_string *link_target) +{ + return FSW_UNSUPPORTED; +} + +// EOF diff --git a/filesystems/fsw_hfs.h b/filesystems/fsw_hfs.h new file mode 100644 index 0000000..6d63643 --- /dev/null +++ b/filesystems/fsw_hfs.h @@ -0,0 +1,192 @@ +/* $Id: fsw_hfs.h 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_hfs.h - HFS file system driver header. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef _FSW_HFS_H_ +#define _FSW_HFS_H_ + +#define VOLSTRUCTNAME fsw_hfs_volume +#define DNODESTRUCTNAME fsw_hfs_dnode + +#include "fsw_core.h" + + +//! Block size for HFS volumes. +#define HFS_BLOCKSIZE 512 + +//! Block number where the HFS superblock resides. +#define HFS_SUPERBLOCK_BLOCKNO 2 + +/* Make world look Applish enough for the system header describing HFS layout */ +#define __APPLE_API_PRIVATE +#define __APPLE_API_UNSTABLE + +#define u_int8_t fsw_u8 +#define u_int16_t fsw_u16 +#define u_int32_t fsw_u32 +#define u_int64_t fsw_u64 +#define int8_t fsw_s8 +#define int16_t fsw_s16 +#define int32_t fsw_s32 +#define int64_t fsw_s64 + +#include "hfs_format.h" + +#undef u_int8_t +#undef u_int16_t +#undef u_int32_t +#undef u_int64_t +#undef int8_t +#undef int16_t +#undef int32_t +#undef int64_t + +#pragma pack(1) +#ifdef _MSC_VER +/* vasily: disable warning for non-standard anonymous struct/union + * declarations + */ +# pragma warning (disable:4201) +# define inline __inline +#endif + +struct hfs_dirrec { + fsw_u8 _dummy; +}; + +struct fsw_hfs_key +{ + union + { + struct HFSPlusExtentKey ext_key; + struct HFSPlusCatalogKey cat_key; + fsw_u16 key_len; /* Length is at the beginning of all keys */ + }; +}; + +#pragma pack() + +typedef enum { + /* Regular HFS */ + FSW_HFS_PLAIN = 0, + /* HFS+ */ + FSW_HFS_PLUS, + /* HFS+ embedded to HFS */ + FSW_HFS_PLUS_EMB +} fsw_hfs_kind; + +/** + * HFS: Dnode structure with HFS-specific data. + */ + +struct fsw_hfs_dnode +{ + struct fsw_dnode g; //!< Generic dnode structure + HFSPlusExtentRecord extents; + fsw_u32 ctime; + fsw_u32 mtime; + fsw_u64 used_bytes; +}; + +/** + * HFS: In-memory B-tree structure. + */ +struct fsw_hfs_btree +{ + fsw_u32 root_node; + fsw_u32 node_size; + struct fsw_hfs_dnode* file; +}; + + +/** + * HFS: In-memory volume structure with HFS-specific data. + */ + +struct fsw_hfs_volume +{ + struct fsw_volume g; //!< Generic volume structure + + struct HFSPlusVolumeHeader *primary_voldesc; //!< Volume Descriptor + struct fsw_hfs_btree catalog_tree; // Catalog tree + struct fsw_hfs_btree extents_tree; // Extents overflow tree + struct fsw_hfs_dnode root_file; + int case_sensitive; + fsw_u32 block_size_shift; + fsw_hfs_kind hfs_kind; + fsw_u32 emb_block_off; +}; + +/* Endianess swappers */ +static inline fsw_u16 +swab16(fsw_u16 x) +{ + return (x<<8 | ((x & 0xff00)>>8)); +} + +static inline fsw_u32 +swab32(fsw_u32 x) +{ + return x<<24 | x>>24 | + (x & (fsw_u32)0x0000ff00UL)<<8 | + (x & (fsw_u32)0x00ff0000UL)>>8; +} + + +static inline fsw_u64 +swab64(fsw_u64 x) +{ + return x<<56 | x>>56 | + (x & (fsw_u64)0x000000000000ff00ULL)<<40 | + (x & (fsw_u64)0x0000000000ff0000ULL)<<24 | + (x & (fsw_u64)0x00000000ff000000ULL)<< 8 | + (x & (fsw_u64)0x000000ff00000000ULL)>> 8 | + (x & (fsw_u64)0x0000ff0000000000ULL)>>24 | + (x & (fsw_u64)0x00ff000000000000ULL)>>40; +} + +static inline fsw_u16 +be16_to_cpu(fsw_u16 x) +{ + return swab16(x); +} + +static inline fsw_u16 +cpu_to_be16(fsw_u16 x) +{ + return swab16(x); +} + + +static inline fsw_u32 +cpu_to_be32(fsw_u32 x) +{ + return swab32(x); +} + +static inline fsw_u32 +be32_to_cpu(fsw_u32 x) +{ + return swab32(x); +} + +static inline fsw_u64 +be64_to_cpu(fsw_u64 x) +{ + return swab64(x); +} + +#endif diff --git a/filesystems/fsw_iso9660.c b/filesystems/fsw_iso9660.c new file mode 100644 index 0000000..271c924 --- /dev/null +++ b/filesystems/fsw_iso9660.c @@ -0,0 +1,704 @@ +/* $Id: fsw_iso9660.c 33540 2010-10-28 09:27:05Z vboxsync $ */ +/** @file + * fsw_iso9660.c - ISO9660 file system driver code. + * + * Current limitations: + * - Files must be in one extent (i.e. Level 2) + * - No Joliet or Rock Ridge extensions + * - No interleaving + * - inode number generation strategy fails on volumes > 2 GB + * - No blocksizes != 2048 + * - No High Sierra or anything else != 'CD001' + * - No volume sets with directories pointing at other volumes + * - No extended attribute records + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_iso9660.h" +//#include + +#ifndef DEBUG_ISO +#define DEBUG_ISO 1 +#endif + +#if DEBUG_ISO == 2 +#define DBG(x...) AsciiPrint(x) +#elif DEBUG_ISO == 1 +#define DBG(x...) BootLog(x) +#else +#define DBG(x...) +#endif + +//#define MsgLog(x...) if(msgCursor){AsciiSPrint(msgCursor, BOOTER_LOG_SIZE, x); while(*msgCursor){msgCursor++;}} + +// extern CHAR8 *msgCursor; +// extern MESSAGE_LOG_PROTOCOL *Msg; +// functions + +static fsw_status_t fsw_iso9660_volume_mount(struct fsw_iso9660_volume *vol); +static void fsw_iso9660_volume_free(struct fsw_iso9660_volume *vol); +static fsw_status_t fsw_iso9660_volume_stat(struct fsw_iso9660_volume *vol, struct fsw_volume_stat *sb); + +static fsw_status_t fsw_iso9660_dnode_fill(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno); +static void fsw_iso9660_dnode_free(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno); +static fsw_status_t fsw_iso9660_dnode_stat(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_dnode_stat *sb); +static fsw_status_t fsw_iso9660_get_extent(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_extent *extent); + +static fsw_status_t fsw_iso9660_dir_lookup(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_string *lookup_name, struct fsw_iso9660_dnode **child_dno); +static fsw_status_t fsw_iso9660_dir_read(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_shandle *shand, struct fsw_iso9660_dnode **child_dno); +static fsw_status_t fsw_iso9660_read_dirrec(struct fsw_iso9660_volume *vol, struct fsw_shandle *shand, struct iso9660_dirrec_buffer *dirrec_buffer); + +static fsw_status_t fsw_iso9660_readlink(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_string *link); + +static fsw_status_t rr_find_sp(struct iso9660_dirrec *dirrec, struct fsw_rock_ridge_susp_sp **psp); +static fsw_status_t rr_find_nm(struct fsw_iso9660_volume *vol, struct iso9660_dirrec *dirrec, int off, struct fsw_string *str); +static fsw_status_t rr_read_ce(struct fsw_iso9660_volume *vol, union fsw_rock_ridge_susp_ce *ce, fsw_u8 *begin); +//static void dump_dirrec(struct iso9660_dirrec *dirrec); +// +// Dispatch Table +// + +struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(iso9660) = { + { FSW_STRING_TYPE_ISO88591, 4, 4, "iso9660" }, + sizeof(struct fsw_iso9660_volume), + sizeof(struct fsw_iso9660_dnode), + + fsw_iso9660_volume_mount, + fsw_iso9660_volume_free, + fsw_iso9660_volume_stat, + fsw_iso9660_dnode_fill, + fsw_iso9660_dnode_free, + fsw_iso9660_dnode_stat, + fsw_iso9660_get_extent, + fsw_iso9660_dir_lookup, + fsw_iso9660_dir_read, + fsw_iso9660_readlink, +}; + +static fsw_status_t rr_find_sp(struct iso9660_dirrec *dirrec, struct fsw_rock_ridge_susp_sp **psp) +{ + fsw_u8 *r; + int off = 0; + struct fsw_rock_ridge_susp_sp *sp; + r = (fsw_u8 *)((fsw_u8 *)dirrec + sizeof(*dirrec) + dirrec->file_identifier_length); + off = (int)(r - (fsw_u8 *)dirrec); + while(off < dirrec->dirrec_length) + { + if (*r == 'S') + { + sp = (struct fsw_rock_ridge_susp_sp *)r; + if( sp->e.sig[0] == 'S' + && sp->e.sig[1] == 'P' + && sp->magic[0] == 0xbe + && sp->magic[1] == 0xef) + { + *psp = sp; + return FSW_SUCCESS; + } + } + r++; + off = (int)(r - (fsw_u8 *)dirrec); + } + *psp = NULL; + return FSW_NOT_FOUND; +} + +static fsw_status_t rr_find_nm(struct fsw_iso9660_volume *vol, struct iso9660_dirrec *dirrec, int off, struct fsw_string *str) +{ + fsw_u8 *r, *begin; + int fCe = 0; + struct fsw_rock_ridge_susp_nm *nm; + int limit = dirrec->dirrec_length; + begin = (fsw_u8 *)dirrec; + r = (fsw_u8 *)dirrec + off; + str->data = NULL; + str->len = 0; + str->size = 0; + str->type = 0; + while(off < limit) + { + if (r[0] == 'C' && r[1] == 'E' && r[2] == 28) + { + int rc; + int ce_off; + union fsw_rock_ridge_susp_ce *ce; + if (fCe == 0) + fsw_alloc_zero(ISO9660_BLOCKSIZE, (void *)&begin); + fCe = 1; + // DEBUG((DEBUG_WARN, "%a:%d we found CE before NM or its continuation\n", __FILE__, __LINE__)); + ce = (union fsw_rock_ridge_susp_ce *)r; + limit = ISOINT(ce->X.len); + ce_off = ISOINT(ce->X.offset); + rc = rr_read_ce(vol, ce, begin); + if (rc != FSW_SUCCESS) + { + fsw_free(begin); + return rc; + } + begin += ce_off; + r = begin; + } + if (r[0] == 'N' && r[1] == 'M') + { + nm = (struct fsw_rock_ridge_susp_nm *)r; + if( nm->e.sig[0] == 'N' + && nm->e.sig[1] == 'M') + { + int len = 0; + fsw_u8 *tmp = NULL; + if (nm->flags & RR_NM_CURR) + { + fsw_memdup(str->data, ".", 1); + str->len = 1; + goto done; + } + if (nm->flags & RR_NM_PARE) + { + fsw_memdup(str->data, "..", 2); + str->len = 2; + goto done; + } + len = nm->e.len - sizeof(struct fsw_rock_ridge_susp_nm) + 1; + fsw_alloc_zero(str->len + len, (void **)&tmp); + if (str->data != NULL) + { + fsw_memcpy(tmp, str->data, str->len); + fsw_free(str->data); + } + // DEBUG((DEBUG_INFO, "dst:%p src:%p len:%d\n", tmp + str->len, &nm->name[0], len)); + fsw_memcpy(tmp + str->len, &nm->name[0], len); + str->data = tmp; + str->len += len; + + if ((nm->flags & RR_NM_CONT) == 0); + goto done; + } + } + r++; + off = (int)(r - (fsw_u8 *)begin); + } + if(fCe == 1) + fsw_free(begin); + return FSW_NOT_FOUND; +done: + str->type = FSW_STRING_TYPE_ISO88591; + str->size = str->len; + if(fCe == 1) + fsw_free(begin); + return FSW_SUCCESS; +} + +static fsw_status_t rr_read_ce(struct fsw_iso9660_volume *vol, union fsw_rock_ridge_susp_ce *ce, fsw_u8 *begin) +{ + int rc; +// int i; +// fsw_u8 *r = begin + ISOINT(ce->X.offset); +// int len = ISOINT(ce->X.len); + rc = vol->g.host_table->read_block(&vol->g, ISOINT(ce->X.block_loc), begin); + if (rc != FSW_SUCCESS) + return rc; +/* for (i = 0; i < len; ++i) + { + DEBUG((DEBUG_INFO, "%d: (%d:%x)%c ", i, r[i], r[i], r[i])); + }*/ + return FSW_SUCCESS; +} +/* +static void dump_dirrec(struct iso9660_dirrec *dirrec) +{ + int i; + fsw_u8 *r = (fsw_u8 *)dirrec + dirrec->file_identifier_length; + int len = dirrec->dirrec_length; + for (i = dirrec->file_identifier_length; i < len; ++i) + { + DEBUG((DEBUG_INFO, "%d: (%d:%x)%c ", i, r[i], r[i], r[i])); + } +}*/ +/** + * Mount an ISO9660 volume. Reads the superblock and constructs the + * root directory dnode. + */ + +static fsw_status_t fsw_iso9660_volume_mount(struct fsw_iso9660_volume *vol) +{ + fsw_status_t status; + void *buffer; + fsw_u32 blockno; + struct iso9660_volume_descriptor *voldesc; + struct iso9660_primary_volume_descriptor *pvoldesc; + fsw_u32 voldesc_type; + int i; + struct fsw_string s; + struct iso9660_dirrec rootdir; + int sua_pos; + char *sig; + int skip; + struct fsw_rock_ridge_susp_entry *entry; + + // read through the Volume Descriptor Set + fsw_set_blocksize(vol, ISO9660_BLOCKSIZE, ISO9660_BLOCKSIZE); + blockno = ISO9660_SUPERBLOCK_BLOCKNO; + + do { +// DBG("iso9660: check blockno=%d\n", blockno); + status = fsw_block_get(vol, blockno, 0, &buffer); + if (status) + return status; + + voldesc = (struct iso9660_volume_descriptor *)buffer; + voldesc_type = voldesc->volume_descriptor_type; + if (fsw_memeq(voldesc->standard_identifier, "CD001", 5)) { + // descriptor follows ISO 9660 standard + if (voldesc_type == 1 && voldesc->volume_descriptor_version == 1) { + // suitable Primary Volume Descriptor found +// DBG("iso9660: suitable Primary Volume Descriptor found\n"); + if (vol->primary_voldesc) { + fsw_free(vol->primary_voldesc); + vol->primary_voldesc = NULL; + } + status = fsw_memdup((void **)&vol->primary_voldesc, voldesc, ISO9660_BLOCKSIZE); + } + } else if (!fsw_memeq(voldesc->standard_identifier, "CD", 2)) { + // completely alien standard identifier, stop reading + voldesc_type = 255; + } + + fsw_block_release(vol, blockno, buffer); + blockno++; + } while (!status && voldesc_type != 255); + if (status) + return status; + + // get information from Primary Volume Descriptor + if (vol->primary_voldesc == NULL) + return FSW_UNSUPPORTED; + pvoldesc = vol->primary_voldesc; + if (ISOINT(pvoldesc->logical_block_size) != 2048) + return FSW_UNSUPPORTED; + + // get volume name + for (i = 32; i > 0; i--) + if (pvoldesc->volume_identifier[i-1] != ' ') + break; + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = i; + s.data = pvoldesc->volume_identifier; + status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); + if (status) + return status; + + // setup the root dnode + status = fsw_dnode_create_root(vol, ISO9660_SUPERBLOCK_BLOCKNO << ISO9660_BLOCKSIZE_BITS, &vol->g.root); + if (status) + return status; + fsw_memcpy(&vol->g.root->dirrec, &pvoldesc->root_directory, sizeof(struct iso9660_dirrec)); + + if ( pvoldesc->escape[0] == 0x25 + && pvoldesc->escape[1] == 0x2f + && ( pvoldesc->escape[2] == 0x40 + || pvoldesc->escape[2] == 0x43 + || pvoldesc->escape[2] == 0x45)) + { + // FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: success (joliet!!!)\n"))); +// DBG("fsw_iso9660_volume_mount: success (joliet!!!)\n"); + vol->fJoliet = 1; + } + + + rootdir = pvoldesc->root_directory; + sua_pos = (sizeof(struct iso9660_dirrec)) + rootdir.file_identifier_length + (rootdir.file_identifier_length % 2) - 2; + //int sua_size = rootdir.dirrec_length - rootdir.file_identifier_length; + //FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: success (SUA(pos:%x, sz:%d)!!!)\n"), sua_pos, sua_size)); + +#if 1 + status = fsw_block_get(vol, ISOINT(rootdir.extent_location), 0, &buffer); + sig = (char *)buffer + sua_pos; + skip = 0; + entry = (struct fsw_rock_ridge_susp_entry *)sig; + if ( entry->sig[0] == 'S' + && entry->sig[1] == 'P') + { + struct fsw_rock_ridge_susp_sp *sp = (struct fsw_rock_ridge_susp_sp *)entry; + if (sp->magic[0] == 0xbe && sp->magic[1] == 0xef) + { + vol->fRockRidge = 1; + } else { + // FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: SP magic isn't valid\n"))); +// DBG("fsw_iso9660_volume_mount: SP magic isn't valid\n"); + } + skip = sp->skip; + } +#endif + // release volume descriptors + fsw_free(vol->primary_voldesc); + vol->primary_voldesc = NULL; + + +// FSW_MSG_DEBUG((FSW_MSGSTR("fsw_iso9660_volume_mount: success\n"))); +// DBG("fsw_iso9660_volume_mount: success\n"); + + return FSW_SUCCESS; +} + +/** + * Free the volume data structure. Called by the core after an unmount or after + * an unsuccessful mount to release the memory used by the file system type specific + * part of the volume structure. + */ + +static void fsw_iso9660_volume_free(struct fsw_iso9660_volume *vol) +{ + if (vol->primary_voldesc) + fsw_free(vol->primary_voldesc); +} + +/** + * Get in-depth information on a volume. + */ + +static fsw_status_t fsw_iso9660_volume_stat(struct fsw_iso9660_volume *vol, struct fsw_volume_stat *sb) +{ + sb->total_bytes = 0; //(fsw_u64)vol->sb->s_blocks_count * vol->g.log_blocksize; + sb->free_bytes = 0; + return FSW_SUCCESS; +} + +/** + * Get full information on a dnode from disk. This function is called by the core + * whenever it needs to access fields in the dnode structure that may not + * be filled immediately upon creation of the dnode. In the case of iso9660, we + * delay fetching of the inode structure until dnode_fill is called. The size and + * type fields are invalid until this function has been called. + */ + +static fsw_status_t fsw_iso9660_dnode_fill(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno) +{ + // get info from the directory record + dno->g.size = ISOINT(dno->dirrec.data_length); + if (dno->dirrec.file_flags & 0x02) + dno->g.type = FSW_DNODE_TYPE_DIR; + else + dno->g.type = FSW_DNODE_TYPE_FILE; + + return FSW_SUCCESS; +} + +/** + * Free the dnode data structure. Called by the core when deallocating a dnode + * structure to release the memory used by the file system type specific part + * of the dnode structure. + */ + +static void fsw_iso9660_dnode_free(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno) +{ +} + +/** + * Get in-depth information on a dnode. The core makes sure that fsw_iso9660_dnode_fill + * has been called on the dnode before this function is called. Note that some + * data is not directly stored into the structure, but passed to a host-specific + * callback that converts it to the host-specific format. + */ + +static fsw_status_t fsw_iso9660_dnode_stat(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_dnode_stat *sb) +{ + sb->used_bytes = (dno->g.size + (ISO9660_BLOCKSIZE-1)) & ~(ISO9660_BLOCKSIZE-1); + /* + sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, dno->raw->i_ctime); + sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, dno->raw->i_atime); + sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, dno->raw->i_mtime); + sb->store_attr_posix(sb, dno->raw->i_mode); + */ + + return FSW_SUCCESS; +} + +/** + * Retrieve file data mapping information. This function is called by the core when + * fsw_shandle_read needs to know where on the disk the required piece of the file's + * data can be found. The core makes sure that fsw_iso9660_dnode_fill has been called + * on the dnode before. Our task here is to get the physical disk block number for + * the requested logical block number. + */ + +static fsw_status_t fsw_iso9660_get_extent(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_extent *extent) +{ + // Preconditions: The caller has checked that the requested logical block + // is within the file's size. The dnode has complete information, i.e. + // fsw_iso9660_dnode_read_info was called successfully on it. + + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + extent->phys_start = ISOINT(dno->dirrec.extent_location); + extent->log_start = 0; + extent->log_count = (ISOINT(dno->dirrec.data_length) + (ISO9660_BLOCKSIZE-1)) >> ISO9660_BLOCKSIZE_BITS; + return FSW_SUCCESS; +} + +/** + * Lookup a directory's child dnode by name. This function is called on a directory + * to retrieve the directory entry with the given name. A dnode is constructed for + * this entry and returned. The core makes sure that fsw_iso9660_dnode_fill has been called + * and the dnode is actually a directory. + */ + +static fsw_status_t fsw_iso9660_dir_lookup(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_string *lookup_name, struct fsw_iso9660_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_shandle shand; + struct iso9660_dirrec_buffer dirrec_buffer; + struct iso9660_dirrec *dirrec = &dirrec_buffer.dirrec; + + // Preconditions: The caller has checked that dno is a directory node. + + // setup handle to read the directory + status = fsw_shandle_open(dno, &shand); + if (status) + return status; + + // scan the directory for the file + while (1) { + // read next entry + status = fsw_iso9660_read_dirrec(vol, &shand, &dirrec_buffer); + if (status) + goto errorexit; + if (dirrec->dirrec_length == 0) { + // end of directory reached + status = FSW_NOT_FOUND; + goto errorexit; + } + + // skip . and .. + if (dirrec->file_identifier_length == 1 && + (dirrec->file_identifier[0] == 0 || dirrec->file_identifier[0] == 1)) + continue; + + // compare name + if (fsw_streq(lookup_name, &dirrec_buffer.name)) // TODO: compare case-insensitively + break; + } + + // setup a dnode for the child item + status = fsw_dnode_create(dno, dirrec_buffer.ino, FSW_DNODE_TYPE_UNKNOWN, &dirrec_buffer.name, child_dno_out); + if (status == FSW_SUCCESS) + fsw_memcpy(&(*child_dno_out)->dirrec, dirrec, sizeof(struct iso9660_dirrec)); + +errorexit: + fsw_shandle_close(&shand); + return status; +} + +/** + * Get the next directory entry when reading a directory. This function is called during + * directory iteration to retrieve the next directory entry. A dnode is constructed for + * the entry and returned. The core makes sure that fsw_iso9660_dnode_fill has been called + * and the dnode is actually a directory. The shandle provided by the caller is used to + * record the position in the directory between calls. + */ + +static fsw_status_t fsw_iso9660_dir_read(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_shandle *shand, struct fsw_iso9660_dnode **child_dno_out) +{ + fsw_status_t status; + struct iso9660_dirrec_buffer dirrec_buffer; + struct iso9660_dirrec *dirrec = &dirrec_buffer.dirrec; + + // Preconditions: The caller has checked that dno is a directory node. The caller + // has opened a storage handle to the directory's storage and keeps it around between + // calls. + /* (vasily) directory nodes are 4096 bytes that is two logical blocks so read dir operation + * should read both blocks. + */ + + while (1) { + // read next entry + if (shand->pos >= dno->g.size) + return FSW_NOT_FOUND; // end of directory + status = fsw_iso9660_read_dirrec(vol, shand, &dirrec_buffer); + if (status) + return status; + if (dirrec->dirrec_length == 0) + { + // try the next block + shand->pos =(shand->pos & ~(vol->g.log_blocksize - 1)) + vol->g.log_blocksize; + continue; + } + + // skip . and .. + if (dirrec->file_identifier_length == 1 && + (dirrec->file_identifier[0] == 0 || dirrec->file_identifier[0] == 1)) + continue; + break; + } + + // setup a dnode for the child item + status = fsw_dnode_create(dno, dirrec_buffer.ino, FSW_DNODE_TYPE_UNKNOWN, &dirrec_buffer.name, child_dno_out); + if (status == FSW_SUCCESS) + fsw_memcpy(&(*child_dno_out)->dirrec, dirrec, sizeof(struct iso9660_dirrec)); + + return status; +} + +/** + * Read a directory entry from the directory's raw data. This internal function is used + * to read a raw iso9660 directory entry into memory. The shandle's position pointer is adjusted + * to point to the next entry. + */ + +static fsw_status_t fsw_iso9660_read_dirrec(struct fsw_iso9660_volume *vol, struct fsw_shandle *shand, struct iso9660_dirrec_buffer *dirrec_buffer) +{ + fsw_status_t status; + fsw_u32 i, buffer_size, remaining_size, name_len; + struct fsw_rock_ridge_susp_sp *sp = NULL; + struct iso9660_dirrec *dirrec = &dirrec_buffer->dirrec; + int sp_off; + int rc; + + dirrec_buffer->ino = (ISOINT(((struct fsw_iso9660_dnode *)shand->dnode)->dirrec.extent_location) + << ISO9660_BLOCKSIZE_BITS) + + (fsw_u32)shand->pos; + + // read fixed size part of directory record + buffer_size = 33; + status = fsw_shandle_read(shand, &buffer_size, dirrec); + if (status) + { + // DEBUG((DEBUG_INFO, "%a:%d \n", __FILE__, __LINE__)); + return status; + } + + if (buffer_size < 33 || dirrec->dirrec_length == 0) { + // end of directory reached + fsw_u8 *r; + r = (fsw_u8 *)dirrec; + // DEBUG((DEBUG_INFO, "%a:%d bs:%d dl:%d\n", __FILE__, __LINE__, buffer_size, dirrec->dirrec_length)); + for(i = 0; i < buffer_size; ++i) + { + DEBUG((DEBUG_INFO, "r[%d]:%c", i, r[i])); + } + dirrec->dirrec_length = 0; + return FSW_SUCCESS; + } + if (dirrec->dirrec_length < 33 || + dirrec->dirrec_length < 33 + dirrec->file_identifier_length) + return FSW_VOLUME_CORRUPTED; + +// DEBUG((DEBUG_INFO, "%a:%d, dirrec_length: %d\n", __FILE__, __LINE__, dirrec->dirrec_length)); + + // read variable size part of directory record + buffer_size = remaining_size = dirrec->dirrec_length - 33; + status = fsw_shandle_read(shand, &buffer_size, dirrec->file_identifier); + if (status) + return status; + if (buffer_size < remaining_size) + return FSW_VOLUME_CORRUPTED; + +// dump_dirrec(dirrec); + if (vol->fRockRidge) + { + sp_off = sizeof(*dirrec) + dirrec->file_identifier_length; + rc = rr_find_sp(dirrec, &sp); + if ( rc == FSW_SUCCESS + && sp != NULL) + { + sp_off = (fsw_u8 *)&sp[1] - (fsw_u8*)dirrec + sp->skip; + } + rc = rr_find_nm(vol, dirrec, sp_off, &dirrec_buffer->name); + if (rc == FSW_SUCCESS) + return FSW_SUCCESS; + } + + // setup name + name_len = dirrec->file_identifier_length; + for (i = name_len - 1; i > 0; i--) { + if (dirrec->file_identifier[i] == ';') { + name_len = i; // cut the ISO9660 version number off + break; + } + } + if (name_len > 0 && dirrec->file_identifier[name_len-1] == '.') + name_len--; // also cut the extension separator if the extension is empty + dirrec_buffer->name.type = FSW_STRING_TYPE_ISO88591; + dirrec_buffer->name.len = dirrec_buffer->name.size = name_len; + dirrec_buffer->name.data = dirrec->file_identifier; +// DEBUG((DEBUG_INFO, "%a:%d: dirrec_buffer->name.data:%a\n", __FILE__, __LINE__, dirrec_buffer->name.data)); + return FSW_SUCCESS; +} + +/** + * Get the target path of a symbolic link. This function is called when a symbolic + * link needs to be resolved. The core makes sure that the fsw_iso9660_dnode_fill has been + * called on the dnode and that it really is a symlink. + * + * For iso9660, the target path can be stored inline in the inode structure (in the space + * otherwise occupied by the block pointers) or in the inode's data. There is no flag + * indicating this, only the number of blocks entry (i_blocks) can be used as an + * indication. The check used here comes from the Linux kernel. + */ + +static fsw_status_t fsw_iso9660_readlink(struct fsw_iso9660_volume *vol, struct fsw_iso9660_dnode *dno, + struct fsw_string *link_target) +{ + fsw_status_t status; + + if (dno->g.size > FSW_PATH_MAX) + return FSW_VOLUME_CORRUPTED; + + status = fsw_dnode_readlink_data(dno, link_target); + + return status; +} + +// EOF diff --git a/filesystems/fsw_iso9660.h b/filesystems/fsw_iso9660.h new file mode 100644 index 0000000..c1dd94b --- /dev/null +++ b/filesystems/fsw_iso9660.h @@ -0,0 +1,206 @@ +/* $Id: fsw_iso9660.h 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_iso9660.h - ISO9660 file system driver 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_ISO9660_H_ +#define _FSW_ISO9660_H_ + +#define VOLSTRUCTNAME fsw_iso9660_volume +#define DNODESTRUCTNAME fsw_iso9660_dnode +#include "fsw_core.h" + + +//! Block size for ISO9660 volumes. +#define ISO9660_BLOCKSIZE 2048 +#define ISO9660_BLOCKSIZE_BITS 11 +//! Block number where the ISO9660 superblock resides. +#define ISO9660_SUPERBLOCK_BLOCKNO 16 +//Slice - we already have shifted blockIO by 16 +//but we should use ParentBlockIo +//#define ISO9660_SUPERBLOCK_BLOCKNO 0 + +#pragma pack(1) + +typedef struct { + fsw_u16 lsb; + fsw_u16 msb; +} iso9660_u16; + +typedef struct { + fsw_u32 lsb; + fsw_u32 msb; +} iso9660_u32; + +#define ISOINT(lsbmsbvalue) ((lsbmsbvalue).lsb) + +struct iso9660_dirrec { + fsw_u8 dirrec_length; + fsw_u8 ear_length; + iso9660_u32 extent_location; + iso9660_u32 data_length; + fsw_u8 recording_datetime[7]; + fsw_u8 file_flags; + fsw_u8 file_unit_size; + fsw_u8 interleave_gap_size; + iso9660_u16 volume_sequence_number; + fsw_u8 file_identifier_length; + char file_identifier[1]; +}; +//#if sizeof(struct fsw_iso9660_dirrec) != 34 +//#fail Structure fsw_iso9660_dirrec has wrong size +//#endif + +struct iso9660_volume_descriptor { + fsw_u8 volume_descriptor_type; + char standard_identifier[5]; + fsw_u8 volume_descriptor_version; +}; + +struct iso9660_primary_volume_descriptor { + fsw_u8 volume_descriptor_type; + char standard_identifier[5]; + fsw_u8 volume_descriptor_version; + fsw_u8 unused1; + char system_identifier[32]; + char volume_identifier[32]; + fsw_u8 unused2[8]; + iso9660_u32 volume_space_size; + fsw_u8 unused3[4]; + fsw_u8 escape[3]; + fsw_u8 unused4[25]; + iso9660_u16 volume_set_size; + iso9660_u16 volume_sequence_number; + iso9660_u16 logical_block_size; + iso9660_u32 path_table_size; + fsw_u32 location_type_l_path_table; + fsw_u32 location_optional_type_l_path_table; + fsw_u32 location_type_m_path_table; + fsw_u32 location_optional_type_m_path_table; + struct iso9660_dirrec root_directory; + char volume_set_identifier[128]; + char publisher_identifier[128]; + char data_preparer_identifier[128]; + char application_identifier[128]; + char copyright_file_identifier[37]; + char abstract_file_identifier[37]; + char bibliographic_file_identifier[37]; + char volume_creation_datetime[17]; + char volume_modification_datetime[17]; + char volume_expiration_datetime[17]; + char volume_effective_datetime[17]; + fsw_u8 file_structure_version; + fsw_u8 reserved1; + fsw_u8 application_use[512]; + fsw_u8 reserved2[653]; +}; +//#if sizeof(struct fsw_iso9660_volume_descriptor) != 2048 +//#fail Structure fsw_iso9660_volume_descriptor has wrong size +//#endif + +#pragma pack() + +struct iso9660_dirrec_buffer { + fsw_u32 ino; + struct fsw_string name; + struct iso9660_dirrec dirrec; + char dirrec_buffer[222]; +}; + + +/** + * ISO9660: Volume structure with ISO9660-specific data. + */ + +struct fsw_iso9660_volume { + struct fsw_volume g; //!< Generic volume structure + /*Note: don't move g!*/ + int fJoliet; + /*Joliet specific fields*/ + int fRockRidge; + /*Rock Ridge specific fields*/ + int rr_susp_skip; + + struct iso9660_primary_volume_descriptor *primary_voldesc; //!< Full Primary Volume Descriptor +}; + +/** + * ISO9660: Dnode structure with ISO9660-specific data. + */ + +struct fsw_iso9660_dnode { + struct fsw_dnode g; //!< Generic dnode structure + + struct iso9660_dirrec dirrec; //!< Fixed part of the directory record (i.e. w/o name) +}; + + +struct fsw_rock_ridge_susp_entry +{ + fsw_u8 sig[2]; + fsw_u8 len; + fsw_u8 ver; +}; + +struct fsw_rock_ridge_susp_sp +{ + struct fsw_rock_ridge_susp_entry e; + fsw_u8 magic[2]; + fsw_u8 skip; +}; + +struct fsw_rock_ridge_susp_nm +{ + struct fsw_rock_ridge_susp_entry e; + fsw_u8 flags; + fsw_u8 name[1]; +}; + +#define RR_NM_CONT (1<<0) +#define RR_NM_CURR (1<<1) +#define RR_NM_PARE (1<<2) + +union fsw_rock_ridge_susp_ce +{ + struct X{ + struct fsw_rock_ridge_susp_entry e; + iso9660_u32 block_loc; + iso9660_u32 offset; + iso9660_u32 len; + } X; + fsw_u8 raw[28]; +}; + +#endif diff --git a/filesystems/fsw_lib.c b/filesystems/fsw_lib.c new file mode 100644 index 0000000..ade2b6b --- /dev/null +++ b/filesystems/fsw_lib.c @@ -0,0 +1,547 @@ +/* $Id: fsw_lib.c 33540 2010-10-28 09:27:05Z vboxsync $ */ +/** @file + * fsw_lib.c - Core file system wrapper library functions. + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/*- + * This code is based on: + * + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_core.h" + +/* Include generated string encoding specific functions */ +#include "fsw_strfunc.h" + + +/** + * Allocate memory and clear it. + */ + +fsw_status_t fsw_alloc_zero(int len, void **ptr_out) +{ + fsw_status_t status; + + status = fsw_alloc(len, ptr_out); + if (status) + return status; + fsw_memzero(*ptr_out, len); + return FSW_SUCCESS; +} + +/** + * Duplicate a piece of data. + */ + +fsw_status_t fsw_memdup(void **dest_out, void *src, int len) +{ + fsw_status_t status; + + status = fsw_alloc(len, dest_out); + if (status) + return status; + fsw_memcpy(*dest_out, src, len); + return FSW_SUCCESS; +} + +/** + * Get the length of a string. Returns the number of characters in the string. + */ + +int fsw_strlen(struct fsw_string *s) +{ + if (s->type == FSW_STRING_TYPE_EMPTY) + return 0; + return s->len; +} + +#if 0 +static const fsw_u16 +fsw_lower_case_table[] = +{ + + /* High-byte indices ( == 0 iff no case mapping and no ignorables ) */ + + /* 0 */ 0x0000, 0x0100, 0x0000, 0x0200, 0x0300, 0x0400, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 1 */ 0x0500, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 2 */ 0x0600, 0x0700, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0800, 0x0900, + + /* Table 1 (for high byte 0x01) */ + + /* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, + /* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, + /* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127, 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, + /* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140, + /* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147, 0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F, + /* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F, + /* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167, 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F, + /* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F, + /* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259, + /* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275, + /* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8, 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF, + /* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292, 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF, + /* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9, 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF, + /* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF, + /* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7, 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF, + /* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF, + + /* Table 2 (for high byte 0x03) */ + + /* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, + /* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, + /* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, + /* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, + /* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, + /* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, + /* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, + /* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F, + /* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F, + /* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + /* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + /* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF, + /* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7, 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, + /* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7, 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF, + /* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7, 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, + + /* Table 3 (for high byte 0x04) */ + + /* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407, 0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F, + /* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + /* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + /* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + /* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + /* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F, + /* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, 0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F, + /* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477, 0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F, + /* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, + /* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, 0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F, + /* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7, 0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF, + /* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7, 0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF, + /* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8, 0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF, + /* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7, 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF, + /* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7, 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF, + /* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7, 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, + + /* Table 4 (for high byte 0x05) */ + + /* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F, + /* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F, + /* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F, + /* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, + /* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, + /* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557, 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F, + /* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, + /* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, + /* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F, + /* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F, + /* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF, + /* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + /* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7, 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF, + /* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + /* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF, + /* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7, 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF, + + /* Table 5 (for high byte 0x10) */ + + /* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F, + /* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F, + /* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F, + /* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F, + /* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, + /* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F, + /* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F, + /* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077, 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F, + /* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F, + /* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F, + /* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, + /* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, + /* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7, 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF, + /* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, + /* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, + /* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7, 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF, + + /* Table 6 (for high byte 0x20) */ + + /* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000, + /* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017, 0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F, + /* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027, 0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F, + /* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037, 0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F, + /* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047, 0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F, + /* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F, + /* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067, 0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + /* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F, + /* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F, + /* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097, 0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F, + /* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7, 0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF, + /* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7, 0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF, + /* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7, 0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF, + /* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7, 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF, + /* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7, 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF, + /* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7, 0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF, + + /* Table 7 (for high byte 0x21) */ + + /* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F, + /* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F, + /* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F, + /* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F, + /* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F, + /* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F, + /* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, + /* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, + /* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187, 0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F, + /* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, 0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F, + /* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7, 0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF, + /* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7, 0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF, + /* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7, 0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF, + /* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7, 0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF, + /* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7, 0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF, + /* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7, 0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF, + + /* Table 8 (for high byte 0xFE) */ + + /* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07, 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, + /* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17, 0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F, + /* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27, 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F, + /* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37, 0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F, + /* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47, 0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F, + /* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57, 0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F, + /* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67, 0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F, + /* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77, 0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F, + /* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87, 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F, + /* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97, 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F, + /* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF, + /* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF, + /* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF, + /* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7, 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF, + /* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF, + /* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000, + + /* Table 9 (for high byte 0xFF) */ + + /* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07, 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, + /* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F, + /* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + /* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F, + /* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + /* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F, + /* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F, + /* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F, + /* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F, + /* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F, + /* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7, 0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF, + /* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7, 0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF, + /* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7, 0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF, + /* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7, 0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF, + /* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7, 0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF, + /* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7, 0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, +}; +#endif + +static const fsw_u16 fsw_latin_case_fold[] = +{ + /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, + /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, + /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, + /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, + /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, + /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, + /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF, + /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, +}; + + +fsw_u16 fsw_to_lower(fsw_u16 ch) +{ +#if 0 + fsw_u16 temp = temp; +#endif + + if (ch < 0x0100) + return fsw_latin_case_fold[ch]; +#if 0 + /* + * Uncomment this along with above huge table (fsw_lower_case_table) + * for full UTF-16 case insensitivity + */ + temp = fsw_lower_case_table[ch>>8]; + if (temp != 0) + return fsw_lower_case_table[temp + (ch & 0x00FF)]; +#endif + + return ch; +} + +/** + * Compare two strings for equality. The two strings are compared, taking their + * encoding into account. If they are considered equal, boolean true is returned. + * Otherwise, boolean false is returned. + */ + +int fsw_streq(struct fsw_string *s1, struct fsw_string *s2) +{ + struct fsw_string temp_s; + + // handle empty strings + if (s1->type == FSW_STRING_TYPE_EMPTY) { + temp_s.type = FSW_STRING_TYPE_ISO88591; + temp_s.size = temp_s.len = 0; + temp_s.data = NULL; + return fsw_streq(&temp_s, s2); + } + if (s2->type == FSW_STRING_TYPE_EMPTY) { + temp_s.type = FSW_STRING_TYPE_ISO88591; + temp_s.size = temp_s.len = 0; + temp_s.data = NULL; + return fsw_streq(s1, &temp_s); + } + + // check length (count of chars) + if (s1->len != s2->len) + return 0; + if (s1->len == 0) // both strings are empty + return 1; + + if (s1->type == s2->type) { + // same type, do a dumb memory compare + if (s1->size != s2->size) + return 0; + return fsw_memeq(s1->data, s2->data, s1->size); + } + + // dispatch to type-specific functions + #define STREQ_DISPATCH(type1, type2) \ + if (s1->type == FSW_STRING_TYPE_##type1 && s2->type == FSW_STRING_TYPE_##type2) \ + return fsw_streq_##type1##_##type2(s1->data, s2->data, s1->len); \ + if (s2->type == FSW_STRING_TYPE_##type1 && s1->type == FSW_STRING_TYPE_##type2) \ + return fsw_streq_##type1##_##type2(s2->data, s1->data, s1->len); + STREQ_DISPATCH(ISO88591, UTF8); + STREQ_DISPATCH(ISO88591, UTF16); + STREQ_DISPATCH(ISO88591, UTF16_SWAPPED); + STREQ_DISPATCH(UTF8, UTF16); + STREQ_DISPATCH(UTF8, UTF16_SWAPPED); + STREQ_DISPATCH(UTF16, UTF16_SWAPPED); + + // final fallback + return 0; +} + +/** + * Compare a string with a C string constant. This sets up a string descriptor + * for the string constant (second argument) and runs fsw_streq on the two + * strings. Currently the C string is interpreted as ISO 8859-1. + * Returns boolean true if the strings are considered equal, boolean false otherwise. + */ + +int fsw_streq_cstr(struct fsw_string *s1, const char *s2) +{ + struct fsw_string temp_s; + int i; + + for (i = 0; s2[i]; i++) + ; + + temp_s.type = FSW_STRING_TYPE_ISO88591; + temp_s.size = temp_s.len = i; + temp_s.data = (char *)s2; + + return fsw_streq(s1, &temp_s); +} + +/** + * Creates a duplicate of a string, converting it to the given encoding during the copy. + * If the function returns FSW_SUCCESS, the caller must free the string later with + * fsw_strfree. + */ + +fsw_status_t fsw_strdup_coerce(struct fsw_string *dest, int type, struct fsw_string *src) +{ + fsw_status_t status; + + if (src->type == FSW_STRING_TYPE_EMPTY || src->len == 0) { + dest->type = type; + dest->size = dest->len = 0; + dest->data = NULL; + return FSW_SUCCESS; + } + + if (src->type == type) { + dest->type = type; + dest->len = src->len; + dest->size = src->size; + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + fsw_memcpy(dest->data, src->data, dest->size); + return FSW_SUCCESS; + } + + // dispatch to type-specific functions + #define STRCOERCE_DISPATCH(type1, type2) \ + if (src->type == FSW_STRING_TYPE_##type1 && type == FSW_STRING_TYPE_##type2) \ + return fsw_strcoerce_##type1##_##type2(src->data, src->len, dest); + STRCOERCE_DISPATCH(UTF8, ISO88591); + STRCOERCE_DISPATCH(UTF16, ISO88591); + STRCOERCE_DISPATCH(UTF16_SWAPPED, ISO88591); + STRCOERCE_DISPATCH(ISO88591, UTF8); + STRCOERCE_DISPATCH(UTF16, UTF8); + STRCOERCE_DISPATCH(UTF16_SWAPPED, UTF8); + STRCOERCE_DISPATCH(ISO88591, UTF16); + STRCOERCE_DISPATCH(UTF8, UTF16); + STRCOERCE_DISPATCH(UTF16_SWAPPED, UTF16); + + return FSW_UNSUPPORTED; +} + +/** + * Splits a string at the first occurrence of the separator character. + * The buffer string is searched for the separator character. If it is found, the + * element string descriptor is filled to point at the part of the buffer string + * before the separator. The buffer string itself is adjusted to point at the + * remaining part of the string (without the separator). + * + * If the separator is not found in the buffer string, then element is changed to + * point at the whole buffer string, and the buffer string itself is changed into + * an empty string. + * + * This function only manipulates the pointers and lengths in the two string descriptors, + * it does not change the actual string. If the buffer string is dynamically allocated, + * you must make a copy of it so that you can release it later. + */ + +void fsw_strsplit(struct fsw_string *element, struct fsw_string *buffer, char separator) +{ + int i, maxlen; + + if (buffer->type == FSW_STRING_TYPE_EMPTY || buffer->len == 0) { + element->type = FSW_STRING_TYPE_EMPTY; + return; + } + + maxlen = buffer->len; + *element = *buffer; + + if (buffer->type == FSW_STRING_TYPE_ISO88591) { + fsw_u8 *p; + + p = (fsw_u8 *)element->data; + for (i = 0; i < maxlen; i++, p++) { + if (*p == separator) { + buffer->data = p + 1; + buffer->len -= i + 1; + break; + } + } + element->len = i; + if (i == maxlen) { + buffer->data = p; + buffer->len -= i; + } + + element->size = element->len; + buffer->size = buffer->len; + + } else if (buffer->type == FSW_STRING_TYPE_UTF16) { + fsw_u16 *p; + + p = (fsw_u16 *)element->data; + for (i = 0; i < maxlen; i++, p++) { + if (*p == separator) { + buffer->data = p + 1; + buffer->len -= i + 1; + break; + } + } + element->len = i; + if (i == maxlen) { + buffer->data = p; + buffer->len -= i; + } + + element->size = element->len * sizeof(fsw_u16); + buffer->size = buffer->len * sizeof(fsw_u16); + + } else { + // fallback + buffer->type = FSW_STRING_TYPE_EMPTY; + } + + // TODO: support UTF8 and UTF16_SWAPPED +} + +/** + * Frees the memory used by a string returned from fsw_strdup_coerce. + */ + +void fsw_strfree(struct fsw_string *s) +{ + if (s->type != FSW_STRING_TYPE_EMPTY && s->data) + fsw_free(s->data); + s->type = FSW_STRING_TYPE_EMPTY; +} + +// EOF diff --git a/filesystems/fsw_reiserfs.c b/filesystems/fsw_reiserfs.c new file mode 100644 index 0000000..7c403f6 --- /dev/null +++ b/filesystems/fsw_reiserfs.c @@ -0,0 +1,874 @@ +/** + * \file fsw_reiserfs.c + * ReiserFS file system driver code. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "fsw_reiserfs.h" + + +// functions + +static fsw_status_t fsw_reiserfs_volume_mount(struct fsw_reiserfs_volume *vol); +static void fsw_reiserfs_volume_free(struct fsw_reiserfs_volume *vol); +static fsw_status_t fsw_reiserfs_volume_stat(struct fsw_reiserfs_volume *vol, struct fsw_volume_stat *sb); + +static fsw_status_t fsw_reiserfs_dnode_fill(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno); +static void fsw_reiserfs_dnode_free(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno); +static fsw_status_t fsw_reiserfs_dnode_stat(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_dnode_stat *sb); +static fsw_status_t fsw_reiserfs_get_extent(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_extent *extent); + +static fsw_status_t fsw_reiserfs_dir_lookup(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_string *lookup_name, struct fsw_reiserfs_dnode **child_dno); +static fsw_status_t fsw_reiserfs_dir_read(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_shandle *shand, struct fsw_reiserfs_dnode **child_dno); + +static fsw_status_t fsw_reiserfs_readlink(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_string *link); + +static fsw_status_t fsw_reiserfs_item_search(struct fsw_reiserfs_volume *vol, + fsw_u32 dir_id, fsw_u32 objectid, fsw_u64 offset, + struct fsw_reiserfs_item *item); +static fsw_status_t fsw_reiserfs_item_next(struct fsw_reiserfs_volume *vol, + struct fsw_reiserfs_item *item); +static void fsw_reiserfs_item_release(struct fsw_reiserfs_volume *vol, + struct fsw_reiserfs_item *item); + +// +// Dispatch Table +// + +struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(reiserfs) = { + { FSW_STRING_TYPE_ISO88591, 8, 8, "reiserfs" }, + sizeof(struct fsw_reiserfs_volume), + sizeof(struct fsw_reiserfs_dnode), + + fsw_reiserfs_volume_mount, + fsw_reiserfs_volume_free, + fsw_reiserfs_volume_stat, + fsw_reiserfs_dnode_fill, + fsw_reiserfs_dnode_free, + fsw_reiserfs_dnode_stat, + fsw_reiserfs_get_extent, + fsw_reiserfs_dir_lookup, + fsw_reiserfs_dir_read, + fsw_reiserfs_readlink, +}; + +// misc data + +static fsw_u32 superblock_offsets[3] = { + REISERFS_DISK_OFFSET_IN_BYTES >> REISERFS_SUPERBLOCK_BLOCKSIZEBITS, + REISERFS_OLD_DISK_OFFSET_IN_BYTES >> REISERFS_SUPERBLOCK_BLOCKSIZEBITS, + 0 +}; + +/** + * Mount an reiserfs volume. Reads the superblock and constructs the + * root directory dnode. + */ + +static fsw_status_t fsw_reiserfs_volume_mount(struct fsw_reiserfs_volume *vol) +{ + fsw_status_t status; + void *buffer; + fsw_u32 blocksize; + int i; + struct fsw_string s; + + // allocate memory to keep the superblock around + status = fsw_alloc(sizeof(struct reiserfs_super_block), &vol->sb); + if (status) + return status; + + // read the superblock into its buffer + fsw_set_blocksize(vol, REISERFS_SUPERBLOCK_BLOCKSIZE, REISERFS_SUPERBLOCK_BLOCKSIZE); + for (i = 0; superblock_offsets[i]; i++) { + status = fsw_block_get(vol, superblock_offsets[i], 0, &buffer); + if (status) + return status; + fsw_memcpy(vol->sb, buffer, sizeof(struct reiserfs_super_block)); + fsw_block_release(vol, superblock_offsets[i], buffer); + + // check for one of the magic strings + if (fsw_memeq(vol->sb->s_v1.s_magic, + REISERFS_SUPER_MAGIC_STRING, 8)) { + vol->version = REISERFS_VERSION_1; + break; + } else if (fsw_memeq(vol->sb->s_v1.s_magic, + REISER2FS_SUPER_MAGIC_STRING, 9)) { + vol->version = REISERFS_VERSION_2; + break; + } else if (fsw_memeq(vol->sb->s_v1.s_magic, + REISER2FS_JR_SUPER_MAGIC_STRING, 9)) { + vol->version = vol->sb->s_v1.s_version; + if (vol->version == REISERFS_VERSION_1 || vol->version == REISERFS_VERSION_2) + break; + } + } + if (superblock_offsets[i] == 0) + return FSW_UNSUPPORTED; + + // check the superblock + if (vol->sb->s_v1.s_root_block == -1) // unfinished 'reiserfsck --rebuild-tree' + return FSW_VOLUME_CORRUPTED; + + /* + if (vol->sb->s_rev_level != EXT2_GOOD_OLD_REV && + vol->sb->s_rev_level != EXT2_DYNAMIC_REV) + return FSW_UNSUPPORTED; + if (vol->sb->s_rev_level == EXT2_DYNAMIC_REV && + (vol->sb->s_feature_incompat & ~(EXT2_FEATURE_INCOMPAT_FILETYPE | EXT3_FEATURE_INCOMPAT_RECOVER))) + return FSW_UNSUPPORTED; + */ + + // set real blocksize + blocksize = vol->sb->s_v1.s_blocksize; + fsw_set_blocksize(vol, blocksize, blocksize); + + // get other info from superblock + /* + vol->ind_bcnt = EXT2_ADDR_PER_BLOCK(vol->sb); + vol->dind_bcnt = vol->ind_bcnt * vol->ind_bcnt; + vol->inode_size = EXT2_INODE_SIZE(vol->sb); + */ + + for (i = 0; i < 16; i++) + if (vol->sb->s_label[i] == 0) + break; + s.type = FSW_STRING_TYPE_ISO88591; + s.size = s.len = i; + s.data = vol->sb->s_label; + status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s); + if (status) + return status; + + // setup the root dnode + status = fsw_dnode_create_root(vol, REISERFS_ROOT_OBJECTID, &vol->g.root); + if (status) + return status; + vol->g.root->dir_id = REISERFS_ROOT_PARENT_OBJECTID; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_reiserfs_volume_mount: success, blocksize %d tree height %d\n"), + blocksize, vol->sb->s_v1.s_tree_height)); + + return FSW_SUCCESS; +} + +/** + * Free the volume data structure. Called by the core after an unmount or after + * an unsuccessful mount to release the memory used by the file system type specific + * part of the volume structure. + */ + +static void fsw_reiserfs_volume_free(struct fsw_reiserfs_volume *vol) +{ + if (vol->sb) + fsw_free(vol->sb); +} + +/** + * Get in-depth information on a volume. + */ + +static fsw_status_t fsw_reiserfs_volume_stat(struct fsw_reiserfs_volume *vol, struct fsw_volume_stat *sb) +{ + sb->total_bytes = (fsw_u64)vol->sb->s_v1.s_block_count * vol->g.log_blocksize; + sb->free_bytes = (fsw_u64)vol->sb->s_v1.s_free_blocks * vol->g.log_blocksize; + return FSW_SUCCESS; +} + +/** + * Get full information on a dnode from disk. This function is called by the core + * whenever it needs to access fields in the dnode structure that may not + * be filled immediately upon creation of the dnode. In the case of reiserfs, we + * delay fetching of the stat data until dnode_fill is called. The size and + * type fields are invalid until this function has been called. + */ + +static fsw_status_t fsw_reiserfs_dnode_fill(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno) +{ + fsw_status_t status; + fsw_u32 item_len, mode; + struct fsw_reiserfs_item item; + + if (dno->sd_v1 || dno->sd_v2) + return FSW_SUCCESS; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_reiserfs_dnode_fill: object %d/%d\n"), dno->dir_id, dno->g.dnode_id)); + + // find stat data item in reiserfs tree + status = fsw_reiserfs_item_search(vol, dno->dir_id, dno->g.dnode_id, 0, &item); + if (status == FSW_NOT_FOUND) { + FSW_MSG_ASSERT((FSW_MSGSTR("fsw_reiserfs_dnode_fill: cannot find stat_data for object %d/%d\n"), + dno->dir_id, dno->g.dnode_id)); + return FSW_VOLUME_CORRUPTED; + } + if (status) + return status; + if (item.item_offset != 0) { + FSW_MSG_ASSERT((FSW_MSGSTR("fsw_reiserfs_dnode_fill: got item that's not stat_data\n"))); + fsw_reiserfs_item_release(vol, &item); + return FSW_VOLUME_CORRUPTED; + } + item_len = item.ih.ih_item_len; + + // get data in appropriate version + if (item.ih.ih_version == KEY_FORMAT_3_5 && item_len == SD_V1_SIZE) { + // have stat_data_v1 structure + status = fsw_memdup((void **)&dno->sd_v1, item.item_data, item_len); + fsw_reiserfs_item_release(vol, &item); + if (status) + return status; + + // get info from the inode + dno->g.size = dno->sd_v1->sd_size; + mode = dno->sd_v1->sd_mode; + + } else if (item.ih.ih_version == KEY_FORMAT_3_6 && item_len == SD_V2_SIZE) { + // have stat_data_v2 structure + status = fsw_memdup((void **)&dno->sd_v2, item.item_data, item_len); + fsw_reiserfs_item_release(vol, &item); + if (status) + return status; + + // get info from the inode + dno->g.size = dno->sd_v2->sd_size; + mode = dno->sd_v2->sd_mode; + + } else { + FSW_MSG_ASSERT((FSW_MSGSTR("fsw_reiserfs_dnode_fill: version %d(%d) and size %d(%d) not recognized for stat_data\n"), + item.ih.ih_version, KEY_FORMAT_3_6, item_len, SD_V2_SIZE)); + fsw_reiserfs_item_release(vol, &item); + return FSW_VOLUME_CORRUPTED; + } + + // get node type from mode field + if (S_ISREG(mode)) + dno->g.type = FSW_DNODE_TYPE_FILE; + else if (S_ISDIR(mode)) + dno->g.type = FSW_DNODE_TYPE_DIR; + else if (S_ISLNK(mode)) + dno->g.type = FSW_DNODE_TYPE_SYMLINK; + else + dno->g.type = FSW_DNODE_TYPE_SPECIAL; + + return FSW_SUCCESS; +} + +/** + * Free the dnode data structure. Called by the core when deallocating a dnode + * structure to release the memory used by the file system type specific part + * of the dnode structure. + */ + +static void fsw_reiserfs_dnode_free(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno) +{ + if (dno->sd_v1) + fsw_free(dno->sd_v1); + if (dno->sd_v2) + fsw_free(dno->sd_v2); +} + +/** + * Get in-depth information on a dnode. The core makes sure that fsw_reiserfs_dnode_fill + * has been called on the dnode before this function is called. Note that some + * data is not directly stored into the structure, but passed to a host-specific + * callback that converts it to the host-specific format. + */ + +static fsw_status_t fsw_reiserfs_dnode_stat(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_dnode_stat *sb) +{ + if (dno->sd_v1) { + if (dno->g.type == FSW_DNODE_TYPE_SPECIAL) + sb->used_bytes = 0; + else + sb->used_bytes = dno->sd_v1->u.sd_blocks * vol->g.log_blocksize; + sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, dno->sd_v1->sd_ctime); + sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, dno->sd_v1->sd_atime); + sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, dno->sd_v1->sd_mtime); + sb->store_attr_posix(sb, dno->sd_v1->sd_mode); + } else if (dno->sd_v2) { + sb->used_bytes = dno->sd_v2->sd_blocks * vol->g.log_blocksize; + sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, dno->sd_v2->sd_ctime); + sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, dno->sd_v2->sd_atime); + sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, dno->sd_v2->sd_mtime); + sb->store_attr_posix(sb, dno->sd_v2->sd_mode); + } + + return FSW_SUCCESS; +} + +/** + * Retrieve file data mapping information. This function is called by the core when + * fsw_shandle_read needs to know where on the disk the required piece of the file's + * data can be found. The core makes sure that fsw_reiserfs_dnode_fill has been called + * on the dnode before. Our task here is to get the physical disk block number for + * the requested logical block number. + */ + +static fsw_status_t fsw_reiserfs_get_extent(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_extent *extent) +{ + fsw_status_t status; + fsw_u64 search_offset, intra_offset; + struct fsw_reiserfs_item item; + fsw_u32 intra_bno, nr_item; + + // Preconditions: The caller has checked that the requested logical block + // is within the file's size. The dnode has complete information, i.e. + // fsw_reiserfs_dnode_read_info was called successfully on it. + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_reiserfs_get_extent: mapping block %d of object %d/%d\n"), + extent->log_start, dno->dir_id, dno->g.dnode_id)); + + extent->type = FSW_EXTENT_TYPE_SPARSE; + extent->log_count = 1; + + // get the item for the requested block + search_offset = (fsw_u64)extent->log_start * vol->g.log_blocksize + 1; + status = fsw_reiserfs_item_search(vol, dno->dir_id, dno->g.dnode_id, search_offset, &item); + if (status) + return status; + if (item.item_offset == 0) { + fsw_reiserfs_item_release(vol, &item); + return FSW_SUCCESS; // no data items found, assume all-sparse file + } + intra_offset = search_offset - item.item_offset; + + // check the kind of block + if (item.item_type == TYPE_INDIRECT || item.item_type == V1_INDIRECT_UNIQUENESS) { + // indirect item, contains block numbers + + if (intra_offset & (vol->g.log_blocksize - 1)) { + FSW_MSG_ASSERT((FSW_MSGSTR("fsw_reiserfs_get_extent: intra_offset not block-aligned for indirect block\n"))); + goto bail; + } + intra_bno = (fsw_u32)FSW_U64_DIV(intra_offset, vol->g.log_blocksize); + nr_item = item.ih.ih_item_len / sizeof(fsw_u32); + if (intra_bno >= nr_item) { + FSW_MSG_ASSERT((FSW_MSGSTR("fsw_reiserfs_get_extent: indirect block too small\n"))); + goto bail; + } + extent->type = FSW_EXTENT_TYPE_PHYSBLOCK; + extent->phys_start = ((fsw_u32 *)item.item_data)[intra_bno]; + + // TODO: check if the following blocks can be aggregated into one extent + + fsw_reiserfs_item_release(vol, &item); + return FSW_SUCCESS; + + } else if (item.item_type == TYPE_DIRECT || item.item_type == V1_DIRECT_UNIQUENESS) { + // direct item, contains file data + + // TODO: Check if direct items always start on block boundaries. If not, we may have + // to do extra work here. + + if (intra_offset != 0) { + FSW_MSG_ASSERT((FSW_MSGSTR("fsw_reiserfs_get_extent: intra_offset not aligned for direct block\n"))); + goto bail; + } + + extent->type = FSW_EXTENT_TYPE_BUFFER; + status = fsw_memdup(&extent->buffer, item.item_data, item.ih.ih_item_len); + fsw_reiserfs_item_release(vol, &item); + if (status) + return status; + + return FSW_SUCCESS; + + } + +bail: + fsw_reiserfs_item_release(vol, &item); + return FSW_VOLUME_CORRUPTED; + + /* + // check if the following blocks can be aggregated into one extent + file_bcnt = (fsw_u32)((dno->g.size + vol->g.log_blocksize - 1) & (vol->g.log_blocksize - 1)); + while (path[i] + extent->log_count < buf_bcnt && // indirect block has more block pointers + extent->log_start + extent->log_count < file_bcnt) { // file has more blocks + if (buffer[path[i] + extent->log_count] == buffer[path[i] + extent->log_count - 1] + 1) + extent->log_count++; + else + break; + } + */ +} + +/** + * 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_reiserfs_dnode_fill has been called + * and the dnode is actually a directory. + */ + +static fsw_status_t fsw_reiserfs_dir_lookup(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_string *lookup_name, struct fsw_reiserfs_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_reiserfs_item item; + fsw_u32 nr_item, i, name_offset, next_name_offset, name_len; + fsw_u32 child_dir_id; + struct reiserfs_de_head *dhead; + struct fsw_string entry_name; + + // Preconditions: The caller has checked that dno is a directory node. + + // BIG TODOS: Use the hash function to start with the item containing the entry. + // Use binary search within the item. + + entry_name.type = FSW_STRING_TYPE_ISO88591; + + // get the item for that position + status = fsw_reiserfs_item_search(vol, dno->dir_id, dno->g.dnode_id, FIRST_ITEM_OFFSET, &item); + if (status) + return status; + if (item.item_offset == 0) { + fsw_reiserfs_item_release(vol, &item); + return FSW_NOT_FOUND; // empty directory or something + } + + for(;;) { + + // search the directory item + dhead = (struct reiserfs_de_head *)item.item_data; + nr_item = item.ih.u.ih_entry_count; + next_name_offset = item.ih.ih_item_len; + for (i = 0; i < nr_item; i++, dhead++, next_name_offset = name_offset) { + // get the name + name_offset = dhead->deh_location; + name_len = next_name_offset - name_offset; + while (name_len > 0 && item.item_data[name_offset + name_len - 1] == 0) + name_len--; + + entry_name.len = entry_name.size = name_len; + entry_name.data = item.item_data + name_offset; + + // compare name + if (fsw_streq(lookup_name, &entry_name)) { + // found the entry we're looking for! + + // setup a dnode for the child item + status = fsw_dnode_create(dno, dhead->deh_objectid, FSW_DNODE_TYPE_UNKNOWN, &entry_name, child_dno_out); + child_dir_id = dhead->deh_dir_id; + fsw_reiserfs_item_release(vol, &item); + if (status) + return status; + (*child_dno_out)->dir_id = child_dir_id; + + return FSW_SUCCESS; + } + } + + // We didn't find the next directory entry in this item. Look for the next + // item of the directory. + + status = fsw_reiserfs_item_next(vol, &item); + if (status) + return status; + + } +} + +/** + * Get the next directory entry when reading a directory. This function is called during + * directory iteration to retrieve the next directory entry. A dnode is constructed for + * the entry and returned. The core makes sure that fsw_reiserfs_dnode_fill has been called + * and the dnode is actually a directory. The shandle provided by the caller is used to + * record the position in the directory between calls. + */ + +static fsw_status_t fsw_reiserfs_dir_read(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_shandle *shand, struct fsw_reiserfs_dnode **child_dno_out) +{ + fsw_status_t status; + struct fsw_reiserfs_item item; + fsw_u32 nr_item, i, name_offset, next_name_offset, name_len; + fsw_u32 child_dir_id; + struct reiserfs_de_head *dhead; + struct fsw_string entry_name; + + // Preconditions: The caller has checked that dno is a directory node. The caller + // has opened a storage handle to the directory's storage and keeps it around between + // calls. + + // BIG TODOS: Use binary search within the item. + + // adjust pointer to first entry if necessary + if (shand->pos == 0) + shand->pos = FIRST_ITEM_OFFSET; + + // get the item for that position + status = fsw_reiserfs_item_search(vol, dno->dir_id, dno->g.dnode_id, shand->pos, &item); + if (status) + return status; + if (item.item_offset == 0) { + fsw_reiserfs_item_release(vol, &item); + return FSW_NOT_FOUND; // empty directory or something + } + + for(;;) { + + // search the directory item + dhead = (struct reiserfs_de_head *)item.item_data; + nr_item = item.ih.u.ih_entry_count; + for (i = 0; i < nr_item; i++, dhead++) { + if (dhead->deh_offset < shand->pos) + continue; // not yet past the last entry returned + if (dhead->deh_offset == DOT_OFFSET || dhead->deh_offset == DOT_DOT_OFFSET) + continue; // never report . or .. + + // get the name + name_offset = dhead->deh_location; + if (i == 0) + next_name_offset = item.ih.ih_item_len; + else + next_name_offset = dhead[-1].deh_location; + name_len = next_name_offset - name_offset; + while (name_len > 0 && item.item_data[name_offset + name_len - 1] == 0) + name_len--; + + entry_name.type = FSW_STRING_TYPE_ISO88591; + entry_name.len = entry_name.size = name_len; + entry_name.data = item.item_data + name_offset; + + if (fsw_streq_cstr(&entry_name, ".reiserfs_priv")) + continue; // never report this special file + + // found the next entry! + shand->pos = dhead->deh_offset + 1; + + // setup a dnode for the child item + status = fsw_dnode_create(dno, dhead->deh_objectid, FSW_DNODE_TYPE_UNKNOWN, &entry_name, child_dno_out); + child_dir_id = dhead->deh_dir_id; + fsw_reiserfs_item_release(vol, &item); + if (status) + return status; + (*child_dno_out)->dir_id = child_dir_id; + + return FSW_SUCCESS; + } + + // We didn't find the next directory entry in this item. Look for the next + // item of the directory. + + status = fsw_reiserfs_item_next(vol, &item); + if (status) + return status; + + } +} + +/** + * 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_reiserfs_dnode_fill has been + * called on the dnode and that it really is a symlink. + */ + +static fsw_status_t fsw_reiserfs_readlink(struct fsw_reiserfs_volume *vol, struct fsw_reiserfs_dnode *dno, + struct fsw_string *link_target) +{ + return fsw_dnode_readlink_data(dno, link_target); +} + +/** + * Compare an on-disk tree key against the search key. + */ + +static int fsw_reiserfs_compare_key(struct reiserfs_key *key, + fsw_u32 dir_id, fsw_u32 objectid, fsw_u64 offset) +{ + fsw_u32 key_type; + fsw_u64 key_offset; + + if (key->k_dir_id > dir_id) + return FIRST_GREATER; + if (key->k_dir_id < dir_id) + return SECOND_GREATER; + + if (key->k_objectid > objectid) + return FIRST_GREATER; + if (key->k_objectid < objectid) + return SECOND_GREATER; + + // determine format of the on-disk key + key_type = (fsw_u32)FSW_U64_SHR(key->u.k_offset_v2.v, 60); + if (key_type != TYPE_DIRECT && key_type != TYPE_INDIRECT && key_type != TYPE_DIRENTRY) { + // detected 3.5 format (_v1) + key_offset = key->u.k_offset_v1.k_offset; + } else { + // detected 3.6 format (_v2) + key_offset = key->u.k_offset_v2.v & (~0ULL >> 4); + } + if (key_offset > offset) + return FIRST_GREATER; + if (key_offset < offset) + return SECOND_GREATER; + return KEYS_IDENTICAL; +} + +/** + * Find an item by key in the reiserfs tree. + */ + +static fsw_status_t fsw_reiserfs_item_search(struct fsw_reiserfs_volume *vol, + fsw_u32 dir_id, fsw_u32 objectid, fsw_u64 offset, + struct fsw_reiserfs_item *item) +{ + fsw_status_t status; + int comp_result; + fsw_u32 tree_bno, next_tree_bno, tree_level, nr_item, i; + fsw_u8 *buffer; + struct block_head *bhead; + struct reiserfs_key *key; + struct item_head *ihead; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_reiserfs_item_search: searching %d/%d/%lld\n"), dir_id, objectid, offset)); + + // BIG TODOS: Use binary search within the item. + // Remember tree path for "get next item" function. + + item->valid = 0; + item->block_bno = 0; + + // walk the tree + tree_bno = vol->sb->s_v1.s_root_block; + for (tree_level = vol->sb->s_v1.s_tree_height - 1; ; tree_level--) { + + // get the current tree block into memory + status = fsw_block_get(vol, tree_bno, tree_level, (void **)&buffer); + if (status) + return status; + bhead = (struct block_head *)buffer; + if (bhead->blk_level != tree_level) { + FSW_MSG_ASSERT((FSW_MSGSTR("fsw_reiserfs_item_search: tree block %d has not expected level %d\n"), tree_bno, tree_level)); + fsw_block_release(vol, tree_bno, buffer); + return FSW_VOLUME_CORRUPTED; + } + nr_item = bhead->blk_nr_item; + FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_reiserfs_item_search: visiting block %d level %d items %d\n"), tree_bno, tree_level, nr_item)); + item->path_bno[tree_level] = tree_bno; + + // check if we have reached a leaf block + if (tree_level == DISK_LEAF_NODE_LEVEL) + break; + + // search internal node block, look for the path to follow + key = (struct reiserfs_key *)(buffer + BLKH_SIZE); + for (i = 0; i < nr_item; i++, key++) { + if (fsw_reiserfs_compare_key(key, dir_id, objectid, offset) == FIRST_GREATER) + break; + } + item->path_index[tree_level] = i; + next_tree_bno = ((struct disk_child *)(buffer + BLKH_SIZE + nr_item * KEY_SIZE))[i].dc_block_number; + fsw_block_release(vol, tree_bno, buffer); + tree_bno = next_tree_bno; + } + + // search leaf node block, look for our data + ihead = (struct item_head *)(buffer + BLKH_SIZE); + for (i = 0; i < nr_item; i++, ihead++) { + comp_result = fsw_reiserfs_compare_key(&ihead->ih_key, dir_id, objectid, offset); + if (comp_result == KEYS_IDENTICAL) + break; + if (comp_result == FIRST_GREATER) { + // Current key is greater than the search key. Use the last key before this + // one as the preliminary result. + if (i == 0) { + fsw_block_release(vol, tree_bno, buffer); + return FSW_NOT_FOUND; + } + i--, ihead--; + break; + } + } + if (i >= nr_item) { + // Go back to the last key, it was smaller than the search key. + // NOTE: The first key of the next leaf block is guaranteed to be greater than + // our search key. + i--, ihead--; + } + item->path_index[tree_level] = i; + // Since we may have a key that is smaller than the search key, verify that + // it is for the same object. + if (ihead->ih_key.k_dir_id != dir_id || ihead->ih_key.k_objectid != objectid) { + fsw_block_release(vol, tree_bno, buffer); + return FSW_NOT_FOUND; // Found no key for this object at all + } + + // return results + fsw_memcpy(&item->ih, ihead, sizeof(struct item_head)); + item->item_type = (fsw_u32)FSW_U64_SHR(ihead->ih_key.u.k_offset_v2.v, 60); + if (item->item_type != TYPE_DIRECT && + item->item_type != TYPE_INDIRECT && + item->item_type != TYPE_DIRENTRY) { + // 3.5 format (_v1) + item->item_type = ihead->ih_key.u.k_offset_v1.k_uniqueness; + item->item_offset = ihead->ih_key.u.k_offset_v1.k_offset; + } else { + // 3.6 format (_v2) + item->item_offset = ihead->ih_key.u.k_offset_v2.v & (~0ULL >> 4); + } + item->item_data = buffer + ihead->ih_item_location; + item->valid = 1; + + // add information for block release + item->block_bno = tree_bno; + item->block_buffer = buffer; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_reiserfs_item_search: found %d/%d/%lld (%d)\n"), + ihead->ih_key.k_dir_id, ihead->ih_key.k_objectid, item->item_offset, item->item_type)); + return FSW_SUCCESS; +} + +/** + * Find the next item in the reiserfs tree for an already-found item. + */ + +static fsw_status_t fsw_reiserfs_item_next(struct fsw_reiserfs_volume *vol, + struct fsw_reiserfs_item *item) +{ + fsw_status_t status; + fsw_u32 dir_id, objectid; + fsw_u64 offset; + fsw_u32 tree_bno, next_tree_bno, tree_level, nr_item, nr_ptr_item; + fsw_u8 *buffer; + struct block_head *bhead; + struct item_head *ihead; + + if (!item->valid) + return FSW_NOT_FOUND; + fsw_reiserfs_item_release(vol, item); // TODO: maybe delay this and/or use the cached block! + + dir_id = item->ih.ih_key.k_dir_id; + objectid = item->ih.ih_key.k_objectid; + offset = item->item_offset; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_reiserfs_item_next: next for %d/%d/%lld\n"), dir_id, objectid, offset)); + + // find a node that has more items, moving up until we find one + + for (tree_level = DISK_LEAF_NODE_LEVEL; tree_level < vol->sb->s_v1.s_tree_height; tree_level++) { + + // get the current tree block into memory + tree_bno = item->path_bno[tree_level]; + status = fsw_block_get(vol, tree_bno, tree_level, (void **)&buffer); + if (status) + return status; + bhead = (struct block_head *)buffer; + if (bhead->blk_level != tree_level) { + FSW_MSG_ASSERT((FSW_MSGSTR("fsw_reiserfs_item_next: tree block %d has not expected level %d\n"), tree_bno, tree_level)); + fsw_block_release(vol, tree_bno, buffer); + return FSW_VOLUME_CORRUPTED; + } + nr_item = bhead->blk_nr_item; + FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_reiserfs_item_next: visiting block %d level %d items %d\n"), tree_bno, tree_level, nr_item)); + + nr_ptr_item = nr_item + ((tree_level > DISK_LEAF_NODE_LEVEL) ? 1 : 0); // internal nodes have (nr_item) keys and (nr_item+1) pointers + item->path_index[tree_level]++; + if (item->path_index[tree_level] >= nr_ptr_item) { + item->path_index[tree_level] = 0; + fsw_block_release(vol, tree_bno, buffer); + continue; // this node doesn't have any more items, move up one level + } + + // we have a new path to follow, move down to the leaf node again + while (tree_level > DISK_LEAF_NODE_LEVEL) { + // get next pointer from current block + next_tree_bno = ((struct disk_child *)(buffer + BLKH_SIZE + nr_item * KEY_SIZE))[item->path_index[tree_level]].dc_block_number; + fsw_block_release(vol, tree_bno, buffer); + tree_bno = next_tree_bno; + tree_level--; + + // get the current tree block into memory + status = fsw_block_get(vol, tree_bno, tree_level, (void **)&buffer); + if (status) + return status; + bhead = (struct block_head *)buffer; + if (bhead->blk_level != tree_level) { + FSW_MSG_ASSERT((FSW_MSGSTR("fsw_reiserfs_item_next: tree block %d has not expected level %d\n"), tree_bno, tree_level)); + fsw_block_release(vol, tree_bno, buffer); + return FSW_VOLUME_CORRUPTED; + } + nr_item = bhead->blk_nr_item; + FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_reiserfs_item_next: visiting block %d level %d items %d\n"), tree_bno, tree_level, nr_item)); + item->path_bno[tree_level] = tree_bno; + } + + // get the item from the leaf node + ihead = ((struct item_head *)(buffer + BLKH_SIZE)) + item->path_index[tree_level]; + + // We now have the item that follows the previous one in the tree. Check that it + // belongs to the same object. + if (ihead->ih_key.k_dir_id != dir_id || ihead->ih_key.k_objectid != objectid) { + fsw_block_release(vol, tree_bno, buffer); + return FSW_NOT_FOUND; // Found no next key for this object + } + + // return results + fsw_memcpy(&item->ih, ihead, sizeof(struct item_head)); + item->item_type = (fsw_u32)FSW_U64_SHR(ihead->ih_key.u.k_offset_v2.v, 60); + if (item->item_type != TYPE_DIRECT && + item->item_type != TYPE_INDIRECT && + item->item_type != TYPE_DIRENTRY) { + // 3.5 format (_v1) + item->item_type = ihead->ih_key.u.k_offset_v1.k_uniqueness; + item->item_offset = ihead->ih_key.u.k_offset_v1.k_offset; + } else { + // 3.6 format (_v2) + item->item_offset = ihead->ih_key.u.k_offset_v2.v & (~0ULL >> 4); + } + item->item_data = buffer + ihead->ih_item_location; + item->valid = 1; + + // add information for block release + item->block_bno = tree_bno; + item->block_buffer = buffer; + + FSW_MSG_DEBUG((FSW_MSGSTR("fsw_reiserfs_item_next: found %d/%d/%lld (%d)\n"), + ihead->ih_key.k_dir_id, ihead->ih_key.k_objectid, item->item_offset, item->item_type)); + return FSW_SUCCESS; + } + + // we went to the highest level node and there still were no more items... + return FSW_NOT_FOUND; +} + +/** + * Release the disk block still referenced by an item search result. + */ + +static void fsw_reiserfs_item_release(struct fsw_reiserfs_volume *vol, + struct fsw_reiserfs_item *item) +{ + if (!item->valid) + return; + + if (item->block_bno > 0) { + fsw_block_release(vol, item->block_bno, item->block_buffer); + item->block_bno = 0; + } +} + +// EOF diff --git a/filesystems/fsw_reiserfs.h b/filesystems/fsw_reiserfs.h new file mode 100644 index 0000000..cbb1ea9 --- /dev/null +++ b/filesystems/fsw_reiserfs.h @@ -0,0 +1,88 @@ +/** + * \file fsw_reiserfs.h + * ReiserFS file system driver header. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _FSW_REISERFS_H_ +#define _FSW_REISERFS_H_ + +#define VOLSTRUCTNAME fsw_reiserfs_volume +#define DNODESTRUCTNAME fsw_reiserfs_dnode +#include "fsw_core.h" + +#include "fsw_reiserfs_disk.h" + + +//! Block size (in shift bits) to be used when reading the reiserfs superblock. +#define REISERFS_SUPERBLOCK_BLOCKSIZEBITS 12 +//! Block size (in bytes) to be used when reading the reiserfs superblock. +#define REISERFS_SUPERBLOCK_BLOCKSIZE (1<s_rs + * the version in RAM is part of a larger structure containing fields never written to disk. + */ +#define UNSET_HASH 0 // read_super will guess about, what hash names + // in directories were sorted with +#define TEA_HASH 1 +#define YURA_HASH 2 +#define R5_HASH 3 +#define DEFAULT_HASH R5_HASH + +struct journal_params { + __le32 jp_journal_1st_block; /* where does journal start from on its + * device */ + __le32 jp_journal_dev; /* journal device st_rdev */ + __le32 jp_journal_size; /* size of the journal */ + __le32 jp_journal_trans_max; /* max number of blocks in a transaction. */ + __le32 jp_journal_magic; /* random value made on fs creation (this + * was sb_journal_block_count) */ + __le32 jp_journal_max_batch; /* max number of blocks to batch into a + * trans */ + __le32 jp_journal_max_commit_age; /* in seconds, how old can an async + * commit be */ + __le32 jp_journal_max_trans_age; /* in seconds, how old can a transaction + * be */ +}; + +/* this is the super from 3.5.X, where X >= 10 */ +struct reiserfs_super_block_v1 { + __le32 s_block_count; /* blocks count */ + __le32 s_free_blocks; /* free blocks count */ + __le32 s_root_block; /* root block number */ + struct journal_params s_journal; + __le16 s_blocksize; /* block size */ + __le16 s_oid_maxsize; /* max size of object id array, see + * get_objectid() commentary */ + __le16 s_oid_cursize; /* current size of object id array */ + __le16 s_umount_state; /* this is set to 1 when filesystem was + * umounted, to 2 - when not */ + char s_magic[10]; /* reiserfs magic string indicates that + * file system is reiserfs: + * "ReIsErFs" or "ReIsEr2Fs" or "ReIsEr3Fs" */ + __le16 s_fs_state; /* it is set to used by fsck to mark which + * phase of rebuilding is done */ + __le32 s_hash_function_code; /* indicate, what hash function is being use + * to sort names in a directory*/ + __le16 s_tree_height; /* height of disk tree */ + __le16 s_bmap_nr; /* amount of bitmap blocks needed to address + * each block of file system */ + __le16 s_version; /* this field is only reliable on filesystem + * with non-standard journal */ + __le16 s_reserved_for_journal; /* size in blocks of journal area on main + * device, we need to keep after + * making fs with non-standard journal */ +} ATTR_PACKED; + +#define SB_SIZE_V1 (sizeof(struct reiserfs_super_block_v1)) + +/* this is the on disk super block */ +struct reiserfs_super_block { + struct reiserfs_super_block_v1 s_v1; + __le32 s_inode_generation; + __le32 s_flags; /* Right now used only by inode-attributes, if enabled */ + unsigned char s_uuid[16]; /* filesystem unique identifier */ + unsigned char s_label[16]; /* filesystem volume label */ + char s_unused[88]; /* zero filled by mkreiserfs and + * reiserfs_convert_objectid_map_v1() + * so any additions must be updated + * there as well. */ +} ATTR_PACKED; + +#define SB_SIZE (sizeof(struct reiserfs_super_block)) + +#define REISERFS_VERSION_1 0 +#define REISERFS_VERSION_2 2 + +// on-disk super block fields converted to cpu form +#define SB_DISK_SUPER_BLOCK(s) (REISERFS_SB(s)->s_rs) +#define SB_V1_DISK_SUPER_BLOCK(s) (&(SB_DISK_SUPER_BLOCK(s)->s_v1)) +#define SB_BLOCKSIZE(s) \ + le32_to_cpu ((SB_V1_DISK_SUPER_BLOCK(s)->s_blocksize)) +#define SB_BLOCK_COUNT(s) \ + le32_to_cpu ((SB_V1_DISK_SUPER_BLOCK(s)->s_block_count)) +#define SB_FREE_BLOCKS(s) \ + le32_to_cpu ((SB_V1_DISK_SUPER_BLOCK(s)->s_free_blocks)) +#define SB_REISERFS_MAGIC(s) \ + (SB_V1_DISK_SUPER_BLOCK(s)->s_magic) +#define SB_ROOT_BLOCK(s) \ + le32_to_cpu ((SB_V1_DISK_SUPER_BLOCK(s)->s_root_block)) +#define SB_TREE_HEIGHT(s) \ + le16_to_cpu ((SB_V1_DISK_SUPER_BLOCK(s)->s_tree_height)) +#define SB_REISERFS_STATE(s) \ + le16_to_cpu ((SB_V1_DISK_SUPER_BLOCK(s)->s_umount_state)) +#define SB_VERSION(s) le16_to_cpu ((SB_V1_DISK_SUPER_BLOCK(s)->s_version)) +#define SB_BMAP_NR(s) le16_to_cpu ((SB_V1_DISK_SUPER_BLOCK(s)->s_bmap_nr)) + +#define SB_ONDISK_JP(s) (&SB_V1_DISK_SUPER_BLOCK(s)->s_journal) +#define SB_ONDISK_JOURNAL_SIZE(s) \ + le32_to_cpu ((SB_ONDISK_JP(s)->jp_journal_size)) +#define SB_ONDISK_JOURNAL_1st_BLOCK(s) \ + le32_to_cpu ((SB_ONDISK_JP(s)->jp_journal_1st_block)) +#define SB_ONDISK_JOURNAL_DEVICE(s) \ + le32_to_cpu ((SB_ONDISK_JP(s)->jp_journal_dev)) +#define SB_ONDISK_RESERVED_FOR_JOURNAL(s) \ + le16_to_cpu ((SB_V1_DISK_SUPER_BLOCK(s)->s_reserved_for_journal)) + +#define is_block_in_log_or_reserved_area(s, block) \ + block >= SB_JOURNAL_1st_RESERVED_BLOCK(s) \ + && block < SB_JOURNAL_1st_RESERVED_BLOCK(s) + \ + ((!is_reiserfs_jr(SB_DISK_SUPER_BLOCK(s)) ? \ + SB_ONDISK_JOURNAL_SIZE(s) + 1 : SB_ONDISK_RESERVED_FOR_JOURNAL(s))) + + /* used by gcc */ +#define REISERFS_SUPER_MAGIC 0x52654973 + /* used by file system utilities that + look at the superblock, etc. */ +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" +#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" + +/* ReiserFS leaves the first 64k unused, so that partition labels have + enough space. If someone wants to write a fancy bootloader that + needs more than 64k, let us know, and this will be increased in size. + This number must be larger than than the largest block size on any + platform, or code will break. -Hans */ +#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) +#define REISERFS_FIRST_BLOCK unused_define +#define REISERFS_JOURNAL_OFFSET_IN_BYTES REISERFS_DISK_OFFSET_IN_BYTES + +/* the spot for the super in versions 3.5 - 3.5.10 (inclusive) */ +#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024) + +// reiserfs internal error code (used by search_by_key adn fix_nodes)) +#define CARRY_ON 0 +#define REPEAT_SEARCH -1 +#define IO_ERROR -2 +#define NO_DISK_SPACE -3 +#define NO_BALANCING_NEEDED (-4) +#define NO_MORE_UNUSED_CONTIGUOUS_BLOCKS (-5) +#define QUOTA_EXCEEDED -6 + +typedef __u32 b_blocknr_t; +typedef __le32 unp_t; + +struct unfm_nodeinfo { + unp_t unfm_nodenum; + unsigned short unfm_freespace; +}; + +/* there are two formats of keys: 3.5 and 3.6 + */ +#define KEY_FORMAT_3_5 0 +#define KEY_FORMAT_3_6 1 + +/* there are two stat datas */ +#define STAT_DATA_V1 0 +#define STAT_DATA_V2 1 + + + +/** this says about version of key of all items (but stat data) the + object consists of */ +#define get_inode_item_key_version( inode ) \ + ((REISERFS_I(inode)->i_flags & i_item_key_version_mask) ? KEY_FORMAT_3_6 : KEY_FORMAT_3_5) + +#define set_inode_item_key_version( inode, version ) \ + ({ if((version)==KEY_FORMAT_3_6) \ + REISERFS_I(inode)->i_flags |= i_item_key_version_mask; \ + else \ + REISERFS_I(inode)->i_flags &= ~i_item_key_version_mask; }) + +#define get_inode_sd_version(inode) \ + ((REISERFS_I(inode)->i_flags & i_stat_data_version_mask) ? STAT_DATA_V2 : STAT_DATA_V1) + +#define set_inode_sd_version(inode, version) \ + ({ if((version)==STAT_DATA_V2) \ + REISERFS_I(inode)->i_flags |= i_stat_data_version_mask; \ + else \ + REISERFS_I(inode)->i_flags &= ~i_stat_data_version_mask; }) + +/* This is an aggressive tail suppression policy, I am hoping it + improves our benchmarks. The principle behind it is that percentage + space saving is what matters, not absolute space saving. This is + non-intuitive, but it helps to understand it if you consider that the + cost to access 4 blocks is not much more than the cost to access 1 + block, if you have to do a seek and rotate. A tail risks a + non-linear disk access that is significant as a percentage of total + time cost for a 4 block file and saves an amount of space that is + less significant as a percentage of space, or so goes the hypothesis. + -Hans */ +#define STORE_TAIL_IN_UNFM_S1(n_file_size,n_tail_size,n_block_size) \ +(\ + (!(n_tail_size)) || \ + (((n_tail_size) > MAX_DIRECT_ITEM_LEN(n_block_size)) || \ + ( (n_file_size) >= (n_block_size) * 4 ) || \ + ( ( (n_file_size) >= (n_block_size) * 3 ) && \ + ( (n_tail_size) >= (MAX_DIRECT_ITEM_LEN(n_block_size))/4) ) || \ + ( ( (n_file_size) >= (n_block_size) * 2 ) && \ + ( (n_tail_size) >= (MAX_DIRECT_ITEM_LEN(n_block_size))/2) ) || \ + ( ( (n_file_size) >= (n_block_size) ) && \ + ( (n_tail_size) >= (MAX_DIRECT_ITEM_LEN(n_block_size) * 3)/4) ) ) \ +) + +/* Another strategy for tails, this one means only create a tail if all the + file would fit into one DIRECT item. + Primary intention for this one is to increase performance by decreasing + seeking. +*/ +#define STORE_TAIL_IN_UNFM_S2(n_file_size,n_tail_size,n_block_size) \ +(\ + (!(n_tail_size)) || \ + (((n_file_size) > MAX_DIRECT_ITEM_LEN(n_block_size)) ) \ +) + +/* + * values for s_umount_state field + */ +#define REISERFS_VALID_FS 1 +#define REISERFS_ERROR_FS 2 + +// +// there are 5 item types currently +// +#define TYPE_STAT_DATA 0 +#define TYPE_INDIRECT 1 +#define TYPE_DIRECT 2 +#define TYPE_DIRENTRY 3 +#define TYPE_MAXTYPE 3 +#define TYPE_ANY 15 // FIXME: comment is required + +/***************************************************************************/ +/* KEY & ITEM HEAD */ +/***************************************************************************/ + +// +// directories use this key as well as old files +// +struct offset_v1 { + __le32 k_offset; + __le32 k_uniqueness; +} ATTR_PACKED; + +struct offset_v2 { + __le64 v; +} ATTR_PACKED; + +/* +static inline __u16 offset_v2_k_type(const struct offset_v2 *v2) +{ + __u8 type = le64_to_cpu(v2->v) >> 60; + return (type <= TYPE_MAXTYPE) ? type : TYPE_ANY; +} + +static inline loff_t offset_v2_k_offset(const struct offset_v2 *v2) +{ + return le64_to_cpu(v2->v) & (~0ULL >> 4); +} +*/ + +/* Key of an item determines its location in the S+tree, and + is composed of 4 components */ +struct reiserfs_key { + __le32 k_dir_id; /* packing locality: by default parent + directory object id */ + __le32 k_objectid; /* object identifier */ + union { + struct offset_v1 k_offset_v1; + struct offset_v2 k_offset_v2; + } ATTR_PACKED u; +} ATTR_PACKED; + +struct in_core_key { + __u32 k_dir_id; /* packing locality: by default parent + directory object id */ + __u32 k_objectid; /* object identifier */ + __u64 k_offset; + __u8 k_type; +}; + +struct cpu_key { + struct in_core_key on_disk_key; + int version; + int key_length; /* 3 in all cases but direct2indirect and + indirect2direct conversion */ +}; + +/* Our function for comparing keys can compare keys of different + lengths. It takes as a parameter the length of the keys it is to + compare. These defines are used in determining what is to be passed + to it as that parameter. */ +#define REISERFS_FULL_KEY_LEN 4 +#define REISERFS_SHORT_KEY_LEN 2 + +/* The result of the key compare */ +#define FIRST_GREATER 1 +#define SECOND_GREATER -1 +#define KEYS_IDENTICAL 0 +#define KEY_FOUND 1 +#define KEY_NOT_FOUND 0 + +#define KEY_SIZE (sizeof(struct reiserfs_key)) +#define SHORT_KEY_SIZE (sizeof (__u32) + sizeof (__u32)) + +/* return values for search_by_key and clones */ +#define ITEM_FOUND 1 +#define ITEM_NOT_FOUND 0 +#define ENTRY_FOUND 1 +#define ENTRY_NOT_FOUND 0 +#define DIRECTORY_NOT_FOUND -1 +#define REGULAR_FILE_FOUND -2 +#define DIRECTORY_FOUND -3 +#define BYTE_FOUND 1 +#define BYTE_NOT_FOUND 0 +#define FILE_NOT_FOUND -1 + +#define POSITION_FOUND 1 +#define POSITION_NOT_FOUND 0 + +// return values for reiserfs_find_entry and search_by_entry_key +#define NAME_FOUND 1 +#define NAME_NOT_FOUND 0 +#define GOTO_PREVIOUS_ITEM 2 +#define NAME_FOUND_INVISIBLE 3 + +/* Everything in the filesystem is stored as a set of items. The + item head contains the key of the item, its free space (for + indirect items) and specifies the location of the item itself + within the block. */ + +struct item_head { + /* Everything in the tree is found by searching for it based on + * its key.*/ + struct reiserfs_key ih_key; + union { + /* The free space in the last unformatted node of an + indirect item if this is an indirect item. This + equals 0xFFFF iff this is a direct item or stat data + item. Note that the key, not this field, is used to + determine the item type, and thus which field this + union contains. */ + __le16 ih_free_space_reserved; + /* Iff this is a directory item, this field equals the + number of directory entries in the directory item. */ + __le16 ih_entry_count; + } ATTR_PACKED u; + __le16 ih_item_len; /* total size of the item body */ + __le16 ih_item_location; /* an offset to the item body + * within the block */ + __le16 ih_version; /* 0 for all old items, 2 for new + ones. Highest bit is set by fsck + temporary, cleaned after all + done */ +} ATTR_PACKED; +/* size of item header */ +#define IH_SIZE (sizeof(struct item_head)) + +#define ih_free_space(ih) le16_to_cpu((ih)->u.ih_free_space_reserved) +#define ih_version(ih) le16_to_cpu((ih)->ih_version) +#define ih_entry_count(ih) le16_to_cpu((ih)->u.ih_entry_count) +#define ih_location(ih) le16_to_cpu((ih)->ih_item_location) +#define ih_item_len(ih) le16_to_cpu((ih)->ih_item_len) + +#define unreachable_item(ih) (ih_version(ih) & (1 << 15)) + +#define get_ih_free_space(ih) (ih_version (ih) == KEY_FORMAT_3_6 ? 0 : ih_free_space (ih)) + +/* these operate on indirect items, where you've got an array of ints +** at a possibly unaligned location. These are a noop on ia32 +** +** p is the array of __u32, i is the index into the array, v is the value +** to store there. +*/ +#define get_block_num(p, i) le32_to_cpu(get_unaligned((p) + (i))) + +// +// in old version uniqueness field shows key type +// +#define V1_SD_UNIQUENESS 0 +#define V1_INDIRECT_UNIQUENESS 0xfffffffe +#define V1_DIRECT_UNIQUENESS 0xffffffff +#define V1_DIRENTRY_UNIQUENESS 500 +#define V1_ANY_UNIQUENESS 555 // FIXME: comment is required + +// +// here are conversion routines +// +/* +static inline int uniqueness2type(__u32 uniqueness) +{ + switch ((int)uniqueness) { + case V1_SD_UNIQUENESS: + return TYPE_STAT_DATA; + case V1_INDIRECT_UNIQUENESS: + return TYPE_INDIRECT; + case V1_DIRECT_UNIQUENESS: + return TYPE_DIRECT; + case V1_DIRENTRY_UNIQUENESS: + return TYPE_DIRENTRY; + default: + reiserfs_warning(NULL, "vs-500: unknown uniqueness %d", + uniqueness); + case V1_ANY_UNIQUENESS: + return TYPE_ANY; + } +} + +static inline __u32 type2uniqueness(int type) +{ + switch (type) { + case TYPE_STAT_DATA: + return V1_SD_UNIQUENESS; + case TYPE_INDIRECT: + return V1_INDIRECT_UNIQUENESS; + case TYPE_DIRECT: + return V1_DIRECT_UNIQUENESS; + case TYPE_DIRENTRY: + return V1_DIRENTRY_UNIQUENESS; + default: + reiserfs_warning(NULL, "vs-501: unknown type %d", type); + case TYPE_ANY: + return V1_ANY_UNIQUENESS; + } +} +*/ + +// +// key is pointer to on disk key which is stored in le, result is cpu, +// there is no way to get version of object from key, so, provide +// version to these defines +// +/* +static inline loff_t le_key_k_offset(int version, + const struct reiserfs_key *key) +{ + return (version == KEY_FORMAT_3_5) ? + le32_to_cpu(key->u.k_offset_v1.k_offset) : + offset_v2_k_offset(&(key->u.k_offset_v2)); +} + +static inline loff_t le_ih_k_offset(const struct item_head *ih) +{ + return le_key_k_offset(ih_version(ih), &(ih->ih_key)); +} + +static inline loff_t le_key_k_type(int version, const struct reiserfs_key *key) +{ + return (version == KEY_FORMAT_3_5) ? + uniqueness2type(le32_to_cpu(key->u.k_offset_v1.k_uniqueness)) : + offset_v2_k_type(&(key->u.k_offset_v2)); +} + +static inline loff_t le_ih_k_type(const struct item_head *ih) +{ + return le_key_k_type(ih_version(ih), &(ih->ih_key)); +} +*/ + +#define is_direntry_le_key(version,key) (le_key_k_type (version, key) == TYPE_DIRENTRY) +#define is_direct_le_key(version,key) (le_key_k_type (version, key) == TYPE_DIRECT) +#define is_indirect_le_key(version,key) (le_key_k_type (version, key) == TYPE_INDIRECT) +#define is_statdata_le_key(version,key) (le_key_k_type (version, key) == TYPE_STAT_DATA) + +// +// item header has version. +// +#define is_direntry_le_ih(ih) is_direntry_le_key (ih_version (ih), &((ih)->ih_key)) +#define is_direct_le_ih(ih) is_direct_le_key (ih_version (ih), &((ih)->ih_key)) +#define is_indirect_le_ih(ih) is_indirect_le_key (ih_version(ih), &((ih)->ih_key)) +#define is_statdata_le_ih(ih) is_statdata_le_key (ih_version (ih), &((ih)->ih_key)) + +// +// key is pointer to cpu key, result is cpu +// +/* +static inline loff_t cpu_key_k_offset(const struct cpu_key *key) +{ + return key->on_disk_key.k_offset; +} + +static inline loff_t cpu_key_k_type(const struct cpu_key *key) +{ + return key->on_disk_key.k_type; +} + +static inline void cpu_key_k_offset_dec(struct cpu_key *key) +{ + key->on_disk_key.k_offset--; +} +*/ + +#define is_direntry_cpu_key(key) (cpu_key_k_type (key) == TYPE_DIRENTRY) +#define is_direct_cpu_key(key) (cpu_key_k_type (key) == TYPE_DIRECT) +#define is_indirect_cpu_key(key) (cpu_key_k_type (key) == TYPE_INDIRECT) +#define is_statdata_cpu_key(key) (cpu_key_k_type (key) == TYPE_STAT_DATA) + +/* are these used ? */ +#define is_direntry_cpu_ih(ih) (is_direntry_cpu_key (&((ih)->ih_key))) +#define is_direct_cpu_ih(ih) (is_direct_cpu_key (&((ih)->ih_key))) +#define is_indirect_cpu_ih(ih) (is_indirect_cpu_key (&((ih)->ih_key))) +#define is_statdata_cpu_ih(ih) (is_statdata_cpu_key (&((ih)->ih_key))) + +#define I_K_KEY_IN_ITEM(p_s_ih, p_s_key, n_blocksize) \ + ( ! COMP_SHORT_KEYS(p_s_ih, p_s_key) && \ + I_OFF_BYTE_IN_ITEM(p_s_ih, k_offset (p_s_key), n_blocksize) ) + +/* maximal length of item */ +#define MAX_ITEM_LEN(block_size) (block_size - BLKH_SIZE - IH_SIZE) +#define MIN_ITEM_LEN 1 + +/* object identifier for root dir */ +#define REISERFS_ROOT_OBJECTID 2 +#define REISERFS_ROOT_PARENT_OBJECTID 1 + +/* + * Picture represents a leaf of the S+tree + * ______________________________________________________ + * | | Array of | | | + * |Block | Object-Item | F r e e | Objects- | + * | head | Headers | S p a c e | Items | + * |______|_______________|___________________|___________| + */ + +/* Header of a disk block. More precisely, header of a formatted leaf + or internal node, and not the header of an unformatted node. */ +struct block_head { + __le16 blk_level; /* Level of a block in the tree. */ + __le16 blk_nr_item; /* Number of keys/items in a block. */ + __le16 blk_free_space; /* Block free space in bytes. */ + __le16 blk_reserved; + /* dump this in v4/planA */ + struct reiserfs_key blk_right_delim_key; /* kept only for compatibility */ +}; + +#define BLKH_SIZE (sizeof(struct block_head)) +#define blkh_level(p_blkh) (le16_to_cpu((p_blkh)->blk_level)) +#define blkh_nr_item(p_blkh) (le16_to_cpu((p_blkh)->blk_nr_item)) +#define blkh_free_space(p_blkh) (le16_to_cpu((p_blkh)->blk_free_space)) +#define blkh_reserved(p_blkh) (le16_to_cpu((p_blkh)->blk_reserved)) +#define blkh_right_delim_key(p_blkh) ((p_blkh)->blk_right_delim_key) + +/* + * values for blk_level field of the struct block_head + */ + +#define FREE_LEVEL 0 /* when node gets removed from the tree its + blk_level is set to FREE_LEVEL. It is then + used to see whether the node is still in the + tree */ + +#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */ + +/* Given the buffer head of a formatted node, resolve to the block head of that node. */ +#define B_BLK_HEAD(p_s_bh) ((struct block_head *)((p_s_bh)->b_data)) +/* Number of items that are in buffer. */ +#define B_NR_ITEMS(p_s_bh) (blkh_nr_item(B_BLK_HEAD(p_s_bh))) +#define B_LEVEL(p_s_bh) (blkh_level(B_BLK_HEAD(p_s_bh))) +#define B_FREE_SPACE(p_s_bh) (blkh_free_space(B_BLK_HEAD(p_s_bh))) + +/* Get right delimiting key. -- little endian */ +#define B_PRIGHT_DELIM_KEY(p_s_bh) (&(blk_right_delim_key(B_BLK_HEAD(p_s_bh)) + +/* Does the buffer contain a disk leaf. */ +#define B_IS_ITEMS_LEVEL(p_s_bh) (B_LEVEL(p_s_bh) == DISK_LEAF_NODE_LEVEL) + +/* Does the buffer contain a disk internal node */ +#define B_IS_KEYS_LEVEL(p_s_bh) (B_LEVEL(p_s_bh) > DISK_LEAF_NODE_LEVEL \ + && B_LEVEL(p_s_bh) <= MAX_HEIGHT) + +/***************************************************************************/ +/* STAT DATA */ +/***************************************************************************/ + +// +// old stat data is 32 bytes long. We are going to distinguish new one by +// different size +// +struct stat_data_v1 { + __le16 sd_mode; /* file type, permissions */ + __le16 sd_nlink; /* number of hard links */ + __le16 sd_uid; /* owner */ + __le16 sd_gid; /* group */ + __le32 sd_size; /* file size */ + __le32 sd_atime; /* time of last access */ + __le32 sd_mtime; /* time file was last modified */ + __le32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */ + union { + __le32 sd_rdev; + __le32 sd_blocks; /* number of blocks file uses */ + } ATTR_PACKED u; + __le32 sd_first_direct_byte; /* first byte of file which is stored + in a direct item: except that if it + equals 1 it is a symlink and if it + equals ~(__u32)0 there is no + direct item. The existence of this + field really grates on me. Let's + replace it with a macro based on + sd_size and our tail suppression + policy. Someday. -Hans */ +} ATTR_PACKED; + +#define SD_V1_SIZE (sizeof(struct stat_data_v1)) +#define stat_data_v1(ih) (ih_version (ih) == KEY_FORMAT_3_5) +#define sd_v1_mode(sdp) (le16_to_cpu((sdp)->sd_mode)) +#define set_sd_v1_mode(sdp,v) ((sdp)->sd_mode = cpu_to_le16(v)) +#define sd_v1_nlink(sdp) (le16_to_cpu((sdp)->sd_nlink)) +#define set_sd_v1_nlink(sdp,v) ((sdp)->sd_nlink = cpu_to_le16(v)) +#define sd_v1_uid(sdp) (le16_to_cpu((sdp)->sd_uid)) +#define set_sd_v1_uid(sdp,v) ((sdp)->sd_uid = cpu_to_le16(v)) +#define sd_v1_gid(sdp) (le16_to_cpu((sdp)->sd_gid)) +#define set_sd_v1_gid(sdp,v) ((sdp)->sd_gid = cpu_to_le16(v)) +#define sd_v1_size(sdp) (le32_to_cpu((sdp)->sd_size)) +#define set_sd_v1_size(sdp,v) ((sdp)->sd_size = cpu_to_le32(v)) +#define sd_v1_atime(sdp) (le32_to_cpu((sdp)->sd_atime)) +#define set_sd_v1_atime(sdp,v) ((sdp)->sd_atime = cpu_to_le32(v)) +#define sd_v1_mtime(sdp) (le32_to_cpu((sdp)->sd_mtime)) +#define set_sd_v1_mtime(sdp,v) ((sdp)->sd_mtime = cpu_to_le32(v)) +#define sd_v1_ctime(sdp) (le32_to_cpu((sdp)->sd_ctime)) +#define set_sd_v1_ctime(sdp,v) ((sdp)->sd_ctime = cpu_to_le32(v)) +#define sd_v1_rdev(sdp) (le32_to_cpu((sdp)->u.sd_rdev)) +#define set_sd_v1_rdev(sdp,v) ((sdp)->u.sd_rdev = cpu_to_le32(v)) +#define sd_v1_blocks(sdp) (le32_to_cpu((sdp)->u.sd_blocks)) +#define set_sd_v1_blocks(sdp,v) ((sdp)->u.sd_blocks = cpu_to_le32(v)) +#define sd_v1_first_direct_byte(sdp) \ + (le32_to_cpu((sdp)->sd_first_direct_byte)) +#define set_sd_v1_first_direct_byte(sdp,v) \ + ((sdp)->sd_first_direct_byte = cpu_to_le32(v)) + +/* +#include +*/ + +/* inode flags stored in sd_attrs (nee sd_reserved) */ + +/* we want common flags to have the same values as in ext2, + so chattr(1) will work without problems */ +#define REISERFS_IMMUTABLE_FL EXT2_IMMUTABLE_FL +#define REISERFS_APPEND_FL EXT2_APPEND_FL +#define REISERFS_SYNC_FL EXT2_SYNC_FL +#define REISERFS_NOATIME_FL EXT2_NOATIME_FL +#define REISERFS_NODUMP_FL EXT2_NODUMP_FL +#define REISERFS_SECRM_FL EXT2_SECRM_FL +#define REISERFS_UNRM_FL EXT2_UNRM_FL +#define REISERFS_COMPR_FL EXT2_COMPR_FL +#define REISERFS_NOTAIL_FL EXT2_NOTAIL_FL + +/* persistent flags that file inherits from the parent directory */ +#define REISERFS_INHERIT_MASK ( REISERFS_IMMUTABLE_FL | \ + REISERFS_SYNC_FL | \ + REISERFS_NOATIME_FL | \ + REISERFS_NODUMP_FL | \ + REISERFS_SECRM_FL | \ + REISERFS_COMPR_FL | \ + REISERFS_NOTAIL_FL ) + +/* Stat Data on disk (reiserfs version of UFS disk inode minus the + address blocks) */ +struct stat_data { + __le16 sd_mode; /* file type, permissions */ + __le16 sd_attrs; /* persistent inode flags */ + __le32 sd_nlink; /* number of hard links */ + __le64 sd_size; /* file size */ + __le32 sd_uid; /* owner */ + __le32 sd_gid; /* group */ + __le32 sd_atime; /* time of last access */ + __le32 sd_mtime; /* time file was last modified */ + __le32 sd_ctime; /* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */ + __le32 sd_blocks; + union { + __le32 sd_rdev; + __le32 sd_generation; + //__le32 sd_first_direct_byte; + /* first byte of file which is stored in a + direct item: except that if it equals 1 + it is a symlink and if it equals + ~(__u32)0 there is no direct item. The + existence of this field really grates + on me. Let's replace it with a macro + based on sd_size and our tail + suppression policy? */ + } ATTR_PACKED u; +} ATTR_PACKED; +// +// this is 44 bytes long +// +#define SD_SIZE (sizeof(struct stat_data)) +#define SD_V2_SIZE SD_SIZE +#define stat_data_v2(ih) (ih_version (ih) == KEY_FORMAT_3_6) +#define sd_v2_mode(sdp) (le16_to_cpu((sdp)->sd_mode)) +#define set_sd_v2_mode(sdp,v) ((sdp)->sd_mode = cpu_to_le16(v)) +/* sd_reserved */ +/* set_sd_reserved */ +#define sd_v2_nlink(sdp) (le32_to_cpu((sdp)->sd_nlink)) +#define set_sd_v2_nlink(sdp,v) ((sdp)->sd_nlink = cpu_to_le32(v)) +#define sd_v2_size(sdp) (le64_to_cpu((sdp)->sd_size)) +#define set_sd_v2_size(sdp,v) ((sdp)->sd_size = cpu_to_le64(v)) +#define sd_v2_uid(sdp) (le32_to_cpu((sdp)->sd_uid)) +#define set_sd_v2_uid(sdp,v) ((sdp)->sd_uid = cpu_to_le32(v)) +#define sd_v2_gid(sdp) (le32_to_cpu((sdp)->sd_gid)) +#define set_sd_v2_gid(sdp,v) ((sdp)->sd_gid = cpu_to_le32(v)) +#define sd_v2_atime(sdp) (le32_to_cpu((sdp)->sd_atime)) +#define set_sd_v2_atime(sdp,v) ((sdp)->sd_atime = cpu_to_le32(v)) +#define sd_v2_mtime(sdp) (le32_to_cpu((sdp)->sd_mtime)) +#define set_sd_v2_mtime(sdp,v) ((sdp)->sd_mtime = cpu_to_le32(v)) +#define sd_v2_ctime(sdp) (le32_to_cpu((sdp)->sd_ctime)) +#define set_sd_v2_ctime(sdp,v) ((sdp)->sd_ctime = cpu_to_le32(v)) +#define sd_v2_blocks(sdp) (le32_to_cpu((sdp)->sd_blocks)) +#define set_sd_v2_blocks(sdp,v) ((sdp)->sd_blocks = cpu_to_le32(v)) +#define sd_v2_rdev(sdp) (le32_to_cpu((sdp)->u.sd_rdev)) +#define set_sd_v2_rdev(sdp,v) ((sdp)->u.sd_rdev = cpu_to_le32(v)) +#define sd_v2_generation(sdp) (le32_to_cpu((sdp)->u.sd_generation)) +#define set_sd_v2_generation(sdp,v) ((sdp)->u.sd_generation = cpu_to_le32(v)) +#define sd_v2_attrs(sdp) (le16_to_cpu((sdp)->sd_attrs)) +#define set_sd_v2_attrs(sdp,v) ((sdp)->sd_attrs = cpu_to_le16(v)) + +/***************************************************************************/ +/* DIRECTORY STRUCTURE */ +/***************************************************************************/ +/* + Picture represents the structure of directory items + ________________________________________________ + | Array of | | | | | | + | directory |N-1| N-2 | .... | 1st |0th| + | entry headers | | | | | | + |_______________|___|_____|________|_______|___| + <---- directory entries ------> + + First directory item has k_offset component 1. We store "." and ".." + in one item, always, we never split "." and ".." into differing + items. This makes, among other things, the code for removing + directories simpler. */ +#define SD_OFFSET 0 +#define SD_UNIQUENESS 0 +#define DOT_OFFSET 1 +#define DOT_DOT_OFFSET 2 +#define DIRENTRY_UNIQUENESS 500 + +/* */ +#define FIRST_ITEM_OFFSET 1 + +/* + Q: How to get key of object pointed to by entry from entry? + + A: Each directory entry has its header. This header has deh_dir_id and deh_objectid fields, those are key + of object, entry points to */ + +/* NOT IMPLEMENTED: + Directory will someday contain stat data of object */ + +struct reiserfs_de_head { + __le32 deh_offset; /* third component of the directory entry key */ + __le32 deh_dir_id; /* objectid of the parent directory of the object, that is referenced + by directory entry */ + __le32 deh_objectid; /* objectid of the object, that is referenced by directory entry */ + __le16 deh_location; /* offset of name in the whole item */ + __le16 deh_state; /* whether 1) entry contains stat data (for future), and 2) whether + entry is hidden (unlinked) */ +} ATTR_PACKED; +#define DEH_SIZE sizeof(struct reiserfs_de_head) +#define deh_offset(p_deh) (le32_to_cpu((p_deh)->deh_offset)) +#define deh_dir_id(p_deh) (le32_to_cpu((p_deh)->deh_dir_id)) +#define deh_objectid(p_deh) (le32_to_cpu((p_deh)->deh_objectid)) +#define deh_location(p_deh) (le16_to_cpu((p_deh)->deh_location)) +#define deh_state(p_deh) (le16_to_cpu((p_deh)->deh_state)) + +#define put_deh_offset(p_deh,v) ((p_deh)->deh_offset = cpu_to_le32((v))) +#define put_deh_dir_id(p_deh,v) ((p_deh)->deh_dir_id = cpu_to_le32((v))) +#define put_deh_objectid(p_deh,v) ((p_deh)->deh_objectid = cpu_to_le32((v))) +#define put_deh_location(p_deh,v) ((p_deh)->deh_location = cpu_to_le16((v))) +#define put_deh_state(p_deh,v) ((p_deh)->deh_state = cpu_to_le16((v))) + +/* empty directory contains two entries "." and ".." and their headers */ +#define EMPTY_DIR_SIZE \ +(DEH_SIZE * 2 + ROUND_UP (strlen (".")) + ROUND_UP (strlen (".."))) + +/* old format directories have this size when empty */ +#define EMPTY_DIR_SIZE_V1 (DEH_SIZE * 2 + 3) + +#define DEH_Statdata 0 /* not used now */ +#define DEH_Visible 2 + +/* 64 bit systems (and the S/390) need to be aligned explicitly -jdm */ +#if BITS_PER_LONG == 64 || defined(__s390__) || defined(__hppa__) +# define ADDR_UNALIGNED_BITS (3) +#endif + +/* These are only used to manipulate deh_state. + * Because of this, we'll use the ext2_ bit routines, + * since they are little endian */ +#ifdef ADDR_UNALIGNED_BITS + +# define aligned_address(addr) ((void *)((long)(addr) & ~((1UL << ADDR_UNALIGNED_BITS) - 1))) +# define unaligned_offset(addr) (((int)((long)(addr) & ((1 << ADDR_UNALIGNED_BITS) - 1))) << 3) + +# define set_bit_unaligned(nr, addr) ext2_set_bit((nr) + unaligned_offset(addr), aligned_address(addr)) +# define clear_bit_unaligned(nr, addr) ext2_clear_bit((nr) + unaligned_offset(addr), aligned_address(addr)) +# define test_bit_unaligned(nr, addr) ext2_test_bit((nr) + unaligned_offset(addr), aligned_address(addr)) + +#else + +# define set_bit_unaligned(nr, addr) ext2_set_bit(nr, addr) +# define clear_bit_unaligned(nr, addr) ext2_clear_bit(nr, addr) +# define test_bit_unaligned(nr, addr) ext2_test_bit(nr, addr) + +#endif + +#define mark_de_with_sd(deh) set_bit_unaligned (DEH_Statdata, &((deh)->deh_state)) +#define mark_de_without_sd(deh) clear_bit_unaligned (DEH_Statdata, &((deh)->deh_state)) +#define mark_de_visible(deh) set_bit_unaligned (DEH_Visible, &((deh)->deh_state)) +#define mark_de_hidden(deh) clear_bit_unaligned (DEH_Visible, &((deh)->deh_state)) + +#define de_with_sd(deh) test_bit_unaligned (DEH_Statdata, &((deh)->deh_state)) +#define de_visible(deh) test_bit_unaligned (DEH_Visible, &((deh)->deh_state)) +#define de_hidden(deh) !test_bit_unaligned (DEH_Visible, &((deh)->deh_state)) + +/* +extern void make_empty_dir_item_v1(char *body, __le32 dirid, __le32 objid, + __le32 par_dirid, __le32 par_objid); +extern void make_empty_dir_item(char *body, __le32 dirid, __le32 objid, + __le32 par_dirid, __le32 par_objid); +*/ + +/* array of the entry headers */ + /* get item body */ +#define B_I_PITEM(bh,ih) ( (bh)->b_data + ih_location(ih) ) +#define B_I_DEH(bh,ih) ((struct reiserfs_de_head *)(B_I_PITEM(bh,ih))) + +/* length of the directory entry in directory item. This define + calculates length of i-th directory entry using directory entry + locations from dir entry head. When it calculates length of 0-th + directory entry, it uses length of whole item in place of entry + location of the non-existent following entry in the calculation. + See picture above.*/ +/* +#define I_DEH_N_ENTRY_LENGTH(ih,deh,i) \ +((i) ? (deh_location((deh)-1) - deh_location((deh))) : (ih_item_len((ih)) - deh_location((deh)))) +*/ +/* +static inline int entry_length(const struct buffer_head *bh, + const struct item_head *ih, int pos_in_item) +{ + struct reiserfs_de_head *deh; + + deh = B_I_DEH(bh, ih) + pos_in_item; + if (pos_in_item) + return deh_location(deh - 1) - deh_location(deh); + + return ih_item_len(ih) - deh_location(deh); +} +*/ + +/* number of entries in the directory item, depends on ENTRY_COUNT being at the start of directory dynamic data. */ +#define I_ENTRY_COUNT(ih) (ih_entry_count((ih))) + +/* name by bh, ih and entry_num */ +#define B_I_E_NAME(bh,ih,entry_num) ((char *)(bh->b_data + ih_location(ih) + deh_location(B_I_DEH(bh,ih)+(entry_num)))) + +// two entries per block (at least) +#define REISERFS_MAX_NAME(block_size) 255 + +/* this structure is used for operations on directory entries. It is + not a disk structure. */ +/* When reiserfs_find_entry or search_by_entry_key find directory + entry, they return filled reiserfs_dir_entry structure */ +struct reiserfs_dir_entry { + struct buffer_head *de_bh; + int de_item_num; + struct item_head *de_ih; + int de_entry_num; + struct reiserfs_de_head *de_deh; + int de_entrylen; + int de_namelen; + char *de_name; + unsigned long *de_gen_number_bit_string; + + __u32 de_dir_id; + __u32 de_objectid; + + struct cpu_key de_entry_key; +}; + +/* these defines are useful when a particular member of a reiserfs_dir_entry is needed */ + +/* pointer to file name, stored in entry */ +#define B_I_DEH_ENTRY_FILE_NAME(bh,ih,deh) (B_I_PITEM (bh, ih) + deh_location(deh)) + +/* length of name */ +#define I_DEH_N_ENTRY_FILE_NAME_LENGTH(ih,deh,entry_num) \ +(I_DEH_N_ENTRY_LENGTH (ih, deh, entry_num) - (de_with_sd (deh) ? SD_SIZE : 0)) + +/* hash value occupies bits from 7 up to 30 */ +#define GET_HASH_VALUE(offset) ((offset) & 0x7fffff80LL) +/* generation number occupies 7 bits starting from 0 up to 6 */ +#define GET_GENERATION_NUMBER(offset) ((offset) & 0x7fLL) +#define MAX_GENERATION_NUMBER 127 + +#define SET_GENERATION_NUMBER(offset,gen_number) (GET_HASH_VALUE(offset)|(gen_number)) + +/* + * Picture represents an internal node of the reiserfs tree + * ______________________________________________________ + * | | Array of | Array of | Free | + * |block | keys | pointers | space | + * | head | N | N+1 | | + * |______|_______________|___________________|___________| + */ + +/***************************************************************************/ +/* DISK CHILD */ +/***************************************************************************/ +/* Disk child pointer: The pointer from an internal node of the tree + to a node that is on disk. */ +struct disk_child { + __le32 dc_block_number; /* Disk child's block number. */ + __le16 dc_size; /* Disk child's used space. */ + __le16 dc_reserved; +}; + +#define DC_SIZE (sizeof(struct disk_child)) +#define dc_block_number(dc_p) (le32_to_cpu((dc_p)->dc_block_number)) +#define dc_size(dc_p) (le16_to_cpu((dc_p)->dc_size)) + +/* Get disk child by buffer header and position in the tree node. */ +#define B_N_CHILD(p_s_bh,n_pos) ((struct disk_child *)\ +((p_s_bh)->b_data+BLKH_SIZE+B_NR_ITEMS(p_s_bh)*KEY_SIZE+DC_SIZE*(n_pos))) + +/* Get disk child number by buffer header and position in the tree node. */ +#define B_N_CHILD_NUM(p_s_bh,n_pos) (dc_block_number(B_N_CHILD(p_s_bh,n_pos))) +#define PUT_B_N_CHILD_NUM(p_s_bh,n_pos, val) (put_dc_block_number(B_N_CHILD(p_s_bh,n_pos), val )) + + /* maximal value of field child_size in structure disk_child */ + /* child size is the combined size of all items and their headers */ +#define MAX_CHILD_SIZE(bh) ((int)( (bh)->b_size - BLKH_SIZE )) + +/* amount of used space in buffer (not including block head) */ +#define B_CHILD_SIZE(cur) (MAX_CHILD_SIZE(cur)-(B_FREE_SPACE(cur))) + +/* max and min number of keys in internal node */ +#define MAX_NR_KEY(bh) ( (MAX_CHILD_SIZE(bh)-DC_SIZE)/(KEY_SIZE+DC_SIZE) ) +#define MIN_NR_KEY(bh) (MAX_NR_KEY(bh)/2) + +/***************************************************************************/ +/* PATH STRUCTURES AND DEFINES */ +/***************************************************************************/ + +/* Search_by_key fills up the path from the root to the leaf as it descends the tree looking for the + key. It uses reiserfs_bread to try to find buffers in the cache given their block number. If it + does not find them in the cache it reads them from disk. For each node search_by_key finds using + reiserfs_bread it then uses bin_search to look through that node. bin_search will find the + position of the block_number of the next node if it is looking through an internal node. If it + is looking through a leaf node bin_search will find the position of the item which has key either + equal to given key, or which is the maximal key less than the given key. */ + +struct path_element { + struct buffer_head *pe_buffer; /* Pointer to the buffer at the path in the tree. */ + int pe_position; /* Position in the tree node which is placed in the */ + /* buffer above. */ +}; + +#define MAX_HEIGHT 5 /* maximal height of a tree. don't change this without changing JOURNAL_PER_BALANCE_CNT */ +#define EXTENDED_MAX_HEIGHT 7 /* Must be equals MAX_HEIGHT + FIRST_PATH_ELEMENT_OFFSET */ +#define FIRST_PATH_ELEMENT_OFFSET 2 /* Must be equal to at least 2. */ + +#define ILLEGAL_PATH_ELEMENT_OFFSET 1 /* Must be equal to FIRST_PATH_ELEMENT_OFFSET - 1 */ +#define MAX_FEB_SIZE 6 /* this MUST be MAX_HEIGHT + 1. See about FEB below */ + +/* We need to keep track of who the ancestors of nodes are. When we + perform a search we record which nodes were visited while + descending the tree looking for the node we searched for. This list + of nodes is called the path. This information is used while + performing balancing. Note that this path information may become + invalid, and this means we must check it when using it to see if it + is still valid. You'll need to read search_by_key and the comments + in it, especially about decrement_counters_in_path(), to understand + this structure. + +Paths make the code so much harder to work with and debug.... An +enormous number of bugs are due to them, and trying to write or modify +code that uses them just makes my head hurt. They are based on an +excessive effort to avoid disturbing the precious VFS code.:-( The +gods only know how we are going to SMP the code that uses them. +znodes are the way! */ + +#define PATH_READA 0x1 /* do read ahead */ +#define PATH_READA_BACK 0x2 /* read backwards */ + +struct path { + int path_length; /* Length of the array above. */ + int reada; + struct path_element path_elements[EXTENDED_MAX_HEIGHT]; /* Array of the path elements. */ + int pos_in_item; +}; + +#define pos_in_item(path) ((path)->pos_in_item) + +#define INITIALIZE_PATH(var) \ +struct path var = {.path_length = ILLEGAL_PATH_ELEMENT_OFFSET, .reada = 0,} + +/* Get path element by path and path position. */ +#define PATH_OFFSET_PELEMENT(p_s_path,n_offset) ((p_s_path)->path_elements +(n_offset)) + +/* Get buffer header at the path by path and path position. */ +#define PATH_OFFSET_PBUFFER(p_s_path,n_offset) (PATH_OFFSET_PELEMENT(p_s_path,n_offset)->pe_buffer) + +/* Get position in the element at the path by path and path position. */ +#define PATH_OFFSET_POSITION(p_s_path,n_offset) (PATH_OFFSET_PELEMENT(p_s_path,n_offset)->pe_position) + +#define PATH_PLAST_BUFFER(p_s_path) (PATH_OFFSET_PBUFFER((p_s_path), (p_s_path)->path_length)) + /* you know, to the person who didn't + write this the macro name does not + at first suggest what it does. + Maybe POSITION_FROM_PATH_END? Or + maybe we should just focus on + dumping paths... -Hans */ +#define PATH_LAST_POSITION(p_s_path) (PATH_OFFSET_POSITION((p_s_path), (p_s_path)->path_length)) + +#define PATH_PITEM_HEAD(p_s_path) B_N_PITEM_HEAD(PATH_PLAST_BUFFER(p_s_path),PATH_LAST_POSITION(p_s_path)) + +/* in do_balance leaf has h == 0 in contrast with path structure, + where root has level == 0. That is why we need these defines */ +#define PATH_H_PBUFFER(p_s_path, h) PATH_OFFSET_PBUFFER (p_s_path, p_s_path->path_length - (h)) /* tb->S[h] */ +#define PATH_H_PPARENT(path, h) PATH_H_PBUFFER (path, (h) + 1) /* tb->F[h] or tb->S[0]->b_parent */ +#define PATH_H_POSITION(path, h) PATH_OFFSET_POSITION (path, path->path_length - (h)) +#define PATH_H_B_ITEM_ORDER(path, h) PATH_H_POSITION(path, h + 1) /* tb->S[h]->b_item_order */ + +#define PATH_H_PATH_OFFSET(p_s_path, n_h) ((p_s_path)->path_length - (n_h)) + +#define get_last_bh(path) PATH_PLAST_BUFFER(path) +#define get_ih(path) PATH_PITEM_HEAD(path) +#define get_item_pos(path) PATH_LAST_POSITION(path) +#define get_item(path) ((void *)B_N_PITEM(PATH_PLAST_BUFFER(path), PATH_LAST_POSITION (path))) +#define item_moved(ih,path) comp_items(ih, path) +#define path_changed(ih,path) comp_items (ih, path) + +/***************************************************************************/ +/* MISC */ +/***************************************************************************/ + +/* Size of pointer to the unformatted node. */ +#define UNFM_P_SIZE (sizeof(unp_t)) +#define UNFM_P_SHIFT 2 + +// in in-core inode key is stored on le form +#define INODE_PKEY(inode) ((struct reiserfs_key *)(REISERFS_I(inode)->i_key)) + +#define MAX_UL_INT 0xffffffff +#define MAX_INT 0x7ffffff +#define MAX_US_INT 0xffff + +// reiserfs version 2 has max offset 60 bits. Version 1 - 32 bit offset +#define U32_MAX (~(__u32)0) + +/* +static inline loff_t max_reiserfs_offset(struct inode *inode) +{ + if (get_inode_item_key_version(inode) == KEY_FORMAT_3_5) + return (loff_t) U32_MAX; + + return (loff_t) ((~(__u64) 0) >> 4); +} +*/ + +/*#define MAX_KEY_UNIQUENESS MAX_UL_INT*/ +#define MAX_KEY_OBJECTID MAX_UL_INT + +#define MAX_B_NUM MAX_UL_INT +#define MAX_FC_NUM MAX_US_INT + +/* the purpose is to detect overflow of an unsigned short */ +#define REISERFS_LINK_MAX (MAX_US_INT - 1000) + +/* The following defines are used in reiserfs_insert_item and reiserfs_append_item */ +#define REISERFS_KERNEL_MEM 0 /* reiserfs kernel memory mode */ +#define REISERFS_USER_MEM 1 /* reiserfs user memory mode */ + +#define fs_generation(s) (REISERFS_SB(s)->s_generation_counter) +#define get_generation(s) atomic_read (&fs_generation(s)) +#define FILESYSTEM_CHANGED_TB(tb) (get_generation((tb)->tb_sb) != (tb)->fs_gen) +#define __fs_changed(gen,s) (gen != get_generation (s)) +#define fs_changed(gen,s) ({cond_resched(); __fs_changed(gen, s);}) + +/***************************************************************************/ +/* FIXATE NODES */ +/***************************************************************************/ + +#define VI_TYPE_LEFT_MERGEABLE 1 +#define VI_TYPE_RIGHT_MERGEABLE 2 + +/* To make any changes in the tree we always first find node, that + contains item to be changed/deleted or place to insert a new + item. We call this node S. To do balancing we need to decide what + we will shift to left/right neighbor, or to a new node, where new + item will be etc. To make this analysis simpler we build virtual + node. Virtual node is an array of items, that will replace items of + node S. (For instance if we are going to delete an item, virtual + node does not contain it). Virtual node keeps information about + item sizes and types, mergeability of first and last items, sizes + of all entries in directory item. We use this array of items when + calculating what we can shift to neighbors and how many nodes we + have to have if we do not any shiftings, if we shift to left/right + neighbor or to both. */ +struct virtual_item { + int vi_index; // index in the array of item operations + unsigned short vi_type; // left/right mergeability + unsigned short vi_item_len; /* length of item that it will have after balancing */ + struct item_head *vi_ih; + const char *vi_item; // body of item (old or new) + const void *vi_new_data; // 0 always but paste mode + void *vi_uarea; // item specific area +}; + +struct virtual_node { + char *vn_free_ptr; /* this is a pointer to the free space in the buffer */ + unsigned short vn_nr_item; /* number of items in virtual node */ + short vn_size; /* size of node , that node would have if it has unlimited size and no balancing is performed */ + short vn_mode; /* mode of balancing (paste, insert, delete, cut) */ + short vn_affected_item_num; + short vn_pos_in_item; + struct item_head *vn_ins_ih; /* item header of inserted item, 0 for other modes */ + const void *vn_data; + struct virtual_item *vn_vi; /* array of items (including a new one, excluding item to be deleted) */ +}; + +/* used by directory items when creating virtual nodes */ +struct direntry_uarea { + int flags; + __u16 entry_count; + __u16 entry_sizes[1]; +} ATTR_PACKED; + +/***************************************************************************/ +/* TREE BALANCE */ +/***************************************************************************/ + +/* This temporary structure is used in tree balance algorithms, and + constructed as we go to the extent that its various parts are + needed. It contains arrays of nodes that can potentially be + involved in the balancing of node S, and parameters that define how + each of the nodes must be balanced. Note that in these algorithms + for balancing the worst case is to need to balance the current node + S and the left and right neighbors and all of their parents plus + create a new node. We implement S1 balancing for the leaf nodes + and S0 balancing for the internal nodes (S1 and S0 are defined in + our papers.)*/ + +#define MAX_FREE_BLOCK 7 /* size of the array of buffers to free at end of do_balance */ + +/* maximum number of FEB blocknrs on a single level */ +#define MAX_AMOUNT_NEEDED 2 + +/* someday somebody will prefix every field in this struct with tb_ */ +struct tree_balance { + int tb_mode; + int need_balance_dirty; + struct super_block *tb_sb; + struct reiserfs_transaction_handle *transaction_handle; + struct path *tb_path; + struct buffer_head *L[MAX_HEIGHT]; /* array of left neighbors of nodes in the path */ + struct buffer_head *R[MAX_HEIGHT]; /* array of right neighbors of nodes in the path */ + struct buffer_head *FL[MAX_HEIGHT]; /* array of fathers of the left neighbors */ + struct buffer_head *FR[MAX_HEIGHT]; /* array of fathers of the right neighbors */ + struct buffer_head *CFL[MAX_HEIGHT]; /* array of common parents of center node and its left neighbor */ + struct buffer_head *CFR[MAX_HEIGHT]; /* array of common parents of center node and its right neighbor */ + + struct buffer_head *FEB[MAX_FEB_SIZE]; /* array of empty buffers. Number of buffers in array equals + cur_blknum. */ + struct buffer_head *used[MAX_FEB_SIZE]; + struct buffer_head *thrown[MAX_FEB_SIZE]; + int lnum[MAX_HEIGHT]; /* array of number of items which must be + shifted to the left in order to balance the + current node; for leaves includes item that + will be partially shifted; for internal + nodes, it is the number of child pointers + rather than items. It includes the new item + being created. The code sometimes subtracts + one to get the number of wholly shifted + items for other purposes. */ + int rnum[MAX_HEIGHT]; /* substitute right for left in comment above */ + int lkey[MAX_HEIGHT]; /* array indexed by height h mapping the key delimiting L[h] and + S[h] to its item number within the node CFL[h] */ + int rkey[MAX_HEIGHT]; /* substitute r for l in comment above */ + int insert_size[MAX_HEIGHT]; /* the number of bytes by we are trying to add or remove from + S[h]. A negative value means removing. */ + int blknum[MAX_HEIGHT]; /* number of nodes that will replace node S[h] after + balancing on the level h of the tree. If 0 then S is + being deleted, if 1 then S is remaining and no new nodes + are being created, if 2 or 3 then 1 or 2 new nodes is + being created */ + + /* fields that are used only for balancing leaves of the tree */ + int cur_blknum; /* number of empty blocks having been already allocated */ + int s0num; /* number of items that fall into left most node when S[0] splits */ + int s1num; /* number of items that fall into first new node when S[0] splits */ + int s2num; /* number of items that fall into second new node when S[0] splits */ + int lbytes; /* number of bytes which can flow to the left neighbor from the left */ + /* most liquid item that cannot be shifted from S[0] entirely */ + /* if -1 then nothing will be partially shifted */ + int rbytes; /* number of bytes which will flow to the right neighbor from the right */ + /* most liquid item that cannot be shifted from S[0] entirely */ + /* if -1 then nothing will be partially shifted */ + int s1bytes; /* number of bytes which flow to the first new node when S[0] splits */ + /* note: if S[0] splits into 3 nodes, then items do not need to be cut */ + int s2bytes; + struct buffer_head *buf_to_free[MAX_FREE_BLOCK]; /* buffers which are to be freed after do_balance finishes by unfix_nodes */ + char *vn_buf; /* kmalloced memory. Used to create + virtual node and keep map of + dirtied bitmap blocks */ + int vn_buf_size; /* size of the vn_buf */ + struct virtual_node *tb_vn; /* VN starts after bitmap of bitmap blocks */ + + int fs_gen; /* saved value of `reiserfs_generation' counter + see FILESYSTEM_CHANGED() macro in reiserfs_fs.h */ +#ifdef DISPLACE_NEW_PACKING_LOCALITIES + struct in_core_key key; /* key pointer, to pass to block allocator or + another low-level subsystem */ +#endif +}; + +/* These are modes of balancing */ + +/* When inserting an item. */ +#define M_INSERT 'i' +/* When inserting into (directories only) or appending onto an already + existant item. */ +#define M_PASTE 'p' +/* When deleting an item. */ +#define M_DELETE 'd' +/* When truncating an item or removing an entry from a (directory) item. */ +#define M_CUT 'c' + +/* used when balancing on leaf level skipped (in reiserfsck) */ +#define M_INTERNAL 'n' + +/* When further balancing is not needed, then do_balance does not need + to be called. */ +#define M_SKIP_BALANCING 's' +#define M_CONVERT 'v' + +/* modes of leaf_move_items */ +#define LEAF_FROM_S_TO_L 0 +#define LEAF_FROM_S_TO_R 1 +#define LEAF_FROM_R_TO_L 2 +#define LEAF_FROM_L_TO_R 3 +#define LEAF_FROM_S_TO_SNEW 4 + +#define FIRST_TO_LAST 0 +#define LAST_TO_FIRST 1 + +/* used in do_balance for passing parent of node information that has + been gotten from tb struct */ +struct buffer_info { + struct tree_balance *tb; + struct buffer_head *bi_bh; + struct buffer_head *bi_parent; + int bi_position; +}; + +/* there are 4 types of items: stat data, directory item, indirect, direct. ++-------------------+------------+--------------+------------+ +| | k_offset | k_uniqueness | mergeable? | ++-------------------+------------+--------------+------------+ +| stat data | 0 | 0 | no | ++-------------------+------------+--------------+------------+ +| 1st directory item| DOT_OFFSET |DIRENTRY_UNIQUENESS| no | +| non 1st directory | hash value | | yes | +| item | | | | ++-------------------+------------+--------------+------------+ +| indirect item | offset + 1 |TYPE_INDIRECT | if this is not the first indirect item of the object ++-------------------+------------+--------------+------------+ +| direct item | offset + 1 |TYPE_DIRECT | if not this is not the first direct item of the object ++-------------------+------------+--------------+------------+ +*/ + +struct item_operations { + int (*bytes_number) (struct item_head * ih, int block_size); + void (*decrement_key) (struct cpu_key *); + int (*is_left_mergeable) (struct reiserfs_key * ih, + unsigned long bsize); + void (*print_item) (struct item_head *, char *item); + void (*check_item) (struct item_head *, char *item); + + int (*create_vi) (struct virtual_node * vn, struct virtual_item * vi, + int is_affected, int insert_size); + int (*check_left) (struct virtual_item * vi, int free, + int start_skip, int end_skip); + int (*check_right) (struct virtual_item * vi, int free); + int (*part_size) (struct virtual_item * vi, int from, int to); + int (*unit_num) (struct virtual_item * vi); + void (*print_vi) (struct virtual_item * vi); +}; + +extern struct item_operations *item_ops[TYPE_ANY + 1]; + +#define op_bytes_number(ih,bsize) item_ops[le_ih_k_type (ih)]->bytes_number (ih, bsize) +#define op_is_left_mergeable(key,bsize) item_ops[le_key_k_type (le_key_version (key), key)]->is_left_mergeable (key, bsize) +#define op_print_item(ih,item) item_ops[le_ih_k_type (ih)]->print_item (ih, item) +#define op_check_item(ih,item) item_ops[le_ih_k_type (ih)]->check_item (ih, item) +#define op_create_vi(vn,vi,is_affected,insert_size) item_ops[le_ih_k_type ((vi)->vi_ih)]->create_vi (vn,vi,is_affected,insert_size) +#define op_check_left(vi,free,start_skip,end_skip) item_ops[(vi)->vi_index]->check_left (vi, free, start_skip, end_skip) +#define op_check_right(vi,free) item_ops[(vi)->vi_index]->check_right (vi, free) +#define op_part_size(vi,from,to) item_ops[(vi)->vi_index]->part_size (vi, from, to) +#define op_unit_num(vi) item_ops[(vi)->vi_index]->unit_num (vi) +#define op_print_vi(vi) item_ops[(vi)->vi_index]->print_vi (vi) + +#define COMP_SHORT_KEYS comp_short_keys + +/* number of blocks pointed to by the indirect item */ +#define I_UNFM_NUM(p_s_ih) ( ih_item_len(p_s_ih) / UNFM_P_SIZE ) + +/* the used space within the unformatted node corresponding to pos within the item pointed to by ih */ +#define I_POS_UNFM_SIZE(ih,pos,size) (((pos) == I_UNFM_NUM(ih) - 1 ) ? (size) - ih_free_space(ih) : (size)) + +/* number of bytes contained by the direct item or the unformatted nodes the indirect item points to */ + +/* get the item header */ +#define B_N_PITEM_HEAD(bh,item_num) ( (struct item_head * )((bh)->b_data + BLKH_SIZE) + (item_num) ) + +/* get key */ +#define B_N_PDELIM_KEY(bh,item_num) ( (struct reiserfs_key * )((bh)->b_data + BLKH_SIZE) + (item_num) ) + +/* get the key */ +#define B_N_PKEY(bh,item_num) ( &(B_N_PITEM_HEAD(bh,item_num)->ih_key) ) + +/* get item body */ +#define B_N_PITEM(bh,item_num) ( (bh)->b_data + ih_location(B_N_PITEM_HEAD((bh),(item_num)))) + +/* get the stat data by the buffer header and the item order */ +#define B_N_STAT_DATA(bh,nr) \ +( (struct stat_data *)((bh)->b_data + ih_location(B_N_PITEM_HEAD((bh),(nr))) ) ) + + /* following defines use reiserfs buffer header and item header */ + +/* get stat-data */ +#define B_I_STAT_DATA(bh, ih) ( (struct stat_data * )((bh)->b_data + ih_location(ih)) ) + +// this is 3976 for size==4096 +#define MAX_DIRECT_ITEM_LEN(size) ((size) - BLKH_SIZE - 2*IH_SIZE - SD_SIZE - UNFM_P_SIZE) + +/* indirect items consist of entries which contain blocknrs, pos + indicates which entry, and B_I_POS_UNFM_POINTER resolves to the + blocknr contained by the entry pos points to */ +#define B_I_POS_UNFM_POINTER(bh,ih,pos) le32_to_cpu(*(((unp_t *)B_I_PITEM(bh,ih)) + (pos))) +#define PUT_B_I_POS_UNFM_POINTER(bh,ih,pos, val) do {*(((unp_t *)B_I_PITEM(bh,ih)) + (pos)) = cpu_to_le32(val); } while (0) + +struct reiserfs_iget_args { + __u32 objectid; + __u32 dirid; +}; + +/***************************************************************************/ +/* FUNCTION DECLARATIONS */ +/***************************************************************************/ + +/*#ifdef __KERNEL__*/ +#define get_journal_desc_magic(bh) (bh->b_data + bh->b_size - 12) + +#define journal_trans_half(blocksize) \ + ((blocksize - sizeof (struct reiserfs_journal_desc) + sizeof (__u32) - 12) / sizeof (__u32)) + +/* journal.c see journal.c for all the comments here */ + +/* first block written in a commit. */ +struct reiserfs_journal_desc { + __le32 j_trans_id; /* id of commit */ + __le32 j_len; /* length of commit. len +1 is the commit block */ + __le32 j_mount_id; /* mount id of this trans */ + __le32 j_realblock[1]; /* real locations for each block */ +}; + +#define get_desc_trans_id(d) le32_to_cpu((d)->j_trans_id) +#define get_desc_trans_len(d) le32_to_cpu((d)->j_len) +#define get_desc_mount_id(d) le32_to_cpu((d)->j_mount_id) + +#define set_desc_trans_id(d,val) do { (d)->j_trans_id = cpu_to_le32 (val); } while (0) +#define set_desc_trans_len(d,val) do { (d)->j_len = cpu_to_le32 (val); } while (0) +#define set_desc_mount_id(d,val) do { (d)->j_mount_id = cpu_to_le32 (val); } while (0) + +/* last block written in a commit */ +struct reiserfs_journal_commit { + __le32 j_trans_id; /* must match j_trans_id from the desc block */ + __le32 j_len; /* ditto */ + __le32 j_realblock[1]; /* real locations for each block */ +}; + +#define get_commit_trans_id(c) le32_to_cpu((c)->j_trans_id) +#define get_commit_trans_len(c) le32_to_cpu((c)->j_len) +#define get_commit_mount_id(c) le32_to_cpu((c)->j_mount_id) + +#define set_commit_trans_id(c,val) do { (c)->j_trans_id = cpu_to_le32 (val); } while (0) +#define set_commit_trans_len(c,val) do { (c)->j_len = cpu_to_le32 (val); } while (0) + +/* this header block gets written whenever a transaction is considered fully flushed, and is more recent than the +** last fully flushed transaction. fully flushed means all the log blocks and all the real blocks are on disk, +** and this transaction does not need to be replayed. +*/ +struct reiserfs_journal_header { + __le32 j_last_flush_trans_id; /* id of last fully flushed transaction */ + __le32 j_first_unflushed_offset; /* offset in the log of where to start replay after a crash */ + __le32 j_mount_id; + /* 12 */ struct journal_params jh_journal; +}; + +/* biggest tunable defines are right here */ +#define JOURNAL_BLOCK_COUNT 8192 /* number of blocks in the journal */ +#define JOURNAL_TRANS_MAX_DEFAULT 1024 /* biggest possible single transaction, don't change for now (8/3/99) */ +#define JOURNAL_TRANS_MIN_DEFAULT 256 +#define JOURNAL_MAX_BATCH_DEFAULT 900 /* max blocks to batch into one transaction, don't make this any bigger than 900 */ +#define JOURNAL_MIN_RATIO 2 +#define JOURNAL_MAX_COMMIT_AGE 30 +#define JOURNAL_MAX_TRANS_AGE 30 +#define JOURNAL_PER_BALANCE_CNT (3 * (MAX_HEIGHT-2) + 9) +#ifdef CONFIG_QUOTA +/* We need to update data and inode (atime) */ +#define REISERFS_QUOTA_TRANS_BLOCKS(s) (REISERFS_SB(s)->s_mount_opt & (1<s_mount_opt & (1<s_mount_opt & (1< max, the node is freed, otherwise, +** it is put on a free list for faster use later. +*/ +#define REISERFS_MIN_BITMAP_NODES 10 +#define REISERFS_MAX_BITMAP_NODES 100 + +#define JBH_HASH_SHIFT 13 /* these are based on journal hash size of 8192 */ +#define JBH_HASH_MASK 8191 + +#define _jhashfn(sb,block) \ + (((unsigned long)sb>>L1_CACHE_SHIFT) ^ \ + (((block)<<(JBH_HASH_SHIFT - 6)) ^ ((block) >> 13) ^ ((block) << (JBH_HASH_SHIFT - 12)))) +#define journal_hash(t,sb,block) ((t)[_jhashfn((sb),(block)) & JBH_HASH_MASK]) + +// We need these to make journal.c code more readable +#define journal_find_get_block(s, block) __find_get_block(SB_JOURNAL(s)->j_dev_bd, block, s->s_blocksize) +#define journal_getblk(s, block) __getblk(SB_JOURNAL(s)->j_dev_bd, block, s->s_blocksize) +#define journal_bread(s, block) __bread(SB_JOURNAL(s)->j_dev_bd, block, s->s_blocksize) + +//enum reiserfs_bh_state_bits { +// BH_JDirty = BH_PrivateStart, /* buffer is in current transaction */ +// BH_JDirty_wait, +// BH_JNew, /* disk block was taken off free list before +// * being in a finished transaction, or +// * written to disk. Can be reused immed. */ +// BH_JPrepared, +// BH_JRestore_dirty, +// BH_JTest, // debugging only will go away +//}; + +/* +BUFFER_FNS(JDirty, journaled); +TAS_BUFFER_FNS(JDirty, journaled); +BUFFER_FNS(JDirty_wait, journal_dirty); +TAS_BUFFER_FNS(JDirty_wait, journal_dirty); +BUFFER_FNS(JNew, journal_new); +TAS_BUFFER_FNS(JNew, journal_new); +BUFFER_FNS(JPrepared, journal_prepared); +TAS_BUFFER_FNS(JPrepared, journal_prepared); +BUFFER_FNS(JRestore_dirty, journal_restore_dirty); +TAS_BUFFER_FNS(JRestore_dirty, journal_restore_dirty); +BUFFER_FNS(JTest, journal_test); +TAS_BUFFER_FNS(JTest, journal_test); +*/ + +/* +** transaction handle which is passed around for all journal calls +*/ +struct reiserfs_transaction_handle { + struct super_block *t_super; /* super for this FS when journal_begin was + called. saves calls to reiserfs_get_super + also used by nested transactions to make + sure they are nesting on the right FS + _must_ be first in the handle + */ + int t_refcount; + int t_blocks_logged; /* number of blocks this writer has logged */ + int t_blocks_allocated; /* number of blocks this writer allocated */ + unsigned long t_trans_id; /* sanity check, equals the current trans id */ + void *t_handle_save; /* save existing current->journal_info */ + unsigned displace_new_blocks:1; /* if new block allocation occurres, that block + should be displaced from others */ + //struct list_head t_list; +}; + +/* used to keep track of ordered and tail writes, attached to the buffer + * head through b_journal_head. + */ +struct reiserfs_jh { + struct reiserfs_journal_list *jl; + struct buffer_head *bh; + //struct list_head list; +}; + +// +// get key version from on disk key - kludge +// +/* +static inline int le_key_version(const struct reiserfs_key *key) +{ + int type; + + type = offset_v2_k_type(&(key->u.k_offset_v2)); + if (type != TYPE_DIRECT && type != TYPE_INDIRECT + && type != TYPE_DIRENTRY) + return KEY_FORMAT_3_5; + + return KEY_FORMAT_3_6; + +} + +static inline void copy_key(struct reiserfs_key *to, + const struct reiserfs_key *from) +{ + memcpy(to, from, KEY_SIZE); +} +*/ + +#define i_block_size(inode) ((inode)->i_sb->s_blocksize) +#define file_size(inode) ((inode)->i_size) +#define tail_size(inode) (file_size (inode) & (i_block_size (inode) - 1)) + +#define tail_has_to_be_packed(inode) (have_large_tails ((inode)->i_sb)?\ +!STORE_TAIL_IN_UNFM_S1(file_size (inode), tail_size(inode), inode->i_sb->s_blocksize):have_small_tails ((inode)->i_sb)?!STORE_TAIL_IN_UNFM_S2(file_size (inode), tail_size(inode), inode->i_sb->s_blocksize):0 ) + +/* inode.c */ +/* args for the create parameter of reiserfs_get_block */ +#define GET_BLOCK_NO_CREATE 0 /* don't create new blocks or convert tails */ +#define GET_BLOCK_CREATE 1 /* add anything you need to find block */ +#define GET_BLOCK_NO_HOLE 2 /* return -ENOENT for file holes */ +#define GET_BLOCK_READ_DIRECT 4 /* read the tail if indirect item not found */ +#define GET_BLOCK_NO_IMUX 8 /* i_mutex is not held, don't preallocate */ +#define GET_BLOCK_NO_DANGLE 16 /* don't leave any transactions running */ + +/* bitmap.c */ + +/* structure contains hints for block allocator, and it is a container for + * arguments, such as node, search path, transaction_handle, etc. */ +struct __reiserfs_blocknr_hint { + struct inode *inode; /* inode passed to allocator, if we allocate unf. nodes */ + long block; /* file offset, in blocks */ + struct in_core_key key; + struct path *path; /* search path, used by allocator to deternine search_start by + * various ways */ + struct reiserfs_transaction_handle *th; /* transaction handle is needed to log super blocks and + * bitmap blocks changes */ + b_blocknr_t beg, end; + b_blocknr_t search_start; /* a field used to transfer search start value (block number) + * between different block allocator procedures + * (determine_search_start() and others) */ + int prealloc_size; /* is set in determine_prealloc_size() function, used by underlayed + * function that do actual allocation */ + + unsigned formatted_node:1; /* the allocator uses different polices for getting disk space for + * formatted/unformatted blocks with/without preallocation */ + unsigned preallocate:1; +}; + +typedef struct __reiserfs_blocknr_hint reiserfs_blocknr_hint_t; + +/* hashes.c */ +__u32 keyed_hash(const signed char *msg, int len); +__u32 yura_hash(const signed char *msg, int len); +__u32 r5_hash(const signed char *msg, int len); + +/* the ext2 bit routines adjust for big or little endian as +** appropriate for the arch, so in our laziness we use them rather +** than using the bit routines they call more directly. These +** routines must be used when changing on disk bitmaps. */ +#define reiserfs_test_and_set_le_bit ext2_set_bit +#define reiserfs_test_and_clear_le_bit ext2_clear_bit +#define reiserfs_test_le_bit ext2_test_bit +#define reiserfs_find_next_zero_le_bit ext2_find_next_zero_bit + +/* sometimes reiserfs_truncate may require to allocate few new blocks + to perform indirect2direct conversion. People probably used to + think, that truncate should work without problems on a filesystem + without free disk space. They may complain that they can not + truncate due to lack of free disk space. This spare space allows us + to not worry about it. 500 is probably too much, but it should be + absolutely safe */ +#define SPARE_SPACE 500 + +/* ioctl's command */ +#define REISERFS_IOC_UNPACK _IOW(0xCD,1,long) +/* define following flags to be the same as in ext2, so that chattr(1), + lsattr(1) will work with us. */ +#define REISERFS_IOC_GETFLAGS EXT2_IOC_GETFLAGS +#define REISERFS_IOC_SETFLAGS EXT2_IOC_SETFLAGS +#define REISERFS_IOC_GETVERSION EXT2_IOC_GETVERSION +#define REISERFS_IOC_SETVERSION EXT2_IOC_SETVERSION + + + +#pragma pack() + + +#endif diff --git a/filesystems/fsw_strfunc.h b/filesystems/fsw_strfunc.h new file mode 100644 index 0000000..61cf25d --- /dev/null +++ b/filesystems/fsw_strfunc.h @@ -0,0 +1,472 @@ +/* $Id: fsw_strfunc.h 29125 2010-05-06 09:43:05Z vboxsync $ */ +/** @file + * fsw_strfunc.h + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* fsw_strfunc.h generated by mk_fsw_strfunc.py */ + +static int fsw_streq_ISO88591_UTF8(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u8 *p2 = (fsw_u8 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + c2 = *p2++; + if ((c2 & 0xe0) == 0xc0) { + c2 = ((c2 & 0x1f) << 6) | (*p2++ & 0x3f); + } else if ((c2 & 0xf0) == 0xe0) { + c2 = ((c2 & 0x0f) << 12) | ((*p2++ & 0x3f) << 6); + c2 |= (*p2++ & 0x3f); + } else if ((c2 & 0xf8) == 0xf0) { + c2 = ((c2 & 0x07) << 18) | ((*p2++ & 0x3f) << 12); + c2 |= ((*p2++ & 0x3f) << 6); + c2 |= (*p2++ & 0x3f); + } + if (c1 != c2) + return 0; + } + return 1; +} + +#ifndef HOST_EFI +static int fsw_streq_ISO88591_UTF16(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + c2 = *p2++; + if (c1 != c2) + return 0; + } + return 1; +} +#endif + +static int fsw_streq_ISO88591_UTF16_SWAPPED(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2); + if (c1 != c2) + return 0; + } + return 1; +} + +static int fsw_streq_UTF8_UTF16(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + if ((c1 & 0xe0) == 0xc0) { + c1 = ((c1 & 0x1f) << 6) | (*p1++ & 0x3f); + } else if ((c1 & 0xf0) == 0xe0) { + c1 = ((c1 & 0x0f) << 12) | ((*p1++ & 0x3f) << 6); + c1 |= (*p1++ & 0x3f); + } else if ((c1 & 0xf8) == 0xf0) { + c1 = ((c1 & 0x07) << 18) | ((*p1++ & 0x3f) << 12); + c1 |= ((*p1++ & 0x3f) << 6); + c1 |= (*p1++ & 0x3f); + } + c2 = *p2++; + if (c1 != c2) + return 0; + } + return 1; +} + +static int fsw_streq_UTF8_UTF16_SWAPPED(void *s1data, void *s2data, int len) +{ + int i; + fsw_u8 *p1 = (fsw_u8 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + if ((c1 & 0xe0) == 0xc0) { + c1 = ((c1 & 0x1f) << 6) | (*p1++ & 0x3f); + } else if ((c1 & 0xf0) == 0xe0) { + c1 = ((c1 & 0x0f) << 12) | ((*p1++ & 0x3f) << 6); + c1 |= (*p1++ & 0x3f); + } else if ((c1 & 0xf8) == 0xf0) { + c1 = ((c1 & 0x07) << 18) | ((*p1++ & 0x3f) << 12); + c1 |= ((*p1++ & 0x3f) << 6); + c1 |= (*p1++ & 0x3f); + } + c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2); + if (c1 != c2) + return 0; + } + return 1; +} + +static int fsw_streq_UTF16_UTF16_SWAPPED(void *s1data, void *s2data, int len) +{ + int i; + fsw_u16 *p1 = (fsw_u16 *)s1data; + fsw_u16 *p2 = (fsw_u16 *)s2data; + fsw_u32 c1, c2; + + for (i = 0; i < len; i++) { + c1 = *p1++; + c2 = *p2++; c2 = FSW_SWAPVALUE_U16(c2); + if (c1 != c2) + return 0; + } + return 1; +} + +static fsw_status_t fsw_strcoerce_UTF8_ISO88591(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u8 *sp; + fsw_u8 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_ISO88591; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u8); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u8 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + if ((c & 0xe0) == 0xc0) { + c = ((c & 0x1f) << 6) | (*sp++ & 0x3f); + } else if ((c & 0xf0) == 0xe0) { + c = ((c & 0x0f) << 12) | ((*sp++ & 0x3f) << 6); + c |= (*sp++ & 0x3f); + } else if ((c & 0xf8) == 0xf0) { + c = ((c & 0x07) << 18) | ((*sp++ & 0x3f) << 12); + c |= ((*sp++ & 0x3f) << 6); + c |= (*sp++ & 0x3f); + } + *dp++ = (fsw_u8)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_ISO88591(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u16 *sp; + fsw_u8 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_ISO88591; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u8); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + *dp++ = (fsw_u8)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_ISO88591(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u16 *sp; + fsw_u8 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_ISO88591; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u8); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; c = FSW_SWAPVALUE_U16(c); + *dp++ = (fsw_u8)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_ISO88591_UTF16(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u8 *sp; + fsw_u16 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_UTF16; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u16); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u8 *)srcdata; + dp = (fsw_u16 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + *dp++ = (fsw_u16)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF8_UTF16(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u8 *sp; + fsw_u16 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_UTF16; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u16); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u8 *)srcdata; + dp = (fsw_u16 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + if ((c & 0xe0) == 0xc0) { + c = ((c & 0x1f) << 6) | (*sp++ & 0x3f); + } else if ((c & 0xf0) == 0xe0) { + c = ((c & 0x0f) << 12) | ((*sp++ & 0x3f) << 6); + c |= (*sp++ & 0x3f); + } else if ((c & 0xf8) == 0xf0) { + c = ((c & 0x07) << 18) | ((*sp++ & 0x3f) << 12); + c |= ((*sp++ & 0x3f) << 6); + c |= (*sp++ & 0x3f); + } + *dp++ = (fsw_u16)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_UTF16(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i; + fsw_u16 *sp; + fsw_u16 *dp; + fsw_u32 c; + + dest->type = FSW_STRING_TYPE_UTF16; + dest->len = srclen; + dest->size = srclen * sizeof(fsw_u16); + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u16 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; c = FSW_SWAPVALUE_U16(c); + *dp++ = (fsw_u16)c; + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_ISO88591_UTF8(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i, destsize; + fsw_u8 *sp; + fsw_u8 *dp; + fsw_u32 c; + + sp = (fsw_u8 *)srcdata; + destsize = 0; + for (i = 0; i < srclen; i++) { + c = *sp++; + + if (c < 0x000080) + destsize++; + else if (c < 0x000800) + destsize += 2; + else if (c < 0x010000) + destsize += 3; + else + destsize += 4; + } + + dest->type = FSW_STRING_TYPE_UTF8; + dest->len = srclen; + dest->size = destsize; + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u8 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + + if (c < 0x000080) { + *dp++ = (fsw_u8)c; + } else if (c < 0x000800) { + *dp++ = (fsw_u8)(0xc0 | ((c >> 6) & 0x1f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else if (c < 0x010000) { + *dp++ = (fsw_u8)(0xe0 | ((c >> 12) & 0x0f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else { + *dp++ = (fsw_u8)(0xf0 | ((c >> 18) & 0x07)); + *dp++ = (fsw_u8)(0x80 | ((c >> 12) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_UTF8(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i, destsize; + fsw_u16 *sp; + fsw_u8 *dp; + fsw_u32 c; + + sp = (fsw_u16 *)srcdata; + destsize = 0; + for (i = 0; i < srclen; i++) { + c = *sp++; + + if (c < 0x000080) + destsize++; + else if (c < 0x000800) + destsize += 2; + else if (c < 0x010000) + destsize += 3; + else + destsize += 4; + } + + dest->type = FSW_STRING_TYPE_UTF8; + dest->len = srclen; + dest->size = destsize; + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; + + if (c < 0x000080) { + *dp++ = (fsw_u8)c; + } else if (c < 0x000800) { + *dp++ = (fsw_u8)(0xc0 | ((c >> 6) & 0x1f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else if (c < 0x010000) { + *dp++ = (fsw_u8)(0xe0 | ((c >> 12) & 0x0f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else { + *dp++ = (fsw_u8)(0xf0 | ((c >> 18) & 0x07)); + *dp++ = (fsw_u8)(0x80 | ((c >> 12) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } + } + return FSW_SUCCESS; +} + +static fsw_status_t fsw_strcoerce_UTF16_SWAPPED_UTF8(void *srcdata, int srclen, struct fsw_string *dest) +{ + fsw_status_t status; + int i, destsize; + fsw_u16 *sp; + fsw_u8 *dp; + fsw_u32 c; + + sp = (fsw_u16 *)srcdata; + destsize = 0; + for (i = 0; i < srclen; i++) { + c = *sp++; c = FSW_SWAPVALUE_U16(c); + + if (c < 0x000080) + destsize++; + else if (c < 0x000800) + destsize += 2; + else if (c < 0x010000) + destsize += 3; + else + destsize += 4; + } + + dest->type = FSW_STRING_TYPE_UTF8; + dest->len = srclen; + dest->size = destsize; + status = fsw_alloc(dest->size, &dest->data); + if (status) + return status; + + sp = (fsw_u16 *)srcdata; + dp = (fsw_u8 *)dest->data; + for (i = 0; i < srclen; i++) { + c = *sp++; c = FSW_SWAPVALUE_U16(c); + + if (c < 0x000080) { + *dp++ = (fsw_u8)c; + } else if (c < 0x000800) { + *dp++ = (fsw_u8)(0xc0 | ((c >> 6) & 0x1f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else if (c < 0x010000) { + *dp++ = (fsw_u8)(0xe0 | ((c >> 12) & 0x0f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } else { + *dp++ = (fsw_u8)(0xf0 | ((c >> 18) & 0x07)); + *dp++ = (fsw_u8)(0x80 | ((c >> 12) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | ((c >> 6) & 0x3f)); + *dp++ = (fsw_u8)(0x80 | (c & 0x3f)); + } + } + return FSW_SUCCESS; +} diff --git a/filesystems/hfs_format.h b/filesystems/hfs_format.h new file mode 100644 index 0000000..9b800e9 --- /dev/null +++ b/filesystems/hfs_format.h @@ -0,0 +1,811 @@ +/* $Id: hfs_format.h 33540 2010-10-28 09:27:05Z vboxsync $ */ +/** @file + * hfs_format.h + */ + +/* + * Copyright (C) 2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* + * This code is based on: + * + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +#ifndef __HFS_FORMAT__ +#define __HFS_FORMAT__ + +#if !defined(VBOX) && !defined(HOST_POSIX) +#include +#include +#endif + +#ifdef _MSC_VER +# pragma pack(push,2) +# define HFS_ALIGNMENT +#else +#define HFS_ALIGNMENT __attribute__((aligned(2), packed)) +#endif + +/* + * hfs_format.c + * + * This file describes the on-disk format for HFS and HFS Plus volumes. + * The HFS Plus volume format is described in detail in Apple Technote 1150. + * + * http://developer.apple.com/technotes/tn/tn1150.html + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* some on-disk hfs structures have 68K alignment (misaligned) */ + +/* Signatures used to differentiate between HFS and HFS Plus volumes */ +enum { + kHFSSigWord = 0x4244, /* 'BD' in ASCII */ + kHFSPlusSigWord = 0x482B, /* 'H+' in ASCII */ + kHFSXSigWord = 0x4858, /* 'HX' in ASCII */ + + kHFSPlusVersion = 0x0004, /* 'H+' volumes are version 4 only */ + kHFSXVersion = 0x0005, /* 'HX' volumes start with version 5 */ + + kHFSPlusMountVersion = 0x31302E30, /* '10.0' for Mac OS X */ + kHFSJMountVersion = 0x4846534a, /* 'HFSJ' for journaled HFS+ on OS X */ + kFSKMountVersion = 0x46534b21 /* 'FSK!' for failed journal replay */ +}; + + +#ifdef __APPLE_API_PRIVATE +/* + * Mac OS X has two special directories on HFS+ volumes for hardlinked files + * and hardlinked directories as well as for open-unlinked files. + * + * These directories and their contents are not exported from the filesystem + * under Mac OS X. + */ +#define HFSPLUSMETADATAFOLDER "\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80\xE2\x90\x80HFS+ Private Data" +#define HFSPLUS_DIR_METADATA_FOLDER ".HFS+ Private Directory Data\xd" + +/* + * Files in the "HFS+ Private Data" folder have one of the following prefixes + * followed by a decimal number (no leading zeros) for the file ID. + * + * Note: Earlier version of Mac OS X used a 32 bit random number for the link + * ref number instead of the file id. + * + * e.g. iNode7182000 and temp3296 + */ +#define HFS_INODE_PREFIX "iNode" +#define HFS_DELETE_PREFIX "temp" + +/* + * Files in the ".HFS+ Private Directory Data" folder have the following + * prefix followed by a decimal number (no leading zeros) for the file ID. + * + * e.g. dir_555 + */ +#define HFS_DIRINODE_PREFIX "dir_" + +/* + * Hardlink inodes save the head of the link chain in + * an extended attribute named FIRST_LINK_XATTR_NAME. + * The attribute data is the decimal value in ASCII + * of the cnid for the first link in the chain. + * + * This extended attribute is private (i.e. its not + * exported in the getxattr/listxattr POSIX APIs). + */ +#define FIRST_LINK_XATTR_NAME "com.apple.system.hfs.firstlink" +#define FIRST_LINK_XATTR_REC_SIZE (sizeof(HFSPlusAttrData) - 2 + 12) + +#endif /* __APPLE_API_PRIVATE */ + +/* + * Indirect link files (hard links) have the following type/creator. + */ +enum { + kHardLinkFileType = 0x686C6E6B, /* 'hlnk' */ + kHFSPlusCreator = 0x6866732B /* 'hfs+' */ +}; + + +/* + * File type and creator for symbolic links + */ +enum { + kSymLinkFileType = 0x736C6E6B, /* 'slnk' */ + kSymLinkCreator = 0x72686170 /* 'rhap' */ +}; + + +#ifndef _HFSUNISTR255_DEFINED_ +#define _HFSUNISTR255_DEFINED_ +/* Unicode strings are used for HFS Plus file and folder names */ +struct HFSUniStr255 { + u_int16_t length; /* number of unicode characters */ + u_int16_t unicode[255]; /* unicode characters */ +} HFS_ALIGNMENT; +typedef struct HFSUniStr255 HFSUniStr255; +typedef const HFSUniStr255 *ConstHFSUniStr255Param; +#endif /* _HFSUNISTR255_DEFINED_ */ + +enum { + kHFSMaxVolumeNameChars = 27, + kHFSMaxFileNameChars = 31, + kHFSPlusMaxFileNameChars = 255 +}; + + +/* Extent overflow file data structures */ + +/* HFS Extent key */ +struct HFSExtentKey { + u_int8_t keyLength; /* length of key, excluding this field */ + u_int8_t forkType; /* 0 = data fork, FF = resource fork */ + u_int32_t fileID; /* file ID */ + u_int16_t startBlock; /* first file allocation block number in this extent */ +} HFS_ALIGNMENT; +typedef struct HFSExtentKey HFSExtentKey; + +/* HFS Plus Extent key */ +struct HFSPlusExtentKey { + u_int16_t keyLength; /* length of key, excluding this field */ + u_int8_t forkType; /* 0 = data fork, FF = resource fork */ + u_int8_t pad; /* make the other fields align on 32-bit boundary */ + u_int32_t fileID; /* file ID */ + u_int32_t startBlock; /* first file allocation block number in this extent */ +} HFS_ALIGNMENT; +typedef struct HFSPlusExtentKey HFSPlusExtentKey; + +/* Number of extent descriptors per extent record */ +enum { + kHFSExtentDensity = 3, + kHFSPlusExtentDensity = 8 +}; + +/* HFS extent descriptor */ +struct HFSExtentDescriptor { + u_int16_t startBlock; /* first allocation block */ + u_int16_t blockCount; /* number of allocation blocks */ +} HFS_ALIGNMENT; +typedef struct HFSExtentDescriptor HFSExtentDescriptor; + +/* HFS Plus extent descriptor */ +struct HFSPlusExtentDescriptor { + u_int32_t startBlock; /* first allocation block */ + u_int32_t blockCount; /* number of allocation blocks */ +} HFS_ALIGNMENT; +typedef struct HFSPlusExtentDescriptor HFSPlusExtentDescriptor; + +/* HFS extent record */ +typedef HFSExtentDescriptor HFSExtentRecord[3]; + +/* HFS Plus extent record */ +typedef HFSPlusExtentDescriptor HFSPlusExtentRecord[8]; + + +/* Finder information */ +struct FndrFileInfo { + u_int32_t fdType; /* file type */ + u_int32_t fdCreator; /* file creator */ + u_int16_t fdFlags; /* Finder flags */ + struct { + int16_t v; /* file's location */ + int16_t h; + } fdLocation; + int16_t opaque; +} HFS_ALIGNMENT; +typedef struct FndrFileInfo FndrFileInfo; + +struct FndrDirInfo { + struct { /* folder's window rectangle */ + int16_t top; + int16_t left; + int16_t bottom; + int16_t right; + } frRect; + unsigned short frFlags; /* Finder flags */ + struct { + u_int16_t v; /* folder's location */ + u_int16_t h; + } frLocation; + int16_t opaque; +} HFS_ALIGNMENT; +typedef struct FndrDirInfo FndrDirInfo; + +struct FndrOpaqueInfo { + int8_t opaque[16]; +} HFS_ALIGNMENT; +typedef struct FndrOpaqueInfo FndrOpaqueInfo; + + +/* HFS Plus Fork data info - 80 bytes */ +struct HFSPlusForkData { + u_int64_t logicalSize; /* fork's logical size in bytes */ + u_int32_t clumpSize; /* fork's clump size in bytes */ + u_int32_t totalBlocks; /* total blocks used by this fork */ + HFSPlusExtentRecord extents; /* initial set of extents */ +} HFS_ALIGNMENT; +typedef struct HFSPlusForkData HFSPlusForkData; + + +/* Mac OS X has 16 bytes worth of "BSD" info. + * + * Note: Mac OS 9 implementations and applications + * should preserve, but not change, this information. + */ +struct HFSPlusBSDInfo { + u_int32_t ownerID; /* user-id of owner or hard link chain previous link */ + u_int32_t groupID; /* group-id of owner or hard link chain next link */ + u_int8_t adminFlags; /* super-user changeable flags */ + u_int8_t ownerFlags; /* owner changeable flags */ + u_int16_t fileMode; /* file type and permission bits */ + union { + u_int32_t iNodeNum; /* indirect node number (hard links only) */ + u_int32_t linkCount; /* links that refer to this indirect node */ + u_int32_t rawDevice; /* special file device (FBLK and FCHR only) */ + } special; +} HFS_ALIGNMENT; +typedef struct HFSPlusBSDInfo HFSPlusBSDInfo; + +/* + * Hardlink "links" resolve to an inode + * and the actual uid/gid comes from that + * inode. + * + * We repurpose the links's uid/gid fields + * for the hardlink link chain. The chain + * consists of a doubly linked list of file + * ids. + */ + +#define hl_firstLinkID reserved1 /* Valid only if HasLinkChain flag is set (indirect nodes only) */ + +#define hl_prevLinkID bsdInfo.ownerID /* Valid only if HasLinkChain flag is set */ +#define hl_nextLinkID bsdInfo.groupID /* Valid only if HasLinkChain flag is set */ + +#define hl_linkReference bsdInfo.special.iNodeNum +#define hl_linkCount bsdInfo.special.linkCount + + +/* Catalog file data structures */ + +enum { + kHFSRootParentID = 1, /* Parent ID of the root folder */ + kHFSRootFolderID = 2, /* Folder ID of the root folder */ + kHFSExtentsFileID = 3, /* File ID of the extents file */ + kHFSCatalogFileID = 4, /* File ID of the catalog file */ + kHFSBadBlockFileID = 5, /* File ID of the bad allocation block file */ + kHFSAllocationFileID = 6, /* File ID of the allocation file (HFS Plus only) */ + kHFSStartupFileID = 7, /* File ID of the startup file (HFS Plus only) */ + kHFSAttributesFileID = 8, /* File ID of the attribute file (HFS Plus only) */ + kHFSAttributeDataFileID = 13, /* Used in Mac OS X runtime for extent based attributes */ + /* kHFSAttributeDataFileID is never stored on disk. */ + kHFSRepairCatalogFileID = 14, /* Used when rebuilding Catalog B-tree */ + kHFSBogusExtentFileID = 15, /* Used for exchanging extents in extents file */ + kHFSFirstUserCatalogNodeID = 16 +}; + +/* HFS catalog key */ +struct HFSCatalogKey { + u_int8_t keyLength; /* key length (in bytes) */ + u_int8_t reserved; /* reserved (set to zero) */ + u_int32_t parentID; /* parent folder ID */ + u_int8_t nodeName[kHFSMaxFileNameChars + 1]; /* catalog node name */ +} HFS_ALIGNMENT; +typedef struct HFSCatalogKey HFSCatalogKey; + +/* HFS Plus catalog key */ +struct HFSPlusCatalogKey { + u_int16_t keyLength; /* key length (in bytes) */ + u_int32_t parentID; /* parent folder ID */ + HFSUniStr255 nodeName; /* catalog node name */ +} HFS_ALIGNMENT; +typedef struct HFSPlusCatalogKey HFSPlusCatalogKey; + +/* Catalog record types */ +enum { + /* HFS Catalog Records */ + kHFSFolderRecord = 0x0100, /* Folder record */ + kHFSFileRecord = 0x0200, /* File record */ + kHFSFolderThreadRecord = 0x0300, /* Folder thread record */ + kHFSFileThreadRecord = 0x0400, /* File thread record */ + + /* HFS Plus Catalog Records */ + kHFSPlusFolderRecord = 1, /* Folder record */ + kHFSPlusFileRecord = 2, /* File record */ + kHFSPlusFolderThreadRecord = 3, /* Folder thread record */ + kHFSPlusFileThreadRecord = 4 /* File thread record */ +}; + + +/* Catalog file record flags */ +enum { + kHFSFileLockedBit = 0x0000, /* file is locked and cannot be written to */ + kHFSFileLockedMask = 0x0001, + + kHFSThreadExistsBit = 0x0001, /* a file thread record exists for this file */ + kHFSThreadExistsMask = 0x0002, + + kHFSHasAttributesBit = 0x0002, /* object has extended attributes */ + kHFSHasAttributesMask = 0x0004, + + kHFSHasSecurityBit = 0x0003, /* object has security data (ACLs) */ + kHFSHasSecurityMask = 0x0008, + + kHFSHasFolderCountBit = 0x0004, /* only for HFSX, folder maintains a separate sub-folder count */ + kHFSHasFolderCountMask = 0x0010, /* (sum of folder records and directory hard links) */ + + kHFSHasLinkChainBit = 0x0005, /* has hardlink chain (inode or link) */ + kHFSHasLinkChainMask = 0x0020, + + kHFSHasChildLinkBit = 0x0006, /* folder has a child that's a dir link */ + kHFSHasChildLinkMask = 0x0040 +}; + + +/* HFS catalog folder record - 70 bytes */ +struct HFSCatalogFolder { + int16_t recordType; /* == kHFSFolderRecord */ + u_int16_t flags; /* folder flags */ + u_int16_t valence; /* folder valence */ + u_int32_t folderID; /* folder ID */ + u_int32_t createDate; /* date and time of creation */ + u_int32_t modifyDate; /* date and time of last modification */ + u_int32_t backupDate; /* date and time of last backup */ + FndrDirInfo userInfo; /* Finder information */ + FndrOpaqueInfo finderInfo; /* additional Finder information */ + u_int32_t reserved[4]; /* reserved - initialized as zero */ +} HFS_ALIGNMENT; +typedef struct HFSCatalogFolder HFSCatalogFolder; + +/* HFS Plus catalog folder record - 88 bytes */ +struct HFSPlusCatalogFolder { + int16_t recordType; /* == kHFSPlusFolderRecord */ + u_int16_t flags; /* file flags */ + u_int32_t valence; /* folder's item count */ + u_int32_t folderID; /* folder ID */ + u_int32_t createDate; /* date and time of creation */ + u_int32_t contentModDate; /* date and time of last content modification */ + u_int32_t attributeModDate; /* date and time of last attribute modification */ + u_int32_t accessDate; /* date and time of last access (MacOS X only) */ + u_int32_t backupDate; /* date and time of last backup */ + HFSPlusBSDInfo bsdInfo; /* permissions (for MacOS X) */ + FndrDirInfo userInfo; /* Finder information */ + FndrOpaqueInfo finderInfo; /* additional Finder information */ + u_int32_t textEncoding; /* hint for name conversions */ + u_int32_t folderCount; /* number of enclosed folders, active when HasFolderCount is set */ +} HFS_ALIGNMENT; +typedef struct HFSPlusCatalogFolder HFSPlusCatalogFolder; + +/* HFS catalog file record - 102 bytes */ +struct HFSCatalogFile { + int16_t recordType; /* == kHFSFileRecord */ + u_int8_t flags; /* file flags */ + int8_t fileType; /* file type (unused ?) */ + FndrFileInfo userInfo; /* Finder information */ + u_int32_t fileID; /* file ID */ + u_int16_t dataStartBlock; /* not used - set to zero */ + int32_t dataLogicalSize; /* logical EOF of data fork */ + int32_t dataPhysicalSize; /* physical EOF of data fork */ + u_int16_t rsrcStartBlock; /* not used - set to zero */ + int32_t rsrcLogicalSize; /* logical EOF of resource fork */ + int32_t rsrcPhysicalSize; /* physical EOF of resource fork */ + u_int32_t createDate; /* date and time of creation */ + u_int32_t modifyDate; /* date and time of last modification */ + u_int32_t backupDate; /* date and time of last backup */ + FndrOpaqueInfo finderInfo; /* additional Finder information */ + u_int16_t clumpSize; /* file clump size (not used) */ + HFSExtentRecord dataExtents; /* first data fork extent record */ + HFSExtentRecord rsrcExtents; /* first resource fork extent record */ + u_int32_t reserved; /* reserved - initialized as zero */ +} HFS_ALIGNMENT; +typedef struct HFSCatalogFile HFSCatalogFile; + +/* HFS Plus catalog file record - 248 bytes */ +struct HFSPlusCatalogFile { + int16_t recordType; /* == kHFSPlusFileRecord */ + u_int16_t flags; /* file flags */ + u_int32_t reserved1; /* reserved - initialized as zero */ + u_int32_t fileID; /* file ID */ + u_int32_t createDate; /* date and time of creation */ + u_int32_t contentModDate; /* date and time of last content modification */ + u_int32_t attributeModDate; /* date and time of last attribute modification */ + u_int32_t accessDate; /* date and time of last access (MacOS X only) */ + u_int32_t backupDate; /* date and time of last backup */ + HFSPlusBSDInfo bsdInfo; /* permissions (for MacOS X) */ + FndrFileInfo userInfo; /* Finder information */ + FndrOpaqueInfo finderInfo; /* additional Finder information */ + u_int32_t textEncoding; /* hint for name conversions */ + u_int32_t reserved2; /* reserved - initialized as zero */ + + /* Note: these start on double long (64 bit) boundary */ + HFSPlusForkData dataFork; /* size and block data for data fork */ + HFSPlusForkData resourceFork; /* size and block data for resource fork */ +} HFS_ALIGNMENT; +typedef struct HFSPlusCatalogFile HFSPlusCatalogFile; + +/* HFS catalog thread record - 46 bytes */ +struct HFSCatalogThread { + int16_t recordType; /* == kHFSFolderThreadRecord or kHFSFileThreadRecord */ + int32_t reserved[2]; /* reserved - initialized as zero */ + u_int32_t parentID; /* parent ID for this catalog node */ + u_int8_t nodeName[kHFSMaxFileNameChars + 1]; /* name of this catalog node */ +} HFS_ALIGNMENT; +typedef struct HFSCatalogThread HFSCatalogThread; + +/* HFS Plus catalog thread record -- 264 bytes */ +struct HFSPlusCatalogThread { + int16_t recordType; /* == kHFSPlusFolderThreadRecord or kHFSPlusFileThreadRecord */ + int16_t reserved; /* reserved - initialized as zero */ + u_int32_t parentID; /* parent ID for this catalog node */ + HFSUniStr255 nodeName; /* name of this catalog node (variable length) */ +} HFS_ALIGNMENT; +typedef struct HFSPlusCatalogThread HFSPlusCatalogThread; + +#ifdef __APPLE_API_UNSTABLE +/* + These are the types of records in the attribute B-tree. The values were + chosen so that they wouldn't conflict with the catalog record types. +*/ +enum { + kHFSPlusAttrInlineData = 0x10, /* attributes whose data fits in a b-tree node */ + kHFSPlusAttrForkData = 0x20, /* extent based attributes (data lives in extents) */ + kHFSPlusAttrExtents = 0x30 /* overflow extents for large attributes */ +}; + + +/* + HFSPlusAttrForkData + For larger attributes, whose value is stored in allocation blocks. + If the attribute has more than 8 extents, there will be additional + records (of type HFSPlusAttrExtents) for this attribute. +*/ +struct HFSPlusAttrForkData { + u_int32_t recordType; /* == kHFSPlusAttrForkData*/ + u_int32_t reserved; + HFSPlusForkData theFork; /* size and first extents of value*/ +} HFS_ALIGNMENT; +typedef struct HFSPlusAttrForkData HFSPlusAttrForkData; + +/* + HFSPlusAttrExtents + This record contains information about overflow extents for large, + fragmented attributes. +*/ +struct HFSPlusAttrExtents { + u_int32_t recordType; /* == kHFSPlusAttrExtents*/ + u_int32_t reserved; + HFSPlusExtentRecord extents; /* additional extents*/ +} HFS_ALIGNMENT; +typedef struct HFSPlusAttrExtents HFSPlusAttrExtents; + +/* + * Attributes B-tree Data Record + * + * For small attributes, whose entire value is stored + * within a single B-tree record. + */ +struct HFSPlusAttrData { + u_int32_t recordType; /* == kHFSPlusAttrInlineData */ + u_int32_t reserved[2]; + u_int32_t attrSize; /* size of attribute data in bytes */ + u_int8_t attrData[2]; /* variable length */ +} HFS_ALIGNMENT; +typedef struct HFSPlusAttrData HFSPlusAttrData; + + +/* HFSPlusAttrInlineData is obsolete use HFSPlusAttrData instead */ +struct HFSPlusAttrInlineData { + u_int32_t recordType; + u_int32_t reserved; + u_int32_t logicalSize; + u_int8_t userData[2]; +} HFS_ALIGNMENT; +typedef struct HFSPlusAttrInlineData HFSPlusAttrInlineData; + + +/* A generic Attribute Record*/ +union HFSPlusAttrRecord { + u_int32_t recordType; + HFSPlusAttrInlineData inlineData; /* NOT USED */ + HFSPlusAttrData attrData; + HFSPlusAttrForkData forkData; + HFSPlusAttrExtents overflowExtents; +}; +typedef union HFSPlusAttrRecord HFSPlusAttrRecord; + +/* Attribute key */ +enum { kHFSMaxAttrNameLen = 127 }; +struct HFSPlusAttrKey { + u_int16_t keyLength; /* key length (in bytes) */ + u_int16_t pad; /* set to zero */ + u_int32_t fileID; /* file associated with attribute */ + u_int32_t startBlock; /* first allocation block number for extents */ + u_int16_t attrNameLen; /* number of unicode characters */ + u_int16_t attrName[kHFSMaxAttrNameLen]; /* attribute name (Unicode) */ +} HFS_ALIGNMENT; +typedef struct HFSPlusAttrKey HFSPlusAttrKey; + +#define kHFSPlusAttrKeyMaximumLength (sizeof(HFSPlusAttrKey) - sizeof(u_int16_t)) +#define kHFSPlusAttrKeyMinimumLength (kHFSPlusAttrKeyMaximumLength - kHFSMaxAttrNameLen*sizeof(u_int16_t)) + +#endif /* __APPLE_API_UNSTABLE */ + + +/* Key and node lengths */ +enum { + kHFSPlusExtentKeyMaximumLength = sizeof(HFSPlusExtentKey) - sizeof(u_int16_t), + kHFSExtentKeyMaximumLength = sizeof(HFSExtentKey) - sizeof(u_int8_t), + kHFSPlusCatalogKeyMaximumLength = sizeof(HFSPlusCatalogKey) - sizeof(u_int16_t), + kHFSPlusCatalogKeyMinimumLength = kHFSPlusCatalogKeyMaximumLength - sizeof(HFSUniStr255) + sizeof(u_int16_t), + kHFSCatalogKeyMaximumLength = sizeof(HFSCatalogKey) - sizeof(u_int8_t), + kHFSCatalogKeyMinimumLength = kHFSCatalogKeyMaximumLength - (kHFSMaxFileNameChars + 1) + sizeof(u_int8_t), + kHFSPlusCatalogMinNodeSize = 4096, + kHFSPlusExtentMinNodeSize = 512, + kHFSPlusAttrMinNodeSize = 4096 +}; + +/* HFS and HFS Plus volume attribute bits */ +enum { + /* Bits 0-6 are reserved (always cleared by MountVol call) */ + kHFSVolumeHardwareLockBit = 7, /* volume is locked by hardware */ + kHFSVolumeUnmountedBit = 8, /* volume was successfully unmounted */ + kHFSVolumeSparedBlocksBit = 9, /* volume has bad blocks spared */ + kHFSVolumeNoCacheRequiredBit = 10, /* don't cache volume blocks (i.e. RAM or ROM disk) */ + kHFSBootVolumeInconsistentBit = 11, /* boot volume is inconsistent (System 7.6 and later) */ + kHFSCatalogNodeIDsReusedBit = 12, + kHFSVolumeJournaledBit = 13, /* this volume has a journal on it */ + kHFSVolumeInconsistentBit = 14, /* serious inconsistencies detected at runtime */ + kHFSVolumeSoftwareLockBit = 15, /* volume is locked by software */ + + kHFSVolumeHardwareLockMask = 1 << kHFSVolumeHardwareLockBit, + kHFSVolumeUnmountedMask = 1 << kHFSVolumeUnmountedBit, + kHFSVolumeSparedBlocksMask = 1 << kHFSVolumeSparedBlocksBit, + kHFSVolumeNoCacheRequiredMask = 1 << kHFSVolumeNoCacheRequiredBit, + kHFSBootVolumeInconsistentMask = 1 << kHFSBootVolumeInconsistentBit, + kHFSCatalogNodeIDsReusedMask = 1 << kHFSCatalogNodeIDsReusedBit, + kHFSVolumeJournaledMask = 1 << kHFSVolumeJournaledBit, + kHFSVolumeInconsistentMask = 1 << kHFSVolumeInconsistentBit, + kHFSVolumeSoftwareLockMask = 1 << kHFSVolumeSoftwareLockBit, + kHFSMDBAttributesMask = 0x8380 +}; + +/* HFS Master Directory Block - 162 bytes */ +/* Stored at sector #2 (3rd sector) and second-to-last sector. */ +struct HFSMasterDirectoryBlock { + u_int16_t drSigWord; /* == kHFSSigWord = 0x4244 = 'BD' or 'H+' or 'HX'*/ + u_int32_t drCrDate; /* date and time of volume creation */ + u_int32_t drLsMod; /* date and time of last modification */ + u_int16_t drAtrb; /* volume attributes */ + u_int16_t drNmFls; /* number of files in root folder */ + u_int16_t drVBMSt; /* first block of volume bitmap */ + u_int16_t drAllocPtr; /* start of next allocation search */ + u_int16_t drNmAlBlks; /* number of allocation blocks in volume */ + u_int32_t drAlBlkSiz; /* size (in bytes) of allocation blocks */ + u_int32_t drClpSiz; /* default clump size */ + u_int16_t drAlBlSt; /* first allocation block in volume */ + u_int32_t drNxtCNID; /* next unused catalog node ID */ + u_int16_t drFreeBks; /* number of unused allocation blocks */ + u_int8_t drVN[kHFSMaxVolumeNameChars + 1]; /* volume name */ + u_int32_t drVolBkUp; /* date and time of last backup */ + u_int16_t drVSeqNum; /* volume backup sequence number */ + u_int32_t drWrCnt; /* volume write count */ + u_int32_t drXTClpSiz; /* clump size for extents overflow file */ + u_int32_t drCTClpSiz; /* clump size for catalog file */ + u_int16_t drNmRtDirs; /* number of directories in root folder */ + u_int32_t drFilCnt; /* number of files in volume */ + u_int32_t drDirCnt; /* number of directories in volume */ + u_int32_t drFndrInfo[8]; /* information used by the Finder */ + u_int16_t drEmbedSigWord; /* embedded volume signature (formerly drVCSize) */ + HFSExtentDescriptor drEmbedExtent; /* embedded volume location and size (formerly drVBMCSize and drCtlCSize) */ + u_int32_t drXTFlSize; /* size of extents overflow file */ + HFSExtentRecord drXTExtRec; /* extent record for extents overflow file */ + u_int32_t drCTFlSize; /* size of catalog file */ + HFSExtentRecord drCTExtRec; /* extent record for catalog file */ +} HFS_ALIGNMENT; +typedef struct HFSMasterDirectoryBlock HFSMasterDirectoryBlock; + + +#ifdef __APPLE_API_UNSTABLE +#define SET_HFS_TEXT_ENCODING(hint) \ + (0x656e6300 | ((hint) & 0xff)) +#define GET_HFS_TEXT_ENCODING(hint) \ + (((hint) & 0xffffff00) == 0x656e6300 ? (hint) & 0x000000ff : 0xffffffffU) +#endif /* __APPLE_API_UNSTABLE */ + + /* + 48 2B 00 04 80 00 20 00 48 46 53 4A 00 AD 7E 98 //H+ HFSJ + C9 12 D3 9E CB 84 F3 1D 00 00 00 00 C9 26 31 A8 + 00 12 1D 9D 00 03 66 B9 00 00 10 00 01 A1 2C CF + 00 44 67 DF 01 35 EB A8 00 01 00 00 00 01 00 00 + 10 8B 1C EA 08 E4 9C 1B 00 00 00 00 02 00 00 8B + 00 00 02 E7 10 3F CB 93 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 02 E7 6A 45 F3 37 EF 97 E9 A6 + 00 00 00 00 00 34 30 00 00 00 00 00 00 00 03 43 + 00 00 00 01 00 00 03 43 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 70 00 00 00 70 00 00 00 00 07 00 + 00 00 13 45 00 00 07 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 2A F8 00 00 01 90 00 00 00 02 AF 80 + 00 01 AA 55 00 01 90 00 00 14 5A C7 00 00 19 00 + 00 3D E4 E3 00 00 19 00 00 95 44 F7 00 00 19 00 + 00 9D 3B 18 00 00 32 00 00 E3 58 1C 00 00 19 00 + 00 48 1C 72 00 00 19 00 00 BB 6A 05 00 00 19 00 + 00 00 00 00 06 40 00 00 01 90 00 00 00 00 64 00 + 00 06 80 00 00 00 19 00 00 38 2A 2F 00 00 19 00 + 00 38 DB 2C 00 00 19 00 00 A6 A2 1F 00 00 19 00 + */ + + +/* HFS Plus Volume Header - 512 bytes */ +/* Stored at sector #2 (3rd sector) and second-to-last sector. */ +struct HFSPlusVolumeHeader { + u_int16_t signature; /* == kHFSPlusSigWord */ + u_int16_t version; /* == kHFSPlusVersion */ + u_int32_t attributes; /* volume attributes */ + u_int32_t lastMountedVersion; /* implementation version which last mounted volume */ + u_int32_t journalInfoBlock; /* block addr of journal info (if volume is journaled, zero otherwise) */ + + u_int32_t createDate; /* date and time of volume creation */ + u_int32_t modifyDate; /* date and time of last modification */ + u_int32_t backupDate; /* date and time of last backup */ + u_int32_t checkedDate; /* date and time of last disk check */ + + u_int32_t fileCount; /* number of files in volume */ + u_int32_t folderCount; /* number of directories in volume */ + + u_int32_t blockSize; /* size (in bytes) of allocation blocks */ + u_int32_t totalBlocks; /* number of allocation blocks in volume (includes this header and VBM*/ + u_int32_t freeBlocks; /* number of unused allocation blocks */ + + u_int32_t nextAllocation; /* start of next allocation search */ + u_int32_t rsrcClumpSize; /* default resource fork clump size */ + u_int32_t dataClumpSize; /* default data fork clump size */ + u_int32_t nextCatalogID; /* next unused catalog node ID */ + + u_int32_t writeCount; /* volume write count */ + u_int64_t encodingsBitmap; /* which encodings have been use on this volume */ + + u_int8_t finderInfo[32]; /* information used by the Finder */ + + HFSPlusForkData allocationFile; /* allocation bitmap file */ + HFSPlusForkData extentsFile; /* extents B-tree file */ + HFSPlusForkData catalogFile; /* catalog B-tree file */ + HFSPlusForkData attributesFile; /* extended attributes B-tree file */ + HFSPlusForkData startupFile; /* boot file (secondary loader) */ +} HFS_ALIGNMENT; +typedef struct HFSPlusVolumeHeader HFSPlusVolumeHeader; + + +/* B-tree structures */ + +enum BTreeKeyLimits{ + kMaxKeyLength = 520 +}; + +union BTreeKey{ + u_int8_t length8; + u_int16_t length16; + u_int8_t rawData [kMaxKeyLength+2]; +}; +typedef union BTreeKey BTreeKey; + +/* BTNodeDescriptor -- Every B-tree node starts with these fields. */ +struct BTNodeDescriptor { + u_int32_t fLink; /* next node at this level*/ + u_int32_t bLink; /* previous node at this level*/ + int8_t kind; /* kind of node (leaf, index, header, map)*/ + u_int8_t height; /* zero for header, map; child is one more than parent*/ + u_int16_t numRecords; /* number of records in this node*/ + u_int16_t reserved; /* reserved - initialized as zero */ +} HFS_ALIGNMENT; +typedef struct BTNodeDescriptor BTNodeDescriptor; + +/* Constants for BTNodeDescriptor kind */ +enum { + kBTLeafNode = -1, + kBTIndexNode = 0, + kBTHeaderNode = 1, + kBTMapNode = 2 +}; + +/* BTHeaderRec -- The first record of a B-tree header node */ +struct BTHeaderRec { + u_int16_t treeDepth; /* maximum height (usually leaf nodes) */ + u_int32_t rootNode; /* node number of root node */ + u_int32_t leafRecords; /* number of leaf records in all leaf nodes */ + u_int32_t firstLeafNode; /* node number of first leaf node */ + u_int32_t lastLeafNode; /* node number of last leaf node */ + u_int16_t nodeSize; /* size of a node, in bytes */ + u_int16_t maxKeyLength; /* reserved */ + u_int32_t totalNodes; /* total number of nodes in tree */ + u_int32_t freeNodes; /* number of unused (free) nodes in tree */ + u_int16_t reserved1; /* unused */ + u_int32_t clumpSize; /* reserved */ + u_int8_t btreeType; /* reserved */ + u_int8_t keyCompareType; /* Key string Comparison Type */ + u_int32_t attributes; /* persistent attributes about the tree */ + u_int32_t reserved3[16]; /* reserved */ +} HFS_ALIGNMENT; +typedef struct BTHeaderRec BTHeaderRec; + +/* Constants for BTHeaderRec attributes */ +enum { + kBTBadCloseMask = 0x00000001, /* reserved */ + kBTBigKeysMask = 0x00000002, /* key length field is 16 bits */ + kBTVariableIndexKeysMask = 0x00000004 /* keys in index nodes are variable length */ +}; + + +/* Catalog Key Name Comparison Type */ +enum { + kHFSCaseFolding = 0xCF, /* case folding (case-insensitive) */ + kHFSBinaryCompare = 0xBC /* binary compare (case-sensitive) */ +}; + +/* JournalInfoBlock - Structure that describes where our journal lives */ +struct JournalInfoBlock { + u_int32_t flags; + u_int32_t device_signature[8]; // signature used to locate our device. + u_int64_t offset; // byte offset to the journal on the device + u_int64_t size; // size in bytes of the journal + u_int32_t reserved[32]; +} HFS_ALIGNMENT; +typedef struct JournalInfoBlock JournalInfoBlock; + +enum { + kJIJournalInFSMask = 0x00000001, + kJIJournalOnOtherDeviceMask = 0x00000002, + kJIJournalNeedInitMask = 0x00000004 +}; + + +#ifdef __cplusplus +} +#endif +#ifdef _MSC_VER +# pragma pack(pop) +#endif + +#endif /* __HFS_FORMAT__ */ diff --git a/filesystems/test/.svn/all-wcprops b/filesystems/test/.svn/all-wcprops new file mode 100644 index 0000000..1c8e48f --- /dev/null +++ b/filesystems/test/.svn/all-wcprops @@ -0,0 +1,41 @@ +K 25 +svn:wc:ra_dav:version-url +V 48 +/svnroot/cloverefiboot/!svn/ver/1/VBoxFsDxe/test +END +fsw_posix_base.h +K 25 +svn:wc:ra_dav:version-url +V 65 +/svnroot/cloverefiboot/!svn/ver/1/VBoxFsDxe/test/fsw_posix_base.h +END +fsw_posix.c +K 25 +svn:wc:ra_dav:version-url +V 60 +/svnroot/cloverefiboot/!svn/ver/1/VBoxFsDxe/test/fsw_posix.c +END +lsroot.c +K 25 +svn:wc:ra_dav:version-url +V 57 +/svnroot/cloverefiboot/!svn/ver/1/VBoxFsDxe/test/lsroot.c +END +fsw_posix.h +K 25 +svn:wc:ra_dav:version-url +V 60 +/svnroot/cloverefiboot/!svn/ver/1/VBoxFsDxe/test/fsw_posix.h +END +README +K 25 +svn:wc:ra_dav:version-url +V 55 +/svnroot/cloverefiboot/!svn/ver/1/VBoxFsDxe/test/README +END +lslr.c +K 25 +svn:wc:ra_dav:version-url +V 55 +/svnroot/cloverefiboot/!svn/ver/1/VBoxFsDxe/test/lslr.c +END diff --git a/filesystems/test/.svn/entries b/filesystems/test/.svn/entries new file mode 100644 index 0000000..f594e04 --- /dev/null +++ b/filesystems/test/.svn/entries @@ -0,0 +1,232 @@ +10 + +dir +432 +https://cloverefiboot.svn.sourceforge.net/svnroot/cloverefiboot/VBoxFsDxe/test +https://cloverefiboot.svn.sourceforge.net/svnroot/cloverefiboot + + + +2011-04-04T13:39:25.410063Z +1 +slice2009 + + + + + + + + + + + + + + +0f885799-303c-4bf3-9050-968124707f2f + +fsw_posix_base.h +file + + + + +2012-05-15T01:06:34.000000Z +17516f05bd8d1ca74803cd8e1bc8b20f +2011-04-04T13:39:25.410063Z +1 +slice2009 + + + + + + + + + + + + + + + + + + + + + +2920 + +fsw_posix.c +file + + + + +2012-05-15T01:06:34.000000Z +c847b0b03df254cb7f31e25315063957 +2011-04-04T13:39:25.410063Z +1 +slice2009 + + + + + + + + + + + + + + + + + + + + + +13763 + +lsroot.c +file + + + + +2012-05-15T01:06:34.000000Z +55a5a9b6c4ec0b2f6ee39d14ec82b276 +2011-04-04T13:39:25.410063Z +1 +slice2009 + + + + + + + + + + + + + + + + + + + + + +2807 + +fsw_posix.h +file + + + + +2012-05-15T01:06:34.000000Z +cbfe2e553a18fcb978b5ebfd3a06def1 +2011-04-04T13:39:25.410063Z +1 +slice2009 + + + + + + + + + + + + + + + + + + + + + +3297 + +README +file + + + + +2012-05-15T01:06:34.000000Z +9fbd16e1fb2413b7e0780f603f26de91 +2011-04-04T13:39:25.410063Z +1 +slice2009 + + + + + + + + + + + + + + + + + + + + + +134 + +lslr.c +file + + + + +2012-05-15T01:06:34.000000Z +a26b88236b0a0e7bfbb2adaa5d4836f4 +2011-04-04T13:39:25.410063Z +1 +slice2009 + + + + + + + + + + + + + + + + + + + + + +4059 + diff --git a/filesystems/test/.svn/text-base/README.svn-base b/filesystems/test/.svn/text-base/README.svn-base new file mode 100644 index 0000000..c7c34e4 --- /dev/null +++ b/filesystems/test/.svn/text-base/README.svn-base @@ -0,0 +1,2 @@ +This folder contains tests for VBoxFsDxe module, allowing up +and test filesystems without EFI environment and launching whole VBox. diff --git a/filesystems/test/.svn/text-base/fsw_posix.c.svn-base b/filesystems/test/.svn/text-base/fsw_posix.c.svn-base new file mode 100644 index 0000000..eee8c5f --- /dev/null +++ b/filesystems/test/.svn/text-base/fsw_posix.c.svn-base @@ -0,0 +1,479 @@ +/** + * \file fsw_posix.c + * POSIX user space host environment code. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_posix.h" + + +#ifndef FSTYPE +/** The file system type name to use. */ +#define FSTYPE ext2 +#endif + + +// function prototypes + +fsw_status_t fsw_posix_open_dno(struct fsw_posix_volume *pvol, const char *path, int required_type, + struct fsw_shandle *shand); + +void fsw_posix_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize); +fsw_status_t fsw_posix_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer); + +/** + * Dispatch table for our FSW host driver. + */ + +struct fsw_host_table fsw_posix_host_table = { + FSW_STRING_TYPE_ISO88591, + + fsw_posix_change_blocksize, + fsw_posix_read_block +}; + +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE); + + +/** + * Mount function. + */ + +struct fsw_posix_volume * fsw_posix_mount(const char *path, struct fsw_fstype_table *fstype_table) +{ + fsw_status_t status; + struct fsw_posix_volume *pvol; + + // allocate volume structure + status = fsw_alloc_zero(sizeof(struct fsw_posix_volume), (void **)&pvol); + if (status) + return NULL; + pvol->fd = -1; + + // open underlying file/device + pvol->fd = open(path, O_RDONLY, 0); + if (pvol->fd < 0) { + fprintf(stderr, "fsw_posix_mount: %s: %s\n", path, strerror(errno)); + fsw_free(pvol); + return NULL; + } + + // mount the filesystem + if (fstype_table == NULL) + fstype_table = &FSW_FSTYPE_TABLE_NAME(FSTYPE); + status = fsw_mount(pvol, &fsw_posix_host_table, fstype_table, &pvol->vol); + if (status) { + fprintf(stderr, "fsw_posix_mount: fsw_mount returned %d\n", status); + fsw_free(pvol); + return NULL; + } + + return pvol; +} + +/** + * Unmount function. + */ + +int fsw_posix_unmount(struct fsw_posix_volume *pvol) +{ + if (pvol->vol != NULL) + fsw_unmount(pvol->vol); + fsw_free(pvol); + return 0; +} + +/** + * Open a named regular file. + */ + +struct fsw_posix_file * fsw_posix_open(struct fsw_posix_volume *pvol, const char *path, int flags, mode_t mode) +{ + fsw_status_t status; + struct fsw_posix_file *file; + + // TODO: check flags for unwanted values + + // allocate file structure + status = fsw_alloc(sizeof(struct fsw_posix_file), &file); + if (status) + return NULL; + file->pvol = pvol; + + // open the file + status = fsw_posix_open_dno(pvol, path, FSW_DNODE_TYPE_FILE, &file->shand); + if (status) { + fprintf(stderr, "fsw_posix_open: open_dno returned %d\n", status); + fsw_free(file); + return NULL; + } + + return file; +} + +/** + * Read data from a regular file. + */ + +ssize_t fsw_posix_read(struct fsw_posix_file *file, void *buf, size_t nbytes) +{ + fsw_status_t status; + fsw_u32 buffer_size; + + buffer_size = nbytes; + status = fsw_shandle_read(&file->shand, &buffer_size, buf); + if (status) + return -1; + return buffer_size; +} + +/** + * Change position within a regular file. + */ + +off_t fsw_posix_lseek(struct fsw_posix_file *file, off_t offset, int whence) +{ + fsw_u64 base_offset = 0; + + // get base offset + base_offset = 0; + if (whence == SEEK_CUR) + base_offset = file->shand.pos; + else if (whence == SEEK_END) + base_offset = file->shand.dnode->size; + + // calculate new offset, prevent seeks before the start of the file + if (offset < 0 && -offset > base_offset) + file->shand.pos = 0; + else + file->shand.pos = base_offset + offset; + + return file->shand.pos; +} + +/** + * Close a regular file. + */ + +int fsw_posix_close(struct fsw_posix_file *file) +{ + fsw_shandle_close(&file->shand); + fsw_free(file); + return 0; +} + +/** + * Open a directory for iteration. + */ + +struct fsw_posix_dir * fsw_posix_opendir(struct fsw_posix_volume *pvol, const char *path) +{ + fsw_status_t status; + struct fsw_posix_dir *dir; + + // allocate file structure + status = fsw_alloc(sizeof(struct fsw_posix_dir), &dir); + if (status) + return NULL; + dir->pvol = pvol; + + // open the directory + status = fsw_posix_open_dno(pvol, path, FSW_DNODE_TYPE_DIR, &dir->shand); + if (status) { + fprintf(stderr, "fsw_posix_opendir: open_dno returned %d\n", status); + fsw_free(dir); + return NULL; + } + + return dir; +} + +/** + * Read the next entry from a directory. + */ + +struct dirent * fsw_posix_readdir(struct fsw_posix_dir *dir) +{ + fsw_status_t status; + struct fsw_dnode *dno; + static struct dirent dent; + + // get next entry from file system + status = fsw_dnode_dir_read(&dir->shand, &dno); + if (status) { + if (status != 4) + fprintf(stderr, "fsw_posix_readdir: fsw_dnode_dir_read returned %d\n", status); + return NULL; + } + status = fsw_dnode_fill(dno); + if (status) { + fprintf(stderr, "fsw_posix_readdir: fsw_dnode_fill returned %d\n", status); + fsw_dnode_release(dno); + return NULL; + } + + // fill dirent structure + dent.d_fileno = dno->dnode_id; + dent.d_reclen = 8 + dno->name.size + 1; + switch (dno->type) { + case FSW_DNODE_TYPE_FILE: + dent.d_type = DT_REG; + break; + case FSW_DNODE_TYPE_DIR: + dent.d_type = DT_DIR; + break; + case FSW_DNODE_TYPE_SYMLINK: + dent.d_type = DT_LNK; + break; + default: + dent.d_type = DT_UNKNOWN; + break; + } +#if 0 + dent.d_namlen = dno->name.size; +#endif + memcpy(dent.d_name, dno->name.data, dno->name.size); + dent.d_name[dno->name.size] = 0; + + return &dent; +} + +/** + * Rewind a directory to the start. + */ + +void fsw_posix_rewinddir(struct fsw_posix_dir *dir) +{ + dir->shand.pos = 0; +} + +/** + * Close a directory. + */ + +int fsw_posix_closedir(struct fsw_posix_dir *dir) +{ + fsw_shandle_close(&dir->shand); + fsw_free(dir); + return 0; +} + +/** + * Open a shand of a required type by path. + */ + +fsw_status_t fsw_posix_open_dno(struct fsw_posix_volume *pvol, const char *path, int required_type, struct fsw_shandle *shand) +{ + fsw_status_t status; + struct fsw_dnode *dno; + struct fsw_dnode *target_dno; + struct fsw_string lookup_path; + + lookup_path.type = FSW_STRING_TYPE_ISO88591; + lookup_path.len = strlen(path); + lookup_path.size = lookup_path.len; + lookup_path.data = (void *)path; + + // resolve the path (symlinks along the way are automatically resolved) + status = fsw_dnode_lookup_path(pvol->vol->root, &lookup_path, '/', &dno); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_lookup_path returned %d\n", status); + return status; + } + + // if the final node is a symlink, also resolve it + status = fsw_dnode_resolve(dno, &target_dno); + fsw_dnode_release(dno); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_resolve returned %d\n", status); + return status; + } + dno = target_dno; + + // check that it is a regular file + status = fsw_dnode_fill(dno); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_fill returned %d\n", status); + fsw_dnode_release(dno); + return status; + } + if (dno->type != required_type) { + fprintf(stderr, "fsw_posix_open_dno: dnode is not of the requested type\n"); + fsw_dnode_release(dno); + return FSW_UNSUPPORTED; + } + + // open shandle + status = fsw_shandle_open(dno, shand); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_shandle_open returned %d\n", status); + } + fsw_dnode_release(dno); + return status; +} + +/** + * FSW interface function for block size changes. This function is called by the FSW core + * when the file system driver changes the block sizes for the volume. + */ + +void fsw_posix_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize) +{ + // nothing to do +} + +/** + * FSW interface function to read data blocks. This function is called by the FSW core + * to read a block of data from the device. The buffer is allocated by the core code. + */ + +fsw_status_t fsw_posix_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer) +{ + struct fsw_posix_volume *pvol = (struct fsw_posix_volume *)vol->host_data; + off_t block_offset, seek_result; + ssize_t read_result; + + FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_posix_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize)); + + // read from disk + block_offset = (off_t)phys_bno * vol->phys_blocksize; + seek_result = lseek(pvol->fd, block_offset, SEEK_SET); + if (seek_result != block_offset) + return FSW_IO_ERROR; + read_result = read(pvol->fd, buffer, vol->phys_blocksize); + if (read_result != vol->phys_blocksize) + return FSW_IO_ERROR; + + return FSW_SUCCESS; +} + + +/** + * Time mapping callback for the fsw_dnode_stat call. This function converts + * a Posix style timestamp into an EFI_TIME structure and writes it to the + * appropriate member of the EFI_FILE_INFO structure that we're filling. + */ + +/* +static void fsw_posix_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if (which == FSW_DNODE_STAT_CTIME) + fsw_posix_decode_time(&FileInfo->CreateTime, posix_time); + else if (which == FSW_DNODE_STAT_MTIME) + fsw_posix_decode_time(&FileInfo->ModificationTime, posix_time); + else if (which == FSW_DNODE_STAT_ATIME) + fsw_posix_decode_time(&FileInfo->LastAccessTime, posix_time); +} +*/ + +/** + * Mode mapping callback for the fsw_dnode_stat call. This function looks at + * the Posix mode passed by the file system driver and makes appropriate + * adjustments to the EFI_FILE_INFO structure that we're filling. + */ + +/* +static void fsw_posix_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if ((posix_mode & S_IWUSR) == 0) + FileInfo->Attribute |= EFI_FILE_READ_ONLY; +} +*/ + +/** + * Common function to fill an EFI_FILE_INFO with information about a dnode. + */ + +/* +EFI_STATUS fsw_posix_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, + IN struct fsw_dnode *dno, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + EFI_FILE_INFO *FileInfo; + UINTN RequiredSize; + struct fsw_dnode_stat sb; + + // make sure the dnode has complete info + Status = fsw_posix_map_status(fsw_dnode_fill(dno), Volume); + if (EFI_ERROR(Status)) + return Status; + + // TODO: check/assert that the dno's name is in UTF16 + + // check buffer size + RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_posix_strsize(&dno->name); + if (*BufferSize < RequiredSize) { + // TODO: wind back the directory in this case + + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // fill structure + ZeroMem(Buffer, RequiredSize); + FileInfo = (EFI_FILE_INFO *)Buffer; + FileInfo->Size = RequiredSize; + FileInfo->FileSize = dno->size; + FileInfo->Attribute = 0; + if (dno->type == FSW_DNODE_TYPE_DIR) + FileInfo->Attribute |= EFI_FILE_DIRECTORY; + fsw_posix_strcpy(FileInfo->FileName, &dno->name); + + // get the missing info from the fs driver + ZeroMem(&sb, sizeof(struct fsw_dnode_stat)); + sb.store_time_posix = fsw_posix_store_time_posix; + sb.store_attr_posix = fsw_posix_store_attr_posix; + sb.host_data = FileInfo; + Status = fsw_posix_map_status(fsw_dnode_stat(dno, &sb), Volume); + if (EFI_ERROR(Status)) + return Status; + FileInfo->PhysicalSize = sb.used_bytes; + + // prepare for return + *BufferSize = RequiredSize; + return EFI_SUCCESS; +} +*/ + +// EOF diff --git a/filesystems/test/.svn/text-base/fsw_posix.h.svn-base b/filesystems/test/.svn/text-base/fsw_posix.h.svn-base new file mode 100644 index 0000000..8ab72f5 --- /dev/null +++ b/filesystems/test/.svn/text-base/fsw_posix.h.svn-base @@ -0,0 +1,98 @@ +/** + * \file fsw_posix.h + * POSIX user space host environment header. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_POSIX_H_ +#define _FSW_POSIX_H_ + +#include "fsw_core.h" + +#include +#include +#include + + +/** + * POSIX Host: Private per-volume structure. + */ + +struct fsw_posix_volume { + struct fsw_volume *vol; //!< FSW volume structure + + int fd; //!< System file descriptor for data access + +}; + +/** + * POSIX Host: Private structure for an open file. + */ + +struct fsw_posix_file { + struct fsw_posix_volume *pvol; //!< POSIX host volume structure + + struct fsw_shandle shand; //!< FSW handle for this file + +}; + +/** + * POSIX Host: Private structure for an open directory. + */ + +struct fsw_posix_dir { + struct fsw_posix_volume *pvol; //!< POSIX host volume structure + + struct fsw_shandle shand; //!< FSW handle for this file + +}; + + +/* functions */ + +struct fsw_posix_volume * fsw_posix_mount(const char *path, struct fsw_fstype_table *fstype_table); +int fsw_posix_unmount(struct fsw_posix_volume *pvol); + +struct fsw_posix_file * fsw_posix_open(struct fsw_posix_volume *pvol, const char *path, int flags, mode_t mode); +ssize_t fsw_posix_read(struct fsw_posix_file *file, void *buf, size_t nbytes); +off_t fsw_posix_lseek(struct fsw_posix_file *file, off_t offset, int whence); +int fsw_posix_close(struct fsw_posix_file *file); + +struct fsw_posix_dir * fsw_posix_opendir(struct fsw_posix_volume *pvol, const char *path); +struct dirent * fsw_posix_readdir(struct fsw_posix_dir *dir); +void fsw_posix_rewinddir(struct fsw_posix_dir *dir); +int fsw_posix_closedir(struct fsw_posix_dir *dir); + + +#endif diff --git a/filesystems/test/.svn/text-base/fsw_posix_base.h.svn-base b/filesystems/test/.svn/text-base/fsw_posix_base.h.svn-base new file mode 100644 index 0000000..ee1d96c --- /dev/null +++ b/filesystems/test/.svn/text-base/fsw_posix_base.h.svn-base @@ -0,0 +1,89 @@ +/** + * \file fsw_posix_base.h + * Base definitions for the POSIX user space host environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_POSIX_BASE_H_ +#define _FSW_POSIX_BASE_H_ + + +#include +#include +#include +#include +#include + +#define FSW_LITTLE_ENDIAN (1) +// TODO: use info from the headers to define FSW_LITTLE_ENDIAN or FSW_BIG_ENDIAN + + +// types + +typedef signed char fsw_s8; +typedef unsigned char fsw_u8; +typedef short fsw_s16; +typedef unsigned short fsw_u16; +typedef long fsw_s32; +typedef unsigned long fsw_u32; +typedef long long fsw_s64; +typedef unsigned long long fsw_u64; + + +// allocation functions + +#define fsw_alloc(size, ptrptr) (((*(ptrptr) = malloc(size)) == NULL) ? FSW_OUT_OF_MEMORY : FSW_SUCCESS) +#define fsw_free(ptr) free(ptr) + +// memory functions + +#define fsw_memzero(dest,size) memset(dest,0,size) +#define fsw_memcpy(dest,src,size) memcpy(dest,src,size) +#define fsw_memeq(p1,p2,size) (memcmp(p1,p2,size) == 0) + +// message printing + +#define FSW_MSGSTR(s) s +#define FSW_MSGFUNC printf + +// 64-bit hooks + +#define FSW_U64_SHR(val,shiftbits) ((val) >> (shiftbits)) +#define FSW_U64_DIV(val,divisor) ((val) / (divisor)) +#define DEBUG(x) + +#define RShiftU64(val, shift) ((val) >> (shift)) +#define LShiftU64(val, shift) ((val) << (shift)) + +#endif diff --git a/filesystems/test/.svn/text-base/lslr.c.svn-base b/filesystems/test/.svn/text-base/lslr.c.svn-base new file mode 100644 index 0000000..d3e4e3c --- /dev/null +++ b/filesystems/test/.svn/text-base/lslr.c.svn-base @@ -0,0 +1,136 @@ +/** + * \file lslr.c + * Test program for the POSIX user space environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_posix.h" + + +//extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext2); +//extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(reiserfs); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE); + +static struct fsw_fstype_table *fstypes[] = { + //&FSW_FSTYPE_TABLE_NAME(ext2), + //&FSW_FSTYPE_TABLE_NAME(reiserfs), + &FSW_FSTYPE_TABLE_NAME(FSTYPE ), + NULL +}; + +static int listdir(struct fsw_posix_volume *vol, char *path, int level) +{ + struct fsw_posix_dir *dir; + struct dirent *dent; + int i; + char subpath[4096]; + + dir = fsw_posix_opendir(vol, path); + if (dir == NULL) { + printf("opendir(%s) call failed.\n", path); + return 1; + } + while ((dent = fsw_posix_readdir(dir)) != NULL) { + for (i = 0; i < level*2; i++) + fputc(' ', stdout); + printf("%d %s\n", dent->d_type, dent->d_name); + + if (dent->d_type == DT_DIR) { + snprintf(subpath, 4095, "%s%s/", path, dent->d_name); + listdir(vol, subpath, level + 1); + } + } + fsw_posix_closedir(dir); + + return 0; +} + +static int catfile(struct fsw_posix_volume *vol, char *path) +{ + struct fsw_posix_file *file; + int r; + char buf[256]; + + file = fsw_posix_open(vol, path, 0, 0); + if (file == NULL) { + printf("open(%s) call failed.\n", path); + return 1; + } + while ((r=fsw_posix_read(file, buf, sizeof(buf))) > 0) + { + int i; + for (i=0; i\n"); + return 1; + } + + for (i = 0; fstypes[i]; i++) { + vol = fsw_posix_mount(argv[1], fstypes[i]); + if (vol != NULL) { + printf("Mounted as '%s'.\n", fstypes[i]->name.data); + break; + } + } + if (vol == NULL) { + printf("Mounting failed.\n"); + return 1; + } + + //listdir(vol, "/System/Library/Extensions/udf.kext/", 0); + //listdir(vol, "/System/Library/Extensions/AppleACPIPlatform.kext/", 0); + //listdir(vol, "/System/Library/Extensions/", 0); + catfile(vol, "/System/Library/Extensions/AppleHPET.kext/Contents/Info.plist"); + //listdir(vol, "/", 0); + + fsw_posix_unmount(vol); + + return 0; +} + +// EOF diff --git a/filesystems/test/.svn/text-base/lsroot.c.svn-base b/filesystems/test/.svn/text-base/lsroot.c.svn-base new file mode 100644 index 0000000..bb2cba0 --- /dev/null +++ b/filesystems/test/.svn/text-base/lsroot.c.svn-base @@ -0,0 +1,79 @@ +/** + * \file lsroot.c + * Example program for the POSIX user space environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_posix.h" + + +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext2); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(reiserfs); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(iso9660); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfs); + +int main(int argc, char **argv) +{ + struct fsw_posix_volume *vol; + struct fsw_posix_dir *dir; + struct dirent *dent; + + if (argc != 2) { + printf("Usage: lsroot \n"); + return 1; + } + + //vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(ext2)); + //vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(reiserfs)); + vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(FSTYPE)); + if (vol == NULL) { + printf("Mounting failed.\n"); + return 1; + } + //dir = fsw_posix_opendir(vol, "/drivers/net/"); + dir = fsw_posix_opendir(vol, "/"); + if (dir == NULL) { + printf("opendir call failed.\n"); + return 1; + } + while ((dent = fsw_posix_readdir(dir)) != NULL) { + printf("- %s\n", dent->d_name); + } + fsw_posix_closedir(dir); + fsw_posix_unmount(vol); + + return 0; +} + +// EOF diff --git a/filesystems/test/README b/filesystems/test/README new file mode 100644 index 0000000..c7c34e4 --- /dev/null +++ b/filesystems/test/README @@ -0,0 +1,2 @@ +This folder contains tests for VBoxFsDxe module, allowing up +and test filesystems without EFI environment and launching whole VBox. diff --git a/filesystems/test/fsw_posix.c b/filesystems/test/fsw_posix.c new file mode 100644 index 0000000..eee8c5f --- /dev/null +++ b/filesystems/test/fsw_posix.c @@ -0,0 +1,479 @@ +/** + * \file fsw_posix.c + * POSIX user space host environment code. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_posix.h" + + +#ifndef FSTYPE +/** The file system type name to use. */ +#define FSTYPE ext2 +#endif + + +// function prototypes + +fsw_status_t fsw_posix_open_dno(struct fsw_posix_volume *pvol, const char *path, int required_type, + struct fsw_shandle *shand); + +void fsw_posix_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize); +fsw_status_t fsw_posix_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer); + +/** + * Dispatch table for our FSW host driver. + */ + +struct fsw_host_table fsw_posix_host_table = { + FSW_STRING_TYPE_ISO88591, + + fsw_posix_change_blocksize, + fsw_posix_read_block +}; + +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE); + + +/** + * Mount function. + */ + +struct fsw_posix_volume * fsw_posix_mount(const char *path, struct fsw_fstype_table *fstype_table) +{ + fsw_status_t status; + struct fsw_posix_volume *pvol; + + // allocate volume structure + status = fsw_alloc_zero(sizeof(struct fsw_posix_volume), (void **)&pvol); + if (status) + return NULL; + pvol->fd = -1; + + // open underlying file/device + pvol->fd = open(path, O_RDONLY, 0); + if (pvol->fd < 0) { + fprintf(stderr, "fsw_posix_mount: %s: %s\n", path, strerror(errno)); + fsw_free(pvol); + return NULL; + } + + // mount the filesystem + if (fstype_table == NULL) + fstype_table = &FSW_FSTYPE_TABLE_NAME(FSTYPE); + status = fsw_mount(pvol, &fsw_posix_host_table, fstype_table, &pvol->vol); + if (status) { + fprintf(stderr, "fsw_posix_mount: fsw_mount returned %d\n", status); + fsw_free(pvol); + return NULL; + } + + return pvol; +} + +/** + * Unmount function. + */ + +int fsw_posix_unmount(struct fsw_posix_volume *pvol) +{ + if (pvol->vol != NULL) + fsw_unmount(pvol->vol); + fsw_free(pvol); + return 0; +} + +/** + * Open a named regular file. + */ + +struct fsw_posix_file * fsw_posix_open(struct fsw_posix_volume *pvol, const char *path, int flags, mode_t mode) +{ + fsw_status_t status; + struct fsw_posix_file *file; + + // TODO: check flags for unwanted values + + // allocate file structure + status = fsw_alloc(sizeof(struct fsw_posix_file), &file); + if (status) + return NULL; + file->pvol = pvol; + + // open the file + status = fsw_posix_open_dno(pvol, path, FSW_DNODE_TYPE_FILE, &file->shand); + if (status) { + fprintf(stderr, "fsw_posix_open: open_dno returned %d\n", status); + fsw_free(file); + return NULL; + } + + return file; +} + +/** + * Read data from a regular file. + */ + +ssize_t fsw_posix_read(struct fsw_posix_file *file, void *buf, size_t nbytes) +{ + fsw_status_t status; + fsw_u32 buffer_size; + + buffer_size = nbytes; + status = fsw_shandle_read(&file->shand, &buffer_size, buf); + if (status) + return -1; + return buffer_size; +} + +/** + * Change position within a regular file. + */ + +off_t fsw_posix_lseek(struct fsw_posix_file *file, off_t offset, int whence) +{ + fsw_u64 base_offset = 0; + + // get base offset + base_offset = 0; + if (whence == SEEK_CUR) + base_offset = file->shand.pos; + else if (whence == SEEK_END) + base_offset = file->shand.dnode->size; + + // calculate new offset, prevent seeks before the start of the file + if (offset < 0 && -offset > base_offset) + file->shand.pos = 0; + else + file->shand.pos = base_offset + offset; + + return file->shand.pos; +} + +/** + * Close a regular file. + */ + +int fsw_posix_close(struct fsw_posix_file *file) +{ + fsw_shandle_close(&file->shand); + fsw_free(file); + return 0; +} + +/** + * Open a directory for iteration. + */ + +struct fsw_posix_dir * fsw_posix_opendir(struct fsw_posix_volume *pvol, const char *path) +{ + fsw_status_t status; + struct fsw_posix_dir *dir; + + // allocate file structure + status = fsw_alloc(sizeof(struct fsw_posix_dir), &dir); + if (status) + return NULL; + dir->pvol = pvol; + + // open the directory + status = fsw_posix_open_dno(pvol, path, FSW_DNODE_TYPE_DIR, &dir->shand); + if (status) { + fprintf(stderr, "fsw_posix_opendir: open_dno returned %d\n", status); + fsw_free(dir); + return NULL; + } + + return dir; +} + +/** + * Read the next entry from a directory. + */ + +struct dirent * fsw_posix_readdir(struct fsw_posix_dir *dir) +{ + fsw_status_t status; + struct fsw_dnode *dno; + static struct dirent dent; + + // get next entry from file system + status = fsw_dnode_dir_read(&dir->shand, &dno); + if (status) { + if (status != 4) + fprintf(stderr, "fsw_posix_readdir: fsw_dnode_dir_read returned %d\n", status); + return NULL; + } + status = fsw_dnode_fill(dno); + if (status) { + fprintf(stderr, "fsw_posix_readdir: fsw_dnode_fill returned %d\n", status); + fsw_dnode_release(dno); + return NULL; + } + + // fill dirent structure + dent.d_fileno = dno->dnode_id; + dent.d_reclen = 8 + dno->name.size + 1; + switch (dno->type) { + case FSW_DNODE_TYPE_FILE: + dent.d_type = DT_REG; + break; + case FSW_DNODE_TYPE_DIR: + dent.d_type = DT_DIR; + break; + case FSW_DNODE_TYPE_SYMLINK: + dent.d_type = DT_LNK; + break; + default: + dent.d_type = DT_UNKNOWN; + break; + } +#if 0 + dent.d_namlen = dno->name.size; +#endif + memcpy(dent.d_name, dno->name.data, dno->name.size); + dent.d_name[dno->name.size] = 0; + + return &dent; +} + +/** + * Rewind a directory to the start. + */ + +void fsw_posix_rewinddir(struct fsw_posix_dir *dir) +{ + dir->shand.pos = 0; +} + +/** + * Close a directory. + */ + +int fsw_posix_closedir(struct fsw_posix_dir *dir) +{ + fsw_shandle_close(&dir->shand); + fsw_free(dir); + return 0; +} + +/** + * Open a shand of a required type by path. + */ + +fsw_status_t fsw_posix_open_dno(struct fsw_posix_volume *pvol, const char *path, int required_type, struct fsw_shandle *shand) +{ + fsw_status_t status; + struct fsw_dnode *dno; + struct fsw_dnode *target_dno; + struct fsw_string lookup_path; + + lookup_path.type = FSW_STRING_TYPE_ISO88591; + lookup_path.len = strlen(path); + lookup_path.size = lookup_path.len; + lookup_path.data = (void *)path; + + // resolve the path (symlinks along the way are automatically resolved) + status = fsw_dnode_lookup_path(pvol->vol->root, &lookup_path, '/', &dno); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_lookup_path returned %d\n", status); + return status; + } + + // if the final node is a symlink, also resolve it + status = fsw_dnode_resolve(dno, &target_dno); + fsw_dnode_release(dno); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_resolve returned %d\n", status); + return status; + } + dno = target_dno; + + // check that it is a regular file + status = fsw_dnode_fill(dno); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_fill returned %d\n", status); + fsw_dnode_release(dno); + return status; + } + if (dno->type != required_type) { + fprintf(stderr, "fsw_posix_open_dno: dnode is not of the requested type\n"); + fsw_dnode_release(dno); + return FSW_UNSUPPORTED; + } + + // open shandle + status = fsw_shandle_open(dno, shand); + if (status) { + fprintf(stderr, "fsw_posix_open_dno: fsw_shandle_open returned %d\n", status); + } + fsw_dnode_release(dno); + return status; +} + +/** + * FSW interface function for block size changes. This function is called by the FSW core + * when the file system driver changes the block sizes for the volume. + */ + +void fsw_posix_change_blocksize(struct fsw_volume *vol, + fsw_u32 old_phys_blocksize, fsw_u32 old_log_blocksize, + fsw_u32 new_phys_blocksize, fsw_u32 new_log_blocksize) +{ + // nothing to do +} + +/** + * FSW interface function to read data blocks. This function is called by the FSW core + * to read a block of data from the device. The buffer is allocated by the core code. + */ + +fsw_status_t fsw_posix_read_block(struct fsw_volume *vol, fsw_u32 phys_bno, void *buffer) +{ + struct fsw_posix_volume *pvol = (struct fsw_posix_volume *)vol->host_data; + off_t block_offset, seek_result; + ssize_t read_result; + + FSW_MSG_DEBUGV((FSW_MSGSTR("fsw_posix_read_block: %d (%d)\n"), phys_bno, vol->phys_blocksize)); + + // read from disk + block_offset = (off_t)phys_bno * vol->phys_blocksize; + seek_result = lseek(pvol->fd, block_offset, SEEK_SET); + if (seek_result != block_offset) + return FSW_IO_ERROR; + read_result = read(pvol->fd, buffer, vol->phys_blocksize); + if (read_result != vol->phys_blocksize) + return FSW_IO_ERROR; + + return FSW_SUCCESS; +} + + +/** + * Time mapping callback for the fsw_dnode_stat call. This function converts + * a Posix style timestamp into an EFI_TIME structure and writes it to the + * appropriate member of the EFI_FILE_INFO structure that we're filling. + */ + +/* +static void fsw_posix_store_time_posix(struct fsw_dnode_stat *sb, int which, fsw_u32 posix_time) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if (which == FSW_DNODE_STAT_CTIME) + fsw_posix_decode_time(&FileInfo->CreateTime, posix_time); + else if (which == FSW_DNODE_STAT_MTIME) + fsw_posix_decode_time(&FileInfo->ModificationTime, posix_time); + else if (which == FSW_DNODE_STAT_ATIME) + fsw_posix_decode_time(&FileInfo->LastAccessTime, posix_time); +} +*/ + +/** + * Mode mapping callback for the fsw_dnode_stat call. This function looks at + * the Posix mode passed by the file system driver and makes appropriate + * adjustments to the EFI_FILE_INFO structure that we're filling. + */ + +/* +static void fsw_posix_store_attr_posix(struct fsw_dnode_stat *sb, fsw_u16 posix_mode) +{ + EFI_FILE_INFO *FileInfo = (EFI_FILE_INFO *)sb->host_data; + + if ((posix_mode & S_IWUSR) == 0) + FileInfo->Attribute |= EFI_FILE_READ_ONLY; +} +*/ + +/** + * Common function to fill an EFI_FILE_INFO with information about a dnode. + */ + +/* +EFI_STATUS fsw_posix_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, + IN struct fsw_dnode *dno, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer) +{ + EFI_STATUS Status; + EFI_FILE_INFO *FileInfo; + UINTN RequiredSize; + struct fsw_dnode_stat sb; + + // make sure the dnode has complete info + Status = fsw_posix_map_status(fsw_dnode_fill(dno), Volume); + if (EFI_ERROR(Status)) + return Status; + + // TODO: check/assert that the dno's name is in UTF16 + + // check buffer size + RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_posix_strsize(&dno->name); + if (*BufferSize < RequiredSize) { + // TODO: wind back the directory in this case + + *BufferSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + // fill structure + ZeroMem(Buffer, RequiredSize); + FileInfo = (EFI_FILE_INFO *)Buffer; + FileInfo->Size = RequiredSize; + FileInfo->FileSize = dno->size; + FileInfo->Attribute = 0; + if (dno->type == FSW_DNODE_TYPE_DIR) + FileInfo->Attribute |= EFI_FILE_DIRECTORY; + fsw_posix_strcpy(FileInfo->FileName, &dno->name); + + // get the missing info from the fs driver + ZeroMem(&sb, sizeof(struct fsw_dnode_stat)); + sb.store_time_posix = fsw_posix_store_time_posix; + sb.store_attr_posix = fsw_posix_store_attr_posix; + sb.host_data = FileInfo; + Status = fsw_posix_map_status(fsw_dnode_stat(dno, &sb), Volume); + if (EFI_ERROR(Status)) + return Status; + FileInfo->PhysicalSize = sb.used_bytes; + + // prepare for return + *BufferSize = RequiredSize; + return EFI_SUCCESS; +} +*/ + +// EOF diff --git a/filesystems/test/fsw_posix.h b/filesystems/test/fsw_posix.h new file mode 100644 index 0000000..8ab72f5 --- /dev/null +++ b/filesystems/test/fsw_posix.h @@ -0,0 +1,98 @@ +/** + * \file fsw_posix.h + * POSIX user space host environment header. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_POSIX_H_ +#define _FSW_POSIX_H_ + +#include "fsw_core.h" + +#include +#include +#include + + +/** + * POSIX Host: Private per-volume structure. + */ + +struct fsw_posix_volume { + struct fsw_volume *vol; //!< FSW volume structure + + int fd; //!< System file descriptor for data access + +}; + +/** + * POSIX Host: Private structure for an open file. + */ + +struct fsw_posix_file { + struct fsw_posix_volume *pvol; //!< POSIX host volume structure + + struct fsw_shandle shand; //!< FSW handle for this file + +}; + +/** + * POSIX Host: Private structure for an open directory. + */ + +struct fsw_posix_dir { + struct fsw_posix_volume *pvol; //!< POSIX host volume structure + + struct fsw_shandle shand; //!< FSW handle for this file + +}; + + +/* functions */ + +struct fsw_posix_volume * fsw_posix_mount(const char *path, struct fsw_fstype_table *fstype_table); +int fsw_posix_unmount(struct fsw_posix_volume *pvol); + +struct fsw_posix_file * fsw_posix_open(struct fsw_posix_volume *pvol, const char *path, int flags, mode_t mode); +ssize_t fsw_posix_read(struct fsw_posix_file *file, void *buf, size_t nbytes); +off_t fsw_posix_lseek(struct fsw_posix_file *file, off_t offset, int whence); +int fsw_posix_close(struct fsw_posix_file *file); + +struct fsw_posix_dir * fsw_posix_opendir(struct fsw_posix_volume *pvol, const char *path); +struct dirent * fsw_posix_readdir(struct fsw_posix_dir *dir); +void fsw_posix_rewinddir(struct fsw_posix_dir *dir); +int fsw_posix_closedir(struct fsw_posix_dir *dir); + + +#endif diff --git a/filesystems/test/fsw_posix_base.h b/filesystems/test/fsw_posix_base.h new file mode 100644 index 0000000..ee1d96c --- /dev/null +++ b/filesystems/test/fsw_posix_base.h @@ -0,0 +1,89 @@ +/** + * \file fsw_posix_base.h + * Base definitions for the POSIX user space host environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSW_POSIX_BASE_H_ +#define _FSW_POSIX_BASE_H_ + + +#include +#include +#include +#include +#include + +#define FSW_LITTLE_ENDIAN (1) +// TODO: use info from the headers to define FSW_LITTLE_ENDIAN or FSW_BIG_ENDIAN + + +// types + +typedef signed char fsw_s8; +typedef unsigned char fsw_u8; +typedef short fsw_s16; +typedef unsigned short fsw_u16; +typedef long fsw_s32; +typedef unsigned long fsw_u32; +typedef long long fsw_s64; +typedef unsigned long long fsw_u64; + + +// allocation functions + +#define fsw_alloc(size, ptrptr) (((*(ptrptr) = malloc(size)) == NULL) ? FSW_OUT_OF_MEMORY : FSW_SUCCESS) +#define fsw_free(ptr) free(ptr) + +// memory functions + +#define fsw_memzero(dest,size) memset(dest,0,size) +#define fsw_memcpy(dest,src,size) memcpy(dest,src,size) +#define fsw_memeq(p1,p2,size) (memcmp(p1,p2,size) == 0) + +// message printing + +#define FSW_MSGSTR(s) s +#define FSW_MSGFUNC printf + +// 64-bit hooks + +#define FSW_U64_SHR(val,shiftbits) ((val) >> (shiftbits)) +#define FSW_U64_DIV(val,divisor) ((val) / (divisor)) +#define DEBUG(x) + +#define RShiftU64(val, shift) ((val) >> (shift)) +#define LShiftU64(val, shift) ((val) << (shift)) + +#endif diff --git a/filesystems/test/lslr.c b/filesystems/test/lslr.c new file mode 100644 index 0000000..d3e4e3c --- /dev/null +++ b/filesystems/test/lslr.c @@ -0,0 +1,136 @@ +/** + * \file lslr.c + * Test program for the POSIX user space environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_posix.h" + + +//extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext2); +//extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(reiserfs); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(FSTYPE); + +static struct fsw_fstype_table *fstypes[] = { + //&FSW_FSTYPE_TABLE_NAME(ext2), + //&FSW_FSTYPE_TABLE_NAME(reiserfs), + &FSW_FSTYPE_TABLE_NAME(FSTYPE ), + NULL +}; + +static int listdir(struct fsw_posix_volume *vol, char *path, int level) +{ + struct fsw_posix_dir *dir; + struct dirent *dent; + int i; + char subpath[4096]; + + dir = fsw_posix_opendir(vol, path); + if (dir == NULL) { + printf("opendir(%s) call failed.\n", path); + return 1; + } + while ((dent = fsw_posix_readdir(dir)) != NULL) { + for (i = 0; i < level*2; i++) + fputc(' ', stdout); + printf("%d %s\n", dent->d_type, dent->d_name); + + if (dent->d_type == DT_DIR) { + snprintf(subpath, 4095, "%s%s/", path, dent->d_name); + listdir(vol, subpath, level + 1); + } + } + fsw_posix_closedir(dir); + + return 0; +} + +static int catfile(struct fsw_posix_volume *vol, char *path) +{ + struct fsw_posix_file *file; + int r; + char buf[256]; + + file = fsw_posix_open(vol, path, 0, 0); + if (file == NULL) { + printf("open(%s) call failed.\n", path); + return 1; + } + while ((r=fsw_posix_read(file, buf, sizeof(buf))) > 0) + { + int i; + for (i=0; i\n"); + return 1; + } + + for (i = 0; fstypes[i]; i++) { + vol = fsw_posix_mount(argv[1], fstypes[i]); + if (vol != NULL) { + printf("Mounted as '%s'.\n", fstypes[i]->name.data); + break; + } + } + if (vol == NULL) { + printf("Mounting failed.\n"); + return 1; + } + + //listdir(vol, "/System/Library/Extensions/udf.kext/", 0); + //listdir(vol, "/System/Library/Extensions/AppleACPIPlatform.kext/", 0); + //listdir(vol, "/System/Library/Extensions/", 0); + catfile(vol, "/System/Library/Extensions/AppleHPET.kext/Contents/Info.plist"); + //listdir(vol, "/", 0); + + fsw_posix_unmount(vol); + + return 0; +} + +// EOF diff --git a/filesystems/test/lsroot.c b/filesystems/test/lsroot.c new file mode 100644 index 0000000..bb2cba0 --- /dev/null +++ b/filesystems/test/lsroot.c @@ -0,0 +1,79 @@ +/** + * \file lsroot.c + * Example program for the POSIX user space environment. + */ + +/*- + * Copyright (c) 2006 Christoph Pfisterer + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Christoph Pfisterer nor the names of the + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fsw_posix.h" + + +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(ext2); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(reiserfs); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(iso9660); +extern struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfs); + +int main(int argc, char **argv) +{ + struct fsw_posix_volume *vol; + struct fsw_posix_dir *dir; + struct dirent *dent; + + if (argc != 2) { + printf("Usage: lsroot \n"); + return 1; + } + + //vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(ext2)); + //vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(reiserfs)); + vol = fsw_posix_mount(argv[1], &FSW_FSTYPE_TABLE_NAME(FSTYPE)); + if (vol == NULL) { + printf("Mounting failed.\n"); + return 1; + } + //dir = fsw_posix_opendir(vol, "/drivers/net/"); + dir = fsw_posix_opendir(vol, "/"); + if (dir == NULL) { + printf("opendir call failed.\n"); + return 1; + } + while ((dent = fsw_posix_readdir(dir)) != NULL) { + printf("- %s\n", dent->d_name); + } + fsw_posix_closedir(dir); + fsw_posix_unmount(vol); + + return 0; +} + +// EOF