Index: /trunk/Config.kmk
===================================================================
--- /trunk/Config.kmk	(revision 75969)
+++ /trunk/Config.kmk	(revision 75970)
@@ -829,4 +829,6 @@
 # windows versions (only define when buildling win.x86).
 #VBOX_WITH_MORE_NT4_COMPAT_BINARIES = 1
+# Set this to enable building of the vboximg-mount FUSE mounting utility.
+#VBOX_WITH_VBOXIMGMOUNT = 1
 ## @}
 
Index: /trunk/src/VBox/ImageMounter/Makefile.kmk
===================================================================
--- /trunk/src/VBox/ImageMounter/Makefile.kmk	(revision 75970)
+++ /trunk/src/VBox/ImageMounter/Makefile.kmk	(revision 75970)
@@ -0,0 +1,26 @@
+# $Id$
+## @file
+# Sub-Makefile for the image mounting utilities.
+#
+
+#
+# Copyright (C) 2018 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+ifdef VBOX_WITH_VBOXIMGMOUNT
+  include $(PATH_SUB_CURRENT)/vboximg-mount/Makefile.kmk
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
Index: /trunk/src/VBox/ImageMounter/vboximg-mount/Makefile.kmk
===================================================================
--- /trunk/src/VBox/ImageMounter/vboximg-mount/Makefile.kmk	(revision 75970)
+++ /trunk/src/VBox/ImageMounter/vboximg-mount/Makefile.kmk	(revision 75970)
@@ -0,0 +1,48 @@
+# $Id$
+## @file
+# Sub-Makefile for the vboximg-mount Program.
+
+#
+# Copyright (C) 2006-2018 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# vboximg-mount - Disk Image Flatting FUSE Program.
+#
+PROGRAMS += vboximg-mount
+
+vboximg-mount_TEMPLATE =  VBOXMAINCLIENTEXE
+vboximg-mount_DEFS.darwin = __FreeBSD_==10
+vboximg-mount_DEFS        = _FILE_OFFSET_BITS=64
+
+vboximg-mount_SOURCES = \
+    vboximg-mount.cpp \
+    vboximg-mount.h \
+    SelfSizingTable.h
+
+vboximg-mount_LIBS = \
+	$(LIB_DDU) \
+	$(LIB_RUNTIME)
+
+vboximg-mount_INCS.darwin = \
+    /usr/local/include \
+    /usr/local/include/osxfuse \
+vboximg-mount_CXXFLAGS.darwin = -std=c++11
+
+vboximg-mount_LIBS.darwin = /usr/local/lib/libosxfuse.dylib
+vboximg-mount_LIBS.linux = fuse
+vboximg-mount_LIBS.freebsd = fuse
+
+include $(FILE_KBUILD_SUB_FOOTER)
Index: /trunk/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h
===================================================================
--- /trunk/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h	(revision 75970)
+++ /trunk/src/VBox/ImageMounter/vboximg-mount/SelfSizingTable.h	(revision 75970)
@@ -0,0 +1,310 @@
+/* $Id$ $Revision$ */
+/** @file
+ * vboxraw header file
+ */
+
+/*
+ * Copyright (C) 2018 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/* SELFSIZINGTABLE
+ *
+ * An ANSI text-display oriented table, whose column widths conform to width of
+ * their contents. The goal is to optimize whitespace usage, so there's neither too
+ * much nor too little whitespace (e.g. min. necessary for optimal readability).
+ *
+ * Contents can only be added to and redisplayed, not manipulated after adding.
+ *
+ * Simple API (see example below):
+ *
+ *   1. Create table instance.
+ *   2. Add column definitions.
+ *   3. Add each row and set data for each column in a row.
+ *   4. Invoke the displayTable() method.
+ *
+ * Each time the table is [re]displayed its contents are [re]evaluated to determine
+ * the column sizes and header and data padding.
+ *
+ * Example:
+ *
+ *  SELFSIZINGTABLE tbl(2);
+ *  void *colPlanet  = tbl.addCol("Planet"          "%s",   1);
+ *  void *colInhabit = tbl.addCol("Inhabitability", "%-12s = %s");
+ *
+ *  // This is an 'unrolled loop' example. More typical would be to iterate,
+ *  // providing data content from arrays, indicies, in-place calculations,
+ *  // databases, etc... rather than just hardcoded literals.
+ *
+ *  void *row = tbl.addRow();
+ *  tbl.setCell(row, colPlanet,  "Earth");
+ *  tbl.setCell(row, colInhabit, "Viability", "Decreasing");
+ *  row = tbl.addRow();
+ *  tbl.setCell(row, colPlanet,  "Mars");
+ *  tbl.setCell(row, colInhabit, "Tolerability", "Miserable");
+ *  row = tbl.addRow();
+ *  tbl.setCell(row, colPlanet,  "Neptune");
+ *  tbl.setCell(row, colInhabit, "Plausibility", "Forget it");
+ *
+ *  tbl.displayTable();
+ *
+ *   Planet  Inhabitability
+ *    Earth  Viability    = Decreasing
+ *     Mars  Tolerability = Miserable
+ *  Neptune  Plausibility = Forget it
+ *
+ *  (note:
+ *     Column headers displayed in bold red to distinguish from data)
+ *
+ */
+
+#ifndef ___SELFSIZINGTABLE_H
+#define ___SELFSIZINGTABLE_H
+
+#include <iprt/types.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/message.h>
+#include <iprt/stream.h>
+
+#define ANSI_BOLD  "\x1b[1m"                        /** ANSI terminal esc. seq [CSI] to switch font to bold */
+#define ANSI_BLACK "\x1b[30m"                       /** ANSI terminal esc. seq [CSI] to switch font to black */
+#define ANSI_RED   "\x1b[31m"                       /** ANSI terminal esc. seq [CSI] to switch font to red */
+#define ANSI_RESET "\x1b[m"                         /** ANSI terminal esc. seq to reset terminal attributes mode */
+
+#define HDRLABEL_MAX                30              /** Maximum column header label length (for RTStrNLen()) */
+#define COLUMN_WIDTH_MAX            256             /** Maximum width of a display column */
+
+typedef class SelfSizingTable
+{
+    public:
+        SelfSizingTable(int cbDefaultPadding = 1);
+        ~SelfSizingTable();
+        void *addCol(const char *pszHdr, const char *pszFmt, int8_t align = LEFT, int8_t padRight = 0);
+        void *addRow();
+        void setCell(void *row, void *col, ...);
+        void displayTable();
+
+   private:
+        typedef struct ColDesc {
+            struct ColDesc *next;
+            char    *pszHdr;
+            uint8_t  hdrLen;
+            char    *pszFmt;
+            int8_t   alignment;
+            uint8_t  cbPadRightOpt;
+            uint8_t  cbWidestDataInCol;
+        } COLDESC;
+
+        typedef struct ColData
+        {
+            struct ColData *next;
+            COLDESC *pColDesc;
+            char    *pszData;
+            uint8_t  cbData;
+        } COLDATA;
+
+        typedef struct Row
+        {
+            struct Row *next;
+            uint32_t id;
+            COLDATA colDataListhead;
+        } ROW;
+
+        int cbDefaultColPadding;
+        COLDESC colDescListhead;
+        ROW rowListhead;
+
+    public:
+        enum Alignment /* column/cell alignment */
+        {
+             CENTER = 0, RIGHT = 1, LEFT = -1,
+        };
+
+} SELFSIZINGTABLE;
+
+SELFSIZINGTABLE::SelfSizingTable(int cbDefaultPadding)
+{
+    this->cbDefaultColPadding = cbDefaultPadding;
+    colDescListhead.next = NULL;
+    rowListhead.next = NULL;
+}
+SELFSIZINGTABLE::~SelfSizingTable()
+{
+    COLDESC *pColDesc = colDescListhead.next;
+    while (pColDesc)
+    {
+        COLDESC *pColDescNext = pColDesc->next;
+        RTMemFree(pColDesc->pszHdr);
+        RTMemFree(pColDesc->pszFmt);
+        delete pColDesc;
+        pColDesc = pColDescNext;
+    }
+    ROW *pRow = rowListhead.next;
+    while(pRow)
+    {
+        ROW *pRowNext = pRow->next;
+        COLDATA *pColData = pRow->colDataListhead.next;
+        while (pColData)
+        {
+            COLDATA *pColDataNext = pColData->next;
+            delete pColData->pszData;
+            delete pColData;
+            pColData = pColDataNext;
+        }
+        delete pRow;
+        pRow = pRowNext;
+    }
+}
+
+void *SELFSIZINGTABLE::addCol(const char *pszHdr, const char *pszFmt, int8_t align, int8_t padRight)
+{
+    COLDESC *pColDescNew = new COLDESC();
+    if (!pColDescNew)
+    {
+        RTMsgErrorExitFailure("out of memory");
+        return NULL;
+    }
+    pColDescNew->pszHdr = RTStrDup(pszHdr);
+    pColDescNew->hdrLen = RTStrNLen(pszHdr, HDRLABEL_MAX);
+    pColDescNew->pszFmt = RTStrDup(pszFmt);
+    pColDescNew->alignment = align;
+    pColDescNew->cbPadRightOpt = padRight;
+    COLDESC *pColDesc = &colDescListhead;
+
+    while (pColDesc->next)
+        pColDesc = pColDesc->next;
+
+    pColDesc->next = pColDescNew;
+    return (void *)pColDescNew;
+}
+
+void *SELFSIZINGTABLE::addRow()
+{
+    ROW *pNewRow = new Row();
+    COLDESC *pColDesc = colDescListhead.next;
+    COLDATA *pCurColData = &pNewRow->colDataListhead;
+    while (pColDesc)
+    {
+        COLDATA *pNewColData = new COLDATA();
+        pNewColData->pColDesc = pColDesc;
+        pCurColData = pCurColData->next = pNewColData;
+        pColDesc = pColDesc->next;
+    }
+    ROW *pRow = &rowListhead;
+    while (pRow->next)
+        pRow = pRow->next;
+    pRow->next = pNewRow;
+    return (void *)pNewRow;
+}
+
+void SELFSIZINGTABLE::setCell(void *row, void *col, ...)
+{
+    ROW *pRow = (ROW *)row;
+    COLDESC *pColDesc = (COLDESC *)col;
+    va_list ap;
+    va_start(ap, col);
+
+    char *pszData = new char[COLUMN_WIDTH_MAX];
+    int cbData = RTStrPrintfV(pszData, COLUMN_WIDTH_MAX, pColDesc->pszFmt, ap);
+    COLDATA *pColData = pRow->colDataListhead.next;
+    while (pColData)
+    {
+        if (pColData->pColDesc == pColDesc)
+        {
+            pColData->pszData = pszData;
+            pColData->cbData = cbData;
+            break;
+        }
+        pColData = pColData->next;
+    }
+}
+
+void SELFSIZINGTABLE::displayTable()
+{
+    /* Determine max cell (and column header) length for each column */
+
+    COLDESC *pColDesc = colDescListhead.next;
+    while (pColDesc)
+    {
+        pColDesc->cbWidestDataInCol = pColDesc->hdrLen;
+        pColDesc = pColDesc->next;
+    }
+    ROW *pRow = rowListhead.next;
+    while(pRow)
+    {
+        COLDATA *pColData = pRow->colDataListhead.next;
+        while (pColData)
+        {
+            pColDesc = pColData->pColDesc;
+            if (pColData->cbData > pColDesc->cbWidestDataInCol)
+                pColDesc->cbWidestDataInCol = pColData->cbData;;
+            pColData = pColData->next;
+        }
+        pRow = pRow->next;
+    }
+
+    /* Display col headers based on actual column size w/alignment & padding */
+    pColDesc = colDescListhead.next;
+    while (pColDesc)
+    {
+        uint8_t colWidth = pColDesc->cbWidestDataInCol;
+        char colHdr[colWidth + 1], *pszColHdr = (char *)colHdr;
+        switch (pColDesc->alignment)
+        {
+            case RIGHT:
+                RTStrPrintf(pszColHdr, colWidth + 1, "%*s", colWidth, pColDesc->pszHdr);
+                break;
+            case LEFT:
+                RTStrPrintf(pszColHdr, colWidth + 1, "%-*s", colWidth, pColDesc->pszHdr);
+                break;
+            case CENTER:
+                int cbPad = (colWidth - pColDesc->hdrLen) / 2;
+                RTStrPrintf(pszColHdr, colWidth + 1, "%*s%s%*s", cbPad, "", pColDesc->pszHdr, cbPad, "");
+        }
+        RTPrintf(ANSI_BOLD ANSI_RED);
+        uint8_t cbPad = pColDesc->cbPadRightOpt ? pColDesc->cbPadRightOpt : cbDefaultColPadding;
+        RTPrintf("%s%*s", pszColHdr, cbPad, " ");
+        RTPrintf(ANSI_RESET);
+        pColDesc = pColDesc->next;
+    }
+    RTPrintf("\n");
+    /*
+     * Display each of the column data items for the row
+     */
+    pRow = rowListhead.next;
+    while(pRow)
+    {
+        COLDATA *pColData = pRow->colDataListhead.next;
+        while (pColData)
+        {   pColDesc = pColData->pColDesc;
+            uint8_t colWidth = pColDesc->cbWidestDataInCol;
+            char aCell[colWidth + 1];
+            switch (pColDesc->alignment)
+            {
+                case RIGHT:
+                    RTStrPrintf(aCell, colWidth + 1, "%*s", colWidth, pColData->pszData);
+                    break;
+                case LEFT:
+                    RTStrPrintf(aCell, colWidth + 1, "%-*s", colWidth, pColData->pszData);
+                    break;
+                case CENTER:
+                    int cbPad = (colWidth - pColData->cbData) / 2;
+                    RTStrPrintf(aCell, colWidth + 1, "%*s%s%*s", cbPad, "", pColData->pszData, cbPad, "");
+            }
+            uint8_t cbPad = pColDesc->cbPadRightOpt ? pColDesc->cbPadRightOpt : this->cbDefaultColPadding;
+            RTPrintf("%s%*s", aCell, cbPad, " ");
+            pColData = pColData->next;
+        }
+        RTPrintf("\n");
+        pRow = pRow->next;
+    }
+}
+#endif
Index: /trunk/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp
===================================================================
--- /trunk/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp	(revision 75970)
+++ /trunk/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.cpp	(revision 75970)
@@ -0,0 +1,1589 @@
+/* $Id$ */
+/** @file
+ * vboximg-mount - Disk Image Flattening FUSE Program.
+ */
+
+/*
+ * Copyright (C) 2009-2018 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+
+#define LOG_GROUP LOG_GROUP_DEFAULT /** @todo log group */
+
+#define FUSE_USE_VERSION 27
+#if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined(RT_OS_FEEBSD)
+#   define UNIX_DERIVATIVE
+#endif
+#define MAX_READERS (INT32_MAX / 32)
+#include <fuse.h>
+#ifdef UNIX_DERIVATIVE
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <math.h>
+//#include <stdarg.h>
+#include <cstdarg>
+#include <sys/stat.h>
+#endif
+#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX)
+# include <sys/param.h>
+# undef PVM     /* Blasted old BSD mess still hanging around darwin. */
+#endif
+#ifdef RT_OS_LINUX
+# include <linux/fs.h>
+# include <linux/hdreg.h>
+#endif
+#include <VirtualBox_XPCOM.h>
+#include <VBox/com/VirtualBox.h>
+#include <VBox/vd.h>
+#include <VBox/vd-ifs.h>
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/NativeEventQueue.h>
+#include <VBox/com/com.h>
+#include <VBox/com/string.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/array.h>
+#include <VBox/com/errorprint.h>
+
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/message.h>
+#include <iprt/critsect.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/initterm.h>
+#include <iprt/stream.h>
+#include <iprt/types.h>
+#include <iprt/path.h>
+#include <iprt/utf16.h>
+
+#include "vboximg-mount.h"
+#include "SelfSizingTable.h"
+
+using namespace com;
+
+enum {
+     USAGE_FLAG,
+};
+
+/* For getting the basename of the image path */
+union
+{
+    RTPATHSPLIT split;
+    uint8_t     abPad[RTPATH_MAX + 1024];
+} g_u;
+
+#if 0 /* unused */
+const uint64_t KB = 1024;
+const uint64_t MB = KB * KB;
+const uint64_t GB = MB * KB;
+const uint64_t TB = GB * KB;
+const uint64_t PB = TB * KB;
+#endif
+
+enum { PARTITION_TABLE_MBR = 1, PARTITION_TABLE_GPT = 2 };
+
+#define GPT_PTABLE_SIZE             32 * BLOCKSIZE   /** Max size we to read for GPT partition table */
+#define MBR_PARTITIONS_MAX          4                /** Fixed number of partitions in Master Boot Record */
+#define BASENAME_MAX                256              /** Maximum name for the basename of a path (for RTStrNLen()*/
+#define VBOXIMG_PARTITION_MAX       256              /** How much storage to allocate to store partition info */
+#define PARTITION_NAME_MAX          72               /** Maximum partition name size (accomodates GPT partition name) */
+#define BLOCKSIZE                   512              /** Commonly used disk block size */
+#define DOS_BOOT_RECORD_SIGNATURE   0xaa55           /** MBR and EBR (partition table) signature [EOT boundary] */
+#define NULL_BOOT_RECORD_SIGNATURE  0x0000           /** MBR or EBR null signature value */
+#define MAX_UUID_LEN                256              /** Max length of a UUID */
+#define LBA(n)                      (n * BLOCKSIZE)
+#define VD_SECTOR_SIZE              512              /** Virtual disk sector/blocksize */
+#define VD_SECTOR_MASK              (VD_SECTOR_SIZE - 1)    /** Masks off a blocks worth of data */
+#define VD_SECTOR_OUT_OF_BOUNDS_MASK  (~UINT64_C(VD_SECTOR_MASK))         /** Masks the overflow of a blocks worth of data */
+
+#define IS_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)
+
+#define PARTTYPE_IS_NULL(parType) ((uint8_t)parType == 0x00)
+#define PARTTYPE_IS_GPT(parType)  ((uint8_t)parType == 0xee)
+#define PARTTYPE_IS_EXT(parType)  ((    (uint8_t)parType) == 0x05  /* Extended           */ \
+                                    || ((uint8_t)parType) == 0x0f  /* W95 Extended (LBA) */ \
+                                    || ((uint8_t)parType) == 0x85) /* Linux Extended     */
+
+#define SAFENULL(strPtr)   (strPtr ? strPtr : "")
+#define CSTR(arg)     Utf8Str(arg).c_str()          /* Converts XPCOM string type to C string type */
+
+static struct fuse_operations g_vboximgOps;         /** FUSE structure that defines allowed ops for this FS */
+
+/* Global variables */
+
+static PVDISK                g_pVDisk;              /** Handle for Virtual Disk in contet */
+static char                 *g_pvDiskUuid;          /** UUID of image (if known, otherwise NULL) */
+static off_t                 g_vDiskOffset;         /** Biases r/w from start of VD */
+static off_t                 g_vDiskSize;           /** Limits r/w length for VD */
+static int32_t               g_cReaders;            /** Number of readers for VD */
+static int32_t               g_cWriters;            /** Number of writers for VD */
+static RTFOFF                g_cbEntireVDisk;       /** Size of VD */
+static char                 *g_pszBaseImageName;    /** Base filename for current VD image */
+static char                 *g_pszBaseImagePath;    /** Full path to current VD image */
+static PVDINTERFACE          g_pVdIfs;              /** @todo Remove when VD I/O becomes threadsafe */
+static VDINTERFACETHREADSYNC g_VDIfThreadSync;      /** @todo Remove when VD I/O becomes threadsafe */
+static RTCRITSECT            g_vdioLock;            /** @todo Remove when VD I/O becomes threadsafe */
+static uint16_t              g_lastPartNbr;         /** Last partition number found in MBR + EBR chain */
+static bool                  g_fGPT;                /** True if GPT type partition table was found */
+
+/* Table entry containing partition info parsed out of GPT or MBR and EBR chain of specified VD */
+
+typedef struct
+{
+    int            idxPartition;            /** partition number */
+    char           *pszName;
+    off_t          offPartition;            /** partition offset from start of disk, in bytes */
+    uint64_t       cbPartition;             /** partition size in bytes */
+    uint8_t        fBootable;               /** partition bootable */
+    union
+    {
+        uint8_t    legacy;                  /** partition type MBR/EBR */
+        uint128_t  gptGuidTypeSpecifier;    /** partition type GPT */
+    } partitionType;                        /** uint8_t for MBR/EBR (legacy) and GUID for GPT */
+    union
+    {
+        MBRPARTITIONENTRY mbrEntry;         /** MBR (also EBR partition entry) */
+        GPTPARTITIONENTRY gptEntry;         /** GPT partition entry */
+    } partitionEntry;
+} PARTITIONINFO;
+
+PARTITIONINFO g_aParsedPartitionInfo[VBOXIMG_PARTITION_MAX + 1]; /* Note: Element 0 reserved for EntireDisk partitionEntry */
+
+static struct vboximgOpts {
+     char         *pszVm;                   /** optional VM UUID */
+     char         *pszImage;                /** Virtual Disk image UUID or path */
+     int32_t       idxPartition;            /** Number of partition to constrain FUSE based FS to (optional) 0 - whole disk*/
+     int32_t       offset;                  /** Offset to base virtual disk reads and writes from (altnerative to partition) */
+     int32_t       size;                    /** Size of accessible disk region, starting at offset, default = offset 0 */
+     uint32_t      cHddImageDiffMax;        /** Max number of differencing images (snapshots) to apply to image */
+     uint32_t      fListMedia;              /** Flag to list virtual disks of all known VMs */
+     uint32_t      fListMediaBrief;         /** Flag to list virtual disks of all known VMs */
+     uint32_t      fListParts;              /** Flag to summarily list partitions associated with pszImage */
+     uint32_t      fAllowRoot;              /** Flag to allow root to access this FUSE FS */
+     uint32_t      fRW;                     /** Flag to allow changes to FUSE-mounted Virtual Disk image */
+     uint32_t      fBriefUsage;             /** Flag to display only FS-specific program usage options */
+     uint32_t      fVerbose;                /** Make some noise */
+} g_vboximgOpts;
+
+#define OPTION(fmt, pos, val) { fmt, offsetof(struct vboximgOpts, pos), val }
+
+static struct fuse_opt vboximgOptDefs[] = {
+    OPTION("-l",              fListMediaBrief,   1),
+    OPTION("-L",              fListMedia,        1),
+    OPTION("-t",              fListParts,        1),
+    OPTION("--root",          fAllowRoot,        1),
+    OPTION("--vm=%s",         pszVm,             0),
+    OPTION("--maxdiff=%d",    cHddImageDiffMax,  1),
+    OPTION("--diff=%d",       cHddImageDiffMax,  1),
+    OPTION("--partition=%d",  idxPartition,      1),
+    OPTION("-p %d",           idxPartition,      1),
+    OPTION("--offset=%d",     offset,            1),
+    OPTION("-o %d",           offset,            1),
+    OPTION("--size=%d",       size,              1),
+    OPTION("-s %d",           size,              1),
+    OPTION("--image=%s",      pszImage,          0),
+    OPTION("-i %s",           pszImage,          0),
+    OPTION("--rw",            fRW,               1),
+    OPTION("--verbose",       fVerbose,          1),
+    OPTION("-v",              fVerbose,          1),
+    OPTION("-h",              fBriefUsage,       1),
+    FUSE_OPT_KEY("--help",    USAGE_FLAG),
+    FUSE_OPT_END
+};
+
+static void
+briefUsage()
+{
+    RTPrintf("usage: vboximg-mount [options] <mountpoint>\n\n"
+        "vboximg-mount options:\n\n"
+        "    [ -l ]                                     List virtual disk media (brief version)\n"
+        "    [ -L ]                                     List virtual disk media (long version)\n"
+        "    [ -t ]                                     List partition table (requires -i or --image option)\n"
+        "\n"
+        "    [ { -i | --image= } <UUID | name | path> ] Virtual Box disk image to use\n"
+        "\n"
+        "    [ { -p | --partition= } <partition #> ]    Mount specified partition number via FUSE\n"
+        "\n"
+        "    [ { -o | --offset= } <byte #> ]            Disk I/O will be based on offset from disk start\n"
+        "                                               (Can't use with -p or --partition options)\n"
+        "\n"
+        "    [ -s | --size=<bytes>]                     Sets size of mounted disk from disk start or from\n"
+        "                                               offset, if specified. (Can't use with\n"
+        "                                               -p or --partition options)\n"
+        "\n"
+        "    [ --diff=<diff #> ]                        Apply diffs (snapshot differencing disk images)\n"
+        "                                               to specified base disk image up to and including\n"
+        "                                               specified diff number.\n"
+        "                                               (0 = Apply no diffs, default = Apply all diffs)\n"
+        "\n"
+        "    [ --rw]                                    Make image writeable (default = readonly)\n"
+        "    [ --root]                                  Same as -o allow_root\n"
+        "\n"
+        "    [ --vm < Path | UUID >]                    VM UUID (limit media list to specific VM)\n"
+        "\n"
+        "    [ --verbose]                               Log extra information\n"
+        "    -o opt[,opt...]                            FUSE mount options\n"
+        "    -h                                         Display short usage info showing only the above\n"
+        "    --help                                     Display long usage info (including FUSE opts)\n\n"
+    );
+    RTPrintf("\n");
+    RTPrintf("When successful, the --image option creates a one-directory-deep filesystem \n");
+    RTPrintf("rooted at the specified mountpoint.  The contents of the directory will be \n");
+    RTPrintf("a symbolic link with the base name of the image file pointing to the path of\n");
+    RTPrintf("the virtual disk image, and a regular file named 'vhdd', which represents\n");
+    RTPrintf("the byte stream of the disk image as interpreted by VirtualBox.\n");
+    RTPrintf("It is the vhdd file that the user or a utility will subsequently mount on\n");
+    RTPrintf("the host OS to gain access to the virtual disk contents.\n\n");
+    RTPrintf("If any of the partition, size or offset related options are used, the\n");
+    RTPrintf("constraining start offset (in bytes) and size (in bytes) will be\n");
+    RTPrintf("appended in brackets to the symbolic link basename to indicate\n");
+    RTPrintf("which part of the image is exposed by the FUSE filesystem implementation.\n\n");
+}
+
+static int
+vboximgOptHandler(void *data, const char *arg, int optKey, struct fuse_args *outargs)
+{
+    (void) data;
+    (void) arg;
+    switch(optKey)
+    {
+        case USAGE_FLAG:
+            briefUsage();
+            fuse_opt_add_arg(outargs, "-ho");
+            fuse_main(outargs->argc, outargs->argv, &g_vboximgOps, NULL);
+            break;
+    }
+    return 1;
+}
+
+/** @copydoc fuse_operations::open */
+static int vboximgOp_open(const char *pszPath, struct fuse_file_info *pInfo)
+{
+    (void) pInfo;
+    LogFlowFunc(("pszPath=%s\n", pszPath));
+    uint32_t notsup = 0;
+    int rc = 0;
+
+#ifdef UNIX_DERIVATIVE
+#   ifdef RT_OS_DARWIN
+        notsup = O_APPEND | O_NONBLOCK | O_SYMLINK | O_NOCTTY | O_SHLOCK | O_EXLOCK |
+                 O_ASYNC  | O_CREAT    | O_TRUNC   | O_EXCL | O_EVTONLY;
+#   elif defined(RT_OS_LINUX)
+        notsup = O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
+                 /* | O_LARGEFILE | O_SYNC | ? */
+#   elif defined(RT_OS_FREEBSD)
+        notsup = O_APPEND | O_ASYNC | O_DIRECT | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
+                 /* | O_LARGEFILE | O_SYNC | ? */
+#   endif
+#else
+#   error "Port me"
+#endif
+
+if (pInfo->flags & notsup)
+    rc -EINVAL;
+
+#ifdef UNIX_DERIVATIVE
+    if ((pInfo->flags & O_ACCMODE) == O_ACCMODE)
+        rc = -EINVAL;
+#   ifdef O_DIRECTORY
+    if (pInfo->flags & O_DIRECTORY)
+        rc = -ENOTDIR;
+#   endif
+#endif
+
+    if (RT_FAILURE(rc))
+    {
+        LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath));
+        return rc;
+    }
+
+    int fWriteable =    (pInfo->flags & O_ACCMODE) == O_WRONLY
+                     || (pInfo->flags & O_ACCMODE) == O_RDWR;
+    if (g_cWriters)
+        rc = -ETXTBSY;
+    else
+    {
+            if (fWriteable)
+                g_cWriters++;
+            else
+            {
+                if (g_cReaders + 1 > MAX_READERS)
+                    rc = -EMLINK;
+                else
+                    g_cReaders++;
+            }
+    }
+    LogFlowFunc(("rc=%d \"%s\"\n", rc, pszPath));
+    return rc;
+
+}
+
+/** @todo Remove when VD I/O becomes threadsafe */
+static DECLCALLBACK(int) vboximgThreadStartRead(void *pvUser)
+{
+    PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
+    return RTCritSectEnter(vdioLock);
+}
+
+static DECLCALLBACK(int) vboximgThreadFinishRead(void *pvUser)
+{
+    PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
+    return RTCritSectLeave(vdioLock);
+}
+
+static DECLCALLBACK(int) vboximgThreadStartWrite(void *pvUser)
+{
+    PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
+    return RTCritSectEnter(vdioLock);
+}
+
+static DECLCALLBACK(int) vboximgThreadFinishWrite(void *pvUser)
+{
+    PRTCRITSECT vdioLock = (PRTCRITSECT)pvUser;
+    return RTCritSectLeave(vdioLock);
+}
+/** @todo (end of to do section) */
+
+/** @copydoc fuse_operations::release */
+static int vboximgOp_release(const char *pszPath, struct fuse_file_info *pInfo)
+{
+    (void) pszPath;
+
+    LogFlowFunc(("pszPath=%s\n", pszPath));
+
+    if (    (pInfo->flags & O_ACCMODE) == O_WRONLY
+        ||  (pInfo->flags & O_ACCMODE) == O_RDWR)
+    {
+        g_cWriters--;
+        Assert(g_cWriters >= 0);
+    }
+    else if ((pInfo->flags & O_ACCMODE) == O_RDONLY)
+    {
+        g_cReaders--;
+        Assert(g_cReaders >= 0);
+    }
+    else
+        AssertFailed();
+
+    LogFlowFunc(("\"%s\"\n", pszPath));
+    return 0;
+}
+
+/**
+ * VD read Sanitizer taking care of unaligned accesses.
+ *
+ * @return  VBox bootIndicator code.
+ * @param   pDisk    VD disk container.
+ * @param   off      Offset to start reading from.
+ * @param   pvBuf    Pointer to the buffer to read into.
+ * @param   cbRead   Amount of bytes to read.
+ */
+static int vdReadSanitizer(PVDISK pDisk, uint64_t off, void *pvBuf, size_t cbRead)
+{
+    int rc;
+
+    uint64_t const cbMisalignmentOfStart = off & VD_SECTOR_MASK;
+    uint64_t const cbMisalignmentOfEnd  = (off + cbRead) & VD_SECTOR_MASK;
+
+    if (cbMisalignmentOfStart + cbMisalignmentOfEnd == 0) /* perfectly aligned request; just read it and done */
+        rc = VDRead(pDisk, off, pvBuf, cbRead);
+    else
+    {
+        uint8_t *pbBuf = (uint8_t *)pvBuf;
+        uint8_t abBuf[VD_SECTOR_SIZE];
+
+        /* If offset not @ sector boundary, read whole sector, then copy unaligned
+         * bytes (requested by user), only up to sector boundary, into user's buffer
+         */
+        if (cbMisalignmentOfStart)
+        {
+            rc = VDRead(pDisk, off - cbMisalignmentOfStart, abBuf, VD_SECTOR_SIZE);
+            if (RT_SUCCESS(rc))
+            {
+                size_t const cbPartial = RT_MIN(VD_SECTOR_SIZE - cbMisalignmentOfStart, cbRead);
+                memcpy(pbBuf, &abBuf[cbMisalignmentOfStart], cbPartial);
+                pbBuf  += cbPartial;
+                off    += cbPartial; /* Beginning of next sector or EOD */
+                cbRead -= cbPartial; /* # left to read */
+            }
+        }
+        else /* user's offset already aligned, did nothing */
+            rc = VINF_SUCCESS;
+
+        /* Read remaining aligned sectors, deferring any tail-skewed bytes */
+        if (RT_SUCCESS(rc) && cbRead >= VD_SECTOR_SIZE)
+        {
+            Assert(!(off % VD_SECTOR_SIZE));
+
+            size_t cbPartial = cbRead - cbMisalignmentOfEnd;
+            Assert(!(cbPartial % VD_SECTOR_SIZE));
+            rc = VDRead(pDisk, off, pbBuf, cbPartial);
+            if (RT_SUCCESS(rc))
+            {
+                pbBuf  += cbPartial;
+                off    += cbPartial;
+                cbRead -= cbPartial;
+            }
+        }
+
+        /* Unaligned buffered read of tail. */
+        if (RT_SUCCESS(rc) && cbRead)
+        {
+            Assert(cbRead == cbMisalignmentOfEnd);
+            Assert(cbRead < VD_SECTOR_SIZE);
+            Assert(!(off % VD_SECTOR_SIZE));
+
+            rc = VDRead(pDisk, off, abBuf, VD_SECTOR_SIZE);
+            if (RT_SUCCESS(rc))
+                memcpy(pbBuf, abBuf, cbRead);
+        }
+    }
+
+    if (RT_FAILURE(rc))
+    {
+        int sysrc = -RTErrConvertToErrno(rc);
+        LogFlowFunc(("error: %s (vbox err: %d)\n", strerror(sysrc), rc));
+        rc = sysrc;
+    }
+    return cbRead;
+}
+
+/**
+ * VD write Sanitizer taking care of unaligned accesses.
+ *
+ * @return  VBox bootIndicator code.
+ * @param   pDisk    VD disk container.
+ * @param   off      Offset to start writing to.
+ * @param   pvSrc    Pointer to the buffer to read from.
+ * @param   cbWrite  Amount of bytes to write.
+ */
+static int vdWriteSanitizer(PVDISK pDisk, uint64_t off, const void *pvSrc, size_t cbWrite)
+{
+    uint8_t const *pbSrc = (uint8_t const *)pvSrc;
+    uint8_t        abBuf[4096];
+    int rc;
+    int cbRemaining = cbWrite;
+
+    /*
+     * Take direct route if the request is sector aligned.
+     */
+    uint64_t const cbMisalignmentOfStart = off & VD_SECTOR_MASK;
+    size_t   const cbMisalignmentOfEnd  = (off + cbWrite) & VD_SECTOR_MASK;
+    if (!cbMisalignmentOfStart && !cbMisalignmentOfEnd)
+    {
+          rc = VDWrite(pDisk, off, pbSrc, cbWrite);
+          do
+            {
+                size_t cbThisWrite = RT_MIN(cbWrite, sizeof(abBuf));
+                rc = VDWrite(pDisk, off, memcpy(abBuf, pbSrc, cbThisWrite), cbThisWrite);
+                if (RT_SUCCESS(rc))
+                {
+                    pbSrc   += cbThisWrite;
+                    off     += cbThisWrite;
+                    cbRemaining -= cbThisWrite;
+                }
+                else
+                    break;
+            } while (cbRemaining > 0);
+    }
+    else
+    {
+        /*
+         * Unaligned buffered read+write of head.  Aligns the offset.
+         */
+        if (cbMisalignmentOfStart)
+        {
+            rc = VDRead(pDisk, off - cbMisalignmentOfStart, abBuf, VD_SECTOR_SIZE);
+            if (RT_SUCCESS(rc))
+            {
+                size_t const cbPartial = RT_MIN(VD_SECTOR_SIZE - cbMisalignmentOfStart, cbWrite);
+                memcpy(&abBuf[cbMisalignmentOfStart], pbSrc, cbPartial);
+                rc = VDWrite(pDisk, off - cbMisalignmentOfStart, abBuf, VD_SECTOR_SIZE);
+                if (RT_SUCCESS(rc))
+                {
+                    pbSrc   += cbPartial;
+                    off     += cbPartial;
+                    cbRemaining -= cbPartial;
+                }
+            }
+        }
+        else
+            rc = VINF_SUCCESS;
+
+        /*
+         * Aligned direct write.
+         */
+        if (RT_SUCCESS(rc) && cbWrite >= VD_SECTOR_SIZE)
+        {
+            Assert(!(off % VD_SECTOR_SIZE));
+            size_t cbPartial = cbWrite - cbMisalignmentOfEnd;
+            Assert(!(cbPartial % VD_SECTOR_SIZE));
+            rc = VDWrite(pDisk, off, pbSrc, cbPartial);
+            if (RT_SUCCESS(rc))
+            {
+                pbSrc   += cbPartial;
+                off     += cbPartial;
+                cbRemaining -= cbPartial;
+            }
+        }
+
+        /*
+         * Unaligned buffered read + write of tail.
+         */
+        if (   RT_SUCCESS(rc) && cbWrite > 0)
+        {
+            Assert(cbWrite == cbMisalignmentOfEnd);
+            Assert(cbWrite < VD_SECTOR_SIZE);
+            Assert(!(off % VD_SECTOR_SIZE));
+            rc = VDRead(pDisk, off, abBuf, VD_SECTOR_SIZE);
+            if (RT_SUCCESS(rc))
+            {
+                memcpy(abBuf, pbSrc, cbWrite);
+                rc = VDWrite(pDisk, off, abBuf, VD_SECTOR_SIZE);
+            }
+        }
+    }
+    if (RT_FAILURE(rc))
+    {
+        int sysrc = -RTErrConvertToErrno(rc);
+        LogFlowFunc(("error: %s (vbox err: %d)\n", strerror(sysrc), rc));
+        return sysrc;
+    }
+    return cbWrite - cbRemaining;
+}
+
+
+/** @copydoc fuse_operations::read */
+static int vboximgOp_read(const char *pszPath, char *pbBuf, size_t cbBuf,
+                           off_t offset, struct fuse_file_info *pInfo)
+{
+    (void) pszPath;
+    (void) pInfo;
+
+    LogFlowFunc(("my offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
+
+    AssertReturn(offset >= 0, -EINVAL);
+    AssertReturn((int)cbBuf >= 0, -EINVAL);
+    AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
+
+    AssertReturn(offset + g_vDiskOffset >= 0, -EINVAL);
+    int64_t adjOff = offset + g_vDiskOffset;
+
+    int rc = 0;
+    if ((off_t)(adjOff + cbBuf) < adjOff)
+        rc = -EINVAL;
+    else if (adjOff >= g_vDiskSize)
+        return 0;
+    else if (!cbBuf)
+        return 0;
+
+    if (rc >= 0)
+        rc = vdReadSanitizer(g_pVDisk, adjOff, pbBuf, cbBuf);
+    if (rc < 0)
+        LogFlowFunc(("%s\n", strerror(rc)));
+    return rc;
+}
+
+/** @copydoc fuse_operations::write */
+static int vboximgOp_write(const char *pszPath, const char *pbBuf, size_t cbBuf,
+                           off_t offset, struct fuse_file_info *pInfo)
+{
+    (void) pszPath;
+    (void) pInfo;
+
+    LogFlowFunc(("offset=%#llx size=%#zx path=\"%s\"\n", (uint64_t)offset, cbBuf, pszPath));
+
+    AssertReturn(offset >= 0, -EINVAL);
+    AssertReturn((int)cbBuf >= 0, -EINVAL);
+    AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL);
+    AssertReturn(offset + g_vDiskOffset >= 0, -EINVAL);
+    int64_t adjOff = offset + g_vDiskOffset;
+
+    int rc = 0;
+    if (!g_vboximgOpts.fRW) {
+        LogFlowFunc(("WARNING: vboximg-mount (FUSE FS) --rw option not specified\n"
+                     "              (write operation ignored w/o error!)\n"));
+        return cbBuf;
+    } else if ((off_t)(adjOff + cbBuf) < adjOff)
+        rc = -EINVAL;
+    else if (offset >= g_vDiskSize)
+        return 0;
+    else if (!cbBuf)
+        return 0;
+
+    if (rc >= 0)
+        rc = vdWriteSanitizer(g_pVDisk, adjOff, pbBuf, cbBuf);
+    if (rc < 0)
+        LogFlowFunc(("%s\n", strerror(rc)));
+
+    return rc;
+}
+
+/** @copydoc fuse_operations::getattr */
+static int
+vboximgOp_getattr(const char *pszPath, struct stat *stbuf)
+{
+    int rc = 0;
+
+    LogFlowFunc(("pszPath=%s, stat(\"%s\")\n", pszPath, g_pszBaseImagePath));
+
+    memset(stbuf, 0, sizeof(struct stat));
+
+    if (RTStrCmp(pszPath, "/") == 0)
+    {
+        stbuf->st_mode = S_IFDIR | 0755;
+        stbuf->st_nlink = 2;
+    }
+    else if (RTStrCmp(pszPath + 1, "vhdd") == 0)
+    {
+        rc = stat(g_pszBaseImagePath, stbuf);
+        if (rc < 0)
+            return rc;
+        /*
+         * st_size represents the size of the FUSE FS-mounted portion of the disk.
+         * By default it is the whole disk, but can be a partition or specified
+         * (or overridden) directly by the { -s | --size } option on the command line.
+         */
+        stbuf->st_size = g_vDiskSize;
+        stbuf->st_nlink = 1;
+    }
+    else if (RTStrNCmp(pszPath + 1, g_pszBaseImageName, strlen(g_pszBaseImageName)) == 0)
+    {
+        /* When the disk is partitioned, the symbolic link named from `basename` of
+         * resolved path to VBox disk image, has appended to it formatted text
+         * representing the offset range of the partition.
+         *
+         *  $ vboximg-mount -i /stroll/along/the/path/simple_fixed_disk.vdi -p 1 /mnt/tmpdir
+         *  $ ls /mnt/tmpdir
+         *  simple_fixed_disk.vdi[20480:2013244928]    vhdd
+         */
+        rc = stat(g_pszBaseImagePath, stbuf);
+        if (rc < 0)
+            return rc;
+        stbuf->st_size = 0;
+        stbuf->st_mode = S_IFLNK | 0444;
+        stbuf->st_nlink = 1;
+        stbuf->st_uid = 0;
+        stbuf->st_gid = 0;
+    } else
+        rc = -ENOENT;
+
+    return rc;
+}
+
+/** @copydoc fuse_operations::readdir */
+static int
+vboximgOp_readdir(const char *pszPath, void *pvBuf, fuse_fill_dir_t pfnFiller,
+                              off_t offset, struct fuse_file_info *pInfo)
+
+{
+
+    (void) offset;
+    (void) pInfo;
+
+    if (RTStrCmp(pszPath, "/") != 0)
+        return -ENOENT;
+
+    /*
+     *  mandatory '.', '..', ...
+     */
+    pfnFiller(pvBuf, ".", NULL, 0);
+    pfnFiller(pvBuf, "..", NULL, 0);
+
+    /*
+     * Create FUSE FS dir entry that is depicted here (and exposed via stat()) as
+     * a symbolic link back to the resolved path to the VBox virtual disk image,
+     * whose symlink name is basename that path. This is a convenience so anyone
+     * listing the dir can figure out easily what the vhdd FUSE node entry
+     * represents.
+     */
+
+    if (g_vDiskOffset == 0 && (g_vDiskSize == 0 || g_vDiskSize == g_cbEntireVDisk))
+        pfnFiller(pvBuf, g_pszBaseImageName, NULL, 0);
+    else
+    {
+        char tmp[BASENAME_MAX];
+        RTStrPrintf(tmp, sizeof (tmp), "%s[%d:%d]", g_pszBaseImageName, g_vDiskOffset, g_vDiskSize);
+        pfnFiller(pvBuf, tmp, NULL, 0);
+    }
+    /*
+     * Create entry named "vhdd", which getattr() will describe as a
+     * regular file, and thus will go through the open/release/read/write vectors
+     * to access the VirtualBox image as processed by the IRPT VD API.
+     */
+    pfnFiller(pvBuf, "vhdd", NULL, 0);
+    return 0;
+}
+
+/** @copydoc fuse_operations::readlink */
+static int
+vboximgOp_readlink(const char *pszPath, char *buf, size_t size)
+{
+    (void) pszPath;
+    RTStrCopy(buf, size, g_pszBaseImagePath);
+    return 0;
+}
+
+static void
+listMedia(IMachine *pMachine, char *vmName, char *vmUuid)
+{
+    int rc = 0;
+    com::SafeIfaceArray<IMediumAttachment> pMediumAttachments;
+
+    CHECK_ERROR(pMachine, COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(pMediumAttachments)));
+    int firstIteration = 1;
+    for (size_t i = 0; i < pMediumAttachments.size(); i++)
+    {
+
+        ComPtr<IMedium> pMedium;
+        DeviceType_T deviceType;
+        Bstr pMediumUuid;
+        Bstr pMediumName;
+        Bstr pMediumPath;
+
+        CHECK_ERROR(pMediumAttachments[i], COMGETTER(Type)(&deviceType));
+
+        if (deviceType == DeviceType_HardDisk)
+        {
+            CHECK_ERROR(pMediumAttachments[i], COMGETTER(Medium)(pMedium.asOutParam()));
+            if (pMedium.isNull())
+                return;
+
+            MediumState_T state;
+            CHECK_ERROR(pMedium, COMGETTER(State)(&state));
+            if (FAILED(rc))
+                return;
+            if (state == MediumState_Inaccessible)
+            {
+                CHECK_ERROR(pMedium, RefreshState(&state));
+                if (FAILED(rc))
+                return;
+            }
+
+            ComPtr<IMedium> pEarliestAncestor;
+            CHECK_ERROR(pMedium, COMGETTER(Base)(pEarliestAncestor.asOutParam()));
+            ComPtr<IMedium> pChild = pEarliestAncestor;
+            uint32_t ancestorNumber = 0;
+            if (pEarliestAncestor.isNull())
+                return;
+            do
+            {
+                com::SafeIfaceArray<IMedium> aMediumChildren;
+                CHECK_ERROR(pChild, COMGETTER(Name)(pMediumName.asOutParam()));
+                CHECK_ERROR(pChild, COMGETTER(Id)(pMediumUuid.asOutParam()));
+                CHECK_ERROR(pChild, COMGETTER(Location)(pMediumPath.asOutParam()));
+
+                if (ancestorNumber == 0)
+                {
+                    if (!g_vboximgOpts.fListMediaBrief)
+                    {
+                        RTPrintf("   -----------------------\n");
+                        RTPrintf("   HDD base:   \"%s\"\n",   CSTR(pMediumName));
+                        RTPrintf("   UUID:       %s\n",       CSTR(pMediumUuid));
+                        RTPrintf("   Location:   %s\n\n",     CSTR(pMediumPath));
+                    }
+                    else
+                    {
+                        if (firstIteration)
+                            RTPrintf("\nVM:    %s " ANSI_BOLD "%-20s" ANSI_RESET "\n",
+                                vmUuid, vmName);
+                        RTPrintf("  img: %s " ANSI_BOLD "  %s" ANSI_RESET "\n",
+                            CSTR(pMediumUuid), CSTR(pMediumName));
+                    }
+                }
+                else
+                {
+                    if (!g_vboximgOpts.fListMediaBrief)
+                    {
+                        RTPrintf("     Diff %d:\n", ancestorNumber);
+                        RTPrintf("          UUID:       %s\n",    CSTR(pMediumUuid));
+                        RTPrintf("          Location:   %s\n",  CSTR(pMediumPath));
+                    }
+                }
+                CHECK_ERROR_BREAK(pChild, COMGETTER(Children)(ComSafeArrayAsOutParam(aMediumChildren)));
+                pChild = (aMediumChildren.size()) ? aMediumChildren[0] : NULL;
+                ++ancestorNumber;
+                firstIteration = 0;
+            } while(pChild);
+        }
+    }
+}
+/**
+ * Display all registered VMs on the screen with some information about each
+ *
+ * @param virtualBox VirtualBox instance object.
+ */
+static void
+listVMs(IVirtualBox *pVirtualBox)
+{
+    HRESULT rc = 0;
+    com::SafeIfaceArray<IMachine> pMachines;
+    CHECK_ERROR(pVirtualBox, COMGETTER(Machines)(ComSafeArrayAsOutParam(pMachines)));
+    for (size_t i = 0; i < pMachines.size(); ++i)
+    {
+        ComPtr<IMachine> pMachine = pMachines[i];
+        if (pMachine)
+        {
+            BOOL fAccessible;
+            CHECK_ERROR(pMachines[i], COMGETTER(Accessible)(&fAccessible));
+            if (fAccessible)
+            {
+                Bstr pMachineName;
+                Bstr pMachineUuid;
+                Bstr pDescription;
+                Bstr pMachineLocation;
+
+                CHECK_ERROR(pMachine, COMGETTER(Name)(pMachineName.asOutParam()));
+                CHECK_ERROR(pMachine, COMGETTER(Id)(pMachineUuid.asOutParam()));
+                CHECK_ERROR(pMachine, COMGETTER(Description)(pDescription.asOutParam()));
+                CHECK_ERROR(pMachine, COMGETTER(SettingsFilePath)(pMachineLocation.asOutParam()));
+
+                if (   g_vboximgOpts.pszVm == NULL
+                    || RTStrNCmp(CSTR(pMachineUuid), g_vboximgOpts.pszVm, MAX_UUID_LEN) == 0
+                    || RTStrNCmp((const char *)pMachineName.raw(), g_vboximgOpts.pszVm, MAX_UUID_LEN) == 0)
+                {
+                    if (!g_vboximgOpts.fListMediaBrief)
+                    {
+                        RTPrintf("------------------------------------------------------\n");
+                        RTPrintf("VM Name:   \"%s\"\n", CSTR(pMachineName));
+                        RTPrintf("UUID:      %s\n",     CSTR(pMachineUuid));
+                        if (*pDescription.raw() != '\0')
+                            RTPrintf("Description:  %s\n",      CSTR(pDescription));
+                        RTPrintf("Location:  %s\n",      CSTR(pMachineLocation));
+                    }
+                    listMedia(pMachine, RTStrDup(CSTR(pMachineName)), RTStrDup(CSTR(pMachineUuid)));
+                }
+                else
+                {
+                    listMedia(pMachine, RTStrDup(CSTR(pMachineName)), RTStrDup(CSTR(pMachineUuid)));
+                }
+            }
+        }
+    }
+}
+
+static void
+searchForBaseImage(IVirtualBox *pVirtualBox, char *pszImageString, ComPtr<IMedium> *pBaseImage)
+{
+    int rc = 0;
+    com::SafeIfaceArray<IMedium> aDisks;
+
+    CHECK_ERROR(pVirtualBox, COMGETTER(HardDisks)(ComSafeArrayAsOutParam(aDisks)));
+    for (size_t i = 0; i < aDisks.size() && aDisks[i]; i++)
+    {
+        if (aDisks[i])
+        {
+            Bstr pMediumUuid;
+            Bstr pMediumName;
+
+            CHECK_ERROR(aDisks[i], COMGETTER(Name)(pMediumName.asOutParam()));
+            CHECK_ERROR(aDisks[i], COMGETTER(Id)(pMediumUuid.asOutParam()));
+
+            if (   RTStrCmp(pszImageString, CSTR(pMediumUuid)) == 0
+                || RTStrCmp(pszImageString, CSTR(pMediumName)) == 0)
+            {
+                *pBaseImage = aDisks[i];
+                return;
+            }
+        }
+    }
+}
+
+uint8_t
+parsePartitionTable(void)
+{
+    MBR_t mbr;
+    EBR_t ebr;
+    PTH_t parTblHdr;
+
+    ASSERT(sizeof (mbr) == 512);
+    ASSERT(sizeof (ebr) == 512);
+    /*
+     * First entry describes entire disk as a single entity
+     */
+    g_aParsedPartitionInfo[0].idxPartition = 0;
+    g_aParsedPartitionInfo[0].offPartition = 0;
+    g_aParsedPartitionInfo[0].cbPartition = VDGetSize(g_pVDisk, 0);
+    g_aParsedPartitionInfo[0].pszName = RTStrDup("EntireDisk");
+
+    /*
+     * Currently only DOS partitioned disks are supported. Ensure this one conforms
+     */
+    int rc = vdReadSanitizer(g_pVDisk, 0, &mbr, sizeof (mbr));
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExitFailure("Error reading MBR block from disk\n");
+
+    if (mbr.signature == NULL_BOOT_RECORD_SIGNATURE)
+        return RTMsgErrorExitFailure("Unprt disk (null MBR signature)\n");
+
+    if (mbr.signature != DOS_BOOT_RECORD_SIGNATURE)
+        return RTMsgErrorExitFailure("Invalid MBR found on image with signature 0x%04hX\n",
+            mbr.signature);
+    /*
+     * Parse the four physical partition entires in the MBR (any one, and only one, can be an EBR)
+     */
+    int idxEbrPartitionInMbr = 0;
+    for (int idxPartition = 1;
+             idxPartition <= MBR_PARTITIONS_MAX;
+             idxPartition++)
+    {
+        MBRPARTITIONENTRY *pMbrPartitionEntry =
+            &g_aParsedPartitionInfo[idxPartition].partitionEntry.mbrEntry;;
+        memcpy (pMbrPartitionEntry, &mbr.partitionEntry[idxPartition - 1], sizeof (MBRPARTITIONENTRY));
+
+        if (PARTTYPE_IS_NULL(pMbrPartitionEntry->type))
+            continue;
+
+        if (PARTTYPE_IS_EXT(pMbrPartitionEntry->type))
+        {
+            if (idxEbrPartitionInMbr)
+                 return RTMsgErrorExitFailure("Multiple EBRs found found in MBR\n");
+            idxEbrPartitionInMbr = idxPartition;
+        }
+
+        PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];
+
+        ppi->idxPartition = idxPartition;
+        ppi->offPartition = (off_t) pMbrPartitionEntry->partitionLba * BLOCKSIZE;
+        ppi->cbPartition  = (off_t) pMbrPartitionEntry->partitionBlkCnt * BLOCKSIZE;
+        ppi->fBootable    = pMbrPartitionEntry->bootIndicator == 0x80;
+        ppi->partitionType.legacy = pMbrPartitionEntry->type;
+
+        g_lastPartNbr = idxPartition;
+
+        if (PARTTYPE_IS_GPT(pMbrPartitionEntry->type))
+        {
+            g_fGPT = true;
+            break;
+        }
+    }
+
+    if (g_fGPT)
+    {
+        g_lastPartNbr = 2;  /* from the 'protective MBR' */
+
+        rc = vdReadSanitizer(g_pVDisk, LBA(1), &parTblHdr, sizeof (parTblHdr));
+        if (RT_FAILURE(rc))
+            return RTMsgErrorExitFailure("Error reading Partition Table Header (LBA 1) from disk\n");
+
+        uint8_t *pTblBuf = (uint8_t *)RTMemAlloc(GPT_PTABLE_SIZE);
+
+        RTPrintf( "Virtual disk image:\n\n");
+        RTPrintf("   Path: %s\n", g_pszBaseImagePath);
+        if (g_pvDiskUuid)
+            RTPrintf("   UUID: %s\n\n", g_pvDiskUuid);
+
+        if (g_vboximgOpts.fVerbose)
+        {
+            RTPrintf("   GPT Partition Table Header:\n\n");
+            if (RTStrCmp((const char *)&parTblHdr.signature, "EFI PART") == 0)
+                RTPrintf(
+                     "      Signature               \"EFI PART\" (0x%llx)\n", parTblHdr.signature);
+            else
+                RTPrintf(
+                     "      Signature:              0x%llx\n",  parTblHdr.signature);
+            RTPrintf("      Revision:               %-8.8x\n",  parTblHdr.revision);
+            RTPrintf("      Current LBA:            %lld\n",    parTblHdr.headerLba);
+            RTPrintf("      Backup LBA:             %lld\n",    parTblHdr.backupLba);
+            RTPrintf("      Partition entries LBA:  %lld\n",    parTblHdr.partitionEntriesLba);
+            RTPrintf("      # of partitions:        %d\n",      parTblHdr.cPartitionEntries);
+            RTPrintf("      size of entry:          %d\n\n",    parTblHdr.cbPartitionEntry);
+        }
+
+        if (!pTblBuf)
+            return RTMsgErrorExitFailure("Out of memory\n");
+
+        rc = vdReadSanitizer(g_pVDisk, LBA(2), pTblBuf, GPT_PTABLE_SIZE);
+        if (RT_FAILURE(rc))
+            return RTMsgErrorExitFailure("Error reading Partition Table blocks from disk\n");
+
+        uint32_t cEntries = parTblHdr.cPartitionEntries;
+        uint32_t cbEntry  = parTblHdr.cbPartitionEntry;
+        if (cEntries * cbEntry > GPT_PTABLE_SIZE)
+        {
+            RTPrintf("Partition entries exceed GPT table read from disk (pruning!)\n");
+            while (cEntries * cbEntry > GPT_PTABLE_SIZE && cEntries > 0)
+                --cEntries;
+        }
+        uint8_t *pEntryRaw = pTblBuf;
+        for (uint32_t i = 0; i < cEntries; i++)
+        {
+            GPTPARTITIONENTRY *pEntry = (GPTPARTITIONENTRY *)pEntryRaw;
+            PARTITIONINFO *ppi = &g_aParsedPartitionInfo[g_lastPartNbr];
+            memcpy(&(ppi->partitionEntry).gptEntry, pEntry, sizeof(GPTPARTITIONENTRY));
+            if (!pEntry->firstLba)
+                break;
+            ppi->offPartition = pEntry->firstLba * BLOCKSIZE;
+            ppi->cbPartition = (pEntry->lastLba - pEntry->firstLba) * BLOCKSIZE;
+            ppi->fBootable = pEntry->attrFlags & (1 << GPT_LEGACY_BIOS_BOOTABLE);
+            ppi->partitionType.gptGuidTypeSpecifier = pEntry->partitionTypeGuid;
+            size_t cwName = sizeof (pEntry->partitionName) / 2;
+            RTUtf16LittleToUtf8Ex((PRTUTF16)pEntry->partitionName, RTSTR_MAX, &ppi->pszName, cwName, NULL);
+            ppi->idxPartition = g_lastPartNbr++;
+            pEntryRaw += cbEntry;
+        }
+        return PARTITION_TABLE_GPT;
+    }
+
+    /*
+     * Starting with EBR located in MBR, walk EBR chain to parse the logical partition entries
+     */
+    if (idxEbrPartitionInMbr)
+    {
+        uint32_t firstEbrLba
+            = g_aParsedPartitionInfo[idxEbrPartitionInMbr].partitionEntry.mbrEntry.partitionLba;
+        off_t    firstEbrOffset   = (off_t)firstEbrLba * BLOCKSIZE;
+        off_t    chainedEbrOffset = 0;
+
+        if (!firstEbrLba)
+            return RTMsgErrorExitFailure("Inconsistency for logical partition start. Aborting\n");
+
+        for (int idxPartition = 5;
+                 idxPartition <= VBOXIMG_PARTITION_MAX;
+                 idxPartition++)
+        {
+
+            off_t currentEbrOffset = firstEbrOffset + chainedEbrOffset;
+            vdReadSanitizer(g_pVDisk, currentEbrOffset, &ebr, sizeof (ebr));
+
+            if (ebr.signature != DOS_BOOT_RECORD_SIGNATURE)
+                return RTMsgErrorExitFailure("Invalid EBR found on image with signature 0x%04hX\n",
+                    ebr.signature);
+
+            MBRPARTITIONENTRY *pEbrPartitionEntry =
+                &g_aParsedPartitionInfo[idxPartition].partitionEntry.mbrEntry; /* EBR entry struct same as MBR */
+            memcpy(pEbrPartitionEntry, &ebr.partitionEntry, sizeof (MBRPARTITIONENTRY));
+
+            if (pEbrPartitionEntry->type == NULL_BOOT_RECORD_SIGNATURE)
+                return RTMsgErrorExitFailure("Logical partition with type 0 encountered");
+
+            if (!pEbrPartitionEntry->partitionLba)
+                return RTMsgErrorExitFailure("Logical partition invalid partition start offset (LBA) encountered");
+
+            PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];
+            ppi->idxPartition         = idxPartition;
+            ppi->offPartition         = currentEbrOffset + (off_t)pEbrPartitionEntry->partitionLba * BLOCKSIZE;
+            ppi->cbPartition          = (off_t)pEbrPartitionEntry->partitionBlkCnt * BLOCKSIZE;
+            ppi->fBootable            = pEbrPartitionEntry->bootIndicator == 0x80;
+            ppi->partitionType.legacy = pEbrPartitionEntry->type;
+
+            g_lastPartNbr = idxPartition;
+
+            if (ebr.chainingPartitionEntry.type == 0) /* end of chain */
+                break;
+
+            if (!PARTTYPE_IS_EXT(ebr.chainingPartitionEntry.type))
+                return RTMsgErrorExitFailure("Logical partition chain broken");
+
+            chainedEbrOffset = ebr.chainingPartitionEntry.partitionLba * BLOCKSIZE;
+        }
+    }
+    return PARTITION_TABLE_MBR;
+}
+
+const char *getClassicPartitionDesc(uint8_t type)
+{
+    for (uint32_t i = 0; i < sizeof (g_partitionDescTable) / sizeof (struct PartitionDesc); i++)
+    {
+        if (g_partitionDescTable[i].type == type)
+            return g_partitionDescTable[i].desc;
+    }
+    return "????";
+}
+
+void
+displayGptPartitionTable(void)
+{
+
+    void *colBoot = NULL;
+
+    SELFSIZINGTABLE tbl(2);
+
+    /* Note: Omitting partition name column because type/UUID seems suffcient */
+    void *colPartNbr   = tbl.addCol("#",                 "%3d",       1);
+
+    /* If none of the partitions supports legacy BIOS boot, don't show column */
+    for (int idxPartition = 2; idxPartition <= g_lastPartNbr; idxPartition++)
+        if (g_aParsedPartitionInfo[idxPartition].fBootable) {
+            colBoot    = tbl.addCol("Boot",         "%c   ",     1);
+            break;
+        }
+
+    void *colStart     = tbl.addCol("Start",             "%lld",      1);
+    void *colSectors   = tbl.addCol("Sectors",           "%lld",     -1, 2);
+    void *colSize      = tbl.addCol("Size",              "%d.%d%c",   1);
+    void *colOffset    = tbl.addCol("Offset",            "%lld",      1);
+    /* Need to see how other OSes with GPT schemes use this field. Seems like type covers it
+    void *colName      = tbl.addCol("Name",              "%s",       -1); */
+    void *colType      = tbl.addCol("Type",              "%s",       -1, 2);
+
+    for (int idxPartition = 2; idxPartition <= g_lastPartNbr; idxPartition++)
+    {
+        PARTITIONINFO *ppi = &g_aParsedPartitionInfo[idxPartition];
+        if (ppi->idxPartition)
+        {
+            uint8_t exp = log2((double)ppi->cbPartition);
+            char scaledMagnitude = ((char []){ ' ', 'K', 'M', 'G', 'T', 'P' })[exp / 10];
+
+             /* This workaround is because IPRT RT*Printf* funcs don't handle floating point format specifiers */
+            double cbPartitionScaled = (double)ppi->cbPartition / pow(2, (double)(((uint8_t)(exp / 10)) * 10));
+            uint8_t cbPartitionIntPart = cbPartitionScaled;
+            uint8_t cbPartitionFracPart = (cbPartitionScaled - (double)cbPartitionIntPart) * 10;
+
+            char abGuid[GUID_STRING_LENGTH * 2];
+            RTStrPrintf(abGuid, sizeof(abGuid), "%RTuuid",  &ppi->partitionType.gptGuidTypeSpecifier);
+
+            char *pszPartitionTypeDesc = NULL;
+            for (uint32_t i = 0; i < sizeof(g_gptPartitionTypes) / sizeof(GPTPARTITIONTYPE); i++)
+                if (RTStrNICmp(abGuid, g_gptPartitionTypes[i].gptPartitionUuid, GUID_STRING_LENGTH) == 0)
+                {
+                    pszPartitionTypeDesc =  (char *)g_gptPartitionTypes[i].gptPartitionTypeDesc;
+                    break;
+                }
+
+            if (!pszPartitionTypeDesc)
+                RTPrintf("Couldn't find GPT partitiontype for GUID: %s\n", abGuid);
+
+            void *row = tbl.addRow();
+            tbl.setCell(row, colPartNbr,    idxPartition - 1);
+            if (colBoot)
+                tbl.setCell(row, colBoot,   ppi->fBootable ? '*' : ' ');
+            tbl.setCell(row, colStart,      ppi->offPartition / BLOCKSIZE);
+            tbl.setCell(row, colSectors,    ppi->cbPartition / BLOCKSIZE);
+            tbl.setCell(row, colSize,       cbPartitionIntPart, cbPartitionFracPart, scaledMagnitude);
+            tbl.setCell(row, colOffset,     ppi->offPartition);
+/*          tbl.setCell(row, colName,       ppi->pszName);    ... see comment for column definition */
+            tbl.setCell(row, colType,       SAFENULL(pszPartitionTypeDesc));
+        }
+    }
+    tbl.displayTable();
+    RTPrintf ("\n");
+}
+
+void
+displayLegacyPartitionTable(void)
+{
+    /*
+     * Partition table is most readable and concise when headers and columns
+     * are adapted to the actual data, to avoid insufficient or excessive whitespace.
+     */
+    RTPrintf( "Virtual disk image:\n\n");
+    RTPrintf("   Path: %s\n", g_pszBaseImagePath);
+    if (g_pvDiskUuid)
+        RTPrintf("   UUID: %s\n\n", g_pvDiskUuid);
+
+    SELFSIZINGTABLE tbl(2);
+
+    void *colPartition = tbl.addCol("Partition",    "%s%d",     -1);
+    void *colBoot      = tbl.addCol("Boot",         "%c   ",     1);
+    void *colStart     = tbl.addCol("Start",        "%lld",      1);
+    void *colSectors   = tbl.addCol("Sectors",      "%lld",     -1, 2);
+    void *colSize      = tbl.addCol("Size",         "%d.%d%c",   1);
+    void *colOffset    = tbl.addCol("Offset",       "%lld",      1);
+    void *colId        = tbl.addCol("Id",           "%2x",       1);
+    void *colType      = tbl.addCol("Type",         "%s",       -1, 2);
+
+    for (int idxPartition = 1; idxPartition <= g_lastPartNbr; idxPartition++)
+    {
+        PARTITIONINFO *p = &g_aParsedPartitionInfo[idxPartition];
+        if (p->idxPartition)
+        {
+            uint8_t exp = log2((double)p->cbPartition);
+            char scaledMagnitude = ((char []){ ' ', 'K', 'M', 'G', 'T', 'P' })[exp / 10];
+
+             /* This workaround is because IPRT RT*Printf* funcs don't handle floating point format specifiers */
+            double  cbPartitionScaled = (double)p->cbPartition / pow(2, (double)(((uint8_t)(exp / 10)) * 10));
+            uint8_t cbPartitionIntPart = cbPartitionScaled;
+            uint8_t cbPartitionFracPart = (cbPartitionScaled - (double)cbPartitionIntPart) * 10;
+
+            void *row = tbl.addRow();
+
+            tbl.setCell(row, colPartition,  g_pszBaseImageName, idxPartition);
+            tbl.setCell(row, colBoot,       p->fBootable ? '*' : ' ');
+            tbl.setCell(row, colStart,      p->offPartition / BLOCKSIZE);
+            tbl.setCell(row, colSectors,    p->cbPartition / BLOCKSIZE);
+            tbl.setCell(row, colSize,       cbPartitionIntPart, cbPartitionFracPart, scaledMagnitude);
+            tbl.setCell(row, colOffset,     p->offPartition);
+            tbl.setCell(row, colId,         p->partitionType.legacy);
+            tbl.setCell(row, colType,       getClassicPartitionDesc((p->partitionType).legacy));
+        }
+    }
+    tbl.displayTable();
+    RTPrintf ("\n");
+}
+
+int
+main(int argc, char **argv)
+{
+
+    int rc = RTR3InitExe(argc, &argv, 0);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExitFailure("RTR3InitExe failed, rc=%Rrc\n", rc);
+
+    rc = VDInit();
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExitFailure("VDInit failed, rc=%Rrc\n", rc);
+
+    memset(&g_vboximgOps, 0, sizeof(g_vboximgOps));
+    g_vboximgOps.open        = vboximgOp_open;
+    g_vboximgOps.read        = vboximgOp_read;
+    g_vboximgOps.write       = vboximgOp_write;
+    g_vboximgOps.getattr     = vboximgOp_getattr;
+    g_vboximgOps.release     = vboximgOp_release;
+    g_vboximgOps.readdir     = vboximgOp_readdir;
+    g_vboximgOps.readlink    = vboximgOp_readlink;
+
+    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+    memset(&g_vboximgOpts, 0, sizeof(g_vboximgOpts));
+
+    rc = fuse_opt_parse(&args, &g_vboximgOpts, vboximgOptDefs, vboximgOptHandler);
+
+    if (g_vboximgOpts.fAllowRoot)
+        fuse_opt_add_arg(&args, "-oallow_root");
+
+    if (rc == -1)
+        return RTMsgErrorExitFailure("Couldn't parse fuse options, rc=%Rrc\n", rc);
+    if (g_vboximgOpts.fBriefUsage)
+    {
+        briefUsage();
+        return 0;
+    }
+
+    /*
+     * Initialize COM.
+     */
+    using namespace com;
+    HRESULT hrc = com::Initialize();
+    if (FAILED(hrc))
+    {
+# ifdef VBOX_WITH_XPCOM
+        if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
+        {
+            char szHome[RTPATH_MAX] = "";
+            com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
+            return RTMsgErrorExit(RTEXITCODE_FAILURE,
+                   "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
+        }
+# endif
+            return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM! (hrc=%Rhrc)", hrc);
+    }
+
+    /*
+     * Get the remote VirtualBox object and create a local session object.
+     */
+    ComPtr<IVirtualBoxClient> pVirtualBoxClient;
+    ComPtr<IVirtualBox> pVirtualBox;
+
+    hrc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
+    if (SUCCEEDED(hrc))
+        hrc = pVirtualBoxClient->COMGETTER(VirtualBox)(pVirtualBox.asOutParam());
+    if (FAILED(hrc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get IVirtualBox object! (hrc=%Rhrc)", hrc);
+
+    if (g_vboximgOpts.fVerbose)
+        RTPrintf("vboximg: VirtualBox XPCOM object created\n");
+
+    if (g_vboximgOpts.fListMedia || g_vboximgOpts.fListMediaBrief)
+    {
+        listVMs(pVirtualBox);
+        return 0;
+    }
+
+
+    if (g_vboximgOpts.pszImage == NULL)
+    {
+        RTMsgErrorExitFailure("To list partitions, must also specify --i or --image option\n");
+        return 0;
+    }
+    ComPtr<IMedium> pBaseImageMedium = NULL;
+    char    *pszFormat;
+    VDTYPE  enmType;
+    searchForBaseImage(pVirtualBox, g_vboximgOpts.pszImage, &pBaseImageMedium);
+    if (pBaseImageMedium == NULL)
+    {
+        /*
+         * Try to locate base image pMedium via the VirtualBox API, given the user-provided path
+         * resolving symlinks back to hard path.
+         */
+        int cbNameMax = pathconf(g_vboximgOpts.pszImage, _PC_PATH_MAX);
+        if (cbNameMax < 0)
+            return cbNameMax;
+
+        g_pszBaseImagePath = RTStrDup(g_vboximgOpts.pszImage);
+        if (g_pszBaseImagePath == NULL)
+            return RTMsgErrorExitFailure("out of memory\n");
+
+        if (access(g_pszBaseImagePath, F_OK) < 0)
+            return RTMsgErrorExitFailure("Virtual disk image not found: \"%s\"\n", g_pszBaseImagePath);
+
+        if (access(g_pszBaseImagePath, R_OK) < 0)
+             return RTMsgErrorExitFailure(
+                    "Virtual disk image not readable: \"%s\"\n", g_pszBaseImagePath);
+
+        if (g_vboximgOpts.fRW && access(g_vboximgOpts.pszImage, W_OK) < 0)
+             return RTMsgErrorExitFailure(
+                    "Virtual disk image not writeable: \"%s\"\n", g_pszBaseImagePath);
+        rc = RTPathSplit(g_pszBaseImagePath, &g_u.split, sizeof(g_u), 0);
+
+        if (RT_FAILURE(rc))
+             return RTMsgErrorExitFailure(
+                    "RTPathSplit failed on '%s': %Rrc",  g_pszBaseImagePath);
+
+        if (!(g_u.split.fProps & RTPATH_PROP_FILENAME))
+             return RTMsgErrorExitFailure(
+                    "RTPATH_PROP_FILENAME not set for: '%s'",  g_pszBaseImagePath);
+
+        g_pszBaseImageName = g_u.split.apszComps[g_u.split.cComps - 1];
+        searchForBaseImage(pVirtualBox, g_pszBaseImageName, &pBaseImageMedium);
+
+        if (pBaseImageMedium == NULL)
+        {
+            /*
+             *  Can't find the user specified image Medium via the VirtualBox API
+             *  Try to 'mount' the image via the user-provided path (without differencing images)
+             *  Create VirtualBox disk container and open main image
+             */
+            rc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/,
+                g_pszBaseImagePath, &pszFormat, &enmType);
+            if (RT_FAILURE(rc))
+                return RTMsgErrorExitFailure("VDGetFormat(%s,) "
+                    "failed, rc=%Rrc\n", g_pszBaseImagePath, rc);
+
+            g_pVDisk = NULL;
+            rc = VDCreate(NULL /* pVDIIfsDisk */, enmType, &g_pVDisk);
+            if (RT_SUCCESS(rc))
+            {
+                rc = VDOpen(g_pVDisk, pszFormat, g_pszBaseImagePath, 0, NULL /* pVDIfsImage */);
+                if (RT_FAILURE(rc))
+                {
+                    VDClose(g_pVDisk, false /* fDeletes */);
+                    return RTMsgErrorExitFailure("VDCreate(,%s,%s,,,) failed,"
+                        " rc=%Rrc\n", pszFormat, g_pszBaseImagePath, rc);
+                }
+            }
+            else
+                return RTMsgErrorExitFailure("VDCreate failed, rc=%Rrc\n", rc);
+        }
+    } else {
+        Bstr pMediumUuid;
+        CHECK_ERROR(pBaseImageMedium, COMGETTER(Id)(pMediumUuid.asOutParam()));
+        g_pvDiskUuid = RTStrDup((char *)CSTR(pMediumUuid));
+    }
+
+    if (g_pVDisk == NULL)
+    {
+
+        com::SafeIfaceArray<IMedium> aMediumChildren;
+        ComPtr<IMedium> pChild = pBaseImageMedium;
+        uint32_t diffNumber = 0; /* diff # 0 = base image */
+        do
+        {
+            Bstr pMediumName;
+            Bstr pMediumPath;
+
+            CHECK_ERROR(pChild, COMGETTER(Name)(pMediumName.asOutParam()));
+            CHECK_ERROR(pChild, COMGETTER(Location)(pMediumPath.asOutParam()));
+
+            if (pChild == pBaseImageMedium)
+            {
+                free((void *)g_pszBaseImageName);
+                g_pszBaseImageName = RTStrDup(CSTR(pMediumName));
+
+                free((void *)g_pszBaseImagePath);
+                g_pszBaseImagePath = RTStrDup(CSTR(pMediumPath));
+                if (g_pszBaseImageName == NULL)
+                    return RTMsgErrorExitFailure("out of memory\n");
+
+                if (g_pszBaseImagePath == NULL)
+                    return RTMsgErrorExitFailure("out of memory\n");
+                /*
+                 * Create HDD container to open base image and differencing images into
+                 */
+                rc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/,
+                        g_pszBaseImagePath, &pszFormat, &enmType);
+                if (RT_FAILURE(rc))
+                    return RTMsgErrorExitFailure("VDGetFormat(,,%s,,) "
+                        "failed (during HDD container creation), rc=%Rrc\n", g_pszBaseImagePath, rc);
+                if (g_vboximgOpts.fVerbose)
+                    RTPrintf("Creating container for base image of format %s\n", pszFormat);
+                /** @todo Remove I/O CB's and crit sect. when VDRead()/VDWrite() are made threadsafe */
+                rc = RTCritSectInit(&g_vdioLock);
+                if (RT_SUCCESS(rc))
+                {
+                    g_VDIfThreadSync.pfnStartRead   = vboximgThreadStartRead;
+                    g_VDIfThreadSync.pfnFinishRead  = vboximgThreadFinishRead;
+                    g_VDIfThreadSync.pfnStartWrite  = vboximgThreadStartWrite;
+                    g_VDIfThreadSync.pfnFinishWrite = vboximgThreadFinishWrite;
+                    rc = VDInterfaceAdd(&g_VDIfThreadSync.Core, "vboximg_ThreadSync", VDINTERFACETYPE_THREADSYNC,
+                                        &g_vdioLock, sizeof(VDINTERFACETHREADSYNC), &g_pVdIfs);
+                }
+                else
+                    return RTMsgErrorExitFailure("ERROR: Failed to create critsects "
+                                                 "for virtual disk I/O, rc=%Rrc\n", rc);
+
+                g_pVDisk = NULL;
+                rc = VDCreate(g_pVdIfs, enmType, &g_pVDisk);
+                if (NS_FAILED(rc))
+                    return RTMsgErrorExitFailure("ERROR: Couldn't create virtual disk container\n");
+            }
+            /** @todo (end of to do section) */
+
+            if ( g_vboximgOpts.cHddImageDiffMax != 0 && diffNumber > g_vboximgOpts.cHddImageDiffMax)
+                break;
+
+            if (g_vboximgOpts.fVerbose)
+            {
+                if (diffNumber == 0)
+                    RTPrintf("\nvboximg-mount: Opening base image into container:\n       %s\n",
+                        g_pszBaseImagePath);
+                else
+                    RTPrintf("\nvboximg-mount: Opening difference image #%d into container:\n       %s\n",
+                        diffNumber, g_pszBaseImagePath);
+            }
+
+            rc = VDOpen(g_pVDisk, pszFormat, g_pszBaseImagePath, 0, NULL /* pVDIfsImage */);
+            if (RT_FAILURE(rc))
+            {
+                VDClose(g_pVDisk, false /* fDeletes */);
+                return RTMsgErrorExitFailure("VDOpen(,,%s,,) failed, rc=%Rrc\n",
+                   g_pszBaseImagePath, rc);
+            }
+
+            CHECK_ERROR(pChild, COMGETTER(Children)(ComSafeArrayAsOutParam(aMediumChildren)));
+
+            if (aMediumChildren.size() != 0) {
+                pChild = aMediumChildren[0];
+            }
+
+            aMediumChildren.setNull();
+
+            ++diffNumber;
+
+
+        } while(NS_SUCCEEDED(rc) && aMediumChildren.size());
+    }
+
+    g_cReaders    = VDIsReadOnly(g_pVDisk) ? INT32_MAX / 2 : 0;
+    g_cWriters   = 0;
+    g_cbEntireVDisk  = VDGetSize(g_pVDisk, 0 /* base */);
+
+    if (g_vboximgOpts.fListParts)
+    {
+        if (g_pVDisk == NULL)
+            return RTMsgErrorExitFailure("No valid --image to list partitions from\n");
+
+        RTPrintf("\n");
+
+        rc = parsePartitionTable();
+        switch(rc)
+        {
+            case PARTITION_TABLE_MBR:
+                displayLegacyPartitionTable();
+                break;
+            case PARTITION_TABLE_GPT:
+                displayGptPartitionTable();
+                break;
+            default:
+                return rc;
+        }
+        return 0;
+    }
+    if (g_vboximgOpts.idxPartition >= 0)
+    {
+        if (g_vboximgOpts.offset)
+            return RTMsgErrorExitFailure("--offset and --partition are mutually exclusive options\n");
+
+        if (g_vboximgOpts.size)
+            return RTMsgErrorExitFailure("--size and --partition are mutually exclusive options\n");
+
+        /*
+         * --partition option specified. That will set the global offset and limit
+         * honored by the disk read and write sanitizers to constrain operations
+         * to within the specified partion based on an initial parsing of the MBR
+         */
+        rc = parsePartitionTable();
+        if (rc < 0)
+            return RTMsgErrorExitFailure("Error parsing disk MBR/Partition table\n");
+        int partNbr = g_vboximgOpts.idxPartition;
+
+        if (partNbr < 0 || partNbr > g_lastPartNbr)
+            return RTMsgErrorExitFailure("Non-valid partition number specified\n");
+
+        if (partNbr == 0)
+        {
+            g_vDiskOffset = 0;
+            g_vDiskSize = VDGetSize(g_pVDisk, 0);
+            if (g_vboximgOpts.fVerbose)
+                RTPrintf("Partition 0 specified - Whole disk will be accessible\n");
+        } else {
+            for (int i = 0; i < g_lastPartNbr; i++)
+            {
+                /* If GPT, display vboximg's representation of partition table starts at partition 2
+                 * but the table is displayed calling it partition 1, because the protective MBR
+                 * record is relatively pointless to display or reference in this context */
+
+                if (g_aParsedPartitionInfo[i].idxPartition == partNbr + g_fGPT ? 1 : 0)
+                {
+                     g_vDiskOffset = g_aParsedPartitionInfo[i].offPartition;
+                     g_vDiskSize = g_vDiskOffset + g_aParsedPartitionInfo[i].cbPartition;
+                     if (g_vboximgOpts.fVerbose)
+                        RTPrintf("Partition %d specified. Only sectors %llu to %llu of disk will be accessible\n",
+                            g_vboximgOpts.idxPartition, g_vDiskOffset / BLOCKSIZE, g_vDiskSize / BLOCKSIZE);
+                }
+            }
+        }
+    } else {
+        if (g_vboximgOpts.offset) {
+            if (g_vboximgOpts.offset < 0 || g_vboximgOpts.offset + g_vboximgOpts.size > g_cbEntireVDisk)
+                return RTMsgErrorExitFailure("User specified offset out of range of virtual disk\n");
+
+            if (g_vboximgOpts.fVerbose)
+                RTPrintf("Setting r/w bias (offset) to user requested value for sector %llu\n", g_vDiskOffset / BLOCKSIZE);
+
+            g_vDiskOffset = g_vboximgOpts.offset;
+        }
+        if (g_vboximgOpts.size) {
+            if (g_vboximgOpts.size < 0 || g_vboximgOpts.offset + g_vboximgOpts.size > g_cbEntireVDisk)
+                return RTMsgErrorExitFailure("User specified size out of range of virtual disk\n");
+
+            if (g_vboximgOpts.fVerbose)
+                RTPrintf("Setting r/w size limit to user requested value %llu\n", g_vDiskSize / BLOCKSIZE);
+
+            g_vDiskSize = g_vboximgOpts.size;
+        }
+    }
+    if (g_vDiskSize == 0)
+        g_vDiskSize = g_cbEntireVDisk - g_vDiskOffset;
+
+    /*
+     * Hand control over to libfuse.
+     */
+    if (g_vboximgOpts.fVerbose)
+        RTPrintf("\nvboximg-mount: Going into background...\n");
+
+    rc = fuse_main(args.argc, args.argv, &g_vboximgOps, NULL);
+
+    int rc2 = VDClose(g_pVDisk, false /* fDelete */);
+    AssertRC(rc2);
+    RTPrintf("vboximg-mount: fuse_main -> %d\n", rc);
+    return rc;
+}
+
Index: /trunk/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.h
===================================================================
--- /trunk/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.h	(revision 75970)
+++ /trunk/src/VBox/ImageMounter/vboximg-mount/vboximg-mount.h	(revision 75970)
@@ -0,0 +1,294 @@
+/* $Id$ $Revision$ */
+/** @file
+ * vboximg-mount header file
+ */
+
+/*
+ * Copyright (C) 2018 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef ___vboximg_mount_h
+#define ___vboximg_mount_h
+
+#define BOOTSTRAP_CODE_AREA_SIZE    446         /** Impl. specific MBR & EBR block not relevant for our use */
+#define GPT_PARTITION_ENTRY_NAME_SIZE 36        /** 72 bytes for GPT partition entry name, but treated as 36 UTF-16 */
+#define GUID_STRING_LENGTH            36
+
+#pragma pack(push,1)  /**** THE FOLLOWING STRUCTURES MUST NOT BE PADDED BY THE COMPILER ****/
+
+typedef struct PartitionEntry                   /* Pos & size critical (See http://en.wikipedia.org/wiki/Master_boot_record) */
+{
+    uint8_t  bootIndicator;                     /** 0x80 = bootable, 0x00 = non-bootable */
+    uint8_t  firstHead;                         /** CHS address of first absolute Head     from MBR entry */
+    uint8_t  firstSector;                       /** CHS address of first absolute Sector   from MBR entry */
+    uint8_t  firstCyl;                          /** CHS address of first absolute Cylinder from MBR entry*/
+    uint8_t  type;                              /** partition type */
+    uint8_t  lastHead;                          /** CHS address of last absolute Head      from MBR entry */
+    uint8_t  lastSector;                        /** CHS address of last absolute Sector    from MBR entry*/
+    uint8_t  lastCyl;                           /** CHS address of last absolute Cylinder  from MBR entry */
+    uint32_t partitionLba;                      /** LBA of first sector in the partition */
+    uint32_t partitionBlkCnt;                   /** partition block count (little endian) */
+} MBRPARTITIONENTRY;
+
+typedef struct MasterBootRecord                 /* Pos & size critical (See http://en.wikipedia.org/wiki/Master_boot_record) */
+{
+    char bootstrapCodeArea[BOOTSTRAP_CODE_AREA_SIZE];
+    MBRPARTITIONENTRY partitionEntry[4];
+    uint16_t signature;
+} MBR_t; /* _t for type to avoid conflict with definition in vd.h */
+
+typedef struct ExtendedBootRecord              /* Pos & size critical (See http://en.wikipedia.org/wiki/Extended_boot_record) */
+{
+    char unused[BOOTSTRAP_CODE_AREA_SIZE];
+    MBRPARTITIONENTRY partitionEntry;          /** EBR-relative offset to 1st sector of partition */
+    MBRPARTITIONENTRY chainingPartitionEntry;  /** EBR-relative offset to next EBR */
+    MBRPARTITIONENTRY unusedPartitionEntry3;   /** Unused for Logical Partitions table entries (EBR) */
+    MBRPARTITIONENTRY unusedPartitionEntry4;   /** Unused for Logical Partitions table entries (EBR) */
+    uint16_t signature;                        /** Boot Record signature */
+} EBR_t; /* _t for type to avoid possible future conflict with vd.h */
+
+typedef struct gptPartitionTableHeader         /* Pos and size critical (See https://en.wikipedia.org/wiki/GUID_Partition_Table) */
+{
+    uint64_t      signature;                   /** Equiv values: "EFI PART", 0x5452415020494645ULL little-endian */
+    uint32_t      revision;                    /** GPT 1.0 would be 0x00,0x00,x01,0x00 */
+    uint32_t      cbHeader;                    /** Typically 0x5C,0x00,0x00,0x00 (e.g. 92, little-endian) */
+    uint32_t      crc32OfHeader;               /** From [0:<header size>], little endian, zero while being calculated */
+    uint32_t      MBZreserved1;                /** MBZ */
+    uint64_t      headerLba;                   /** Location of this header copy */
+    uint64_t      backupLba;                   /** Location of the other header copy */
+    uint64_t      firstUsableLba;              /** Ending LBA of primary partition table, +1 */
+    uint64_t      lastUsableLba;               /** Starting LBA of secondary partition tabel, -1 */
+    uint128_t     diskUuid;                    /** Disk UUID */
+    uint64_t      partitionEntriesLba;         /** Starting lba of partition entreis array (always 2 in primary copy) */
+    uint32_t      cPartitionEntries;           /** Number of partitions in the array */
+    uint32_t      cbPartitionEntry;            /** Size of a single partition entry (usually 0x80, e.g. 128) */
+    uint32_t      crc32OfPartitionArray;       /** CRC32/zlib of partition array, little-endian */
+    uint8_t       MBZreserved2[];              /** Zeros until end of block, 420 bytes for 512 block but can be more */
+} PTH_t; /*_t for type, to avoid possible future conflict with vd.h */
+
+typedef struct gptPartitionEntry
+{
+    uint128_t     partitionTypeGuid;           /** Partition type UUID (RFC4122: 128 bits, big endian, network byte order) */
+    uint128_t     uniquePartitionGuid;         /** UUID unique to this partition */
+    uint64_t      firstLba;                    /** First LBA of partition (little-endian) */
+    uint64_t      lastLba;                     /** Last LBA of partition (inclusive, usually odd) */
+    uint64_t      attrFlags;                   /** Attribute flags */
+    uint16_t      partitionName[GPT_PARTITION_ENTRY_NAME_SIZE];  /** Partition Name, 72-bytes (e.g. 36 UTF-16LE code units) */
+} GPTPARTITIONENTRY;
+
+/* GPT partition attributes */
+
+#define GPT_ATTR_REQUIRED_BY_PLATORM        0  /* bit number */
+#define GPT_ATTR_EFI_SHOULD_IGNORE          1  /* bit number */
+#define GPT_LEGACY_BIOS_BOOTABLE            2  /* bit number */
+#define GPT_ATTR_PARTITION_READONLY         60 /* bit number */
+#define GPT_ATTR_SHADOW_COPY                61 /* bit number */
+#define GPT_ATTR_HIDDEN                     62 /* bit number */
+#define GPT_ATTR_NO_DRIVE_LETTER            63 /* bit number */
+
+#pragma pack(pop) /**** End of packed data declarations ****/
+
+struct PartitionDesc
+{
+    uint8_t type;
+    const char *desc;
+} g_partitionDescTable[] = {
+        { 0x00, "Empty" }, { 0x01, "FAT12" }, { 0x02, "XENIX" }, { 0x03, "XENIX" }, { 0x04, "FAT16 <32M" }, { 0x05, "Extended" },
+        { 0x06, "FAT16" }, { 0x07, "HPFS; NTFS; exFAT; WinCE; QNX" }, { 0x08, "AIX; DOS 3.x; OS/2; QNX 1.x" },
+        { 0x09, "AIX; Coherent" }, { 0x0a, "OS/2 Boot Mgr; Coherent" }, { 0x0b, "W95 FAT32" }, { 0x0c, "W95 FAT32 (LBA)" },
+        { 0x0e, "W95 FAT16 (LBA)" }, { 0x0f, "W95 Ext'd (LBA)" }, { 0x10, "OPUS" }, { 0x11, "FAT12" }, { 0x12, "Diag. & FW; Service; Compaq; EISA; AST" },
+        { 0x14, "FAT16; MAVERICK; OS/2" }, { 0x16, "FAT16" }, { 0x17, "HPFS; NTFS; exFAT; IFS" },
+        { 0x18, "AST" }, { 0x1b, "OS/2 BootMgr" }, { 0x1c, "OS/2 BootMgr FAT16/LBA; ASUS Recovery FAT32/LBA" },
+        { 0x1e, "OS/2 BootMgr Ext. LBA" }, { 0x20, "Windows Mobile" },  { 0x21, "HP Vol. Expansion; FSo2" }, { 0x22, "FSo2 ext. part" },
+        { 0x23, "Windows Mobile "}, { 0x24, "NEC" }, {0x25, "Windows Mobile" }, {  0x27, "NTFS; MirOS, RooterBoot" },
+        { 0x2a, "AtheOS" }, { 0x2b, "Syllable OS" }, { 0x32, "NOS" }, { 0x35, "JFS" }, { 0x38, "Plan; THEOS" },
+        { 0x39, "Plan; THEOS" }, { 0x3c, "PartitionMagic" }, { 0x40, "Venix; PICK" }, { 0x41, "PPC; Linux; SFS" }, { 0x42, "Linux; Win 2000" },
+        { 0x43, "Linux" }, { 0x44, "Wildfile" }, { 0x45, "Priam; Boot-US; EMUEL/ELAN (L2)" }, { 0x46, "EMUEL/ELAN (L2)" },
+        { 0x47, "EMUEL/ELAN (L2)" }, {0x48, "EMUEL/ELAN" }, { 0x4a, "AdaOS; ALFS/THIN" }, { 0x4c, "ETH Oberon" }, { 0x4d, "QNX4.x Primary" },
+        { 0x4e, "QNX4.x 2nd part " }, { 0x4f, "QNX4.x 3rd part; ETH Oberon boot " }, { 0x50, "OnTrack; LynxOS; Novel" },
+        { 0x51, "OnTrack" }, { 0x52, "CP/M; System V/AT, V/386" }, { 0x53, "OnTrack" }, { 0x54, "OnTrack" }, { 0x55, "EZ-Drive" },
+        { 0x56, "AT&T; EZ-Drive; VFeature" }, {0x57, "DrivePro" }, { 0x5c, "Priam" }, { 0x61, "SpeedStor" },
+        { 0x63, "GNU; SCO Unix; ISC; UnixWare; SYSV/386; ix; MtXinu BSD 4.3 on Mach" }, { 0x64, "Netware, SpeedStor; PC-ARMOUR" },
+        { 0x65, "Netware" }, { 0x66, "Netware" }, { 0x67, "Netware" }, { 0x68, "Netware" }, { 0x69, "Netware" }, { 0x70, "DiskSecure" },
+        { 0x72, "APTI FAT12 (CHS, SFN)" }, { 0x75, "PC/IX" }, { 0x77, "VNDI, M2FS, M2CS" }, { 0x78, "XOSL bootloader" }, { 0x79, "APTI FAT16 (LBA, SFN)" },
+        { 0x7a, "APTI FAT16 (LBA, SFN)" }, { 0x7b, "APTI FAT16 (LBA, SFN)" }, { 0x7c, "APTI FAT16 (LBA, SFN)" }, { 0x7d, "APTI FAT16 (LBA, SFN)" },
+        { 0x7e, "PrimoCache" }, { 0x7f, "Alt OS Dev. Partition Std." }, { 0x80, "MINIX (old)" }, { 0x81, "Minix" },
+        { 0x82, "Linux swap; Solaris; PRIMOS" }, { 0x83, "Linux" }, { 0x84, "OS/2; Rapid Start Tech." }, { 0x85, "Linux Ext." },
+        { 0x86, "NTFS" }, { 0x87, "NTFS" }, { 0x88, "Linux" }, { 0x8e, "Linux" }, {0x90, "Free FDISK" }, { 0x91, "Free FDISK" },
+        { 0x92, "Free FDISK" }, { 0x93, "Amoeba" }, { 0x94, "Amoeba" }, { 0x95, "EXOPC" }, { 0x96, "CHRP" }, { 0x97, "Free FDISK" },
+        { 0x98, "ROM-DOS; Free FDISK" }, { 0x9a, "Free FDISK" }, { 0x9b, "Free FDISK" },
+        { 0x9e, "VSTa; ForthOS" }, { 0x9f, "BSD/OS / BSDI" }, { 0xa0, "Phoenix; IBM; Toshiba; Sony" }, { 0xa1, "HP Volume Expansion; Phoenix; NEC" },
+        { 0xa5, "FreeBSD" }, { 0xa6, "OpenBSD" }, { 0xa7, "NeXTSTEP" }, { 0xa8, "Darwin; macOS" }, { 0xa9, "NetBSD" }, { 0xab, "Darwin; GO! OS" },
+        { 0xad, "RISC OS" },  { 0xae, "ShagOS"}, { 0xaf, "HFS / ShagOS" }, { 0xb0, "Boot-Star" }, {0xb1, "HP Vol. Expansion; QNX 6.x" },
+        { 0xb2, "QNX 6.x" }, { 0xB3, "HP Vol. Expansion; QNX 6.x" }, { 0xb7, "BSDI; Win NT 4 Server" }, { 0xb8, "BSDI" },
+        { 0xbb, "BootWizard; OS Selector;  Acronis True Image; Win NT 4 Server" }, { 0xbc, "Acronis" }, { 0xbe, "Solaris" }, { 0xbf, "Solaris" },
+        { 0xc0, "DR-DOS; Multiuser DOS; REAL/32"}, { 0xc1, "DRDOS/sec" }, { 0xc2, "Power Boot" }, { 0xc3, "Power Boot" }, { 0xc4, "DRDOS/sec" },
+        { 0xc6, "DRDOS/sec" }, { 0xc7, "Syrinx; Win NT Server" }, { 0xcb, "Win NT 4 Server" }, { 0xcc, "DR-DOS 7.0x; Win NT 4 Server" },
+        { 0xcd, "CTOS" }, { 0xce, "DR-DOS 7.0x" }, { 0xcf, "DR-DOS 7.0x" }, { 0xd1, "Multiuser DOS" }, { 0xd4, "Multiuser DOS" },
+        { 0xd5, "Multiuser DOS" }, { 0xd6, "Multiuser DOS" }, { 0xd8, "CP/M-86" }, { 0xda, "Non-FS data; Powercopy Backup" },
+        { 0xdb, "CP/M; Concurrent DOS; CTOS; D800; DRMK " }, { 0xdd, "CTOS" }, { 0xde, "Dell" }, { 0xdf, "BootIt" }, { 0xe0, "Aviion" },
+        { 0xe1, "SpeedStor" }, { 0xe2, "SpeedStor"}, { 0xe3, "SpeedStor" }, { 0xe4, "SpeedStor" }, { 0xe5, "Tandy MS-DOS" }, { 0xe6, "SpeedStor" },
+        { 0xe8, "LUKS" },  { 0xea, "Rufus" }, { 0xeb, "BeOS" }, { 0xec, "SkyOS" }, { 0xed, "EFS; Sprytix" },  { 0xed, "EFS" },
+        { 0xee, "GPT" }, { 0xef, "EFI" }, { 0xf0, "Linux; PA-RISC" }, { 0xf1, "SpeedStor" }, { 0xf2, "Sperry MS-DOS 3.x" },
+        { 0xf3, "SpeedStor" }, { 0xf4, "SpeedStor; Prologue" }, { 0xf5, "Prologue" }, { 0xf6, "SpeedStor" }, { 0xf7, "O.S.G.; X1" },
+        { 0xf9, "Linux" }, { 0xfa, "Bochs" }, { 0xfb, "VMware" }, { 0xfc, "VMware" }, { 0xfd, "Linux; FreeDOS" },
+        { 0xfe, "SpeedStor; LANstep; PS/2; Win NT; Linux" }, { 0xff, "Xenis; BBT" },
+};
+
+typedef struct GptPartitionTypeTable
+{
+    const char *gptPartitionUuid;
+    const char *osType;
+    const char *gptPartitionTypeDesc;
+} GPTPARTITIONTYPE;
+
+GPTPARTITIONTYPE g_gptPartitionTypes[] =
+{
+    { "00000000-0000-0000-0000-000000000000", "", "Unused" },
+    { "024DEE41-33E7-11D3-9D69-0008C781F39F", "", "MBR scheme" },
+    { "C12A7328-F81F-11D2-BA4B-00A0C93EC93B", "", "EFI System" },
+    { "21686148-6449-6E6F-744E-656564454649", "", "BIOS boot" },
+    { "D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", "", "Intel Fast Flash (iFFS)" },
+    { "F4019732-066E-4E12-8273-346C5641494F", "", "Sony boot" },
+    { "BFBFAFE7-A34F-448A-9A5B-6213EB736C22", "", "Lenovo boot" },
+    { "E3C9E316-0B5C-4DB8-817D-F92DF00215AE", "Windows", "Microsoft Reserved" },
+    { "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Windows", "Basic data" },
+    { "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", "Windows", "Logical Disk Manager (LDM) metadata" },
+    { "AF9B60A0-1431-4F62-BC68-3311714A69AD", "Windows", "Logical Disk Manager (LDM) data" },
+    { "DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", "Windows", "Windows Recovery Environment" },
+    { "37AFFC90-EF7D-4E96-91C3-2D7AE055B174", "Windows", "IBM General Parallel File System (GPFS)" },
+    { "E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", "Windows", "Storage Spaces" },
+    { "75894C1E-3AEB-11D3-B7C1-7B03A0000000", "HP-UX", "Data" },
+    { "E2A1E728-32E3-11D6-A682-7B03A0000000", "HP-UX", "Service" },
+    { "0FC63DAF-8483-4772-8E79-3D69D8477DE4", "Linux", "Filesystem Data" },
+    { "A19D880F-05FC-4D3B-A006-743F0F84911E", "Linux", "RAID" },
+    { "44479540-F297-41B2-9AF7-D131D5F0458A", "Linux", "Root (x86)" },
+    { "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", "Linux", "Root (x86-64)" },
+    { "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", "Linux", "Root (32-bit ARM)" },
+    { "B921B045-1DF0-41C3-AF44-4C6F280D3FAE", "Linux", "Root (64-bit ARM/AArch64)" },
+    { "A2A0D0EB-E5B9-3344-87C0-68B6B72699C7", "Linux", "Data" },
+    { "AF3DC60F-8384-7247-8E79-3D69D8477DE4", "Linux", "Data" },
+    { "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", "Linux", "Swap" },
+    { "E6D6D379-F507-44C2-A23C-238F2A3DF928", "Linux", "LVM" },
+    { "933AC7E1-2EB4-4F13-B844-0E14E2AEF915", "Linux", "/home" },
+    { "3B8F8425-20E0-4F3B-907F-1A25A76F98E8", "Linux", "/srv " },
+    { "7FFEC5C9-2D00-49B7-8941-3EA10A5586B7", "Linux", "Plain dm-crypt" },
+    { "CA7D7CCB-63ED-4C53-861C-1742536059CC", "Linux", "LUKS" },
+    { "8DA63339-0007-60C0-C436-083AC8230908", "Linux", "Reserved" },
+    { "83BD6B9D-7F41-11DC-BE0B-001560B84F0F", "FreeBSD", "Boot" },
+    { "516E7CB4-6ECF-11D6-8FF8-00022D09712B", "FreeBSD", "Data" },
+    { "516E7CB5-6ECF-11D6-8FF8-00022D09712B", "FreeBSD", "Swap" },
+    { "516E7CB6-6ECF-11D6-8FF8-00022D09712B", "FreeBSD",  "Unix File System (UFS)" },
+    { "516E7CB8-6ECF-11D6-8FF8-00022D09712B", "FreeBSD", "Vinum volume manager" },
+    { "516E7CBA-6ECF-11D6-8FF8-00022D09712B", "FreeBSD", "ZFS" },
+    { "48465300-0000-11AA-AA11-00306543ECAC", "macOS Darwin", "Hierarchical File System Plus (HFS+)" },
+    { "7C3457EF-0000-11AA-AA11-00306543ECAC", "macOS Darwin", "Apple APFS" },
+    { "55465300-0000-11AA-AA11-00306543ECAC", "macOS Darwin", "Apple UFS container" },
+    { "6A898CC3-1DD2-11B2-99A6-080020736631", "macOS Darwin", "ZFS" },
+    { "52414944-0000-11AA-AA11-00306543ECAC", "macOS Darwin", "Apple RAID" },
+    { "52414944-5F4F-11AA-AA11-00306543ECAC", "macOS Darwin", "Apple RAID , offline" },
+    { "426F6F74-0000-11AA-AA11-00306543ECAC", "macOS Darwin", "Apple Boot (Recovery HD)" },
+    { "4C616265-6C00-11AA-AA11-00306543ECAC", "macOS Darwin", "Apple Label" },
+    { "5265636F-7665-11AA-AA11-00306543ECAC", "macOS Darwin", "Apple TV Recovery" },
+    { "53746F72-6167-11AA-AA11-00306543ECAC", "macOS Darwin", "Apple Core Storage (i.e. Lion FileVault)" },
+    { "B6FA30DA-92D2-4A9A-96F1-871EC6486200", "macOS Darwin", "SoftRAID_Status" },
+    { "2E313465-19B9-463F-8126-8A7993773801", "macOS Darwin", "SoftRAID_Scratch" },
+    { "FA709C7E-65B1-4593-BFD5-E71D61DE9B02", "macOS Darwin", "SoftRAID_Volume" },
+    { "BBBA6DF5-F46F-4A89-8F59-8765B2727503", "macOS Darwin", "SoftRAID_Cache" },
+    { "6A82CB45-1DD2-11B2-99A6-080020736631", "Solaris illumos", "Boot" },
+    { "6A85CF4D-1DD2-11B2-99A6-080020736631", "Solaris illumos", "Root" },
+    { "6A87C46F-1DD2-11B2-99A6-080020736631", "Solaris illumos", "Swap" },
+    { "6A8B642B-1DD2-11B2-99A6-080020736631", "Solaris illumos", "Backup" },
+    { "6A898CC3-1DD2-11B2-99A6-080020736631", "Solaris illumos", "/usr" },
+    { "6A8EF2E9-1DD2-11B2-99A6-080020736631", "Solaris illumos", "/var" },
+    { "6A90BA39-1DD2-11B2-99A6-080020736631", "Solaris illumos", "/home" },
+    { "6A9283A5-1DD2-11B2-99A6-080020736631", "Solaris illumos", "Alternate sector" },
+    { "6A945A3B-1DD2-11B2-99A6-080020736631", "Solaris illumos",  "Reserved" },
+    { "6A9630D1-1DD2-11B2-99A6-080020736631", "Solaris illumos", "Reserved" },
+    { "6A980767-1DD2-11B2-99A6-080020736631", "Solaris illumos", "Reserved"},
+    { "6A96237F-1DD2-11B2-99A6-080020736631", "Solaris illumos", "Reserved" },
+    { "6A8D2AC7-1DD2-11B2-99A6-080020736631", "Solaris illumos", "Reserved" },
+    { "49F48D32-B10E-11DC-B99B-0019D1879648", "NetBSD", "Swap" },
+    { "49F48D5A-B10E-11DC-B99B-0019D1879648", "NetBSD", "FFS" },
+    { "49F48D82-B10E-11DC-B99B-0019D1879648", "NetBSD", "LFS" },
+    { "49F48DAA-B10E-11DC-B99B-0019D1879648", "NetBSD", "RAID" },
+    { "2DB519C4-B10F-11DC-B99B-0019D1879648", "NetBSD", "Concatenated" },
+    { "2DB519EC-B10F-11DC-B99B-0019D1879648", "NetBSD", "Encrypted" },
+    { "FE3A2A5D-4F32-41A7-B725-ACCC3285A309", "Chrome OS", "kernel" },
+    { "3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", "Chrome OS", "rootfs" },
+    { "2E0A753D-9E48-43B0-8337-B15192CB1B5E", "Chrome OS", "future use" },
+    { "5DFBF5F4-2848-4BAC-AA5E-0D9A20B745A6", "Container Linux", "/usr (coreos-usr)" },
+    { "3884DD41-8582-4404-B9A8-E9B84F2DF50E", "Container Linux", "Resizable rootfs (coreos-resize)" },
+    { "C95DC21A-DF0E-4340-8D7B-26CBFA9A03E0", "Container Linux",  "OEM customizations (coreos-reserved)" },
+    { "BE9067B9-EA49-4F15-B4F6-F36F8C9E1818", "Container Linux", "Root filesystem on RAID (coreos-root-raid)" },
+    { "42465331-3BA3-10F1-802A-4861696B7521",  "Haiku", "BFS" },
+    { "85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD", "Boot" },
+    { "85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD",  "Data" },
+    { "85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD", "Swap" },
+    { "0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD", "Unix File System (UFS)" },
+    { "85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD", "Vinum volume manager" },
+    { "85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD", "ZFS" },
+    { "45B0969E-9B03-4F30-B4C6-B4B80CEFF106", "Ceph", "Journal" },
+    { "45B0969E-9B03-4F30-B4C6-5EC00CEFF106", "Ceph", "dm-crypt journal" },
+    { "4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D", "Ceph", "OSD" },
+    { "4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D", "Ceph", "dm-crypt OSD" },
+    { "89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE", "Ceph", "Disk in creation" },
+    { "89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE", "Ceph", "dm-crypt disk in creation" },
+    { "CAFECAFE-9B03-4F30-B4C6-B4B80CEFF106", "Ceph", "Block" },
+    { "30CD0809-C2B2-499C-8879-2D6B78529876", "Ceph", "Block DB" },
+    { "5CE17FCE-4087-4169-B7FF-056CC58473F9", "Ceph", "Block write-ahead log" },
+    { "FB3AABF9-D25F-47CC-BF5E-721D1816496B", "Ceph", "Lockbox for dm-crypt keys" },
+    { "4FBD7E29-8AE0-4982-BF9D-5A8D867AF560", "Ceph", "Multipath OSD" },
+    { "45B0969E-8AE0-4982-BF9D-5A8D867AF560", "Ceph", "Multipath journal" },
+    { "CAFECAFE-8AE0-4982-BF9D-5A8D867AF560", "Ceph", "Multipath block" },
+    { "7F4A666A-16F3-47A2-8445-152EF4D03F6C", "Ceph", "Multipath block" },
+    { "EC6D6385-E346-45DC-BE91-DA2A7C8B3261", "Ceph", "Multipath block DB" },
+    { "01B41E1B-002A-453C-9F17-88793989FF8F", "Ceph", "Multipath block write-ahead log" },
+    { "CAFECAFE-9B03-4F30-B4C6-5EC00CEFF106", "Ceph", "dm-crypt block" },
+    { "93B0052D-02D9-4D8A-A43B-33A3EE4DFBC3", "Ceph", "dm-crypt block DB" },
+    { "306E8683-4FE2-4330-B7C0-00A917C16966", "Ceph", "dm-crypt block write-ahead log" },
+    { "45B0969E-9B03-4F30-B4C6-35865CEFF106", "Ceph", "dm-crypt LUKS journal" },
+    { "CAFECAFE-9B03-4F30-B4C6-35865CEFF106", "Ceph", "dm-crypt LUKS block" },
+    { "166418DA-C469-4022-ADF4-B30AFD37F176", "Ceph", "dm-crypt LUKS block DB" },
+    { "86A32090-3647-40B9-BBBD-38D8C573AA86", "Ceph", "dm-crypt LUKS block write-ahead log" },
+    { "4FBD7E29-9D25-41B8-AFD0-35865CEFF05D", "Ceph", "dm-crypt LUKS OSD" },
+    { "824CC7A0-36A8-11E3-890A-952519AD3F61", "OpenBSD", "Data" },
+    { "CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1", "QNX", "Power-safe (QNX6) file system[45]" },
+    { "C91818F9-8025-47AF-89D2-F030D7000C2C", "Plan 9" },
+    { "9D275380-40AD-11DB-BF97-000C2911D1B8", "VMware ESX",  "vmkcore (coredump )" },
+    { "AA31E02A-400F-11DB-9590-000C2911D1B8", "VMware ESX", "VMFS filesystem" },
+    { "9198EFFC-31C0-11DB-8F78-000C2911D1B8", "VMware ESX", "VMware Reserved" },
+    { "2568845D-2332-4675-BC39-8FA5A4748D15", "Android-IA", "Bootloader" },
+    { "114EAFFE-1552-4022-B26E-9B053604CF84", "Android-IA", "Bootloader2" },
+    { "49A4D17F-93A3-45C1-A0DE-F50B2EBE2599", "Android-IA", "Boot" },
+    { "4177C722-9E92-4AAB-8644-43502BFD5506", "Android-IA", "Recovery" },
+    { "EF32A33B-A409-486C-9141-9FFB711F6266", "Android-IA", "Misc" },
+    { "20AC26BE-20B7-11E3-84C5-6CFDB94711E9", "Android-IA", "Metadata" },
+    { "38F428E6-D326-425D-9140-6E0EA133647C", "Android-IA", "System" },
+    { "A893EF21-E428-470A-9E55-0668FD91A2D9", "Android-IA", "Cache" },
+    { "DC76DDA9-5AC1-491C-AF42-A82591580C0D", "Android-IA", "Data" },
+    { "EBC597D0-2053-4B15-8B64-E0AAC75F4DB1", "Android-IA", "Persistent" },
+    { "C5A0AEEC-13EA-11E5-A1B1-001E67CA0C3C", "Android-IA", "Vendor" },
+    { "BD59408B-4514-490D-BF12-9878D963F378", "Android-IA", "Config" },
+    { "8F68CC74-C5E5-48DA-BE91-A0C8C15E9C80", "Android-IA", "Factory" },
+    { "9FDAA6EF-4B3F-40D2-BA8D-BFF16BFB887B", "Android-IA", "Factory (alt)[50]" },
+    { "767941D0-2085-11E3-AD3B-6CFDB94711E9", "Android-IA", "Fastboot / Tertiary" },
+    { "AC6D7924-EB71-4DF8-B48D-E267B27148FF", "Android-IA", "OEM" },
+    { "19A710A2-B3CA-11E4-B026-10604B889DCF", "Android 6.0+ ARM", "Android Meta" },
+    { "193D1EA4-B3CA-11E4-B075-10604B889DCF", "Android 6.0+ ARM", "Android EXT" },
+    { "7412F7D5-A156-4B13-81DC-867174929325", "ONIE", "Boot" },
+    { "D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149", "ONIE", "Config" },
+    { "9E1A2D38-C612-4316-AA26-8B49521E5A8B", "PowerPC", "PReP boot" },
+    { "BC13C2FF-59E6-4262-A352-B275FD6F7172", "freedesktop.org", "Shared boot loader configuration" },
+    { "734E5AFE-F61A-11E6-BC64-92361F002671", "Atari TOS", "Basic data (GEM, BGM, F32)" },
+};
+
+#endif
Index: /trunk/src/VBox/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Makefile.kmk	(revision 75969)
+++ /trunk/src/VBox/Makefile.kmk	(revision 75970)
@@ -101,4 +101,5 @@
  endif
  include $(PATH_SUB_CURRENT)/ExtPacks/Makefile.kmk
+ include $(PATH_SUB_CURRENT)/ImageMounter/Makefile.kmk
  ifdef VBOX_WITH_INSTALLER
   include $(PATH_SUB_CURRENT)/Installer/Makefile.kmk 		        # Keep this last.
