VirtualBox

Ignore:
Timestamp:
Nov 29, 2018 2:03:50 PM (6 years ago)
Author:
vboxsync
Message:

Improved cmd line options, and vm/media listing. Added partition mounting at FUSE level, Added support for MBR/EBR and GPT partition tables (ability to parse them and display summary information), and added self-sizing table class to neatly display partition data. (See #3641 for more information).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ImageMounter/vboxraw/vboxraw.cpp

    r75476 r75815  
    55
    66/*
    7  * Copyright (C) 2009-2017 Oracle Corporation
     7 * Copyright (C) 2009-2018 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2020*   Header Files                                                                                                                 *
    2121*********************************************************************************************************************************/
     22
     23/*** PKK TEMPORARY FOR DEBUGGING DON'T PUT INTO PRODUCTION CODE */
     24#include <stdio.h>
     25/*** END OF ADMONITION */
     26
     27
    2228#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo log group */
    2329
     
    2733#endif
    2834#define MAX_READERS (INT32_MAX / 32)
    29 
    3035#include <fuse.h>
    3136#ifdef UNIX_DERIVATIVE
     
    3540#include <libgen.h>
    3641#include <unistd.h>
     42#include <math.h>
     43//#include <stdarg.h>
     44#include <cstdarg>
    3745#include <sys/stat.h>
    3846#endif
     
    7078#include <iprt/types.h>
    7179#include <iprt/path.h>
    72 
     80#include <iprt/utf16.h>
     81
     82#include "vboxraw.h"
     83#include "SelfSizingTable.h"
     84
     85/*
     86 * PKK: REMOVE AFTER DEBUGGING
     87 */
    7388#pragma GCC diagnostic ignored "-Wunused-variable"
    7489#pragma GCC diagnostic ignored "-Wunused-parameter"
     
    88103} g_u;
    89104
    90 
    91 #define VD_SECTOR_SIZE                   0x200 /* 0t512 */
    92 #define VD_SECTOR_MASK                   (VD_SECTOR_SIZE - 1)
    93 #define VD_SECTOR_OUT_OF_BOUNDS_MASK     (~UINT64_C(VD_SECTOR_MASK))
    94 
    95 #define PADMAX                           50
    96 #define MAX_ID_LEN                       256
    97 #define CSTR(arg)                        Utf8Str(arg).c_str()
    98 
    99 static struct fuse_operations   g_vboxrawOps;
    100 PVDISK                g_pVDisk;
    101 int32_t               g_cReaders;
    102 int32_t               g_cWriters;
    103 RTFOFF                g_cbPrimary;
    104 char                 *g_pszBaseImageName;
    105 char                 *g_pszBaseImagePath;
    106 PVDINTERFACE          g_pVdIfs;             /** @todo Remove when VD I/O becomes threadsafe */
    107 VDINTERFACETHREADSYNC g_VDIfThreadSync;     /** @todo Remove when VD I/O becomes threadsafe */
    108 RTCRITSECT            g_vdioLock;           /** @todo Remove when VD I/O becomes threadsafe */
    109 
    110 char *nsIDToString(nsID *guid);
    111 void printErrorInfo();
    112 /** XPCOM stuff */
     105const uint64_t KB = 1024;
     106const uint64_t MB = KB * KB;
     107const uint64_t GB = MB * KB;
     108const uint64_t TB = GB * KB;
     109const uint64_t PB = TB * KB;
     110
     111
     112
     113enum { PARTITION_TABLE_MBR = 1, PARTITION_TABLE_GPT = 2 };
     114
     115#define GPT_PTABLE_SIZE             32 * BLOCKSIZE   /** Max size we to read for GPT partition table */
     116#define MBR_PARTITIONS_MAX          4                /** Fixed number of partitions in Master Boot Record */
     117#define BASENAME_MAX                256              /** Maximum name for the basename of a path (for RTStrNLen()*/
     118#define VBOXRAW_PARTITION_MAX       256              /** How much storage to allocate to store partition info */
     119#define PARTITION_NAME_MAX          72               /** Maximum partition name size (accomodates GPT partition name) */
     120#define BLOCKSIZE                   512              /** Commonly used disk block size */
     121#define DOS_BOOT_RECORD_SIGNATURE   0xaa55           /** MBR and EBR (partition table) signature [EOT boundary] */
     122#define NULL_BOOT_RECORD_SIGNATURE  0x0000           /** MBR or EBR null signature value */
     123#define MAX_UUID_LEN                256              /** Max length of a UUID */
     124#define LBA(n)                      (n * BLOCKSIZE)
     125#define VD_SECTOR_SIZE              512              /** Virtual disk sector/blocksize */
     126#define VD_SECTOR_MASK              (VD_SECTOR_SIZE - 1)    /** Masks off a blocks worth of data */
     127#define VD_SECTOR_OUT_OF_BOUNDS_MASK  (~UINT64_C(VD_SECTOR_MASK))         /** Masks the overflow of a blocks worth of data */
     128
     129#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)
     130
     131#define PARTTYPE_IS_NULL(parType) ((uint8_t)parType == 0x00)
     132#define PARTTYPE_IS_GPT(parType)  ((uint8_t)parType == 0xee)
     133#define PARTTYPE_IS_EXT(parType)  ((    (uint8_t)parType) == 0x05  /* Extended           */ \
     134                                    || ((uint8_t)parType) == 0x0f  /* W95 Extended (LBA) */ \
     135                                    || ((uint8_t)parType) == 0x85) /* Linux Extended     */
     136
     137#define SAFENULL(strPtr)   (strPtr ? strPtr : "")
     138
     139#define CSTR(arg)     Utf8Str(arg).c_str()          /* Converts XPCOM string type to C string type */
     140
     141
     142static struct fuse_operations g_vboxrawOps;         /** FUSE structure that defines allowed ops for this FS */
     143
     144/* Global variables */
     145
     146static PVDISK                g_pVDisk;              /** Handle for Virtual Disk in contet */
     147static char                 *g_pvDiskUuid;          /** UUID of image (if known, otherwise NULL) */
     148static off_t                 g_vDiskOffset;         /** Biases r/w from start of VD */
     149static off_t                 g_vDiskSize;           /** Limits r/w length for VD */
     150static int32_t               g_cReaders;            /** Number of readers for VD */
     151static int32_t               g_cWriters;            /** Number of writers for VD */
     152static RTFOFF                g_cbEntireVDisk;       /** Size of VD */
     153static char                 *g_pszBaseImageName;    /** Base filename for current VD image */
     154static char                 *g_pszBaseImagePath;    /** Full path to current VD image */
     155static PVDINTERFACE          g_pVdIfs;              /** @todo Remove when VD I/O becomes threadsafe */
     156static VDINTERFACETHREADSYNC g_VDIfThreadSync;      /** @todo Remove when VD I/O becomes threadsafe */
     157static RTCRITSECT            g_vdioLock;            /** @todo Remove when VD I/O becomes threadsafe */
     158static uint16_t              g_lastPartNbr;         /** Last partition number found in MBR + EBR chain */
     159static bool                  g_fGPT;                /** True if GPT type partition table was found */
     160
     161/* Table entry containing partition info parsed out of GPT or MBR and EBR chain of specified VD */
     162
     163typedef struct
     164{
     165    int            idxPartition;            /** partition number */
     166    char           *pszName;
     167    off_t          offPartition;        /** partition offset from start of disk, in bytes */
     168    uint64_t       cbPartition;           /** partition size in bytes */
     169    uint8_t        fBootable;               /** partition bootable */
     170    union
     171    {
     172        uint8_t    legacy;                  /** partition type MBR/EBR */
     173        uint128_t  gptGuidTypeSpecifier;    /** partition type GPT */
     174    } partitionType;                        /** uint8_t for MBR/EBR (legacy) and GUID for GPT */
     175
     176    union
     177    {
     178        MBRPARTITIONENTRY mbrEntry;          /** MBR (also EBR partition entry) */
     179        GPTPARTITIONENTRY gptEntry;          /** GPT partition entry */
     180    } partitionEntry;
     181} PARTITIONINFO;
     182
     183PARTITIONINFO g_aParsedPartitionInfo[VBOXRAW_PARTITION_MAX + 1]; /* Note: Element 0 reserved for EntireDisk partitionEntry */
    113184
    114185static struct vboxrawOpts {
    115      char    *pszVm;
    116      char    *pszImage;
    117      char    *pszImageUuid;
    118      uint32_t cHddImageDiffMax;
    119      uint32_t fList;
    120      uint32_t fAllowRoot;
    121      uint32_t fRW;
    122      uint32_t fVerbose;
     186     char         *pszVm;                   /** optional VM UUID */
     187     char         *pszImage;                /** Virtual Disk image UUID or path */
     188     int32_t       idxPartition;            /** Number of partition to constrain FUSE based FS to (optional) 0 - whole disk*/
     189     int32_t       offset;                  /** Offset to base virtual disk reads and writes from (altnerative to partition) */
     190     int32_t       size;                    /** Size of accessible disk region, starting at offset, default = offset 0 */
     191     uint32_t      cHddImageDiffMax;        /** Max number of differencing images (snapshots) to apply to image */
     192     uint32_t      fListMedia;              /** Flag to list virtual disks of all known VMs */
     193     uint32_t      fListMediaBrief;         /** Flag to list virtual disks of all known VMs */
     194     uint32_t      fListParts;              /** Flag to summarily list partitions associated with pszImage */
     195     uint32_t      fAllowRoot;              /** Flag to allow root to access this FUSE FS */
     196     uint32_t      fRW;                     /** Flag to allow changes to FUSE-mounted Virtual Disk image */
     197     uint32_t      fBriefUsage;             /** Flag to display only FS-specific program usage options */
     198     uint32_t      fVerbose;                /** Make some noise */
    123199} g_vboxrawOpts;
    124200
     
    126202
    127203static struct fuse_opt vboxrawOptDefs[] = {
    128     OPTION("--list",          fList,            1),
    129     OPTION("--root",          fAllowRoot,       1),
    130     OPTION("--vm=%s",         pszVm,            0),
    131     OPTION("--maxdiff=%d",    cHddImageDiffMax, 1),
    132     OPTION("--diff=%d",       cHddImageDiffMax, 1),
    133     OPTION("--image=%s",      pszImage,         0),
    134     OPTION("--rw",            fRW,              1),
    135     OPTION("--verbose",       fVerbose,         1),
    136     FUSE_OPT_KEY("-h",        USAGE_FLAG),
     204    OPTION("-l",              fListMediaBrief,   1),
     205    OPTION("-L",              fListMedia,        1),
     206    OPTION("-t",              fListParts,        1),
     207    OPTION("--root",          fAllowRoot,        1),
     208    OPTION("--vm=%s",         pszVm,             0),
     209    OPTION("--maxdiff=%d",    cHddImageDiffMax,  1),
     210    OPTION("--diff=%d",       cHddImageDiffMax,  1),
     211    OPTION("--partition=%d",  idxPartition,      1),
     212    OPTION("-p %d",           idxPartition,      1),
     213    OPTION("--offset=%d",     offset,            1),
     214    OPTION("-o %d",           offset,            1),
     215    OPTION("--size=%d",       size,              1),
     216    OPTION("-s %d",           size,              1),
     217    OPTION("--image=%s",      pszImage,          0),
     218    OPTION("-i %s",           pszImage,          0),
     219    OPTION("--rw",            fRW,               1),
     220    OPTION("--verbose",       fVerbose,          1),
     221    OPTION("-v",              fVerbose,          1),
     222    OPTION("-h",              fBriefUsage,       1),
    137223    FUSE_OPT_KEY("--help",    USAGE_FLAG),
    138224    FUSE_OPT_END
    139225};
    140226
    141 static int vboxrawOptHandler(void *data, const char *arg, int optKey, struct fuse_args *outargs)
     227static void
     228briefUsage()
     229{
     230    RTPrintf("usage: vboxraw [options] <mountpoint>\n\n"
     231        "vboxraw options:\n\n"
     232        "    [ -l ]                                     List virtual disk media (brief version)\n"
     233        "    [ -L ]                                     List virtual disk media (long version)\n"
     234        "    [ -t ]                                     List partition table (requires -i or --image option)\n"
     235        "\n"
     236        "    [ { -i | --image= } <UUID | name | path> ] Virtual Box disk image to use\n"
     237        "\n"
     238        "    [ { -p | --partition= } <partition #> ]    Mount specified partition number via FUSE\n"
     239        "\n"
     240        "    [ { -o | --offset= } <byte #> ]            Disk I/O will be based on offset from disk start\n"
     241        "                                               (Can't use with -p or --partition options)\n"
     242        "\n"
     243        "    [ -s | --size=<bytes>]                     Sets size of mounted disk from disk start or from\n"
     244        "                                               offset, if specified. (Can't use with\n"
     245        "                                               -p or --partition options)\n"
     246        "\n"
     247        "    [ --diff=<diff #> ]                        Apply diffs (snapshot differencing disk images)\n"
     248        "                                               to specified base disk image up to and including\n"
     249        "                                               specified diff number.\n"
     250        "                                               (0 = Apply no diffs, default = Apply all diffs)\n"
     251        "\n"
     252        "    [ --rw]                                    Make image writeable (default = readonly)\n"
     253        "    [ --root]                                  Same as -o allow_root\n"
     254        "\n"
     255        "    [ --vm < Path | UUID >]                    VM UUID (limit media list to specific VM)\n"
     256        "\n"
     257        "    [ --verbose]                               Log extra information\n"
     258        "    -o opt[,opt...]                            FUSE mount options\n"
     259        "    -h                                         Display short usage info showing only the above\n"
     260        "    --help                                     Display long usage info (including FUSE opts)\n\n"
     261    );
     262    RTPrintf("\n");
     263    RTPrintf("When successful, the --image option creates a one-directory-deep filesystem \n");
     264    RTPrintf("rooted at the specified mountpoint.  The contents of the directory will be \n");
     265    RTPrintf("a symbolic link with the base name of the image file pointing to the path of\n");
     266    RTPrintf("the virtual disk image, and a regular file named 'vhdd', which represents\n");
     267    RTPrintf("the byte stream of the disk image as interpreted by VirtualBox.\n");
     268    RTPrintf("It is the vhdd file that the user or a utility will subsequently mount on\n");
     269    RTPrintf("the host OS to gain access to the virtual disk contents.\n\n");
     270    RTPrintf("If any of the partition, size or offset related options are used the\n");
     271    RTPrintf("The constraining start offset (in bytes) and size (in bytes) will be\n");
     272    RTPrintf("appended in brackets to the symbolic link basename to indicate\n");
     273    RTPrintf("what part of the image is exposed by the FUSE filesystem implementation.\n\n");
     274}
     275
     276static int
     277vboxrawOptHandler(void *data, const char *arg, int optKey, struct fuse_args *outargs)
    142278{
    143279    (void) data;
    144280    (void) arg;
    145 
    146     char *progname = basename(outargs->argv[0]);
    147281    switch(optKey)
    148282    {
    149283        case USAGE_FLAG:
    150             RTPrintf("usage: %s [options] <mountpoint>\n\n"
    151                 "%s options:\n"
    152                 "    [--list]                              List media\n"
    153                 "    [--root]                              Same as -o allow_root\n"
    154                 "    [--rw]                                writeable (default = readonly)\n"
    155                 "    [--vm <name | UUID >]                 vm UUID (limit media list to specific VM)\n\n"
    156                 "    [--diff=<diff #> ]                    Apply diffs to base image up "
    157                                                           "to and including specified diff #\n"
    158                 "                                          (0 = no diffs, default: all diffs)\n\n"
    159                 "    [--image=<UUID, name or path>]        Image UUID or path\n"
    160                 "    [--verbose]                           Log extra information\n"
    161                 "    -o opt[,opt...]                       mount options\n"
    162                 "    -h --help                             print usage\n\n",
    163                     progname, progname);
    164             RTPrintf("\n");
    165             RTPrintf("When successful, the --image option creates a one-directory-deep filesystem \n");
    166             RTPrintf("rooted at the specified mountpoint.  The contents of the directory will be \n");
    167             RTPrintf("a symbolic link with the base name of the image file pointing to the path of\n");
    168             RTPrintf("the virtual disk image, and a regular file named 'vhdd', which represents\n");
    169             RTPrintf("the byte stream of the disk image as interpreted by VirtualBox.\n");
    170             RTPrintf("It is the vhdd file that the user or a utility will subsequently mount on\n");
    171             RTPrintf("the host OS to gain access to the virtual disk contents.\n\n");
     284            briefUsage();
    172285            fuse_opt_add_arg(outargs, "-ho");
    173286            fuse_main(outargs->argc, outargs->argv, &g_vboxrawOps, NULL);
    174             exit(1);
     287            break;
    175288    }
    176289    return 1;
     
    207320        rc = -EINVAL;
    208321#   ifdef O_DIRECTORY
    209         if (pInfo->flags & O_DIRECTORY)
    210             rc = -ENOTDIR;
     322    if (pInfo->flags & O_DIRECTORY)
     323        rc = -ENOTDIR;
    211324#   endif
    212325#endif
     
    263376    return RTCritSectLeave(vdioLock);
    264377}
    265 
    266378/** @todo (end of to do section) */
    267379
     
    290402    return 0;
    291403}
    292 
    293404
    294405/**
    295406 * VD read Sanitizer taking care of unaligned accesses.
    296407 *
    297  * @return  VBox status code.
     408 * @return  VBox bootIndicator code.
    298409 * @param   pDisk    VD disk container.
    299410 * @param   off      Offset to start reading from.
     
    305416    int rc;
    306417
    307     uint64_t const cbMisalignHead = off & VD_SECTOR_MASK;
    308     uint64_t const cbMisalignTail  = (off + cbRead) & VD_SECTOR_MASK;
    309 
    310     if (cbMisalignHead + cbMisalignTail == 0) /* perfectly aligned request; just read it and done */
     418    uint64_t const cbMisalignmentOfStart = off & VD_SECTOR_MASK;
     419    uint64_t const cbMisalignmentOfEnd  = (off + cbRead) & VD_SECTOR_MASK;
     420
     421    if (cbMisalignmentOfStart + cbMisalignmentOfEnd == 0) /* perfectly aligned request; just read it and done */
    311422        rc = VDRead(pDisk, off, pvBuf, cbRead);
    312423    else
     
    318429         * bytes (requested by user), only up to sector boundary, into user's buffer
    319430         */
    320         if (cbMisalignHead)
    321         {
    322             rc = VDRead(pDisk, off - cbMisalignHead, abBuf, VD_SECTOR_SIZE);
     431        if (cbMisalignmentOfStart)
     432        {
     433            rc = VDRead(pDisk, off - cbMisalignmentOfStart, abBuf, VD_SECTOR_SIZE);
    323434            if (RT_SUCCESS(rc))
    324435            {
    325                 size_t const cbPart = RT_MIN(VD_SECTOR_SIZE - cbMisalignHead, cbRead);
    326                 memcpy(pbBuf, &abBuf[cbMisalignHead], cbPart);
    327                 pbBuf  += cbPart;
    328                 off    += cbPart; /* Beginning of next sector or EOD */
    329                 cbRead -= cbPart; /* # left to read */
     436                size_t const cbPartial = RT_MIN(VD_SECTOR_SIZE - cbMisalignmentOfStart, cbRead);
     437                memcpy(pbBuf, &abBuf[cbMisalignmentOfStart], cbPartial);
     438                pbBuf  += cbPartial;
     439                off    += cbPartial; /* Beginning of next sector or EOD */
     440                cbRead -= cbPartial; /* # left to read */
    330441            }
    331442        }
     
    338449            Assert(!(off % VD_SECTOR_SIZE));
    339450
    340             size_t cbPart = cbRead - cbMisalignTail;
    341             Assert(!(cbPart % VD_SECTOR_SIZE));
    342             rc = VDRead(pDisk, off, pbBuf, cbPart);
     451            size_t cbPartial = cbRead - cbMisalignmentOfEnd;
     452            Assert(!(cbPartial % VD_SECTOR_SIZE));
     453            rc = VDRead(pDisk, off, pbBuf, cbPartial);
    343454            if (RT_SUCCESS(rc))
    344455            {
    345                 pbBuf  += cbPart;
    346                 off    += cbPart;
    347                 cbRead -= cbPart;
     456                pbBuf  += cbPartial;
     457                off    += cbPartial;
     458                cbRead -= cbPartial;
    348459            }
    349460        }
     
    352463        if (RT_SUCCESS(rc) && cbRead)
    353464        {
    354             Assert(cbRead == cbMisalignTail);
     465            Assert(cbRead == cbMisalignmentOfEnd);
    355466            Assert(cbRead < VD_SECTOR_SIZE);
    356467            Assert(!(off % VD_SECTOR_SIZE));
     
    374485 * VD write Sanitizer taking care of unaligned accesses.
    375486 *
    376  * @return  VBox status code.
     487 * @return  VBox bootIndicator code.
    377488 * @param   pDisk    VD disk container.
    378489 * @param   off      Offset to start writing to.
     
    386497    int rc;
    387498    int cbRemaining = cbWrite;
     499
    388500    /*
    389501     * Take direct route if the request is sector aligned.
    390502     */
    391     uint64_t const cbMisalignHead = off & 511;
    392     size_t   const cbMisalignTail  = (off + cbWrite) & 511;
    393     if (!cbMisalignHead && !cbMisalignTail)
     503    uint64_t const cbMisalignmentOfStart = off & VD_SECTOR_MASK;
     504    size_t   const cbMisalignmentOfEnd  = (off + cbWrite) & VD_SECTOR_MASK;
     505    if (!cbMisalignmentOfStart && !cbMisalignmentOfEnd)
    394506    {
    395507          rc = VDWrite(pDisk, off, pbSrc, cbWrite);
     
    413525         * Unaligned buffered read+write of head.  Aligns the offset.
    414526         */
    415         if (cbMisalignHead)
    416         {
    417             rc = VDRead(pDisk, off - cbMisalignHead, abBuf, VD_SECTOR_SIZE);
     527        if (cbMisalignmentOfStart)
     528        {
     529            rc = VDRead(pDisk, off - cbMisalignmentOfStart, abBuf, VD_SECTOR_SIZE);
    418530            if (RT_SUCCESS(rc))
    419531            {
    420                 size_t const cbPart = RT_MIN(VD_SECTOR_SIZE - cbMisalignHead, cbWrite);
    421                 memcpy(&abBuf[cbMisalignHead], pbSrc, cbPart);
    422                 rc = VDWrite(pDisk, off - cbMisalignHead, abBuf, VD_SECTOR_SIZE);
     532                size_t const cbPartial = RT_MIN(VD_SECTOR_SIZE - cbMisalignmentOfStart, cbWrite);
     533                memcpy(&abBuf[cbMisalignmentOfStart], pbSrc, cbPartial);
     534                rc = VDWrite(pDisk, off - cbMisalignmentOfStart, abBuf, VD_SECTOR_SIZE);
    423535                if (RT_SUCCESS(rc))
    424536                {
    425                     pbSrc   += cbPart;
    426                     off     += cbPart;
    427                     cbRemaining -= cbPart;
     537                    pbSrc   += cbPartial;
     538                    off     += cbPartial;
     539                    cbRemaining -= cbPartial;
    428540                }
    429541            }
     
    438550        {
    439551            Assert(!(off % VD_SECTOR_SIZE));
    440             size_t cbPart = cbWrite - cbMisalignTail;
    441             Assert(!(cbPart % VD_SECTOR_SIZE));
    442             rc = VDWrite(pDisk, off, pbSrc, cbPart);
     552            size_t cbPartial = cbWrite - cbMisalignmentOfEnd;
     553            Assert(!(cbPartial % VD_SECTOR_SIZE));
     554            rc = VDWrite(pDisk, off, pbSrc, cbPartial);
    443555            if (RT_SUCCESS(rc))
    444556            {
    445                 pbSrc   += cbPart;
    446                 off     += cbPart;
    447                 cbRemaining -= cbPart;
     557                pbSrc   += cbPartial;
     558                off     += cbPartial;
     559                cbRemaining -= cbPartial;
    448560            }
    449561        }
     
    454566        if (   RT_SUCCESS(rc) && cbWrite > 0)
    455567        {
    456             Assert(cbWrite == cbMisalignTail);
     568            Assert(cbWrite == cbMisalignmentOfEnd);
    457569            Assert(cbWrite < VD_SECTOR_SIZE);
    458570            Assert(!(off % VD_SECTOR_SIZE));
     
    488600    AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
    489601
     602    AssertReturn(offset + g_vDiskOffset >= 0, -EINVAL);
     603    int64_t adjOff = offset + g_vDiskOffset;
     604
    490605    int rc = 0;
    491     if ((off_t)(offset + cbBuf) < offset)
     606    if ((off_t)(adjOff + cbBuf) < adjOff)
    492607        rc = -EINVAL;
    493     else if (offset >= g_cbPrimary)
     608    else if (adjOff >= g_vDiskSize)
    494609        return 0;
    495610    else if (!cbBuf)
     
    497612
    498613    if (rc >= 0)
    499         rc = vdReadSanitizer(g_pVDisk, offset, pbBuf, cbBuf);
     614        rc = vdReadSanitizer(g_pVDisk, adjOff, pbBuf, cbBuf);
    500615    if (rc < 0)
    501616        LogFlowFunc(("%s\n", strerror(rc)));
     
    515630    AssertReturn((int)cbBuf >= 0, -EINVAL);
    516631    AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
     632    AssertReturn(offset + g_vDiskOffset >= 0, -EINVAL);
     633    int64_t adjOff = offset + g_vDiskOffset;
    517634
    518635    int rc = 0;
     
    521638                     "              (write operation ignored w/o error!)\n"));
    522639        return cbBuf;
    523     } else if ((off_t)(offset + cbBuf) < offset)
     640    } else if ((off_t)(adjOff + cbBuf) < adjOff)
    524641        rc = -EINVAL;
    525     else if (offset >= g_cbPrimary)
     642    else if (offset >= g_vDiskSize)
    526643        return 0;
    527644    else if (!cbBuf)
     
    529646
    530647    if (rc >= 0)
    531         rc = vdWriteSanitizer(g_pVDisk, offset, pbBuf, cbBuf);
     648        rc = vdWriteSanitizer(g_pVDisk, adjOff, pbBuf, cbBuf);
    532649    if (rc < 0)
    533650        LogFlowFunc(("%s\n", strerror(rc)));
     
    556673        if (rc < 0)
    557674            return rc;
    558 
    559         stbuf->st_size = VDGetSize(g_pVDisk, 0);
     675        /*
     676         * st_size represents the size of the FUSE FS-mounted portion of the disk.
     677         * By default it is the whole disk, but can be a partition or specified
     678         * (or overridden) directly by the { -s | --size } option on the command line.
     679         */
     680        stbuf->st_size = g_vDiskSize;
    560681        stbuf->st_nlink = 1;
    561682    }
    562     else if (RTStrCmp(pszPath + 1, g_pszBaseImageName) == 0)
    563     {
     683    else if (RTStrNCmp(pszPath + 1, g_pszBaseImageName, strlen(g_pszBaseImageName)) == 0)
     684    {
     685        /* When the disk is partitioned, the symbolic link named from `basename` of
     686         * resolved path to VBox disk image, has appended to it formatted text
     687         * representing the offset range of the partition.
     688         *
     689         *  $ vboxraw -i /stroll/along/the/path/simple_fixed_disk.vdi -p 1 /mnt/tmpdir
     690         *  $ ls /mnt/tmpdir
     691         *  simple_fixed_disk.vdi[20480:2013244928]    vhdd
     692         */
    564693        rc = stat(g_pszBaseImagePath, stbuf);
    565694        if (rc < 0)
     
    590719
    591720    /*
    592      * The usual suspects (mandatory)
     721     *  mandatory '.', '..', ...
    593722     */
    594723    pfnFiller(pvBuf, ".", NULL, 0);
     
    596725
    597726    /*
    598      * Create one entry w/basename of original image that getattr() will
    599      * depict as a symbolic link pointing back to the original file,
    600      * as a convenience, so anyone who lists the FUSE file system can
    601      * easily find the associated vdisk.
     727     * Create FUSE FS dir entry that is depicted here (and exposed via stat()) as
     728     * a symbolic link back to the resolved path to the VBox virtual disk image,
     729     * whose symlink name is basename that path. This is a convenience so anyone
     730     * listing the dir can figure out easily what the vhdd FUSE node entry
     731     * represents.
    602732     */
    603     pfnFiller(pvBuf, g_pszBaseImageName, NULL, 0);
    604 
     733
     734    if (g_vDiskOffset == 0 && (g_vDiskSize == 0 || g_vDiskSize == g_cbEntireVDisk))
     735        pfnFiller(pvBuf, g_pszBaseImageName, NULL, 0);
     736    else
     737    {
     738        char tmp[BASENAME_MAX];
     739        RTStrPrintf(tmp, sizeof (tmp), "%s[%d:%d]", g_pszBaseImageName, g_vDiskOffset, g_vDiskSize);
     740        pfnFiller(pvBuf, tmp, NULL, 0);
     741    }
    605742    /*
    606743     * Create entry named "vhdd", which getattr() will describe as a
     
    622759
    623760static void
    624 listMedia(IMachine *pMachine)
     761listMedia(IMachine *pMachine, char *vmName, char *vmUuid)
    625762{
    626763    int rc = 0;
     
    628765
    629766    CHECK_ERROR(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(pMediumAttachments)));
     767    int firstIteration = 1;
    630768    for (size_t i = 0; i < pMediumAttachments.size(); i++)
    631769    {
     770
    632771        ComPtr<IMedium> pMedium;
    633772        DeviceType_T deviceType;
     
    661800            if (pEarliestAncestor.isNull())
    662801                return;
    663             RTPrintf("\n");
    664802            do
    665803            {
     
    671809                if (ancestorNumber == 0)
    672810                {
    673                     RTPrintf("   -----------------------\n");
    674                     RTPrintf("   HDD base:   \"%s\"\n",   CSTR(pMediumName));
    675                     RTPrintf("   UUID:       %s\n",       CSTR(pMediumUuid));
    676                     RTPrintf("   Location:   %s\n\n",     CSTR(pMediumPath));
     811                    if (!g_vboxrawOpts.fListMediaBrief)
     812                    {
     813                        RTPrintf("   -----------------------\n");
     814                        RTPrintf("   HDD base:   \"%s\"\n",   CSTR(pMediumName));
     815                        RTPrintf("   UUID:       %s\n",       CSTR(pMediumUuid));
     816                        RTPrintf("   Location:   %s\n\n",     CSTR(pMediumPath));
     817                    }
     818                    else
     819                    {
     820                        if (firstIteration)
     821                            RTPrintf("\nVM:    %s " ANSI_BOLD "%-20s" ANSI_RESET "\n",
     822                                vmUuid, vmName);
     823                        RTPrintf("  img: %s " ANSI_BOLD "  %s" ANSI_RESET "\n",
     824                            CSTR(pMediumUuid), CSTR(pMediumName));
     825                    }
    677826                }
    678827                else
    679828                {
    680                     RTPrintf("     Diff %d:\n", ancestorNumber);
    681                     RTPrintf("          UUID:       %s\n",    CSTR(pMediumUuid));
    682                     RTPrintf("          Location:   %s\n\n",  CSTR(pMediumPath));
     829                    if (!g_vboxrawOpts.fListMediaBrief)
     830                    {
     831                        RTPrintf("     Diff %d:\n", ancestorNumber);
     832                        RTPrintf("          UUID:       %s\n",    CSTR(pMediumUuid));
     833                        RTPrintf("          Location:   %s\n",  CSTR(pMediumPath));
     834                    }
    683835                }
    684836                CHECK_ERROR_BREAK(pChild, COMGETTER(Children)(ComSafeArrayAsOutParam(aMediumChildren)));
    685837                pChild = (aMediumChildren.size()) ? aMediumChildren[0] : NULL;
    686838                ++ancestorNumber;
     839                firstIteration = 0;
    687840            } while(pChild);
    688841        }
     
    720873
    721874                if (   g_vboxrawOpts.pszVm == NULL
    722                     || RTStrNCmp(CSTR(pMachineUuid), g_vboxrawOpts.pszVm, MAX_ID_LEN) == 0
    723                     || RTStrNCmp((const char *)pMachineName.raw(), g_vboxrawOpts.pszVm, MAX_ID_LEN) == 0)
     875                    || RTStrNCmp(CSTR(pMachineUuid), g_vboxrawOpts.pszVm, MAX_UUID_LEN) == 0
     876                    || RTStrNCmp((const char *)pMachineName.raw(), g_vboxrawOpts.pszVm, MAX_UUID_LEN) == 0)
    724877                {
    725                     RTPrintf("------------------------------------------------------\n");
    726                     RTPrintf("VM Name:   \"%s\"\n", CSTR(pMachineName));
    727                     RTPrintf("UUID:      %s\n",     CSTR(pMachineUuid));
    728                     if (*pDescription.raw() != '\0')
    729                         RTPrintf("Description:  %s\n",      CSTR(pDescription));
    730                     RTPrintf("Location:  %s\n",      CSTR(pMachineLocation));
    731 
    732                     listMedia(pMachine);
    733 
    734                     RTPrintf("\n");
     878                    if (!g_vboxrawOpts.fListMediaBrief)
     879                    {
     880                        RTPrintf("------------------------------------------------------\n");
     881                        RTPrintf("VM Name:   \"%s\"\n", CSTR(pMachineName));
     882                        RTPrintf("UUID:      %s\n",     CSTR(pMachineUuid));
     883                        if (*pDescription.raw() != '\0')
     884                            RTPrintf("Description:  %s\n",      CSTR(pDescription));
     885                        RTPrintf("Location:  %s\n",      CSTR(pMachineLocation));
     886                    }
     887                    listMedia(pMachine, RTStrDup(CSTR(pMachineName)), RTStrDup(CSTR(pMachineUuid)));
     888                }
     889                else
     890                {
     891                    listMedia(pMachine, RTStrDup(CSTR(pMachineName)), RTStrDup(CSTR(pMachineUuid)));
    735892                }
    736893            }
     
    766923}
    767924
     925uint8_t
     926parsePartitionTable(void)
     927{
     928    MBR_t mbr;
     929    EBR_t ebr;
     930    PTH_t parTblHdr;
     931
     932    ASSERT(sizeof (mbr) == 512);
     933    ASSERT(sizeof (ebr) == 512);
     934    /*
     935     * First entry describes entire disk as a single entity
     936     */
     937    g_aParsedPartitionInfo[0].idxPartition = 0;
     938    g_aParsedPartitionInfo[0].offPartition = 0;
     939    g_aParsedPartitionInfo[0].cbPartition = VDGetSize(g_pVDisk, 0);
     940    g_aParsedPartitionInfo[0].pszName = RTStrDup("EntireDisk");
     941
     942    /*
     943     * Currently only DOS partitioned disks are supported. Ensure this one conforms
     944     */
     945    int rc = vdReadSanitizer(g_pVDisk, 0, &mbr, sizeof (mbr));
     946    if (RT_FAILURE(rc))
     947        return RTMsgErrorExitFailure("Error reading MBR block from disk\n");
     948
     949    if (mbr.signature == NULL_BOOT_RECORD_SIGNATURE)
     950        return RTMsgErrorExitFailure("Unprt disk (null MBR signature)\n");
     951
     952    if (mbr.signature != DOS_BOOT_RECORD_SIGNATURE)
     953        return RTMsgErrorExitFailure("Invalid MBR found on image with signature 0x%04hX\n",
     954            mbr.signature);
     955    /*
     956     * Parse the four physical partition entires in the MBR (any one, and only one, can be an EBR)
     957     */
     958    int ebridxPartitionInMbr = 0;
     959    for (int idxPartition = 1;
     960             idxPartition <= MBR_PARTITIONS_MAX;
     961             idxPartition++)
     962    {
     963        MBRPARTITIONENTRY *pMbrPartitionEntry =
     964            &g_aParsedPartitionInfo[idxPartition].partitionEntry.mbrEntry;;
     965        memcpy (pMbrPartitionEntry, &mbr.partitionEntry[idxPartition - 1], sizeof (MBRPARTITIONENTRY));
     966
     967        if (PARTTYPE_IS_NULL(pMbrPartitionEntry->type))
     968            continue;
     969
     970        if (PARTTYPE_IS_EXT(pMbrPartitionEntry->type))
     971        {
     972            if (ebridxPartitionInMbr)
     973                 return RTMsgErrorExitFailure("Multiple EBRs found found in MBR\n");
     974            ebridxPartitionInMbr = idxPartition;
     975        }
     976
     977        PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];
     978
     979        ppi->idxPartition = idxPartition;
     980        ppi->offPartition = (off_t) pMbrPartitionEntry->partitionLba * BLOCKSIZE;
     981        ppi->cbPartition = (off_t) pMbrPartitionEntry->partitionBlkCnt * BLOCKSIZE;
     982        ppi->fBootable = pMbrPartitionEntry->bootIndicator == 0x80;
     983        (ppi->partitionType).legacy = pMbrPartitionEntry->type;
     984
     985        g_lastPartNbr = idxPartition;
     986
     987        if (PARTTYPE_IS_GPT(pMbrPartitionEntry->type))
     988        {
     989            g_fGPT = true;
     990            break;
     991        }
     992    }
     993
     994    if (g_fGPT)
     995    {
     996        g_lastPartNbr = 2;  /* from the 'protective MBR' */
     997
     998        rc = vdReadSanitizer(g_pVDisk, LBA(1), &parTblHdr, sizeof (parTblHdr));
     999        if (RT_FAILURE(rc))
     1000            return RTMsgErrorExitFailure("Error reading Partition Table Header (LBA 1) from disk\n");
     1001
     1002        uint8_t *pTblBuf = (uint8_t *)RTMemAlloc(GPT_PTABLE_SIZE);
     1003
     1004        RTPrintf( "Virtual disk image:\n\n");
     1005        RTPrintf("   Path: %s\n", g_pszBaseImagePath);
     1006        if (g_pvDiskUuid)
     1007            RTPrintf("   UUID: %s\n\n", g_pvDiskUuid);
     1008
     1009        if (g_vboxrawOpts.fVerbose)
     1010        {
     1011            RTPrintf("   GPT Partition Table Header:\n\n");
     1012            if (RTStrCmp((const char *)&parTblHdr.signature, "EFI PART") == 0)
     1013                RTPrintf(
     1014                     "      Signature               \"EFI PART\" (0x%llx)\n", parTblHdr.signature);
     1015            else
     1016                RTPrintf(
     1017                     "      Signature:              0x%llx\n",  parTblHdr.signature);
     1018            RTPrintf("      Revision:               %-8.8x\n",  parTblHdr.revision);
     1019            RTPrintf("      Current LBA:            %lld\n",    parTblHdr.headerLba);
     1020            RTPrintf("      Backup LBA:             %lld\n",    parTblHdr.backupLba);
     1021            RTPrintf("      Partition entries LBA:  %lld\n",    parTblHdr.partitionEntriesLba);
     1022            RTPrintf("      # of partitions:        %d\n",      parTblHdr.cPartitionEntries);
     1023            RTPrintf("      size of entry:          %d\n\n",    parTblHdr.cbPartitionEntry);
     1024        }
     1025
     1026        if (!pTblBuf)
     1027            return RTMsgErrorExitFailure("Out of memory\n");
     1028
     1029        rc = vdReadSanitizer(g_pVDisk, LBA(2), pTblBuf, GPT_PTABLE_SIZE);
     1030        if (RT_FAILURE(rc))
     1031            return RTMsgErrorExitFailure("Error reading Partition Table blocks from disk\n");
     1032
     1033        uint32_t cEntries = parTblHdr.cPartitionEntries;
     1034        uint32_t cbEntry  = parTblHdr.cbPartitionEntry;
     1035        if (cEntries * cbEntry > GPT_PTABLE_SIZE)
     1036        {
     1037            RTPrintf("Partition entries exceed GPT table read from disk (pruning!)\n");
     1038            while (cEntries * cbEntry > GPT_PTABLE_SIZE && cEntries > 0)
     1039                --cEntries;
     1040        }
     1041        uint8_t *pEntryRaw = pTblBuf;
     1042        for (uint32_t i = 0; i < cEntries; i++)
     1043        {
     1044            GPTPARTITIONENTRY *pEntry = (GPTPARTITIONENTRY *)pEntryRaw;
     1045            PARTITIONINFO *ppi = &g_aParsedPartitionInfo[g_lastPartNbr];
     1046            memcpy(&(ppi->partitionEntry).gptEntry, pEntry, sizeof(GPTPARTITIONENTRY));
     1047            uint64_t firstLba = pEntry->firstLba;
     1048            uint64_t lastLba  = pEntry->lastLba;
     1049            if (!firstLba)
     1050                break;
     1051            ppi->offPartition = firstLba * BLOCKSIZE;
     1052            ppi->cbPartition = (lastLba - firstLba) * BLOCKSIZE;
     1053            ppi->fBootable = pEntry->attrFlags & (1 << GPT_LEGACY_BIOS_BOOTABLE);
     1054            ppi->partitionType.gptGuidTypeSpecifier = pEntry->partitionTypeGuid;
     1055            size_t cwName = sizeof (pEntry->partitionName) / 2;
     1056            char *pszTmp = NULL, **pPszTmp = &pszTmp;
     1057            RTUtf16LittleToUtf8Ex((PRTUTF16)pEntry->partitionName, RTSTR_MAX, &ppi->pszName, cwName, NULL);
     1058            ppi->idxPartition = g_lastPartNbr++;
     1059            pEntryRaw += cbEntry;
     1060        }
     1061        return PARTITION_TABLE_GPT;
     1062    }
     1063
     1064    /*
     1065     * Starting with EBR located in MBR, walk EBR chain to parse the logical partition entries
     1066     */
     1067    if (ebridxPartitionInMbr)
     1068    {
     1069        uint32_t firstEbrLba
     1070            = g_aParsedPartitionInfo[ebridxPartitionInMbr].partitionEntry.mbrEntry.partitionLba;
     1071        off_t    firstEbrOffset   = (off_t)firstEbrLba * BLOCKSIZE;
     1072        off_t    chainedEbrOffset = 0;
     1073
     1074        if (!firstEbrLba)
     1075            return RTMsgErrorExitFailure("Inconsistency for logical partition start. Aborting\n");
     1076
     1077        for (int idxPartition = 5;
     1078                 idxPartition <= VBOXRAW_PARTITION_MAX;
     1079                 idxPartition++)
     1080        {
     1081
     1082            off_t currentEbrOffset = firstEbrOffset + chainedEbrOffset;
     1083            vdReadSanitizer(g_pVDisk, currentEbrOffset, &ebr, sizeof (ebr));
     1084
     1085            if (ebr.signature != DOS_BOOT_RECORD_SIGNATURE)
     1086                return RTMsgErrorExitFailure("Invalid EBR found on image with signature 0x%04hX\n",
     1087                    ebr.signature);
     1088
     1089            MBRPARTITIONENTRY *pEbrPartitionEntry =
     1090                &g_aParsedPartitionInfo[idxPartition].partitionEntry.mbrEntry; /* EBR entry struct same as MBR */
     1091            memcpy(pEbrPartitionEntry, &ebr.partitionEntry, sizeof (MBRPARTITIONENTRY));
     1092
     1093            if (pEbrPartitionEntry->type == NULL_BOOT_RECORD_SIGNATURE)
     1094                return RTMsgErrorExitFailure("Logical partition with type 0 encountered");
     1095
     1096            if (!pEbrPartitionEntry->partitionLba)
     1097                return RTMsgErrorExitFailure("Logical partition invalid partition start offset (LBA) encountered");
     1098
     1099            PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];
     1100            ppi->idxPartition         = idxPartition;
     1101            ppi->offPartition    = currentEbrOffset + (off_t)pEbrPartitionEntry->partitionLba * BLOCKSIZE;
     1102            ppi->cbPartition        = (off_t)pEbrPartitionEntry->partitionBlkCnt * BLOCKSIZE;
     1103            ppi->fBootable            = pEbrPartitionEntry->bootIndicator == 0x80;
     1104            ppi->partitionType.legacy = pEbrPartitionEntry->type;
     1105
     1106            g_lastPartNbr = idxPartition;
     1107
     1108            if (ebr.chainingPartitionEntry.type == 0) /* end of chain */
     1109                break;
     1110
     1111            if (!PARTTYPE_IS_EXT(ebr.chainingPartitionEntry.type))
     1112                return RTMsgErrorExitFailure("Logical partition chain broken");
     1113
     1114            chainedEbrOffset = ebr.chainingPartitionEntry.partitionLba * BLOCKSIZE;
     1115        }
     1116    }
     1117    return PARTITION_TABLE_MBR;
     1118}
     1119
     1120const char *getClassicPartitionDesc(uint8_t type)
     1121{
     1122    for (uint32_t i = 0; i < sizeof (g_partitionDescTable) / sizeof (struct PartitionDesc); i++)
     1123    {
     1124        if (g_partitionDescTable[i].type == type)
     1125            return g_partitionDescTable[i].desc;
     1126    }
     1127    return "????";
     1128}
     1129
     1130void
     1131displayGptPartitionTable(void)
     1132{
     1133
     1134    void *colBoot = NULL;
     1135
     1136    SELFSIZINGTABLE tbl(2);
     1137
     1138    /* Note: Omitting partition name column because type/UUID seems suffcient */
     1139    void *colPartNbr   = tbl.addCol("#",                 "%3d",       1);
     1140
     1141    /* If none of the partitions supports legacy BIOS boot, don't show column */
     1142    for (int idxPartition = 2; idxPartition <= g_lastPartNbr; idxPartition++)
     1143        if (g_aParsedPartitionInfo[idxPartition].fBootable) {
     1144            colBoot    = tbl.addCol("Boot",         "%c   ",     1);
     1145            break;
     1146        }
     1147
     1148    void *colStart     = tbl.addCol("Start",             "%lld",      1);
     1149    void *colSectors   = tbl.addCol("Sectors",           "%lld",     -1, 2);
     1150    void *colSize      = tbl.addCol("Size",              "%d.%d%c",   1);
     1151    void *colOffset    = tbl.addCol("Offset",            "%lld",      1);
     1152    /* Need to see how other OSes with GPT schemes use this field. Seems like type covers it
     1153    void *colName      = tbl.addCol("Name",              "%s",       -1); */
     1154    void *colType      = tbl.addCol("Type",              "%s",       -1, 2);
     1155
     1156    for (int idxPartition = 2; idxPartition <= g_lastPartNbr; idxPartition++)
     1157    {
     1158        PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];
     1159        if (ppi->idxPartition)
     1160        {
     1161            uint8_t exp = log2((double)ppi->cbPartition);
     1162            char scaledMagnitude = ((char []){ ' ', 'K', 'M', 'G', 'T', 'P' })[exp / 10];
     1163
     1164             /* This workaround is because IPRT RT*Printf* funcs don't handle floating point format specifiers */
     1165            double cbPartitionScaled = (double)ppi->cbPartition / pow(2, (double)(((uint8_t)(exp / 10)) * 10));
     1166            uint8_t cbPartitionIntPart = cbPartitionScaled;
     1167            uint8_t cbPartitionFracPart = (cbPartitionScaled - (double)cbPartitionIntPart) * 10;
     1168
     1169            char abGuid[GUID_STRING_LENGTH * 2];
     1170            RTStrPrintf(abGuid, sizeof(abGuid), "%RTuuid",  &ppi->partitionType.gptGuidTypeSpecifier);
     1171
     1172            char *pszPartitionTypeDesc = NULL;
     1173            for (uint32_t i = 0; i < sizeof(g_gptPartitionTypes) / sizeof(GPTPARTITIONTYPE); i++)
     1174                if (RTStrNICmp(abGuid, g_gptPartitionTypes[i].gptPartitionUuid, GUID_STRING_LENGTH) == 0)
     1175                {
     1176                    pszPartitionTypeDesc =  (char *)g_gptPartitionTypes[i].gptPartitionTypeDesc;
     1177                    break;
     1178                }
     1179
     1180            if (!pszPartitionTypeDesc)
     1181                RTPrintf("Couldn't find GPT partitiontype for GUID: %s\n", abGuid);
     1182
     1183            void *row = tbl.addRow();
     1184            tbl.setCell(row, colPartNbr,    idxPartition - 1);
     1185            if (colBoot)
     1186                tbl.setCell(row, colBoot,   ppi->fBootable ? '*' : ' ');
     1187            tbl.setCell(row, colStart,      ppi->offPartition / BLOCKSIZE);
     1188            tbl.setCell(row, colSectors,    ppi->cbPartition / BLOCKSIZE);
     1189            tbl.setCell(row, colSize,       cbPartitionIntPart, cbPartitionFracPart, scaledMagnitude);
     1190            tbl.setCell(row, colOffset,     ppi->offPartition);
     1191/*          tbl.setCell(row, colName,       ppi->pszName);    ... see comment for column definition */
     1192            tbl.setCell(row, colType,       SAFENULL(pszPartitionTypeDesc));
     1193        }
     1194    }
     1195    tbl.displayTable();
     1196    RTPrintf ("\n");
     1197}
     1198
     1199void
     1200displayLegacyPartitionTable(void)
     1201{
     1202    /*
     1203     * Partition table is most readable and concise when headers and columns
     1204     * are adapted to the actual data, to avoid insufficient or excessive whitespace.
     1205     */
     1206    RTPrintf( "Virtual disk image:\n\n");
     1207    RTPrintf("   Path: %s\n", g_pszBaseImagePath);
     1208    if (g_pvDiskUuid)
     1209        RTPrintf("   UUID: %s\n\n", g_pvDiskUuid);
     1210
     1211    SELFSIZINGTABLE tbl(2);
     1212
     1213    void *colPartition = tbl.addCol("Partition",    "%s%d",     -1);
     1214    void *colBoot      = tbl.addCol("Boot",         "%c   ",     1);
     1215    void *colStart     = tbl.addCol("Start",        "%lld",      1);
     1216    void *colSectors   = tbl.addCol("Sectors",      "%lld",     -1, 2);
     1217    void *colSize      = tbl.addCol("Size",         "%d.%d%c",   1);
     1218    void *colOffset    = tbl.addCol("Offset",       "%lld",      1);
     1219    void *colId        = tbl.addCol("Id",           "%2x",       1);
     1220    void *colType      = tbl.addCol("Type",         "%s",       -1, 2);
     1221
     1222    for (int idxPartition = 1; idxPartition <= g_lastPartNbr; idxPartition++)
     1223    {
     1224        PARTITIONINFO *p = &g_aParsedPartitionInfo[idxPartition];
     1225        if (p->idxPartition)
     1226        {
     1227            uint8_t exp = log2((double)p->cbPartition);
     1228            char scaledMagnitude = ((char []){ ' ', 'K', 'M', 'G', 'T', 'P' })[exp / 10];
     1229
     1230             /* This workaround is because IPRT RT*Printf* funcs don't handle floating point format specifiers */
     1231            double  cbPartitionScaled = (double)p->cbPartition / pow(2, (double)(((uint8_t)(exp / 10)) * 10));
     1232            uint8_t cbPartitionIntPart = cbPartitionScaled;
     1233            uint8_t cbPartitionFracPart = (cbPartitionScaled - (double)cbPartitionIntPart) * 10;
     1234
     1235            void *row = tbl.addRow();
     1236
     1237            tbl.setCell(row, colPartition,  g_pszBaseImageName, idxPartition);
     1238            tbl.setCell(row, colBoot,       p->fBootable ? '*' : ' ');
     1239            tbl.setCell(row, colStart,      p->offPartition / BLOCKSIZE);
     1240            tbl.setCell(row, colSectors,    p->cbPartition / BLOCKSIZE);
     1241            tbl.setCell(row, colSize,       cbPartitionIntPart, cbPartitionFracPart, scaledMagnitude);
     1242            tbl.setCell(row, colOffset,     p->offPartition);
     1243            tbl.setCell(row, colId,         p->partitionType.legacy);
     1244            tbl.setCell(row, colType,       getClassicPartitionDesc((p->partitionType).legacy));
     1245        }
     1246    }
     1247    tbl.displayTable();
     1248    RTPrintf ("\n");
     1249}
     1250
    7681251int
    7691252main(int argc, char **argv)
     
    7721255    int rc = RTR3InitExe(argc, &argv, 0);
    7731256    if (RT_FAILURE(rc))
    774         return RTMsgErrorExitFailure("vboxraw: ERROR: RTR3InitExe failed, rc=%Rrc\n", rc);
     1257        return RTMsgErrorExitFailure("RTR3InitExe failed, rc=%Rrc\n", rc);
    7751258
    7761259    rc = VDInit();
    7771260    if (RT_FAILURE(rc))
    778         return RTMsgErrorExitFailure("vboxraw: ERROR: VDInit failed, rc=%Rrc\n", rc);
     1261        return RTMsgErrorExitFailure("VDInit failed, rc=%Rrc\n", rc);
    7791262
    7801263    memset(&g_vboxrawOps, 0, sizeof(g_vboxrawOps));
     
    7861269    g_vboxrawOps.readdir     = vboxrawOp_readdir;
    7871270    g_vboxrawOps.readlink    = vboxrawOp_readlink;
     1271
    7881272    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    789 
    7901273    memset(&g_vboxrawOpts, 0, sizeof(g_vboxrawOpts));
    7911274
    7921275    rc = fuse_opt_parse(&args, &g_vboxrawOpts, vboxrawOptDefs, vboxrawOptHandler);
     1276
    7931277    if (g_vboxrawOpts.fAllowRoot)
    7941278        fuse_opt_add_arg(&args, "-oallow_root");
    7951279
    7961280    if (rc == -1)
    797         return RTMsgErrorExitFailure("vboxraw: ERROR: Couldn't parse fuse options, rc=%Rrc\n", rc);
     1281        return RTMsgErrorExitFailure("Couldn't parse fuse options, rc=%Rrc\n", rc);
     1282    if (g_vboxrawOpts.fBriefUsage)
     1283    {
     1284        briefUsage();
     1285        return 0;
     1286    }
    7981287
    7991288    /*
     
    8261315        hrc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
    8271316    if (FAILED(hrc))
    828          return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get IVirtualBox object! (hrc=%Rhrc)", hrc);
     1317        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get IVirtualBox object! (hrc=%Rhrc)", hrc);
    8291318
    8301319    if (g_vboxrawOpts.fVerbose)
    8311320        RTPrintf("vboxraw: VirtualBox XPCOM object created\n");
    8321321
    833     if (g_vboxrawOpts.fList)
    834     {
    835          listVMs(pVirtualBox);
    836          return 0;
    837     }
     1322    if (g_vboxrawOpts.fListMedia || g_vboxrawOpts.fListMediaBrief)
     1323    {
     1324        listVMs(pVirtualBox);
     1325        return 0;
     1326    }
     1327
    8381328
    8391329    if (g_vboxrawOpts.pszImage == NULL)
    8401330    {
    841             RTMsgErrorExitFailure("vboxraw: ERROR: "
    842                 "Must provide at at least --list or --image option\n");
    843             return 0;
     1331        RTMsgErrorExitFailure("To list partitions, must also specify --i or --image option\n");
     1332        return 0;
    8441333    }
    8451334    ComPtr<IMedium> pBaseImageMedium = NULL;
    8461335    char    *pszFormat;
    8471336    VDTYPE  enmType;
    848 
    8491337    searchForBaseImage(pVirtualBox, g_vboxrawOpts.pszImage, &pBaseImageMedium);
    8501338    if (pBaseImageMedium == NULL)
     
    8601348        g_pszBaseImagePath = RTStrDup(g_vboxrawOpts.pszImage);
    8611349        if (g_pszBaseImagePath == NULL)
    862             return RTMsgErrorExitFailure("vboxraw: out of memory\n");
     1350            return RTMsgErrorExitFailure("out of memory\n");
    8631351
    8641352        if (access(g_pszBaseImagePath, F_OK) < 0)
    865             return RTMsgErrorExitFailure("vboxraw: ERROR: "
    866                     "Virtual disk image not found: \"%s\"\n", g_pszBaseImagePath);
     1353            return RTMsgErrorExitFailure("Virtual disk image not found: \"%s\"\n", g_pszBaseImagePath);
    8671354
    8681355        if (access(g_pszBaseImagePath, R_OK) < 0)
    869              return RTMsgErrorExitFailure("vboxraw: ERROR: "
     1356             return RTMsgErrorExitFailure(
    8701357                    "Virtual disk image not readable: \"%s\"\n", g_pszBaseImagePath);
    8711358
    872         if ( g_vboxrawOpts.fRW && access(g_vboxrawOpts.pszImage, W_OK) < 0)
    873              return RTMsgErrorExitFailure("vboxraw: ERROR: "
     1359        if (g_vboxrawOpts.fRW && access(g_vboxrawOpts.pszImage, W_OK) < 0)
     1360             return RTMsgErrorExitFailure(
    8741361                    "Virtual disk image not writeable: \"%s\"\n", g_pszBaseImagePath);
    8751362        rc = RTPathSplit(g_pszBaseImagePath, &g_u.split, sizeof(g_u), 0);
    8761363
    8771364        if (RT_FAILURE(rc))
    878              return RTMsgErrorExitFailure("vboxraw: "
     1365             return RTMsgErrorExitFailure(
    8791366                    "RTPathSplit failed on '%s': %Rrc",  g_pszBaseImagePath);
    8801367
    8811368        if (!(g_u.split.fProps & RTPATH_PROP_FILENAME))
    882              return RTMsgErrorExitFailure("vboxraw: "
     1369             return RTMsgErrorExitFailure(
    8831370                    "RTPATH_PROP_FILENAME not set for: '%s'",  g_pszBaseImagePath);
    8841371
     
    8961383                g_pszBaseImagePath, &pszFormat, &enmType);
    8971384            if (RT_FAILURE(rc))
    898                 return RTMsgErrorExitFailure("vboxraw: ERROR: VDGetFormat(%s,) "
     1385                return RTMsgErrorExitFailure("VDGetFormat(%s,) "
    8991386                    "failed, rc=%Rrc\n", g_pszBaseImagePath, rc);
    9001387
     
    9071394                {
    9081395                    VDClose(g_pVDisk, false /* fDeletes */);
    909                     return RTMsgErrorExitFailure("vboxraw: ERROR: VDCreate(,%s,%s,,,) failed,"
     1396                    return RTMsgErrorExitFailure("VDCreate(,%s,%s,,,) failed,"
    9101397                        " rc=%Rrc\n", pszFormat, g_pszBaseImagePath, rc);
    9111398                }
    9121399            }
    9131400            else
    914                 return RTMsgErrorExitFailure("vboxraw: ERROR: VDCreate failed, rc=%Rrc\n", rc);
    915         }
     1401                return RTMsgErrorExitFailure("VDCreate failed, rc=%Rrc\n", rc);
     1402        }
     1403    } else {
     1404        Bstr pMediumUuid;
     1405        CHECK_ERROR(pBaseImageMedium, COMGETTER(Id)(pMediumUuid.asOutParam()));
     1406        g_pvDiskUuid = RTStrDup((char *)CSTR(pMediumUuid));
    9161407    }
    9171408
    9181409    if (g_pVDisk == NULL)
    9191410    {
     1411
    9201412        com::SafeIfaceArray<IMedium> aMediumChildren;
    9211413        ComPtr<IMedium> pChild = pBaseImageMedium;
     
    9371429                g_pszBaseImagePath = RTStrDup(CSTR(pMediumPath));
    9381430                if (g_pszBaseImageName == NULL)
    939                     return RTMsgErrorExitFailure("vboxraw: out of memory\n");
     1431                    return RTMsgErrorExitFailure("out of memory\n");
    9401432
    9411433                if (g_pszBaseImagePath == NULL)
    942                     return RTMsgErrorExitFailure("vboxraw: out of memory\n");
     1434                    return RTMsgErrorExitFailure("out of memory\n");
    9431435                /*
    9441436                 * Create HDD container to open base image and differencing images into
     
    9471439                        g_pszBaseImagePath, &pszFormat, &enmType);
    9481440                if (RT_FAILURE(rc))
    949                     return RTMsgErrorExitFailure("vboxraw: VDGetFormat(,,%s,,) "
     1441                    return RTMsgErrorExitFailure("VDGetFormat(,,%s,,) "
    9501442                        "failed (during HDD container creation), rc=%Rrc\n", g_pszBaseImagePath, rc);
    9511443                if (g_vboxrawOpts.fVerbose)
    952                     RTPrintf("vboxraw: Creating container for base image of format %s\n", pszFormat);
     1444                    RTPrintf("Creating container for base image of format %s\n", pszFormat);
    9531445                /** @todo Remove I/O CB's and crit sect. when VDRead()/VDWrite() are made threadsafe */
    9541446                rc = RTCritSectInit(&g_vdioLock);
     
    9631455                }
    9641456                else
    965                     return RTMsgErrorExitFailure("vboxraw: ERROR: Failed to create critsects "
     1457                    return RTMsgErrorExitFailure("ERROR: Failed to create critsects "
    9661458                                                 "for virtual disk I/O, rc=%Rrc\n", rc);
    9671459
     
    9691461                rc = VDCreate(g_pVdIfs, enmType, &g_pVDisk);
    9701462                if (NS_FAILED(rc))
    971                     return RTMsgErrorExitFailure("vboxraw: ERROR: Couldn't create virtual disk container\n");
     1463                    return RTMsgErrorExitFailure("ERROR: Couldn't create virtual disk container\n");
    9721464            }
    9731465            /** @todo (end of to do section) */
     
    9901482            {
    9911483                VDClose(g_pVDisk, false /* fDeletes */);
    992                 return RTMsgErrorExitFailure("vboxraw: VDOpen(,,%s,,) failed, rc=%Rrc\n",
     1484                return RTMsgErrorExitFailure("VDOpen(,,%s,,) failed, rc=%Rrc\n",
    9931485                   g_pszBaseImagePath, rc);
    9941486            }
     
    10101502    g_cReaders    = VDIsReadOnly(g_pVDisk) ? INT32_MAX / 2 : 0;
    10111503    g_cWriters   = 0;
    1012     g_cbPrimary  = VDGetSize(g_pVDisk, 0 /* base */);
     1504    g_cbEntireVDisk  = VDGetSize(g_pVDisk, 0 /* base */);
     1505
     1506    if (g_vboxrawOpts.fListParts)
     1507    {
     1508        if (g_pVDisk == NULL)
     1509            return RTMsgErrorExitFailure("No valid --image to list partitions from\n");
     1510
     1511        RTPrintf("\n");
     1512
     1513        rc = parsePartitionTable();
     1514        switch(rc)
     1515        {
     1516            case PARTITION_TABLE_MBR:
     1517                displayLegacyPartitionTable();
     1518                break;
     1519            case PARTITION_TABLE_GPT:
     1520                displayGptPartitionTable();
     1521                break;
     1522            default:
     1523                return rc;
     1524        }
     1525        return 0;
     1526    }
     1527    if (g_vboxrawOpts.idxPartition >= 0)
     1528    {
     1529        if (g_vboxrawOpts.offset)
     1530            return RTMsgErrorExitFailure("--offset and --partition are mutually exclusive options\n");
     1531
     1532        if (g_vboxrawOpts.size)
     1533            return RTMsgErrorExitFailure("--size and --partition are mutually exclusive options\n");
     1534
     1535        /*
     1536         * --partition option specified. That will set the global offset and limit
     1537         * honored by the disk read and write sanitizers to constrain operations
     1538         * to within the specified partion based on an initial parsing of the MBR
     1539         */
     1540        rc = parsePartitionTable();
     1541        if (rc < 0)
     1542            return RTMsgErrorExitFailure("Error parsing disk MBR/Partition table\n");
     1543        int partNbr = g_vboxrawOpts.idxPartition;
     1544
     1545        if (partNbr < 0 || partNbr > g_lastPartNbr)
     1546            return RTMsgErrorExitFailure("Non-valid partition number specified\n");
     1547
     1548        if (partNbr == 0)
     1549        {
     1550            g_vDiskOffset = 0;
     1551            g_vDiskSize = VDGetSize(g_pVDisk, 0);
     1552            if (g_vboxrawOpts.fVerbose)
     1553                RTPrintf("Partition 0 specified - Whole disk will be accessible\n");
     1554        } else {
     1555            for (int i = 0; i < g_lastPartNbr; i++)
     1556            {
     1557                /* If GPT, display vboxraw's representation of partition table starts at partition 2
     1558                 * but the table is displayed calling it partition 1, because the protective MBR
     1559                 * record is relatively pointless to display or reference in this context */
     1560
     1561                if (g_aParsedPartitionInfo[i].idxPartition == partNbr + g_fGPT ? 1 : 0)
     1562                {
     1563                     g_vDiskOffset = g_aParsedPartitionInfo[i].offPartition;
     1564                     g_vDiskSize = g_vDiskOffset + g_aParsedPartitionInfo[i].cbPartition;
     1565                     if (g_vboxrawOpts.fVerbose)
     1566                        RTPrintf("Partition %d specified. Only sectors %llu to %llu of disk will be accessible\n",
     1567                            g_vboxrawOpts.idxPartition, g_vDiskOffset / BLOCKSIZE, g_vDiskSize / BLOCKSIZE);
     1568                }
     1569            }
     1570        }
     1571    } else {
     1572        if (g_vboxrawOpts.offset) {
     1573            if (g_vboxrawOpts.offset < 0 || g_vboxrawOpts.offset + g_vboxrawOpts.size > g_cbEntireVDisk)
     1574                return RTMsgErrorExitFailure("User specified offset out of range of virtual disk\n");
     1575
     1576            if (g_vboxrawOpts.fVerbose)
     1577                RTPrintf("Setting r/w bias (offset) to user requested value for sector %llu\n", g_vDiskOffset / BLOCKSIZE);
     1578
     1579            g_vDiskOffset = g_vboxrawOpts.offset;
     1580        }
     1581        if (g_vboxrawOpts.size) {
     1582            if (g_vboxrawOpts.size < 0 || g_vboxrawOpts.offset + g_vboxrawOpts.size > g_cbEntireVDisk)
     1583                return RTMsgErrorExitFailure("User specified size out of range of virtual disk\n");
     1584
     1585            if (g_vboxrawOpts.fVerbose)
     1586                RTPrintf("Setting r/w size limit to user requested value %llu\n", g_vDiskSize / BLOCKSIZE);
     1587
     1588            g_vDiskSize = g_vboxrawOpts.size;
     1589        }
     1590    }
     1591    if (g_vDiskSize == 0)
     1592        g_vDiskSize = g_cbEntireVDisk - g_vDiskOffset;
    10131593
    10141594    /*
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