VirtualBox

Changeset 76292 in vbox for trunk/src/VBox/Runtime/common/fs


Ignore:
Timestamp:
Dec 19, 2018 1:49:08 PM (6 years ago)
Author:
vboxsync
Message:

Runtime/fs/extvfs: Working on parsing block group and inode descriptors

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/fs/extvfs.cpp

    r76256 r76292  
    3232#include <iprt/fsvfs.h>
    3333
     34#include <iprt/asm.h>
    3435#include <iprt/assert.h>
     36#include <iprt/avl.h>
    3537#include <iprt/file.h>
     38#include <iprt/list.h>
    3639#include <iprt/log.h>
    3740#include <iprt/mem.h>
     
    4346
    4447/*********************************************************************************************************************************
     48*   Defined Constants And Macros                                                                                                 *
     49*********************************************************************************************************************************/
     50/** The maximum block group cache size (in bytes). */
     51#if ARCH_BITS >= 64
     52# define RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE _512K
     53#else
     54# define RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE _128K
     55#endif
     56/** The maximum inode cache size (in bytes). */
     57#if ARCH_BITS >= 64
     58# define RTFSEXT_MAX_INODE_CACHE_SIZE       _512K
     59#else
     60# define RTFSEXT_MAX_INODE_CACHE_SIZE       _128K
     61#endif
     62
     63
     64/*********************************************************************************************************************************
    4565*   Structures and Typedefs                                                                                                      *
    4666*********************************************************************************************************************************/
     67/** Pointer to the ext filesystem data. */
     68typedef struct RTFSEXTVOL *PRTFSEXTVOL;
     69
     70
    4771/**
    4872 * Cached block group descriptor data.
     
    5074typedef struct RTFSEXTBLKGRP
    5175{
    52     /** Start offset (in bytes and from the start of the disk). */
    53     uint64_t    offStart;
    54     /** Last offset in the block group (inclusive). */
    55     uint64_t    offLast;
     76    /** AVL tree node, indexed by the block group number. */
     77    AVLU32NODECORE    Core;
     78    /** List node for the LRU list used for eviction. */
     79    RTLISTNODE        NdLru;
     80    /** Reference counter. */
     81    volatile uint32_t cRefs;
     82    /** Block number where the inode table is store. */
     83    uint64_t          iBlockInodeTbl;
     84    /** Pointer to the inode bitmap. */
     85    uint8_t           *pabInodeBitmap;
    5686    /** Block bitmap - variable in size (depends on the block size
    5787     * and number of blocks per group). */
    58     uint8_t     abBlockBitmap[1];
     88    uint8_t           abBlockBitmap[1];
    5989} RTFSEXTBLKGRP;
    6090/** Pointer to block group descriptor data. */
    6191typedef RTFSEXTBLKGRP *PRTFSEXTBLKGRP;
     92
     93
     94/**
     95 * In-memory inode.
     96 */
     97typedef struct RTFSEXTINODE
     98{
     99    /** AVL tree node, indexed by the inode number. */
     100    AVLU32NODECORE    Core;
     101    /** List node for the inode LRU list used for eviction. */
     102    RTLISTNODE        NdLru;
     103    /** Reference counter. */
     104    volatile uint32_t cRefs;
     105    /** Block number where the inode is stored. */
     106    uint64_t          iBlockInode;
     107    /** Offset in bytes into block where the inode is stored. */
     108    uint32_t          offInode;
     109    /** Inode data size. */
     110    uint64_t          cbData;
     111} RTFSEXTINODE;
     112/** Pointer to an in-memory inode. */
     113typedef RTFSEXTINODE *PRTFSEXTINODE;
     114
     115
     116/**
     117 * Open directory instance.
     118 */
     119typedef struct RTFSEXTDIR
     120{
     121    /** Volume this directory belongs to. */
     122    PRTFSEXTVOL         pVol;
     123    /** Set if we've reached the end of the directory enumeration. */
     124    bool                fNoMoreFiles;
     125} RTFSEXTDIR;
     126/** Pointer to an open directory instance. */
     127typedef RTFSEXTDIR *PRTFSEXTDIR;
     128
    62129
    63130/**
     
    72139    /** The size of the backing thingy. */
    73140    uint64_t            cbBacking;
    74     /** The formatted size of the volume. */
    75     uint64_t            cbVolume;
    76     /** cbVolume expressed as a cluster count. */
    77     uint64_t            cClusters;
    78141
    79142    /** RTVFSMNT_F_XXX. */
     
    82145    uint32_t            fExtFlags;
    83146
    84     /** The (logical) sector size. */
    85     uint32_t            cbSector;
    86 
    87     /** The (logical) cluster size. */
    88     uint32_t            cbCluster;
    89 
    90     /** Block number of the superblock. */
    91     uint32_t            iSbBlock;
     147    /** Flag whether the filesystem is 64bit. */
     148    bool                f64Bit;
    92149    /** Size of one block. */
    93150    size_t              cbBlock;
     151    /** Number of bits to shift left for fast conversion of block numbers to offsets. */
     152    uint32_t            cBlockShift;
    94153    /** Number of blocks in one group. */
    95     unsigned            cBlocksPerGroup;
     154    uint32_t            cBlocksPerGroup;
     155    /** Number of inodes in each block group. */
     156    uint32_t            cInodesPerGroup;
    96157    /** Number of blocks groups in the volume. */
    97     unsigned            cBlockGroups;
    98     /** Cached block group descriptor data. */
    99     PRTFSEXTBLKGRP      pBlkGrpDesc;
     158    uint32_t            cBlockGroups;
     159    /** Size of the block bitmap. */
     160    size_t              cbBlockBitmap;
     161    /** Size of the inode bitmap. */
     162    size_t              cbInodeBitmap;
     163    /** Size of block group descriptor. */
     164    size_t              cbBlkGrpDesc;
     165    /** Size of an inode. */
     166    size_t              cbInode;
     167
     168    /** @name Block group cache.
     169     * @{ */
     170    /** LRU list anchor. */
     171    RTLISTANCHOR        LstBlockGroupLru;
     172    /** Root of the cached block group tree. */
     173    AVLU32TREE          BlockGroupRoot;
     174    /** Size of the cached block groups. */
     175    size_t              cbBlockGroups;
     176    /** @} */
     177
     178    /** @name Inode cache.
     179     * @{ */
     180    /** LRU list anchor for the inode cache. */
     181    RTLISTANCHOR        LstInodeLru;
     182    /** Root of the cached inode tree. */
     183    AVLU32TREE          InodeRoot;
     184    /** Size of the cached inodes. */
     185    size_t              cbInodes;
     186    /** @} */
    100187} RTFSEXTVOL;
    101 /** Pointer to the ext filesystem data. */
    102 typedef RTFSEXTVOL *PRTFSEXTVOL;
    103 
    104 
    105 
    106 /**
    107  * Loads the block descriptor of the given block group from the medium.
     188
     189
     190
     191/*********************************************************************************************************************************
     192*   Internal Functions                                                                                                           *
     193*********************************************************************************************************************************/
     194
     195#ifdef LOG_ENABLED
     196/**
     197 * Logs the ext filesystem superblock.
     198 *
     199 * @returns nothing.
     200 * @param   pSb                 Pointer to the superblock.
     201 */
     202static void rtFsExtSb_Log(PCEXTSUPERBLOCK pSb)
     203{
     204    if (LogIs2Enabled())
     205    {
     206        RTTIMESPEC Spec;
     207        char       sz[80];
     208
     209        Log2(("EXT: Superblock:\n"));
     210        Log2(("EXT:   cInodesTotal                %RU32\n", RT_LE2H_U32(pSb->cInodesTotal)));
     211        Log2(("EXT:   cBlocksTotalLow             %RU32\n", RT_LE2H_U32(pSb->cBlocksTotalLow)));
     212        Log2(("EXT:   cBlocksRsvdForSuperUserLow  %RU32\n", RT_LE2H_U32(pSb->cBlocksRsvdForSuperUserLow)));
     213        Log2(("EXT:   cBlocksFreeLow              %RU32\n", RT_LE2H_U32(pSb->cBlocksFreeLow)));
     214        Log2(("EXT:   cInodesFree                 %RU32\n", RT_LE2H_U32(pSb->cInodesFree)));
     215        Log2(("EXT:   iBlockOfSuperblock          %RU32\n", RT_LE2H_U32(pSb->iBlockOfSuperblock)));
     216        Log2(("EXT:   cLogBlockSize               %RU32\n", RT_LE2H_U32(pSb->cLogBlockSize)));
     217        Log2(("EXT:   cLogClusterSize             %RU32\n", RT_LE2H_U32(pSb->cLogClusterSize)));
     218        Log2(("EXT:   cBlocksPerGroup             %RU32\n", RT_LE2H_U32(pSb->cBlocksPerGroup)));
     219        Log2(("EXT:   cClustersPerBlockGroup      %RU32\n", RT_LE2H_U32(pSb->cClustersPerBlockGroup)));
     220        Log2(("EXT:   cInodesPerBlockGroup        %RU32\n", RT_LE2H_U32(pSb->cInodesPerBlockGroup)));
     221        Log2(("EXT:   u32LastMountTime            %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastMountTime),
     222              RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastMountTime)), sz, sizeof(sz))));
     223        Log2(("EXT:   u32LastWrittenTime          %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastWrittenTime),
     224              RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastWrittenTime)), sz, sizeof(sz))));
     225        Log2(("EXT:   cMountsSinceLastCheck       %RU16\n", RT_LE2H_U32(pSb->cMountsSinceLastCheck)));
     226        Log2(("EXT:   cMaxMountsUntilCheck        %RU16\n", RT_LE2H_U32(pSb->cMaxMountsUntilCheck)));
     227        Log2(("EXT:   u16Signature                %#RX16\n", RT_LE2H_U32(pSb->u16Signature)));
     228        Log2(("EXT:   u16FilesystemState          %#RX16\n", RT_LE2H_U32(pSb->u16FilesystemState)));
     229        Log2(("EXT:   u16ActionOnError            %#RX16\n", RT_LE2H_U32(pSb->u16ActionOnError)));
     230        Log2(("EXT:   u16RevLvlMinor              %#RX16\n", RT_LE2H_U32(pSb->u16RevLvlMinor)));
     231        Log2(("EXT:   u32LastCheckTime            %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastCheckTime),
     232              RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastCheckTime)), sz, sizeof(sz))));
     233        Log2(("EXT:   u32CheckInterval            %RU32\n", RT_LE2H_U32(pSb->u32CheckInterval)));
     234        Log2(("EXT:   u32OsIdCreator              %#RX32\n", RT_LE2H_U32(pSb->u32OsIdCreator)));
     235        Log2(("EXT:   u32RevLvl                   %#RX32\n", RT_LE2H_U32(pSb->u32RevLvl)));
     236        Log2(("EXT:   u16UidReservedBlocks        %#RX16\n", RT_LE2H_U32(pSb->u16UidReservedBlocks)));
     237        Log2(("EXT:   u16GidReservedBlocks        %#RX16\n", RT_LE2H_U32(pSb->u16GidReservedBlocks)));
     238        if (RT_LE2H_U32(pSb->u32RevLvl) == EXT_SB_REV_V2_DYN_INODE_SZ)
     239        {
     240            Log2(("EXT:   iFirstInodeNonRsvd          %#RX32\n", RT_LE2H_U32(pSb->iFirstInodeNonRsvd)));
     241            Log2(("EXT:   cbInode                     %#RX16\n", RT_LE2H_U32(pSb->cbInode)));
     242            Log2(("EXT:   iBlkGrpSb                   %#RX16\n", RT_LE2H_U32(pSb->iBlkGrpSb)));
     243            Log2(("EXT:   fFeaturesCompat             %#RX32%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesCompat),
     244                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_DIR_PREALLOC   ? " dir-prealloc"  : "",
     245                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_IMAGIC_INODES  ? " imagic-inode"  : "",
     246                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_HAS_JOURNAL    ? " has-journal"   : "",
     247                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXT_ATTR       ? " ext-attrs"     : "",
     248                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_RESIZE_INODE   ? " resize-inode"  : "",
     249                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_DIR_INDEX      ? " dir-index"     : "",
     250                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_LAZY_BG        ? " lazy-bg"       : "",
     251                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXCLUDE_INODE  ? " excl-inode"    : "",
     252                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXCLUDE_BITMAP ? " excl-bitmap"   : "",
     253                  RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_SPARSE_SUPER2  ? " sparse-super2" : ""));
     254            Log2(("EXT:   fFeaturesIncompat           %#RX32%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesIncompat),
     255                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_COMPRESSION    ? " compression"   : "",
     256                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE   ? " dir-filetype"  : "",
     257                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_RECOVER        ? " recovery"      : "",
     258                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_JOURNAL_DEV    ? " journal-dev"   : "",
     259                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_META_BG        ? " meta-bg"       : "",
     260                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_EXTENTS        ? " extents"       : "",
     261                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_64BIT          ? " 64bit"         : "",
     262                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_MMP            ? " mmp"           : "",
     263                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_FLEX_BG        ? " flex-bg"       : "",
     264                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_EXT_ATTR_INODE ? " extattr-inode" : "",
     265                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_DIRDATA        ? " dir-data"      : "",
     266                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_CSUM_SEED      ? " csum-seed"     : "",
     267                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_LARGE_DIR      ? " large-dir"     : "",
     268                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_INLINE_DATA    ? " inline-data"   : "",
     269                  RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_ENCRYPT        ? " encrypt"       : ""));
     270            Log2(("EXT:   fFeaturesCompatRo           %#RX32%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesCompatRo),
     271                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_SPARSE_SUPER    ? " sparse-super"  : "",
     272                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_LARGE_FILE      ? " large-file"    : "",
     273                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_BTREE_DIR       ? " btree-dir"     : "",
     274                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_HUGE_FILE       ? " huge-file"     : "",
     275                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_GDT_CHSKUM      ? " gdt-chksum"    : "",
     276                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_DIR_NLINK       ? " dir-nlink"     : "",
     277                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_EXTRA_INODE_SZ  ? " extra-inode"   : "",
     278                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_HAS_SNAPSHOTS   ? " snapshots"     : "",
     279                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_QUOTA           ? " quota"         : "",
     280                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_BIGALLOC        ? " big-alloc"     : "",
     281                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_METADATA_CHKSUM ? " meta-chksum"   : "",
     282                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_REPLICA         ? " replica"       : "",
     283                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_READONLY        ? " ro"            : "",
     284                  RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_PROJECT         ? " project"       : ""));
     285            Log2(("EXT:   au8Uuid                     <todo>\n"));
     286            Log2(("EXT:   achVolumeName               %16s\n", &pSb->achVolumeName[0]));
     287            Log2(("EXT:   achLastMounted              %64s\n", &pSb->achLastMounted[0]));
     288            Log2(("EXT:   u32AlgoUsageBitmap          %#RX32\n", RT_LE2H_U32(pSb->u32AlgoUsageBitmap)));
     289            Log2(("EXT:   cBlocksPrealloc             %RU8\n", pSb->cBlocksPrealloc));
     290            Log2(("EXT:   cBlocksPreallocDirectory    %RU8\n", pSb->cBlocksPreallocDirectory));
     291            Log2(("EXT:   cGdtEntriesRsvd             %RU16\n", pSb->cGdtEntriesRsvd));
     292            Log2(("EXT:   au8JournalUuid              <todo>\n"));
     293            Log2(("EXT:   iJournalInode               %#RX32\n", RT_LE2H_U32(pSb->iJournalInode)));
     294            Log2(("EXT:   u32JournalDev               %#RX32\n", RT_LE2H_U32(pSb->u32JournalDev)));
     295            Log2(("EXT:   u32LastOrphan               %#RX32\n", RT_LE2H_U32(pSb->u32LastOrphan)));
     296            Log2(("EXT:   au32HashSeedHtree[0]        %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[0])));
     297            Log2(("EXT:   au32HashSeedHtree[1]        %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[1])));
     298            Log2(("EXT:   au32HashSeedHtree[2]        %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[2])));
     299            Log2(("EXT:   au32HashSeedHtree[3]        %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[3])));
     300            Log2(("EXT:   u8HashVersionDef            %#RX8\n", pSb->u8HashVersionDef));
     301            Log2(("EXT:   u8JnlBackupType             %#RX8\n", pSb->u8JnlBackupType));
     302            Log2(("EXT:   cbGroupDesc                 %RU16\n", RT_LE2H_U16(pSb->cbGroupDesc)));
     303            Log2(("EXT:   fMntOptsDef                 %#RX32\n", RT_LE2H_U32(pSb->fMntOptsDef)));
     304            Log2(("EXT:   iFirstMetaBg                %#RX32\n", RT_LE2H_U32(pSb->iFirstMetaBg)));
     305            Log2(("EXT:   u32TimeFsCreation           %#RX32 %s\n", RT_LE2H_U32(pSb->u32TimeFsCreation),
     306                  RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32TimeFsCreation)), sz, sizeof(sz))));
     307            for (unsigned i = 0; i < RT_ELEMENTS(pSb->au32JnlBlocks); i++)
     308                Log2(("EXT:   au32JnlBlocks[%u]           %#RX32\n", i, RT_LE2H_U32(pSb->au32JnlBlocks[i])));
     309            Log2(("EXT:   cBlocksTotalHigh            %#RX32\n", RT_LE2H_U32(pSb->cBlocksTotalHigh)));
     310            Log2(("EXT:   cBlocksRsvdForSuperUserHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksRsvdForSuperUserHigh)));
     311            Log2(("EXT:   cBlocksFreeHigh             %#RX32\n", RT_LE2H_U32(pSb->cBlocksFreeHigh)));
     312            Log2(("EXT:   cbInodesExtraMin            %#RX16\n", RT_LE2H_U16(pSb->cbInodesExtraMin)));
     313            Log2(("EXT:   cbNewInodesRsv              %#RX16\n", RT_LE2H_U16(pSb->cbInodesExtraMin)));
     314            Log2(("EXT:   fFlags                      %#RX32\n", RT_LE2H_U32(pSb->fFlags)));
     315            Log2(("EXT:   cRaidStride                 %RU16\n", RT_LE2H_U16(pSb->cRaidStride)));
     316            Log2(("EXT:   cSecMmpInterval             %RU16\n", RT_LE2H_U16(pSb->cSecMmpInterval)));
     317            Log2(("EXT:   iMmpBlock                   %#RX64\n", RT_LE2H_U64(pSb->iMmpBlock)));
     318            Log2(("EXT:   cRaidStrideWidth            %#RX32\n", RT_LE2H_U32(pSb->cRaidStrideWidth)));
     319            Log2(("EXT:   cLogGroupsPerFlex           %RU8\n", pSb->cLogGroupsPerFlex));
     320            Log2(("EXT:   u8ChksumType                %RX8\n", pSb->u8ChksumType));
     321            Log2(("EXT:   cKbWritten                  %#RX64\n", RT_LE2H_U64(pSb->cKbWritten)));
     322            Log2(("EXT:   iSnapshotInode              %#RX32\n", RT_LE2H_U32(pSb->iSnapshotInode)));
     323            Log2(("EXT:   iSnapshotId                 %#RX32\n", RT_LE2H_U32(pSb->iSnapshotId)));
     324            Log2(("EXT:   cSnapshotRsvdBlocks         %#RX64\n", RT_LE2H_U64(pSb->cSnapshotRsvdBlocks)));
     325            Log2(("EXT:   iSnapshotListInode          %#RX32\n", RT_LE2H_U32(pSb->iSnapshotListInode)));
     326            Log2(("EXT:   cErrorsSeen                 %#RX32\n", RT_LE2H_U32(pSb->cErrorsSeen)));
     327            Log2(("EXT:   [...]\n")); /** @todo: Missing fields if becoming interesting. */
     328            Log2(("EXT:   iInodeLostFound             %#RX32\n", RT_LE2H_U32(pSb->iInodeLostFound)));
     329            Log2(("EXT:   iInodeProjQuota             %#RX32\n", RT_LE2H_U32(pSb->iInodeProjQuota)));
     330            Log2(("EXT:   u32ChksumSeed               %#RX32\n", RT_LE2H_U32(pSb->u32ChksumSeed)));
     331            Log2(("EXT:   [...]\n")); /** @todo: Missing fields if becoming interesting. */
     332            Log2(("EXT:   u32Chksum                   %#RX32\n", RT_LE2H_U32(pSb->u32Chksum)));
     333        }
     334    }
     335}
     336
     337
     338/**
     339 * Logs a ext filesystem block group descriptor.
     340 *
     341 * @returns nothing.
     342 * @param   pThis               The ext volume instance.
     343 * @param   iBlockGroup         Block group number.
     344 * @param   pBlockGroup         Pointer to the block group.
     345 */
     346static void rtFsExtBlockGroup_Log(PRTFSEXTVOL pThis, uint32_t iBlockGroup, PCEXTBLOCKGROUPDESC pBlockGroup)
     347{
     348    if (LogIs2Enabled())
     349    {
     350        uint64_t iBlockStart = (uint64_t)iBlockGroup * pThis->cBlocksPerGroup;
     351        Log2(("EXT: Block group %#RX32 (blocks %#RX64 to %#RX64):\n",
     352              iBlockGroup, iBlockStart, iBlockStart + pThis->cBlocksPerGroup - 1));
     353        Log2(("EXT:   offBlockBitmapLow               %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offBlockBitmapLow)));
     354        Log2(("EXT:   offInodeBitmapLow               %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offInodeBitmapLow)));
     355        Log2(("EXT:   offInodeTableLow                %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offInodeTableLow)));
     356        Log2(("EXT:   cBlocksFreeLow                  %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cBlocksFreeLow)));
     357        Log2(("EXT:   cInodesFreeLow                  %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cInodesFreeLow)));
     358        Log2(("EXT:   cDirectoriesLow                 %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cDirectoriesLow)));
     359        Log2(("EXT:   fFlags                          %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.fFlags)));
     360        Log2(("EXT:   offSnapshotExclBitmapLow        %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offSnapshotExclBitmapLow)));
     361        Log2(("EXT:   u16ChksumBlockBitmapLow         %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16ChksumBlockBitmapLow)));
     362        Log2(("EXT:   u16ChksumInodeBitmapLow         %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16ChksumInodeBitmapLow)));
     363        Log2(("EXT:   cInodeTblUnusedLow              %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cInodeTblUnusedLow)));
     364        Log2(("EXT:   u16Chksum                       %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16Chksum)));
     365        if (pThis->cbBlkGrpDesc == sizeof(EXTBLOCKGROUPDESC64))
     366        {
     367            Log2(("EXT:   offBlockBitmapHigh              %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offBlockBitmapHigh)));
     368            Log2(("EXT:   offInodeBitmapHigh              %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offInodeBitmapHigh)));
     369            Log2(("EXT:   offInodeTableHigh               %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offInodeTableHigh)));
     370            Log2(("EXT:   cBlocksFreeHigh                 %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cBlocksFreeHigh)));
     371            Log2(("EXT:   cInodesFreeHigh                 %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cInodesFreeHigh)));
     372            Log2(("EXT:   cDirectoriesHigh                %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cDirectoriesHigh)));
     373            Log2(("EXT:   cInodeTblUnusedHigh             %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cInodeTblUnusedHigh)));
     374            Log2(("EXT:   offSnapshotExclBitmapHigh       %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offSnapshotExclBitmapHigh)));
     375            Log2(("EXT:   u16ChksumBlockBitmapHigh        %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.u16ChksumBlockBitmapHigh)));
     376            Log2(("EXT:   u16ChksumInodeBitmapHigh        %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.u16ChksumInodeBitmapHigh)));
     377        }
     378    }
     379}
     380
     381
     382/**
     383 * Logs a ext filesystem inode.
     384 *
     385 * @returns nothing.
     386 * @param   pThis               The ext volume instance.
     387 * @param   iInode              Inode number.
     388 * @param   pInode              Pointer to the inode.
     389 */
     390static void rtFsExtInode_Log(PRTFSEXTVOL pThis, uint32_t iInode, PCEXTINODECOMB pInode)
     391{
     392    if (LogIs2Enabled())
     393    {
     394        RTTIMESPEC Spec;
     395        char       sz[80];
     396
     397        Log2(("EXT: Inode %#RX32:\n", iInode));
     398        Log2(("EXT:   fMode                               %#RX16\n", RT_LE2H_U16(pInode->Core.fMode)));
     399        Log2(("EXT:   uUidLow                             %#RX16\n", RT_LE2H_U16(pInode->Core.uUidLow)));
     400        Log2(("EXT:   cbSizeLow                           %#RX32\n", RT_LE2H_U32(pInode->Core.cbSizeLow)));
     401        Log2(("EXT:   u32TimeLastAccess                   %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastAccess),
     402              RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastAccess)), sz, sizeof(sz))));
     403        Log2(("EXT:   u32TimeLastChange                   %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastChange),
     404              RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastChange)), sz, sizeof(sz))));
     405        Log2(("EXT:   u32TimeLastModification             %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastModification),
     406              RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastModification)), sz, sizeof(sz))));
     407        Log2(("EXT:   u32TimeDeletion                     %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeDeletion),
     408              RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeDeletion)), sz, sizeof(sz))));
     409        Log2(("EXT:   uGidLow                             %#RX16\n", RT_LE2H_U16(pInode->Core.uGidLow)));
     410        Log2(("EXT:   cHardLinks                          %#RU16\n", RT_LE2H_U16(pInode->Core.cHardLinks)));
     411        Log2(("EXT:   cBlocksLow                          %#RX32\n", RT_LE2H_U32(pInode->Core.cBlocksLow)));
     412        Log2(("EXT:   fFlags                              %#RX32\n", RT_LE2H_U32(pInode->Core.fFlags)));
     413        Log2(("EXT:   Osd1.u32LnxVersion                  %#RX32\n", RT_LE2H_U32(pInode->Core.Osd1.u32LnxVersion)));
     414        for (unsigned i = 0; i < RT_ELEMENTS(pInode->Core.au32Block); i++)
     415            Log2(("EXT:   au32Block[%u]                       %#RX32\n", i, RT_LE2H_U32(pInode->Core.au32Block[i])));
     416        Log2(("EXT:   u32Version                          %#RX32\n", RT_LE2H_U32(pInode->Core.u32Version)));
     417        Log2(("EXT:   offExtAttrLow                       %#RX32\n", RT_LE2H_U32(pInode->Core.offExtAttrLow)));
     418        Log2(("EXT:   cbSizeHigh                          %#RX32\n", RT_LE2H_U32(pInode->Core.cbSizeHigh)));
     419        Log2(("EXT:   u32FragmentAddrObs                  %#RX32\n", RT_LE2H_U32(pInode->Core.u32FragmentAddrObs)));
     420        Log2(("EXT:   Osd2.Lnx.cBlocksHigh                %#RX32\n", RT_LE2H_U32(pInode->Core.Osd2.Lnx.cBlocksHigh)));
     421        Log2(("EXT:   Osd2.Lnx.offExtAttrHigh             %#RX32\n", RT_LE2H_U32(pInode->Core.Osd2.Lnx.offExtAttrHigh)));
     422        Log2(("EXT:   Osd2.Lnx.uUidHigh                   %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.uUidHigh)));
     423        Log2(("EXT:   Osd2.Lnx.uGidHigh                   %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.uGidHigh)));
     424        Log2(("EXT:   Osd2.Lnx.u16ChksumLow               %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.u16ChksumLow)));
     425
     426        if (pThis->cbInode >= sizeof(EXTINODECOMB))
     427        {
     428            Log2(("EXT:   cbInodeExtra                        %#RU16\n", RT_LE2H_U16(pInode->Extra.cbInodeExtra)));
     429            Log2(("EXT:   u16ChksumHigh                       %#RX16\n", RT_LE2H_U16(pInode->Extra.u16ChksumHigh)));
     430            Log2(("EXT:   u32ExtraTimeLastChange              %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastChange)));
     431            Log2(("EXT:   u32ExtraTimeLastModification        %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastModification)));
     432            Log2(("EXT:   u32ExtraTimeLastAccess              %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastAccess)));
     433            Log2(("EXT:   u32TimeCreation                     %#RX32 %s\n", RT_LE2H_U32(pInode->Extra.u32TimeCreation),
     434                  RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Extra.u32TimeCreation)), sz, sizeof(sz))));
     435            Log2(("EXT:   u32ExtraTimeCreation                %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeCreation)));
     436            Log2(("EXT:   u32VersionHigh                      %#RX32\n", RT_LE2H_U16(pInode->Extra.u32VersionHigh)));
     437            Log2(("EXT:   u32ProjectId                        %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ProjectId)));
     438        }
     439    }
     440}
     441#endif
     442
     443
     444/**
     445 * Converts a block number to a byte offset.
     446 *
     447 * @returns Offset in bytes for the given block number.
     448 * @param   pThis               The ext volume instance.
     449 * @param   iBlock              The block number to convert.
     450 */
     451DECLINLINE(uint64_t) rtFsExtBlockIdxToDiskOffset(PRTFSEXTVOL pThis, uint64_t iBlock)
     452{
     453    return iBlock << pThis->cBlockShift;
     454}
     455
     456
     457/**
     458 * Converts a byte offset to a block number.
     459 *
     460 * @returns Block number.
     461 * @param   pThis               The ext volume instance.
     462 * @param   iBlock              The offset to convert.
     463 */
     464DECLINLINE(uint64_t) rtFsExtDiskOffsetToBlockIdx(PRTFSEXTVOL pThis, uint64_t off)
     465{
     466    return off >> pThis->cBlockShift;
     467}
     468
     469
     470/**
     471 * Creates the proper block number from the given low and high parts in case a 64bit
     472 * filesystem is used.
     473 *
     474 * @returns 64bit block number.
     475 * @param   pThis               The ext volume instance.
     476 * @param   uLow                The lower 32bit part.
     477 * @param   uHigh               The upper 32bit part.
     478 */
     479DECLINLINE(uint64_t) rtFsExtBlockFromLowHigh(PRTFSEXTVOL pThis, uint32_t uLow, uint32_t uHigh)
     480{
     481    return pThis->f64Bit ? RT_MAKE_U64(uLow, uHigh): uLow;
     482}
     483
     484
     485/**
     486 * Converts the given high and low parts of the block number to a byte offset.
     487 *
     488 * @returns Offset in bytes for the given block number.
     489 * @param   uLow                The lower 32bit part of the block number.
     490 * @param   uHigh               The upper 32bit part of the block number.
     491 */
     492DECLINLINE(uint64_t) rtFsExtBlockIdxLowHighToDiskOffset(PRTFSEXTVOL pThis, uint32_t uLow, uint32_t uHigh)
     493{
     494    uint64_t iBlock = rtFsExtBlockFromLowHigh(pThis, uLow, uHigh);
     495    return rtFsExtBlockIdxToDiskOffset(pThis, iBlock);
     496}
     497
     498
     499/**
     500 * Allocates a new block group.
     501 *
     502 * @returns Pointer to the new block group descriptor or NULL if out of memory.
     503 * @param   pThis               The ext volume instance.
     504 * @param   cbAlloc             How much to allocate.
     505 * @param   iBlockGroup         Block group number.
     506 */
     507static PRTFSEXTBLKGRP rtFsExtBlockGroupAlloc(PRTFSEXTVOL pThis, size_t cbAlloc, uint32_t iBlockGroup)
     508{
     509    PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)RTMemAllocZ(cbAlloc);
     510    if (RT_LIKELY(pBlockGroup))
     511    {
     512        pBlockGroup->Core.Key       = iBlockGroup;
     513        pBlockGroup->cRefs          = 0;
     514        pBlockGroup->pabInodeBitmap = &pBlockGroup->abBlockBitmap[pThis->cbBlockBitmap];
     515        pThis->cbBlockGroups       += cbAlloc;
     516    }
     517
     518    return pBlockGroup;
     519}
     520
     521
     522/**
     523 * Frees the given block group.
     524 *
     525 * @returns nothing.
     526 * @param   pThis               The ext volume instance.
     527 * @param   pBlockGroup         The block group to free.
     528 */
     529static void rtFsExtBlockGroupFree(PRTFSEXTVOL pThis, PRTFSEXTBLKGRP pBlockGroup)
     530{
     531    Assert(!pBlockGroup->cRefs);
     532
     533    /*
     534     * Put it into the cache if the limit wasn't exceeded, otherwise the block group
     535     * is freed right away.
     536     */
     537    if (pThis->cbBlockGroups <= RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE)
     538    {
     539        /* Put onto the LRU list. */
     540        RTListPrepend(&pThis->LstBlockGroupLru, &pBlockGroup->NdLru);
     541    }
     542    else
     543    {
     544        /* Remove from the tree and free memory. */
     545        PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->BlockGroupRoot, pBlockGroup->Core.Key);
     546        Assert(pCore == &pBlockGroup->Core); RT_NOREF(pCore);
     547        RTMemFree(pBlockGroup);
     548        pThis->cbBlockGroups -= sizeof(RTFSEXTBLKGRP) + pThis->cbBlockBitmap + pThis->cbInodeBitmap;
     549    }
     550}
     551
     552
     553/**
     554 * Returns a new block group utilizing the cache if possible.
     555 *
     556 * @returns Pointer to the new block group descriptor or NULL if out of memory.
     557 * @param   pThis               The ext volume instance.
     558 * @param   iBlockGroup         Block group number.
     559 */
     560static PRTFSEXTBLKGRP rtFsExtBlockGroupGetNew(PRTFSEXTVOL pThis, uint32_t iBlockGroup)
     561{
     562    PRTFSEXTBLKGRP pBlockGroup = NULL;
     563    size_t cbAlloc = sizeof(RTFSEXTBLKGRP) + pThis->cbBlockBitmap + pThis->cbInodeBitmap;
     564    if (pThis->cbBlockGroups + cbAlloc <= RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE)
     565        pBlockGroup = rtFsExtBlockGroupAlloc(pThis, cbAlloc, iBlockGroup);
     566    else
     567    {
     568        pBlockGroup = RTListRemoveLast(&pThis->LstBlockGroupLru, RTFSEXTBLKGRP, NdLru);
     569        if (!pBlockGroup)
     570            pBlockGroup = rtFsExtBlockGroupAlloc(pThis, cbAlloc, iBlockGroup);
     571        else
     572        {
     573            /* Remove the block group from the tree because it gets a new key. */
     574            PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->BlockGroupRoot, pBlockGroup->Core.Key);
     575            Assert(pCore == &pBlockGroup->Core); RT_NOREF(pCore);
     576        }
     577    }
     578
     579    Assert(!pBlockGroup->cRefs);
     580    pBlockGroup->Core.Key = iBlockGroup;
     581    pBlockGroup->cRefs    = 1;
     582
     583    return pBlockGroup;
     584}
     585
     586
     587/**
     588 * Loads the given block group number and returns it on success.
    108589 *
    109590 * @returns IPRT status code.
    110  * @param   pThis    EXT filesystem instance data.
    111  * @param   iBlkGrp  Block group number to load.
    112  */
    113 static int rtFsExtLoadBlkGrpDesc(PRTFSEXTVOL pThis, uint32_t iBlkGrp)
    114 {
    115     size_t cbBlockBitmap = pThis->cBlocksPerGroup / 8;
    116     if (pThis->cBlocksPerGroup % 8)
    117         cbBlockBitmap++;
    118 
    119     PRTFSEXTBLKGRP pBlkGrpDesc = pThis->pBlkGrpDesc;
    120     if (!pBlkGrpDesc)
    121     {
    122         size_t cbBlkDesc = RT_UOFFSETOF_DYN(RTFSEXTBLKGRP, abBlockBitmap[cbBlockBitmap]);
    123         pBlkGrpDesc = (PRTFSEXTBLKGRP)RTMemAllocZ(cbBlkDesc);
    124         if (!pBlkGrpDesc)
    125             return VERR_NO_MEMORY;
    126     }
    127 
    128     uint64_t            offRead = (pThis->iSbBlock + 1) * pThis->cbBlock;
    129     EXTBLOCKGROUPDESC32 BlkDesc;
    130     int rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &BlkDesc, sizeof(BlkDesc), NULL);
     591 * @param   pThis               The ext volume instance.
     592 * @param   iBlockGroup         The block group to load.
     593 * @param   ppBlockGroup        Where to store the block group on success.
     594 */
     595static int rtFsExtBlockGroupLoad(PRTFSEXTVOL pThis, uint32_t iBlockGroup, PRTFSEXTBLKGRP *ppBlockGroup)
     596{
     597    int rc = VINF_SUCCESS;
     598
     599    /* Try to fetch the block group from the cache first. */
     600    PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)RTAvlU32Get(&pThis->BlockGroupRoot, iBlockGroup);
     601    if (!pBlockGroup)
     602    {
     603        /* Slow path, load from disk. */
     604        pBlockGroup = rtFsExtBlockGroupGetNew(pThis, iBlockGroup);
     605        if (RT_LIKELY(pBlockGroup))
     606        {
     607            uint64_t          offRead =   rtFsExtBlockIdxToDiskOffset(pThis, pThis->cbBlock == _1K ? 2 : 1)
     608                                        + (uint64_t)iBlockGroup * pThis->cbBlkGrpDesc;
     609            EXTBLOCKGROUPDESC BlockGroupDesc;
     610            rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &BlockGroupDesc, pThis->cbBlkGrpDesc, NULL);
     611            if (RT_SUCCESS(rc))
     612            {
     613#ifdef LOG_ENABLED
     614                rtFsExtBlockGroup_Log(pThis, iBlockGroup, &BlockGroupDesc);
     615#endif
     616                pBlockGroup->iBlockInodeTbl =   RT_LE2H_U32(BlockGroupDesc.v32.offInodeTableLow)
     617                                              | ((pThis->cbBlkGrpDesc == sizeof(EXTBLOCKGROUPDESC64))
     618                                              ? (uint64_t)RT_LE2H_U32(BlockGroupDesc.v64.offInodeTableHigh) << 32
     619                                              : 0);
     620
     621                offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offBlockBitmapLow),
     622                                                             RT_LE2H_U32(BlockGroupDesc.v64.offBlockBitmapHigh));
     623                rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->abBlockBitmap[0], pThis->cbBlockBitmap, NULL);
     624                if (RT_SUCCESS(rc))
     625                {
     626                    offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offInodeBitmapLow),
     627                                                                 RT_LE2H_U32(BlockGroupDesc.v64.offInodeBitmapHigh));
     628                    rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->pabInodeBitmap[0], pThis->cbInodeBitmap, NULL);
     629                    if (RT_SUCCESS(rc))
     630                    {
     631                        bool fIns = RTAvlU32Insert(&pThis->BlockGroupRoot, &pBlockGroup->Core);
     632                        Assert(fIns); RT_NOREF(fIns);
     633                    }
     634                }
     635            }
     636        }
     637        else
     638            rc = VERR_NO_MEMORY;
     639    }
     640    else
     641    {
     642        /* Remove from current LRU list position and add to the beginning. */
     643        uint32_t cRefs = ASMAtomicIncU32(&pBlockGroup->cRefs);
     644        if (cRefs == 1) /* Block groups get removed from the LRU list if they are referenced. */
     645            RTListNodeRemove(&pBlockGroup->NdLru);
     646    }
     647
    131648    if (RT_SUCCESS(rc))
    132     {
    133         pBlkGrpDesc->offStart = pThis->iSbBlock + (uint64_t)iBlkGrp * pThis->cBlocksPerGroup * pThis->cbBlock;
    134         pBlkGrpDesc->offLast  = pBlkGrpDesc->offStart + pThis->cBlocksPerGroup * pThis->cbBlock;
    135         rc = RTVfsFileReadAt(pThis->hVfsBacking, BlkDesc.offBlockBitmapLow * pThis->cbBlock,
    136                              &pBlkGrpDesc->abBlockBitmap[0], cbBlockBitmap, NULL);
    137     }
    138 
    139     pThis->pBlkGrpDesc = pBlkGrpDesc;
     649        *ppBlockGroup = pBlockGroup;
     650    else if (pBlockGroup)
     651    {
     652        ASMAtomicDecU32(&pBlockGroup->cRefs);
     653        rtFsExtBlockGroupFree(pThis, pBlockGroup); /* Free the block group. */
     654    }
     655
    140656    return rc;
    141657}
    142658
    143659
    144 static bool rtFsExtIsBlockRangeInUse(PRTFSEXTBLKGRP pBlkGrpDesc, uint32_t offBlockStart, size_t cBlocks)
    145 {
     660/**
     661 * Releases a reference of the given block group.
     662 *
     663 * @returns nothing.
     664 * @param   pThis               The ext volume instance.
     665 * @param   pBlockGroup         The block group to release.
     666 */
     667static void rtFsExtBlockGroupRelease(PRTFSEXTVOL pThis, PRTFSEXTBLKGRP pBlockGroup)
     668{
     669    uint32_t cRefs = ASMAtomicDecU32(&pBlockGroup->cRefs);
     670    if (!cRefs)
     671        rtFsExtBlockGroupFree(pThis, pBlockGroup);
     672}
     673
     674
     675/**
     676 * Allocates a new inode.
     677 *
     678 * @returns Pointer to the new inode or NULL if out of memory.
     679 * @param   pThis               The ext volume instance.
     680 * @param   iInode              Inode number.
     681 */
     682static PRTFSEXTINODE rtFsExtInodeAlloc(PRTFSEXTVOL pThis, uint32_t iInode)
     683{
     684    PRTFSEXTINODE pInode = (PRTFSEXTINODE)RTMemAllocZ(sizeof(RTFSEXTINODE));
     685    if (RT_LIKELY(pInode))
     686    {
     687        pInode->Core.Key = iInode;
     688        pInode->cRefs    = 0;
     689        pThis->cbInodes  += sizeof(RTFSEXTINODE);
     690    }
     691
     692    return pInode;
     693}
     694
     695
     696/**
     697 * Frees the given inode.
     698 *
     699 * @returns nothing.
     700 * @param   pThis               The ext volume instance.
     701 * @param   pInode              The inode to free.
     702 */
     703static void rtFsExtInodeFree(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode)
     704{
     705    Assert(!pInode->cRefs);
     706
     707    /*
     708     * Put it into the cache if the limit wasn't exceeded, otherwise the inode
     709     * is freed right away.
     710     */
     711    if (pThis->cbInodes <= RTFSEXT_MAX_INODE_CACHE_SIZE)
     712    {
     713        /* Put onto the LRU list. */
     714        RTListPrepend(&pThis->LstInodeLru, &pInode->NdLru);
     715    }
     716    else
     717    {
     718        /* Remove from the tree and free memory. */
     719        PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
     720        Assert(pCore == &pInode->Core); RT_NOREF(pCore);
     721        RTMemFree(pInode);
     722        pThis->cbInodes -= sizeof(RTFSEXTINODE);
     723    }
     724}
     725
     726
     727/**
     728 * Returns a new inodep utilizing the cache if possible.
     729 *
     730 * @returns Pointer to the new inode or NULL if out of memory.
     731 * @param   pThis               The ext volume instance.
     732 * @param   iInode              Inode number.
     733 */
     734static PRTFSEXTINODE rtFsExtInodeGetNew(PRTFSEXTVOL pThis, uint32_t iInode)
     735{
     736    PRTFSEXTINODE pInode = NULL;
     737    if (pThis->cbInodes + sizeof(RTFSEXTINODE) <= RTFSEXT_MAX_INODE_CACHE_SIZE)
     738        pInode = rtFsExtInodeAlloc(pThis, iInode);
     739    else
     740    {
     741        pInode = RTListRemoveLast(&pThis->LstInodeLru, RTFSEXTINODE, NdLru);
     742        if (!pInode)
     743            pInode = rtFsExtInodeAlloc(pThis, iInode);
     744        else
     745        {
     746            /* Remove the block group from the tree because it gets a new key. */
     747            PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
     748            Assert(pCore == &pInode->Core); RT_NOREF(pCore);
     749        }
     750    }
     751
     752    Assert(!pInode->cRefs);
     753    pInode->Core.Key = iInode;
     754    pInode->cRefs    = 1;
     755
     756    return pInode;
     757}
     758
     759
     760/**
     761 * Loads the given inode number and returns it on success.
     762 *
     763 * @returns IPRT status code.
     764 * @param   pThis               The ext volume instance.
     765 * @param   iInode              The inode to load.
     766 * @param   ppInode             Where to store the inode on success.
     767 */
     768static int rtFsExtInodeLoad(PRTFSEXTVOL pThis, uint32_t iInode, PRTFSEXTINODE *ppInode)
     769{
     770    int rc = VINF_SUCCESS;
     771
     772    /* Try to fetch the inode from the cache first. */
     773    PRTFSEXTINODE pInode = (PRTFSEXTINODE)RTAvlU32Get(&pThis->InodeRoot, iInode);
     774    if (!pInode)
     775    {
     776        /* Slow path, load from disk. */
     777        pInode = rtFsExtInodeGetNew(pThis, iInode);
     778        if (RT_LIKELY(pInode))
     779        {
     780            /* Calculate the block group and load that one first to get at the inode table location. */
     781            PRTFSEXTBLKGRP pBlockGroup = NULL;
     782            rc = rtFsExtBlockGroupLoad(pThis, (iInode - 1) / pThis->cInodesPerGroup, &pBlockGroup);
     783            if (RT_SUCCESS(rc))
     784            {
     785                uint32_t idxInodeInTbl = (iInode - 1) % pThis->cInodesPerGroup;
     786                uint64_t offRead =   rtFsExtBlockIdxToDiskOffset(pThis, pBlockGroup->iBlockInodeTbl)
     787                                   + idxInodeInTbl * pThis->cbInode;
     788
     789                /* Release block group here already as it is not required. */
     790                rtFsExtBlockGroupRelease(pThis, pBlockGroup);
     791
     792                EXTINODECOMB Inode;
     793                rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &Inode, RT_MIN(sizeof(Inode), pThis->cbInode), NULL);
     794                if (RT_SUCCESS(rc))
     795                {
     796#ifdef LOG_ENABLED
     797                    rtFsExtInode_Log(pThis, iInode, &Inode);
     798#endif
     799                    /** @todo Fill in data. */
     800                }
     801            }
     802        }
     803        else
     804            rc = VERR_NO_MEMORY;
     805    }
     806    else
     807    {
     808        /* Remove from current LRU list position and add to the beginning. */
     809        uint32_t cRefs = ASMAtomicIncU32(&pInode->cRefs);
     810        if (cRefs == 1) /* Inodes get removed from the LRU list if they are referenced. */
     811            RTListNodeRemove(&pInode->NdLru);
     812    }
     813
     814    if (RT_SUCCESS(rc))
     815        *ppInode = pInode;
     816    else if (pInode)
     817    {
     818        ASMAtomicDecU32(&pInode->cRefs);
     819        rtFsExtInodeFree(pThis, pInode); /* Free the inode. */
     820    }
     821
     822    return rc;
     823}
     824
     825
     826/**
     827 * Releases a reference of the given inode.
     828 *
     829 * @returns nothing.
     830 * @param   pThis               The ext volume instance.
     831 * @param   pInode              The inode to release.
     832 */
     833static void rtFsExtInodeRelease(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode)
     834{
     835    uint32_t cRefs = ASMAtomicDecU32(&pInode->cRefs);
     836    if (!cRefs)
     837        rtFsExtInodeFree(pThis, pInode);
     838}
     839
     840
     841/*
     842 *
     843 * Directory instance methods
     844 * Directory instance methods
     845 * Directory instance methods
     846 *
     847 */
     848
     849/**
     850 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
     851 */
     852static DECLCALLBACK(int) rtFsExtDir_Close(void *pvThis)
     853{
     854    PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
     855    LogFlowFunc(("pThis=%p\n", pThis));
     856
     857    return VINF_SUCCESS;
     858}
     859
     860
     861/**
     862 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
     863 */
     864static DECLCALLBACK(int) rtFsExtDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
     865{
     866    PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
     867    LogFlowFunc(("\n"));
     868    RT_NOREF(pThis, pObjInfo, enmAddAttr);
     869    return VERR_NOT_IMPLEMENTED;
     870}
     871
     872
     873/**
     874 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
     875 */
     876static DECLCALLBACK(int) rtFsExtDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
     877{
     878    LogFlowFunc(("\n"));
     879    RT_NOREF(pvThis, fMode, fMask);
     880    return VERR_WRITE_PROTECT;
     881}
     882
     883
     884/**
     885 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
     886 */
     887static DECLCALLBACK(int) rtFsExtDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
     888                                             PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
     889{
     890    LogFlowFunc(("\n"));
     891    RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
     892    return VERR_WRITE_PROTECT;
     893}
     894
     895
     896/**
     897 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
     898 */
     899static DECLCALLBACK(int) rtFsExtDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
     900{
     901    LogFlowFunc(("\n"));
     902    RT_NOREF(pvThis, uid, gid);
     903    return VERR_WRITE_PROTECT;
     904}
     905
     906
     907/**
     908 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
     909 */
     910static DECLCALLBACK(int) rtFsExtDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
     911                                         uint32_t fFlags, PRTVFSOBJ phVfsObj)
     912{
     913    LogFlowFunc(("pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
     914    PRTFSEXTDIR  pThis = (PRTFSEXTDIR)pvThis;
     915    PRTFSEXTVOL  pVol  = pThis->pVol;
     916    int rc = VINF_SUCCESS;
     917
     918    RT_NOREF(pThis, pVol, phVfsObj);
     919
     920    /*
     921     * We cannot create or replace anything, just open stuff.
     922     */
     923    if (   (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
     924        || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
     925    { /* likely */ }
     926    else
     927        return VERR_WRITE_PROTECT;
     928
     929    LogFlow(("rtFsExtDir_Open(%s): returns %Rrc\n", pszEntry, rc));
     930    return rc;
     931}
     932
     933
     934/**
     935 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
     936 */
     937static DECLCALLBACK(int) rtFsExtDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
     938{
     939    RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
     940    LogFlowFunc(("\n"));
     941    return VERR_WRITE_PROTECT;
     942}
     943
     944
     945/**
     946 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
     947 */
     948static DECLCALLBACK(int) rtFsExtDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
     949{
     950    RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
     951    LogFlowFunc(("\n"));
     952    return VERR_NOT_SUPPORTED;
     953}
     954
     955
     956/**
     957 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
     958 */
     959static DECLCALLBACK(int) rtFsExtDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
     960                                                  RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
     961{
     962    RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
     963    LogFlowFunc(("\n"));
     964    return VERR_WRITE_PROTECT;
     965}
     966
     967
     968/**
     969 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
     970 */
     971static DECLCALLBACK(int) rtFsExtDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
     972{
     973    RT_NOREF(pvThis, pszEntry, fType);
     974    LogFlowFunc(("\n"));
     975    return VERR_WRITE_PROTECT;
     976}
     977
     978
     979/**
     980 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
     981 */
     982static DECLCALLBACK(int) rtFsExtDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
     983{
     984    RT_NOREF(pvThis, pszEntry, fType, pszNewName);
     985    LogFlowFunc(("\n"));
     986    return VERR_WRITE_PROTECT;
     987}
     988
     989
     990/**
     991 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
     992 */
     993static DECLCALLBACK(int) rtFsExtDir_RewindDir(void *pvThis)
     994{
     995    PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
     996    LogFlowFunc(("\n"));
     997
     998    pThis->fNoMoreFiles = false;
     999    return VINF_SUCCESS;
     1000}
     1001
     1002
     1003/**
     1004 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
     1005 */
     1006static DECLCALLBACK(int) rtFsExtDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
     1007                                            RTFSOBJATTRADD enmAddAttr)
     1008{
     1009    PRTFSEXTDIR     pThis = (PRTFSEXTDIR)pvThis;
     1010    int             rc = VINF_SUCCESS;
     1011    LogFlowFunc(("\n"));
     1012
     1013    RT_NOREF(pThis, rc, pDirEntry, pcbDirEntry, enmAddAttr);
     1014
     1015    /*
     1016     * The End.
     1017     */
     1018    LogFlowFunc(("no more files\n"));
     1019    pThis->fNoMoreFiles = true;
     1020    return VERR_NO_MORE_FILES;
     1021}
     1022
     1023
     1024/**
     1025 * EXT directory operations.
     1026 */
     1027static const RTVFSDIROPS g_rtFsExtDirOps =
     1028{
     1029    { /* Obj */
     1030        RTVFSOBJOPS_VERSION,
     1031        RTVFSOBJTYPE_DIR,
     1032        "EXT Dir",
     1033        rtFsExtDir_Close,
     1034        rtFsExtDir_QueryInfo,
     1035        RTVFSOBJOPS_VERSION
     1036    },
     1037    RTVFSDIROPS_VERSION,
     1038    0,
     1039    { /* ObjSet */
     1040        RTVFSOBJSETOPS_VERSION,
     1041        RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
     1042        rtFsExtDir_SetMode,
     1043        rtFsExtDir_SetTimes,
     1044        rtFsExtDir_SetOwner,
     1045        RTVFSOBJSETOPS_VERSION
     1046    },
     1047    rtFsExtDir_Open,
     1048    NULL /* pfnFollowAbsoluteSymlink */,
     1049    NULL /* pfnOpenFile */,
     1050    NULL /* pfnOpenDir */,
     1051    rtFsExtDir_CreateDir,
     1052    rtFsExtDir_OpenSymlink,
     1053    rtFsExtDir_CreateSymlink,
     1054    NULL /* pfnQueryEntryInfo */,
     1055    rtFsExtDir_UnlinkEntry,
     1056    rtFsExtDir_RenameEntry,
     1057    rtFsExtDir_RewindDir,
     1058    rtFsExtDir_ReadDir,
     1059    RTVFSDIROPS_VERSION,
     1060};
     1061
     1062
     1063/**
     1064 * Opens a directory by the given inode.
     1065 *
     1066 * @returns IPRT status code.
     1067 * @param   pThis               The ext volume instance.
     1068 * @param   iInode              The inode to open.
     1069 * @param   phVfsDir            Where to store the handle to the VFS directory on success.
     1070 */
     1071static int rtFsExtVol_OpenDirByInode(PRTFSEXTVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir)
     1072{
     1073    PRTFSEXTINODE pInode = NULL;
     1074    int rc = rtFsExtInodeLoad(pThis, iInode, &pInode);
     1075    if (RT_SUCCESS(rc))
     1076        rtFsExtInodeRelease(pThis, pInode);
     1077    RT_NOREF(phVfsDir, g_rtFsExtDirOps);
     1078    return rc;
     1079}
     1080
     1081
     1082/*
     1083 *
     1084 * Volume level code.
     1085 * Volume level code.
     1086 * Volume level code.
     1087 *
     1088 */
     1089
     1090
     1091/**
     1092 * Checks whether the block range in the given block group is in use by checking the
     1093 * block bitmap.
     1094 *
     1095 * @returns Flag whether the range is in use.
     1096 * @param   pBlkGrpDesc         The block group to check for.
     1097 * @param   iBlockStart         The starting block to check relative from the beginning of the block group.
     1098 * @param   cBlocks             How many blocks to check.
     1099 */
     1100static bool rtFsExtIsBlockRangeInUse(PRTFSEXTBLKGRP pBlkGrpDesc, uint64_t iBlockStart, size_t cBlocks)
     1101{
     1102    /** @todo: Optimize with ASMBitFirstSet(). */
    1461103    while (cBlocks)
    1471104    {
    148         uint32_t idxByte = offBlockStart / 8;
    149         uint32_t iBit = offBlockStart % 8;
     1105        uint32_t idxByte = iBlockStart / 8;
     1106        uint32_t iBit = iBlockStart % 8;
    1501107
    1511108        if (pBlkGrpDesc->abBlockBitmap[idxByte] & RT_BIT(iBit))
     
    1531110
    1541111        cBlocks--;
    155         offBlockStart++;
     1112        iBlockStart++;
    1561113    }
    1571114
     
    1601117
    1611118
     1119static DECLCALLBACK(int) rtFsExtVolBlockGroupTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
     1120{
     1121    RT_NOREF(pvUser);
     1122
     1123    PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)pCore;
     1124    Assert(!pBlockGroup->cRefs);
     1125    RTMemFree(pBlockGroup);
     1126    return VINF_SUCCESS;
     1127}
     1128
     1129
    1621130/**
    1631131 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
     
    1671135    PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
    1681136
    169     if (pThis->pBlkGrpDesc)
    170         RTMemFree(pThis->pBlkGrpDesc);
     1137    /* Destroy the block group tree. */
     1138    RTAvlU32Destroy(&pThis->BlockGroupRoot, rtFsExtVolBlockGroupTreeDestroy, pThis);
     1139    pThis->BlockGroupRoot = NULL;
     1140    RTListInit(&pThis->LstBlockGroupLru);
     1141
     1142    /*
     1143     * Backing file and handles.
     1144     */
     1145    RTVfsFileRelease(pThis->hVfsBacking);
     1146    pThis->hVfsBacking = NIL_RTVFSFILE;
     1147    pThis->hVfsSelf    = NIL_RTVFS;
    1711148
    1721149    return VINF_SUCCESS;
     
    1891166static DECLCALLBACK(int) rtFsExtVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
    1901167{
    191     NOREF(pvThis);
    192     NOREF(phVfsDir);
    193     return VERR_NOT_IMPLEMENTED;
     1168    PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
     1169    int rc = rtFsExtVol_OpenDirByInode(pThis, EXT_INODE_NR_ROOT_DIR, phVfsDir);
     1170    LogFlowFunc(("returns %Rrc\n", rc));
     1171    return rc;
    1941172}
    1951173
     
    2051183    *pfUsed = false;
    2061184
    207     while (cb > 0)
    208     {
    209         uint32_t const offBlockStart    = (uint32_t)(off / pThis->cbBlock);
    210         uint32_t const iBlockGroup      = (offBlockStart - pThis->iSbBlock) / pThis->cBlocksPerGroup;
    211         uint32_t const offBlockRelStart = offBlockStart - iBlockGroup * pThis->cBlocksPerGroup;
    212 
    213         if (   off < pThis->pBlkGrpDesc->offStart
    214             || off > pThis->pBlkGrpDesc->offLast)
    215         {
    216             /* Load new block descriptor. */
    217             rc = rtFsExtLoadBlkGrpDesc(pThis, iBlockGroup);
    218             if (RT_FAILURE(rc))
    219                 break;
    220         }
    221 
    222         size_t cbThis = RT_MIN(cb, pThis->pBlkGrpDesc->offLast - off + 1);
    223         if (rtFsExtIsBlockRangeInUse(pThis->pBlkGrpDesc,
    224                                      offBlockRelStart,
    225                                      cbThis / pThis->cbBlock + (cbThis % pThis->cbBlock ? 1 : 0)) )
     1185    uint64_t iBlock  = rtFsExtDiskOffsetToBlockIdx(pThis, off);
     1186    uint64_t cBlocks = rtFsExtDiskOffsetToBlockIdx(pThis, cb) + (cb % pThis->cbBlock ? 1 : 0);
     1187    while (cBlocks > 0)
     1188    {
     1189        uint32_t const iBlockGroup    = iBlock / pThis->cBlocksPerGroup;
     1190        uint32_t const iBlockRelStart = iBlock - iBlockGroup * pThis->cBlocksPerGroup;
     1191        PRTFSEXTBLKGRP pBlockGroup = NULL;
     1192
     1193        rc = rtFsExtBlockGroupLoad(pThis, iBlockGroup, &pBlockGroup);
     1194        if (RT_FAILURE(rc))
     1195            break;
     1196
     1197        uint64_t cBlocksThis = RT_MIN(cBlocks, iBlockRelStart - pThis->cBlocksPerGroup);
     1198        if (rtFsExtIsBlockRangeInUse(pBlockGroup, iBlockRelStart, cBlocksThis))
    2261199        {
    2271200            *pfUsed = true;
     
    2291202        }
    2301203
    231         cb  -= cbThis;
    232         off += cbThis;
     1204        rtFsExtBlockGroupRelease(pThis, pBlockGroup);
     1205        cBlocks -= cBlocksThis;
     1206        iBlock  += cBlocksThis;
    2331207    }
    2341208
     
    2561230
    2571231
     1232
     1233/**
     1234 * Loads the parameters from the given original ext2 format superblock (EXT_SB_REV_ORIG).
     1235 *
     1236 * @returns IPRT status code.
     1237 * @param   pThis               The ext volume instance.
     1238 * @param   pSb                 The superblock to load.
     1239 * @param   pErrInfo            Where to return additional error info.
     1240 */
     1241static int rtFsExtVolLoadAndParseSuperBlockV0(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
     1242{
     1243    RT_NOREF(pErrInfo);
     1244
     1245    /*
     1246     * Linux never supported a differing cluster (also called fragment) size for
     1247     * the original ext2 layout so we reject such filesystems as it is not clear what
     1248     * the purpose is really.
     1249     */
     1250    if (RT_LE2H_U32(pSb->cLogBlockSize) != RT_LE2H_U32(pSb->cLogClusterSize))
     1251        return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem cluster and block size differ");
     1252
     1253    pThis->f64Bit          = false;
     1254    pThis->cBlockShift     = 10 + RT_LE2H_U32(pSb->cLogBlockSize);
     1255    pThis->cbBlock         = 1 << pThis->cBlockShift;
     1256    pThis->cbInode         = sizeof(EXTINODE);
     1257    pThis->cbBlkGrpDesc    = sizeof(EXTBLOCKGROUPDESC32);
     1258    pThis->cBlocksPerGroup = RT_LE2H_U32(pSb->cBlocksPerGroup);
     1259    pThis->cInodesPerGroup = RT_LE2H_U32(pSb->cInodesPerBlockGroup);
     1260    pThis->cBlockGroups    = RT_LE2H_U32(pSb->cBlocksTotalLow) / pThis->cBlocksPerGroup;
     1261    pThis->cbBlockBitmap   = pThis->cBlocksPerGroup / 8;
     1262    if (pThis->cBlocksPerGroup % 8)
     1263        pThis->cbBlockBitmap++;
     1264    pThis->cbInodeBitmap   = pThis->cInodesPerGroup / 8;
     1265    if (pThis->cInodesPerGroup % 8)
     1266        pThis->cbInodeBitmap++;
     1267
     1268    return VINF_SUCCESS;
     1269}
     1270
     1271
     1272/**
     1273 * Loads the parameters from the given ext superblock (EXT_SB_REV_V2_DYN_INODE_SZ).
     1274 *
     1275 * @returns IPRT status code.
     1276 * @param   pThis               The ext volume instance.
     1277 * @param   pSb                 The superblock to load.
     1278 * @param   pErrInfo            Where to return additional error info.
     1279 */
     1280static int rtFsExtVolLoadAndParseSuperBlockV1(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
     1281{
     1282    if (RT_LE2H_U32(pSb->fFeaturesIncompat) != 0)
     1283        return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported incompatible features: %RX32",
     1284                                   RT_LE2H_U32(pSb->fFeaturesIncompat));
     1285    if (   RT_LE2H_U32(pSb->fFeaturesCompatRo) != 0
     1286        && !(pThis->fMntFlags & RTVFSMNT_F_READ_ONLY))
     1287        return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported readonly features: %RX32",
     1288                                   RT_LE2H_U32(pSb->fFeaturesCompatRo));
     1289
     1290    return VINF_SUCCESS;
     1291}
     1292
     1293
     1294/**
     1295 * Loads and parses the superblock of the filesystem.
     1296 *
     1297 * @returns IPRT status code.
     1298 * @param   pThis               The ext volume instance.
     1299 * @param   pErrInfo            Where to return additional error info.
     1300 */
     1301static int rtFsExtVolLoadAndParseSuperblock(PRTFSEXTVOL pThis, PRTERRINFO pErrInfo)
     1302{
     1303    int rc = VINF_SUCCESS;
     1304    EXTSUPERBLOCK Sb;
     1305    rc = RTVfsFileReadAt(pThis->hVfsBacking, EXT_SB_OFFSET, &Sb, sizeof(EXTSUPERBLOCK), NULL);
     1306    if (RT_FAILURE(rc))
     1307        return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading super block");
     1308
     1309    /* Validate the superblock. */
     1310    if (RT_LE2H_U16(Sb.u16Signature) != EXT_SB_SIGNATURE)
     1311        return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not EXT - Signature mismatch: %RX16", RT_LE2H_U16(Sb.u16Signature));
     1312
     1313#ifdef LOG_ENABLED
     1314    rtFsExtSb_Log(&Sb);
     1315#endif
     1316
     1317    if (RT_LE2H_U16(Sb.u16FilesystemState) == EXT_SB_STATE_ERRORS)
     1318        return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains errors");
     1319
     1320    if (RT_LE2H_U32(Sb.u32RevLvl) == EXT_SB_REV_ORIG)
     1321        rc = rtFsExtVolLoadAndParseSuperBlockV0(pThis, &Sb, pErrInfo);
     1322    else
     1323        rc = rtFsExtVolLoadAndParseSuperBlockV1(pThis, &Sb, pErrInfo);
     1324
     1325    return rc;
     1326}
     1327
     1328
    2581329RTDECL(int) RTFsExtVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fExtFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
    2591330{
     
    2651336    AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
    2661337
     1338    /*
     1339     * Create a VFS instance and initialize the data so rtFsExtVol_Close works.
     1340     */
     1341    RTVFS       hVfs;
    2671342    PRTFSEXTVOL pThis;
    268     int rc = RTVfsNew(&g_rtFsExtVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, phVfs, (void **)&pThis);
     1343    int rc = RTVfsNew(&g_rtFsExtVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
    2691344    if (RT_SUCCESS(rc))
    2701345    {
    271         pThis->hVfsBacking = hVfsFileIn;
    272         pThis->pBlkGrpDesc = NULL;
    273 
    274         EXTSUPERBLOCK SuperBlock;
    275         rc = RTVfsFileReadAt(hVfsFileIn, 1024, &SuperBlock, sizeof(EXTSUPERBLOCK), NULL);
     1346        pThis->hVfsBacking    = hVfsFileIn;
     1347        pThis->hVfsSelf       = hVfs;
     1348        pThis->fMntFlags      = fMntFlags;
     1349        pThis->fExtFlags      = fExtFlags;
     1350        pThis->BlockGroupRoot = NULL;
     1351        pThis->InodeRoot      = NULL;
     1352        pThis->cbBlockGroups  = 0;
     1353        pThis->cbInodes       = 0;
     1354        RTListInit(&pThis->LstBlockGroupLru);
     1355        RTListInit(&pThis->LstInodeLru);
     1356
     1357        rc = RTVfsFileGetSize(pThis->hVfsBacking, &pThis->cbBacking);
    2761358        if (RT_SUCCESS(rc))
    2771359        {
    278 #if defined(RT_BIGENDIAN)
    279             /** @todo Convert to host endianess. */
    280 #endif
    281             if (SuperBlock.u16FilesystemState == EXT_SB_STATE_ERRORS)
    282                 rc = RTERRINFO_LOG_SET(pErrInfo, VERR_FILESYSTEM_CORRUPT, "EXT_STATE_ERRORS");
    283             else
    284             {
    285                 pThis->iSbBlock = SuperBlock.iBlockOfSuperblock;
    286                 pThis->cbBlock = 1024 << SuperBlock.cLogBlockSize;
    287                 pThis->cBlocksPerGroup = SuperBlock.cBlocksPerGroup;
    288                 pThis->cBlockGroups = SuperBlock.cBlocksTotalLow / pThis->cBlocksPerGroup;
    289 
    290                 /* Load first block group descriptor. */
    291                 rc = rtFsExtLoadBlkGrpDesc(pThis, 0);
    292             }
     1360            rc = rtFsExtVolLoadAndParseSuperblock(pThis, pErrInfo);
    2931361            if (RT_SUCCESS(rc))
    2941362            {
     1363                *phVfs = hVfs;
    2951364                return VINF_SUCCESS;
    2961365            }
    2971366        }
    298         else
    299             rc = RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading super block");
    300 
    301         RTVfsRelease(*phVfs);
     1367
     1368        RTVfsRelease(hVfs);
    3021369        *phVfs = NIL_RTVFS;
    3031370    }
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette