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.

3962 lines
106 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. mountpoints.c
  5. Abstract:
  6. This module processes mount point information for the disk resource DLL.
  7. Author:
  8. Steve Dziok (stevedz) 15-May-2000
  9. Environment:
  10. User Mode
  11. Revision History:
  12. --*/
  13. #define UNICODE 1
  14. #include "disksp.h"
  15. #include "arbitrat.h"
  16. #include "newdisks.h"
  17. #include "newmount.h"
  18. #include <mountmgr.h>
  19. #define SPACE_CHAR L' '
  20. #define MAX_OFFSET_CHARS 80 // Maximum number of chars allowed in offset string
  21. #define LOG_CURRENT_MODULE LOG_MODULE_DISK
  22. extern PWCHAR GLOBALROOT_HARDDISK_PARTITION_FMT; // L"\\\\\?\\GLOBALROOT\\Device\\Harddisk%u\\Partition%u\\";
  23. extern PWCHAR DEVICE_HARDDISK_PARTITION_FMT; // L"\\Device\\Harddisk%u\\Partition%u";
  24. #ifndef ClusterHashGuid
  25. //
  26. // Hash a GUID to a ULONG value.
  27. //
  28. #define ClusterHashGuid(_guid) ( ((PULONG) &(_guid))[0] ^ ((PULONG) &(_guid))[1] ^ \
  29. ((PULONG) &(_guid))[2] ^ ((PULONG) &(_guid))[3] )
  30. #endif
  31. #define MPS_ENABLED 0x00000001
  32. #define MPS_DELETE_INVALID_MPS 0x00000002 // Not currently used
  33. #define MPS_NONCLUSTERED_TO_CLUSTERED_MPS 0x00000010 // Not currently used
  34. #define MPS_KEEP_EXISTING_MPS 0x00000020 // Not currently used
  35. #define MPS_IGNORE_MAX_VOLGUIDS 0x00000100
  36. // Assume 32 nodes with 8 partitions per disk.
  37. #define MAX_ALLOWED_VOLGUID_ENTRIES_PER_DISK 32 * 8
  38. #define STOP_CLUSTER_ENUMERATIONS ERROR_INVALID_PRINTER_COMMAND
  39. #define PHYSICAL_DISK_WSTR L"Physical Disk"
  40. #define CHAR_TO_REPLACE 2
  41. #define MOUNTDEV_WSZ_VOLUME_GUID_PREFIX L"\\??\\Volume{" // Forms: \??\Volume{
  42. #define MOUNTDEV_CB_VOLUME_GUID_PREFIX 22
  43. #define MOUNTDEV_LOOKS_LIKE_VOLUME_GUID(name, length) \
  44. ((length > MOUNTDEV_CB_VOLUME_GUID_PREFIX) && \
  45. (!memcmp(name, MOUNTDEV_WSZ_VOLUME_GUID_PREFIX, MOUNTDEV_CB_VOLUME_GUID_PREFIX)))
  46. #define MOUNTDEV_WSZ_ALT_VOLUME_GUID_PREFIX L"\\\\?\\Volume{" // Forms: \\?\Volume{
  47. #define MOUNTDEV_CB_ALT_VOLUME_GUID_PREFIX 22
  48. #define MOUNTDEV_LOOKS_LIKE_ALT_VOLUME_GUID(name, bytelength) \
  49. ((bytelength > MOUNTDEV_CB_ALT_VOLUME_GUID_PREFIX) && \
  50. (!memcmp(name, MOUNTDEV_WSZ_ALT_VOLUME_GUID_PREFIX, MOUNTDEV_CB_ALT_VOLUME_GUID_PREFIX)))
  51. typedef struct _SIG_INFO {
  52. DWORD Signature;
  53. BOOL Clustered;
  54. } SIG_INFO, *PSIG_INFO;
  55. typedef struct _DEPENDENCY_INFO {
  56. DWORD SrcSignature;
  57. DWORD TargetSignature;
  58. BOOL DependencyCorrect;
  59. } DEPENDENCY_INFO, *PDEPENDENCY_INFO;
  60. typedef struct _STR_LIST {
  61. LPWSTR MultiSzList; // REG_MULTI_SZ string
  62. DWORD ListBytes; // Number of bytes, not number of WCHARs!
  63. } STR_LIST, *PSTR_LIST;
  64. DWORD
  65. AddStrToList(
  66. IN PDISK_RESOURCE ResourceEntry,
  67. IN PWSTR NewStr,
  68. IN DWORD PartitionNumber,
  69. IN OUT PSTR_LIST StrList
  70. );
  71. DWORD
  72. AssignDevice(
  73. HANDLE MountMgrHandle,
  74. PWCHAR MountName,
  75. PWCHAR VolumeDevName
  76. );
  77. DWORD
  78. CheckDependencies(
  79. DWORD SrcSignature,
  80. DWORD TargetSignature,
  81. PBOOL DependencyCorrect
  82. );
  83. VOID
  84. CheckMPsForVolume(
  85. IN OUT PDISK_RESOURCE ResourceEntry,
  86. IN PWSTR VolumeName
  87. );
  88. DWORD
  89. CheckSignatureClustered(
  90. DWORD Signature,
  91. PBOOL IsClustered
  92. );
  93. DWORD
  94. CreateVolGuidList(
  95. IN OUT PDISK_RESOURCE ResourceEntry
  96. );
  97. DWORD
  98. DeleteVolGuidList(
  99. PDISK_RESOURCE ResourceEntry
  100. );
  101. DWORD
  102. DependencyCallback(
  103. RESOURCE_HANDLE hOriginal,
  104. RESOURCE_HANDLE hResource,
  105. PVOID lpParams
  106. );
  107. #if DBG
  108. VOID
  109. DumpDiskInfoParams(
  110. PDISK_RESOURCE ResourceEntry
  111. );
  112. #endif
  113. DWORD
  114. EnumSigDependencies(
  115. RESOURCE_HANDLE DependentResource,
  116. DWORD DependsOnSignature,
  117. PBOOL DependencyCorrect
  118. );
  119. DWORD
  120. GetMountPoints(
  121. PWSTR VolumeName,
  122. PWSTR *VolumePaths
  123. );
  124. BOOL
  125. GetOffsetFromPartNo(
  126. DWORD PartitionNo,
  127. PMOUNTIE_INFO Info,
  128. PLARGE_INTEGER Offset
  129. );
  130. BOOL
  131. GetPartNoFromOffset(
  132. PLARGE_INTEGER Offset,
  133. PMOUNTIE_INFO Info,
  134. PDWORD PartitionNumber
  135. );
  136. DWORD
  137. GetSignatureForVolume(
  138. PDISK_RESOURCE ResourceEntry,
  139. PWSTR Volume,
  140. PDWORD Signature
  141. );
  142. DWORD
  143. GetSignatureFromDiskInfo(
  144. RESOURCE_HANDLE hResource,
  145. DWORD *dwSignature
  146. );
  147. BOOL
  148. IsMountPointAllowed(
  149. PWSTR MpName,
  150. PWSTR TargetVol,
  151. PDISK_RESOURCE ResourceEntry
  152. );
  153. BOOL
  154. MPIsDriveLetter(
  155. IN PWSTR MountPoint
  156. );
  157. VOID
  158. PrintStrList(
  159. PDISK_RESOURCE ResourceEntry,
  160. LPWSTR MultiSzList,
  161. DWORD ListBytes
  162. );
  163. DWORD
  164. ProcessVolGuidList(
  165. IN OUT PDISK_RESOURCE ResourceEntry
  166. );
  167. static
  168. DWORD
  169. SetMPListThread(
  170. LPVOID lpThreadParameter
  171. );
  172. DWORD
  173. SetupVolGuids(
  174. IN OUT PDISK_RESOURCE ResourceEntry
  175. );
  176. DWORD
  177. SigInfoCallback(
  178. RESOURCE_HANDLE hOriginal,
  179. RESOURCE_HANDLE hResource,
  180. PVOID lpParams
  181. );
  182. DWORD
  183. ValidateListOffsets(
  184. IN OUT PDISK_RESOURCE ResourceEntry,
  185. IN PWSTR MasterList
  186. );
  187. DWORD
  188. ValidateMountPoints(
  189. IN OUT PDISK_RESOURCE ResourceEntry
  190. );
  191. DWORD
  192. WrapClusterResourceControl(
  193. RESOURCE_HANDLE hResource,
  194. DWORD dwControlCode,
  195. LPVOID *OutBuffer,
  196. DWORD *dwBytesReturned
  197. );
  198. DWORD
  199. DisksProcessMountPointInfo(
  200. IN OUT PDISK_RESOURCE ResourceEntry
  201. )
  202. /*++
  203. Routine Description:
  204. During online processing, find all mount points directed towards this volume
  205. (identified by the ResourceEntry), and process the VolGuid list for this
  206. volume.
  207. If the VolGuid list exists in the cluster database, use it. Otherwise,
  208. get the current VolGuid and add it to the VolGuid list.
  209. VolGuid list is of the form:
  210. SectorOffset1 VolGuid1
  211. SectorOffset1 VolGuid2
  212. SectorOffset1 VolGuid3
  213. SectorOffset2 VolGuid1
  214. SectorOffset2 VolGuid2
  215. SectorOffset3 VolGuid1
  216. ... ...
  217. There are three possible mount point configurations involving clustered disks (we
  218. are not concerned about nonshared disks pointing to nonshared disks):
  219. Source --> Target
  220. ----------------- -----------------
  221. 1. clustered disk clustered disk
  222. 2. nonclustered disk clustered disk
  223. 3. clustered disk nonclustered disk
  224. Only configuration (1) is supported. Configurations (2) and (3) are not supported.
  225. Arguments:
  226. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  227. Return Value:
  228. ERROR_NOT_READY - MPInfo structure not yet initialized.
  229. Win32 error code.
  230. --*/
  231. {
  232. DWORD dwError = NO_ERROR;
  233. //
  234. // Mount point structures not initialized (i.e. critical section). Don't continue.
  235. //
  236. if ( !ResourceEntry->MPInfo.Initialized ) {
  237. (DiskpLogEvent)(
  238. ResourceEntry->ResourceHandle,
  239. LOG_ERROR,
  240. L"DisksProcessMountPointInfo: Mount point information not initialized. \n" );
  241. return ERROR_NOT_READY;
  242. }
  243. #if USEMOUNTPOINTS_KEY
  244. //
  245. // Mount point support disabled, don't do anything.
  246. //
  247. if ( !( ResourceEntry->DiskInfo.Params.UseMountPoints & MPS_ENABLED ) ) {
  248. (DiskpLogEvent)(
  249. ResourceEntry->ResourceHandle,
  250. LOG_INFORMATION,
  251. L"DisksProcessMountPointInfo: Mount point processing disabled via registry \n" );
  252. //
  253. // Delete the VolGuid list if it exists and remove this info
  254. // from the cluster database.
  255. //
  256. dwError = DeleteVolGuidList( ResourceEntry );
  257. if ( ERROR_SHARING_PAUSED == dwError ) {
  258. PostMPInfoIntoRegistry( ResourceEntry );
  259. }
  260. dwError = NO_ERROR;
  261. return dwError;
  262. }
  263. #endif
  264. //
  265. // Check if we are currently processing mount point info. If so, exit with an error.
  266. //
  267. if ( InterlockedCompareExchange(
  268. &ResourceEntry->MPInfo.MPListCreateInProcess,
  269. 1, 0 ) ) {
  270. (DiskpLogEvent)(
  271. ResourceEntry->ResourceHandle,
  272. LOG_WARNING,
  273. L"DisksProcessMountPointInfo: MPList creation in process, bypassing request \n" );
  274. return ERROR_BUSY;
  275. }
  276. __try {
  277. dwError = ProcessVolGuidList( ResourceEntry );
  278. ValidateMountPoints( ResourceEntry );
  279. // Fall through...
  280. #if 0
  281. // Add code similar to this when MPs from nonclustered to clustered disks is supported.
  282. if ( ( ResourceEntry->DiskInfo.Params.UseMountPoints & MPS_ENABLED ) &&
  283. ( ResourceEntry->DiskInfo.Params.UseMountPoints & MPS_NONCLUSTERED_TO_CLUSTERED_MPS ) ) {
  284. (DiskpLogEvent)(
  285. ResourceEntry->ResourceHandle,
  286. LOG_INFORMATION,
  287. L"DisksProcessMountPointInfo: ProcessMPList \n" );
  288. dwError = ProcessMPListConfig2( ResourceEntry );
  289. #endif
  290. } __finally {
  291. InterlockedExchange( &ResourceEntry->MPInfo.MPListCreateInProcess, 0 );
  292. }
  293. return dwError;
  294. } // DisksProcessMountPointInfo
  295. DWORD
  296. ProcessVolGuidList(
  297. IN OUT PDISK_RESOURCE ResourceEntry
  298. )
  299. /*++
  300. Routine Description:
  301. Main routine to create a new VolGuid list or to process an existing VolGuid list.
  302. Arguments:
  303. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  304. Return Value:
  305. Win32 error code.
  306. --*/
  307. {
  308. DWORD dwError = NO_ERROR;
  309. __try {
  310. //
  311. // Always try to add the current VolGuid to the list.
  312. //
  313. dwError = CreateVolGuidList( ResourceEntry );
  314. if ( NO_ERROR != dwError ) {
  315. (DiskpLogEvent)(
  316. ResourceEntry->ResourceHandle,
  317. LOG_ERROR,
  318. L"ProcessVolGuidList: Stop processing VolGuid list, Create failed %1!u! \n",
  319. dwError );
  320. __leave;
  321. }
  322. //
  323. // If the list is empty here (it shouldn't be), then exit with an error.
  324. //
  325. if ( !ResourceEntry->DiskInfo.Params.MPVolGuids ||
  326. 0 == ResourceEntry->DiskInfo.Params.MPVolGuidsSize ) {
  327. dwError = ERROR_INVALID_DATA;
  328. __leave;
  329. }
  330. PrintStrList( ResourceEntry,
  331. ResourceEntry->DiskInfo.Params.MPVolGuids,
  332. ResourceEntry->DiskInfo.Params.MPVolGuidsSize );
  333. //
  334. // Make sure the offsets are correct in the VolGuid list.
  335. // Note that it is possible for the list to be deleted and
  336. // recreated after this validation, but that is not a problem (because
  337. // when they are recreated they will have the correct offsets).
  338. //
  339. dwError = ValidateListOffsets( ResourceEntry,
  340. ResourceEntry->DiskInfo.Params.MPVolGuids );
  341. if ( ERROR_INVALID_DATA == dwError ) {
  342. //
  343. // At least one of the offsets is invalid. Possibly, the partition
  344. // layout on the disk has been changed. Delete the existing
  345. // list, and create a new one.
  346. //
  347. // This code should run infrequently...
  348. //
  349. // The partition layout might change if ASR runs and creates new partitions
  350. // that don't match the previous system exactly. Since NTBACKUP saves the
  351. // cluster DB information, the mount point list will be restored but won't
  352. // match the actual "new" partition layout. ASR will insure that all the
  353. // mount points and VolGuids on the system are created, so we should be able
  354. // to simply delete and recreate the mount point list.
  355. //
  356. (DiskpLogEvent)(
  357. ResourceEntry->ResourceHandle,
  358. LOG_WARNING,
  359. L"ProcessVolGuidList: Invalid offset in existing VolGuid list. Deleting and recreating. \n" );
  360. DeleteVolGuidList( ResourceEntry );
  361. dwError = CreateVolGuidList( ResourceEntry );
  362. __leave;
  363. } else if ( ERROR_INSUFFICIENT_BUFFER == dwError ) {
  364. //
  365. // The Volguid list is too large and likely corrupt. We cannot
  366. // proceed.
  367. //
  368. __leave;
  369. }
  370. //
  371. // For every VolGuid in the list, make sure they are assigned to the correct
  372. // volumes on this system.
  373. //
  374. dwError = SetupVolGuids( ResourceEntry );
  375. } __finally {
  376. }
  377. return dwError;
  378. } // ProcessVolGuidList
  379. DWORD
  380. CreateVolGuidList(
  381. IN OUT PDISK_RESOURCE ResourceEntry
  382. )
  383. /*++
  384. Routine Description:
  385. Add the current system VolGuid to the list. If the list is empty, create a new list.
  386. For each partition on this disk (identified by the ResourceEntry), get the byte
  387. offset of that partition. Get the current VolGuid for that parition, and add
  388. it to the list. If the current VolGuid is already in the list, ignore the error.
  389. Arguments:
  390. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  391. Return Value:
  392. ERROR_INVALID_DATA - partition info in disk resource is invalid
  393. Win32 error code.
  394. --*/
  395. {
  396. PMOUNTIE_PARTITION entry;
  397. HANDLE mountMgrHandle = INVALID_HANDLE_VALUE;
  398. DWORD dwError = ERROR_SUCCESS;
  399. DWORD nPartitions = MountiePartitionCount( &ResourceEntry->MountieInfo );
  400. DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive;
  401. DWORD idx;
  402. DWORD volumeNameLenChars; // Number of characters
  403. DWORD newStrListLenBytes; // Number of bytes
  404. WCHAR szGlobalDiskPartName[MAX_PATH];
  405. WCHAR szVolumeName[MAX_PATH];
  406. STR_LIST newStrList;
  407. BOOL freeNewList = TRUE;
  408. __try {
  409. (DiskpLogEvent)(
  410. ResourceEntry->ResourceHandle,
  411. LOG_INFORMATION,
  412. L"CreateVolGuidList: Adding current VolGuid to list \n" );
  413. EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
  414. ZeroMemory( szGlobalDiskPartName, sizeof(szGlobalDiskPartName) );
  415. ZeroMemory( szVolumeName, sizeof(szVolumeName) );
  416. ZeroMemory( &newStrList, sizeof(STR_LIST) );
  417. dwError = DevfileOpen( &mountMgrHandle, MOUNTMGR_DEVICE_NAME );
  418. if ( dwError != NO_ERROR ) {
  419. (DiskpLogEvent)(
  420. ResourceEntry->ResourceHandle,
  421. LOG_ERROR,
  422. L"CreateVolGuidList: Failed to open MountMgr %1!u! \n",
  423. dwError );
  424. __leave;
  425. }
  426. newStrListLenBytes = ResourceEntry->DiskInfo.Params.MPVolGuidsSize;
  427. if ( newStrListLenBytes ) {
  428. newStrList.MultiSzList = LocalAlloc( LPTR, newStrListLenBytes );
  429. if ( !(newStrList.MultiSzList) ) {
  430. dwError = GetLastError();
  431. (DiskpLogEvent)(
  432. ResourceEntry->ResourceHandle,
  433. LOG_ERROR,
  434. L"CreateVolGuidList: Failed to allocate storage for new list %1!u! \n",
  435. dwError );
  436. __leave;
  437. }
  438. //
  439. // Copy current list to newStrList.
  440. //
  441. memcpy( newStrList.MultiSzList, ResourceEntry->DiskInfo.Params.MPVolGuids, newStrListLenBytes );
  442. newStrList.ListBytes = newStrListLenBytes;
  443. #if 0 // Useful for debugging
  444. (DiskpLogEvent)(
  445. ResourceEntry->ResourceHandle,
  446. LOG_INFORMATION,
  447. L"CreateVolGuidList: newStrListLen %1!u! \n",
  448. newStrList.ListBytes );
  449. PrintStrList( ResourceEntry, newStrList.MultiSzList, newStrList.ListBytes );
  450. #endif
  451. } else {
  452. (DiskpLogEvent)(
  453. ResourceEntry->ResourceHandle,
  454. LOG_INFORMATION,
  455. L"CreateVolGuidList: Current list empty, creating new list \n" );
  456. }
  457. //
  458. // Check each interesting partition. Since only "valid" partitions are
  459. // saved in the MountieInfo structure, we will only look at those (ignoring those
  460. // partitions that are not NTFS).
  461. //
  462. for ( idx = 0; idx < nPartitions; ++idx ) {
  463. entry = MountiePartition( &ResourceEntry->MountieInfo, idx );
  464. #if DBG
  465. (DiskpLogEvent)(
  466. ResourceEntry->ResourceHandle,
  467. LOG_INFORMATION,
  468. L"CreateVolGuidList: index %1!u! entry %2!x! \n", idx, entry );
  469. #endif
  470. if ( !entry ) {
  471. (DiskpLogEvent)(
  472. ResourceEntry->ResourceHandle,
  473. LOG_ERROR,
  474. L"CreateVolGuidList: no partition entry for index %1!u! \n", idx );
  475. //
  476. // Something bad happened to our data structures.
  477. //
  478. dwError = ERROR_INVALID_DATA;
  479. __leave;
  480. }
  481. //
  482. // Create the device name of the form:
  483. // \\?\GLOBALROOT\Device\HarddiskX\PartitionY\ (uses trailing backslash)
  484. //
  485. _snwprintf( szGlobalDiskPartName,
  486. MAX_PATH, // Number of CHARS, not bytes
  487. GLOBALROOT_HARDDISK_PARTITION_FMT,
  488. physicalDrive,
  489. entry->PartitionNumber );
  490. #if DBG
  491. (DiskpLogEvent)(
  492. ResourceEntry->ResourceHandle,
  493. LOG_INFORMATION,
  494. L"CreateVolGuidList: Using name (%1!ws!) \n",
  495. szGlobalDiskPartName );
  496. #endif
  497. //
  498. // Get the current VolGuid for this partition.
  499. //
  500. if ( !GetVolumeNameForVolumeMountPointW( szGlobalDiskPartName,
  501. szVolumeName,
  502. sizeof(szVolumeName)/sizeof(WCHAR) )) {
  503. dwError = GetLastError();
  504. (DiskpLogEvent)(
  505. ResourceEntry->ResourceHandle,
  506. LOG_ERROR,
  507. L"CreateVolGuidList: GetVolumeNameForVolumeMountPoint for (%1!ws!) returned %2!u!\n",
  508. szGlobalDiskPartName,
  509. dwError );
  510. // Try next partition.
  511. continue;
  512. }
  513. #if DBG
  514. (DiskpLogEvent)(
  515. ResourceEntry->ResourceHandle,
  516. LOG_INFORMATION,
  517. L"CreateVolGuidList: Returned volume name (%1!ws!) \n",
  518. szVolumeName );
  519. #endif
  520. //
  521. // Fix current VolGuid name.
  522. //
  523. // GetVolumeNameForVolumeMountPoint returns name of the form:
  524. // \\?\Volume{-GUID-}\
  525. //
  526. // But we need the name to be in the form:
  527. // \??\Volume{-GUID-}
  528. //
  529. volumeNameLenChars = wcslen( szVolumeName );
  530. if ( !(MOUNTDEV_LOOKS_LIKE_ALT_VOLUME_GUID( szVolumeName, volumeNameLenChars * sizeof(WCHAR) ) ) ) {
  531. (DiskpLogEvent)(
  532. ResourceEntry->ResourceHandle,
  533. LOG_ERROR,
  534. L"CreateVolGuidList: Improper volume name format (%1!ws!) \n",
  535. szVolumeName );
  536. // Try next partition.
  537. continue;
  538. }
  539. szVolumeName[1] = L'?';
  540. if ( L'\\' == szVolumeName[volumeNameLenChars-1]) {
  541. szVolumeName[volumeNameLenChars-1] = L'\0';
  542. }
  543. #if DBG
  544. (DiskpLogEvent)(
  545. ResourceEntry->ResourceHandle,
  546. LOG_INFORMATION,
  547. L"CreateVolGuidList: Fixed volume name (%1!ws!) \n",
  548. szVolumeName );
  549. #endif
  550. //
  551. // Add the new string to the list. If the new string is already in the list, this
  552. // routine won't do anything and will return NO_ERROR.
  553. //
  554. dwError = AddStrToList( ResourceEntry,
  555. szVolumeName,
  556. entry->PartitionNumber,
  557. &newStrList );
  558. if ( NO_ERROR != dwError ) {
  559. __leave;
  560. }
  561. }
  562. //
  563. // Optimization:
  564. // If the new list is the same as the old list, we are done.
  565. //
  566. if ( newStrList.MultiSzList &&
  567. newStrList.ListBytes &&
  568. newStrList.ListBytes == ResourceEntry->DiskInfo.Params.MPVolGuidsSize &&
  569. ( 0 == memcmp( newStrList.MultiSzList, ResourceEntry->DiskInfo.Params.MPVolGuids, newStrListLenBytes ) ) ) {
  570. (DiskpLogEvent)(
  571. ResourceEntry->ResourceHandle,
  572. LOG_INFORMATION,
  573. L"CreateVolGuidList: New list same as old list, skipping update \n" );
  574. __leave;
  575. }
  576. (DiskpLogEvent)(
  577. ResourceEntry->ResourceHandle,
  578. LOG_INFORMATION,
  579. L"CreateVolGuidList: Saving new VolGuid list \n" );
  580. freeNewList = FALSE;
  581. //
  582. // The new list is different from the old list. Free the old list, update
  583. // the ResourceEntry with the new list information, and update the cluster
  584. // database.
  585. //
  586. DeleteVolGuidList( ResourceEntry );
  587. EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
  588. ResourceEntry->DiskInfo.Params.MPVolGuids = newStrList.MultiSzList;
  589. ResourceEntry->DiskInfo.Params.MPVolGuidsSize = newStrList.ListBytes;
  590. LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
  591. PrintStrList( ResourceEntry,
  592. ResourceEntry->DiskInfo.Params.MPVolGuids,
  593. ResourceEntry->DiskInfo.Params.MPVolGuidsSize );
  594. dwError = PostMPInfoIntoRegistry( ResourceEntry );
  595. } __finally {
  596. if ( INVALID_HANDLE_VALUE != mountMgrHandle ) {
  597. CloseHandle( mountMgrHandle );
  598. }
  599. if ( freeNewList && newStrList.MultiSzList ) {
  600. LocalFree( newStrList.MultiSzList );
  601. }
  602. LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
  603. }
  604. return dwError;
  605. } // CreateVolGuidList
  606. DWORD
  607. AddStrToList(
  608. IN PDISK_RESOURCE ResourceEntry,
  609. IN PWSTR NewStr,
  610. IN DWORD PartitionNumber,
  611. IN OUT PSTR_LIST StrList
  612. )
  613. /*++
  614. Routine Description:
  615. Add the string to the MULTI_SZ list. Convert the partition number to a byte offset
  616. so we don't rely on partition numbers.
  617. List format will be:
  618. ByteOffset1 Str1
  619. ByteOffset1 Str2
  620. ByteOffset1 Str3
  621. ByteOffset2 Str1
  622. ByteOffset2 Str2
  623. ByteOffset3 Str1
  624. ... ...
  625. Arguments:
  626. Return Value:
  627. --*/
  628. {
  629. PWCHAR listEntry = NULL;
  630. DWORD listEntrySizeChars;
  631. DWORD lenChars;
  632. DWORD newStrLenChars;
  633. DWORD listChars;
  634. DWORD remainingLen;
  635. DWORD dwError = ERROR_INVALID_DATA;
  636. LARGE_INTEGER offset;
  637. #if DBG
  638. (DiskpLogEvent)(
  639. ResourceEntry->ResourceHandle,
  640. LOG_INFORMATION,
  641. L"AddStrToList: Adding str (%1!ws!) \n", NewStr );
  642. #endif
  643. newStrLenChars = wcslen( NewStr );
  644. if ( 0 == newStrLenChars ) {
  645. //
  646. // Something wrong with the string length.
  647. //
  648. (DiskpLogEvent)(
  649. ResourceEntry->ResourceHandle,
  650. LOG_ERROR,
  651. L"AddStrToList: Invalid length: NewStrLen = %1!u! \n",
  652. newStrLenChars );
  653. goto FnExit;
  654. }
  655. //
  656. // Indicate an error unless we can allocate and copy the info
  657. // into the list. Calculate the minimum size needed, then get
  658. // larger buffer. This buffer is temporary and freed later.
  659. //
  660. listEntrySizeChars = ( newStrLenChars + // Char length of parameter string
  661. MAX_OFFSET_CHARS + // Char length of offset string
  662. 1 + // Room to change end of offset string to space and extend it
  663. 1 ) // Unicode NULL
  664. * 2; // Make sure buffer is large enough
  665. listEntry = LocalAlloc( LPTR, listEntrySizeChars * sizeof(WCHAR) );
  666. if ( !listEntry ) {
  667. dwError = GetLastError();
  668. goto FnExit;
  669. }
  670. //
  671. // Get the offset for the specified partition.
  672. //
  673. if ( !GetOffsetFromPartNo( PartitionNumber,
  674. &ResourceEntry->MountieInfo,
  675. &offset ) ) {
  676. //
  677. // Can't get the offset for the specified partition.
  678. //
  679. (DiskpLogEvent)(
  680. ResourceEntry->ResourceHandle,
  681. LOG_ERROR,
  682. L"AddStrToList: GetOffsetFromPartNo failed \n" );
  683. goto FnExit;
  684. }
  685. //
  686. // Convert the offset into a string. Put the offset into listEntry.
  687. //
  688. _ui64tow( offset.QuadPart, listEntry, 16 );
  689. lenChars = wcslen( listEntry );
  690. if ( 0 == lenChars || lenChars >= MAX_OFFSET_CHARS ) {
  691. //
  692. // The length of the offset string is invalid.
  693. //
  694. (DiskpLogEvent)(
  695. ResourceEntry->ResourceHandle,
  696. LOG_ERROR,
  697. L"AddStrToList: Invalid offset string length = %1!u! \n",
  698. lenChars );
  699. goto FnExit;
  700. }
  701. // Format will be:
  702. // ByteOffset1 Str1
  703. // ByteOffset1 Str2
  704. // ByteOffset1 Str3
  705. // ByteOffset2 Str1
  706. // ByteOffset2 Str2
  707. // ByteOffset3 Str1
  708. // ... ...
  709. //
  710. // Change the end of the offset string to another character. Move the end of string
  711. // out one character. This extra space was included when we allocated the buffer.
  712. //
  713. listEntry[lenChars+1] = UNICODE_NULL;
  714. listEntry[lenChars] = SPACE_CHAR;
  715. //
  716. // One more check. Make sure enough space remaining for adding string.
  717. //
  718. remainingLen = listEntrySizeChars - wcslen( listEntry ) - 1;
  719. #if DBG
  720. (DiskpLogEvent)(
  721. ResourceEntry->ResourceHandle,
  722. LOG_INFORMATION,
  723. L"AddStrToList: New string length %1!u! Remaining list entry length %2!u! \n",
  724. newStrLenChars,
  725. remainingLen );
  726. #endif
  727. if ( newStrLenChars >= remainingLen ) {
  728. (DiskpLogEvent)(
  729. ResourceEntry->ResourceHandle,
  730. LOG_ERROR,
  731. L"AddStrToList: New string length %1!u! larger than remaining list entry length %2!u! \n",
  732. newStrLenChars,
  733. remainingLen );
  734. goto FnExit;
  735. }
  736. //
  737. // Put the rest of the string in list entry.
  738. //
  739. wcsncat( listEntry, NewStr, newStrLenChars );
  740. //
  741. // If the string is already in the list, skip it.
  742. //
  743. if ( ClRtlMultiSzScan( ResourceEntry->DiskInfo.Params.MPVolGuids, listEntry ) ) {
  744. (DiskpLogEvent)(
  745. ResourceEntry->ResourceHandle,
  746. LOG_INFORMATION,
  747. L"AddStrToList: Skipping duplicate entry (%1!ws!) \n",
  748. listEntry );
  749. dwError = NO_ERROR;
  750. goto FnExit;
  751. }
  752. //
  753. // Note that ClRtlMultiSzAppend updates the number of CHARACTERS, but we
  754. // need to have the number of BYTES in the property table. We will adjust
  755. // this value later.
  756. //
  757. listChars = StrList->ListBytes / sizeof(WCHAR);
  758. #if DBG
  759. (DiskpLogEvent)(
  760. ResourceEntry->ResourceHandle,
  761. LOG_INFORMATION,
  762. L"AddStrToList: StrList->MultiSzList at %1!x!, numBytes %2!u! \n",
  763. StrList->MultiSzList,
  764. StrList->ListBytes );
  765. (DiskpLogEvent)(
  766. ResourceEntry->ResourceHandle,
  767. LOG_INFORMATION,
  768. L"AddStrToList: Adding str entry: (%1!ws!) numChars %2!u! \n",
  769. listEntry,
  770. listChars );
  771. #endif
  772. dwError = ClRtlMultiSzAppend( &(StrList->MultiSzList),
  773. &listChars,
  774. listEntry );
  775. //
  776. // Convert the number of CHARACTERS back to bytes.
  777. //
  778. StrList->ListBytes = listChars * sizeof(WCHAR);
  779. if ( ERROR_SUCCESS == dwError) {
  780. #if DBG
  781. (DiskpLogEvent)(
  782. ResourceEntry->ResourceHandle,
  783. LOG_INFORMATION,
  784. L"AddStrToList: Added str, numBytes %1!u! numChars %2!u! \n",
  785. StrList->ListBytes,
  786. listChars );
  787. #endif
  788. } else {
  789. (DiskpLogEvent)(
  790. ResourceEntry->ResourceHandle,
  791. LOG_ERROR,
  792. L"AddStrToList: Unable to append MultiSz string for (%1!ws!), failed %x \n",
  793. NewStr,
  794. dwError );
  795. }
  796. FnExit:
  797. if ( listEntry ) {
  798. LocalFree( listEntry );
  799. }
  800. return dwError;
  801. } // AddStrToList
  802. DWORD
  803. SetupVolGuids(
  804. IN OUT PDISK_RESOURCE ResourceEntry
  805. )
  806. /*++
  807. Routine Description:
  808. Add every VolGuid in the existing MULTI_SZ VolGuid list to the system.
  809. Arguments:
  810. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  811. Return Value:
  812. Win32 error code.
  813. --*/
  814. {
  815. PWCHAR currentStr;
  816. PWCHAR volGuid;
  817. HANDLE mountMgrHandle = INVALID_HANDLE_VALUE;
  818. DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive;
  819. DWORD currentStrLenChars = 0;
  820. DWORD dwError = NO_ERROR;
  821. DWORD partitionNo;
  822. DWORD count;
  823. LARGE_INTEGER offset;
  824. WCHAR szDiskPartName[MAX_PATH];
  825. __try {
  826. (DiskpLogEvent)(
  827. ResourceEntry->ResourceHandle,
  828. LOG_INFORMATION,
  829. L"SetupVolGuids: Processing VolGuid list \n" );
  830. EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
  831. dwError = DevfileOpen( &mountMgrHandle, MOUNTMGR_DEVICE_NAME );
  832. if ( dwError != NO_ERROR ) {
  833. __leave;
  834. }
  835. //
  836. // Parse through the list.
  837. //
  838. for ( currentStr = (PWCHAR)ResourceEntry->DiskInfo.Params.MPVolGuids,
  839. currentStrLenChars = wcslen( currentStr ) ;
  840. currentStrLenChars ;
  841. currentStr += currentStrLenChars + 1,
  842. currentStrLenChars = wcslen( currentStr ) ) {
  843. offset.QuadPart = 0;
  844. #if DBG
  845. (DiskpLogEvent)(
  846. ResourceEntry->ResourceHandle,
  847. LOG_INFORMATION,
  848. L"SetupVolGuids: CurrentStr (%1!ws!), numChars %2!u! \n",
  849. currentStr,
  850. currentStrLenChars );
  851. #endif
  852. //
  853. // Convert the offset from a string to a large integer value.
  854. //
  855. count = swscanf( currentStr, L"%I64x ", &offset.QuadPart );
  856. if ( 0 == count ) {
  857. (DiskpLogEvent)(
  858. ResourceEntry->ResourceHandle,
  859. LOG_ERROR,
  860. L"SetupVolGuids: Unable to parse offset from currentStr (%1!ws!) \n",
  861. currentStr );
  862. continue;
  863. }
  864. //
  865. // Convert the offset to a partition number.
  866. //
  867. if ( !GetPartNoFromOffset( &offset, &ResourceEntry->MountieInfo, &partitionNo ) ) {
  868. (DiskpLogEvent)(
  869. ResourceEntry->ResourceHandle,
  870. LOG_ERROR,
  871. L"SetupVolGuids: Unable to convert offset ( %1!08X!%2!08X! ) to partition number \n",
  872. offset.HighPart,
  873. offset.LowPart ); // couldn't get !I64X! to work...
  874. continue;
  875. }
  876. //
  877. // Get a pointer to the VolGuid data, just after the byte offset.
  878. //
  879. volGuid = wcsstr( currentStr, MOUNTDEV_WSZ_VOLUME_GUID_PREFIX );
  880. if ( !volGuid ) {
  881. (DiskpLogEvent)(
  882. ResourceEntry->ResourceHandle,
  883. LOG_ERROR,
  884. L"SetupVolGuids: Unable to find volume string in current list entry (%1!ws) \n",
  885. currentStr );
  886. continue;
  887. }
  888. #if DBG
  889. (DiskpLogEvent)(
  890. ResourceEntry->ResourceHandle,
  891. LOG_INFORMATION,
  892. L"SetupVolGuids: Using VolGuid (%1!ws!) \n",
  893. volGuid );
  894. #endif
  895. ZeroMemory( szDiskPartName, sizeof( szDiskPartName ) );
  896. //
  897. // Create the device name of the form:
  898. // \Device\HarddiskX\PartitionY (no trailing backslash)
  899. //
  900. _snwprintf( szDiskPartName,
  901. MAX_PATH, // Number of CHARS, not bytes
  902. DEVICE_HARDDISK_PARTITION_FMT,
  903. physicalDrive,
  904. partitionNo );
  905. #if DBG
  906. (DiskpLogEvent)(
  907. ResourceEntry->ResourceHandle,
  908. LOG_INFORMATION,
  909. L"SetupVolGuids: Using device name (%1!ws!) \n",
  910. szDiskPartName );
  911. #endif
  912. dwError = AssignDevice( mountMgrHandle, volGuid, szDiskPartName );
  913. if ( NO_ERROR != dwError &&
  914. STATUS_OBJECT_NAME_COLLISION != dwError ) {
  915. // Assign device will return: 0xC0000035 STATUS_OBJECT_NAME_COLLISION
  916. // if we are setting a VolGuid that was previously set. This is not
  917. // a problem.
  918. (DiskpLogEvent)(
  919. ResourceEntry->ResourceHandle,
  920. LOG_WARNING,
  921. L"SetupVolGuids: Unable to assign VolGuid to device, error %1!x! \n",
  922. dwError );
  923. // Continue processing with error...
  924. }
  925. dwError = STATUS_SUCCESS;
  926. }
  927. } __finally {
  928. if ( INVALID_HANDLE_VALUE != mountMgrHandle ) {
  929. CloseHandle( mountMgrHandle );
  930. }
  931. LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
  932. }
  933. return dwError;
  934. } // SetupVolGuids
  935. DWORD
  936. AssignDevice(
  937. HANDLE MountMgrHandle,
  938. PWCHAR MountName,
  939. PWCHAR VolumeDevName
  940. )
  941. /*++
  942. Routine Description:
  943. Put the specified MountName (i.e. mount point name) into the mount manager's internal table
  944. of mount points.
  945. Inputs:
  946. MountMgrHandle - Handle to the mount manager. The caller is responsible for
  947. opening and closing this handle.
  948. MountName - Mountpoint name of the form:
  949. \??\Volume{-GUID-} - note prefix "\??\" and no trailing backslash.
  950. \DosDevices\X: - works if a drive letter is not already assigned
  951. VolumeDevName - Volume device name. Can be one of the following forms (note that case is
  952. important). The "#" is a zero-based device number (and partition number
  953. as appropriate).
  954. \Device\CdRom#
  955. \Device\Floppy#
  956. \Device\HarddiskVolume#
  957. \Device\Harddisk#\Partition#
  958. Return value:
  959. A Win32 error code.
  960. --*/
  961. {
  962. PMOUNTMGR_CREATE_POINT_INPUT input;
  963. DWORD status;
  964. USHORT mountNameLenBytes;
  965. USHORT volumeDevNameLenBytes;
  966. USHORT inputlengthBytes;
  967. if ( INVALID_HANDLE_VALUE == MountMgrHandle ) {
  968. return ERROR_INVALID_PARAMETER;
  969. }
  970. mountNameLenBytes = wcslen( MountName ) * sizeof(WCHAR);
  971. volumeDevNameLenBytes = wcslen( VolumeDevName ) * sizeof(WCHAR);
  972. inputlengthBytes = sizeof(MOUNTMGR_CREATE_POINT_INPUT) + mountNameLenBytes + volumeDevNameLenBytes;
  973. input = (PMOUNTMGR_CREATE_POINT_INPUT)LocalAlloc( LPTR, inputlengthBytes );
  974. if ( !input ) {
  975. return ERROR_NOT_ENOUGH_MEMORY;
  976. }
  977. input->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
  978. input->SymbolicLinkNameLength = mountNameLenBytes;
  979. input->DeviceNameOffset = input->SymbolicLinkNameOffset +
  980. input->SymbolicLinkNameLength;
  981. input->DeviceNameLength = volumeDevNameLenBytes;
  982. RtlCopyMemory((PCHAR)input + input->SymbolicLinkNameOffset,
  983. MountName, mountNameLenBytes);
  984. RtlCopyMemory((PCHAR)input + input->DeviceNameOffset,
  985. VolumeDevName, volumeDevNameLenBytes);
  986. status = DevfileIoctl( MountMgrHandle,
  987. IOCTL_MOUNTMGR_CREATE_POINT,
  988. input,
  989. inputlengthBytes,
  990. NULL,
  991. 0,
  992. NULL );
  993. LocalFree( input );
  994. return status;
  995. } // AssignDevice
  996. DWORD
  997. DeleteVolGuidList(
  998. PDISK_RESOURCE ResourceEntry
  999. )
  1000. /*++
  1001. Routine Description:
  1002. Delete the list from the DISK_RESOURCE structure, if it exists (free the
  1003. memmory). Also deletes the information from the cluster database.
  1004. Arguments:
  1005. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  1006. Return Value:
  1007. NO_ERROR - The list was deleted.
  1008. ERROR_NOT_READY - The mount point information was not yet initialized.
  1009. Win32 error code.
  1010. --*/
  1011. {
  1012. DWORD dwError = NO_ERROR;
  1013. //
  1014. // Mount point structures not initialized (i.e. critical section). Don't continue.
  1015. //
  1016. if ( !ResourceEntry->MPInfo.Initialized ) {
  1017. (DiskpLogEvent)(
  1018. ResourceEntry->ResourceHandle,
  1019. LOG_ERROR,
  1020. L"DeleteVolGuidList: Mount point info not initialized. List not deleted. \n" );
  1021. dwError = ERROR_NOT_READY;
  1022. goto FnExit;
  1023. }
  1024. EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
  1025. //
  1026. // If existing list, free it.
  1027. //
  1028. if ( ResourceEntry->DiskInfo.Params.MPVolGuids ) {
  1029. LocalFree( ResourceEntry->DiskInfo.Params.MPVolGuids );
  1030. ResourceEntry->DiskInfo.Params.MPVolGuidsSize = 0;
  1031. ResourceEntry->DiskInfo.Params.MPVolGuids = NULL;
  1032. dwError = ClusterRegDeleteValue( ResourceEntry->ResourceParametersKey,
  1033. CLUSREG_NAME_PHYSDISK_MPVOLGUIDS );
  1034. //
  1035. // If the update failed and the disk is not yet online, it will fail with
  1036. // ERROR_SHARING_PAUSED. Just return the error. If the caller really,
  1037. // really, really wants the cluster database cleaned up, they can
  1038. // use the PostMPInfoIntoRegistry call to create a thread to do this
  1039. // work.
  1040. if ( NO_ERROR != dwError ) {
  1041. (DiskpLogEvent)(
  1042. ResourceEntry->ResourceHandle,
  1043. LOG_WARNING,
  1044. L"DeleteVolGuidList: Unable to delete VolGuid from cluster database %1!u! \n",
  1045. dwError );
  1046. }
  1047. }
  1048. LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
  1049. FnExit:
  1050. #if DBG
  1051. (DiskpLogEvent)(
  1052. ResourceEntry->ResourceHandle,
  1053. LOG_INFORMATION,
  1054. L"DeleteVolGuidList: returns %1!u! \n",
  1055. dwError );
  1056. #endif
  1057. return dwError;
  1058. } // DeleteVolGuidList
  1059. BOOL
  1060. IsMountPointAllowed(
  1061. PWSTR MpName,
  1062. PWSTR TargetVol,
  1063. PDISK_RESOURCE ResourceEntry
  1064. )
  1065. /*++
  1066. Routine Description:
  1067. Verify that the mount point is allowed. There are several reasons why the mount
  1068. point would not be allowed.
  1069. At this point, the source volume will be accessible. If the source were offline,
  1070. we wouldn't even know about it, and we wouldn't even get to this routine. The
  1071. fact that the source is accessible allows us to do some things differently (i.e. we
  1072. can talk to the disk if needed).
  1073. Dependencies:
  1074. If source disk S has a mount point to target disk T ( S:\tdir --> T: ), then
  1075. source S is dependent on target T and target T must be brought online before
  1076. source S is online.
  1077. Quorum drive restrictions:
  1078. Generally, the quorum drive cannot have a mount point to another disk. This
  1079. is because the quorum drive is not allowed to be dependent on another resource.
  1080. A mount point from clustered source S to quorum target Q is allowed. The
  1081. dependency requirement is that quorum drive Q must come online before
  1082. source drive S (i.e. S depends on Q).
  1083. A mount point from quorum source Q to a clustered target T is not allowed because
  1084. this would require T to be online before Q (i.e. Q depnends on T), and the
  1085. quorum drive cannot have dependencies.
  1086. A mount point from quorum source Q to a nonclustered target C is invalid.
  1087. Mount points to non-clustered disks:
  1088. These types of mountpoints should not be used.
  1089. Configurations supported:
  1090. C is a non-clustered disk.
  1091. X, Y are clustered disks, not quorum disks.
  1092. Q is quorum disk.
  1093. Source Target Status
  1094. ------ ------ ------------------------------------------------------------
  1095. C --> Q Not supported. Log error to system event log.
  1096. C --> X Not supported. Log error to system event log.
  1097. X --> C Not supported. We never process non-clustered target C.
  1098. X --> Q Valid if drive X is dependent on drive Q (same group).
  1099. X --> Y Valid if drive X is dependent on drive Y (Y online first).
  1100. Q --> X Invalid. Quorum drive cannot be dependent on another resource.
  1101. Q --> C Not supported. We never process target C.
  1102. Future work:
  1103. Log event message when invalid mount point used.
  1104. Arguments:
  1105. MpName - Possible mount point. This will either be a mount point or a drive letter
  1106. (which is actually a mount point). Format can be:
  1107. x:\ [Note trailing backslash!]
  1108. x:\some-mp-name\ [Note trailing backslash!]
  1109. x:\some-dir\some-mp-name\ [Note trailing backslash!]
  1110. TargetVol - Mount point target volume name. Format:
  1111. \\?\Volume{GUID}\ [Note trailing backslash!]
  1112. ResourceEntry - Pointer to the DISK_RESOURCE structure for the target volume.
  1113. Return Value:
  1114. TRUE if the mount point is allowed.
  1115. --*/
  1116. {
  1117. DWORD srcSignature;
  1118. DWORD targetSignature = ResourceEntry->DiskInfo.Params.Signature;
  1119. DWORD quorumSignature;
  1120. DWORD dwError = NO_ERROR;
  1121. DWORD messageId = 0;
  1122. BOOL mpAllowed = TRUE;
  1123. BOOL srcSigIsClustered;
  1124. BOOL dependencyCorrect;
  1125. #if DBG
  1126. (DiskpLogEvent)(
  1127. ResourceEntry->ResourceHandle,
  1128. LOG_INFORMATION,
  1129. L"IsMountPointAllowed: MP source (%1!ws!) \n",
  1130. MpName );
  1131. #endif
  1132. //
  1133. // Since the drive letter is also a mountpoint, a drive letter is valid.
  1134. //
  1135. if ( MPIsDriveLetter( MpName ) ) {
  1136. #if DBG
  1137. (DiskpLogEvent)(
  1138. ResourceEntry->ResourceHandle,
  1139. LOG_INFORMATION,
  1140. L"IsMountPointAllowed: Valid MP: MP is a drive letter \n" );
  1141. #endif
  1142. mpAllowed = TRUE;
  1143. goto FnExit;
  1144. }
  1145. //
  1146. // Get the signature of the source drive. This drive is accessible (or
  1147. // we wouldn't even have the mount point info yet) but we cannot assume it
  1148. // is a clustered drive. If this fails, we can't use the mountpoint.
  1149. //
  1150. dwError = GetSignatureForVolume( ResourceEntry, MpName, &srcSignature );
  1151. if ( NO_ERROR != dwError || !srcSignature ) {
  1152. (DiskpLogEvent)(
  1153. ResourceEntry->ResourceHandle,
  1154. LOG_ERROR,
  1155. L"IsMountPointAllowed: Unable to get signature from volume, error %1!u! \n",
  1156. dwError );
  1157. mpAllowed = FALSE;
  1158. messageId = RES_DISK_INVALID_MP_SIG_UNAVAILABLE;
  1159. goto FnExit;
  1160. }
  1161. //
  1162. // If source points back to target, this mount point is not allowed. Even though
  1163. // the mount point code seems to allow this, there are some strange circular
  1164. // dependencies that show up.
  1165. //
  1166. if ( srcSignature == targetSignature ) {
  1167. (DiskpLogEvent)(
  1168. ResourceEntry->ResourceHandle,
  1169. LOG_WARNING,
  1170. L"IsMountPointAllowed: Invalid MP: Source and target volumes are the same device \n" );
  1171. mpAllowed = FALSE;
  1172. messageId = RES_DISK_INVALID_MP_SOURCE_EQUAL_TARGET;
  1173. goto FnExit;
  1174. }
  1175. //
  1176. // If we can't enumerate the cluster disk signatures, assume that this mount
  1177. // point is not allowed.
  1178. //
  1179. dwError = CheckSignatureClustered( srcSignature, &srcSigIsClustered );
  1180. if ( NO_ERROR != dwError ) {
  1181. (DiskpLogEvent)(
  1182. ResourceEntry->ResourceHandle,
  1183. LOG_ERROR,
  1184. L"IsMountPointAllowed: Unable to enumerate disk signatures, error %1!u! \n",
  1185. dwError );
  1186. mpAllowed = FALSE;
  1187. messageId = RES_DISK_INVALID_MP_SIG_ENUMERATION_FAILED;
  1188. goto FnExit;
  1189. }
  1190. //
  1191. // In the future, a non-clustered source might be valid only if a special registry
  1192. // key is specified - this is not currently implemented. So even though this will
  1193. // work without us doing anything (on this one node - when the disk is moved to
  1194. // another node, the MP won't work), we will warn the user that it is invalid.
  1195. //
  1196. // The user would have to manually set these types of mountpoints on each node.
  1197. //
  1198. // C --> X - Invalid, but should work
  1199. // C --> Q - Invalid, but should work
  1200. if ( !srcSigIsClustered ) {
  1201. (DiskpLogEvent)(
  1202. ResourceEntry->ResourceHandle,
  1203. LOG_WARNING,
  1204. L"IsMountPointAllowed: Invalid MP: Source volume is non-clustered \n" );
  1205. mpAllowed = FALSE;
  1206. messageId = RES_DISK_INVALID_MP_SOURCE_NOT_CLUSTERED;
  1207. goto FnExit;
  1208. }
  1209. //
  1210. // At this point, the source drive is a clustered drive but we don't yet know
  1211. // what the target looks like.
  1212. //
  1213. // X --> ??
  1214. // Q --> ??
  1215. //
  1216. // Get quorum signature. If this fails, assume the mount point is not allowed.
  1217. //
  1218. dwError = GetQuorumSignature( &quorumSignature );
  1219. if ( NO_ERROR != dwError ) {
  1220. (DiskpLogEvent)(
  1221. ResourceEntry->ResourceHandle,
  1222. LOG_ERROR,
  1223. L"IsMountPointAllowed: Unable to get quorum signature, error %1!u! \n",
  1224. dwError );
  1225. mpAllowed = FALSE;
  1226. messageId = RES_DISK_INVALID_MP_QUORUM_SIG_UNAVAILABLE;
  1227. goto FnExit;
  1228. }
  1229. //
  1230. // If the source is the quorum drive, the mount point is not allowed because
  1231. // the quorum cannot be dependent on another disk resource. We already know
  1232. // that the source and target are different devices from an ealier check.
  1233. //
  1234. if ( quorumSignature == srcSignature ) {
  1235. (DiskpLogEvent)(
  1236. ResourceEntry->ResourceHandle,
  1237. LOG_WARNING,
  1238. L"IsMountPointAllowed: Invalid MP: source sig %1!x! is quorum disk, target sig %2!x! is clustered \n",
  1239. srcSignature,
  1240. targetSignature );
  1241. mpAllowed = FALSE;
  1242. messageId = RES_DISK_INVALID_MP_SOURCE_IS_QUORUM;
  1243. goto FnExit;
  1244. }
  1245. #if 0
  1246. // This config is valid only if the dependencies are correct between x and q. Fall through
  1247. // to check dependencies.
  1248. //
  1249. // If the target is the quorum drive and the source and target are different, then
  1250. // this mount point is allowed.
  1251. //
  1252. // X --> Q
  1253. if ( quorumSignature == targetSignature &&
  1254. srcSignature != targetSignature ) {
  1255. (DiskpLogEvent)(
  1256. ResourceEntry->ResourceHandle,
  1257. LOG_INFORMATION,
  1258. L"IsMountPointAllowed: Valid MP: source sig %1!x! is clustered, target sig %2!x! is quorum disk \n",
  1259. srcSignature,
  1260. targetSignature );
  1261. mpAllowed = TRUE;
  1262. goto FnExit;
  1263. }
  1264. #endif
  1265. //
  1266. // We have two possibilities left:
  1267. //
  1268. // X --> Q - valid
  1269. // X --> Y - valid
  1270. //
  1271. //
  1272. // These are valid only if the dependencies are set up correctly. Y must
  1273. // be online before X, so X is dependent on Y.
  1274. //
  1275. dependencyCorrect = FALSE;
  1276. dwError = CheckDependencies( srcSignature,
  1277. targetSignature,
  1278. &dependencyCorrect );
  1279. if ( NO_ERROR != dwError ) {
  1280. (DiskpLogEvent)(
  1281. ResourceEntry->ResourceHandle,
  1282. LOG_ERROR,
  1283. L"IsMountPointAllowed: Unable to enumerate disk dependencies, error %1!u! \n",
  1284. dwError );
  1285. mpAllowed = FALSE;
  1286. messageId = RES_DISK_INVALID_MP_ENUM_DISK_DEP_FAILED;
  1287. goto FnExit;
  1288. }
  1289. if ( dependencyCorrect ) {
  1290. #if DBG
  1291. (DiskpLogEvent)(
  1292. ResourceEntry->ResourceHandle,
  1293. LOG_INFORMATION,
  1294. L"IsMountPointAllowed: Valid MP: Dependencies are correct \n" );
  1295. #endif
  1296. mpAllowed = TRUE;
  1297. goto FnExit;
  1298. }
  1299. (DiskpLogEvent)(
  1300. ResourceEntry->ResourceHandle,
  1301. LOG_WARNING,
  1302. L"IsMountPointAllowed: Invalid MP: Dependencies are incorrect \n",
  1303. MpName );
  1304. //
  1305. // If we get here, the mount point is not allowed.
  1306. //
  1307. mpAllowed = FALSE;
  1308. messageId = RES_DISK_INVALID_MP_INVALID_DEPENDENCIES;
  1309. FnExit:
  1310. if ( !mpAllowed && messageId ) {
  1311. // Log event...
  1312. ClusResLogSystemEventByKey2(ResourceEntry->ResourceKey,
  1313. LOG_UNUSUAL,
  1314. messageId,
  1315. MpName,
  1316. TargetVol);
  1317. }
  1318. return mpAllowed;
  1319. } // IsMountPointAllowed
  1320. DWORD
  1321. CheckDependencies(
  1322. DWORD SrcSignature,
  1323. DWORD TargetSignature,
  1324. PBOOL DependencyCorrect
  1325. )
  1326. /*++
  1327. Routine Description:
  1328. Determine if the dependency between the source volume and target volume is set up
  1329. correctly. Since we are using the cluster APIs, they should insure that the
  1330. resources are in the same group and have dependencies set.
  1331. Arguments:
  1332. SrcSignature - Disk signature of the source volume.
  1333. TargetSignature - Disk signature of the targe volume.
  1334. DependencyCorrect - Indicates whether the dependency is set up correctly between the
  1335. source and target. If set up correctly, this will be returned
  1336. as TRUE.
  1337. Return Value:
  1338. Win32 error code.
  1339. --*/
  1340. {
  1341. DWORD dwError = NO_ERROR;
  1342. DEPENDENCY_INFO dependInfo;
  1343. ZeroMemory( &dependInfo, sizeof(DEPENDENCY_INFO) );
  1344. //
  1345. // We need to find the source's resources first.
  1346. //
  1347. dependInfo.SrcSignature = SrcSignature;
  1348. dependInfo.TargetSignature = TargetSignature;
  1349. dependInfo.DependencyCorrect = FALSE;
  1350. //
  1351. // Worst case assume that the dependency is invalid.
  1352. //
  1353. *DependencyCorrect = FALSE;
  1354. dwError = ResUtilEnumResources( NULL,
  1355. PHYSICAL_DISK_WSTR,
  1356. DependencyCallback,
  1357. &dependInfo
  1358. );
  1359. //
  1360. // STOP_CLUSTER_ENUMERATIONS is our way to indicate that the
  1361. // enumerations stopped (possibly early). Check for this return
  1362. // value and also if the DependencyCorrect flag was set to indicate
  1363. // status to the caller.
  1364. //
  1365. if ( STOP_CLUSTER_ENUMERATIONS == dwError ) {
  1366. dwError = NO_ERROR;
  1367. if ( dependInfo.DependencyCorrect ) {
  1368. *DependencyCorrect = TRUE;
  1369. }
  1370. }
  1371. return dwError;
  1372. } // CheckDependencies
  1373. DWORD
  1374. DependencyCallback(
  1375. RESOURCE_HANDLE hOriginal,
  1376. RESOURCE_HANDLE hResource,
  1377. PVOID lpParams
  1378. )
  1379. /*++
  1380. Routine Description:
  1381. For each enumerated disk resource, get the signature and see if it matches the
  1382. mount point source signature (passed in the DEPENDENCY_INFO structure). If it
  1383. does not match, return success so that the disk enumeration continues.
  1384. If the enumerated resource signature matches the mount point source signature,
  1385. then check the cluster dependencies and make sure they are correct. Once we
  1386. have had a match on the signatures, we need to return an error to stop the
  1387. disk enumeration, so we use STOP_CLUSTER_ENUMERATIONS as that special error
  1388. value.
  1389. If the cluster dependencies are acceptable, the DependencyCorrect flag will be
  1390. set to TRUE in the DEPENDENCY_INFO structure.
  1391. Arguments:
  1392. hOriginal - Handle to the original resource. Not used.
  1393. hResource - Handle to a cluster resource of type PHYSICAL_DISK.
  1394. lpParams - Pointer to DEPENDENCY_INFO structure.
  1395. Return Value:
  1396. STOP_CLUSTER_ENUMERATIONS - Special flag to stop the enumeration process.
  1397. Win32 error code.
  1398. --*/
  1399. {
  1400. PDEPENDENCY_INFO dependInfo = lpParams;
  1401. DWORD dwSignature;
  1402. DWORD dwError = NO_ERROR;
  1403. //
  1404. // Get the disk info and parse the signature from it.
  1405. //
  1406. dwError = GetSignatureFromDiskInfo( hResource, &dwSignature );
  1407. if ( NO_ERROR != dwError ) {
  1408. return dwError;
  1409. }
  1410. //
  1411. // Check if we have a resource handle to the source disk or to
  1412. // a different disk. If the resource is the source disk,
  1413. // enumerate the dependencies and make sure they are correct.
  1414. //
  1415. if ( dwSignature == dependInfo->SrcSignature ) {
  1416. dwError = EnumSigDependencies( hResource,
  1417. dependInfo->TargetSignature,
  1418. &dependInfo->DependencyCorrect );
  1419. //
  1420. // If the dependency check did not get an error, set a fake
  1421. // error to make the disk enumeration stop.
  1422. //
  1423. if ( NO_ERROR == dwError ) {
  1424. dwError = STOP_CLUSTER_ENUMERATIONS;
  1425. }
  1426. }
  1427. return dwError;
  1428. } // DependencyCallback
  1429. DWORD
  1430. EnumSigDependencies(
  1431. RESOURCE_HANDLE DependentResource,
  1432. DWORD DependsOnSignature,
  1433. PBOOL DependencyCorrect
  1434. )
  1435. /*++
  1436. Routine Description:
  1437. Check that the cluster disk dependencies are correct between the source and
  1438. target of the mount point.
  1439. To do this, we open the dependent resource and use the cluster APIs to enumerate
  1440. all the disk resources dependencies. For each dependency found, check for a
  1441. match of the DependsOnSignature. If the signatures match, the dependency is
  1442. correct and we are done. Otherwise, keep checking all the dependencies until
  1443. we exhaust the list or find a match.
  1444. Note: Dependency is brought online before the DependentResource.
  1445. Arguments:
  1446. DependentResource - Resource Handle to check all the dependencies.
  1447. DependsOnSignature - Signature of a possibly dependent disk. This disk must be
  1448. brought online before the DependentResource.
  1449. DependencyCorrect - Flag set to TRUE when the cluster dependencies between
  1450. the DependentResource and it's dependency (identified by the
  1451. DependsOnSignature) are correct.
  1452. Return Value:
  1453. Win32 error code.
  1454. --*/
  1455. {
  1456. HRESENUM resEnum = NULL;
  1457. HCLUSTER hCluster = NULL;
  1458. HRESOURCE dependsOnResource = NULL;
  1459. DWORD idx;
  1460. DWORD dwError = NO_ERROR;
  1461. DWORD enumType;
  1462. DWORD nameLen;
  1463. DWORD signature;
  1464. WCHAR enumNameW[MAX_PATH * 2];
  1465. __try {
  1466. hCluster = OpenCluster( NULL );
  1467. if ( NULL == hCluster ) {
  1468. dwError = GetLastError();
  1469. __leave;
  1470. }
  1471. //
  1472. // Open an enumerator for iterating through the resources.
  1473. //
  1474. resEnum = ClusterResourceOpenEnum( DependentResource,
  1475. CLUSTER_RESOURCE_ENUM_DEPENDS );
  1476. if ( !resEnum ) {
  1477. dwError = GetLastError();
  1478. __leave;
  1479. }
  1480. //
  1481. // Iterate through the dependencies.
  1482. //
  1483. idx = 0;
  1484. while ( TRUE ) {
  1485. nameLen = MAX_PATH;
  1486. ZeroMemory( enumNameW, sizeof(enumNameW) );
  1487. dwError = ClusterResourceEnum( resEnum,
  1488. idx,
  1489. &enumType,
  1490. enumNameW,
  1491. &nameLen );
  1492. if ( ERROR_NO_MORE_ITEMS == dwError ) {
  1493. //
  1494. // The list is exhausted. Indicate no error and leave. This
  1495. // just means we checked all the dependencies and we didn't find
  1496. // a match.
  1497. //
  1498. dwError = NO_ERROR;
  1499. __leave;
  1500. }
  1501. if ( ERROR_SUCCESS != dwError ) {
  1502. //
  1503. // Some type of error, we have to stop processing.
  1504. //
  1505. __leave;
  1506. }
  1507. //
  1508. // Now we have the name (in the form of a string) of a resource we are
  1509. // dependent on. We need to get the signature and compare to the
  1510. // signature passed in.
  1511. //
  1512. dependsOnResource = OpenClusterResource( hCluster,
  1513. enumNameW );
  1514. if ( NULL == dependsOnResource ) {
  1515. dwError = GetLastError();
  1516. __leave;
  1517. }
  1518. //
  1519. // Get the disk signature from the resources disk info.
  1520. //
  1521. dwError = GetSignatureFromDiskInfo( dependsOnResource, &signature );
  1522. //
  1523. // If the signature passed in matches the signature we are dependent on,
  1524. // then the dependency is correct. Otherwise, we have to keep looking.
  1525. //
  1526. if ( NO_ERROR == dwError && signature == DependsOnSignature ) {
  1527. *DependencyCorrect = TRUE;
  1528. dwError = NO_ERROR;
  1529. __leave;
  1530. }
  1531. //
  1532. // Look at the next enumeration resource.
  1533. //
  1534. CloseClusterResource( dependsOnResource );
  1535. dependsOnResource = NULL;
  1536. idx++;
  1537. }
  1538. } __finally {
  1539. if ( dependsOnResource ) {
  1540. CloseClusterResource( dependsOnResource );
  1541. }
  1542. if ( resEnum ) {
  1543. ClusterResourceCloseEnum( resEnum );
  1544. }
  1545. if ( hCluster ) {
  1546. CloseCluster( hCluster );
  1547. }
  1548. }
  1549. return dwError;
  1550. } // EnumSigDependencies
  1551. DWORD
  1552. CheckSignatureClustered(
  1553. DWORD Signature,
  1554. PBOOL IsClustered
  1555. )
  1556. /*++
  1557. Routine Description:
  1558. Determine if the specified disk signature belongs to a clustered disk.
  1559. Enumerates the cluster physical disks and tries to find a signature
  1560. match.
  1561. The enumeration returns STOP_CLUSTER_ENUMERATIONS when it has found
  1562. a matching signature. This special error code is to stop the disk
  1563. enumeration.
  1564. Arguments:
  1565. Signature - Disk signature to be checked.
  1566. IsClustered - Flag indicating disk is clustered. If TRUE, disk is a
  1567. clustered disk.
  1568. Return Value:
  1569. Win32 error code.
  1570. --*/
  1571. {
  1572. DWORD dwError = NO_ERROR;
  1573. SIG_INFO sigInfo;
  1574. ZeroMemory( &sigInfo, sizeof(SIG_INFO) );
  1575. sigInfo.Signature = Signature;
  1576. sigInfo.Clustered = FALSE;
  1577. *IsClustered = FALSE;
  1578. dwError = ResUtilEnumResources( NULL,
  1579. PHYSICAL_DISK_WSTR,
  1580. SigInfoCallback,
  1581. &sigInfo
  1582. );
  1583. if ( STOP_CLUSTER_ENUMERATIONS == dwError && sigInfo.Clustered ) {
  1584. dwError = NO_ERROR;
  1585. *IsClustered = TRUE;
  1586. }
  1587. return dwError;
  1588. } // CheckSignatureClustered
  1589. DWORD
  1590. SigInfoCallback(
  1591. RESOURCE_HANDLE hOriginal,
  1592. RESOURCE_HANDLE hResource,
  1593. PVOID lpParams
  1594. )
  1595. /*++
  1596. Routine Description:
  1597. For each enumerated disk resource, get the signature and see if it matches the
  1598. specified disk signature (passed in the SIG_INFO structure). If it does not
  1599. match, return success so that the disk enumeration continues.
  1600. If the enumerated resource signature matches the mount point source signature,
  1601. sets the Clustered flag in the SIG_INFO structure to TRUE.
  1602. Arguments:
  1603. hOriginal - Handle to the original resource. Not used.
  1604. hResource - Handle to a cluster resource of type PHYSICAL_DISK.
  1605. lpParams - Pointer to SIGN_INFO structure.
  1606. Return Value:
  1607. STOP_CLUSTER_ENUMERATIONS - Special flag to stop the enumeration process.
  1608. Win32 error code.
  1609. --*/
  1610. {
  1611. PSIG_INFO sigInfo = lpParams;
  1612. DWORD dwSignature;
  1613. DWORD dwError = NO_ERROR;
  1614. //
  1615. // Get the disk info and parse the signature from it.
  1616. //
  1617. dwError = GetSignatureFromDiskInfo( hResource, &dwSignature );
  1618. if ( NO_ERROR != dwError ) {
  1619. return dwError;
  1620. }
  1621. if ( dwSignature == sigInfo->Signature ) {
  1622. sigInfo->Clustered = TRUE;
  1623. //
  1624. // Return an error to stop the enumeration.
  1625. //
  1626. dwError = STOP_CLUSTER_ENUMERATIONS;
  1627. }
  1628. return dwError;
  1629. } // SigInfoCallback
  1630. DWORD
  1631. GetSignatureFromDiskInfo(
  1632. RESOURCE_HANDLE hResource,
  1633. DWORD *dwSignature
  1634. )
  1635. /*++
  1636. Routine Description:
  1637. Get the signature for the given volume from the cluster using
  1638. ClusterResourceControl.
  1639. Arguments:
  1640. hResource - Handle to a cluster resource of type PHYSICAL_DISK.
  1641. Signature - On success, the signature is returned into this pointer.
  1642. Return Value:
  1643. Win32 error code.
  1644. --*/
  1645. {
  1646. PBYTE outBuffer = NULL;
  1647. PCLUSPROP_DISK_SIGNATURE diskSignature;
  1648. DWORD dwError;
  1649. DWORD dwBytesReturned;
  1650. DWORD signature;
  1651. *dwSignature = 0;
  1652. dwError = WrapClusterResourceControl( hResource,
  1653. CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO,
  1654. &outBuffer,
  1655. &dwBytesReturned );
  1656. if ( NO_ERROR == dwError && outBuffer ) {
  1657. //
  1658. // CLUSPROP_DISK_SIGNATURE must always begin the value list returned from the
  1659. // CLUSCTL code.
  1660. //
  1661. diskSignature = (PCLUSPROP_DISK_SIGNATURE)outBuffer;
  1662. if ( diskSignature->Syntax.dw != CLUSPROP_SYNTAX_DISK_SIGNATURE ) {
  1663. //
  1664. // Invalid syntax.
  1665. //
  1666. dwError = ERROR_BAD_FORMAT;
  1667. } else if ( diskSignature->cbLength != sizeof(DWORD) ) {
  1668. //
  1669. // Invalid length.
  1670. //
  1671. dwError = ERROR_BAD_FORMAT;
  1672. } else {
  1673. //
  1674. // Return the signature.
  1675. //
  1676. *dwSignature = diskSignature->dw;
  1677. }
  1678. }
  1679. if (outBuffer) {
  1680. LocalFree(outBuffer);
  1681. }
  1682. return dwError;
  1683. } // GetSignatureFromDiskInfo
  1684. DWORD
  1685. GetSignatureForVolume(
  1686. PDISK_RESOURCE ResourceEntry,
  1687. PWSTR MpName,
  1688. PDWORD Signature
  1689. )
  1690. /*++
  1691. Routine Description:
  1692. Get the signature for the given volume. The signature is found by issuing
  1693. IOCTL_DISK_GET_DRIVE_LAYOUT_EX or IOCTL_DISK_GET_DRIVE_LAYOUT.
  1694. The volume must be online for this to work and not reserved by another node.
  1695. Arguments:
  1696. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  1697. MpName - Possible mount point. This will either be a mount point or a drive letter
  1698. (which is actually a mount point). Format can be:
  1699. x:\ [Note trailing backslash!]
  1700. x:\some-mp-name\ [Note trailing backslash!]
  1701. x:\some-dir\some-mp-name\ [Note trailing backslash!]
  1702. Signature - On success, the signature is returned into this pointer.
  1703. Return Value:
  1704. Win32 error code.
  1705. --*/
  1706. {
  1707. PDRIVE_LAYOUT_INFORMATION_EX layoutEx = NULL;
  1708. PDRIVE_LAYOUT_INFORMATION layout = NULL;
  1709. HANDLE handle = NULL;
  1710. DWORD bytesReturned;
  1711. DWORD dwError = NO_ERROR;
  1712. DWORD lenChars = 0;
  1713. BOOL restoreStr = FALSE;
  1714. WCHAR replaceChar;
  1715. WCHAR drive[ 64 ];
  1716. __try {
  1717. if ( !MpName || !Signature ) {
  1718. dwError = ERROR_INVALID_PARAMETER;
  1719. __leave;
  1720. }
  1721. *Signature = 0;
  1722. #if DBG
  1723. (DiskpLogEvent)(
  1724. ResourceEntry->ResourceHandle,
  1725. LOG_INFORMATION,
  1726. L"GetSignatureForVolume: Checking mount point (%1!ws!) \n",
  1727. MpName );
  1728. #endif
  1729. //
  1730. // If mount point is of form x:\aaa\bbb, temporarily strip off the
  1731. // string to make it into a drive letter.
  1732. //
  1733. lenChars = wcslen( MpName );
  1734. if ( lenChars < 3 ) {
  1735. dwError = ERROR_INVALID_PARAMETER;
  1736. __leave;
  1737. }
  1738. if ( lenChars > 3 ) {
  1739. restoreStr = TRUE;
  1740. replaceChar = (WCHAR)*(MpName + CHAR_TO_REPLACE);
  1741. (WCHAR)*(MpName + CHAR_TO_REPLACE) = 0;
  1742. }
  1743. //
  1744. // Make the name into the form: \\?\x: [Note: no trailing backslash!]
  1745. //
  1746. ZeroMemory( drive, sizeof( drive ) );
  1747. wcscpy( drive, L"\\\\.\\" );
  1748. wcscat( drive, MpName );
  1749. #if DBG
  1750. (DiskpLogEvent)(
  1751. ResourceEntry->ResourceHandle,
  1752. LOG_INFORMATION,
  1753. L"GetSignatureForVolume: CreateFile using %1!ws! \n",
  1754. drive );
  1755. #endif
  1756. handle = CreateFileW( drive,
  1757. GENERIC_READ,
  1758. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1759. NULL,
  1760. OPEN_EXISTING,
  1761. FILE_ATTRIBUTE_NORMAL,
  1762. NULL );
  1763. if ( INVALID_HANDLE_VALUE == handle ) {
  1764. dwError = GetLastError();
  1765. (DiskpLogEvent)(
  1766. ResourceEntry->ResourceHandle,
  1767. LOG_ERROR,
  1768. L"GetSignatureForVolume: CreateFile failed, error %1!u! \n",
  1769. dwError );
  1770. __leave;
  1771. }
  1772. //
  1773. // Try IOCTL_DISK_GET_DRIVE_LAYOUT_EX first. If it fails, try with
  1774. // IOCTL_DISK_GET_DRIVE_LAYOUT.
  1775. //
  1776. layoutEx = DoIoctlAndAllocate( handle,
  1777. IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
  1778. 0,
  1779. 0,
  1780. &bytesReturned );
  1781. if ( !layoutEx ) {
  1782. dwError = GetLastError();
  1783. (DiskpLogEvent)(
  1784. ResourceEntry->ResourceHandle,
  1785. LOG_WARNING,
  1786. L"GetSignatureForVolume: IOCTL_DISK_GET_DRIVE_LAYOUT_EX failed, error %1!u! \n",
  1787. dwError );
  1788. //
  1789. // Try the old IOCTL...
  1790. //
  1791. layout = DoIoctlAndAllocate( handle,
  1792. IOCTL_DISK_GET_DRIVE_LAYOUT,
  1793. 0,
  1794. 0,
  1795. &bytesReturned );
  1796. if ( !layout ) {
  1797. dwError = GetLastError();
  1798. (DiskpLogEvent)(
  1799. ResourceEntry->ResourceHandle,
  1800. LOG_ERROR,
  1801. L"GetSignatureForVolume: IOCTL_DISK_GET_DRIVE_LAYOUT failed, error %1!u! \n",
  1802. dwError );
  1803. //
  1804. // Fall through and cleanup.
  1805. //
  1806. } else {
  1807. //
  1808. // Get the signature from the returned structure and return it to
  1809. // the caller.
  1810. //
  1811. *Signature = layout->Signature;
  1812. //
  1813. // Have to set NO_ERROR here because the current value shows the
  1814. // first IOCTL that failed.
  1815. //
  1816. dwError = NO_ERROR;
  1817. }
  1818. } else {
  1819. //
  1820. // Get the signature from the returned structure and return it to
  1821. // the caller.
  1822. //
  1823. if ( PARTITION_STYLE_MBR == layoutEx->PartitionStyle ) {
  1824. *Signature = layoutEx->Mbr.Signature;
  1825. } else if ( PARTITION_STYLE_GPT == layoutEx->PartitionStyle ) {
  1826. //
  1827. // Since our signatures won't handle the GPT GUID, we have to
  1828. // simulate a signature.
  1829. //
  1830. *Signature = ClusterHashGuid(layoutEx->Gpt.DiskId);
  1831. }
  1832. }
  1833. } __finally {
  1834. if ( layoutEx ) {
  1835. free( layoutEx );
  1836. }
  1837. if ( layout ) {
  1838. free( layout );
  1839. }
  1840. if ( handle ) {
  1841. CloseHandle( handle );
  1842. }
  1843. //
  1844. // Restore string to original format.
  1845. //
  1846. if ( lenChars > 3 && restoreStr ) {
  1847. (WCHAR)*(MpName + CHAR_TO_REPLACE) = replaceChar;
  1848. }
  1849. }
  1850. #if DBG
  1851. (DiskpLogEvent)(
  1852. ResourceEntry->ResourceHandle,
  1853. LOG_INFORMATION,
  1854. L"GetSignatureForVolume: Returning signature %1!x! for mount point (%2!ws!) \n",
  1855. *Signature,
  1856. MpName );
  1857. #endif
  1858. return dwError;
  1859. } // GetSignatureForVolume
  1860. BOOL
  1861. GetOffsetFromPartNo(
  1862. DWORD PartitionNo,
  1863. PMOUNTIE_INFO Info,
  1864. PLARGE_INTEGER Offset
  1865. )
  1866. /*++
  1867. Routine Description:
  1868. Given the partition number and the drive layout, return the byte offset for the
  1869. specified partition.
  1870. Arguments:
  1871. PartitionNo - Supplies the partition number. Zero is invalid since partition zero
  1872. represents the entire disk.
  1873. Info - Pointer to MOUNTIE_INFO based on drive layout information.
  1874. Offset - Pointer to hold the returned byte offset for the partition. Space is
  1875. allocated by the caller.
  1876. Return Value:
  1877. TRUE if successful.
  1878. --*/
  1879. {
  1880. PMOUNTIE_PARTITION entry;
  1881. DWORD idx;
  1882. DWORD partitionCount;
  1883. BOOL retVal = FALSE;
  1884. if ( !PartitionNo || !Info || !Offset ) {
  1885. goto FnExit;
  1886. }
  1887. if ( 0 == Info->Volume ) {
  1888. goto FnExit;
  1889. }
  1890. #if DBG
  1891. (DiskpLogEvent)(
  1892. NULL, // ResourceEntry->ResourceHandle,
  1893. LOG_INFORMATION,
  1894. L"GetOffsetFromPartNo: partition %1!u! \n",
  1895. PartitionNo );
  1896. #endif
  1897. Offset->QuadPart = 0; // Offset of zero is invalid. This will indicate an error.
  1898. partitionCount = Info->Volume->PartitionCount;
  1899. entry = Info->Volume->Partition;
  1900. for ( idx = 0; idx < partitionCount; ++idx, ++entry) {
  1901. #if DBG
  1902. (DiskpLogEvent)(
  1903. NULL, // ResourceEntry->ResourceHandle,
  1904. LOG_INFORMATION,
  1905. L"GetOffsetFromPartNo: index %1!u! offset %2!x! \n",
  1906. idx,
  1907. entry->StartingOffset.LowPart );
  1908. #endif
  1909. if ( entry->PartitionNumber == PartitionNo ) {
  1910. Offset->QuadPart = entry->StartingOffset.QuadPart;
  1911. retVal = TRUE;
  1912. break;
  1913. }
  1914. }
  1915. FnExit:
  1916. return retVal;
  1917. } // GetOffsetFromPartNo
  1918. BOOL
  1919. GetPartNoFromOffset(
  1920. PLARGE_INTEGER Offset,
  1921. PMOUNTIE_INFO Info,
  1922. PDWORD PartitionNumber
  1923. )
  1924. /*++
  1925. Routine Description:
  1926. Given the offset and the drive layout, return the partition number for the specified offset.
  1927. Arguments:
  1928. Offset - Pointer to the byte offset.
  1929. Info - Pointer to MOUNTIE_INFO based on drive layout information.
  1930. PartitionNo - Pointer to hold the returned partition number. Space is allocated by the
  1931. caller.
  1932. Return Value:
  1933. TRUE if successful.
  1934. --*/
  1935. {
  1936. PMOUNTIE_PARTITION entry;
  1937. DWORD idx;
  1938. DWORD partitionCount;
  1939. BOOL retVal = FALSE;
  1940. if ( !Offset->QuadPart || !Info || !PartitionNumber) {
  1941. goto FnExit;
  1942. }
  1943. if ( 0 == Info->Volume ) {
  1944. goto FnExit;
  1945. }
  1946. *PartitionNumber = 0; // Partition zero is invalid. This will indicate an error.
  1947. partitionCount = Info->Volume->PartitionCount;
  1948. entry = Info->Volume->Partition;
  1949. for ( idx = 0; idx < partitionCount; ++idx, ++entry ) {
  1950. if ( entry->StartingOffset.QuadPart == Offset->QuadPart ) {
  1951. *PartitionNumber = entry->PartitionNumber;
  1952. retVal = TRUE;
  1953. break;
  1954. }
  1955. }
  1956. FnExit:
  1957. return retVal;
  1958. } // GetPartNoFromOffset
  1959. VOID
  1960. PrintStrList(
  1961. PDISK_RESOURCE ResourceEntry,
  1962. LPWSTR MultiSzList,
  1963. DWORD ListBytes
  1964. )
  1965. /*++
  1966. Routine Description:
  1967. Display the list in the cluster log.
  1968. Arguments:
  1969. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  1970. MultiSzList - REG_MULTI_SZ string
  1971. ListBytes - Number of bytes in MultiSzList, not number of WCHARs!
  1972. Return Value:
  1973. None.
  1974. --*/
  1975. {
  1976. PWSTR currentStr;
  1977. PWCHAR data;
  1978. LARGE_INTEGER offset;
  1979. DWORD currentStrLenChars = 0;
  1980. DWORD count;
  1981. if ( !ResourceEntry || !MultiSzList || 0 == ListBytes ) {
  1982. return;
  1983. }
  1984. (DiskpLogEvent)(
  1985. ResourceEntry->ResourceHandle,
  1986. LOG_INFORMATION,
  1987. L" Offset String \n" );
  1988. (DiskpLogEvent)(
  1989. ResourceEntry->ResourceHandle,
  1990. LOG_INFORMATION,
  1991. L"================ ====================================== \n" );
  1992. EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
  1993. currentStr = (PWCHAR)MultiSzList;
  1994. currentStrLenChars = wcslen( currentStr );
  1995. while ( currentStrLenChars ) {
  1996. data = NULL;
  1997. offset.QuadPart = 0;
  1998. //
  1999. // Convert the offset from a string to a large integer value.
  2000. //
  2001. count = swscanf( currentStr, L"%I64x ", &offset.QuadPart );
  2002. if ( 0 == count ) {
  2003. (DiskpLogEvent)(
  2004. ResourceEntry->ResourceHandle,
  2005. LOG_ERROR,
  2006. L"Error: Unable to parse offset from currentStr (%1!ws!) \n",
  2007. currentStr );
  2008. // Stop processing the list...
  2009. break;
  2010. }
  2011. //
  2012. // Data starts just after the first space.
  2013. //
  2014. data = wcschr( currentStr, SPACE_CHAR );
  2015. if ( !data || wcslen(data) < 3 ) {
  2016. (DiskpLogEvent)(
  2017. ResourceEntry->ResourceHandle,
  2018. LOG_INFORMATION,
  2019. L"Error: Unable to get mount point str from currentStr %1!ws! \n",
  2020. currentStr );
  2021. // Stop processing the list...
  2022. break;
  2023. }
  2024. //
  2025. // Skip past the space character. Note that the length was previously validated.
  2026. //
  2027. if ( SPACE_CHAR == *data ) {
  2028. data++;
  2029. }
  2030. (DiskpLogEvent)(
  2031. ResourceEntry->ResourceHandle,
  2032. LOG_INFORMATION,
  2033. L"%1!08X!%2!08X! %3!ws! \n", // couldn't get !I64X! to work...
  2034. offset.HighPart,
  2035. offset.LowPart,
  2036. data );
  2037. currentStr += currentStrLenChars + 1;
  2038. currentStrLenChars = wcslen( currentStr );
  2039. }
  2040. LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
  2041. (DiskpLogEvent)(
  2042. ResourceEntry->ResourceHandle,
  2043. LOG_INFORMATION,
  2044. L"*** End of list *** \n" );
  2045. } // PrintStrList
  2046. static
  2047. DWORD
  2048. SetMPListThread(
  2049. LPVOID lpThreadParameter
  2050. )
  2051. /*++
  2052. Routine Description:
  2053. Mount point list update thread. Updates the cluster data base.
  2054. Arguments:
  2055. lpThreadParameter - stores ResourceEntry.
  2056. Return Value:
  2057. None
  2058. --*/
  2059. {
  2060. DWORD dwError;
  2061. PDISK_RESOURCE ResourceEntry = lpThreadParameter;
  2062. DWORD idx;
  2063. (DiskpLogEvent)(
  2064. ResourceEntry->ResourceHandle,
  2065. LOG_INFORMATION,
  2066. L"SetMPListThread: started.\n");
  2067. //
  2068. // Will die in 10 minutes if unsuccessful
  2069. //
  2070. for ( idx = 0; idx < 300; ++idx ) {
  2071. //
  2072. // Wait for either the terminate event or the timeout
  2073. //
  2074. dwError = WaitForSingleObject( DisksTerminateEvent, 2000 );
  2075. if ( WAIT_TIMEOUT == dwError ) {
  2076. EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
  2077. #if DBG
  2078. DumpDiskInfoParams( ResourceEntry );
  2079. #endif
  2080. //
  2081. // Bug in ResUtilSetPropertyParameterBlock. It will update the cluster
  2082. // database with updated MULTI_SZ data, but it doesn't clear the values
  2083. // appropriately. Use ClusterRegDeleteValue to make sure the lists are
  2084. // cleared if they have been deleted.
  2085. //
  2086. if ( !ResourceEntry->DiskInfo.Params.MPVolGuids &&
  2087. 0 == ResourceEntry->DiskInfo.Params.MPVolGuidsSize ) {
  2088. dwError = ClusterRegDeleteValue( ResourceEntry->ResourceParametersKey,
  2089. CLUSREG_NAME_PHYSDISK_MPVOLGUIDS );
  2090. }
  2091. //
  2092. // Timer expired. Update the cluster database.
  2093. //
  2094. dwError = ResUtilSetPropertyParameterBlock( ResourceEntry->ResourceParametersKey,
  2095. DiskResourcePrivateProperties,
  2096. NULL,
  2097. (LPBYTE) &ResourceEntry->DiskInfo.Params,
  2098. NULL,
  2099. 0,
  2100. NULL );
  2101. LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
  2102. if ( ERROR_SUCCESS == dwError ) {
  2103. //
  2104. // We're done.
  2105. //
  2106. (DiskpLogEvent)(
  2107. ResourceEntry->ResourceHandle,
  2108. LOG_INFORMATION,
  2109. L"SetMPListThread: mount point info updated in cluster data base \n" );
  2110. break;
  2111. } else if ( ERROR_SHARING_PAUSED != dwError ) {
  2112. //
  2113. // If the drive is not yet online, we should have seen ERROR_SHARING_PAUSED. If
  2114. // we see any other error, something bad happened.
  2115. //
  2116. (DiskpLogEvent)(
  2117. ResourceEntry->ResourceHandle,
  2118. LOG_ERROR,
  2119. L"SetMPListThread: Failed to update cluster data base, error = %1!u! \n",
  2120. dwError );
  2121. break;
  2122. }
  2123. (DiskpLogEvent)(
  2124. ResourceEntry->ResourceHandle,
  2125. LOG_INFORMATION,
  2126. L"SetMPListThread: Wait again for event or timeout, count %1!u! \n",
  2127. idx );
  2128. } else {
  2129. //
  2130. // The terminate event is possibly set.
  2131. //
  2132. (DiskpLogEvent)(
  2133. ResourceEntry->ResourceHandle,
  2134. LOG_ERROR,
  2135. L"SetMPListThread: WaitForSingleObject returned error = %1!u! \n",
  2136. dwError );
  2137. break;
  2138. }
  2139. }
  2140. //
  2141. // Thread ending, clear the flag.
  2142. //
  2143. InterlockedExchange( &ResourceEntry->MPInfo.MPUpdateThreadIsActive, 0 );
  2144. return(ERROR_SUCCESS);
  2145. } // SetMPListThread
  2146. DWORD
  2147. PostMPInfoIntoRegistry(
  2148. PDISK_RESOURCE ResourceEntry
  2149. )
  2150. /*++
  2151. Routine Description:
  2152. Set the DiskResourcePrivateProperties in the cluster database. If the disk
  2153. is not yet online, create a thread to update the cluster database. The disk
  2154. might not be fully online if we are in the process of bringing the quorum disk
  2155. online and trying to update the mount point information.
  2156. Arguments:
  2157. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  2158. Return Value:
  2159. Win32 error code.
  2160. --*/
  2161. {
  2162. DWORD dwError;
  2163. //
  2164. // Update the cluster database.
  2165. //
  2166. EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
  2167. #if DBG
  2168. DumpDiskInfoParams( ResourceEntry );
  2169. #endif
  2170. //
  2171. // Bug in ResUtilSetPropertyParameterBlock. It will update the cluster
  2172. // database with updated MULTI_SZ data, but it doesn't clear the values
  2173. // appropriately. Use ClusterRegDeleteValue to make sure the lists are
  2174. // cleared if they have been deleted.
  2175. //
  2176. if ( !ResourceEntry->DiskInfo.Params.MPVolGuids &&
  2177. 0 == ResourceEntry->DiskInfo.Params.MPVolGuidsSize ) {
  2178. dwError = ClusterRegDeleteValue( ResourceEntry->ResourceParametersKey,
  2179. CLUSREG_NAME_PHYSDISK_MPVOLGUIDS );
  2180. }
  2181. dwError = ResUtilSetPropertyParameterBlock( ResourceEntry->ResourceParametersKey,
  2182. DiskResourcePrivateProperties,
  2183. NULL,
  2184. (LPBYTE) &ResourceEntry->DiskInfo.Params,
  2185. NULL,
  2186. 0,
  2187. NULL );
  2188. LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
  2189. //
  2190. // If the update failed and the disk is not yet online, it will fail with
  2191. // ERROR_SHARING_PAUSED. In this case, create a thread to update the cluster
  2192. // data base. Any other error or success should simply continue.
  2193. //
  2194. if ( ERROR_SHARING_PAUSED == dwError ) {
  2195. //
  2196. // Check if the thread is already active. If it is, exit with an error.
  2197. //
  2198. if ( InterlockedCompareExchange(
  2199. &ResourceEntry->MPInfo.MPUpdateThreadIsActive,
  2200. 1, 0 ) ) {
  2201. (DiskpLogEvent)(
  2202. ResourceEntry->ResourceHandle,
  2203. LOG_WARNING,
  2204. L"PostMPInfoIntoRegistry: MountPoint thread is already running \n" );
  2205. dwError = ERROR_ALREADY_EXISTS;
  2206. } else {
  2207. HANDLE thread;
  2208. DWORD threadId;
  2209. thread = CreateThread( NULL,
  2210. 0,
  2211. SetMPListThread,
  2212. ResourceEntry,
  2213. 0,
  2214. &threadId );
  2215. if ( NULL == thread ) {
  2216. //
  2217. // Thread creation failed. Log error, clear thread active flag,
  2218. // and return.
  2219. //
  2220. dwError = GetLastError();
  2221. (DiskpLogEvent)(
  2222. ResourceEntry->ResourceHandle,
  2223. LOG_ERROR,
  2224. L"PostMPInfoIntoRegistry: CreateThread failed, error %1!u!\n",
  2225. dwError );
  2226. InterlockedExchange( &ResourceEntry->MPInfo.MPUpdateThreadIsActive, 0 );
  2227. } else {
  2228. (DiskpLogEvent)(
  2229. ResourceEntry->ResourceHandle,
  2230. LOG_INFORMATION,
  2231. L"PostMPInfoIntoRegistry: Thread created \n" );
  2232. //
  2233. // Thread created. Indicate no error.
  2234. //
  2235. CloseHandle( thread );
  2236. dwError = ERROR_SUCCESS;
  2237. }
  2238. }
  2239. } else {
  2240. (DiskpLogEvent)(
  2241. ResourceEntry->ResourceHandle,
  2242. ( NO_ERROR == dwError ? LOG_INFORMATION : LOG_ERROR ),
  2243. L"PostMPInfoIntoRegistry: ResUtilSetPropertyParameterBlock returned %1!u! \n",
  2244. dwError );
  2245. }
  2246. return dwError;
  2247. } // PostMpInfoIntoRegistry
  2248. DWORD
  2249. WrapClusterResourceControl(
  2250. RESOURCE_HANDLE hResource,
  2251. DWORD dwControlCode,
  2252. LPVOID *OutBuffer,
  2253. DWORD *dwBytesReturned
  2254. )
  2255. /*++
  2256. Routine Description:
  2257. Wrap the call to ClusterResourceControl. This routine will allocate the
  2258. output buffer, call ClusterResourceControl, and return the address of the
  2259. output buffer to the caller.
  2260. The caller of this routine is responsible for freeing the output buffer.
  2261. Arguments:
  2262. hResource - Handle to a cluster resource of type PHYSICAL_DISK.
  2263. dwControlCode - The resource control code to be performed.
  2264. OutBuffer - Address to store the output buffer.
  2265. dwBytesReturned - Number of bytes in the allocated output buffer.
  2266. Return Value:
  2267. Win32 error code.
  2268. --*/
  2269. {
  2270. DWORD dwError;
  2271. DWORD cbOutBufferSize = MAX_PATH;
  2272. DWORD cbResultSize = MAX_PATH;
  2273. LPVOID tempOutBuffer = LocalAlloc( LPTR, cbOutBufferSize );
  2274. dwError = ClusterResourceControl( hResource,
  2275. NULL,
  2276. dwControlCode,
  2277. NULL,
  2278. 0,
  2279. tempOutBuffer,
  2280. cbOutBufferSize,
  2281. &cbResultSize );
  2282. //
  2283. // Reallocation routine if buffer is too small
  2284. //
  2285. if ( ERROR_MORE_DATA == dwError )
  2286. {
  2287. LocalFree( tempOutBuffer );
  2288. cbOutBufferSize = cbResultSize;
  2289. tempOutBuffer = LocalAlloc( LPTR, cbOutBufferSize );
  2290. dwError = ClusterResourceControl( hResource,
  2291. NULL,
  2292. dwControlCode,
  2293. NULL,
  2294. 0,
  2295. tempOutBuffer,
  2296. cbOutBufferSize,
  2297. &cbResultSize );
  2298. }
  2299. //
  2300. // On success, give the user the allocated buffer. The user is responsible
  2301. // for freeing this buffer. On failure, free the buffer and return a status.
  2302. //
  2303. if ( NO_ERROR == dwError ) {
  2304. *OutBuffer = tempOutBuffer;
  2305. *dwBytesReturned = cbResultSize;
  2306. } else {
  2307. *OutBuffer = NULL;
  2308. *dwBytesReturned = 0;
  2309. LocalFree( tempOutBuffer );
  2310. }
  2311. return dwError;
  2312. } // WrapClusterResourceControl
  2313. VOID
  2314. DisksMountPointCleanup(
  2315. PDISK_RESOURCE ResourceEntry
  2316. )
  2317. /*++
  2318. Routine Description:
  2319. Cleanup everything the mount point code used.
  2320. This routine should be called in DisksClose.
  2321. Arguments:
  2322. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  2323. Return Value:
  2324. None.
  2325. --*/
  2326. {
  2327. (DiskpLogEvent)(
  2328. ResourceEntry->ResourceHandle,
  2329. LOG_INFORMATION,
  2330. L"DisksMountPointCleanup: Cleanup mount point information \n" );
  2331. //
  2332. // If existing MPVolGuids list, free it.
  2333. //
  2334. if ( ResourceEntry->DiskInfo.Params.MPVolGuids ) {
  2335. LocalFree( ResourceEntry->DiskInfo.Params.MPVolGuids );
  2336. ResourceEntry->DiskInfo.Params.MPVolGuidsSize = 0;
  2337. ResourceEntry->DiskInfo.Params.MPVolGuids = NULL;
  2338. }
  2339. ResourceEntry->MPInfo.Initialized = FALSE;
  2340. DeleteCriticalSection( &ResourceEntry->MPInfo.MPLock );
  2341. } // DisksMountPointCleanup
  2342. VOID
  2343. DisksMountPointInitialize(
  2344. PDISK_RESOURCE ResourceEntry
  2345. )
  2346. /*++
  2347. Routine Description:
  2348. Prepare the mount point structures in the ResourceEntry for use.
  2349. This routine should be called in DisksOpen.
  2350. Arguments:
  2351. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  2352. Return Value:
  2353. None.
  2354. --*/
  2355. {
  2356. InitializeCriticalSection( &ResourceEntry->MPInfo.MPLock );
  2357. ResourceEntry->MPInfo.Initialized = TRUE;
  2358. InterlockedExchange( &ResourceEntry->MPInfo.MPUpdateThreadIsActive, 0 );
  2359. InterlockedExchange( &ResourceEntry->MPInfo.MPListCreateInProcess, 0 );
  2360. } // DisksMountPointInitialize
  2361. DWORD
  2362. DisksUpdateMPList(
  2363. PDISK_RESOURCE ResourceEntry
  2364. )
  2365. /*++
  2366. Routine Description:
  2367. Validate the mount points.
  2368. Arguments:
  2369. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  2370. Return Value:
  2371. Win32 error code.
  2372. --*/
  2373. {
  2374. DWORD dwError = NO_ERROR;
  2375. (DiskpLogEvent)(
  2376. ResourceEntry->ResourceHandle,
  2377. LOG_INFORMATION,
  2378. L"DisksUpdateMPList: Processing PNP mountpoint notification \n" );
  2379. //
  2380. // Check if the MPList is in process of being updated. If it is, exit with an error.
  2381. //
  2382. if ( InterlockedCompareExchange(
  2383. &ResourceEntry->MPInfo.MPListCreateInProcess,
  2384. 1, 0 ) ) {
  2385. (DiskpLogEvent)(
  2386. ResourceEntry->ResourceHandle,
  2387. LOG_INFORMATION,
  2388. L"DisksUpdateMPList: Update in process, bypassing PNP notification \n" );
  2389. return ERROR_BUSY;
  2390. }
  2391. dwError = ValidateMountPoints( ResourceEntry );
  2392. InterlockedExchange( &ResourceEntry->MPInfo.MPListCreateInProcess, 0 );
  2393. return dwError;
  2394. } // DisksUpdateMPList
  2395. DWORD
  2396. DisksProcessMPControlCode(
  2397. PDISK_RESOURCE ResourceEntry,
  2398. DWORD ControlCode
  2399. )
  2400. /*++
  2401. Routine Description:
  2402. Process the disk mount point control code. Since we are in the thread
  2403. that handed us the control code (DisksResourceControl), we can't do
  2404. much except a separate thread to do the bulk of the mount point
  2405. processing.
  2406. Arguments:
  2407. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  2408. ControlCode - Cluster resource control for mount point processing.
  2409. Return Value:
  2410. --*/
  2411. {
  2412. HANDLE thread;
  2413. DWORD threadId;
  2414. DWORD dwError = NO_ERROR;
  2415. __try {
  2416. //
  2417. // Create a thread to update the mount point list. We don't need to
  2418. // copy the ResourceEntry as this pointer will be valid when the thread
  2419. // runs.
  2420. //
  2421. thread = CreateThread( NULL,
  2422. 0,
  2423. DisksUpdateMPList,
  2424. ResourceEntry,
  2425. 0,
  2426. &threadId );
  2427. if ( NULL == thread ) {
  2428. dwError = GetLastError();
  2429. (DiskpLogEvent)(
  2430. ResourceEntry->ResourceHandle,
  2431. LOG_ERROR,
  2432. L"DisksProcessMPControlCode: CreateThread failed %1!u! \n",
  2433. dwError );
  2434. __leave;
  2435. }
  2436. //
  2437. // Thread created. Indicate no error.
  2438. //
  2439. CloseHandle( thread );
  2440. dwError = NO_ERROR;
  2441. #if DBG
  2442. (DiskpLogEvent)(
  2443. ResourceEntry->ResourceHandle,
  2444. LOG_ERROR,
  2445. L"DisksProcessMPControlCode: Created thread to process control code \n" );
  2446. #endif
  2447. } __finally {
  2448. }
  2449. return dwError;
  2450. } // DisksProcessMPControlCode
  2451. DWORD
  2452. ValidateMountPoints(
  2453. IN OUT PDISK_RESOURCE ResourceEntry
  2454. )
  2455. /*++
  2456. Routine Description:
  2457. For each partition on this disk, get the mountpoints directed toward this
  2458. partition. Check each mountpoint to make sure it is allowed. For those
  2459. mountpoints not allowed, write a message to system event log indicating
  2460. why it is an invalid mountpoint.
  2461. Arguments:
  2462. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  2463. Return Value:
  2464. ERROR_INVALID_DATA - Partition info stored in MountieInfo is invalid.
  2465. Win32 error code.
  2466. --*/
  2467. {
  2468. PMOUNTIE_PARTITION entry;
  2469. DWORD dwError = ERROR_SUCCESS;
  2470. DWORD nPartitions = MountiePartitionCount( &ResourceEntry->MountieInfo );
  2471. DWORD physicalDrive = ResourceEntry->DiskInfo.PhysicalDrive;
  2472. DWORD idx;
  2473. WCHAR szGlobalDiskPartName[MAX_PATH];
  2474. WCHAR szVolumeName[MAX_PATH];
  2475. ZeroMemory( szGlobalDiskPartName, sizeof(szGlobalDiskPartName) );
  2476. ZeroMemory( szVolumeName, sizeof(szVolumeName) );
  2477. //
  2478. // Check each interesting partition. Since only "valid" partitions are
  2479. // saved in the MountieInfo structure, we will only look at those (ignoring those
  2480. // partitions that are not NTFS).
  2481. //
  2482. for ( idx = 0; idx < nPartitions; ++idx ) {
  2483. entry = MountiePartition( &ResourceEntry->MountieInfo, idx );
  2484. #if DBG
  2485. (DiskpLogEvent)(
  2486. ResourceEntry->ResourceHandle,
  2487. LOG_INFORMATION,
  2488. L"ValidateMountPoints: index %1!u! entry %2!x! \n", idx, entry );
  2489. #endif
  2490. if ( !entry ) {
  2491. (DiskpLogEvent)(
  2492. ResourceEntry->ResourceHandle,
  2493. LOG_ERROR,
  2494. L"ValidateMountPoints: no partition entry for index %1!u! \n", idx );
  2495. //
  2496. // Something bad happened to our data structures.
  2497. //
  2498. dwError = ERROR_INVALID_DATA;
  2499. break;
  2500. }
  2501. //
  2502. // Create the device name of the form:
  2503. // \\?\GLOBALROOT\Device\HarddiskX\PartitionY\ (uses trailing backslash)
  2504. //
  2505. _snwprintf( szGlobalDiskPartName,
  2506. MAX_PATH, // Number of CHARS, not bytes
  2507. GLOBALROOT_HARDDISK_PARTITION_FMT,
  2508. physicalDrive,
  2509. entry->PartitionNumber );
  2510. #if DBG
  2511. (DiskpLogEvent)(
  2512. ResourceEntry->ResourceHandle,
  2513. LOG_INFORMATION,
  2514. L"ValidateMountPoints: Using name (%1!ws!) \n",
  2515. szGlobalDiskPartName );
  2516. #endif
  2517. if ( !GetVolumeNameForVolumeMountPointW( szGlobalDiskPartName,
  2518. szVolumeName,
  2519. sizeof(szVolumeName)/sizeof(WCHAR) )) {
  2520. dwError = GetLastError();
  2521. (DiskpLogEvent)(
  2522. ResourceEntry->ResourceHandle,
  2523. LOG_ERROR,
  2524. L"ValidateMountPoints: GetVolumeNameForVolumeMountPoint for (%1!ws!) returned %2!u!\n",
  2525. szGlobalDiskPartName,
  2526. dwError );
  2527. break;
  2528. }
  2529. #if DBG
  2530. (DiskpLogEvent)(
  2531. ResourceEntry->ResourceHandle,
  2532. LOG_INFORMATION,
  2533. L"ValidateMountPoints: Returned volume name (%1!ws!) \n",
  2534. szVolumeName );
  2535. #endif
  2536. CheckMPsForVolume( ResourceEntry,
  2537. szVolumeName );
  2538. }
  2539. return dwError;
  2540. } // ValidateMountPoints
  2541. VOID
  2542. CheckMPsForVolume(
  2543. IN OUT PDISK_RESOURCE ResourceEntry,
  2544. IN PWSTR VolumeName
  2545. )
  2546. /*++
  2547. Routine Description:
  2548. For the specified volume, find all mount points directed towards this volume.
  2549. For each mountpoint, make sure it is allowed.
  2550. Arguments:
  2551. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  2552. VolumeName - Target volume for the mount point. Format is:
  2553. \\?\Volume{GUID}\ [Note trailing backslash!]
  2554. Return Value:
  2555. None.
  2556. --*/
  2557. {
  2558. PWSTR volumePaths = NULL;
  2559. PWSTR currentMP;
  2560. DWORD dwError;
  2561. __try {
  2562. //
  2563. // GetMountPoints will allocate a MultiSz buffer with
  2564. // all the mount points for this target volume.
  2565. //
  2566. dwError = GetMountPoints( VolumeName, &volumePaths );
  2567. if ( NO_ERROR != dwError || !volumePaths ) {
  2568. (DiskpLogEvent)(
  2569. ResourceEntry->ResourceHandle,
  2570. LOG_ERROR,
  2571. L"CheckMPsForVolume: GetMountPoints returns %1!u! \n", dwError );
  2572. __leave;
  2573. }
  2574. //
  2575. // Loop through each mount point in the list.
  2576. //
  2577. // Each mount point will either be a mount point or a drive letter
  2578. // (which is actually a mount point). Format can be:
  2579. //
  2580. // x:\ [Note trailing backslash!]
  2581. // x:\some-mp-name\ [Note trailing backslash!]
  2582. // x:\some-dir\some-mp-name\ [Note trailing backslash!]
  2583. //
  2584. currentMP = volumePaths;
  2585. for (;;) {
  2586. IsMountPointAllowed( currentMP, VolumeName, ResourceEntry );
  2587. //
  2588. // Skip through current mount point to end of string.
  2589. //
  2590. while (*currentMP++);
  2591. //
  2592. // If next mount point is empty, the list is exhausted.
  2593. //
  2594. if (!*currentMP) {
  2595. break;
  2596. }
  2597. }
  2598. } __finally {
  2599. if ( volumePaths ) {
  2600. LocalFree( volumePaths );
  2601. }
  2602. }
  2603. } // CheckMPsForVolume
  2604. DWORD
  2605. GetMountPoints(
  2606. PWSTR VolumeName,
  2607. PWSTR *VolumePaths
  2608. )
  2609. /*++
  2610. Routine Description:
  2611. For the specified volume, find all mount points directed towards this volume.
  2612. The mount point buffer will be allocated by this routine and must be freed by
  2613. the caller.
  2614. Arguments:
  2615. VolumeName - Target volume for the mount point. Format is:
  2616. \\?\Volume{GUID}\ [Note trailing backslash!]
  2617. VolumePaths - Pointer to a MultiSz string containing all mount points directed
  2618. toward this volume. If there are no mount points, this pointer will
  2619. be set to NULL. The caller is responsible for freeing this buffer.
  2620. Return Value:
  2621. Win32 error code.
  2622. --*/
  2623. {
  2624. DWORD lenChars;
  2625. WCHAR messageBuffer[MAX_PATH];
  2626. PWSTR paths = NULL;
  2627. PWSTR currentMP;
  2628. DWORD dwError;
  2629. if ( !VolumeName || !VolumePaths ) {
  2630. return ERROR_INVALID_PARAMETER;
  2631. }
  2632. *VolumePaths = NULL;
  2633. //
  2634. // Determine the size of the buffer we need.
  2635. //
  2636. if ( !GetVolumePathNamesForVolumeName( VolumeName, NULL, 0, &lenChars ) ) {
  2637. dwError = GetLastError();
  2638. if ( ERROR_MORE_DATA != dwError ) {
  2639. return dwError;
  2640. }
  2641. }
  2642. //
  2643. // Allocate the mount point buffer.
  2644. //
  2645. paths = LocalAlloc( 0, lenChars * sizeof(WCHAR) );
  2646. if ( !paths ) {
  2647. dwError = GetLastError();
  2648. return dwError;
  2649. }
  2650. //
  2651. // Get the mount points.
  2652. //
  2653. if ( !GetVolumePathNamesForVolumeName( VolumeName, paths, lenChars, NULL ) ) {
  2654. dwError = GetLastError();
  2655. LocalFree( paths );
  2656. return dwError;
  2657. }
  2658. //
  2659. // If no mount points, free the buffer and return to the caller.
  2660. //
  2661. if ( !paths[0] ) {
  2662. LocalFree(paths);
  2663. //
  2664. // If no mount points for this volume, return no error and a NULL
  2665. // pointer to the mount point list.
  2666. //
  2667. return NO_ERROR;
  2668. }
  2669. *VolumePaths = paths;
  2670. return NO_ERROR;
  2671. } // GetMountPoints
  2672. DWORD
  2673. ValidateListOffsets(
  2674. IN OUT PDISK_RESOURCE ResourceEntry,
  2675. IN PWSTR MasterList
  2676. )
  2677. /*++
  2678. Routine Description:
  2679. Verify each entry in the list to make sure the byte offset
  2680. is valid. Also, count the number of entries to make sure
  2681. there are not too many entries saved (there should be one
  2682. VolGuid per node times the number of volumes on the disk).
  2683. Arguments:
  2684. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  2685. MasterList - REG_MULTI_SZ list to be checked.
  2686. Return Value:
  2687. ERROR_INVALID_DATA - List contains at least one invalid byte offset
  2688. value, possibly more.
  2689. ERROR_INSUFFICIENT_BUFFER - List possibly corrupt as it contains too
  2690. many entries.
  2691. Win32 error code.
  2692. --*/
  2693. {
  2694. PWCHAR currentStr;
  2695. DWORD currentStrLenChars = 0;
  2696. DWORD dwError = NO_ERROR;
  2697. DWORD partitionNo;
  2698. DWORD numberOfEntries = 0;
  2699. DWORD count;
  2700. LARGE_INTEGER offset;
  2701. BOOL invalidOffset = FALSE;
  2702. EnterCriticalSection( &ResourceEntry->MPInfo.MPLock );
  2703. //
  2704. // Parse through the list.
  2705. //
  2706. for ( currentStr = (PWCHAR)MasterList,
  2707. currentStrLenChars = wcslen( currentStr ) ;
  2708. currentStrLenChars ;
  2709. currentStr += currentStrLenChars + 1,
  2710. currentStrLenChars = wcslen( currentStr ) ) {
  2711. offset.QuadPart = 0;
  2712. #if DBG
  2713. (DiskpLogEvent)(
  2714. ResourceEntry->ResourceHandle,
  2715. LOG_INFORMATION,
  2716. L"ValidateListOffsets: CurrentStr (%1!ws!), numChars %2!u! \n",
  2717. currentStr,
  2718. currentStrLenChars );
  2719. #endif
  2720. //
  2721. // Convert the offset from a string to a large integer value.
  2722. //
  2723. count = swscanf( currentStr, L"%I64x ", &offset.QuadPart );
  2724. if ( 0 == count ) {
  2725. (DiskpLogEvent)(
  2726. ResourceEntry->ResourceHandle,
  2727. LOG_ERROR,
  2728. L"ValidateListOffsets: Unable to parse offset from currentStr (%1!ws!) \n",
  2729. currentStr );
  2730. numberOfEntries++;
  2731. continue;
  2732. }
  2733. //
  2734. // Convert the offset to a partition number.
  2735. //
  2736. if ( !GetPartNoFromOffset( &offset, &ResourceEntry->MountieInfo, &partitionNo ) ) {
  2737. (DiskpLogEvent)(
  2738. ResourceEntry->ResourceHandle,
  2739. LOG_ERROR,
  2740. L"ValidateListOffsets: Unable to convert offset ( %1!08X!%2!08X! ) to partition number \n",
  2741. offset.HighPart,
  2742. offset.LowPart ); // couldn't get !I64X! to work...
  2743. invalidOffset = TRUE;
  2744. // As soon as we find an invalid partition number, we are done.
  2745. break;
  2746. }
  2747. numberOfEntries++;
  2748. }
  2749. if ( invalidOffset ) {
  2750. dwError = ERROR_INVALID_DATA;
  2751. } else if ( numberOfEntries > MAX_ALLOWED_VOLGUID_ENTRIES_PER_DISK ) {
  2752. (DiskpLogEvent)(
  2753. ResourceEntry->ResourceHandle,
  2754. LOG_WARNING,
  2755. L"ValidateListOffset: VolGuid list too large, %1!u! entries \n",
  2756. numberOfEntries );
  2757. #if USEMOUNTPOINTS_KEY
  2758. //
  2759. // See if the user wants to ignore the number of entries in VolGuid list.
  2760. //
  2761. if ( !(ResourceEntry->DiskInfo.Params.UseMountPoints & MPS_IGNORE_MAX_VOLGUIDS) ) {
  2762. //
  2763. // Log an error to system event log.
  2764. //
  2765. ClusResLogSystemEventByKey(ResourceEntry->ResourceKey,
  2766. LOG_UNUSUAL,
  2767. RES_DISK_MP_VOLGUID_LIST_EXCESSIVE );
  2768. dwError = ERROR_INSUFFICIENT_BUFFER;
  2769. }
  2770. #endif
  2771. }
  2772. LeaveCriticalSection( &ResourceEntry->MPInfo.MPLock );
  2773. return dwError;
  2774. } // ValidateListOffsets
  2775. BOOL
  2776. MPIsDriveLetter(
  2777. IN PWSTR MountPoint
  2778. )
  2779. /*++
  2780. Routine Description:
  2781. Determine if the mount point string is a drive letter. A drive letter will be
  2782. represented by a string of the form "x:\" with a length of 3.
  2783. Arguments:
  2784. MountPoint - Mount point string to be verified.
  2785. Return Value:
  2786. TRUE if the mount point string represents a drive letter.
  2787. --*/
  2788. {
  2789. DWORD lenChars;
  2790. lenChars = wcslen( MountPoint );
  2791. if ( 3 == lenChars &&
  2792. L':' == MountPoint[1] &&
  2793. L'\\' == MountPoint[2] &&
  2794. iswalpha( MountPoint[0] ) ) {
  2795. return TRUE;
  2796. }
  2797. return FALSE;
  2798. } // MPIsDriveLetter
  2799. #if DBG
  2800. //
  2801. // Debug helper routine
  2802. //
  2803. VOID
  2804. DumpDiskInfoParams(
  2805. PDISK_RESOURCE ResourceEntry
  2806. )
  2807. /*++
  2808. Routine Description:
  2809. Display in the cluster log interesting mountpoint information.
  2810. Arguments:
  2811. ResourceEntry - Pointer to the DISK_RESOURCE structure.
  2812. Return Value:
  2813. None.
  2814. --*/
  2815. {
  2816. #if 0 // Drive is not currently stored
  2817. (DiskpLogEvent)(
  2818. ResourceEntry->ResourceHandle,
  2819. LOG_INFORMATION,
  2820. L"SetMPListThread: Signature %1!x! Drive (%2!ws!) \n",
  2821. ResourceEntry->DiskInfo.Params.Signature,
  2822. ResourceEntry->DiskInfo.Params.Drive );
  2823. #endif
  2824. (DiskpLogEvent)(
  2825. ResourceEntry->ResourceHandle,
  2826. LOG_INFORMATION,
  2827. L"SetMPListThread: Signature %1!x! \n",
  2828. ResourceEntry->DiskInfo.Params.Signature );
  2829. #if USEMOUNTPOINTS_KEY
  2830. (DiskpLogEvent)(
  2831. ResourceEntry->ResourceHandle,
  2832. LOG_INFORMATION,
  2833. L"SetMPListThread: SkipChkdsk %1!x! ConditionalMount %2!x! UseMountPoints %3!x! \n",
  2834. ResourceEntry->DiskInfo.Params.SkipChkdsk,
  2835. ResourceEntry->DiskInfo.Params.ConditionalMount,
  2836. ResourceEntry->DiskInfo.Params.UseMountPoints );
  2837. #else
  2838. (DiskpLogEvent)(
  2839. ResourceEntry->ResourceHandle,
  2840. LOG_INFORMATION,
  2841. L"SetMPListThread: SkipChkdsk %1!x! ConditionalMount %2!x! \n",
  2842. ResourceEntry->DiskInfo.Params.SkipChkdsk,
  2843. ResourceEntry->DiskInfo.Params.ConditionalMount );
  2844. #endif
  2845. (DiskpLogEvent)(
  2846. ResourceEntry->ResourceHandle,
  2847. LOG_INFORMATION,
  2848. L"SetMPListThread: VolGuid list %1!x! VolGuid size %2!u! \n",
  2849. ResourceEntry->DiskInfo.Params.MPVolGuids,
  2850. ResourceEntry->DiskInfo.Params.MPVolGuidsSize );
  2851. } // DumpDiskInfoParams
  2852. #endif