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.

3839 lines
102 KiB

  1. /*++
  2. Copyright (c) 1995-2001 Microsoft Corporation
  3. Module Name:
  4. disks.c
  5. Abstract:
  6. Resource DLL for disks.
  7. Author:
  8. John Vert (jvert) 5-Dec-1995
  9. Rod Gamache (rodga) 18-Dec-1995
  10. Revision History:
  11. --*/
  12. #include "disksp.h"
  13. #include "lm.h"
  14. #include "arbitrat.h"
  15. #include "newdisks.h"
  16. #include "newmount.h"
  17. #define UNICODE 1
  18. #define LOG_CURRENT_MODULE LOG_MODULE_DISK
  19. #define DISKS_REG_CLUSTER_QUORUM "Cluster\\Quorum"
  20. #define DISKS_REG_QUORUM_PATH CLUSREG_NAME_QUORUM_PATH
  21. #define MAX_HANDLES 10
  22. extern PWCHAR GLOBALROOT_HARDDISK_PARTITION_FMT; // L"\\\\\?\\GLOBALROOT\\Device\\Harddisk%u\\Partition%u\\";
  23. PDISK_RESOURCE DiskTable[MAX_DISKS] = {0};
  24. LONG DiskCount = 0;
  25. CRITICAL_SECTION DisksLock;
  26. LIST_ENTRY DisksListHead;
  27. HANDLE DisksRegistryThread = NULL;
  28. HANDLE DisksTerminateEvent = NULL;
  29. HANDLE DiskspClusDiskZero = NULL;
  30. DWORD SystemDiskAddressFound = 0;
  31. PSCSI_ADDRESS_ENTRY SysDiskAddrList = NULL;
  32. extern CLRES_FUNCTION_TABLE DisksFunctionTable;
  33. extern RTL_RESOURCE PnpVolumeLock;
  34. extern RTL_RESOURCE PnpWaitingListLock;
  35. //
  36. // Disk resource property names
  37. //
  38. #define DISKS_SIGNATURE CLUSREG_NAME_PHYSDISK_SIGNATURE
  39. #define DISKS_DRIVE CLUSREG_NAME_PHYSDISK_DRIVE // pseudonym for signature
  40. #define DISKS_SKIPCHKDSK CLUSREG_NAME_PHYSDISK_SKIPCHKDSK
  41. #define DISKS_CONDITIONAL_MOUNT CLUSREG_NAME_PHYSDISK_CONDITIONAL_MOUNT
  42. #define DISKS_MPVOLGUIDS CLUSREG_NAME_PHYSDISK_MPVOLGUIDS
  43. #define DISKS_VOLGUID CLUSREG_NAME_PHYSDISK_VOLGUID // Not saved in cluster DB
  44. #define DISKS_SERIALNUMBER CLUSREG_NAME_PHYSDISK_SERIALNUMBER
  45. //
  46. // Disk resource private read-write properties.
  47. // Allow for a pseudonym for Signature (Drive), but don't allow both
  48. // drive and signature to be passed.
  49. //
  50. RESUTIL_PROPERTY_ITEM
  51. DiskResourcePrivateProperties[] = {
  52. { DISKS_SIGNATURE, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 0xFFFFFFFF, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(DISK_PARAMS,Signature) },
  53. { DISKS_SKIPCHKDSK, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS,SkipChkdsk) },
  54. { DISKS_CONDITIONAL_MOUNT, NULL, CLUSPROP_FORMAT_DWORD, 1, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS,ConditionalMount) },
  55. #if USEMOUNTPOINTS_KEY
  56. { DISKS_USEMOUNTPOINTS, NULL, CLUSPROP_FORMAT_DWORD, 1, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS, UseMountPoints) },
  57. #endif
  58. { DISKS_MPVOLGUIDS, NULL, CLUSPROP_FORMAT_MULTI_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS, MPVolGuids) },
  59. { DISKS_SERIALNUMBER, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS,SerialNumber) },
  60. { 0 }
  61. };
  62. RESUTIL_PROPERTY_ITEM
  63. DiskResourcePrivatePropertiesAlt[] = {
  64. { DISKS_SIGNATURE, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 0xFFFFFFFF, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(DISK_PARAMS,Signature) },
  65. { DISKS_SKIPCHKDSK, NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS,SkipChkdsk) },
  66. { DISKS_DRIVE, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(DISK_PARAMS,Drive) },
  67. { DISKS_CONDITIONAL_MOUNT, NULL, CLUSPROP_FORMAT_DWORD, 1, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS,ConditionalMount) },
  68. #if USEMOUNTPOINTS_KEY
  69. { DISKS_USEMOUNTPOINTS, NULL, CLUSPROP_FORMAT_DWORD, 1, 0, 1, 0, FIELD_OFFSET(DISK_PARAMS, UseMountPoints) },
  70. #endif
  71. { DISKS_MPVOLGUIDS, NULL, CLUSPROP_FORMAT_MULTI_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS, MPVolGuids) },
  72. { DISKS_VOLGUID, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS, VolGuid) },
  73. { DISKS_SERIALNUMBER, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(DISK_PARAMS,SerialNumber) },
  74. { 0 }
  75. };
  76. #define CLUSTERLOG_ENV_VARIABLE L"ClusterLog"
  77. //
  78. // Local functions.
  79. //
  80. DWORD
  81. DisksValidatePrivateResProperties(
  82. IN OUT PDISK_RESOURCE ResourceEntry,
  83. IN PVOID InBuffer,
  84. IN DWORD InBufferSize,
  85. OUT PDISK_PARAMS Params
  86. );
  87. DWORD
  88. DisksSetPrivateResProperties(
  89. IN OUT PDISK_RESOURCE ResourceEntry,
  90. IN PVOID InBuffer,
  91. IN DWORD InBufferSize
  92. );
  93. DWORD
  94. DisksGetPrivateResProperties(
  95. IN OUT PDISK_RESOURCE ResourceEntry,
  96. OUT PVOID OutBuffer,
  97. IN DWORD OutBufferSize,
  98. OUT LPDWORD BytesReturned
  99. );
  100. DWORD
  101. ProcessDllExtension(
  102. IN PDISK_RESOURCE ResourceEntry,
  103. IN PVOID InBuffer,
  104. IN DWORD InBufferSize,
  105. OUT PVOID OutBuffer,
  106. IN DWORD OutBufferSize,
  107. OUT LPDWORD BytesReturned
  108. );
  109. DWORD
  110. DisksOpenChkdskLogFile(
  111. IN PDISK_RESOURCE ResourceEntry,
  112. IN OUT PHANDLE ChkdskLogFile,
  113. IN OUT LPWSTR *ChkdskLogFileName
  114. );
  115. //
  116. // Error callbacks from disk library
  117. //
  118. VOID
  119. DiskErrorFatal(
  120. INT MessageId,
  121. DWORD Error,
  122. LPSTR File,
  123. DWORD Line
  124. )
  125. {
  126. DWORD Data[3];
  127. Data[0] = MessageId;
  128. Data[1] = Error;
  129. Data[2] = Line;
  130. ClusResLogSystemEventData(LOG_CRITICAL,
  131. RES_FTSET_DISK_ERROR,
  132. sizeof(Data),
  133. Data);
  134. }
  135. VOID
  136. DiskErrorLogInfo(
  137. LPSTR Format,
  138. ...
  139. )
  140. {
  141. }
  142. BOOLEAN
  143. WINAPI
  144. DisksDllEntryPoint(
  145. IN HINSTANCE DllHandle,
  146. IN DWORD Reason,
  147. IN LPVOID Reserved
  148. )
  149. {
  150. DWORD status;
  151. switch ( Reason ) {
  152. case DLL_PROCESS_ATTACH:
  153. InitializeCriticalSection( &DisksLock );
  154. InitializeListHead( &DisksListHead );
  155. RtlInitializeResource( &PnpVolumeLock );
  156. RtlInitializeResource( &PnpWaitingListLock );
  157. ArbitrationInitialize();
  158. GetSystemBusInfo();
  159. break;
  160. case DLL_PROCESS_DETACH:
  161. //
  162. // only do clean up if we're not exiting the process.
  163. // ClRtlDestroyWorkQueue waits on an event to be set and it is
  164. // possible at this point that there are no threads to do that.
  165. // This causes resmon to linger and generally be a pest.
  166. //
  167. if (DiskspClusDiskZero) {
  168. DevfileClose(DiskspClusDiskZero);
  169. }
  170. CleanupSystemBusInfo();
  171. ArbitrationCleanup();
  172. DeleteCriticalSection( &DisksLock );
  173. RtlDeleteResource( &PnpVolumeLock );
  174. RtlDeleteResource( &PnpWaitingListLock );
  175. break;
  176. default:
  177. break;
  178. }
  179. return(TRUE);
  180. } // DisksDllEntryPoint
  181. RESID
  182. DiskspGetNextDisk(
  183. VOID
  184. )
  185. /*++
  186. Routine Description:
  187. This routine will find the next disk slot entry.
  188. Arguments:
  189. None.
  190. Return Value:
  191. A valid resource ID if one is found.
  192. -1 if no free entry is found.
  193. --*/
  194. {
  195. DWORD index;
  196. for ( index = 1; index < MAX_DISKS; index++ ) {
  197. if ( DiskTable[index] == NULL ) {
  198. return((RESID)( UlongToPtr(index) ) );
  199. }
  200. }
  201. return((RESID)-1);
  202. } // DiskspGetNextDisk
  203. VOID
  204. WINAPI
  205. DisksTerminate(
  206. IN RESID Resource
  207. )
  208. {
  209. ULONG uResId=PtrToUlong(Resource);
  210. PDISK_RESOURCE resourceEntry = DiskTable[uResId];
  211. if ( resourceEntry == NULL ) {
  212. DISKS_PRINT("Terminate, bad resource id, %u\n", uResId);
  213. return;
  214. }
  215. // Wait for offline thread to complete, if there is one //
  216. ClusWorkerTerminate(&(resourceEntry->OfflineThread));
  217. DisksOfflineOrTerminate(resourceEntry, TERMINATE);
  218. }
  219. DWORD
  220. WINAPI
  221. DisksArbitrate(
  222. IN RESID Resource,
  223. IN PQUORUM_RESOURCE_LOST LostQuorumResource
  224. )
  225. /*++
  226. Routine Description:
  227. Arbitrate for a device by performing a reservation on the device.
  228. Arguments:
  229. Resource - supplies resource id to be brought online.
  230. LostQuorumResource - routine to call when quorum resource is lost.
  231. Return Value:
  232. ERROR_SUCCESS if successful.
  233. A Win32 error code if other failure.
  234. --*/
  235. {
  236. PDISK_RESOURCE resourceEntry;
  237. DWORD status;
  238. ULONG uResId = PtrToUlong(Resource);
  239. //
  240. // Make sure the RESID is okay.
  241. //
  242. resourceEntry = DiskTable[uResId];
  243. if ( resourceEntry == NULL ) {
  244. DISKS_PRINT("Arbitrate, bad resource id, %u\n", uResId);
  245. return(ERROR_RESOURCE_NOT_FOUND);
  246. }
  247. // [HACKHACK]
  248. // [GorN] 10/28/1999. If Offline thread detects that
  249. // it is being terminated, it will not set the resource status to
  250. // offline. ArbitrateCount != 0 will give it a hint on whether
  251. // to set the resource status or not
  252. InterlockedIncrement(&resourceEntry->ArbitrationInfo.ArbitrateCount);
  253. (DiskpLogEvent)(
  254. resourceEntry->ResourceHandle,
  255. LOG_INFORMATION,
  256. L"[DiskArb]Wait for offline thread to complete...\n"
  257. );
  258. ClusWorkerTerminate(&(resourceEntry->OfflineThread));
  259. //
  260. // Perform DoAttach only. Do not open.
  261. //
  262. status = DisksOpenResourceFileHandle(resourceEntry, L"Arbitrate",0);
  263. if (status != ERROR_SUCCESS) {
  264. goto error_exit;
  265. }
  266. status = DiskArbitration( resourceEntry, DiskspClusDiskZero );
  267. (DiskpLogEvent)(
  268. resourceEntry->ResourceHandle,
  269. LOG_INFORMATION,
  270. L"[DiskArb]Arbitrate returned status %1!u!.\n",
  271. status );
  272. if (status == ERROR_SUCCESS) {
  273. resourceEntry->LostQuorum = LostQuorumResource;
  274. }
  275. error_exit:
  276. InterlockedDecrement(&resourceEntry->ArbitrationInfo.ArbitrateCount);
  277. return status;
  278. } // DisksArbitrate //
  279. RESID
  280. WINAPI
  281. DisksOpen(
  282. IN LPCWSTR ResourceName,
  283. IN HKEY ResourceKey,
  284. IN RESOURCE_HANDLE ResourceHandle
  285. )
  286. /*++
  287. Routine Description:
  288. Open routine for Disk resource.
  289. Arguments:
  290. ResourceName - supplies the resource name
  291. ResourceKey - supplies handle to this resource's cluster
  292. registry key
  293. ResourceHandle - the resource handle to be supplied with SetResourceStatus
  294. is called.
  295. Return Value:
  296. RESID of created resource
  297. Zero on failure
  298. --*/
  299. {
  300. DWORD diskResource = 0;
  301. UCHAR deviceName[MAX_PATH];
  302. DWORD status;
  303. HKEY clusDiskParametersKey = NULL;
  304. HKEY resourceParametersKey = NULL;
  305. HKEY resKey = NULL;
  306. PDISK_RESOURCE resourceEntry;
  307. DWORD valueType;
  308. DWORD valueLength;
  309. LPWSTR nameOfPropInError;
  310. DWORD previousDiskCount;
  311. //
  312. // Open registry parameters key for ClusDisk.
  313. //
  314. status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  315. CLUSDISK_REGISTRY_SIGNATURES,
  316. 0,
  317. KEY_READ,
  318. &clusDiskParametersKey );
  319. if ( status != ERROR_SUCCESS ) {
  320. (DiskpLogEvent)(
  321. ResourceHandle,
  322. LOG_ERROR,
  323. L"Unable to open ClusDisk parameters key. Error: %1!u!.\n",
  324. status );
  325. return(0);
  326. }
  327. //
  328. // Open the resource's parameters key.
  329. //
  330. status = ClusterRegOpenKey( ResourceKey,
  331. CLUSREG_KEYNAME_PARAMETERS,
  332. KEY_READ | KEY_WRITE,
  333. &resourceParametersKey );
  334. if ( status != ERROR_SUCCESS ) {
  335. (DiskpLogEvent)(
  336. ResourceHandle,
  337. LOG_ERROR,
  338. L"Unable to open resource parameters key. Error: %1!u!.\n",
  339. status );
  340. goto error_exit;
  341. }
  342. //
  343. // Get a handle to our resource key so that we can get our name later
  344. // if we need to log an event.
  345. //
  346. status = ClusterRegOpenKey( ResourceKey,
  347. L"",
  348. KEY_READ,
  349. &resKey);
  350. if (status != ERROR_SUCCESS) {
  351. (DiskpLogEvent)(ResourceHandle,
  352. LOG_ERROR,
  353. L"Unable to open resource key. Error: %1!u!.\n",
  354. status );
  355. goto error_exit;
  356. }
  357. //
  358. // Allocate and zero disk info structure.
  359. //
  360. resourceEntry = LocalAlloc(LMEM_FIXED, sizeof(DISK_RESOURCE));
  361. if (!resourceEntry) {
  362. status = GetLastError();
  363. (DiskpLogEvent)(ResourceHandle,
  364. LOG_ERROR,
  365. L"Unable to allocate disk resource data. Error: %1!u!.\n",
  366. status );
  367. goto error_exit;
  368. }
  369. ZeroMemory( resourceEntry, sizeof(DISK_RESOURCE));
  370. resourceEntry->ResourceParametersKey = resourceParametersKey;
  371. resourceEntry->ClusDiskParametersKey = clusDiskParametersKey;
  372. resourceEntry->ResourceKey = resKey;
  373. resourceEntry->ResourceHandle = ResourceHandle;
  374. //resourceEntry->Inserted = FALSE;
  375. //resourceEntry->Attached = FALSE;
  376. //resourceEntry->DiskInfo.Params.Signature = 0;
  377. status = ArbitrationInfoInit(resourceEntry);
  378. if ( status != ERROR_SUCCESS ) {
  379. LocalFree( resourceEntry );
  380. goto error_exit;
  381. }
  382. status = CreateArbWorkQueue(ResourceHandle);
  383. if ( status != ERROR_SUCCESS ) {
  384. LocalFree( resourceEntry );
  385. goto error_exit;
  386. }
  387. #if 0
  388. //
  389. // GN: It seems that there is no point doing this here
  390. // If we are on the join path, we cannot get
  391. // any information about the disk and the call will fail
  392. //
  393. // If we are forming the cluster we will update the information
  394. // when we bring the disk online
  395. //
  396. status = DiskspSsyncDiskInfo( L"Open", resourceEntry , 0 );
  397. if ( status != ERROR_SUCCESS ) {
  398. (DiskpLogEvent)(ResourceHandle,
  399. LOG_ERROR,
  400. L"Unable to ssync DiskInfo. Error: %1!u!.\n",
  401. status );
  402. }
  403. #endif
  404. //
  405. // Save disk info structure.
  406. //
  407. EnterCriticalSection( &DisksLock );
  408. if (DiskspClusDiskZero == NULL) {
  409. status = DevfileOpen(&DiskspClusDiskZero, L"\\Device\\ClusDisk0");
  410. if (!NT_SUCCESS(status) ) {
  411. MountieCleanup ( &resourceEntry -> MountieInfo );
  412. ArbitrationInfoCleanup( resourceEntry );
  413. LocalFree( resourceEntry );
  414. LeaveCriticalSection( &DisksLock );
  415. (DiskpLogEvent)(
  416. ResourceHandle,
  417. LOG_ERROR,
  418. L"Cannot open a handle to clusdisk driver, %1!x!.\n",
  419. status);
  420. goto error_exit;
  421. }
  422. }
  423. diskResource = PtrToUlong(DiskspGetNextDisk());
  424. if ( diskResource >= MAX_DISKS ) {
  425. MountieCleanup ( &resourceEntry -> MountieInfo );
  426. ArbitrationInfoCleanup( resourceEntry );
  427. LocalFree( resourceEntry );
  428. LeaveCriticalSection( &DisksLock );
  429. (DiskpLogEvent)(
  430. ResourceHandle,
  431. LOG_ERROR,
  432. L"Couldn't find an available resource slot %1!u!.\n",
  433. diskResource);
  434. status = ERROR_ALLOTTED_SPACE_EXCEEDED;
  435. goto error_exit;
  436. }
  437. DiskTable[diskResource] = resourceEntry;
  438. previousDiskCount = InterlockedExchangeAdd(&DiskCount, 1);
  439. LeaveCriticalSection( &DisksLock );
  440. if (previousDiskCount == 0) {
  441. StartNotificationWatcherThread();
  442. }
  443. DisksMountPointInitialize( resourceEntry );
  444. //
  445. // Read our disk signature from the resource parameters.
  446. //
  447. status = ResUtilGetPropertiesToParameterBlock( resourceEntry->ResourceParametersKey,
  448. DiskResourcePrivateProperties,
  449. (LPBYTE) &resourceEntry->DiskInfo.Params,
  450. FALSE, //CheckForRequiredProperties
  451. &nameOfPropInError );
  452. if ( status != ERROR_SUCCESS ) {
  453. (DiskpLogEvent)(
  454. resourceEntry->ResourceHandle,
  455. LOG_ERROR,
  456. L"Open: Unable to read the '%1' property. Error: %2!u!.\n",
  457. (nameOfPropInError == NULL ? L"" : nameOfPropInError),
  458. status );
  459. }
  460. return((RESID)( UlongToPtr(diskResource) ) );
  461. error_exit:
  462. if ( clusDiskParametersKey != NULL ) {
  463. RegCloseKey( clusDiskParametersKey );
  464. }
  465. if ( resourceParametersKey != NULL ) {
  466. ClusterRegCloseKey( resourceParametersKey );
  467. }
  468. if ( resKey != NULL ) {
  469. ClusterRegCloseKey( resKey );
  470. }
  471. SetLastError( status );
  472. return((RESID)0);
  473. } // DisksOpen
  474. DWORD
  475. WINAPI
  476. DisksRelease(
  477. IN RESID Resource
  478. )
  479. /*++
  480. Routine Description:
  481. Release arbitration for a device by stopping the reservation thread.
  482. Arguments:
  483. Resource - supplies resource id to be brought online
  484. Return Value:
  485. ERROR_SUCCESS if successful.
  486. ERROR_HOST_NODE_NOT_OWNER if the resource is not owned.
  487. A Win32 error code if other failure.
  488. --*/
  489. {
  490. DWORD status;
  491. PDISK_RESOURCE resourceEntry;
  492. ULONG uResId=PtrToUlong(Resource);
  493. //
  494. // Make sure the RESID is okay.
  495. //
  496. resourceEntry = DiskTable[uResId];
  497. if ( resourceEntry == NULL ) {
  498. DISKS_PRINT("Release, bad resource id, %u\n", uResId);
  499. return(ERROR_RESOURCE_NOT_FOUND);
  500. }
  501. (DiskpLogEvent)(
  502. resourceEntry->ResourceHandle,
  503. LOG_INFORMATION,
  504. L"DisksRelease started, Inserted = %1!u! \n",
  505. resourceEntry->Inserted );
  506. if (resourceEntry->Inserted) { // [GN] #209018 //
  507. (DiskpLogEvent)(
  508. resourceEntry->ResourceHandle,
  509. LOG_ERROR,
  510. L"Cannot release, Disk is online.\n");
  511. } else {
  512. StopPersistentReservations(resourceEntry);
  513. }
  514. resourceEntry->LostQuorum = NULL;
  515. return(ERROR_SUCCESS);
  516. } // DisksRelease
  517. DWORD
  518. WINAPI
  519. DisksOnline(
  520. IN RESID Resource,
  521. IN OUT PHANDLE EventHandle
  522. )
  523. /*++
  524. Routine Description:
  525. Online routine for Disk resource.
  526. Arguments:
  527. Resource - supplies resource id to be brought online
  528. EventHandle - supplies a pointer to a handle to signal on error.
  529. Return Value:
  530. ERROR_SUCCESS if successful.
  531. ERROR_RESOURCE_NOT_FOUND if RESID is not valid.
  532. ERROR_RESOURCE_NOT_AVAILABLE if resource was arbitrated but failed to
  533. acquire 'ownership'.
  534. Win32 error code if other failure.
  535. --*/
  536. {
  537. PDISK_RESOURCE resourceEntry;
  538. DWORD Status;
  539. ULONG uResId=PtrToUlong(Resource);
  540. //
  541. // Make sure the RESID is okay.
  542. //
  543. resourceEntry = DiskTable[uResId];
  544. if ( resourceEntry == NULL ) {
  545. DISKS_PRINT("Online, bad resource id, %u\n", uResId);
  546. return(ERROR_RESOURCE_NOT_FOUND);
  547. }
  548. //
  549. // Shutdown the online thread if it's running.
  550. //
  551. ClusWorkerTerminate(&resourceEntry->OnlineThread);
  552. Status = ClusWorkerCreate(&resourceEntry->OnlineThread,
  553. DisksOnlineThread,
  554. resourceEntry);
  555. if (Status == ERROR_SUCCESS) {
  556. Status = ERROR_IO_PENDING;
  557. }
  558. return(Status);
  559. } // DisksOnline
  560. DWORD
  561. DisksOfflineThread(
  562. IN PCLUS_WORKER Worker,
  563. IN PDISK_RESOURCE ResourceEntry
  564. )
  565. {
  566. RESOURCE_STATUS resourceStatus;
  567. DWORD status;
  568. ResUtilInitializeResourceStatus( &resourceStatus );
  569. resourceStatus.ResourceState = ClusterResourceFailed;
  570. //resourceStatus.WaitHint = 0;
  571. resourceStatus.CheckPoint = 1;
  572. ClusWorkerTerminate( &ResourceEntry->OnlineThread );
  573. status = DisksOfflineOrTerminate(ResourceEntry, OFFLINE);
  574. if (status == ERROR_SUCCESS) {
  575. resourceStatus.ResourceState = ClusterResourceOffline;
  576. }
  577. //
  578. // [HACKHACK] [GorN 10/04/1999]
  579. // If Terminate is called when the offline is in progress,
  580. // the terminate blocks waiting for OfflineThread to complete.
  581. // However, offline thread is stuck trying
  582. // to set ResourceStatus, since event list lock in the resmon
  583. // is taken out by Terminate thread.
  584. //
  585. // The following code doesn't fix this deadlock completely.
  586. // It just reduces the window during which the problem can occur.
  587. // [Resmon times out SetResourceStatus in 3 minutes, this breaks the deadlock]
  588. //
  589. // [HACKHACK] [GorN 10/28/1999]
  590. // Arbitrate is also trying to terminate the offline thread
  591. // We need some way to distinguish between these two cases
  592. //
  593. // The order of setting is
  594. // ArbitrateCount
  595. // ClusWorkerTerminate
  596. //
  597. // Order of checking is ClusWorkerTerminate then ArbitrateCount.
  598. // (Won't work with aggressive memory access reordering, but who cares <grin>)
  599. //
  600. if ( !ClusWorkerCheckTerminate( Worker ) ||
  601. ResourceEntry->ArbitrationInfo.ArbitrateCount)
  602. {
  603. (DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
  604. &resourceStatus );
  605. }
  606. return status;
  607. }
  608. DWORD
  609. WINAPI DisksOffline(
  610. IN RESID ResourceId
  611. )
  612. {
  613. PDISK_RESOURCE ResourceEntry;
  614. DWORD status = ERROR_SUCCESS;
  615. ULONG uResId=PtrToUlong(ResourceId);
  616. //
  617. // Make sure the RESID is okay.
  618. //
  619. ResourceEntry = DiskTable[uResId];
  620. if ( ResourceEntry == NULL ) {
  621. DISKS_PRINT("Offline, bad resource id, %u\n", uResId);
  622. return(ERROR_RESOURCE_NOT_FOUND);
  623. }
  624. status = ClusWorkerCreate( &ResourceEntry->OfflineThread,
  625. (PWORKER_START_ROUTINE)DisksOfflineThread,
  626. ResourceEntry );
  627. if ( status != ERROR_SUCCESS )
  628. {
  629. (DiskpLogEvent)(
  630. ResourceEntry->ResourceHandle,
  631. LOG_ERROR,
  632. L"Offline: Unable to start thread, status %1!u!.\n",
  633. status
  634. );
  635. }
  636. else
  637. {
  638. status = ERROR_IO_PENDING;
  639. }
  640. return status;
  641. } // DisksOffline
  642. BOOL
  643. WINAPI
  644. DisksIsAlive(
  645. IN RESID Resource
  646. )
  647. /*++
  648. Routine Description:
  649. IsAlive routine for Disk resource.
  650. Arguments:
  651. Resource - supplies the resource id to be polled.
  652. Return Value:
  653. TRUE - Resource is alive and well
  654. FALSE - Resource is toast.
  655. --*/
  656. {
  657. DWORD status;
  658. PDISK_RESOURCE resourceEntry;
  659. ULONG uResId=PtrToUlong(Resource);
  660. //
  661. // Make sure the RESID is okay.
  662. //
  663. resourceEntry = DiskTable[uResId];
  664. if ( resourceEntry == NULL ) {
  665. DISKS_PRINT("IsAlive, bad resource id, %u\n", uResId);
  666. return(FALSE);
  667. }
  668. if ( resourceEntry->DiskInfo.FailStatus != 0 ) {
  669. (DiskpLogEvent)(
  670. resourceEntry->ResourceHandle,
  671. LOG_ERROR,
  672. L"IsAlive, error checking device, error %1!u!.\n",
  673. resourceEntry->DiskInfo.FailStatus );
  674. ClusResLogSystemEventByKey(resourceEntry->ResourceKey,
  675. LOG_CRITICAL,
  676. RES_DISK_FAILED_SCSI_CHECK);
  677. resourceEntry->DiskInfo.FailStatus = 0;
  678. return(FALSE);
  679. }
  680. //
  681. // Check out the interesting partitions.
  682. //
  683. #if 0
  684. (DiskpLogEvent)(
  685. resourceEntry->ResourceHandle,
  686. LOG_INFORMATION,
  687. L"About to call DriveIsAlive!\n" );
  688. #endif
  689. status = DisksDriveIsAlive( resourceEntry,
  690. FALSE);
  691. if (status == ERROR_SUCCESS) {
  692. return(TRUE);
  693. } else {
  694. ClusResLogSystemEventByKeyData(resourceEntry->ResourceKey,
  695. LOG_CRITICAL,
  696. RES_DISK_FILESYSTEM_FAILED,
  697. sizeof(status),
  698. &status);
  699. return(FALSE);
  700. }
  701. } // DisksIsAlive
  702. BOOL
  703. WINAPI
  704. DisksLooksAlive(
  705. IN RESID Resource
  706. )
  707. /*++
  708. Routine Description:
  709. LooksAlive routine for Disk resource.
  710. Arguments:
  711. Resource - supplies the resource id to be polled.
  712. Return Value:
  713. TRUE - Resource looks like it is alive and well
  714. FALSE - Resource looks like it is toast.
  715. --*/
  716. {
  717. BOOL success;
  718. PDISK_RESOURCE resourceEntry;
  719. ULONG uResId=PtrToUlong(Resource);
  720. //
  721. // Make sure the RESID is okay.
  722. //
  723. resourceEntry = DiskTable[uResId];
  724. if ( resourceEntry == NULL ) {
  725. DISKS_PRINT("Online, bad resource id, %u\n", uResId);
  726. return(FALSE);
  727. }
  728. if ( resourceEntry->DiskInfo.FailStatus != 0 ) {
  729. (DiskpLogEvent)(
  730. resourceEntry->ResourceHandle,
  731. LOG_ERROR,
  732. L"LooksAlive, error checking device, error %1!u!.\n",
  733. resourceEntry->DiskInfo.FailStatus );
  734. return FALSE;
  735. }
  736. return(TRUE);
  737. } // DisksLooksAlive
  738. VOID
  739. WINAPI
  740. DisksClose(
  741. IN RESID Resource
  742. )
  743. /*++
  744. Routine Description:
  745. Close routine for Disk resource.
  746. Arguments:
  747. Resource - supplies resource id to be closed.
  748. Return Value:
  749. None.
  750. --*/
  751. {
  752. PDISK_RESOURCE resourceEntry;
  753. ULONG uResId=PtrToUlong(Resource);
  754. //
  755. // Make sure the RESID is okay.
  756. //
  757. resourceEntry = DiskTable[uResId];
  758. if ( resourceEntry == NULL ) {
  759. DISKS_PRINT("Close, bad resource id, %u\n", uResId);
  760. return;
  761. }
  762. //
  763. // Wait for the online thread to finish.
  764. //
  765. DisksTerminate( Resource );
  766. DisksMountPointCleanup( resourceEntry );
  767. if ( resourceEntry->DiskInfo.Params.SerialNumber ) {
  768. LocalFree( resourceEntry->DiskInfo.Params.SerialNumber );
  769. resourceEntry->DiskInfo.Params.SerialNumber = NULL;
  770. }
  771. DiskTable[uResId] = NULL;
  772. if ( InterlockedExchangeAdd(&DiskCount, -1) == 1 ) {
  773. // This is the last disk //
  774. StopNotificationWatcher();
  775. DestroyArbWorkQueue();
  776. }
  777. ClusterRegCloseKey( resourceEntry->ResourceParametersKey);
  778. ClusterRegCloseKey( resourceEntry->ResourceKey);
  779. RegCloseKey(resourceEntry->ClusDiskParametersKey);
  780. ArbitrationInfoCleanup(resourceEntry);
  781. MountieCleanup( &resourceEntry->MountieInfo );
  782. LocalFree(resourceEntry);
  783. return;
  784. } // DisksClose
  785. #define TEST_WRITE_BUFFER_SIZE 512
  786. #define TEST_WRITE_BUFFER_BLOCKS 10
  787. DWORD
  788. DisksWriteTestFile(
  789. IN PWCHAR VolumeName,
  790. IN PDISK_RESOURCE ResourceEntry
  791. )
  792. /*++
  793. Routine Description:
  794. Checks for disk corruption problem by opening a temporary file, writing
  795. a test pattern, and saving the file. Then it reopens the file, reads
  796. the data and verifies the data. If any part of this fails, indicate an
  797. error to the caller so that chkdsk can run.
  798. Arguments:
  799. VolumeName - Supplies the device name of the form:
  800. \\?\Volume{GUID}\ [Note trailing backslash!]
  801. ResourceEntry - Supplies a pointer to the resource structure
  802. Return Value:
  803. ERROR_SUCCESS if successful
  804. Win32 error code otherwise
  805. --*/
  806. {
  807. PTEB currentTeb;
  808. HANDLE hFile = INVALID_HANDLE_VALUE;
  809. PCHAR testBuffer = NULL;
  810. PCHAR testBufferSave;
  811. PCHAR readBuffer = NULL;
  812. WCHAR szFilePrefix[] = L"zClusterOnlineChk.tmp";
  813. WCHAR tempFileName[MAX_PATH * 2];
  814. ULONG hardErrorValue;
  815. BOOL hardErrorsDisabled = FALSE;
  816. DWORD error = ERROR_SUCCESS;
  817. DWORD bytesTransferred;
  818. DWORD i;
  819. DWORD len;
  820. //
  821. // Wrap in try-finally.
  822. //
  823. _try {
  824. //
  825. // Temporarily disable hard pop-ups.
  826. //
  827. currentTeb = NtCurrentTeb();
  828. hardErrorValue = currentTeb->HardErrorsAreDisabled;
  829. currentTeb->HardErrorsAreDisabled = 1;
  830. hardErrorsDisabled = TRUE;
  831. //
  832. // Allocate and prepare the test buffer.
  833. //
  834. testBuffer = LocalAlloc( LMEM_FIXED, TEST_WRITE_BUFFER_SIZE );
  835. if ( !testBuffer ) {
  836. error = GetLastError();
  837. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  838. L"DisksWriteTestFile: Unable to allocate test buffer %1!u! \n",
  839. error );
  840. _leave;
  841. }
  842. testBufferSave = testBuffer;
  843. for ( i = 0; i < TEST_WRITE_BUFFER_SIZE; i++ ) {
  844. testBuffer[i] = (CHAR)i;
  845. }
  846. testBuffer = testBufferSave;
  847. //
  848. // Allocate the read buffer.
  849. //
  850. readBuffer = LocalAlloc( LMEM_FIXED, TEST_WRITE_BUFFER_SIZE );
  851. if ( !readBuffer ) {
  852. error = GetLastError();
  853. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  854. L"DisksWriteTestFile: Unable to allocate read buffer %1!u! \n",
  855. error );
  856. _leave;
  857. }
  858. //
  859. // Create a temporary file name. GetTempFileNameW doesn't like the path
  860. // \\?\Volume{GUID}\ so we build our own file name.
  861. //
  862. len = wcslen( VolumeName );
  863. if ( len > sizeof(tempFileName) ) {
  864. error = ERROR_ALLOTTED_SPACE_EXCEEDED;
  865. _leave;
  866. }
  867. ZeroMemory( tempFileName, sizeof(tempFileName) );
  868. wcsncpy( tempFileName, VolumeName, len );
  869. wcsncat( tempFileName, szFilePrefix, wcslen( szFilePrefix ) );
  870. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION,
  871. L"DisksWriteTestFile: Creating test file (%1!ws!) \n",
  872. tempFileName );
  873. //
  874. // First, try to delete the file to make sure we didn't leave one laying around.
  875. // We will ignore the return status as the next CreateFile will fail when we try
  876. // to open the temporary file again.
  877. //
  878. DeleteFileW( tempFileName );
  879. //
  880. // Open temporary file. Make sure the data is actually written to the disk
  881. // and is not cached. Do NOT specify FILE_ATTRIBUTE_TEMPORARY as the system
  882. // will then try to keep all the file's data in memory instead of on the
  883. // disk.
  884. //
  885. hFile = CreateFileW( tempFileName,
  886. GENERIC_READ | GENERIC_WRITE,
  887. 0,
  888. NULL,
  889. CREATE_NEW,
  890. FILE_FLAG_WRITE_THROUGH,
  891. NULL );
  892. if ( INVALID_HANDLE_VALUE == hFile ) {
  893. error = GetLastError();
  894. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  895. L"DisksWriteTestFile: CreateFile for (%1!ws!) failed %2!u! \n",
  896. tempFileName,
  897. error );
  898. _leave;
  899. }
  900. //
  901. // Write the data pattern for some number of blocks.
  902. //
  903. for ( i = 0; i < TEST_WRITE_BUFFER_BLOCKS; i++ ) {
  904. if ( !WriteFile( hFile,
  905. testBuffer,
  906. TEST_WRITE_BUFFER_SIZE,
  907. &bytesTransferred,
  908. NULL ) ) {
  909. // Write failed...
  910. error = GetLastError();
  911. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  912. L"DisksWriteTestFile: Write test file failed %1!u! \n",
  913. error );
  914. _leave;
  915. }
  916. if ( bytesTransferred != TEST_WRITE_BUFFER_SIZE ) {
  917. error = ERROR_IO_DEVICE;
  918. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  919. L"DisksWriteTestFile: bytes written (%1!u!) != test buffer (%2!u!) \n",
  920. bytesTransferred,
  921. TEST_WRITE_BUFFER_SIZE );
  922. _leave;
  923. }
  924. }
  925. //
  926. // Close the file.
  927. //
  928. CloseHandle( hFile );
  929. hFile = INVALID_HANDLE_VALUE;
  930. //
  931. // Reopen the same file.
  932. //
  933. hFile = CreateFileW( tempFileName,
  934. GENERIC_READ,
  935. 0,
  936. NULL,
  937. OPEN_EXISTING,
  938. FILE_ATTRIBUTE_NORMAL,
  939. NULL );
  940. if ( INVALID_HANDLE_VALUE == hFile ) {
  941. error = GetLastError();
  942. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  943. L"DisksWriteTestFile: CreateFile (second open) for (%1!ws!) failed %2!u! \n",
  944. tempFileName,
  945. error );
  946. _leave;
  947. }
  948. //
  949. // Read the data and compare to original buffer.
  950. //
  951. for ( i = 0; i < TEST_WRITE_BUFFER_BLOCKS; i++ ) {
  952. if ( !ReadFile( hFile,
  953. readBuffer,
  954. TEST_WRITE_BUFFER_SIZE,
  955. &bytesTransferred,
  956. NULL ) ) {
  957. // Read failed...
  958. error = GetLastError();
  959. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  960. L"DisksWriteTestFile: Read test file failed %1!u! \n",
  961. error );
  962. _leave;
  963. }
  964. if ( bytesTransferred != TEST_WRITE_BUFFER_SIZE ) {
  965. error = ERROR_IO_DEVICE;
  966. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  967. L"DisksWriteTestFile: bytes read (%1!u!) != test buffer (%2!u!) \n",
  968. bytesTransferred,
  969. TEST_WRITE_BUFFER_SIZE );
  970. _leave;
  971. }
  972. //
  973. // Compare original buffer to that just read from file.
  974. //
  975. if ( !memcmp( testBuffer, readBuffer, TEST_WRITE_BUFFER_SIZE ) ) {
  976. //
  977. // Buffers identical, check next buffer.
  978. //
  979. continue;
  980. }
  981. // Miscompare...
  982. error = ERROR_IO_DEVICE;
  983. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  984. L"DisksWriteTestFile: byte miscompare in block %1!u! \n",
  985. i );
  986. _leave;
  987. }
  988. }
  989. _finally {
  990. //
  991. // Clean up all resources.
  992. //
  993. if ( testBuffer ) {
  994. LocalFree( testBuffer );
  995. }
  996. if ( readBuffer ) {
  997. LocalFree( readBuffer );
  998. }
  999. if ( hFile != INVALID_HANDLE_VALUE ) {
  1000. //
  1001. // Close must happen before the delete.
  1002. //
  1003. CloseHandle( hFile );
  1004. //
  1005. // Only report an error deleting file if the error code was not set
  1006. // in the code above. We don't want to overwrite a potentially
  1007. // informative error code.
  1008. //
  1009. if ( !DeleteFileW( tempFileName ) && ( ERROR_SUCCESS == error ) ) {
  1010. error = GetLastError();
  1011. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  1012. L"DisksWriteTestFile: Unable to delete file (%1!ws!) failed %2!u! \n",
  1013. tempFileName,
  1014. error );
  1015. }
  1016. }
  1017. //
  1018. // Re-enable hard pop-ups.
  1019. //
  1020. if ( hardErrorsDisabled ) {
  1021. currentTeb = NtCurrentTeb();
  1022. currentTeb->HardErrorsAreDisabled = hardErrorValue;
  1023. }
  1024. }
  1025. if ( ERROR_DISK_FULL == error ) {
  1026. DWORD quorumSignature;
  1027. DWORD logMsg;
  1028. DWORD logLevel;
  1029. //
  1030. // We don't want chkdsk to run because the disk is full, so indicate there
  1031. // was no error and write a message to system event log.
  1032. //
  1033. error = NO_ERROR;
  1034. if ( NO_ERROR == GetQuorumSignature( &quorumSignature ) ) {
  1035. if ( quorumSignature == ResourceEntry->DiskInfo.Params.Signature ) {
  1036. logMsg = RES_DISK_FULL_DISK_QUORUM;
  1037. logLevel = LOG_CRITICAL;
  1038. } else {
  1039. logMsg = RES_DISK_FULL_DISK_NOT_QUORUM;
  1040. logLevel = LOG_UNUSUAL;
  1041. }
  1042. } else {
  1043. logMsg = RES_DISK_FULL_DISK_UNKNOWN;
  1044. logLevel = LOG_CRITICAL;
  1045. }
  1046. ClusResLogSystemEventByKey1(ResourceEntry->ResourceKey,
  1047. logLevel,
  1048. logMsg,
  1049. VolumeName );
  1050. }
  1051. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_INFORMATION,
  1052. L"DisksWriteTestFile: Returns status %1!u! \n",
  1053. error );
  1054. return error;
  1055. } // DisksWriteTestFile
  1056. DWORD
  1057. DisksCheckCorruption(
  1058. IN PWCHAR DeviceName,
  1059. IN PWCHAR VolumeName,
  1060. IN PDISK_RESOURCE ResourceEntry
  1061. )
  1062. /*++
  1063. Routine Description:
  1064. Checks for disk corruption problems.
  1065. Arguments:
  1066. DeviceName - Supplies name of the form:
  1067. \Device\HarddiskX\PartitionY [Note: no trailing backslash]
  1068. VolumeName - Supplies the device name of the form:
  1069. \\?\Volume{GUID}\ [Note trailing backslash!]
  1070. ResourceEntry - Supplies a pointer to the resource structure
  1071. Return Value:
  1072. ERROR_SUCCESS if successful
  1073. Win32 error code otherwise
  1074. --*/
  1075. {
  1076. BOOL dirty;
  1077. DWORD status = ERROR_SUCCESS;
  1078. if ( ResourceEntry->DiskInfo.Params.SkipChkdsk ) {
  1079. status = ERROR_SUCCESS;
  1080. goto FnExit;
  1081. }
  1082. status = DisksIsVolumeDirty( DeviceName, ResourceEntry, &dirty );
  1083. if (status == ERROR_SUCCESS && dirty) {
  1084. status = ERROR_DISK_CORRUPT;
  1085. } else {
  1086. status = DisksWriteTestFile( VolumeName, ResourceEntry );
  1087. if ( status != ERROR_SUCCESS ) {
  1088. status = ERROR_DISK_CORRUPT;
  1089. }
  1090. }
  1091. FnExit:
  1092. return status;
  1093. } // DisksCheckCorruption
  1094. DWORD
  1095. DisksFixCorruption(
  1096. IN PWCHAR VolumeName,
  1097. IN PDISK_RESOURCE ResourceEntry,
  1098. IN DWORD CorruptStatus
  1099. )
  1100. /*++
  1101. Routine Description:
  1102. Fix file or disk corrupt problems.
  1103. Arguments:
  1104. VolumeName - Supplies the device name of the form:
  1105. \\?\Volume{GUID}\ [Note trailing backslash!]
  1106. ResourceEntry - Supplies a pointer to the disk resource entry
  1107. Return Value:
  1108. ERROR_SUCCESS if successful
  1109. Win32 error code otherwise
  1110. Notes:
  1111. We'll need to lock the volume exclusive while we do this...
  1112. So threads that call this routine should ensure there are no
  1113. open files!
  1114. --*/
  1115. {
  1116. LPWSTR chkdskLogFileName = NULL;
  1117. DWORD Status;
  1118. DWORD len;
  1119. STARTUPINFOW StartupInfo;
  1120. PROCESS_INFORMATION ProcessInfo;
  1121. WCHAR CheckDiskInfo[MAX_PATH*2];
  1122. RESOURCE_STATUS resourceStatus;
  1123. RESOURCE_EXIT_STATE exit;
  1124. BOOL replaceBackslash;
  1125. BOOL bInheritHandles;
  1126. HANDLE chkdskLogFile = INVALID_HANDLE_VALUE;
  1127. UINT previousMode;
  1128. previousMode = SetErrorMode( SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX );
  1129. //
  1130. // We need to strip the trailing backslash off so chkdsk will work.
  1131. //
  1132. len = wcslen( VolumeName );
  1133. if ( len > MAX_PATH ) {
  1134. SetErrorMode( previousMode );
  1135. return ERROR_ALLOTTED_SPACE_EXCEEDED;
  1136. }
  1137. if ( VolumeName[len-1] == L'\\') {
  1138. VolumeName[len-1] = UNICODE_NULL;
  1139. replaceBackslash = TRUE;
  1140. } else {
  1141. replaceBackslash = FALSE;
  1142. }
  1143. //
  1144. // Now handle the corruption problem by running CHKDSK.
  1145. //
  1146. _snwprintf(CheckDiskInfo, MAX_PATH * 2, L"ChkDsk /x /f %ws", VolumeName);
  1147. //
  1148. // Restore the backslash.
  1149. //
  1150. if ( replaceBackslash ) {
  1151. VolumeName[len-1] = L'\\';
  1152. }
  1153. ZeroMemory( &StartupInfo, sizeof(STARTUPINFOW) );
  1154. StartupInfo.cb = sizeof(STARTUPINFO);
  1155. StartupInfo.lpDesktop = L"WinSta0\\Default";
  1156. bInheritHandles = FALSE;
  1157. Status = DisksOpenChkdskLogFile( ResourceEntry,
  1158. &chkdskLogFile,
  1159. &chkdskLogFileName );
  1160. if ( NO_ERROR == Status && INVALID_HANDLE_VALUE != chkdskLogFile ) {
  1161. //
  1162. // When the output is redirected, we don't want to show the console window because it
  1163. // will be blank with simply a title in it. The event log message will let the user
  1164. // know to look in the chkdsk file.
  1165. //
  1166. StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  1167. StartupInfo.wShowWindow = SW_HIDE;
  1168. // Someone watching the console won't know what is happening, so show
  1169. // the window anyway...
  1170. // StartupInfo.dwFlags = STARTF_USESTDHANDLES;
  1171. StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  1172. //
  1173. // [169631] Chkdsk now verifies that hStdInput is not NULL.
  1174. // Since the resmon process was started with InheritHandles set to FALSE,
  1175. // GetStdHandle(STD_INPUT_HANDLE) will return NULL. When we run chkdsk with
  1176. // the options "/f /x", chkdsk should not be prompting the user and the
  1177. // input handle wouldn't be used. However, ulibs.dll was changed to always
  1178. // insure a nonzero input handle was supplied. So we have to supply some type
  1179. // of input handle. We could put INVALID_HANDLE_VALUE here, but the checks
  1180. // may change and it will fail later. For now, point input to the temporary
  1181. // output file we created.
  1182. //
  1183. if ( NULL == StartupInfo.hStdInput ) {
  1184. StartupInfo.hStdInput = chkdskLogFile;
  1185. }
  1186. StartupInfo.hStdOutput = chkdskLogFile;
  1187. StartupInfo.hStdError = chkdskLogFile;
  1188. bInheritHandles = TRUE;
  1189. }
  1190. //
  1191. // Log an event
  1192. //
  1193. if ( CorruptStatus == ERROR_DISK_CORRUPT ) {
  1194. // Must be corrupt disk
  1195. ClusResLogSystemEventByKey2(ResourceEntry->ResourceKey,
  1196. LOG_CRITICAL,
  1197. RES_DISK_CORRUPT_DISK,
  1198. VolumeName,
  1199. chkdskLogFileName);
  1200. } else {
  1201. // Must be corrupt file.
  1202. ClusResLogSystemEventByKey2(ResourceEntry->ResourceKey,
  1203. LOG_CRITICAL,
  1204. RES_DISK_CORRUPT_FILE,
  1205. VolumeName,
  1206. chkdskLogFileName);
  1207. }
  1208. if ( chkdskLogFileName ) {
  1209. LocalFree( chkdskLogFileName );
  1210. chkdskLogFileName = NULL;
  1211. }
  1212. if ( !CreateProcessW( NULL,
  1213. CheckDiskInfo,
  1214. NULL,
  1215. NULL,
  1216. bInheritHandles,
  1217. NORMAL_PRIORITY_CLASS,
  1218. NULL,
  1219. NULL,
  1220. &StartupInfo,
  1221. &ProcessInfo ) ) {
  1222. Status = GetLastError();
  1223. (DiskpLogEvent)(ResourceEntry->ResourceHandle, LOG_ERROR,
  1224. L"DisksFixCorruption: CreateProcessW for chkdsk failed %1!u! \n",
  1225. Status );
  1226. if ( INVALID_HANDLE_VALUE != chkdskLogFile ) {
  1227. CloseHandle( chkdskLogFile );
  1228. }
  1229. SetErrorMode( previousMode );
  1230. return(Status);
  1231. }
  1232. CloseHandle( ProcessInfo.hThread );
  1233. //
  1234. // Wait for CHKDSK to finish.
  1235. //
  1236. #if 1
  1237. //
  1238. // Don't wait "forever"... things could get ugly if we dismount the file
  1239. // system while ChkDsk is running! But KeithKa says its okay to kill
  1240. // ChkDsk while it is running - it has to handle powerfails, crashes, etc.
  1241. //
  1242. resourceStatus.ResourceState = ClusterResourceOnlinePending;
  1243. while ( !ResourceEntry->OnlineThread.Terminate ) {
  1244. (DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
  1245. &resourceStatus );
  1246. Status = WaitForSingleObject( ProcessInfo.hProcess, 2000 );
  1247. if ( Status != WAIT_TIMEOUT ) {
  1248. break;
  1249. }
  1250. }
  1251. if ( ResourceEntry->OnlineThread.Terminate ) {
  1252. // If we were asked to terminate, make sure ChkNtfs is killed
  1253. TerminateProcess( ProcessInfo.hProcess, 999 );
  1254. CloseHandle( ProcessInfo.hProcess );
  1255. if ( INVALID_HANDLE_VALUE != chkdskLogFile ) {
  1256. CloseHandle( chkdskLogFile );
  1257. }
  1258. SetErrorMode( previousMode );
  1259. return(ERROR_SHUTDOWN_CLUSTER);
  1260. }
  1261. //
  1262. // Update our checkpoint state.
  1263. //
  1264. ++resourceStatus.CheckPoint;
  1265. exit = (DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
  1266. &resourceStatus );
  1267. if ( exit == ResourceExitStateTerminate ) {
  1268. TerminateProcess( ProcessInfo.hProcess, 998 );
  1269. CloseHandle( ProcessInfo.hProcess );
  1270. if ( INVALID_HANDLE_VALUE != chkdskLogFile ) {
  1271. CloseHandle( chkdskLogFile );
  1272. }
  1273. SetErrorMode( previousMode );
  1274. return(ERROR_SHUTDOWN_CLUSTER);
  1275. }
  1276. #else
  1277. // Wait "forever"...
  1278. Status = WaitForSingleObject( ProcessInfo.hProcess, INFINITE );
  1279. #endif
  1280. if ( (Status == 0) &&
  1281. GetExitCodeProcess( ProcessInfo.hProcess, &Status ) ) {
  1282. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1283. LOG_ERROR,
  1284. L"FixCorruption: CHKDSK returned status of %1!u!.\n",
  1285. Status );
  1286. // [From supera.hxx]
  1287. //
  1288. // These symbols are used by Chkdsk functions to return an appropriate
  1289. // exit status to the chkdsk program.
  1290. // In order of most important first, the error level order are as follows:
  1291. // 3 > 1 > 2 > 0
  1292. // An error level of 3 will overwrite an error level of 1, 2, or 0.
  1293. // #define CHKDSK_EXIT_SUCCESS 0
  1294. // #define CHKDSK_EXIT_ERRS_FIXED 1
  1295. // #define CHKDSK_EXIT_MINOR_ERRS 2 // whether or not "/f"
  1296. // #define CHKDSK_EXIT_CLEANUP_WORK 2 // whether or not "/f"
  1297. // #define CHKDSK_EXIT_COULD_NOT_CHK 3
  1298. // #define CHKDSK_EXIT_ERRS_NOT_FIXED 3
  1299. // #define CHKDSK_EXIT_COULD_NOT_FIX 3
  1300. if (Status >= 3) {
  1301. Status = ERROR_DISK_CORRUPT;
  1302. } else {
  1303. Status = ERROR_SUCCESS;
  1304. }
  1305. }
  1306. CloseHandle( ProcessInfo.hProcess );
  1307. if ( INVALID_HANDLE_VALUE != chkdskLogFile ) {
  1308. CloseHandle( chkdskLogFile );
  1309. }
  1310. SetErrorMode( previousMode );
  1311. return(Status);
  1312. } // DisksFixCorruption
  1313. DWORD
  1314. DiskspGetQuorumPath(
  1315. OUT LPWSTR* lpQuorumLogPath
  1316. )
  1317. /*++
  1318. Routine Description:
  1319. Reads QuorumPath value from the registry.
  1320. Arguments:
  1321. lpQuorumLogPath - receives the poiner to a buffer containing QuorumLogPath
  1322. the buffer needs to be deallocated later via LocalFree
  1323. Return Value:
  1324. ERROR_SUCCESS if successful
  1325. Win32 error code otherwise
  1326. --*/
  1327. {
  1328. DWORD Status;
  1329. LPWSTR QuorumLogPath = NULL;
  1330. DWORD QuorumLogSize = 0;
  1331. DWORD Type;
  1332. HKEY QuorumKey;
  1333. Status = RegOpenKey( HKEY_LOCAL_MACHINE,
  1334. DISKS_REG_CLUSTER_QUORUM,
  1335. &QuorumKey );
  1336. if ( Status == ERROR_SUCCESS ) {
  1337. Status = RegQueryValueExW(QuorumKey,
  1338. DISKS_REG_QUORUM_PATH,
  1339. 0,
  1340. &Type,
  1341. NULL,
  1342. &QuorumLogSize );
  1343. if ( Status != ERROR_SUCCESS ) {
  1344. return( Status );
  1345. }
  1346. if ( (Type != REG_SZ) ||
  1347. (QuorumLogSize > (MAX_PATH - 2)) ) {
  1348. return(ERROR_INVALID_DATA);
  1349. }
  1350. if ( (Status == ERROR_SUCCESS) ||
  1351. (Status == ERROR_MORE_DATA) ) {
  1352. QuorumLogPath = LocalAlloc( LMEM_FIXED,
  1353. (QuorumLogSize + 1) * sizeof(WCHAR) );
  1354. if ( QuorumLogPath == NULL ) {
  1355. RegCloseKey( QuorumKey );
  1356. return(ERROR_NOT_ENOUGH_MEMORY); // Mostly catastrophic
  1357. }
  1358. Status = RegQueryValueExW(QuorumKey,
  1359. DISKS_REG_QUORUM_PATH,
  1360. 0,
  1361. &Type,
  1362. (LPBYTE)QuorumLogPath,
  1363. &QuorumLogSize );
  1364. if (Status == ERROR_SUCCESS) {
  1365. *lpQuorumLogPath = QuorumLogPath;
  1366. } else {
  1367. LocalFree(QuorumLogPath);
  1368. *lpQuorumLogPath = 0;
  1369. }
  1370. }
  1371. RegCloseKey( QuorumKey );
  1372. }
  1373. return Status;
  1374. }
  1375. DWORD
  1376. DiskspSetQuorumPath(
  1377. IN LPWSTR QuorumLogPath
  1378. )
  1379. /*++
  1380. Routine Description:
  1381. Reads QuorumPath value from the registry.
  1382. Arguments:
  1383. lpQuorumLogPath - receives the poiner to a buffer containing QuorumLogPath
  1384. the buffer needs to be deallocated later via LocalFree
  1385. ResourceEntry - receives a drive letter of the quorum
  1386. Return Value:
  1387. ERROR_SUCCESS if successful
  1388. Win32 error code otherwise
  1389. --*/
  1390. {
  1391. DWORD status;
  1392. HKEY QuorumKey;
  1393. HKEY ClusterKey;
  1394. HCLUSTER hCluster;
  1395. hCluster = OpenCluster(NULL);
  1396. if (!hCluster) {
  1397. status = GetLastError();
  1398. return status;
  1399. }
  1400. ClusterKey = GetClusterKey(hCluster, KEY_READ | KEY_WRITE);
  1401. if (!ClusterKey) {
  1402. status = GetLastError();
  1403. CloseCluster(hCluster);
  1404. return status;
  1405. }
  1406. status = ClusterRegOpenKey( ClusterKey,
  1407. CLUSREG_KEYNAME_QUORUM,
  1408. KEY_READ | KEY_WRITE,
  1409. &QuorumKey );
  1410. if (status != ERROR_SUCCESS) {
  1411. ClusterRegCloseKey(ClusterKey);
  1412. CloseCluster(hCluster);
  1413. return status;
  1414. }
  1415. status = ResUtilSetSzValue(
  1416. QuorumKey,
  1417. CLUSREG_NAME_QUORUM_PATH,
  1418. QuorumLogPath,
  1419. 0);
  1420. ClusterRegCloseKey(QuorumKey);
  1421. ClusterRegCloseKey(ClusterKey);
  1422. CloseCluster(hCluster);
  1423. return status;
  1424. }
  1425. DWORD
  1426. DisksDriveIsAlive(
  1427. IN PDISK_RESOURCE ResourceEntry,
  1428. IN BOOL Online
  1429. )
  1430. /*++
  1431. Routine Description:
  1432. Checks out a drive partition to see if the filesystem has mounted
  1433. it and it's working. We will also run CHKDSK if the partition/certain
  1434. files are Corrupt and the Online flag is TRUE.
  1435. Arguments:
  1436. ResourceEntry - Supplies a pointer to the resource entry for this disk
  1437. Online - TRUE if the disk was just brought online.
  1438. Return Value:
  1439. ERROR_SUCCESS if successful
  1440. Win32 error code otherwise
  1441. --*/
  1442. {
  1443. PMOUNTIE_PARTITION entry;
  1444. DWORD Status = ERROR_SUCCESS;
  1445. DWORD nPartitions = MountiePartitionCount( &ResourceEntry->MountieInfo );
  1446. DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive;
  1447. DWORD i;
  1448. LPWSTR QuorumLogPath = NULL;
  1449. BOOL QuorumResource = FALSE;
  1450. WCHAR szDiskPartName[MAX_PATH];
  1451. WCHAR szGlobalDiskPartName[MAX_PATH];
  1452. WCHAR szVolumeName[MAX_PATH];
  1453. WCHAR szQuorumVolumeName[MAX_PATH];
  1454. WCHAR szQuorumDriveLetter[16];
  1455. ZeroMemory( szDiskPartName, sizeof(szDiskPartName) );
  1456. ZeroMemory( szGlobalDiskPartName, sizeof(szGlobalDiskPartName) );
  1457. ZeroMemory( szVolumeName, sizeof(szVolumeName) );
  1458. ZeroMemory( szQuorumVolumeName, sizeof(szQuorumVolumeName) );
  1459. ZeroMemory( szQuorumDriveLetter, sizeof(szQuorumDriveLetter) );
  1460. //
  1461. // Find the quorum path... this is a hack!
  1462. //
  1463. if ( Online ) {
  1464. (DiskpLogEvent)(
  1465. ResourceEntry->ResourceHandle,
  1466. LOG_INFORMATION,
  1467. L"DriveIsAlive called for Online check\n" );
  1468. {
  1469. DWORD QuorumSignature;
  1470. Status = GetQuorumSignature( &QuorumSignature );
  1471. if (Status == ERROR_SUCCESS) {
  1472. QuorumResource =
  1473. (QuorumSignature == ResourceEntry->DiskInfo.Params.Signature);
  1474. } else {
  1475. (DiskpLogEvent)(
  1476. ResourceEntry->ResourceHandle,
  1477. LOG_INFORMATION,
  1478. L"GetQuorumSignature returned %1!u!\n", Status );
  1479. }
  1480. }
  1481. Status = DiskspGetQuorumPath( &QuorumLogPath );
  1482. if (Status != ERROR_SUCCESS) {
  1483. (DiskpLogEvent)(
  1484. ResourceEntry->ResourceHandle,
  1485. LOG_ERROR,
  1486. L"DiskspGetQuorumPath returned %1!u!\n", Status );
  1487. } else {
  1488. //
  1489. // For now, quorum path will have a drive letter. Get the corresponding volume name.
  1490. //
  1491. _snwprintf( szQuorumDriveLetter,
  1492. 16,
  1493. L"%wc:\\",
  1494. QuorumLogPath[0] );
  1495. if ( !GetVolumeNameForVolumeMountPointW( szQuorumDriveLetter,
  1496. szQuorumVolumeName,
  1497. sizeof(szQuorumVolumeName)/sizeof(WCHAR) )) {
  1498. Status = GetLastError();
  1499. (DiskpLogEvent)(
  1500. ResourceEntry->ResourceHandle,
  1501. LOG_ERROR,
  1502. L"DriveIsAlive: GetVolumeNameForVolumeMountPoint (quorum) returned %1!u!\n", Status );
  1503. }
  1504. }
  1505. }
  1506. #if 0
  1507. (DiskpLogEvent)(
  1508. ResourceEntry->ResourceHandle,
  1509. LOG_INFORMATION,
  1510. L"DriveIsAlive is now checking each partition\n" );
  1511. #endif
  1512. //
  1513. // Now check out each interesting partition. Since only "valid" partitions are
  1514. // saved in the MountieInfo structure, we will only look at those (ignoring those
  1515. // partitions that are not NTFS).
  1516. //
  1517. for ( i = 0; i < nPartitions; ++i ) {
  1518. entry = MountiePartition( &ResourceEntry->MountieInfo, i );
  1519. if ( !entry ) {
  1520. (DiskpLogEvent)(
  1521. ResourceEntry->ResourceHandle,
  1522. LOG_ERROR,
  1523. L"DriveIsAlive no partition entry for partition %1!u! \n", i );
  1524. //
  1525. // Something bad happened to our data structures. We have to indicate that the
  1526. // drive is not alive.
  1527. //
  1528. Status = ERROR_INVALID_DATA;
  1529. break;
  1530. }
  1531. //
  1532. // Create device name of form \Device\HarddiskX\PartitionY (no trailing backslash).
  1533. //
  1534. _snwprintf( szDiskPartName,
  1535. MAX_PATH,
  1536. DEVICE_HARDDISK_PARTITION_FMT,
  1537. physicalDrive,
  1538. entry->PartitionNumber );
  1539. //
  1540. // Given the DiskPartName, get the VolGuid name. This name must have a trailing
  1541. // backslash to work correctly.
  1542. //
  1543. _snwprintf( szGlobalDiskPartName,
  1544. MAX_PATH,
  1545. GLOBALROOT_HARDDISK_PARTITION_FMT,
  1546. physicalDrive,
  1547. entry->PartitionNumber );
  1548. if ( !GetVolumeNameForVolumeMountPointW( szGlobalDiskPartName,
  1549. szVolumeName,
  1550. sizeof(szVolumeName)/sizeof(WCHAR) )) {
  1551. Status = GetLastError();
  1552. (DiskpLogEvent)(
  1553. ResourceEntry->ResourceHandle,
  1554. LOG_ERROR,
  1555. L"DriveIsAlive: GetVolumeNameForVolumeMountPoint for %1!ws! returned %2!u!\n",
  1556. szGlobalDiskPartName,
  1557. Status );
  1558. //
  1559. // If disk is not corrupt, exit. If disk is corrupt, fall through so chkdsk runs.
  1560. //
  1561. if ( ERROR_DISK_CORRUPT != Status && ERROR_FILE_CORRUPT != Status ) {
  1562. //
  1563. // Something bad happened. We have to stop checking this disk. Return the
  1564. // error status we received.
  1565. //
  1566. break;
  1567. }
  1568. }
  1569. //
  1570. // Simple algorithm used here is to do a FindFirstFile on X:\* and see
  1571. // if it works. Then we open each file for read access. This is the
  1572. // cluster directory, and all files in it are subject to our opening.
  1573. //
  1574. Status = DiskspCheckPath( szVolumeName,
  1575. ResourceEntry,
  1576. FALSE,
  1577. Online );
  1578. //
  1579. // [HACKHACK] Ignore error 21 during periodic IsAlive/LooksAlive
  1580. //
  1581. if ( !Online && (Status == ERROR_NOT_READY) ) {
  1582. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1583. LOG_WARNING,
  1584. L"DiskpCheckPath for %1!ws!: returned status = %2!u! (ChkDsk running?)\n",
  1585. szVolumeName,
  1586. Status );
  1587. Status = ERROR_SUCCESS;
  1588. }
  1589. // if we haven't chkdsk'd yet, keep looking.
  1590. if ( Status != ERROR_SUCCESS ) {
  1591. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1592. LOG_ERROR,
  1593. L"DiskpCheckPath for %1!ws!: returned status = %2!u!\n",
  1594. szVolumeName,
  1595. Status );
  1596. }
  1597. if ( (Status == ERROR_SUCCESS) && Online &&
  1598. QuorumLogPath && QuorumResource &&
  1599. ( wcslen( szVolumeName ) == wcslen( szQuorumVolumeName ) ) &&
  1600. ( !wcsncmp( szVolumeName, szQuorumVolumeName, wcslen( szQuorumVolumeName ) ))) {
  1601. (DiskpLogEvent)(
  1602. ResourceEntry->ResourceHandle,
  1603. LOG_INFORMATION,
  1604. L"DriveIsAlive checking quorum drive \n" );
  1605. //
  1606. // Everything looks fine... if this is the quorum device, then
  1607. // we should check the quorum log path if given
  1608. //
  1609. Status = DiskspCheckPath( szQuorumVolumeName,
  1610. ResourceEntry,
  1611. TRUE,
  1612. Online );
  1613. }
  1614. if ( (Status == ERROR_SUCCESS) &&
  1615. Online ) {
  1616. //
  1617. // Check if the volume dirty bit is on
  1618. //
  1619. Status = DisksCheckCorruption( szDiskPartName,
  1620. szVolumeName,
  1621. ResourceEntry );
  1622. //
  1623. // If we're requested to shutdown, then do that immediately
  1624. if ( ResourceEntry->OnlineThread.Terminate ) {
  1625. Status = ERROR_SHUTDOWN_CLUSTER;
  1626. break;
  1627. }
  1628. }
  1629. if ( (Status != ERROR_SUCCESS) && Online) {
  1630. if ( ResourceEntry->DiskInfo.Params.ConditionalMount ) {
  1631. Status = DisksFixCorruption( szVolumeName,
  1632. ResourceEntry,
  1633. ERROR_DISK_CORRUPT );
  1634. //
  1635. // Since ConditionalMount is set, if we couldn't fix the corruption
  1636. // on the disk, we don't want to continue checking the other
  1637. // partitions - we want to return an error. So we fall through
  1638. // and check the status. If status wasn't successful, we break out
  1639. // of the loop to return the error.
  1640. //
  1641. } else {
  1642. if ( QuorumLogPath ) {
  1643. LocalFree( QuorumLogPath );
  1644. }
  1645. return( Status );
  1646. }
  1647. }
  1648. if ( Status != ERROR_SUCCESS ) {
  1649. break;
  1650. }
  1651. }
  1652. if ( QuorumLogPath ) {
  1653. LocalFree( QuorumLogPath );
  1654. }
  1655. return(Status);
  1656. } // DisksDriveIsAlive
  1657. DWORD
  1658. DisksMountDrives(
  1659. IN PDISK_INFO DiskInfo,
  1660. IN PDISK_RESOURCE ResourceEntry,
  1661. IN DWORD Signature
  1662. )
  1663. /*++
  1664. Routine Description:
  1665. For each drive letter on the supplied disk, this mounts the filesystem
  1666. and checks it out.
  1667. Arguments:
  1668. DiskInfo - Supplies the disk information
  1669. ResourceEntry - Supplies a pointer to the disk resource
  1670. Signature - the signature for the disk.
  1671. Return Value:
  1672. ERROR_SUCCESS if successful
  1673. Win32 error code otherwise
  1674. --*/
  1675. {
  1676. DWORD Status;
  1677. WCHAR wDeviceName[4];
  1678. WCHAR wShareName[4];
  1679. DWORD letterMask;
  1680. UCHAR index;
  1681. UCHAR driveLetter;
  1682. SHARE_INFO_2 shareInfo;
  1683. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1684. LOG_INFORMATION,
  1685. L"DisksMountDrives: calling IsAlive function.\n" );
  1686. //
  1687. // Call the IsAlive to see if the filesystem checks out ok.
  1688. //
  1689. Status = DisksDriveIsAlive( ResourceEntry,
  1690. TRUE);
  1691. if ( (Status != ERROR_SUCCESS) ||
  1692. (ResourceEntry->OnlineThread.Terminate) ) {
  1693. return(Status);
  1694. }
  1695. //
  1696. // Now create the drive$ share name for each drive letter;
  1697. //
  1698. letterMask = DisksGetLettersForSignature(
  1699. ResourceEntry);
  1700. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1701. LOG_INFORMATION,
  1702. L"DisksMountDrives: letter mask is %1!08x!.\n",
  1703. letterMask);
  1704. index = 0;
  1705. while ( letterMask ) {
  1706. while ( !(letterMask & 1) ) {
  1707. letterMask = letterMask >> 1;
  1708. index++;
  1709. }
  1710. driveLetter = 'A' + index;
  1711. letterMask = letterMask >> 1;
  1712. index++;
  1713. if ( isalpha(driveLetter) ) {
  1714. wsprintfW(wDeviceName, L"%c:\\", driveLetter);
  1715. wsprintfW(wShareName, L"%c$", driveLetter);
  1716. shareInfo.shi2_netname = wShareName;
  1717. shareInfo.shi2_type = STYPE_DISKTREE;
  1718. shareInfo.shi2_remark = NULL;
  1719. shareInfo.shi2_permissions = 0;
  1720. shareInfo.shi2_max_uses = (DWORD)-1;
  1721. shareInfo.shi2_current_uses = 0;
  1722. shareInfo.shi2_path = wDeviceName;
  1723. shareInfo.shi2_passwd = NULL;
  1724. Status = NetShareAdd( NULL, 2, (PBYTE)&shareInfo, NULL );
  1725. if ( Status != ERROR_SUCCESS && Status != NERR_DuplicateShare ) {
  1726. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1727. LOG_ERROR,
  1728. L"DisksMountDrives, error creating default share %1!ws!. Error: %2!u!.\n",
  1729. wShareName,
  1730. Status);
  1731. }
  1732. }
  1733. }
  1734. return(ERROR_SUCCESS);
  1735. } // DisksMountDrives
  1736. DWORD
  1737. DisksDismountDrive(
  1738. IN PDISK_RESOURCE ResourceEntry,
  1739. IN DWORD Signature
  1740. )
  1741. /*++
  1742. Routine Desccription:
  1743. Delete the default device share names for a given disk.
  1744. Arguments:
  1745. ResourceHandle - the resource handle for logging events
  1746. Signature - the disk's signature
  1747. Return Value:
  1748. WIN32 error code.
  1749. --*/
  1750. {
  1751. WCHAR shareName[8];
  1752. DWORD letterMask;
  1753. UCHAR index;
  1754. UCHAR driveLetter;
  1755. letterMask = DisksGetLettersForSignature(
  1756. ResourceEntry);
  1757. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1758. LOG_INFORMATION,
  1759. L"DisksDismountDrives: letter mask is %1!08x!.\n",
  1760. letterMask);
  1761. index = 0;
  1762. while ( letterMask ) {
  1763. while ( !(letterMask & 1) ) {
  1764. letterMask = letterMask >> 1;
  1765. index++;
  1766. }
  1767. driveLetter = 'A' + index;
  1768. letterMask = letterMask >> 1;
  1769. index++;
  1770. if ( isalpha(driveLetter) ) {
  1771. shareName[0] = (WCHAR)driveLetter;
  1772. shareName[1] = (WCHAR)'$';
  1773. shareName[2] = (WCHAR)0;
  1774. NetShareDel( NULL,
  1775. shareName,
  1776. 0 );
  1777. }
  1778. }
  1779. return (ERROR_SUCCESS);
  1780. } // DisksDismountDrive
  1781. LPSTR
  1782. GetRegParameter(
  1783. IN HKEY RegKey,
  1784. IN LPCSTR ValueName
  1785. )
  1786. /*++
  1787. Routine Description:
  1788. Queries a REG_SZ parameter out of the registry and allocates the
  1789. necessary storage for it.
  1790. Arguments:
  1791. RegKey - Supplies the cluster key where the parameter is stored
  1792. ValueName - Supplies the name of the value.
  1793. Return Value:
  1794. A pointer to a buffer containing the parameter if successful.
  1795. NULL if unsuccessful.
  1796. --*/
  1797. {
  1798. LPSTR Value;
  1799. DWORD ValueLength;
  1800. DWORD ValueType;
  1801. DWORD Status;
  1802. ValueLength = 0;
  1803. Status = RegQueryValueEx(RegKey,
  1804. ValueName,
  1805. NULL,
  1806. &ValueType,
  1807. NULL,
  1808. &ValueLength);
  1809. if ( (Status != ERROR_SUCCESS) &&
  1810. (Status != ERROR_MORE_DATA) ) {
  1811. return(NULL);
  1812. }
  1813. if ( ValueType == REG_SZ ) {
  1814. ValueLength++;
  1815. }
  1816. Value = LocalAlloc(LMEM_FIXED, ValueLength);
  1817. if (Value == NULL) {
  1818. return(NULL);
  1819. }
  1820. Status = RegQueryValueEx(RegKey,
  1821. ValueName,
  1822. NULL,
  1823. &ValueType,
  1824. (LPBYTE)Value,
  1825. &ValueLength);
  1826. if (Status != ERROR_SUCCESS) {
  1827. LocalFree(Value);
  1828. Value = NULL;
  1829. }
  1830. return(Value);
  1831. } // GetRegParameter
  1832. DWORD
  1833. DisksResourceControl(
  1834. IN RESID Resource,
  1835. IN DWORD ControlCode,
  1836. IN PVOID InBuffer,
  1837. IN DWORD InBufferSize,
  1838. OUT PVOID OutBuffer,
  1839. IN DWORD OutBufferSize,
  1840. OUT LPDWORD BytesReturned
  1841. )
  1842. /*++
  1843. Routine Description:
  1844. Arguments:
  1845. Return Value:
  1846. --*/
  1847. {
  1848. DWORD status = ERROR_SUCCESS;
  1849. PDISK_RESOURCE resourceEntry;
  1850. DWORD required;
  1851. ULONG uResId=PtrToUlong(Resource);
  1852. //
  1853. // Make sure the RESID is okay.
  1854. //
  1855. resourceEntry = DiskTable[uResId];
  1856. if ( resourceEntry == NULL ) {
  1857. DISKS_PRINT("ResourceControl, bad resource id, %u\n", uResId);
  1858. return(ERROR_RESOURCE_NOT_FOUND);
  1859. }
  1860. switch ( ControlCode ) {
  1861. case CLUSCTL_RESOURCE_UNKNOWN:
  1862. *BytesReturned = 0;
  1863. break;
  1864. case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTY_FMTS:
  1865. status = ResUtilGetPropertyFormats( DiskResourcePrivateProperties,
  1866. OutBuffer,
  1867. OutBufferSize,
  1868. BytesReturned,
  1869. &required );
  1870. if ( status == ERROR_MORE_DATA ) {
  1871. *BytesReturned = required;
  1872. }
  1873. break;
  1874. case CLUSCTL_RESOURCE_GET_CLASS_INFO:
  1875. *BytesReturned = sizeof(CLUS_RESOURCE_CLASS_INFO);
  1876. if ( OutBufferSize < sizeof(CLUS_RESOURCE_CLASS_INFO) ) {
  1877. status = ERROR_MORE_DATA;
  1878. } else {
  1879. PCLUS_RESOURCE_CLASS_INFO ptrResClassInfo = OutBuffer;
  1880. ptrResClassInfo->rc = CLUS_RESCLASS_STORAGE;
  1881. ptrResClassInfo->SubClass = (DWORD) CLUS_RESSUBCLASS_SHARED;
  1882. }
  1883. break;
  1884. case CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO:
  1885. status = GetDiskInfo( resourceEntry->DiskInfo.Params.Signature,
  1886. &OutBuffer,
  1887. OutBufferSize,
  1888. BytesReturned,
  1889. FALSE );
  1890. // Add the endmark.
  1891. if ( OutBufferSize > *BytesReturned ) {
  1892. OutBufferSize -= *BytesReturned;
  1893. } else {
  1894. OutBufferSize = 0;
  1895. }
  1896. *BytesReturned += sizeof(CLUSPROP_SYNTAX);
  1897. if ( OutBufferSize >= sizeof(CLUSPROP_SYNTAX) ) {
  1898. PCLUSPROP_SYNTAX ptrSyntax = OutBuffer;
  1899. ptrSyntax->dw = CLUSPROP_SYNTAX_ENDMARK;
  1900. }
  1901. break;
  1902. case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES:
  1903. status = ResUtilEnumProperties( DiskResourcePrivateProperties,
  1904. OutBuffer,
  1905. OutBufferSize,
  1906. BytesReturned,
  1907. &required );
  1908. if ( status == ERROR_MORE_DATA ) {
  1909. *BytesReturned = required;
  1910. }
  1911. break;
  1912. case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES:
  1913. status = DisksGetPrivateResProperties( resourceEntry,
  1914. OutBuffer,
  1915. OutBufferSize,
  1916. BytesReturned );
  1917. break;
  1918. case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES:
  1919. status = DisksValidatePrivateResProperties( resourceEntry,
  1920. InBuffer,
  1921. InBufferSize,
  1922. NULL );
  1923. break;
  1924. case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES:
  1925. status = DisksSetPrivateResProperties( resourceEntry,
  1926. InBuffer,
  1927. InBufferSize );
  1928. #if 0
  1929. status = ClusterRegQueryValue(resourceEntry->ResourceParametersKey,
  1930. DISKS_DISK_INFO,
  1931. NULL,
  1932. NULL,
  1933. &required);
  1934. if (status == ERROR_FILE_NOT_FOUND ) {
  1935. // If not found, then make it.
  1936. PFT_INFO diskInfo;
  1937. diskInfo = DiskGetFtInfo();
  1938. if ( diskInfo ) {
  1939. status = DiskspRestoreState( resourceEntry,
  1940. diskInfo );
  1941. DiskFreeFtInfo( diskInfo );
  1942. }
  1943. }
  1944. #else
  1945. DiskspSsyncDiskInfo(L"ResourceControl", resourceEntry, 0);
  1946. #endif
  1947. break;
  1948. case CLUSCTL_RESOURCE_DELETE:
  1949. if ( resourceEntry->DiskInfo.Params.Signature ) {
  1950. (DiskpLogEvent)(
  1951. resourceEntry->ResourceHandle,
  1952. LOG_INFORMATION,
  1953. L"Delete disk resource %1!lx!\n",
  1954. resourceEntry->DiskInfo.Params.Signature );
  1955. status = DoDetach( resourceEntry->DiskInfo.Params.Signature,
  1956. resourceEntry->ResourceHandle );
  1957. }
  1958. break;
  1959. case CLUSCTL_RESOURCE_GET_CHARACTERISTICS:
  1960. *BytesReturned = sizeof(DWORD);
  1961. if ( OutBufferSize < sizeof(DWORD) ) {
  1962. status = ERROR_MORE_DATA;
  1963. } else {
  1964. LPDWORD ptrDword = OutBuffer;
  1965. *ptrDword = CLUS_CHAR_QUORUM | CLUS_CHAR_DELETE_REQUIRES_ALL_NODES;
  1966. }
  1967. break;
  1968. case CLUSCTL_RESOURCE_STORAGE_DLL_EXTENSION:
  1969. status = ProcessDllExtension( resourceEntry,
  1970. InBuffer,
  1971. InBufferSize,
  1972. OutBuffer,
  1973. OutBufferSize,
  1974. BytesReturned );
  1975. break;
  1976. case CLUSCTL_RESOURCE_REMOVE_DEPENDENCY:
  1977. case CLUSCTL_RESOURCE_ADD_DEPENDENCY:
  1978. (DiskpLogEvent)(
  1979. resourceEntry->ResourceHandle,
  1980. LOG_INFORMATION,
  1981. L"Add/Remove dependency: source signature %1!lx! target name (%2!ws!) \n",
  1982. resourceEntry->DiskInfo.Params.Signature,
  1983. InBuffer );
  1984. status = DisksProcessMPControlCode( resourceEntry,
  1985. ControlCode );
  1986. break;
  1987. default:
  1988. status = ERROR_INVALID_FUNCTION;
  1989. break;
  1990. }
  1991. return(status);
  1992. } // DisksResourceControl
  1993. DWORD
  1994. DisksResourceTypeControl(
  1995. IN LPCWSTR ResourceTypeName,
  1996. IN DWORD ControlCode,
  1997. IN PVOID InBuffer,
  1998. IN DWORD InBufferSize,
  1999. OUT PVOID OutBuffer,
  2000. IN DWORD OutBufferSize,
  2001. OUT LPDWORD BytesReturned
  2002. )
  2003. /*++
  2004. Routine Description:
  2005. Process control requests for this resource type.
  2006. Arguments:
  2007. ResourceTypeName - the name of the resource type - not very useful!
  2008. ControlCode - the control request
  2009. InBuffer - pointer to the input buffer
  2010. InBufferSize - the size of the input buffer
  2011. OutBuffer - pointer to the output buffer
  2012. OutBufferSize - the size of the output buffer
  2013. BytesReturned - the number of bytes returned (or needed if larger than
  2014. OutBufferSize and ERROR_MORE_DATA is returned
  2015. Return Value:
  2016. ERROR_SUCCESS if successful
  2017. A WIN32 error on failure
  2018. --*/
  2019. {
  2020. DWORD status = ERROR_SUCCESS;
  2021. DWORD required;
  2022. switch ( ControlCode ) {
  2023. case CLUSCTL_RESOURCE_TYPE_UNKNOWN:
  2024. *BytesReturned = 0;
  2025. status = ERROR_SUCCESS;
  2026. break;
  2027. case CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_RESOURCE_PROPERTY_FMTS:
  2028. status = ResUtilGetPropertyFormats( DiskResourcePrivateProperties,
  2029. OutBuffer,
  2030. OutBufferSize,
  2031. BytesReturned,
  2032. &required );
  2033. if ( status == ERROR_MORE_DATA ) {
  2034. *BytesReturned = required;
  2035. }
  2036. break;
  2037. case CLUSCTL_RESOURCE_TYPE_HOLD_IO:
  2038. DoHoldIO();
  2039. break;
  2040. case CLUSCTL_RESOURCE_TYPE_RESUME_IO:
  2041. DoResumeIO();
  2042. break;
  2043. case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES:
  2044. status = ResUtilEnumProperties( DiskResourcePrivateProperties,
  2045. OutBuffer,
  2046. OutBufferSize,
  2047. BytesReturned,
  2048. &required );
  2049. if ( status == ERROR_MORE_DATA ) {
  2050. *BytesReturned = required;
  2051. }
  2052. break;
  2053. case CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO:
  2054. *BytesReturned = sizeof(CLUS_RESOURCE_CLASS_INFO);
  2055. if ( OutBufferSize < sizeof(CLUS_RESOURCE_CLASS_INFO) ) {
  2056. status = ERROR_MORE_DATA;
  2057. } else {
  2058. PCLUS_RESOURCE_CLASS_INFO ptrResClassInfo = OutBuffer;
  2059. ptrResClassInfo->rc = CLUS_RESCLASS_STORAGE;
  2060. ptrResClassInfo->SubClass = (DWORD) CLUS_RESSUBCLASS_SHARED;
  2061. status = ERROR_SUCCESS;
  2062. }
  2063. break;
  2064. case CLUSCTL_RESOURCE_TYPE_STORAGE_GET_AVAILABLE_DISKS:
  2065. status = ClusDiskGetAvailableDisks( OutBuffer,
  2066. OutBufferSize,
  2067. BytesReturned,
  2068. FALSE );
  2069. break;
  2070. case CLUSCTL_RESOURCE_TYPE_GET_CHARACTERISTICS:
  2071. *BytesReturned = sizeof(DWORD);
  2072. if ( OutBufferSize < sizeof(DWORD) ) {
  2073. status = ERROR_MORE_DATA;
  2074. } else {
  2075. LPDWORD ptrDword = OutBuffer;
  2076. *ptrDword = CLUS_CHAR_QUORUM | CLUS_CHAR_DELETE_REQUIRES_ALL_NODES;
  2077. }
  2078. break;
  2079. default:
  2080. status = ERROR_INVALID_FUNCTION;
  2081. break;
  2082. }
  2083. return(status);
  2084. } // DisksResourceTypeControl
  2085. DWORD
  2086. DisksGetPrivateResProperties(
  2087. IN OUT PDISK_RESOURCE ResourceEntry,
  2088. OUT PVOID OutBuffer,
  2089. IN DWORD OutBufferSize,
  2090. OUT LPDWORD BytesReturned
  2091. )
  2092. /*++
  2093. Routine Description:
  2094. Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control function
  2095. for resources of type Physical Disk.
  2096. Arguments:
  2097. ResourceEntry - Supplies the resource entry on which to operate.
  2098. OutBuffer - Returns the output data.
  2099. OutBufferSize - Supplies the size, in bytes, of the data pointed
  2100. to by OutBuffer.
  2101. BytesReturned - The number of bytes returned in OutBuffer.
  2102. Return Value:
  2103. ERROR_SUCCESS - The function completed successfully.
  2104. ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
  2105. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  2106. Win32 error code - The function failed.
  2107. --*/
  2108. {
  2109. DWORD status;
  2110. DWORD required;
  2111. status = ResUtilGetAllProperties(
  2112. ResourceEntry->ResourceParametersKey,
  2113. DiskResourcePrivateProperties,
  2114. OutBuffer,
  2115. OutBufferSize,
  2116. BytesReturned,
  2117. &required );
  2118. if ( status == ERROR_MORE_DATA ) {
  2119. *BytesReturned = required;
  2120. }
  2121. return(status);
  2122. } // DisksGetPrivateResProperties
  2123. DWORD
  2124. DisksConvertDriveToSignature(
  2125. IN OUT PDISK_PARAMS Params,
  2126. IN RESOURCE_HANDLE ResourceHandle
  2127. )
  2128. /*++
  2129. Routine Description:
  2130. Change the drive letter or VolGuid to the associated disk signature.
  2131. Arguments:
  2132. Params - Supplies the parameter block that contains the drive letter
  2133. (or VolGuid) to convert to a signature.
  2134. VolGuid syntax is returned from mountvol.exe and in the form:
  2135. \\?\Volume{e6de97f1-6f97-11d3-bb7f-806d6172696f}\
  2136. ResourceHandle -
  2137. Return Value:
  2138. ERROR_SUCCESS - The function completed successfully.
  2139. ERROR_INVALID_PARAMETER - The data is formatted incorrectly or is invalid.
  2140. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  2141. Win32 error code - The function failed.
  2142. --*/
  2143. {
  2144. PWCHAR deviceName = NULL;
  2145. DWORD deviceNameBytes;
  2146. DWORD lenChar;
  2147. DWORD status = ERROR_SUCCESS;
  2148. LPWSTR drive = Params->Drive;
  2149. LPWSTR volGuid = Params->VolGuid;
  2150. HANDLE fileHandle;
  2151. BOOL success;
  2152. PDRIVE_LAYOUT_INFORMATION driveLayout = NULL;
  2153. //
  2154. // Build device string for CreateFile
  2155. //
  2156. if ( drive ) {
  2157. deviceNameBytes = (( wcslen( drive ) + wcslen( L"\\\\.\\" ) ) * sizeof(WCHAR) ) +
  2158. sizeof( UNICODE_NULL );
  2159. deviceName = LocalAlloc( LPTR, deviceNameBytes );
  2160. if ( !deviceName ) {
  2161. goto FnExit;
  2162. }
  2163. _snwprintf( deviceName,
  2164. deviceNameBytes/sizeof(WCHAR),
  2165. L"\\\\.\\%ws",
  2166. drive );
  2167. } else if ( volGuid ) {
  2168. deviceNameBytes = ( wcslen( volGuid ) * sizeof( WCHAR ) ) + sizeof( UNICODE_NULL );
  2169. deviceName = LocalAlloc( LPTR, deviceNameBytes );
  2170. if ( !deviceName ) {
  2171. goto FnExit;
  2172. }
  2173. CopyMemory( deviceName,
  2174. volGuid,
  2175. deviceNameBytes );
  2176. //
  2177. // If user specified \\?\Volume{guid}\ with trailing backslash, we need to get
  2178. // rid of the backslash.
  2179. //
  2180. lenChar = wcslen( deviceName );
  2181. if ( lenChar > 1 && L'\\' == deviceName[lenChar-1] ) {
  2182. deviceName[lenChar-1] = L'\0';
  2183. }
  2184. } else {
  2185. goto FnExit;
  2186. }
  2187. fileHandle = CreateFileW( deviceName,
  2188. GENERIC_READ | GENERIC_WRITE,
  2189. FILE_SHARE_READ | FILE_SHARE_WRITE,
  2190. NULL,
  2191. OPEN_EXISTING,
  2192. 0,
  2193. NULL );
  2194. if ( (fileHandle == INVALID_HANDLE_VALUE) ||
  2195. (fileHandle == NULL) ) {
  2196. status = GetLastError();
  2197. (DiskpLogEvent)(
  2198. ResourceHandle,
  2199. LOG_ERROR,
  2200. L"ConvertDriveToSignature, error opening device '%1!ws!'. Error: %2!u!\n",
  2201. deviceName,
  2202. status );
  2203. goto FnExit;
  2204. }
  2205. //
  2206. // Get drive layout - in order to get disk signature.
  2207. //
  2208. success = ClRtlGetDriveLayoutTable( fileHandle, &driveLayout, NULL );
  2209. if ( success &&
  2210. driveLayout->Signature ) {
  2211. Params->Signature = driveLayout->Signature;
  2212. if ( Params->Drive ) {
  2213. LocalFree( Params->Drive );
  2214. Params->Drive = NULL;
  2215. }
  2216. if ( Params->VolGuid ) {
  2217. LocalFree( Params->VolGuid );
  2218. Params->VolGuid = NULL;
  2219. }
  2220. } else {
  2221. status = ERROR_FILE_NOT_FOUND;
  2222. }
  2223. if ( driveLayout ) {
  2224. LocalFree( driveLayout );
  2225. }
  2226. CloseHandle( fileHandle );
  2227. FnExit:
  2228. if ( deviceName ) {
  2229. LocalFree( deviceName );
  2230. }
  2231. return(status);
  2232. } // DisksConvertDriveToSignature
  2233. DWORD
  2234. DisksValidatePrivateResProperties(
  2235. IN OUT PDISK_RESOURCE ResourceEntry,
  2236. IN PVOID InBuffer,
  2237. IN DWORD InBufferSize,
  2238. OUT PDISK_PARAMS Params
  2239. )
  2240. /*++
  2241. Routine Description:
  2242. Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control
  2243. function for resources of type Physical Disk.
  2244. Arguments:
  2245. ResourceEntry - Supplies the resource entry on which to operate.
  2246. InBuffer - Supplies a pointer to a buffer containing input data.
  2247. InBufferSize - Supplies the size, in bytes, of the data pointed
  2248. to by InBuffer.
  2249. Params - Supplies the parameter block to fill in.
  2250. Return Value:
  2251. ERROR_SUCCESS - The function completed successfully.
  2252. ERROR_INVALID_PARAMETER - The data is formatted incorrectly or is invalid.
  2253. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  2254. Win32 error code - The function failed.
  2255. --*/
  2256. {
  2257. DWORD status;
  2258. DWORD enableSanBoot;
  2259. DWORD diskNumber;
  2260. DWORD oldSerNumLen = 0;
  2261. DWORD newSerNumLen = 0;
  2262. DISK_PARAMS params;
  2263. PDISK_PARAMS pParams;
  2264. PDISK_PARAMS currentParams = &ResourceEntry->DiskInfo.Params;
  2265. SCSI_ADDRESS scsiAddress;
  2266. CLUSPROP_SCSI_ADDRESS clusScsiAddress;
  2267. //
  2268. // Check if there is input data.
  2269. //
  2270. if ( (InBuffer == NULL) ||
  2271. (InBufferSize < sizeof(DWORD)) ) {
  2272. return(ERROR_INVALID_DATA);
  2273. }
  2274. //
  2275. // Duplicate the resource parameter block.
  2276. //
  2277. if ( Params == NULL ) {
  2278. pParams = &params;
  2279. } else {
  2280. pParams = Params;
  2281. }
  2282. ZeroMemory( pParams, sizeof(DISK_PARAMS) );
  2283. status = ResUtilDupParameterBlock( (LPBYTE) pParams,
  2284. (LPBYTE) currentParams,
  2285. DiskResourcePrivateProperties );
  2286. if ( status != ERROR_SUCCESS ) {
  2287. return(status);
  2288. }
  2289. //
  2290. // Parse and validate the properties. Accept the alternate name here.
  2291. //
  2292. status = ResUtilVerifyPropertyTable( DiskResourcePrivatePropertiesAlt,
  2293. NULL,
  2294. TRUE, // Allow unknowns
  2295. InBuffer,
  2296. InBufferSize,
  2297. (LPBYTE) pParams );
  2298. if ( status != ERROR_SUCCESS ) {
  2299. goto FnExit;
  2300. }
  2301. //
  2302. // First make sure there are no bogus properties - i.e. we don't allow
  2303. // specifying both the signature and the drive in the same request.
  2304. // We also don't allow specifying the VolGuid and signature in the same
  2305. // request.
  2306. //
  2307. if ( (pParams->Drive || pParams->VolGuid) && pParams->Signature ) {
  2308. status = ERROR_INVALID_PARAMETER;
  2309. goto FnExit;
  2310. }
  2311. if ( pParams->Drive || pParams->VolGuid ) {
  2312. //
  2313. // Convert from drive to signature.
  2314. //
  2315. status = DisksConvertDriveToSignature( pParams,
  2316. ResourceEntry->ResourceHandle );
  2317. }
  2318. if ( status != ERROR_SUCCESS ) {
  2319. goto FnExit;
  2320. }
  2321. //
  2322. // Verify the new serial number is valid and not overwriting an existing
  2323. // serial number.
  2324. //
  2325. if ( pParams->SerialNumber ) {
  2326. newSerNumLen = wcslen( pParams->SerialNumber );
  2327. }
  2328. if ( currentParams->SerialNumber ) {
  2329. oldSerNumLen = wcslen( currentParams->SerialNumber );
  2330. }
  2331. //
  2332. // If there was an old serial number, make sure the new serial number is
  2333. // the same.
  2334. //
  2335. if ( oldSerNumLen &&
  2336. ( oldSerNumLen != newSerNumLen ||
  2337. 0 != wcsncmp( currentParams->SerialNumber, pParams->SerialNumber, newSerNumLen ) ) ) {
  2338. status = ERROR_INVALID_PARAMETER;
  2339. goto FnExit;
  2340. }
  2341. //
  2342. // Check serial number and signature.
  2343. //
  2344. if ( 0 == oldSerNumLen &&
  2345. pParams->SerialNumber &&
  2346. 0 == currentParams->Signature ) {
  2347. //
  2348. // New serial number specified and no current signature.
  2349. //
  2350. if ( 0 == pParams->Signature ) {
  2351. //
  2352. // No new signature specified, use the new serial number to
  2353. // find the new signature.
  2354. //
  2355. status = GetSignatureFromSerialNumber( pParams->SerialNumber,
  2356. &pParams->Signature );
  2357. if ( status != ERROR_SUCCESS ) {
  2358. goto FnExit;
  2359. }
  2360. } else {
  2361. //
  2362. // New signature and new serial number specified. Fail the request.
  2363. //
  2364. status = ERROR_INVALID_PARAMETER;
  2365. goto FnExit;
  2366. }
  2367. }
  2368. //
  2369. // Validate the parameter values.
  2370. //
  2371. // Make sure the disk signature is not zero.
  2372. //
  2373. if ( 0 == pParams->Signature ) {
  2374. status = ERROR_INVALID_PARAMETER;
  2375. goto FnExit;
  2376. }
  2377. // At this point, we have a valid disk signature.
  2378. //
  2379. // If no serial number, get it.
  2380. //
  2381. if ( !pParams->SerialNumber || newSerNumLen <= 1 ) {
  2382. status = GetSerialNumber( pParams->Signature,
  2383. &pParams->SerialNumber );
  2384. }
  2385. if ( ERROR_SUCCESS != status ) {
  2386. goto FnExit;
  2387. }
  2388. ZeroMemory( &clusScsiAddress, sizeof(clusScsiAddress) );
  2389. status = GetScsiAddress( pParams->Signature, &clusScsiAddress.dw, &diskNumber );
  2390. if ( ERROR_SUCCESS != status ) {
  2391. status = ERROR_INVALID_PARAMETER;
  2392. goto FnExit;
  2393. }
  2394. scsiAddress.Length = sizeof(SCSI_ADDRESS);
  2395. scsiAddress.PortNumber = clusScsiAddress.PortNumber;
  2396. scsiAddress.PathId = clusScsiAddress.PathId;
  2397. scsiAddress.TargetId = clusScsiAddress.TargetId;
  2398. scsiAddress.Lun = clusScsiAddress.Lun;
  2399. //
  2400. // Make sure the SCSI address is not system disk.
  2401. //
  2402. enableSanBoot = 0;
  2403. GetRegDwordValue( CLUSREG_KEYNAME_CLUSSVC_PARAMETERS,
  2404. CLUSREG_VALUENAME_MANAGEDISKSONSYSTEMBUSES,
  2405. &enableSanBoot );
  2406. if ( !enableSanBoot ) {
  2407. //
  2408. // Signature is valid if:
  2409. // - the signature is for a disk not on system bus
  2410. //
  2411. if ( IsDiskOnSystemBus( &scsiAddress ) ) {
  2412. status = ERROR_INVALID_PARAMETER;
  2413. goto FnExit;
  2414. }
  2415. } else {
  2416. // Allow disks on system bus to be added to cluster.
  2417. //
  2418. // Signature is valid if:
  2419. // - the signature is not for the system disk
  2420. if ( IsDiskSystemDisk( &scsiAddress ) ) {
  2421. status = ERROR_INVALID_PARAMETER;
  2422. goto FnExit;
  2423. }
  2424. }
  2425. //
  2426. // For now, we don't allow setting mount point volume GUIDs this way.
  2427. // This is a multi SZ string, so use memcmp to skip past each string's
  2428. // terminating NULL.
  2429. //
  2430. if ( ( currentParams->MPVolGuidsSize != pParams->MPVolGuidsSize ) ||
  2431. ( 0 != memcmp( currentParams->MPVolGuids, pParams->MPVolGuids, currentParams->MPVolGuidsSize ) ) ) {
  2432. status = ERROR_INVALID_PARAMETER;
  2433. goto FnExit;
  2434. }
  2435. FnExit:
  2436. //
  2437. // Cleanup our parameter block.
  2438. //
  2439. if ( pParams == &params ) {
  2440. ResUtilFreeParameterBlock( (LPBYTE) &params,
  2441. (LPBYTE) &ResourceEntry->DiskInfo.Params,
  2442. DiskResourcePrivateProperties );
  2443. }
  2444. return(status);
  2445. } // DisksValidatePrivateResProperties
  2446. DWORD
  2447. DisksSetPrivateResProperties(
  2448. IN OUT PDISK_RESOURCE ResourceEntry,
  2449. IN PVOID InBuffer,
  2450. IN DWORD InBufferSize
  2451. )
  2452. /*++
  2453. Routine Description:
  2454. Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function
  2455. for resources of type Physical Disk.
  2456. Arguments:
  2457. ResourceEntry - Supplies the resource entry on which to operate.
  2458. InBuffer - Supplies a pointer to a buffer containing input data.
  2459. InBufferSize - Supplies the size, in bytes, of the data pointed
  2460. to by InBuffer.
  2461. Return Value:
  2462. ERROR_SUCCESS - The function completed successfully.
  2463. ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
  2464. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  2465. Win32 error code - The function failed.
  2466. --*/
  2467. {
  2468. DWORD status;
  2469. DISK_PARAMS params;
  2470. ZeroMemory( &params, sizeof(DISK_PARAMS) );
  2471. //
  2472. // Parse and validate the properties.
  2473. //
  2474. status = DisksValidatePrivateResProperties( ResourceEntry,
  2475. InBuffer,
  2476. InBufferSize,
  2477. &params );
  2478. if ( status != ERROR_SUCCESS ) {
  2479. ResUtilFreeParameterBlock( (LPBYTE) &params,
  2480. (LPBYTE) &ResourceEntry->DiskInfo.Params,
  2481. DiskResourcePrivateProperties );
  2482. return(status);
  2483. }
  2484. //
  2485. // We cannot allow changing the Signature 'on the fly'...
  2486. //
  2487. if ( (ResourceEntry->DiskInfo.Params.Signature != 0) &&
  2488. (params.Signature != ResourceEntry->DiskInfo.Params.Signature) ) {
  2489. ResUtilFreeParameterBlock( (LPBYTE) &params,
  2490. (LPBYTE) &ResourceEntry->DiskInfo.Params,
  2491. DiskResourcePrivateProperties );
  2492. (DiskpLogEvent)(
  2493. NULL,
  2494. LOG_ERROR,
  2495. L"SetPrivateProperties doesn't allow changing signature, old %1!lx!, new %2!lx!\n",
  2496. ResourceEntry->DiskInfo.Params.Signature,
  2497. params.Signature );
  2498. return(ERROR_INVALID_STATE);
  2499. }
  2500. //
  2501. // Save the parameter values.
  2502. //
  2503. // NB: Unknown, or non-property table values are dealt with farther below. That is why InBuffer and
  2504. // InBufferSize are not used in this call. Only the propertys in the parameter block are handled here.
  2505. //
  2506. status = ResUtilSetPropertyParameterBlock( ResourceEntry->ResourceParametersKey,
  2507. DiskResourcePrivateProperties,
  2508. NULL,
  2509. (LPBYTE) &params,
  2510. NULL,
  2511. 0,
  2512. (LPBYTE) &ResourceEntry->DiskInfo.Params );
  2513. ResUtilFreeParameterBlock( (LPBYTE) &params,
  2514. (LPBYTE) &ResourceEntry->DiskInfo.Params,
  2515. DiskResourcePrivateProperties );
  2516. if ( status != ERROR_SUCCESS ) {
  2517. (DiskpLogEvent)(
  2518. NULL,
  2519. LOG_ERROR,
  2520. L"SetPrivateResProperties: Error %1!d! saving properties\n",
  2521. status );
  2522. return(status);
  2523. }
  2524. //
  2525. // Save any unknown properties.
  2526. //
  2527. status = ResUtilSetUnknownProperties(
  2528. ResourceEntry->ResourceParametersKey,
  2529. DiskResourcePrivatePropertiesAlt,
  2530. InBuffer,
  2531. InBufferSize );
  2532. if ( status != ERROR_SUCCESS ) {
  2533. (DiskpLogEvent)(
  2534. NULL,
  2535. LOG_ERROR,
  2536. L"SetPrivateResProperties: Error %1!d! saving unknown properties\n",
  2537. status );
  2538. }
  2539. //
  2540. // Try to attach to this device if we have a signature.
  2541. //
  2542. if ( ResourceEntry->DiskInfo.Params.Signature ) {
  2543. #if 0
  2544. DiskspVerifyState( ResourceEntry );
  2545. #endif
  2546. DoAttach( ResourceEntry->DiskInfo.Params.Signature,
  2547. ResourceEntry->ResourceHandle );
  2548. // ignore status return
  2549. }
  2550. //
  2551. // If the resource is online, return a non-success status.
  2552. //
  2553. if (status == ERROR_SUCCESS) {
  2554. if ( ResourceEntry->Valid ) {
  2555. status = ERROR_RESOURCE_PROPERTIES_STORED;
  2556. } else {
  2557. status = ERROR_SUCCESS;
  2558. }
  2559. }
  2560. return status;
  2561. } // DisksSetPrivateResProperties
  2562. DWORD
  2563. ProcessDllExtension(
  2564. IN PDISK_RESOURCE ResourceEntry,
  2565. IN PVOID InBuffer,
  2566. IN DWORD InBufferSize,
  2567. OUT PVOID OutBuffer,
  2568. IN DWORD OutBufferSize,
  2569. OUT LPDWORD BytesReturned
  2570. )
  2571. /*++
  2572. Routine Description:
  2573. Processes the CLUSCTL_RESOURCE_STORAGE_DLL_EXTENSION control function
  2574. for resources of type Physical Disk. The caller will specify a DLL
  2575. and DLL entry point that should be called. This routine will call
  2576. into that entry point with the disk device name represented by the
  2577. Signature parameter.
  2578. Arguments:
  2579. ResourceEntry -
  2580. InBuffer - Supplies a pointer to a buffer containing input data.
  2581. InBufferSize - Supplies the size, in bytes, of the data pointed
  2582. to by InBuffer.
  2583. OutBuffer - Supplies a pointer to a buffer containing output data.
  2584. OutBufferSize - Supplies the size, in bytes, of the data pointed
  2585. to by OutBuffer.
  2586. BytesReturned - the number of bytes returned (or needed if larger than
  2587. OutBufferSize and ERROR_MORE_DATA is returned
  2588. Return Value:
  2589. ERROR_SUCCESS - The function completed successfully.
  2590. ERROR_INVALID_PARAMETER - The input data is formatted incorrectly.
  2591. ERROR_REVISION_MISMATCH - The input buffer did not have the correct
  2592. revision information.
  2593. ERROR_MORE_DATA - The output buffer is not large enough to hold all
  2594. the requested data.
  2595. Win32 error code - The function failed.
  2596. --*/
  2597. {
  2598. lpPassThruFunc passThruFunc = NULL;
  2599. PCHAR dllModuleName;
  2600. PCHAR dllProcName;
  2601. PCHAR contextStr;
  2602. PDISK_DLL_EXTENSION_INFO passThru = InBuffer;
  2603. HINSTANCE dllModule = NULL;
  2604. DWORD scsiAddress;
  2605. DWORD diskNumber;
  2606. DWORD dwStatus;
  2607. DWORD signature = ResourceEntry->DiskInfo.Params.Signature;
  2608. DWORD expandedSize;
  2609. CHAR deviceName[MAX_PATH];
  2610. CHAR expandedDllModuleName[MAX_PATH];
  2611. if ( !InBuffer || !OutBuffer || !OutBufferSize ) {
  2612. dwStatus = ERROR_INVALID_PARAMETER;
  2613. goto FnExit;
  2614. }
  2615. if ( InBufferSize < sizeof(DISK_DLL_EXTENSION_INFO) ) {
  2616. dwStatus = ERROR_INVALID_PARAMETER;
  2617. goto FnExit;
  2618. }
  2619. try {
  2620. if ( passThru->MajorVersion != NT5_MAJOR_VERSION ||
  2621. passThru->MinorVersion != 0 ) {
  2622. dwStatus = ERROR_REVISION_MISMATCH;
  2623. goto FnExit;
  2624. }
  2625. //
  2626. // Get the DLL module name from the Input buffer.
  2627. //
  2628. dllModuleName = passThru->DllModuleName;
  2629. //
  2630. // Get the DLL entry point name from the Input buffer.
  2631. //
  2632. dllProcName = passThru->DllProcName;
  2633. contextStr = passThru->ContextStr;
  2634. //
  2635. // Get the SCSI address to build the device name.
  2636. //
  2637. dwStatus = GetScsiAddress( signature, &scsiAddress, &diskNumber );
  2638. if ( NO_ERROR != dwStatus ) {
  2639. goto FnExit;
  2640. }
  2641. _snprintf( deviceName, sizeof(deviceName), "\\\\.\\PhysicalDrive%d", diskNumber );
  2642. expandedSize = ExpandEnvironmentStrings( dllModuleName, expandedDllModuleName, MAX_PATH );
  2643. if ( !expandedSize ) {
  2644. dwStatus = GetLastError();
  2645. goto FnExit;
  2646. }
  2647. if ( expandedSize > MAX_PATH ) {
  2648. dwStatus = ERROR_BAD_PATHNAME;
  2649. goto FnExit;
  2650. }
  2651. //
  2652. // Assume the DLL has not yet been loaded into the address space.
  2653. // The caller should specify a fully qualified path name to make sure their
  2654. // DLL is called, and they are responsible for making sure that the DLL is
  2655. // protected.
  2656. //
  2657. dllModule = LoadLibraryEx( expandedDllModuleName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
  2658. if ( NULL == dllModule ) {
  2659. dwStatus = GetLastError();
  2660. goto FnExit;
  2661. }
  2662. //
  2663. // The function name MUST be as defined (i.e. with exactly the same type
  2664. // and number of parameters) or we will have stack problems.
  2665. //
  2666. passThruFunc = (lpPassThruFunc)GetProcAddress( dllModule, dllProcName );
  2667. if ( NULL == passThruFunc ) {
  2668. //
  2669. // The specified function is not available in the DLL, exit now.
  2670. //
  2671. dwStatus = GetLastError();
  2672. goto FnExit;
  2673. }
  2674. //
  2675. // Call into the specified DLL.
  2676. //
  2677. dwStatus = (passThruFunc)( deviceName,
  2678. contextStr,
  2679. OutBuffer,
  2680. OutBufferSize,
  2681. BytesReturned );
  2682. } except (EXCEPTION_EXECUTE_HANDLER) {
  2683. dwStatus = GetExceptionCode();
  2684. (DiskpLogEvent)( ResourceEntry->ResourceHandle,
  2685. LOG_ERROR,
  2686. L"ProcessDllExtension: Exception occurred %1!X! \n",
  2687. dwStatus );
  2688. }
  2689. FnExit:
  2690. if ( dllModule ) {
  2691. FreeLibrary( dllModule );
  2692. }
  2693. return dwStatus;
  2694. } // ProcessDllExtension
  2695. DWORD
  2696. DisksOpenChkdskLogFile(
  2697. IN PDISK_RESOURCE ResourceEntry,
  2698. IN OUT PHANDLE ChkdskLogFile,
  2699. IN OUT LPWSTR *ChkdskLogFileName
  2700. )
  2701. /*++
  2702. Routine Description:
  2703. Creates a file to log chkdsk output and returns the handle to the caller.
  2704. The file will not be deleted on close.
  2705. Arguments:
  2706. ResourceEntry - Supplies a pointer to the resource structure
  2707. ChkdskLogFile - Returned handled of newly opened log file.
  2708. ChkdskLogFileName - String pointer to the name of the newly
  2709. opened log file. Caller is responsible
  2710. for freeing storage.
  2711. Return Value:
  2712. ERROR_SUCCESS if successful
  2713. Win32 error code otherwise
  2714. --*/
  2715. {
  2716. DWORD status = NO_ERROR;
  2717. HANDLE retHandle = INVALID_HANDLE_VALUE;
  2718. PWCHAR last;
  2719. PWCHAR current;
  2720. LPWSTR clusterDir = NULL;
  2721. LPWSTR fileName = NULL;
  2722. LPWSTR finalFileName = NULL;
  2723. DWORD clusterDirLength; // This value can change.
  2724. DWORD fileNameLength = MAX_PATH;
  2725. DWORD finalFileNameLength;
  2726. SECURITY_ATTRIBUTES sa;
  2727. if ( !ChkdskLogFile || !ChkdskLogFileName ) {
  2728. return ERROR_INVALID_PARAMETER;
  2729. }
  2730. *ChkdskLogFile = INVALID_HANDLE_VALUE;
  2731. *ChkdskLogFileName = NULL;
  2732. _try {
  2733. //
  2734. // Get the environment variable "ClusterLog". Embedded in this string
  2735. // is the cluster directory.
  2736. //
  2737. clusterDir = LocalAlloc( LMEM_FIXED, MAX_PATH * sizeof(WCHAR) );
  2738. if ( !clusterDir ) {
  2739. status = GetLastError();
  2740. _leave;
  2741. }
  2742. clusterDirLength = GetEnvironmentVariableW( CLUSTERLOG_ENV_VARIABLE,
  2743. clusterDir,
  2744. MAX_PATH );
  2745. if ( !clusterDirLength ) {
  2746. status = GetLastError();
  2747. _leave;
  2748. }
  2749. if ( clusterDirLength > MAX_PATH ) {
  2750. LocalFree( clusterDir );
  2751. clusterDir = LocalAlloc( LMEM_FIXED, clusterDirLength * sizeof( WCHAR ) );
  2752. if ( NULL == clusterDir ) {
  2753. status = GetLastError();
  2754. _leave;
  2755. }
  2756. clusterDirLength = GetEnvironmentVariableW( CLUSTERLOG_ENV_VARIABLE,
  2757. clusterDir,
  2758. clusterDirLength );
  2759. if ( !clusterDirLength ) {
  2760. status = GetLastError();
  2761. LocalFree( clusterDir );
  2762. clusterDir = NULL;
  2763. _leave;
  2764. }
  2765. }
  2766. //
  2767. // We have the log file path and name. Find the last backslash and strip off the
  2768. // log file name. This will be used as our temporary file path.
  2769. //
  2770. last = NULL;
  2771. current = (PWCHAR) clusterDir;
  2772. while ( *current != L'\0' ) {
  2773. if ( L'\\' == *current ) {
  2774. last = current;
  2775. }
  2776. current++;
  2777. }
  2778. if ( !last ) {
  2779. status = ERROR_BAD_FORMAT;
  2780. _leave;
  2781. }
  2782. //
  2783. // Change the last backslash to the end of string mark.
  2784. //
  2785. *last = L'\0';
  2786. //
  2787. // Now create a file name based on the disk signature.
  2788. //
  2789. fileName = LocalAlloc( LMEM_FIXED, fileNameLength * sizeof(WCHAR) );
  2790. if ( !fileName ) {
  2791. status = GetLastError();
  2792. _leave;
  2793. }
  2794. _snwprintf( fileName, fileNameLength,
  2795. L"ChkDsk_Disk%d_Sig%08X.log",
  2796. ResourceEntry->DiskInfo.PhysicalDrive,
  2797. ResourceEntry->DiskInfo.Params.Signature );
  2798. //
  2799. // Put it all together for the final name.
  2800. //
  2801. finalFileNameLength = fileNameLength + clusterDirLength + MAX_PATH;
  2802. finalFileName = LocalAlloc( LMEM_FIXED, finalFileNameLength * sizeof(WCHAR));
  2803. if ( !finalFileName ) {
  2804. status = GetLastError();
  2805. _leave;
  2806. }
  2807. _snwprintf( finalFileName, finalFileNameLength, L"%ws\\%ws", clusterDir, fileName );
  2808. //
  2809. // Now open up the file name to log the chkdsk info.
  2810. //
  2811. ZeroMemory( &sa, sizeof(sa) );
  2812. sa.nLength = sizeof(sa);
  2813. sa.lpSecurityDescriptor = NULL;
  2814. sa.bInheritHandle = TRUE;
  2815. retHandle = CreateFileW( finalFileName,
  2816. GENERIC_READ | GENERIC_WRITE,
  2817. FILE_SHARE_READ | FILE_SHARE_WRITE,
  2818. &sa,
  2819. CREATE_ALWAYS, // Create a new file or overwrite existing file
  2820. FILE_ATTRIBUTE_NORMAL,
  2821. NULL );
  2822. if ( INVALID_HANDLE_VALUE == retHandle ) {
  2823. status = GetLastError();
  2824. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  2825. LOG_ERROR,
  2826. L"DisksOpenChkdskLogFile: CreateFile returned status of %1!u! \n",
  2827. status );
  2828. _leave;
  2829. }
  2830. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  2831. LOG_INFORMATION,
  2832. L"DisksOpenChkdskLogFile: ChkDsk output is in file: %1!s! \n",
  2833. finalFileName );
  2834. } _finally {
  2835. *ChkdskLogFile = retHandle;
  2836. if ( clusterDir ) {
  2837. LocalFree( clusterDir );
  2838. }
  2839. if ( fileName ) {
  2840. LocalFree( fileName );
  2841. }
  2842. if ( finalFileName ) {
  2843. *ChkdskLogFileName = finalFileName;
  2844. }
  2845. }
  2846. return status;
  2847. } // DisksOpenChkdskLogFile
  2848. DWORD
  2849. GetRegDwordValue(
  2850. IN LPWSTR RegKeyName,
  2851. IN LPWSTR ValueName,
  2852. OUT LPDWORD ValueBuffer
  2853. )
  2854. {
  2855. DWORD dwValue;
  2856. DWORD dwValueType;
  2857. DWORD dwDataBufferSize = sizeof( DWORD );
  2858. DWORD dwError;
  2859. HKEY hClusParmKey = NULL;
  2860. if ( !ValueBuffer ) {
  2861. dwError = ERROR_INVALID_PARAMETER;
  2862. goto FnExit;
  2863. }
  2864. *ValueBuffer = 0;
  2865. //
  2866. // Open the cluster service parameters key.
  2867. //
  2868. dwError = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  2869. RegKeyName,
  2870. 0,
  2871. KEY_READ,
  2872. &hClusParmKey );
  2873. if ( ERROR_SUCCESS != dwError ) {
  2874. goto FnExit;
  2875. }
  2876. //
  2877. // Get the DWORD value.
  2878. //
  2879. dwError = RegQueryValueExW( hClusParmKey,
  2880. ValueName,
  2881. NULL,
  2882. &dwValueType,
  2883. (LPBYTE) &dwValue,
  2884. &dwDataBufferSize );
  2885. if ( ERROR_SUCCESS == dwError ) {
  2886. // Insure that a DWORD value was returned. If not, return an error.
  2887. if ( REG_DWORD == dwValueType ) {
  2888. *ValueBuffer = dwValue;
  2889. } else {
  2890. dwError = ERROR_BAD_FORMAT;
  2891. }
  2892. }
  2893. FnExit:
  2894. if ( hClusParmKey ) {
  2895. RegCloseKey( hClusParmKey );
  2896. }
  2897. return dwError;
  2898. } // GetRegDwordValue
  2899. //***********************************************************
  2900. //
  2901. // Define Function Table
  2902. //
  2903. //***********************************************************
  2904. CLRES_V1_FUNCTION_TABLE( DisksFunctionTable,
  2905. CLRES_VERSION_V1_00,
  2906. Disks,
  2907. DisksArbitrate,
  2908. DisksRelease,
  2909. DisksResourceControl,
  2910. DisksResourceTypeControl );