Changeset 23522 in vbox for trunk/src/VBox/Main/linux
- Timestamp:
- Oct 2, 2009 11:27:33 PM (16 years ago)
- svn:sync-xref-src-repo-rev:
- 53164
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/linux/HostHardwareLinux.cpp
r23386 r23522 37 37 #include <iprt/mem.h> 38 38 #include <iprt/param.h> 39 #include <iprt/path.h> 39 40 #include <iprt/thread.h> /* for RTThreadSleep() */ 40 41 #include <iprt/string.h> … … 54 55 # include <linux/cdrom.h> 55 56 # include <linux/fd.h> 57 # include <linux/major.h> 56 58 # ifdef VBOX_WITH_DBUS 57 59 # include <vbox-dbus.h> … … 59 61 # include <errno.h> 60 62 # include <scsi/scsi.h> 61 # include <scsi/sg.h>62 63 63 64 # include <iprt/linux/sysfs.h> … … 65 66 #include <vector> 66 67 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 73 static bool testing() { return true; } 74 static bool fNoProbe = false; 75 static bool noProbe() { return fNoProbe; } 76 static void setNoProbe(bool val) { fNoProbe = val; } 77 #else 78 static bool testing() { return false; } 79 static bool noProbe() { return false; } 80 static void setNoProbe(bool val) { (void)val; } 81 #endif 82 83 /****************************************************************************** 84 * Typedefs and Defines * 85 ******************************************************************************/ 77 86 78 87 /** When waiting for hotplug events, we currently restart the wait after at … … 80 89 enum { DBUS_POLL_TIMEOUT = 2000 /* ms */ }; 81 90 82 83 static bool validateDevice(const char *deviceNode, bool isDVD); 84 static int getDriveInfoFromEnv(const char *pszVar, DriveInfoList *pList, 91 static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, 85 92 bool isDVD, bool *pfSuccess); 93 static int getDriveInfoFromDev(DriveInfoList *pList, bool isDVD, 94 bool *pfSuccess); 86 95 static int getDriveInfoFromSysfs(DriveInfoList *pList, bool isDVD, 87 96 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);91 97 #ifdef VBOX_WITH_DBUS 92 98 /* These must be extern to be usable in the RTMemAutoPtr template */ … … 119 125 bool *pfMatches, bool *pfSuccess); 120 126 */ 121 static int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD,122 bool *pfSuccess);123 127 static int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess); 124 128 static int getOldUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess); … … 129 133 #endif /* VBOX_WITH_DBUS */ 130 134 135 131 136 /** Find the length of a string, ignoring trailing non-ascii or control 132 137 * characters */ 133 static size_t strLenStripped(const char *p sz)138 static size_t strLenStripped(const char *pcsz) 134 139 { 135 140 size_t cch = 0; 136 for (size_t i = 0; p sz[i] != '\0'; ++i)137 if (p sz[i] > 32 && psz[i] < 127)141 for (size_t i = 0; pcsz[i] != '\0'; ++i) 142 if (pcsz[i] > 32 && pcsz[i] < 127) 138 143 cch = i; 139 144 return cch + 1; 140 145 } 146 147 148 static 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 */ 186 static 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 228 static 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 */ 302 static 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 */ 363 void 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 */ 420 static 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 141 479 142 480 int VBoxMainDriveInfo::updateDVDs () … … 148 486 { 149 487 mDVDList.clear (); 150 #if defined(RT_OS_LINUX)151 488 /* Always allow the user to override our auto-detection using an 152 489 * environment variable. */ … … 154 491 rc = getDriveInfoFromEnv ("VBOX_CDROM", &mDVDList, true /* isDVD */, 155 492 &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); 160 494 if (RT_SUCCESS(rc) && (!success | testing())) 161 495 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); 181 504 } 182 505 catch(std::bad_alloc &e) … … 196 519 { 197 520 mFloppyList.clear (); 198 #if defined(RT_OS_LINUX)199 521 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); 229 537 } 230 538 catch(std::bad_alloc &e) … … 235 543 return rc; 236 544 } 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 */ 560 int 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 605 class sysfsBlockDev 606 { 607 public: 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 } 620 private: 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]; 637 private: 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 747 public: 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 */ 781 int 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 */ 840 struct 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. */ 853 enum { MAX_DEVICE_NODES = 8 }; 854 /** An array of MAX_DEVICE_NODES devices */ 855 typedef 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 */ 869 static 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 */ 956 int 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 237 990 238 991 int VBoxMainUSBDeviceInfo::UpdateDevices () … … 371 1124 } 372 1125 373 #ifdef RT_OS_LINUX374 /**375 * Helper function to check whether the given device node is a valid drive376 */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 check386 if (!deviceNode)387 {388 return false;389 }390 391 // first a simple stat() call392 if (stat(deviceNode, &statInfo) < 0)393 {394 return false;395 } else396 {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 device403 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 truth409 #ifdef RT_OS_LINUX410 if ((ioctl(fileHandle, CDROMSUBCHNL, &cdChannelInfo) == 0) ||411 (errno == EIO) || (errno == ENOENT) ||412 (errno == EINVAL) || (errno == ENOMEDIUM))413 #endif414 {415 retValue = true;416 }417 close(fileHandle);418 }419 }420 } else421 {422 // floppy case423 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 a439 * list if they are valid.440 * @returns iprt status code441 * @param pszVar the name of the environment variable. The variable442 * value should be a list of device node names, separated443 * by ':' characters.444 * @param pList the list to append the drives found to445 * @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 drive447 * 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 try462 {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 else490 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_LINUX505 /**506 * Helper function to parse the given mount file and add found entries507 */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_LINUX514 LogFlowFunc (("mountTable=%s, pList=%p\n", mountTable, pList));515 int rc = VINF_SUCCESS;516 FILE *mtab = setmntent(mountTable, "r");517 if (mtab)518 {519 try520 {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 case531 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 else540 {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 else553 {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 #endif591 }592 593 #endif /* RT_OS_LINUX */594 1126 595 1127 #if defined(RT_OS_LINUX) && defined(VBOX_WITH_DBUS) … … 611 1143 return (mError.name != NULL); 612 1144 } 613 bool HasName (const char *p szName)1145 bool HasName (const char *pcszName) 614 1146 { 615 1147 Assert ((mError.name == NULL) == (mError.message == NULL)); 616 return (RTStrCmp (mError.name, p szName) == 0);1148 return (RTStrCmp (mError.name, pcszName) == 0); 617 1149 } 618 1150 void FlowLog () … … 1075 1607 } 1076 1608 1077 /**1078 * Send an SCSI INQUIRY command to a device and return selected information.1079 * @returns iprt status code1080 * @returns VERR_TRY_AGAIN if the query failed but might succeed next time1081 * @param pszNode the full path to the device node1082 * @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 buffer1085 * @param pchModel where to store the product id string on success (optional)1086 * @param cchModel the size of the @a pchModel buffer1087 * @note check documentation on the SCSI INQUIRY command and the Linux kernel1088 * SCSI headers included above if you want to understand what is going1089 * 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 != BUSY1141 && ScsiIoReq.status >> 1 != QUEUE_FULL1142 && ScsiIoReq.status >> 1 != CHECK_CONDITION)1143 rc = VERR_DEV_IO_ERROR; /* Actually, these should never happen */1144 else1145 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 sysfsBlockDev1156 {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 else1166 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 for1181 * 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 value1189 */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 and1202 * initialise the string data members for the object. We try to get all1203 * the information we need from sysfs if possible, to avoid unnecessarily1204 * poking the device, and if that fails we fall back to an SCSI INQUIRY1205 * 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 an1236 * SCSI INQUIRY command. If it is, initialise the string and validity1237 * 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 on1266 * vendor and model name strings.1267 * @param pszVendor the vendor ID string1268 * @param pszModel the product ID string1269 */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 else1282 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 else1291 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 else1298 mszUdi[0] = '\0';1299 }1300 1301 /** Check whether the sysfs block entry is valid for a floppy device and1302 * initialise the string data members for the object. Since we only1303 * support floppies using the basic "floppy" driver, we check the driver1304 * 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 request1318 * field is defined as signed, but the Linux ioctl definition macros1319 * 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 DVD1355 * drives attached to the system.1356 * @returns iprt status code1357 * @param pList where to add information about the drives detected1358 * @param isDVD are we looking for DVDs or floppies?1359 * @param pfSuccess Did we find anything?1360 *1361 * @returns IPRT status code1362 */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 try1390 {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 drives1417 * attached to the system.1418 * @returns iprt status code1419 * @param pList where to add information about the drives detected1420 * @param isDVD are we looking for DVDs or floppies?1421 * @param pfSuccess will be set to true if all interactions with hal1422 * succeeded and to false otherwise. Optional.1423 *1424 * @returns IPRT status code1425 */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 try1441 {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, so1492 * 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 }1506 1609 1507 1610 /**
Note:
See TracChangeset
for help on using the changeset viewer.