VirtualBox

Changeset 23522 in vbox for trunk/src/VBox/Main/linux


Ignore:
Timestamp:
Oct 2, 2009 11:27:33 PM (16 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
53164
Message:

Main/HostHardwareLinux: more rewriting of the Linux host drive code, including a complete rewrite of the legacy code

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/linux/HostHardwareLinux.cpp

    r23386 r23522  
    3737#include <iprt/mem.h>
    3838#include <iprt/param.h>
     39#include <iprt/path.h>
    3940#include <iprt/thread.h>  /* for RTThreadSleep() */
    4041#include <iprt/string.h>
     
    5455# include <linux/cdrom.h>
    5556# include <linux/fd.h>
     57# include <linux/major.h>
    5658# ifdef VBOX_WITH_DBUS
    5759#  include <vbox-dbus.h>
     
    5961# include <errno.h>
    6062# include <scsi/scsi.h>
    61 # include <scsi/sg.h>
    6263
    6364# include <iprt/linux/sysfs.h>
     
    6566#include <vector>
    6667
    67 /*******************************************************************************
    68 *   Global Variables                                                           *
    69 *******************************************************************************/
    70 
    71 bool g_testHostHardwareLinux = false;
    72 static bool testing () { return g_testHostHardwareLinux; }
    73 
    74 /*******************************************************************************
    75 *   Typedefs and Defines                                                       *
    76 *******************************************************************************/
     68/******************************************************************************
     69*   Global Variables                                                          *
     70******************************************************************************/
     71
     72#ifdef TESTCASE
     73static bool testing() { return true; }
     74static bool fNoProbe = false;
     75static bool noProbe() { return fNoProbe; }
     76static void setNoProbe(bool val) { fNoProbe = val; }
     77#else
     78static bool testing() { return false; }
     79static bool noProbe() { return false; }
     80static void setNoProbe(bool val) { (void)val; }
     81#endif
     82
     83/******************************************************************************
     84*   Typedefs and Defines                                                      *
     85******************************************************************************/
    7786
    7887/** When waiting for hotplug events, we currently restart the wait after at
     
    8089enum { DBUS_POLL_TIMEOUT = 2000 /* ms */ };
    8190
    82 
    83 static bool validateDevice(const char *deviceNode, bool isDVD);
    84 static int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
     91static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
    8592                               bool isDVD, bool *pfSuccess);
     93static int getDriveInfoFromDev(DriveInfoList *pList, bool isDVD,
     94                               bool *pfSuccess);
    8695static int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD,
    8796                                 bool *pfSuccess);
    88 int scsiDoInquiry(const char *pszNode, uint8_t *pu8Type, char *pchVendor,
    89                   size_t cchVendor, char *pchModel, size_t cchModel);
    90 static int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList);
    9197#ifdef VBOX_WITH_DBUS
    9298/* These must be extern to be usable in the RTMemAutoPtr template */
     
    119125                                        bool *pfMatches, bool *pfSuccess);
    120126*/
    121 static int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD,
    122                                bool *pfSuccess);
    123127static int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
    124128static int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
     
    129133#endif  /* VBOX_WITH_DBUS */
    130134
     135
    131136/** Find the length of a string, ignoring trailing non-ascii or control
    132137 * characters */
    133 static size_t strLenStripped(const char *psz)
     138static size_t strLenStripped(const char *pcsz)
    134139{
    135140    size_t cch = 0;
    136     for (size_t i = 0; psz[i] != '\0'; ++i)
    137         if (psz[i] > 32 && psz[i] < 127)
     141    for (size_t i = 0; pcsz[i] != '\0'; ++i)
     142        if (pcsz[i] > 32 && pcsz[i] < 127)
    138143            cch = i;
    139144    return cch + 1;
    140145}
     146
     147
     148static bool floppyGetName(const char *pcszNode, unsigned Number,
     149                          floppy_drive_name pszName)
     150{
     151    AssertPtrReturn(pcszNode, false);
     152    AssertPtrReturn(pszName, false);
     153    AssertReturn(Number <= 7, false);
     154    RTFILE File;
     155    int rc = RTFileOpen(&File, pcszNode, RTFILE_O_READ | RTFILE_O_NON_BLOCK);
     156    if (RT_SUCCESS(rc))
     157    {
     158        int rcIoCtl;
     159        /** @todo The next line can produce a warning, as the ioctl request
     160         * field is defined as signed, but the Linux ioctl definition macros
     161         * produce unsigned constants. */
     162        rc = RTFileIoCtl(File, FDGETDRVTYP, pszName, 0, &rcIoCtl);
     163        RTFileClose(File);
     164        if (RT_SUCCESS(rc) && rcIoCtl >= 0)
     165            return true;
     166    }
     167    return false;
     168}
     169
     170
     171/**
     172 * Create a UDI and a description for a floppy drive based on a number and the
     173 * driver's name for it.  We deliberately return an ugly sequence of
     174 * characters as the description rather than an English language string to
     175 * avoid translation issues.
     176 *
     177 * @returns true if we know the device to be valid, false otherwise
     178 * @param   pcszName     the floppy driver name for the device (optional)
     179 * @param   Number       the number of the floppy (0 to 3 on FDC 0, 4 to 7 on
     180 *                       FDC 1)
     181 * @param   pszDesc      where to store the device description (optional)
     182 * @param   cchDesc      the size of the buffer in @a pszDesc
     183 * @param   pszUdi       where to store the device UDI (optional)
     184 * @param   cchUdi       the size of the buffer in @a pszUdi
     185 */
     186static void floppyCreateDeviceStrings(const floppy_drive_name pcszName,
     187                                      unsigned Number, char *pszDesc,
     188                                      size_t cchDesc, char *pszUdi,
     189                                      size_t cchUdi)
     190{
     191    AssertPtrNullReturnVoid(pcszName);
     192    AssertPtrNullReturnVoid(pszDesc);
     193    AssertReturnVoid(!pszDesc || cchDesc > 0);
     194    AssertPtrNullReturnVoid(pszUdi);
     195    AssertReturnVoid(!pszUdi || cchUdi > 0);
     196    AssertReturnVoid(Number <= 7);
     197    if (pcszName)
     198    {
     199        const char *pcszSize;
     200        switch(pcszName[0])
     201        {
     202            case 'd': case 'q': case 'h':
     203                pcszSize = "5.25\"";
     204                break;
     205            case 'D': case 'H': case 'E': case 'u':
     206                pcszSize = "3.5\"";
     207                break;
     208            default:
     209                pcszSize = "(unknown)";
     210        }
     211        if (pszDesc)
     212            RTStrPrintf(pszDesc, cchDesc, "%s %s K%s", pcszSize, &pcszName[1],
     213                        Number > 3 ? ", FDC 2" : "");
     214    }
     215    else
     216    {
     217        if (pszDesc)
     218            RTStrPrintf(pszDesc, cchDesc, "FDD %d%s", (Number & 4) + 1,
     219                        Number > 3 ? ", FDC 2" : "");
     220    }
     221    if (pszUdi)
     222        RTStrPrintf(pszUdi, cchUdi,
     223                    "/org/freedesktop/Hal/devices/platform_floppy_%u_storage",
     224                    Number);
     225}
     226
     227
     228static bool isCdromDevNum(dev_t Number)
     229{
     230    int major = major(Number);
     231    int minor = minor(Number);
     232    if ((major == IDE0_MAJOR) && !(minor & 0x3f))
     233        return true;
     234    if (major == SCSI_CDROM_MAJOR)
     235        return true;
     236    if (major == CDU31A_CDROM_MAJOR)
     237        return true;
     238    if (major == GOLDSTAR_CDROM_MAJOR)
     239        return true;
     240    if (major == OPTICS_CDROM_MAJOR)
     241        return true;
     242    if (major == SANYO_CDROM_MAJOR)
     243        return true;
     244    if (major == MITSUMI_X_CDROM_MAJOR)
     245        return true;
     246    if ((major == IDE1_MAJOR) && !(minor & 0x3f))
     247        return true;
     248    if (major == MITSUMI_CDROM_MAJOR)
     249        return true;
     250    if (major == CDU535_CDROM_MAJOR)
     251        return true;
     252    if (major == MATSUSHITA_CDROM_MAJOR)
     253        return true;
     254    if (major == MATSUSHITA_CDROM2_MAJOR)
     255        return true;
     256    if (major == MATSUSHITA_CDROM3_MAJOR)
     257        return true;
     258    if (major == MATSUSHITA_CDROM4_MAJOR)
     259        return true;
     260    if (major == AZTECH_CDROM_MAJOR)
     261        return true;
     262    if (major == 30 /* CM205_CDROM_MAJOR */)  /* no #define for some reason */
     263        return true;
     264    if (major == CM206_CDROM_MAJOR)
     265        return true;
     266    if ((major == IDE3_MAJOR) && !(minor & 0x3f))
     267        return true;
     268    if (major == 46 /* Parallel port ATAPI CD-ROM */)  /* no #define */
     269        return true;
     270    if ((major == IDE4_MAJOR) && !(minor & 0x3f))
     271        return true;
     272    if ((major == IDE5_MAJOR) && !(minor & 0x3f))
     273        return true;
     274    if ((major == IDE6_MAJOR) && !(minor & 0x3f))
     275        return true;
     276    if ((major == IDE7_MAJOR) && !(minor & 0x3f))
     277        return true;
     278    if ((major == IDE8_MAJOR) && !(minor & 0x3f))
     279        return true;
     280    if ((major == IDE9_MAJOR) && !(minor & 0x3f))
     281        return true;
     282    if (major == 113 /* VIOCD_MAJOR */)
     283        return true;
     284    return false;
     285}
     286
     287
     288/**
     289 * Send an SCSI INQUIRY command to a device and return selected information.
     290 * @returns  iprt status code
     291 * @returns  VERR_TRY_AGAIN if the query failed but might succeed next time
     292 * @param pcszNode    the full path to the device node
     293 * @param pu8Type    where to store the SCSI device type on success (optional)
     294 * @param pchVendor  where to store the vendor id string on success (optional)
     295 * @param cchVendor  the size of the @a pchVendor buffer
     296 * @param pchModel   where to store the product id string on success (optional)
     297 * @param cchModel   the size of the @a pchModel buffer
     298 * @note check documentation on the SCSI INQUIRY command and the Linux kernel
     299 *       SCSI headers included above if you want to understand what is going
     300 *       on in this method.
     301 */
     302static int cdromDoInquiry(const char *pcszNode, uint8_t *pu8Type,
     303                          char *pchVendor, size_t cchVendor, char *pchModel,
     304                          size_t cchModel)
     305{
     306    LogRelFlowFunc(("pcszNode=%s, pu8Type=%p, pchVendor=%p, cchVendor=%llu, pchModel=%p, cchModel=%llu\n",
     307                    pcszNode, pu8Type, pchVendor, cchVendor, pchModel,
     308                    cchModel));
     309    AssertPtrReturn(pcszNode, VERR_INVALID_POINTER);
     310    AssertPtrNullReturn(pu8Type, VERR_INVALID_POINTER);
     311    AssertPtrNullReturn(pchVendor, VERR_INVALID_POINTER);
     312    AssertPtrNullReturn(pchModel, VERR_INVALID_POINTER);
     313
     314    unsigned char u8Response[96] = { 0 };
     315    struct cdrom_generic_command CdromCommandReq =
     316    { { INQUIRY, 0, 0, 0, sizeof(u8Response), 0 }  /* INQUIRY */ };
     317    int rc, rcIoCtl = 0;
     318    RTFILE file;
     319    rc = RTFileOpen(&file, pcszNode, RTFILE_O_READ | RTFILE_O_NON_BLOCK);
     320    if (RT_SUCCESS(rc))
     321    {
     322        CdromCommandReq.buffer = u8Response;
     323        CdromCommandReq.buflen = sizeof(u8Response);
     324        CdromCommandReq.data_direction = CGC_DATA_READ;
     325        CdromCommandReq.timeout = 5000;  /* ms */
     326        rc = RTFileIoCtl(file, CDROM_SEND_PACKET, &CdromCommandReq, 0,
     327                         &rcIoCtl);
     328        if (RT_SUCCESS(rc) && rcIoCtl < 0)
     329            rc = RTErrConvertFromErrno(-CdromCommandReq.stat);
     330        RTFileClose(file);
     331    }
     332    if (RT_SUCCESS(rc))
     333    {
     334        if (pu8Type)
     335            *pu8Type = u8Response[0] & 0x1f;
     336        if (pchVendor)
     337            RTStrPrintf(pchVendor, cchVendor, "%.8s",
     338                        (char *) &u8Response[8] /* vendor id string */);
     339        if (pchModel)
     340            RTStrPrintf(pchModel, cchModel, "%.16s",
     341                        (char *) &u8Response[16] /* product id string */);
     342    }
     343    LogRelFlowFunc(("returning %Rrc\n", rc));
     344    if (RT_SUCCESS(rc))
     345        LogRelFlowFunc(("    type=%u, vendor=%.8s, product=%.16s\n",
     346                        u8Response[0] & 0x1f, (char *) &u8Response[8],
     347                        (char *) &u8Response[16]));
     348    return rc;
     349}
     350
     351
     352/**
     353 * Initialise the device strings (description and UDI) for a DVD drive based on
     354 * vendor and model name strings.
     355 * @param pcszVendor  the vendor ID string
     356 * @param pcszModel   the product ID string
     357 * @param pszDesc    where to store the description string (optional)
     358 * @param cchDesc    the size of the buffer in @pszDesc
     359 * @param pszUdi     where to store the UDI string (optional)
     360 * @param cchUdi     the size of the buffer in @pszUdi
     361 */
     362/* static */
     363void dvdCreateDeviceStrings(const char *pcszVendor, const char *pcszModel,
     364                            char *pszDesc, size_t cchDesc, char *pszUdi,
     365                            size_t cchUdi)
     366{
     367    AssertPtrReturnVoid(pcszVendor);
     368    AssertPtrReturnVoid(pcszModel);
     369    AssertPtrNullReturnVoid(pszDesc);
     370    AssertReturnVoid(!pszDesc || cchDesc > 0);
     371    AssertPtrNullReturnVoid(pszUdi);
     372    AssertReturnVoid(!pszUdi || cchUdi > 0);
     373    char szCleaned[128];
     374    size_t cchVendor = strLenStripped(pcszVendor);
     375    size_t cchModel = strLenStripped(pcszModel);
     376
     377    /* Create a cleaned version of the model string for the UDI string. */
     378    for (unsigned i = 0; pcszModel[i] != '\0' && i < sizeof(szCleaned); ++i)
     379        if (   (pcszModel[i] >= '0' && pcszModel[i] <= '9')
     380            || (pcszModel[i] >= 'A' && pcszModel[i] <= 'z'))
     381            szCleaned[i] = pcszModel[i];
     382        else
     383            szCleaned[i] = '_';
     384    szCleaned[RT_MIN(cchModel, sizeof(szCleaned) - 1)] = '\0';
     385
     386    /* Construct the description string as "Vendor Product" */
     387    if (pszDesc)
     388    {
     389        if (cchVendor > 0)
     390            RTStrPrintf(pszDesc, cchDesc, "%.*s %s", cchVendor, pcszVendor,
     391                        cchModel > 0 ? pcszModel : "(unknown drive model)");
     392        else
     393            RTStrPrintf(pszDesc, cchDesc, "%s", pcszModel);
     394    }
     395    /* Construct the UDI string */
     396    if (pszUdi)
     397    {
     398        if (cchModel > 0)
     399            RTStrPrintf(pszUdi, cchUdi,
     400                        "/org/freedesktop/Hal/devices/storage_model_%s",
     401                        szCleaned);
     402        else
     403            pszUdi[0] = '\0';
     404    }
     405}
     406
     407
     408/**
     409 * Check whether a device node points to a valid device and create a UDI and
     410 * a description for it, and store the device number, if it does.
     411 * @returns true if the device is valid, false otherwise
     412 * @param   pcszNode   the path to the device node
     413 * @param   isDVD     are we looking for a DVD device (or a floppy device)?
     414 * @param   pDevice   where to store the device node (optional)
     415 * @param   pszDesc   where to store the device description (optional)
     416 * @param   cchDesc   the size of the buffer in @a pszDesc
     417 * @param   pszUdi    where to store the device UDI (optional)
     418 * @param   cchUdi    the size of the buffer in @a pszUdi
     419 */
     420static bool devValidateDevice(const char *pcszNode, bool isDVD, dev_t *pDevice,
     421                              char *pszDesc, size_t cchDesc, char *pszUdi,
     422                              size_t cchUdi)
     423{
     424    AssertPtrReturn(pcszNode, false);
     425    AssertPtrNullReturn(pDevice, false);
     426    AssertPtrNullReturn(pszDesc, false);
     427    AssertReturn(!pszDesc || cchDesc > 0, false);
     428    AssertPtrNullReturn(pszUdi, false);
     429    AssertReturn(!pszUdi || cchUdi > 0, false);
     430    RTFSOBJINFO ObjInfo;
     431    if (RT_FAILURE(RTPathQueryInfo(pcszNode, &ObjInfo, RTFSOBJATTRADD_UNIX)))
     432        return false;
     433    if (!RTFS_IS_DEV_BLOCK(ObjInfo.Attr.fMode))
     434        return false;
     435    if (pDevice)
     436        *pDevice = ObjInfo.Attr.u.Unix.Device;
     437    if (isDVD)
     438    {
     439        char szVendor[128], szModel[128];
     440        uint8_t u8Type;
     441        if (!isCdromDevNum(ObjInfo.Attr.u.Unix.Device))
     442            return false;
     443        if (RT_FAILURE(cdromDoInquiry(pcszNode, &u8Type,
     444                                      szVendor, sizeof(szVendor),
     445                                      szModel, sizeof(szModel))))
     446            return false;
     447        if (u8Type != TYPE_ROM)
     448            return false;
     449        dvdCreateDeviceStrings(szVendor, szModel, pszDesc, cchDesc,
     450                               pszUdi, cchUdi);
     451    }
     452    else
     453    {
     454        /* Floppies on Linux are legacy devices with hardcoded majors and
     455         * minors */
     456        unsigned Number;
     457        floppy_drive_name szName;
     458        if (major(ObjInfo.Attr.u.Unix.Device) != FLOPPY_MAJOR)
     459            return false;
     460        switch (minor(ObjInfo.Attr.u.Unix.Device))
     461        {
     462            case 0: case 1: case 2: case 3:
     463                Number = minor(ObjInfo.Attr.u.Unix.Device);
     464                break;
     465            case 128: case 129: case 130: case 131:
     466                Number = minor(ObjInfo.Attr.u.Unix.Device) - 128 + 4;
     467                break;
     468            default:
     469                return false;
     470        }
     471        if (!floppyGetName(pcszNode, Number, szName))
     472            return false;
     473        floppyCreateDeviceStrings(szName, Number, pszDesc, cchDesc, pszUdi,
     474                                  cchUdi);
     475    }
     476    return true;
     477}
     478
    141479
    142480int VBoxMainDriveInfo::updateDVDs ()
     
    148486    {
    149487        mDVDList.clear ();
    150 #if defined(RT_OS_LINUX)
    151488        /* Always allow the user to override our auto-detection using an
    152489         * environment variable. */
     
    154491            rc = getDriveInfoFromEnv ("VBOX_CDROM", &mDVDList, true /* isDVD */,
    155492                                      &success);
    156 #ifdef VBOX_WITH_DBUS
    157         if (RT_SUCCESS(rc) && RT_SUCCESS(VBoxLoadDBusLib()) && (!success || testing()))
    158             rc = getDriveInfoFromHal(&mDVDList, true /* isDVD */, &success);
    159 #endif /* VBOX_WITH_DBUS defined */
     493        setNoProbe(false);
    160494        if (RT_SUCCESS(rc) && (!success | testing()))
    161495            rc = getDriveInfoFromSysfs(&mDVDList, true /* isDVD */, &success);
    162         /* On Linux without hal, the situation is much more complex. We will
    163          * take a heuristical approach.  The general strategy is to try some
    164          * known device names and see of they exist.  Failing that, we
    165          * enumerate the /etc/fstab file (luckily there's an API to parse it)
    166          * for CDROM devices. Ok, let's start! */
    167         if (RT_SUCCESS(rc) && (!success || testing()))
    168         {
    169             // this is a good guess usually
    170             if (validateDevice("/dev/cdrom", true))
    171                 mDVDList.push_back(DriveInfo ("/dev/cdrom"));
    172 
    173             // check the mounted drives
    174             rc = getDVDInfoFromMTab((char*)"/etc/mtab", &mDVDList);
    175 
    176             // check the drives that can be mounted
    177             if (RT_SUCCESS(rc))
    178                 rc = getDVDInfoFromMTab((char*)"/etc/fstab", &mDVDList);
    179         }
    180 #endif
     496        if (RT_SUCCESS(rc) && testing())
     497        {
     498            setNoProbe(true);
     499            rc = getDriveInfoFromSysfs(&mDVDList, true /* isDVD */, &success);
     500        }
     501        /* Walk through the /dev subtree if nothing else has helped. */
     502        if (RT_SUCCESS(rc) && (!success | testing()))
     503            rc = getDriveInfoFromDev(&mDVDList, true /* isDVD */, &success);
    181504    }
    182505    catch(std::bad_alloc &e)
     
    196519    {
    197520        mFloppyList.clear ();
    198 #if defined(RT_OS_LINUX)
    199521        if (RT_SUCCESS(rc) && (!success || testing()))
    200             rc = getDriveInfoFromEnv ("VBOX_FLOPPY", &mFloppyList, false /* isDVD */,
    201                                       &success);
    202 #ifdef VBOX_WITH_DBUS
    203         if (   RT_SUCCESS(rc)
    204             && RT_SUCCESS(VBoxLoadDBusLib())
    205             && (!success || testing()))
    206             rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success);
    207 #endif /* VBOX_WITH_DBUS defined */
    208         if (   RT_SUCCESS(rc)
    209             && RT_SUCCESS(VBoxLoadDBusLib())
    210             && (!success || testing()))
    211             rc = getDriveInfoFromHal(&mFloppyList, false /* isDVD */, &success);
    212         /* As with the CDROMs, on Linux we have to take a multi-level approach
    213          * involving parsing the mount tables. As this is not bulletproof, we
    214          * give the user the chance to override the detection using an
    215          * environment variable, skiping the detection. */
    216 
    217         if (RT_SUCCESS(rc) && (!success || testing()))
    218         {
    219             // we assume that a floppy is always /dev/fd[x] with x from 0 to 7
    220             char devName[10];
    221             for (int i = 0; i <= 7; i++)
    222             {
    223                 RTStrPrintf(devName, sizeof(devName), "/dev/fd%d", i);
    224                 if (validateDevice(devName, false))
    225                     mFloppyList.push_back (DriveInfo (devName));
    226             }
    227         }
    228 #endif
     522            rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList,
     523                                     false /* isDVD */, &success);
     524        setNoProbe(false);
     525        if (   RT_SUCCESS(rc) && (!success || testing()))
     526            rc = getDriveInfoFromSysfs(&mFloppyList, false /* isDVD */,
     527                                       &success);
     528        if (RT_SUCCESS(rc) && testing())
     529        {
     530            setNoProbe(true);
     531            rc = getDriveInfoFromSysfs(&mFloppyList, false /* isDVD */, &success);
     532        }
     533        /* Walk through the /dev subtree if nothing else has helped. */
     534        if (   RT_SUCCESS(rc) && (!success || testing()))
     535            rc = getDriveInfoFromDev(&mFloppyList, false /* isDVD */,
     536                                     &success);
    229537    }
    230538    catch(std::bad_alloc &e)
     
    235543    return rc;
    236544}
     545
     546
     547/**
     548 * Extract the names of drives from an environment variable and add them to a
     549 * list if they are valid.
     550 * @returns iprt status code
     551 * @param   pcszVar     the name of the environment variable.  The variable
     552 *                     value should be a list of device node names, separated
     553 *                     by ':' characters.
     554 * @param   pList      the list to append the drives found to
     555 * @param   isDVD      are we looking for DVD drives or for floppies?
     556 * @param   pfSuccess  this will be set to true if we found at least one drive
     557 *                     and to false otherwise.  Optional.
     558 */
     559/* static */
     560int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList,
     561                        bool isDVD, bool *pfSuccess)
     562{
     563    AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
     564    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     565    AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
     566    LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar,
     567                 pList, isDVD, pfSuccess));
     568    int rc = VINF_SUCCESS;
     569    bool success = false;
     570
     571    try
     572    {
     573        const char *pcszCurrent = RTEnvGet (pcszVar);
     574        while (pcszCurrent && *pcszCurrent != '\0')
     575        {
     576            const char *pcszNext = strchr(pcszCurrent, ':');
     577            char szPath[RTPATH_MAX], szReal[RTPATH_MAX];
     578            char szDesc[256], szUdi[256];
     579            if (pcszNext)
     580                RTStrPrintf(szPath, sizeof(szPath), "%.*s",
     581                            pcszNext - pcszCurrent - 1, pcszCurrent);
     582            else
     583                RTStrPrintf(szPath, sizeof(szPath), "%s", pcszCurrent);
     584            if (   RT_SUCCESS(RTPathReal(szPath, szReal, sizeof(szReal)))
     585                && devValidateDevice(szReal, isDVD, NULL, szDesc,
     586                                     sizeof(szDesc), szUdi, sizeof(szUdi)))
     587            {
     588                pList->push_back(DriveInfo(szReal, szUdi, szDesc));
     589                success = true;
     590            }
     591            pcszCurrent = pcszNext ? pcszNext + 1 : NULL;
     592        }
     593        if (pfSuccess != NULL)
     594            *pfSuccess = success;
     595    }
     596    catch(std::bad_alloc &e)
     597    {
     598        rc = VERR_NO_MEMORY;
     599    }
     600    LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
     601    return rc;
     602}
     603
     604
     605class sysfsBlockDev
     606{
     607public:
     608    sysfsBlockDev(const char *pcszName, bool wantDVD)
     609            : mpcszName(pcszName), mwantDVD(wantDVD), misConsistent(true),
     610              misValid(false)
     611    {
     612        if (findDeviceNode())
     613        {
     614            if (mwantDVD)
     615                validateAndInitForDVD();
     616            else
     617                validateAndInitForFloppy();
     618        }
     619    }
     620private:
     621    /** The name of the subdirectory of /sys/block for this device */
     622    const char *mpcszName;
     623    /** Are we looking for a floppy or a DVD device? */
     624    bool mwantDVD;
     625    /** The device node for the device */
     626    char mszNode[RTPATH_MAX];
     627    /** Does the sysfs entry look like we expect it too?  This is a canary
     628     * for future sysfs ABI changes. */
     629    bool misConsistent;
     630    /** Is this entry a valid specimen of what we are looking for? */
     631    bool misValid;
     632    /** Human readible drive description string */
     633    char mszDesc[256];
     634    /** Unique identifier for the drive.  Should be identical to hal's UDI for
     635     * the device.  May not be unique for two identical drives. */
     636    char mszUdi[256];
     637private:
     638    /* Private methods */
     639
     640    /**
     641     * Fill in the device node member based on the /sys/block subdirectory.
     642     * @returns boolean success value
     643     */
     644    bool findDeviceNode()
     645    {
     646        dev_t dev = RTLinuxSysFsReadDevNumFile("block/%s/dev", mpcszName);
     647        if (dev == 0)
     648        {
     649            misConsistent = false;
     650            return false;
     651        }
     652        if (RTLinuxFindDevicePath(dev, RTFS_TYPE_DEV_BLOCK, mszNode,
     653                                  sizeof(mszNode), "%s", mpcszName) < 0)
     654            return false;
     655        return true;
     656    }
     657
     658    /** Check whether the sysfs block entry is valid for a DVD device and
     659     * initialise the string data members for the object.  We try to get all
     660     * the information we need from sysfs if possible, to avoid unnecessarily
     661     * poking the device, and if that fails we fall back to an SCSI INQUIRY
     662     * command. */
     663    void validateAndInitForDVD()
     664    {
     665        char szVendor[128], szModel[128];
     666        ssize_t cchVendor, cchModel;
     667        int64_t type = RTLinuxSysFsReadIntFile(10, "block/%s/device/type",
     668                                               mpcszName);
     669        if (type >= 0 && type != TYPE_ROM)
     670            return;
     671        if (type == TYPE_ROM)
     672        {
     673            cchVendor = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor),
     674                                                "block/%s/device/vendor",
     675                                                mpcszName);
     676            if (cchVendor >= 0)
     677            {
     678                cchModel = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel),
     679                                                   "block/%s/device/model",
     680                                                   mpcszName);
     681                if (cchModel >= 0)
     682                {
     683                    misValid = true;
     684                    dvdCreateDeviceStrings(szVendor, szModel,
     685                                           mszDesc, sizeof(mszDesc),
     686                                           mszUdi, sizeof(mszUdi));
     687                    return;
     688                }
     689            }
     690        }
     691        if (!noProbe())
     692            probeAndInitForDVD();
     693    }
     694
     695    /** Try to find out whether a device is a DVD drive by sending it an
     696     * SCSI INQUIRY command.  If it is, initialise the string and validity
     697     * data members for the object based on the returned data.
     698     */
     699    void probeAndInitForDVD()
     700    {
     701        AssertReturnVoid(mszNode[0] != '\0');
     702        uint8_t u8Type = 0;
     703        char szVendor[128] = "";
     704        char szModel[128] = "";
     705        int rc = cdromDoInquiry(mszNode, &u8Type, szVendor,
     706                                sizeof(szVendor), szModel,
     707                                sizeof(szModel));
     708        if (RT_SUCCESS(rc) && (u8Type == TYPE_ROM))
     709        {
     710            misValid = true;
     711            dvdCreateDeviceStrings(szVendor, szModel, mszDesc, sizeof(mszDesc),
     712                                   mszUdi, sizeof(mszUdi));
     713        }
     714    }
     715
     716    /** Check whether the sysfs block entry is valid for a floppy device and
     717     * initialise the string data members for the object.  Since we only
     718     * support floppies using the basic "floppy" driver, we check the driver
     719     * using the entry name and a driver-specific ioctl. */
     720    void validateAndInitForFloppy()
     721    {
     722        bool haveName = false;
     723        floppy_drive_name szName;
     724        char szDriver[8];
     725        if (   mpcszName[0] != 'f'
     726            || mpcszName[1] != 'd'
     727            || mpcszName[2] < '0'
     728            || mpcszName[2] > '7'
     729            || mpcszName[3] != '\0')
     730            return;
     731        if (!noProbe())
     732            haveName = floppyGetName(mszNode, mpcszName[2] - '0', szName);
     733        if (RTLinuxSysFsGetLinkDest(szDriver, sizeof(szDriver), "block/%s/%s",
     734                                    mpcszName, "device/driver") >= 0)
     735        {
     736            if (RTStrCmp(szDriver, "floppy"))
     737                return;
     738        }
     739        else if (!haveName)
     740            return;
     741        floppyCreateDeviceStrings(haveName ? szName : NULL,
     742                                  mpcszName[2] - '0', mszDesc,
     743                                  sizeof(mszDesc), mszUdi, sizeof(mszUdi));
     744        misValid = true;
     745    }
     746
     747public:
     748    bool isConsistent()
     749    {
     750        return misConsistent;
     751    }
     752    bool isValid()
     753    {
     754        return misValid;
     755    }
     756    const char *getDesc()
     757    {
     758        return mszDesc;
     759    }
     760    const char *getUdi()
     761    {
     762        return mszUdi;
     763    }
     764    const char *getNode()
     765    {
     766        return mszNode;
     767    }
     768};
     769
     770/**
     771 * Helper function to query the sysfs subsystem for information about DVD
     772 * drives attached to the system.
     773 * @returns iprt status code
     774 * @param   pList      where to add information about the drives detected
     775 * @param   isDVD      are we looking for DVDs or floppies?
     776 * @param   pfSuccess  Did we find anything?
     777 *
     778 * @returns IPRT status code
     779 */
     780/* static */
     781int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
     782{
     783    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     784    AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER); /* Valid or Null */
     785    LogFlowFunc (("pList=%p, isDVD=%u, pfSuccess=%p\n",
     786                  pList, (unsigned) isDVD, pfSuccess));
     787    PRTDIR pDir = NULL;
     788    RTDIRENTRY entry = {0};
     789    int rc;
     790    bool fSuccess = false;
     791    unsigned cFound = 0;
     792
     793    if (!RTPathExists("/sys"))
     794        return VINF_SUCCESS;
     795    rc = RTDirOpen(&pDir, "/sys/block");
     796    /* This might mean that sysfs semantics have changed */
     797    AssertReturn(rc != VERR_FILE_NOT_FOUND, VINF_SUCCESS);
     798    fSuccess = true;
     799    if (RT_SUCCESS(rc))
     800        while (true)
     801        {
     802            rc = RTDirRead(pDir, &entry, NULL);
     803            Assert(rc != VERR_BUFFER_OVERFLOW);  /* Should never happen... */
     804            if (RT_FAILURE(rc))  /* Including overflow and no more files */
     805                break;
     806            if (entry.szName[0] == '.')
     807                continue;
     808            sysfsBlockDev dev(entry.szName, isDVD);
     809            /* This might mean that sysfs semantics have changed */
     810            AssertBreakStmt(dev.isConsistent(), fSuccess = false);
     811            if (!dev.isValid())
     812                continue;
     813            try
     814            {
     815                pList->push_back(DriveInfo(dev.getNode(), dev.getUdi(),
     816                                           dev.getDesc()));
     817            }
     818            catch(std::bad_alloc &e)
     819            {
     820                rc = VERR_NO_MEMORY;
     821                break;
     822            }
     823            ++cFound;
     824        }
     825    RTDirClose(pDir);
     826    if (rc == VERR_NO_MORE_FILES)
     827        rc = VINF_SUCCESS;
     828    if (RT_FAILURE(rc))
     829        /* Clean up again */
     830        for (unsigned i = 0; i < cFound; ++i)
     831            pList->pop_back();
     832    if (pfSuccess)
     833        *pfSuccess = fSuccess;
     834    LogFlow (("rc=%Rrc, fSuccess=%u\n", rc, (unsigned) fSuccess));
     835    return rc;
     836}
     837
     838
     839/** Structure for holding information about a drive we have found */
     840struct deviceNodeInfo
     841{
     842    /** The device number */
     843    dev_t Device;
     844    /** The device node path */
     845    char szPath[RTPATH_MAX];
     846    /** The device description */
     847    char szDesc[256];
     848    /** The device UDI */
     849    char szUdi[256];
     850};
     851
     852/** The maximum number of devices we will search for. */
     853enum { MAX_DEVICE_NODES = 8 };
     854/** An array of MAX_DEVICE_NODES devices */
     855typedef struct deviceNodeInfo deviceNodeArray[MAX_DEVICE_NODES];
     856
     857/**
     858 * Recursive worker function to walk the /dev tree looking for DVD or floppy
     859 * devices.
     860 * @returns true if we have already found MAX_DEVICE_NODES devices, false
     861 *          otherwise
     862 * @param   pszPath   the path to start recursing.  The function can modify
     863 *                    this string at and after the terminating zero
     864 * @param   cchPath   the size of the buffer (not the string!) in @a pszPath
     865 * @param   aDevices  where to fill in information about devices that we have
     866 *                    found
     867 * @param   wantDVD   are we looking for DVD devices (or floppies)?
     868 */
     869static bool devFindDeviceRecursive(char *pszPath, size_t cchPath,
     870                                   deviceNodeArray aDevices, bool wantDVD)
     871{
     872    /*
     873     * Check assumptions made by the code below.
     874     */
     875    size_t const cchBasePath = strlen(pszPath);
     876    AssertReturn(cchBasePath < RTPATH_MAX - 10U, false);
     877    AssertReturn(pszPath[cchBasePath - 1] != '/', false);
     878
     879    PRTDIR  pDir;
     880    if (RT_FAILURE(RTDirOpen(&pDir, pszPath)))
     881        return false;
     882    for (;;)
     883    {
     884        RTDIRENTRY Entry;
     885        RTFSOBJINFO ObjInfo;
     886        int rc = RTDirRead(pDir, &Entry, NULL);
     887        if (RT_FAILURE(rc))
     888            break;
     889        if (Entry.enmType == RTDIRENTRYTYPE_UNKNOWN)
     890        {
     891            if (RT_FAILURE(RTPathQueryInfo(pszPath, &ObjInfo,
     892                           RTFSOBJATTRADD_UNIX)))
     893                continue;
     894            if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
     895                continue;
     896        }
     897
     898        if (Entry.enmType == RTDIRENTRYTYPE_SYMLINK)
     899            continue;
     900        pszPath[cchBasePath] = '\0';
     901        if (RT_FAILURE(RTPathAppend(pszPath, cchPath, Entry.szName)))
     902            break;
     903
     904        /* Do the matching. */
     905        dev_t DevNode;
     906        char szDesc[256], szUdi[256];
     907        if (!devValidateDevice(pszPath, wantDVD, &DevNode, szDesc,
     908                               sizeof(szDesc), szUdi, sizeof(szUdi)))
     909            continue;
     910        unsigned i;
     911        for (i = 0; i < MAX_DEVICE_NODES; ++i)
     912            if (!aDevices[i].Device || (aDevices[i].Device == DevNode))
     913                break;
     914        AssertBreak(i < MAX_DEVICE_NODES);
     915        if (aDevices[i].Device)
     916            continue;
     917        aDevices[i].Device = DevNode;
     918        RTStrPrintf(aDevices[i].szPath, sizeof(aDevices[i].szPath),
     919                    "%s", pszPath);
     920        AssertCompile(sizeof(aDevices[i].szDesc) == sizeof(szDesc));
     921        strcpy(aDevices[i].szDesc, szDesc);
     922        AssertCompile(sizeof(aDevices[i].szUdi) == sizeof(szUdi));
     923        strcpy(aDevices[i].szUdi, szUdi);
     924        if (i == MAX_DEVICE_NODES - 1)
     925            break;
     926        continue;
     927
     928        /* Recurse into subdirectories. */
     929        if (   (Entry.enmType == RTDIRENTRYTYPE_UNKNOWN)
     930            && !RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
     931            continue;
     932        if (Entry.enmType != RTDIRENTRYTYPE_DIRECTORY)
     933            continue;
     934        if (Entry.szName[0] == '.')
     935            continue;
     936
     937        if (devFindDeviceRecursive(pszPath, cchPath, aDevices, wantDVD))
     938            break;
     939    }
     940    RTDirClose(pDir);
     941    return aDevices[MAX_DEVICE_NODES - 1].Device ? true : false;
     942}
     943
     944
     945/**
     946 * Recursively walk through the /dev tree and add any DVD or floppy drives we
     947 * find and can access to our list.  (If we can't access them we can't check
     948 * whether or not they are really DVD or floppy drives).
     949 * @returns iprt status code
     950 * @param   pList      the list to append the drives found to
     951 * @param   isDVD      are we looking for DVD drives or for floppies?
     952 * @param   pfSuccess  this will be set to true if we found at least one drive
     953 *                     and to false otherwise.  Optional.
     954 */
     955/* static */
     956int getDriveInfoFromDev(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
     957{
     958    AssertPtrReturn(pList, VERR_INVALID_POINTER);
     959    AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
     960    LogFlowFunc(("pList=%p, isDVD=%d, pfSuccess=%p\n", pList, isDVD,
     961                 pfSuccess));
     962    int rc = VINF_SUCCESS;
     963    bool success = false;
     964
     965    deviceNodeArray aDevices = { { 0 } };
     966    char szPath[RTPATH_MAX] = "/dev";
     967    devFindDeviceRecursive(szPath, sizeof(szPath), aDevices, isDVD);
     968    try
     969    {
     970        for (unsigned i = 0; i < MAX_DEVICE_NODES; ++i)
     971        {
     972            if (aDevices[i].Device)
     973            {
     974                pList->push_back(DriveInfo(aDevices[i].szPath,
     975                                 aDevices[i].szUdi, aDevices[i].szDesc));
     976                success = true;
     977            }
     978        }
     979        if (pfSuccess != NULL)
     980            *pfSuccess = success;
     981    }
     982    catch(std::bad_alloc &e)
     983    {
     984        rc = VERR_NO_MEMORY;
     985    }
     986    LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
     987    return rc;
     988}
     989
    237990
    238991int VBoxMainUSBDeviceInfo::UpdateDevices ()
     
    3711124}
    3721125
    373 #ifdef RT_OS_LINUX
    374 /**
    375  * Helper function to check whether the given device node is a valid drive
    376  */
    377 /* static */
    378 bool validateDevice(const char *deviceNode, bool isDVD)
    379 {
    380     AssertReturn(VALID_PTR (deviceNode), VERR_INVALID_POINTER);
    381     LogFlowFunc (("deviceNode=%s, isDVD=%d\n", deviceNode, isDVD));
    382     struct stat statInfo;
    383     bool retValue = false;
    384 
    385     // sanity check
    386     if (!deviceNode)
    387     {
    388         return false;
    389     }
    390 
    391     // first a simple stat() call
    392     if (stat(deviceNode, &statInfo) < 0)
    393     {
    394         return false;
    395     } else
    396     {
    397         if (isDVD)
    398         {
    399             if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
    400             {
    401                 int fileHandle;
    402                 // now try to open the device
    403                 fileHandle = open(deviceNode, O_RDONLY | O_NONBLOCK, 0);
    404                 if (fileHandle >= 0)
    405                 {
    406                     cdrom_subchnl cdChannelInfo;
    407                     cdChannelInfo.cdsc_format = CDROM_MSF;
    408                     // this call will finally reveal the whole truth
    409 #ifdef RT_OS_LINUX
    410                     if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||
    411                         (errno == EIO) || (errno == ENOENT) ||
    412                         (errno == EINVAL) || (errno == ENOMEDIUM))
    413 #endif
    414                     {
    415                         retValue = true;
    416                     }
    417                     close(fileHandle);
    418                 }
    419             }
    420         } else
    421         {
    422             // floppy case
    423             if (S_ISCHR(statInfo.st_mode) || S_ISBLK(statInfo.st_mode))
    424             {
    425                 /// @todo do some more testing, maybe a nice IOCTL!
    426                 retValue = true;
    427             }
    428         }
    429     }
    430     LogFlowFunc (("retValue=%d\n", retValue));
    431     return retValue;
    432 }
    433 #else  /* !RT_OS_LINUX */
    434 # error Port me!  Copying code over from HostImpl.cpp should be most of the job though.
    435 #endif  /* !RT_OS_LINUX */
    436 
    437 /**
    438  * Extract the names of drives from an environment variable and add them to a
    439  * list if they are valid.
    440  * @returns iprt status code
    441  * @param   pszVar     the name of the environment variable.  The variable
    442  *                     value should be a list of device node names, separated
    443  *                     by ':' characters.
    444  * @param   pList      the list to append the drives found to
    445  * @param   isDVD      are we looking for DVD drives or for floppies?
    446  * @param   pfSuccess  this will be set to true if we found at least one drive
    447  *                     and to false otherwise.  Optional.
    448  */
    449 /* static */
    450 int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList,
    451                                bool isDVD, bool *pfSuccess)
    452 {
    453     AssertReturn(   VALID_PTR (pszVar) && VALID_PTR (pList)
    454                  && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
    455                  VERR_INVALID_POINTER);
    456     LogFlowFunc (("pszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pszVar,
    457                   pList, isDVD, pfSuccess));
    458     int rc = VINF_SUCCESS;
    459     bool success = false;
    460 
    461     try
    462     {
    463         RTMemAutoPtr<char, RTStrFree> drive;
    464         const char *pszValue = RTEnvGet (pszVar);
    465         if (pszValue != NULL)
    466         {
    467             drive = RTStrDup (pszValue);
    468             if (!drive)
    469                 rc = VERR_NO_MEMORY;
    470         }
    471         if (pszValue != NULL && RT_SUCCESS(rc))
    472         {
    473             char *pDrive = drive.get();
    474             char *pDriveNext = strchr (pDrive, ':');
    475             while (pDrive != NULL && *pDrive != '\0')
    476             {
    477                 if (pDriveNext != NULL)
    478                     *pDriveNext = '\0';
    479                 if (validateDevice(pDrive, isDVD))
    480                 {
    481                     pList->push_back (DriveInfo (pDrive));
    482                     success = true;
    483                 }
    484                 if (pDriveNext != NULL)
    485                 {
    486                     pDrive = pDriveNext + 1;
    487                     pDriveNext = strchr (pDrive, ':');
    488                 }
    489                 else
    490                     pDrive = NULL;
    491             }
    492         }
    493         if (pfSuccess != NULL)
    494             *pfSuccess = success;
    495     }
    496     catch(std::bad_alloc &e)
    497     {
    498         rc = VERR_NO_MEMORY;
    499     }
    500     LogFlowFunc (("rc=%Rrc, success=%d\n", rc, success));
    501     return rc;
    502 }
    503 
    504 #ifdef RT_OS_LINUX
    505 /**
    506  * Helper function to parse the given mount file and add found entries
    507  */
    508 /* static */
    509 int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList)
    510 {
    511     AssertReturn(VALID_PTR (mountTable) && VALID_PTR (pList),
    512                  VERR_INVALID_POINTER);
    513 #ifdef RT_OS_LINUX
    514     LogFlowFunc (("mountTable=%s, pList=%p\n", mountTable, pList));
    515     int rc = VINF_SUCCESS;
    516     FILE *mtab = setmntent(mountTable, "r");
    517     if (mtab)
    518     {
    519         try
    520         {
    521             struct mntent *mntent;
    522             RTMemAutoPtr <char, RTStrFree> mnt_type, mnt_dev;
    523             char *tmp;
    524             while (RT_SUCCESS(rc) && (mntent = getmntent(mtab)))
    525             {
    526                 mnt_type = RTStrDup (mntent->mnt_type);
    527                 mnt_dev = RTStrDup (mntent->mnt_fsname);
    528                 if (!mnt_type || !mnt_dev)
    529                     rc = VERR_NO_MEMORY;
    530                 // supermount fs case
    531                 if (RT_SUCCESS(rc) && strcmp(mnt_type.get(), "supermount") == 0)
    532                 {
    533                     tmp = strstr(mntent->mnt_opts, "fs=");
    534                     if (tmp)
    535                     {
    536                         mnt_type = RTStrDup(tmp + strlen("fs="));
    537                         if (!mnt_type)
    538                             rc = VERR_NO_MEMORY;
    539                         else
    540                         {
    541                             tmp = strchr(mnt_type.get(), ',');
    542                             if (tmp)
    543                                 *tmp = '\0';
    544                         }
    545                     }
    546                     tmp = strstr(mntent->mnt_opts, "dev=");
    547                     if (tmp)
    548                     {
    549                         mnt_dev = RTStrDup(tmp + strlen("dev="));
    550                         if (!mnt_dev)
    551                             rc = VERR_NO_MEMORY;
    552                         else
    553                         {
    554                             tmp = strchr(mnt_dev.get(), ',');
    555                             if (tmp)
    556                                 *tmp = '\0';
    557                         }
    558                     }
    559                 }
    560                 // use strstr here to cover things fs types like "udf,iso9660"
    561                 if (RT_SUCCESS(rc) && strstr(mnt_type.get(), "iso9660") == 0)
    562                 {
    563                     if (validateDevice(mnt_dev.get(), true))
    564                     {
    565                         bool insert = true;
    566                         struct stat srcInfo;
    567                         if (stat (mnt_dev.get(), &srcInfo) < 0)
    568                             insert = false;
    569                         for (DriveInfoList::const_iterator it = pList->begin();
    570                             insert && it != pList->end(); ++it)
    571                         {
    572                             struct stat destInfo;
    573                             if (   (stat (it->mDevice.c_str(), &destInfo) == 0)
    574                                 && (srcInfo.st_rdev == destInfo.st_rdev))
    575                                 insert = false;
    576                         }
    577                         if (insert)
    578                             pList->push_back (DriveInfo (mnt_dev.get()));
    579                     }
    580                 }
    581             }
    582         }
    583         catch(std::bad_alloc &e)
    584         {
    585             rc = VERR_NO_MEMORY;
    586         }
    587         endmntent(mtab);
    588     }
    589     return rc;
    590 #endif
    591 }
    592 
    593 #endif  /* RT_OS_LINUX */
    5941126
    5951127#if defined(RT_OS_LINUX) && defined(VBOX_WITH_DBUS)
     
    6111143        return (mError.name != NULL);
    6121144    }
    613     bool HasName (const char *pszName)
     1145    bool HasName (const char *pcszName)
    6141146    {
    6151147        Assert ((mError.name == NULL) == (mError.message == NULL));
    616         return (RTStrCmp (mError.name, pszName) == 0);
     1148        return (RTStrCmp (mError.name, pcszName) == 0);
    6171149    }
    6181150    void FlowLog ()
     
    10751607}
    10761608
    1077 /**
    1078  * Send an SCSI INQUIRY command to a device and return selected information.
    1079  * @returns  iprt status code
    1080  * @returns  VERR_TRY_AGAIN if the query failed but might succeed next time
    1081  * @param pszNode    the full path to the device node
    1082  * @param pu8Type    where to store the SCSI device type on success (optional)
    1083  * @param pchVendor  where to store the vendor id string on success (optional)
    1084  * @param cchVendor  the size of the @a pchVendor buffer
    1085  * @param pchModel   where to store the product id string on success (optional)
    1086  * @param cchModel   the size of the @a pchModel buffer
    1087  * @note check documentation on the SCSI INQUIRY command and the Linux kernel
    1088  *       SCSI headers included above if you want to understand what is going
    1089  *       on in this method.
    1090  */
    1091 /* static */
    1092 int scsiDoInquiry(const char *pszNode, uint8_t *pu8Type, char *pchVendor,
    1093                   size_t cchVendor, char *pchModel, size_t cchModel)
    1094 {
    1095     LogRelFlowFunc(("pszNode=%s, pu8Type=%p, pchVendor=%p, cchVendor=%llu, pchModel=%p, cchModel=%llu\n",
    1096                     pszNode, pu8Type, pchVendor, cchVendor, pchModel,
    1097                     cchModel));
    1098     AssertPtrReturn(pszNode, VERR_INVALID_POINTER);
    1099     AssertPtrNullReturn(pu8Type, VERR_INVALID_POINTER);
    1100     AssertPtrNullReturn(pchVendor, VERR_INVALID_POINTER);
    1101     AssertPtrNullReturn(pchModel, VERR_INVALID_POINTER);
    1102 
    1103     sg_io_hdr_t ScsiIoReq = {0};
    1104     unsigned char u8Response[96] = { 0 };
    1105     unsigned char u8Command[6] =
    1106         { INQUIRY, 0, 0, 0, sizeof(u8Response), 0 };  /* INQUIRY */
    1107     int rc, rcIoCtl = 0;
    1108     RTFILE file;
    1109     rc = RTFileOpen(&file, pszNode, RTFILE_O_READ);
    1110     if (RT_SUCCESS(rc))
    1111     {
    1112         ScsiIoReq.interface_id = 'S';
    1113         ScsiIoReq.dxfer_direction = SG_DXFER_FROM_DEV;
    1114         ScsiIoReq.cmd_len = sizeof(u8Command);
    1115         ScsiIoReq.dxfer_len = sizeof(u8Response);
    1116         ScsiIoReq.dxferp = u8Response;
    1117         ScsiIoReq.cmdp = u8Command;
    1118         ScsiIoReq.timeout = 5000 /* ms */;
    1119         rc = RTFileIoCtl(file, SG_IO, &ScsiIoReq, 0, &rcIoCtl);
    1120         if (RT_SUCCESS(rc) && rcIoCtl < 0)
    1121             rc = VERR_NOT_SUPPORTED;
    1122         RTFileClose(file);
    1123     }
    1124     if (RT_SUCCESS(rc))
    1125     {
    1126         if (   (ScsiIoReq.status >> 1 == GOOD)
    1127             || (ScsiIoReq.status >> 1 == INTERMEDIATE_GOOD)
    1128             || (ScsiIoReq.status >> 1 == INTERMEDIATE_C_GOOD)
    1129             || (ScsiIoReq.status >> 1 == COMMAND_TERMINATED))
    1130         {
    1131             if (pu8Type)
    1132                 *pu8Type = u8Response[0] & 0x1f;
    1133             if (pchVendor)
    1134                 RTStrPrintf(pchVendor, cchVendor, "%.8s",
    1135                             (char *) &u8Response[8] /* vendor id string */);
    1136             if (pchModel)
    1137                 RTStrPrintf(pchModel, cchModel, "%.16s",
    1138                             (char *) &u8Response[16] /* product id string */);
    1139         }
    1140         else if (   ScsiIoReq.status >> 1 != BUSY
    1141                  && ScsiIoReq.status >> 1 != QUEUE_FULL
    1142                  && ScsiIoReq.status >> 1 != CHECK_CONDITION)
    1143             rc = VERR_DEV_IO_ERROR;  /* Actually, these should never happen */
    1144         else
    1145             rc = VERR_TRY_AGAIN;
    1146     }
    1147     LogRelFlowFunc(("returning %Rrc\n", rc));
    1148     if (RT_SUCCESS(rc))
    1149         LogRelFlowFunc(("    type=%u, vendor=%.8s, product=%.16s\n",
    1150                         u8Response[0] & 0x1f, (char *) &u8Response[8],
    1151                         (char *) &u8Response[16]));
    1152     return rc;
    1153 }
    1154 
    1155 class sysfsBlockDev
    1156 {
    1157 public:
    1158     sysfsBlockDev(const char *pcszName, bool wantDVD)
    1159             : mpcszName(pcszName), mwantDVD(wantDVD), misValid(false)
    1160     {
    1161         if (findDeviceNode())
    1162         {
    1163             if (mwantDVD)
    1164                 validateAndInitForDVD();
    1165             else
    1166                 validateAndInitForFloppy();
    1167         }
    1168     }
    1169 private:
    1170     /** The name of the subdirectory of /sys/block for this device */
    1171     const char *mpcszName;
    1172     /** Are we looking for a floppy or a DVD device? */
    1173     bool mwantDVD;
    1174     /** The device node for the device */
    1175     char mszNode[RTPATH_MAX];
    1176     /** Is this entry a valid specimen of what we are looking for? */
    1177     bool misValid;
    1178     /** Human readible drive description string */
    1179     char mszDesc[256];
    1180     /** Unique identifier for the drive.  Should be identical to hal's UDI for
    1181      * the device.  May not be unique for two identical drives. */
    1182     char mszUdi[256];
    1183 private:
    1184     /* Private methods */
    1185 
    1186     /**
    1187      * Fill in the device node member based on the /sys/block subdirectory.
    1188      * @returns boolean success value
    1189      */
    1190     bool findDeviceNode()
    1191     {
    1192         dev_t dev = RTLinuxSysFsReadDevNumFile("block/%s/dev", mpcszName);
    1193         if (dev == 0)
    1194             return false;
    1195         if (RTLinuxFindDevicePath(dev, RTFS_TYPE_DEV_BLOCK, mszNode,
    1196                                   sizeof(mszNode), "%s", mpcszName) < 0)
    1197             return false;
    1198         return true;
    1199     }
    1200 
    1201     /** Check whether the sysfs block entry is valid for a DVD device and
    1202      * initialise the string data members for the object.  We try to get all
    1203      * the information we need from sysfs if possible, to avoid unnecessarily
    1204      * poking the device, and if that fails we fall back to an SCSI INQUIRY
    1205      * command. */
    1206     void validateAndInitForDVD()
    1207     {
    1208         char szVendor[128], szModel[128];
    1209         ssize_t cchVendor, cchModel;
    1210         int64_t type = RTLinuxSysFsReadIntFile(10, "block/%s/device/type",
    1211                                                mpcszName);
    1212         if (type >= 0 && type != TYPE_ROM)
    1213             return;
    1214         if (type == 5)
    1215         {
    1216             cchVendor = RTLinuxSysFsReadStrFile(szVendor, sizeof(szVendor),
    1217                                                 "block/%s/device/vendor",
    1218                                                 mpcszName);
    1219             if (cchVendor >= 0)
    1220             {
    1221                 cchModel = RTLinuxSysFsReadStrFile(szModel, sizeof(szModel),
    1222                                                    "block/%s/device/model",
    1223                                                    mpcszName);
    1224                 if (cchModel >= 0)
    1225                 {
    1226                     misValid = true;
    1227                     setDeviceStrings(szVendor, szModel);
    1228                     return;
    1229                 }
    1230             }
    1231         }
    1232         probeAndInitForDVD();
    1233     }
    1234 
    1235     /** Try to find out whether a device is a DVD drive by sending it an
    1236      * SCSI INQUIRY command.  If it is, initialise the string and validity
    1237      * data members for the object based on the returned data.
    1238      */
    1239     void probeAndInitForDVD()
    1240     {
    1241         AssertReturnVoid(mszNode[0] != '\0');
    1242         uint8_t u8Type = 0;
    1243         char szVendor[128] = "";
    1244         char szModel[128] = "";
    1245         for (unsigned i = 0; i < 5; ++i)  /* Give the device five chances */
    1246         {
    1247             int rc = scsiDoInquiry(mszNode, &u8Type, szVendor,
    1248                                    sizeof(szVendor), szModel,
    1249                                    sizeof(szModel));
    1250             if (RT_SUCCESS(rc))
    1251             {
    1252                 if (u8Type != TYPE_ROM)
    1253                     return;
    1254                 misValid = true;
    1255                 setDeviceStrings(szVendor, szModel);
    1256                 return;
    1257             }
    1258             if (rc != VERR_TRY_AGAIN)
    1259                 return;
    1260             RTThreadSleep(100);  /* wait a little before retrying */
    1261         }
    1262     }
    1263 
    1264     /**
    1265      * Initialise the object device strings (description and UDI) based on
    1266      * vendor and model name strings.
    1267      * @param pszVendor  the vendor ID string
    1268      * @param pszModel   the product ID string
    1269      */
    1270     void setDeviceStrings(const char *pszVendor, const char *pszModel)
    1271     {
    1272         char szCleaned[128];
    1273         size_t cchVendor = strLenStripped(pszVendor);
    1274         size_t cchModel = strLenStripped(pszModel);
    1275 
    1276         /* Create a cleaned version of the model string for the UDI string. */
    1277         for (unsigned i = 0; pszModel[i] != '\0' && i < sizeof(szCleaned); ++i)
    1278             if (   (pszModel[i] >= '0' && pszModel[i] <= '9')
    1279                 || (pszModel[i] >= 'A' && pszModel[i] <= 'z'))
    1280                 szCleaned[i] = pszModel[i];
    1281             else
    1282                 szCleaned[i] = '_';
    1283         szCleaned[RT_MIN(cchModel, sizeof(szCleaned) - 1)] = '\0';
    1284 
    1285         /* Construct the description string as "Vendor Product" */
    1286         if (cchVendor > 0)
    1287             RTStrPrintf(mszDesc, sizeof(mszDesc), "%.*s %s", cchVendor,
    1288                         pszVendor,
    1289                         cchModel > 0 ? pszModel : "(unknown drive model)");
    1290         else
    1291             RTStrPrintf(mszDesc, sizeof(mszDesc), "%s", pszModel);
    1292         /* Construct the UDI string */
    1293         if (cchModel)
    1294             RTStrPrintf(mszUdi, sizeof(mszUdi),
    1295                         "/org/freedesktop/Hal/devices/storage_model_%s",
    1296                         szCleaned);
    1297         else
    1298             mszUdi[0] = '\0';
    1299     }
    1300 
    1301     /** Check whether the sysfs block entry is valid for a floppy device and
    1302      * initialise the string data members for the object.  Since we only
    1303      * support floppies using the basic "floppy" driver, we check the driver
    1304      * using the entry name and a driver-specific ioctl. */
    1305     void validateAndInitForFloppy()
    1306     {
    1307         floppy_drive_name szName;
    1308         int rcIoCtl;
    1309         if (   mpcszName[0] != 'f'
    1310             || mpcszName[1] != 'd'
    1311             || mpcszName[2] < '0'
    1312             || mpcszName[2] > '3'
    1313             || mpcszName[3] != '\0')
    1314             return;
    1315         RTFILE file;
    1316         int rc = RTFileOpen(&file, mszNode, RTFILE_O_READ);
    1317         /** @todo The next line can produce a warning, as the ioctl request
    1318          * field is defined as signed, but the Linux ioctl definition macros
    1319          * produce unsigned constants. */
    1320         rc = RTFileIoCtl(file, FDGETDRVTYP, szName, 0, &rcIoCtl);
    1321         RTFileClose(file);
    1322         if (rcIoCtl < 0)
    1323             return;
    1324         misValid = true;
    1325         strcpy(mszDesc,   (mpcszName[2] == '0') ? "PC Floppy drive"
    1326                         : (mpcszName[2] == '1') ? "Second PC Floppy drive"
    1327                         : (mpcszName[2] == '2') ? "Third PC Floppy drive"
    1328                         : "Fourth PC Floppy drive");
    1329         RTStrPrintf(mszUdi, sizeof(mszUdi),
    1330                     "/org/freedesktop/Hal/devices/platform_floppy_%u_storage",
    1331                     mpcszName[2]);
    1332     }
    1333 
    1334 public:
    1335     bool isValid()
    1336     {
    1337         return misValid;
    1338     }
    1339     const char *getDesc()
    1340     {
    1341         return mszDesc;
    1342     }
    1343     const char *getUdi()
    1344     {
    1345         return mszUdi;
    1346     }
    1347     const char *getNode()
    1348     {
    1349         return mszNode;
    1350     }
    1351 };
    1352 
    1353 /**
    1354  * Helper function to query the sysfs subsystem for information about DVD
    1355  * drives attached to the system.
    1356  * @returns iprt status code
    1357  * @param   pList      where to add information about the drives detected
    1358  * @param   isDVD      are we looking for DVDs or floppies?
    1359  * @param   pfSuccess  Did we find anything?
    1360  *
    1361  * @returns IPRT status code
    1362  */
    1363 /* static */
    1364 int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
    1365 {
    1366     AssertPtrReturn(pList, VERR_INVALID_POINTER);
    1367     AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER); /* Valid or Null */
    1368     LogFlowFunc (("pList=%p, isDVD=%u, pfSuccess=%p\n",
    1369                   pList, (unsigned) isDVD, pfSuccess));
    1370     PRTDIR pDir = NULL;
    1371     RTDIRENTRY entry = {0};
    1372     int rc;
    1373     bool fSuccess;
    1374     unsigned cFound = 0;
    1375 
    1376     rc = RTDirOpen(&pDir, "/sys/block");
    1377     if (RT_SUCCESS(rc))
    1378         while (true)
    1379         {
    1380             rc = RTDirRead(pDir, &entry, NULL);
    1381             Assert(rc != VERR_BUFFER_OVERFLOW);  /* Should never happen... */
    1382             if (RT_FAILURE(rc))  /* Including overflow and no more files */
    1383                 break;
    1384             if (entry.szName[0] == '.')
    1385                 continue;
    1386             sysfsBlockDev dev(entry.szName, isDVD);
    1387             if (!dev.isValid())
    1388                 continue;
    1389             try
    1390             {
    1391                 pList->push_back(DriveInfo(dev.getNode(), dev.getUdi(),
    1392                                            dev.getDesc()));
    1393             }
    1394             catch(std::bad_alloc &e)
    1395             {
    1396                 rc = VERR_NO_MEMORY;
    1397                 break;
    1398             }
    1399             ++cFound;
    1400         }
    1401     RTDirClose(pDir);
    1402     if (rc == VERR_NO_MORE_FILES)
    1403         rc = VINF_SUCCESS;
    1404     fSuccess = (RT_SUCCESS(rc) && cFound > 0);
    1405     if (!fSuccess)
    1406         /* Clean up again */
    1407         for (unsigned i = 0; i < cFound; ++i)
    1408             pList->pop_back();
    1409     if (pfSuccess)
    1410         *pfSuccess = fSuccess;
    1411     LogFlow (("rc=%Rrc, fSuccess=%u\n", rc, (unsigned) fSuccess));
    1412     return rc;
    1413 }
    1414 
    1415 /**
    1416  * Helper function to query the hal subsystem for information about drives
    1417  * attached to the system.
    1418  * @returns iprt status code
    1419  * @param   pList      where to add information about the drives detected
    1420  * @param   isDVD      are we looking for DVDs or floppies?
    1421  * @param   pfSuccess  will be set to true if all interactions with hal
    1422  *                     succeeded and to false otherwise.  Optional.
    1423  *
    1424  * @returns IPRT status code
    1425  */
    1426 /* static */
    1427 int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD, bool *pfSuccess)
    1428 {
    1429     AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
    1430                  VERR_INVALID_POINTER);
    1431     LogFlowFunc (("pList=%p, isDVD=%d, pfSuccess=%p\n", pList, isDVD, pfSuccess));
    1432     int rc = VINF_SUCCESS;  /* We set this to failure on fatal errors. */
    1433     bool halSuccess = true;  /* We set this to false to abort the operation. */
    1434     autoDBusError dbusError;
    1435 
    1436     RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
    1437     RTMemAutoPtr <DBusConnection, VBoxHalShutdown> dbusConnection;
    1438     DBusMessageIter iterFind, iterUdis;
    1439 
    1440     try
    1441     {
    1442         rc = halInit (&dbusConnection);
    1443         if (!dbusConnection)
    1444             halSuccess = false;
    1445         if (halSuccess && RT_SUCCESS(rc))
    1446         {
    1447             rc = halFindDeviceStringMatch (dbusConnection.get(), "storage.drive_type",
    1448                                         isDVD ? "cdrom" : "floppy", &replyFind);
    1449             if (!replyFind)
    1450                 halSuccess = false;
    1451         }
    1452         if (halSuccess && RT_SUCCESS(rc))
    1453         {
    1454             dbus_message_iter_init (replyFind.get(), &iterFind);
    1455             if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
    1456                 halSuccess = false;
    1457         }
    1458         if (halSuccess && RT_SUCCESS(rc))
    1459             dbus_message_iter_recurse (&iterFind, &iterUdis);
    1460         for (;    halSuccess && RT_SUCCESS(rc)
    1461             && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
    1462             dbus_message_iter_next(&iterUdis))
    1463         {
    1464             /* Now get all properties from the iterator */
    1465             const char *pszUdi;
    1466             dbus_message_iter_get_basic (&iterUdis, &pszUdi);
    1467             static const char *papszKeys[] =
    1468                     { "block.device", "info.product", "info.vendor" };
    1469             char *papszValues[RT_ELEMENTS (papszKeys)];
    1470             rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
    1471                                         papszKeys, papszValues, &replyGet);
    1472             iprt::MiniString description;
    1473             const char *pszDevice = papszValues[0], *pszProduct = papszValues[1],
    1474                     *pszVendor = papszValues[2];
    1475             if (!!replyGet && pszDevice == NULL)
    1476                 halSuccess = false;
    1477             if (!!replyGet && pszDevice != NULL)
    1478             {
    1479                 if ((pszVendor != NULL) && (pszVendor[0] != '\0'))
    1480                 {
    1481                     description.append(pszVendor);
    1482                     description.append(" ");
    1483                 }
    1484                 if ((pszProduct != NULL && pszProduct[0] != '\0'))
    1485                     description.append(pszProduct);
    1486                 pList->push_back (DriveInfo (pszDevice, pszUdi, description));
    1487             }
    1488         }
    1489         if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
    1490             rc = VERR_NO_MEMORY;
    1491         /* If we found nothing something may have gone wrong with hal, so
    1492          * report failure to fall back to other methods. */
    1493         if (pList->size() == 0)
    1494             halSuccess = false;
    1495         if (pfSuccess != NULL)
    1496             *pfSuccess = halSuccess;
    1497     }
    1498     catch(std::bad_alloc &e)
    1499     {
    1500         rc = VERR_NO_MEMORY;
    1501     }
    1502     LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
    1503     dbusError.FlowLog();
    1504     return rc;
    1505 }
    15061609
    15071610/**
Note: See TracChangeset for help on using the changeset viewer.

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