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.

2661 lines
82 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. newdisks.c
  5. Abstract:
  6. Some of the functions that used to be
  7. in disks.c reside now here
  8. Author:
  9. Gor Nishanov (GorN) 31-July-1998
  10. Revision History:
  11. --*/
  12. #include "disksp.h"
  13. #include "partmgrp.h"
  14. #include "arbitrat.h"
  15. #include "newdisks.h"
  16. #include "newmount.h"
  17. #include "mountmgr.h"
  18. #include <strsafe.h> // Should be included last.
  19. #define LOG_CURRENT_MODULE LOG_MODULE_DISK
  20. DWORD
  21. WaitForVolumes(
  22. IN PDISK_RESOURCE ResourceEntry,
  23. IN DWORD timeOutInSeconds
  24. );
  25. DWORD
  26. DiskspCheckPathLite(
  27. IN LPWSTR VolumeName,
  28. IN PDISK_RESOURCE ResourceEntry
  29. );
  30. NTSTATUS
  31. ForcePnpVolChangeEvent(
  32. PWSTR RootPath
  33. );
  34. DWORD
  35. DiskCleanup(
  36. PDISK_RESOURCE ResourceEntry
  37. )
  38. /*++
  39. Routine Description:
  40. Stops the reservations, dismount drive and frees DiskCpInfo
  41. Arguments:
  42. ResourceEntry - the disk info structure for the disk.
  43. Return Value:
  44. ERROR_SUCCESS if successful.
  45. A Win32 error code on failure.
  46. --*/
  47. {
  48. DWORD status = ERROR_SUCCESS;
  49. (DiskpLogEvent)(
  50. ResourceEntry->ResourceHandle,
  51. LOG_INFORMATION,
  52. L"DiskCleanup started.\n");
  53. StopPersistentReservations(ResourceEntry);
  54. //
  55. // If the remaining data in the resource entry is not valid, then leave
  56. // now.
  57. //
  58. if ( !ResourceEntry->Valid ) {
  59. goto FnExit;
  60. }
  61. //
  62. // Delete the DiskCpInfo.
  63. //
  64. if ( ResourceEntry->DiskCpInfo ) {
  65. LocalFree(ResourceEntry->DiskCpInfo);
  66. ResourceEntry->DiskCpInfo = NULL;
  67. ResourceEntry->DiskCpSize = 0;
  68. }
  69. ResourceEntry->Attached = FALSE;
  70. ResourceEntry->Valid = FALSE;
  71. //
  72. // Remove the Dos Drive Letters, this is better done here rather than
  73. // in ClusDisk.
  74. //
  75. DisksDismountDrive( ResourceEntry,
  76. ResourceEntry->DiskInfo.Params.Signature );
  77. FnExit:
  78. (DiskpLogEvent)(
  79. ResourceEntry->ResourceHandle,
  80. LOG_INFORMATION,
  81. L"DiskCleanup returning final error %1!u! \n",
  82. status );
  83. return(status);
  84. } // DiskCleanup
  85. static
  86. DWORD
  87. DisksSetDiskInfoThread(
  88. LPVOID lpThreadParameter
  89. )
  90. /*++
  91. Routine Description:
  92. Registry update thread.
  93. Arguments:
  94. lpThreadParameter - stores ResourceEntry.
  95. Return Value:
  96. None
  97. --*/
  98. {
  99. DWORD Status;
  100. PDISK_RESOURCE ResourceEntry = lpThreadParameter;
  101. DWORD i;
  102. (DiskpLogEvent)(
  103. ResourceEntry->ResourceHandle,
  104. LOG_INFORMATION,
  105. L"Checkpoint thread started.\n");
  106. //
  107. // Will die in 10 minutes if unsuccessful
  108. //
  109. for(i = 0; i < 300; ++i) {
  110. //
  111. // Wait for either the terminate event or the timeout
  112. //
  113. Status = WaitForSingleObject( DisksTerminateEvent, 2000 );
  114. if (Status == WAIT_TIMEOUT ) {
  115. Status = MountieUpdate(&ResourceEntry->MountieInfo, ResourceEntry);
  116. if ( Status == ERROR_SUCCESS ) {
  117. // We're done
  118. break;
  119. } else if ( Status != ERROR_SHARING_PAUSED ) {
  120. (DiskpLogEvent)(
  121. ResourceEntry->ResourceHandle,
  122. LOG_ERROR,
  123. L"Watcher Failed to checkpoint disk info, status = %1!u!.\n", Status );
  124. break;
  125. }
  126. } else {
  127. (DiskpLogEvent)(
  128. ResourceEntry->ResourceHandle,
  129. LOG_INFORMATION,
  130. L"CheckpointThread: WaitForSingleObject returned status = %1!u!.\n", Status );
  131. break;
  132. }
  133. }
  134. InterlockedExchange(
  135. &ResourceEntry->MountieInfo.UpdateThreadIsActive, 0);
  136. return(ERROR_SUCCESS);
  137. } // DisksSetDiskInfoThread
  138. DWORD
  139. DisksOfflineOrTerminate(
  140. IN PDISK_RESOURCE resourceEntry,
  141. IN BOOL Terminate
  142. )
  143. /*++
  144. Routine Description:
  145. Used by DisksOffline and DisksTerminate.
  146. Routine performs the following steps:
  147. 1. ClusWorkerTerminate (Terminate only)
  148. 2. Then for all of the partitions on the drive...
  149. a. Flush the file buffers. (Offline only)
  150. b. Lock the volume to purge all in memory contents of the volume. (Offline only)
  151. c. Dismount the volume
  152. 3. Removes default network shares (C$, F$, etc)
  153. Arguments:
  154. ResourceEntry - A pointer to the DISK_RESOURCE block for this resource.
  155. Terminate - Set it to TRUE to cause Termination Behavior
  156. Returns:
  157. ERROR_SUCCESS if successful.
  158. Win32 error code on failure.
  159. --*/
  160. {
  161. PWCHAR WideName = (Terminate)?L"Terminate":L"Offline";
  162. PHANDLE handleArray = NULL;
  163. BOOL Offline = !Terminate;
  164. DWORD status;
  165. DWORD idx;
  166. DWORD PartitionCount;
  167. DWORD handleArraySize;
  168. HANDLE fileHandle;
  169. DWORD bytesReturned;
  170. WCHAR szDiskPartName[MAX_PATH];
  171. NTSTATUS ntStatus;
  172. ACCESS_MASK access = SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA;
  173. (DiskpLogEvent)(
  174. resourceEntry->ResourceHandle,
  175. LOG_INFORMATION,
  176. L"%1!ws!, ResourceEntry @ %2!p! Valid %3!x! \n",
  177. WideName,
  178. resourceEntry,
  179. resourceEntry->Valid );
  180. if (Terminate) {
  181. access = SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
  182. ClusWorkerTerminate(&resourceEntry->OnlineThread);
  183. }
  184. StopWatchingDisk(resourceEntry);
  185. //
  186. // If the disk info is not valid, then don't use it!
  187. //
  188. if ( !resourceEntry->Valid ) {
  189. DiskCleanup( resourceEntry );
  190. return(ERROR_SUCCESS);
  191. }
  192. (DiskpLogEvent)(
  193. resourceEntry->ResourceHandle,
  194. LOG_INFORMATION,
  195. L"%1!ws!, Processing disk number %2!u!.\n",
  196. WideName,
  197. resourceEntry->DiskInfo.PhysicalDrive );
  198. #if 0
  199. if( Offline ) {
  200. //
  201. // Checkpoint our registry state
  202. //
  203. }
  204. #endif
  205. PartitionCount = MountiePartitionCount(&resourceEntry->MountieInfo);
  206. //
  207. // Allocate a buffer to hold handle for each partition. Since the
  208. // lock is released as soon as we call CloseHandle, we need to save all
  209. // the handles and close them after the disk is marked offline by
  210. // DiskCleanup. If we cannot allocate the storage for the handle
  211. // array, we will fall back to the previous behavior.
  212. //
  213. handleArraySize = PartitionCount * sizeof(HANDLE);
  214. handleArray = LocalAlloc( LPTR, handleArraySize );
  215. if ( !handleArray ) {
  216. (DiskpLogEvent)(
  217. resourceEntry->ResourceHandle,
  218. LOG_WARNING,
  219. L"%1!ws!, Unable to allocate storage for handle array, error %2!u!.\n",
  220. WideName,
  221. GetLastError() );
  222. } else {
  223. (DiskpLogEvent)(
  224. resourceEntry->ResourceHandle,
  225. LOG_INFORMATION,
  226. L"%1!ws!, Using handle array. \n",
  227. WideName );
  228. }
  229. //
  230. // For all of the partitions on the drive...
  231. //
  232. // 1. Flush the file buffers. (Offline only)
  233. // 2. Lock the volume to purge all in memory contents of the volume. (Offline only)
  234. // 3. Dismount the volume
  235. //
  236. for ( idx = 0; idx < PartitionCount; ++idx ) {
  237. PMOUNTIE_PARTITION partition = MountiePartition(&resourceEntry->MountieInfo, idx);
  238. (VOID) StringCchPrintf( szDiskPartName,
  239. RTL_NUMBER_OF( szDiskPartName),
  240. DEVICE_HARDDISK_PARTITION_FMT,
  241. resourceEntry->DiskInfo.PhysicalDrive,
  242. partition->PartitionNumber );
  243. (DiskpLogEvent)(
  244. resourceEntry->ResourceHandle,
  245. LOG_INFORMATION,
  246. L"%1!ws!, Opening device %2!ws!.\n",
  247. WideName,
  248. szDiskPartName );
  249. ntStatus = DevfileOpenEx( &fileHandle, szDiskPartName, access );
  250. if ( !NT_SUCCESS(ntStatus) ) {
  251. (DiskpLogEvent)(
  252. resourceEntry->ResourceHandle,
  253. LOG_ERROR,
  254. L"%1!ws!, error opening %2!ws!, error %3!X!.\n",
  255. WideName, szDiskPartName, ntStatus );
  256. //
  257. // For terminate, we can't do anything else but try the next
  258. // partition.
  259. //
  260. if ( Terminate ) {
  261. continue;
  262. }
  263. (DiskpLogEvent)(
  264. resourceEntry->ResourceHandle,
  265. LOG_INFORMATION,
  266. L"%1!ws!, Opening %2!ws! with restricted access.\n",
  267. WideName, szDiskPartName );
  268. //
  269. // If creating the device handle failed for offline, try opening
  270. // the device once more with the restricted attributes.
  271. //
  272. access = SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
  273. ntStatus = DevfileOpenEx( &fileHandle, szDiskPartName, access );
  274. //
  275. // If this still fails, then try the next partition.
  276. //
  277. if ( !NT_SUCCESS(ntStatus) ) {
  278. (DiskpLogEvent)(
  279. resourceEntry->ResourceHandle,
  280. LOG_ERROR,
  281. L"%1!ws!, error opening %2!ws! (restricted access), error %3!X!.\n",
  282. WideName, szDiskPartName, ntStatus );
  283. continue;
  284. }
  285. //
  286. // Offline: created handle with restricted attributes.
  287. // Fall through and try to flush everything...
  288. //
  289. }
  290. //
  291. // Save the current partition handle and close it after the device has been
  292. // marked offline.
  293. //
  294. if ( handleArray ) {
  295. handleArray[idx] = fileHandle;
  296. }
  297. if (Offline) {
  298. DWORD retryCount;
  299. //
  300. // Flush file buffers
  301. //
  302. (DiskpLogEvent)(
  303. resourceEntry->ResourceHandle,
  304. LOG_INFORMATION,
  305. L"%1!ws!, FlushFileBuffers for %2!ws!.\n",
  306. WideName,
  307. szDiskPartName );
  308. if ( !FlushFileBuffers( fileHandle ) ) {
  309. (DiskpLogEvent)(
  310. resourceEntry->ResourceHandle,
  311. LOG_WARNING,
  312. L"%1!ws!, error flushing file buffers on device %2!ws!. Error: %3!u!.\n",
  313. WideName, szDiskPartName, GetLastError() );
  314. //
  315. // If FlushFileBuffers fails, we should still try locking
  316. // and dismounting the volume.
  317. //
  318. // Fall through...
  319. }
  320. (DiskpLogEvent)(
  321. resourceEntry->ResourceHandle,
  322. LOG_INFORMATION,
  323. L"%1!ws!, Locking volume for %2!ws!.\n",
  324. WideName,
  325. szDiskPartName );
  326. //
  327. // Lock the volume, try this twice
  328. //
  329. retryCount = 0;
  330. while ( ( retryCount < 2 ) &&
  331. !DeviceIoControl( fileHandle,
  332. FSCTL_LOCK_VOLUME,
  333. NULL,
  334. 0,
  335. NULL,
  336. 0,
  337. &bytesReturned,
  338. NULL ) ) {
  339. (DiskpLogEvent)(
  340. resourceEntry->ResourceHandle,
  341. LOG_WARNING,
  342. L"%1!ws!, Locking volume failed, error %2!u!.\n",
  343. WideName,
  344. GetLastError() );
  345. retryCount++;
  346. Sleep(600);
  347. }
  348. }
  349. //
  350. // Now Dismount the volume.
  351. //
  352. (DiskpLogEvent)(
  353. resourceEntry->ResourceHandle,
  354. LOG_INFORMATION,
  355. L"%1!ws!, Dismounting volume %2!ws!.\n", WideName, szDiskPartName);
  356. if ( !DeviceIoControl( fileHandle,
  357. FSCTL_DISMOUNT_VOLUME,
  358. NULL,
  359. 0,
  360. NULL,
  361. 0,
  362. &bytesReturned,
  363. NULL ) ) {
  364. (DiskpLogEvent)(
  365. resourceEntry->ResourceHandle,
  366. LOG_WARNING,
  367. L"%1!ws!, error dismounting volume %2!ws!. Error %3!u!.\n",
  368. WideName, szDiskPartName, GetLastError() );
  369. }
  370. (DiskpLogEvent)(
  371. resourceEntry->ResourceHandle,
  372. LOG_INFORMATION,
  373. L"%1!ws!, Dismount complete, volume %2!ws!.\n", WideName, szDiskPartName);
  374. //
  375. // Close the handle only if we couldn't allocate the handle array.
  376. //
  377. if ( !handleArray ) {
  378. (DiskpLogEvent)(
  379. resourceEntry->ResourceHandle,
  380. LOG_INFORMATION,
  381. L"%1!ws!, No handle array, closing device %2!ws!.\n",
  382. WideName,
  383. szDiskPartName );
  384. DevfileClose( fileHandle );
  385. fileHandle = INVALID_HANDLE_VALUE;
  386. }
  387. }
  388. //
  389. // Take this resource off the 'online' list.
  390. //
  391. EnterCriticalSection( &DisksLock );
  392. if ( resourceEntry->Inserted ) {
  393. RemoveEntryList( &resourceEntry->ListEntry );
  394. resourceEntry->Inserted = FALSE;
  395. if ( IsListEmpty( &DisksListHead ) ) {
  396. //
  397. // Fire Disk Terminate Event
  398. //
  399. SetEvent( DisksTerminateEvent ) ;
  400. CloseHandle( DisksTerminateEvent );
  401. DisksTerminateEvent = NULL;
  402. }
  403. }
  404. LeaveCriticalSection( &DisksLock );
  405. status = ERROR_SUCCESS;
  406. DiskCleanup( resourceEntry );
  407. //
  408. // If we have the handle array, close the handles to all the partitions. This
  409. // is done after DiskCleanup sets the disk state to offline. Issuing the lock
  410. // and keeping the handle open will prevent new mounts on the partition.
  411. //
  412. if ( handleArray ) {
  413. for ( idx = 0; idx < PartitionCount; idx++ ) {
  414. if ( handleArray[idx] ) {
  415. DevfileClose( handleArray[idx] );
  416. }
  417. }
  418. LocalFree( handleArray );
  419. }
  420. resourceEntry->Valid = FALSE;
  421. (DiskpLogEvent)(
  422. resourceEntry->ResourceHandle,
  423. LOG_INFORMATION,
  424. L"%1!ws!, Returning final error %2!u!.\n",
  425. WideName,
  426. status );
  427. return(status);
  428. } // DisksOfflineOrTerminate
  429. DWORD
  430. DisksOnlineThread(
  431. IN PCLUS_WORKER Worker,
  432. IN PDISK_RESOURCE ResourceEntry
  433. )
  434. /*++
  435. Routine Description:
  436. Brings a disk resource online.
  437. Arguments:
  438. Worker - Supplies the cluster worker context
  439. ResourceEntry - A pointer to the DISK_RESOURCE block for this resource.
  440. Returns:
  441. ERROR_SUCCESS if successful.
  442. Win32 error code on failure.
  443. --*/
  444. {
  445. DWORD status = ERROR_INVALID_PARAMETER;
  446. HANDLE fileHandle = NULL;
  447. HANDLE event = NULL;
  448. RESOURCE_STATUS resourceStatus;
  449. BOOL success;
  450. DWORD bytesReturned;
  451. UINT i, nRetries;
  452. HANDLE MountManager = 0;
  453. DWORD ntStatus;
  454. BOOL autoMountDisabled;
  455. MOUNTMGR_QUERY_AUTO_MOUNT queryAutoMount;
  456. ResUtilInitializeResourceStatus( &resourceStatus );
  457. resourceStatus.ResourceState = ClusterResourceFailed;
  458. //resourceStatus.WaitHint = 0;
  459. resourceStatus.CheckPoint = 1;
  460. //
  461. // Check if we've been here before... without offline/terminate
  462. //
  463. if ( ResourceEntry->Inserted ) {
  464. (DiskpLogEvent)(
  465. ResourceEntry->ResourceHandle,
  466. LOG_ERROR,
  467. L"Online, resource is already in online list! Skip this online request.\n");
  468. goto exit;
  469. }
  470. ResourceEntry->DiskInfo.FailStatus = 0;
  471. status = DisksOpenResourceFileHandle(ResourceEntry, L"Online", &fileHandle);
  472. if (status != ERROR_SUCCESS) {
  473. (DiskpLogEvent)(
  474. ResourceEntry->ResourceHandle,
  475. LOG_ERROR,
  476. L"Online, DisksOpenResourceFileHandle failed. Error: %1!u!\n", status);
  477. goto exit;
  478. }
  479. if( !ReservationInProgress(ResourceEntry) ) { // [GN] 209018 //
  480. #if DBG
  481. (DiskpLogEvent)(
  482. ResourceEntry->ResourceHandle,
  483. LOG_WARNING,
  484. L"DiskArbitration must be called before DisksOnline.\n");
  485. #endif
  486. // [GN] We can steal the disk while regular arbitration is in progress in another thread //
  487. // We have to do a regular arbitration, no shortcuts as the one below
  488. // status = StartPersistentReservations(ResourceEntry, fileHandle);
  489. // if ( status != ERROR_SUCCESS ) {
  490. status = DiskArbitration( ResourceEntry, DiskspClusDiskZero );
  491. // }
  492. if ( status != ERROR_SUCCESS ) {
  493. (DiskpLogEvent)(
  494. ResourceEntry->ResourceHandle,
  495. LOG_ERROR,
  496. L"Online, arbitration failed. Error: %1!u!.\n",
  497. status );
  498. status = ERROR_RESOURCE_NOT_AVAILABLE;
  499. goto exit;
  500. }
  501. }
  502. (DiskpLogEvent)(
  503. ResourceEntry->ResourceHandle,
  504. LOG_INFORMATION,
  505. L"Online, Wait for async cleanup worker thread in ClusDisk to complete. \n");
  506. //
  507. // Synchronize with async cleanup worker
  508. //
  509. {
  510. ULONG waitTimeInSeconds = 10;
  511. status = DevfileIoctl( fileHandle,
  512. IOCTL_DISK_CLUSTER_WAIT_FOR_CLEANUP,
  513. &waitTimeInSeconds, sizeof(waitTimeInSeconds),
  514. NULL, 0,
  515. &bytesReturned );
  516. if ( !NT_SUCCESS(status) ) {
  517. (DiskpLogEvent)(
  518. ResourceEntry->ResourceHandle,
  519. LOG_ERROR,
  520. L"Online, WaitForCleanup returned. Status: 0x%1!x!.\n",
  521. status );
  522. status = RtlNtStatusToDosError( status );
  523. goto exit;
  524. }
  525. }
  526. (DiskpLogEvent)(
  527. ResourceEntry->ResourceHandle,
  528. LOG_INFORMATION,
  529. L"Online, Send Offline IOCTL to all existing volumes, then Online IOCTL. \n");
  530. //
  531. // Volume snapshots require us to make sure everything is offline first.
  532. // Ignore the returned status.
  533. //
  534. GoOffline( ResourceEntry );
  535. //
  536. // Bring the device online.
  537. // we have to do this here for PartMgr poke to succeed.
  538. //
  539. status = GoOnline( ResourceEntry );
  540. if ( status != ERROR_SUCCESS ) {
  541. (DiskpLogEvent)(
  542. ResourceEntry->ResourceHandle,
  543. LOG_ERROR,
  544. L"Online, error bringing device online. Error: %1!u!.\n",
  545. status );
  546. goto exit;
  547. }
  548. (DiskpLogEvent)(
  549. ResourceEntry->ResourceHandle,
  550. LOG_INFORMATION,
  551. L"Online, Recreate volume information from cluster database. \n");
  552. //
  553. // Create volume information
  554. //
  555. status = MountieRecreateVolumeInfoFromHandle(fileHandle,
  556. ResourceEntry->DiskInfo.PhysicalDrive,
  557. ResourceEntry->ResourceHandle,
  558. &ResourceEntry->MountieInfo);
  559. if (status != ERROR_SUCCESS) {
  560. (DiskpLogEvent)(
  561. ResourceEntry->ResourceHandle,
  562. LOG_ERROR,
  563. L"Online, MountieCreateFromHandle failed. Error: %1!u!\n", status);
  564. goto exit;
  565. }
  566. //
  567. // Check that the cached signature is the same as the signature on the disk.
  568. // They can be different if the disk fails and the user replaces the failed
  569. // disk with a new disk having a different disk signature. If the user doesn't
  570. // change the signature of the new disk to be the same as the original disk,
  571. // and doesn't reboot or reinstall the disk (uninstall via devmgmt and rescan
  572. // via diskmgmt), then the disk will try to come online using the old signature.
  573. // Either online will succeed, or pnp will say the volume doesn't come online.
  574. // We don't want online to succeed because if the disk fails to another node,
  575. // it will likely fail when DoAttach is called (if disk wasn't ever online to
  576. // the other node). If the disk was previously online, then we didn't previously
  577. // verify the cached signature in DiskInfo.Params matches the disk, so now we
  578. // make that check.
  579. //
  580. if ( ResourceEntry->DiskInfo.Params.Signature &&
  581. ResourceEntry->DiskInfo.Params.Signature != ResourceEntry->MountieInfo.Volume->Signature ) {
  582. WCHAR sigStr[10];
  583. (DiskpLogEvent)(
  584. ResourceEntry->ResourceHandle,
  585. LOG_ERROR,
  586. L"Online, Cluster DB signature %1!lX! does not match PhysicalDrive%2!u! signature %3!lX!\n",
  587. ResourceEntry->DiskInfo.Params.Signature,
  588. ResourceEntry->DiskInfo.PhysicalDrive,
  589. ResourceEntry->MountieInfo.Volume->Signature );
  590. if ( SUCCEEDED( StringCchPrintf( sigStr,
  591. RTL_NUMBER_OF(sigStr),
  592. TEXT("%08lX"),
  593. ResourceEntry->DiskInfo.Params.Signature ) ) ) {
  594. ClusResLogSystemEventByKey1(ResourceEntry->ResourceKey,
  595. LOG_CRITICAL,
  596. RES_DISK_MISSING,
  597. sigStr);
  598. }
  599. status = ERROR_FILE_NOT_FOUND;
  600. goto exit;
  601. }
  602. //
  603. // Now poke partition manager
  604. //
  605. (DiskpLogEvent)(
  606. ResourceEntry->ResourceHandle,
  607. LOG_INFORMATION,
  608. L"Online, Ask partition manager to create volumes for any new partitions. \n");
  609. success = DeviceIoControl( fileHandle,
  610. IOCTL_PARTMGR_CHECK_UNCLAIMED_PARTITIONS,
  611. NULL,
  612. 0,
  613. NULL,
  614. 0,
  615. &bytesReturned,
  616. FALSE );
  617. if (!success) {
  618. status = GetLastError();
  619. (DiskpLogEvent)(
  620. ResourceEntry->ResourceHandle,
  621. LOG_ERROR,
  622. L"Online, Partmgr failed to claim all partitions. Error: %1!u!.\n",
  623. status );
  624. goto exit;
  625. }
  626. //
  627. // Before attempting to access the device, close our open handles.
  628. //
  629. CloseHandle( fileHandle );
  630. fileHandle = NULL;
  631. ntStatus = DevfileOpen(&MountManager, MOUNTMGR_DEVICE_NAME);
  632. if (!NT_SUCCESS(ntStatus)) {
  633. status = RtlNtStatusToDosError(ntStatus);
  634. (DiskpLogEvent)(
  635. ResourceEntry->ResourceHandle,
  636. LOG_ERROR,
  637. L"Online, MountMgr open failed. Error: %1!u!.\n", status);
  638. goto exit;
  639. }
  640. resourceStatus.ResourceState = ClusterResourceOnlinePending;
  641. //
  642. // Find out if volume auto mount is enabled or disabled.
  643. //
  644. if ( !DeviceIoControl( MountManager,
  645. IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT,
  646. NULL,
  647. 0,
  648. &queryAutoMount,
  649. sizeof (queryAutoMount),
  650. &bytesReturned,
  651. NULL )) {
  652. status = GetLastError();
  653. (DiskpLogEvent)(
  654. ResourceEntry->ResourceHandle,
  655. LOG_WARNING,
  656. L"Online, MountMgr unable to return automount status - assume automount disabled. Error: %1!u!.\n",
  657. status );
  658. autoMountDisabled = TRUE;
  659. } else {
  660. autoMountDisabled = (Disabled == queryAutoMount.CurrentState);
  661. }
  662. (DiskpLogEvent)(
  663. ResourceEntry->ResourceHandle,
  664. LOG_INFORMATION,
  665. L"Online, volume auto-mount is %1!ws! \n",
  666. autoMountDisabled ? L"disabled" : L"enabled" );
  667. // we will wait not more than 48 seconds per partition
  668. nRetries = 24 * ResourceEntry->MountieInfo.Volume->PartitionCount;
  669. // Don't wait for longer than 10 minutes total time //
  670. if (nRetries > 300) {
  671. nRetries = 300;
  672. }
  673. __try {
  674. // We cannot call VolumesReady because pnp might be adding this device
  675. // the same time we are trying to get the volume name. We must wait
  676. // for the volume to show up in the pnp list.
  677. //
  678. // Check the PnP thread's volume list to see if the volume arrived.
  679. // If not in the list, fall through and wait for the event to be
  680. // signaled.
  681. //
  682. // First time through the list, don't update the volume list as this
  683. // can be time consuming.
  684. //
  685. if ( IsDiskInPnpVolumeList( ResourceEntry, FALSE ) ) {
  686. (DiskpLogEvent)(
  687. ResourceEntry->ResourceHandle,
  688. LOG_INFORMATION,
  689. L"Online, disk found in PnP volume list on first attempt \n");
  690. //
  691. // VolumesReady should work now. If not, there is some other problem.
  692. //
  693. status = VolumesReadyLoop(&ResourceEntry->MountieInfo, ResourceEntry);
  694. (DiskpLogEvent)(
  695. ResourceEntry->ResourceHandle,
  696. LOG_INFORMATION,
  697. L"Online, VolumesReady returns: %1!u!. PnP informs us that all volumes exist. \n",
  698. status );
  699. __leave;
  700. }
  701. //
  702. // Create event for waiting.
  703. //
  704. event = CreateEvent( NULL, // security attributes
  705. TRUE, // manual reset
  706. FALSE, // initial state: nonsignaled
  707. NULL ); // object name
  708. if ( !event ) {
  709. status = GetLastError();
  710. (DiskpLogEvent)(
  711. ResourceEntry->ResourceHandle,
  712. LOG_ERROR,
  713. L"Online, Failed to create event for PnP notification. Error: %1!u!.\n",
  714. status );
  715. __leave;
  716. }
  717. //
  718. // Tell our PnP code we are waiting for this disk.
  719. //
  720. status = QueueWaitForVolumeEvent( event,
  721. ResourceEntry );
  722. if ( ERROR_SUCCESS != status ) {
  723. (DiskpLogEvent)(
  724. ResourceEntry->ResourceHandle,
  725. LOG_ERROR,
  726. L"Online, Failed to create event for PnP notification. Error: %1!u!.\n",
  727. status );
  728. __leave;
  729. }
  730. resourceStatus.CheckPoint += 1;
  731. (DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
  732. &resourceStatus );
  733. for (i = 0; i < nRetries; ++i) {
  734. //
  735. // Make sure we aren't terminated. We don't need to wait for pnp
  736. // in this case.
  737. //
  738. if ( ResourceEntry->OnlineThread.Terminate ) {
  739. status = ERROR_SHUTDOWN_CLUSTER;
  740. __leave;
  741. }
  742. //
  743. // Ask mountmgr to process volumes.
  744. //
  745. (DiskpLogEvent)(
  746. ResourceEntry->ResourceHandle,
  747. LOG_INFORMATION,
  748. L"Online, call MountMgr to check for new, unprocessed volumes.\n");
  749. success = DeviceIoControl( MountManager,
  750. IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES,
  751. NULL,
  752. 0,
  753. NULL,
  754. 0,
  755. &bytesReturned,
  756. FALSE );
  757. if ( !success ) {
  758. status = GetLastError();
  759. (DiskpLogEvent)(
  760. ResourceEntry->ResourceHandle,
  761. LOG_WARNING,
  762. L"Online, MountMgr failed to check unprocessed volumes. Error: %1!u!.\n",
  763. status );
  764. // The call to the mountmgr can return an error if there are
  765. // disks reserved by another node. Fall through...
  766. }
  767. //
  768. // Wait for event signal or timeout. We wait only 2 seconds
  769. // at a time so we can update the checkpoint.
  770. //
  771. (DiskpLogEvent)(
  772. ResourceEntry->ResourceHandle,
  773. LOG_INFORMATION,
  774. L"Online, waiting for notification from PnP thread that all volumes exist...\n");
  775. status = WaitForSingleObject( event,
  776. 2000 );
  777. (DiskpLogEvent)(
  778. ResourceEntry->ResourceHandle,
  779. LOG_INFORMATION,
  780. L"Online, wait for PnP notification returns %1!u!. \n", status );
  781. if ( WAIT_OBJECT_0 == status ) {
  782. //
  783. // Event was signaled, try VolumesReady one more time.
  784. // This should work or there is a more serious problem.
  785. status = VolumesReadyLoop(&ResourceEntry->MountieInfo, ResourceEntry);
  786. __leave;
  787. }
  788. if ( WAIT_TIMEOUT != status ) {
  789. status = GetLastError();
  790. __leave;
  791. }
  792. //
  793. // Force a check in the PnP thread's volume list in
  794. // case we somehow missed the volume arrival. If all
  795. // volumes are available for this disk, we are done
  796. // waiting.
  797. //
  798. if ( IsDiskInPnpVolumeList( ResourceEntry, TRUE ) ) {
  799. (DiskpLogEvent)(
  800. ResourceEntry->ResourceHandle,
  801. LOG_INFORMATION,
  802. L"Online, disk found in PnP volume list - all volumes exist. \n");
  803. //
  804. // VolumesReady should work now. If not, there is some other problem.
  805. //
  806. status = VolumesReadyLoop(&ResourceEntry->MountieInfo, ResourceEntry);
  807. __leave;
  808. }
  809. resourceStatus.CheckPoint += 1;
  810. (DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
  811. &resourceStatus );
  812. //
  813. // If auto mount is disabled in mountmgr, the volumes will
  814. // arrive from pnp in offline state. In order for our pnp
  815. // thread to read the drive layout, we have to make sure
  816. // they are online. If we online the physical disk, the
  817. // volumes associated with this disk will also be brought
  818. // online.
  819. //
  820. // Only do the clusdisk online after waiting for a "reasonable"
  821. // period of time. If we send online to clusdisk, then there is
  822. // a chance the volume may be just arriving via pnp and then
  823. // the volume might be marked as requiring a reboot. We are
  824. // going to wait for at least 6 seconds, and every 6 seconds
  825. // we will try another clusdisk online request.
  826. //
  827. //
  828. // Remove the check for autoMountDisabled. Now send the volume
  829. // offline and volume online after a "reasonable" time has passed.
  830. //
  831. if ( i && ( i % 3 == 0 ) ) {
  832. (DiskpLogEvent)(
  833. ResourceEntry->ResourceHandle,
  834. LOG_INFORMATION,
  835. L"Online, send IOCTL to mark all volumes online.\n" );
  836. GoOffline( ResourceEntry );
  837. status = GoOnline( ResourceEntry );
  838. if ( status != ERROR_SUCCESS ) {
  839. (DiskpLogEvent)(
  840. ResourceEntry->ResourceHandle,
  841. LOG_WARNING,
  842. L"Online, error bringing device online. Error: %1!u!.\n",
  843. status );
  844. status = ERROR_SUCCESS;
  845. }
  846. }
  847. }
  848. } __finally {
  849. //
  850. // Tell our PnP code that we are no longer waiting. Not a
  851. // problem if we never queued the request previously.
  852. //
  853. RemoveWaitForVolumeEvent( ResourceEntry );
  854. if ( event ) {
  855. CloseHandle( event );
  856. }
  857. }
  858. DevfileClose(MountManager);
  859. resourceStatus.ResourceState = ClusterResourceFailed;
  860. if (status != ERROR_SUCCESS) {
  861. (DiskpLogEvent)(
  862. ResourceEntry->ResourceHandle,
  863. LOG_ERROR,
  864. L"Online, volumes not ready. Error: %1!u!.\n",
  865. status );
  866. goto exit;
  867. }
  868. (DiskpLogEvent)(
  869. ResourceEntry->ResourceHandle,
  870. LOG_INFORMATION,
  871. L"Online, all volumes for this disk exist.\n");
  872. //
  873. // Need to synchronize with ClusDisk by issuing
  874. // IOCTL_CLUSTER_VOLUME_TEST on all partitions
  875. //
  876. // Need to read volume type. If it is RAW, we need to
  877. // dismount and mount it again as a workaround for 389861
  878. //
  879. resourceStatus.ResourceState = ClusterResourceOnline;
  880. ResourceEntry->Valid = TRUE;
  881. //
  882. // Start the registry notification thread, if needed.
  883. //
  884. EnterCriticalSection( &DisksLock );
  885. if ( IsListEmpty( &DisksListHead ) ) {
  886. DisksTerminateEvent = CreateEventW( NULL,
  887. TRUE,
  888. FALSE,
  889. NULL );
  890. if ( DisksTerminateEvent == NULL ) {
  891. status = GetLastError();
  892. (DiskpLogEvent)(
  893. ResourceEntry->ResourceHandle,
  894. LOG_ERROR,
  895. L"Online, error %1!d! creating registry thread terminate event\n",
  896. status);
  897. LeaveCriticalSection( &DisksLock );
  898. goto exit;
  899. }
  900. (DiskpLogEvent)(
  901. ResourceEntry->ResourceHandle,
  902. LOG_INFORMATION,
  903. L"Online, created registry thread terminate event \n");
  904. }
  905. ResourceEntry->Inserted = TRUE;
  906. InsertTailList( &DisksListHead, &ResourceEntry->ListEntry );
  907. LeaveCriticalSection( &DisksLock );
  908. DiskspSsyncDiskInfo(L"Online", ResourceEntry, MOUNTIE_VALID | MOUNTIE_THREAD );
  909. (DiskpLogEvent)(
  910. ResourceEntry->ResourceHandle,
  911. LOG_INFORMATION,
  912. L"Online, Wait for all volume GUIDs and drive letters to be accessible and mounted. \n");
  913. status = WaitForVolumes( ResourceEntry,
  914. 30 // seconds timeout //
  915. );
  916. //
  917. // We cannot just check for NO_ERROR status. The problem is that WaitForVolumes
  918. // calls GetFileAttributes, which can return ERROR_DISK_CORRUPT (1393). If the
  919. // disk is corrupt, we should fall through and let chkdsk try to clean it up.
  920. // We should also do this if ERROR_FILE_CORRUPT (1392) is returned (haven't
  921. // yet seen file corrupt error returned from GetFileAttributes, but one never knows).
  922. //
  923. if ( NO_ERROR != status && ERROR_DISK_CORRUPT != status && ERROR_FILE_CORRUPT != status ) {
  924. ClusResLogSystemEventByKey( ResourceEntry->ResourceKey,
  925. LOG_CRITICAL,
  926. RES_DISK_MOUNT_FAILED );
  927. (DiskpLogEvent)(
  928. ResourceEntry->ResourceHandle,
  929. LOG_ERROR,
  930. L"Online, error waiting for file system to mount. Error: %1!u!.\n",
  931. status );
  932. resourceStatus.ResourceState = ClusterResourceFailed;
  933. ResourceEntry->Valid = FALSE;
  934. EnterCriticalSection( &DisksLock );
  935. CL_ASSERT ( ResourceEntry->Inserted );
  936. ResourceEntry->Inserted = FALSE;
  937. RemoveEntryList( &ResourceEntry->ListEntry );
  938. if ( IsListEmpty( &DisksListHead ) ) {
  939. //
  940. // Fire Disk Terminate Event
  941. //
  942. SetEvent( DisksTerminateEvent ) ;
  943. CloseHandle( DisksTerminateEvent );
  944. DisksTerminateEvent = NULL;
  945. }
  946. LeaveCriticalSection( &DisksLock );
  947. goto exit;
  948. }
  949. status = DisksMountDrives( &ResourceEntry->DiskInfo,
  950. ResourceEntry,
  951. ResourceEntry->DiskInfo.Params.Signature );
  952. if ( status != ERROR_SUCCESS ) {
  953. ClusResLogSystemEventByKey( ResourceEntry->ResourceKey,
  954. LOG_CRITICAL,
  955. RES_DISK_MOUNT_FAILED );
  956. (DiskpLogEvent)(
  957. ResourceEntry->ResourceHandle,
  958. LOG_ERROR,
  959. L"Online, error mounting disk or creating share names for disk. Error: %1!u!.\n",
  960. status );
  961. resourceStatus.ResourceState = ClusterResourceFailed;
  962. ResourceEntry->Valid = FALSE;
  963. EnterCriticalSection( &DisksLock );
  964. CL_ASSERT ( ResourceEntry->Inserted );
  965. ResourceEntry->Inserted = FALSE;
  966. RemoveEntryList( &ResourceEntry->ListEntry );
  967. if ( IsListEmpty( &DisksListHead ) ) {
  968. //
  969. // Fire Disk Terminate Event
  970. //
  971. SetEvent( DisksTerminateEvent ) ;
  972. CloseHandle( DisksTerminateEvent );
  973. DisksTerminateEvent = NULL;
  974. }
  975. LeaveCriticalSection( &DisksLock );
  976. }
  977. if ( ERROR_SUCCESS == status ) {
  978. LPWSTR newSerialNumber = NULL;
  979. DWORD newSerNumLen = 0;
  980. DWORD oldSerNumLen = 0;
  981. WCHAR deviceName[64];
  982. (DiskpLogEvent)(
  983. ResourceEntry->ResourceHandle,
  984. LOG_INFORMATION,
  985. L"Online, Insure mount point information is correct.\n" );
  986. DisksProcessMountPointInfo( ResourceEntry );
  987. (VOID) StringCchPrintf( deviceName,
  988. RTL_NUMBER_OF( deviceName ),
  989. TEXT("\\\\.\\PhysicalDrive%d"),
  990. ResourceEntry->DiskInfo.PhysicalDrive );
  991. fileHandle = CreateFile( deviceName,
  992. GENERIC_READ | GENERIC_WRITE,
  993. FILE_SHARE_READ | FILE_SHARE_WRITE,
  994. NULL,
  995. OPEN_EXISTING,
  996. 0,
  997. NULL );
  998. if ( INVALID_HANDLE_VALUE == fileHandle ||
  999. NULL == fileHandle ) {
  1000. status = GetLastError();
  1001. (DiskpLogEvent)(
  1002. ResourceEntry->ResourceHandle,
  1003. LOG_ERROR,
  1004. L"Online, error opening disk to retrieve serial number. Error: %1!u!.\n",
  1005. status );
  1006. } else {
  1007. // Retrieve and validate serial number.
  1008. (DiskpLogEvent)(
  1009. ResourceEntry->ResourceHandle,
  1010. LOG_INFORMATION,
  1011. L"Online, Retrieve and validate the disk serial number.\n" );
  1012. status = RetrieveSerialNumber( fileHandle,
  1013. &newSerialNumber );
  1014. CloseHandle( fileHandle );
  1015. fileHandle = NULL;
  1016. if ( NO_ERROR == status && newSerialNumber ) {
  1017. newSerNumLen = wcslen( newSerialNumber );
  1018. if ( ResourceEntry->DiskInfo.Params.SerialNumber ) {
  1019. oldSerNumLen = wcslen( ResourceEntry->DiskInfo.Params.SerialNumber );
  1020. }
  1021. (DiskpLogEvent)(
  1022. ResourceEntry->ResourceHandle,
  1023. LOG_INFORMATION,
  1024. L"Online, Old SerNum (%1!ws!) Old SerNumLen (%2!d!) \n",
  1025. ResourceEntry->DiskInfo.Params.SerialNumber,
  1026. oldSerNumLen
  1027. );
  1028. (DiskpLogEvent)(
  1029. ResourceEntry->ResourceHandle,
  1030. LOG_INFORMATION,
  1031. L"Online, New SerNum (%1!ws!) New SerNumLen (%2!d!) \n",
  1032. newSerialNumber,
  1033. newSerNumLen
  1034. );
  1035. if ( 0 == oldSerNumLen ||
  1036. NULL == ResourceEntry->DiskInfo.Params.SerialNumber ||
  1037. newSerNumLen != oldSerNumLen ||
  1038. ( 0 != wcsncmp( ResourceEntry->DiskInfo.Params.SerialNumber,
  1039. newSerialNumber,
  1040. newSerNumLen ) ) ) {
  1041. // Need to log an error?
  1042. (DiskpLogEvent)(
  1043. ResourceEntry->ResourceHandle,
  1044. LOG_WARNING,
  1045. L"Online, disk serial number has changed. Saving new serial number.\n" );
  1046. // Free existing serial number and update serial number.
  1047. if ( ResourceEntry->DiskInfo.Params.SerialNumber ) {
  1048. LocalFree( ResourceEntry->DiskInfo.Params.SerialNumber );
  1049. }
  1050. ResourceEntry->DiskInfo.Params.SerialNumber = newSerialNumber;
  1051. newSerialNumber = NULL;
  1052. // User MP thread to post info into registry.
  1053. PostMPInfoIntoRegistry( ResourceEntry );
  1054. }
  1055. }
  1056. }
  1057. if ( newSerialNumber ) {
  1058. LocalFree( newSerialNumber );
  1059. }
  1060. // Reset status to success so online completes.
  1061. status = ERROR_SUCCESS;
  1062. }
  1063. exit:
  1064. (DiskpLogEvent)(
  1065. ResourceEntry->ResourceHandle,
  1066. LOG_INFORMATION,
  1067. L"Online, setting ResourceState %1!u! .\n",
  1068. resourceStatus.ResourceState );
  1069. (DiskpSetResourceStatus)(ResourceEntry->ResourceHandle,
  1070. &resourceStatus );
  1071. if ( fileHandle != NULL) {
  1072. CloseHandle( fileHandle );
  1073. }
  1074. if (status == ERROR_SUCCESS) {
  1075. WatchDisk(ResourceEntry);
  1076. }
  1077. (DiskpLogEvent)(
  1078. ResourceEntry->ResourceHandle,
  1079. LOG_INFORMATION,
  1080. L"Online, returning final error %1!u! ResourceState %2!u! Valid %3!x! \n",
  1081. status,
  1082. resourceStatus.ResourceState,
  1083. ResourceEntry->Valid );
  1084. return(status);
  1085. } // DisksOnlineThread
  1086. DWORD
  1087. DisksOpenResourceFileHandle(
  1088. IN PDISK_RESOURCE ResourceEntry,
  1089. IN PWCHAR InfoString,
  1090. OUT PHANDLE fileHandle OPTIONAL
  1091. )
  1092. /*++
  1093. Routine Description:
  1094. Open a file handle for the resource.
  1095. It performs the following steps:
  1096. 1. Read Disk signature from cluster registry
  1097. 2. Attaches clusdisk driver to a disk with this signature
  1098. 3. Gets Harddrive no from ClusDisk driver
  1099. 4. Opens \\.\PhysicalDrive%d device and returns the handle
  1100. Arguments:
  1101. ResourceEntry - A pointer to the DISK_RESOURCE block for this resource.
  1102. InfoString - Supplies a label to be printed with error messages
  1103. fileHandle - receives file handle
  1104. Returns:
  1105. ERROR_SUCCESS if successful.
  1106. Win32 error code on failure.
  1107. --*/
  1108. {
  1109. DWORD status;
  1110. WCHAR deviceName[MAX_PATH];
  1111. HKEY signatureKey = NULL;
  1112. PWCHAR diskName;
  1113. DWORD count;
  1114. LPWSTR nameOfPropInError;
  1115. WCHAR resourceString[MAX_PATH];
  1116. (DiskpLogEvent)(
  1117. ResourceEntry->ResourceHandle,
  1118. LOG_INFORMATION,
  1119. L"[DiskArb]------- Disks%1!ws! -------.\n", InfoString);
  1120. //
  1121. // Read our disk signature from the resource parameters.
  1122. //
  1123. status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ResourceParametersKey,
  1124. DiskResourcePrivateProperties,
  1125. (LPBYTE) &ResourceEntry->DiskInfo.Params,
  1126. TRUE, // CheckForRequiredProperties
  1127. &nameOfPropInError );
  1128. if ( status != ERROR_SUCCESS ) {
  1129. (DiskpLogEvent)(
  1130. ResourceEntry->ResourceHandle,
  1131. LOG_ERROR,
  1132. L"%3!ws!: Unable to read the '%1' property from cluster database. Error: %2!u!.\n",
  1133. (nameOfPropInError == NULL ? L"" : nameOfPropInError),
  1134. status, InfoString );
  1135. return(status);
  1136. }
  1137. (DiskpLogEvent)(
  1138. ResourceEntry->ResourceHandle,
  1139. LOG_INFORMATION,
  1140. L"[DiskArb] DisksOpenResourceFileHandle: Attaching to disk with signature %1!x! \n",
  1141. ResourceEntry->DiskInfo.Params.Signature
  1142. );
  1143. //
  1144. // Try to attach to this device.
  1145. //
  1146. status = DoAttach( ResourceEntry->DiskInfo.Params.Signature,
  1147. ResourceEntry->ResourceHandle,
  1148. FALSE ); // Offline, then dismount
  1149. if ( status != ERROR_SUCCESS ) {
  1150. WCHAR Signature[9];
  1151. (DiskpLogEvent)(
  1152. ResourceEntry->ResourceHandle,
  1153. LOG_ERROR,
  1154. L"%3!ws!: Unable to attach to signature %1!lx!. Error: %2!u!.\n",
  1155. ResourceEntry->DiskInfo.Params.Signature,
  1156. status, InfoString );
  1157. // Added this event message back in as replication has been changed (regroup
  1158. // shouldn't be blocked now).
  1159. if ( SUCCEEDED( StringCchPrintf( Signature,
  1160. RTL_NUMBER_OF(Signature),
  1161. TEXT("%08lX"),
  1162. ResourceEntry->DiskInfo.Params.Signature ) ) ) {
  1163. ClusResLogSystemEventByKey1(ResourceEntry->ResourceKey,
  1164. LOG_CRITICAL,
  1165. RES_DISK_MISSING,
  1166. Signature);
  1167. }
  1168. return(status);
  1169. }
  1170. //
  1171. // Now open the signature key under ClusDisk.
  1172. //
  1173. (DiskpLogEvent)(
  1174. ResourceEntry->ResourceHandle,
  1175. LOG_INFORMATION,
  1176. L"[DiskArb] DisksOpenResourceFileHandle: Retrieving disk number from ClusDisk registry key \n" );
  1177. if ( FAILED( StringCchPrintf( resourceString,
  1178. RTL_NUMBER_OF(resourceString),
  1179. TEXT("%08lX"),
  1180. ResourceEntry->DiskInfo.Params.Signature ) ) ) {
  1181. return ERROR_INSUFFICIENT_BUFFER;
  1182. }
  1183. status = RegOpenKeyEx( ResourceEntry->ClusDiskParametersKey,
  1184. resourceString,
  1185. 0,
  1186. KEY_READ,
  1187. &signatureKey );
  1188. if ( status != ERROR_SUCCESS ) {
  1189. (DiskpLogEvent)(
  1190. ResourceEntry->ResourceHandle,
  1191. LOG_ERROR,
  1192. L"%3!ws!: Unable to open ClusDisk signature key %1!lx!. Error: %2!u!.\n",
  1193. ResourceEntry->DiskInfo.Params.Signature,
  1194. status, InfoString );
  1195. return(status);
  1196. }
  1197. //
  1198. // Read our disk name from ClusDisk.
  1199. //
  1200. diskName = GetRegParameter(signatureKey, L"DiskName");
  1201. RegCloseKey( signatureKey );
  1202. if ( diskName == NULL ) {
  1203. (DiskpLogEvent)(
  1204. ResourceEntry->ResourceHandle,
  1205. LOG_ERROR,
  1206. L"%1!ws!: Unable to read disk name.\n", InfoString );
  1207. return(ERROR_INVALID_PARAMETER);
  1208. }
  1209. //
  1210. // Parse disk name to find disk drive number.
  1211. //
  1212. count = swscanf( diskName, DEVICE_HARDDISK, &ResourceEntry->DiskInfo.PhysicalDrive );
  1213. // count is zero if failed! Otherwise count of parsed values.
  1214. LocalFree(diskName);
  1215. if (count == 0) {
  1216. (DiskpLogEvent)(
  1217. ResourceEntry->ResourceHandle,
  1218. LOG_ERROR,
  1219. L"%1!ws!: Unable to parse disk name.\n", InfoString );
  1220. return ERROR_INVALID_PARAMETER;
  1221. }
  1222. //
  1223. // Build device string for CreateFile
  1224. //
  1225. (DiskpLogEvent)(
  1226. ResourceEntry->ResourceHandle,
  1227. LOG_INFORMATION,
  1228. L"[DiskArb] DisksOpenResourceFileHandle: Retrieving handle to PhysicalDrive%1!u! \n",
  1229. ResourceEntry->DiskInfo.PhysicalDrive );
  1230. if ( fileHandle ) {
  1231. (VOID) StringCchPrintf( deviceName,
  1232. RTL_NUMBER_OF( deviceName ),
  1233. TEXT("\\\\.\\PhysicalDrive%d"),
  1234. ResourceEntry->DiskInfo.PhysicalDrive );
  1235. *fileHandle = CreateFile( deviceName,
  1236. GENERIC_READ | GENERIC_WRITE,
  1237. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1238. NULL,
  1239. OPEN_EXISTING,
  1240. 0,
  1241. NULL );
  1242. if ( (*fileHandle == INVALID_HANDLE_VALUE) ||
  1243. (*fileHandle == NULL) ) {
  1244. status = GetLastError();
  1245. (DiskpLogEvent)(
  1246. ResourceEntry->ResourceHandle,
  1247. LOG_ERROR,
  1248. L"%3!ws!: error opening device '%1!ws!'. Error: %2!u!\n",
  1249. deviceName,
  1250. status, InfoString );
  1251. return(status);
  1252. }
  1253. }
  1254. (DiskpLogEvent)(
  1255. ResourceEntry->ResourceHandle,
  1256. LOG_INFORMATION,
  1257. L"[DiskArb] DisksOpenResourceFileHandle: Returns success.\n" );
  1258. return(ERROR_SUCCESS);
  1259. } // DisksOpenResourceFileHandle
  1260. DWORD
  1261. DiskspSsyncDiskInfo(
  1262. IN PWCHAR InfoLabel,
  1263. IN PDISK_RESOURCE ResourceEntry,
  1264. IN DWORD Options
  1265. )
  1266. /*++
  1267. Routine Description:
  1268. Restores the disk registry information
  1269. if necessary.
  1270. Arguments:
  1271. InfoLabel - Supplies a label to be printed with error messages
  1272. ResourceEntry - Supplies the disk resource structure.
  1273. Options - 0 or combination of the following:
  1274. MOUNTIE_VALID: ResourceEntry contains up to date MountieInfo.
  1275. If this flag is not set, MountieInfo will be recomputed
  1276. MOUNTIE_THREAD: If ERROR_SHARING_PAUSED prevents updating cluster registry,
  1277. launch a thread to do it later
  1278. MOUNTIE_QUIET: Quiet mode. Less noise in logs.
  1279. Return Value:
  1280. ERROR_SUCCESS if successful
  1281. Win32 error code otherwise
  1282. --*/
  1283. {
  1284. DWORD status;
  1285. DWORD errorLevel;
  1286. (DiskpLogEvent)(
  1287. ResourceEntry->ResourceHandle,
  1288. LOG_INFORMATION,
  1289. L"%1!ws!: Update disk registry information. \n",
  1290. InfoLabel );
  1291. if ( !(Options & MOUNTIE_VALID) ) {
  1292. HANDLE fileHandle;
  1293. status = DisksOpenResourceFileHandle(ResourceEntry, InfoLabel, &fileHandle);
  1294. if (status != ERROR_SUCCESS) {
  1295. return status;
  1296. }
  1297. status = MountieRecreateVolumeInfoFromHandle(fileHandle,
  1298. ResourceEntry->DiskInfo.PhysicalDrive,
  1299. ResourceEntry->ResourceHandle,
  1300. &ResourceEntry->MountieInfo);
  1301. CloseHandle(fileHandle);
  1302. if (status != ERROR_SUCCESS) {
  1303. if ( !(Options & MOUNTIE_QUIET) ) {
  1304. (DiskpLogEvent)(
  1305. ResourceEntry->ResourceHandle,
  1306. LOG_ERROR,
  1307. L"%1!ws!: MountieCreateFromHandle failed, error: %2!u!\n",
  1308. InfoLabel, status );
  1309. }
  1310. return status;
  1311. }
  1312. }
  1313. status = MountieVerify(&ResourceEntry->MountieInfo, ResourceEntry, FALSE);
  1314. if (status != ERROR_SUCCESS) {
  1315. if ( !(Options & MOUNTIE_QUIET) ) {
  1316. if ( !DisksGetLettersForSignature( ResourceEntry ) ) {
  1317. // No drive letters, we are using mount points and this is not an error.
  1318. errorLevel = LOG_WARNING;
  1319. } else {
  1320. // Drive letters exist, this is likely an error.
  1321. errorLevel = LOG_ERROR;
  1322. }
  1323. (DiskpLogEvent)(
  1324. ResourceEntry->ResourceHandle,
  1325. errorLevel,
  1326. L"%1!ws!: MountieVerify failed, error: %2!u! \n",
  1327. InfoLabel, status );
  1328. }
  1329. }
  1330. status = MountieUpdate(&ResourceEntry->MountieInfo, ResourceEntry);
  1331. if (status != ERROR_SUCCESS) {
  1332. if (status != ERROR_SHARING_PAUSED) {
  1333. if ( !(Options & MOUNTIE_QUIET) ) {
  1334. (DiskpLogEvent)(
  1335. ResourceEntry->ResourceHandle,
  1336. LOG_ERROR,
  1337. L"%1!ws!: MountieUpdate failed, error: %2!u!\n",
  1338. InfoLabel, status );
  1339. }
  1340. return status;
  1341. }
  1342. if ( Options & MOUNTIE_THREAD ) {
  1343. if ( InterlockedCompareExchange(
  1344. &ResourceEntry->MountieInfo.UpdateThreadIsActive,
  1345. 1, 0)
  1346. )
  1347. {
  1348. (DiskpLogEvent)(
  1349. ResourceEntry->ResourceHandle,
  1350. LOG_ERROR,
  1351. L"%1!ws!: update thread is already running.\n",
  1352. InfoLabel );
  1353. status = ERROR_ALREADY_EXISTS;
  1354. } else {
  1355. HANDLE thread;
  1356. DWORD threadId;
  1357. thread = CreateThread( NULL,
  1358. 0,
  1359. DisksSetDiskInfoThread,
  1360. ResourceEntry,
  1361. 0,
  1362. &threadId );
  1363. if ( thread == NULL ) {
  1364. status = GetLastError();
  1365. if ( !(Options & MOUNTIE_QUIET) ) {
  1366. (DiskpLogEvent)(
  1367. ResourceEntry->ResourceHandle,
  1368. LOG_ERROR,
  1369. L"%1!ws!: CreateThread failed, error: %2!u!\n",
  1370. InfoLabel, status );
  1371. }
  1372. InterlockedExchange(
  1373. &ResourceEntry->MountieInfo.UpdateThreadIsActive, 0);
  1374. } else {
  1375. CloseHandle( thread );
  1376. status = ERROR_SUCCESS;
  1377. }
  1378. }
  1379. }
  1380. }
  1381. return status;
  1382. } // DiskspSsyncDiskInfo
  1383. DWORD
  1384. DisksIsVolumeDirty(
  1385. IN PWCHAR DeviceName,
  1386. IN PDISK_RESOURCE ResourceEntry,
  1387. OUT PBOOL Dirty
  1388. )
  1389. /*++
  1390. Routine Description:
  1391. This routine opens the given nt drive and sends down
  1392. FSCTL_IS_VOLUME_DIRTY to determine the state of that volume's
  1393. dirty bit.
  1394. Arguments:
  1395. DeviceName -- name of the form:
  1396. \Device\HarddiskX\PartitionY [Note: no trailing backslash]
  1397. Dirty -- receives TRUE if the dirty bit is set
  1398. Return Value:
  1399. dos error code
  1400. --*/
  1401. {
  1402. DWORD status;
  1403. NTSTATUS ntStatus;
  1404. HANDLE fileHandle;
  1405. DWORD result = 0;
  1406. DWORD bytesReturned;
  1407. ntStatus = DevfileOpen( &fileHandle, DeviceName );
  1408. if ( !NT_SUCCESS(ntStatus) ) {
  1409. (DiskpLogEvent)(
  1410. ResourceEntry->ResourceHandle,
  1411. LOG_ERROR,
  1412. L"Error opening %1!ws!, error %2!x!.\n",
  1413. DeviceName, ntStatus );
  1414. return RtlNtStatusToDosError(ntStatus);
  1415. }
  1416. status = ERROR_SUCCESS;
  1417. if ( !DeviceIoControl( fileHandle,
  1418. FSCTL_IS_VOLUME_DIRTY,
  1419. NULL,
  1420. 0,
  1421. &result,
  1422. sizeof(result),
  1423. &bytesReturned,
  1424. NULL ) ) {
  1425. status = GetLastError();
  1426. (DiskpLogEvent)(
  1427. ResourceEntry->ResourceHandle,
  1428. LOG_ERROR,
  1429. L"FSCTL_IS_VOLUME_DIRTY for volume %1!ws! returned error %2!u!.\n",
  1430. DeviceName, status );
  1431. }
  1432. DevfileClose( fileHandle );
  1433. if ( status != ERROR_SUCCESS ) {
  1434. return status;
  1435. }
  1436. if (result & VOLUME_IS_DIRTY) {
  1437. (DiskpLogEvent)(
  1438. ResourceEntry->ResourceHandle,
  1439. LOG_INFORMATION,
  1440. L"DisksIsVolumeDirty: Volume is dirty \n");
  1441. *Dirty = TRUE;
  1442. } else {
  1443. (DiskpLogEvent)(
  1444. ResourceEntry->ResourceHandle,
  1445. LOG_INFORMATION,
  1446. L"DisksIsVolumeDirty: Volume is clean \n");
  1447. *Dirty = FALSE;
  1448. }
  1449. return ERROR_SUCCESS;
  1450. } // DisksIsVolumeDirty
  1451. #define IS_SPECIAL_DIR(x) ( (x)[0]=='.' && ( (x)[1]==0 || ( (x)[1]=='.'&& (x)[2] == 0) ) )
  1452. #define FA_SUPER_HIDDEN (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)
  1453. #define IS_SUPER_HIDDEN(x) ( ((x) & FA_SUPER_HIDDEN) == FA_SUPER_HIDDEN )
  1454. #define IS_REPARSE_POINT(x) ( (x) & FILE_ATTRIBUTE_REPARSE_POINT )
  1455. #define DISKP_CHECK_PATH_BUF_SIZE (MAX_PATH)
  1456. typedef struct _DISKP_CHECK_PATH_DATA {
  1457. WCHAR FileName[DISKP_CHECK_PATH_BUF_SIZE];
  1458. WIN32_FIND_DATAW FindData;
  1459. PDISK_RESOURCE ResourceEntry;
  1460. BOOL OpenFiles;
  1461. DWORD FileCount;
  1462. DWORD Level;
  1463. BOOL LogFileNotFound;
  1464. } DISKP_CHECK_PATH_DATA, *PDISKP_CHECK_PATH_DATA;
  1465. DWORD
  1466. DiskspCheckPathInternal(
  1467. IN OUT PDISKP_CHECK_PATH_DATA data,
  1468. IN DWORD FileNameLength
  1469. )
  1470. /*++
  1471. Routine Description:
  1472. Checks out a disk partition to see if the filesystem has mounted
  1473. it and it's working.
  1474. Arguments:
  1475. data - Filled in structure for checking the volume.
  1476. FileNameLength - Number of characters (not including trailing NULL) in
  1477. data->FileName.
  1478. VolumeGUID names always end in a trailing backslash.
  1479. Return Value:
  1480. --*/
  1481. {
  1482. HANDLE FindHandle;
  1483. DWORD Status;
  1484. DWORD len;
  1485. DWORD adjust;
  1486. if ( FileNameLength < 1 ) {
  1487. return ERROR_INVALID_DATA;
  1488. }
  1489. data->FileName[FileNameLength] = 0;
  1490. //
  1491. // GetFileAttributes must use a trailing slash on the Volume{GUID} name.
  1492. //
  1493. Status = GetFileAttributesW(data->FileName);
  1494. if (Status == 0xFFFFFFFF) {
  1495. Status = GetLastError();
  1496. (DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
  1497. LOG_ERROR,
  1498. L"DiskspCheckPath: GetFileAttrs(%1!s!) returned status of %2!d!.\n",
  1499. data->FileName,
  1500. Status);
  1501. return Status;
  1502. }
  1503. Status = ERROR_SUCCESS;
  1504. if ( data->FileName[FileNameLength - 1] == L'\\' ) {
  1505. //
  1506. // If path ends in backslash, simply add the asterisk.
  1507. //
  1508. if ( FileNameLength + 1 >= DISKP_CHECK_PATH_BUF_SIZE ) {
  1509. (DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
  1510. LOG_ERROR,
  1511. L"DiskspCheckPath: FileNameLength > buffer size (#1) \n" );
  1512. return(ERROR_ALLOTTED_SPACE_EXCEEDED);
  1513. }
  1514. data->FileName[FileNameLength + 0] = '*';
  1515. data->FileName[FileNameLength + 1] = 0;
  1516. adjust = 0;
  1517. } else {
  1518. if ( FileNameLength + 2 >= DISKP_CHECK_PATH_BUF_SIZE ) {
  1519. (DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
  1520. LOG_ERROR,
  1521. L"DiskspCheckPath: FileNameLength > buffer size (#2) \n" );
  1522. return(ERROR_ALLOTTED_SPACE_EXCEEDED);
  1523. }
  1524. data->FileName[FileNameLength + 0] = '\\';
  1525. data->FileName[FileNameLength + 1] = '*';
  1526. data->FileName[FileNameLength + 2] = 0;
  1527. adjust = 1;
  1528. }
  1529. FindHandle = FindFirstFileW(data->FileName, &data->FindData);
  1530. if (FindHandle == INVALID_HANDLE_VALUE) {
  1531. Status = GetLastError();
  1532. if (Status != ERROR_FILE_NOT_FOUND || data->LogFileNotFound) {
  1533. (DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
  1534. LOG_WARNING,
  1535. L"DiskspCheckPath: fff(%1!s!) returned status of %2!d!.\n",
  1536. data->FileName,
  1537. Status);
  1538. }
  1539. if (Status == ERROR_FILE_NOT_FOUND) {
  1540. Status = ERROR_EMPTY;
  1541. }
  1542. return Status;
  1543. }
  1544. ++ data->Level;
  1545. ++ data->FileCount;
  1546. if (data->OpenFiles) {
  1547. do {
  1548. if ( data->ResourceEntry->OnlineThread.Terminate ) {
  1549. // Returning SUCCESS means that we've closed all
  1550. // FindFile handles.
  1551. return(ERROR_SHUTDOWN_CLUSTER);
  1552. }
  1553. if ( IS_SPECIAL_DIR(data->FindData.cFileName )
  1554. || IS_SUPER_HIDDEN(data->FindData.dwFileAttributes)
  1555. || IS_REPARSE_POINT( data->FindData.dwFileAttributes ) )
  1556. {
  1557. continue;
  1558. }
  1559. len = wcslen(data->FindData.cFileName);
  1560. if (FileNameLength + len + 1 >= DISKP_CHECK_PATH_BUF_SIZE ) {
  1561. return(ERROR_ALLOTTED_SPACE_EXCEEDED);
  1562. }
  1563. MoveMemory(data->FileName + FileNameLength + adjust,
  1564. data->FindData.cFileName,
  1565. sizeof(WCHAR) * (len + 1) );
  1566. if ( data->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
  1567. Status = DiskspCheckPathInternal(data, FileNameLength + len + adjust);
  1568. if (Status != ERROR_SUCCESS) {
  1569. goto exit;
  1570. }
  1571. } else {
  1572. HANDLE FileHandle;
  1573. //
  1574. // Open with the same parameters that LogpCreate uses to try to catch quorum
  1575. // log corruption during online.
  1576. //
  1577. // We previously used OPEN_EXISTING parameter. Try OPEN_ALWAYS to match exactly what
  1578. // LogpCreate is using.
  1579. //
  1580. FileHandle = CreateFileW(data->FileName,
  1581. GENERIC_READ | GENERIC_WRITE,
  1582. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1583. NULL,
  1584. OPEN_ALWAYS,
  1585. FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
  1586. NULL );
  1587. if ( FileHandle == INVALID_HANDLE_VALUE ) {
  1588. Status = GetLastError();
  1589. (DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
  1590. LOG_ERROR,
  1591. L"DiskspCheckPath: Open %1!ws! failed, status %2!d!.\n",
  1592. data->FileName,
  1593. Status
  1594. );
  1595. if (Status != ERROR_SHARING_VIOLATION) {
  1596. goto exit;
  1597. }
  1598. } else {
  1599. (DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
  1600. LOG_INFORMATION,
  1601. L"DiskspCheckPath: Open %1!ws! succeeded. \n",
  1602. data->FileName
  1603. );
  1604. CloseHandle( FileHandle );
  1605. }
  1606. }
  1607. ++(data->FileCount);
  1608. } while ( FindNextFileW( FindHandle, &data->FindData ) );
  1609. --(data->FileCount);
  1610. Status = GetLastError();
  1611. if (Status != ERROR_NO_MORE_FILES) {
  1612. (DiskpLogEvent)(data->ResourceEntry->ResourceHandle,
  1613. LOG_ERROR,
  1614. L"DiskspCheckPath: fnf(%1!s!) returned status of %2!d!.\n",
  1615. data->FileName,
  1616. Status);
  1617. } else {
  1618. Status = ERROR_SUCCESS;
  1619. }
  1620. }
  1621. exit:
  1622. FindClose(FindHandle);
  1623. --(data->Level);
  1624. return Status;
  1625. } // DiskspCheckPathInternal
  1626. DWORD
  1627. DiskspCheckPath(
  1628. IN LPWSTR VolumeName,
  1629. IN PDISK_RESOURCE ResourceEntry,
  1630. IN BOOL OpenFiles,
  1631. IN BOOL LogFileNotFound
  1632. )
  1633. /*++
  1634. Routine Description:
  1635. Checks out a drive letter to see if the filesystem has mounted
  1636. it and it's working. We will also run CHKDSK if the partition/certain
  1637. files are Corrupt
  1638. Arguments:
  1639. VolumeName - Supplies the device name of the form:
  1640. \\?\Volume{GUID}\ [Note trailing backslash!]
  1641. ResourceEntry - Supplies a pointer to the disk resource entry.
  1642. OpenFiles - Span subdirectories and open files if TRUE.
  1643. Online - FILE_NOT_FOUND error is logged if TRUE
  1644. Return Value:
  1645. ERROR_SUCCESS if no corruption or corruption was found and corrected.
  1646. Win32 error code otherwise
  1647. --*/
  1648. {
  1649. DISKP_CHECK_PATH_DATA data;
  1650. DWORD len;
  1651. DWORD status;
  1652. ZeroMemory( &data, sizeof(data) );
  1653. data.OpenFiles = OpenFiles;
  1654. data.LogFileNotFound = LogFileNotFound;
  1655. data.ResourceEntry = ResourceEntry;
  1656. len = wcslen( VolumeName );
  1657. if (len >= RTL_NUMBER_OF(data.FileName) - 1 ) {
  1658. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1659. LOG_ERROR,
  1660. L"DiskspCheckPath: VolumeName length > buffer size \n" );
  1661. return ERROR_ALLOTTED_SPACE_EXCEEDED;
  1662. }
  1663. if ( FAILED( StringCchCopy( data.FileName,
  1664. RTL_NUMBER_OF(data.FileName),
  1665. VolumeName ) ) ) {
  1666. return ERROR_INSUFFICIENT_BUFFER;
  1667. }
  1668. //
  1669. // Send the path with trailing backslash to DiskspCheckPathInternal.
  1670. //
  1671. status = DiskspCheckPathInternal( &data, len );
  1672. data.FileName[len] = 0;
  1673. if (status != ERROR_SUCCESS || data.FileCount == 0) {
  1674. if (status != ERROR_EMPTY || data.LogFileNotFound) {
  1675. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1676. LOG_WARNING,
  1677. L"DiskspCheckPath: DCPI(%1!s!) returned status of %2!d!, files scanned %3!d!.\n",
  1678. data.FileName, status, data.FileCount);
  1679. }
  1680. if ( (status == ERROR_DISK_CORRUPT) ||
  1681. (status == ERROR_FILE_CORRUPT) )
  1682. {
  1683. if ( ResourceEntry->OnlineThread.Terminate ) {
  1684. return(ERROR_SHUTDOWN_CLUSTER);
  1685. }
  1686. // Should FixCorruption take forever? For now, "yes".
  1687. status = DisksFixCorruption( VolumeName,
  1688. ResourceEntry,
  1689. status );
  1690. if ( status != ERROR_SUCCESS ) {
  1691. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1692. LOG_ERROR,
  1693. L"DiskspCheckPath: FixCorruption for drive <%1!ws!:> returned status of %2!d!.\n",
  1694. VolumeName,
  1695. status);
  1696. }
  1697. } else {
  1698. // Some other error
  1699. // Assume that the error is benign if data.FileCount > 0
  1700. // ERROR_FILE_NOT_FOUND will be returned if there is no files on the volume
  1701. if (data.FileCount > 0 || status == ERROR_EMPTY) {
  1702. status = ERROR_SUCCESS;
  1703. }
  1704. }
  1705. }
  1706. return status;
  1707. } // DiskspCheckPath
  1708. DWORD
  1709. WaitForVolumes(
  1710. IN PDISK_RESOURCE ResourceEntry,
  1711. IN DWORD TimeOutInSeconds
  1712. )
  1713. /*++
  1714. Routine Description:
  1715. Checks out all volumes on a disk resource. In addition, will check
  1716. all drive letters on the volumes. If no drive letters on the disk,
  1717. volumes are always checked.
  1718. If a volume or drive letter is not ready, wait the specified number
  1719. of seconds before retrying.
  1720. Arguments:
  1721. ResourceEntry - Supplies a pointer to the disk resource entry.
  1722. TimeOutInSeconds - Number of seconds to wait for drive letter to come active.
  1723. Return Value:
  1724. ERROR_SUCCESS - all drive letters and volumes on disk are accessible.
  1725. ERROR_DISK_CORRUPT - drive letters or volumes not accessible.
  1726. Chkdsk.exe should be run on all volumes.
  1727. Win32 error code - one or more drive letters is not accessible.
  1728. --*/
  1729. {
  1730. PMOUNTIE_PARTITION entry;
  1731. DWORD retryInterval = 2000;
  1732. DWORD retries = TimeOutInSeconds / (retryInterval / 1000);
  1733. DWORD i;
  1734. DWORD partMap;
  1735. DWORD tempPartMap;
  1736. DWORD status;
  1737. DWORD nPartitions = MountiePartitionCount( &ResourceEntry->MountieInfo );
  1738. DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive;
  1739. WCHAR szGlobalDiskPartName[MAX_PATH];
  1740. WCHAR szVolumeName[MAX_PATH];
  1741. //
  1742. // Check drive letters, if any. If no drive letters, check only volumes.
  1743. // If drive letters, we still fall through and check volumes.
  1744. //
  1745. status = WaitForDriveLetters( DisksGetLettersForSignature( ResourceEntry ),
  1746. ResourceEntry,
  1747. TimeOutInSeconds // seconds timeout
  1748. );
  1749. if ( NO_ERROR != status ) {
  1750. //
  1751. // For any error checking drive letters, always return
  1752. // either disk or file corrupt so chkdsk will run.
  1753. //
  1754. return ERROR_DISK_CORRUPT;
  1755. }
  1756. //
  1757. // Make sure the partition count is not too large for the bitmap.
  1758. //
  1759. if ( nPartitions > ( sizeof(partMap) * 8 ) ) {
  1760. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1761. LOG_ERROR,
  1762. L"WaitForVolumes: Partition count (%1!u!) greater than bitmap (%2!u!) \n",
  1763. nPartitions, sizeof(partMap) );
  1764. return ERROR_INVALID_DATA;
  1765. }
  1766. //
  1767. // Convert the partition count to a bitmap.
  1768. //
  1769. partMap = 0;
  1770. for (i = 0; i < nPartitions; i++) {
  1771. partMap |= (1 << i);
  1772. }
  1773. while ( TRUE ) {
  1774. tempPartMap = partMap;
  1775. for ( i = 0; tempPartMap; i++ ) {
  1776. if ( (1 << i) & tempPartMap ) {
  1777. tempPartMap &= ~(1 << i);
  1778. entry = MountiePartition( &ResourceEntry->MountieInfo, i );
  1779. if ( !entry ) {
  1780. (DiskpLogEvent)(
  1781. ResourceEntry->ResourceHandle,
  1782. LOG_ERROR,
  1783. L"WaitForVolumes: No partition entry for partition %1!u! \n", i );
  1784. //
  1785. // Something bad happened to our data structures. We want to keep checking
  1786. // each of the other partitions.
  1787. continue;
  1788. }
  1789. //
  1790. // Given the DiskPartName, get the VolGuid name. This name must have a trailing
  1791. // backslash to work correctly.
  1792. //
  1793. (VOID)StringCchPrintf( szGlobalDiskPartName,
  1794. RTL_NUMBER_OF( szGlobalDiskPartName ),
  1795. GLOBALROOT_HARDDISK_PARTITION_FMT,
  1796. physicalDrive,
  1797. entry->PartitionNumber );
  1798. (DiskpLogEvent)(
  1799. ResourceEntry->ResourceHandle,
  1800. LOG_INFORMATION,
  1801. L"WaitForVolumes: Insure volume GUID name exists and accessible for %1!ws! \n",
  1802. szGlobalDiskPartName );
  1803. szVolumeName[0] = L'\0';
  1804. if ( !GetVolumeNameForVolumeMountPointW( szGlobalDiskPartName,
  1805. szVolumeName,
  1806. RTL_NUMBER_OF(szVolumeName) )) {
  1807. status = GetLastError();
  1808. (DiskpLogEvent)(
  1809. ResourceEntry->ResourceHandle,
  1810. LOG_ERROR,
  1811. L"WaitForVolumes: GetVolumeNameForVolumeMountPoint for %1!ws! returned %2!u!\n",
  1812. szGlobalDiskPartName,
  1813. status );
  1814. //
  1815. // Disk is corrupt. Immediately return an error so chkdsk can run during
  1816. // online processing.
  1817. //
  1818. if ( ERROR_DISK_CORRUPT == status || ERROR_FILE_CORRUPT == status ) {
  1819. return status;
  1820. }
  1821. //
  1822. // Something bad happened. Continue with the next partition.
  1823. continue;
  1824. }
  1825. ForcePnpVolChangeEvent( szVolumeName );
  1826. status = DiskspCheckPathLite( szVolumeName, ResourceEntry );
  1827. switch (status) {
  1828. case ERROR_SUCCESS:
  1829. case ERROR_EMPTY:
  1830. // Not an error
  1831. // Clear this partition number from the check list
  1832. partMap &= ~(1 << i);
  1833. break;
  1834. case ERROR_FILE_NOT_FOUND:
  1835. case ERROR_INVALID_PARAMETER:
  1836. // This is an error we expect to get when the volume
  1837. // wasn't mounted yet
  1838. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1839. LOG_ERROR,
  1840. L"WaitForVolumes: Volume (%1!ws!) file system not mounted (%2!u!) \n",
  1841. szVolumeName, status );
  1842. break;
  1843. default:
  1844. // This is not an error we expect.
  1845. // Probably something is very wrong with the system
  1846. // bail out
  1847. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1848. LOG_ERROR,
  1849. L"WaitForVolumes: Volume (%1!ws!) returns (%2!u!) \n",
  1850. szVolumeName, status );
  1851. return status;
  1852. }
  1853. }
  1854. }
  1855. if ( !partMap ) {
  1856. // All partitions are verified //
  1857. return ERROR_SUCCESS;
  1858. }
  1859. if ( retries-- == 0 ) {
  1860. return status;
  1861. }
  1862. Sleep(retryInterval);
  1863. }
  1864. return ERROR_SUCCESS;
  1865. } // WaitForVolumes
  1866. DWORD
  1867. DiskspCheckPathLite(
  1868. IN LPWSTR VolumeName,
  1869. IN PDISK_RESOURCE ResourceEntry
  1870. )
  1871. /*++
  1872. Routine Description:
  1873. Checks out a disk partition to see if the filesystem has mounted
  1874. it and it's working.
  1875. Arguments:
  1876. VolumeName - Supplies the device name of the form:
  1877. \\?\Volume{GUID}\ [Note trailing backslash!]
  1878. ResourceEntry - Supplies a pointer to the disk resource entry.
  1879. Return Value:
  1880. ERROR_SUCCESS if no corruption or corruption was found and corrected.
  1881. Win32 error code otherwise
  1882. --*/
  1883. {
  1884. DISKP_CHECK_PATH_DATA data;
  1885. DWORD len;
  1886. DWORD status;
  1887. (DiskpLogEvent)(
  1888. ResourceEntry->ResourceHandle,
  1889. LOG_INFORMATION,
  1890. L"DiskspCheckPathLite: Volume name %1!ws! \n",
  1891. VolumeName );
  1892. ZeroMemory( &data, sizeof(data) );
  1893. data.OpenFiles = FALSE;
  1894. data.LogFileNotFound = FALSE;
  1895. data.ResourceEntry = ResourceEntry;
  1896. len = wcslen( VolumeName );
  1897. if ( len >= RTL_NUMBER_OF(data.FileName) - 1 ) {
  1898. (DiskpLogEvent)(ResourceEntry->ResourceHandle,
  1899. LOG_ERROR,
  1900. L"DiskspCheckPathLite: VolumeName length > buffer size \n" );
  1901. return ERROR_ALLOTTED_SPACE_EXCEEDED;
  1902. }
  1903. if ( FAILED( StringCchCopy( data.FileName,
  1904. RTL_NUMBER_OF(data.FileName),
  1905. VolumeName ) ) ) {
  1906. return ERROR_INSUFFICIENT_BUFFER;
  1907. }
  1908. //
  1909. // Send the path with trailing backslash to DiskspCheckPathInternal.
  1910. //
  1911. status = DiskspCheckPathInternal( &data, len );
  1912. return status;
  1913. } // DiskspCheckPathLite
  1914. DWORD
  1915. DiskspCheckDriveLetter(
  1916. IN WCHAR DriveLetter,
  1917. IN PDISK_RESOURCE ResourceEntry,
  1918. IN BOOL Online
  1919. )
  1920. /*++
  1921. Routine Description:
  1922. Checks out a drive letter to see if the filesystem has mounted
  1923. it and it's working. This is lightweight version of DiskspCheckPath
  1924. Arguments:
  1925. DriveLetter - Supplies find first file failed
  1926. ResourceEntry - Supplies a pointer to the disk resource entry.
  1927. Online - Indicates whether this is Online or IsAlive processing.
  1928. ERROR_FILE_NOT_FOUND error is logged only during Online.
  1929. Return Value:
  1930. ERROR_SUCCESS or
  1931. Win32 error code otherwise
  1932. --*/
  1933. {
  1934. DISKP_CHECK_PATH_DATA data;
  1935. DWORD len;
  1936. DWORD status;
  1937. ZeroMemory( &data, sizeof(data) );
  1938. data.OpenFiles = FALSE;
  1939. data.LogFileNotFound = FALSE;
  1940. data.ResourceEntry = ResourceEntry;
  1941. data.FileName[0] = DriveLetter;
  1942. data.FileName[1] = L':';
  1943. // data->FileName is zero initialized data->FileName[2] = 0 //
  1944. len = 2;
  1945. status = DiskspCheckPathInternal( &data, len );
  1946. if ( NO_ERROR != status ) {
  1947. //
  1948. // Log all errors during Online, but don't log errors indicating
  1949. // the volume is empty (no files) during IsAlive (too noisy).
  1950. //
  1951. if ( ( ERROR_FILE_NOT_FOUND != status &&
  1952. ERROR_EMPTY != status ) || Online ) {
  1953. (DiskpLogEvent)(
  1954. ResourceEntry->ResourceHandle,
  1955. LOG_WARNING,
  1956. L"DiskspCheckDriveLetter: Checking drive name (%1!ws!) returns %2!u! \n",
  1957. data.FileName,
  1958. status );
  1959. }
  1960. }
  1961. return status;
  1962. } // DiskspCheckDriveLetter
  1963. DWORD
  1964. WaitForDriveLetters(
  1965. IN DWORD DriveLetters,
  1966. IN PDISK_RESOURCE ResourceEntry,
  1967. IN DWORD TimeOutInSeconds
  1968. )
  1969. /*++
  1970. Routine Description:
  1971. Checks out all drive letters on a disk resource. If a drive letter
  1972. is not ready, wait the specified number of seconds before retrying.
  1973. Arguments:
  1974. DriveLetter - Bit map of the drive letters for the disk
  1975. ResourceEntry - Supplies a pointer to the disk resource entry.
  1976. TimeOutInSeconds - Number of seconds to wait for drive letter to come active.
  1977. If zero, this routine is called during IsAlive.
  1978. If nonzero, this routine is called during Online.
  1979. Return Value:
  1980. ERROR_SUCCESS - all drive letters on disk are accessible.
  1981. Win32 error code - one or more drive letters is not accessible.
  1982. --*/
  1983. {
  1984. DWORD retryInterval = 2000;
  1985. DWORD retries = TimeOutInSeconds / (retryInterval / 1000);
  1986. BOOL online = ( TimeOutInSeconds ? TRUE : FALSE );
  1987. //
  1988. // If device has no drive letters, then we are done. Only log
  1989. // this fact during online, not IsAlive (TimeOutInSeconds
  1990. // is zero for IsAlive).
  1991. //
  1992. if ( !DriveLetters && online ) {
  1993. (DiskpLogEvent)(
  1994. ResourceEntry->ResourceHandle,
  1995. LOG_INFORMATION,
  1996. L"WaitForDriveLetters: No drive letters for volume, skipping drive letter check \n" );
  1997. return ERROR_SUCCESS;
  1998. }
  1999. for(;;) {
  2000. DWORD tempDriveLetters = DriveLetters;
  2001. UINT i = 0;
  2002. DWORD status = ERROR_SUCCESS;
  2003. while (tempDriveLetters) {
  2004. if ( (1 << i) & tempDriveLetters ) {
  2005. tempDriveLetters &= ~(1 << i);
  2006. status = DiskspCheckDriveLetter( (WCHAR)(i + L'A'), ResourceEntry, online );
  2007. switch (status) {
  2008. case ERROR_SUCCESS:
  2009. case ERROR_EMPTY:
  2010. // Not an error
  2011. // Clear this drive letter from the check list
  2012. DriveLetters &= ~(1 << i);
  2013. break;
  2014. case ERROR_FILE_NOT_FOUND:
  2015. case ERROR_INVALID_PARAMETER:
  2016. // This is an error we expect to get when the volume
  2017. // wasn't mounted yet
  2018. break;
  2019. default:
  2020. // This is not an error we expect.
  2021. // Probably something is very wrong with the system
  2022. // bail out
  2023. return status;
  2024. }
  2025. }
  2026. ++i;
  2027. }
  2028. if (!DriveLetters) {
  2029. // All drive letters are verified //
  2030. return ERROR_SUCCESS;
  2031. }
  2032. //
  2033. // If user requested no wait time (ie. IsAlive running),
  2034. // retries will be zero. In this case, return status
  2035. // immediately.
  2036. //
  2037. if (retries-- == 0) {
  2038. return status;
  2039. }
  2040. Sleep(retryInterval);
  2041. }
  2042. return ERROR_SUCCESS;
  2043. }
  2044. NTSTATUS
  2045. ForcePnpVolChangeEvent(
  2046. PWSTR RootPath
  2047. )
  2048. /*++
  2049. Routine Description:
  2050. Get the current volume label, then write it back. This causes a
  2051. PnP event for GUID_IO_VOLUME_CHANGE to take place. We do this so
  2052. the shell can see the new online disk. The problem is that the
  2053. shell sees a GUID_IO_VOLUME_MOUNT, but the disk is not yet online
  2054. so the shell doesn't correctly display the file system type or
  2055. volume label.
  2056. Arguments:
  2057. RootPath - Supplies the device name of the form:
  2058. \\?\Volume{GUID}\ [Note trailing backslash!]
  2059. Return Value:
  2060. ERROR_SUCCESS or
  2061. Win32 error code otherwise
  2062. --*/
  2063. {
  2064. LPWSTR volumeLabel = NULL;
  2065. LPWSTR fileSystemName = NULL;
  2066. DWORD dwError = NO_ERROR;
  2067. DWORD maxComponentLength;
  2068. DWORD fileSystemFlags;
  2069. if ( !RootPath ) {
  2070. dwError = ERROR_INVALID_PARAMETER;
  2071. goto FnExit;
  2072. }
  2073. volumeLabel = LocalAlloc( LPTR, MAX_PATH * sizeof(WCHAR) );
  2074. if ( !volumeLabel ) {
  2075. dwError = GetLastError();
  2076. goto FnExit;
  2077. }
  2078. fileSystemName = LocalAlloc( LPTR, MAX_PATH * sizeof(WCHAR) );
  2079. if ( !fileSystemName ) {
  2080. dwError = GetLastError();
  2081. goto FnExit;
  2082. }
  2083. //
  2084. // RootPath must end in a trailing backslash.
  2085. //
  2086. if ( !GetVolumeInformationW( RootPath,
  2087. volumeLabel,
  2088. MAX_PATH, // Number of chars
  2089. NULL,
  2090. &maxComponentLength,
  2091. &fileSystemFlags,
  2092. fileSystemName,
  2093. MAX_PATH ) ) { // Number of chars
  2094. dwError = GetLastError();
  2095. goto FnExit;
  2096. }
  2097. //
  2098. // Set the volume label to the same as the current. This will force
  2099. // PnP event for GUID_IO_VOLUME_CHANGE to take place.
  2100. //
  2101. if ( !SetVolumeLabelW( RootPath,
  2102. volumeLabel )) {
  2103. dwError = GetLastError();
  2104. goto FnExit;
  2105. }
  2106. FnExit:
  2107. if ( volumeLabel ) {
  2108. LocalFree( volumeLabel );
  2109. }
  2110. if ( fileSystemName ) {
  2111. LocalFree( fileSystemName );
  2112. }
  2113. return dwError;
  2114. } // ForcePnpVolChangeEvent