Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2520 lines
62 KiB

  1. /*++
  2. Copyright (c) 1996-1997 Microsoft Corporation
  3. Module Name:
  4. scsi.c
  5. Abstract:
  6. Common routines for dealing with SCSI disks, usable
  7. by both raw disks and FT sets
  8. Author:
  9. John Vert (jvert) 11/6/1996
  10. Revision History:
  11. --*/
  12. #include <nt.h>
  13. #include <ntdef.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #include <windows.h>
  17. #include <ntddstor.h> // IOCTL_STORAGE_QUERY_PROPERTY
  18. #include "disksp.h"
  19. #include "newmount.h"
  20. #include <string.h>
  21. #include <shlwapi.h> // SHDeleteKey
  22. #include <ntddvol.h> // IOCTL_VOLUME_QUERY_FAILOVER_SET
  23. #include <setupapi.h>
  24. //
  25. // The registry key containing the system partition
  26. //
  27. const CHAR DISKS_REGKEY_SETUP[] = "SYSTEM\\SETUP";
  28. const CHAR DISKS_REGVALUE_SYSTEM_PARTITION[] = "SystemPartition";
  29. extern PWCHAR DEVICE_HARDDISK_PARTITION_FMT; // L"\\Device\\Harddisk%u\\Partition%u" //
  30. extern PWCHAR GLOBALROOT_HARDDISK_PARTITION_FMT; // L"\\\\\?\\GLOBALROOT\\Device\\Harddisk%u\\Partition%u\\"
  31. extern DWORD SystemDiskAddressFound;
  32. extern PSCSI_ADDRESS_ENTRY SysDiskAddrList;
  33. #define INVALID_SCSIADDRESS_VALUE (DWORD)-1
  34. typedef struct _SCSI_PASS_THROUGH_WITH_SENSE {
  35. SCSI_PASS_THROUGH Spt;
  36. UCHAR SenseBuf[32];
  37. } SCSI_PASS_THROUGH_WITH_SENSE, *PSCSI_PASS_THROUGH_WITH_SENSE;
  38. typedef struct _UPDATE_AVAIL_DISKS {
  39. HKEY AvailDisksKey;
  40. HKEY SigKey;
  41. DWORD EnableSanBoot;
  42. BOOL SigKeyIsEmpty;
  43. } UPDATE_AVAIL_DISKS, *PUPDATE_AVAIL_DISKS;
  44. typedef struct _SCSI_INFO {
  45. DWORD Signature;
  46. DWORD DiskNumber;
  47. DWORD ScsiAddress;
  48. } SCSI_INFO, *PSCSI_INFO;
  49. typedef struct _SERIAL_INFO {
  50. DWORD Signature;
  51. DWORD Error;
  52. LPWSTR SerialNumber;
  53. } SERIAL_INFO, *PSERIAL_INFO;
  54. typedef
  55. DWORD
  56. (*LPDISK_ENUM_CALLBACK) (
  57. HANDLE DeviceHandle,
  58. DWORD Index,
  59. PVOID Param1
  60. );
  61. //
  62. // Local Routines
  63. //
  64. DWORD
  65. AddSignatureToRegistry(
  66. HKEY RegKey,
  67. DWORD Signature
  68. );
  69. BOOL
  70. IsClusterCapable(
  71. IN DWORD Signature
  72. );
  73. BOOL
  74. IsSignatureInRegistry(
  75. HKEY RegKey,
  76. DWORD Signature
  77. );
  78. DWORD
  79. UpdateAvailableDisks(
  80. );
  81. DWORD
  82. UpdateAvailableDisksCallback(
  83. HANDLE DeviceHandle,
  84. DWORD Index,
  85. PVOID Param1
  86. );
  87. DWORD
  88. AddScsiAddressToList(
  89. PSCSI_ADDRESS ScsiAddress
  90. );
  91. DWORD
  92. GetVolumeFailoverSet(
  93. IN HANDLE DevHandle,
  94. OUT PVOLUME_FAILOVER_SET *FailoverSet
  95. );
  96. DWORD
  97. BuildScsiListFromFailover(
  98. IN HANDLE DevHandle
  99. );
  100. HANDLE
  101. OpenNtldrDisk(
  102. );
  103. HANDLE
  104. OpenOSDisk(
  105. );
  106. DWORD
  107. EnumerateDisks(
  108. LPDISK_ENUM_CALLBACK DiskEnumCallback,
  109. PVOID Param1
  110. );
  111. DWORD
  112. GetScsiAddressCallback(
  113. HANDLE DevHandle,
  114. DWORD Index,
  115. PVOID Param1
  116. );
  117. DWORD
  118. GetSerialNumberCallback(
  119. HANDLE DevHandle,
  120. DWORD Index,
  121. PVOID Param1
  122. );
  123. DWORD
  124. GetSigFromSerNumCallback(
  125. HANDLE DevHandle,
  126. DWORD Index,
  127. PVOID Param1
  128. );
  129. DWORD
  130. RetrieveSerialNumber(
  131. HANDLE DevHandle,
  132. LPWSTR *SerialNumber
  133. );
  134. DWORD
  135. ScsiIsAlive(
  136. IN HANDLE DiskHandle
  137. )
  138. /*++
  139. Routine Description:
  140. Sends a SCSI passthrough command down to the disk to see if
  141. it is still there.
  142. Arguments:
  143. DiskHandle - Supplies a handle to the disk.
  144. Return Value:
  145. ERROR_SUCCESS if successful
  146. Win32 error code otherwise
  147. --*/
  148. {
  149. BOOL success;
  150. DWORD bytesReturned;
  151. success = DeviceIoControl( DiskHandle,
  152. IOCTL_DISK_CLUSTER_ALIVE_CHECK,
  153. NULL,
  154. 0,
  155. NULL,
  156. 0,
  157. &bytesReturned,
  158. FALSE );
  159. if ( !success ) {
  160. return(GetLastError());
  161. }
  162. return(ERROR_SUCCESS);
  163. }
  164. DWORD
  165. ClusDiskGetAvailableDisks(
  166. OUT PVOID OutBuffer,
  167. IN DWORD OutBufferSize,
  168. OUT LPDWORD BytesReturned,
  169. IN BOOL FtSet
  170. )
  171. /*++
  172. Routine Description:
  173. Enumerate and build a list of available disks on this system.
  174. Arguments:
  175. OutBuffer - pointer to the output buffer to return the data.
  176. OutBufferSize - size of the output buffer.
  177. BytesReturned - the actual number of bytes that were returned (or
  178. the number of bytes that should have been returned if
  179. OutBufferSize is too small).
  180. FtSet - TRUE if FT Set info wanted, FALSE otherwise.
  181. Return Value:
  182. ERROR_SUCCESS if successful.
  183. A Win32 error on failure.
  184. --*/
  185. {
  186. DWORD status;
  187. HKEY resKey;
  188. DWORD ival;
  189. DWORD signature;
  190. DWORD bytesReturned = 0;
  191. DWORD totalBytesReturned = 0;
  192. DWORD dataLength;
  193. DWORD outBufferSize = OutBufferSize;
  194. PVOID ptrBuffer = OutBuffer;
  195. CHAR signatureName[9];
  196. PCLUSPROP_SYNTAX ptrSyntax;
  197. //
  198. // Make sure the AvailableDisks key is current.
  199. //
  200. UpdateAvailableDisks();
  201. *BytesReturned = 0;
  202. status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  203. CLUSDISK_REGISTRY_AVAILABLE_DISKS,
  204. 0,
  205. KEY_READ,
  206. &resKey );
  207. if ( status != ERROR_SUCCESS ) {
  208. // If the key wasn't found, return an empty list.
  209. if ( status == ERROR_FILE_NOT_FOUND ) {
  210. // Add the endmark.
  211. bytesReturned += sizeof(CLUSPROP_SYNTAX);
  212. if ( bytesReturned <= outBufferSize ) {
  213. ptrSyntax = ptrBuffer;
  214. ptrSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
  215. ptrSyntax++;
  216. ptrBuffer = ptrSyntax;
  217. status = ERROR_SUCCESS;
  218. } else {
  219. status = ERROR_MORE_DATA;
  220. }
  221. *BytesReturned = bytesReturned;
  222. }
  223. // We can't log an error, we have no resource handle!
  224. return(status);
  225. }
  226. totalBytesReturned = bytesReturned;
  227. bytesReturned = 0;
  228. for ( ival = 0; ; ival++ ) {
  229. dataLength = sizeof(signatureName);
  230. status = RegEnumKey( resKey,
  231. ival,
  232. signatureName,
  233. dataLength );
  234. if ( status == ERROR_NO_MORE_ITEMS ) {
  235. status = ERROR_SUCCESS;
  236. break;
  237. } else if ( status != ERROR_SUCCESS ) {
  238. break;
  239. }
  240. dataLength = sscanf( signatureName, "%x", &signature );
  241. if ( dataLength != 1 ) {
  242. status = ERROR_INVALID_DATA;
  243. break;
  244. }
  245. //
  246. // If not cluster capable, then skip it.
  247. //
  248. if ( !IsClusterCapable(signature) ) {
  249. continue;
  250. }
  251. GetDiskInfo( signature,
  252. &ptrBuffer,
  253. outBufferSize,
  254. &bytesReturned,
  255. FtSet );
  256. if ( outBufferSize > bytesReturned ) {
  257. outBufferSize -= bytesReturned;
  258. } else {
  259. outBufferSize = 0;
  260. }
  261. totalBytesReturned += bytesReturned;
  262. bytesReturned = 0;
  263. }
  264. RegCloseKey( resKey );
  265. bytesReturned = totalBytesReturned;
  266. // Add the endmark.
  267. bytesReturned += sizeof(CLUSPROP_SYNTAX);
  268. if ( bytesReturned <= outBufferSize ) {
  269. ptrSyntax = ptrBuffer;
  270. ptrSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
  271. ptrSyntax++;
  272. ptrBuffer = ptrSyntax;
  273. }
  274. if ( bytesReturned > OutBufferSize ) {
  275. status = ERROR_MORE_DATA;
  276. }
  277. *BytesReturned = bytesReturned;
  278. return(status);
  279. } // ClusDiskGetAvailableDisks
  280. DWORD
  281. GetScsiAddress(
  282. IN DWORD Signature,
  283. OUT LPDWORD ScsiAddress,
  284. OUT LPDWORD DiskNumber
  285. )
  286. /*++
  287. Routine Description:
  288. Find the SCSI addressing for a given signature.
  289. Arguments:
  290. Signature - the signature to find.
  291. ScsiAddress - pointer to a DWORD to return the SCSI address information.
  292. DiskNumber - the disk number associated with the signature.
  293. Return Value:
  294. ERROR_SUCCESS if successful.
  295. A Win32 error on failure.
  296. --*/
  297. {
  298. DWORD dwError;
  299. SCSI_INFO scsiInfo;
  300. ZeroMemory( &scsiInfo, sizeof(scsiInfo) );
  301. scsiInfo.Signature = Signature;
  302. dwError = EnumerateDisks( GetScsiAddressCallback, &scsiInfo );
  303. //
  304. // If the SCSI address was not set, we know the disk was not found.
  305. //
  306. if ( INVALID_SCSIADDRESS_VALUE == scsiInfo.ScsiAddress ) {
  307. dwError = ERROR_FILE_NOT_FOUND;
  308. goto FnExit;
  309. }
  310. //
  311. // The callback routine will use ERROR_POPUP_ALREADY_ACTIVE to stop
  312. // the disk enumeration. Reset the value to success if that special
  313. // value is returned.
  314. //
  315. if ( ERROR_POPUP_ALREADY_ACTIVE == dwError ) {
  316. dwError = ERROR_SUCCESS;
  317. }
  318. *ScsiAddress = scsiInfo.ScsiAddress;
  319. *DiskNumber = scsiInfo.DiskNumber;
  320. FnExit:
  321. return dwError;
  322. } // GetScsiAddress
  323. DWORD
  324. GetScsiAddressCallback(
  325. HANDLE DevHandle,
  326. DWORD Index,
  327. PVOID Param1
  328. )
  329. /*++
  330. Routine Description:
  331. Find the SCSI address and disk number for a given signature.
  332. Arguments:
  333. DevHandle - open handle to a physical disk. Do not close
  334. the handle on exit.
  335. Index - current disk count. Not used.
  336. Param1 - pointer to PSCSI_INFO structure.
  337. Return Value:
  338. ERROR_SUCCESS to continue disk enumeration.
  339. ERROR_POPUP_ALREADY_ACTIVE to stop disk enumeration. This
  340. value will be changed to ERROR_SUCCESS by GetScsiAddress.
  341. --*/
  342. {
  343. PSCSI_INFO scsiInfo = Param1;
  344. PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
  345. // Always return success to keep enumerating disks. Any
  346. // error value will stop the disk enumeration.
  347. DWORD dwError = ERROR_SUCCESS;
  348. DWORD bytesReturned;
  349. BOOL success;
  350. SCSI_ADDRESS scsiAddress;
  351. STORAGE_DEVICE_NUMBER deviceNumber;
  352. CLUSPROP_SCSI_ADDRESS clusScsiAddress;
  353. scsiInfo->ScsiAddress = INVALID_SCSIADDRESS_VALUE;
  354. success = ClRtlGetDriveLayoutTable( DevHandle,
  355. &driveLayout,
  356. NULL );
  357. if ( !success || !driveLayout ||
  358. ( driveLayout->Signature != scsiInfo->Signature ) ) {
  359. goto FnExit;
  360. }
  361. //
  362. // We have a signature match. Now get the scsi address.
  363. //
  364. ZeroMemory( &scsiAddress, sizeof(scsiAddress) );
  365. success = DeviceIoControl( DevHandle,
  366. IOCTL_SCSI_GET_ADDRESS,
  367. NULL,
  368. 0,
  369. &scsiAddress,
  370. sizeof(scsiAddress),
  371. &bytesReturned,
  372. FALSE );
  373. if ( !success ) {
  374. dwError = GetLastError();
  375. goto FnExit;
  376. }
  377. //
  378. // Get the disk number.
  379. //
  380. ZeroMemory( &deviceNumber, sizeof(deviceNumber) );
  381. success = DeviceIoControl( DevHandle,
  382. IOCTL_STORAGE_GET_DEVICE_NUMBER,
  383. NULL,
  384. 0,
  385. &deviceNumber,
  386. sizeof(deviceNumber),
  387. &bytesReturned,
  388. NULL );
  389. if ( !success ) {
  390. dwError = GetLastError();
  391. goto FnExit;
  392. }
  393. clusScsiAddress.PortNumber = scsiAddress.PortNumber;
  394. clusScsiAddress.PathId = scsiAddress.PathId;
  395. clusScsiAddress.TargetId = scsiAddress.TargetId;
  396. clusScsiAddress.Lun = scsiAddress.Lun;
  397. scsiInfo->ScsiAddress = clusScsiAddress.dw;
  398. scsiInfo->DiskNumber = deviceNumber.DeviceNumber;
  399. dwError = ERROR_POPUP_ALREADY_ACTIVE;
  400. FnExit:
  401. if ( driveLayout ) {
  402. LocalFree( driveLayout );
  403. }
  404. return dwError;
  405. } // GetScsiAddressCallback
  406. BOOL
  407. IsClusterCapable(
  408. IN DWORD Signature
  409. )
  410. /*++
  411. Routine Description:
  412. Check if the device is cluster capable. If this function cannot read
  413. the disk information, then it will assume that the device is cluster
  414. capable!
  415. Arguments:
  416. Signature - the signature to find.
  417. Return Value:
  418. TRUE if the device is cluster capable, FALSE otherwise.
  419. Notes:
  420. On failures... we err on the side of being cluster capable.
  421. --*/
  422. {
  423. NTSTATUS ntStatus;
  424. HANDLE fileHandle;
  425. ANSI_STRING objName;
  426. UNICODE_STRING unicodeName;
  427. OBJECT_ATTRIBUTES objAttributes;
  428. IO_STATUS_BLOCK ioStatusBlock;
  429. BOOL success;
  430. DWORD bytesReturned;
  431. PWCHAR clusDiskControlDevice = L"\\Device\\ClusDisk0";
  432. RtlInitUnicodeString( &unicodeName, clusDiskControlDevice );
  433. InitializeObjectAttributes( &objAttributes,
  434. &unicodeName,
  435. OBJ_CASE_INSENSITIVE,
  436. NULL,
  437. NULL );
  438. ntStatus = NtCreateFile( &fileHandle,
  439. SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
  440. &objAttributes,
  441. &ioStatusBlock,
  442. NULL,
  443. FILE_ATTRIBUTE_NORMAL,
  444. FILE_SHARE_READ | FILE_SHARE_WRITE,
  445. FILE_OPEN,
  446. 0,
  447. NULL,
  448. 0 );
  449. if ( !NT_SUCCESS(ntStatus) ) {
  450. return(TRUE);
  451. }
  452. success = DeviceIoControl( fileHandle,
  453. IOCTL_DISK_CLUSTER_NOT_CLUSTER_CAPABLE,
  454. &Signature,
  455. sizeof(DWORD),
  456. NULL,
  457. 0,
  458. &bytesReturned,
  459. FALSE );
  460. NtClose( fileHandle );
  461. return(!success);
  462. } // IsClusterCapable
  463. DWORD
  464. GetDiskInfo(
  465. IN DWORD Signature,
  466. OUT PVOID *OutBuffer,
  467. IN DWORD OutBufferSize,
  468. OUT LPDWORD BytesReturned,
  469. IN BOOL FtSet
  470. )
  471. /*++
  472. Routine Description:
  473. Gets all of the disk information for a given signature.
  474. Arguments:
  475. Signature - the signature of the disk to return info.
  476. OutBuffer - pointer to the output buffer to return the data.
  477. OutBufferSize - size of the output buffer.
  478. BytesReturned - the actual number of bytes that were returned (or
  479. the number of bytes that should have been returned if
  480. OutBufferSize is too small).
  481. FtSet - TRUE if FT Set info wanted, FALSE otherwise.
  482. Return Value:
  483. ERROR_SUCCESS if successful.
  484. A Win32 error on failure.
  485. --*/
  486. {
  487. DWORD status;
  488. DWORD bytesReturned = *BytesReturned;
  489. PVOID ptrBuffer = *OutBuffer;
  490. PCLUSPROP_DWORD ptrDword;
  491. PCLUSPROP_SCSI_ADDRESS ptrScsiAddress;
  492. PCLUSPROP_DISK_NUMBER ptrDiskNumber;
  493. PCLUSPROP_PARTITION_INFO ptrPartitionInfo;
  494. PCLUSPROP_SZ ptrSerialNumber;
  495. DWORD cbSzSize;
  496. DWORD cbDataSize;
  497. DWORD scsiAddress;
  498. DWORD diskNumber;
  499. NTSTATUS ntStatus;
  500. CHAR driveLetter;
  501. DWORD i;
  502. MOUNTIE_INFO mountieInfo;
  503. PMOUNTIE_PARTITION entry;
  504. PWCHAR serialNumber = NULL;
  505. WCHAR szDiskPartName[MAX_PATH];
  506. WCHAR szGlobalDiskPartName[MAX_PATH];
  507. LONGLONG llCurrentMinUsablePartLength = 0x7FFFFFFFFFFFFFFF;
  508. PCLUSPROP_PARTITION_INFO ptrMinUsablePartitionInfo = NULL;
  509. // Return the signature - a DWORD
  510. bytesReturned += sizeof(CLUSPROP_DWORD);
  511. if ( bytesReturned <= OutBufferSize ) {
  512. ptrDword = ptrBuffer;
  513. ptrDword->Syntax.dw = CLUSPROP_SYNTAX_DISK_SIGNATURE;
  514. ptrDword->cbLength = sizeof(DWORD);
  515. ptrDword->dw = Signature;
  516. ptrDword++;
  517. ptrBuffer = ptrDword;
  518. }
  519. // Return the SCSI_ADDRESS - a DWORD
  520. status = GetScsiAddress( Signature,
  521. &scsiAddress,
  522. &diskNumber );
  523. if ( status == ERROR_SUCCESS ) {
  524. bytesReturned += sizeof(CLUSPROP_SCSI_ADDRESS);
  525. if ( bytesReturned <= OutBufferSize ) {
  526. ptrScsiAddress = ptrBuffer;
  527. ptrScsiAddress->Syntax.dw = CLUSPROP_SYNTAX_SCSI_ADDRESS;
  528. ptrScsiAddress->cbLength = sizeof(DWORD);
  529. ptrScsiAddress->dw = scsiAddress;
  530. ptrScsiAddress++;
  531. ptrBuffer = ptrScsiAddress;
  532. }
  533. // Return the DISK NUMBER - a DWORD
  534. bytesReturned += sizeof(CLUSPROP_DISK_NUMBER);
  535. if ( bytesReturned <= OutBufferSize ) {
  536. ptrDiskNumber = ptrBuffer;
  537. ptrDiskNumber->Syntax.dw = CLUSPROP_SYNTAX_DISK_NUMBER;
  538. ptrDiskNumber->cbLength = sizeof(DWORD);
  539. ptrDiskNumber->dw = diskNumber;
  540. ptrDiskNumber++;
  541. ptrBuffer = ptrDiskNumber;
  542. }
  543. } else {
  544. return( status);
  545. }
  546. // Get the disk serial number.
  547. status = GetSerialNumber( Signature,
  548. &serialNumber );
  549. if ( ERROR_SUCCESS == status && serialNumber ) {
  550. cbSzSize = (wcslen( serialNumber ) + 1) * sizeof(WCHAR);
  551. cbDataSize = sizeof(CLUSPROP_SZ) + ALIGN_CLUSPROP( cbSzSize );
  552. bytesReturned += cbDataSize;
  553. if ( bytesReturned <= OutBufferSize ) {
  554. ptrSerialNumber = ptrBuffer;
  555. ZeroMemory( ptrSerialNumber, cbDataSize );
  556. ptrSerialNumber->Syntax.dw = CLUSPROP_SYNTAX_DISK_SERIALNUMBER;
  557. ptrSerialNumber->cbLength = cbSzSize;
  558. wcsncpy( ptrSerialNumber->sz, serialNumber, wcslen(serialNumber) );
  559. ptrBuffer = (PCHAR)ptrBuffer + cbDataSize;
  560. }
  561. if ( serialNumber ) {
  562. LocalFree( serialNumber );
  563. }
  564. }
  565. // Get all the valid partitions on the disk. We must free the
  566. // volume info structure later.
  567. status = MountieFindPartitionsForDisk( diskNumber,
  568. &mountieInfo
  569. );
  570. if ( ERROR_SUCCESS == status ) {
  571. // For each partition, build a Property List.
  572. for ( i = 0; i < MountiePartitionCount( &mountieInfo ); ++i ) {
  573. entry = MountiePartition( &mountieInfo, i );
  574. if ( !entry ) {
  575. break;
  576. }
  577. // Always update the bytesReturned, even if there is more data than the
  578. // caller requested. On return, the caller will see that there is more
  579. // data available.
  580. bytesReturned += sizeof(CLUSPROP_PARTITION_INFO);
  581. if ( bytesReturned <= OutBufferSize ) {
  582. ptrPartitionInfo = ptrBuffer;
  583. ZeroMemory( ptrPartitionInfo, sizeof(CLUSPROP_PARTITION_INFO) );
  584. ptrPartitionInfo->Syntax.dw = CLUSPROP_SYNTAX_PARTITION_INFO;
  585. ptrPartitionInfo->cbLength = sizeof(CLUSPROP_PARTITION_INFO) - sizeof(CLUSPROP_VALUE);
  586. // Create a name that can be used with some of our routines.
  587. // Don't use the drive letter as the name because we might be using
  588. // partitions without drive letters assigned.
  589. _snwprintf( szDiskPartName,
  590. sizeof(szDiskPartName)/sizeof(WCHAR),
  591. DEVICE_HARDDISK_PARTITION_FMT,
  592. diskNumber,
  593. entry->PartitionNumber );
  594. //
  595. // Create a global DiskPart name that we can use with the Win32
  596. // GetVolumeInformationW call. This name must have a trailing
  597. // backslash to work correctly.
  598. //
  599. _snwprintf( szGlobalDiskPartName,
  600. sizeof(szGlobalDiskPartName)/sizeof(WCHAR),
  601. GLOBALROOT_HARDDISK_PARTITION_FMT,
  602. diskNumber,
  603. entry->PartitionNumber );
  604. // If partition has a drive letter assigned, return this info.
  605. // If no drive letter assigned, need a system-wide (i.e. across nodes)
  606. // way of identifying the device.
  607. ntStatus = GetAssignedLetter( szDiskPartName, &driveLetter );
  608. if ( NT_SUCCESS(status) && driveLetter ) {
  609. // Return the drive letter as the device name.
  610. _snwprintf( ptrPartitionInfo->szDeviceName,
  611. sizeof(ptrPartitionInfo->szDeviceName),
  612. L"%hc:",
  613. driveLetter );
  614. ptrPartitionInfo->dwFlags |= CLUSPROP_PIFLAG_STICKY;
  615. } else {
  616. // Return a physical device name.
  617. #if 0
  618. // Return string name:
  619. // Signature xxxxxxxx Partition YYY
  620. _snwprintf( ptrPartitionInfo->szDeviceName,
  621. sizeof(ptrPartitionInfo->szDeviceName),
  622. L"Signature %X Partition %u",
  623. Signature,
  624. entry->PartitionNumber );
  625. #endif
  626. // Return string name:
  627. // DiskXXXPartionYYY
  628. _snwprintf( ptrPartitionInfo->szDeviceName,
  629. sizeof(ptrPartitionInfo->szDeviceName),
  630. L"Disk%uPartition%u",
  631. diskNumber,
  632. entry->PartitionNumber );
  633. }
  634. //
  635. // Call GetVolumeInformationW with the GlobalName we have created.
  636. //
  637. if ( !GetVolumeInformationW ( szGlobalDiskPartName,
  638. ptrPartitionInfo->szVolumeLabel,
  639. sizeof(ptrPartitionInfo->szVolumeLabel)/sizeof(WCHAR),
  640. &ptrPartitionInfo->dwSerialNumber,
  641. &ptrPartitionInfo->rgdwMaximumComponentLength,
  642. &ptrPartitionInfo->dwFileSystemFlags,
  643. ptrPartitionInfo->szFileSystem,
  644. sizeof(ptrPartitionInfo->szFileSystem)/sizeof(WCHAR) ) ) {
  645. ptrPartitionInfo->szVolumeLabel[0] = L'\0';
  646. } else if ( CompareStringW( LOCALE_INVARIANT,
  647. NORM_IGNORECASE,
  648. ptrPartitionInfo->szFileSystem,
  649. -1,
  650. L"NTFS",
  651. -1
  652. ) == CSTR_EQUAL ) {
  653. // Only NTFS drives are currently supported.
  654. ptrPartitionInfo->dwFlags |= CLUSPROP_PIFLAG_USABLE;
  655. //
  656. // Find the minimum size partition that is larger than MIN_QUORUM_PARTITION_LENGTH
  657. //
  658. if ( ( entry->PartitionLength.QuadPart >= MIN_USABLE_QUORUM_PARTITION_LENGTH ) &&
  659. ( entry->PartitionLength.QuadPart < llCurrentMinUsablePartLength ) )
  660. {
  661. ptrMinUsablePartitionInfo = ptrPartitionInfo;
  662. llCurrentMinUsablePartLength = entry->PartitionLength.QuadPart;
  663. }
  664. }
  665. ptrPartitionInfo++;
  666. ptrBuffer = ptrPartitionInfo;
  667. }
  668. } // for
  669. // Free the volume information.
  670. MountieCleanup( &mountieInfo );
  671. }
  672. //
  673. // If we managed to find a default quorum partition, change the flags to indicate this.
  674. //
  675. if ( ptrMinUsablePartitionInfo != NULL )
  676. {
  677. ptrMinUsablePartitionInfo->dwFlags |= CLUSPROP_PIFLAG_DEFAULT_QUORUM;
  678. }
  679. *OutBuffer = ptrBuffer;
  680. *BytesReturned = bytesReturned;
  681. return(status);
  682. } // GetDiskInfo
  683. DWORD
  684. UpdateAvailableDisks(
  685. )
  686. /*++
  687. Routine Description:
  688. Arguments:
  689. Return Value:
  690. ERROR_SUCCESS if successful.
  691. A Win32 error on failure.
  692. --*/
  693. {
  694. HKEY availDisksKey;
  695. HKEY sigKey;
  696. DWORD dwError = NO_ERROR;
  697. DWORD enableSanBoot;
  698. BOOL availDisksOpened = FALSE;
  699. BOOL sigKeyOpened = FALSE;
  700. BOOL sigKeyIsEmpty = FALSE;
  701. UPDATE_AVAIL_DISKS updateDisks;
  702. __try {
  703. //
  704. // If we haven't yet determined the system disk information, don't do anything else.
  705. //
  706. if ( !SystemDiskAddressFound ) {
  707. __leave;
  708. }
  709. enableSanBoot = 0;
  710. GetRegDwordValue( CLUSREG_KEYNAME_CLUSSVC_PARAMETERS,
  711. CLUSREG_VALUENAME_MANAGEDISKSONSYSTEMBUSES,
  712. &enableSanBoot );
  713. //
  714. // Delete the old AvailableDisks key. This will remove any stale information.
  715. //
  716. SHDeleteKey( HKEY_LOCAL_MACHINE, CLUSDISK_REGISTRY_AVAILABLE_DISKS );
  717. //
  718. // Open the AvailableDisks key. If the key doesn't exist, it will be created.
  719. //
  720. dwError = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
  721. CLUSDISK_REGISTRY_AVAILABLE_DISKS,
  722. 0,
  723. NULL,
  724. REG_OPTION_NON_VOLATILE,
  725. KEY_CREATE_SUB_KEY,
  726. NULL,
  727. &availDisksKey,
  728. NULL );
  729. if ( NO_ERROR != dwError) {
  730. __leave;
  731. }
  732. availDisksOpened = TRUE;
  733. //
  734. // Open the Signatures key.
  735. //
  736. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  737. CLUSDISK_REGISTRY_SIGNATURES,
  738. 0,
  739. KEY_READ,
  740. &sigKey );
  741. //
  742. // If Signatures key does not exist, save all valid signatures
  743. // in the AvailableDisks key.
  744. //
  745. if ( ERROR_FILE_NOT_FOUND == dwError ) {
  746. dwError = NO_ERROR;
  747. sigKeyIsEmpty = TRUE;
  748. } else if ( NO_ERROR != dwError) {
  749. __leave;
  750. } else {
  751. sigKeyOpened = TRUE;
  752. }
  753. ZeroMemory( &updateDisks, sizeof(updateDisks) );
  754. updateDisks.EnableSanBoot = enableSanBoot;
  755. updateDisks.SigKeyIsEmpty = sigKeyIsEmpty;
  756. updateDisks.SigKey = sigKey;
  757. updateDisks.AvailDisksKey = availDisksKey;
  758. EnumerateDisks( UpdateAvailableDisksCallback, &updateDisks );
  759. } __finally {
  760. if ( availDisksOpened ) {
  761. RegCloseKey( availDisksKey );
  762. }
  763. if ( sigKeyOpened ) {
  764. RegCloseKey( sigKey );
  765. }
  766. }
  767. return dwError;
  768. } // UpdateAvailableDisks
  769. DWORD
  770. UpdateAvailableDisksCallback(
  771. HANDLE DeviceHandle,
  772. DWORD Index,
  773. PVOID Param1
  774. )
  775. {
  776. PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
  777. PUPDATE_AVAIL_DISKS updateDisks = Param1;
  778. DWORD bytesReturned;
  779. DWORD enableSanBoot;
  780. BOOL success;
  781. SCSI_ADDRESS scsiAddress;
  782. //
  783. // Look at all disks on the system. For each valid signature, add it to the
  784. // AvailableList if it is not already in the Signature key.
  785. //
  786. success = ClRtlGetDriveLayoutTable( DeviceHandle,
  787. &driveLayout,
  788. NULL );
  789. if ( success && driveLayout &&
  790. ( 0 != driveLayout->Signature ) ) {
  791. //
  792. // Get SCSI address info.
  793. //
  794. if ( DeviceIoControl( DeviceHandle,
  795. IOCTL_SCSI_GET_ADDRESS,
  796. NULL,
  797. 0,
  798. &scsiAddress,
  799. sizeof(scsiAddress),
  800. &bytesReturned,
  801. NULL ) ) {
  802. if ( !updateDisks->EnableSanBoot ) {
  803. //
  804. // Add signature to AvailableDisks key if:
  805. // - the signature is for a disk not on system bus
  806. // - the signature is not already in the Signatures key
  807. //
  808. if ( !IsDiskOnSystemBus( &scsiAddress ) &&
  809. ( updateDisks->SigKeyIsEmpty ||
  810. !IsSignatureInRegistry( updateDisks->SigKey, driveLayout->Signature ) ) ) {
  811. AddSignatureToRegistry( updateDisks->AvailDisksKey,
  812. driveLayout->Signature );
  813. }
  814. } else {
  815. // Allow disks on system bus to be added to cluster.
  816. //
  817. // Add signature to AvailableDisks key if:
  818. // - the signature is not for the system disk
  819. // - the signature is not already in the Signatures key
  820. //
  821. if ( !IsDiskSystemDisk( &scsiAddress ) &&
  822. ( updateDisks->SigKeyIsEmpty ||
  823. !IsSignatureInRegistry( updateDisks->SigKey, driveLayout->Signature ) ) ) {
  824. AddSignatureToRegistry( updateDisks->AvailDisksKey,
  825. driveLayout->Signature );
  826. }
  827. }
  828. }
  829. }
  830. if ( driveLayout ) {
  831. LocalFree( driveLayout );
  832. }
  833. //
  834. // Always return success so all disks are enumerated.
  835. //
  836. return ERROR_SUCCESS;
  837. } // UpdateAvailableDisksCallback
  838. DWORD
  839. AddSignatureToRegistry(
  840. HKEY RegKey,
  841. DWORD Signature
  842. )
  843. /*++
  844. Routine Description:
  845. Add the specified disk signature to the ClusDisk registry subkey.
  846. The disk signatures are subkeys of the ClusDisk\Parameters\AvailableDisks
  847. and ClusDisk\Parameters\Signatures keys.
  848. Arguments:
  849. RegKey - Previously opened ClusDisk registry subkey
  850. Signature - Signature value to add
  851. Return Value:
  852. Win32 error on failure.
  853. --*/
  854. {
  855. HKEY subKey;
  856. DWORD dwError;
  857. CHAR signatureName[MAX_PATH];
  858. ZeroMemory( signatureName, sizeof(signatureName) );
  859. _snprintf( signatureName, 8, "%08X", Signature );
  860. //
  861. // Try and create the key. If it exists, the existing key will be opened.
  862. //
  863. dwError = RegCreateKeyEx( RegKey,
  864. signatureName,
  865. 0,
  866. NULL,
  867. REG_OPTION_NON_VOLATILE,
  868. KEY_WRITE,
  869. NULL,
  870. &subKey,
  871. NULL );
  872. //
  873. // If the key exists, ERROR_SUCCESS is still returned.
  874. //
  875. if ( ERROR_SUCCESS == dwError ) {
  876. RegCloseKey( subKey );
  877. }
  878. return dwError;
  879. } // AddSignatureToRegistry
  880. BOOL
  881. IsSignatureInRegistry(
  882. HKEY RegKey,
  883. DWORD Signature
  884. )
  885. /*++
  886. Routine Description:
  887. Check if the specified disk signature is in the ClusDisk registry subkey.
  888. The disk signatures are subkeys of the ClusDisk\Parameters\AvailableDisks
  889. and ClusDisk\Parameters\Signatures keys.
  890. On error, assume the key is in the registry so it is not recreated.
  891. Arguments:
  892. RegKey - Previously opened ClusDisk registry subkey
  893. Signature - Signature value to check
  894. Return Value:
  895. TRUE - Signature is in registry
  896. --*/
  897. {
  898. DWORD ival;
  899. DWORD sig;
  900. DWORD dataLength;
  901. DWORD dwError;
  902. BOOL retVal = FALSE;
  903. CHAR signatureName[9];
  904. for ( ival = 0; ; ival++ ) {
  905. dataLength = sizeof(signatureName);
  906. dwError = RegEnumKey( RegKey,
  907. ival,
  908. signatureName,
  909. dataLength );
  910. // If the list is exhausted, return FALSE.
  911. if ( ERROR_NO_MORE_ITEMS == dwError ) {
  912. break;
  913. }
  914. // If some other type of error, return TRUE.
  915. if ( ERROR_SUCCESS != dwError ) {
  916. retVal = TRUE;
  917. break;
  918. }
  919. dataLength = sscanf( signatureName, "%x", &sig );
  920. if ( dataLength != 1 ) {
  921. retVal = TRUE;
  922. break;
  923. }
  924. // If signature is a subkey, return TRUE.
  925. if ( sig == Signature ) {
  926. retVal = TRUE;
  927. break;
  928. }
  929. }
  930. return retVal;
  931. } // IsSignatureInRegistry
  932. VOID
  933. GetSystemBusInfo(
  934. )
  935. /*++
  936. Routine Description:
  937. Need to find out where the NTLDR files reside (the "system disk") and where the
  938. OS files reside (the "boot disk"). We will call all these disks the "system disk".
  939. There may be more than one system disk if the disks are mirrored. So if the NTLDR
  940. files are on a different disk than the OS files, and each of these disks is
  941. mirrored, we could be looking at 4 different disks.
  942. Find all the system disks and save the information in a list we can look at later.
  943. Arguments:
  944. Return Value:
  945. None
  946. --*/
  947. {
  948. HANDLE hOsDevice = INVALID_HANDLE_VALUE;
  949. HANDLE hNtldrDevice = INVALID_HANDLE_VALUE;
  950. DWORD dwError;
  951. //
  952. // Open the disk with the OS files on it.
  953. //
  954. hOsDevice = OpenOSDisk();
  955. if ( INVALID_HANDLE_VALUE == hOsDevice ) {
  956. goto FnExit;
  957. }
  958. //
  959. // Find all mirrored disks and add them to the SCSI address list.
  960. //
  961. if ( ERROR_SUCCESS != BuildScsiListFromFailover( hOsDevice ) ) {
  962. goto FnExit;
  963. }
  964. //
  965. // Now find the disks with NTLDR on it. Disk could be mirrored.
  966. //
  967. hNtldrDevice = OpenNtldrDisk();
  968. if ( INVALID_HANDLE_VALUE == hNtldrDevice ) {
  969. goto FnExit;
  970. }
  971. //
  972. // Find all mirrored disks and add them to the SCSI address list.
  973. //
  974. if ( ERROR_SUCCESS != BuildScsiListFromFailover( hNtldrDevice ) ) {
  975. goto FnExit;
  976. }
  977. //
  978. // Indicate we went through this code one more time.
  979. //
  980. SystemDiskAddressFound++;
  981. FnExit:
  982. if ( INVALID_HANDLE_VALUE != hOsDevice ) {
  983. CloseHandle( hOsDevice );
  984. }
  985. if ( INVALID_HANDLE_VALUE != hNtldrDevice ) {
  986. CloseHandle( hNtldrDevice );
  987. }
  988. return;
  989. } // GetSystemBusInfo
  990. HANDLE
  991. OpenOSDisk(
  992. )
  993. {
  994. PCHAR systemDir = NULL;
  995. HANDLE hDevice = INVALID_HANDLE_VALUE;
  996. DWORD len;
  997. CHAR systemPath[] = "\\\\.\\?:";
  998. //
  999. // First find the disks with OS files. Disk could be mirrored.
  1000. //
  1001. systemDir = LocalAlloc( LPTR, MAX_PATH );
  1002. if ( !systemDir ) {
  1003. goto FnExit;
  1004. }
  1005. len = GetSystemDirectory( systemDir,
  1006. MAX_PATH );
  1007. if ( !len || len < 3 ) {
  1008. goto FnExit;
  1009. }
  1010. //
  1011. // If system path doesn't start with a drive letter, exit.
  1012. // c:\windows ==> c:
  1013. if ( ':' != systemDir[1] ) {
  1014. goto FnExit;
  1015. }
  1016. //
  1017. // Stuff the drive letter in the system path.
  1018. //
  1019. systemPath[4] = systemDir[0];
  1020. //
  1021. // Now open the device.
  1022. //
  1023. hDevice = CreateFile( systemPath,
  1024. GENERIC_READ | GENERIC_WRITE,
  1025. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1026. NULL,
  1027. OPEN_EXISTING,
  1028. FILE_ATTRIBUTE_NORMAL,
  1029. NULL );
  1030. FnExit:
  1031. if ( systemDir ) {
  1032. LocalFree( systemDir );
  1033. }
  1034. return hDevice;
  1035. } // OpenOSDisk
  1036. HANDLE
  1037. OpenNtldrDisk(
  1038. )
  1039. {
  1040. PSTR systemPartition = NULL;
  1041. HANDLE hDevice = INVALID_HANDLE_VALUE;
  1042. HKEY regKey = NULL;
  1043. NTSTATUS ntStatus;
  1044. DWORD dwError;
  1045. DWORD cbSystemPartition;
  1046. DWORD cbDeviceName;
  1047. DWORD type = 0;
  1048. ANSI_STRING objName;
  1049. UNICODE_STRING unicodeName;
  1050. OBJECT_ATTRIBUTES objAttributes;
  1051. IO_STATUS_BLOCK ioStatusBlock;
  1052. //
  1053. // Open the reg key to find the system partition
  1054. //
  1055. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // hKey
  1056. DISKS_REGKEY_SETUP, // lpSubKey
  1057. 0, // ulOptions--Reserved, must be 0
  1058. KEY_READ, // samDesired
  1059. &regKey // phkResult
  1060. );
  1061. if ( ERROR_SUCCESS != dwError ) {
  1062. goto FnExit;
  1063. }
  1064. //
  1065. // Allocate a reasonably sized buffer for the system partition, to
  1066. // start off with. If this isn't big enough, we'll re-allocate as
  1067. // needed.
  1068. //
  1069. cbSystemPartition = MAX_PATH + 1;
  1070. systemPartition = LocalAlloc( LPTR, cbSystemPartition );
  1071. if ( !systemPartition ) {
  1072. goto FnExit;
  1073. }
  1074. //
  1075. // Get the system partition device Name. This is of the form
  1076. // \Device\Harddisk0\Partition1 (basic disks)
  1077. // \Device\HarddiskDmVolumes\DgName\Volume1 (dynamic disks)
  1078. //
  1079. dwError = RegQueryValueEx( regKey,
  1080. DISKS_REGVALUE_SYSTEM_PARTITION,
  1081. NULL,
  1082. &type,
  1083. (LPBYTE)systemPartition,
  1084. &cbSystemPartition // \0 is included
  1085. );
  1086. while ( ERROR_MORE_DATA == dwError ) {
  1087. //
  1088. // Our buffer wasn't big enough, cbSystemPartition contains
  1089. // the required size.
  1090. //
  1091. LocalFree( systemPartition );
  1092. systemPartition = NULL;
  1093. systemPartition = LocalAlloc( LPTR, cbSystemPartition );
  1094. if ( !systemPartition ) {
  1095. goto FnExit;
  1096. }
  1097. dwError = RegQueryValueEx( regKey,
  1098. DISKS_REGVALUE_SYSTEM_PARTITION,
  1099. NULL,
  1100. &type,
  1101. (LPBYTE)systemPartition,
  1102. &cbSystemPartition // \0 is included
  1103. );
  1104. }
  1105. RtlInitString( &objName, systemPartition );
  1106. ntStatus = RtlAnsiStringToUnicodeString( &unicodeName,
  1107. &objName,
  1108. TRUE );
  1109. if ( !NT_SUCCESS(ntStatus) ) {
  1110. goto FnExit;
  1111. }
  1112. InitializeObjectAttributes( &objAttributes,
  1113. &unicodeName,
  1114. OBJ_CASE_INSENSITIVE,
  1115. NULL,
  1116. NULL );
  1117. ntStatus = NtCreateFile( &hDevice,
  1118. SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
  1119. &objAttributes,
  1120. &ioStatusBlock,
  1121. NULL,
  1122. FILE_ATTRIBUTE_NORMAL,
  1123. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1124. FILE_OPEN,
  1125. 0,
  1126. NULL,
  1127. 0 );
  1128. RtlFreeUnicodeString( &unicodeName );
  1129. FnExit:
  1130. if ( regKey ) {
  1131. RegCloseKey( regKey );
  1132. }
  1133. if ( systemPartition ) {
  1134. LocalFree( systemPartition );
  1135. }
  1136. return hDevice;
  1137. } // OpenNtldrDisk
  1138. DWORD
  1139. BuildScsiListFromFailover(
  1140. IN HANDLE DevHandle
  1141. )
  1142. {
  1143. PVOLUME_FAILOVER_SET failoverSet = NULL;
  1144. HANDLE hDevice;
  1145. DWORD dwError = ERROR_SUCCESS;
  1146. DWORD idx;
  1147. DWORD bytesReturned;
  1148. SCSI_ADDRESS scsiAddress;
  1149. PCHAR deviceName = NULL;
  1150. deviceName = LocalAlloc( LPTR, MAX_PATH+1 );
  1151. if ( !deviceName ) {
  1152. dwError = GetLastError();
  1153. goto FnExit;
  1154. }
  1155. //
  1156. // Find out how many physical disks are represented by this device.
  1157. //
  1158. dwError = GetVolumeFailoverSet( DevHandle, &failoverSet );
  1159. if ( ERROR_SUCCESS != dwError || !failoverSet ) {
  1160. goto FnExit;
  1161. }
  1162. //
  1163. // For each physical disk, get the scsi address and add it to the list.
  1164. //
  1165. for ( idx = 0; idx < failoverSet->NumberOfDisks; idx++ ) {
  1166. ZeroMemory( deviceName, MAX_PATH );
  1167. _snprintf( deviceName,
  1168. MAX_PATH,
  1169. "\\\\.\\\\PhysicalDrive%d",
  1170. failoverSet->DiskNumbers[idx] );
  1171. hDevice = CreateFile( deviceName,
  1172. GENERIC_READ | GENERIC_WRITE,
  1173. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1174. NULL,
  1175. OPEN_EXISTING,
  1176. FILE_ATTRIBUTE_NORMAL,
  1177. NULL );
  1178. if ( INVALID_HANDLE_VALUE == hDevice ) {
  1179. continue;
  1180. }
  1181. ZeroMemory( &scsiAddress, sizeof( scsiAddress ) );
  1182. if ( !DeviceIoControl( hDevice,
  1183. IOCTL_SCSI_GET_ADDRESS,
  1184. NULL,
  1185. 0,
  1186. &scsiAddress,
  1187. sizeof(scsiAddress),
  1188. &bytesReturned,
  1189. NULL ) ) {
  1190. continue;
  1191. }
  1192. CloseHandle( hDevice );
  1193. hDevice = INVALID_HANDLE_VALUE;
  1194. AddScsiAddressToList( &scsiAddress );
  1195. }
  1196. FnExit:
  1197. if ( failoverSet ) {
  1198. LocalFree( failoverSet );
  1199. }
  1200. if ( deviceName ) {
  1201. LocalFree( deviceName );
  1202. }
  1203. return dwError;
  1204. } // BuildScsiListFromFailover
  1205. DWORD
  1206. GetVolumeFailoverSet(
  1207. IN HANDLE DevHandle,
  1208. OUT PVOLUME_FAILOVER_SET *FailoverSet
  1209. )
  1210. {
  1211. PVOLUME_FAILOVER_SET failover = NULL;
  1212. DWORD dwError = ERROR_SUCCESS;
  1213. DWORD bytesReturned;
  1214. DWORD sizeFailover;
  1215. BOOL result;
  1216. if ( !FailoverSet ) {
  1217. goto FnExit;
  1218. }
  1219. sizeFailover = ( sizeof(VOLUME_FAILOVER_SET) + 10 * sizeof(ULONG) );
  1220. failover = (PVOLUME_FAILOVER_SET) LocalAlloc( LPTR, sizeFailover );
  1221. if ( !failover ) {
  1222. dwError = GetLastError();
  1223. goto FnExit;
  1224. }
  1225. result = DeviceIoControl( DevHandle,
  1226. IOCTL_VOLUME_QUERY_FAILOVER_SET,
  1227. NULL,
  1228. 0,
  1229. failover,
  1230. sizeFailover,
  1231. &bytesReturned,
  1232. NULL );
  1233. //
  1234. // We're doing this in a while loop because if the disk configuration
  1235. // changes in the small interval between when we get the reqd buffer
  1236. // size and when we send the ioctl again with a buffer of the "reqd"
  1237. // size, we may still end up with a buffer that isn't big enough.
  1238. //
  1239. while ( !result ) {
  1240. dwError = GetLastError();
  1241. if ( ERROR_MORE_DATA == dwError ) {
  1242. //
  1243. // The buffer was too small, reallocate the requested size.
  1244. //
  1245. dwError = ERROR_SUCCESS;
  1246. sizeFailover = ( sizeof(VOLUME_FAILOVER_SET) +
  1247. ((failover->NumberOfDisks) * sizeof(ULONG)) );
  1248. LocalFree( failover );
  1249. failover = (PVOLUME_FAILOVER_SET) LocalAlloc( LPTR, sizeFailover );
  1250. if ( !failover ) {
  1251. dwError = GetLastError();
  1252. goto FnExit;
  1253. }
  1254. result = DeviceIoControl( DevHandle,
  1255. IOCTL_VOLUME_QUERY_FAILOVER_SET,
  1256. NULL,
  1257. 0,
  1258. failover,
  1259. sizeFailover,
  1260. &bytesReturned,
  1261. NULL );
  1262. } else {
  1263. // Some other error, return error.
  1264. goto FnExit;
  1265. }
  1266. }
  1267. *FailoverSet = failover;
  1268. FnExit:
  1269. if ( ERROR_SUCCESS != dwError && failover ) {
  1270. LocalFree( failover );
  1271. }
  1272. return dwError;
  1273. } // GetVolumeFailoverSet
  1274. DWORD
  1275. AddScsiAddressToList(
  1276. PSCSI_ADDRESS ScsiAddress
  1277. )
  1278. {
  1279. PSCSI_ADDRESS_ENTRY entry;
  1280. DWORD dwError = ERROR_SUCCESS;
  1281. //
  1282. // Optimization: don't add the SCSI address if it already
  1283. // matches one in the list.
  1284. //
  1285. if ( IsDiskSystemDisk( ScsiAddress ) ) {
  1286. goto FnExit;
  1287. }
  1288. entry = LocalAlloc( LPTR, sizeof( SCSI_ADDRESS_ENTRY ) );
  1289. if ( !entry ) {
  1290. dwError = GetLastError();
  1291. goto FnExit;
  1292. }
  1293. entry->ScsiAddress.Length = ScsiAddress->Length;
  1294. entry->ScsiAddress.PortNumber = ScsiAddress->PortNumber;
  1295. entry->ScsiAddress.PathId = ScsiAddress->PathId;
  1296. entry->ScsiAddress.TargetId = ScsiAddress->TargetId;
  1297. entry->ScsiAddress.Lun = ScsiAddress->Lun;
  1298. if ( SysDiskAddrList ) {
  1299. entry->Next = SysDiskAddrList;
  1300. SysDiskAddrList = entry;
  1301. } else {
  1302. SysDiskAddrList = entry;
  1303. }
  1304. FnExit:
  1305. return dwError;
  1306. } // AddScsiAddressToList
  1307. VOID
  1308. CleanupSystemBusInfo(
  1309. )
  1310. {
  1311. PSCSI_ADDRESS_ENTRY entry;
  1312. PSCSI_ADDRESS_ENTRY next;
  1313. entry = SysDiskAddrList;
  1314. while ( entry ) {
  1315. next = entry->Next;
  1316. LocalFree( entry );
  1317. entry = next;
  1318. }
  1319. } // CleanupSystemBusInfo
  1320. BOOL
  1321. IsDiskSystemDisk(
  1322. PSCSI_ADDRESS DiskAddr
  1323. )
  1324. {
  1325. PSCSI_ADDRESS_ENTRY entry = SysDiskAddrList;
  1326. PSCSI_ADDRESS_ENTRY next;
  1327. while ( entry ) {
  1328. next = entry->Next;
  1329. if ( DiskAddr->PortNumber == entry->ScsiAddress.PortNumber &&
  1330. DiskAddr->PathId == entry->ScsiAddress.PathId &&
  1331. DiskAddr->TargetId == entry->ScsiAddress.TargetId &&
  1332. DiskAddr->Lun == entry->ScsiAddress.Lun ) {
  1333. return TRUE;
  1334. }
  1335. entry = next;
  1336. }
  1337. return FALSE;
  1338. } // IsDiskSystemDisk
  1339. BOOL
  1340. IsDiskOnSystemBus(
  1341. PSCSI_ADDRESS DiskAddr
  1342. )
  1343. {
  1344. PSCSI_ADDRESS_ENTRY entry = SysDiskAddrList;
  1345. PSCSI_ADDRESS_ENTRY next;
  1346. while ( entry ) {
  1347. next = entry->Next;
  1348. if ( DiskAddr->PortNumber == entry->ScsiAddress.PortNumber &&
  1349. DiskAddr->PathId == entry->ScsiAddress.PathId ) {
  1350. return TRUE;
  1351. }
  1352. entry = next;
  1353. }
  1354. return FALSE;
  1355. } // IsDiskOnSystemBus
  1356. DWORD
  1357. EnumerateDisks(
  1358. LPDISK_ENUM_CALLBACK DiskEnumCallback,
  1359. PVOID Param1
  1360. )
  1361. {
  1362. PSP_DEVICE_INTERFACE_DETAIL_DATA pDiDetail = NULL;
  1363. HANDLE hDevice;
  1364. DWORD dwError = ERROR_SUCCESS;
  1365. DWORD count;
  1366. DWORD sizeDiDetail;
  1367. BOOL result;
  1368. HDEVINFO hdevInfo;
  1369. SP_DEVICE_INTERFACE_DATA devInterfaceData;
  1370. SP_DEVINFO_DATA devInfoData;
  1371. if ( !DiskEnumCallback ) {
  1372. dwError = ERROR_INVALID_PARAMETER;
  1373. goto FnExit;
  1374. }
  1375. //
  1376. // Get a device interface set which includes all Disk devices
  1377. // present on the machine. DiskClassGuid is a predefined GUID that
  1378. // will return all disk-type device interfaces
  1379. //
  1380. hdevInfo = SetupDiGetClassDevs( &DiskClassGuid,
  1381. NULL,
  1382. NULL,
  1383. DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
  1384. if ( !hdevInfo ) {
  1385. dwError = GetLastError();
  1386. goto FnExit;
  1387. }
  1388. ZeroMemory( &devInterfaceData, sizeof( SP_DEVICE_INTERFACE_DATA) );
  1389. //
  1390. // Iterate over all devices interfaces in the set
  1391. //
  1392. for ( count = 0; ; count++ ) {
  1393. // must set size first
  1394. devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  1395. //
  1396. // Retrieve the device interface data for each device interface
  1397. //
  1398. result = SetupDiEnumDeviceInterfaces( hdevInfo,
  1399. NULL,
  1400. &DiskClassGuid,
  1401. count,
  1402. &devInterfaceData );
  1403. if ( !result ) {
  1404. //
  1405. // If we retrieved the last item, break
  1406. //
  1407. dwError = GetLastError();
  1408. if ( ERROR_NO_MORE_ITEMS == dwError ) {
  1409. dwError = ERROR_SUCCESS;
  1410. break;
  1411. }
  1412. //
  1413. // Some other error occurred. Stop processing.
  1414. //
  1415. goto FnExit;
  1416. }
  1417. //
  1418. // Get the required buffer-size for the device path. Note that
  1419. // this call is expected to fail with insufficient buffer error.
  1420. //
  1421. result = SetupDiGetDeviceInterfaceDetail( hdevInfo,
  1422. &devInterfaceData,
  1423. NULL,
  1424. 0,
  1425. &sizeDiDetail,
  1426. NULL
  1427. );
  1428. if ( !result ) {
  1429. dwError = GetLastError();
  1430. //
  1431. // If a value other than "insufficient buffer" is returned,
  1432. // we have to skip this device.
  1433. //
  1434. if ( ERROR_INSUFFICIENT_BUFFER != dwError ) {
  1435. continue;
  1436. }
  1437. } else {
  1438. //
  1439. // The call should have failed since we're getting the
  1440. // required buffer size. If it doesn't fail, something bad
  1441. // happened.
  1442. //
  1443. continue;
  1444. }
  1445. //
  1446. // Allocate memory for the device interface detail.
  1447. //
  1448. pDiDetail = LocalAlloc( LPTR, sizeDiDetail );
  1449. if ( !pDiDetail ) {
  1450. dwError = GetLastError();
  1451. goto FnExit;
  1452. }
  1453. // must set the struct's size member
  1454. pDiDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  1455. devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  1456. //
  1457. // Finally, retrieve the device interface detail info.
  1458. //
  1459. result = SetupDiGetDeviceInterfaceDetail( hdevInfo,
  1460. &devInterfaceData,
  1461. pDiDetail,
  1462. sizeDiDetail,
  1463. NULL,
  1464. &devInfoData
  1465. );
  1466. if ( !result ) {
  1467. dwError = GetLastError();
  1468. //
  1469. // Shouldn't fail, if it does, try the next device.
  1470. //
  1471. continue;
  1472. }
  1473. //
  1474. // Open a handle to the device.
  1475. //
  1476. hDevice = CreateFile( pDiDetail->DevicePath,
  1477. GENERIC_READ | GENERIC_WRITE,
  1478. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1479. NULL,
  1480. OPEN_EXISTING,
  1481. FILE_ATTRIBUTE_NORMAL,
  1482. NULL );
  1483. LocalFree( pDiDetail );
  1484. pDiDetail = NULL;
  1485. if ( INVALID_HANDLE_VALUE == hDevice ) {
  1486. continue;
  1487. }
  1488. //
  1489. // Call the specified callback routine. An error returned stops the
  1490. // disk enumeration.
  1491. //
  1492. dwError = (*DiskEnumCallback)( hDevice, count, Param1 );
  1493. CloseHandle( hDevice );
  1494. if ( ERROR_SUCCESS != dwError ) {
  1495. goto FnExit;
  1496. }
  1497. }
  1498. FnExit:
  1499. if ( pDiDetail ) {
  1500. LocalFree( pDiDetail );
  1501. }
  1502. return dwError;
  1503. } // EnumerateDisks
  1504. DWORD
  1505. GetSerialNumber(
  1506. IN DWORD Signature,
  1507. OUT LPWSTR *SerialNumber
  1508. )
  1509. /*++
  1510. Routine Description:
  1511. Find the disk serial number for a given signature.
  1512. Arguments:
  1513. Signature - the signature to find.
  1514. SerialNumber - pointer to allocated buffer holding the returned serial
  1515. number. The caller must free this buffer.
  1516. Return Value:
  1517. ERROR_SUCCESS if successful.
  1518. A Win32 error on failure.
  1519. --*/
  1520. {
  1521. DWORD dwError;
  1522. SERIAL_INFO serialInfo;
  1523. if ( !Signature || !SerialNumber ) {
  1524. dwError = ERROR_INVALID_PARAMETER;
  1525. goto FnExit;
  1526. }
  1527. *SerialNumber = NULL;
  1528. ZeroMemory( &serialInfo, sizeof(serialInfo) );
  1529. serialInfo.Signature = Signature;
  1530. serialInfo.Error = ERROR_SUCCESS;
  1531. dwError = EnumerateDisks( GetSerialNumberCallback, &serialInfo );
  1532. //
  1533. // The callback routine will use ERROR_POPUP_ALREADY_ACTIVE to stop
  1534. // the disk enumeration. Reset the value to the value returned
  1535. // in the SERIAL_INFO structure.
  1536. //
  1537. if ( ERROR_POPUP_ALREADY_ACTIVE == dwError ) {
  1538. dwError = serialInfo.Error;
  1539. }
  1540. // This will either be NULL or an allocated buffer. Caller is responsible
  1541. // to free.
  1542. *SerialNumber = serialInfo.SerialNumber;
  1543. FnExit:
  1544. return dwError;
  1545. } // GetSerialNumber
  1546. DWORD
  1547. GetSerialNumberCallback(
  1548. HANDLE DevHandle,
  1549. DWORD Index,
  1550. PVOID Param1
  1551. )
  1552. /*++
  1553. Routine Description:
  1554. Find the disk serial number for a given signature.
  1555. Arguments:
  1556. DevHandle - open handle to a physical disk. Do not close
  1557. the handle on exit.
  1558. Index - current disk count. Not used.
  1559. Param1 - pointer to PSERIAL_INFO structure.
  1560. Return Value:
  1561. ERROR_SUCCESS to continue disk enumeration.
  1562. ERROR_POPUP_ALREADY_ACTIVE to stop disk enumeration. This
  1563. value will be changed to ERROR_SUCCESS by GetScsiAddress.
  1564. --*/
  1565. {
  1566. PSERIAL_INFO serialInfo = Param1;
  1567. PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
  1568. DWORD dwError = ERROR_SUCCESS;
  1569. BOOL success;
  1570. // Always return success to keep enumerating disks. Any
  1571. // error value will stop the disk enumeration.
  1572. STORAGE_PROPERTY_QUERY propQuery;
  1573. success = ClRtlGetDriveLayoutTable( DevHandle,
  1574. &driveLayout,
  1575. NULL );
  1576. if ( !success || !driveLayout ||
  1577. ( driveLayout->Signature != serialInfo->Signature ) ) {
  1578. goto FnExit;
  1579. }
  1580. //
  1581. // At this point, we have a signature match. Now this function
  1582. // must return this error value to stop the disk enumeration. The
  1583. // error value for the serial number retrieval will be returned in
  1584. // the SERIAL_INFO structure.
  1585. //
  1586. dwError = ERROR_POPUP_ALREADY_ACTIVE;
  1587. serialInfo->Error = RetrieveSerialNumber( DevHandle, &serialInfo->SerialNumber );
  1588. FnExit:
  1589. if ( driveLayout ) {
  1590. LocalFree( driveLayout );
  1591. }
  1592. if ( serialInfo->Error != ERROR_SUCCESS && serialInfo->SerialNumber ) {
  1593. LocalFree( serialInfo->SerialNumber );
  1594. }
  1595. return dwError;
  1596. } // GetSerialNumberCallback
  1597. DWORD
  1598. GetSignatureFromSerialNumber(
  1599. IN LPWSTR SerialNumber,
  1600. OUT LPDWORD Signature
  1601. )
  1602. /*++
  1603. Routine Description:
  1604. Find the disk signature for the given serial number.
  1605. Arguments:
  1606. SerialNumber - pointer to allocated buffer holding the serial number.
  1607. Signature - pointer to location to hold the signature.
  1608. Return Value:
  1609. ERROR_SUCCESS if successful.
  1610. A Win32 error on failure.
  1611. --*/
  1612. {
  1613. DWORD dwError;
  1614. SERIAL_INFO serialInfo;
  1615. if ( !Signature || !SerialNumber ) {
  1616. dwError = ERROR_INVALID_PARAMETER;
  1617. goto FnExit;
  1618. }
  1619. *Signature = 0;
  1620. ZeroMemory( &serialInfo, sizeof(serialInfo) );
  1621. serialInfo.SerialNumber = SerialNumber;
  1622. serialInfo.Error = ERROR_SUCCESS;
  1623. dwError = EnumerateDisks( GetSigFromSerNumCallback, &serialInfo );
  1624. //
  1625. // The callback routine will use ERROR_POPUP_ALREADY_ACTIVE to stop
  1626. // the disk enumeration. Reset the value to the value returned
  1627. // in the SERIAL_INFO structure.
  1628. //
  1629. if ( ERROR_POPUP_ALREADY_ACTIVE == dwError ) {
  1630. dwError = serialInfo.Error;
  1631. }
  1632. // This signature will either be zero or valid.
  1633. *Signature = serialInfo.Signature;
  1634. FnExit:
  1635. return dwError;
  1636. } // GetSignatureFromSerialNumber
  1637. DWORD
  1638. GetSigFromSerNumCallback(
  1639. HANDLE DevHandle,
  1640. DWORD Index,
  1641. PVOID Param1
  1642. )
  1643. /*++
  1644. Routine Description:
  1645. Find the disk signature for a given serial number.
  1646. Arguments:
  1647. DevHandle - open handle to a physical disk. Do not close
  1648. the handle on exit.
  1649. Index - current disk count. Not used.
  1650. Param1 - pointer to PSERIAL_INFO structure.
  1651. Return Value:
  1652. ERROR_SUCCESS to continue disk enumeration.
  1653. ERROR_POPUP_ALREADY_ACTIVE to stop disk enumeration. This
  1654. value will be changed to ERROR_SUCCESS by GetScsiAddress.
  1655. --*/
  1656. {
  1657. PSERIAL_INFO serialInfo = Param1;
  1658. LPWSTR serialNum = NULL;
  1659. PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
  1660. DWORD dwError = ERROR_SUCCESS;
  1661. DWORD oldLen;
  1662. DWORD newLen;
  1663. BOOL success;
  1664. // Always return success to keep enumerating disks. Any
  1665. // error value will stop the disk enumeration.
  1666. dwError = RetrieveSerialNumber( DevHandle, &serialNum );
  1667. if ( NO_ERROR != dwError || !serialNum ) {
  1668. dwError = ERROR_SUCCESS;
  1669. goto FnExit;
  1670. }
  1671. //
  1672. // We have a serial number, now try to match it.
  1673. //
  1674. newLen = wcslen( serialNum );
  1675. oldLen = wcslen( serialInfo->SerialNumber );
  1676. if ( newLen != oldLen ||
  1677. 0 != wcsncmp( serialNum, serialInfo->SerialNumber, newLen ) ) {
  1678. goto FnExit;
  1679. }
  1680. //
  1681. // At this point, we have a serial number match. Now this function
  1682. // must return this error value to stop the disk enumeration. The
  1683. // error value for the signature retrieval will be returned in
  1684. // the SERIAL_INFO structure.
  1685. //
  1686. dwError = ERROR_POPUP_ALREADY_ACTIVE;
  1687. success = ClRtlGetDriveLayoutTable( DevHandle,
  1688. &driveLayout,
  1689. NULL );
  1690. if ( !success || !driveLayout ) {
  1691. serialInfo->Error = ERROR_INVALID_DATA;
  1692. goto FnExit;
  1693. }
  1694. serialInfo->Signature = driveLayout->Signature;
  1695. serialInfo->Error = NO_ERROR;
  1696. FnExit:
  1697. if ( driveLayout ) {
  1698. LocalFree( driveLayout );
  1699. }
  1700. if ( serialNum ) {
  1701. LocalFree( serialNum );
  1702. }
  1703. return dwError;
  1704. } // GetSigFromSerNumCallback
  1705. DWORD
  1706. RetrieveSerialNumber(
  1707. HANDLE DevHandle,
  1708. LPWSTR *SerialNumber
  1709. )
  1710. {
  1711. PSTORAGE_DEVICE_DESCRIPTOR descriptor = NULL;
  1712. PWCHAR wSerNum = NULL;
  1713. PCHAR sigString;
  1714. DWORD dwError = ERROR_SUCCESS;
  1715. DWORD bytesReturned;
  1716. DWORD descriptorSize;
  1717. size_t count1;
  1718. size_t count2;
  1719. BOOL success;
  1720. STORAGE_PROPERTY_QUERY propQuery;
  1721. if ( !SerialNumber ) {
  1722. dwError = ERROR_INVALID_PARAMETER;
  1723. goto FnExit;
  1724. }
  1725. *SerialNumber = NULL;
  1726. descriptorSize = sizeof( STORAGE_DEVICE_DESCRIPTOR) + 4096;
  1727. descriptor = LocalAlloc( LPTR, descriptorSize );
  1728. if ( !descriptor ) {
  1729. dwError = GetLastError();
  1730. goto FnExit;
  1731. }
  1732. ZeroMemory( &propQuery, sizeof( propQuery ) );
  1733. propQuery.PropertyId = StorageDeviceProperty;
  1734. propQuery.QueryType = PropertyStandardQuery;
  1735. success = DeviceIoControl( DevHandle,
  1736. IOCTL_STORAGE_QUERY_PROPERTY,
  1737. &propQuery,
  1738. sizeof(propQuery),
  1739. descriptor,
  1740. descriptorSize,
  1741. &bytesReturned,
  1742. NULL );
  1743. if ( !success ) {
  1744. dwError = GetLastError();
  1745. goto FnExit;
  1746. }
  1747. if ( !bytesReturned ) {
  1748. dwError = ERROR_INVALID_DATA;
  1749. goto FnExit;
  1750. }
  1751. //
  1752. // Make sure the offset is reasonable.
  1753. // IA64 sometimes returns -1 for SerialNumberOffset.
  1754. //
  1755. if ( 0 == descriptor->SerialNumberOffset ||
  1756. descriptor->SerialNumberOffset > descriptor->Size ) {
  1757. dwError = ERROR_INVALID_DATA;
  1758. goto FnExit;
  1759. }
  1760. //
  1761. // Serial number string is a zero terminated ASCII string.
  1762. //
  1763. // The header ntddstor.h says the for devices with no serial number,
  1764. // the offset will be zero. This doesn't seem to be true.
  1765. //
  1766. // For devices with no serial number, it looks like a string with a single
  1767. // null character '\0' is returned.
  1768. //
  1769. sigString = (PCHAR)descriptor + (DWORD)descriptor->SerialNumberOffset;
  1770. if ( strlen(sigString) == 0) {
  1771. dwError = ERROR_INVALID_DATA;
  1772. goto FnExit;
  1773. }
  1774. //
  1775. // Convert ASCII string to WCHAR.
  1776. //
  1777. // Figure out how big the WCHAR buffer should be. Allocate the WCHAR
  1778. // buffer and copy the string into it.
  1779. count1 = mbstowcs( NULL, sigString, strlen(sigString) );
  1780. if ( -1 == count1 || 0 == count1 ) {
  1781. dwError = ERROR_INVALID_DATA;
  1782. goto FnExit;
  1783. }
  1784. wSerNum = LocalAlloc( LPTR, ( count1 * sizeof(WCHAR) + sizeof(UNICODE_NULL) ) );
  1785. if ( !wSerNum ) {
  1786. dwError = GetLastError();
  1787. goto FnExit;
  1788. }
  1789. count2 = mbstowcs( wSerNum, sigString, strlen(sigString) );
  1790. if ( count1 != count2 ) {
  1791. dwError = ERROR_INVALID_DATA;
  1792. goto FnExit;
  1793. }
  1794. *SerialNumber = wSerNum;
  1795. dwError = NO_ERROR;
  1796. FnExit:
  1797. if ( descriptor ) {
  1798. LocalFree( descriptor );
  1799. }
  1800. return dwError;
  1801. } // RetrieveSerialNumber