Index: /trunk/doc/manual/en_US/user_AdvancedTopics.xml
===================================================================
--- /trunk/doc/manual/en_US/user_AdvancedTopics.xml	(revision 85928)
+++ /trunk/doc/manual/en_US/user_AdvancedTopics.xml	(revision 85929)
@@ -1421,6 +1421,6 @@
         </para>
 
-<screen>$ VBoxManage internalcommands createrawvmdk -filename \
-/path/to/file.vmdk -rawdisk /dev/sda</screen>
+<screen>$ VBoxManage createmedium disk --filename /path/to/file.vmdk --format=VMDK
+ --variant RawDisk --property RawDrive=/dev/sda</screen>
 
         <para>
@@ -1435,6 +1435,6 @@
           for example use <filename>\\.\PhysicalDrive0</filename>. On a
           Mac OS X host, instead of the above device specification use
-          for example <filename>/dev/disk1</filename>. Note that on Mac
-          OS X you can only get access to an entire disk if no volume is
+          for example <filename>/dev/rdisk1</filename>. Note that on Mac
+          OS X you can only get access to entire disk if no volume is
           mounted from it.
         </para>
@@ -1488,12 +1488,13 @@
         </para>
 
-<screen>$ VBoxManage internalcommands createrawvmdk -filename \
-/path/to/file.vmdk -rawdisk /dev/sda -partitions 1,5</screen>
+<screen>$ VBoxManage createmedium disk --filename /path/to/file.vmdk --format=VMDK
+--variant RawDisk --property RawDrive=/dev/sda --property Partitions=1,5</screen>
 
         <para>
           The command is identical to the one for full hard disk access,
-          except for the additional <option>-partitions</option>
+          except for the additional
+          <option>--property Partitions=1,5</option>
           parameter. This example would create the image
-          <filename>/<replaceable>path-to-file</replaceable>.vmdk</filename>,
+          <filename>/path/to/file.vmdk</filename>,
           which must be absolute, and partitions 1 and 5 of
           <filename>/dev/sda</filename> would be made accessible to the
@@ -1512,8 +1513,9 @@
           use for example <filename>\\.\PhysicalDrive0</filename>. On a
           Mac OS X host, instead of the above device specification use
-          <filename>/dev/disk1</filename>, for example. Note that on OS
-          X you can only use partitions which are not mounted. Eject the
-          respective volume first. Partition numbers are the same on
-          Linux, Windows, and Mac OS X hosts.
+          <filename>/dev/rdisk1</filename>, for example. Note that on OS
+          X you can only use partitions which are not mounted. Unmount
+          the respective disk first using
+          <emphasis>diskutil unmountDisk <filename>/dev/diskX</filename></emphasis>.
+          Partition numbers are the same on Linux, Windows, and Mac OS X hosts.
         </para>
 
@@ -1523,10 +1525,10 @@
         </para>
 
-<screen>$ VBoxManage internalcommands listpartitions -rawdisk /dev/sda</screen>
-
-        <para>
-          The output lists the partition types and sizes to give the
-          user enough information to identify the partitions necessary
-          for the guest.
+<screen>$ VBoxManage list hostdrives</screen>
+
+        <para>
+          The output lists available drives and their partitions with
+          the partition types and sizes to give the user enough information
+          to identify the partitions necessary for the guest.
         </para>
 
@@ -1548,6 +1550,7 @@
         </para>
 
-<screen>$ VBoxManage internalcommands createrawvmdk -filename \
-/path/to/file.vmdk -rawdisk /dev/sda -partitions 1,5 -relative</screen>
+<screen>$ VBoxManage createmedium disk --filename /path/to/file.vmdk --format=VMDK
+--variant RawDisk --property RawDrive=/dev/sda --property Partitions=1,5
+--property Relative=1</screen>
 
         <para>
@@ -1568,13 +1571,15 @@
           This enables for example the guest to boot directly to
           Windows, while the host boots Linux from the "same" disk. For
-          this purpose the <option>-mbr</option> option is provided. It
-          specifies a file name from which to take the MBR code. The
-          partition table is not modified at all, so a MBR file from a
-          system with totally different partitioning can be used. An
-          example of this is:
-        </para>
-
-<screen>$ VBoxManage internalcommands createrawvmdk -filename
-/path/to/file.vmdk -rawdisk /dev/sda -partitions 1,5 -mbr winxp.mbr</screen>
+          this purpose the <option>--property-file
+          BootSector=/path/to/file/with/boot_sector</option>
+          parameter is provided. It specifies a file name from which to
+          take the MBR code. The partition table is not modified at all,
+          so a MBR file from a system with totally different
+          partitioning can be used. An example of this is:
+        </para>
+
+<screen>$ VBoxManage createmedium disk --filename /path/to/file.vmdk --format=VMDK
+--variant RawDisk --property RawDrive=/dev/sda --property Partitions=1,5
+--property-file BootSector=winxp.mbr</screen>
 
         <para>
Index: /trunk/doc/manual/en_US/user_VBoxManage.xml
===================================================================
--- /trunk/doc/manual/en_US/user_VBoxManage.xml	(revision 85928)
+++ /trunk/doc/manual/en_US/user_VBoxManage.xml	(revision 85929)
@@ -480,4 +480,13 @@
           Cloud profiles are used when exporting VMs to a cloud service.
           See <xref linkend="cloud-export-oci"/>.
+        </para>
+      </listitem>
+
+      <listitem>
+        <para>
+          <command>hostdrives</command>: Displays a list of host fixed
+          drives with their partitions.  If you also specify
+          <option>--long</option> or <option>-l</option> the sizes and
+          offsets will be in bytes.
         </para>
       </listitem>
@@ -5294,5 +5303,9 @@
                             [--diffparent &lt;uuid&gt;|&lt;filename&gt;
                             [--format VDI|VMDK|VHD] (default: VDI)
-                            [--variant Standard,Fixed,Split2G,Stream,ESX]</screen>
+                            [--variant Standard,Fixed,Split2G,Stream,ESX,RawDisk]
+                            [[--property &lt;name&gt;=&lt;value&gt;]
+                             --property &lt;name&gt;=&lt;value&gt;]...
+                            [[--property-file &lt;name&gt;=&lt;/path/to/file/with/value&gt;]
+                             --property-file &lt;name&gt;=&lt;/path/to/file/with/value&gt;]...</screen>
 
     <para>
@@ -5369,4 +5382,31 @@
             combinations are supported, and specifying mutually
             incompatible flags results in an error message. Optional.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <computeroutput>--property &lt;name&gt;=&lt;value&gt;</computeroutput>
+        </term>
+
+        <listitem>
+          <para>
+            Specifies any required file format dependent parameters in
+            <literal>key=value</literal> form. Optional.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <computeroutput>--property-file &lt;name&gt;=&lt;/path/to/file/with/value&gt;</computeroutput>
+        </term>
+
+        <listitem>
+          <para>
+            Specifies any required file format dependent parameters in
+            <literal>key=file/with/value</literal> form. The value is
+            taken from the file. Optional.
           </para>
         </listitem>
Index: /trunk/include/VBox/com/Guid.h
===================================================================
--- /trunk/include/VBox/com/Guid.h	(revision 85928)
+++ /trunk/include/VBox/com/Guid.h	(revision 85929)
@@ -324,4 +324,8 @@
     bool operator<(const RTUUID &guid) const { return ::RTUuidCompare(&mUuid, &guid) < 0; }
 
+    /** Compare with a UUID string representation.
+     * @note Not an operator as that could lead to confusion.  */
+    bool equalsString(const char *pszUuid2) const { return ::RTUuidCompareStr(&mUuid, pszUuid2) == 0; }
+
     /**
      * To directly copy the contents to a GUID, or for passing it as an input
Index: /trunk/include/VBox/log.h
===================================================================
--- /trunk/include/VBox/log.h	(revision 85928)
+++ /trunk/include/VBox/log.h	(revision 85929)
@@ -477,4 +477,10 @@
     /** Main group, IHost. */
     LOG_GROUP_MAIN_HOST,
+    /** Main group, IHostDrive. */
+    LOG_GROUP_MAIN_HOSTDRIVE,
+    /** Main group, IHostDriveList. */
+    LOG_GROUP_MAIN_HOSTDRIVELIST,
+    /** Main group, IHostDrivePartition. */
+    LOG_GROUP_MAIN_HOSTDRIVEPARTITION,
     /** Main group, IHostNetworkInterface. */
     LOG_GROUP_MAIN_HOSTNETWORKINTERFACE,
@@ -981,4 +987,7 @@
     "MAIN_GUESTSESSIONEVENT", \
     "MAIN_HOST", \
+    "MAIN_HOSTDRIVE", \
+    "MAIN_HOSTDRIVELIST", \
+    "MAIN_HOSTDRIVEPARTITION", \
     "MAIN_HOSTNETWORKINTERFACE", \
     "MAIN_HOSTUPDATE", \
Index: /trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp	(revision 85928)
+++ /trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp	(revision 85929)
@@ -29,4 +29,5 @@
 
 #include <iprt/asm.h>
+#include <iprt/base64.h>
 #include <iprt/file.h>
 #include <iprt/path.h>
@@ -38,4 +39,6 @@
 #include <VBox/log.h>
 #include <VBox/vd.h>
+
+#include <list>
 
 #include "VBoxManage.h"
@@ -96,4 +99,7 @@
             else if (!RTStrNICmp(psz, "formatted", len))
                 uMediumVariant |= MediumVariant_Formatted;
+            else if (   !RTStrNICmp(psz, "raw", len)
+                     || !RTStrNICmp(psz, "rawdisk", len))
+                uMediumVariant |= MediumVariant_VmdkRawDisk;
             else
                 rc = VERR_PARSE_ERROR;
@@ -244,9 +250,55 @@
     { "--variant",      'm', RTGETOPT_REQ_STRING },
     { "-variant",       'm', RTGETOPT_REQ_STRING },     // deprecated
-    { "--property",     'p', RTGETOPT_REQ_STRING }
+    { "--property",     'p', RTGETOPT_REQ_STRING },
+    { "--property-file",'P', RTGETOPT_REQ_STRING },
 };
 
 RTEXITCODE handleCreateMedium(HandlerArg *a)
 {
+    class MediumProperty
+    {
+    public:
+        const char *m_pszKey;
+        const char *m_pszValue; /**< Can be binary too. */
+        size_t      m_cbValue;
+        char       *m_pszFreeValue;
+        MediumProperty() : m_pszKey(NULL), m_pszValue(NULL), m_cbValue(0), m_pszFreeValue(NULL) { }
+        MediumProperty(MediumProperty const &a_rThat)
+            : m_pszKey(a_rThat.m_pszKey)
+            , m_pszValue(a_rThat.m_pszValue)
+            , m_cbValue(a_rThat.m_cbValue)
+            , m_pszFreeValue(NULL)
+        {
+            Assert(a_rThat.m_pszFreeValue == NULL); /* not expected here! */
+        }
+        ~MediumProperty()
+        {
+            RTMemFree(m_pszFreeValue);
+            m_pszFreeValue = NULL;
+        }
+
+    private:
+        MediumProperty &operator=(MediumProperty const &a_rThat)
+        {
+            m_pszKey = a_rThat.m_pszKey;
+            m_pszValue = a_rThat.m_pszValue;
+            m_cbValue = a_rThat.m_cbValue;
+            m_pszFreeValue = a_rThat.m_pszFreeValue;
+            if (a_rThat.m_pszFreeValue != NULL)
+            {
+                m_pszFreeValue = (char *)RTMemAlloc(m_cbValue + 1);
+                if (m_pszFreeValue)
+                {
+                    memcpy(m_pszFreeValue, m_pszValue, m_cbValue + 1);
+                    m_pszValue = m_pszFreeValue;
+                }
+                else
+                    RTMsgError("Out of memory copying '%s'", m_pszValue);
+            }
+            return *this;
+        }
+    };
+    std::list<MediumProperty> lstProperties;
+
     HRESULT rc;
     int vrc;
@@ -254,11 +306,4 @@
     const char *diffparent = NULL;
     uint64_t size = 0;
-    typedef struct MEDIUMPROPERTY_LIST
-    {
-        struct MEDIUMPROPERTY_LIST *next;
-        const char *key;
-        const char *value;
-    } MEDIUMPROPERTY, *PMEDIUMPROPERTY;
-    PMEDIUMPROPERTY pMediumProps = NULL;
     enum
     {
@@ -322,28 +367,55 @@
 
             case 'p':   // --property
+            case 'P':   // --property-file
             {
                 /* allocate property kvp, parse, and append to end of singly linked list */
-# define PROP_MAXLEN 256
-                PMEDIUMPROPERTY pNewProp = (PMEDIUMPROPERTY)RTMemAlloc(sizeof(MEDIUMPROPERTY));
-                if (!pNewProp)
-                    return errorArgument("Can't allocate memory for property '%s'", ValueUnion.psz);
-                size_t cchKvp = RTStrNLen(ValueUnion.psz, PROP_MAXLEN);
-                char *pszEqual = (char *)memchr(ValueUnion.psz, '=', cchKvp);
-                if (pszEqual)
+                char *pszValue = (char *)strchr(ValueUnion.psz, '=');
+                if (!pszValue)
+                    return RTMsgErrorExitFailure("Invalid key value pair: No '='.");
+
+                lstProperties.push_back(MediumProperty());
+                MediumProperty &rNewProp = lstProperties.back();
+                *pszValue++ = '\0';       /* Warning! Modifies argument string. */
+                rNewProp.m_pszKey = ValueUnion.psz;
+                if (c == 'p')
                 {
-                    *pszEqual = '\0';       /* Warning! Modifies argument string. */
-                    pNewProp->next = NULL;
-                    pNewProp->key = (char *)ValueUnion.psz;
-                    pNewProp->value = pszEqual + 1;
+                    rNewProp.m_pszValue = pszValue;
+                    rNewProp.m_cbValue  = strlen(pszValue);
                 }
-                if (pMediumProps)
+                else // 'P'
                 {
-                    PMEDIUMPROPERTY pProp;
-                    for (pProp = pMediumProps; pProp->next; pProp = pProp->next)
-                        continue;
-                    pProp->next = pNewProp;
+                    RTFILE hValueFile = NIL_RTFILE;
+                    vrc = RTFileOpen(&hValueFile, pszValue, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+                    if (RT_FAILURE(vrc))
+                        return RTMsgErrorExitFailure("Cannot open replacement value file '%s': %Rrc", pszValue, vrc);
+
+                    uint64_t cbValue = 0;
+                    vrc = RTFileQuerySize(hValueFile, &cbValue);
+                    if (RT_SUCCESS(vrc))
+                    {
+                        if (cbValue <= _16M)
+                        {
+                            rNewProp.m_cbValue  = (size_t)cbValue;
+                            rNewProp.m_pszValue = rNewProp.m_pszFreeValue = (char *)RTMemAlloc(rNewProp.m_cbValue + 1);
+                            if (rNewProp.m_pszFreeValue)
+                            {
+                                vrc = RTFileReadAt(hValueFile, 0, rNewProp.m_pszFreeValue, cbValue, NULL);
+                                if (RT_SUCCESS(vrc))
+                                    rNewProp.m_pszFreeValue[rNewProp.m_cbValue] = '\0';
+                                else
+                                    RTMsgError("Error reading replacement MBR file '%s': %Rrc", pszValue, vrc);
+                            }
+                            else
+                                vrc = RTMsgErrorRc(VERR_NO_MEMORY, "Out of memory reading '%s': %Rrc", pszValue, vrc);
+                        }
+                        else
+                            vrc = RTMsgErrorRc(VERR_OUT_OF_RANGE, "Replacement value file '%s' is to big: %Rhcb, max 16MiB", pszValue, cbValue);
+                    }
+                    else
+                        RTMsgError("Cannot get the size of the value file '%s': %Rrc", pszValue, vrc);
+                    RTFileClose(hValueFile);
+                    if (RT_FAILURE(vrc))
+                        return RTEXITCODE_FAILURE;
                 }
-                else
-                    pMediumProps = pNewProp;
                 break;
             }
@@ -389,8 +461,8 @@
     if (fBase)
     {
-        if (   !filename
-            || !*filename
-            || size == 0)
-            return errorSyntax(USAGE_CREATEMEDIUM, "Parameters --filename and --size are required");
+        if (!filename || !*filename)
+            return errorSyntax(USAGE_CREATEMEDIUM, "Parameters --filename is required");
+        if ((enmMediumVariant & MediumVariant_VmdkRawDisk) == 0 && size == 0)
+            return errorSyntax(USAGE_CREATEMEDIUM, "Parameters --size is required");
         if (!format || !*format)
         {
@@ -482,12 +554,47 @@
     if (SUCCEEDED(rc) && pMedium)
     {
-        if (pMediumProps)
-            for (PMEDIUMPROPERTY pProp = pMediumProps; pProp;)
+        if (lstProperties.size() > 0)
+        {
+            ComPtr<IMediumFormat> pMediumFormat;
+            CHECK_ERROR2I_RET(pMedium, COMGETTER(MediumFormat)(pMediumFormat.asOutParam()), RTEXITCODE_FAILURE);
+            com::SafeArray<BSTR> propertyNames;
+            com::SafeArray<BSTR> propertyDescriptions;
+            com::SafeArray<DataType_T> propertyTypes;
+            com::SafeArray<ULONG> propertyFlags;
+            com::SafeArray<BSTR> propertyDefaults;
+            CHECK_ERROR2I_RET(pMediumFormat,
+                              DescribeProperties(ComSafeArrayAsOutParam(propertyNames),
+                                                 ComSafeArrayAsOutParam(propertyDescriptions),
+                                                 ComSafeArrayAsOutParam(propertyTypes),
+                                                 ComSafeArrayAsOutParam(propertyFlags),
+                                                 ComSafeArrayAsOutParam(propertyDefaults)),
+                              RTEXITCODE_FAILURE);
+
+            for (std::list<MediumProperty>::iterator it = lstProperties.begin();
+                 it != lstProperties.end();
+                 ++it)
             {
-                CHECK_ERROR(pMedium, SetProperty(Bstr(pProp->key).raw(), Bstr(pProp->value).raw()));
-                PMEDIUMPROPERTY next = pProp->next;
-                RTMemFree(pProp);
-                pProp = next;
+                const char * const pszKey = it->m_pszKey;
+                bool fBinary = true;
+                for (size_t i = 0; i < propertyNames.size(); ++i)
+                    if (RTUtf16CmpUtf8(propertyNames[i], pszKey) == 0)
+                    {
+                        fBinary = propertyTypes[i] == DataType_Int8;
+                        break;
+                    }
+                if (!fBinary)
+                    CHECK_ERROR2I_RET(pMedium, SetProperty(Bstr(pszKey).raw(), Bstr(it->m_pszValue).raw()),
+                                      RTEXITCODE_FAILURE);
+                else
+                {
+                    com::Bstr bstrBase64Value;
+                    HRESULT hrc = bstrBase64Value.base64Encode(it->m_pszValue, it->m_cbValue);
+                    if (FAILED(hrc))
+                        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Base64 encoding of the property %s failed. (%Rhrc)",
+                                              pszKey, hrc);
+                    CHECK_ERROR2I_RET(pMedium, SetProperty(Bstr(pszKey).raw(), bstrBase64Value.raw()), RTEXITCODE_FAILURE);
+                }
             }
+        }
 
         ComPtr<IProgress> pProgress;
Index: /trunk/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp	(revision 85928)
+++ /trunk/src/VBox/Frontends/VBoxManage/VBoxManageHelp.cpp	(revision 85929)
@@ -480,7 +480,7 @@
                      "                            groups|webcams|screenshotformats|cloudproviders|\n"
 #if defined(VBOX_WITH_CLOUD_NET)
-                     "                            cloudprofiles|cloudnets|cpu-profiles\n"
+                     "                            cloudprofiles|cloudnets|cpu-profiles|hostdrives\n"
 #else
-                     "                            cloudprofiles|cpu-profiles\n"
+                     "                            cloudprofiles|cpu-profiles|hostdrives\n"
 #endif
                      "\n", SEP);
@@ -1011,6 +1011,7 @@
                      "                            [--format VDI|VMDK|VHD] (default: VDI)]\n"
                      "                            [--variant Standard,Fixed,Split2G,Stream,ESX,\n"
-                     "                                       Formatted]\n"
-                     "                            [[--property <name>=<value>] --property <name>=<value]...\n"
+                     "                                       Formatted,RawDisk]\n"
+                     "                            [[--property <name>=<value>] --property <name>=<value>\n"
+                     "                              --property-file <name>=</path/to/file/with/value>]...\n"
                      "\n", SEP);
 
Index: /trunk/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp	(revision 85928)
+++ /trunk/src/VBox/Frontends/VBoxManage/VBoxManageList.cpp	(revision 85929)
@@ -1415,4 +1415,345 @@
 
 /**
+ * Translates PartitionType_T to a string if possible.
+ * @returns read-only string if known value, @a pszUnknown if not.
+ */
+static const char *PartitionTypeToString(PartitionType_T enmType, const char *pszUnknown)
+{
+#define MY_CASE_STR(a_Type) case RT_CONCAT(PartitionType_,a_Type): return #a_Type
+    switch (enmType)
+    {
+        MY_CASE_STR(Empty);
+        MY_CASE_STR(FAT12);
+        MY_CASE_STR(FAT16);
+        MY_CASE_STR(FAT);
+        MY_CASE_STR(IFS);
+        MY_CASE_STR(FAT32CHS);
+        MY_CASE_STR(FAT32LBA);
+        MY_CASE_STR(FAT16B);
+        MY_CASE_STR(Extended);
+        MY_CASE_STR(WindowsRE);
+        MY_CASE_STR(LinuxSwapOld);
+        MY_CASE_STR(LinuxOld);
+        MY_CASE_STR(DragonFlyBSDSlice);
+        MY_CASE_STR(LinuxSwap);
+        MY_CASE_STR(Linux);
+        MY_CASE_STR(LinuxExtended);
+        MY_CASE_STR(LinuxLVM);
+        MY_CASE_STR(BSDSlice);
+        MY_CASE_STR(AppleUFS);
+        MY_CASE_STR(AppleHFS);
+        MY_CASE_STR(Solaris);
+        MY_CASE_STR(GPT);
+        MY_CASE_STR(EFI);
+        MY_CASE_STR(Unknown);
+        MY_CASE_STR(MBR);
+        MY_CASE_STR(iFFS);
+        MY_CASE_STR(SonyBoot);
+        MY_CASE_STR(LenovoBoot);
+        MY_CASE_STR(WindowsMSR);
+        MY_CASE_STR(WindowsBasicData);
+        MY_CASE_STR(WindowsLDMMeta);
+        MY_CASE_STR(WindowsLDMData);
+        MY_CASE_STR(WindowsRecovery);
+        MY_CASE_STR(WindowsStorageSpaces);
+        MY_CASE_STR(WindowsStorageReplica);
+        MY_CASE_STR(IBMGPFS);
+        MY_CASE_STR(LinuxData);
+        MY_CASE_STR(LinuxRAID);
+        MY_CASE_STR(LinuxRootX86);
+        MY_CASE_STR(LinuxRootAMD64);
+        MY_CASE_STR(LinuxRootARM32);
+        MY_CASE_STR(LinuxRootARM64);
+        MY_CASE_STR(LinuxHome);
+        MY_CASE_STR(LinuxSrv);
+        MY_CASE_STR(LinuxPlainDmCrypt);
+        MY_CASE_STR(LinuxLUKS);
+        MY_CASE_STR(LinuxReserved);
+        MY_CASE_STR(FreeBSDBoot);
+        MY_CASE_STR(FreeBSDData);
+        MY_CASE_STR(FreeBSDSwap);
+        MY_CASE_STR(FreeBSDUFS);
+        MY_CASE_STR(FreeBSDVinum);
+        MY_CASE_STR(FreeBSDZFS);
+        MY_CASE_STR(FreeBSDUnknown);
+        MY_CASE_STR(AppleHFSPlus);
+        MY_CASE_STR(AppleAPFS);
+        MY_CASE_STR(AppleRAID);
+        MY_CASE_STR(AppleRAIDOffline);
+        MY_CASE_STR(AppleBoot);
+        MY_CASE_STR(AppleLabel);
+        MY_CASE_STR(AppleTvRecovery);
+        MY_CASE_STR(AppleCoreStorage);
+        MY_CASE_STR(SoftRAIDStatus);
+        MY_CASE_STR(SoftRAIDScratch);
+        MY_CASE_STR(SoftRAIDVolume);
+        MY_CASE_STR(SoftRAIDCache);
+        MY_CASE_STR(AppleUnknown);
+        MY_CASE_STR(SolarisBoot);
+        MY_CASE_STR(SolarisRoot);
+        MY_CASE_STR(SolarisSwap);
+        MY_CASE_STR(SolarisBackup);
+        MY_CASE_STR(SolarisUsr);
+        MY_CASE_STR(SolarisVar);
+        MY_CASE_STR(SolarisHome);
+        MY_CASE_STR(SolarisAltSector);
+        MY_CASE_STR(SolarisReserved);
+        MY_CASE_STR(SolarisUnknown);
+        MY_CASE_STR(NetBSDSwap);
+        MY_CASE_STR(NetBSDFFS);
+        MY_CASE_STR(NetBSDLFS);
+        MY_CASE_STR(NetBSDRAID);
+        MY_CASE_STR(NetBSDConcatenated);
+        MY_CASE_STR(NetBSDEncrypted);
+        MY_CASE_STR(NetBSDUnknown);
+        MY_CASE_STR(ChromeOSKernel);
+        MY_CASE_STR(ChromeOSRootFS);
+        MY_CASE_STR(ChromeOSFuture);
+        MY_CASE_STR(ContLnxUsr);
+        MY_CASE_STR(ContLnxRoot);
+        MY_CASE_STR(ContLnxReserved);
+        MY_CASE_STR(ContLnxRootRAID);
+        MY_CASE_STR(HaikuBFS);
+        MY_CASE_STR(MidntBSDBoot);
+        MY_CASE_STR(MidntBSDData);
+        MY_CASE_STR(MidntBSDSwap);
+        MY_CASE_STR(MidntBSDUFS);
+        MY_CASE_STR(MidntBSDVium);
+        MY_CASE_STR(MidntBSDZFS);
+        MY_CASE_STR(MidntBSDUnknown);
+        MY_CASE_STR(OpenBSDData);
+        MY_CASE_STR(QNXPowerSafeFS);
+        MY_CASE_STR(Plan9);
+        MY_CASE_STR(VMWareVMKCore);
+        MY_CASE_STR(VMWareVMFS);
+        MY_CASE_STR(VMWareReserved);
+        MY_CASE_STR(VMWareUnknown);
+        MY_CASE_STR(AndroidX86Bootloader);
+        MY_CASE_STR(AndroidX86Bootloader2);
+        MY_CASE_STR(AndroidX86Boot);
+        MY_CASE_STR(AndroidX86Recovery);
+        MY_CASE_STR(AndroidX86Misc);
+        MY_CASE_STR(AndroidX86Metadata);
+        MY_CASE_STR(AndroidX86System);
+        MY_CASE_STR(AndroidX86Cache);
+        MY_CASE_STR(AndroidX86Data);
+        MY_CASE_STR(AndroidX86Persistent);
+        MY_CASE_STR(AndroidX86Vendor);
+        MY_CASE_STR(AndroidX86Config);
+        MY_CASE_STR(AndroidX86Factory);
+        MY_CASE_STR(AndroidX86FactoryAlt);
+        MY_CASE_STR(AndroidX86Fastboot);
+        MY_CASE_STR(AndroidX86OEM);
+        MY_CASE_STR(AndroidARMMeta);
+        MY_CASE_STR(AndroidARMExt);
+        MY_CASE_STR(ONIEBoot);
+        MY_CASE_STR(ONIEConfig);
+        MY_CASE_STR(PowerPCPrep);
+        MY_CASE_STR(XDGShrBootConfig);
+        MY_CASE_STR(CephBlock);
+        MY_CASE_STR(CephBlockDB);
+        MY_CASE_STR(CephBlockDBDmc);
+        MY_CASE_STR(CephBlockDBDmcLUKS);
+        MY_CASE_STR(CephBlockDmc);
+        MY_CASE_STR(CephBlockDmcLUKS);
+        MY_CASE_STR(CephBlockWALog);
+        MY_CASE_STR(CephBlockWALogDmc);
+        MY_CASE_STR(CephBlockWALogDmcLUKS);
+        MY_CASE_STR(CephDisk);
+        MY_CASE_STR(CephDiskDmc);
+        MY_CASE_STR(CephJournal);
+        MY_CASE_STR(CephJournalDmc);
+        MY_CASE_STR(CephJournalDmcLUKS);
+        MY_CASE_STR(CephLockbox);
+        MY_CASE_STR(CephMultipathBlock1);
+        MY_CASE_STR(CephMultipathBlock2);
+        MY_CASE_STR(CephMultipathBlockDB);
+        MY_CASE_STR(CephMultipathBLockWALog);
+        MY_CASE_STR(CephMultipathJournal);
+        MY_CASE_STR(CephMultipathOSD);
+        MY_CASE_STR(CephOSD);
+        MY_CASE_STR(CephOSDDmc);
+        MY_CASE_STR(CephOSDDmcLUKS);
+#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
+        case PartitionType_32BitHack: break;
+#endif
+        /* no default! */
+    }
+#undef MY_CASE_STR
+    return pszUnknown;
+}
+
+
+/**
+ * List all available host drives with their partitions.
+ *
+ * @returns See produceList.
+ * @param   pVirtualBox         Reference to the IVirtualBox pointer.
+ * @param   fOptLong            Long listing or human readable.
+ */
+static HRESULT listHostDrives(const ComPtr<IVirtualBox> pVirtualBox, bool fOptLong)
+{
+    HRESULT rc = S_OK;
+    ComPtr<IHost> pHost;
+    CHECK_ERROR2I_RET(pVirtualBox, COMGETTER(Host)(pHost.asOutParam()), hrcCheck);
+    com::SafeIfaceArray<IHostDrive> apHostDrives;
+    CHECK_ERROR2I_RET(pHost, COMGETTER(HostDrives)(ComSafeArrayAsOutParam(apHostDrives)), hrcCheck);
+    for (size_t i = 0; i < apHostDrives.size(); ++i)
+    {
+        ComPtr<IHostDrive> pHostDrive = apHostDrives[i];
+
+        com::Bstr bstrDrivePath;
+        CHECK_ERROR(pHostDrive,COMGETTER(DrivePath)(bstrDrivePath.asOutParam()));
+        RTPrintf("%sDrive:       %ls\n", i > 0 ? "\n" : "", bstrDrivePath.raw());
+
+        com::Bstr bstrModel;
+        com::Bstr bstrUuidDisk;
+        ULONG cbSectorSize = 0;
+        LONG64 cbSize = 0;
+        PartitioningType_T partitioningType;
+        HRESULT hrc;
+        if (   SUCCEEDED(hrc = pHostDrive->COMGETTER(Model)(bstrModel.asOutParam()))
+            && SUCCEEDED(hrc = pHostDrive->COMGETTER(Uuid)(bstrUuidDisk.asOutParam()))
+            && SUCCEEDED(hrc = pHostDrive->COMGETTER(SectorSize)(&cbSectorSize))
+            && SUCCEEDED(hrc = pHostDrive->COMGETTER(Size)(&cbSize))
+            && SUCCEEDED(hrc = pHostDrive->COMGETTER(PartitioningType)(&partitioningType)))
+        {
+            if (bstrModel.isNotEmpty())
+                RTPrintf("Model:       %ls\n", bstrModel.raw());
+            else
+                RTPrintf("Model:       Unknown\n");
+
+            if (partitioningType == PartitioningType_GPT || com::Guid(bstrUuidDisk).isZero())
+                RTPrintf("UUID:        %ls\n", bstrUuidDisk.raw());
+            if (fOptLong)
+                RTPrintf("Size:        %llu bytes (%Rhcb)\n", cbSize, cbSize);
+            else
+                RTPrintf("Size:        %Rhcb\n", cbSize);
+            RTPrintf("Sector Size: %u bytes\n", cbSectorSize);
+            RTPrintf("Scheme:      %s\n", partitioningType == PartitioningType_MBR ? "MBR" : "GPT");
+
+            com::SafeIfaceArray<IHostDrivePartition> apHostDrivesPartitions;
+            CHECK_ERROR(pHostDrive, COMGETTER(Partitions)(ComSafeArrayAsOutParam(apHostDrivesPartitions)));
+
+            if (partitioningType == PartitioningType_MBR)
+            {
+                if (fOptLong)
+                    RTPrintf("Partitions:                              First         Last\n"
+                             "##  Type      Byte Size     Byte Offset  Cyl/Head/Sec  Cyl/Head/Sec Active\n");
+                else
+                    RTPrintf("Partitions:                   First         Last\n"
+                             "##  Type  Size      Start     Cyl/Head/Sec  Cyl/Head/Sec Active\n");
+                for (size_t j = 0; j < apHostDrivesPartitions.size(); ++j)
+                {
+                    ComPtr<IHostDrivePartition> pHostDrivePartition = apHostDrivesPartitions[j];
+
+                    ULONG idx = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Number)(&idx));
+                    ULONG uType = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(TypeMBR)(&uType));
+                    ULONG uStartCylinder = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(StartCylinder)(&uStartCylinder));
+                    ULONG uStartHead = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(StartHead)(&uStartHead));
+                    ULONG uStartSector = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(StartSector)(&uStartSector));
+                    ULONG uEndCylinder = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(EndCylinder)(&uEndCylinder));
+                    ULONG uEndHead = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(EndHead)(&uEndHead));
+                    ULONG uEndSector = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(EndSector)(&uEndSector));
+                    cbSize = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Size)(&cbSize));
+                    LONG64 offStart = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Start)(&offStart));
+                    BOOL fActive = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Active)(&fActive));
+                    PartitionType_T enmType = PartitionType_Unknown;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Type)(&enmType));
+
+                    /* Max size & offset  here is around 16TiB with 4KiB sectors. */
+                    if (fOptLong) /* cb/off: max 16TiB; idx: max 64. */
+                        RTPrintf("%2u   %02x  %14llu  %14llu  %4u/%3u/%2u   %4u/%3u/%2u    %s   %s\n",
+                                 idx, uType, cbSize, offStart,
+                                 uStartCylinder, uStartHead, uStartSector, uEndCylinder, uEndHead, uEndSector,
+                                 fActive ? "yes" : "no", PartitionTypeToString(enmType, ""));
+                    else
+                        RTPrintf("%2u   %02x   %8Rhcb  %8Rhcb  %4u/%3u/%2u   %4u/%3u/%2u   %s   %s\n",
+                                 idx, uType, (uint64_t)cbSize, (uint64_t)offStart,
+                                 uStartCylinder, uStartHead, uStartSector, uEndCylinder, uEndHead, uEndSector,
+                                 fActive ? "yes" : "no", PartitionTypeToString(enmType, ""));
+                }
+            }
+            else /* GPT */
+            {
+                /* Determin the max partition type length to try reduce the table width: */
+                size_t cchMaxType = 0;
+                for (size_t j = 0; j < apHostDrivesPartitions.size(); ++j)
+                {
+                    ComPtr<IHostDrivePartition> pHostDrivePartition = apHostDrivesPartitions[j];
+                    PartitionType_T enmType = PartitionType_Unknown;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Type)(&enmType));
+                    size_t const cchTypeNm = strlen(PartitionTypeToString(enmType, "e530bf6d-2754-4e9d-b260-60a5d0b80457"));
+                    cchMaxType = RT_MAX(cchTypeNm, cchMaxType);
+                }
+                cchMaxType = RT_MIN(cchMaxType, RTUUID_STR_LENGTH);
+
+                if (fOptLong)
+                    RTPrintf("Partitions:\n"
+                             "## %-*s Uuid                                           Byte Size         Byte Offset Active Name\n",
+                             (int)cchMaxType, "Type");
+                else
+                    RTPrintf("Partitions:\n"
+                             "##  %-*s  Uuid                                   Size      Start   Active Name\n",
+                             (int)cchMaxType, "Type");
+
+                for (size_t j = 0; j < apHostDrivesPartitions.size(); ++j)
+                {
+                    ComPtr<IHostDrivePartition> pHostDrivePartition = apHostDrivesPartitions[j];
+
+                    ULONG idx = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Number)(&idx));
+                    com::Bstr bstrUuidType;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(TypeUuid)(bstrUuidType.asOutParam()));
+                    com::Bstr bstrUuidPartition;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Uuid)(bstrUuidPartition.asOutParam()));
+                    cbSize = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Size)(&cbSize));
+                    LONG64 offStart = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Start)(&offStart));
+                    BOOL fActive = 0;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Active)(&fActive));
+                    com::Bstr bstrName;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Name)(bstrName.asOutParam()));
+
+                    PartitionType_T enmType = PartitionType_Unknown;
+                    CHECK_ERROR(pHostDrivePartition, COMGETTER(Type)(&enmType));
+
+                    Utf8Str strTypeConv;
+                    const char *pszTypeNm = PartitionTypeToString(enmType, NULL);
+                    if (!pszTypeNm)
+                        pszTypeNm = (strTypeConv = bstrUuidType).c_str();
+                    else if (strlen(pszTypeNm) >= RTUUID_STR_LENGTH /* includes '\0' */)
+                        pszTypeNm -= RTUUID_STR_LENGTH - 1 - strlen(pszTypeNm);
+
+                    if (fOptLong)
+                        RTPrintf("%2u %-*s %36ls %19llu %19llu   %-3s  %ls\n", idx, cchMaxType, pszTypeNm,
+                                 bstrUuidPartition.raw(), cbSize, offStart, fActive ? "on" : "off", bstrName.raw());
+                    else
+                        RTPrintf("%2u  %-*s  %36ls  %8Rhcb  %8Rhcb  %-3s   %ls\n", idx, cchMaxType, pszTypeNm,
+                                 bstrUuidPartition.raw(), cbSize, offStart, fActive ? "on" : "off", bstrName.raw());
+                }
+            }
+        }
+        else
+            RTPrintf("Partitions and disk info for the drive %ls are not available. Error %Rhrc (%#RX32)\n",
+                     bstrDrivePath.raw(), hrc, hrc);
+    }
+    return rc;
+}
+
+
+/**
  * The type of lists we can produce.
  */
@@ -1450,5 +1791,6 @@
     kListCloudProviders,
     kListCloudProfiles,
-    kListCPUProfiles
+    kListCPUProfiles,
+    kListHostDrives
 };
 
@@ -1807,4 +2149,7 @@
             break;
 
+        case kListHostDrives:
+            rc = listHostDrives(pVirtualBox, fOptLong);
+            break;
         /* No default here, want gcc warnings. */
 
@@ -1865,4 +2210,5 @@
         { "cloudprofiles",      kListCloudProfiles,      RTGETOPT_REQ_NOTHING },
         { "cpu-profiles",       kListCPUProfiles,        RTGETOPT_REQ_NOTHING },
+        { "hostdrives",         kListHostDrives,         RTGETOPT_REQ_NOTHING },
     };
 
@@ -1922,4 +2268,5 @@
             case kListCloudProfiles:
             case kListCPUProfiles:
+            case kListHostDrives:
                 enmOptCommand = (enum ListType_T)ch;
                 if (fOptMultiple)
Index: /trunk/src/VBox/Main/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Main/Makefile.kmk	(revision 85928)
+++ /trunk/src/VBox/Main/Makefile.kmk	(revision 85929)
@@ -582,4 +582,6 @@
 	src-server/HostUpdateImpl.cpp \
 	src-server/HostVideoInputDeviceImpl.cpp \
+	src-server/HostDrivePartitionImpl.cpp \
+	src-server/HostDriveImpl.cpp \
 	src-server/MachineImpl.cpp \
 	src-all/MachineLaunchVMCommonWorker.cpp \
@@ -653,4 +655,6 @@
 	src-server/freebsd/HostHardwareFreeBSD.cpp \
 	src-server/HostDnsServiceResolvConf.cpp
+
+VBoxSVC_LDFLAGS.freebsd += -lcam
 
 
Index: /trunk/src/VBox/Main/idl/VirtualBox.xidl
===================================================================
--- /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 85928)
+++ /trunk/src/VBox/Main/idl/VirtualBox.xidl	(revision 85929)
@@ -1535,4 +1535,202 @@
     </const>
   </enum>
+
+  <enum
+    name="PartitioningType"
+    uuid="64c4c806-8908-4c0b-9a51-2d7a0151321f"
+    >
+    <desc>
+      The type of the disk partition.
+    </desc>
+
+    <const name="MBR"     value="0"/>
+    <const name="GPT"     value="1"/>
+  </enum>
+
+  <enum
+    name="PartitionType"
+    uuid="84a6629c-8e9c-474c-adbb-21995671597f"
+    >
+    <desc></desc>
+
+    <!-- MBR partition IDs using the same value: -->
+    <const name="Empty"                 value="0"><desc>Empty partition entry</desc></const>
+    <const name="FAT12"                 value="1">  <desc>FAT12 if partition size less than 65536 sectors</desc></const>
+    <const name="FAT16"                 value="4">  <desc>FAT16 if partition size less than 65536 sectors</desc></const>
+    <const name="FAT"                   value="6">  <desc>FAT12 or FAT16 if partition size greater or equal than 65536 sectors</desc></const>
+    <const name="IFS"                   value="7">  <desc>NT and OS/2 installable file system, e.g. NTFS, exFAT, HPFS.</desc></const>
+    <const name="FAT32CHS"              value="11"> <desc>The FAT32 with CHS addressing.</desc></const>
+    <const name="FAT32LBA"              value="12"> <desc>The FAT32 with LBA addressing.</desc></const>
+    <const name="FAT16B"                value="14"> <desc>The FAT16 with LBA addressing.</desc></const>
+    <const name="Extended"              value="15"> <desc>The extended partition with LBA addressing.</desc></const>
+    <const name="WindowsRE"             value="39"> <desc>Windows Recovery Environment (RE) partition (hidden NTFS partition).</desc></const>
+    <const name="LinuxSwapOld"          value="66"> <desc>The linux swap partition (old versions).</desc></const>
+    <const name="LinuxOld"              value="67"> <desc>The linux native partition (old versions).</desc></const>
+    <const name="DragonFlyBSDSlice"     value="108"><desc>The BSD slice.</desc></const>
+    <const name="LinuxSwap"             value="130"><desc>The linux swap partition.</desc></const>
+    <const name="Linux"                 value="131"><desc>The linux native partition.</desc></const>
+    <const name="LinuxExtended"         value="133"><desc>The linux extended partition.</desc></const>
+    <const name="LinuxLVM"              value="142"><desc>The linux LVM partition.</desc></const>
+    <const name="BSDSlice"              value="165"><desc>The BSD slice.</desc></const>
+    <const name="AppleUFS"              value="168"><desc>The Apple UFS partition.</desc></const>
+    <const name="AppleHFS"              value="175"><desc>The Apple HFS partition.</desc></const>
+    <const name="Solaris"               value="191"><desc>The Apple HFS partition.</desc></const>
+    <const name="GPT"                   value="238"><desc>The GPT protective MBR partition.</desc></const>
+    <const name="EFI"                   value="239"><desc>The EFI system partition.</desc></const>
+
+    <!-- Dummy value: -->
+    <const name="Unknown"               value="256"><desc>Unknown partition type.</desc></const>
+
+    <!-- GPT partition types: -->
+    <const name="MBR"                   value="257"><desc>MBR partition scheme.</desc></const>
+    <const name="iFFS"                  value="258"><desc>Intel Fast Flash (iFFS) partition.</desc></const>
+    <const name="SonyBoot"              value="259"><desc>Sony boot partition.</desc></const>
+    <const name="LenovoBoot"            value="260"><desc>Lenovo boot partition.</desc></const>
+
+    <const name="WindowsMSR"            value="270"><desc>Microsoft Reserved Partition (MSR).</desc></const>
+    <const name="WindowsBasicData"      value="271"><desc>Windows Basic data partition</desc></const>
+    <const name="WindowsLDMMeta"        value="272"><desc>Windows Logical Disk Manager (LDM) metadata partition.</desc></const>
+    <const name="WindowsLDMData"        value="273"><desc>Windows Logical Disk Manager data partition.</desc></const>
+    <const name="WindowsRecovery"       value="274"><desc>Windows Recovery Environment.</desc></const>
+    <const name="WindowsStorageSpaces"  value="276"><desc>Windows Storage Spaces partition.</desc></const>
+    <const name="WindowsStorageReplica" value="277"><desc>Windows Storage Replica partition.</desc></const>
+    <const name="IBMGPFS"               value="275"><desc>IBM General Parallel File System (GPFS) partition.</desc></const>
+
+    <const name="LinuxData"             value="300"><desc>Linux filesystem data.</desc></const>
+    <const name="LinuxRAID"             value="301"><desc>Linux RAID partition.</desc></const>
+    <const name="LinuxRootX86"          value="302"><desc>Linux root partition for x86.</desc></const>
+    <const name="LinuxRootAMD64"        value="303"><desc>Linux root partition for AMD64.</desc></const>
+    <const name="LinuxRootARM32"        value="304"><desc>Linux root partition for ARM32.</desc></const>
+    <const name="LinuxRootARM64"        value="305"><desc>Linux root partition for ARM64 / AArch64.</desc></const>
+    <const name="LinuxHome"             value="306"><desc>Linux /home partition.</desc></const>
+    <const name="LinuxSrv"              value="307"><desc>Linux /srv partition..</desc></const>
+    <const name="LinuxPlainDmCrypt"     value="308"><desc>Linux plain dm-crypt partition.</desc></const>
+    <const name="LinuxLUKS"             value="309"><desc>Linux unitified key setup (LUKS) partition.</desc></const>
+    <const name="LinuxReserved"         value="310"><desc>Linux reserved partition.</desc></const>
+
+    <const name="FreeBSDBoot"           value="330"><desc>FreeBSD boot partition.</desc></const>
+    <const name="FreeBSDData"           value="331"><desc>FreeBSD data partition.</desc></const>
+    <const name="FreeBSDSwap"           value="332"><desc>FreeBSD swap partition.</desc></const>
+    <const name="FreeBSDUFS"            value="333"><desc>FreeBSD unix file system (UFS) partition.</desc></const>
+    <const name="FreeBSDVinum"          value="334"><desc>FreeBSD Vinum volume manager partition.</desc></const>
+    <const name="FreeBSDZFS"            value="335"><desc>FreeBSD ZFS partition.</desc></const>
+    <const name="FreeBSDUnknown"        value="359"><desc>Unknown FreeBSD partition.</desc></const>
+
+    <const name="AppleHFSPlus"          value="360"><desc>Apple hierarchical file system plus (HFS+) partition.</desc></const>
+    <const name="AppleAPFS"             value="361"><desc>Apple APFS/FileFault container partition.</desc></const>
+    <const name="AppleRAID"             value="362"><desc>Apple RAID partition.</desc></const>
+    <const name="AppleRAIDOffline"      value="363"><desc>Apple RAID partition, offline.</desc></const>
+    <const name="AppleBoot"             value="364"><desc>Apple boot partition.</desc></const>
+    <const name="AppleLabel"            value="365"><desc>Apple label.</desc></const>
+    <const name="AppleTvRecovery"       value="366"><desc>Apple TV recovery partition.</desc></const>
+    <const name="AppleCoreStorage"      value="367"><desc>Apple Core Storage Containe.</desc></const>
+    <const name="SoftRAIDStatus"        value="370"><desc>SoftRAID status.</desc></const>
+    <const name="SoftRAIDScratch"       value="371"><desc>SoftRAID scratch.</desc></const>
+    <const name="SoftRAIDVolume"        value="372"><desc>SoftRAID volume.</desc></const>
+    <const name="SoftRAIDCache"         value="373"><desc>SoftRAID cache.</desc></const>
+    <const name="AppleUnknown"          value="389"><desc>Unknown Apple partition.</desc></const>
+
+    <const name="SolarisBoot"           value="390"><desc>Solaris boot partition.</desc></const>
+    <const name="SolarisRoot"           value="391"><desc>Solaris root partition.</desc></const>
+    <const name="SolarisSwap"           value="392"><desc>Solaris swap partition.</desc></const>
+    <const name="SolarisBackup"         value="393"><desc>Solaris backup partition.</desc></const>
+    <const name="SolarisUsr"            value="394"><desc>Solaris /usr partition.</desc></const>
+    <const name="SolarisVar"            value="395"><desc>Solaris /var partition.</desc></const>
+    <const name="SolarisHome"           value="396"><desc>Solaris /home partition.</desc></const>
+    <const name="SolarisAltSector"      value="397"><desc>Solaris alternate sector.</desc></const>
+    <const name="SolarisReserved"       value="398"><desc>Solaris reserved partition.</desc></const>
+    <const name="SolarisUnknown"        value="419"><desc>Unknown Solaris partition.</desc></const>
+
+    <const name="NetBSDSwap"            value="420"><desc>NetBSD swap partition.</desc></const>
+    <const name="NetBSDFFS"             value="421"><desc>NetBSD fast file system (FFS) partition.</desc></const>
+    <const name="NetBSDLFS"             value="422"><desc>NetBSD log structured file system (LFS) partition.</desc></const>
+    <const name="NetBSDRAID"            value="423"><desc>NetBSD RAID partition.</desc></const>
+    <const name="NetBSDConcatenated"    value="424"><desc>NetBSD concatenated partition.</desc></const>
+    <const name="NetBSDEncrypted"       value="425"><desc>NetBSD encrypted partition.</desc></const>
+    <const name="NetBSDUnknown"         value="449"><desc>Unknown NetBSD partition.</desc></const>
+
+    <const name="ChromeOSKernel"        value="450"><desc>Chrome OS kernel partition.</desc></const>
+    <const name="ChromeOSRootFS"        value="451"><desc>Chrome OS root file system partition.</desc></const>
+    <const name="ChromeOSFuture"        value="452"><desc>Chrome OS partition reserved for future.</desc></const>
+
+    <const name="ContLnxUsr"            value="480"><desc>Container Linux /usr partition.</desc></const>
+    <const name="ContLnxRoot"           value="481"><desc>Container Linux resizable root filesystem partition.</desc></const>
+    <const name="ContLnxReserved"       value="482"><desc>Container Linux OEM customization partition.</desc></const>
+    <const name="ContLnxRootRAID"       value="483"><desc>Container Linux root filesystem on RAID partition.</desc></const>
+
+    <const name="HaikuBFS"              value="510"><desc>Haiku BFS</desc></const>
+
+    <const name="MidntBSDBoot"          value="540"><desc>MidnightBSD boot partition.</desc></const>
+    <const name="MidntBSDData"          value="541"><desc>MidnightBSD data partition.</desc></const>
+    <const name="MidntBSDSwap"          value="542"><desc>MidnightBSD swap partition.</desc></const>
+    <const name="MidntBSDUFS"           value="543"><desc>MidnightBSD unix file system (UFS) partition.</desc></const>
+    <const name="MidntBSDVium"          value="544"><desc>MidnightBSD Vinum volume manager partition.</desc></const>
+    <const name="MidntBSDZFS"           value="545"><desc>MidnightBSD ZFS partition.</desc></const>
+    <const name="MidntBSDUnknown"       value="569"><desc>Unknown MidnightBSD partition.</desc></const>
+
+    <const name="OpenBSDData"           value="570"><desc>OpenBSD data partition.</desc></const>
+
+    <const name="QNXPowerSafeFS"        value="600"><desc>QNX power-safe file system partition.</desc></const>
+
+    <const name="Plan9"                 value="630"><desc>Plan 9 partition.</desc></const>
+
+    <const name="VMWareVMKCore"         value="660"><desc>VMWare ESX coredump partition.</desc></const>
+    <const name="VMWareVMFS"            value="661"><desc>VMWare ESX virtual machine file system (VMFS) partition.</desc></const>
+    <const name="VMWareReserved"        value="662"><desc>VMWare ESX reserved partition.</desc></const>
+    <const name="VMWareUnknown"         value="689"><desc>Unknown VMWare partition.</desc></const>
+
+    <const name="AndroidX86Bootloader"  value="690"><desc>Android x86 bootloader partition.</desc></const>
+    <const name="AndroidX86Bootloader2" value="691"><desc>Android x86 bootloader2 partition.</desc></const>
+    <const name="AndroidX86Boot"        value="692"><desc>Android x86 boot partition.</desc></const>
+    <const name="AndroidX86Recovery"    value="693"><desc>Android x86 recovery partition.</desc></const>
+    <const name="AndroidX86Misc"        value="694"><desc>Android x86 misc partition.</desc></const>
+    <const name="AndroidX86Metadata"    value="695"><desc>Android x86 metadata partition.</desc></const>
+    <const name="AndroidX86System"      value="696"><desc>Android x86 system partition.</desc></const>
+    <const name="AndroidX86Cache"       value="697"><desc>Android x86 cache partition.</desc></const>
+    <const name="AndroidX86Data"        value="698"><desc>Android x86 data partition.</desc></const>
+    <const name="AndroidX86Persistent"  value="699"><desc>Android x86 persistent data partition.</desc></const>
+    <const name="AndroidX86Vendor"      value="700"><desc>Android x86 vendor partition.</desc></const>
+    <const name="AndroidX86Config"      value="701"><desc>Android x86 config partition.</desc></const>
+    <const name="AndroidX86Factory"     value="702"><desc>Android x86 factory partition.</desc></const>
+    <const name="AndroidX86FactoryAlt"  value="703"><desc>Android x86 alternative factory partition.</desc></const>
+    <const name="AndroidX86Fastboot"    value="704"><desc>Android x86 fastboot partition.</desc></const>
+    <const name="AndroidX86OEM"         value="705"><desc>Android x86 OEM partition.</desc></const>
+
+    <const name="AndroidARMMeta"        value="720"><desc>Android ARM meta partition.</desc></const>
+    <const name="AndroidARMExt"         value="721"><desc>Android ARM EXT partition.</desc></const>
+
+    <const name="ONIEBoot"              value="750"><desc>Open Network Install Environment (ONIE) boot partition.</desc></const>
+    <const name="ONIEConfig"            value="751"><desc>Open Network Install Environment (ONIE) config partition.</desc></const>
+
+    <const name="PowerPCPrep"           value="780"><desc>PowerPC PReP boot partition.</desc></const>
+
+    <const name="XDGShrBootConfig"      value="810"><desc>freedesktop.org shared boot loader configuration partition.</desc></const>
+
+    <const name="CephBlock"             value="830"><desc>Ceph block partition.</desc></const>
+    <const name="CephBlockDB"           value="831"><desc>Ceph block DB partition.</desc></const>
+    <const name="CephBlockDBDmc"        value="832"><desc>Ceph dm-crypt block DB partition.</desc></const>
+    <const name="CephBlockDBDmcLUKS"    value="833"><desc>Ceph dm-crypt Linux unitified key setup (LUKS) block DB partition.</desc></const>
+    <const name="CephBlockDmc"          value="834"><desc>Ceph dm-crypt block partition.</desc></const>
+    <const name="CephBlockDmcLUKS"      value="835"><desc>Ceph dm-crypt Linux unitified key setup (LUKS) block partition.</desc></const>
+    <const name="CephBlockWALog"        value="836"><desc>Ceph block write-ahead log partition.</desc></const>
+    <const name="CephBlockWALogDmc"     value="837"><desc>Ceph dm-crypt block write-ahead log partition.</desc></const>
+    <const name="CephBlockWALogDmcLUKS" value="838"><desc>Ceph dm-crypt Linux unitified key setup (LUKS) block write-ahead log partition.</desc></const>
+    <const name="CephDisk"              value="839"><desc>Ceph disk in creation partition.</desc></const>
+    <const name="CephDiskDmc"           value="840"><desc>Ceph dm-crypt disk in creation partition.</desc></const>
+    <const name="CephJournal"           value="841"><desc>Ceph Journal partition.</desc></const>
+    <const name="CephJournalDmc"        value="842"><desc>Ceph dm-crypt journal partition.</desc></const>
+    <const name="CephJournalDmcLUKS"    value="843"><desc>Ceph dm-crypt Linux unitified key setup (LUKS) journal partition.</desc></const>
+    <const name="CephLockbox"           value="844"><desc>Ceph Lockbox for dm-crypt keys partition.</desc></const>
+    <const name="CephMultipathBlock1"   value="845"><desc>Ceph multipath block 1 partition.</desc></const>
+    <const name="CephMultipathBlock2"   value="846"><desc>Ceph multipath block 2 partition.</desc></const>
+    <const name="CephMultipathBlockDB"  value="847"><desc>Ceph multipath block DB partition.</desc></const>
+    <const name="CephMultipathBLockWALog" value="848"><desc>Ceph multipath block write-ahead log partition.</desc></const>
+    <const name="CephMultipathJournal"  value="849"><desc>Ceph multipath journal partition.</desc></const>
+    <const name="CephMultipathOSD"      value="851"><desc>Ceph multipath object storage deamon (OSD) partition.</desc></const>
+    <const name="CephOSD"               value="852"><desc>Ceph object storage deamon (OSD) partition.</desc></const>
+    <const name="CephOSDDmc"            value="853"><desc>Ceph dm-crypt object storage deamon (OSD) partition.</desc></const>
+    <const name="CephOSDDmcLUKS"        value="854"><desc>Ceph dm-crypt Linux unitified key setup (LUKS) object storage deamon (OSD) partition.</desc></const>
+  </enum>
+
 
   <!--
@@ -10300,6 +10498,171 @@
 
   <interface
+    name="IHostDrivePartition" extends="$unknown"
+    uuid="4f529a14-ace3-407c-9c49-066e8e8027f0"
+    wsmap="struct"
+    >
+    <desc>
+      The IHostDrivePartition interface represents the partition of the host drive.
+      To enumerate all available drives partitions in the host, use the
+      <link to="IHost::hostDrives"/> attribute.
+    </desc>
+
+    <attribute name="number" type="unsigned long" readonly="yes">
+      <desc>
+        The number of the partition.
+        <!-- @todo r=bird: This is weird numbering scheme for MBR disk as it goes 1,2,3,4,5,7,9,11,...
+             And has no practical use.  It would be better to use the system specific device node numbering here. -->
+      </desc>
+    </attribute>
+
+    <attribute name="size" type="long long" readonly="yes">
+      <desc>
+        The partition size in bytes.
+      </desc>
+    </attribute>
+
+    <attribute name="start" type="long long" readonly="yes">
+      <desc>
+        The start byte offset of this partition in bytes relative to the
+        beginning of the hard disk.
+      </desc>
+    </attribute>
+
+    <attribute name="type" type="PartitionType" readonly="yes">
+      <desc>
+        A translation of <link to="IHostDrivePartition::typeMBR"/> and
+        <link to="IHostDrivePartition::typeUuid"/> when possible, otherwise
+        set to <link to="PartitionType::Unknown" />.
+      </desc>
+    </attribute>
+
+    <attribute name="active" type="boolean" readonly="yes">
+      <desc>
+        The partition is bootable when TRUE.
+      </desc>
+    </attribute>
+
+    <!-- MBR: -->
+
+    <attribute name="typeMBR" type="unsigned long" readonly="yes">
+      <desc>
+        The raw MBR partition type, 0 for non-MBR disks.
+      </desc>
+    </attribute>
+
+    <attribute name="startCylinder" type="unsigned long" readonly="yes">
+      <desc>
+        The cylinder (0..1023) of the first sector in the partition on an MBR disk, zero for not an MBR disk.
+      </desc>
+    </attribute>
+
+    <attribute name="startHead" type="unsigned long" readonly="yes">
+      <desc>
+        The head (0..255) of the first sector in the partition on an MBR disk, zero for not an MBR disk.
+      </desc>
+    </attribute>
+
+    <attribute name="startSector" type="unsigned long" readonly="yes">
+      <desc>
+        The sector (0..63) of the first sector in the partition on an MBR disk, zero for not an MBR disk.
+      </desc>
+    </attribute>
+
+    <attribute name="endCylinder" type="unsigned long" readonly="yes">
+      <desc>
+        The cylinder (0..1023) of the last sector (inclusive) in the partition on an MBR disk, zero for not an MBR disk.
+      </desc>
+    </attribute>
+
+    <attribute name="endHead" type="unsigned long" readonly="yes">
+      <desc>
+        The head (0..255) of the last sector (inclusive) in the partition on an MBR disk, zero for not an MBR disk.
+      </desc>
+    </attribute>
+
+    <attribute name="endSector" type="unsigned long" readonly="yes">
+      <desc>
+        The sector (1..63) of the last sector (inclusive) in the partition on an MBR disk, zero for not an MBR disk.
+      </desc>
+    </attribute>
+
+    <!-- GPT: -->
+    <attribute name="typeUuid" type="uuid" mod="string" readonly="yes">
+    <!-- @todo r=bird: Not sure about the casing here. typeUUID Klaus? -->
+      <desc>
+        The partition type when GUID partitioning scheme is used, NULL UUID value for not a GPT disks.
+      </desc>
+    </attribute>
+
+    <attribute name="uuid" type="uuid" mod="string" readonly="yes">
+      <desc>
+        The GUID of the partition when GUID partitioning scheme is used, NULL UUID value for not a GPT disks.
+      </desc>
+    </attribute>
+
+    <attribute name="name" type="wstring" readonly="yes">
+      <desc>
+        The name of the partition if GPT partitioning is used, empty if not a GPT disk.
+      </desc>
+    </attribute>
+
+  </interface>
+
+  <interface
+    name="IHostDrive" extends="$unknown"
+    uuid="70e2e0c3-332c-4d72-b822-2db16e2cb31b"
+    wsmap="managed"
+    >
+    <desc>
+      The IHostDrive interface represents the drive of the physical machine.
+      It is not complete medium description and, therefore, it is not IMedium
+      based. The interface is used just for getting a host drive partitions info.
+    </desc>
+
+    <attribute name="drivePath" type="wstring" readonly="yes" wrap-hint-server="limitedcaller">
+      <desc>
+        The path of the drive. Platform dependent.
+      </desc>
+    </attribute>
+
+    <attribute name="partitioningType" type="PartitioningType" readonly="yes">
+     <desc>
+       The scheme of the partitions the disk has.
+     </desc>
+    </attribute>
+
+    <attribute name="uuid" type="uuid" mod="string" readonly="yes">
+      <desc>
+        The GUID of the disk.
+      </desc>
+    </attribute>
+
+    <attribute name="sectorSize" type="unsigned long" readonly="yes">
+     <desc>
+       The size of the sector in bytes.
+     </desc>
+    </attribute>
+
+    <attribute name="size" type="long long" readonly="yes">
+     <desc>
+       The size of the disk in bytes.
+     </desc>
+    </attribute>
+
+    <attribute name="model" type="wstring" readonly="yes">
+     <desc>
+       The model string of the drive if available.
+     </desc>
+    </attribute>
+
+    <attribute name="partitions" type="IHostDrivePartition" readonly="yes" safearray="yes">
+      <desc>List of partitions available on the host drive.</desc>
+    </attribute>
+
+  </interface>
+
+  <interface
     name="IHost" extends="$unknown"
-    uuid="16ced992-5fdc-4aba-aff5-6a39bbd7c38b"
+    uuid="fc0759a6-a5e2-41e1-93ca-64776335eb2d"
     wsmap="managed"
     reservedMethods="6" reservedAttributes="12"
@@ -10391,4 +10754,8 @@
     <attribute name="processorOnlineCoreCount" type="unsigned long" readonly="yes">
       <desc>Number of physical processor cores online in the host system.</desc>
+    </attribute>
+
+    <attribute name="hostDrives" type="IHostDrive" readonly="yes" safearray="yes">
+      <desc>List of the host drive available to use in the VirtualBox.</desc>
     </attribute>
 
Index: /trunk/src/VBox/Main/include/AutoCaller.h
===================================================================
--- /trunk/src/VBox/Main/include/AutoCaller.h	(revision 85928)
+++ /trunk/src/VBox/Main/include/AutoCaller.h	(revision 85929)
@@ -333,4 +333,17 @@
 
     /**
+     * Sets the initialization status to Succeeded to indicate limited
+     * (partly successful) initialization but also adds the initialization
+     * error if required for further reporting. The AutoInitSpan destructor
+     * will place the managed VirtualBoxBase object to the Limited state.
+     */
+    void setLimited(HRESULT rc)
+    {
+        mResult = Limited;
+        mFailedRC = rc;
+        mpFailedEI = new ErrorInfo();
+    }
+
+    /**
      * Sets the initialization status to Failure to indicates failed
      * initialization. The AutoInitSpan destructor will place the managed
Index: /trunk/src/VBox/Main/include/HostHardwareLinux.h
===================================================================
--- /trunk/src/VBox/Main/include/HostHardwareLinux.h	(revision 85928)
+++ /trunk/src/VBox/Main/include/HostHardwareLinux.h	(revision 85929)
@@ -69,5 +69,5 @@
      * @returns iprt status code
      */
-    int updateFloppies();
+    int updateFloppies() RT_NOEXCEPT;
 
     /**
@@ -76,5 +76,12 @@
      * @returns iprt status code
      */
-    int updateDVDs();
+    int updateDVDs() RT_NOEXCEPT;
+
+    /**
+     * Search for fixed disks (HDDs) and rebuild the list, which remains empty until
+     * the first time this method is called.
+     * @returns iprt status code
+     */
+    int updateFixedDrives() RT_NOEXCEPT;
 
     /** Get the first element in the list of floppy drives. */
@@ -101,4 +108,16 @@
         return mDVDList.end();
     }
+
+    /** Get the first element in the list of fixed drives. */
+    DriveInfoList::const_iterator FixedDriveBegin()
+    {
+        return mFixedDriveList.begin();
+    }
+
+    /** Get the last element in the list of fixed drives. */
+    DriveInfoList::const_iterator FixedDriveEnd()
+    {
+        return mFixedDriveList.end();
+    }
 private:
     /** The list of currently available floppy drives */
@@ -106,4 +125,6 @@
     /** The list of currently available DVD drives */
     DriveInfoList mDVDList;
+    /** The list of currently available fixed drives */
+    DriveInfoList mFixedDriveList;
 };
 
Index: /trunk/src/VBox/Main/include/HostImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/HostImpl.h	(revision 85928)
+++ /trunk/src/VBox/Main/include/HostImpl.h	(revision 85929)
@@ -29,4 +29,6 @@
 class Progress;
 class PerformanceCollector;
+class HostDrive;
+class HostDrivePartition;
 
 namespace settings
@@ -109,4 +111,5 @@
     HRESULT getProcessorCoreCount(ULONG *aProcessorCoreCount);
     HRESULT getProcessorOnlineCoreCount(ULONG *aProcessorOnlineCoreCount);
+    HRESULT getHostDrives(std::vector<ComPtr<IHostDrive> > &aHostDrives);
     HRESULT getMemorySize(ULONG *aMemorySize);
     HRESULT getMemoryAvailable(ULONG *aMemoryAvailable);
@@ -177,4 +180,5 @@
     bool i_getDVDInfoFromHal(std::list< ComObjPtr<Medium> > &list);
     bool i_getFloppyInfoFromHal(std::list< ComObjPtr<Medium> > &list);
+    HRESULT i_getFixedDrivesFromHal(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &list) RT_NOEXCEPT;
 #endif
 
@@ -183,4 +187,5 @@
     void i_parseMountTable(char *mountTable, std::list< ComObjPtr<Medium> > &list);
     bool i_validateDevice(const char *deviceNode, bool isCDROM);
+    HRESULT i_getFixedDrivesFromDevTree(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &list) RT_NOEXCEPT;
 #endif
 
@@ -199,4 +204,6 @@
 #endif /* VBOX_WITH_RESOURCE_USAGE_API */
 
+    HRESULT i_getDrivesPathsList(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &aDriveList) RT_NOEXCEPT;
+
     struct Data;        // opaque data structure, defined in HostImpl.cpp
     Data *m;
Index: /trunk/src/VBox/Main/src-all/AutoCaller.cpp
===================================================================
--- /trunk/src/VBox/Main/src-all/AutoCaller.cpp	(revision 85928)
+++ /trunk/src/VBox/Main/src-all/AutoCaller.cpp	(revision 85929)
@@ -294,10 +294,11 @@
     }
 
-    if (aNewState == InitFailed)
+    if (aNewState == InitFailed || aNewState == Limited)
     {
         mFailedRC = aFailedRC;
-        /* apFailedEI may be NULL, when there is no explicit setFailed() call,
-         * which also implies that aFailedRC is S_OK. This case is used by
-         * objects (the majority) which don't want delayed error signalling. */
+        /* apFailedEI may be NULL, when there is no explicit setFailed() or
+         * setLimited() call, which also implies that aFailedRC is S_OK.
+         * This case is used by objects (the majority) which don't want
+         * delayed error signalling. */
         mpFailedEI = apFailedEI;
     }
Index: /trunk/src/VBox/Main/src-all/GlobalStatusConversion.cpp
===================================================================
--- /trunk/src/VBox/Main/src-all/GlobalStatusConversion.cpp	(revision 85928)
+++ /trunk/src/VBox/Main/src-all/GlobalStatusConversion.cpp	(revision 85929)
@@ -108,4 +108,5 @@
         case VERR_NOT_EQUAL:                    return VBOX_E_FILE_ERROR;
         case VERR_FILE_NOT_FOUND:               return VBOX_E_OBJECT_NOT_FOUND;
+        case VERR_IO_NOT_READY:                 return VBOX_E_INVALID_OBJECT_STATE;
 
         /* Guest Control errors. */
Index: /trunk/src/VBox/Main/src-server/HostDriveImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/HostDriveImpl.cpp	(revision 85929)
+++ /trunk/src/VBox/Main/src-server/HostDriveImpl.cpp	(revision 85929)
@@ -0,0 +1,271 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Main - IHostDrive implementation, VBoxSVC.
+ */
+
+/*
+ * Copyright (C) 2013-2019 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.
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_HOSTDRIVE
+#include "Global.h"
+#include "HostDriveImpl.h"
+#include "HostDrivePartitionImpl.h"
+#include "LoggingNew.h"
+#include "VirtualBoxImpl.h"
+
+#include <iprt/dvm.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#include <iprt/vfs.h>
+#include <VBox/com/Guid.h>
+
+
+/*
+ * HostDrive implementation.
+ */
+DEFINE_EMPTY_CTOR_DTOR(HostDrive)
+
+HRESULT HostDrive::FinalConstruct()
+{
+    return BaseFinalConstruct();
+}
+
+void HostDrive::FinalRelease()
+{
+    uninit();
+
+    BaseFinalRelease();
+}
+
+/**
+ * Initializes the instance.
+ */
+HRESULT HostDrive::initFromPathAndModel(const com::Utf8Str &drivePath, const com::Utf8Str &driveModel)
+{
+    LogFlowThisFunc(("\n"));
+
+    AssertReturn(!drivePath.isEmpty(), E_INVALIDARG);
+
+    /* Enclose the state transition NotReady->InInit->Ready */
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    m.partitioningType = PartitioningType_MBR;
+    m.drivePath = drivePath;
+    m.model = driveModel;
+    m.partitions.clear();
+
+    const char *pszDrivePath = drivePath.c_str();
+
+#ifdef RT_OS_DARWIN
+    /*
+     * Ensure the path specified for the drive in raw mode, i.e. drive name begins with 'r'
+     * Otherwise, the RTFileOpen will always return the error about 'busy resource'
+     */
+    /** @todo r=bird: Shouldn't we make iokit.cpp just return the /dev/rdiskX*
+     *        paths instead then? */
+    com::Utf8Str strAdjustedDrivePath;
+    const char *pszDriveName = RTPathFilename(pszDrivePath);
+    if (pszDriveName && *pszDriveName != 'r')
+    {
+        strAdjustedDrivePath = drivePath.substr(0, (size_t)(pszDriveName - pszDrivePath));
+        strAdjustedDrivePath.append('r');
+        strAdjustedDrivePath.append(pszDriveName);
+        pszDrivePath = strAdjustedDrivePath.c_str();
+    }
+#endif
+
+    /*
+     * Try open the drive so we can extract futher details,
+     * like the size, sector size and partitions.
+     */
+    HRESULT hrc = E_FAIL;
+    RTFILE hRawFile = NIL_RTFILE;
+    int vrc = RTFileOpen(&hRawFile, pszDrivePath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+    if (RT_SUCCESS(vrc))
+    {
+        vrc = RTFileQuerySize(hRawFile, &m.cbDisk);
+        int vrc2 = RTFileQuerySectorSize(hRawFile, &m.cbSector);
+        if (RT_FAILURE(vrc2))
+            vrc = vrc2;
+        if (RT_SUCCESS(vrc))
+        {
+            /*
+             * Hand it to DVM.
+             */
+            RTVFSFILE hVfsFile = NIL_RTVFSFILE;
+            vrc = RTVfsFileFromRTFile(hRawFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE /*fOpen*/,
+                                      true /*fLeaveOpen*/, &hVfsFile);
+            if (RT_SUCCESS(vrc))
+            {
+                RTDVM hVolMgr = NIL_RTDVM;
+                vrc = RTDvmCreate(&hVolMgr, hVfsFile, m.cbSector, 0 /*fFlags*/);
+                if (RT_SUCCESS(vrc))
+                {
+                    vrc = RTDvmMapOpen(hVolMgr);
+                    if (RT_SUCCESS(vrc))
+                    {
+                        hrc = S_OK;
+
+                        /*
+                         * Get details.
+                         */
+                        switch (RTDvmMapGetFormatType(hVolMgr))
+                        {
+                            case RTDVMFORMATTYPE_GPT:
+                                m.partitioningType = PartitioningType_GPT;
+                                break;
+                            case RTDVMFORMATTYPE_MBR:
+                                m.partitioningType = PartitioningType_MBR;
+                                break;
+                            case RTDVMFORMATTYPE_BSD_LABEL:
+                                AssertMsgFailed(("TODO\n"));
+                                break;
+                            default:
+                                AssertFailed();
+                        }
+
+                        RTUUID Uuid = RTUUID_INITIALIZE_NULL;
+                        if (RT_SUCCESS(RTDvmMapQueryDiskUuid(hVolMgr, &Uuid)))
+                            m.uuid = Uuid;
+
+                        /*
+                         * Enumerate volumes and tuck them into the partitions list..
+                         */
+                        uint32_t const  cVolumes = RTDvmMapGetValidVolumes(hVolMgr);
+                        RTDVMVOLUME     hVol     = NIL_RTDVMVOLUME;
+                        for (uint32_t i = 0; i < cVolumes; i++)
+                        {
+                            /* Enumeration cruft: */
+                            RTDVMVOLUME hVolNext = NIL_RTDVMVOLUME;
+                            if (i == 0)
+                                vrc = RTDvmMapQueryFirstVolume(hVolMgr, &hVolNext);
+                            else
+                                vrc = RTDvmMapQueryNextVolume(hVolMgr, hVol, &hVolNext);
+                            AssertRCBreakStmt(vrc, hrc = Global::vboxStatusCodeToCOM(vrc));
+
+                            uint32_t cRefs = RTDvmVolumeRelease(hVol);
+                            Assert(cRefs != UINT32_MAX); RT_NOREF(cRefs);
+                            hVol = hVolNext;
+
+                            /* Instantiate a new partition object and add it to the list: */
+                            ComObjPtr<HostDrivePartition> ptrHostPartition;
+                            hrc = ptrHostPartition.createObject();
+                            if (SUCCEEDED(hrc))
+                                hrc = ptrHostPartition->initFromDvmVol(hVol);
+                            if (SUCCEEDED(hrc))
+                                try
+                                {
+                                    m.partitions.push_back(ptrHostPartition);
+                                }
+                                catch (std::bad_alloc &)
+                                {
+                                    AssertFailedBreakStmt(hrc = E_OUTOFMEMORY);
+                                }
+                        }
+                        RTDvmVolumeRelease(hVol);
+                    }
+                    else
+                        hrc = Global::vboxStatusCodeToCOM(vrc);
+                    RTDvmRelease(hVolMgr);
+                }
+                else
+                    hrc = Global::vboxStatusCodeToCOM(vrc);
+                RTVfsFileRelease(hVfsFile);
+            }
+            else
+                hrc = Global::vboxStatusCodeToCOM(vrc);
+        }
+        else /* VERR_IO_NOT_READ / STATUS_NO_MEDIA_IN_DEVICE is likely for card readers on windows. */
+            hrc = Global::vboxStatusCodeToCOM(vrc);
+        RTFileClose(hRawFile);
+    }
+    else
+        hrc = Global::vboxStatusCodeToCOM(vrc);
+
+    /* Confirm a successful initialization */
+    if (SUCCEEDED(hrc))
+        autoInitSpan.setSucceeded();
+    else
+        autoInitSpan.setLimited(hrc);
+    return S_OK;
+}
+
+/**
+ * Uninitializes the instance.
+ * Called either from FinalRelease() or by the parent when it gets destroyed.
+ */
+void HostDrive::uninit()
+{
+    LogFlowThisFunc(("\n"));
+
+    /* Enclose the state transition Ready->InUninit->NotReady */
+    AutoUninitSpan autoUninitSpan(this);
+    if (autoUninitSpan.uninitDone())
+        return;
+
+    m.drivePath.setNull();
+    m.partitions.clear();
+}
+
+
+/*********************************************************************************************************************************
+*   IHostDrive properties                                                                                                        *
+*********************************************************************************************************************************/
+
+HRESULT HostDrive::getPartitioningType(PartitioningType_T *aPartitioningType)
+{
+    *aPartitioningType = m.partitioningType;
+    return S_OK;
+}
+
+HRESULT HostDrive::getDrivePath(com::Utf8Str &aDrivePath)
+{
+    aDrivePath = m.drivePath;
+    return S_OK;
+}
+
+HRESULT HostDrive::getUuid(com::Guid &aUuid)
+{
+    aUuid = m.uuid;
+    return S_OK;
+}
+
+HRESULT HostDrive::getSectorSize(ULONG *aSectorSize)
+{
+    *aSectorSize = m.cbSector;
+    return S_OK;
+}
+
+HRESULT HostDrive::getSize(LONG64 *aSize)
+{
+    *aSize = (LONG64)m.cbDisk;
+    if (*aSize < 0)
+        *aSize = INT64_MAX;
+    return S_OK;
+}
+
+HRESULT HostDrive::getModel(com::Utf8Str &aModel)
+{
+    return aModel.assignEx(m.model);
+}
+
+HRESULT HostDrive::getPartitions(std::vector<ComPtr<IHostDrivePartition> > &aPartitions)
+{
+    aPartitions = m.partitions;
+    return S_OK;
+}
+
+
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Index: /trunk/src/VBox/Main/src-server/HostDrivePartitionImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/HostDrivePartitionImpl.cpp	(revision 85929)
+++ /trunk/src/VBox/Main/src-server/HostDrivePartitionImpl.cpp	(revision 85929)
@@ -0,0 +1,371 @@
+/* $Id$ */
+/** @file
+ * VirtualBox Main - IHostDrivePartition implementation, VBoxSVC.
+ */
+
+/*
+ * Copyright (C) 2013-2019 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.
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_HOSTDRIVEPARTITION
+#include "HostDrivePartitionImpl.h"
+#include "LoggingNew.h"
+
+#include <iprt/err.h>
+
+/*
+ * HostDrivePartition implementation.
+ */
+DEFINE_EMPTY_CTOR_DTOR(HostDrivePartition)
+
+HRESULT HostDrivePartition::FinalConstruct()
+{
+    return BaseFinalConstruct();
+}
+
+void HostDrivePartition::FinalRelease()
+{
+    uninit();
+
+    BaseFinalRelease();
+}
+
+/*
+ * Initializes the instance.
+ */
+HRESULT HostDrivePartition::initFromDvmVol(RTDVMVOLUME hVol)
+{
+    LogFlowThisFunc(("\n"));
+
+    AssertReturn(hVol != NIL_RTDVMVOLUME, E_INVALIDARG);
+
+    /* Enclose the state transition NotReady->InInit->Ready */
+    AutoInitSpan autoInitSpan(this);
+    AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+    /* Common attributes: */
+    m.number = RTDvmVolumeGetIndex(hVol, RTDVMVOLIDX_ALL); /** @todo r=bird: who can use this one? The host specific index would be better. */
+    m.cbVol  = (LONG64)RTDvmVolumeGetSize(hVol);
+    if (m.cbVol < 0)
+        m.cbVol = INT64_MAX;
+
+    uint64_t offStart = 0;
+    uint64_t offLastIgnored = 0;
+    int vrc = RTDvmVolumeQueryRange(hVol, &offStart, &offLastIgnored);
+    AssertRC(vrc);
+    m.offStart = RT_SUCCESS(vrc) ? (LONG64)offStart : 0;
+    Assert((uint64_t)m.cbVol == offLastIgnored - offStart + 1 || RT_FAILURE(vrc));
+    if (m.offStart < 0)
+        m.offStart = INT64_MAX;
+
+    uint64_t fFlags = RTDvmVolumeGetFlags(hVol);
+    m.active = (fFlags & (DVMVOLUME_FLAGS_BOOTABLE | DVMVOLUME_FLAGS_ACTIVE)) != 0;
+
+    /* MBR: */
+    m.firstCylinder = (uint16_t)RTDvmVolumeGetPropU64(hVol, RTDVMVOLPROP_MBR_FIRST_CYLINDER, 0);
+    m.firstHead     = (uint8_t )RTDvmVolumeGetPropU64(hVol, RTDVMVOLPROP_MBR_FIRST_HEAD, 0);
+    m.firstSector   = (uint8_t )RTDvmVolumeGetPropU64(hVol, RTDVMVOLPROP_MBR_FIRST_SECTOR, 0);
+    m.lastCylinder  = (uint16_t)RTDvmVolumeGetPropU64(hVol, RTDVMVOLPROP_MBR_LAST_CYLINDER, 0);
+    m.lastHead      = (uint8_t )RTDvmVolumeGetPropU64(hVol, RTDVMVOLPROP_MBR_LAST_HEAD, 0);
+    m.lastSector    = (uint8_t )RTDvmVolumeGetPropU64(hVol, RTDVMVOLPROP_MBR_LAST_SECTOR, 0);
+    m.bMBRType      = (uint8_t )RTDvmVolumeGetPropU64(hVol, RTDVMVOLPROP_MBR_TYPE, 0);
+
+    /* GPT: */
+    RTUUID Uuid;
+    vrc = RTDvmVolumeQueryProp(hVol, RTDVMVOLPROP_GPT_TYPE, &Uuid, sizeof(Uuid), NULL);
+    if (RT_SUCCESS(vrc))
+        m.typeUuid = Uuid;
+    vrc = RTDvmVolumeQueryProp(hVol, RTDVMVOLPROP_GPT_UUID, &Uuid, sizeof(Uuid), NULL);
+    if (RT_SUCCESS(vrc))
+        m.uuid = Uuid;
+
+    char *pszName = NULL;
+    vrc = RTDvmVolumeQueryName(hVol, &pszName);
+    if (RT_SUCCESS(vrc))
+    {
+        HRESULT hrc = m.name.assignEx(pszName);
+        RTStrFree(pszName);
+        AssertComRCReturn(hrc, hrc);
+    }
+
+    /*
+     * Do the type translation to the best of our ability.
+     */
+    m.enmType = PartitionType_Unknown;
+    if (m.typeUuid.isZero())
+        switch ((PartitionType_T)m.bMBRType)
+        {
+            case PartitionType_FAT12:
+            case PartitionType_FAT16:
+            case PartitionType_FAT:
+            case PartitionType_IFS:
+            case PartitionType_FAT32CHS:
+            case PartitionType_FAT32LBA:
+            case PartitionType_FAT16B:
+            case PartitionType_Extended:
+            case PartitionType_WindowsRE:
+            case PartitionType_LinuxSwapOld:
+            case PartitionType_LinuxOld:
+            case PartitionType_DragonFlyBSDSlice:
+            case PartitionType_LinuxSwap:
+            case PartitionType_Linux:
+            case PartitionType_LinuxExtended:
+            case PartitionType_LinuxLVM:
+            case PartitionType_BSDSlice:
+            case PartitionType_AppleUFS:
+            case PartitionType_AppleHFS:
+            case PartitionType_Solaris:
+            case PartitionType_GPT:
+            case PartitionType_EFI:
+                m.enmType = (PartitionType_T)m.bMBRType;
+                break;
+
+            case PartitionType_Empty:
+            default:
+                break;
+        }
+    else
+    {
+        static struct { const char *pszUuid; PartitionType_T enmType; } const s_aUuidToType[] =
+        {
+            { "024dee41-33e7-11d3-9d69-0008c781f39f", PartitionType_MBR },
+            { "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", PartitionType_EFI },
+            { "d3bfe2de-3daf-11df-ba40-e3a556d89593", PartitionType_iFFS },
+            { "f4019732-066e-4e12-8273-346c5641494f", PartitionType_SonyBoot },
+            { "bfbfafe7-a34f-448a-9a5b-6213eb736c22", PartitionType_LenovoBoot },
+            /* Win: */
+            { "e3c9e316-0b5c-4db8-817d-f92df00215ae", PartitionType_WindowsMSR },
+            { "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7", PartitionType_WindowsBasicData },
+            { "5808c8aa-7e8f-42e0-85d2-e1e90434cfb3", PartitionType_WindowsLDMMeta },
+            { "af9b60a0-1431-4f62-bc68-3311714a69ad", PartitionType_WindowsLDMData },
+            { "de94bba4-06d1-4d40-a16a-bfd50179d6ac", PartitionType_WindowsRecovery },
+            { "e75caf8f-f680-4cee-afa3-b001e56efc2d", PartitionType_WindowsStorageSpaces },
+            { "558d43c5-a1ac-43c0-aac8-d1472b2923d1", PartitionType_WindowsStorageReplica },
+            { "37affc90-ef7d-4e96-91c3-2d7ae055b174", PartitionType_IBMGPFS },
+            /* Linux: */
+            { "0fc63daf-8483-4772-8e79-3d69d8477de4", PartitionType_LinuxData },
+            { "a19d880f-05fc-4d3b-a006-743f0f84911e", PartitionType_LinuxRAID },
+            { "44479540-f297-41b2-9af7-d131d5f0458a", PartitionType_LinuxRootX86 },
+            { "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", PartitionType_LinuxRootAMD64 },
+            { "69dad710-2ce4-4e3c-b16c-21a1d49abed3", PartitionType_LinuxRootARM32 },
+            { "b921b045-1df0-41c3-af44-4c6f280d3fae", PartitionType_LinuxRootARM64 },
+            { "933ac7e1-2eb4-4f13-b844-0e14e2aef915", PartitionType_LinuxHome },
+            { "3b8f8425-20e0-4f3b-907f-1a25a76f98e8", PartitionType_LinuxSrv },
+            { "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f", PartitionType_LinuxSwap },
+            { "e6d6d379-f507-44c2-a23c-238f2a3df928", PartitionType_LinuxLVM },
+            { "7ffec5c9-2d00-49b7-8941-3ea10a5586b7", PartitionType_LinuxPlainDmCrypt },
+            { "ca7d7ccb-63ed-4c53-861c-1742536059cc", PartitionType_LinuxLUKS },
+            { "8da63339-0007-60c0-c436-083ac8230908", PartitionType_LinuxReserved },
+            /* FreeBSD: */
+            { "83bd6b9d-7f41-11dc-be0b-001560b84f0f", PartitionType_FreeBSDBoot },
+            { "516e7cb4-6ecf-11d6-8ff8-00022d09712b", PartitionType_FreeBSDData },
+            { "516e7cb5-6ecf-11d6-8ff8-00022d09712b", PartitionType_FreeBSDSwap },
+            { "516e7cb6-6ecf-11d6-8ff8-00022d09712b", PartitionType_FreeBSDUFS },
+            { "516e7cb8-6ecf-11d6-8ff8-00022d09712b", PartitionType_FreeBSDVinum },
+            { "516e7cba-6ecf-11d6-8ff8-00022d09712b", PartitionType_FreeBSDZFS },
+            /* Apple/macOS: */
+            { "48465300-0000-11aa-aa11-00306543ecac", PartitionType_AppleHFSPlus },
+            { "7c3457ef-0000-11aa-aa11-00306543ecac", PartitionType_AppleAPFS },
+            { "55465300-0000-11aa-aa11-00306543ecac", PartitionType_AppleUFS },
+            { "52414944-0000-11aa-aa11-00306543ecac", PartitionType_AppleRAID },
+            { "52414944-5f4f-11aa-aa11-00306543ecac", PartitionType_AppleRAIDOffline },
+            { "426f6f74-0000-11aa-aa11-00306543ecac", PartitionType_AppleBoot },
+            { "4c616265-6c00-11aa-aa11-00306543ecac", PartitionType_AppleLabel },
+            { "5265636f-7665-11aa-aa11-00306543ecac", PartitionType_AppleTvRecovery },
+            { "53746f72-6167-11aa-aa11-00306543ecac", PartitionType_AppleCoreStorage },
+            { "b6fa30da-92d2-4a9a-96f1-871ec6486200", PartitionType_SoftRAIDStatus },
+            { "2e313465-19b9-463f-8126-8a7993773801", PartitionType_SoftRAIDScratch },
+            { "fa709c7e-65b1-4593-bfd5-e71d61de9b02", PartitionType_SoftRAIDVolume },
+            { "bbba6df5-f46f-4a89-8f59-8765b2727503", PartitionType_SoftRAIDCache },
+            /* Solaris */
+            { "6a82cb45-1dd2-11b2-99a6-080020736631", PartitionType_SolarisBoot },
+            { "6a85cf4d-1dd2-11b2-99a6-080020736631", PartitionType_SolarisRoot },
+            { "6a87c46f-1dd2-11b2-99a6-080020736631", PartitionType_SolarisSwap },
+            { "6a8b642b-1dd2-11b2-99a6-080020736631", PartitionType_SolarisBackup },
+            { "6a898cc3-1dd2-11b2-99a6-080020736631", PartitionType_SolarisUsr },
+            { "6a8ef2e9-1dd2-11b2-99a6-080020736631", PartitionType_SolarisVar },
+            { "6a90ba39-1dd2-11b2-99a6-080020736631", PartitionType_SolarisHome },
+            { "6a9283a5-1dd2-11b2-99a6-080020736631", PartitionType_SolarisAltSector },
+            { "6a945a3b-1dd2-11b2-99a6-080020736631", PartitionType_SolarisReserved },
+            { "6a9630d1-1dd2-11b2-99a6-080020736631", PartitionType_SolarisReserved },
+            { "6a980767-1dd2-11b2-99a6-080020736631", PartitionType_SolarisReserved },
+            { "6a96237f-1dd2-11b2-99a6-080020736631", PartitionType_SolarisReserved },
+            { "6a8d2ac7-1dd2-11b2-99a6-080020736631", PartitionType_SolarisReserved },
+            /* NetBSD: */
+            { "49f48d32-b10e-11dc-b99b-0019d1879648", PartitionType_NetBSDSwap },
+            { "49f48d5a-b10e-11dc-b99b-0019d1879648", PartitionType_NetBSDFFS },
+            { "49f48d82-b10e-11dc-b99b-0019d1879648", PartitionType_NetBSDLFS },
+            { "49f48daa-b10e-11dc-b99b-0019d1879648", PartitionType_NetBSDRAID },
+            { "2db519c4-b10f-11dc-b99b-0019d1879648", PartitionType_NetBSDConcatenated },
+            { "2db519ec-b10f-11dc-b99b-0019d1879648", PartitionType_NetBSDEncrypted },
+            /* Chrome OS: */
+            { "fe3a2a5d-4f32-41a7-b725-accc3285a309", PartitionType_ChromeOSKernel },
+            { "3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec", PartitionType_ChromeOSRootFS },
+            { "2e0a753d-9e48-43b0-8337-b15192cb1b5e", PartitionType_ChromeOSFuture },
+            /* Container Linux: */
+            { "5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6", PartitionType_ContLnxUsr },
+            { "3884dd41-8582-4404-b9a8-e9b84f2df50e", PartitionType_ContLnxRoot },
+            { "c95dc21a-df0e-4340-8d7b-26cbfa9a03e0", PartitionType_ContLnxReserved },
+            { "be9067b9-ea49-4f15-b4f6-f36f8c9e1818", PartitionType_ContLnxRootRAID },
+            /* Haiku: */
+            { "42465331-3ba3-10f1-802a-4861696b7521", PartitionType_HaikuBFS },
+            /* MidnightBSD */
+            { "85d5e45e-237c-11e1-b4b3-e89a8f7fc3a7", PartitionType_MidntBSDBoot },
+            { "85d5e45a-237c-11e1-b4b3-e89a8f7fc3a7", PartitionType_MidntBSDData },
+            { "85d5e45b-237c-11e1-b4b3-e89a8f7fc3a7", PartitionType_MidntBSDSwap },
+            { "0394ef8b-237e-11e1-b4b3-e89a8f7fc3a7", PartitionType_MidntBSDUFS },
+            { "85d5e45c-237c-11e1-b4b3-e89a8f7fc3a7", PartitionType_MidntBSDVium },
+            { "85d5e45d-237c-11e1-b4b3-e89a8f7fc3a7", PartitionType_MidntBSDZFS },
+            /* OpenBSD: */
+            { "824cc7a0-36a8-11e3-890a-952519ad3f61", PartitionType_OpenBSDData },
+            /* QNX: */
+            { "cef5a9ad-73bc-4601-89f3-cdeeeee321a1", PartitionType_QNXPowerSafeFS },
+            /* Plan 9: */
+            { "c91818f9-8025-47af-89d2-f030d7000c2c", PartitionType_Plan9 },
+            /* VMWare ESX: */
+            { "9d275380-40ad-11db-bf97-000c2911d1b8", PartitionType_VMWareVMKCore },
+            { "aa31e02a-400f-11db-9590-000c2911d1b8", PartitionType_VMWareVMFS },
+            { "9198effc-31c0-11db-8f78-000c2911d1b8", PartitionType_VMWareReserved },
+            /* Android-x86: */
+            { "2568845d-2332-4675-bc39-8fa5a4748d15", PartitionType_AndroidX86Bootloader },
+            { "114eaffe-1552-4022-b26e-9b053604cf84", PartitionType_AndroidX86Bootloader2 },
+            { "49a4d17f-93a3-45c1-a0de-f50b2ebe2599", PartitionType_AndroidX86Boot },
+            { "4177c722-9e92-4aab-8644-43502bfd5506", PartitionType_AndroidX86Recovery },
+            { "ef32a33b-a409-486c-9141-9ffb711f6266", PartitionType_AndroidX86Misc },
+            { "20ac26be-20b7-11e3-84c5-6cfdb94711e9", PartitionType_AndroidX86Metadata },
+            { "38f428e6-d326-425d-9140-6e0ea133647c", PartitionType_AndroidX86System },
+            { "a893ef21-e428-470a-9e55-0668fd91a2d9", PartitionType_AndroidX86Cache },
+            { "dc76dda9-5ac1-491c-af42-a82591580c0d", PartitionType_AndroidX86Data },
+            { "ebc597d0-2053-4b15-8b64-e0aac75f4db1", PartitionType_AndroidX86Persistent },
+            { "c5a0aeec-13ea-11e5-a1b1-001e67ca0c3c", PartitionType_AndroidX86Vendor },
+            { "bd59408b-4514-490d-bf12-9878d963f378", PartitionType_AndroidX86Config },
+            { "8f68cc74-c5e5-48da-be91-a0c8c15e9c80", PartitionType_AndroidX86Factory },
+            { "9fdaa6ef-4b3f-40d2-ba8d-bff16bfb887b", PartitionType_AndroidX86FactoryAlt },
+            { "767941d0-2085-11e3-ad3b-6cfdb94711e9", PartitionType_AndroidX86Fastboot },
+            { "ac6d7924-eb71-4df8-b48d-e267b27148ff", PartitionType_AndroidX86OEM },
+            /* Android ARM: */
+            { "19a710a2-b3ca-11e4-b026-10604b889dcf", PartitionType_AndroidARMMeta },
+            { "193d1ea4-b3ca-11e4-b075-10604b889dcf", PartitionType_AndroidARMExt },
+            /* Open Network Install Environment: */
+            { "7412f7d5-a156-4b13-81dc-867174929325", PartitionType_ONIEBoot },
+            { "d4e6e2cd-4469-46f3-b5cb-1bff57afc149", PartitionType_ONIEConfig },
+            /* PowerPC: */
+            { "9e1a2d38-c612-4316-aa26-8b49521e5a8b", PartitionType_PowerPCPrep },
+            /* freedesktop.org: */
+            { "bc13c2ff-59e6-4262-a352-b275fd6f7172", PartitionType_XDGShrBootConfig },
+            /* Ceph: */
+            { "cafecafe-9b03-4f30-b4c6-b4b80ceff106", PartitionType_CephBlock },
+            { "30cd0809-c2b2-499c-8879-2d6b78529876", PartitionType_CephBlockDB },
+            { "93b0052d-02d9-4d8a-a43b-33a3ee4dfbc3", PartitionType_CephBlockDBDmc },
+            { "166418da-c469-4022-adf4-b30afd37f176", PartitionType_CephBlockDBDmcLUKS },
+            { "cafecafe-9b03-4f30-b4c6-5ec00ceff106", PartitionType_CephBlockDmc },
+            { "cafecafe-9b03-4f30-b4c6-35865ceff106", PartitionType_CephBlockDmcLUKS },
+            { "5ce17fce-4087-4169-b7ff-056cc58473f9", PartitionType_CephBlockWALog },
+            { "306e8683-4fe2-4330-b7c0-00a917c16966", PartitionType_CephBlockWALogDmc },
+            { "86a32090-3647-40b9-bbbd-38d8c573aa86", PartitionType_CephBlockWALogDmcLUKS },
+            { "89c57f98-2fe5-4dc0-89c1-f3ad0ceff2be", PartitionType_CephDisk },
+            { "89c57f98-2fe5-4dc0-89c1-5ec00ceff2be", PartitionType_CephDiskDmc },
+            { "45b0969e-9b03-4f30-b4c6-b4b80ceff106", PartitionType_CephJournal },
+            { "45b0969e-9b03-4f30-b4c6-5ec00ceff106", PartitionType_CephJournalDmc },
+            { "45b0969e-9b03-4f30-b4c6-35865ceff106", PartitionType_CephJournalDmcLUKS },
+            { "fb3aabf9-d25f-47cc-bf5e-721d1816496b", PartitionType_CephLockbox },
+            { "cafecafe-8ae0-4982-bf9d-5a8d867af560", PartitionType_CephMultipathBlock1 },
+            { "7f4a666a-16f3-47a2-8445-152ef4d03f6c", PartitionType_CephMultipathBlock2 },
+            { "ec6d6385-e346-45dc-be91-da2a7c8b3261", PartitionType_CephMultipathBlockDB },
+            { "01b41e1b-002a-453c-9f17-88793989ff8f", PartitionType_CephMultipathBLockWALog },
+            { "45b0969e-8ae0-4982-bf9d-5a8d867af560", PartitionType_CephMultipathJournal },
+            { "4fbd7e29-8ae0-4982-bf9d-5a8d867af560", PartitionType_CephMultipathOSD },
+            { "4fbd7e29-9d25-41b8-afd0-062c0ceff05d", PartitionType_CephOSD },
+            { "4fbd7e29-9d25-41b8-afd0-5ec00ceff05d", PartitionType_CephOSDDmc },
+            { "4fbd7e29-9d25-41b8-afd0-35865ceff05d", PartitionType_CephOSDDmcLUKS },
+        };
+        for (size_t i = 0; i < RT_ELEMENTS(s_aUuidToType); i++)
+            if (m.typeUuid.equalsString(s_aUuidToType[i].pszUuid))
+            {
+                m.enmType = s_aUuidToType[i].enmType;
+                break;
+            }
+
+        /* Some OSes are using non-random UUIDs and we can at least identify the
+           OS if not the exact type. */
+        if (m.enmType == PartitionType_Unknown)
+        {
+            char szType[RTUUID_STR_LENGTH];
+            m.typeUuid.toString(szType, sizeof(szType));
+            RTStrToLower(szType);
+            if (RTStrSimplePatternMatch(szType, "516e7c??-6ecf-11d6-8ff8-00022d09712b"))
+                m.enmType = PartitionType_FreeBSDUnknown;
+            else if (RTStrSimplePatternMatch(szType, "????????-????-11aa-aa11-00306543ecac"))
+                m.enmType = PartitionType_AppleUnknown;
+            else if (RTStrSimplePatternMatch(szType, "????????-1dd2-11b2-99a6-080020736631"))
+                m.enmType = PartitionType_SolarisUnknown;
+            else if (RTStrSimplePatternMatch(szType, "????????-b1??-11dc-b99b-0019d1879648"))
+                m.enmType = PartitionType_NetBSDUnknown;
+            else if (RTStrSimplePatternMatch(szType, "????????-23??-11e1-b4b3-e89a8f7fc3a7"))
+                m.enmType = PartitionType_MidntBSDUnknown;
+            else if (RTStrSimplePatternMatch(szType, "????????-????-11db-????-000c2911d1b8"))
+                m.enmType = PartitionType_VMWareUnknown;
+        }
+
+#ifdef VBOX_STRICT
+        /* Make sure we've done have any duplicates in the translation table: */
+        static bool s_fCheckedForDuplicates = false;
+        if (!s_fCheckedForDuplicates)
+        {
+            for (size_t i = 0; i < RT_ELEMENTS(s_aUuidToType); i++)
+                for (size_t j = i + 1; j < RT_ELEMENTS(s_aUuidToType); j++)
+                    AssertMsg(RTUuidCompare2Strs(s_aUuidToType[i].pszUuid, s_aUuidToType[j].pszUuid) != 0,
+                              ("%d & %d: %s\n", i, j, s_aUuidToType[i].pszUuid));
+            s_fCheckedForDuplicates = true;
+        }
+#endif
+
+    }
+
+    /* Confirm a successful initialization */
+    autoInitSpan.setSucceeded();
+
+    return S_OK;
+}
+
+/*
+ * Uninitializes the instance.
+ * Called either from FinalRelease() or by the parent when it gets destroyed.
+ */
+void HostDrivePartition::uninit()
+{
+    LogFlowThisFunc(("\n"));
+
+    /* Enclose the state transition Ready->InUninit->NotReady */
+    AutoUninitSpan autoUninitSpan(this);
+    if (autoUninitSpan.uninitDone())
+        return;
+
+    m.number = 0;
+    m.cbVol = 0;
+    m.offStart = 0;
+    m.enmType = PartitionType_Empty;
+    m.active = 0;
+
+    m.bMBRType = 0;
+    m.firstCylinder = 0;
+    m.firstHead = 0;
+    m.firstSector = 0;
+    m.lastCylinder = 0;
+    m.lastHead = 0;
+    m.lastSector = 0;
+
+    m.typeUuid.clear();
+    m.uuid.clear();
+    m.name.setNull();
+}
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Index: /trunk/src/VBox/Main/src-server/HostImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/HostImpl.cpp	(revision 85928)
+++ /trunk/src/VBox/Main/src-server/HostImpl.cpp	(revision 85929)
@@ -113,4 +113,9 @@
 typedef SOLARISDVD *PSOLARISDVD;
 
+/** Solaris fixed drive (SSD, HDD, ++) descriptor list entry as returned by the
+ * solarisWalkDeviceNodeForFixedDrive callback. */
+typedef SOLARISDVD SOLARISFIXEDDISK;
+/** Pointer to a Solaris fixed drive (SSD, HDD, ++) descriptor. */
+typedef SOLARISFIXEDDISK *PSOLARISFIXEDDISK;
 
 
@@ -136,17 +141,21 @@
 
 #include <iprt/asm-amd64-x86.h>
-#include <iprt/string.h>
+#ifdef RT_OS_SOLARIS
+# include <iprt/ctype.h>
+#endif
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS)
+# include <iprt/file.h>
+#endif
 #include <iprt/mp.h>
-#include <iprt/time.h>
-#include <iprt/param.h>
 #include <iprt/env.h>
 #include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
 #include <iprt/system.h>
 #ifndef RT_OS_WINDOWS
 # include <iprt/path.h>
 #endif
-#ifdef RT_OS_SOLARIS
-# include <iprt/ctype.h>
-#endif
+#include <iprt/time.h>
+
 #ifdef VBOX_WITH_HOSTNETIF_API
 # include "netif.h"
@@ -169,4 +178,6 @@
 
 #include "HostDnsService.h"
+#include "HostDriveImpl.h"
+#include "HostDrivePartitionImpl.h"
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1971,4 +1982,27 @@
 
 
+HRESULT  Host::getHostDrives(std::vector<ComPtr<IHostDrive> > &aHostDrives)
+{
+    std::list<std::pair<com::Utf8Str, com::Utf8Str> > llDrivesPathsList;
+    HRESULT hrc = i_getDrivesPathsList(llDrivesPathsList);
+    if (SUCCEEDED(hrc))
+    {
+        for (std::list<std::pair<com::Utf8Str, com::Utf8Str> >::const_iterator it = llDrivesPathsList.begin();
+             it != llDrivesPathsList.end();
+             ++it)
+        {
+            ComObjPtr<HostDrive> pHostDrive;
+            hrc = pHostDrive.createObject();
+            if (SUCCEEDED(hrc))
+                hrc = pHostDrive->initFromPathAndModel(it->first, it->second);
+            if (FAILED(hrc))
+                break;
+            aHostDrives.push_back(pHostDrive);
+        }
+    }
+    return hrc;
+}
+
+
 // public methods only for internal purposes
 ////////////////////////////////////////////////////////////////////////////////
@@ -2558,8 +2592,8 @@
 static char *solarisGetSliceFromPath(const char *pszDevLinkPath)
 {
-    char *pszFound = NULL;
-    char *pszSlice = strrchr(pszDevLinkPath, 's');
-    char *pszDisk  = strrchr(pszDevLinkPath, 'd');
-    if (pszSlice && pszSlice > pszDisk)
+    char *pszSlice = (char *)strrchr(pszDevLinkPath, 's');
+    char *pszDisk  = (char *)strrchr(pszDevLinkPath, 'd');
+    char *pszFound;
+    if (pszSlice && (uintptr_t)pszSlice > (uintptr_t)pszDisk)
         pszFound = pszSlice;
     else
@@ -2576,11 +2610,12 @@
  *
  * @param   DevLink     Handle to the device link being walked.
- * @param   pvArg       Opaque data containing the pointer to the path.
- * @returns Pointer to an allocated device path string.
+ * @param   pvArg       Opaque pointer that we use to point to the return
+ *                      variable (char *).  Caller must call RTStrFree on it.
+ * @returns DI_WALK_TERMINATE to stop the walk.
  */
 static int solarisWalkDevLink(di_devlink_t DevLink, void *pvArg)
 {
     char **ppszPath = (char **)pvArg;
-    *ppszPath = strdup(di_devlink_path(DevLink));
+    *ppszPath = RTStrDup(di_devlink_path(DevLink));
     return DI_WALK_TERMINATE;
 }
@@ -2655,4 +2690,5 @@
                                         RTStrPrintf(pDrive->szDescription, sizeof(pDrive->szDescription),
                                                     "%s %s", pszVendor, pszProduct);
+                                        RTStrPurgeEncoding(pDrive->szDescription);
                                         RTStrCopy(pDrive->szRawDiskPath, sizeof(pDrive->szRawDiskPath), pszDevLinkPath);
                                         if (*ppDrives)
@@ -2661,9 +2697,9 @@
 
                                         /* We're not interested in any of the other slices, stop minor nodes traversal. */
-                                        free(pszDevLinkPath);
+                                        RTStrFree(pszDevLinkPath);
                                         break;
                                     }
                                 }
-                                free(pszDevLinkPath);
+                                RTStrFree(pszDevLinkPath);
                             }
                         }
@@ -2703,4 +2739,152 @@
 }
 
+
+/**
+ * Walk all devices in the system and enumerate fixed drives.
+ * @param   Node        Handle to the current node.
+ * @param   pvArg       Opaque data (holds list pointer).
+ * @returns Solaris specific code whether to continue walking or not.
+ */
+static int solarisWalkDeviceNodeForFixedDrive(di_node_t Node, void *pvArg) RT_NOEXCEPT
+{
+    PSOLARISFIXEDDISK *ppDrives = (PSOLARISFIXEDDISK *)pvArg;
+
+    int *pInt = NULL;
+    if (    di_prop_lookup_ints(DDI_DEV_T_ANY, Node, "inquiry-device-type", &pInt) > 0
+        && *pInt == DTYPE_DIRECT) /* Fixed drive */
+    {
+        char *pszProduct = NULL;
+        if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "inquiry-product-id", &pszProduct) > 0)
+        {
+            char *pszVendor = NULL;
+            if (di_prop_lookup_strings(DDI_DEV_T_ANY, Node, "inquiry-vendor-id", &pszVendor) > 0)
+            {
+                /*
+                 * Found a fixed drive, we need to scan the minor nodes to find the correct
+                 * slice that represents the whole drive.
+                 */
+                int Major = di_driver_major(Node);
+                di_minor_t Minor = DI_MINOR_NIL;
+                di_devlink_handle_t DevLink = di_devlink_init(NULL /* name */, 0 /* flags */);
+                if (DevLink)
+                {
+                    /*
+                     * The device name we have to select depends on drive type. For fixed drives, the
+                     * name without slice or partition should be selected, for USB flash drive the
+                     * partition 0 should be selected and slice 0 for other cases.
+                     */
+                    char *pszDisk       = NULL;
+                    char *pszPartition0 = NULL;
+                    char *pszSlice0     = NULL;
+                    while ((Minor = di_minor_next(Node, Minor)) != DI_MINOR_NIL)
+                    {
+                        dev_t Dev = di_minor_devt(Minor);
+                        if (   Major != (int)major(Dev)
+                            || di_minor_spectype(Minor) == S_IFBLK
+                            || di_minor_type(Minor) != DDM_MINOR)
+                            continue;
+
+                        char *pszMinorPath = di_devfs_minor_path(Minor);
+                        if (!pszMinorPath)
+                            continue;
+
+                        char *pszDevLinkPath = NULL;
+                        di_devlink_walk(DevLink, NULL, pszMinorPath, DI_PRIMARY_LINK, &pszDevLinkPath, solarisWalkDevLink);
+                        di_devfs_path_free(pszMinorPath);
+
+                        if (pszDevLinkPath)
+                        {
+                            char const *pszCurSlice = strrchr(pszDevLinkPath, 's');
+                            char const *pszCurDisk  = strrchr(pszDevLinkPath, 'd');
+                            char const *pszCurPart  = strrchr(pszDevLinkPath, 'p');
+                            char **ppszDst  = NULL;
+                            if (pszCurSlice && (uintptr_t)pszCurSlice > (uintptr_t)pszCurDisk && !strcmp(pszCurSlice, "s0"))
+                                ppszDst = &pszSlice0;
+                            else if (pszCurPart && (uintptr_t)pszCurPart > (uintptr_t)pszCurDisk && !strcmp(pszCurPart, "p0"))
+                                ppszDst = &pszPartition0;
+                            else if (   (!pszCurSlice || (uintptr_t)pszCurSlice < (uintptr_t)pszCurDisk)
+                                     && (!pszCurPart  || (uintptr_t)pszCurPart  < (uintptr_t)pszCurDisk)
+                                     && *pszDevLinkPath != '\0')
+                                ppszDst = &pszDisk;
+                            else
+                                RTStrFree(pszDevLinkPath);
+                            if (ppszDst)
+                            {
+                                if (*ppszDst != NULL)
+                                    RTStrFree(*ppszDst);
+                                *ppszDst = pszDevLinkPath;
+                            }
+                        }
+                    }
+                    di_devlink_fini(&DevLink);
+                    if (pszDisk || pszPartition0 || pszSlice0)
+                    {
+                        PSOLARISFIXEDDISK pDrive = (PSOLARISFIXEDDISK)RTMemAllocZ(sizeof(*pDrive));
+                        if (RT_LIKELY(pDrive))
+                        {
+                            RTStrPrintf(pDrive->szDescription, sizeof(pDrive->szDescription), "%s %s", pszVendor, pszProduct);
+                            RTStrPurgeEncoding(pDrive->szDescription);
+
+                            const char *pszDevPath = pszDisk ? pszDisk : pszPartition0 ? pszPartition0 : pszSlice0;
+                            int rc = RTStrCopy(pDrive->szRawDiskPath, sizeof(pDrive->szRawDiskPath), pszDevPath);
+                            AssertRC(rc);
+
+                            if (*ppDrives)
+                                pDrive->pNext = *ppDrives;
+                            *ppDrives = pDrive;
+                        }
+                        RTStrFree(pszDisk);
+                        RTStrFree(pszPartition0);
+                        RTStrFree(pszSlice0);
+                    }
+                }
+            }
+        }
+    }
+    return DI_WALK_CONTINUE;
+}
+
+
+/**
+ * Solaris specific function to enumerate fixed drives via the device tree.
+ * Works on Solaris 10 as well as OpenSolaris without depending on libhal.
+ *
+ * @returns COM status, either S_OK or E_OUTOFMEMORY.
+ * @param   list        Reference to list where the the path/model pairs are to
+ *                      be returned.
+ */
+HRESULT Host::i_getFixedDrivesFromDevTree(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &list) RT_NOEXCEPT
+{
+    PSOLARISFIXEDDISK pDrives = NULL;
+    di_node_t RootNode = di_init("/", DINFOCPYALL);
+    if (RootNode != DI_NODE_NIL)
+        di_walk_node(RootNode, DI_WALK_CLDFIRST, &pDrives, solarisWalkDeviceNodeForFixedDrive);
+    di_fini(RootNode);
+
+    HRESULT hrc = S_OK;
+    try
+    {
+        for (PSOLARISFIXEDDISK pCurDrv = pDrives; pCurDrv; pCurDrv = pCurDrv->pNext)
+            list.push_back(std::pair<com::Utf8Str, com::Utf8Str>(pCurDrv->szRawDiskPath, pCurDrv->szDescription));
+    }
+    catch (std::bad_alloc &)
+    {
+        LogRelFunc(("Out of memory!\n"));
+        list.clear();
+        hrc = E_OUTOFMEMORY;
+    }
+
+    while (pDrives)
+    {
+        PSOLARISFIXEDDISK pFreeMe = pDrives;
+        pDrives = pDrives->pNext;
+        ASMCompilerBarrier();
+        RTMemFree(pFreeMe);
+    }
+
+    return hrc;
+}
+
+
 /* Solaris hosts, loading libhal at runtime */
 
@@ -2710,5 +2894,5 @@
  *
  * @returns true if information was successfully obtained, false otherwise
- * @retval  list drives found will be attached to this list
+ * @param   list        Reference to list where the DVDs drives are to be returned.
  */
 bool Host::i_getDVDInfoFromHal(std::list<ComObjPtr<Medium> > &list)
@@ -3021,4 +3205,129 @@
     return halSuccess;
 }
+
+
+/**
+ * Helper function to query the hal subsystem for information about fixed drives attached to the
+ * system.
+ *
+ * @returns COM status code. (setError is not called on failure as we only fail
+ *          with E_OUTOFMEMORY.)
+ * @retval  S_OK on success.
+ * @retval  S_FALSE if HAL cannot be used.
+ * @param   list        Reference to list to return the path/model string pairs.
+ */
+HRESULT Host::i_getFixedDrivesFromHal(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &list) RT_NOEXCEPT
+{
+    HRESULT hrc = S_FALSE;
+    if (!gLibHalCheckPresence())
+        return hrc;
+
+    DBusError dbusError;
+    gDBusErrorInit(&dbusError);
+    DBusConnection *dbusConnection = gDBusBusGet(DBUS_BUS_SYSTEM, &dbusError);
+    if (dbusConnection != 0)
+    {
+        LibHalContext *halContext = gLibHalCtxNew();
+        if (halContext != 0)
+        {
+            if (gLibHalCtxSetDBusConnection(halContext, dbusConnection))
+            {
+                if (gLibHalCtxInit(halContext, &dbusError))
+                {
+                    int cDevices;
+                    char **halDevices = gLibHalFindDeviceStringMatch(halContext, "storage.drive_type", "disk",
+                                                                     &cDevices, &dbusError);
+                    if (halDevices != 0)
+                    {
+                        /* Hal is installed and working, so if no devices are reported, assume
+                           that there are none. */
+                        hrc = S_OK;
+                        for (int i = 0; i < cDevices && hrc == S_OK; i++)
+                        {
+                            char *pszDevNode = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "block.device",
+                                                                              &dbusError);
+                            /* The fixed drive ioctls work only for raw device nodes. */
+                            char *pszTmp = getfullrawname(pszDevNode);
+                            gLibHalFreeString(pszDevNode);
+                            pszDevNode = pszTmp;
+                            if (pszDevNode != 0)
+                            {
+                                /* We do not check the error here, as this field may
+                                   not even exist. */
+                                char *pszVendor = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "info.vendor", 0);
+                                char *pszProduct = gLibHalDeviceGetPropertyString(halContext, halDevices[i], "info.product",
+                                                                                  &dbusError);
+                                Utf8Str strDescription;
+                                if (pszProduct != NULL && pszProduct[0] != '\0')
+                                {
+                                    int rc;
+                                    if (pszVendor != NULL && pszVendor[0] != '\0')
+                                        vrc = strDescription.printfNoThrow("%s %s", pszVendor, pszProduct);
+                                    else
+                                        vrc = strDescription.assignNoThrow(pszProduct);
+                                    AssertRCStmt(vrc, hrc = E_OUTOFMEMORY);
+                                }
+                                if (pszVendor != NULL)
+                                    gLibHalFreeString(pszVendor);
+                                if (pszProduct != NULL)
+                                    gLibHalFreeString(pszProduct);
+
+                                /* Correct device/partition/slice already choosen. Just add it to the return list */
+                                if (hrc == S_OK)
+                                    try
+                                    {
+                                        list.push_back(std::pair<com::Utf8Str, com::Utf8Str>(pszDevNode, strDescription));
+                                    }
+                                    catch (std::bad_alloc &)
+                                    {
+                                        AssertFailedStmt(hrc = E_OUTOFMEMORY);
+                                    }
+                                gLibHalFreeString(pszDevNode);
+                            }
+                            else
+                            {
+                                LogRel(("Host::COMGETTER(HostDrives): failed to get property \"block.device\" for device %s.  dbus error: %s (%s)\n",
+                                        halDevices[i], dbusError.name, dbusError.message));
+                                gDBusErrorFree(&dbusError);
+                            }
+                        }
+                        gLibHalFreeStringArray(halDevices);
+                    }
+                    else
+                    {
+                        LogRel(("Host::COMGETTER(HostDrives): failed to get devices with capability \"storage.disk\".  dbus error: %s (%s)\n", dbusError.name, dbusError.message));
+                        gDBusErrorFree(&dbusError);
+                    }
+                    if (!gLibHalCtxShutdown(halContext, &dbusError))  /* what now? */
+                    {
+                        LogRel(("Host::COMGETTER(HostDrives): failed to shutdown the libhal context.  dbus error: %s (%s)\n",
+                                dbusError.name, dbusError.message));
+                        gDBusErrorFree(&dbusError);
+                    }
+                }
+                else
+                {
+                    LogRel(("Host::COMGETTER(HostDrives): failed to initialise libhal context.  dbus error: %s (%s)\n",
+                            dbusError.name, dbusError.message));
+                    gDBusErrorFree(&dbusError);
+                }
+                gLibHalCtxFree(halContext);
+            }
+            else
+                LogRel(("Host::COMGETTER(HostDrives): failed to set libhal connection to dbus.\n"));
+        }
+        else
+            LogRel(("Host::COMGETTER(HostDrives): failed to get a libhal context - out of memory?\n"));
+        gDBusConnectionUnref(dbusConnection);
+    }
+    else
+    {
+        LogRel(("Host::COMGETTER(HostDrives): failed to connect to dbus.  dbus error: %s (%s)\n",
+                dbusError.name, dbusError.message));
+        gDBusErrorFree(&dbusError);
+    }
+    return hrc;
+}
+
 #endif  /* RT_OS_SOLARIS and VBOX_USE_HAL */
 
@@ -3574,3 +3883,177 @@
 }
 
+/**
+ * @throws nothing
+ */
+HRESULT Host::i_getDrivesPathsList(std::list<std::pair<com::Utf8Str, com::Utf8Str> > &aDriveList) RT_NOEXCEPT
+{
+#ifdef RT_OS_WINDOWS
+    /** @todo r=bird: This approach is seriously flawed. The "Count" value refers to
+     * the registry entries next to it and as little if anything to do with
+     * PhysicalDriveX numbering.  The registry entries doesn't immediately give
+     * you the PhysicalDrive address either.
+     *
+     * One option would be to enumerate the \Device or \GLOBAL?? directories looking
+     * for Harddisk* directories and PhysicalDrive* symlinks respectively.  This can
+     * be explored using
+     *  - "out\win.amd64\debug\bin\tools\RTLs.exe -la \\:iprtnt:\Device"
+     *  - "out\win.amd64\debug\bin\tools\RTLs.exe -la \\:iprtnt:\GLOBAL??"
+     *  - WinObj from sysinternals.
+     *
+     * "wmic diskdrive list" somehow gets the info too.   There is more here:
+     * https://stackoverflow.com/questions/327718/how-to-list-physical-disks
+     *
+     * A third option would be to just be to go significantly higher than what
+     * "Count" indicates, to span gaps and stuff.
+     *
+     *
+     * How to create gaps in the PhysicalDriveX numbers:
+     *      1. Insert 2 USB sticks to you box.
+     *      2. Remove the first USB stick you inserted.
+     *      3. You've got a gap: RTLs -la \\:iprtnt:\GLOBAL?? | grep PhysicalDrive
+     */
+    HKEY hKeyEnum = (HKEY)INVALID_HANDLE_VALUE;
+    LONG lRc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                             L"SYSTEM\\CurrentControlSet\\Services\\Disk\\Enum",
+                             0,
+                             KEY_READ | KEY_QUERY_VALUE,
+                             &hKeyEnum);
+    if (lRc != ERROR_SUCCESS)
+        return setError(E_FAIL, tr("Failed to open key Disk\\Enum (error %u/%#x)"), lRc, lRc);
+
+    DWORD cCount = 0;
+    DWORD cBufSize = sizeof(cCount);
+    lRc = RegQueryValueExW(hKeyEnum, L"Count", NULL, NULL, (PBYTE)&cCount, &cBufSize);
+    RegCloseKey(hKeyEnum);
+    if (lRc != ERROR_SUCCESS)
+        return setError(E_FAIL, tr("Failed to get physical drives count (error %u/%#x)"), lRc, lRc);
+
+    for (uint32_t i = 0; i < cCount; ++i)
+    {
+        char szPhysicalDrive[64];
+        RTStrPrintf(szPhysicalDrive, sizeof(szPhysicalDrive), "\\\\.\\PhysicalDrive%d", i);
+
+        /** @todo r=bird: Why RTFILE_O_DENY_WRITE? */
+        RTFILE hRawFile = NIL_RTFILE;
+        int vrc = RTFileOpen(&hRawFile, szPhysicalDrive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+        if (RT_FAILURE(vrc))
+            continue; /** @todo r=bird: you might not be able to open all disks... */
+
+        DWORD   cbBytesReturned = 0;
+        uint8_t abBuffer[1024];
+        RT_ZERO(abBuffer);
+
+        STORAGE_PROPERTY_QUERY query;
+        RT_ZERO(query);
+        query.PropertyId = StorageDeviceProperty;
+        query.QueryType  = PropertyStandardQuery;
+
+        BOOL fRc = DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
+                                   IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query),
+                                   abBuffer, sizeof(abBuffer), &cbBytesReturned, NULL);
+        RTFileClose(hRawFile);
+        char szModel[1024];
+        if (fRc)
+        {
+            PSTORAGE_DEVICE_DESCRIPTOR pDevDescriptor = (PSTORAGE_DEVICE_DESCRIPTOR)abBuffer;
+            char *pszProduct = pDevDescriptor->ProductIdOffset ? (char *)&abBuffer[pDevDescriptor->ProductIdOffset] : NULL;
+            if (pszProduct)
+            {
+                RTStrPurgeEncoding(pszProduct);
+                if (*pszProduct != '\0')
+                {
+                    char *pszVendor = pDevDescriptor->VendorIdOffset  ? (char *)&abBuffer[pDevDescriptor->VendorIdOffset] : NULL;
+                    if (pszVendor)
+                        RTStrPurgeEncoding(pszVendor);
+                    if (pszVendor && *pszVendor)
+                        RTStrPrintf(szModel, sizeof(szModel), "%s %s", pszVendor, pszProduct);
+                    else
+                        RTStrCopy(szModel, sizeof(szModel), pszProduct);
+                }
+            }
+        }
+        try
+        {
+            aDriveList.push_back(std::pair<com::Utf8Str, com::Utf8Str>(szPhysicalDrive, szModel));
+        }
+        catch (std::bad_alloc &)
+        {
+            aDriveList.clear();
+            return E_OUTOFMEMORY;
+        }
+    }
+
+    return S_OK;
+
+#elif defined(RT_OS_DARWIN)
+    /*
+     * Get the list of fixed drives from iokit.cpp and transfer it to aDriveList.
+     */
+    PDARWINFIXEDDRIVE pDrives = DarwinGetFixedDrives();
+    HRESULT hrc;
+    try
+    {
+        for (PDARWINFIXEDDRIVE pCurDrv = pDrives; pCurDrv; pCurDrv = pCurDrv->pNext)
+            aDriveList.push_back(std::pair<com::Utf8Str, com::Utf8Str>(pCurDrv->szName, pCurDrv->pszModel));
+        hrc = S_OK;
+    }
+    catch (std::bad_alloc &)
+    {
+        aDriveList.clear();
+        hrc = E_OUTOFMEMORY;
+    }
+
+    while (pDrives)
+    {
+        PDARWINFIXEDDRIVE pFreeMe = pDrives;
+        pDrives = pDrives->pNext;
+        ASMCompilerBarrier();
+        RTMemFree(pFreeMe);
+    }
+    return hrc;
+
+#elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
+    /*
+     * The list of fixed drives is kept in the VBoxMainDriveInfo instance, so
+     * update it and tranfer the info to aDriveList.
+     *
+     * This obviously requires us to write lock the object!
+     */
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+    int vrc = m->hostDrives.updateFixedDrives(); /* nothrow */
+    if (RT_FAILURE(vrc))
+        return setErrorBoth(E_FAIL, vrc, tr("Failed to update fixed drive list (%Rrc)"), vrc);
+
+    try
+    {
+        for (DriveInfoList::const_iterator it = m->hostDrives.FixedDriveBegin(); it != m->hostDrives.FixedDriveEnd(); ++it)
+            aDriveList.push_back(std::pair<com::Utf8Str, com::Utf8Str>(it->mDevice, it->mDescription));
+    }
+    catch (std::bad_alloc &)
+    {
+        aDriveList.clear();
+        return E_OUTOFMEMORY;
+    }
+    return S_OK;
+
+#elif defined(RT_OS_SOLARIS)
+    /*
+     * We can get the info from HAL, if not present/working we'll get by
+     * walking the device tree.
+     */
+# ifdef VBOX_USE_LIBHAL
+    HRESULT hrc = i_getFixedDrivesFromHal(aDriveList);
+    if (hrc != S_FALSE)
+        return hrc;
+    aDriveList.clear(); /* just in case */
+# endif
+    return i_getFixedDrivesFromDevTree(aDriveList);
+
+#else
+    /* PORTME */
+    RT_NOREF(aDriveList);
+    return E_NOTIMPL;
+#endif
+}
+
 /* vi: set tabstop=4 shiftwidth=4 expandtab: */
Index: /trunk/src/VBox/Main/src-server/darwin/iokit.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/darwin/iokit.cpp	(revision 85928)
+++ /trunk/src/VBox/Main/src-server/darwin/iokit.cpp	(revision 85929)
@@ -31,13 +31,19 @@
 #include <mach/mach.h>
 #include <Carbon/Carbon.h>
+#include <CoreFoundation/CFBase.h>
 #include <IOKit/IOKitLib.h>
+#include <IOKit/IOBSD.h>
 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
+#include <IOKit/storage/IOBlockStorageDevice.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDMedia.h>
 #include <IOKit/scsi/SCSITaskLib.h>
 #include <SystemConfiguration/SystemConfiguration.h>
 #include <mach/mach_error.h>
+#include <sys/param.h>
+#include <paths.h>
 #ifdef VBOX_WITH_USB
 # include <IOKit/usb/IOUSBLib.h>
 # include <IOKit/IOCFPlugIn.h>
-# include <IOKit/storage/IOMedia.h>
 #endif
 
@@ -1570,4 +1576,168 @@
 
     IOObjectRelease(DVDServices);
+
+    return pHead;
+}
+
+
+/**
+ * Enumerate the fixed drives (HDDs, SSD, ++) returning a FIFO of device paths
+ * strings and model strings separated by ':'.
+ *
+ * @returns Pointer to the head.
+ *          The caller is responsible for calling RTMemFree() on each of the nodes.
+ */
+PDARWINFIXEDDRIVE DarwinGetFixedDrives(void)
+{
+    AssertReturn(darwinOpenMasterPort(), NULL);
+
+    /*
+     * Create a matching dictionary for searching drives in the IOKit.
+     *
+     * The idea is to find all the IOMedia objects with "Whole"="True" which identify the disks but
+     * not partitions.
+     */
+    CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOMedia");
+    AssertReturn(RefMatchingDict, NULL);
+    CFDictionaryAddValue(RefMatchingDict, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
+
+    /*
+     * Perform the search and get a collection of IOMedia objects.
+     */
+    io_iterator_t MediaServices = IO_OBJECT_NULL;
+    IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &MediaServices);
+    AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
+    RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
+
+    /*
+     * Enumerate the matching services.
+     * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
+     */
+    PDARWINFIXEDDRIVE pHead = NULL;
+    PDARWINFIXEDDRIVE pTail = NULL;
+    unsigned i = 0;
+    io_object_t MediaService;
+    while ((MediaService = IOIteratorNext(MediaServices)) != IO_OBJECT_NULL)
+    {
+        DARWIN_IOKIT_DUMP_OBJ(MediaService);
+
+        /*
+         * Find the IOMedia parents having the IOBlockStorageDevice type and check they have "device-type" = "Generic".
+         * If the IOMedia object hasn't IOBlockStorageDevices with such device-type in parents the one is not general
+         * disk but either CDROM-like device or some another device which has no interest for the function.
+         */
+
+        /*
+         * Just avoid parents enumeration if the IOMedia is IOCDMedia, i.e. CDROM-like disk
+         */
+        if (IOObjectConformsTo(MediaService, kIOCDMediaClass))
+        {
+            IOObjectRelease(MediaService);
+            continue;
+        }
+
+        bool fIsGenericStorage = false;
+        io_registry_entry_t ChildEntry = MediaService;
+        io_registry_entry_t ParentEntry = IO_OBJECT_NULL;
+        kern_return_t krc = KERN_SUCCESS;
+        while (   !fIsGenericStorage
+               && (krc = IORegistryEntryGetParentEntry(ChildEntry, kIOServicePlane, &ParentEntry)) == KERN_SUCCESS)
+        {
+            if (!IOObjectIsEqualTo(ChildEntry, MediaService))
+                IOObjectRelease(ChildEntry);
+
+            DARWIN_IOKIT_DUMP_OBJ(ParentEntry);
+            if (IOObjectConformsTo(ParentEntry, kIOBlockStorageDeviceClass))
+            {
+                CFTypeRef DeviceTypeValueRef = IORegistryEntryCreateCFProperty(ParentEntry,
+                                                                               CFSTR("device-type"),
+                                                                               kCFAllocatorDefault, 0);
+                if (   DeviceTypeValueRef
+                    && CFGetTypeID(DeviceTypeValueRef) == CFStringGetTypeID()
+                    && CFStringCompare((CFStringRef)DeviceTypeValueRef, CFSTR("Generic"),
+                                       kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+                    fIsGenericStorage = true;
+
+                if (DeviceTypeValueRef != NULL)
+                    CFRelease(DeviceTypeValueRef);
+            }
+            ChildEntry = ParentEntry;
+        }
+        if (ChildEntry != IO_OBJECT_NULL && !IOObjectIsEqualTo(ChildEntry, MediaService))
+            IOObjectRelease(ChildEntry);
+
+        if (!fIsGenericStorage)
+        {
+            IOObjectRelease(MediaService);
+            continue;
+        }
+
+        CFTypeRef DeviceName;
+        DeviceName = IORegistryEntryCreateCFProperty(MediaService,
+                                                     CFSTR(kIOBSDNameKey),
+                                                     kCFAllocatorDefault,0);
+        if (DeviceName)
+        {
+            char szDeviceFilePath[MAXPATHLEN];
+            strcpy(szDeviceFilePath, _PATH_DEV);
+            size_t cchPathSize = strlen(szDeviceFilePath);
+            if (CFStringGetCString((CFStringRef)DeviceName,
+                                   &szDeviceFilePath[cchPathSize],
+                                   (CFIndex)(sizeof(szDeviceFilePath) - cchPathSize),
+                                   kCFStringEncodingUTF8))
+            {
+                PDARWINFIXEDDRIVE pDuplicate = pHead;
+                while (pDuplicate && strcmp(szDeviceFilePath, pDuplicate->szName) != 0)
+                    pDuplicate = pDuplicate->pNext;
+                if (pDuplicate == NULL)
+                {
+                    /* Get model for the IOMedia object.
+                     *
+                     * Due to vendor and product property names are different and
+                     * depend on interface and device type, the best way to get a drive
+                     * model is get IORegistry name for the IOMedia object. Usually,
+                     * it takes "<vendor> <product> <revision> Media" form. Noticed,
+                     * such naming are used by only IOMedia objects having
+                     * "Whole" = True and "BSDName" properties set.
+                     */
+                    io_name_t szEntryName = { 0 };
+                    if ((krc = IORegistryEntryGetName(MediaService, szEntryName)) == KERN_SUCCESS)
+                    {
+                        /* remove " Media" from the end of the name */
+                        char *pszMedia = strrchr(szEntryName, ' ');
+                        if (   pszMedia != NULL
+                            && (uintptr_t)pszMedia < (uintptr_t)&szEntryName[sizeof(szEntryName)]
+                            && strcmp(pszMedia, " Media") == 0)
+                        {
+                            *pszMedia = '\0';
+                            RTStrPurgeEncoding(szEntryName);
+                        }
+                    }
+                    /* Create the device path and model name in form "/device/path:model". */
+                    cchPathSize = strlen(szDeviceFilePath);
+                    size_t const cchModelSize = strlen(szEntryName);
+                    size_t const cbExtra = cchPathSize + 1 + cchModelSize + !!cchModelSize;
+                    PDARWINFIXEDDRIVE pNew = (PDARWINFIXEDDRIVE)RTMemAlloc(RT_UOFFSETOF_DYN(DARWINFIXEDDRIVE, szName[cbExtra]));
+                    if (pNew)
+                    {
+                        pNew->pNext = NULL;
+                        memcpy(pNew->szName, szDeviceFilePath, cchPathSize + 1);
+                        pNew->pszModel = NULL;
+                        if (cchModelSize)
+                            pNew->pszModel = (const char *)memcpy(&pNew->szName[cchPathSize + 1], szEntryName, cchModelSize + 1);
+
+                        if (pTail)
+                            pTail = pTail->pNext = pNew;
+                        else
+                            pTail = pHead = pNew;
+                    }
+                }
+            }
+            CFRelease(DeviceName);
+        }
+        IOObjectRelease(MediaService);
+        i++;
+    }
+    IOObjectRelease(MediaServices);
 
     return pHead;
Index: /trunk/src/VBox/Main/src-server/darwin/iokit.h
===================================================================
--- /trunk/src/VBox/Main/src-server/darwin/iokit.h	(revision 85928)
+++ /trunk/src/VBox/Main/src-server/darwin/iokit.h	(revision 85929)
@@ -24,4 +24,5 @@
 #include <iprt/cdefs.h>
 #include <iprt/types.h>
+#include <iprt/cpp/ministring.h>
 #ifdef VBOX_WITH_USB
 # include <VBox/usb.h>
@@ -40,4 +41,19 @@
 /** Pointer to a Darwin DVD descriptor. */
 typedef DARWINDVD *PDARWINDVD;
+
+/** Darwin fixed drive (SSD, HDD, ++) descriptor as returned by
+ *  DarwinGetFixedDrives(). */
+typedef struct DARWINFIXEDDRIVE
+{
+    /** Pointer to the next DVD. */
+    struct DARWINFIXEDDRIVE *pNext;
+    /** Pointer to the model name, NULL if none.
+     * This points after szName and needs not be freed separately. */
+    const char *pszModel;
+    /** Variable length name / identifier. */
+    char szName[1];
+} DARWINFIXEDDRIVE;
+/** Pointer to a Darwin fixed drive. */
+typedef DARWINFIXEDDRIVE *PDARWINFIXEDDRIVE;
 
 
@@ -85,4 +101,5 @@
 #endif /* VBOX_WITH_USB */
 PDARWINDVD      DarwinGetDVDDrives(void);
+PDARWINFIXEDDRIVE DarwinGetFixedDrives(void);
 PDARWINETHERNIC DarwinGetEthernetControllers(void);
 RT_C_DECLS_END
Index: /trunk/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp	(revision 85928)
+++ /trunk/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp	(revision 85929)
@@ -1,5 +1,5 @@
 /* $Id$ */
 /** @file
- * Classes for handling hardware detection under FreeBSD.
+ * VirtualBox Main - Code for handling hardware detection under FreeBSD, VBoxSVC.
  */
 
@@ -16,12 +16,10 @@
  */
 
-#define LOG_GROUP LOG_GROUP_MAIN
-
 
 /*********************************************************************************************************************************
 *   Header Files                                                                                                                 *
 *********************************************************************************************************************************/
-
-#include <HostHardwareLinux.h>
+#define LOG_GROUP LOG_GROUP_MAIN
+#include "HostHardwareLinux.h"
 
 #include <VBox/log.h>
@@ -35,16 +33,16 @@
 #include <iprt/string.h>
 
-#ifdef RT_OS_FREEBSD
-# include <sys/param.h>
-# include <sys/types.h>
-# include <sys/stat.h>
-# include <unistd.h>
-# include <stdio.h>
-# include <sys/ioctl.h>
-# include <fcntl.h>
-# include <cam/cam.h>
-# include <cam/cam_ccb.h>
-# include <cam/scsi/scsi_pass.h>
-#endif /* RT_OS_FREEBSD */
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <camlib.h>
+#include <cam/scsi/scsi_pass.h>
+
 #include <vector>
 
@@ -53,43 +51,50 @@
 *   Typedefs and Defines                                                                                                         *
 *********************************************************************************************************************************/
-
-static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
-                               bool isDVD, bool *pfSuccess);
-static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess);
+typedef enum DriveType_T
+{
+    Fixed,
+    DVD,
+    Any
+} DriveType_T;
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_PROTP;
+static int getDriveInfoFromCAM(DriveInfoList *pList, DriveType_T enmDriveType, bool *pfSuccess) RT_NOTHROW_PROTP;
+
 
 /** Find the length of a string, ignoring trailing non-ascii or control
- * characters */
-static size_t strLenStripped(const char *pcsz)
+ * characters
+ * @note Code duplicated in HostHardwareLinux.cpp  */
+static size_t strLenStripped(const char *pcsz) RT_NOTHROW_DEF
 {
     size_t cch = 0;
     for (size_t i = 0; pcsz[i] != '\0'; ++i)
-        if (pcsz[i] > 32 && pcsz[i] < 127)
+        if (pcsz[i] > 32 /*space*/ && pcsz[i] < 127 /*delete*/)
             cch = i;
     return cch + 1;
 }
 
-static void strLenRemoveTrailingWhiteSpace(char *psz, size_t cchStr)
-{
-    while (   (cchStr > 0)
-           && (psz[cchStr -1] == ' '))
-        psz[--cchStr] = '\0';
-}
 
 /**
- * Initialise the device description for a DVD drive based on
- * vendor and model name strings.
- * @param pcszVendor  the vendor ID string
- * @param pcszModel   the product ID string
- * @param pszDesc    where to store the description string (optional)
- * @param cchDesc    the size of the buffer in @pszDesc
+ * Initialize the device description for a drive based on vendor and model name
+ * strings.
+ *
+ * @param   pcszVendor  The raw vendor ID string.
+ * @param   pcszModel   The raw product ID string.
+ * @param   pszDesc     Where to store the description string (optional)
+ * @param   cbDesc      The size of the buffer in @pszDesc
+ *
+ * @note    Used for disks as well as DVDs.
  */
 /* static */
-void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel,
-                            char *pszDesc, size_t cchDesc)
+void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
 {
     AssertPtrReturnVoid(pcszVendor);
     AssertPtrReturnVoid(pcszModel);
     AssertPtrNullReturnVoid(pszDesc);
-    AssertReturnVoid(!pszDesc || cchDesc > 0);
+    AssertReturnVoid(!pszDesc || cbDesc > 0);
     size_t cchVendor = strLenStripped(pcszVendor);
     size_t cchModel = strLenStripped(pcszModel);
@@ -99,30 +104,28 @@
     {
         if (cchVendor > 0)
-            RTStrPrintf(pszDesc, cchDesc, "%.*s %s", cchVendor, pcszVendor,
+            RTStrPrintf(pszDesc, cbDesc, "%.*s %s", cchVendor, pcszVendor,
                         cchModel > 0 ? pcszModel : "(unknown drive model)");
         else
-            RTStrPrintf(pszDesc, cchDesc, "%s", pcszModel);
-    }
-}
-
-
-int VBoxMainDriveInfo::updateDVDs ()
+            RTStrPrintf(pszDesc, cbDesc, "%s", pcszModel);
+        RTStrPurgeEncoding(pszDesc);
+    }
+}
+
+
+int VBoxMainDriveInfo::updateDVDs() RT_NOEXCEPT
 {
     LogFlowThisFunc(("entered\n"));
-    int rc = VINF_SUCCESS;
-    bool fSuccess = false;  /* Have we succeeded in finding anything yet? */
-
+    int rc;
     try
     {
-        mDVDList.clear ();
+        mDVDList.clear();
         /* Always allow the user to override our auto-detection using an
          * environment variable. */
+        bool fSuccess = false;  /* Have we succeeded in finding anything yet? */
+        rc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */, &fSuccess);
         if (RT_SUCCESS(rc) && !fSuccess)
-            rc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */,
-                                     &fSuccess);
-        if (RT_SUCCESS(rc) && !fSuccess)
-            rc = getDVDInfoFromCAM(&mDVDList, &fSuccess);
-    }
-    catch(std::bad_alloc &e)
+            rc = getDriveInfoFromCAM(&mDVDList, DVD, &fSuccess);
+    }
+    catch (std::bad_alloc &)
     {
         rc = VERR_NO_MEMORY;
@@ -132,20 +135,16 @@
 }
 
-int VBoxMainDriveInfo::updateFloppies ()
+int VBoxMainDriveInfo::updateFloppies() RT_NOEXCEPT
 {
     LogFlowThisFunc(("entered\n"));
-    int rc = VINF_SUCCESS;
-    bool fSuccess = false;  /* Have we succeeded in finding anything yet? */
-
+    int rc;
     try
     {
-        mFloppyList.clear ();
-        /* Always allow the user to override our auto-detection using an
-         * environment variable. */
-        if (RT_SUCCESS(rc) && !fSuccess)
-            rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
-                                     &fSuccess);
-    }
-    catch(std::bad_alloc &e)
+        /* Only got the enviornment variable here... */
+        mFloppyList.clear();
+        bool fSuccess = false;  /* ignored */
+        rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */, &fSuccess);
+    }
+    catch (std::bad_alloc &)
     {
         rc = VERR_NO_MEMORY;
@@ -155,18 +154,165 @@
 }
 
+int VBoxMainDriveInfo::updateFixedDrives() RT_NOEXCEPT
+{
+    LogFlowThisFunc(("entered\n"));
+    int rc;
+    try
+    {
+        mFixedDriveList.clear();
+        bool fSuccess = false;  /* ignored */
+        rc = getDriveInfoFromCAM(&mFixedDriveList, Fixed, &fSuccess);
+    }
+    catch (std::bad_alloc &)
+    {
+        rc = VERR_NO_MEMORY;
+    }
+    LogFlowThisFunc(("rc=%Rrc\n", rc));
+    return rc;
+}
+
+static void strDeviceStringSCSI(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+    char szVendor[128];
+    cam_strvis((uint8_t *)szVendor, (const uint8_t *)pDevResult->inq_data.vendor,
+               sizeof(pDevResult->inq_data.vendor), sizeof(szVendor));
+    char szProduct[128];
+    cam_strvis((uint8_t *)szProduct, (const uint8_t *)pDevResult->inq_data.product,
+               sizeof(pDevResult->inq_data.product), sizeof(szProduct));
+    dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc);
+}
+
+static void strDeviceStringATA(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+    char szProduct[256];
+    cam_strvis((uint8_t *)szProduct, (const uint8_t *)pDevResult->ident_data.model,
+               sizeof(pDevResult->ident_data.model), sizeof(szProduct));
+    dvdCreateDeviceString("", szProduct, pszDesc, cbDesc);
+}
+
+static void strDeviceStringSEMB(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+    sep_identify_data *pSid = (sep_identify_data *)&pDevResult->ident_data;
+
+    char szVendor[128];
+    cam_strvis((uint8_t *)szVendor, (const uint8_t *)pSid->vendor_id,
+               sizeof(pSid->vendor_id), sizeof(szVendor));
+    char szProduct[128];
+    cam_strvis((uint8_t *)szProduct, (const uint8_t *)pSid->product_id,
+               sizeof(pSid->product_id), sizeof(szProduct));
+    dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc);
+}
+
+static void strDeviceStringMMCSD(device_match_result *pDevResult, char *pszDesc, size_t cbDesc)  RT_NOTHROW_DEF
+{
+    struct cam_device *pDev = cam_open_btl(pDevResult->path_id, pDevResult->target_id,
+                                           pDevResult->target_lun, O_RDWR, NULL);
+    if (pDev == NULL)
+    {
+        Log(("Error while opening drive device. Error: %s\n", cam_errbuf));
+        return;
+    }
+
+    union ccb *pCcb = cam_getccb(pDev);
+    if (pCcb != NULL)
+    {
+        struct mmc_params mmcIdentData;
+        RT_ZERO(mmcIdentData);
+
+        struct ccb_dev_advinfo *pAdvi = &pCcb->cdai;
+        pAdvi->ccb_h.flags = CAM_DIR_IN;
+        pAdvi->ccb_h.func_code = XPT_DEV_ADVINFO;
+        pAdvi->flags = CDAI_FLAG_NONE;
+        pAdvi->buftype = CDAI_TYPE_MMC_PARAMS;
+        pAdvi->bufsiz = sizeof(mmcIdentData);
+        pAdvi->buf = (uint8_t *)&mmcIdentData;
+
+        if (cam_send_ccb(pDev, pCcb) >= 0)
+        {
+            if (strlen((char *)mmcIdentData.model) > 0)
+                dvdCreateDeviceString("", (const char *)mmcIdentData.model, pszDesc, cbDesc);
+            else
+                dvdCreateDeviceString("", mmcIdentData.card_features & CARD_FEATURE_SDIO ? "SDIO card" : "Unknown card",
+                                      pszDesc, cbDesc);
+        }
+        else
+            Log(("error sending XPT_DEV_ADVINFO CCB\n"));
+
+        cam_freeccb(pCcb);
+    }
+    else
+        Log(("Could not allocate CCB\n"));
+    cam_close_device(pDev);
+}
+
+/** @returns boolean success indicator (true/false). */
+static int nvmeGetCData(struct cam_device *pDev, struct nvme_controller_data *pCData) RT_NOTHROW_DEF
+{
+    bool fSuccess = false;
+    union ccb *pCcb = cam_getccb(pDev);
+    if (pCcb != NULL)
+    {
+        struct ccb_dev_advinfo *pAdvi = &pCcb->cdai;
+        pAdvi->ccb_h.flags = CAM_DIR_IN;
+        pAdvi->ccb_h.func_code = XPT_DEV_ADVINFO;
+        pAdvi->flags = CDAI_FLAG_NONE;
+        pAdvi->buftype = CDAI_TYPE_NVME_CNTRL;
+        pAdvi->bufsiz = sizeof(struct nvme_controller_data);
+        pAdvi->buf = (uint8_t *)pCData;
+        RT_BZERO(pAdvi->buf, pAdvi->bufsiz);
+
+        if (cam_send_ccb(pDev, pCcb) >= 0)
+        {
+            if (pAdvi->ccb_h.status == CAM_REQ_CMP)
+                fSuccess = true;
+            else
+                Log(("Got CAM error %#x\n", pAdvi->ccb_h.status));
+        }
+        else
+            Log(("Error sending XPT_DEV_ADVINFO CC\n"));
+        cam_freeccb(pCcb);
+    }
+    else
+        Log(("Could not allocate CCB\n"));
+    return fSuccess;
+}
+
+static void strDeviceStringNVME(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
+{
+    struct cam_device *pDev = cam_open_btl(pDevResult->path_id, pDevResult->target_id,
+                                           pDevResult->target_lun, O_RDWR, NULL);
+    if (pDev)
+    {
+        struct nvme_controller_data CData;
+        if (nvmeGetCData(pDev, &CData))
+        {
+            char szVendor[128];
+            cam_strvis((uint8_t *)szVendor, CData.mn, sizeof(CData.mn), sizeof(szVendor));
+            char szProduct[128];
+            cam_strvis((uint8_t *)szProduct, CData.fr, sizeof(CData.fr), sizeof(szProduct));
+            dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc);
+        }
+        else
+            Log(("Error while getting NVME drive info\n"));
+        cam_close_device(pDev);
+    }
+    else
+        Log(("Error while opening drive device. Error: %s\n", cam_errbuf));
+}
+
+
 /**
- * Search for available CD/DVD drives using the CAM layer.
+ * Search for available drives using the CAM layer.
  *
  * @returns iprt status code
- * @param   pList      the list to append the drives found to
- * @param   pfSuccess  this will be set to true if we found at least one drive
- *                     and to false otherwise.  Optional.
+ * @param   pList         the list to append the drives found to
+ * @param   enmDriveType  search drives of specified type
+ * @param   pfSuccess     this will be set to true if we found at least one drive
+ *                        and to false otherwise.  Optional.
  */
-static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess)
-{
-    int rc = VINF_SUCCESS;
-    RTFILE FileXpt;
-
-    rc = RTFileOpen(&FileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
+static int getDriveInfoFromCAM(DriveInfoList *pList, DriveType_T enmDriveType, bool *pfSuccess) RT_NOTHROW_DEF
+{
+    RTFILE hFileXpt = NIL_RTFILE;
+    int rc = RTFileOpen(&hFileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
     if (RT_SUCCESS(rc))
     {
@@ -196,5 +342,6 @@
  #define INQ_PAT inq_pat
 #endif
-        DeviceMatchPattern.pattern.device_pattern.INQ_PAT.type = T_CDROM;
+        DeviceMatchPattern.pattern.device_pattern.INQ_PAT.type = enmDriveType == Fixed ? T_DIRECT
+                                                               : enmDriveType == DVD   ? T_CDROM : T_ANY;
         DeviceMatchPattern.pattern.device_pattern.INQ_PAT.media_type  = SIP_MEDIA_REMOVABLE | SIP_MEDIA_FIXED;
         DeviceMatchPattern.pattern.device_pattern.INQ_PAT.vendor[0]   = '*'; /* Matches anything */
@@ -220,5 +367,5 @@
             do
             {
-                rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL);
+                rc = RTFileIoCtl(hFileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL);
                 if (RT_FAILURE(rc))
                 {
@@ -231,4 +378,12 @@
                     if (paMatches[i].type == DEV_MATCH_DEVICE)
                     {
+                        /*
+                         * The result list can contain some empty entries with DEV_RESULT_UNCONFIGURED
+                         * flag set, e.g. in case of T_DIRECT. Ignore them.
+                         */
+                        if (   (paMatches[i].result.device_result.flags & DEV_RESULT_UNCONFIGURED)
+                            == DEV_RESULT_UNCONFIGURED)
+                            continue;
+
                         /* We have the drive now but need the appropriate device node */
                         struct device_match_result *pDevResult = &paMatches[i].result.device_result;
@@ -254,5 +409,6 @@
                         PeriphMatchPattern.pattern.periph_pattern.target_id  = paMatches[i].result.device_result.target_id;
                         PeriphMatchPattern.pattern.periph_pattern.target_lun = paMatches[i].result.device_result.target_lun;
-                        PeriphMatchPattern.pattern.periph_pattern.flags      = (periph_pattern_flags)(  PERIPH_MATCH_PATH | PERIPH_MATCH_TARGET
+                        PeriphMatchPattern.pattern.periph_pattern.flags      = (periph_pattern_flags)(  PERIPH_MATCH_PATH
+                                                                                                      | PERIPH_MATCH_TARGET
                                                                                                       | PERIPH_MATCH_LUN);
                         PeriphCCB.cdm.num_patterns    = 1;
@@ -265,5 +421,5 @@
                         do
                         {
-                            rc = RTFileIoCtl(FileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL);
+                            rc = RTFileIoCtl(hFileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL);
                             if (RT_FAILURE(rc))
                             {
@@ -274,6 +430,7 @@
                             for (iPeriphMatch = 0; iPeriphMatch < PeriphCCB.cdm.num_matches; iPeriphMatch++)
                             {
-                                if (   (aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH)
-                                    && (!strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "cd")))
+                                /* Ignore "passthrough mode" paths */
+                                if (   aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH
+                                    && strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "pass"))
                                 {
                                     pPeriphResult = &aPeriphMatches[iPeriphMatch].result.periph_result;
@@ -285,26 +442,34 @@
                                 break;
 
-                        } while (   (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
-                                 && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
+                        } while (   DeviceCCB.ccb_h.status == CAM_REQ_CMP
+                                 && DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE);
 
                         if (pPeriphResult)
                         {
                             char szPath[RTPATH_MAX];
-                            char szDesc[256];
-
                             RTStrPrintf(szPath, sizeof(szPath), "/dev/%s%d",
                                         pPeriphResult->periph_name, pPeriphResult->unit_number);
 
-                            /* Remove trailing white space. */
-                            strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.vendor,
-                                                            sizeof(pDevResult->inq_data.vendor));
-                            strLenRemoveTrailingWhiteSpace(pDevResult->inq_data.product,
-                                                            sizeof(pDevResult->inq_data.product));
-
-                            dvdCreateDeviceString(pDevResult->inq_data.vendor,
-                                                    pDevResult->inq_data.product,
-                                                    szDesc, sizeof(szDesc));
-
-                            pList->push_back(DriveInfo(szPath, "", szDesc));
+                            char szDesc[256] = { 0 };
+                            switch (pDevResult->protocol)
+                            {
+                                case PROTO_SCSI:  strDeviceStringSCSI( pDevResult, szDesc, sizeof(szDesc)); break;
+                                case PROTO_ATA:   strDeviceStringATA(  pDevResult, szDesc, sizeof(szDesc)); break;
+                                case PROTO_MMCSD: strDeviceStringMMCSD(pDevResult, szDesc, sizeof(szDesc)); break;
+                                case PROTO_SEMB:  strDeviceStringSEMB( pDevResult, szDesc, sizeof(szDesc)); break;
+                                case PROTO_NVME:  strDeviceStringNVME( pDevResult, szDesc, sizeof(szDesc)); break;
+                                default: break;
+                            }
+
+                            try
+                            {
+                                pList->push_back(DriveInfo(szPath, "", szDesc));
+                            }
+                            catch (std::bad_alloc &)
+                            {
+                                pList->clear();
+                                rc = VERR_NO_MEMORY;
+                                break;
+                            }
                             if (pfSuccess)
                                 *pfSuccess = true;
@@ -312,6 +477,7 @@
                     }
                 }
-            } while (   (DeviceCCB.ccb_h.status == CAM_REQ_CMP)
-                     && (DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE));
+            } while (   DeviceCCB.ccb_h.status == CAM_REQ_CMP
+                     && DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE
+                     && RT_SUCCESS(rc));
 
             RTMemFree(paMatches);
@@ -320,13 +486,15 @@
             rc = VERR_NO_MEMORY;
 
-        RTFileClose(FileXpt);
+        RTFileClose(hFileXpt);
     }
 
     return rc;
 }
+
 
 /**
  * Extract the names of drives from an environment variable and add them to a
  * list if they are valid.
+ *
  * @returns iprt status code
  * @param   pcszVar     the name of the environment variable.  The variable
@@ -337,13 +505,13 @@
  * @param   pfSuccess  this will be set to true if we found at least one drive
  *                     and to false otherwise.  Optional.
+ *
+ * @note    This is duplicated in HostHardwareLinux.cpp.
  */
-static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
-                               bool isDVD, bool *pfSuccess)
+static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_DEF
 {
     AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
     AssertPtrReturn(pList, VERR_INVALID_POINTER);
     AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
-    LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar,
-                 pList, isDVD, pfSuccess));
+    LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar, pList, isDVD, pfSuccess));
     int rc = VINF_SUCCESS;
     bool success = false;
@@ -352,28 +520,25 @@
     try
     {
-        const char *pcszCurrent = pszFreeMe;
-        while (pcszCurrent && *pcszCurrent != '\0')
+        char *pszCurrent = pszFreeMe;
+        while (pszCurrent && *pszCurrent != '\0')
         {
-            const char *pcszNext = strchr(pcszCurrent, ':');
-            char szPath[RTPATH_MAX], szReal[RTPATH_MAX];
-            char szDesc[256], szUdi[256];
-            if (pcszNext)
-                RTStrPrintf(szPath, sizeof(szPath), "%.*s",
-                            pcszNext - pcszCurrent - 1, pcszCurrent);
-            else
-                RTStrPrintf(szPath, sizeof(szPath), "%s", pcszCurrent);
-            if (RT_SUCCESS(RTPathReal(szPath, szReal, sizeof(szReal))))
+            char *pszNext = strchr(pszCurrent, ':');
+            if (pszNext)
+                *pszNext++ = '\0';
+
+            char szReal[RTPATH_MAX];
+            char szDesc[1] = "", szUdi[1] = ""; /* differs on freebsd because no devValidateDevice */
+            if (   RT_SUCCESS(RTPathReal(pszCurrent, szReal, sizeof(szReal)))
+                /*&& devValidateDevice(szReal, isDVD, NULL, szDesc, sizeof(szDesc), szUdi, sizeof(szUdi)) - linux only */)
             {
-                szUdi[0] = '\0'; /** @todo r=bird: missing a call to devValidateDevice() here and szUdi wasn't
-                                  *        initialized because of that.  Need proper fixing. */
                 pList->push_back(DriveInfo(szReal, szUdi, szDesc));
                 success = true;
             }
-            pcszCurrent = pcszNext ? pcszNext + 1 : NULL;
+            pszCurrent = pszNext;
         }
         if (pfSuccess != NULL)
             *pfSuccess = success;
     }
-    catch(std::bad_alloc &e)
+    catch (std::bad_alloc &)
     {
         rc = VERR_NO_MEMORY;
@@ -383,2 +548,3 @@
     return rc;
 }
+
Index: /trunk/src/VBox/Main/src-server/linux/HostHardwareLinux.cpp
===================================================================
--- /trunk/src/VBox/Main/src-server/linux/HostHardwareLinux.cpp	(revision 85928)
+++ /trunk/src/VBox/Main/src-server/linux/HostHardwareLinux.cpp	(revision 85929)
@@ -1,7 +1,5 @@
 /* $Id$ */
 /** @file
- * Classes for handling hardware detection under Linux.  Please feel free to
- * expand these to work for other systems (Solaris!) or to add new ones for
- * other systems.
+ * VirtualBox Main - Code for handling hardware detection under Linux, VBoxSVC.
  */
 
@@ -18,13 +16,10 @@
  */
 
-#define LOG_GROUP LOG_GROUP_MAIN
-
 
 /*********************************************************************************************************************************
 *   Header Files                                                                                                                 *
 *********************************************************************************************************************************/
-
-#include <HostHardwareLinux.h>
-#include <vector.h>
+#define LOG_GROUP LOG_GROUP_MAIN
+#include "HostHardwareLinux.h"
 
 #include <VBox/err.h>
@@ -71,5 +66,4 @@
 *   Global Variables                                                                                                             *
 *********************************************************************************************************************************/
-
 #ifdef TESTCASE
 static bool testing() { return true; }
@@ -87,17 +81,27 @@
 *   Typedefs and Defines                                                                                                         *
 *********************************************************************************************************************************/
-
-static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
-                               bool isDVD, bool *pfSuccess);
-static int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD,
-                                 bool *pfSuccess);
+typedef enum SysfsWantDevice_T
+{
+    DVD,
+    Floppy,
+    FixedDisk
+} SysfsWantDevice_T;
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_PROTO;
+static int getDriveInfoFromSysfs(DriveInfoList *pList, SysfsWantDevice_T wantDevice, bool *pfSuccess) RT_NOTHROW_PROTO;
+
 
 /** Find the length of a string, ignoring trailing non-ascii or control
- * characters */
-static size_t strLenStripped(const char *pcsz)
+ * characters
+ * @note Code duplicated in HostHardwareFreeBSD.cpp  */
+static size_t strLenStripped(const char *pcsz) RT_NOTHROW_DEF
 {
     size_t cch = 0;
     for (size_t i = 0; pcsz[i] != '\0'; ++i)
-        if (pcsz[i] > 32 && pcsz[i] < 127)
+        if (pcsz[i] > 32 /*space*/ && pcsz[i] < 127 /*delete*/)
             cch = i;
     return cch + 1;
@@ -114,6 +118,5 @@
  * @param  pszName   where to store the name retrieved
  */
-static bool floppyGetName(const char *pcszNode, unsigned Number,
-                          floppy_drive_name pszName)
+static bool floppyGetName(const char *pcszNode, unsigned Number, floppy_drive_name pszName) RT_NOTHROW_DEF
 {
     AssertPtrReturn(pcszNode, false);
@@ -145,18 +148,16 @@
  *                       FDC 1)
  * @param   pszDesc      where to store the device description (optional)
- * @param   cchDesc      the size of the buffer in @a pszDesc
+ * @param   cbDesc       the size of the buffer in @a pszDesc
  * @param   pszUdi       where to store the device UDI (optional)
- * @param   cchUdi       the size of the buffer in @a pszUdi
+ * @param   cbUdi        the size of the buffer in @a pszUdi
  */
-static void floppyCreateDeviceStrings(const floppy_drive_name pcszName,
-                                      unsigned Number, char *pszDesc,
-                                      size_t cchDesc, char *pszUdi,
-                                      size_t cchUdi)
+static void floppyCreateDeviceStrings(const floppy_drive_name pcszName, unsigned Number,
+                                      char *pszDesc, size_t cbDesc, char *pszUdi, size_t cbUdi) RT_NOTHROW_DEF
 {
     AssertPtrNullReturnVoid(pcszName);
     AssertPtrNullReturnVoid(pszDesc);
-    AssertReturnVoid(!pszDesc || cchDesc > 0);
+    AssertReturnVoid(!pszDesc || cbDesc > 0);
     AssertPtrNullReturnVoid(pszUdi);
-    AssertReturnVoid(!pszUdi || cchUdi > 0);
+    AssertReturnVoid(!pszUdi || cbUdi > 0);
     AssertReturnVoid(Number <= 7);
     if (pcszName)
@@ -175,5 +176,5 @@
         }
         if (pszDesc)
-            RTStrPrintf(pszDesc, cchDesc, "%s %s K%s", pcszSize, &pcszName[1],
+            RTStrPrintf(pszDesc, cbDesc, "%s %s K%s", pcszSize, &pcszName[1],
                         Number > 3 ? ", FDC 2" : "");
     }
@@ -181,9 +182,9 @@
     {
         if (pszDesc)
-            RTStrPrintf(pszDesc, cchDesc, "FDD %d%s", (Number & 4) + 1,
+            RTStrPrintf(pszDesc, cbDesc, "FDD %d%s", (Number & 4) + 1,
                         Number > 3 ? ", FDC 2" : "");
     }
     if (pszUdi)
-        RTStrPrintf(pszUdi, cchUdi,
+        RTStrPrintf(pszUdi, cbUdi,
                     "/org/freedesktop/Hal/devices/platform_floppy_%u_storage",
                     Number);
@@ -194,12 +195,13 @@
  * Check whether a device number might correspond to a CD-ROM device according
  * to Documentation/devices.txt in the Linux kernel source.
+ *
  * @returns true if it might, false otherwise
  * @param   Number  the device number (major and minor combination)
  */
-static bool isCdromDevNum(dev_t Number)
+static bool isCdromDevNum(dev_t Number) RT_NOTHROW_DEF
 {
     int major = major(Number);
     int minor = minor(Number);
-    if ((major == IDE0_MAJOR) && !(minor & 0x3f))
+    if (major == IDE0_MAJOR && !(minor & 0x3f))
         return true;
     if (major == SCSI_CDROM_MAJOR)
@@ -215,5 +217,5 @@
     if (major == MITSUMI_X_CDROM_MAJOR)
         return true;
-    if ((major == IDE1_MAJOR) && !(minor & 0x3f))
+    if (major == IDE1_MAJOR && !(minor & 0x3f))
         return true;
     if (major == MITSUMI_CDROM_MAJOR)
@@ -235,19 +237,19 @@
     if (major == CM206_CDROM_MAJOR)
         return true;
-    if ((major == IDE3_MAJOR) && !(minor & 0x3f))
+    if (major == IDE3_MAJOR && !(minor & 0x3f))
         return true;
     if (major == 46 /* Parallel port ATAPI CD-ROM */)  /* no #define */
         return true;
-    if ((major == IDE4_MAJOR) && !(minor & 0x3f))
-        return true;
-    if ((major == IDE5_MAJOR) && !(minor & 0x3f))
-        return true;
-    if ((major == IDE6_MAJOR) && !(minor & 0x3f))
-        return true;
-    if ((major == IDE7_MAJOR) && !(minor & 0x3f))
-        return true;
-    if ((major == IDE8_MAJOR) && !(minor & 0x3f))
-        return true;
-    if ((major == IDE9_MAJOR) && !(minor & 0x3f))
+    if (major == IDE4_MAJOR && !(minor & 0x3f))
+        return true;
+    if (major == IDE5_MAJOR && !(minor & 0x3f))
+        return true;
+    if (major == IDE6_MAJOR && !(minor & 0x3f))
+        return true;
+    if (major == IDE7_MAJOR && !(minor & 0x3f))
+        return true;
+    if (major == IDE8_MAJOR && !(minor & 0x3f))
+        return true;
+    if (major == IDE9_MAJOR && !(minor & 0x3f))
         return true;
     if (major == 113 /* VIOCD_MAJOR */)
@@ -259,40 +261,39 @@
 /**
  * Send an SCSI INQUIRY command to a device and return selected information.
+ *
  * @returns  iprt status code
- * @returns  VERR_TRY_AGAIN if the query failed but might succeed next time
+ * @retval   VERR_TRY_AGAIN if the query failed but might succeed next time
  * @param pcszNode    the full path to the device node
- * @param pu8Type    where to store the SCSI device type on success (optional)
- * @param pchVendor  where to store the vendor id string on success (optional)
- * @param cchVendor  the size of the @a pchVendor buffer
- * @param pchModel   where to store the product id string on success (optional)
- * @param cchModel   the size of the @a pchModel buffer
+ * @param pbType     where to store the SCSI device type on success (optional)
+ * @param pszVendor  where to store the vendor id string on success (optional)
+ * @param cbVendor   the size of the @a pszVendor buffer
+ * @param pszModel   where to store the product id string on success (optional)
+ * @param cchModel   the size of the @a pszModel buffer
  * @note check documentation on the SCSI INQUIRY command and the Linux kernel
  *       SCSI headers included above if you want to understand what is going
  *       on in this method.
  */
-static int cdromDoInquiry(const char *pcszNode, uint8_t *pu8Type,
-                          char *pchVendor, size_t cchVendor, char *pchModel,
-                          size_t cchModel)
-{
-    LogRelFlowFunc(("pcszNode=%s, pu8Type=%p, pchVendor=%p, cchVendor=%llu, pchModel=%p, cchModel=%llu\n",
-                    pcszNode, pu8Type, pchVendor, cchVendor, pchModel,
-                    cchModel));
+static int cdromDoInquiry(const char *pcszNode, uint8_t *pbType, char *pszVendor, size_t cbVendor,
+                          char *pszModel, size_t cbModel) RT_NOTHROW_DEF
+{
+    LogRelFlowFunc(("pcszNode=%s, pbType=%p, pszVendor=%p, cbVendor=%zu, pszModel=%p, cbModel=%zu\n",
+                    pcszNode, pbType, pszVendor, cbVendor, pszModel, cbModel));
     AssertPtrReturn(pcszNode, VERR_INVALID_POINTER);
-    AssertPtrNullReturn(pu8Type, VERR_INVALID_POINTER);
-    AssertPtrNullReturn(pchVendor, VERR_INVALID_POINTER);
-    AssertPtrNullReturn(pchModel, VERR_INVALID_POINTER);
-
-    RTFILE hFile;
+    AssertPtrNullReturn(pbType, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pszVendor, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pszModel, VERR_INVALID_POINTER);
+
+    RTFILE hFile = NIL_RTFILE;
     int rc = RTFileOpen(&hFile, pcszNode, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_NON_BLOCK);
     if (RT_SUCCESS(rc))
     {
-        int                             rcIoCtl        = 0;
-        unsigned char                   u8Response[96] = { 0 };
+        int                             rcIoCtl          = 0;
+        unsigned char                   auchResponse[96] = { 0 };
         struct cdrom_generic_command    CdromCommandReq;
         RT_ZERO(CdromCommandReq);
         CdromCommandReq.cmd[0]         = INQUIRY;
-        CdromCommandReq.cmd[4]         = sizeof(u8Response);
-        CdromCommandReq.buffer         = u8Response;
-        CdromCommandReq.buflen         = sizeof(u8Response);
+        CdromCommandReq.cmd[4]         = sizeof(auchResponse);
+        CdromCommandReq.buffer         = auchResponse;
+        CdromCommandReq.buflen         = sizeof(auchResponse);
         CdromCommandReq.data_direction = CGC_DATA_READ;
         CdromCommandReq.timeout        = 5000;  /* ms */
@@ -304,14 +305,18 @@
         if (RT_SUCCESS(rc))
         {
-            if (pu8Type)
-                *pu8Type = u8Response[0] & 0x1f;
-            if (pchVendor)
-                RTStrPrintf(pchVendor, cchVendor, "%.8s",
-                            &u8Response[8] /* vendor id string */);
-            if (pchModel)
-                RTStrPrintf(pchModel, cchModel, "%.16s",
-                            &u8Response[16] /* product id string */);
+            if (pbType)
+                *pbType = auchResponse[0] & 0x1f;
+            if (pszVendor)
+            {
+                RTStrPrintf(pszVendor, cbVendor, "%.8s", &auchResponse[8] /* vendor id string */);
+                RTStrPurgeEncoding(pszVendor);
+            }
+            if (pszModel)
+            {
+                RTStrPrintf(pszModel, cbModel, "%.16s", &auchResponse[16] /* product id string */);
+                RTStrPurgeEncoding(pszModel);
+            }
             LogRelFlowFunc(("returning success: type=%u, vendor=%.8s, product=%.16s\n",
-                            u8Response[0] & 0x1f, &u8Response[8], &u8Response[16]));
+                            auchResponse[0] & 0x1f, &auchResponse[8], &auchResponse[16]));
             return VINF_SUCCESS;
         }
@@ -328,24 +333,25 @@
  * @param pcszModel   the product ID string
  * @param pszDesc    where to store the description string (optional)
- * @param cchDesc    the size of the buffer in @a pszDesc
+ * @param cbDesc     the size of the buffer in @a pszDesc
  * @param pszUdi     where to store the UDI string (optional)
- * @param cchUdi     the size of the buffer in @a pszUdi
+ * @param cbUdi      the size of the buffer in @a pszUdi
+ *
+ * @note  Used for more than DVDs these days.
  */
-/* static */
-void dvdCreateDeviceStrings(const char *pcszVendor, const char *pcszModel,
-                            char *pszDesc, size_t cchDesc, char *pszUdi,
-                            size_t cchUdi)
+static void dvdCreateDeviceStrings(const char *pcszVendor, const char *pcszModel,
+                                   char *pszDesc, size_t cbDesc, char *pszUdi, size_t cbUdi) RT_NOEXCEPT
 {
     AssertPtrReturnVoid(pcszVendor);
     AssertPtrReturnVoid(pcszModel);
     AssertPtrNullReturnVoid(pszDesc);
-    AssertReturnVoid(!pszDesc || cchDesc > 0);
+    AssertReturnVoid(!pszDesc || cbDesc > 0);
     AssertPtrNullReturnVoid(pszUdi);
-    AssertReturnVoid(!pszUdi || cchUdi > 0);
-    char szCleaned[128];
+    AssertReturnVoid(!pszUdi || cbUdi > 0);
+
     size_t cchVendor = strLenStripped(pcszVendor);
     size_t cchModel = strLenStripped(pcszModel);
 
     /* Create a cleaned version of the model string for the UDI string. */
+    char szCleaned[128];
     for (unsigned i = 0; i < sizeof(szCleaned) && pcszModel[i] != '\0'; ++i)
         if (   (pcszModel[i] >= '0' && pcszModel[i] <= '9')
@@ -360,8 +366,10 @@
     {
         if (cchVendor > 0)
-            RTStrPrintf(pszDesc, cchDesc, "%.*s %s", cchVendor, pcszVendor,
-                        cchModel > 0 ? pcszModel : "(unknown drive model)");
+        {
+            RTStrPrintf(pszDesc, cbDesc, "%.*s %s", cchVendor, pcszVendor, cchModel > 0 ? pcszModel : "(unknown drive model)");
+            RTStrPurgeEncoding(pszDesc);
+        }
         else
-            RTStrPrintf(pszDesc, cchDesc, "%s", pcszModel);
+            RTStrCopy(pszDesc, cbDesc, pcszModel);
     }
     /* Construct the UDI string */
@@ -369,7 +377,5 @@
     {
         if (cchModel > 0)
-            RTStrPrintf(pszUdi, cchUdi,
-                        "/org/freedesktop/Hal/devices/storage_model_%s",
-                        szCleaned);
+            RTStrPrintf(pszUdi, cbUdi, "/org/freedesktop/Hal/devices/storage_model_%s", szCleaned);
         else
             pszUdi[0] = '\0';
@@ -381,4 +387,5 @@
  * Check whether a device node points to a valid device and create a UDI and
  * a description for it, and store the device number, if it does.
+ *
  * @returns true if the device is valid, false otherwise
  * @param   pcszNode   the path to the device node
@@ -386,18 +393,18 @@
  * @param   pDevice   where to store the device node (optional)
  * @param   pszDesc   where to store the device description (optional)
- * @param   cchDesc   the size of the buffer in @a pszDesc
+ * @param   cbDesc    the size of the buffer in @a pszDesc
  * @param   pszUdi    where to store the device UDI (optional)
- * @param   cchUdi    the size of the buffer in @a pszUdi
+ * @param   cbUdi     the size of the buffer in @a pszUdi
  */
 static bool devValidateDevice(const char *pcszNode, bool isDVD, dev_t *pDevice,
-                              char *pszDesc, size_t cchDesc, char *pszUdi,
-                              size_t cchUdi)
+                              char *pszDesc, size_t cbDesc, char *pszUdi, size_t cbUdi) RT_NOTHROW_DEF
 {
     AssertPtrReturn(pcszNode, false);
     AssertPtrNullReturn(pDevice, false);
     AssertPtrNullReturn(pszDesc, false);
-    AssertReturn(!pszDesc || cchDesc > 0, false);
+    AssertReturn(!pszDesc || cbDesc > 0, false);
     AssertPtrNullReturn(pszUdi, false);
-    AssertReturn(!pszUdi || cchUdi > 0, false);
+    AssertReturn(!pszUdi || cbUdi > 0, false);
+
     RTFSOBJINFO ObjInfo;
     if (RT_FAILURE(RTPathQueryInfo(pcszNode, &ObjInfo, RTFSOBJATTRADD_UNIX)))
@@ -407,4 +414,5 @@
     if (pDevice)
         *pDevice = ObjInfo.Attr.u.Unix.Device;
+
     if (isDVD)
     {
@@ -419,15 +427,13 @@
         if (u8Type != TYPE_ROM)
             return false;
-        dvdCreateDeviceStrings(szVendor, szModel, pszDesc, cchDesc,
-                               pszUdi, cchUdi);
+        dvdCreateDeviceStrings(szVendor, szModel, pszDesc, cbDesc, pszUdi, cbUdi);
     }
     else
     {
-        /* Floppies on Linux are legacy devices with hardcoded majors and
-         * minors */
-        unsigned Number;
-        floppy_drive_name szName;
+        /* Floppies on Linux are legacy devices with hardcoded majors and minors */
         if (major(ObjInfo.Attr.u.Unix.Device) != FLOPPY_MAJOR)
             return false;
+
+        unsigned Number;
         switch (minor(ObjInfo.Attr.u.Unix.Device))
         {
@@ -441,8 +447,9 @@
                 return false;
         }
+
+        floppy_drive_name szName;
         if (!floppyGetName(pcszNode, Number, szName))
             return false;
-        floppyCreateDeviceStrings(szName, Number, pszDesc, cchDesc, pszUdi,
-                                  cchUdi);
+        floppyCreateDeviceStrings(szName, Number, pszDesc, cbDesc, pszUdi, cbUdi);
     }
     return true;
@@ -450,27 +457,25 @@
 
 
-int VBoxMainDriveInfo::updateDVDs ()
+int VBoxMainDriveInfo::updateDVDs() RT_NOEXCEPT
 {
     LogFlowThisFunc(("entered\n"));
-    int rc = VINF_SUCCESS;
-    bool success = false;  /* Have we succeeded in finding anything yet? */
+    int rc;
     try
     {
-        mDVDList.clear ();
+        mDVDList.clear();
         /* Always allow the user to override our auto-detection using an
          * environment variable. */
-        if (RT_SUCCESS(rc) && (!success || testing()))
-            rc = getDriveInfoFromEnv ("VBOX_CDROM", &mDVDList, true /* isDVD */,
-                                      &success);
+        bool fSuccess = false;  /* Have we succeeded in finding anything yet? */
+        rc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */, &fSuccess);
         setNoProbe(false);
-        if (RT_SUCCESS(rc) && (!success || testing()))
-            rc = getDriveInfoFromSysfs(&mDVDList, true /* isDVD */, &success);
+        if (RT_SUCCESS(rc) && (!fSuccess || testing()))
+            rc = getDriveInfoFromSysfs(&mDVDList, DVD, &fSuccess);
         if (RT_SUCCESS(rc) && testing())
         {
             setNoProbe(true);
-            rc = getDriveInfoFromSysfs(&mDVDList, true /* isDVD */, &success);
-        }
-    }
-    catch(std::bad_alloc &e)
+            rc = getDriveInfoFromSysfs(&mDVDList, DVD, &fSuccess);
+        }
+    }
+    catch (std::bad_alloc &e)
     {
         rc = VERR_NO_MEMORY;
@@ -480,26 +485,23 @@
 }
 
-int VBoxMainDriveInfo::updateFloppies ()
+int VBoxMainDriveInfo::updateFloppies() RT_NOEXCEPT
 {
     LogFlowThisFunc(("entered\n"));
-    int rc = VINF_SUCCESS;
-    bool success = false;  /* Have we succeeded in finding anything yet? */
+    int rc;
     try
     {
-        mFloppyList.clear ();
-        if (RT_SUCCESS(rc) && (!success || testing()))
-            rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList,
-                                     false /* isDVD */, &success);
+        mFloppyList.clear();
+        bool fSuccess = false;  /* Have we succeeded in finding anything yet? */
+        rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */, &fSuccess);
         setNoProbe(false);
-        if (   RT_SUCCESS(rc) && (!success || testing()))
-            rc = getDriveInfoFromSysfs(&mFloppyList, false /* isDVD */,
-                                       &success);
+        if (RT_SUCCESS(rc) && (!fSuccess || testing()))
+            rc = getDriveInfoFromSysfs(&mFloppyList, Floppy, &fSuccess);
         if (RT_SUCCESS(rc) && testing())
         {
             setNoProbe(true);
-            rc = getDriveInfoFromSysfs(&mFloppyList, false /* isDVD */, &success);
-        }
-    }
-    catch(std::bad_alloc &e)
+            rc = getDriveInfoFromSysfs(&mFloppyList, Floppy, &fSuccess);
+        }
+    }
+    catch (std::bad_alloc &)
     {
         rc = VERR_NO_MEMORY;
@@ -507,4 +509,28 @@
     LogFlowThisFunc(("rc=%Rrc\n", rc));
     return rc;
+}
+
+int VBoxMainDriveInfo::updateFixedDrives() RT_NOEXCEPT
+{
+    LogFlowThisFunc(("entered\n"));
+    int vrc;
+    try
+    {
+        mFixedDriveList.clear();
+        setNoProbe(false);
+        bool fSuccess = false;  /* Have we succeeded in finding anything yet? */
+        vrc = getDriveInfoFromSysfs(&mFixedDriveList, FixedDisk, &fSuccess);
+        if (RT_SUCCESS(vrc) && testing())
+        {
+            setNoProbe(true);
+            vrc = getDriveInfoFromSysfs(&mFixedDriveList, FixedDisk, &fSuccess);
+        }
+    }
+    catch (std::bad_alloc &)
+    {
+        vrc = VERR_NO_MEMORY;
+    }
+    LogFlowThisFunc(("vrc=%Rrc\n", vrc));
+    return vrc;
 }
 
@@ -513,4 +539,5 @@
  * Extract the names of drives from an environment variable and add them to a
  * list if they are valid.
+ *
  * @returns iprt status code
  * @param   pcszVar     the name of the environment variable.  The variable
@@ -521,14 +548,13 @@
  * @param   pfSuccess  this will be set to true if we found at least one drive
  *                     and to false otherwise.  Optional.
+ *
+ * @note    This is duplicated in HostHardwareFreeBSD.cpp.
  */
-/* static */
-int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
-                        bool isDVD, bool *pfSuccess)
+static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_DEF
 {
     AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
     AssertPtrReturn(pList, VERR_INVALID_POINTER);
     AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
-    LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar,
-                 pList, isDVD, pfSuccess));
+    LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar, pList, isDVD, pfSuccess));
     int rc = VINF_SUCCESS;
     bool success = false;
@@ -537,28 +563,25 @@
     try
     {
-        const char *pcszCurrent = pszFreeMe;
-        while (pcszCurrent && *pcszCurrent != '\0')
-        {
-            const char *pcszNext = strchr(pcszCurrent, ':');
-            char szPath[RTPATH_MAX], szReal[RTPATH_MAX];
+        char *pszCurrent = pszFreeMe;
+        while (pszCurrent && *pszCurrent != '\0')
+        {
+            char *pszNext = strchr(pszCurrent, ':');
+            if (pszNext)
+                *pszNext++ = '\0';
+
+            char szReal[RTPATH_MAX];
             char szDesc[256], szUdi[256];
-            if (pcszNext)
-                RTStrPrintf(szPath, sizeof(szPath), "%.*s",
-                            pcszNext - pcszCurrent - 1, pcszCurrent);
-            else
-                RTStrPrintf(szPath, sizeof(szPath), "%s", pcszCurrent);
-            if (   RT_SUCCESS(RTPathReal(szPath, szReal, sizeof(szReal)))
-                && devValidateDevice(szReal, isDVD, NULL, szDesc,
-                                     sizeof(szDesc), szUdi, sizeof(szUdi)))
+            if (   RT_SUCCESS(RTPathReal(pszCurrent, szReal, sizeof(szReal)))
+                && devValidateDevice(szReal, isDVD, NULL, szDesc, sizeof(szDesc), szUdi, sizeof(szUdi)))
             {
                 pList->push_back(DriveInfo(szReal, szUdi, szDesc));
                 success = true;
             }
-            pcszCurrent = pcszNext ? pcszNext + 1 : NULL;
+            pszCurrent = pszNext;
         }
         if (pfSuccess != NULL)
             *pfSuccess = success;
     }
-    catch(std::bad_alloc &e)
+    catch (std::bad_alloc &)
     {
         rc = VERR_NO_MEMORY;
@@ -570,17 +593,18 @@
 
 
-class sysfsBlockDev
+class SysfsBlockDev
 {
 public:
-    sysfsBlockDev(const char *pcszName, bool wantDVD)
-            : mpcszName(pcszName), mwantDVD(wantDVD), misConsistent(true),
-              misValid(false)
+    SysfsBlockDev(const char *pcszName, SysfsWantDevice_T wantDevice) RT_NOEXCEPT
+        : mpcszName(pcszName), mWantDevice(wantDevice), misConsistent(true), misValid(false)
     {
         if (findDeviceNode())
         {
-            if (mwantDVD)
-                validateAndInitForDVD();
-            else
-                validateAndInitForFloppy();
+            switch (mWantDevice)
+            {
+                case DVD:    validateAndInitForDVD(); break;
+                case Floppy: validateAndInitForFloppy(); break;
+                default:     validateAndInitForFixedDisk(); break;
+            }
         }
     }
@@ -588,6 +612,6 @@
     /** The name of the subdirectory of /sys/block for this device */
     const char *mpcszName;
-    /** Are we looking for a floppy or a DVD device? */
-    bool mwantDVD;
+    /** Are we looking for a floppy, a DVD or a fixed disk device? */
+    SysfsWantDevice_T mWantDevice;
     /** The device node for the device */
     char mszNode[RTPATH_MAX];
@@ -609,5 +633,5 @@
      * @returns boolean success value
      */
-    bool findDeviceNode()
+    bool findDeviceNode() RT_NOEXCEPT
     {
         dev_t dev = 0;
@@ -618,9 +642,6 @@
             return false;
         }
-        rc = RTLinuxCheckDevicePath(dev, RTFS_TYPE_DEV_BLOCK, mszNode,
-                                    sizeof(mszNode), "%s", mpcszName);
-        if (RT_FAILURE(rc))
-            return false;
-        return true;
+        rc = RTLinuxCheckDevicePath(dev, RTFS_TYPE_DEV_BLOCK, mszNode, sizeof(mszNode), "%s", mpcszName);
+        return RT_SUCCESS(rc);
     }
 
@@ -630,7 +651,6 @@
      * poking the device, and if that fails we fall back to an SCSI INQUIRY
      * command. */
-    void validateAndInitForDVD()
-    {
-        char szVendor[128], szModel[128];
+    void validateAndInitForDVD() RT_NOEXCEPT
+    {
         int64_t type = 0;
         int rc = RTLinuxSysFsReadIntFile(10, &type, "block/%s/device/type", mpcszName);
@@ -639,16 +659,14 @@
         if (type == TYPE_ROM)
         {
-            rc = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor), NULL,
-                                         "block/%s/device/vendor", mpcszName);
+            char szVendor[128];
+            rc = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor), NULL, "block/%s/device/vendor", mpcszName);
             if (RT_SUCCESS(rc))
             {
-                rc = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel), NULL,
-                                             "block/%s/device/model", mpcszName);
+                char szModel[128];
+                rc = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel), NULL, "block/%s/device/model", mpcszName);
                 if (RT_SUCCESS(rc))
                 {
                     misValid = true;
-                    dvdCreateDeviceStrings(szVendor, szModel,
-                                           mszDesc, sizeof(mszDesc),
-                                           mszUdi, sizeof(mszUdi));
+                    dvdCreateDeviceStrings(szVendor, szModel, mszDesc, sizeof(mszDesc), mszUdi, sizeof(mszUdi));
                     return;
                 }
@@ -663,18 +681,15 @@
      * data members for the object based on the returned data.
      */
-    void probeAndInitForDVD()
+    void probeAndInitForDVD() RT_NOEXCEPT
     {
         AssertReturnVoid(mszNode[0] != '\0');
-        uint8_t u8Type = 0;
+        uint8_t bType = 0;
         char szVendor[128] = "";
         char szModel[128] = "";
-        int rc = cdromDoInquiry(mszNode, &u8Type, szVendor,
-                                sizeof(szVendor), szModel,
-                                sizeof(szModel));
-        if (RT_SUCCESS(rc) && (u8Type == TYPE_ROM))
+        int rc = cdromDoInquiry(mszNode, &bType, szVendor, sizeof(szVendor), szModel, sizeof(szModel));
+        if (RT_SUCCESS(rc) && bType == TYPE_ROM)
         {
             misValid = true;
-            dvdCreateDeviceStrings(szVendor, szModel, mszDesc, sizeof(mszDesc),
-                                   mszUdi, sizeof(mszUdi));
+            dvdCreateDeviceStrings(szVendor, szModel, mszDesc, sizeof(mszDesc), mszUdi, sizeof(mszUdi));
         }
     }
@@ -684,7 +699,6 @@
      * support floppies using the basic "floppy" driver, we check the driver
      * using the entry name and a driver-specific ioctl. */
-    void validateAndInitForFloppy()
-    {
-        bool haveName = false;
+    void validateAndInitForFloppy() RT_NOEXCEPT
+    {
         floppy_drive_name szName;
         char szDriver[8];
@@ -695,6 +709,7 @@
             || mpcszName[3] != '\0')
             return;
+        bool fHaveName = false;
         if (!noProbe())
-            haveName = floppyGetName(mszNode, mpcszName[2] - '0', szName);
+            fHaveName = floppyGetName(mszNode, mpcszName[2] - '0', szName);
         int rc = RTLinuxSysFsGetLinkDest(szDriver, sizeof(szDriver), NULL, "block/%s/%s",
                                          mpcszName, "device/driver");
@@ -704,7 +719,7 @@
                 return;
         }
-        else if (!haveName)
+        else if (!fHaveName)
             return;
-        floppyCreateDeviceStrings(haveName ? szName : NULL,
+        floppyCreateDeviceStrings(fHaveName ? szName : NULL,
                                   mpcszName[2] - '0', mszDesc,
                                   sizeof(mszDesc), mszUdi, sizeof(mszUdi));
@@ -712,22 +727,48 @@
     }
 
+    void validateAndInitForFixedDisk() RT_NOEXCEPT
+    {
+        /*
+         * For current task only device path is needed. Therefore, device probing
+         * is skipped and other fields are empty if there aren't files in the
+         * device entry.
+         */
+        int64_t type = 0;
+        int rc = RTLinuxSysFsReadIntFile(10, &type, "block/%s/device/type", mpcszName);
+        if (RT_SUCCESS(rc) && type != TYPE_DISK)
+            return;
+        char szVendor[128];
+        rc = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor), NULL, "block/%s/device/vendor", mpcszName);
+        if (RT_SUCCESS(rc))
+        {
+            char szModel[128];
+            rc = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel), NULL, "block/%s/device/model", mpcszName);
+            if (RT_SUCCESS(rc))
+            {
+                misValid = true;
+                dvdCreateDeviceStrings(szVendor, szModel, mszDesc, sizeof(mszDesc), mszUdi, sizeof(mszUdi));
+                return;
+            }
+        }
+    }
+
 public:
-    bool isConsistent()
+    bool isConsistent() const RT_NOEXCEPT
     {
         return misConsistent;
     }
-    bool isValid()
+    bool isValid() const RT_NOEXCEPT
     {
         return misValid;
     }
-    const char *getDesc()
+    const char *getDesc() const RT_NOEXCEPT
     {
         return mszDesc;
     }
-    const char *getUdi()
+    const char *getUdi() const RT_NOEXCEPT
     {
         return mszUdi;
     }
-    const char *getNode()
+    const char *getNode() const RT_NOEXCEPT
     {
         return mszNode;
@@ -739,28 +780,26 @@
  * drives attached to the system.
  * @returns iprt status code
- * @param   pList      where to add information about the drives detected
- * @param   isDVD      are we looking for DVDs or floppies?
- * @param   pfSuccess  Did we find anything?
+ * @param   pList       where to add information about the drives detected
+ * @param   wantDevice  The kind of devices we're looking for.
+ * @param   pfSuccess   Did we find anything?
  *
  * @returns IPRT status code
+ * @throws  Nothing.
  */
-/* static */
-int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
+static int getDriveInfoFromSysfs(DriveInfoList *pList, SysfsWantDevice_T wantDevice, bool *pfSuccess) RT_NOTHROW_DEF
 {
     AssertPtrReturn(pList, VERR_INVALID_POINTER);
     AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER); /* Valid or Null */
-    LogFlowFunc (("pList=%p, isDVD=%u, pfSuccess=%p\n",
-                  pList, (unsigned) isDVD, pfSuccess));
-    RTDIR hDir;
-    int rc;
-    bool fSuccess = false;
-    unsigned cFound = 0;
-
+    LogFlowFunc (("pList=%p, wantDevice=%u, pfSuccess=%p\n",
+                  pList, (unsigned)wantDevice, pfSuccess));
     if (!RTPathExists("/sys"))
         return VINF_SUCCESS;
-    rc = RTDirOpen(&hDir, "/sys/block");
+
+    bool fSuccess = true;
+    unsigned cFound = 0;
+    RTDIR hDir = NIL_RTDIR;
+    int rc = RTDirOpen(&hDir, "/sys/block");
     /* This might mean that sysfs semantics have changed */
     AssertReturn(rc != VERR_FILE_NOT_FOUND, VINF_SUCCESS);
-    fSuccess = true;
     if (RT_SUCCESS(rc))
     {
@@ -774,5 +813,5 @@
             if (entry.szName[0] == '.')
                 continue;
-            sysfsBlockDev dev(entry.szName, isDVD);
+            SysfsBlockDev dev(entry.szName, wantDevice);
             /* This might mean that sysfs semantics have changed */
             AssertBreakStmt(dev.isConsistent(), fSuccess = false);
@@ -783,5 +822,5 @@
                 pList->push_back(DriveInfo(dev.getNode(), dev.getUdi(), dev.getDesc()));
             }
-            catch(std::bad_alloc &e)
+            catch (std::bad_alloc &e)
             {
                 rc = VERR_NO_MEMORY;
@@ -794,11 +833,11 @@
     if (rc == VERR_NO_MORE_FILES)
         rc = VINF_SUCCESS;
-    if (RT_FAILURE(rc))
+    else if (RT_FAILURE(rc))
         /* Clean up again */
-        for (unsigned i = 0; i < cFound; ++i)
+        while (cFound-- > 0)
             pList->pop_back();
     if (pfSuccess)
         *pfSuccess = fSuccess;
-    LogFlow (("rc=%Rrc, fSuccess=%u\n", rc, (unsigned) fSuccess));
+    LogFlow (("rc=%Rrc, fSuccess=%u\n", rc, (unsigned)fSuccess));
     return rc;
 }
@@ -807,26 +846,28 @@
 /** Helper for readFilePathsFromDir().  Adds a path to the vector if it is not
  * NULL and not a dotfile (".", "..", ".*"). */
-static int maybeAddPathToVector(const char *pcszPath, const char *pcszEntry,
-                                VECTOR_PTR(char *) *pvecpchDevs)
-{
-    char *pszPath;
-
+static int maybeAddPathToVector(const char *pcszPath, const char *pcszEntry, VECTOR_PTR(char *) *pvecpchDevs) RT_NOTHROW_DEF
+{
     if (!pcszPath)
         return 0;
     if (pcszEntry[0] == '.')
         return 0;
-    pszPath = RTStrDup(pcszPath);
-    if (!pszPath)
-        return ENOMEM;
-    if (RT_FAILURE(VEC_PUSH_BACK_PTR(pvecpchDevs, char *, pszPath)))
-        return ENOMEM;
-    return 0;
-}
-
-/** Helper for readFilePaths().  Adds the entries from the open directory
- * @a pDir to the vector @a pvecpchDevs using either the full path or the
- * realpath() and skipping hidden files and files on which realpath() fails. */
-static int readFilePathsFromDir(const char *pcszPath, DIR *pDir,
-                                VECTOR_PTR(char *) *pvecpchDevs, int withRealPath)
+    char *pszPath = RTStrDup(pcszPath);
+    if (pszPath)
+    {
+        int vrc = VEC_PUSH_BACK_PTR(pvecpchDevs, char *, pszPath);
+        if (RT_SUCCESS(vrc))
+            return 0;
+    }
+    return ENOMEM;
+}
+
+/**
+ * Helper for readFilePaths().
+ *
+ * Adds the entries from the open directory @a pDir to the vector @a pvecpchDevs
+ * using either the full path or the realpath() and skipping hidden files
+ * and files on which realpath() fails.
+ */
+static int readFilePathsFromDir(const char *pcszPath, DIR *pDir, VECTOR_PTR(char *) *pvecpchDevs, int withRealPath) RT_NOTHROW_DEF
 {
     struct dirent entry, *pResult;
@@ -837,5 +878,6 @@
 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 #endif
-    for (err = readdir_r(pDir, &entry, &pResult); pResult;
+    for (err = readdir_r(pDir, &entry, &pResult);
+         pResult /** @todo r=bird: && err == 0 ? */;
          err = readdir_r(pDir, &entry, &pResult))
 #if RT_GNUC_PREREQ(4, 6)
@@ -869,18 +911,14 @@
  * @param   withRealPath  whether to canonicalise the filename with realpath
  */
-static int readFilePaths(const char *pcszPath, VECTOR_PTR(char *) *pvecpchDevs,
-                         int withRealPath)
-{
-    DIR *pDir;
-    int err;
-
+static int readFilePaths(const char *pcszPath, VECTOR_PTR(char *) *pvecpchDevs, int withRealPath) RT_NOTHROW_DEF
+{
     AssertPtrReturn(pvecpchDevs, EINVAL);
     AssertReturn(VEC_SIZE_PTR(pvecpchDevs) == 0, EINVAL);
     AssertPtrReturn(pcszPath, EINVAL);
 
-    pDir = opendir(pcszPath);
+    DIR *pDir = opendir(pcszPath);
     if (!pDir)
         return RTErrConvertFromErrno(errno);
-    err = readFilePathsFromDir(pcszPath, pDir, pvecpchDevs, withRealPath);
+    int err = readFilePathsFromDir(pcszPath, pDir, pvecpchDevs, withRealPath);
     if (closedir(pDir) < 0 && !err)
         err = errno;
@@ -1262,5 +1300,5 @@
         mImpl = new hotplugNullImpl(pcszDevicesRoot);
     }
-    catch(std::bad_alloc &e)
+    catch (std::bad_alloc &e)
     { }
 }
