VirtualBox

Changeset 80513 in vbox


Ignore:
Timestamp:
Aug 30, 2019 2:43:53 PM (6 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
133014
Message:

vboximg-mount: Get rid of the duplicated partition parsing code and switch to the RTDvm* API. Allow access to all volumes in an image by creating multiple vol<id> files, the vhdd file always accesses the whole disk now

Location:
trunk/src/VBox/ImageMounter/vboximg-mount
Files:
1 deleted
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp

    r80512 r80513  
    7575#include <iprt/base64.h>
    7676#include <iprt/vfs.h>
    77 
    78 #include "vboximg-mount.h"
     77#include <iprt/dvm.h>
     78
    7979#include "vboximgCrypto.h"
    8080#include "vboximgMedia.h"
     
    8888};
    8989
    90 enum { PARTITION_TABLE_MBR = 1, PARTITION_TABLE_GPT = 2 };
    91 
    9290#define VBOX_EXTPACK                "Oracle VM VirtualBox Extension Pack"
    93 #define GPT_PTABLE_SIZE             32 * 512         /** Max size we to read for GPT partition table */
    94 #define MBR_PARTITIONS_MAX          4                /** Fixed number of partitions in Master Boot Record */
    95 #define BASENAME_MAX                256              /** Maximum name for the basename of a path (for RTStrNLen()*/
    96 #define VBOXIMG_PARTITION_MAX       256              /** How much storage to allocate to store partition info */
    97 #define PARTITION_NAME_MAX          72               /** Maximum partition name size (accomodates GPT partition name) */
    98 #define DOS_BOOT_RECORD_SIGNATURE   0xaa55           /** MBR and EBR (partition table) signature [EOT boundary] */
    99 #define NULL_BOOT_RECORD_SIGNATURE  0x0000           /** MBR or EBR null signature value */
    100 #define MAX_UUID_LEN                256              /** Max length of a UUID */
    101 #define LBA(n)                      (n * g_cbSector)
    10291#define VERBOSE                     g_vboximgOpts.fVerbose
    103 
    104 #define PARTTYPE_IS_NULL(parType) ((uint8_t)parType == 0x00)
    105 #define PARTTYPE_IS_GPT(parType)  ((uint8_t)parType == 0xee)
    106 #define PARTTYPE_IS_EXT(parType)  ((    (uint8_t)parType) == 0x05  /* Extended           */ \
    107                                     || ((uint8_t)parType) == 0x0f  /* W95 Extended (LBA) */ \
    108                                     || ((uint8_t)parType) == 0x85) /* Linux Extended     */
    10992
    11093#define SAFENULL(strPtr)   (strPtr ? strPtr : "")
     
    11396static struct fuse_operations g_vboximgOps;         /** FUSE structure that defines allowed ops for this FS */
    11497
     98/**
     99 * Volume data.
     100 */
     101typedef struct VBOXIMGMOUNTVOL
     102{
     103    /** The volume handle. */
     104    RTDVMVOLUME                 hVol;
     105    /** The VFS file associated with the volume. */
     106    RTVFSFILE                   hVfsFileVol;
     107} VBOXIMGMOUNTVOL;
     108/** Pointer to a volume data structure. */
     109typedef VBOXIMGMOUNTVOL *PVBOXIMGMOUNTVOL;
     110
    115111/* Global variables */
    116 
    117112static RTVFSFILE             g_hVfsFileDisk;        /** Disk as VFS file handle. */
    118113static uint32_t              g_cbSector;            /** Disk sector size. */
     114static RTDVM                 g_hDvmMgr;             /** Handle to the volume manager. */
    119115static char                 *g_pszDiskUuid;         /** UUID of image (if known, otherwise NULL) */
    120 static off_t                 g_vDiskOffset;         /** Biases r/w from start of VD */
    121 static off_t                 g_vDiskSize;           /** Limits r/w length for VD */
    122 static int32_t               g_cReaders;            /** Number of readers for VD */
    123 static int32_t               g_cWriters;            /** Number of writers for VD */
    124 static RTFOFF                g_cbEntireVDisk;       /** Size of VD */
    125116static PVDINTERFACE          g_pVdIfs;              /** @todo Remove when VD I/O becomes threadsafe */
    126117static VDINTERFACETHREADSYNC g_VDIfThreadSync;      /** @todo Remove when VD I/O becomes threadsafe */
    127118static RTCRITSECT            g_vdioLock;            /** @todo Remove when VD I/O becomes threadsafe */
    128 static uint16_t              g_lastPartNbr;         /** Last partition number found in MBR + EBR chain */
    129 static bool                  g_fGPT;                /** True if GPT type partition table was found */
    130119static char                 *g_pszImageName;        /** Base filename for current VD image */
    131120static char                 *g_pszImagePath;        /** Full path to current VD image */
     
    134123static uint32_t              g_cImages;             /** Number of images in diff chain */
    135124
    136 /* Table entry containing partition info parsed out of GPT or MBR and EBR chain of specified VD */
    137 
    138 typedef struct
    139 {
    140     int            idxPartition;            /** partition number */
    141     char           *pszName;
    142     off_t          offPartition;            /** partition offset from start of disk, in bytes */
    143     uint64_t       cbPartition;             /** partition size in bytes */
    144     uint8_t        fBootable;               /** partition bootable */
    145     union
    146     {
    147         uint8_t    legacy;                  /** partition type MBR/EBR */
    148         uint128_t  gptGuidTypeSpecifier;    /** partition type GPT */
    149     } partitionType;                        /** uint8_t for MBR/EBR (legacy) and GUID for GPT */
    150     union
    151     {
    152         MBRPARTITIONENTRY mbrEntry;         /** MBR (also EBR partition entry) */
    153         GPTPARTITIONENTRY gptEntry;         /** GPT partition entry */
    154     } partitionEntry;
    155 } PARTITIONINFO;
    156 
    157 PARTITIONINFO g_aParsedPartitionInfo[VBOXIMG_PARTITION_MAX + 1]; /* Note: Element 0 reserved for EntireDisk partitionEntry */
     125/** Pointer to the detected volumes. */
     126static PVBOXIMGMOUNTVOL      g_paVolumes;
     127/** Number of detected volumes. */
     128static uint32_t              g_cVolumes;
    158129
    159130VBOXIMGOPTS g_vboximgOpts;
     
    201172
    202173IMAGELIST listHeadLockList;  /* flink & blink intentionally left NULL */
     174
     175
     176
     177/** @todo Remove when VD I/O becomes threadsafe */
     178static DECLCALLBACK(int) vboximgThreadStartRead(void *pvUser)
     179{
     180    PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
     181    return RTCritSectEnter(vdioLock);
     182}
     183
     184static DECLCALLBACK(int) vboximgThreadFinishRead(void *pvUser)
     185{
     186    PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
     187    return RTCritSectLeave(vdioLock);
     188}
     189
     190static DECLCALLBACK(int) vboximgThreadStartWrite(void *pvUser)
     191{
     192    PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
     193    return RTCritSectEnter(vdioLock);
     194}
     195
     196static DECLCALLBACK(int) vboximgThreadFinishWrite(void *pvUser)
     197{
     198    PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
     199    return RTCritSectLeave(vdioLock);
     200}
     201/** @todo (end of to do section) */
     202
    203203
    204204static void
     
    283283}
    284284
     285
     286/**
     287 * Queries the VFS object handle from the given path.
     288 *
     289 * @returns IPRT status code.
     290 * @retval  VERR_NOT_FOUND if the object denoted by the path couldn't be found.
     291 * @param   pszPath             The path.
     292 * @param   phVfsObj            Where to store the handle to the VFS object on success.
     293 */
     294static int vboxImgMntVfsObjQueryFromPath(const char *pszPath, PRTVFSOBJ phVfsObj)
     295{
     296    PRTPATHSPLIT pPathSplit = NULL;
     297    int rc = RTPathSplitA(pszPath, &pPathSplit, RTPATH_STR_F_STYLE_HOST);
     298    if (RT_SUCCESS(rc))
     299    {
     300        if (   RTPATH_PROP_HAS_ROOT_SPEC(pPathSplit->fProps)
     301            && pPathSplit->cComps == 2)
     302        {
     303            /* Skip the root specifier and start with the component coming afterwards. */
     304            if (!RTStrCmp(pPathSplit->apszComps[1], "vhdd"))
     305                *phVfsObj = RTVfsObjFromFile(g_hVfsFileDisk);
     306            else if (!RTStrNCmp(&pszPath[1], "vol", sizeof("vol") - 1))
     307            {
     308                /* Retrieve the accessed volume and return the stat data. */
     309                uint32_t idxVol;
     310                int rcIprt = RTStrToUInt32Full(&pszPath[4], 10, &idxVol);
     311                if (   rcIprt == VINF_SUCCESS
     312                    && idxVol < g_cVolumes)
     313                    *phVfsObj = RTVfsObjFromFile(g_paVolumes[idxVol].hVfsFileVol);
     314                else
     315                    rc = VERR_NOT_FOUND;
     316            }
     317            else
     318                rc = VERR_NOT_FOUND;
     319
     320            rc = VINF_SUCCESS;
     321        }
     322        else
     323            rc = VERR_NOT_FOUND;
     324        RTPathSplitFree(pPathSplit);
     325    }
     326
     327    return rc;
     328}
     329
     330
    285331/** @copydoc fuse_operations::open */
    286332static int vboximgOp_open(const char *pszPath, struct fuse_file_info *pInfo)
    287333{
    288     RT_NOREF(pszPath, pInfo);;
    289334    LogFlowFunc(("pszPath=%s\n", pszPath));
    290     uint32_t notsup = 0;
    291335    int rc = 0;
    292336
     337    RTVFSOBJ hVfsObj;
     338    int rcIprt = vboxImgMntVfsObjQueryFromPath(pszPath, &hVfsObj);
     339    if (RT_SUCCESS(rc))
     340    {
     341        uint32_t fNotSup = 0;
     342
    293343#ifdef UNIX_DERIVATIVE
    294 #   ifdef RT_OS_DARWIN
    295         notsup = O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK |
    296                  O_ASYNC  | O_CREAT    | O_TRUNC   | O_EXCL | O_EVTONLY;
    297 #   elif defined(RT_OS_LINUX)
    298         notsup = O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
    299                  /* | O_LARGEFILE | O_SYNC | ? */
    300 #   elif defined(RT_OS_FREEBSD)
    301         notsup = O_APPEND | O_ASYNC | O_DIRECT | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
    302                  /* | O_LARGEFILE | O_SYNC | ? */
    303 #   endif
     344# ifdef RT_OS_DARWIN
     345        fNotSup = O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK |
     346                  O_ASYNC  | O_CREAT    | O_TRUNC   | O_EXCL | O_EVTONLY;
     347# elif defined(RT_OS_LINUX)
     348        fNotSup = O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
     349                  /* | O_LARGEFILE | O_SYNC | ? */
     350# elif defined(RT_OS_FREEBSD)
     351        fNotSup = O_APPEND | O_ASYNC | O_DIRECT | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
     352                  /* | O_LARGEFILE | O_SYNC | ? */
     353# endif
    304354#else
    305355#   error "Port me"
    306356#endif
    307357
    308 if (pInfo->flags & notsup)
    309     rc -EINVAL;
    310 
     358        if (!(pInfo->flags & fNotSup))
     359        {
    311360#ifdef UNIX_DERIVATIVE
    312     if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
    313         rc = -EINVAL;
    314 #   ifdef O_DIRECTORY
    315     if (pInfo->flags & O_DIRECTORY)
    316         rc = -ENOTDIR;
    317 #   endif
     361            if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
     362                rc = -EINVAL;
     363# ifdef O_DIRECTORY
     364            if (pInfo->flags & O_DIRECTORY)
     365                rc = -ENOTDIR;
     366# endif
    318367#endif
    319368
    320     if (RT_FAILURE(rc))
    321     {
    322         LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath));
    323         return rc;
    324     }
    325 
    326     int fWriteable =    (pInfo->flags & O_ACCMODE) == O_WRONLY
    327                      || (pInfo->flags & O_ACCMODE) == O_RDWR;
    328     if (g_cWriters)
    329         rc = -ETXTBSY;
     369            if (!rc)
     370            {
     371                pInfo->fh = (uintptr_t)hVfsObj;
     372                return 0;
     373            }
     374        }
     375        else
     376            rc = -EINVAL;
     377
     378        RTVfsObjRelease(hVfsObj);
     379    }
    330380    else
    331     {
    332             if (fWriteable)
    333                 g_cWriters++;
    334             else
    335             {
    336                 if (g_cReaders + 1 > MAX_READERS)
    337                     rc = -EMLINK;
    338                 else
    339                     g_cReaders++;
    340             }
    341     }
     381        rc = -RTErrConvertToErrno(rcIprt);
     382
    342383    LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath));
    343384    return rc;
     
    345386}
    346387
    347 
    348 /** @todo Remove when VD I/O becomes threadsafe */
    349 static DECLCALLBACK(int) vboximgThreadStartRead(void *pvUser)
    350 {
    351     PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
    352     return RTCritSectEnter(vdioLock);
    353 }
    354 
    355 static DECLCALLBACK(int) vboximgThreadFinishRead(void *pvUser)
    356 {
    357     PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
    358     return RTCritSectLeave(vdioLock);
    359 }
    360 
    361 static DECLCALLBACK(int) vboximgThreadStartWrite(void *pvUser)
    362 {
    363     PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
    364     return RTCritSectEnter(vdioLock);
    365 }
    366 
    367 static DECLCALLBACK(int) vboximgThreadFinishWrite(void *pvUser)
    368 {
    369     PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
    370     return RTCritSectLeave(vdioLock);
    371 }
    372 /** @todo (end of to do section) */
    373 
    374388/** @copydoc fuse_operations::release */
    375389static int vboximgOp_release(const char *pszPath, struct fuse_file_info *pInfo)
     
    379393    LogFlowFunc(("pszPath=%s\n", pszPath));
    380394
    381     if (    (pInfo->flags & O_ACCMODE) == O_WRONLY
    382         ||  (pInfo->flags & O_ACCMODE) == O_RDWR)
    383     {
    384         g_cWriters--;
    385         Assert(g_cWriters >= 0);
    386     }
    387     else if ((pInfo->flags & O_ACCMODE) == O_RDONLY)
    388     {
    389         g_cReaders--;
    390         Assert(g_cReaders >= 0);
    391     }
    392     else
    393         AssertFailed();
     395    RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh;
     396    RTVfsObjRelease(hVfsObj);
    394397
    395398    LogFlowFunc(("\"%s\"\n", pszPath));
     
    403406{
    404407    NOREF(pszPath);
    405     NOREF(pInfo);
    406 
    407     LogFlowFunc(("my offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
     408
     409    LogFlowFunc(("offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
    408410
    409411    AssertReturn(offset >= 0, -EINVAL);
     
    411413    AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
    412414
    413     AssertReturn(offset + g_vDiskOffset >= 0, -EINVAL);
    414     int64_t adjOff = offset + g_vDiskOffset;
    415 
    416415    int rc = 0;
    417     if ((off_t)(adjOff + cbBuf) < adjOff)
    418         rc = -EINVAL;
    419     else if (adjOff >= g_vDiskSize)
    420         return 0;
    421     else if (!cbBuf)
    422         return 0;
    423 
    424     if (rc >= 0)
    425     {
    426         int rcIprt = RTVfsFileReadAt(g_hVfsFileDisk, adjOff, pbBuf, cbBuf, NULL);
    427         if (RT_FAILURE(rc))
    428             rc = -RTErrConvertToErrno(rcIprt);
    429         else
    430             rc = cbBuf;
    431     }
     416    RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh;
     417    switch (RTVfsObjGetType(hVfsObj))
     418    {
     419        case RTVFSOBJTYPE_FILE:
     420        {
     421            RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
     422            int rcIprt = RTVfsFileReadAt(hVfsFile, offset, pbBuf, cbBuf, NULL);
     423            if (RT_FAILURE(rc))
     424                rc = -RTErrConvertToErrno(rcIprt);
     425            else
     426                rc = cbBuf;
     427            RTVfsFileRelease(hVfsFile);
     428            break;
     429        }
     430        default:
     431            rc = -EINVAL;
     432    }
     433
    432434    if (rc < 0)
    433435        LogFlowFunc(("%s\n", strerror(rc)));
     
    447449    AssertReturn((int)cbBuf >= 0, -EINVAL);
    448450    AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
    449     AssertReturn(offset + g_vDiskOffset >= 0, -EINVAL);
    450     int64_t adjOff = offset + g_vDiskOffset;
    451 
    452     int rc = 0;
    453     if (!g_vboximgOpts.fRW) {
     451
     452    if (!g_vboximgOpts.fRW)
     453    {
    454454        LogFlowFunc(("WARNING: vboximg-mount (FUSE FS) --rw option not specified\n"
    455455                     "              (write operation ignored w/o error!)\n"));
    456456        return cbBuf;
    457     } else if ((off_t)(adjOff + cbBuf) < adjOff)
    458         rc = -EINVAL;
    459     else if (offset >= g_vDiskSize)
    460         return 0;
    461     else if (!cbBuf)
    462         return 0;
    463 
    464     if (rc >= 0)
    465     {
    466         int rcIprt = RTVfsFileWriteAt(g_hVfsFileDisk, adjOff, pbBuf, cbBuf, NULL);
    467         if (RT_FAILURE(rc))
    468             rc = -RTErrConvertToErrno(rcIprt);
    469         else
    470             rc = cbBuf;
    471     }
     457    }
     458
     459    int rc = 0;
     460    RTVFSOBJ hVfsObj = (RTVFSOBJ)(uintptr_t)pInfo->fh;
     461    switch (RTVfsObjGetType(hVfsObj))
     462    {
     463        case RTVFSOBJTYPE_FILE:
     464        {
     465            RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
     466            int rcIprt = RTVfsFileWriteAt(hVfsFile, offset, pbBuf, cbBuf, NULL);
     467            if (RT_FAILURE(rc))
     468                rc = -RTErrConvertToErrno(rcIprt);
     469            else
     470                rc = cbBuf;
     471            RTVfsFileRelease(hVfsFile);
     472            break;
     473        }
     474        default:
     475            rc = -EINVAL;
     476    }
     477
    472478    if (rc < 0)
    473479        LogFlowFunc(("%s\n", strerror(rc)));
     
    501507         * (or overridden) directly by the { -s | --size } option on the command line.
    502508         */
    503         stbuf->st_size = g_vDiskSize;
     509        uint64_t cbFile = 0;
     510        int rcIprt = RTVfsFileGetSize(g_hVfsFileDisk, &cbFile);
     511        if (RT_SUCCESS(rcIprt))
     512            stbuf->st_size = cbFile;
     513        else
     514            rc = -RTErrConvertToErrno(rcIprt);
    504515        stbuf->st_nlink = 1;
     516    }
     517    else if (!RTStrNCmp(&pszPath[1], "vol", sizeof("vol") - 1))
     518    {
     519        /* Retrieve the accessed volume and return the stat data. */
     520        uint32_t idxVol;
     521        int rcIprt = RTStrToUInt32Full(&pszPath[4], 10, &idxVol);
     522        if (   rcIprt == VINF_SUCCESS
     523            && idxVol < g_cVolumes)
     524        {
     525            rc = stat(g_pszImagePath, stbuf);
     526            if (rc < 0)
     527                return rc;
     528            /*
     529             * st_size represents the size of the FUSE FS-mounted portion of the disk.
     530             * By default it is the whole disk, but can be a partition or specified
     531             * (or overridden) directly by the { -s | --size } option on the command line.
     532             */
     533            stbuf->st_size = RTDvmVolumeGetSize(g_paVolumes[idxVol].hVol);
     534            stbuf->st_nlink = 1;
     535        }
     536        else
     537            rc = -ENOENT;
    505538    }
    506539    else if (RTStrNCmp(pszPath + 1, g_pszImageName, strlen(g_pszImageName)) == 0)
     
    553586     * represents.
    554587     */
    555 
    556     if (g_vDiskOffset == 0 && (g_vDiskSize == 0 || g_vDiskSize == g_cbEntireVDisk))
    557         pfnFiller(pvBuf, g_pszImageName, NULL, 0);
    558     else
    559     {
    560         char tmp[BASENAME_MAX];
    561         RTStrPrintf(tmp, sizeof (tmp), "%s[%jd:%jd]", g_pszImageName, g_vDiskOffset, g_vDiskSize);
    562         pfnFiller(pvBuf, tmp, NULL, 0);
    563     }
     588    pfnFiller(pvBuf, g_pszImageName, NULL, 0);
     589
    564590    /*
    565      * Create entry named "vhdd", which getattr() will describe as a
     591     * Create entry named "vhdd" denoting the whole disk, which getattr() will describe as a
    566592     * regular file, and thus will go through the open/release/read/write vectors
    567593     * to access the VirtualBox image as processed by the IRPT VD API.
    568594     */
    569595    pfnFiller(pvBuf, "vhdd", NULL, 0);
     596
     597    /* Create entries for the individual volumes. */
     598    for (uint32_t i = 0; i < g_cVolumes; i++)
     599    {
     600        char tmp[64];
     601        RTStrPrintf(tmp, sizeof (tmp), "vol%u", i);
     602        pfnFiller(pvBuf, tmp, NULL, 0);
     603    }
    570604    return 0;
    571605}
     
    580614}
    581615
    582 uint8_t
    583 parsePartitionTable(void)
    584 {
    585     MBR_t mbr;
    586     EBR_t ebr;
    587     PTH_t parTblHdr;
    588 
    589     ASSERT(sizeof (mbr) == 512);
    590     ASSERT(sizeof (ebr) == 512);
     616
     617/**
     618 * Displays the list of volumes on the opened image.
     619 *
     620 * @returns nothing.
     621 */
     622static void vboxImgMntVolumesDisplay(void)
     623{
    591624    /*
    592      * First entry describes entire disk as a single entity
     625     * Partition table is most readable and concise when headers and columns
     626     * are adapted to the actual data, to avoid insufficient or excessive whitespace.
    593627     */
    594     g_aParsedPartitionInfo[0].idxPartition = 0;
    595     g_aParsedPartitionInfo[0].offPartition = 0;
    596     g_aParsedPartitionInfo[0].pszName = RTStrDup("EntireDisk");
    597 
    598     int rc = RTVfsFileGetSize(g_hVfsFileDisk, &g_aParsedPartitionInfo[0].cbPartition);
    599     if (RT_FAILURE(rc))
    600         return RTMsgErrorExitFailure("Error querying disk image size\n");
    601 
    602     /*
    603      * Currently only DOS partitioned disks are supported. Ensure this one conforms
    604      */
    605     rc = RTVfsFileReadAt(g_hVfsFileDisk, 0, &mbr, sizeof (mbr), NULL);
    606     if (RT_FAILURE(rc))
    607         return RTMsgErrorExitFailure("Error reading MBR block from disk\n");
    608 
    609     if (mbr.signature == NULL_BOOT_RECORD_SIGNATURE)
    610         return RTMsgErrorExitFailure("Unprt disk (null MBR signature)\n");
    611 
    612     if (mbr.signature != DOS_BOOT_RECORD_SIGNATURE)
    613         return RTMsgErrorExitFailure("Invalid MBR found on image with signature 0x%04hX\n",
    614             mbr.signature);
    615     /*
    616      * Parse the four physical partition entires in the MBR (any one, and only one, can be an EBR)
    617      */
    618     int idxEbrPartitionInMbr = 0;
    619     for (int idxPartition = 1;
    620              idxPartition <= MBR_PARTITIONS_MAX;
    621              idxPartition++)
    622     {
    623         MBRPARTITIONENTRY *pMbrPartitionEntry =
    624             &g_aParsedPartitionInfo[idxPartition].partitionEntry.mbrEntry;;
    625         memcpy (pMbrPartitionEntry, &mbr.partitionEntry[idxPartition - 1], sizeof (MBRPARTITIONENTRY));
    626 
    627         if (PARTTYPE_IS_NULL(pMbrPartitionEntry->type))
    628             continue;
    629 
    630         if (PARTTYPE_IS_EXT(pMbrPartitionEntry->type))
    631         {
    632             if (idxEbrPartitionInMbr)
    633                  return RTMsgErrorExitFailure("Multiple EBRs found found in MBR\n");
    634             idxEbrPartitionInMbr = idxPartition;
    635         }
    636 
    637         PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];
    638 
    639         ppi->idxPartition = idxPartition;
    640         ppi->offPartition = (off_t) pMbrPartitionEntry->partitionLba * g_cbSector;
    641         ppi->cbPartition  = (off_t) pMbrPartitionEntry->partitionBlkCnt * g_cbSector;
    642         ppi->fBootable    = pMbrPartitionEntry->bootIndicator == 0x80;
    643         ppi->partitionType.legacy = pMbrPartitionEntry->type;
    644 
    645         g_lastPartNbr = idxPartition;
    646 
    647         if (PARTTYPE_IS_GPT(pMbrPartitionEntry->type))
    648         {
    649             g_fGPT = true;
    650             break;
    651         }
    652     }
    653 
    654     if (g_fGPT)
    655     {
    656         g_lastPartNbr = 2;  /* from the 'protective MBR' */
    657 
    658         rc = RTVfsFileReadAt(g_hVfsFileDisk, LBA(1), &parTblHdr, sizeof (parTblHdr), NULL);
    659         if (RT_FAILURE(rc))
    660             return RTMsgErrorExitFailure("Error reading Partition Table Header (LBA 1) from disk\n");
    661 
    662         uint8_t *pTblBuf = (uint8_t *)RTMemAlloc(GPT_PTABLE_SIZE);
    663 
    664         if (!pTblBuf)
    665             return RTMsgErrorExitFailure("Out of memory\n");
    666 
    667         rc = RTVfsFileReadAt(g_hVfsFileDisk, LBA(2), pTblBuf, GPT_PTABLE_SIZE, NULL);
    668         if (RT_FAILURE(rc))
    669             return RTMsgErrorExitFailure("Error reading Partition Table blocks from disk\n");
    670 
    671         uint32_t cEntries = parTblHdr.cPartitionEntries;
    672         uint32_t cbEntry  = parTblHdr.cbPartitionEntry;
    673         if (cEntries * cbEntry > GPT_PTABLE_SIZE)
    674         {
    675             RTPrintf("Partition entries exceed GPT table read from disk (pruning!)\n");
    676             while (cEntries * cbEntry > GPT_PTABLE_SIZE && cEntries > 0)
    677                 --cEntries;
    678         }
    679         uint8_t *pEntryRaw = pTblBuf;
    680         for (uint32_t i = 0; i < cEntries; i++)
    681         {
    682             GPTPARTITIONENTRY *pEntry = (GPTPARTITIONENTRY *)pEntryRaw;
    683             PARTITIONINFO *ppi = &g_aParsedPartitionInfo[g_lastPartNbr];
    684             memcpy(&(ppi->partitionEntry).gptEntry, pEntry, sizeof(GPTPARTITIONENTRY));
    685             if (!pEntry->firstLba)
    686                 break;
    687             ppi->offPartition = pEntry->firstLba * g_cbSector;
    688             ppi->cbPartition = (pEntry->lastLba - pEntry->firstLba) * g_cbSector;
    689             ppi->fBootable = pEntry->attrFlags & (1 << GPT_LEGACY_BIOS_BOOTABLE);
    690             ppi->partitionType.gptGuidTypeSpecifier = pEntry->partitionTypeGuid;
    691             size_t cwName = sizeof (pEntry->partitionName) / 2;
    692             RTUtf16LittleToUtf8Ex((PRTUTF16)pEntry->partitionName, RTSTR_MAX, &ppi->pszName, cwName, NULL);
    693             ppi->idxPartition = g_lastPartNbr++;
    694             pEntryRaw += cbEntry;
    695         }
    696         return PARTITION_TABLE_GPT;
    697     }
    698 
    699     /*
    700      * Starting with EBR located in MBR, walk EBR chain to parse the logical partition entries
    701      */
    702     if (idxEbrPartitionInMbr)
    703     {
    704         uint32_t firstEbrLba
    705             = g_aParsedPartitionInfo[idxEbrPartitionInMbr].partitionEntry.mbrEntry.partitionLba;
    706         off_t    firstEbrOffset   = (off_t)firstEbrLba * g_cbSector;
    707         off_t    chainedEbrOffset = 0;
    708 
    709         if (!firstEbrLba)
    710             return RTMsgErrorExitFailure("Inconsistency for logical partition start. Aborting\n");
    711 
    712         for (int idxPartition = 5;
    713                  idxPartition <= VBOXIMG_PARTITION_MAX;
    714                  idxPartition++)
    715         {
    716 
    717             off_t currentEbrOffset = firstEbrOffset + chainedEbrOffset;
    718             RTVfsFileReadAt(g_hVfsFileDisk, currentEbrOffset, &ebr, sizeof (ebr), NULL);
    719 
    720             if (ebr.signature != DOS_BOOT_RECORD_SIGNATURE)
    721                 return RTMsgErrorExitFailure("Invalid EBR found on image with signature 0x%04hX\n",
    722                     ebr.signature);
    723 
    724             MBRPARTITIONENTRY *pEbrPartitionEntry =
    725                 &g_aParsedPartitionInfo[idxPartition].partitionEntry.mbrEntry; /* EBR entry struct same as MBR */
    726             memcpy(pEbrPartitionEntry, &ebr.partitionEntry, sizeof (MBRPARTITIONENTRY));
    727 
    728             if (pEbrPartitionEntry->type == NULL_BOOT_RECORD_SIGNATURE)
    729                 return RTMsgErrorExitFailure("Logical partition with type 0 encountered");
    730 
    731             if (!pEbrPartitionEntry->partitionLba)
    732                 return RTMsgErrorExitFailure("Logical partition invalid partition start offset (LBA) encountered");
    733 
    734             PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];
    735             ppi->idxPartition         = idxPartition;
    736             ppi->offPartition         = currentEbrOffset + (off_t)pEbrPartitionEntry->partitionLba * g_cbSector;
    737             ppi->cbPartition          = (off_t)pEbrPartitionEntry->partitionBlkCnt * g_cbSector;
    738             ppi->fBootable            = pEbrPartitionEntry->bootIndicator == 0x80;
    739             ppi->partitionType.legacy = pEbrPartitionEntry->type;
    740 
    741             g_lastPartNbr = idxPartition;
    742 
    743             if (ebr.chainingPartitionEntry.type == 0) /* end of chain */
    744                 break;
    745 
    746             if (!PARTTYPE_IS_EXT(ebr.chainingPartitionEntry.type))
    747                 return RTMsgErrorExitFailure("Logical partition chain broken");
    748 
    749             chainedEbrOffset = ebr.chainingPartitionEntry.partitionLba * g_cbSector;
    750         }
    751     }
    752     return PARTITION_TABLE_MBR;
    753 }
    754 
    755 const char *getClassicPartitionDesc(uint8_t type)
    756 {
    757     for (uint32_t i = 0; i < sizeof (g_partitionDescTable) / sizeof (struct PartitionDesc); i++)
    758     {
    759         if (g_partitionDescTable[i].type == type)
    760             return g_partitionDescTable[i].desc;
    761     }
    762     return "????";
    763 }
    764 
    765 void
    766 displayGptPartitionTable(void)
    767 {
    768628
    769629    RTPrintf( "Virtual disk image:\n\n");
     
    774634            RTPrintf("   UUID: %s\n\n", g_pszDiskUuid);
    775635
    776     void *colBoot = NULL;
    777 
    778     SELFSIZINGTABLE tbl(2);
    779 
    780     /* Note: Omitting partition name column because type/UUID seems suffcient */
    781     void *colPartNbr   = tbl.addCol("#",                 "%3d",       1);
    782 
    783     /* If none of the partitions supports legacy BIOS boot, don't show that column */
    784     for (int idxPartition = 2; idxPartition <= g_lastPartNbr; idxPartition++)
    785         if (g_aParsedPartitionInfo[idxPartition].fBootable) {
    786             colBoot    = tbl.addCol("Boot",         "%c   ",     1);
    787             break;
    788         }
    789 
    790     void *colStart     = tbl.addCol("Start",             "%lld",      1);
    791     void *colSectors   = tbl.addCol("Sectors",           "%lld",     -1, 2);
    792     void *colSize      = tbl.addCol("Size",              "%s",        1);
    793     void *colOffset    = tbl.addCol("Offset",            "%lld",      1);
    794     void *colType      = tbl.addCol("Type",              "%s",       -1, 2);
    795 
    796 #if 0 /* need to see how other OSes w/GPT use 'Name' field, right now 'Type' seems to suffice */
    797     void *colName      = tbl.addCol("Name",              "%s",       -1); */
    798 #endif
    799 
    800     for (int idxPartition = 2; idxPartition <= g_lastPartNbr; idxPartition++)
    801     {
    802         PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];
    803         if (ppi->idxPartition)
    804         {
    805             char abGuid[GUID_STRING_LENGTH * 2];
    806             RTStrPrintf(abGuid, sizeof(abGuid), "%RTuuid",  &ppi->partitionType.gptGuidTypeSpecifier);
    807 
    808             char *pszPartitionTypeDesc = NULL;
    809             for (uint32_t i = 0; i < sizeof(g_gptPartitionTypes) / sizeof(GPTPARTITIONTYPE); i++)
    810                 if (RTStrNICmp(abGuid, g_gptPartitionTypes[i].gptPartitionUuid, GUID_STRING_LENGTH) == 0)
    811                 {
    812                     pszPartitionTypeDesc =  (char *)g_gptPartitionTypes[i].gptPartitionTypeDesc;
    813                     break;
    814                 }
    815 
    816             if (!pszPartitionTypeDesc)
    817                 RTPrintf("Couldn't find GPT partitiontype for GUID: %s\n", abGuid);
    818 
    819             void *row = tbl.addRow();
    820             tbl.setCell(row, colPartNbr,    idxPartition - 1);
    821             if (colBoot)
    822                 tbl.setCell(row, colBoot,   ppi->fBootable ? '*' : ' ');
    823             tbl.setCell(row, colStart,      ppi->offPartition / g_cbSector);
    824             tbl.setCell(row, colSectors,    ppi->cbPartition / g_cbSector);
    825             tbl.setCell(row, colSize,       vboximgScaledSize(ppi->cbPartition));
    826             tbl.setCell(row, colOffset,     ppi->offPartition);
    827             tbl.setCell(row, colType,       SAFENULL(pszPartitionTypeDesc));
    828 
    829 #if 0 /* see comment for stubbed-out 'Name' column definition above */
    830             tbl.setCell(row, colName,       ppi->pszName);
    831 #endif
    832 
    833         }
    834     }
    835     tbl.displayTable();
    836     RTPrintf ("\n");
    837 }
    838 
    839 void
    840 displayLegacyPartitionTable(void)
    841 {
    842     /*
    843      * Partition table is most readable and concise when headers and columns
    844      * are adapted to the actual data, to avoid insufficient or excessive whitespace.
    845      */
    846 
    847     RTPrintf( "Virtual disk image:\n\n");
    848     RTPrintf("   Base: %s\n", g_pszBaseImagePath);
    849     if (g_cImages > 1)
    850         RTPrintf("   Diff: %s\n", g_pszImagePath);
    851     if (g_pszDiskUuid)
    852             RTPrintf("   UUID: %s\n\n", g_pszDiskUuid);
    853 
    854636    SELFSIZINGTABLE tbl(2);
    855637
     
    860642    void *colSize      = tbl.addCol("Size",         "%s",        1);
    861643    void *colOffset    = tbl.addCol("Offset",       "%lld",      1);
    862     void *colId        = tbl.addCol("Id",           "%2x",       1);
    863644    void *colType      = tbl.addCol("Type",         "%s",       -1, 2);
    864645
    865     for (int idxPartition = 1; idxPartition <= g_lastPartNbr; idxPartition++)
    866     {
    867         PARTITIONINFO *p = &g_aParsedPartitionInfo[idxPartition];
    868         if (p->idxPartition)
     646    for (uint32_t i = 0; i < g_cVolumes; i++)
     647    {
     648        PVBOXIMGMOUNTVOL pVol = &g_paVolumes[i];
     649        uint64_t fVolFlags = RTDvmVolumeGetFlags(pVol->hVol);
     650        uint64_t cbVol = RTDvmVolumeGetSize(pVol->hVol);
     651        RTDVMVOLTYPE enmType = RTDvmVolumeGetType(pVol->hVol);
     652        uint64_t offStart = 0;
     653        uint64_t offEnd = 0;
     654
     655        if (fVolFlags & DVMVOLUME_F_CONTIGUOUS)
    869656        {
    870             void *row = tbl.addRow();
    871             tbl.setCell(row, colPartition,  g_pszBaseImageName, idxPartition);
    872             tbl.setCell(row, colBoot,       p->fBootable ? '*' : ' ');
    873             tbl.setCell(row, colStart,      p->offPartition / g_cbSector);
    874             tbl.setCell(row, colSectors,    p->cbPartition / g_cbSector);
    875             tbl.setCell(row, colSize,       vboximgScaledSize(p->cbPartition));
    876             tbl.setCell(row, colOffset,     p->offPartition);
    877             tbl.setCell(row, colId,         p->partitionType.legacy);
    878             tbl.setCell(row, colType,       getClassicPartitionDesc((p->partitionType).legacy));
     657            int rc = RTDvmVolumeQueryRange(pVol->hVol, &offStart, &offEnd);
     658            AssertRC(rc);
    879659        }
     660
     661        void *row = tbl.addRow();
     662        tbl.setCell(row, colPartition,  g_pszBaseImageName, i);
     663        tbl.setCell(row, colBoot,       (fVolFlags & DVMVOLUME_FLAGS_BOOTABLE) ? '*' : ' ');
     664        tbl.setCell(row, colStart,      offStart / g_cbSector);
     665        tbl.setCell(row, colSectors,    cbVol / g_cbSector);
     666        tbl.setCell(row, colSize,       vboximgScaledSize(cbVol));
     667        tbl.setCell(row, colOffset,     offStart);
     668        tbl.setCell(row, colType,       RTDvmVolumeTypeGetDescr(enmType));
    880669    }
    881670    tbl.displayTable();
    882671    RTPrintf ("\n");
     672}
     673
     674
     675/**
     676 * Sets up the volumes for the disk.
     677 *
     678 * @returns IPRT status code.
     679 */
     680static int vboxImgMntVolumesSetup(void)
     681{
     682    g_cVolumes = 0;
     683    g_paVolumes = NULL;
     684
     685    int rc = RTDvmCreate(&g_hDvmMgr, g_hVfsFileDisk, g_cbSector, 0 /*fFlags*/);
     686    if (RT_SUCCESS(rc))
     687    {
     688        rc = RTDvmMapOpen(g_hDvmMgr);
     689        if (RT_SUCCESS(rc))
     690        {
     691            g_cVolumes = RTDvmMapGetValidVolumes(g_hDvmMgr);
     692            if (   g_cVolumes != UINT32_MAX
     693                && g_cVolumes > 0)
     694            {
     695                g_paVolumes = (PVBOXIMGMOUNTVOL)RTMemAllocZ(g_cVolumes * sizeof(VBOXIMGMOUNTVOL));
     696                if (RT_LIKELY(g_paVolumes))
     697                {
     698                    rc = RTDvmMapQueryFirstVolume(g_hDvmMgr, &g_paVolumes[0].hVol);
     699                    if (RT_SUCCESS(rc))
     700                        rc = RTDvmVolumeCreateVfsFile(g_paVolumes[0].hVol,
     701                                                      RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE,
     702                                                      &g_paVolumes[0].hVfsFileVol);
     703
     704                    for (uint32_t i = 1; i < g_cVolumes && RT_SUCCESS(rc); i++)
     705                    {
     706                        rc = RTDvmMapQueryNextVolume(g_hDvmMgr, g_paVolumes[i-1].hVol, &g_paVolumes[i].hVol);
     707                        if (RT_SUCCESS(rc))
     708                            rc = RTDvmVolumeCreateVfsFile(g_paVolumes[i].hVol,
     709                                                          RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE,
     710                                                          &g_paVolumes[i].hVfsFileVol);
     711                    }
     712
     713                    if (RT_SUCCESS(rc))
     714                        return VINF_SUCCESS;
     715
     716                    RTMemFree(g_paVolumes);
     717                    g_paVolumes = NULL;
     718                    g_cVolumes  = 0;
     719                }
     720                else
     721                    rc = VERR_NO_MEMORY;
     722            }
     723            else if (g_cVolumes == UINT32_MAX)
     724            {
     725                g_cVolumes = 0;
     726                rc = VERR_INTERNAL_ERROR;
     727            }
     728
     729            RTDvmRelease(g_hDvmMgr);
     730        }
     731        else if (rc == VERR_NOT_FOUND)
     732            rc = VINF_SUCCESS;
     733    }
     734
     735    return rc;
    883736}
    884737
     
    12471100        return RTMsgErrorExitFailure("Error creating VFS file wrapper for disk image\n");
    12481101
    1249     g_cReaders  = VDIsReadOnly(pVDisk) ? INT32_MAX / 2 : 0;
    1250     g_cWriters  = 0;
    1251     rc = RTVfsFileGetSize(g_hVfsFileDisk, (uint64_t *)&g_cbEntireVDisk);
     1102    g_cbSector = VDGetSectorSize(pVDisk, VD_LAST_IMAGE);
     1103
     1104    rc = vboxImgMntVolumesSetup();
    12521105    if (RT_FAILURE(rc))
    1253         return RTMsgErrorExitFailure("Error querying disk image size from VFS wrapper\n");
    1254 
    1255     g_cbSector = VDGetSectorSize(pVDisk, VD_LAST_IMAGE);
     1106        return RTMsgErrorExitFailure("Error parsing volumes on disk\n");
    12561107
    12571108    if (g_vboximgOpts.fList)
     
    12611112
    12621113        RTPrintf("\n");
    1263         rc = parsePartitionTable();
    1264         switch(rc)
    1265         {
    1266             case PARTITION_TABLE_MBR:
    1267                 displayLegacyPartitionTable();
    1268                 break;
    1269             case PARTITION_TABLE_GPT:
    1270                 displayGptPartitionTable();
    1271                 break;
    1272             default:
    1273                 return rc;
    1274         }
     1114        vboxImgMntVolumesDisplay();
    12751115        return 0;
    12761116    }
    1277 
    1278     if (g_vboximgOpts.idxPartition >= 0)
    1279     {
    1280         if (g_vboximgOpts.offset)
    1281             return RTMsgErrorExitFailure("--offset and --partition are mutually exclusive options\n");
    1282 
    1283         if (g_vboximgOpts.size)
    1284             return RTMsgErrorExitFailure("--size and --partition are mutually exclusive options\n");
    1285 
    1286         /*
    1287          * --partition option specified. That will set the global offset and limit
    1288          * honored by the disk read and write sanitizers to constrain operations
    1289          * to within the specified partion based on an initial parsing of the MBR
    1290          */
    1291         rc = parsePartitionTable();
    1292         if (rc < 0)
    1293             return RTMsgErrorExitFailure("Error parsing disk MBR/Partition table\n");
    1294         int partNbr = g_vboximgOpts.idxPartition;
    1295 
    1296         if (partNbr < 0 || partNbr > g_lastPartNbr)
    1297             return RTMsgErrorExitFailure("Non-valid partition number specified\n");
    1298         if (partNbr == 0)
    1299         {
    1300             g_vDiskOffset = 0;
    1301             g_vDiskSize = g_cbEntireVDisk;
    1302             if (VERBOSE)
    1303                 RTPrintf("\nPartition 0 specified - Whole disk will be accessible\n");
    1304         } else {
    1305             int fFoundPartition = false;
    1306             for (int i = 1; i < g_lastPartNbr + 1; i++)
    1307             {
    1308                 /* If GPT, display vboximg's representation of partition table starts at partition 2
    1309                  * but the table is displayed calling it partition 1, because the protective MBR
    1310                  * record is relatively pointless to display or reference in this context */
    1311                 if (g_aParsedPartitionInfo[i].idxPartition == partNbr + (g_fGPT ? 1 : 0))
    1312                 {
    1313                      fFoundPartition = true;
    1314                      g_vDiskOffset = g_aParsedPartitionInfo[i].offPartition;
    1315                      g_vDiskSize = g_vDiskOffset + g_aParsedPartitionInfo[i].cbPartition;
    1316                      if (VERBOSE)
    1317                         RTPrintf("\nPartition %d specified. Only sectors %llu to %llu of disk will be accessible\n",
    1318                             g_vboximgOpts.idxPartition, g_vDiskOffset / g_cbSector, g_vDiskSize / g_cbSector);
    1319                 }
    1320             }
    1321             if (!fFoundPartition)
    1322                 return RTMsgErrorExitFailure("Couldn't find partition %d in partition table\n", partNbr);
    1323         }
    1324     } else {
    1325         if (g_vboximgOpts.offset) {
    1326             if (g_vboximgOpts.offset < 0 || g_vboximgOpts.offset + g_vboximgOpts.size > g_cbEntireVDisk)
    1327                 return RTMsgErrorExitFailure("User specified offset out of range of virtual disk\n");
    1328 
    1329             if (VERBOSE)
    1330                 RTPrintf("Setting r/w bias (offset) to user requested value for sector %llu\n", g_vDiskOffset / g_cbSector);
    1331 
    1332             g_vDiskOffset = g_vboximgOpts.offset;
    1333         }
    1334         if (g_vboximgOpts.size) {
    1335             if (g_vboximgOpts.size < 0 || g_vboximgOpts.offset + g_vboximgOpts.size > g_cbEntireVDisk)
    1336                 return RTMsgErrorExitFailure("User specified size out of range of virtual disk\n");
    1337 
    1338             if (VERBOSE)
    1339                 RTPrintf("Setting r/w size limit to user requested value %llu\n", g_vDiskSize / g_cbSector);
    1340 
    1341             g_vDiskSize = g_vboximgOpts.size;
    1342         }
    1343     }
    1344     if (g_vDiskSize == 0)
    1345         g_vDiskSize = g_cbEntireVDisk - g_vDiskOffset;
    13461117
    13471118    /*
Note: See TracChangeset for help on using the changeset viewer.

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