Leaked source code of windows server 2003
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.

3104 lines
78 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. #include <strsafe.h> // Should be included last.
  25. #define _NTSCSI_USER_MODE_
  26. #include <scsi.h>
  27. #undef _NTSCSI_USER_MODE_
  28. //
  29. // The registry key containing the system partition
  30. //
  31. #define DISKS_REGKEY_SETUP L"SYSTEM\\SETUP"
  32. #define DISKS_REGVALUE_SYSTEM_PARTITION L"SystemPartition"
  33. extern PWCHAR g_DiskResource; // L"rtPhysical Disk"
  34. #define RESOURCE_TYPE ((RESOURCE_HANDLE)g_DiskResource)
  35. #define INVALID_SCSIADDRESS_VALUE (DWORD)-1
  36. #define SIG_LEN_WITH_NULL 9
  37. typedef struct _SCSI_PASS_THROUGH_WITH_SENSE {
  38. SCSI_PASS_THROUGH Spt;
  39. UCHAR SenseBuf[32];
  40. } SCSI_PASS_THROUGH_WITH_SENSE, *PSCSI_PASS_THROUGH_WITH_SENSE;
  41. typedef struct _UPDATE_AVAIL_DISKS {
  42. HKEY AvailDisksKey;
  43. HKEY SigKey;
  44. DWORD EnableSanBoot;
  45. BOOL SigKeyIsEmpty;
  46. PSCSI_ADDRESS_ENTRY CriticalDiskList;
  47. } UPDATE_AVAIL_DISKS, *PUPDATE_AVAIL_DISKS;
  48. typedef struct _SCSI_INFO {
  49. DWORD Signature;
  50. DWORD DiskNumber;
  51. DWORD ScsiAddress;
  52. } SCSI_INFO, *PSCSI_INFO;
  53. typedef struct _SCSI_INFO_ARRAY {
  54. int Capacity;
  55. int Count;
  56. SCSI_INFO Info[1];
  57. } SCSI_INFO_ARRAY, *PSCSI_INFO_ARRAY;
  58. typedef struct _SERIAL_INFO {
  59. DWORD Signature;
  60. DWORD Error;
  61. LPWSTR SerialNumber;
  62. } SERIAL_INFO, *PSERIAL_INFO;
  63. typedef
  64. DWORD
  65. (*LPDISK_ENUM_CALLBACK) (
  66. HANDLE DeviceHandle,
  67. DWORD Index,
  68. PVOID Param1
  69. );
  70. //
  71. // Local Routines
  72. //
  73. DWORD
  74. AddSignatureToRegistry(
  75. HKEY RegKey,
  76. DWORD Signature
  77. );
  78. BOOL
  79. IsClusterCapable(
  80. IN DWORD ScsiAddress
  81. );
  82. BOOL
  83. IsSignatureInRegistry(
  84. HKEY RegKey,
  85. DWORD Signature
  86. );
  87. DWORD
  88. UpdateAvailableDisks(
  89. );
  90. DWORD
  91. UpdateAvailableDisksCallback(
  92. HANDLE DeviceHandle,
  93. DWORD Index,
  94. PVOID Param1
  95. );
  96. DWORD
  97. AddScsiAddressToList(
  98. PSCSI_ADDRESS ScsiAddress,
  99. PSCSI_ADDRESS_ENTRY *AddressList
  100. );
  101. VOID
  102. GetSystemBusInfo(
  103. PSCSI_ADDRESS_ENTRY *AddressList
  104. );
  105. DWORD
  106. GetVolumeDiskExtents(
  107. IN HANDLE DevHandle,
  108. OUT PVOLUME_DISK_EXTENTS *DiskExtents
  109. );
  110. DWORD
  111. BuildScsiListFromDiskExtents(
  112. IN HANDLE DevHandle,
  113. PSCSI_ADDRESS_ENTRY *AddressList
  114. );
  115. HANDLE
  116. OpenNtldrDisk(
  117. );
  118. HANDLE
  119. OpenOSDisk(
  120. );
  121. DWORD
  122. EnumerateDisks(
  123. LPDISK_ENUM_CALLBACK DiskEnumCallback,
  124. PVOID Param1
  125. );
  126. DWORD
  127. GetScsiAddressCallback(
  128. HANDLE DevHandle,
  129. DWORD Index,
  130. PVOID Param1
  131. );
  132. DWORD
  133. GetSerialNumberCallback(
  134. HANDLE DevHandle,
  135. DWORD Index,
  136. PVOID Param1
  137. );
  138. DWORD
  139. GetSigFromSerNumCallback(
  140. HANDLE DevHandle,
  141. DWORD Index,
  142. PVOID Param1
  143. );
  144. DWORD
  145. GetScsiAddressForDrive(
  146. WCHAR DriveLetter,
  147. PSCSI_ADDRESS ScsiAddress
  148. );
  149. DWORD
  150. FillScsiAddressCallback(
  151. HANDLE DevHandle,
  152. DWORD Index,
  153. PVOID Param1
  154. );
  155. DWORD
  156. GetDiskInfoEx(
  157. IN DWORD Signature,
  158. IN PSCSI_INFO_ARRAY scsiInfos,
  159. OUT PVOID *OutBuffer,
  160. IN DWORD OutBufferSize,
  161. OUT LPDWORD BytesReturned
  162. );
  163. DWORD
  164. ClusDiskGetAvailableDisks(
  165. OUT PVOID OutBuffer,
  166. IN DWORD OutBufferSize,
  167. OUT LPDWORD BytesReturned
  168. )
  169. /*++
  170. Routine Description:
  171. Enumerate and build a list of available disks on this system.
  172. Arguments:
  173. OutBuffer - pointer to the output buffer to return the data.
  174. OutBufferSize - size of the output buffer.
  175. BytesReturned - the actual number of bytes that were returned (or
  176. the number of bytes that should have been returned if
  177. OutBufferSize is too small).
  178. Return Value:
  179. ERROR_SUCCESS if successful.
  180. A Win32 error on failure.
  181. Remarks:
  182. Disk grovelling algorithm was changed to linear (see bug 738013).
  183. Previous behavior:
  184. For every signature in the clusdisk registery,
  185. we used to call GetDiskInfo that will in turn do another scan of all available disks via SetupDI api's
  186. in order to find a scsi address for a disk.
  187. It used to take about a minute to add a new disk on a system with 72 disks
  188. New behavior:
  189. Collect ScsiInfo for all known disks in one pass and then give it to GetDiskInfo, so
  190. that it doesn't have to enum all the disks to find a ScsiAddress for it.
  191. --*/
  192. {
  193. DWORD status;
  194. HKEY resKey;
  195. DWORD ival;
  196. DWORD signature;
  197. DWORD bytesReturned = 0;
  198. DWORD totalBytesReturned = 0;
  199. DWORD dataLength;
  200. DWORD outBufferSize = OutBufferSize;
  201. PVOID ptrBuffer = OutBuffer;
  202. WCHAR signatureName[SIG_LEN_WITH_NULL];
  203. PCLUSPROP_SYNTAX ptrSyntax;
  204. int diskCount = 0;
  205. PSCSI_INFO_ARRAY scsiInfos = NULL;
  206. //
  207. // Make sure the AvailableDisks key is current.
  208. //
  209. UpdateAvailableDisks();
  210. *BytesReturned = 0;
  211. status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  212. CLUSDISK_REGISTRY_AVAILABLE_DISKS,
  213. 0,
  214. KEY_READ,
  215. &resKey );
  216. if ( status != ERROR_SUCCESS ) {
  217. // If the key wasn't found, return an empty list.
  218. if ( status == ERROR_FILE_NOT_FOUND ) {
  219. // Add the endmark.
  220. bytesReturned += sizeof(CLUSPROP_SYNTAX);
  221. if ( bytesReturned <= outBufferSize ) {
  222. ptrSyntax = ptrBuffer;
  223. ptrSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
  224. ptrSyntax++;
  225. ptrBuffer = ptrSyntax;
  226. status = ERROR_SUCCESS;
  227. } else {
  228. status = ERROR_MORE_DATA;
  229. }
  230. *BytesReturned = bytesReturned;
  231. }
  232. // We can't log an error, we have no resource handle!
  233. return(status);
  234. }
  235. status = RegQueryInfoKey(
  236. resKey,
  237. NULL, // lpClass,
  238. NULL, // lpcClass,
  239. NULL, // lpReserved,
  240. &diskCount, // lpcSubKeys,
  241. NULL, // lpcMaxSubKeyLen,
  242. NULL, // lpcMaxClassLen,
  243. NULL, // lpcValues,
  244. NULL, // lpcMaxValueNameLen,
  245. NULL, // lpcMaxValueLen,
  246. NULL, // lpcbSecurityDescriptor,
  247. NULL // lpftLastWriteTime
  248. );
  249. if (status != ERROR_SUCCESS) {
  250. goto exit_gracefully;
  251. }
  252. scsiInfos = LocalAlloc(LMEM_ZEROINIT, sizeof(SCSI_INFO_ARRAY) + (diskCount - 1) * sizeof(SCSI_INFO));
  253. if (scsiInfos == NULL) {
  254. status = GetLastError();
  255. goto exit_gracefully;
  256. }
  257. scsiInfos->Capacity = diskCount;
  258. scsiInfos->Count = 0;
  259. totalBytesReturned = bytesReturned;
  260. bytesReturned = 0;
  261. for ( ival = 0; ; ival++ ) {
  262. dataLength = SIG_LEN_WITH_NULL;
  263. status = RegEnumKey( resKey,
  264. ival,
  265. signatureName,
  266. dataLength );
  267. if ( status == ERROR_NO_MORE_ITEMS ) {
  268. status = ERROR_SUCCESS;
  269. break;
  270. } else if ( status != ERROR_SUCCESS ) {
  271. goto exit_gracefully;
  272. }
  273. dataLength = swscanf( signatureName, TEXT("%08x"), &signature );
  274. if ( dataLength != 1 ) {
  275. status = ERROR_INVALID_DATA;
  276. goto exit_gracefully;
  277. }
  278. if (scsiInfos->Count >= scsiInfos->Capacity) {
  279. // a signature was added after we queried number of keys.
  280. // ignore newly added disks.
  281. break;
  282. }
  283. scsiInfos->Info[scsiInfos->Count++].Signature = signature;
  284. }
  285. // query SCSI-INFO information for all the disks in one pass
  286. status = EnumerateDisks( FillScsiAddressCallback, scsiInfos );
  287. if (status != ERROR_SUCCESS) {
  288. goto exit_gracefully;
  289. }
  290. for (ival = 0; ival < (DWORD)scsiInfos->Count; ++ival) {
  291. //
  292. // If not cluster capable, then skip it.
  293. //
  294. if ( !IsClusterCapable(scsiInfos->Info[ival].ScsiAddress) ) {
  295. continue;
  296. }
  297. signature = scsiInfos->Info[ival].Signature;
  298. GetDiskInfoEx( signature, scsiInfos,
  299. &ptrBuffer,
  300. outBufferSize,
  301. &bytesReturned );
  302. if ( outBufferSize > bytesReturned ) {
  303. outBufferSize -= bytesReturned;
  304. } else {
  305. outBufferSize = 0;
  306. }
  307. totalBytesReturned += bytesReturned;
  308. bytesReturned = 0;
  309. }
  310. exit_gracefully:
  311. if (scsiInfos != NULL) {
  312. LocalFree(scsiInfos);
  313. }
  314. RegCloseKey( resKey );
  315. bytesReturned = totalBytesReturned;
  316. // Add the endmark.
  317. bytesReturned += sizeof(CLUSPROP_SYNTAX);
  318. if ( bytesReturned <= outBufferSize ) {
  319. ptrSyntax = ptrBuffer;
  320. ptrSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
  321. ptrSyntax++;
  322. ptrBuffer = ptrSyntax;
  323. }
  324. if ( bytesReturned > OutBufferSize ) {
  325. status = ERROR_MORE_DATA;
  326. }
  327. *BytesReturned = bytesReturned;
  328. return(status);
  329. } // ClusDiskGetAvailableDisks
  330. DWORD
  331. GetScsiAddressEx(
  332. IN DWORD Signature,
  333. IN PSCSI_INFO_ARRAY ScsiInfos,
  334. OUT LPDWORD ScsiAddress,
  335. OUT LPDWORD DiskNumber
  336. )
  337. /*++
  338. Routine Description:
  339. Find the SCSI addressing for a given signature.
  340. Arguments:
  341. Signature - the signature to find.
  342. ScsiInfos - array of signature/scsi-address pairs.
  343. Can be NULL. (reverts to enumerating all the disks to find the address)
  344. ScsiAddress - pointer to a DWORD to return the SCSI address information.
  345. DiskNumber - the disk number associated with the signature.
  346. Return Value:
  347. ERROR_SUCCESS if successful.
  348. A Win32 error on failure.
  349. --*/
  350. {
  351. DWORD dwError;
  352. SCSI_INFO scsiInfo;
  353. if (ScsiInfos != NULL) {
  354. // if ScsiInfos was provided, gets ScsiAddress from there
  355. int i;
  356. for(i = 0; i < ScsiInfos->Count; ++i) {
  357. if (ScsiInfos->Info[i].Signature == Signature) {
  358. *ScsiAddress = ScsiInfos->Info[i].ScsiAddress;
  359. *DiskNumber = ScsiInfos->Info[i].DiskNumber;
  360. return ERROR_SUCCESS;
  361. }
  362. }
  363. return ERROR_FILE_NOT_FOUND;
  364. }
  365. ZeroMemory( &scsiInfo, sizeof(scsiInfo) );
  366. scsiInfo.Signature = Signature;
  367. dwError = EnumerateDisks( GetScsiAddressCallback, &scsiInfo );
  368. //
  369. // If the SCSI address was not set, we know the disk was not found.
  370. //
  371. if ( INVALID_SCSIADDRESS_VALUE == scsiInfo.ScsiAddress ) {
  372. dwError = ERROR_FILE_NOT_FOUND;
  373. goto FnExit;
  374. }
  375. //
  376. // The callback routine will use ERROR_POPUP_ALREADY_ACTIVE to stop
  377. // the disk enumeration. Reset the value to success if that special
  378. // value is returned.
  379. //
  380. if ( ERROR_POPUP_ALREADY_ACTIVE == dwError ) {
  381. dwError = ERROR_SUCCESS;
  382. }
  383. *ScsiAddress = scsiInfo.ScsiAddress;
  384. *DiskNumber = scsiInfo.DiskNumber;
  385. FnExit:
  386. return dwError;
  387. } // GetScsiAddress
  388. DWORD
  389. GetScsiAddress(
  390. IN DWORD Signature,
  391. OUT LPDWORD ScsiAddress,
  392. OUT LPDWORD DiskNumber
  393. )
  394. {
  395. return GetScsiAddressEx(Signature, NULL, ScsiAddress, DiskNumber);
  396. }
  397. DWORD
  398. GetScsiAddressCommon(
  399. HANDLE DevHandle,
  400. DWORD Index,
  401. PSCSI_INFO scsiInfo,
  402. PSCSI_INFO_ARRAY scsiInfoArray
  403. )
  404. /*++
  405. Routine Description:
  406. Find the SCSI address and disk number for a given signature.
  407. Arguments:
  408. DevHandle - open handle to a physical disk. Do not close
  409. the handle on exit.
  410. Index - current disk count. Not used.
  411. scsiInfo - pointer to PSCSI_INFO structure.
  412. scsiInfoArray - pointer to PSCSI_INFO_ARRAY
  413. Return Value:
  414. ERROR_SUCCESS to continue disk enumeration.
  415. ERROR_POPUP_ALREADY_ACTIVE to stop disk enumeration. This
  416. value will be changed to ERROR_SUCCESS by GetScsiAddress.
  417. Remarks:
  418. If scsiInfo is not null, the address will be stored in scsiInfo->address,
  419. otherwise, it will be stored in the corresponding entry of scsiInfoArray.
  420. --*/
  421. {
  422. int i;
  423. PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
  424. // Always return success to keep enumerating disks. Any
  425. // error value will stop the disk enumeration.
  426. DWORD dwError = ERROR_SUCCESS;
  427. DWORD bytesReturned;
  428. BOOL success;
  429. SCSI_ADDRESS scsiAddress;
  430. STORAGE_DEVICE_NUMBER deviceNumber;
  431. CLUSPROP_SCSI_ADDRESS clusScsiAddress;
  432. if (scsiInfo != NULL) {
  433. scsiInfo->ScsiAddress = INVALID_SCSIADDRESS_VALUE;
  434. }
  435. UpdateCachedDriveLayout( DevHandle );
  436. success = ClRtlGetDriveLayoutTable( DevHandle,
  437. &driveLayout,
  438. NULL );
  439. if ( !success || !driveLayout ) {
  440. goto FnExit;
  441. }
  442. if ( scsiInfoArray == NULL ) {
  443. if ( driveLayout->Signature != scsiInfo->Signature ) {
  444. goto FnExit;
  445. }
  446. } else {
  447. // find the disk with that signature
  448. scsiInfo = NULL;
  449. for (i = 0; i < scsiInfoArray->Count; ++i) {
  450. if ( driveLayout->Signature == scsiInfoArray->Info[i].Signature ) {
  451. scsiInfo = &scsiInfoArray->Info[i];
  452. break;
  453. }
  454. }
  455. if (scsiInfo == NULL) {
  456. goto FnExit;
  457. }
  458. }
  459. //
  460. // We have a signature match. Now get the scsi address.
  461. //
  462. ZeroMemory( &scsiAddress, sizeof(scsiAddress) );
  463. success = DeviceIoControl( DevHandle,
  464. IOCTL_SCSI_GET_ADDRESS,
  465. NULL,
  466. 0,
  467. &scsiAddress,
  468. sizeof(scsiAddress),
  469. &bytesReturned,
  470. FALSE );
  471. if ( !success ) {
  472. dwError = GetLastError();
  473. goto FnExit;
  474. }
  475. //
  476. // Get the disk number.
  477. //
  478. ZeroMemory( &deviceNumber, sizeof(deviceNumber) );
  479. success = DeviceIoControl( DevHandle,
  480. IOCTL_STORAGE_GET_DEVICE_NUMBER,
  481. NULL,
  482. 0,
  483. &deviceNumber,
  484. sizeof(deviceNumber),
  485. &bytesReturned,
  486. NULL );
  487. if ( !success ) {
  488. dwError = GetLastError();
  489. goto FnExit;
  490. }
  491. clusScsiAddress.PortNumber = scsiAddress.PortNumber;
  492. clusScsiAddress.PathId = scsiAddress.PathId;
  493. clusScsiAddress.TargetId = scsiAddress.TargetId;
  494. clusScsiAddress.Lun = scsiAddress.Lun;
  495. scsiInfo->ScsiAddress = clusScsiAddress.dw;
  496. scsiInfo->DiskNumber = deviceNumber.DeviceNumber;
  497. if ( scsiInfoArray == NULL ) {
  498. dwError = ERROR_POPUP_ALREADY_ACTIVE;
  499. }
  500. FnExit:
  501. if ( driveLayout ) {
  502. LocalFree( driveLayout );
  503. }
  504. return dwError;
  505. } // GetScsiAddressCommon
  506. DWORD
  507. GetScsiAddressCallback(
  508. HANDLE DevHandle,
  509. DWORD Index,
  510. PVOID Param1
  511. )
  512. {
  513. return GetScsiAddressCommon(DevHandle, Index, (PSCSI_INFO)Param1, NULL);
  514. }
  515. DWORD
  516. FillScsiAddressCallback(
  517. HANDLE DevHandle,
  518. DWORD Index,
  519. PVOID Param1
  520. )
  521. {
  522. return GetScsiAddressCommon(DevHandle, Index, NULL, (PSCSI_INFO_ARRAY)Param1);
  523. }
  524. BOOL
  525. IsClusterCapable(
  526. IN DWORD ScsiAddress
  527. )
  528. /*++
  529. Routine Description:
  530. Check if the device is cluster capable. If this function cannot read
  531. the disk information, then it will assume that the device is cluster
  532. capable!
  533. Arguments:
  534. ScsiAddress - ScsiAddress of a disk to test.
  535. Return Value:
  536. TRUE if the device is cluster capable, FALSE otherwise.
  537. Notes:
  538. On failures... we err on the side of being cluster capable.
  539. --*/
  540. {
  541. NTSTATUS ntStatus;
  542. HANDLE fileHandle;
  543. ANSI_STRING objName;
  544. UNICODE_STRING unicodeName;
  545. OBJECT_ATTRIBUTES objAttributes;
  546. IO_STATUS_BLOCK ioStatusBlock;
  547. BOOL success;
  548. DWORD bytesReturned;
  549. CLUS_SCSI_ADDRESS address;
  550. WCHAR buf[] = L"\\device\\ScsiPort000000000000000000";
  551. SRB_IO_CONTROL srbControl;
  552. address.dw = ScsiAddress;
  553. (VOID) StringCchPrintf( buf,
  554. RTL_NUMBER_OF( buf ),
  555. L"\\device\\ScsiPort%u",
  556. address.PortNumber );
  557. RtlInitUnicodeString( &unicodeName, buf );
  558. InitializeObjectAttributes( &objAttributes,
  559. &unicodeName,
  560. OBJ_CASE_INSENSITIVE,
  561. NULL,
  562. NULL );
  563. ntStatus = NtCreateFile( &fileHandle,
  564. SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
  565. &objAttributes,
  566. &ioStatusBlock,
  567. NULL,
  568. FILE_ATTRIBUTE_NORMAL,
  569. FILE_SHARE_READ | FILE_SHARE_WRITE,
  570. FILE_OPEN,
  571. 0,
  572. NULL,
  573. 0 );
  574. if ( !NT_SUCCESS(ntStatus) ) {
  575. return(TRUE);
  576. }
  577. ZeroMemory(&srbControl, sizeof(srbControl));
  578. srbControl.HeaderLength = sizeof(SRB_IO_CONTROL);
  579. CopyMemory( srbControl.Signature, "CLUSDISK", 8 );
  580. srbControl.Timeout = 3;
  581. srbControl.Length = 0;
  582. srbControl.ControlCode = IOCTL_SCSI_MINIPORT_NOT_QUORUM_CAPABLE;
  583. success = DeviceIoControl( fileHandle,
  584. IOCTL_SCSI_MINIPORT,
  585. &srbControl,
  586. sizeof(srbControl),
  587. NULL,
  588. 0,
  589. &bytesReturned,
  590. FALSE );
  591. NtClose( fileHandle );
  592. return(!success);
  593. } // IsClusterCapable
  594. DWORD
  595. GetDiskInfo(
  596. IN DWORD Signature,
  597. OUT PVOID *OutBuffer,
  598. IN DWORD OutBufferSize,
  599. OUT LPDWORD BytesReturned
  600. )
  601. {
  602. return GetDiskInfoEx(
  603. Signature, NULL,
  604. OutBuffer, OutBufferSize, BytesReturned);
  605. }
  606. DWORD
  607. GetDiskInfoEx(
  608. IN DWORD Signature,
  609. IN PSCSI_INFO_ARRAY scsiInfos,
  610. OUT PVOID *OutBuffer,
  611. IN DWORD OutBufferSize,
  612. OUT LPDWORD BytesReturned
  613. )
  614. /*++
  615. Routine Description:
  616. Gets all of the disk information for a given signature.
  617. Arguments:
  618. Signature - the signature of the disk to return info.
  619. OutBuffer - pointer to the output buffer to return the data.
  620. OutBufferSize - size of the output buffer.
  621. BytesReturned - the actual number of bytes that were returned (or
  622. the number of bytes that should have been returned if
  623. OutBufferSize is too small).
  624. Return Value:
  625. ERROR_SUCCESS if successful.
  626. A Win32 error on failure.
  627. --*/
  628. {
  629. DWORD status;
  630. DWORD bytesReturned = *BytesReturned;
  631. PVOID ptrBuffer = *OutBuffer;
  632. PCLUSPROP_DWORD ptrDword;
  633. PCLUSPROP_SCSI_ADDRESS ptrScsiAddress;
  634. PCLUSPROP_DISK_NUMBER ptrDiskNumber;
  635. PCLUSPROP_PARTITION_INFO ptrPartitionInfo;
  636. PCLUSPROP_SZ ptrSerialNumber;
  637. DWORD cbSzSize;
  638. DWORD cbDataSize;
  639. DWORD scsiAddress;
  640. DWORD diskNumber;
  641. NTSTATUS ntStatus;
  642. CHAR driveLetter;
  643. DWORD i;
  644. MOUNTIE_INFO mountieInfo;
  645. PMOUNTIE_PARTITION entry;
  646. PWCHAR serialNumber = NULL;
  647. WCHAR szDiskPartName[MAX_PATH];
  648. WCHAR szGlobalDiskPartName[MAX_PATH];
  649. LONGLONG llCurrentMinUsablePartLength = 0x7FFFFFFFFFFFFFFF;
  650. PCLUSPROP_PARTITION_INFO ptrMinUsablePartitionInfo = NULL;
  651. // Return the signature - a DWORD
  652. bytesReturned += sizeof(CLUSPROP_DWORD);
  653. if ( bytesReturned <= OutBufferSize ) {
  654. ptrDword = ptrBuffer;
  655. ptrDword->Syntax.dw = CLUSPROP_SYNTAX_DISK_SIGNATURE;
  656. ptrDword->cbLength = sizeof(DWORD);
  657. ptrDword->dw = Signature;
  658. ptrDword++;
  659. ptrBuffer = ptrDword;
  660. }
  661. // Return the SCSI_ADDRESS - a DWORD
  662. status = GetScsiAddressEx( Signature,
  663. scsiInfos,
  664. &scsiAddress,
  665. &diskNumber );
  666. if ( status == ERROR_SUCCESS ) {
  667. bytesReturned += sizeof(CLUSPROP_SCSI_ADDRESS);
  668. if ( bytesReturned <= OutBufferSize ) {
  669. ptrScsiAddress = ptrBuffer;
  670. ptrScsiAddress->Syntax.dw = CLUSPROP_SYNTAX_SCSI_ADDRESS;
  671. ptrScsiAddress->cbLength = sizeof(DWORD);
  672. ptrScsiAddress->dw = scsiAddress;
  673. ptrScsiAddress++;
  674. ptrBuffer = ptrScsiAddress;
  675. }
  676. // Return the DISK NUMBER - a DWORD
  677. bytesReturned += sizeof(CLUSPROP_DISK_NUMBER);
  678. if ( bytesReturned <= OutBufferSize ) {
  679. ptrDiskNumber = ptrBuffer;
  680. ptrDiskNumber->Syntax.dw = CLUSPROP_SYNTAX_DISK_NUMBER;
  681. ptrDiskNumber->cbLength = sizeof(DWORD);
  682. ptrDiskNumber->dw = diskNumber;
  683. ptrDiskNumber++;
  684. ptrBuffer = ptrDiskNumber;
  685. }
  686. } else {
  687. return( status);
  688. }
  689. #if 0
  690. // Remove this until SQL team can fix their setup program.
  691. // SQL is shipping a version of setup that doesn't parse the property
  692. // list correctly and causes SQL setup to AV. Since the SQL
  693. // setup is shipping and broken, remove the serial number.
  694. // SQL setup doesn't use ALIGN_CLUSPROP to find the next list entry.
  695. // Get the disk serial number.
  696. status = GetSerialNumber( Signature,
  697. &serialNumber );
  698. if ( ERROR_SUCCESS == status && serialNumber ) {
  699. cbSzSize = (wcslen( serialNumber ) + 1) * sizeof(WCHAR);
  700. cbDataSize = sizeof(CLUSPROP_SZ) + ALIGN_CLUSPROP( cbSzSize );
  701. bytesReturned += cbDataSize;
  702. if ( bytesReturned <= OutBufferSize ) {
  703. ptrSerialNumber = ptrBuffer;
  704. ZeroMemory( ptrSerialNumber, cbDataSize );
  705. ptrSerialNumber->Syntax.dw = CLUSPROP_SYNTAX_DISK_SERIALNUMBER;
  706. ptrSerialNumber->cbLength = cbSzSize;
  707. (VOID) StringCbCopy( ptrSerialNumber->sz, cbSzSize, serialNumber );
  708. ptrBuffer = (PCHAR)ptrBuffer + cbDataSize;
  709. }
  710. if ( serialNumber ) {
  711. LocalFree( serialNumber );
  712. }
  713. }
  714. #endif
  715. // Get all the valid partitions on the disk. We must free the
  716. // volume info structure later.
  717. status = MountieFindPartitionsForDisk( diskNumber,
  718. &mountieInfo
  719. );
  720. if ( ERROR_SUCCESS == status ) {
  721. // For each partition, build a Property List.
  722. for ( i = 0; i < MountiePartitionCount( &mountieInfo ); ++i ) {
  723. entry = MountiePartition( &mountieInfo, i );
  724. if ( !entry ) {
  725. break;
  726. }
  727. // Always update the bytesReturned, even if there is more data than the
  728. // caller requested. On return, the caller will see that there is more
  729. // data available.
  730. bytesReturned += sizeof(CLUSPROP_PARTITION_INFO);
  731. if ( bytesReturned <= OutBufferSize ) {
  732. ptrPartitionInfo = ptrBuffer;
  733. ZeroMemory( ptrPartitionInfo, sizeof(CLUSPROP_PARTITION_INFO) );
  734. ptrPartitionInfo->Syntax.dw = CLUSPROP_SYNTAX_PARTITION_INFO;
  735. ptrPartitionInfo->cbLength = sizeof(CLUSPROP_PARTITION_INFO) - sizeof(CLUSPROP_VALUE);
  736. // Create a name that can be used with some of our routines.
  737. // Don't use the drive letter as the name because we might be using
  738. // partitions without drive letters assigned.
  739. (VOID) StringCchPrintf( szDiskPartName,
  740. RTL_NUMBER_OF( szDiskPartName ),
  741. DEVICE_HARDDISK_PARTITION_FMT,
  742. diskNumber,
  743. entry->PartitionNumber );
  744. //
  745. // Create a global DiskPart name that we can use with the Win32
  746. // GetVolumeInformationW call. This name must have a trailing
  747. // backslash to work correctly.
  748. //
  749. (VOID) StringCchPrintf( szGlobalDiskPartName,
  750. RTL_NUMBER_OF( szGlobalDiskPartName ),
  751. GLOBALROOT_HARDDISK_PARTITION_FMT,
  752. diskNumber,
  753. entry->PartitionNumber );
  754. // If partition has a drive letter assigned, return this info.
  755. // If no drive letter assigned, need a system-wide (i.e. across nodes)
  756. // way of identifying the device.
  757. ntStatus = GetAssignedLetter( szDiskPartName, &driveLetter );
  758. if ( NT_SUCCESS(status) && driveLetter ) {
  759. // Return the drive letter as the device name.
  760. (VOID) StringCchPrintf( ptrPartitionInfo->szDeviceName,
  761. RTL_NUMBER_OF( ptrPartitionInfo->szDeviceName ),
  762. TEXT("%hc:"),
  763. driveLetter );
  764. ptrPartitionInfo->dwFlags |= CLUSPROP_PIFLAG_STICKY;
  765. } else {
  766. // Return a physical device name.
  767. // Return string name:
  768. // DiskXXXPartionYYY
  769. (VOID) StringCchPrintf( ptrPartitionInfo->szDeviceName,
  770. RTL_NUMBER_OF( ptrPartitionInfo->szDeviceName ),
  771. TEXT("Disk%uPartition%u"),
  772. diskNumber,
  773. entry->PartitionNumber );
  774. }
  775. //
  776. // Call GetVolumeInformationW with the GlobalName we have created.
  777. //
  778. if ( !GetVolumeInformationW ( szGlobalDiskPartName,
  779. ptrPartitionInfo->szVolumeLabel,
  780. sizeof(ptrPartitionInfo->szVolumeLabel)/sizeof(WCHAR),
  781. &ptrPartitionInfo->dwSerialNumber,
  782. &ptrPartitionInfo->rgdwMaximumComponentLength,
  783. &ptrPartitionInfo->dwFileSystemFlags,
  784. ptrPartitionInfo->szFileSystem,
  785. sizeof(ptrPartitionInfo->szFileSystem)/sizeof(WCHAR) ) ) {
  786. ptrPartitionInfo->szVolumeLabel[0] = L'\0';
  787. } else if ( CompareStringW( LOCALE_INVARIANT,
  788. NORM_IGNORECASE,
  789. ptrPartitionInfo->szFileSystem,
  790. -1,
  791. L"NTFS",
  792. -1
  793. ) == CSTR_EQUAL ) {
  794. // Only NTFS drives are currently supported.
  795. ptrPartitionInfo->dwFlags |= CLUSPROP_PIFLAG_USABLE;
  796. //
  797. // Find the minimum size partition that is larger than MIN_QUORUM_PARTITION_LENGTH
  798. //
  799. if ( ( entry->PartitionLength.QuadPart >= MIN_USABLE_QUORUM_PARTITION_LENGTH ) &&
  800. ( entry->PartitionLength.QuadPart < llCurrentMinUsablePartLength ) )
  801. {
  802. ptrMinUsablePartitionInfo = ptrPartitionInfo;
  803. llCurrentMinUsablePartLength = entry->PartitionLength.QuadPart;
  804. }
  805. }
  806. ptrPartitionInfo++;
  807. ptrBuffer = ptrPartitionInfo;
  808. }
  809. } // for
  810. // Free the volume information.
  811. MountieCleanup( &mountieInfo );
  812. }
  813. //
  814. // If we managed to find a default quorum partition, change the flags to indicate this.
  815. //
  816. if ( ptrMinUsablePartitionInfo != NULL )
  817. {
  818. ptrMinUsablePartitionInfo->dwFlags |= CLUSPROP_PIFLAG_DEFAULT_QUORUM;
  819. }
  820. *OutBuffer = ptrBuffer;
  821. *BytesReturned = bytesReturned;
  822. return(status);
  823. } // GetDiskInfo
  824. DWORD
  825. UpdateAvailableDisks(
  826. )
  827. /*++
  828. Routine Description:
  829. Arguments:
  830. Return Value:
  831. ERROR_SUCCESS if successful.
  832. A Win32 error on failure.
  833. --*/
  834. {
  835. HKEY availDisksKey;
  836. HKEY sigKey;
  837. PSCSI_ADDRESS_ENTRY criticalDiskList = NULL;
  838. DWORD dwError = NO_ERROR;
  839. DWORD enableSanBoot;
  840. BOOL availDisksOpened = FALSE;
  841. BOOL sigKeyOpened = FALSE;
  842. BOOL sigKeyIsEmpty = FALSE;
  843. UPDATE_AVAIL_DISKS updateDisks;
  844. __try {
  845. enableSanBoot = 0;
  846. GetRegDwordValue( CLUSREG_KEYNAME_CLUSSVC_PARAMETERS,
  847. CLUSREG_VALUENAME_MANAGEDISKSONSYSTEMBUSES,
  848. &enableSanBoot );
  849. //
  850. // Delete the old AvailableDisks key. This will remove any stale information.
  851. //
  852. SHDeleteKey( HKEY_LOCAL_MACHINE, CLUSDISK_REGISTRY_AVAILABLE_DISKS );
  853. //
  854. // Open the AvailableDisks key. If the key doesn't exist, it will be created.
  855. //
  856. dwError = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
  857. CLUSDISK_REGISTRY_AVAILABLE_DISKS,
  858. 0,
  859. NULL,
  860. REG_OPTION_NON_VOLATILE,
  861. KEY_CREATE_SUB_KEY,
  862. NULL,
  863. &availDisksKey,
  864. NULL );
  865. if ( NO_ERROR != dwError) {
  866. __leave;
  867. }
  868. availDisksOpened = TRUE;
  869. //
  870. // Open the Signatures key.
  871. //
  872. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  873. CLUSDISK_REGISTRY_SIGNATURES,
  874. 0,
  875. KEY_READ,
  876. &sigKey );
  877. //
  878. // If Signatures key does not exist, save all valid signatures
  879. // in the AvailableDisks key.
  880. //
  881. if ( ERROR_FILE_NOT_FOUND == dwError ) {
  882. dwError = NO_ERROR;
  883. sigKeyIsEmpty = TRUE;
  884. } else if ( NO_ERROR != dwError) {
  885. __leave;
  886. } else {
  887. sigKeyOpened = TRUE;
  888. }
  889. GetCriticalDisks( &criticalDiskList );
  890. ZeroMemory( &updateDisks, sizeof(updateDisks) );
  891. updateDisks.EnableSanBoot = enableSanBoot;
  892. updateDisks.SigKeyIsEmpty = sigKeyIsEmpty;
  893. updateDisks.SigKey = sigKey;
  894. updateDisks.AvailDisksKey = availDisksKey;
  895. updateDisks.CriticalDiskList = criticalDiskList;
  896. EnumerateDisks( UpdateAvailableDisksCallback, &updateDisks );
  897. } __finally {
  898. if ( criticalDiskList ) {
  899. CleanupScsiAddressList( criticalDiskList );
  900. }
  901. if ( availDisksOpened ) {
  902. RegCloseKey( availDisksKey );
  903. }
  904. if ( sigKeyOpened ) {
  905. RegCloseKey( sigKey );
  906. }
  907. }
  908. return dwError;
  909. } // UpdateAvailableDisks
  910. DWORD
  911. UpdateAvailableDisksCallback(
  912. HANDLE DeviceHandle,
  913. DWORD Index,
  914. PVOID Param1
  915. )
  916. {
  917. PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
  918. PUPDATE_AVAIL_DISKS updateDisks = Param1;
  919. PPARTITION_INFORMATION partitionInfo;
  920. PSCSI_ADDRESS_ENTRY criticalDiskList = updateDisks->CriticalDiskList;
  921. DWORD bytesReturned;
  922. DWORD enableSanBoot;
  923. DWORD idx;
  924. BOOL success;
  925. SCSI_ADDRESS scsiAddress;
  926. //
  927. // Look at all disks on the system. For each valid signature, add it to the
  928. // AvailableList if it is not already in the Signature key.
  929. //
  930. UpdateCachedDriveLayout( DeviceHandle );
  931. success = ClRtlGetDriveLayoutTable( DeviceHandle,
  932. &driveLayout,
  933. NULL );
  934. if ( !success || !driveLayout || 0 == driveLayout->Signature ) {
  935. goto FnExit;
  936. }
  937. //
  938. // Walk through partitions and make sure none are dynamic. If any
  939. // partition is dynamic, ignore the disk.
  940. //
  941. for ( idx = 0; idx < driveLayout->PartitionCount; idx++ ) {
  942. partitionInfo = &driveLayout->PartitionEntry[idx];
  943. if ( 0 == partitionInfo->PartitionNumber ) {
  944. continue;
  945. }
  946. //
  947. // If any partition on the disk is dynamic, skip the disk.
  948. //
  949. if ( PARTITION_LDM == partitionInfo->PartitionType ) {
  950. (DiskpLogEvent)(
  951. RESOURCE_TYPE,
  952. LOG_INFORMATION,
  953. L"UpdateAvailableDisks: skipping dynamic disk with signature %1!08x! \n",
  954. driveLayout->Signature );
  955. goto FnExit;
  956. }
  957. }
  958. //
  959. // Get SCSI address info.
  960. //
  961. success = DeviceIoControl( DeviceHandle,
  962. IOCTL_SCSI_GET_ADDRESS,
  963. NULL,
  964. 0,
  965. &scsiAddress,
  966. sizeof(scsiAddress),
  967. &bytesReturned,
  968. NULL );
  969. if ( !success ) {
  970. goto FnExit;
  971. }
  972. //
  973. // Check if disk can be a cluster resource.
  974. //
  975. if ( !updateDisks->EnableSanBoot ) {
  976. //
  977. // Add signature to AvailableDisks key if:
  978. // - the signature is for a disk not on system bus
  979. // - the signature is for a disk not on same bus as paging disk
  980. // - the signature is not already in the Signatures key
  981. //
  982. if ( !IsBusInList( &scsiAddress, criticalDiskList ) &&
  983. ( updateDisks->SigKeyIsEmpty ||
  984. !IsSignatureInRegistry( updateDisks->SigKey, driveLayout->Signature ) ) ) {
  985. AddSignatureToRegistry( updateDisks->AvailDisksKey,
  986. driveLayout->Signature );
  987. } else {
  988. (DiskpLogEvent)(
  989. RESOURCE_TYPE,
  990. LOG_INFORMATION,
  991. L"UpdateAvailableDisks: Disk %1!08x! on critical bus or already clustered \n",
  992. driveLayout->Signature );
  993. }
  994. } else {
  995. (DiskpLogEvent)(
  996. RESOURCE_TYPE,
  997. LOG_INFORMATION,
  998. L"UpdateAvailableDisks: Enable SAN boot key set \n" );
  999. // Allow disks on system bus to be added to cluster.
  1000. //
  1001. // Add signature to AvailableDisks key if:
  1002. // - the signature is not for the system disk
  1003. // - the signature is not a pagefile disk
  1004. // - the signature is not already in the Signatures key
  1005. //
  1006. if ( !IsDiskInList( &scsiAddress, criticalDiskList ) &&
  1007. ( updateDisks->SigKeyIsEmpty ||
  1008. !IsSignatureInRegistry( updateDisks->SigKey, driveLayout->Signature ) ) ) {
  1009. AddSignatureToRegistry( updateDisks->AvailDisksKey,
  1010. driveLayout->Signature );
  1011. } else {
  1012. (DiskpLogEvent)(
  1013. RESOURCE_TYPE,
  1014. LOG_INFORMATION,
  1015. L"UpdateAvailableDisks: Disk %1!08x! is critical disk or already clustered \n",
  1016. driveLayout->Signature );
  1017. }
  1018. }
  1019. FnExit:
  1020. if ( driveLayout ) {
  1021. LocalFree( driveLayout );
  1022. }
  1023. //
  1024. // Always return success so all disks are enumerated.
  1025. //
  1026. return ERROR_SUCCESS;
  1027. } // UpdateAvailableDisksCallback
  1028. DWORD
  1029. AddSignatureToRegistry(
  1030. HKEY RegKey,
  1031. DWORD Signature
  1032. )
  1033. /*++
  1034. Routine Description:
  1035. Add the specified disk signature to the ClusDisk registry subkey.
  1036. The disk signatures are subkeys of the ClusDisk\Parameters\AvailableDisks
  1037. and ClusDisk\Parameters\Signatures keys.
  1038. Arguments:
  1039. RegKey - Previously opened ClusDisk registry subkey
  1040. Signature - Signature value to add
  1041. Return Value:
  1042. Win32 error on failure.
  1043. --*/
  1044. {
  1045. HKEY subKey;
  1046. DWORD dwError;
  1047. WCHAR signatureName[MAX_PATH];
  1048. (DiskpLogEvent)(
  1049. RESOURCE_TYPE,
  1050. LOG_INFORMATION,
  1051. L"AddSignatureToRegistry: Disk %1!08x! added to registry \n",
  1052. Signature );
  1053. if ( FAILED( StringCchPrintf( signatureName,
  1054. SIG_LEN_WITH_NULL,
  1055. TEXT("%08X"),
  1056. Signature ) ) ) {
  1057. dwError = ERROR_INSUFFICIENT_BUFFER;
  1058. } else {
  1059. //
  1060. // Try and create the key. If it exists, the existing key will be opened.
  1061. //
  1062. dwError = RegCreateKeyEx( RegKey,
  1063. signatureName,
  1064. 0,
  1065. NULL,
  1066. REG_OPTION_NON_VOLATILE,
  1067. KEY_WRITE,
  1068. NULL,
  1069. &subKey,
  1070. NULL );
  1071. //
  1072. // If the key exists, ERROR_SUCCESS is still returned.
  1073. //
  1074. if ( ERROR_SUCCESS == dwError ) {
  1075. RegCloseKey( subKey );
  1076. }
  1077. }
  1078. return dwError;
  1079. } // AddSignatureToRegistry
  1080. BOOL
  1081. IsSignatureInRegistry(
  1082. HKEY RegKey,
  1083. DWORD Signature
  1084. )
  1085. /*++
  1086. Routine Description:
  1087. Check if the specified disk signature is in the ClusDisk registry subkey.
  1088. The disk signatures are subkeys of the ClusDisk\Parameters\AvailableDisks
  1089. and ClusDisk\Parameters\Signatures keys.
  1090. On error, assume the key is in the registry so it is not recreated.
  1091. Arguments:
  1092. RegKey - Previously opened ClusDisk registry subkey
  1093. Signature - Signature value to check
  1094. Return Value:
  1095. TRUE - Signature is in registry
  1096. --*/
  1097. {
  1098. DWORD ival;
  1099. DWORD sig;
  1100. DWORD dataLength;
  1101. DWORD dwError;
  1102. BOOL retVal = FALSE;
  1103. WCHAR signatureName[SIG_LEN_WITH_NULL];
  1104. for ( ival = 0; ; ival++ ) {
  1105. dataLength = SIG_LEN_WITH_NULL;
  1106. dwError = RegEnumKey( RegKey,
  1107. ival,
  1108. signatureName,
  1109. dataLength );
  1110. // If the list is exhausted, return FALSE.
  1111. if ( ERROR_NO_MORE_ITEMS == dwError ) {
  1112. break;
  1113. }
  1114. // If some other type of error, return TRUE.
  1115. if ( ERROR_SUCCESS != dwError ) {
  1116. retVal = TRUE;
  1117. break;
  1118. }
  1119. dataLength = swscanf( signatureName, TEXT("%08x"), &sig );
  1120. if ( dataLength != 1 ) {
  1121. retVal = TRUE;
  1122. break;
  1123. }
  1124. // If signature is a subkey, return TRUE.
  1125. if ( sig == Signature ) {
  1126. retVal = TRUE;
  1127. break;
  1128. }
  1129. }
  1130. return retVal;
  1131. } // IsSignatureInRegistry
  1132. VOID
  1133. GetSystemBusInfo(
  1134. PSCSI_ADDRESS_ENTRY *AddressList
  1135. )
  1136. /*++
  1137. Routine Description:
  1138. Need to find out where the NTLDR files reside (the "system disk") and where the
  1139. OS files reside (the "boot disk"). We will call all these disks the "system disk".
  1140. There may be more than one system disk if the disks are mirrored. So if the NTLDR
  1141. files are on a different disk than the OS files, and each of these disks is
  1142. mirrored, we could be looking at 4 different disks.
  1143. Find all the system disks and save the information in a list we can look at later.
  1144. Arguments:
  1145. Return Value:
  1146. None
  1147. --*/
  1148. {
  1149. HANDLE hOsDevice = INVALID_HANDLE_VALUE;
  1150. HANDLE hNtldrDevice = INVALID_HANDLE_VALUE;
  1151. DWORD dwError;
  1152. DWORD bytesReturned;
  1153. if ( !AddressList ) {
  1154. dwError = ERROR_INVALID_PARAMETER;
  1155. goto FnExit;
  1156. }
  1157. //
  1158. // Open the disk with the OS files on it.
  1159. //
  1160. hOsDevice = OpenOSDisk();
  1161. if ( INVALID_HANDLE_VALUE == hOsDevice ) {
  1162. goto FnExit;
  1163. }
  1164. BuildScsiListFromDiskExtents( hOsDevice, AddressList );
  1165. //
  1166. // Now find the disks with NTLDR on it. Disk could be mirrored.
  1167. //
  1168. hNtldrDevice = OpenNtldrDisk();
  1169. if ( INVALID_HANDLE_VALUE == hNtldrDevice ) {
  1170. goto FnExit;
  1171. }
  1172. BuildScsiListFromDiskExtents( hNtldrDevice, AddressList );
  1173. FnExit:
  1174. if ( INVALID_HANDLE_VALUE != hOsDevice ) {
  1175. CloseHandle( hOsDevice );
  1176. }
  1177. if ( INVALID_HANDLE_VALUE != hNtldrDevice ) {
  1178. NtClose( hNtldrDevice );
  1179. }
  1180. return;
  1181. } // GetSystemBusInfo
  1182. HANDLE
  1183. OpenOSDisk(
  1184. )
  1185. {
  1186. PWCHAR systemDir = NULL;
  1187. HANDLE hDevice = INVALID_HANDLE_VALUE;
  1188. DWORD len;
  1189. WCHAR systemPath[] = TEXT("\\\\.\\?:");
  1190. //
  1191. // First find the disks with OS files. Disk could be mirrored.
  1192. //
  1193. systemDir = LocalAlloc( LPTR, MAX_PATH * sizeof(WCHAR) );
  1194. if ( !systemDir ) {
  1195. goto FnExit;
  1196. }
  1197. len = GetSystemDirectory( systemDir,
  1198. MAX_PATH );
  1199. if ( !len || len < 3 ) {
  1200. goto FnExit;
  1201. }
  1202. //
  1203. // If system path doesn't start with a drive letter, exit.
  1204. // c:\windows ==> c:
  1205. if ( L':' != systemDir[1] ) {
  1206. goto FnExit;
  1207. }
  1208. //
  1209. // Stuff the drive letter in the system path.
  1210. //
  1211. systemPath[4] = systemDir[0];
  1212. //
  1213. // Now open the device.
  1214. //
  1215. hDevice = CreateFile( systemPath,
  1216. GENERIC_READ | GENERIC_WRITE,
  1217. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1218. NULL,
  1219. OPEN_EXISTING,
  1220. FILE_ATTRIBUTE_NORMAL,
  1221. NULL );
  1222. FnExit:
  1223. if ( systemDir ) {
  1224. LocalFree( systemDir );
  1225. }
  1226. return hDevice;
  1227. } // OpenOSDisk
  1228. HANDLE
  1229. OpenNtldrDisk(
  1230. )
  1231. {
  1232. PWSTR systemPartition = NULL;
  1233. HANDLE hDevice = INVALID_HANDLE_VALUE;
  1234. HKEY regKey = NULL;
  1235. NTSTATUS ntStatus;
  1236. DWORD dwError;
  1237. DWORD cbSystemPartition;
  1238. DWORD cbDeviceName;
  1239. DWORD type = 0;
  1240. UNICODE_STRING unicodeName;
  1241. OBJECT_ATTRIBUTES objAttributes;
  1242. IO_STATUS_BLOCK ioStatusBlock;
  1243. //
  1244. // Open the reg key to find the system partition
  1245. //
  1246. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // hKey
  1247. DISKS_REGKEY_SETUP, // lpSubKey
  1248. 0, // ulOptions--Reserved, must be 0
  1249. KEY_READ, // samDesired
  1250. &regKey // phkResult
  1251. );
  1252. if ( ERROR_SUCCESS != dwError ) {
  1253. goto FnExit;
  1254. }
  1255. //
  1256. // Allocate a reasonably sized buffer for the system partition, to
  1257. // start off with. If this isn't big enough, we'll re-allocate as
  1258. // needed.
  1259. //
  1260. cbSystemPartition = MAX_PATH + 1;
  1261. systemPartition = LocalAlloc( LPTR, cbSystemPartition );
  1262. if ( !systemPartition ) {
  1263. goto FnExit;
  1264. }
  1265. //
  1266. // Get the system partition device Name. This is of the form
  1267. // \Device\Harddisk0\Partition1 (basic disks)
  1268. // \Device\HarddiskDmVolumes\DgName\Volume1 (dynamic disks)
  1269. //
  1270. dwError = RegQueryValueEx( regKey,
  1271. DISKS_REGVALUE_SYSTEM_PARTITION,
  1272. NULL,
  1273. &type,
  1274. (LPBYTE)systemPartition,
  1275. &cbSystemPartition // \0 is included
  1276. );
  1277. while ( ERROR_MORE_DATA == dwError ) {
  1278. //
  1279. // Our buffer wasn't big enough, cbSystemPartition contains
  1280. // the required size.
  1281. //
  1282. LocalFree( systemPartition );
  1283. systemPartition = NULL;
  1284. systemPartition = LocalAlloc( LPTR, cbSystemPartition );
  1285. if ( !systemPartition ) {
  1286. goto FnExit;
  1287. }
  1288. dwError = RegQueryValueEx( regKey,
  1289. DISKS_REGVALUE_SYSTEM_PARTITION,
  1290. NULL,
  1291. &type,
  1292. (LPBYTE)systemPartition,
  1293. &cbSystemPartition // \0 is included
  1294. );
  1295. }
  1296. RtlInitUnicodeString( &unicodeName, systemPartition );
  1297. InitializeObjectAttributes( &objAttributes,
  1298. &unicodeName,
  1299. OBJ_CASE_INSENSITIVE,
  1300. NULL,
  1301. NULL );
  1302. ntStatus = NtCreateFile( &hDevice,
  1303. SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
  1304. &objAttributes,
  1305. &ioStatusBlock,
  1306. NULL,
  1307. FILE_ATTRIBUTE_NORMAL,
  1308. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1309. FILE_OPEN,
  1310. 0,
  1311. NULL,
  1312. 0 );
  1313. if ( !NT_SUCCESS(ntStatus) ) {
  1314. hDevice = INVALID_HANDLE_VALUE;
  1315. }
  1316. FnExit:
  1317. if ( regKey ) {
  1318. RegCloseKey( regKey );
  1319. }
  1320. if ( systemPartition ) {
  1321. LocalFree( systemPartition );
  1322. }
  1323. return hDevice;
  1324. } // OpenNtldrDisk
  1325. DWORD
  1326. BuildScsiListFromDiskExtents(
  1327. IN HANDLE DevHandle,
  1328. PSCSI_ADDRESS_ENTRY *AddressList
  1329. )
  1330. {
  1331. PVOLUME_DISK_EXTENTS diskExtents = NULL;
  1332. PDISK_EXTENT diskExt;
  1333. HANDLE hDevice = INVALID_HANDLE_VALUE;
  1334. DWORD dwError = ERROR_SUCCESS;
  1335. DWORD idx;
  1336. DWORD bytesReturned;
  1337. DWORD deviceNameChars = MAX_PATH;
  1338. SCSI_ADDRESS scsiAddress;
  1339. PWCHAR deviceName = NULL;
  1340. deviceName = LocalAlloc( LPTR, deviceNameChars * sizeof(WCHAR) );
  1341. if ( !deviceName ) {
  1342. dwError = GetLastError();
  1343. goto FnExit;
  1344. }
  1345. //
  1346. // Find out how many physical disks are represented by this device.
  1347. //
  1348. dwError = GetVolumeDiskExtents( DevHandle, &diskExtents );
  1349. if ( ERROR_SUCCESS != dwError || !diskExtents ) {
  1350. goto FnExit;
  1351. }
  1352. //
  1353. // For each physical disk, get the scsi address and add it to the list.
  1354. //
  1355. for ( idx = 0; idx < diskExtents->NumberOfDiskExtents; idx++ ) {
  1356. diskExt = &diskExtents->Extents[idx];
  1357. (VOID) StringCchPrintf( deviceName,
  1358. deviceNameChars,
  1359. TEXT("\\\\.\\\\PhysicalDrive%d"),
  1360. diskExt->DiskNumber );
  1361. hDevice = CreateFile( deviceName,
  1362. GENERIC_READ | GENERIC_WRITE,
  1363. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1364. NULL,
  1365. OPEN_EXISTING,
  1366. FILE_ATTRIBUTE_NORMAL,
  1367. NULL );
  1368. if ( INVALID_HANDLE_VALUE == hDevice ) {
  1369. continue;
  1370. }
  1371. ZeroMemory( &scsiAddress, sizeof( scsiAddress ) );
  1372. if ( DeviceIoControl( hDevice,
  1373. IOCTL_SCSI_GET_ADDRESS,
  1374. NULL,
  1375. 0,
  1376. &scsiAddress,
  1377. sizeof(scsiAddress),
  1378. &bytesReturned,
  1379. NULL ) ) {
  1380. AddScsiAddressToList( &scsiAddress, AddressList );
  1381. }
  1382. CloseHandle( hDevice );
  1383. hDevice = INVALID_HANDLE_VALUE;
  1384. }
  1385. FnExit:
  1386. if ( diskExtents ) {
  1387. LocalFree( diskExtents );
  1388. }
  1389. if ( deviceName ) {
  1390. LocalFree( deviceName );
  1391. }
  1392. return dwError;
  1393. } // BuildScsiListFromDiskExtents
  1394. DWORD
  1395. GetVolumeDiskExtents(
  1396. IN HANDLE DevHandle,
  1397. OUT PVOLUME_DISK_EXTENTS *DiskExtents
  1398. )
  1399. {
  1400. PVOLUME_DISK_EXTENTS extents = NULL;
  1401. DWORD dwError = ERROR_SUCCESS;
  1402. DWORD bytesReturned;
  1403. DWORD sizeExtents;
  1404. BOOL result;
  1405. if ( !DiskExtents ) {
  1406. goto FnExit;
  1407. }
  1408. *DiskExtents = NULL;
  1409. sizeExtents = ( sizeof(VOLUME_DISK_EXTENTS) + 10 * sizeof(DISK_EXTENT) );
  1410. extents = (PVOLUME_DISK_EXTENTS) LocalAlloc( LPTR, sizeExtents );
  1411. if ( !extents ) {
  1412. dwError = GetLastError();
  1413. goto FnExit;
  1414. }
  1415. result = DeviceIoControl( DevHandle,
  1416. IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
  1417. NULL,
  1418. 0,
  1419. extents,
  1420. sizeExtents,
  1421. &bytesReturned,
  1422. NULL );
  1423. //
  1424. // We're doing this in a while loop because if the disk configuration
  1425. // changes in the small interval between when we get the reqd buffer
  1426. // size and when we send the ioctl again with a buffer of the "reqd"
  1427. // size, we may still end up with a buffer that isn't big enough.
  1428. //
  1429. while ( !result ) {
  1430. dwError = GetLastError();
  1431. if ( ERROR_MORE_DATA == dwError ) {
  1432. //
  1433. // The buffer was too small, reallocate the requested size.
  1434. //
  1435. dwError = ERROR_SUCCESS;
  1436. sizeExtents = ( sizeof(VOLUME_DISK_EXTENTS) +
  1437. ((extents->NumberOfDiskExtents) * sizeof(DISK_EXTENT)) );
  1438. LocalFree( extents );
  1439. extents = NULL;
  1440. extents = (PVOLUME_DISK_EXTENTS) LocalAlloc( LPTR, sizeExtents );
  1441. if ( !extents ) {
  1442. dwError = GetLastError();
  1443. goto FnExit;
  1444. }
  1445. result = DeviceIoControl( DevHandle,
  1446. IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
  1447. NULL,
  1448. 0,
  1449. extents,
  1450. sizeExtents,
  1451. &bytesReturned,
  1452. NULL );
  1453. } else {
  1454. // Some other error, return error.
  1455. goto FnExit;
  1456. }
  1457. }
  1458. *DiskExtents = extents;
  1459. FnExit:
  1460. if ( ERROR_SUCCESS != dwError && extents ) {
  1461. LocalFree( extents );
  1462. }
  1463. return dwError;
  1464. } // GetVolumeDiskExtents
  1465. DWORD
  1466. GetScsiAddressForDrive(
  1467. WCHAR DriveLetter,
  1468. PSCSI_ADDRESS ScsiAddress
  1469. )
  1470. {
  1471. HANDLE hDev = INVALID_HANDLE_VALUE;
  1472. DWORD dwError = NO_ERROR;
  1473. DWORD bytesReturned;
  1474. WCHAR diskName[32];
  1475. //
  1476. // Open device to get SCSI address.
  1477. //
  1478. (VOID) StringCchPrintf( diskName,
  1479. RTL_NUMBER_OF(diskName),
  1480. TEXT("\\\\.\\%wc:"),
  1481. DriveLetter );
  1482. hDev = CreateFile( diskName,
  1483. GENERIC_READ | GENERIC_WRITE,
  1484. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1485. NULL,
  1486. OPEN_EXISTING,
  1487. FILE_ATTRIBUTE_NORMAL,
  1488. NULL );
  1489. if ( INVALID_HANDLE_VALUE == hDev ) {
  1490. dwError = GetLastError();
  1491. goto FnExit;
  1492. }
  1493. ZeroMemory( ScsiAddress, sizeof(SCSI_ADDRESS) );
  1494. if ( !DeviceIoControl( hDev,
  1495. IOCTL_SCSI_GET_ADDRESS,
  1496. NULL,
  1497. 0,
  1498. ScsiAddress,
  1499. sizeof(SCSI_ADDRESS),
  1500. &bytesReturned,
  1501. FALSE ) ) {
  1502. dwError = GetLastError();
  1503. }
  1504. FnExit:
  1505. if ( INVALID_HANDLE_VALUE != hDev ) {
  1506. CloseHandle( hDev );
  1507. }
  1508. return dwError;
  1509. } // GetScsiAddressForDrive
  1510. DWORD
  1511. GetCriticalDisks(
  1512. PSCSI_ADDRESS_ENTRY *AddressList
  1513. )
  1514. {
  1515. //
  1516. // Add system disk(s) to the list.
  1517. //
  1518. GetSystemBusInfo( AddressList );
  1519. //
  1520. // Add disks with page files to the list.
  1521. //
  1522. GetPagefileDisks( AddressList );
  1523. //
  1524. // Add disks with crash dump files to the list.
  1525. //
  1526. GetCrashdumpDisks( AddressList );
  1527. //
  1528. // Add disks with hibernation files to the list.
  1529. //
  1530. return NO_ERROR;
  1531. } // GetCriticalDisks
  1532. DWORD
  1533. GetPagefileDisks(
  1534. PSCSI_ADDRESS_ENTRY *AddressList
  1535. )
  1536. /*++
  1537. Routine Description:
  1538. Finds pagefile disks and saves path info. Since these files can
  1539. be added and removed as the system is running, it is best to build
  1540. this list when we need it.
  1541. Arguments:
  1542. Return Value:
  1543. NO_ERROR if successful
  1544. Win32 error code otherwise
  1545. --*/
  1546. {
  1547. LPWSTR pagefileStrs = NULL;
  1548. PWCHAR currentStr;
  1549. HKEY pagefileKey = INVALID_HANDLE_VALUE;
  1550. DWORD dwError = NO_ERROR;
  1551. SCSI_ADDRESS scsiAddress;
  1552. if ( !AddressList ) {
  1553. dwError = ERROR_INVALID_PARAMETER;
  1554. goto FnExit;
  1555. }
  1556. dwError = RegOpenKey( HKEY_LOCAL_MACHINE,
  1557. TEXT("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management"),
  1558. &pagefileKey );
  1559. if ( dwError != NO_ERROR ) {
  1560. goto FnExit;
  1561. }
  1562. //
  1563. // Get the pagefile REG_MULTI_SZ buffer.
  1564. //
  1565. pagefileStrs = GetRegParameter( pagefileKey,
  1566. TEXT("PagingFiles") );
  1567. if ( !pagefileStrs ) {
  1568. dwError = ERROR_INVALID_DATA;
  1569. goto FnExit;
  1570. }
  1571. //
  1572. // Walk through the REG_MULTI_SZ buffer. For each entry, get the
  1573. // SCSI address and add it to the list.
  1574. //
  1575. // Syntax is:
  1576. // "C:\pagefile.sys 1152 1152"
  1577. // "D:\pagefile.sys 1152 1152"
  1578. //
  1579. currentStr = (PWCHAR)pagefileStrs;
  1580. while ( *currentStr != L'\0' ) {
  1581. //
  1582. // Check that beginning of line looks like a drive letter and path.
  1583. //
  1584. if ( wcslen( currentStr ) <= 3 ||
  1585. !isalpha( *currentStr ) ||
  1586. *(currentStr + 1) != L':' ||
  1587. *(currentStr + 2) != L'\\' ) {
  1588. dwError = ERROR_INVALID_DATA;
  1589. goto FnExit;
  1590. }
  1591. dwError = GetScsiAddressForDrive( *currentStr,
  1592. &scsiAddress );
  1593. if ( NO_ERROR == dwError ) {
  1594. AddScsiAddressToList( &scsiAddress, AddressList );
  1595. }
  1596. //
  1597. // Skip to next string. Should be pointing to the next string
  1598. // if it exists, or to another NULL if the end of the list.
  1599. //
  1600. while ( *currentStr++ != L'\0' ) {
  1601. ; // do nothing...
  1602. }
  1603. }
  1604. FnExit:
  1605. if ( pagefileStrs ) {
  1606. LocalFree( pagefileStrs );
  1607. }
  1608. if ( INVALID_HANDLE_VALUE != pagefileKey ) {
  1609. RegCloseKey( pagefileKey );
  1610. }
  1611. return dwError;
  1612. } // GetPagefileDisks
  1613. DWORD
  1614. GetCrashdumpDisks(
  1615. PSCSI_ADDRESS_ENTRY *AddressList
  1616. )
  1617. /*++
  1618. Routine Description:
  1619. Finds crash dump disks and saves path info. Since these files can
  1620. be added and removed as the system is running, it is best to build
  1621. this list when we need it.
  1622. Arguments:
  1623. Return Value:
  1624. NO_ERROR if successful
  1625. Win32 error code otherwise
  1626. --*/
  1627. {
  1628. LPWSTR dumpfileStr = NULL;
  1629. PWCHAR currentStr;
  1630. HKEY crashKey = INVALID_HANDLE_VALUE;
  1631. DWORD dwError = NO_ERROR;
  1632. DWORD enableCrashDump;
  1633. SCSI_ADDRESS scsiAddress;
  1634. //
  1635. // First check whether crash dump is enabled. If not, we are done.
  1636. //
  1637. enableCrashDump = 0;
  1638. dwError = GetRegDwordValue( TEXT("SYSTEM\\CurrentControlSet\\Control\\CrashControl"),
  1639. TEXT("CrashDumpEnabled"),
  1640. &enableCrashDump );
  1641. if ( NO_ERROR != dwError || 0 == enableCrashDump ) {
  1642. goto FnExit;
  1643. }
  1644. dwError = RegOpenKey( HKEY_LOCAL_MACHINE,
  1645. TEXT("SYSTEM\\CurrentControlSet\\Control\\CrashControl"),
  1646. &crashKey );
  1647. if ( dwError != NO_ERROR ) {
  1648. goto FnExit;
  1649. }
  1650. //
  1651. // Get the pagefile REG_EXPAND_SZ buffer. The routine will expand the
  1652. // string before returning.
  1653. //
  1654. dumpfileStr = GetRegParameter( crashKey,
  1655. TEXT("DumpFile") );
  1656. if ( !dumpfileStr ) {
  1657. dwError = ERROR_INVALID_DATA;
  1658. goto FnExit;
  1659. }
  1660. currentStr = (PWCHAR)dumpfileStr;
  1661. //
  1662. // Check that beginning of line looks like a drive letter and path.
  1663. //
  1664. if ( wcslen( currentStr ) <= 3 ||
  1665. !iswalpha( *currentStr ) ||
  1666. *(currentStr + 1) != L':' ||
  1667. *(currentStr + 2) != L'\\' ) {
  1668. dwError = ERROR_INVALID_DATA;
  1669. goto FnExit;
  1670. }
  1671. dwError = GetScsiAddressForDrive( *currentStr,
  1672. &scsiAddress );
  1673. if ( NO_ERROR == dwError ) {
  1674. AddScsiAddressToList( &scsiAddress, AddressList );
  1675. }
  1676. // Do we need to also store the disk where MinidumpDir is located?
  1677. FnExit:
  1678. if ( INVALID_HANDLE_VALUE != crashKey ) {
  1679. RegCloseKey( crashKey );
  1680. }
  1681. if ( dumpfileStr ) {
  1682. LocalFree( dumpfileStr );
  1683. }
  1684. return dwError;
  1685. } // GetCrashdumpDisks
  1686. DWORD
  1687. AddScsiAddressToList(
  1688. PSCSI_ADDRESS ScsiAddress,
  1689. PSCSI_ADDRESS_ENTRY *AddressList
  1690. )
  1691. {
  1692. PSCSI_ADDRESS_ENTRY entry;
  1693. DWORD dwError = ERROR_SUCCESS;
  1694. if ( !ScsiAddress || !AddressList ) {
  1695. goto FnExit;
  1696. }
  1697. //
  1698. // Optimization: don't add the SCSI address if it already
  1699. // matches one in the list.
  1700. //
  1701. if ( IsDiskInList( ScsiAddress, *AddressList ) ) {
  1702. goto FnExit;
  1703. }
  1704. entry = LocalAlloc( LPTR, sizeof( SCSI_ADDRESS_ENTRY ) );
  1705. if ( !entry ) {
  1706. dwError = GetLastError();
  1707. goto FnExit;
  1708. }
  1709. entry->ScsiAddress.Length = ScsiAddress->Length;
  1710. entry->ScsiAddress.PortNumber = ScsiAddress->PortNumber;
  1711. entry->ScsiAddress.PathId = ScsiAddress->PathId;
  1712. entry->ScsiAddress.TargetId = ScsiAddress->TargetId;
  1713. entry->ScsiAddress.Lun = ScsiAddress->Lun;
  1714. if ( *AddressList ) {
  1715. entry->Next = *AddressList;
  1716. *AddressList = entry;
  1717. } else {
  1718. *AddressList = entry;
  1719. }
  1720. FnExit:
  1721. return dwError;
  1722. } // AddScsiAddressToList
  1723. VOID
  1724. CleanupScsiAddressList(
  1725. PSCSI_ADDRESS_ENTRY AddressList
  1726. )
  1727. {
  1728. PSCSI_ADDRESS_ENTRY entry;
  1729. PSCSI_ADDRESS_ENTRY next;
  1730. entry = AddressList;
  1731. while ( entry ) {
  1732. next = entry->Next;
  1733. LocalFree( entry );
  1734. entry = next;
  1735. }
  1736. } // CleanupSystemBusInfo
  1737. BOOL
  1738. IsDiskInList(
  1739. PSCSI_ADDRESS DiskAddr,
  1740. PSCSI_ADDRESS_ENTRY AddressList
  1741. )
  1742. {
  1743. PSCSI_ADDRESS_ENTRY entry = AddressList;
  1744. PSCSI_ADDRESS_ENTRY next;
  1745. while ( entry ) {
  1746. next = entry->Next;
  1747. if ( DiskAddr->PortNumber == entry->ScsiAddress.PortNumber &&
  1748. DiskAddr->PathId == entry->ScsiAddress.PathId &&
  1749. DiskAddr->TargetId == entry->ScsiAddress.TargetId &&
  1750. DiskAddr->Lun == entry->ScsiAddress.Lun ) {
  1751. return TRUE;
  1752. }
  1753. entry = next;
  1754. }
  1755. return FALSE;
  1756. } // IsDiskInList
  1757. BOOL
  1758. IsBusInList(
  1759. PSCSI_ADDRESS DiskAddr,
  1760. PSCSI_ADDRESS_ENTRY AddressList
  1761. )
  1762. {
  1763. PSCSI_ADDRESS_ENTRY entry = AddressList;
  1764. PSCSI_ADDRESS_ENTRY next;
  1765. while ( entry ) {
  1766. next = entry->Next;
  1767. if ( DiskAddr->PortNumber == entry->ScsiAddress.PortNumber &&
  1768. DiskAddr->PathId == entry->ScsiAddress.PathId ) {
  1769. return TRUE;
  1770. }
  1771. entry = next;
  1772. }
  1773. return FALSE;
  1774. } // IsBusInList
  1775. DWORD
  1776. EnumerateDisks(
  1777. LPDISK_ENUM_CALLBACK DiskEnumCallback,
  1778. PVOID Param1
  1779. )
  1780. {
  1781. PSP_DEVICE_INTERFACE_DETAIL_DATA pDiDetail = NULL;
  1782. HANDLE hDevice;
  1783. DWORD dwError = ERROR_SUCCESS;
  1784. DWORD count;
  1785. DWORD sizeDiDetail;
  1786. BOOL result;
  1787. HDEVINFO hdevInfo = INVALID_HANDLE_VALUE;
  1788. SP_DEVICE_INTERFACE_DATA devInterfaceData;
  1789. SP_DEVINFO_DATA devInfoData;
  1790. if ( !DiskEnumCallback ) {
  1791. dwError = ERROR_INVALID_PARAMETER;
  1792. goto FnExit;
  1793. }
  1794. //
  1795. // Get a device interface set which includes all Disk devices
  1796. // present on the machine. DiskClassGuid is a predefined GUID that
  1797. // will return all disk-type device interfaces
  1798. //
  1799. hdevInfo = SetupDiGetClassDevs( &DiskClassGuid,
  1800. NULL,
  1801. NULL,
  1802. DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
  1803. if ( INVALID_HANDLE_VALUE == hdevInfo ) {
  1804. dwError = GetLastError();
  1805. goto FnExit;
  1806. }
  1807. ZeroMemory( &devInterfaceData, sizeof( SP_DEVICE_INTERFACE_DATA) );
  1808. //
  1809. // Iterate over all devices interfaces in the set
  1810. //
  1811. for ( count = 0; ; count++ ) {
  1812. // must set size first
  1813. devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  1814. //
  1815. // Retrieve the device interface data for each device interface
  1816. //
  1817. result = SetupDiEnumDeviceInterfaces( hdevInfo,
  1818. NULL,
  1819. &DiskClassGuid,
  1820. count,
  1821. &devInterfaceData );
  1822. if ( !result ) {
  1823. //
  1824. // If we retrieved the last item, break
  1825. //
  1826. dwError = GetLastError();
  1827. if ( ERROR_NO_MORE_ITEMS == dwError ) {
  1828. dwError = ERROR_SUCCESS;
  1829. break;
  1830. }
  1831. //
  1832. // Some other error occurred. Stop processing.
  1833. //
  1834. goto FnExit;
  1835. }
  1836. //
  1837. // Get the required buffer-size for the device path. Note that
  1838. // this call is expected to fail with insufficient buffer error.
  1839. //
  1840. result = SetupDiGetDeviceInterfaceDetail( hdevInfo,
  1841. &devInterfaceData,
  1842. NULL,
  1843. 0,
  1844. &sizeDiDetail,
  1845. NULL
  1846. );
  1847. if ( !result ) {
  1848. dwError = GetLastError();
  1849. //
  1850. // If a value other than "insufficient buffer" is returned,
  1851. // we have to skip this device.
  1852. //
  1853. if ( ERROR_INSUFFICIENT_BUFFER != dwError ) {
  1854. continue;
  1855. }
  1856. } else {
  1857. //
  1858. // The call should have failed since we're getting the
  1859. // required buffer size. If it doesn't fail, something bad
  1860. // happened.
  1861. //
  1862. continue;
  1863. }
  1864. //
  1865. // Allocate memory for the device interface detail.
  1866. //
  1867. pDiDetail = LocalAlloc( LPTR, sizeDiDetail );
  1868. if ( !pDiDetail ) {
  1869. dwError = GetLastError();
  1870. goto FnExit;
  1871. }
  1872. // must set the struct's size member
  1873. pDiDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  1874. devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  1875. //
  1876. // Finally, retrieve the device interface detail info.
  1877. //
  1878. result = SetupDiGetDeviceInterfaceDetail( hdevInfo,
  1879. &devInterfaceData,
  1880. pDiDetail,
  1881. sizeDiDetail,
  1882. NULL,
  1883. &devInfoData
  1884. );
  1885. if ( !result ) {
  1886. dwError = GetLastError();
  1887. LocalFree( pDiDetail );
  1888. pDiDetail = NULL;
  1889. //
  1890. // Shouldn't fail, if it does, try the next device.
  1891. //
  1892. continue;
  1893. }
  1894. //
  1895. // Open a handle to the device.
  1896. //
  1897. hDevice = CreateFile( pDiDetail->DevicePath,
  1898. GENERIC_READ | GENERIC_WRITE,
  1899. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1900. NULL,
  1901. OPEN_EXISTING,
  1902. FILE_ATTRIBUTE_NORMAL,
  1903. NULL );
  1904. LocalFree( pDiDetail );
  1905. pDiDetail = NULL;
  1906. if ( INVALID_HANDLE_VALUE == hDevice ) {
  1907. continue;
  1908. }
  1909. //
  1910. // Call the specified callback routine. An error returned stops the
  1911. // disk enumeration.
  1912. //
  1913. dwError = (*DiskEnumCallback)( hDevice, count, Param1 );
  1914. CloseHandle( hDevice );
  1915. if ( ERROR_SUCCESS != dwError ) {
  1916. goto FnExit;
  1917. }
  1918. }
  1919. FnExit:
  1920. if ( INVALID_HANDLE_VALUE != hdevInfo ) {
  1921. SetupDiDestroyDeviceInfoList( hdevInfo );
  1922. }
  1923. if ( pDiDetail ) {
  1924. LocalFree( pDiDetail );
  1925. }
  1926. return dwError;
  1927. } // EnumerateDisks
  1928. DWORD
  1929. GetSerialNumber(
  1930. IN DWORD Signature,
  1931. OUT LPWSTR *SerialNumber
  1932. )
  1933. /*++
  1934. Routine Description:
  1935. Find the disk serial number for a given signature.
  1936. Arguments:
  1937. Signature - the signature to find.
  1938. SerialNumber - pointer to allocated buffer holding the returned serial
  1939. number. The caller must free this buffer.
  1940. Return Value:
  1941. ERROR_SUCCESS if successful.
  1942. A Win32 error on failure.
  1943. --*/
  1944. {
  1945. DWORD dwError;
  1946. SERIAL_INFO serialInfo;
  1947. if ( !Signature || !SerialNumber ) {
  1948. dwError = ERROR_INVALID_PARAMETER;
  1949. goto FnExit;
  1950. }
  1951. *SerialNumber = NULL;
  1952. ZeroMemory( &serialInfo, sizeof(serialInfo) );
  1953. serialInfo.Signature = Signature;
  1954. serialInfo.Error = ERROR_SUCCESS;
  1955. dwError = EnumerateDisks( GetSerialNumberCallback, &serialInfo );
  1956. //
  1957. // The callback routine will use ERROR_POPUP_ALREADY_ACTIVE to stop
  1958. // the disk enumeration. Reset the value to the value returned
  1959. // in the SERIAL_INFO structure.
  1960. //
  1961. if ( ERROR_POPUP_ALREADY_ACTIVE == dwError ) {
  1962. dwError = serialInfo.Error;
  1963. }
  1964. // This will either be NULL or an allocated buffer. Caller is responsible
  1965. // to free.
  1966. *SerialNumber = serialInfo.SerialNumber;
  1967. FnExit:
  1968. return dwError;
  1969. } // GetSerialNumber
  1970. DWORD
  1971. GetSerialNumberCallback(
  1972. HANDLE DevHandle,
  1973. DWORD Index,
  1974. PVOID Param1
  1975. )
  1976. /*++
  1977. Routine Description:
  1978. Find the disk serial number for a given signature.
  1979. Arguments:
  1980. DevHandle - open handle to a physical disk. Do not close
  1981. the handle on exit.
  1982. Index - current disk count. Not used.
  1983. Param1 - pointer to PSERIAL_INFO structure.
  1984. Return Value:
  1985. ERROR_SUCCESS to continue disk enumeration.
  1986. ERROR_POPUP_ALREADY_ACTIVE to stop disk enumeration. This
  1987. value will be changed to ERROR_SUCCESS by GetScsiAddress.
  1988. --*/
  1989. {
  1990. PSERIAL_INFO serialInfo = Param1;
  1991. PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
  1992. DWORD dwError = ERROR_SUCCESS;
  1993. BOOL success;
  1994. // Always return success to keep enumerating disks. Any
  1995. // error value will stop the disk enumeration.
  1996. STORAGE_PROPERTY_QUERY propQuery;
  1997. UpdateCachedDriveLayout( DevHandle );
  1998. success = ClRtlGetDriveLayoutTable( DevHandle,
  1999. &driveLayout,
  2000. NULL );
  2001. if ( !success || !driveLayout ||
  2002. ( driveLayout->Signature != serialInfo->Signature ) ) {
  2003. goto FnExit;
  2004. }
  2005. //
  2006. // At this point, we have a signature match. Now this function
  2007. // must return this error value to stop the disk enumeration. The
  2008. // error value for the serial number retrieval will be returned in
  2009. // the SERIAL_INFO structure.
  2010. //
  2011. dwError = ERROR_POPUP_ALREADY_ACTIVE;
  2012. serialInfo->Error = RetrieveSerialNumber( DevHandle, &serialInfo->SerialNumber );
  2013. FnExit:
  2014. if ( driveLayout ) {
  2015. LocalFree( driveLayout );
  2016. }
  2017. if ( serialInfo->Error != ERROR_SUCCESS && serialInfo->SerialNumber ) {
  2018. LocalFree( serialInfo->SerialNumber );
  2019. }
  2020. return dwError;
  2021. } // GetSerialNumberCallback
  2022. DWORD
  2023. GetSignatureFromSerialNumber(
  2024. IN LPWSTR SerialNumber,
  2025. OUT LPDWORD Signature
  2026. )
  2027. /*++
  2028. Routine Description:
  2029. Find the disk signature for the given serial number.
  2030. Arguments:
  2031. SerialNumber - pointer to allocated buffer holding the serial number.
  2032. Signature - pointer to location to hold the signature.
  2033. Return Value:
  2034. ERROR_SUCCESS if successful.
  2035. A Win32 error on failure.
  2036. --*/
  2037. {
  2038. DWORD dwError;
  2039. SERIAL_INFO serialInfo;
  2040. if ( !Signature || !SerialNumber ) {
  2041. dwError = ERROR_INVALID_PARAMETER;
  2042. goto FnExit;
  2043. }
  2044. *Signature = 0;
  2045. ZeroMemory( &serialInfo, sizeof(serialInfo) );
  2046. serialInfo.SerialNumber = SerialNumber;
  2047. serialInfo.Error = ERROR_SUCCESS;
  2048. dwError = EnumerateDisks( GetSigFromSerNumCallback, &serialInfo );
  2049. //
  2050. // The callback routine will use ERROR_POPUP_ALREADY_ACTIVE to stop
  2051. // the disk enumeration. Reset the value to the value returned
  2052. // in the SERIAL_INFO structure.
  2053. //
  2054. if ( ERROR_POPUP_ALREADY_ACTIVE == dwError ) {
  2055. dwError = serialInfo.Error;
  2056. }
  2057. // This signature will either be zero or valid.
  2058. *Signature = serialInfo.Signature;
  2059. FnExit:
  2060. return dwError;
  2061. } // GetSignatureFromSerialNumber
  2062. DWORD
  2063. GetSigFromSerNumCallback(
  2064. HANDLE DevHandle,
  2065. DWORD Index,
  2066. PVOID Param1
  2067. )
  2068. /*++
  2069. Routine Description:
  2070. Find the disk signature for a given serial number.
  2071. Arguments:
  2072. DevHandle - open handle to a physical disk. Do not close
  2073. the handle on exit.
  2074. Index - current disk count. Not used.
  2075. Param1 - pointer to PSERIAL_INFO structure.
  2076. Return Value:
  2077. ERROR_SUCCESS to continue disk enumeration.
  2078. ERROR_POPUP_ALREADY_ACTIVE to stop disk enumeration. This
  2079. value will be changed to ERROR_SUCCESS by GetScsiAddress.
  2080. --*/
  2081. {
  2082. PSERIAL_INFO serialInfo = Param1;
  2083. LPWSTR serialNum = NULL;
  2084. PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
  2085. DWORD dwError = ERROR_SUCCESS;
  2086. DWORD oldLen;
  2087. DWORD newLen;
  2088. BOOL success;
  2089. // Always return success to keep enumerating disks. Any
  2090. // error value will stop the disk enumeration.
  2091. dwError = RetrieveSerialNumber( DevHandle, &serialNum );
  2092. if ( NO_ERROR != dwError || !serialNum ) {
  2093. dwError = ERROR_SUCCESS;
  2094. goto FnExit;
  2095. }
  2096. //
  2097. // We have a serial number, now try to match it.
  2098. //
  2099. newLen = wcslen( serialNum );
  2100. oldLen = wcslen( serialInfo->SerialNumber );
  2101. if ( newLen != oldLen ||
  2102. 0 != wcsncmp( serialNum, serialInfo->SerialNumber, newLen ) ) {
  2103. goto FnExit;
  2104. }
  2105. //
  2106. // At this point, we have a serial number match. Now this function
  2107. // must return this error value to stop the disk enumeration. The
  2108. // error value for the signature retrieval will be returned in
  2109. // the SERIAL_INFO structure.
  2110. //
  2111. dwError = ERROR_POPUP_ALREADY_ACTIVE;
  2112. UpdateCachedDriveLayout( DevHandle );
  2113. success = ClRtlGetDriveLayoutTable( DevHandle,
  2114. &driveLayout,
  2115. NULL );
  2116. if ( !success || !driveLayout ) {
  2117. serialInfo->Error = ERROR_INVALID_DATA;
  2118. goto FnExit;
  2119. }
  2120. serialInfo->Signature = driveLayout->Signature;
  2121. serialInfo->Error = NO_ERROR;
  2122. FnExit:
  2123. if ( driveLayout ) {
  2124. LocalFree( driveLayout );
  2125. }
  2126. if ( serialNum ) {
  2127. LocalFree( serialNum );
  2128. }
  2129. return dwError;
  2130. } // GetSigFromSerNumCallback
  2131. DWORD
  2132. RetrieveSerialNumber(
  2133. HANDLE DevHandle,
  2134. LPWSTR *SerialNumber
  2135. )
  2136. {
  2137. PSTORAGE_DEVICE_DESCRIPTOR descriptor = NULL;
  2138. PWCHAR wSerNum = NULL;
  2139. PCHAR sigString;
  2140. DWORD dwError = ERROR_SUCCESS;
  2141. DWORD bytesReturned;
  2142. DWORD descriptorSize;
  2143. size_t count;
  2144. BOOL success;
  2145. STORAGE_PROPERTY_QUERY propQuery;
  2146. if ( !SerialNumber ) {
  2147. dwError = ERROR_INVALID_PARAMETER;
  2148. goto FnExit;
  2149. }
  2150. *SerialNumber = NULL;
  2151. descriptorSize = sizeof( STORAGE_DEVICE_DESCRIPTOR) + 4096;
  2152. descriptor = LocalAlloc( LPTR, descriptorSize );
  2153. if ( !descriptor ) {
  2154. dwError = GetLastError();
  2155. goto FnExit;
  2156. }
  2157. ZeroMemory( &propQuery, sizeof( propQuery ) );
  2158. propQuery.PropertyId = StorageDeviceProperty;
  2159. propQuery.QueryType = PropertyStandardQuery;
  2160. success = DeviceIoControl( DevHandle,
  2161. IOCTL_STORAGE_QUERY_PROPERTY,
  2162. &propQuery,
  2163. sizeof(propQuery),
  2164. descriptor,
  2165. descriptorSize,
  2166. &bytesReturned,
  2167. NULL );
  2168. if ( !success ) {
  2169. dwError = GetLastError();
  2170. goto FnExit;
  2171. }
  2172. if ( !bytesReturned ) {
  2173. dwError = ERROR_INVALID_DATA;
  2174. goto FnExit;
  2175. }
  2176. //
  2177. // Make sure the offset is reasonable.
  2178. // IA64 sometimes returns -1 for SerialNumberOffset.
  2179. //
  2180. if ( 0 == descriptor->SerialNumberOffset ||
  2181. descriptor->SerialNumberOffset > descriptor->Size ) {
  2182. dwError = ERROR_INVALID_DATA;
  2183. goto FnExit;
  2184. }
  2185. //
  2186. // Serial number string is a zero terminated ASCII string.
  2187. //
  2188. // The header ntddstor.h says the for devices with no serial number,
  2189. // the offset will be zero. This doesn't seem to be true.
  2190. //
  2191. // For devices with no serial number, it looks like a string with a single
  2192. // null character '\0' is returned.
  2193. //
  2194. sigString = (PCHAR)descriptor + (DWORD)descriptor->SerialNumberOffset;
  2195. if ( strlen(sigString) == 0) {
  2196. dwError = ERROR_INVALID_DATA;
  2197. goto FnExit;
  2198. }
  2199. //
  2200. // Convert string to WCHAR.
  2201. //
  2202. // Figure out how big the WCHAR buffer should be. Allocate the WCHAR
  2203. // buffer and copy the string into it.
  2204. wSerNum = LocalAlloc( LPTR, ( strlen(sigString) + 1 ) * sizeof(WCHAR) );
  2205. if ( !wSerNum ) {
  2206. dwError = GetLastError();
  2207. goto FnExit;
  2208. }
  2209. count = mbstowcs( wSerNum, sigString, strlen(sigString) );
  2210. if ( count != strlen(sigString) ) {
  2211. dwError = ERROR_INVALID_DATA;
  2212. LocalFree( wSerNum );
  2213. wSerNum = NULL;
  2214. goto FnExit;
  2215. }
  2216. *SerialNumber = wSerNum;
  2217. dwError = NO_ERROR;
  2218. FnExit:
  2219. if ( descriptor ) {
  2220. LocalFree( descriptor );
  2221. }
  2222. return dwError;
  2223. } // RetrieveSerialNumber
  2224. DWORD
  2225. UpdateCachedDriveLayout(
  2226. IN HANDLE DiskHandle
  2227. )
  2228. /*++
  2229. Routine Description:
  2230. Tell storage drivers to flush their cached drive layout information.
  2231. This routine must only be called for the physical disk or a deadlock may
  2232. occur in partmgr/ftdisk.
  2233. Arguments:
  2234. DevHandle - open handle to a physical disk. Do not close
  2235. the handle on exit.
  2236. Return Value:
  2237. Win32 error value
  2238. --*/
  2239. {
  2240. DWORD dwBytes;
  2241. DWORD dwError;
  2242. if ( !DeviceIoControl( DiskHandle,
  2243. IOCTL_DISK_UPDATE_PROPERTIES,
  2244. NULL,
  2245. 0,
  2246. NULL,
  2247. 0,
  2248. &dwBytes,
  2249. NULL ) ) {
  2250. dwError = GetLastError();
  2251. } else {
  2252. dwError = NO_ERROR;
  2253. }
  2254. return dwError;
  2255. } // UpdateCachedDriveLayout