Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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