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

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