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.

1521 lines
44 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. ckmach.c
  5. Abstract:
  6. This is for supporting checking a machine to see if it can be converted to IntelliMirror.
  7. Author:
  8. Sean Selitrennikoff - 4/5/98
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. #include <ntverp.h>
  14. PMIRROR_CFG_INFO GlobalMirrorCfgInfo = NULL;
  15. //
  16. // Support functions to do individual tasks
  17. //
  18. NTSTATUS
  19. AddCheckMachineToDoItems(
  20. VOID
  21. )
  22. /*++
  23. Routine Description:
  24. This routine adds all the to do items necessary for checking out the local machine for
  25. conversion.
  26. Arguments:
  27. None
  28. Return Value:
  29. STATUS_SUCCESS if it completes adding all the to do items properly.
  30. --*/
  31. {
  32. NTSTATUS Status;
  33. Status = AddToDoItem(VerifySystemIsNt5, NULL, 0);
  34. if (!NT_SUCCESS(Status)) {
  35. IMirrorHandleError(Status, IMirrorInitialize);
  36. return Status;
  37. }
  38. return STATUS_SUCCESS;
  39. }
  40. NTSTATUS
  41. CheckIfNt5(
  42. IN PVOID pBuffer,
  43. IN ULONG Length
  44. )
  45. /*++
  46. Routine Description:
  47. This routine verifies that the current system is NT5 workstation, x86
  48. Arguments:
  49. pBuffer - Pointer to any arguments passed in the to do item.
  50. Length - Length, in bytes of the arguments.
  51. Return Value:
  52. STATUS_SUCCESS if it completes adding all the to do items properly.
  53. --*/
  54. {
  55. OSVERSIONINFO OsVersion;
  56. DWORD productVersion[] = { VER_PRODUCTVERSION };
  57. IMirrorNowDoing(VerifySystemIsNt5, NULL);
  58. RtlZeroMemory(&OsVersion, sizeof(OSVERSIONINFO));
  59. OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  60. if (!GetVersionEx(&OsVersion)) {
  61. IMirrorHandleError(GetLastError(), VerifySystemIsNt5);
  62. return GetLastError();
  63. }
  64. if (OsVersion.dwPlatformId != VER_PLATFORM_WIN32_NT) {
  65. IMirrorHandleError(ERROR_OLD_WIN_VERSION, VerifySystemIsNt5);
  66. return ERROR_OLD_WIN_VERSION;
  67. }
  68. if (OsVersion.dwMajorVersion != productVersion[0]) {
  69. IMirrorHandleError(ERROR_OLD_WIN_VERSION, VerifySystemIsNt5);
  70. return ERROR_OLD_WIN_VERSION;
  71. }
  72. //
  73. // We're changing the format of the alternate data stream. As such,
  74. // we're introducing an incompatiblility. We'll pick this up here and
  75. // return to riprep.exe the error. Otherwise the user doesn't find out
  76. // about it until text mode setup on restoring the image.
  77. //
  78. // The NT build number that this is getting checked into is 2080.
  79. //
  80. if (OsVersion.dwBuildNumber < 2080) {
  81. DbgPrint("build number is %u\n", OsVersion.dwBuildNumber);
  82. IMirrorHandleError(ERROR_OLD_WIN_VERSION, VerifySystemIsNt5);
  83. return ERROR_OLD_WIN_VERSION;
  84. }
  85. return STATUS_SUCCESS;
  86. }
  87. BOOLEAN
  88. ReadRegistryString(
  89. IN PWCHAR KeyName,
  90. IN PWCHAR ValueName,
  91. IN PVOID Buffer,
  92. IN ULONG BufferLength
  93. )
  94. /*++
  95. Routine Description:
  96. This routine reads a string from the registry into Buffer.
  97. Arguments:
  98. KeyName - The registry key.
  99. ValueName - The value under that key to read, or NULL if the name of
  100. the first key under that key is to be read.
  101. Buffer - The buffer to hold the result.
  102. BufferLength - The length of Buffer.
  103. Return Value:
  104. TRUE if success, FALSE if any errors occur.
  105. --*/
  106. {
  107. UNICODE_STRING UnicodeString;
  108. OBJECT_ATTRIBUTES ObjectAttributes;
  109. PKEY_VALUE_PARTIAL_INFORMATION pValueInfo = NULL;
  110. PKEY_BASIC_INFORMATION pKeyInfo = NULL;
  111. HANDLE Handle = NULL;
  112. ULONG ByteCount;
  113. ULONG BytesLeft;
  114. PBYTE pBuffer;
  115. NTSTATUS Status;
  116. PVOID ResultData;
  117. ULONG ResultDataLength;
  118. BOOLEAN ReturnValue = FALSE;
  119. //
  120. //
  121. // Open the key.
  122. //
  123. //
  124. RtlInitUnicodeString(&UnicodeString, KeyName);
  125. InitializeObjectAttributes(&ObjectAttributes,
  126. &UnicodeString,
  127. OBJ_CASE_INSENSITIVE,
  128. NULL,
  129. NULL
  130. );
  131. Status = NtOpenKey(&Handle,
  132. KEY_ALL_ACCESS,
  133. &ObjectAttributes
  134. );
  135. if (!NT_SUCCESS(Status)) {
  136. goto Cleanup;
  137. }
  138. if (ValueName != NULL) {
  139. RtlInitUnicodeString(&UnicodeString, ValueName);
  140. //
  141. // Get the size of the buffer needed
  142. //
  143. ByteCount = 0;
  144. Status = NtQueryValueKey(Handle,
  145. &UnicodeString,
  146. KeyValuePartialInformation,
  147. NULL,
  148. 0,
  149. &ByteCount
  150. );
  151. if (Status != STATUS_BUFFER_TOO_SMALL) {
  152. goto Cleanup;
  153. }
  154. pValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)IMirrorAllocMem(ByteCount);
  155. if (pValueInfo == NULL) {
  156. goto Cleanup;
  157. }
  158. //
  159. // Get the buffer from the registry
  160. //
  161. Status = NtQueryValueKey(Handle,
  162. &UnicodeString,
  163. KeyValuePartialInformation,
  164. pValueInfo,
  165. ByteCount,
  166. &ByteCount
  167. );
  168. if (!NT_SUCCESS(Status)) {
  169. goto Cleanup;
  170. }
  171. if (pValueInfo->Type != REG_SZ) {
  172. goto Cleanup;
  173. }
  174. ResultData = pValueInfo->Data;
  175. ResultDataLength = pValueInfo->DataLength;
  176. } else {
  177. //
  178. // Get the size of the buffer needed
  179. //
  180. ByteCount = 0;
  181. Status = NtEnumerateKey(Handle,
  182. 0,
  183. KeyBasicInformation,
  184. NULL,
  185. 0,
  186. &ByteCount
  187. );
  188. if (Status != STATUS_BUFFER_TOO_SMALL) {
  189. goto Cleanup;
  190. }
  191. pKeyInfo = (PKEY_BASIC_INFORMATION)IMirrorAllocMem(ByteCount);
  192. if (pKeyInfo == NULL) {
  193. goto Cleanup;
  194. }
  195. //
  196. // Get the name from the registry
  197. //
  198. Status = NtEnumerateKey(Handle,
  199. 0,
  200. KeyBasicInformation,
  201. pKeyInfo,
  202. ByteCount,
  203. &ByteCount
  204. );
  205. if (!NT_SUCCESS(Status)) {
  206. goto Cleanup;
  207. }
  208. ResultData = pKeyInfo->Name;
  209. ResultDataLength = pKeyInfo->NameLength;
  210. }
  211. if (ResultDataLength > BufferLength) {
  212. goto Cleanup;
  213. }
  214. memcpy(Buffer, ResultData, ResultDataLength);
  215. //
  216. // NULL-terminate it just in case, if there is room.
  217. //
  218. if (ResultDataLength <= BufferLength - sizeof(WCHAR)) {
  219. ((PWCHAR)Buffer)[ResultDataLength / sizeof(WCHAR)] = L'\0';
  220. }
  221. ReturnValue = TRUE;
  222. Cleanup:
  223. if (pValueInfo != NULL) {
  224. IMirrorFreeMem(pValueInfo);
  225. }
  226. if (pKeyInfo != NULL) {
  227. IMirrorFreeMem(pKeyInfo);
  228. }
  229. if (Handle != NULL) {
  230. NtClose(Handle);
  231. }
  232. return ReturnValue;
  233. }
  234. NTSTATUS
  235. CheckForPartitions(
  236. IN PVOID pBuffer,
  237. IN ULONG Length
  238. )
  239. /*++
  240. Routine Description:
  241. This routine enumerates all partitions and formats the GlobalMirrorCfgInfo
  242. global structure.
  243. It also fills in the pConfigPath.
  244. Arguments:
  245. pBuffer - Pointer to any arguments passed in the to do item.
  246. Length - Length, in bytes of the arguments.
  247. Return Value:
  248. STATUS_SUCCESS if it completes adding all the to do items properly.
  249. --*/
  250. {
  251. UNICODE_STRING UnicodeString;
  252. OBJECT_ATTRIBUTES ObjectAttributes;
  253. IO_STATUS_BLOCK IoStatus;
  254. PARTITION_INFORMATION_EX PartitionInfoEx;
  255. PARTITION_INFORMATION PartitionInfo;
  256. HANDLE Handle;
  257. ULONG MirrorNumber;
  258. ULONG DiskNumber;
  259. ULONG PartitionNumber;
  260. PULONG pTmpUlong;
  261. NTSTATUS Status;
  262. BOOLEAN foundBoot = FALSE;
  263. BOOLEAN foundSystem = FALSE;
  264. FILE_FS_SIZE_INFORMATION SizeInfo;
  265. LARGE_INTEGER UsedSpace;
  266. LARGE_INTEGER FreeSpace;
  267. ON_DISK_MBR OnDiskMbr;
  268. PUCHAR AlignedBuffer;
  269. UINT previousMode;
  270. HANDLE DosDevicesDir;
  271. ULONG Context;
  272. WCHAR SystemDriveLetter;
  273. POBJECT_DIRECTORY_INFORMATION DirInfo;
  274. ULONG dosLength;
  275. BOOLEAN RestartScan;
  276. PMIRROR_VOLUME_INFO mirrorVolInfo;
  277. ULONG diskSignature;
  278. DWORD fileSystemFlags;
  279. WCHAR fileSystemName[16];
  280. WCHAR volumeLabel[33];
  281. ULONG volumeLabelLength;
  282. WCHAR arcName[MAX_PATH];
  283. ULONG ntNameLength;
  284. ULONG arcNameLength;
  285. OSVERSIONINFO osVersionInfo;
  286. SYSTEM_INFO systemInfo;
  287. DWORD fileVersionInfoSize;
  288. DWORD versionHandle;
  289. PVOID versionInfo;
  290. VS_FIXEDFILEINFO * fixedFileInfo;
  291. UINT fixedFileInfoLength;
  292. WCHAR kernelPath[MAX_PATH];
  293. PWCHAR kernelPathPart;
  294. WCHAR versionString[64];
  295. BOOL b;
  296. #ifndef IMIRROR_NO_TESTING_LIMITATIONS
  297. ULONG numberOfDrives = 0;
  298. #endif
  299. BOOLEAN isDynamic = FALSE;
  300. BOOLEAN UsePartitionInfoEx = TRUE;
  301. IMirrorNowDoing(CheckPartitions, NULL);
  302. if (GlobalMirrorCfgInfo) {
  303. return STATUS_SUCCESS;
  304. }
  305. GlobalMirrorCfgInfo = IMirrorAllocMem(sizeof(MIRROR_CFG_INFO));
  306. if (GlobalMirrorCfgInfo == NULL) {
  307. Status = STATUS_NO_MEMORY;
  308. IMirrorHandleError(Status, CheckPartitions);
  309. return Status;
  310. }
  311. //
  312. // Disable hard error popups for this thread.
  313. //
  314. previousMode = SetErrorMode( SEM_FAILCRITICALERRORS );
  315. GlobalMirrorCfgInfo->MirrorVersion = IMIRROR_CURRENT_VERSION;
  316. GlobalMirrorCfgInfo->FileLength = 0;
  317. GlobalMirrorCfgInfo->SystemPath = NULL;
  318. GlobalMirrorCfgInfo->SysPrepImage = TRUE;
  319. GlobalMirrorCfgInfo->NumberVolumes = 0;
  320. osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  321. if (GetVersionEx(&osVersionInfo)) {
  322. GlobalMirrorCfgInfo->MajorVersion = osVersionInfo.dwMajorVersion;
  323. GlobalMirrorCfgInfo->MinorVersion = osVersionInfo.dwMinorVersion;
  324. GlobalMirrorCfgInfo->BuildNumber = osVersionInfo.dwBuildNumber;
  325. lstrcpyW(pCSDVersion, osVersionInfo.szCSDVersion);
  326. GlobalMirrorCfgInfo->CSDVersion = pCSDVersion;
  327. }
  328. if (SearchPath(
  329. NULL,
  330. L"ntoskrnl.exe",
  331. NULL,
  332. MAX_PATH,
  333. kernelPath,
  334. &kernelPathPart)) {
  335. fileVersionInfoSize = GetFileVersionInfoSize(kernelPath, &versionHandle);
  336. if (fileVersionInfoSize != 0) {
  337. versionInfo = IMirrorAllocMem(fileVersionInfoSize);
  338. if (versionInfo != NULL) {
  339. if (GetFileVersionInfo(
  340. kernelPath,
  341. versionHandle,
  342. fileVersionInfoSize,
  343. versionInfo)) {
  344. if (VerQueryValue(
  345. versionInfo,
  346. L"\\",
  347. &fixedFileInfo,
  348. &fixedFileInfoLength)) {
  349. GlobalMirrorCfgInfo->KernelFileVersionMS = fixedFileInfo->dwFileVersionMS;
  350. GlobalMirrorCfgInfo->KernelFileVersionLS = fixedFileInfo->dwFileVersionLS;
  351. GlobalMirrorCfgInfo->KernelFileFlags = fixedFileInfo->dwFileFlags;
  352. DbgPrint("MS %lx LS %lx flags %lx\n",
  353. GlobalMirrorCfgInfo->KernelFileVersionMS,
  354. GlobalMirrorCfgInfo->KernelFileVersionLS,
  355. GlobalMirrorCfgInfo->KernelFileFlags);
  356. }
  357. }
  358. IMirrorFreeMem(versionInfo);
  359. }
  360. }
  361. }
  362. if (GetSystemMetrics(SM_DEBUG)) {
  363. GlobalMirrorCfgInfo->Debug = TRUE;
  364. }
  365. GetSystemInfo(&systemInfo);
  366. GlobalMirrorCfgInfo->NumberOfProcessors = systemInfo.dwNumberOfProcessors;
  367. if (ReadRegistryString(
  368. L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Environment",
  369. L"PROCESSOR_ARCHITECTURE",
  370. pProcessorArchitecture,
  371. sizeof(pProcessorArchitecture))) {
  372. DbgPrint("processor arch is %ws\n", pProcessorArchitecture);
  373. GlobalMirrorCfgInfo->ProcessorArchitecture = pProcessorArchitecture;
  374. }
  375. if (ReadRegistryString(
  376. L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
  377. L"CurrentType",
  378. pCurrentType,
  379. sizeof(pCurrentType))) {
  380. DbgPrint("current type is %ws\n", pCurrentType);
  381. GlobalMirrorCfgInfo->CurrentType = pCurrentType;
  382. }
  383. if (ReadRegistryString(
  384. L"\\Registry\\Machine\\Hardware\\RESOURCEMAP\\Hardware Abstraction Layer",
  385. NULL,
  386. pHalName,
  387. sizeof(pHalName))) {
  388. DbgPrint("HAL name is %ws\n", pHalName);
  389. GlobalMirrorCfgInfo->HalName = pHalName;
  390. }
  391. InitializeListHead( &GlobalMirrorCfgInfo->MirrorVolumeList );
  392. //
  393. // Get local system drive letter and \\Systemroot\System32\Config path
  394. //
  395. Status = GetBaseDeviceName(L"\\SystemRoot", (PWCHAR)TmpBuffer2, sizeof(TmpBuffer2));
  396. if (!NT_SUCCESS(Status)) {
  397. IMirrorHandleError(Status, CheckPartitions);
  398. goto ExitCheckPartitions;
  399. }
  400. Status = NtPathToDosPath( (PWCHAR) TmpBuffer2,
  401. pConfigPath,
  402. FALSE,
  403. FALSE);
  404. if (!NT_SUCCESS(Status)) {
  405. IMirrorHandleError(Status, CheckPartitions);
  406. goto ExitCheckPartitions;
  407. }
  408. ASSERT( pConfigPath[1] == L':' );
  409. SystemDriveLetter = (WCHAR) pConfigPath[0];
  410. //
  411. // save off the system path so that we can write it out to
  412. // the imirror.dat file
  413. //
  414. lstrcpyW( pSystemPath, pConfigPath );
  415. GlobalMirrorCfgInfo->SystemPath = pSystemPath;
  416. wcscat( pConfigPath, L"\\System32\\Config");
  417. //
  418. // Open \DosDevices directory.
  419. //
  420. RtlInitUnicodeString(&UnicodeString,L"\\Device");
  421. InitializeObjectAttributes(&ObjectAttributes,
  422. &UnicodeString,
  423. OBJ_CASE_INSENSITIVE,
  424. NULL,
  425. NULL
  426. );
  427. Status = NtOpenDirectoryObject(&DosDevicesDir,
  428. DIRECTORY_QUERY,
  429. &ObjectAttributes
  430. );
  431. if (!NT_SUCCESS(Status)) {
  432. IMirrorHandleError(Status, CheckPartitions);
  433. goto ExitCheckPartitions;
  434. }
  435. //
  436. // Iterate each object in that directory that is a directory.
  437. //
  438. Context = 0;
  439. RestartScan = TRUE;
  440. Status = NtQueryDirectoryObject(DosDevicesDir,
  441. TmpBuffer,
  442. sizeof(TmpBuffer),
  443. TRUE,
  444. RestartScan,
  445. &Context,
  446. &dosLength
  447. );
  448. RestartScan = FALSE;
  449. DirInfo = (POBJECT_DIRECTORY_INFORMATION)TmpBuffer;
  450. MirrorNumber = 1;
  451. while (NT_SUCCESS(Status)) {
  452. DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0;
  453. DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0;
  454. //
  455. // Skip this entry if it's not a "HardDiskN"
  456. //
  457. if ((DirInfo->Name.Length > (sizeof(L"Harddisk")-sizeof(WCHAR))) &&
  458. (!wcsncmp(DirInfo->Name.Buffer,L"Harddisk",(sizeof(L"Harddisk")/sizeof(WCHAR))-1)) &&
  459. !lstrcmpi(DirInfo->TypeName.Buffer, L"Directory")) {
  460. PWCHAR diskNumberPtr;
  461. PartitionNumber = 0;
  462. DiskNumber = 0;
  463. diskNumberPtr = &DirInfo->Name.Buffer[(sizeof(L"Harddisk")/sizeof(WCHAR))-1];
  464. while (*diskNumberPtr >= L'0' && *diskNumberPtr <= L'9' ) {
  465. DiskNumber *= 10;
  466. DiskNumber += *(diskNumberPtr) - L'0';
  467. diskNumberPtr++;
  468. }
  469. if (*diskNumberPtr != L'\0') {
  470. //
  471. // if the device name wasn't of form HardDiskN, skip this entry.
  472. //
  473. goto getNextDevice;
  474. }
  475. diskSignature = 0;
  476. //
  477. // get the disk signature, continue if it fails.
  478. //
  479. swprintf((PWCHAR)TmpBuffer2, L"\\Device\\Harddisk%d\\Partition0", DiskNumber);
  480. RtlInitUnicodeString(&UnicodeString, (PWCHAR)TmpBuffer2);
  481. InitializeObjectAttributes(&ObjectAttributes,
  482. &UnicodeString,
  483. OBJ_CASE_INSENSITIVE,
  484. NULL,
  485. NULL
  486. );
  487. Status = NtCreateFile(&Handle,
  488. (ACCESS_MASK)FILE_GENERIC_READ,
  489. &ObjectAttributes,
  490. &IoStatus,
  491. NULL,
  492. FILE_ATTRIBUTE_NORMAL,
  493. FILE_SHARE_READ,
  494. FILE_OPEN,
  495. FILE_SYNCHRONOUS_IO_NONALERT,
  496. NULL,
  497. 0
  498. );
  499. if (NT_SUCCESS(Status)) {
  500. ASSERT(sizeof(ON_DISK_MBR) == 512);
  501. AlignedBuffer = ALIGN(TmpBuffer, 512);
  502. Status = NtReadFile(Handle,
  503. NULL,
  504. NULL,
  505. NULL,
  506. &IoStatus,
  507. AlignedBuffer,
  508. sizeof(ON_DISK_MBR),
  509. NULL,
  510. NULL
  511. );
  512. if (NT_SUCCESS(Status)) {
  513. RtlMoveMemory(&OnDiskMbr, AlignedBuffer, sizeof(ON_DISK_MBR));
  514. ASSERT(U_USHORT(OnDiskMbr.AA55Signature) == 0xAA55);
  515. diskSignature = U_ULONG(OnDiskMbr.NTFTSignature);
  516. //
  517. // check to see if this disk is dynamic
  518. //
  519. if (OnDiskMbr.PartitionTable[0].SystemId == PARTITION_LDM ||
  520. OnDiskMbr.PartitionTable[1].SystemId == PARTITION_LDM ||
  521. OnDiskMbr.PartitionTable[2].SystemId == PARTITION_LDM ||
  522. OnDiskMbr.PartitionTable[3].SystemId == PARTITION_LDM) {
  523. isDynamic = TRUE;
  524. NtClose(Handle);
  525. goto getNextDevice;
  526. }
  527. }
  528. NtClose(Handle);
  529. }
  530. while (1) {
  531. PartitionNumber++;
  532. swprintf((PWCHAR)TmpBuffer2, L"\\Device\\Harddisk%d\\Partition%d", DiskNumber, PartitionNumber);
  533. RtlInitUnicodeString(&UnicodeString, (PWCHAR)TmpBuffer2);
  534. InitializeObjectAttributes(&ObjectAttributes,
  535. &UnicodeString,
  536. OBJ_CASE_INSENSITIVE,
  537. NULL,
  538. NULL
  539. );
  540. Status = NtCreateFile(&Handle,
  541. (ACCESS_MASK)FILE_GENERIC_READ,
  542. &ObjectAttributes,
  543. &IoStatus,
  544. NULL,
  545. FILE_ATTRIBUTE_NORMAL,
  546. FILE_SHARE_READ | FILE_SHARE_WRITE,
  547. FILE_OPEN,
  548. FILE_SYNCHRONOUS_IO_NONALERT,
  549. NULL,
  550. 0
  551. );
  552. if (!NT_SUCCESS(Status)) {
  553. break; // on to next disk
  554. }
  555. Status = NtDeviceIoControlFile(Handle,
  556. NULL,
  557. NULL,
  558. NULL,
  559. &IoStatus,
  560. IOCTL_DISK_GET_PARTITION_INFO_EX,
  561. NULL,
  562. 0,
  563. &PartitionInfoEx,
  564. sizeof(PARTITION_INFORMATION_EX) );
  565. if( (Status == STATUS_NOT_IMPLEMENTED) || (Status == STATUS_INVALID_DEVICE_REQUEST) ) {
  566. //
  567. // We're on an old build that didn't have this IOCTL.
  568. //
  569. UsePartitionInfoEx = FALSE;
  570. Status = NtDeviceIoControlFile(Handle,
  571. NULL,
  572. NULL,
  573. NULL,
  574. &IoStatus,
  575. IOCTL_DISK_GET_PARTITION_INFO,
  576. NULL,
  577. 0,
  578. &PartitionInfo,
  579. sizeof(PARTITION_INFORMATION) );
  580. }
  581. if (!NT_SUCCESS(Status)) {
  582. NtClose(Handle);
  583. continue; // on to next partition
  584. }
  585. //
  586. // For Whistler, ignore GPT partitions.
  587. //
  588. if( (UsePartitionInfoEx) && (PartitionInfoEx.PartitionStyle != PARTITION_STYLE_MBR) ) {
  589. NtClose(Handle);
  590. continue;
  591. }
  592. Status = NtQueryVolumeInformationFile(Handle,
  593. &IoStatus,
  594. &SizeInfo,
  595. sizeof(SizeInfo),
  596. FileFsSizeInformation );
  597. NtClose(Handle);
  598. if (!NT_SUCCESS(Status)) {
  599. continue; // on to next partition
  600. }
  601. Status = NtPathToDosPath( (PWCHAR) TmpBuffer2,
  602. (PWCHAR) TmpBuffer,
  603. TRUE,
  604. FALSE);
  605. if (!NT_SUCCESS(Status)) {
  606. continue; // on to next partition
  607. }
  608. if ((lstrlenW((PWCHAR) TmpBuffer) == 0) ||
  609. *(((PWCHAR)TmpBuffer)+1) != L':') {
  610. continue; // on to next partition
  611. }
  612. //
  613. // Get the ARC name of the partition.
  614. //
  615. NtNameToArcName( (PWCHAR) TmpBuffer2,
  616. (PWCHAR) arcName,
  617. FALSE);
  618. //
  619. // Get the file system type. We add a \ to the end
  620. // of TmpBuffer if there isn't one.
  621. //
  622. if (((PWCHAR)TmpBuffer)[lstrlenW((PWCHAR)TmpBuffer) - 1] != L'\\') {
  623. wcscat((PWCHAR)TmpBuffer, L"\\");
  624. }
  625. b = GetVolumeInformationW(
  626. (PWCHAR) TmpBuffer,
  627. volumeLabel,
  628. ARRAYSIZE(volumeLabel),
  629. NULL, // no volume serial number requested
  630. NULL, // no maximum name length requested
  631. &fileSystemFlags,
  632. fileSystemName,
  633. ARRAYSIZE(fileSystemName));
  634. if (!b) {
  635. continue;
  636. }
  637. //
  638. // Calculate the amount of free space on the drive.
  639. //
  640. FreeSpace = RtlExtendedIntegerMultiply(
  641. SizeInfo.AvailableAllocationUnits,
  642. SizeInfo.SectorsPerAllocationUnit * SizeInfo.BytesPerSector
  643. );
  644. UsedSpace = RtlExtendedIntegerMultiply(
  645. SizeInfo.TotalAllocationUnits,
  646. SizeInfo.SectorsPerAllocationUnit * SizeInfo.BytesPerSector
  647. );
  648. UsedSpace = RtlLargeIntegerSubtract(
  649. UsedSpace,
  650. FreeSpace
  651. );
  652. #ifndef IMIRROR_NO_TESTING_LIMITATIONS
  653. numberOfDrives++;
  654. //
  655. // for NT 5.0, the test group doesn't want to test more than a single
  656. // partition. Now that the test team is dictating what the feature set
  657. // is, we'll return an error if we have more than a single partition or
  658. // disk.
  659. //
  660. if ( (UsePartitionInfoEx && !PartitionInfoEx.Mbr.BootIndicator) ||
  661. (!UsePartitionInfoEx && !PartitionInfo.BootIndicator)) {
  662. if (*(PWCHAR)TmpBuffer == SystemDriveLetter) {
  663. IMirrorHandleError(STATUS_MISSING_SYSTEMFILE, CheckPartitions);
  664. NtClose(DosDevicesDir);
  665. Status = STATUS_MISSING_SYSTEMFILE;
  666. goto ExitCheckPartitions;
  667. }
  668. continue;
  669. }
  670. if (*(PWCHAR)TmpBuffer != SystemDriveLetter) {
  671. // if another drive is marked bootable but it isn't the
  672. // system drive, we'll ignore it. We'll pick up the
  673. // error down below if this is the only bootable drive.
  674. #if 0
  675. if ( (UsePartitionInfoEx && PartitionInfoEx.Mbr.BootIndicator) ||
  676. (!UsePartitionInfoEx && PartitionInfo.BootIndicator)) {
  677. IMirrorHandleError(STATUS_MISSING_SYSTEMFILE, CheckPartitions);
  678. NtClose(DosDevicesDir);
  679. Status = STATUS_MISSING_SYSTEMFILE;
  680. goto ExitCheckPartitions;
  681. }
  682. #endif
  683. continue;
  684. }
  685. #endif
  686. mirrorVolInfo = IMirrorAllocMem(sizeof(MIRROR_VOLUME_INFO));
  687. if (mirrorVolInfo == NULL) {
  688. NtClose(DosDevicesDir);
  689. Status = STATUS_NO_MEMORY;
  690. IMirrorHandleError(Status, CheckPartitions);
  691. goto ExitCheckPartitions;
  692. }
  693. //
  694. // Save the NT and ARC device names.
  695. //
  696. ntNameLength = (lstrlenW( (PWCHAR)TmpBuffer2 ) + 1) * sizeof(WCHAR);
  697. mirrorVolInfo->NtName = IMirrorAllocMem(ntNameLength);
  698. if (mirrorVolInfo->NtName == NULL) {
  699. Status = STATUS_NO_MEMORY;
  700. IMirrorHandleError(Status, CheckPartitions);
  701. NtClose(DosDevicesDir);
  702. goto ExitCheckPartitions;
  703. }
  704. arcNameLength = (lstrlenW( (PWCHAR)arcName ) + 1) * sizeof(WCHAR);
  705. mirrorVolInfo->ArcName = IMirrorAllocMem(arcNameLength);
  706. if (mirrorVolInfo->ArcName == NULL) {
  707. Status = STATUS_NO_MEMORY;
  708. IMirrorHandleError(Status, CheckPartitions);
  709. NtClose(DosDevicesDir);
  710. goto ExitCheckPartitions;
  711. }
  712. memcpy(mirrorVolInfo->NtName, TmpBuffer2, ntNameLength);
  713. memcpy(mirrorVolInfo->ArcName, arcName, arcNameLength);
  714. mirrorVolInfo->DriveLetter = *(PWCHAR)TmpBuffer;
  715. mirrorVolInfo->PartitionType = UsePartitionInfoEx ? PartitionInfoEx.Mbr.PartitionType : PartitionInfo.PartitionType;
  716. //
  717. // If this is a non-NTFS volume, check if it is configured
  718. // for compression
  719. //
  720. if ( ((UsePartitionInfoEx && (PartitionInfoEx.Mbr.PartitionType != PARTITION_IFS)) ||
  721. (!UsePartitionInfoEx && (PartitionInfo.PartitionType != PARTITION_IFS)))
  722. &&
  723. (fileSystemFlags & FS_VOL_IS_COMPRESSED) ) {
  724. mirrorVolInfo->CompressedVolume = TRUE;
  725. } else {
  726. mirrorVolInfo->CompressedVolume = FALSE;
  727. }
  728. if ( (UsePartitionInfoEx && (PartitionInfoEx.Mbr.BootIndicator)) ||
  729. (!UsePartitionInfoEx && (PartitionInfo.BootIndicator)) ) {
  730. foundBoot = TRUE;
  731. mirrorVolInfo->PartitionActive = TRUE;
  732. } else {
  733. mirrorVolInfo->PartitionActive = FALSE;
  734. }
  735. if (*(PWCHAR)TmpBuffer == SystemDriveLetter) {
  736. foundSystem = TRUE;
  737. mirrorVolInfo->IsBootDisk = TRUE;
  738. } else {
  739. mirrorVolInfo->IsBootDisk = FALSE;
  740. }
  741. mirrorVolInfo->DiskNumber = DiskNumber;
  742. mirrorVolInfo->PartitionNumber = PartitionNumber;
  743. mirrorVolInfo->MirrorTableIndex = MirrorNumber++;
  744. mirrorVolInfo->MirrorUncPath = NULL;
  745. mirrorVolInfo->LastUSNMirrored = 0;
  746. mirrorVolInfo->BlockSize = SizeInfo.BytesPerSector;
  747. mirrorVolInfo->DiskSignature = diskSignature;
  748. mirrorVolInfo->FileSystemFlags = fileSystemFlags;
  749. wcscpy(mirrorVolInfo->FileSystemName, fileSystemName);
  750. volumeLabelLength = (lstrlenW( (PWCHAR)volumeLabel ) + 1) * sizeof(WCHAR);
  751. mirrorVolInfo->VolumeLabel = IMirrorAllocMem(volumeLabelLength);
  752. if (mirrorVolInfo->VolumeLabel == NULL) {
  753. Status = STATUS_NO_MEMORY;
  754. IMirrorHandleError(Status, CheckPartitions);
  755. NtClose(DosDevicesDir);
  756. goto ExitCheckPartitions;
  757. }
  758. memcpy(mirrorVolInfo->VolumeLabel, volumeLabel, volumeLabelLength);
  759. mirrorVolInfo->StartingOffset = UsePartitionInfoEx ? PartitionInfoEx.StartingOffset : PartitionInfo.StartingOffset;
  760. mirrorVolInfo->PartitionSize = UsePartitionInfoEx ? PartitionInfoEx.PartitionLength : PartitionInfo.PartitionLength;
  761. mirrorVolInfo->DiskSpaceUsed = UsedSpace;
  762. InsertTailList( &GlobalMirrorCfgInfo->MirrorVolumeList,
  763. &mirrorVolInfo->ListEntry );
  764. GlobalMirrorCfgInfo->NumberVolumes = MirrorNumber - 1;
  765. }
  766. }
  767. //
  768. // Go on to next object.
  769. //
  770. getNextDevice:
  771. Status = NtQueryDirectoryObject(
  772. DosDevicesDir,
  773. TmpBuffer,
  774. sizeof(TmpBuffer),
  775. TRUE,
  776. RestartScan,
  777. &Context,
  778. &dosLength
  779. );
  780. }
  781. NtClose(DosDevicesDir);
  782. if ((!foundBoot) || (!foundSystem) ) {
  783. Status = (isDynamic ? STATUS_OBJECT_TYPE_MISMATCH : STATUS_MISSING_SYSTEMFILE);
  784. IMirrorHandleError(Status, CheckPartitions);
  785. goto ExitCheckPartitions;
  786. }
  787. #ifndef IMIRROR_NO_TESTING_LIMITATIONS
  788. if (numberOfDrives > 1) {
  789. IMirrorHandleError(ERROR_INVALID_DRIVE, CheckPartitions);
  790. Status = ERROR_INVALID_DRIVE;
  791. } else {
  792. Status = STATUS_SUCCESS;
  793. }
  794. #else
  795. Status = STATUS_SUCCESS;
  796. #endif
  797. ExitCheckPartitions:
  798. SetErrorMode( previousMode );
  799. return Status;
  800. }
  801. NTSTATUS
  802. NtPathToDosPath(
  803. IN PWSTR NtPath,
  804. OUT PWSTR DosPath,
  805. IN BOOLEAN GetDriveOnly,
  806. IN BOOLEAN NtPathIsBasic
  807. )
  808. /*++
  809. Routine Description:
  810. This routine calls off to convert a \Device\HarddiskX\PartitionY\<path> to Z:\<path>
  811. Arguments:
  812. NtPath - Something like \Device\Harddisk0\Partition2\WINNT
  813. DosPath - Will be something like D: or D:\WINNT, depending on flag below.
  814. GetDriveOnly - TRUE if the caller only wants the DOS drive.
  815. NtPathIsBasic - TRUE if NtPath is not symbolic link.
  816. Return Value:
  817. STATUS_SUCCESS if it completes filling in DosDrive, else an appropriate error code.
  818. --*/
  819. {
  820. OBJECT_ATTRIBUTES ObjectAttributes;
  821. UNICODE_STRING UnicodeString;
  822. NTSTATUS Status;
  823. HANDLE DosDevicesDir;
  824. HANDLE DosDevicesObj;
  825. HANDLE ObjectHandle;
  826. ULONG Context;
  827. ULONG Length;
  828. BOOLEAN RestartScan;
  829. WCHAR LinkTarget[2*MAX_PATH];
  830. POBJECT_DIRECTORY_INFORMATION DirInfo;
  831. WCHAR LocalBuffer[MAX_PATH];
  832. WCHAR LocalBuffer2[MAX_PATH];
  833. PWCHAR pTmp;
  834. PWCHAR NameSpace[] = { L"\\??", L"\\GLOBAL??" };
  835. UINT i;
  836. if (NtPath == NULL) {
  837. return ERROR_PATH_NOT_FOUND;
  838. }
  839. if (!NtPathIsBasic) {
  840. //
  841. // Find the end of the \device\harddiskX\partitionY string
  842. //
  843. wcscpy(LocalBuffer2, NtPath);
  844. pTmp = LocalBuffer2;
  845. if (*pTmp != L'\\') {
  846. return ERROR_PATH_NOT_FOUND;
  847. }
  848. pTmp = wcsstr(pTmp + 1, L"\\");
  849. if (pTmp == NULL) {
  850. return ERROR_PATH_NOT_FOUND;
  851. }
  852. pTmp = wcsstr(pTmp + 1, L"\\");
  853. if (pTmp == NULL) {
  854. return ERROR_PATH_NOT_FOUND;
  855. }
  856. pTmp = wcsstr(pTmp + 1, L"\\");
  857. if (pTmp != NULL) {
  858. *pTmp = UNICODE_NULL;
  859. pTmp++;
  860. }
  861. //
  862. // Find the base NT device name
  863. //
  864. Status = GetBaseDeviceName(LocalBuffer2, LocalBuffer, sizeof(LocalBuffer));
  865. if (!NT_SUCCESS(Status)) {
  866. return Status;
  867. }
  868. } else {
  869. wcscpy(LocalBuffer, NtPath);
  870. pTmp = NULL;
  871. }
  872. //
  873. // Open \DosDevices directory. First try the "normal" dosdevices path,
  874. // then try the global dosdevices path.
  875. //
  876. for (i = 0; i < sizeof(NameSpace)/sizeof(PWCHAR *); i++) {
  877. RtlInitUnicodeString(&UnicodeString,NameSpace[i]);
  878. InitializeObjectAttributes(&ObjectAttributes,
  879. &UnicodeString,
  880. OBJ_CASE_INSENSITIVE,
  881. NULL,
  882. NULL
  883. );
  884. Status = NtOpenDirectoryObject(&DosDevicesDir,
  885. DIRECTORY_QUERY,
  886. &ObjectAttributes
  887. );
  888. if (!NT_SUCCESS(Status)) {
  889. DosDevicesDir = NULL;
  890. } else {
  891. //
  892. // Iterate each object in that directory.
  893. //
  894. Context = 0;
  895. RestartScan = TRUE;
  896. Status = NtQueryDirectoryObject(DosDevicesDir,
  897. TmpBuffer3,
  898. sizeof(TmpBuffer3),
  899. TRUE,
  900. RestartScan,
  901. &Context,
  902. &Length
  903. );
  904. RestartScan = FALSE;
  905. DirInfo = (POBJECT_DIRECTORY_INFORMATION)TmpBuffer3;
  906. while (NT_SUCCESS(Status)) {
  907. DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0;
  908. DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0;
  909. //
  910. // Skip this entry if it's not a symbolic link.
  911. //
  912. if ((DirInfo->Name.Length != 0) &&
  913. (DirInfo->Name.Buffer[1] == L':') &&
  914. !lstrcmpi(DirInfo->TypeName.Buffer, L"SymbolicLink")) {
  915. //
  916. // Get this \DosDevices object's link target.
  917. //
  918. swprintf(LocalBuffer2, L"%ws\\%ws", NameSpace[i], DirInfo->Name.Buffer);
  919. Status = GetBaseDeviceName(LocalBuffer2, LinkTarget, sizeof(LinkTarget));
  920. if (NT_SUCCESS(Status)) {
  921. //
  922. // See if it's a prefix of the path we're converting,
  923. //
  924. if(!_wcsnicmp(LocalBuffer, LinkTarget, wcslen(LinkTarget))) {
  925. //
  926. // Got a match.
  927. //
  928. lstrcpy(DosPath, DirInfo->Name.Buffer);
  929. if (!GetDriveOnly) {
  930. if (NtPathIsBasic) {
  931. lstrcat(DosPath, LocalBuffer + wcslen(LinkTarget));
  932. } else if (pTmp != NULL) {
  933. lstrcat(DosPath, L"\\");
  934. lstrcat(DosPath, pTmp);
  935. }
  936. }
  937. NtClose(DosDevicesDir);
  938. return(STATUS_SUCCESS);
  939. }
  940. }
  941. }
  942. //
  943. // Go on to next object.
  944. //
  945. Status = NtQueryDirectoryObject(
  946. DosDevicesDir,
  947. TmpBuffer3,
  948. sizeof(TmpBuffer3),
  949. TRUE,
  950. RestartScan,
  951. &Context,
  952. &Length
  953. );
  954. }
  955. NtClose(DosDevicesDir);
  956. }
  957. }
  958. return(Status);
  959. }
  960. NTSTATUS
  961. NtNameToArcName(
  962. IN PWSTR NtName,
  963. OUT PWSTR ArcName,
  964. IN BOOLEAN NtNameIsBasic
  965. )
  966. /*++
  967. Routine Description:
  968. This routine calls off to convert a \Device\HarddiskX\PartitionY to
  969. the ARC name.
  970. Arguments:
  971. NtPath - Something like \Device\Harddisk0\Partition2
  972. DosPath - Will be something like \Arcname\multi(0)disk(0)rdisk(0)partition(1).
  973. NtNameIsBasic - TRUE if NtName is not symbolic link.
  974. Return Value:
  975. STATUS_SUCCESS if it completes filling in ArcName, else an appropriate error code.
  976. --*/
  977. {
  978. OBJECT_ATTRIBUTES ObjectAttributes;
  979. UNICODE_STRING UnicodeString;
  980. NTSTATUS Status;
  981. HANDLE DosDevicesDir;
  982. HANDLE DosDevicesObj;
  983. HANDLE ObjectHandle;
  984. ULONG Context;
  985. ULONG Length;
  986. BOOLEAN RestartScan;
  987. WCHAR LinkTarget[2*MAX_PATH];
  988. POBJECT_DIRECTORY_INFORMATION DirInfo;
  989. WCHAR LocalBuffer[MAX_PATH];
  990. WCHAR LocalBuffer2[MAX_PATH];
  991. PWCHAR pTmp;
  992. if (!NtNameIsBasic) {
  993. //
  994. // Find the base NT device name
  995. //
  996. Status = GetBaseDeviceName(NtName, LocalBuffer, sizeof(LocalBuffer));
  997. if (!NT_SUCCESS(Status)) {
  998. return Status;
  999. }
  1000. } else {
  1001. wcscpy(LocalBuffer, NtName);
  1002. }
  1003. //
  1004. // Open \ArcName directory.
  1005. //
  1006. RtlInitUnicodeString(&UnicodeString,L"\\ArcName");
  1007. InitializeObjectAttributes(&ObjectAttributes,
  1008. &UnicodeString,
  1009. OBJ_CASE_INSENSITIVE,
  1010. NULL,
  1011. NULL
  1012. );
  1013. Status = NtOpenDirectoryObject(&DosDevicesDir,
  1014. DIRECTORY_QUERY,
  1015. &ObjectAttributes
  1016. );
  1017. if (!NT_SUCCESS(Status)) {
  1018. return Status;
  1019. }
  1020. //
  1021. // Iterate each object in that directory.
  1022. //
  1023. Context = 0;
  1024. RestartScan = TRUE;
  1025. Status = NtQueryDirectoryObject(DosDevicesDir,
  1026. TmpBuffer3,
  1027. sizeof(TmpBuffer3),
  1028. TRUE,
  1029. RestartScan,
  1030. &Context,
  1031. &Length
  1032. );
  1033. RestartScan = FALSE;
  1034. DirInfo = (POBJECT_DIRECTORY_INFORMATION)TmpBuffer3;
  1035. while (NT_SUCCESS(Status)) {
  1036. DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0;
  1037. DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0;
  1038. //
  1039. // Skip this entry if it's not a symbolic link.
  1040. //
  1041. if ((DirInfo->Name.Length != 0) &&
  1042. !lstrcmpi(DirInfo->TypeName.Buffer, L"SymbolicLink")) {
  1043. //
  1044. // Get this \DosDevices object's link target.
  1045. //
  1046. swprintf(LocalBuffer2, L"\\ArcName\\%ws", DirInfo->Name.Buffer);
  1047. Status = GetBaseDeviceName(LocalBuffer2, LinkTarget, sizeof(LinkTarget));
  1048. if (NT_SUCCESS(Status)) {
  1049. //
  1050. // See if the base name of this link matches the base
  1051. // name of what we are looking for.
  1052. //
  1053. if(!_wcsnicmp(LocalBuffer, LinkTarget, wcslen(LinkTarget))) {
  1054. //
  1055. // Got a match.
  1056. //
  1057. lstrcpy(ArcName, DirInfo->Name.Buffer);
  1058. NtClose(DosDevicesDir);
  1059. return STATUS_SUCCESS;
  1060. }
  1061. }
  1062. }
  1063. //
  1064. // Go on to next object.
  1065. //
  1066. Status = NtQueryDirectoryObject(
  1067. DosDevicesDir,
  1068. TmpBuffer3,
  1069. sizeof(TmpBuffer3),
  1070. TRUE,
  1071. RestartScan,
  1072. &Context,
  1073. &Length
  1074. );
  1075. }
  1076. NtClose(DosDevicesDir);
  1077. return Status;
  1078. }
  1079. NTSTATUS
  1080. GetBaseDeviceName(
  1081. IN PWSTR SymbolicName,
  1082. OUT PWSTR Buffer,
  1083. IN ULONG Size
  1084. )
  1085. /*++
  1086. Routine Description:
  1087. This routine drills down thru symbolic links until it finds the base device name.
  1088. Arguments:
  1089. SymbolicName - The name to start with.
  1090. Buffer - The output buffer.
  1091. Size - Length, in bytes of Buffer
  1092. Return Value:
  1093. STATUS_SUCCESS if it completes adding all the to do items properly.
  1094. --*/
  1095. {
  1096. UNICODE_STRING UnicodeString;
  1097. OBJECT_ATTRIBUTES ObjectAttributes;
  1098. HANDLE Handle;
  1099. NTSTATUS Status;
  1100. //
  1101. // Start at the first name
  1102. //
  1103. RtlInitUnicodeString(&UnicodeString, SymbolicName);
  1104. InitializeObjectAttributes(
  1105. &ObjectAttributes,
  1106. &UnicodeString,
  1107. OBJ_CASE_INSENSITIVE,
  1108. NULL,
  1109. NULL
  1110. );
  1111. Status = NtOpenSymbolicLinkObject(&Handle,
  1112. (ACCESS_MASK)SYMBOLIC_LINK_QUERY,
  1113. &ObjectAttributes
  1114. );
  1115. if (!NT_SUCCESS(Status)) {
  1116. return Status;
  1117. }
  1118. while (TRUE) {
  1119. //
  1120. // Take this open and get the next name
  1121. //
  1122. UnicodeString.Length = 0;
  1123. UnicodeString.MaximumLength = (USHORT)Size;
  1124. UnicodeString.Buffer = (PWCHAR)Buffer;
  1125. Status = NtQuerySymbolicLinkObject(Handle,
  1126. &UnicodeString,
  1127. NULL
  1128. );
  1129. NtClose(Handle);
  1130. Buffer[(UnicodeString.Length / sizeof(WCHAR))] = UNICODE_NULL;
  1131. if (!NT_SUCCESS(Status)) {
  1132. return Status;
  1133. }
  1134. //
  1135. // See if the next name is also a symbolic name
  1136. //
  1137. RtlInitUnicodeString(&UnicodeString, Buffer);
  1138. InitializeObjectAttributes(
  1139. &ObjectAttributes,
  1140. &UnicodeString,
  1141. OBJ_CASE_INSENSITIVE,
  1142. NULL,
  1143. NULL
  1144. );
  1145. Status = NtOpenSymbolicLinkObject(&Handle,
  1146. (ACCESS_MASK)SYMBOLIC_LINK_QUERY,
  1147. &ObjectAttributes
  1148. );
  1149. if (!NT_SUCCESS(Status)) {
  1150. return STATUS_SUCCESS;
  1151. }
  1152. }
  1153. }