/*++ Copyright (c) 1998 Microsoft Corporation Module Name: ckmach.c Abstract: This is for supporting checking a machine to see if it can be converted to IntelliMirror. Author: Sean Selitrennikoff - 4/5/98 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include PMIRROR_CFG_INFO GlobalMirrorCfgInfo = NULL; // // Support functions to do individual tasks // NTSTATUS AddCheckMachineToDoItems( VOID ) /*++ Routine Description: This routine adds all the to do items necessary for checking out the local machine for conversion. Arguments: None Return Value: STATUS_SUCCESS if it completes adding all the to do items properly. --*/ { NTSTATUS Status; Status = AddToDoItem(VerifySystemIsNt5, NULL, 0); if (!NT_SUCCESS(Status)) { IMirrorHandleError(Status, IMirrorInitialize); return Status; } return STATUS_SUCCESS; } NTSTATUS CheckIfNt5( ) /*++ Routine Description: This routine verifies that the current system is NT5 workstation, x86 Arguments: None. Return Value: STATUS_SUCCESS if it completes adding all the to do items properly. --*/ { OSVERSIONINFO OsVersion; DWORD productVersion[] = { VER_PRODUCTVERSION }; IMirrorNowDoing(VerifySystemIsNt5, NULL); RtlZeroMemory(&OsVersion, sizeof(OSVERSIONINFO)); OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&OsVersion)) { IMirrorHandleError(GetLastError(), VerifySystemIsNt5); return GetLastError(); } if (OsVersion.dwPlatformId != VER_PLATFORM_WIN32_NT) { IMirrorHandleError(ERROR_OLD_WIN_VERSION, VerifySystemIsNt5); return ERROR_OLD_WIN_VERSION; } if (OsVersion.dwMajorVersion != productVersion[0]) { IMirrorHandleError(ERROR_OLD_WIN_VERSION, VerifySystemIsNt5); return ERROR_OLD_WIN_VERSION; } // // We're changing the format of the alternate data stream. As such, // we're introducing an incompatiblility. We'll pick this up here and // return to riprep.exe the error. Otherwise the user doesn't find out // about it until text mode setup on restoring the image. // // The NT build number that this is getting checked into is 2080. // if (OsVersion.dwBuildNumber < 2080) { DbgPrint("build number is %u\n", OsVersion.dwBuildNumber); IMirrorHandleError(ERROR_OLD_WIN_VERSION, VerifySystemIsNt5); return ERROR_OLD_WIN_VERSION; } return STATUS_SUCCESS; } BOOLEAN ReadRegistryString( IN PWCHAR KeyName, IN PWCHAR ValueName, IN PVOID Buffer, IN ULONG BufferLength ) /*++ Routine Description: This routine reads a string from the registry into Buffer. Arguments: KeyName - The registry key. ValueName - The value under that key to read, or NULL if the name of the first key under that key is to be read. Buffer - The buffer to hold the result. BufferLength - The length of Buffer. Return Value: TRUE if success, FALSE if any errors occur. --*/ { UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; PKEY_VALUE_PARTIAL_INFORMATION pValueInfo = NULL; PKEY_BASIC_INFORMATION pKeyInfo = NULL; HANDLE Handle = NULL; ULONG ByteCount; NTSTATUS Status; PVOID ResultData; ULONG ResultDataLength; BOOLEAN ReturnValue = FALSE; // // // Open the key. // // RtlInitUnicodeString(&UnicodeString, KeyName); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey(&Handle, KEY_ALL_ACCESS, &ObjectAttributes ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (ValueName != NULL) { RtlInitUnicodeString(&UnicodeString, ValueName); // // Get the size of the buffer needed // ByteCount = 0; Status = NtQueryValueKey(Handle, &UnicodeString, KeyValuePartialInformation, NULL, 0, &ByteCount ); if (Status != STATUS_BUFFER_TOO_SMALL) { goto Cleanup; } pValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)IMirrorAllocMem(ByteCount); if (pValueInfo == NULL) { goto Cleanup; } // // Get the buffer from the registry // Status = NtQueryValueKey(Handle, &UnicodeString, KeyValuePartialInformation, pValueInfo, ByteCount, &ByteCount ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (pValueInfo->Type != REG_SZ) { goto Cleanup; } ResultData = pValueInfo->Data; ResultDataLength = pValueInfo->DataLength; } else { // // Get the size of the buffer needed // ByteCount = 0; Status = NtEnumerateKey(Handle, 0, KeyBasicInformation, NULL, 0, &ByteCount ); if (Status != STATUS_BUFFER_TOO_SMALL) { goto Cleanup; } pKeyInfo = (PKEY_BASIC_INFORMATION)IMirrorAllocMem(ByteCount); if (pKeyInfo == NULL) { goto Cleanup; } // // Get the name from the registry // Status = NtEnumerateKey(Handle, 0, KeyBasicInformation, pKeyInfo, ByteCount, &ByteCount ); if (!NT_SUCCESS(Status)) { goto Cleanup; } ResultData = pKeyInfo->Name; ResultDataLength = pKeyInfo->NameLength; } if (ResultDataLength > BufferLength) { goto Cleanup; } memcpy(Buffer, ResultData, ResultDataLength); // // NULL-terminate it just in case, if there is room. // if (ResultDataLength <= BufferLength - sizeof(WCHAR)) { ((PWCHAR)Buffer)[ResultDataLength / sizeof(WCHAR)] = L'\0'; } ReturnValue = TRUE; Cleanup: if (pValueInfo != NULL) { IMirrorFreeMem(pValueInfo); } if (pKeyInfo != NULL) { IMirrorFreeMem(pKeyInfo); } if (Handle != NULL) { NtClose(Handle); } return ReturnValue; } NTSTATUS CheckForPartitions( VOID ) /*++ Routine Description: This routine enumerates all partitions and formats the GlobalMirrorCfgInfo global structure. It also fills in the pConfigPath. Arguments: None. Return Value: STATUS_SUCCESS if it completes adding all the to do items properly. --*/ { UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatus; PARTITION_INFORMATION_EX PartitionInfoEx; PARTITION_INFORMATION PartitionInfo; HANDLE Handle; ULONG MirrorNumber; ULONG DiskNumber; ULONG PartitionNumber; NTSTATUS Status; BOOLEAN foundBoot = FALSE; BOOLEAN foundSystem = FALSE; FILE_FS_SIZE_INFORMATION SizeInfo; LARGE_INTEGER UsedSpace; LARGE_INTEGER FreeSpace; ON_DISK_MBR OnDiskMbr; PUCHAR AlignedBuffer; UINT previousMode; HANDLE DosDevicesDir; ULONG Context; WCHAR SystemDriveLetter; POBJECT_DIRECTORY_INFORMATION DirInfo; ULONG dosLength; BOOLEAN RestartScan; PMIRROR_VOLUME_INFO mirrorVolInfo; ULONG diskSignature; DWORD fileSystemFlags; WCHAR fileSystemName[16]; WCHAR volumeLabel[33]; ULONG volumeLabelLength; WCHAR arcName[MAX_PATH]; ULONG ntNameLength; ULONG arcNameLength; OSVERSIONINFO osVersionInfo; SYSTEM_INFO systemInfo; DWORD fileVersionInfoSize; DWORD versionHandle; PVOID versionInfo; VS_FIXEDFILEINFO * fixedFileInfo; UINT fixedFileInfoLength; WCHAR kernelPath[MAX_PATH]; PWCHAR kernelPathPart; BOOL b; #ifndef IMIRROR_NO_TESTING_LIMITATIONS ULONG numberOfDrives = 0; #endif BOOLEAN isDynamic = FALSE; BOOLEAN UsePartitionInfoEx = TRUE; IMirrorNowDoing(CheckPartitions, NULL); if (GlobalMirrorCfgInfo) { return STATUS_SUCCESS; } // // alloc space for the data structure that tracks the partition // information. // GlobalMirrorCfgInfo = IMirrorAllocMem(sizeof(MIRROR_CFG_INFO)); if (GlobalMirrorCfgInfo == NULL) { Status = STATUS_NO_MEMORY; IMirrorHandleError(Status, CheckPartitions); return Status; } // // Disable hard error popups for this thread. // previousMode = SetErrorMode( SEM_FAILCRITICALERRORS ); // // save away system information in the data structure that tracks the // partition information. // GlobalMirrorCfgInfo->MirrorVersion = IMIRROR_CURRENT_VERSION; GlobalMirrorCfgInfo->FileLength = 0; GlobalMirrorCfgInfo->SystemPath = NULL; GlobalMirrorCfgInfo->SysPrepImage = TRUE; GlobalMirrorCfgInfo->NumberVolumes = 0; osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx(&osVersionInfo)) { GlobalMirrorCfgInfo->MajorVersion = osVersionInfo.dwMajorVersion; GlobalMirrorCfgInfo->MinorVersion = osVersionInfo.dwMinorVersion; GlobalMirrorCfgInfo->BuildNumber = osVersionInfo.dwBuildNumber; lstrcpyW(pCSDVersion, osVersionInfo.szCSDVersion); GlobalMirrorCfgInfo->CSDVersion = pCSDVersion; } // // save kernel version information. // if (SearchPath( NULL, L"ntoskrnl.exe", NULL, MAX_PATH, kernelPath, &kernelPathPart)) { fileVersionInfoSize = GetFileVersionInfoSize(kernelPath, &versionHandle); if (fileVersionInfoSize != 0) { versionInfo = IMirrorAllocMem(fileVersionInfoSize); if (versionInfo != NULL) { if (GetFileVersionInfo( kernelPath, versionHandle, fileVersionInfoSize, versionInfo)) { if (VerQueryValue( versionInfo, L"\\", &fixedFileInfo, &fixedFileInfoLength)) { GlobalMirrorCfgInfo->KernelFileVersionMS = fixedFileInfo->dwFileVersionMS; GlobalMirrorCfgInfo->KernelFileVersionLS = fixedFileInfo->dwFileVersionLS; GlobalMirrorCfgInfo->KernelFileFlags = fixedFileInfo->dwFileFlags; DbgPrint("MS %lx LS %lx flags %lx\n", GlobalMirrorCfgInfo->KernelFileVersionMS, GlobalMirrorCfgInfo->KernelFileVersionLS, GlobalMirrorCfgInfo->KernelFileFlags); } } IMirrorFreeMem(versionInfo); } } } // // is it a debug system? // if (GetSystemMetrics(SM_DEBUG)) { GlobalMirrorCfgInfo->Debug = TRUE; } // // save # of processors. // GetSystemInfo(&systemInfo); GlobalMirrorCfgInfo->NumberOfProcessors = systemInfo.dwNumberOfProcessors; // // save architecture // if (ReadRegistryString( L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager\\Environment", L"PROCESSOR_ARCHITECTURE", pProcessorArchitecture, sizeof(pProcessorArchitecture))) { DbgPrint("processor arch is %ws\n", pProcessorArchitecture); GlobalMirrorCfgInfo->ProcessorArchitecture = pProcessorArchitecture; } // // "current type" of system (server/workstation/etc.) // if (ReadRegistryString( L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion", L"CurrentType", pCurrentType, sizeof(pCurrentType))) { DbgPrint("current type is %ws\n", pCurrentType); GlobalMirrorCfgInfo->CurrentType = pCurrentType; } // // save the hal type // if (ReadRegistryString( L"\\Registry\\Machine\\Hardware\\RESOURCEMAP\\Hardware Abstraction Layer", NULL, pHalName, sizeof(pHalName))) { DbgPrint("HAL name is %ws\n", pHalName); GlobalMirrorCfgInfo->HalName = pHalName; } InitializeListHead( &GlobalMirrorCfgInfo->MirrorVolumeList ); // // Get local system drive letter and \\Systemroot\System32\Config path // Status = GetBaseDeviceName(L"\\SystemRoot", (PWCHAR)TmpBuffer2, sizeof(TmpBuffer2)); if (!NT_SUCCESS(Status)) { IMirrorHandleError(Status, CheckPartitions); goto ExitCheckPartitions; } Status = NtPathToDosPath( (PWCHAR) TmpBuffer2, pConfigPath, ARRAYSIZE(pConfigPath), FALSE, FALSE); if (!NT_SUCCESS(Status)) { IMirrorHandleError(Status, CheckPartitions); goto ExitCheckPartitions; } ASSERT( pConfigPath[1] == L':' ); SystemDriveLetter = (WCHAR) pConfigPath[0]; // // save off the system path so that we can write it out to // the imirror.dat file // lstrcpynW( pSystemPath, pConfigPath, MAX_PATH ); pSystemPath[MAX_PATH-1] = L'\0'; GlobalMirrorCfgInfo->SystemPath = pSystemPath; wcscat( pConfigPath, L"\\System32\\Config"); // // Open \DosDevices directory. // RtlInitUnicodeString(&UnicodeString,L"\\Device"); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenDirectoryObject(&DosDevicesDir, DIRECTORY_QUERY, &ObjectAttributes ); if (!NT_SUCCESS(Status)) { IMirrorHandleError(Status, CheckPartitions); goto ExitCheckPartitions; } // // Iterate each object in that directory that is a directory. // Context = 0; RestartScan = TRUE; Status = NtQueryDirectoryObject(DosDevicesDir, TmpBuffer, sizeof(TmpBuffer), TRUE, RestartScan, &Context, &dosLength ); RestartScan = FALSE; DirInfo = (POBJECT_DIRECTORY_INFORMATION)TmpBuffer; MirrorNumber = 1; while (NT_SUCCESS(Status)) { DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0; DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0; // // Skip this entry if it's not a "HardDiskN" // if ((DirInfo->Name.Length > (sizeof(L"Harddisk")-sizeof(WCHAR))) && (!wcsncmp(DirInfo->Name.Buffer,L"Harddisk",(sizeof(L"Harddisk")/sizeof(WCHAR))-1)) && !_wcsicmp(DirInfo->TypeName.Buffer, L"Directory")) { PWCHAR diskNumberPtr; PartitionNumber = 0; DiskNumber = 0; diskNumberPtr = &DirInfo->Name.Buffer[(sizeof(L"Harddisk")/sizeof(WCHAR))-1]; while (*diskNumberPtr >= L'0' && *diskNumberPtr <= L'9' ) { DiskNumber *= 10; DiskNumber += *(diskNumberPtr) - L'0'; diskNumberPtr++; } if (*diskNumberPtr != L'\0') { // // if the device name wasn't of form HardDiskN, skip this entry. // goto getNextDevice; } diskSignature = 0; // // get the MBR disk signature, continue if it fails. // swprintf((PWCHAR)TmpBuffer2, L"\\Device\\Harddisk%d\\Partition0", DiskNumber); RtlInitUnicodeString(&UnicodeString, (PWCHAR)TmpBuffer2); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtCreateFile(&Handle, (ACCESS_MASK)FILE_GENERIC_READ, &ObjectAttributes, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if (NT_SUCCESS(Status)) { ASSERT(sizeof(ON_DISK_MBR) == 512); AlignedBuffer = ALIGN(TmpBuffer, 512); Status = NtReadFile(Handle, NULL, NULL, NULL, &IoStatus, AlignedBuffer, sizeof(ON_DISK_MBR), NULL, NULL ); if (NT_SUCCESS(Status)) { RtlMoveMemory(&OnDiskMbr, AlignedBuffer, sizeof(ON_DISK_MBR)); ASSERT(U_USHORT(OnDiskMbr.AA55Signature) == 0xAA55); diskSignature = U_ULONG(OnDiskMbr.NTFTSignature); // // check to see if this disk is dynamic // if (OnDiskMbr.PartitionTable[0].SystemId == PARTITION_LDM || OnDiskMbr.PartitionTable[1].SystemId == PARTITION_LDM || OnDiskMbr.PartitionTable[2].SystemId == PARTITION_LDM || OnDiskMbr.PartitionTable[3].SystemId == PARTITION_LDM) { isDynamic = TRUE; NtClose(Handle); goto getNextDevice; } } NtClose(Handle); } while (1) { PartitionNumber++; swprintf((PWCHAR)TmpBuffer2, L"\\Device\\Harddisk%d\\Partition%d", DiskNumber, PartitionNumber); RtlInitUnicodeString(&UnicodeString, (PWCHAR)TmpBuffer2); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtCreateFile(&Handle, (ACCESS_MASK)FILE_GENERIC_READ, &ObjectAttributes, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if (!NT_SUCCESS(Status)) { break; // on to next disk } ZeroMemory(&PartitionInfo,sizeof(PARTITION_INFORMATION)); Status = NtDeviceIoControlFile(Handle, NULL, NULL, NULL, &IoStatus, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &PartitionInfoEx, sizeof(PARTITION_INFORMATION_EX) ); if( (Status == STATUS_NOT_IMPLEMENTED) || (Status == STATUS_INVALID_DEVICE_REQUEST) ) { // // We're on an old build that didn't have this IOCTL. // UsePartitionInfoEx = FALSE; Status = NtDeviceIoControlFile(Handle, NULL, NULL, NULL, &IoStatus, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &PartitionInfo, sizeof(PARTITION_INFORMATION) ); } if (!NT_SUCCESS(Status)) { NtClose(Handle); continue; // on to next partition } // // For Whistler, ignore GPT partitions. if we want to pay // attention to GPT partitions, then we would have to start // paying attention to the GPT disk signature as well. // if( (UsePartitionInfoEx) && (PartitionInfoEx.PartitionStyle != PARTITION_STYLE_MBR) ) { NtClose(Handle); continue; } Status = NtQueryVolumeInformationFile(Handle, &IoStatus, &SizeInfo, sizeof(SizeInfo), FileFsSizeInformation ); NtClose(Handle); if (!NT_SUCCESS(Status)) { continue; // on to next partition } Status = NtPathToDosPath( (PWCHAR) TmpBuffer2, (PWCHAR) TmpBuffer, WCHARSIZE(TmpBuffer), TRUE, FALSE); if (!NT_SUCCESS(Status)) { continue; // on to next partition } if ((lstrlenW((PWCHAR) TmpBuffer) == 0) || *(((PWCHAR)TmpBuffer)+1) != L':') { continue; // on to next partition } // // Get the ARC name of the partition. // NtNameToArcName( (PWCHAR) TmpBuffer2, (PWCHAR) arcName, WCHARSIZE(arcName), FALSE); // // Get the file system type. We add a \ to the end // of TmpBuffer if there isn't one. // if (((PWCHAR)TmpBuffer)[lstrlenW((PWCHAR)TmpBuffer) - 1] != L'\\') { wcscat((PWCHAR)TmpBuffer, L"\\"); } b = GetVolumeInformationW( (PWCHAR) TmpBuffer, volumeLabel, ARRAYSIZE(volumeLabel), NULL, // no volume serial number requested NULL, // no maximum name length requested &fileSystemFlags, fileSystemName, ARRAYSIZE(fileSystemName)); if (!b) { continue; } // // Calculate the amount of free space on the drive. // FreeSpace = RtlExtendedIntegerMultiply( SizeInfo.AvailableAllocationUnits, SizeInfo.SectorsPerAllocationUnit * SizeInfo.BytesPerSector ); UsedSpace = RtlExtendedIntegerMultiply( SizeInfo.TotalAllocationUnits, SizeInfo.SectorsPerAllocationUnit * SizeInfo.BytesPerSector ); UsedSpace = RtlLargeIntegerSubtract( UsedSpace, FreeSpace ); #ifndef IMIRROR_NO_TESTING_LIMITATIONS numberOfDrives++; // // for NT 5.0, the test group doesn't want to test more than a single // partition. Now that the test team is dictating what the feature set // is, we'll return an error if we have more than a single partition or // disk. // if ( (UsePartitionInfoEx && !PartitionInfoEx.Mbr.BootIndicator) || (!UsePartitionInfoEx && !PartitionInfo.BootIndicator)) { if (*(PWCHAR)TmpBuffer == SystemDriveLetter) { IMirrorHandleError(STATUS_MISSING_SYSTEMFILE, CheckPartitions); NtClose(DosDevicesDir); Status = STATUS_MISSING_SYSTEMFILE; goto ExitCheckPartitions; } continue; } if (*(PWCHAR)TmpBuffer != SystemDriveLetter) { // if another drive is marked bootable but it isn't the // system drive, we'll ignore it. We'll pick up the // error down below if this is the only bootable drive. #if 0 if ( (UsePartitionInfoEx && PartitionInfoEx.Mbr.BootIndicator) || (!UsePartitionInfoEx && PartitionInfo.BootIndicator)) { IMirrorHandleError(STATUS_MISSING_SYSTEMFILE, CheckPartitions); NtClose(DosDevicesDir); Status = STATUS_MISSING_SYSTEMFILE; goto ExitCheckPartitions; } #endif continue; } #endif mirrorVolInfo = IMirrorAllocMem(sizeof(MIRROR_VOLUME_INFO)); if (mirrorVolInfo == NULL) { NtClose(DosDevicesDir); Status = STATUS_NO_MEMORY; IMirrorHandleError(Status, CheckPartitions); goto ExitCheckPartitions; } // // Save the NT and ARC device names. // ntNameLength = (lstrlenW( (PWCHAR)TmpBuffer2 ) + 1) * sizeof(WCHAR); mirrorVolInfo->NtName = IMirrorAllocMem(ntNameLength); if (mirrorVolInfo->NtName == NULL) { Status = STATUS_NO_MEMORY; IMirrorHandleError(Status, CheckPartitions); NtClose(DosDevicesDir); goto ExitCheckPartitions; } arcNameLength = (lstrlenW( (PWCHAR)arcName ) + 1) * sizeof(WCHAR); mirrorVolInfo->ArcName = IMirrorAllocMem(arcNameLength); if (mirrorVolInfo->ArcName == NULL) { Status = STATUS_NO_MEMORY; IMirrorHandleError(Status, CheckPartitions); NtClose(DosDevicesDir); goto ExitCheckPartitions; } memcpy(mirrorVolInfo->NtName, TmpBuffer2, ntNameLength); memcpy(mirrorVolInfo->ArcName, arcName, arcNameLength); mirrorVolInfo->DriveLetter = *(PWCHAR)TmpBuffer; mirrorVolInfo->PartitionType = UsePartitionInfoEx ? PartitionInfoEx.Mbr.PartitionType : PartitionInfo.PartitionType; // // If this is a non-NTFS volume, check if it is configured // for compression // if ( ((UsePartitionInfoEx && (PartitionInfoEx.Mbr.PartitionType != PARTITION_IFS)) || (!UsePartitionInfoEx && (PartitionInfo.PartitionType != PARTITION_IFS))) && (fileSystemFlags & FS_VOL_IS_COMPRESSED) ) { mirrorVolInfo->CompressedVolume = TRUE; } else { mirrorVolInfo->CompressedVolume = FALSE; } if ( (UsePartitionInfoEx && (PartitionInfoEx.Mbr.BootIndicator)) || (!UsePartitionInfoEx && (PartitionInfo.BootIndicator)) ) { foundBoot = TRUE; mirrorVolInfo->PartitionActive = TRUE; } else { mirrorVolInfo->PartitionActive = FALSE; } if (*(PWCHAR)TmpBuffer == SystemDriveLetter) { foundSystem = TRUE; mirrorVolInfo->IsBootDisk = TRUE; } else { mirrorVolInfo->IsBootDisk = FALSE; } mirrorVolInfo->DiskNumber = DiskNumber; mirrorVolInfo->PartitionNumber = PartitionNumber; mirrorVolInfo->MirrorTableIndex = MirrorNumber++; mirrorVolInfo->MirrorUncPath = NULL; mirrorVolInfo->LastUSNMirrored = 0; mirrorVolInfo->BlockSize = SizeInfo.BytesPerSector; mirrorVolInfo->DiskSignature = diskSignature; mirrorVolInfo->FileSystemFlags = fileSystemFlags; wcscpy(mirrorVolInfo->FileSystemName, fileSystemName); volumeLabelLength = (lstrlenW( (PWCHAR)volumeLabel ) + 1) * sizeof(WCHAR); mirrorVolInfo->VolumeLabel = IMirrorAllocMem(volumeLabelLength); if (mirrorVolInfo->VolumeLabel == NULL) { Status = STATUS_NO_MEMORY; IMirrorHandleError(Status, CheckPartitions); NtClose(DosDevicesDir); goto ExitCheckPartitions; } memcpy(mirrorVolInfo->VolumeLabel, volumeLabel, volumeLabelLength); mirrorVolInfo->StartingOffset = UsePartitionInfoEx ? PartitionInfoEx.StartingOffset : PartitionInfo.StartingOffset; mirrorVolInfo->PartitionSize = UsePartitionInfoEx ? PartitionInfoEx.PartitionLength : PartitionInfo.PartitionLength; mirrorVolInfo->DiskSpaceUsed = UsedSpace; InsertTailList( &GlobalMirrorCfgInfo->MirrorVolumeList, &mirrorVolInfo->ListEntry ); GlobalMirrorCfgInfo->NumberVolumes = MirrorNumber - 1; } } // // Go on to next object. // getNextDevice: Status = NtQueryDirectoryObject( DosDevicesDir, TmpBuffer, sizeof(TmpBuffer), TRUE, RestartScan, &Context, &dosLength ); } NtClose(DosDevicesDir); if ((!foundBoot) || (!foundSystem) ) { Status = (isDynamic ? STATUS_OBJECT_TYPE_MISMATCH : STATUS_MISSING_SYSTEMFILE); IMirrorHandleError(Status, CheckPartitions); goto ExitCheckPartitions; } #ifndef IMIRROR_NO_TESTING_LIMITATIONS if (numberOfDrives > 1) { IMirrorHandleError(ERROR_INVALID_DRIVE, CheckPartitions); Status = ERROR_INVALID_DRIVE; } else { Status = STATUS_SUCCESS; } #else Status = STATUS_SUCCESS; #endif ExitCheckPartitions: SetErrorMode( previousMode ); return Status; } NTSTATUS NtPathToDosPath( IN PWSTR NtPath, OUT PWSTR DosPath, IN ULONG DosPathBufferSize, IN BOOLEAN GetDriveOnly, IN BOOLEAN NtPathIsBasic ) /*++ Routine Description: This routine calls off to convert a \Device\HarddiskX\PartitionY\ to Z:\ Arguments: NtPath - Something like \Device\Harddisk0\Partition2\WINNT DosPath - Will be something like D: or D:\WINNT, depending on flag below. DosPathBufferSize - size in characters of DosPath buffer GetDriveOnly - TRUE if the caller only wants the DOS drive. NtPathIsBasic - TRUE if NtPath is not symbolic link. Return Value: STATUS_SUCCESS if it completes filling in DosDrive, else an appropriate error code. --*/ { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UnicodeString; NTSTATUS Status = STATUS_SUCCESS; HANDLE DosDevicesDir; ULONG Context; ULONG Length; BOOLEAN RestartScan; WCHAR LinkTarget[2*MAX_PATH]; POBJECT_DIRECTORY_INFORMATION DirInfo; WCHAR LocalBuffer[MAX_PATH]; WCHAR LocalBuffer2[MAX_PATH]; PWCHAR pTmp; PWCHAR NameSpace[] = { L"\\??", L"\\GLOBAL??" }; UINT i; if (NtPath == NULL) { return ERROR_PATH_NOT_FOUND; } if (!NtPathIsBasic) { // // Find the end of the \device\harddiskX\partitionY string // wcsncpy(LocalBuffer2, NtPath, MAX_PATH); LocalBuffer2[MAX_PATH-1] = L'\0'; pTmp = LocalBuffer2; if (*pTmp != L'\\') { return ERROR_PATH_NOT_FOUND; } pTmp = wcsstr(pTmp + 1, L"\\"); if (pTmp == NULL) { return ERROR_PATH_NOT_FOUND; } pTmp = wcsstr(pTmp + 1, L"\\"); if (pTmp == NULL) { return ERROR_PATH_NOT_FOUND; } pTmp = wcsstr(pTmp + 1, L"\\"); if (pTmp != NULL) { *pTmp = UNICODE_NULL; pTmp++; } // // Find the base NT device name // Status = GetBaseDeviceName(LocalBuffer2, LocalBuffer, sizeof(LocalBuffer)); if (!NT_SUCCESS(Status)) { return Status; } } else { wcsncpy(LocalBuffer, NtPath, MAX_PATH); LocalBuffer[MAX_PATH-1] = L'\0'; pTmp = NULL; } // // Open \DosDevices directory. First try the "normal" dosdevices path, // then try the global dosdevices path. // for (i = 0; i < sizeof(NameSpace)/sizeof(PWCHAR *); i++) { RtlInitUnicodeString(&UnicodeString,NameSpace[i]); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenDirectoryObject(&DosDevicesDir, DIRECTORY_QUERY, &ObjectAttributes ); if (!NT_SUCCESS(Status)) { DosDevicesDir = NULL; } else { // // Iterate each object in that directory. // Context = 0; RestartScan = TRUE; Status = NtQueryDirectoryObject(DosDevicesDir, TmpBuffer3, sizeof(TmpBuffer3), TRUE, RestartScan, &Context, &Length ); RestartScan = FALSE; DirInfo = (POBJECT_DIRECTORY_INFORMATION)TmpBuffer3; while (NT_SUCCESS(Status)) { DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0; DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0; // // Skip this entry if it's not a symbolic link. // if ((DirInfo->Name.Length != 0) && (DirInfo->Name.Buffer[1] == L':') && !_wcsicmp(DirInfo->TypeName.Buffer, L"SymbolicLink")) { // // Get this \DosDevices object's link target. // if (_snwprintf( LocalBuffer2, WCHARSIZE(LocalBuffer2), L"%ws\\%ws", NameSpace[i], DirInfo->Name.Buffer) < 0) { // // not enough buffer space, go onto next link. // goto next; } LocalBuffer2[MAX_PATH-1] = L'\0'; Status = GetBaseDeviceName(LocalBuffer2, LinkTarget, sizeof(LinkTarget)); if (NT_SUCCESS(Status)) { // // See if it's a prefix of the path we're converting, // if(!_wcsnicmp(LocalBuffer, LinkTarget, wcslen(LinkTarget))) { // // Got a match. // lstrcpynW(DosPath, DirInfo->Name.Buffer,DosPathBufferSize); DosPath[DosPathBufferSize-1] = L'\0'; if (!GetDriveOnly) { if (NtPathIsBasic) { if (wcslen(LocalBuffer + wcslen(LinkTarget)) + wcslen(DosPath) + 1 <= DosPathBufferSize) { lstrcat(DosPath, LocalBuffer + wcslen(LinkTarget)); } else { goto next; } } else if (pTmp != NULL) { if (wcslen(DosPath) + wcslen(pTmp) + sizeof(L"\\")/sizeof(WCHAR) <= DosPathBufferSize) { lstrcat(DosPath, L"\\"); lstrcat(DosPath, pTmp); } else { goto next; } } } NtClose(DosDevicesDir); return(STATUS_SUCCESS); } } } next: // // Go on to next object. // Status = NtQueryDirectoryObject( DosDevicesDir, TmpBuffer3, sizeof(TmpBuffer3), TRUE, RestartScan, &Context, &Length ); } NtClose(DosDevicesDir); } } return(Status); } NTSTATUS NtNameToArcName( IN PWSTR NtName, OUT PWSTR ArcName, IN ULONG ArcNameBufferSize, IN BOOLEAN NtNameIsBasic ) /*++ Routine Description: This routine calls off to convert a \Device\HarddiskX\PartitionY to the ARC name. Arguments: NtName - Something like \Device\Harddisk0\Partition2 ArcName - Will be something like \Arcname\multi(0)disk(0)rdisk(0)partition(1). ArcNameBufferSize - size in characters of arcname buffer NtNameIsBasic - TRUE if NtName is not symbolic link. Return Value: STATUS_SUCCESS if it completes filling in ArcName, else an appropriate error code. --*/ { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UnicodeString; NTSTATUS Status; HANDLE DosDevicesDir; ULONG Context; ULONG Length; BOOLEAN RestartScan; WCHAR LinkTarget[2*MAX_PATH]; POBJECT_DIRECTORY_INFORMATION DirInfo; WCHAR LocalBuffer[MAX_PATH]; WCHAR LocalBuffer2[MAX_PATH]; if (!NtNameIsBasic) { // // Find the base NT device name // Status = GetBaseDeviceName(NtName, LocalBuffer, sizeof(LocalBuffer)); if (!NT_SUCCESS(Status)) { return Status; } } else { wcsncpy(LocalBuffer, NtName, MAX_PATH); LocalBuffer[MAX_PATH-1] = L'\0'; } // // Open \ArcName directory. // RtlInitUnicodeString(&UnicodeString,L"\\ArcName"); InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenDirectoryObject(&DosDevicesDir, DIRECTORY_QUERY, &ObjectAttributes ); if (!NT_SUCCESS(Status)) { return Status; } // // Iterate each object in that directory. // Context = 0; RestartScan = TRUE; Status = NtQueryDirectoryObject(DosDevicesDir, TmpBuffer3, sizeof(TmpBuffer3), TRUE, RestartScan, &Context, &Length ); RestartScan = FALSE; DirInfo = (POBJECT_DIRECTORY_INFORMATION)TmpBuffer3; while (NT_SUCCESS(Status)) { DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0; DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0; // // Skip this entry if it's not a symbolic link. // if ((DirInfo->Name.Length != 0) && !_wcsicmp(DirInfo->TypeName.Buffer, L"SymbolicLink")) { // // Get this \DosDevices object's link target. // if (_snwprintf( LocalBuffer2, MAX_PATH, L"\\ArcName\\%ws", DirInfo->Name.Buffer) < 0) { goto next; } LocalBuffer2[MAX_PATH-1] = L'\0'; Status = GetBaseDeviceName(LocalBuffer2, LinkTarget, sizeof(LinkTarget)); if (NT_SUCCESS(Status)) { // // See if the base name of this link matches the base // name of what we are looking for. // if(!_wcsnicmp(LocalBuffer, LinkTarget, wcslen(LinkTarget))) { if (wcslen(DirInfo->Name.Buffer) + 1 > ArcNameBufferSize) { return ERROR_BUFFER_OVERFLOW; } // // Got a match. // lstrcpyn(ArcName, DirInfo->Name.Buffer, ArcNameBufferSize); ArcName[ArcNameBufferSize-1] = L'\0'; NtClose(DosDevicesDir); return STATUS_SUCCESS; } } } next: // // Go on to next object. // Status = NtQueryDirectoryObject( DosDevicesDir, TmpBuffer3, sizeof(TmpBuffer3), TRUE, RestartScan, &Context, &Length ); } NtClose(DosDevicesDir); return Status; } NTSTATUS GetBaseDeviceName( IN PWSTR SymbolicName, OUT PWSTR Buffer, IN ULONG Size ) /*++ Routine Description: This routine drills down thru symbolic links until it finds the base device name. Arguments: SymbolicName - The name to start with. Buffer - The output buffer. Size - Length, in bytes of Buffer Return Value: STATUS_SUCCESS if it completes adding all the to do items properly. --*/ { UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE Handle; NTSTATUS Status; // // Start at the first name // RtlInitUnicodeString(&UnicodeString, SymbolicName); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenSymbolicLinkObject(&Handle, (ACCESS_MASK)SYMBOLIC_LINK_QUERY, &ObjectAttributes ); if (!NT_SUCCESS(Status)) { return Status; } while (TRUE) { // // Take this open and get the next name // UnicodeString.Length = 0; UnicodeString.MaximumLength = (USHORT)Size; UnicodeString.Buffer = (PWCHAR)Buffer; Status = NtQuerySymbolicLinkObject(Handle, &UnicodeString, NULL ); NtClose(Handle); Buffer[(UnicodeString.Length / sizeof(WCHAR))] = UNICODE_NULL; if (!NT_SUCCESS(Status)) { return Status; } // // See if the next name is also a symbolic name // RtlInitUnicodeString(&UnicodeString, Buffer); InitializeObjectAttributes( &ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenSymbolicLinkObject(&Handle, (ACCESS_MASK)SYMBOLIC_LINK_QUERY, &ObjectAttributes ); if (!NT_SUCCESS(Status)) { return STATUS_SUCCESS; } } }