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.
1579 lines
48 KiB
1579 lines
48 KiB
/*++
|
|
|
|
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 <ntverp.h>
|
|
|
|
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\<path> to Z:\<path>
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|