/* $Id: filesystemext.cpp 56290 2015-06-09 14:01:31Z vboxsync $ */ /** @file * IPRT Filesystem API (FileSys) - ext2/3 format. */ /* * Copyright (C) 2012-2015 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. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. */ /******************************************************************************* * Header Files * *******************************************************************************/ #define LOG_GROUP LOG_GROUP_DEFAULT #include #include #include #include #include #include #include "internal/filesystem.h" /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /* * The filesystem structures are from http://wiki.osdev.org/Ext2 and * http://www.nongnu.org/ext2-doc/ext2.html */ /** * Ext superblock. * * Everything is stored little endian on the disk. */ #pragma pack(1) typedef struct ExtSuperBlock { /** Total number of inodes in the filesystem. */ uint32_t cInodesTotal; /** Total number of blocks in the filesystem. */ uint32_t cBlocksTotal; /** Number of blocks reserved for the super user. */ uint32_t cBlocksRsvdForSuperUser; /** Total number of unallocated blocks. */ uint32_t cBlocksUnallocated; /** Total number of unallocated inodes. */ uint32_t cInodesUnallocated; /** Block number of block containing the superblock. */ uint32_t iBlockOfSuperblock; /** Number of bits to shift 1024 to the left to get the block size */ uint32_t cBitsShiftLeftBlockSize; /** Number of bits to shift 1024 to the left to get the fragment size */ uint32_t cBitsShiftLeftFragmentSize; /** Number of blocks in each block group. */ uint32_t cBlocksPerGroup; /** Number of fragments in each block group. */ uint32_t cFragmentsPerBlockGroup; /** Number of inodes in each block group. */ uint32_t cInodesPerBlockGroup; /** Last mount time. */ uint32_t u32LastMountTime; /** Last written time. */ uint32_t u32LastWrittenTime; /** Number of times the volume was mounted since the last check. */ uint16_t cMountsSinceLastCheck; /** Number of mounts allowed before a consistency check. */ uint16_t cMaxMountsUntilCheck; /** Signature to identify a ext2 volume. */ uint16_t u16Signature; /** State of the filesystem (clean/errors) */ uint16_t u16FilesystemState; /** What to do on an error. */ uint16_t u16ActionOnError; /** Minor version field. */ uint16_t u16VersionMinor; /** Time of last check. */ uint32_t u32LastCheckTime; /** Interval between consistency checks. */ uint32_t u32CheckInterval; /** Operating system ID of the filesystem creator. */ uint32_t u32OsIdCreator; /** Major version field. */ uint32_t u32VersionMajor; /** User ID that is allowed to use reserved blocks. */ uint16_t u16UidReservedBlocks; /** Group ID that is allowed to use reserved blocks. */ uint16_t u16GidReservedBlocks; /** Reserved fields. */ uint8_t abReserved[940]; } ExtSuperBlock; #pragma pack() AssertCompileSize(ExtSuperBlock, 1024); /** Pointer to an ext super block. */ typedef ExtSuperBlock *PExtSuperBlock; /** Ext2 signature. */ #define RTFILESYSTEM_EXT2_SIGNATURE (0xef53) /** Clean filesystem state. */ #define RTFILESYSTEM_EXT2_STATE_CLEAN (0x0001) /** Error filesystem state. */ #define RTFILESYSTEM_EXT2_STATE_ERRORS (0x0002) /** * Block group descriptor. */ #pragma pack(1) typedef struct BlockGroupDesc { /** Block address of the block bitmap. */ uint32_t offBlockBitmap; /** Block address of the inode bitmap. */ uint32_t offInodeBitmap; /** Start block address of the inode table. */ uint32_t offInodeTable; /** Number of unallocated blocks in group. */ uint16_t cBlocksUnallocated; /** Number of unallocated inodes in group. */ uint16_t cInodesUnallocated; /** Number of directories in the group. */ uint16_t cDirectories; /** Padding. */ uint16_t u16Padding; /** Reserved. */ uint8_t abReserved[12]; } BlockGroupDesc; #pragma pack() AssertCompileSize(BlockGroupDesc, 32); /** Pointer to an ext block group descriptor. */ typedef BlockGroupDesc *PBlockGroupDesc; /** * Cached block group descriptor data. */ typedef struct RTFILESYSTEMEXTBLKGRP { /** Start offset (in bytes and from the start of the disk). */ uint64_t offStart; /** Last offset in the block group (inclusive). */ uint64_t offLast; /** Block bitmap - variable in size (depends on the block size * and number of blocks per group). */ uint8_t abBlockBitmap[1]; } RTFILESYSTEMEXTBLKGRP; /** Pointer to block group descriptor data. */ typedef RTFILESYSTEMEXTBLKGRP *PRTFILESYSTEMEXTBLKGRP; /** * Ext2/3 filesystem data. */ typedef struct RTFILESYSTEMEXT { /** VFS file handle. */ RTVFSFILE hVfsFile; /** Block number of the superblock. */ uint32_t iSbBlock; /** Size of one block. */ size_t cbBlock; /** Number of blocks in one group. */ unsigned cBlocksPerGroup; /** Number of blocks groups in the volume. */ unsigned cBlockGroups; /** Cached block group descriptor data. */ PRTFILESYSTEMEXTBLKGRP pBlkGrpDesc; } RTFILESYSTEMEXT; /** Pointer to the ext filesystem data. */ typedef RTFILESYSTEMEXT *PRTFILESYSTEMEXT; /******************************************************************************* * Methods * *******************************************************************************/ /** * Loads the block descriptor of the given block group from the medium. * * @returns IPRT status code. * @param pThis EXT filesystem instance data. * @param iBlkGrp Block group number to load. */ static int rtFsExtLoadBlkGrpDesc(PRTFILESYSTEMEXT pThis, uint32_t iBlkGrp) { int rc = VINF_SUCCESS; PRTFILESYSTEMEXTBLKGRP pBlkGrpDesc = pThis->pBlkGrpDesc; uint64_t offRead = (pThis->iSbBlock + 1) * pThis->cbBlock; BlockGroupDesc BlkDesc; size_t cbBlockBitmap; cbBlockBitmap = pThis->cBlocksPerGroup / 8; if (pThis->cBlocksPerGroup % 8) cbBlockBitmap++; if (!pBlkGrpDesc) { size_t cbBlkDesc = RT_OFFSETOF(RTFILESYSTEMEXTBLKGRP, abBlockBitmap[cbBlockBitmap]); pBlkGrpDesc = (PRTFILESYSTEMEXTBLKGRP)RTMemAllocZ(cbBlkDesc); if (!pBlkGrpDesc) return VERR_NO_MEMORY; } rc = RTVfsFileReadAt(pThis->hVfsFile, offRead, &BlkDesc, sizeof(BlkDesc), NULL); if (RT_SUCCESS(rc)) { pBlkGrpDesc->offStart = pThis->iSbBlock + (uint64_t)iBlkGrp * pThis->cBlocksPerGroup * pThis->cbBlock; pBlkGrpDesc->offLast = pBlkGrpDesc->offStart + pThis->cBlocksPerGroup * pThis->cbBlock; rc = RTVfsFileReadAt(pThis->hVfsFile, BlkDesc.offBlockBitmap * pThis->cbBlock, &pBlkGrpDesc->abBlockBitmap[0], cbBlockBitmap, NULL); } pThis->pBlkGrpDesc = pBlkGrpDesc; return rc; } static bool rtFsExtIsBlockRangeInUse(PRTFILESYSTEMEXTBLKGRP pBlkGrpDesc, uint32_t offBlockStart, size_t cBlocks) { bool fUsed = false; while (cBlocks) { uint32_t idxByte = offBlockStart / 8; uint32_t iBit = offBlockStart % 8; if (pBlkGrpDesc->abBlockBitmap[idxByte] & RT_BIT(iBit)) { fUsed = true; break; } cBlocks--; offBlockStart++; } return fUsed; } static DECLCALLBACK(int) rtFsExtProbe(RTVFSFILE hVfsFile, uint32_t *puScore) { int rc = VINF_SUCCESS; uint64_t cbMedium = 0; *puScore = RTFILESYSTEM_MATCH_SCORE_UNSUPPORTED; rc = RTVfsFileGetSize(hVfsFile, &cbMedium); if (RT_SUCCESS(rc)) { if (cbMedium >= 2*sizeof(ExtSuperBlock)) { ExtSuperBlock SuperBlock; rc = RTVfsFileReadAt(hVfsFile, 1024, &SuperBlock, sizeof(ExtSuperBlock), NULL); if (RT_SUCCESS(rc)) { #if defined(RT_BIGENDIAN) /** @todo: Convert to host endianess. */ #endif if (SuperBlock.u16Signature == RTFILESYSTEM_EXT2_SIGNATURE) *puScore = RTFILESYSTEM_MATCH_SCORE_SUPPORTED; } } } return rc; } static DECLCALLBACK(int) rtFsExtInit(void *pvThis, RTVFSFILE hVfsFile) { int rc = VINF_SUCCESS; PRTFILESYSTEMEXT pThis = (PRTFILESYSTEMEXT)pvThis; ExtSuperBlock SuperBlock; pThis->hVfsFile = hVfsFile; pThis->pBlkGrpDesc = NULL; rc = RTVfsFileReadAt(hVfsFile, 1024, &SuperBlock, sizeof(ExtSuperBlock), NULL); if (RT_SUCCESS(rc)) { #if defined(RT_BIGENDIAN) /** @todo: Convert to host endianess. */ #endif if (SuperBlock.u16FilesystemState == RTFILESYSTEM_EXT2_STATE_ERRORS) rc = VERR_FILESYSTEM_CORRUPT; else { pThis->iSbBlock = SuperBlock.iBlockOfSuperblock; pThis->cbBlock = 1024 << SuperBlock.cBitsShiftLeftBlockSize; pThis->cBlocksPerGroup = SuperBlock.cBlocksPerGroup; pThis->cBlockGroups = SuperBlock.cBlocksTotal / pThis->cBlocksPerGroup; /* Load first block group descriptor. */ rc = rtFsExtLoadBlkGrpDesc(pThis, 0); } } return rc; } static DECLCALLBACK(void) rtFsExtDestroy(void *pvThis) { PRTFILESYSTEMEXT pThis = (PRTFILESYSTEMEXT)pvThis; if (pThis->pBlkGrpDesc) RTMemFree(pThis->pBlkGrpDesc); } static DECLCALLBACK(int) rtFsExtOpenRoot(void *pvThis, PRTVFSDIR phVfsDir) { NOREF(pvThis); NOREF(phVfsDir); return VERR_NOT_IMPLEMENTED; } static DECLCALLBACK(int) rtFsExtIsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed) { int rc = VINF_SUCCESS; uint64_t offStart = (uint64_t)off; PRTFILESYSTEMEXT pThis = (PRTFILESYSTEMEXT)pvThis; *pfUsed = false; while (cb > 0) { bool fUsed; uint32_t offBlockStart = (uint32_t)(offStart / pThis->cbBlock); uint32_t iBlockGroup = (offBlockStart - pThis->iSbBlock) / pThis->cBlocksPerGroup; uint32_t offBlockRelStart = offBlockStart - iBlockGroup * pThis->cBlocksPerGroup; size_t cbThis = 0; if ( offStart < pThis->pBlkGrpDesc->offStart || offStart > pThis->pBlkGrpDesc->offLast) { /* Load new block descriptor. */ rc = rtFsExtLoadBlkGrpDesc(pThis, iBlockGroup); if (RT_FAILURE(rc)) break; } cbThis = RT_MIN(cb, pThis->pBlkGrpDesc->offLast - offStart + 1); fUsed = rtFsExtIsBlockRangeInUse(pThis->pBlkGrpDesc, offBlockRelStart, cbThis / pThis->cbBlock + (cbThis % pThis->cbBlock ? 1 : 0)); if (fUsed) { *pfUsed = true; break; } cb -= cbThis; offStart += cbThis; } return rc; } DECL_HIDDEN_CONST(RTFILESYSTEMDESC) const g_rtFsExt = { /** cbFs */ sizeof(RTFILESYSTEMEXT), /** VfsOps */ { /** uVersion. */ RTVFSOPS_VERSION, /** fFeatures */ 0, /** pszName */ "ExtVfsOps", /** pfnDestroy */ rtFsExtDestroy, /** pfnOpenRoot */ rtFsExtOpenRoot, /** pfnIsRangeInUse */ rtFsExtIsRangeInUse, /** uEndMarker */ RTVFSOPS_VERSION }, /** pfnProbe */ rtFsExtProbe, /** pfnInit */ rtFsExtInit };