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.
4641 lines
140 KiB
4641 lines
140 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
spsetup.c
|
|
|
|
Abstract:
|
|
|
|
Module for supporing installation of SysPrep images from a remote share
|
|
|
|
Author:
|
|
|
|
Sean Selitrennikoff (v-seasel) 6-10-1998
|
|
|
|
--*/
|
|
|
|
#include "spprecmp.h"
|
|
#pragma hdrstop
|
|
#include "spcmdcon.h"
|
|
#include <remboot.h>
|
|
#include <oscpkt.h>
|
|
#include <regstr.h>
|
|
|
|
NET_CARD_INFO RemoteSysPrepNetCardInfo;
|
|
PVOID pGlobalResponsePacket = NULL;
|
|
ULONG GlobalResponsePacketLength = 0;
|
|
|
|
#define SYSPREP_PARTITION_SLOP 10 // in percent
|
|
|
|
VOID
|
|
SpInstallSysPrepImage(
|
|
IN HANDLE SetupSifHandle,
|
|
IN HANDLE WinntSifHandle,
|
|
IN PMIRROR_CFG_INFO_FILE pFileData,
|
|
IN PMIRROR_CFG_INFO_MEMORY pMemoryData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main routine for installing a SysPrep images from a remote share.
|
|
|
|
Arguments:
|
|
|
|
WinntSifHandle - Handle to the SIF file.
|
|
|
|
pFileData - The IMirror.dat data, as saved in the file.
|
|
|
|
pMemoryData - The IMirror.dat data, as modified to match this computer.
|
|
|
|
Return Value:
|
|
|
|
None. Doesn't return on fatal failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD cDisk;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Right here we should check to see if any patching is going to be needed
|
|
// by opening the hive files on the server and checking with the passed in
|
|
// PCI ids. If patching is necessary, and the SIF file contains a pointer
|
|
// to a CD image that matches the SysPrep image, then we call off to BINL
|
|
// to find the appropriate drivers. If BINL does not return an error, then
|
|
// we assume that later we will be able to do the patch (after the file copy
|
|
// below).
|
|
//
|
|
// If it looks like the patch will fail, either because there is no pointer
|
|
// to a CD image, or BINL returned an error, then we present the user with
|
|
// a screen telling them that any hardware differences between their machine
|
|
// and the SysPrep image may result in an unbootable system. They may choose
|
|
// to continue the setup, or quit.
|
|
//
|
|
// NOTE: seanse - Put all of the above here.
|
|
|
|
//
|
|
// For each disk, copy all the files to the local store.
|
|
//
|
|
for (cDisk = 0; cDisk < pFileData->NumberVolumes; cDisk++) {
|
|
if (!SpCopyMirrorDisk(pFileData, cDisk)) {
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Patch up the SysPrep image.
|
|
//
|
|
Status = SpPatchSysPrepImage( SetupSifHandle,
|
|
WinntSifHandle,
|
|
pFileData,
|
|
pMemoryData);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ULONG ValidKeys[2] = { KEY_F3, 0 };
|
|
ULONG Mnemonics[2] = { MnemonicContinueSetup,0 };
|
|
|
|
while (1) {
|
|
|
|
if (Status == STATUS_INVALID_PARAMETER) {
|
|
|
|
SpStartScreen(
|
|
SP_SCRN_SYSPREP_PATCH_MISSING_OS,
|
|
3,
|
|
HEADER_HEIGHT+1,
|
|
FALSE,
|
|
FALSE,
|
|
DEFAULT_ATTRIBUTE
|
|
);
|
|
|
|
} else {
|
|
|
|
SpStartScreen(
|
|
SP_SCRN_SYSPREP_PATCH_FAILURE,
|
|
3,
|
|
HEADER_HEIGHT+1,
|
|
FALSE,
|
|
FALSE,
|
|
DEFAULT_ATTRIBUTE
|
|
);
|
|
}
|
|
|
|
SpDisplayStatusOptions(
|
|
DEFAULT_STATUS_ATTRIBUTE,
|
|
SP_STAT_C_EQUALS_CONTINUE_SETUP,
|
|
SP_STAT_F3_EQUALS_EXIT,
|
|
0
|
|
);
|
|
|
|
switch(SpWaitValidKey(ValidKeys,NULL,Mnemonics)) {
|
|
case KEY_F3:
|
|
SpConfirmExit();
|
|
break;
|
|
default:
|
|
//
|
|
// must be c=continue
|
|
//
|
|
goto CleanUp;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CleanUp:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
SpFixupThirdPartyComponents(
|
|
IN PVOID SifHandle,
|
|
IN PWSTR ThirdPartySourceDevicePath,
|
|
IN PDISK_REGION NtPartitionRegion,
|
|
IN PWSTR Sysroot,
|
|
IN PDISK_REGION SystemPartitionRegion,
|
|
IN PWSTR SystemPartitionDirectory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will take care of installing any 3rd party drivers detected during setupldr.
|
|
We have to take care of this here because the normal code path for textmode setup has
|
|
been bypassed in lieu of installing a sysprep'd image.
|
|
|
|
Arguments:
|
|
|
|
SifHandle - supplies handle to loaded setup information file.
|
|
|
|
ThirdPartySourceDevicePath - path to 3rd party install files.
|
|
|
|
NtPartitionRegion - region where installation is located.
|
|
|
|
Sysroot - string containing %windir% (with no drive).
|
|
|
|
SystemPartitionRegion - region where system partition is located.
|
|
|
|
SystemPartitionDirectory - directory on the system partition where
|
|
system-specific files are located (should
|
|
be NULL for non-ARC machines)
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS which will indicate success or failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PWSTR NtPartition = NULL,
|
|
SystemPartition = NULL;
|
|
PDISK_FILE_LIST DiskFileLists;
|
|
ULONG DiskCount;
|
|
HANDLE hKeyControlSetServices;
|
|
UNICODE_STRING UnicodeString1;
|
|
UNICODE_STRING UnicodeString2;
|
|
UNICODE_STRING UnicodeString;
|
|
WCHAR Path[MAX_PATH];
|
|
HANDLE DstHandle = NULL;
|
|
DWORD Size, Number;
|
|
PVOID Buffer = NULL;
|
|
OBJECT_ATTRIBUTES Obj;
|
|
OBJECT_ATTRIBUTES DstObj;
|
|
|
|
//
|
|
// See if there's anything for us to do.
|
|
//
|
|
if( PreinstallScsiHardware == NULL ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// =================
|
|
// Install the files.
|
|
// =================
|
|
//
|
|
|
|
//
|
|
// Get the device path of the nt partition.
|
|
//
|
|
SpNtNameFromRegion( NtPartitionRegion,
|
|
TemporaryBuffer,
|
|
sizeof(TemporaryBuffer),
|
|
PartitionOrdinalCurrent );
|
|
NtPartition = SpDupStringW(TemporaryBuffer);
|
|
|
|
//
|
|
// Get the device path of the system partition.
|
|
//
|
|
if (SystemPartitionRegion != NULL) {
|
|
SpNtNameFromRegion( SystemPartitionRegion,
|
|
TemporaryBuffer,
|
|
sizeof(TemporaryBuffer),
|
|
PartitionOrdinalCurrent );
|
|
SystemPartition = SpDupStringW(TemporaryBuffer);
|
|
} else {
|
|
SystemPartition = NULL;
|
|
}
|
|
|
|
//
|
|
// Generate media descriptors for the source media.
|
|
//
|
|
SpInitializeFileLists( SifHandle,
|
|
&DiskFileLists,
|
|
&DiskCount );
|
|
|
|
SpCopyThirdPartyDrivers( ThirdPartySourceDevicePath,
|
|
NtPartition,
|
|
Sysroot,
|
|
SystemPartition,
|
|
SystemPartitionDirectory,
|
|
DiskFileLists,
|
|
DiskCount );
|
|
|
|
|
|
//
|
|
// =================
|
|
// Set the registry.
|
|
// =================
|
|
//
|
|
|
|
//
|
|
// We need to open the hive of the target install, not
|
|
// our own. Get a path to the system hive.
|
|
//
|
|
wcscpy(Path, NtPartition);
|
|
SpConcatenatePaths(Path, Sysroot);
|
|
SpConcatenatePaths(Path, L"system32\\config\\system");
|
|
|
|
//
|
|
// Load him up.
|
|
//
|
|
INIT_OBJA(&Obj, &UnicodeString2, Path);
|
|
INIT_OBJA(&DstObj, &UnicodeString1, L"\\Registry\\SysPrepReg");
|
|
|
|
Status = ZwLoadKey(&DstObj, &Obj);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpFixupThirdPartyComponents: ZwLoadKey to SysPrepReg failed %lx\n", Status));
|
|
goto CleanUp0;
|
|
}
|
|
|
|
|
|
//
|
|
// Now get path to services key in the SysPrep image
|
|
//
|
|
wcscpy(Path, L"\\Registry\\SysPrepReg");
|
|
INIT_OBJA(&Obj, &UnicodeString2, Path);
|
|
Status = ZwOpenKey(&DstHandle, KEY_READ, &Obj);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpFixupThirdPartyComponents: ZwOpenKey of root SysPrepReg failed %lx\n", Status));
|
|
goto CleanUp1;
|
|
}
|
|
|
|
//
|
|
// Allocate a temporary buffer, then figure out which is the current control set.
|
|
//
|
|
Buffer = SpMemAlloc(1024 * 4);
|
|
if( Buffer == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto CleanUp1;
|
|
}
|
|
|
|
Status = SpGetValueKey( DstHandle,
|
|
L"Select",
|
|
L"Current",
|
|
1024 * 4,
|
|
Buffer,
|
|
&Size );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpFixupThirdPartyComponents: SpGetValueKey of Select\\Current failed %lx\n", Status));
|
|
goto CleanUp1;
|
|
}
|
|
|
|
if ( (ULONG)(((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Type) != REG_DWORD ) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpFixupThirdPartyComponents: SpGetValueKey of Select\\Current didn't return a REG_DWORD.\n"));
|
|
goto CleanUp1;
|
|
}
|
|
|
|
Number = *((DWORD *)(((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data));
|
|
|
|
ZwClose(DstHandle);
|
|
DstHandle = NULL;
|
|
|
|
|
|
|
|
//
|
|
// We're ready to actually open CCS\Services key.
|
|
//
|
|
swprintf(Path,
|
|
L"\\Registry\\SysPrepReg\\ControlSet%03d\\Services",
|
|
Number
|
|
);
|
|
INIT_OBJA(&Obj, &UnicodeString, Path);
|
|
Status = ZwOpenKey(&DstHandle, KEY_ALL_ACCESS, &Obj);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpFixupThirdPartyComponents: ZwOpenKey of SysPrepReg services failed %lx for %ws\n", Status,Path));
|
|
goto CleanUp1;
|
|
}
|
|
|
|
//
|
|
// Do it.
|
|
//
|
|
Status = SpThirdPartyRegistry(DstHandle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpFixupThirdPartyComponents: SpThirdPartyRegistry failed %lx\n", Status));
|
|
goto CleanUp1;
|
|
}
|
|
|
|
|
|
CleanUp1:
|
|
ZwUnloadKey(&DstObj);
|
|
|
|
if( Buffer ) {
|
|
SpMemFree( Buffer );
|
|
}
|
|
|
|
if( DstHandle ) {
|
|
ZwClose(DstHandle);
|
|
}
|
|
|
|
CleanUp0:
|
|
if( NtPartition ) {
|
|
SpMemFree( NtPartition );
|
|
}
|
|
|
|
if( SystemPartition ) {
|
|
SpMemFree( SystemPartition );
|
|
}
|
|
|
|
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SpReadIMirrorFile(
|
|
OUT PMIRROR_CFG_INFO_FILE *ppFileData,
|
|
IN PCHAR IMirrorFilePath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens the file in IMirrorFilePath, allocates a buffer, copies the data
|
|
into the buffer and returns the buffer. This buffer needs to be freed later.
|
|
|
|
Arguments:
|
|
|
|
ppFileData - If TRUE is returned, a pointer to an in-memory copy of the file.
|
|
|
|
IMirrorFilePath - The UNC to the root directory containing all the IMirrorX directories.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, else it generates a fatal error.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR wszRootDir[MAX_PATH];
|
|
ULONG ulReturnData;
|
|
|
|
mbstowcs(wszRootDir, IMirrorFilePath, strlen(IMirrorFilePath) + 1);
|
|
|
|
*ppFileData = NULL;
|
|
|
|
//
|
|
// Enumerate thru all the files in the base directory looking for the IMirror data file.
|
|
// If it is found, the callback function fills in pFileData.
|
|
//
|
|
if ((SpEnumFiles(wszRootDir, SpFindMirrorDataFile, &ulReturnData, (PVOID)ppFileData) == EnumFileError) ||
|
|
(*ppFileData == NULL)) {
|
|
|
|
SpSysPrepFailure( SP_SYSPREP_NO_MIRROR_FILE, wszRootDir, NULL );
|
|
|
|
// shouldn't get here.
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpFindMirrorDataFile(
|
|
IN PCWSTR SrcPath,
|
|
IN PFILE_BOTH_DIR_INFORMATION FileInfo,
|
|
OUT PULONG ReturnData,
|
|
IN PVOID *ppFileData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the file enumerator as a callback for
|
|
each file or subdirectory found in the source directory.
|
|
If FileInfo represents a file, then we skip it.
|
|
If FileInfo represents a directory, then we search it for the IMirror data file.
|
|
|
|
Arguments:
|
|
|
|
SrcPath - Absolute path to the source directory. This path should contain
|
|
the path to the source device.
|
|
|
|
FileInfo - supplies find data for a file in the source dir.
|
|
|
|
ReturnData - receives an error code if an error occurs.
|
|
|
|
ppFileData - If successful in finding the IMirror data file, this is a buffer which is
|
|
a copy of the file.
|
|
|
|
Return Value:
|
|
|
|
FALSE if we find the IMirror data file, else TRUE. (return value is used to continue
|
|
the enumeration or not)
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR Temp1;
|
|
PWSTR Temp2;
|
|
ULONG ulLen;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE Handle;
|
|
|
|
if(!(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return TRUE;
|
|
}
|
|
|
|
Handle = NULL;
|
|
|
|
//
|
|
// Build the path name to the IMirror data file
|
|
//
|
|
Temp1 = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2);
|
|
ulLen = FileInfo->FileNameLength/sizeof(WCHAR);
|
|
|
|
wcsncpy(Temp1,FileInfo->FileName, ulLen);
|
|
Temp1[ulLen] = 0;
|
|
|
|
wcscpy(TemporaryBuffer, SrcPath);
|
|
SpConcatenatePaths(TemporaryBuffer, Temp1);
|
|
SpConcatenatePaths(TemporaryBuffer, IMIRROR_DAT_FILE_NAME);
|
|
Temp2 = SpDupStringW(TemporaryBuffer);
|
|
|
|
INIT_OBJA(&Obja, &UnicodeString, Temp2);
|
|
|
|
Status = ZwCreateFile(&Handle,
|
|
FILE_GENERIC_READ,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
SpMemFree(Temp2);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
Status = SpGetFileSize(Handle, &ulLen);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
ZwClose(Handle);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Now allocate memory and read in the file.
|
|
//
|
|
*ppFileData = SpMemAlloc(ulLen);
|
|
|
|
Status = ZwReadFile(Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
*ppFileData,
|
|
ulLen,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
SpMemFree(*ppFileData);
|
|
*ppFileData = NULL;
|
|
ZwClose(Handle);
|
|
return TRUE;
|
|
}
|
|
|
|
ZwClose(Handle);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpDetermineDiskLayout(
|
|
IN PMIRROR_CFG_INFO_FILE pFileData,
|
|
OUT PMIRROR_CFG_INFO_MEMORY *pMemoryData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes the passed-in IMirror.dat file and produces a
|
|
resulting memory structure indicating how the local disks should
|
|
be partitioned.
|
|
|
|
Arguments:
|
|
|
|
pFileData - A pointer to an in-memory copy of IMirror.Dat.
|
|
|
|
pMemoryData - Returnes an allocated pointed to how the disks should
|
|
be partitioned.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, else it generates a fatal error.
|
|
|
|
--*/
|
|
{
|
|
PMIRROR_CFG_INFO_MEMORY memData;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
NTSTATUS Status;
|
|
ULONG ResultLength;
|
|
PKEY_BASIC_INFORMATION KeyInfo;
|
|
PWSTR CurrentHalName, OriginalHalName;
|
|
ULONG CurrentHalNameLength;
|
|
ULONG i, j;
|
|
ULONG diskNumber;
|
|
PPARTITIONED_DISK pDisk;
|
|
|
|
if (pFileData->MirrorVersion != IMIRROR_CURRENT_VERSION) {
|
|
|
|
SpSysPrepFailure( SP_SYSPREP_INVALID_VERSION, NULL, NULL );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check if the current HAL that textmode installed for this
|
|
// system is different from the one that is running on this system
|
|
// (note that this works because for remote install boots, setupldr
|
|
// loads the real HAL, not the one from the short list of HALs
|
|
// included on the boot floppy).
|
|
//
|
|
|
|
INIT_OBJA(&Obja, &UnicodeString, L"\\Registry\\Machine\\Hardware\\RESOURCEMAP\\Hardware Abstraction Layer");
|
|
Status = ZwOpenKey(&Handle, KEY_READ, &Obja);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpDetermineDiskLayout: ZwOpenKey of HAL key failed %lx\n", Status));
|
|
SpSysPrepFailure( SP_SYSPREP_WRONG_HAL, NULL, NULL );
|
|
return FALSE;
|
|
}
|
|
|
|
KeyInfo = (PKEY_BASIC_INFORMATION)TemporaryBuffer;
|
|
Status = ZwEnumerateKey(Handle, 0, KeyBasicInformation, KeyInfo, sizeof(TemporaryBuffer), &ResultLength);
|
|
|
|
ZwClose(Handle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpDetermineDiskLayout: ZwEnumerateKey of HAL key failed %lx\n", Status));
|
|
SpSysPrepFailure( SP_SYSPREP_WRONG_HAL, NULL, NULL );
|
|
return FALSE;
|
|
}
|
|
|
|
KeyInfo->Name[KeyInfo->NameLength / sizeof(WCHAR)] = L'\0';
|
|
CurrentHalName = SpDupStringW(KeyInfo->Name);
|
|
CurrentHalNameLength = KeyInfo->NameLength;
|
|
|
|
OriginalHalName = (PWCHAR)(((PUCHAR)pFileData) + pFileData->HalNameOffset);
|
|
|
|
if (!CurrentHalName ||
|
|
(memcmp(OriginalHalName, CurrentHalName, CurrentHalNameLength) != 0)) {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL,
|
|
"SpDetermineDiskLayout: HAL strings different, old <%ws> new <%ws>\n",
|
|
OriginalHalName,
|
|
CurrentHalName));
|
|
|
|
SpSysPrepFailure(
|
|
SP_SYSPREP_WRONG_HAL,
|
|
OriginalHalName,
|
|
CurrentHalName);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// For the moment, don't worry about the number of processors being
|
|
// different. The HAL check will probably catch it, and if not it should
|
|
// still work as long as the build is internally consistent, since we
|
|
// don't replace any components right now. There are two error screens
|
|
// defined for this case, SP_SYSPREP_WRONG_PROCESSOR_COUNT_UNI and
|
|
// SP_SYSPREP_WRONG_PROCESSOR_COUNT_MULTI.
|
|
//
|
|
|
|
memData = SpMemAlloc(FIELD_OFFSET(MIRROR_CFG_INFO_MEMORY, Volumes[0]) +
|
|
(pFileData->NumberVolumes * sizeof(MIRROR_VOLUME_INFO_MEMORY)));
|
|
|
|
memData->NumberVolumes = pFileData->NumberVolumes;
|
|
for (i = 0; i < pFileData->NumberVolumes; i++) {
|
|
|
|
memData->Volumes[i].DriveLetter = pFileData->Volumes[i].DriveLetter;
|
|
memData->Volumes[i].PartitionType = pFileData->Volumes[i].PartitionType;
|
|
memData->Volumes[i].PartitionActive = pFileData->Volumes[i].PartitionActive;
|
|
memData->Volumes[i].IsBootDisk = pFileData->Volumes[i].IsBootDisk;
|
|
memData->Volumes[i].CompressedVolume = pFileData->Volumes[i].CompressedVolume;
|
|
diskNumber = pFileData->Volumes[i].DiskNumber;
|
|
memData->Volumes[i].DiskNumber = diskNumber;
|
|
memData->Volumes[i].PartitionNumber = pFileData->Volumes[i].PartitionNumber;
|
|
memData->Volumes[i].DiskSignature = pFileData->Volumes[i].DiskSignature;
|
|
memData->Volumes[i].BlockSize = pFileData->Volumes[i].BlockSize;
|
|
memData->Volumes[i].LastUSNMirrored = pFileData->Volumes[i].LastUSNMirrored;
|
|
memData->Volumes[i].FileSystemFlags = pFileData->Volumes[i].FileSystemFlags;
|
|
|
|
wcscpy(memData->Volumes[i].FileSystemName, pFileData->Volumes[i].FileSystemName);
|
|
memData->Volumes[i].VolumeLabel = SpDupStringW((PWCHAR)(((PUCHAR)pFileData) + pFileData->Volumes[i].VolumeLabelOffset));
|
|
memData->Volumes[i].OriginalArcName = SpDupStringW((PWCHAR)(((PUCHAR)pFileData) + pFileData->Volumes[i].ArcNameOffset));
|
|
|
|
memData->Volumes[i].DiskSpaceUsed = pFileData->Volumes[i].DiskSpaceUsed;
|
|
memData->Volumes[i].StartingOffset = pFileData->Volumes[i].StartingOffset;
|
|
memData->Volumes[i].PartitionSize = pFileData->Volumes[i].PartitionSize;
|
|
|
|
//
|
|
// Ensure that the required disk number actually exists, and that the
|
|
// disk is online.
|
|
//
|
|
|
|
pDisk = &PartitionedDisks[diskNumber];
|
|
if ((diskNumber >= HardDiskCount) ||
|
|
(pDisk->HardDisk == NULL) ||
|
|
(pDisk->HardDisk->Status == DiskOffLine) ) {
|
|
SpSysPrepFailure( SP_SYSPREP_INVALID_PARTITION, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
*pMemoryData = memData;
|
|
|
|
SpMemFree(CurrentHalName);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
SpGetBaseDeviceName(
|
|
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 Obja;
|
|
HANDLE Handle;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Start at the first name
|
|
//
|
|
INIT_OBJA(&Obja,&UnicodeString,SymbolicName);
|
|
|
|
Status = ZwOpenSymbolicLinkObject(&Handle,
|
|
(ACCESS_MASK)SYMBOLIC_LINK_QUERY,
|
|
&Obja
|
|
);
|
|
|
|
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 = ZwQuerySymbolicLinkObject(Handle,
|
|
&UnicodeString,
|
|
NULL
|
|
);
|
|
|
|
ZwClose(Handle);
|
|
|
|
Buffer[(UnicodeString.Length / sizeof(WCHAR))] = UNICODE_NULL;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// See if the next name is also a symbolic name
|
|
//
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,MOUNTMGR_DEVICE_NAME);
|
|
|
|
Status = ZwOpenSymbolicLinkObject(&Handle,
|
|
(ACCESS_MASK)SYMBOLIC_LINK_QUERY,
|
|
&Obja
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
SpVerifyDriveLetter(
|
|
IN PWSTR RegionName,
|
|
IN WCHAR DriveLetter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine makes sure that the specified region has been assigned
|
|
the correct drive letter by the mount manager, if not it changes it.
|
|
|
|
Arguments:
|
|
|
|
RegionName - The region name, \Device\HardiskX\PartitionY.
|
|
|
|
DriveLetter - The desired drive letter.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, else FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR currentLetter;
|
|
ULONG i;
|
|
PMOUNTMGR_MOUNT_POINT mountPoint = NULL;
|
|
PMOUNTMGR_CREATE_POINT_INPUT createMountPoint;
|
|
WCHAR NewSymbolicLink[16];
|
|
PWSTR regionBaseName;
|
|
ULONG mountPointSize;
|
|
ULONG createMountPointSize;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING UnicodeString;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE Handle;
|
|
PMOUNTMGR_MOUNT_POINTS mountPointsReturned;
|
|
LARGE_INTEGER DelayTime;
|
|
|
|
//
|
|
// See what drive letter the region has. Since the mount manager
|
|
// assigns drive letters asynchronously, we wait a little while
|
|
// if we don't get one back.
|
|
//
|
|
|
|
for (i = 0; ; i++) {
|
|
|
|
currentLetter = SpGetDriveLetter(RegionName, &mountPoint);
|
|
|
|
if (currentLetter == DriveLetter) {
|
|
if (mountPoint) {
|
|
SpMemFree(mountPoint);
|
|
}
|
|
|
|
return TRUE;
|
|
} else if (currentLetter != L'\0') {
|
|
break;
|
|
} else if (i == 5) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Wait 2 sec and try again.
|
|
//
|
|
DelayTime.HighPart = -1;
|
|
DelayTime.LowPart = (ULONG)(-20000000);
|
|
KeDelayExecutionThread(KernelMode,FALSE,&DelayTime);
|
|
}
|
|
|
|
//
|
|
// At this point, we either have no drive letter assigned, or a
|
|
// wrong one.
|
|
//
|
|
|
|
if (currentLetter != L'\0') {
|
|
|
|
//
|
|
// There is an existing drive letter, so delete it.
|
|
//
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,MOUNTMGR_DEVICE_NAME);
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
// (ACCESS_MASK)(FILE_GENERIC_READ | FILE_GENERIC_WRITE),
|
|
(ACCESS_MASK)(FILE_GENERIC_READ),
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE ,
|
|
FILE_NON_DIRECTORY_FILE
|
|
);
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
SpMemFree(mountPoint);
|
|
FALSE;
|
|
}
|
|
|
|
mountPointsReturned = SpMemAlloc( 4096 );
|
|
|
|
mountPointSize = sizeof(MOUNTMGR_MOUNT_POINT) +
|
|
mountPoint->SymbolicLinkNameLength +
|
|
mountPoint->UniqueIdLength +
|
|
mountPoint->DeviceNameLength;
|
|
|
|
Status = ZwDeviceIoControlFile(
|
|
Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_MOUNTMGR_DELETE_POINTS,
|
|
mountPoint,
|
|
mountPointSize,
|
|
mountPointsReturned,
|
|
4096
|
|
);
|
|
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
SpMemFree(mountPointsReturned);
|
|
SpMemFree(mountPoint);
|
|
ZwClose(Handle);
|
|
return FALSE;
|
|
}
|
|
|
|
SpMemFree(mountPointsReturned);
|
|
SpMemFree(mountPoint); // don't need this anymore
|
|
}
|
|
|
|
//
|
|
// Now add the one we want.
|
|
//
|
|
|
|
//
|
|
// We need to get the real base name (\Device\HardDiskX\PartitionY
|
|
// is a symbolic link).
|
|
//
|
|
|
|
SpGetBaseDeviceName(RegionName, TemporaryBuffer, sizeof(TemporaryBuffer));
|
|
regionBaseName = SpDupStringW(TemporaryBuffer);
|
|
|
|
swprintf(NewSymbolicLink, L"\\DosDevices\\%c:", DriveLetter);
|
|
createMountPointSize = sizeof(MOUNTMGR_CREATE_POINT_INPUT) +
|
|
(wcslen(regionBaseName) * sizeof(WCHAR)) +
|
|
(wcslen(NewSymbolicLink) * sizeof(WCHAR));
|
|
createMountPoint = SpMemAlloc(createMountPointSize);
|
|
createMountPoint->SymbolicLinkNameLength = wcslen(NewSymbolicLink) * sizeof(WCHAR);
|
|
createMountPoint->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
|
|
memcpy((PCHAR)createMountPoint + createMountPoint->SymbolicLinkNameOffset,
|
|
NewSymbolicLink,
|
|
createMountPoint->SymbolicLinkNameLength);
|
|
|
|
createMountPoint->DeviceNameLength = wcslen(regionBaseName) * sizeof(WCHAR);
|
|
createMountPoint->DeviceNameOffset = createMountPoint->SymbolicLinkNameOffset +
|
|
createMountPoint->SymbolicLinkNameLength;
|
|
memcpy((PCHAR)createMountPoint + createMountPoint->DeviceNameOffset,
|
|
regionBaseName,
|
|
createMountPoint->DeviceNameLength);
|
|
|
|
Status = ZwDeviceIoControlFile(
|
|
Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_MOUNTMGR_CREATE_POINT,
|
|
createMountPoint,
|
|
createMountPointSize,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
SpMemFree(createMountPoint);
|
|
SpMemFree(regionBaseName);
|
|
ZwClose(Handle);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpSetVolumeLabel(
|
|
IN PWSTR RegionName,
|
|
IN PWSTR VolumeLabel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the volume label on the specified region.
|
|
|
|
Arguments:
|
|
|
|
RegionName - The region name, \Device\HardiskX\PartitionY.
|
|
|
|
DriveLetter - The desired volume label.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, else FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING UnicodeString;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE Handle;
|
|
struct LABEL_BUFFER {
|
|
FILE_FS_LABEL_INFORMATION VolumeInfo;
|
|
WCHAR Label[64];
|
|
} LabelBuffer;
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,RegionName);
|
|
|
|
Status = ZwOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)(FILE_GENERIC_READ | FILE_GENERIC_WRITE),
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE ,
|
|
FILE_NON_DIRECTORY_FILE
|
|
);
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
LabelBuffer.VolumeInfo.VolumeLabelLength = wcslen(VolumeLabel) * sizeof(WCHAR);
|
|
wcscpy(LabelBuffer.VolumeInfo.VolumeLabel, VolumeLabel);
|
|
|
|
Status = ZwSetVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&LabelBuffer,
|
|
FIELD_OFFSET(FILE_FS_LABEL_INFORMATION, VolumeLabel[0]) + LabelBuffer.VolumeInfo.VolumeLabelLength,
|
|
FileFsLabelInformation);
|
|
|
|
ZwClose(Handle);
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
SpFixupLocalDisks(
|
|
IN HANDLE SifHandle,
|
|
OUT PDISK_REGION *InstallRegion,
|
|
OUT PDISK_REGION *SystemPartitionRegion,
|
|
IN PWSTR SetupSourceDevicePath,
|
|
IN PWSTR DirectoryOnSetupSource,
|
|
IN PMIRROR_CFG_INFO_MEMORY pMemoryData,
|
|
IN BOOLEAN UseWholeDisk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses the IMirror.dat file given and makes the local disk(s) look as
|
|
closely as possible to the configuration in the file.
|
|
|
|
Arguments:
|
|
|
|
SifHandle - Controlling sif file.
|
|
|
|
InstallRegion - Returns the install region to use.
|
|
|
|
SystemPartitionRegion - Returns the system partition to use.
|
|
|
|
SetupSourceDevicePath - Path to the setup device.
|
|
|
|
DirectoryOnSetupSource - Subdirectory of the setup files.
|
|
|
|
pMemoryData - A pointer to an in-memory copy of the file.
|
|
|
|
UseWholeDisk - TRUE if disks should be partitioned as their current
|
|
physical size; FALSE if they should be partitioned to match
|
|
the size that the original source machine had.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, else it generates a fatal error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDISK_REGION pRegion=NULL;
|
|
PDISK_REGION p;
|
|
PWSTR RegionDescr;
|
|
PWSTR RegionNtName;
|
|
NTSTATUS Status;
|
|
BOOLEAN DiskCleaned[8] = { FALSE }; // track if a disk has been cleaned up.
|
|
ULONG volume, disk;
|
|
PMIRROR_VOLUME_INFO_MEMORY volumeInfo;
|
|
LARGE_INTEGER SizeInMB;
|
|
PPARTITIONED_DISK pDisk;
|
|
BOOLEAN ExpandToEnd;
|
|
ULONG j;
|
|
PARTITION_INFORMATION_EX PartInfo;
|
|
ULONGLONG SysPartStartSector = 0;
|
|
ULONG SysPartDisk = 0;
|
|
|
|
LARGE_INTEGER SizeAvailable;
|
|
LARGE_INTEGER SlopSize;
|
|
LARGE_INTEGER SlopSizeTimes100;
|
|
LARGE_INTEGER SizeRequiredMax;
|
|
LARGE_INTEGER SizeRequiredMin;
|
|
|
|
PULONGLONG StartSectors = NULL;
|
|
|
|
if (pMemoryData->NumberVolumes) {
|
|
StartSectors = (PULONGLONG)(SpMemAlloc(sizeof(ULONGLONG) * pMemoryData->NumberVolumes));
|
|
|
|
if (!StartSectors) {
|
|
*InstallRegion = NULL;
|
|
*SystemPartitionRegion = NULL;
|
|
|
|
return FALSE; // ran out of memory
|
|
}
|
|
}
|
|
|
|
RtlZeroMemory(StartSectors, (sizeof(ULONGLONG) * pMemoryData->NumberVolumes));
|
|
|
|
//
|
|
// NOTE: imirror.dat
|
|
// doesn't have information about which partitions were in the extended
|
|
// partition. We could read the boot sector from the server, or just
|
|
// try to guess at when we need an extended partition. For the moment,
|
|
// we will just let the partition creation code create extended
|
|
// partitions whent it wants to (all regions after the first primary
|
|
// become logical disks in an extended partition).
|
|
//
|
|
|
|
for (volume = 0; volume < pMemoryData->NumberVolumes; volume++) {
|
|
|
|
volumeInfo = &pMemoryData->Volumes[volume];
|
|
|
|
//
|
|
// If this disk has not been cleaned up, then do so.
|
|
//
|
|
|
|
disk = volumeInfo->DiskNumber;
|
|
|
|
if (!DiskCleaned[disk]) {
|
|
|
|
//
|
|
// Clean out the different partitions on disk.
|
|
//
|
|
|
|
SpPtPartitionDiskForRemoteBoot(
|
|
disk,
|
|
&pRegion);
|
|
|
|
//
|
|
// That function may leave one big partitioned region, if so delete
|
|
// it so we can start from scratch.
|
|
//
|
|
|
|
if (pRegion && pRegion->PartitionedSpace) {
|
|
SpPtDelete(pRegion->DiskNumber,pRegion->StartSector);
|
|
}
|
|
|
|
DiskCleaned[disk] = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We have already cleaned out this disk, so pRegion points
|
|
// to the last partition we created. However, we have these 2 dirty looking validation checks on pRegion
|
|
// to make PREFIX happy. pRegion is never NULL but if it is we think that something is wrong and move on.
|
|
//
|
|
|
|
if( pRegion == NULL )
|
|
continue;
|
|
|
|
pRegion = pRegion->Next;
|
|
|
|
if( pRegion == NULL )
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Create a region of the specified size.
|
|
// NOTE: Worry about volumeInfo->PartitionType/CompressedVolume?
|
|
// NOTE: What if the rounding to the nearest MB loses something?
|
|
//
|
|
// We allow for some slop.
|
|
// a) If the new disk is <= x% smaller, and the image will still fit, then we'll do it.
|
|
// b) If the new disk is <= x% bigger, then we'll make one partition out of the whole disk.
|
|
// c) If the new disk is >x% bigger, then we'll make one partition equal to the original one and leave the rest of the disk raw.
|
|
// d) If the new disk is >x% smaller, then we'll fail.
|
|
|
|
pDisk = &PartitionedDisks[pRegion->DiskNumber];
|
|
// SizeAvailable = RtlEnlargedUnsignedMultiply( pRegion->SectorCount, pDisk->HardDisk->Geometry.BytesPerSector );
|
|
SizeAvailable.QuadPart = pRegion->SectorCount * pDisk->HardDisk->Geometry.BytesPerSector;
|
|
|
|
// SYSPREP_PARTITION_SLOP is specified as a percentage
|
|
|
|
SlopSizeTimes100 = RtlExtendedIntegerMultiply(SizeAvailable, SYSPREP_PARTITION_SLOP);
|
|
SlopSize = RtlExtendedLargeIntegerDivide( SlopSizeTimes100, 100, NULL );
|
|
|
|
SizeRequiredMin = RtlLargeIntegerSubtract( volumeInfo->PartitionSize, SlopSize );
|
|
|
|
if ( SizeRequiredMin.QuadPart < volumeInfo->DiskSpaceUsed.QuadPart ) {
|
|
|
|
SizeRequiredMin = volumeInfo->DiskSpaceUsed;
|
|
}
|
|
|
|
SizeRequiredMax = RtlLargeIntegerAdd( volumeInfo->PartitionSize, SlopSize );
|
|
|
|
ExpandToEnd = FALSE;
|
|
if (UseWholeDisk) {
|
|
ExpandToEnd = TRUE;
|
|
|
|
//
|
|
// If this is the last partition on the disk, then use the rest of it.
|
|
//
|
|
for (j = 0; j < pMemoryData->NumberVolumes; j++) {
|
|
if ((j != volume) &&
|
|
(pMemoryData->Volumes[j].DiskNumber == pMemoryData->Volumes[volume].DiskNumber) &&
|
|
(pMemoryData->Volumes[j].StartingOffset.QuadPart > pMemoryData->Volumes[volume].StartingOffset.QuadPart)) {
|
|
ExpandToEnd = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SizeInMB = RtlExtendedLargeIntegerDivide(volumeInfo->PartitionSize, 1024 * 1024, NULL);
|
|
|
|
if (!ExpandToEnd && (SizeAvailable.QuadPart > SizeRequiredMax.QuadPart)) {
|
|
|
|
// use volumeInfo->PartitionSize
|
|
|
|
} else if (SizeAvailable.QuadPart >= SizeRequiredMin.QuadPart ) {
|
|
|
|
SizeInMB.QuadPart = 0; // use all available space
|
|
|
|
} else {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Sysprep partition of %d Mb is too big\n", SizeInMB.LowPart));
|
|
|
|
SpSysPrepFailure( SP_SYSPREP_NOT_ENOUGH_DISK_SPACE, NULL, NULL );
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// TBD : fix the partition type
|
|
// if(!SpPtDoCreate(pRegion,&p,TRUE,SizeInMB.LowPart,volumeInfo->PartitionType,TRUE)) {
|
|
//
|
|
RtlZeroMemory(&PartInfo, sizeof(PARTITION_INFORMATION_EX));
|
|
PartInfo.Mbr.PartitionType = volumeInfo->PartitionType;
|
|
|
|
if(!SpPtDoCreate(pRegion, &p, TRUE, SizeInMB.LowPart, &PartInfo, TRUE)) {
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Could not create sys prep partition %d Mb\n", SizeInMB.LowPart));
|
|
|
|
SpSysPrepFailure( SP_SYSPREP_NOT_ENOUGH_DISK_SPACE, NULL, NULL );
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// If we just created an extended partition and a logical drive,
|
|
// we'll need to switch regions -- Region points to the extended partition
|
|
// region, but we want to point to the logical drive region.
|
|
//
|
|
ASSERT(p);
|
|
pRegion = p;
|
|
|
|
#if defined(_AMD64_) || defined(_X86_)
|
|
if (volumeInfo->PartitionActive) {
|
|
|
|
if (volumeInfo->IsBootDisk) {
|
|
|
|
//
|
|
// On an amd64/x86 machine, make sure that we have a valid primary partition
|
|
// on drive 0 (C:), for booting.
|
|
//
|
|
PDISK_REGION SysPart = SpPtValidSystemPartition();
|
|
|
|
ASSERT(pRegion == SysPart);
|
|
|
|
SPPT_MARK_REGION_AS_SYSTEMPARTITION(pRegion, TRUE);
|
|
SPPT_SET_REGION_DIRTY(pRegion, TRUE);
|
|
|
|
SysPartDisk = disk;
|
|
SysPartStartSector = pRegion->StartSector;
|
|
}
|
|
|
|
//
|
|
// Make sure the system partition is active and all others are inactive.
|
|
//
|
|
SpPtMakeRegionActive(pRegion);
|
|
}
|
|
#endif // defined(_AMD64_) || defined(_X86_)
|
|
|
|
volumeInfo->CreatedRegion = NULL;
|
|
StartSectors[disk] = pRegion->StartSector;
|
|
}
|
|
|
|
if (SysPartStartSector == 0) {
|
|
*InstallRegion = *SystemPartitionRegion = NULL;
|
|
SpMemFree(StartSectors);
|
|
|
|
return FALSE; // We need the system partition and install region
|
|
}
|
|
|
|
//
|
|
// At this point, everything is fine, so commit any
|
|
// partition changes the user may have made.
|
|
// This won't return if an error occurs while updating the disk.
|
|
//
|
|
SpPtDoCommitChanges();
|
|
|
|
//
|
|
// Now format all the partitions and make sure the drive letter
|
|
// is correct.
|
|
//
|
|
|
|
for (volume = 0; volume < pMemoryData->NumberVolumes; volume++) {
|
|
ULONG FilesystemType;
|
|
ULONG DiskNumber = volumeInfo->DiskNumber;
|
|
|
|
volumeInfo = &pMemoryData->Volumes[volume];
|
|
|
|
if (StartSectors[DiskNumber]) {
|
|
volumeInfo->CreatedRegion = SpPtLookupRegionByStart(
|
|
SPPT_GET_PARTITIONED_DISK(DiskNumber),
|
|
TRUE,
|
|
StartSectors[DiskNumber]);
|
|
} else {
|
|
ASSERT(FALSE);
|
|
|
|
continue;
|
|
}
|
|
|
|
pRegion = volumeInfo->CreatedRegion;
|
|
|
|
SpPtRegionDescription(
|
|
&PartitionedDisks[pRegion->DiskNumber],
|
|
pRegion,
|
|
TemporaryBuffer,
|
|
sizeof(TemporaryBuffer)
|
|
);
|
|
|
|
RegionDescr = SpDupStringW(TemporaryBuffer);
|
|
|
|
if (wcscmp(volumeInfo->FileSystemName, L"FAT") == 0) {
|
|
FilesystemType = FilesystemFat;
|
|
} else if (wcscmp(volumeInfo->FileSystemName, L"FAT32") == 0) {
|
|
FilesystemType = FilesystemFat32;
|
|
} else {
|
|
FilesystemType = FilesystemNtfs;
|
|
}
|
|
|
|
Status = SpDoFormat(
|
|
RegionDescr,
|
|
pRegion,
|
|
FilesystemType,
|
|
FALSE,
|
|
FALSE, // don't need to worry about fat size
|
|
TRUE,
|
|
SifHandle,
|
|
0, // default cluster size
|
|
SetupSourceDevicePath,
|
|
DirectoryOnSetupSource
|
|
);
|
|
|
|
SpMemFree(RegionDescr);
|
|
|
|
//
|
|
// This checks that the drive letter is correct.
|
|
//
|
|
|
|
SpNtNameFromRegion(
|
|
pRegion,
|
|
TemporaryBuffer,
|
|
sizeof(TemporaryBuffer),
|
|
PartitionOrdinalCurrent);
|
|
|
|
RegionNtName = SpDupStringW(TemporaryBuffer);
|
|
|
|
SpVerifyDriveLetter(
|
|
RegionNtName,
|
|
volumeInfo->DriveLetter
|
|
);
|
|
|
|
SpSetVolumeLabel(
|
|
RegionNtName,
|
|
volumeInfo->VolumeLabel
|
|
);
|
|
|
|
pRegion->DriveLetter = volumeInfo->DriveLetter;
|
|
wcsncpy(pRegion->VolumeLabel,
|
|
volumeInfo->VolumeLabel,
|
|
(sizeof(pRegion->VolumeLabel) / sizeof(WCHAR)) - 1
|
|
);
|
|
|
|
pRegion->VolumeLabel[ (sizeof(pRegion->VolumeLabel) / sizeof(WCHAR)) - 1] = UNICODE_NULL;
|
|
|
|
SpMemFree(RegionNtName);
|
|
|
|
}
|
|
|
|
//
|
|
// Locate the system and install region
|
|
//
|
|
*SystemPartitionRegion = SpPtLookupRegionByStart(SPPT_GET_PARTITIONED_DISK(SysPartDisk),
|
|
TRUE,
|
|
SysPartStartSector);
|
|
|
|
*InstallRegion = *SystemPartitionRegion;
|
|
|
|
SpMemFree(StartSectors);
|
|
|
|
|
|
return (*InstallRegion != NULL);
|
|
}
|
|
|
|
BOOLEAN
|
|
SpCopyMirrorDisk(
|
|
IN PMIRROR_CFG_INFO_FILE pFileData,
|
|
IN ULONG cDisk
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine uses the IMirror.dat file given and a disk number to copy the contents
|
|
on the mirror share down to the local machine.
|
|
|
|
Arguments:
|
|
|
|
pFileData - A pointer to an in-memory copy of the file.
|
|
|
|
cDisk - The disk number to copy.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, else FALSE.
|
|
|
|
--*/
|
|
{
|
|
PMIRROR_VOLUME_INFO_FILE pVolume;
|
|
PDISK_REGION pRegion;
|
|
WCHAR Buffer[MAX_PATH];
|
|
NTSTATUS Status;
|
|
PWSTR pNtName;
|
|
|
|
if (pFileData->NumberVolumes <= cDisk) {
|
|
SpSysPrepFailure( SP_SYSPREP_INVALID_PARTITION, NULL, NULL );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Find the correct region.
|
|
// NOTE: the drive with this
|
|
// letter might not be on the same disk, we should scan all disks
|
|
// for this drive letter.
|
|
//
|
|
pVolume = &(pFileData->Volumes[cDisk]);
|
|
pRegion = PartitionedDisks[pVolume->DiskNumber].PrimaryDiskRegions;
|
|
|
|
while (pRegion != NULL) {
|
|
if (pRegion->DriveLetter == pVolume->DriveLetter) {
|
|
break;
|
|
}
|
|
pRegion = pRegion->Next;
|
|
}
|
|
|
|
if (pRegion == NULL) {
|
|
|
|
pRegion = PartitionedDisks[pVolume->DiskNumber].ExtendedDiskRegions;
|
|
|
|
while (pRegion != NULL) {
|
|
if (pRegion->DriveLetter == pVolume->DriveLetter) {
|
|
break;
|
|
}
|
|
pRegion = pRegion->Next;
|
|
}
|
|
|
|
if (pRegion == NULL) {
|
|
SpSysPrepFailure( SP_SYSPREP_NOT_ENOUGH_PARTITIONS, NULL, NULL );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
SpPtRegionDescription(
|
|
&PartitionedDisks[pRegion->DiskNumber],
|
|
pRegion,
|
|
Buffer,
|
|
sizeof(Buffer)
|
|
);
|
|
|
|
//
|
|
// Now copy all files over
|
|
//
|
|
SpNtNameFromRegion(
|
|
pRegion,
|
|
TemporaryBuffer,
|
|
sizeof(TemporaryBuffer),
|
|
PartitionOrdinalCurrent
|
|
);
|
|
|
|
pNtName = SpDupStringW(TemporaryBuffer);
|
|
|
|
mbstowcs(Buffer, RemoteIMirrorFilePath, strlen(RemoteIMirrorFilePath) + 1);
|
|
wcscat(Buffer, (PWSTR)(((PUCHAR)pFileData) + pVolume->MirrorUncPathOffset));
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Copying directories from %ws to %ws%ws\n",
|
|
Buffer, pNtName, L"\\"));
|
|
|
|
//
|
|
// setup the global that says whether we look at compression bit or ACL.
|
|
//
|
|
|
|
if ((wcscmp(pVolume->FileSystemName, L"FAT") == 0) ||
|
|
(wcscmp(pVolume->FileSystemName, L"FAT32") == 0)) {
|
|
|
|
RemoteSysPrepVolumeIsNtfs = FALSE;
|
|
|
|
} else {
|
|
|
|
RemoteSysPrepVolumeIsNtfs = TRUE;
|
|
}
|
|
|
|
//
|
|
// copy the acl onto the root.
|
|
//
|
|
|
|
Status = SpSysPrepSetExtendedInfo( Buffer, pNtName, TRUE, TRUE );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
SpSysPrepFailure( SP_SYSPREP_ACL_FAILURE, pNtName, NULL );
|
|
SpMemFree( pNtName );
|
|
return FALSE;
|
|
}
|
|
|
|
SpCopyDirRecursive(
|
|
Buffer,
|
|
pNtName,
|
|
L"\\",
|
|
0
|
|
);
|
|
|
|
//
|
|
// create the \sysprep\sysprep.inf file as a dup of our sif file for gui
|
|
// mode setup answer file.
|
|
//
|
|
|
|
if (pVolume->IsBootDisk) {
|
|
|
|
//
|
|
// first we create the sysprep directory, then we create the inf
|
|
// file in it.
|
|
//
|
|
|
|
SpCreateDirectory( pNtName,
|
|
NULL,
|
|
L"sysprep",
|
|
0,
|
|
0 );
|
|
|
|
Status = SpWriteSetupTextFile(WinntSifHandle,pNtName,L"sysprep",L"sysprep.inf");
|
|
}
|
|
|
|
SpMemFree( pNtName );
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
SpDeleteStorageVolumes (
|
|
IN HANDLE SysPrepRegHandle,
|
|
IN DWORD ControlSetNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine deletes those subkeys of the CCS\Enum\STORAGE\Volume key that
|
|
represent volumes that were never fully installed. This eliminates stale
|
|
information about volumes that may not exist on this computer.
|
|
|
|
The motivation for this is this scenario and others like it:
|
|
|
|
On the initial install of the OS, the disk has one large partition. The user
|
|
chooses to delete this partition and create a smaller one to hold the OS.
|
|
The result of this is that textmode setup transfers volume information about
|
|
both partitions into the system hive for the running OS. GUI mode setup then
|
|
completely installs the smaller volume, but the larger volume is left partially
|
|
installed and marked with CONFIGFLAG_REINSTALL.
|
|
|
|
Next RIPREP is run to copy the OS image to a RIS server. When the image is
|
|
brought back down, say to the same machine or to a machine with the same
|
|
hard disk size, and the automatic UseWholeDisk partitioning is done, there
|
|
will be one large partition of the same size as the original large partition.
|
|
The Volume instance name will match the partially installed one, so when
|
|
mini-GUI mode setup starts, it will get a bugcheck 7B because the partially
|
|
installed volume cannot be used as the system disk.
|
|
|
|
To combat this problem, this routine deletes all partially installed volumes
|
|
from the CCS\Enum\STORAGE\Volume key.
|
|
|
|
Arguments:
|
|
|
|
SysPrepRegHandle - Handle to the system hive of the build we are patching.
|
|
|
|
ControlSetNumber - Current control set number in the hive.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES obja;
|
|
UNICODE_STRING unicodeString;
|
|
HANDLE volumeKeyHandle;
|
|
DWORD enumIndex;
|
|
DWORD resultLength;
|
|
PKEY_BASIC_INFORMATION keyInfo;
|
|
PKEY_VALUE_FULL_INFORMATION valueInfo;
|
|
PWCH instanceName;
|
|
HANDLE instanceKeyHandle;
|
|
DWORD configFlags;
|
|
|
|
//
|
|
// Open the Enum\STORAGE\Volume key in the current control set.
|
|
//
|
|
|
|
swprintf(
|
|
TemporaryBuffer,
|
|
L"ControlSet%03d\\Enum\\STORAGE\\Volume",
|
|
ControlSetNumber
|
|
);
|
|
|
|
INIT_OBJA( &obja, &unicodeString, TemporaryBuffer );
|
|
obja.RootDirectory = SysPrepRegHandle;
|
|
|
|
status = ZwOpenKey( &volumeKeyHandle, KEY_ALL_ACCESS, &obja );
|
|
if( !NT_SUCCESS(status) ) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpDeleteStorageVolumes: Unable to open %ws. status = %lx\n", TemporaryBuffer, status ));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Enumerate all of the instance keys.
|
|
//
|
|
|
|
enumIndex = 0;
|
|
|
|
while ( TRUE ) {
|
|
|
|
status = ZwEnumerateKey(
|
|
volumeKeyHandle,
|
|
enumIndex,
|
|
KeyBasicInformation,
|
|
TemporaryBuffer,
|
|
sizeof(TemporaryBuffer),
|
|
&resultLength
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
if ( status == STATUS_NO_MORE_ENTRIES ) {
|
|
|
|
//
|
|
// Enumeration completed successfully.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Some kind of error occurred. Print a message and bail.
|
|
//
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpDeleteStorageVolumes: Unable to enumerate existing storage volumes (%lx)\n", status ));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Zero-terminate the subkey name just in case. Copy it out of the
|
|
// temporary buffer into "local" storage.
|
|
//
|
|
|
|
keyInfo = (PKEY_BASIC_INFORMATION)TemporaryBuffer;
|
|
keyInfo->Name[keyInfo->NameLength/sizeof(WCHAR)] = UNICODE_NULL;
|
|
instanceName = SpDupStringW( keyInfo->Name );
|
|
|
|
//
|
|
// Open the key for the volume instance.
|
|
//
|
|
|
|
INIT_OBJA( &obja, &unicodeString, instanceName );
|
|
obja.RootDirectory = volumeKeyHandle;
|
|
|
|
status = ZwOpenKey( &instanceKeyHandle, KEY_ALL_ACCESS, &obja );
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// Unable to open the instance key. Print a message and move
|
|
// on to the next one.
|
|
//
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpDeleteStorageVolumes: Unable to open %ws. status = %lx\n", instanceName, status ));
|
|
SpMemFree( instanceName );
|
|
enumIndex++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Query the ConfigFlags value.
|
|
//
|
|
|
|
RtlInitUnicodeString( &unicodeString, L"ConfigFlags");
|
|
status = ZwQueryValueKey(
|
|
instanceKeyHandle,
|
|
&unicodeString,
|
|
KeyValueFullInformation,
|
|
TemporaryBuffer,
|
|
sizeof(TemporaryBuffer),
|
|
&resultLength
|
|
);
|
|
valueInfo = (PKEY_VALUE_FULL_INFORMATION)TemporaryBuffer;
|
|
|
|
if ( NT_SUCCESS(status) &&
|
|
(valueInfo->Type == REG_DWORD) ) {
|
|
|
|
//
|
|
// Got the value. If the volume isn't fully installed, delete the
|
|
// whole instance key.
|
|
//
|
|
|
|
configFlags = *(PULONG)((PUCHAR)valueInfo + valueInfo->DataOffset);
|
|
|
|
if ( (configFlags &
|
|
(CONFIGFLAG_REINSTALL | CONFIGFLAG_FINISH_INSTALL)) != 0 ) {
|
|
|
|
//KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SpDeleteStorageVolumes: instance %ws has ConfigFlags %x; DELETING\n", instanceName, configFlags ));
|
|
ZwClose( instanceKeyHandle );
|
|
status = SppDeleteKeyRecursive(
|
|
volumeKeyHandle,
|
|
instanceName,
|
|
TRUE // ThisKeyToo
|
|
);
|
|
SpMemFree( instanceName );
|
|
// Don't increment enumIndex, because we just deleted a key.
|
|
continue;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This volume is fully installed. Leave it alone.
|
|
//
|
|
|
|
//KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SpDeleteStorageVolumes: instance %ws has ConfigFlags %x; not deleting\n", instanceName, configFlags ));
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// ConfigFlags value not present or not a DWORD. Print a
|
|
// message and move on.
|
|
//
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpDeleteStorageVolumes: instance %ws has invalid ConfigFlags\n", instanceName ));
|
|
}
|
|
|
|
//
|
|
// Clean up and move on to the next volume instance.
|
|
//
|
|
|
|
ZwClose( instanceKeyHandle );
|
|
SpMemFree( instanceName );
|
|
enumIndex++;
|
|
}
|
|
|
|
//
|
|
// All done. Close the Volume key.
|
|
//
|
|
|
|
ZwClose( volumeKeyHandle );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpPatchSysPrepImage(
|
|
IN HANDLE SetupSifHandle,
|
|
IN HANDLE WinntSifHandle,
|
|
IN PMIRROR_CFG_INFO_FILE pFileData,
|
|
IN PMIRROR_CFG_INFO_MEMORY pMemoryData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine uses the IMirror.dat file given and the given SIF file to make the
|
|
following modifications to a locally copied SysPrep image.
|
|
- Replace the disk controller driver in the image with one that supports the current hardware.
|
|
- Replace the NIC driver in the image with one that supports the current hardware.
|
|
- Replace the HAL, kernel and other mp/up dependent drivers if necessary.
|
|
- Migrate the mounted device settings.
|
|
- Modify boot.ini ARC names if necessary.
|
|
|
|
Arguments:
|
|
|
|
WinntSifHandle - Handle to the open SIF file.
|
|
|
|
pFileData - A pointer to an in-memory copy of IMirror.Dat
|
|
|
|
pMemoryData - A pointer to an in-memory copy of IMirror.Dat, modified to
|
|
match the specs of this computer (disk sizes etc).
|
|
|
|
Return Value:
|
|
|
|
The NTSTATUS of the operation.
|
|
--*/
|
|
{
|
|
PWCHAR SysPrepDriversDevice;
|
|
PWCHAR Tmp;
|
|
ULONG Index;
|
|
DWORD Size;
|
|
DWORD Number;
|
|
WCHAR Path[MAX_PATH];
|
|
WCHAR Path2[MAX_PATH];
|
|
WCHAR ImageName[MAX_PATH];
|
|
WCHAR SrvPath[MAX_PATH];
|
|
HANDLE SrcHandle = NULL;
|
|
HANDLE DstHandle = NULL;
|
|
HANDLE TmpHandle = NULL;
|
|
HANDLE TmpHandle2 = NULL;
|
|
HANDLE FileHandle = NULL;
|
|
HANDLE FileHandle2 = NULL;
|
|
PVOID Buffer = NULL;
|
|
BOOLEAN NeedToUnload = FALSE;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Obj;
|
|
OBJECT_ATTRIBUTES DstObj;
|
|
UNICODE_STRING UnicodeString1;
|
|
UNICODE_STRING UnicodeString2;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PKEY_BASIC_INFORMATION pKeyNode;
|
|
ULONG volume;
|
|
PMIRROR_VOLUME_INFO_MEMORY volumeInfo;
|
|
PDISK_FILE_LIST DiskFileLists;
|
|
ULONG DiskCount;
|
|
BOOLEAN HaveCopyList = FALSE;
|
|
BOOLEAN CopyListEmpty = TRUE;
|
|
PMIRROR_VOLUME_INFO_FILE pVolume = NULL;
|
|
PWSTR pVolumePath = NULL;
|
|
|
|
//
|
|
// Find the volume descriptor for the boot disk.
|
|
//
|
|
DiskCount = 0;
|
|
while (DiskCount < pFileData->NumberVolumes) {
|
|
|
|
pVolume = &(pFileData->Volumes[DiskCount]);
|
|
if (pVolume->IsBootDisk) {
|
|
break;
|
|
}
|
|
pVolume = NULL;
|
|
DiskCount++;
|
|
}
|
|
if (pVolume == NULL) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: Couldn't find boot drive record\n"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// First check if the SIF file has a value for SysPrepDriversDevice so that we can
|
|
// get new drivers, etc, if necessary.
|
|
//
|
|
SysPrepDriversDevice = SpGetSectionKeyIndex(WinntSifHandle,
|
|
L"SetupData",
|
|
L"SysPrepDriversDevice",
|
|
0
|
|
);
|
|
|
|
if ((SysPrepDriversDevice == NULL) || (wcslen(SysPrepDriversDevice) == 0)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SIF has no SysPrepDriversDevice value\n"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Tmp = SysPrepDriversDevice;
|
|
while(*Tmp != UNICODE_NULL) {
|
|
if (*Tmp == L'%') {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
Tmp++;
|
|
}
|
|
|
|
//
|
|
// Generate media descriptors for the source media.
|
|
//
|
|
SpInitializeFileLists(
|
|
SetupSifHandle,
|
|
&DiskFileLists,
|
|
&DiskCount
|
|
);
|
|
|
|
HaveCopyList = TRUE;
|
|
|
|
//
|
|
// Allocate a temporary buffer
|
|
//
|
|
Buffer = SpMemAlloc(1024 * 4);
|
|
|
|
//
|
|
// Make a string that contains the path to the volume (\??\X:).
|
|
//
|
|
wcscpy(TemporaryBuffer, L"\\??\\X:");
|
|
TemporaryBuffer[4] = pVolume->DriveLetter;
|
|
pVolumePath = SpDupStringW(TemporaryBuffer);
|
|
|
|
//
|
|
// Now load the local version of the SysPrep hives, using IMirror.Dat to find them
|
|
// NOTE: DstObj is assumed by CleanUp to still be the key.
|
|
//
|
|
Tmp = (PWCHAR)(((PUCHAR)pFileData) + pFileData->SystemPathOffset);
|
|
wcscpy(Path, L"\\??\\");
|
|
wcscat(Path, Tmp);
|
|
wcscat(Path, L"\\System32\\Config\\System");
|
|
|
|
INIT_OBJA(&DstObj, &UnicodeString1, L"\\Registry\\SysPrepReg");
|
|
INIT_OBJA(&Obj, &UnicodeString2, Path);
|
|
|
|
Status = ZwLoadKey(&DstObj, &Obj);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwLoadKey to SysPrepReg failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
NeedToUnload = TRUE;
|
|
|
|
//
|
|
// Compare the local SysPrep NIC to the NIC that is currently running
|
|
//
|
|
|
|
//
|
|
// If different, then replace the NIC
|
|
//
|
|
|
|
//
|
|
// Put all critical devices in the currently running hives into the SysPrep hive.
|
|
//
|
|
wcscpy(Path, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\CriticalDeviceDatabase");
|
|
|
|
INIT_OBJA(&Obj, &UnicodeString2, Path);
|
|
|
|
Status = ZwOpenKey(&SrcHandle, KEY_READ, &Obj);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwOpenKey of local CriticalDeviceDatabase failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
wcscpy(Path, L"\\Registry\\SysPrepReg");
|
|
|
|
INIT_OBJA(&Obj, &UnicodeString2, Path);
|
|
|
|
Status = ZwOpenKey(&DstHandle, KEY_READ, &Obj);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwOpenKey of root SysPrepReg failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
Status = SpGetValueKey(
|
|
DstHandle,
|
|
L"Select",
|
|
L"Current",
|
|
1024 * 4,
|
|
Buffer,
|
|
&Size
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SpGetValueKey of Select\\Current failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
Number = *((DWORD *)(((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data));
|
|
|
|
ZwClose(DstHandle);
|
|
DstHandle = NULL;
|
|
|
|
//
|
|
// Print the current control set number to find the current control set
|
|
//
|
|
swprintf(Path,
|
|
L"\\Registry\\SysPrepReg\\ControlSet%03d\\Control\\CriticalDeviceDatabase",
|
|
Number
|
|
);
|
|
|
|
//
|
|
// Open the critical device database in the SysPrep image
|
|
//
|
|
INIT_OBJA(&Obj, &UnicodeString2, Path);
|
|
|
|
Status = ZwOpenKey(&DstHandle, KEY_READ | KEY_WRITE, &Obj);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwOpenKey of SysPrepReg CriticalDeviceDatabase failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Start looping and copying the data from the currently running critical device database
|
|
// into the SysPrep's database.
|
|
//
|
|
pKeyNode = (PKEY_BASIC_INFORMATION)Buffer;
|
|
|
|
for (Index = 0; ; Index++) {
|
|
|
|
if (TmpHandle) {
|
|
ZwClose(TmpHandle);
|
|
TmpHandle = NULL;
|
|
}
|
|
if (TmpHandle2) {
|
|
ZwClose(TmpHandle2);
|
|
TmpHandle2 = NULL;
|
|
}
|
|
|
|
Status = ZwEnumerateKey(SrcHandle,
|
|
Index,
|
|
KeyBasicInformation,
|
|
pKeyNode,
|
|
1024 * 4,
|
|
&Size
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
RtlCopyMemory((PUCHAR)Path2, (PUCHAR)(pKeyNode->Name), pKeyNode->NameLength);
|
|
Path2[pKeyNode->NameLength/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
|
|
|
|
//
|
|
// We need to quit migrating everything from the current critical device database.
|
|
// In order to do that, we'll only migrate the following types:
|
|
//
|
|
|
|
//
|
|
// Make sure this is the type of device we really want to migrate. We will
|
|
// accept any of the following classes:
|
|
// {4D36E965-E325-11CE-BFC1-08002BE10318} CDROM
|
|
// {4D36E967-E325-11CE-BFC1-08002BE10318} DiskDrive
|
|
// {4D36E96A-E325-11CE-BFC1-08002BE10318} hdc
|
|
// {4D36E96B-E325-11CE-BFC1-08002BE10318} Keyboard
|
|
// {4D36E96F-E325-11CE-BFC1-08002BE10318} Mouse
|
|
// {4D36E97B-E325-11CE-BFC1-08002BE10318} SCSIAdapter
|
|
// {4D36E97D-E325-11CE-BFC1-08002BE10318} System
|
|
//
|
|
Status = SpGetValueKey( SrcHandle,
|
|
Path2,
|
|
L"ClassGUID",
|
|
1024 * 4,
|
|
Buffer,
|
|
&Size );
|
|
if( NT_SUCCESS(Status) ) {
|
|
|
|
if( ( _wcsnicmp(L"{4D36E965-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, (((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->DataLength/sizeof(WCHAR))) ) &&
|
|
( _wcsnicmp(L"{4D36E967-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, (((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->DataLength/sizeof(WCHAR))) ) &&
|
|
( _wcsnicmp(L"{4D36E96A-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, (((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->DataLength/sizeof(WCHAR))) ) &&
|
|
( _wcsnicmp(L"{4D36E96B-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, (((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->DataLength/sizeof(WCHAR))) ) &&
|
|
( _wcsnicmp(L"{4D36E96F-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, (((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->DataLength/sizeof(WCHAR))) ) &&
|
|
( _wcsnicmp(L"{4D36E97B-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, (((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->DataLength/sizeof(WCHAR))) ) &&
|
|
( _wcsnicmp(L"{4D36E97D-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, (((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->DataLength/sizeof(WCHAR))) ) ) {
|
|
|
|
// he's not something we want to migrate.
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SpPatchSysPrepImage: We're skipping migration of %ws because his type is %ws\n", Path2, ((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data));
|
|
continue;
|
|
} else {
|
|
|
|
// looks good.
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SpPatchSysPrepImage: We're going to migration %ws because his ClassGUID is %ws\n", Path2, ((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data));
|
|
}
|
|
} else {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SpPatchSysPrepImage: SpGetValueKey failed to open %ws\\ClassGUID, but we're going to migrate this key anyway. (%lx)\n", Path2, Status));
|
|
}
|
|
|
|
|
|
INIT_OBJA(&Obj, &UnicodeString, Path2);
|
|
|
|
Obj.RootDirectory = DstHandle;
|
|
|
|
Status = ZwOpenKey(&TmpHandle, KEY_ALL_ACCESS, &Obj);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Delete the current item to rid of stale data
|
|
//
|
|
ZwDeleteKey(TmpHandle);
|
|
ZwClose(TmpHandle);
|
|
}
|
|
|
|
TmpHandle = NULL;
|
|
|
|
Status = SppCopyKeyRecursive(SrcHandle,
|
|
DstHandle,
|
|
Path2,
|
|
Path2,
|
|
TRUE, // CopyAlways
|
|
FALSE // ApplyACLsAlways
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SppCopyKeyRecursive of %ws failed %lx\n", Path2, Status));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now open the services key in both registries
|
|
//
|
|
Status = SpGetValueKey(
|
|
DstHandle,
|
|
Path2,
|
|
L"Service",
|
|
sizeof(TemporaryBuffer),
|
|
(PVOID)TemporaryBuffer,
|
|
&Size
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: Couldn't get target service for %ws, 0x%x\n", Path2,Status));
|
|
continue;
|
|
}
|
|
|
|
|
|
RtlCopyMemory(Path,
|
|
((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->Data,
|
|
((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength
|
|
);
|
|
|
|
Path[((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
INIT_OBJA(&Obj,
|
|
&UnicodeString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services"
|
|
);
|
|
|
|
Status = ZwOpenKey(&TmpHandle, KEY_ALL_ACCESS, &Obj);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwOpenKey of Services failed %lx for %ws\n", Status, Path2));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Get the image path -- remember, since we are in Textmode Setup, the path
|
|
// does *not* contain system32\drivers
|
|
//
|
|
Status = SpGetValueKey(TmpHandle,
|
|
Path,
|
|
L"ImagePath",
|
|
sizeof(TemporaryBuffer),
|
|
(PVOID)TemporaryBuffer,
|
|
&Size
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
//
|
|
// if ImagePath isn't there, we default to using the service name.
|
|
//
|
|
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: GetValue for ImagePath failed %lx for %ws, we'll default it.\n", Status, Path));
|
|
wcscpy( ImageName, Path );
|
|
wcscat( ImageName, L".sys" );
|
|
|
|
} else {
|
|
|
|
RtlCopyMemory(ImageName,
|
|
((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->Data,
|
|
((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength
|
|
);
|
|
|
|
ImageName[((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength/sizeof(WCHAR)] = UNICODE_NULL;
|
|
}
|
|
|
|
//
|
|
// Now delete the old services entry first
|
|
//
|
|
swprintf(TemporaryBuffer,
|
|
L"\\Registry\\SysPrepReg\\ControlSet%03d\\Services\\%ws",
|
|
Number,
|
|
Path
|
|
);
|
|
|
|
INIT_OBJA(&Obj, &UnicodeString, TemporaryBuffer);
|
|
|
|
Status = ZwOpenKey(&TmpHandle2, KEY_ALL_ACCESS, &Obj);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: Deleting target service %ws so that we can recreate it cleanly.\n", Path));
|
|
ZwDeleteKey(TmpHandle2);
|
|
ZwClose(TmpHandle2);
|
|
TmpHandle2 = NULL;
|
|
}
|
|
|
|
//
|
|
// Now get path to services key in the SysPrep image
|
|
//
|
|
swprintf(TemporaryBuffer,
|
|
L"\\Registry\\SysPrepReg\\ControlSet%03d\\Services",
|
|
Number
|
|
);
|
|
|
|
INIT_OBJA(&Obj, &UnicodeString, TemporaryBuffer);
|
|
|
|
Status = ZwOpenKey(&TmpHandle2, KEY_ALL_ACCESS, &Obj);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwOpenKey of SysPrepReg services failed %lx for %ws\n", Status,Path2));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Build the path to the server source.
|
|
//
|
|
wcscpy(SrvPath, SysPrepDriversDevice);
|
|
SpConcatenatePaths(SrvPath, ImageName);
|
|
|
|
//
|
|
// Build the path in the SysPrep image for where to store it
|
|
//
|
|
Tmp = (PWCHAR)(((PUCHAR)pFileData) + pFileData->SystemPathOffset);
|
|
wcscpy(TemporaryBuffer, L"\\??\\");
|
|
SpConcatenatePaths(TemporaryBuffer, Tmp);
|
|
SpConcatenatePaths(TemporaryBuffer, L"\\System32\\Drivers\\");
|
|
SpConcatenatePaths(TemporaryBuffer, ImageName);
|
|
|
|
wcsncpy(Path2,
|
|
TemporaryBuffer,
|
|
MAX_COPY_SIZE(Path2));
|
|
Path2[MAX_COPY_SIZE(Path2)] = L'\0';
|
|
//
|
|
// Copy the driver from the server
|
|
//
|
|
Status = SpCopyFileUsingNames(SrvPath,
|
|
Path2,
|
|
0,
|
|
COPY_ONLY_IF_NOT_PRESENT | COPY_DECOMPRESS_SYSPREP
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SpCopyFilesUsingNames for %ws failed %lx\n", Path2, Status));
|
|
continue;
|
|
}
|
|
|
|
wcscpy( ImageName, L"Files." );
|
|
wcscat( ImageName, Path );
|
|
|
|
CopyListEmpty = FALSE;
|
|
|
|
// copy the rest of the files for this service by looking up the
|
|
// appropriate section in txtsetup.inf
|
|
|
|
SpAddSectionFilesToCopyList(
|
|
SetupSifHandle,
|
|
DiskFileLists,
|
|
DiskCount,
|
|
ImageName, // this is now L"Files.Path"
|
|
pVolumePath, // L"\\Device\\Harddisk0\\Partition1"
|
|
NULL, // it should look up the target directory
|
|
COPY_ONLY_IF_NOT_PRESENT,
|
|
TRUE, // force nocompression, we don't know what type of driver it is.
|
|
FALSE);
|
|
|
|
//
|
|
// Now duplicate the services key
|
|
//
|
|
Status = SppCopyKeyRecursive(TmpHandle,
|
|
TmpHandle2,
|
|
Path,
|
|
Path,
|
|
TRUE, // CopyAlways
|
|
FALSE // ApplyACLsAlways
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SppCopyKeyRecursive for %ws failed %lx\n", Path, Status));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set the start type to 0
|
|
//
|
|
Size = 0;
|
|
Status = SpOpenSetValueAndClose(TmpHandle2,
|
|
Path,
|
|
L"Start",
|
|
REG_DWORD,
|
|
&Size,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SpOpenSetValueAndClose for %ws Start failed %lx\n", Path, Status));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set the image path to one with system32\drivers on the front. We do this by
|
|
// moving backwards thru the target path we have already built to the 3rd backslash
|
|
// from the end.
|
|
//
|
|
Tmp = &(Path2[wcslen(Path2)]);
|
|
for (Size = 0; Size < 3; Size++) {
|
|
while (*Tmp != L'\\') {
|
|
Tmp--;
|
|
}
|
|
Tmp--;
|
|
}
|
|
|
|
Tmp += 2;
|
|
|
|
Status = SpOpenSetValueAndClose(TmpHandle2,
|
|
Path,
|
|
L"ImagePath",
|
|
REG_EXPAND_SZ,
|
|
Tmp,
|
|
(wcslen(Tmp) + 1) * sizeof(WCHAR)
|
|
);
|
|
|
|
ZwClose(TmpHandle);
|
|
ZwClose(TmpHandle2);
|
|
TmpHandle = NULL;
|
|
TmpHandle2 = NULL;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SpOpenSetValueAndClose for %ws ImagePath failed %lx\n", Path, Status));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy over the NIC files, including the INF file.
|
|
//
|
|
Tmp = (PWCHAR)(((PUCHAR)pFileData) + pFileData->SystemPathOffset);
|
|
wcscpy(Path, L"\\??\\");
|
|
SpConcatenatePaths(Path, Tmp);
|
|
Status = SpCopyNicFiles(SysPrepDriversDevice, Path);
|
|
|
|
//
|
|
// Get the HAL and just always copy it over.
|
|
//
|
|
|
|
//
|
|
// Now test for mp/up and then replace dependent drivers as necessary.
|
|
//
|
|
|
|
//
|
|
// Migrate the MountedDevices and DISK registry information.
|
|
//
|
|
|
|
ZwClose(SrcHandle);
|
|
SrcHandle = NULL;
|
|
ZwClose(DstHandle);
|
|
DstHandle = NULL;
|
|
|
|
//
|
|
// Open the system hive of the current build.
|
|
//
|
|
INIT_OBJA(&Obj, &UnicodeString2, L"\\Registry\\Machine\\SYSTEM");
|
|
Status = ZwOpenKey(&SrcHandle, KEY_READ, &Obj);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwOpenKey for local SYSTEM failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Open the system hive of the build we are patching.
|
|
//
|
|
INIT_OBJA(&Obj, &UnicodeString2, L"\\Registry\\SysPrepReg");
|
|
Status = ZwOpenKey(&DstHandle, KEY_READ | KEY_WRITE, &Obj);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwOpenKey for SysPrepReg SYSTEM failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Delete the existing subkeys of the MountedDevices key.
|
|
//
|
|
|
|
Status = SppDeleteKeyRecursive(DstHandle,
|
|
L"MountedDevices",
|
|
TRUE); // ThisKeyToo
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SppDeleteKeyRecursive of MountedDevices failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Copy the MountedDevices key over.
|
|
//
|
|
|
|
Status = SppCopyKeyRecursive(SrcHandle,
|
|
DstHandle,
|
|
L"MountedDevices",
|
|
L"MountedDevices",
|
|
TRUE, // CopyAlways
|
|
TRUE); // ApplyACLsAlways
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SppCopyKeyRecursive of MountedDevices failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Delete the existing subkeys of the DISK key. This routine returns
|
|
// STATUS_OBJECT_NAME_NOT_FOUND if the key does not exist, which is
|
|
// not an error in this case.
|
|
//
|
|
|
|
Status = SppDeleteKeyRecursive(DstHandle,
|
|
L"DISK",
|
|
TRUE); // ThisKeyToo
|
|
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SppDeleteKeyRecursive of DISK failed %lx\n", Status));
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Copy the DISK key over.
|
|
//
|
|
|
|
Status = SppCopyKeyRecursive(SrcHandle,
|
|
DstHandle,
|
|
L"DISK",
|
|
L"DISK",
|
|
TRUE, // CopyAlways
|
|
TRUE); // ApplyACLsAlways
|
|
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: SppCopyKeyRecursive of DISK failed %lx\n", Status));
|
|
Status = STATUS_SUCCESS;
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Delete those subkeys of the CCS\Enum\STORAGE\Volume key that
|
|
// represent volumes that were never fully installed. This eliminates
|
|
// stale information about volumes that may not exist on this computer.
|
|
//
|
|
|
|
SpDeleteStorageVolumes( DstHandle, Number );
|
|
|
|
CleanUp:
|
|
|
|
if (!CopyListEmpty) {
|
|
//
|
|
// Copy files in the copy list.
|
|
//
|
|
|
|
WCHAR emptyString = L'\0';
|
|
PWCHAR lastComponent;
|
|
|
|
Tmp = (PWCHAR)(((PUCHAR)pFileData) + pFileData->SystemPathOffset);
|
|
|
|
if (*Tmp != L'\0' && *(Tmp+1) == L':') {
|
|
Tmp += 2; // have it skip L"C:" at front of path
|
|
wcscpy(Path2, Tmp);
|
|
} else {
|
|
wcscpy(Path2, L"\\??\\");
|
|
SpConcatenatePaths(Path2, Tmp);
|
|
}
|
|
|
|
//
|
|
// first we need to remove the L"\i386" off the end of the source
|
|
// path since SpCopyFilesInCopyList or decendent will tack it on.
|
|
//
|
|
// divide up the source path into two parts so that SpConcatenatePaths
|
|
// does the right thing when it puts it back together.
|
|
//
|
|
|
|
wcscpy( SrvPath, SysPrepDriversDevice );
|
|
|
|
lastComponent = SrvPath + wcslen( SrvPath ) - 1;
|
|
|
|
while (lastComponent > SrvPath && *lastComponent != L'\\') {
|
|
lastComponent--;
|
|
}
|
|
|
|
if (lastComponent > SrvPath) {
|
|
|
|
*lastComponent = L'\0'; // this removes the architecture off the end
|
|
|
|
// now move backwards until we find the start of the last component
|
|
while (lastComponent > SrvPath && *lastComponent != L'\\') {
|
|
lastComponent--;
|
|
}
|
|
|
|
if (lastComponent > SrvPath) {
|
|
*lastComponent = L'\0';
|
|
lastComponent++;
|
|
} else {
|
|
lastComponent = &emptyString;
|
|
}
|
|
} else {
|
|
lastComponent = &emptyString;
|
|
}
|
|
|
|
SpCopyFilesInCopyList(
|
|
SetupSifHandle,
|
|
DiskFileLists,
|
|
DiskCount,
|
|
SrvPath, // L"\\device\\harddisk0\\partition1"
|
|
lastComponent, // L"\\$win_nt$.~ls"
|
|
Path2, // L"\\WINNT"
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if (HaveCopyList) {
|
|
SpFreeCopyLists(&DiskFileLists,DiskCount);
|
|
}
|
|
|
|
if (SrcHandle != NULL) {
|
|
ZwClose(SrcHandle);
|
|
}
|
|
|
|
if (DstHandle != NULL) {
|
|
ZwClose(DstHandle);
|
|
}
|
|
|
|
if (TmpHandle != NULL) {
|
|
ZwClose(TmpHandle);
|
|
}
|
|
|
|
if (TmpHandle2 != NULL) {
|
|
ZwClose(TmpHandle2);
|
|
}
|
|
|
|
if (NeedToUnload) {
|
|
ZwUnloadKey(&DstObj);
|
|
NeedToUnload = FALSE;
|
|
}
|
|
|
|
if (pVolumePath != NULL) {
|
|
SpMemFree( pVolumePath );
|
|
}
|
|
|
|
//
|
|
// update the NT source path in the software section of the registry
|
|
// since we have a valid SysPrepDriversDevice
|
|
//
|
|
|
|
if (SysPrepDriversDevice && *SysPrepDriversDevice != L'\0') {
|
|
|
|
//
|
|
// Now load the local version of the SysPrep hives, using IMirror.Dat to find them
|
|
// NOTE: DstObj is assumed by CleanUp to still be the key.
|
|
//
|
|
Tmp = (PWCHAR)(((PUCHAR)pFileData) + pFileData->SystemPathOffset);
|
|
wcscpy(Path, L"\\??\\");
|
|
wcscat(Path, Tmp);
|
|
wcscat(Path, L"\\System32\\Config\\Software");
|
|
|
|
INIT_OBJA(&DstObj, &UnicodeString1, L"\\Registry\\SysPrepReg");
|
|
INIT_OBJA(&Obj, &UnicodeString2, Path);
|
|
|
|
Status = ZwLoadKey(&DstObj, &Obj);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
NeedToUnload = TRUE;
|
|
|
|
//
|
|
// Open the system hive of the build we are patching.
|
|
//
|
|
INIT_OBJA(&Obj, &UnicodeString2, L"\\Registry\\SysPrepReg\\Microsoft\\Windows\\CurrentVersion\\Setup");
|
|
Status = ZwOpenKey(&DstHandle, KEY_READ | KEY_WRITE, &Obj);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
BOOLEAN haveDecentString = FALSE;
|
|
|
|
//
|
|
// the path is of the form
|
|
// \device\lanmanredirector\server\share\..\flat\i386
|
|
// when we want it of the form \\server\share\..\flat
|
|
//
|
|
|
|
Tmp = SysPrepDriversDevice + 1;
|
|
|
|
while (*Tmp != L'\0' && *Tmp != L'\\') {
|
|
Tmp++;
|
|
}
|
|
if (*Tmp == L'\\') {
|
|
|
|
Tmp++;
|
|
while (*Tmp != L'\0' && *Tmp != L'\\') {
|
|
Tmp++;
|
|
}
|
|
if (*Tmp == L'\\') { // back up one before the \server\share
|
|
// so we can put another \ on it
|
|
Tmp--;
|
|
wcscpy( Path, Tmp );
|
|
Tmp = Path;
|
|
*Tmp = L'\\'; // we now have \\server\share\..\flat\i386
|
|
|
|
Tmp = Path + wcslen(Path);
|
|
|
|
while (Tmp > Path && *Tmp != L'\\') {
|
|
Tmp--;
|
|
}
|
|
if (Tmp > Path) {
|
|
*Tmp = L'\0'; // remove the \i386
|
|
haveDecentString = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (haveDecentString) {
|
|
INIT_OBJA(&Obj, &UnicodeString2, L"SourcePath");
|
|
Status = ZwSetValueKey (DstHandle,
|
|
&UnicodeString2,
|
|
0,
|
|
REG_SZ,
|
|
Path,
|
|
(wcslen(Path) + 1 ) * sizeof(WCHAR));
|
|
if (!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: Setting SourcePath to %ws failed with 0x%x\n", Path, Status));
|
|
}
|
|
} else {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: Couldn't set SourcePath to %ws\n", SysPrepDriversDevice));
|
|
Status = STATUS_OBJECT_PATH_INVALID;
|
|
}
|
|
ZwClose(DstHandle);
|
|
DstHandle = NULL;
|
|
|
|
} else {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwOpenKey for SysPrepReg SOFTWARE failed %lx\n", Status));
|
|
}
|
|
} else {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpPatchSysPrepImage: ZwLoadKey to SysPrepReg failed %lx\n", Status));
|
|
}
|
|
}
|
|
|
|
//
|
|
// patch boot.ini regardless of the status of the patching of everything
|
|
// else. if we don't patch boot.ini, the whole image has no hope of
|
|
// booting.
|
|
//
|
|
|
|
#if defined(_AMD64_) || defined(_X86_)
|
|
//
|
|
// Patch boot.ini if the ARC names have changed. Boot.ini will
|
|
// be on the active partition of disk 0.
|
|
//
|
|
|
|
for (volume = 0; volume < pMemoryData->NumberVolumes; volume++) {
|
|
volumeInfo = &pMemoryData->Volumes[volume];
|
|
if ((volumeInfo->DiskNumber == 0) &&
|
|
(volumeInfo->PartitionActive)) {
|
|
|
|
ULONG tmpLen;
|
|
wcscpy(Path, L"\\??\\");
|
|
tmpLen = wcslen(Path);
|
|
Path[tmpLen] = volumeInfo->DriveLetter;
|
|
Path[tmpLen+1] = L'\0';
|
|
wcscat(Path, L":\\boot.ini");
|
|
|
|
SpPatchBootIni(Path, pMemoryData);
|
|
break;
|
|
}
|
|
}
|
|
#endif // defined(_AMD64_) || defined(_X86_)
|
|
|
|
if (NeedToUnload) {
|
|
ZwUnloadKey(&DstObj);
|
|
}
|
|
|
|
if (Buffer != NULL) {
|
|
SpMemFree(Buffer);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
SpReplaceArcName(
|
|
IN PUCHAR CurrentArcName,
|
|
IN PMIRROR_CFG_INFO_MEMORY pMemoryData,
|
|
OUT PBOOLEAN Replaced
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks in pMemoryData to see if there is a partition
|
|
whose OriginalArcName is equal to CurrentArcName, and if so it
|
|
replaces CurrentArcName, adjusting the rest of the string that
|
|
follows CurrentArcName if needed.
|
|
|
|
Arguments:
|
|
|
|
CurrentArcName - The ARC name to check.
|
|
|
|
pMemoryData - A pointer to an in-memory copy of IMirror.Dat, modified to
|
|
match the specs of this computer (disk sizes etc).
|
|
|
|
Replaced - Returns TRUE if the name is replaced.
|
|
|
|
Return Value:
|
|
|
|
The NTSTATUS of the operation.
|
|
--*/
|
|
{
|
|
ULONG volume;
|
|
PMIRROR_VOLUME_INFO_MEMORY volumeInfo;
|
|
ULONG originalArcNameLength, newArcNameLength;
|
|
CHAR TmpArcName[128];
|
|
|
|
//
|
|
// Scan pMemoryData to see if any ARC names match.
|
|
//
|
|
|
|
*Replaced = FALSE;
|
|
|
|
for (volume = 0; volume < pMemoryData->NumberVolumes; volume++) {
|
|
volumeInfo = &pMemoryData->Volumes[volume];
|
|
|
|
originalArcNameLength = wcslen(volumeInfo->OriginalArcName);
|
|
wcstombs(TmpArcName, volumeInfo->OriginalArcName, originalArcNameLength+1);
|
|
|
|
if (RtlCompareMemory(TmpArcName, CurrentArcName, originalArcNameLength) == originalArcNameLength) {
|
|
|
|
//
|
|
// This is the partition that CurrentArcName refers to,
|
|
// see what the ARC name is now.
|
|
//
|
|
|
|
SpArcNameFromRegion(
|
|
volumeInfo->CreatedRegion,
|
|
TemporaryBuffer,
|
|
sizeof(TemporaryBuffer),
|
|
PartitionOrdinalOnDisk,
|
|
PrimaryArcPath);
|
|
|
|
//
|
|
// If we got an ARC name and it is different from what it was on
|
|
// the old machine, we need to replace.
|
|
//
|
|
|
|
if (*TemporaryBuffer &&
|
|
(wcscmp(volumeInfo->OriginalArcName, TemporaryBuffer) != 0)) {
|
|
|
|
//
|
|
// See if we need to adjust the buffer because the length
|
|
// of the ARC names is different.
|
|
//
|
|
|
|
newArcNameLength = wcslen(TemporaryBuffer);
|
|
if (newArcNameLength != originalArcNameLength) {
|
|
memmove(
|
|
CurrentArcName+newArcNameLength,
|
|
CurrentArcName+originalArcNameLength,
|
|
strlen(CurrentArcName+originalArcNameLength) + 1);
|
|
}
|
|
|
|
//
|
|
// Copy over the new ARC name.
|
|
//
|
|
|
|
wcstombs(TmpArcName, TemporaryBuffer, newArcNameLength);
|
|
memcpy(CurrentArcName, TmpArcName, newArcNameLength);
|
|
|
|
*Replaced = TRUE;
|
|
break; // no need to look at any other volumeInfo's.
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
SpPatchBootIni(
|
|
IN PWCHAR BootIniPath,
|
|
IN PMIRROR_CFG_INFO_MEMORY pMemoryData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine modifies boot.ini to modify any ARC names that have
|
|
changed.
|
|
|
|
Arguments:
|
|
|
|
BootIniPath - The path to the local boot.ini.
|
|
|
|
pMemoryData - A pointer to an in-memory copy of IMirror.Dat, modified to
|
|
match the specs of this computer (disk sizes etc).
|
|
|
|
Return Value:
|
|
|
|
The NTSTATUS of the operation.
|
|
--*/
|
|
{
|
|
ULONG ulLen;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE Handle;
|
|
PCHAR pFileData = NULL, pNewFileData = NULL;
|
|
PCHAR curLoc, endOfLine;
|
|
BOOLEAN changedFile = FALSE;
|
|
PWCHAR TmpBootIni = NULL;
|
|
|
|
//
|
|
// Read in the current boot.ini.
|
|
//
|
|
|
|
INIT_OBJA(&Obja, &UnicodeString, BootIniPath);
|
|
|
|
Status = ZwCreateFile(&Handle,
|
|
FILE_GENERIC_READ,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPatchBootIni could not open %ws: %lx\n", BootIniPath, Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = SpGetFileSize(Handle, &ulLen);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPatchBootIni could not SpGetFileSize: %lx\n", Status));
|
|
ZwClose(Handle);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate memory and read in the file.
|
|
//
|
|
pFileData = SpMemAlloc(ulLen);
|
|
|
|
Status = ZwReadFile(Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
pFileData,
|
|
ulLen,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
ZwClose(Handle);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPatchBootIni could not ZwReadFile: %lx\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate memory for the new copy of the file (we use twice the
|
|
// current size as a worst-case scenario).
|
|
//
|
|
|
|
pNewFileData = SpMemAlloc(ulLen * 2);
|
|
memcpy(pNewFileData, pFileData, ulLen);
|
|
pNewFileData[ulLen] = '\0'; // NULL-terminate to make replace easier.
|
|
|
|
//
|
|
// Run through each line of the file, looking for ARC names to
|
|
// replace. ARC names are either after the "default=" text or
|
|
// else they start a line.
|
|
//
|
|
|
|
curLoc = pNewFileData;
|
|
|
|
while (TRUE) {
|
|
|
|
BOOLEAN replaced = FALSE;
|
|
LONG adjustment;
|
|
|
|
//
|
|
// Replace if this is a "default=" line, or a line that does not
|
|
// start with "timeout=" or a '['.
|
|
//
|
|
|
|
if (RtlCompareMemory(curLoc, "default=", strlen("default=")) == strlen("default=")) {
|
|
SpReplaceArcName(curLoc + strlen("default="), pMemoryData, &replaced);
|
|
} else if ((*curLoc != '[') &&
|
|
(RtlCompareMemory(curLoc, "timeout=", strlen("timeout=")) != strlen("timeout="))) {
|
|
SpReplaceArcName(curLoc, pMemoryData, &replaced);
|
|
}
|
|
|
|
if (replaced) {
|
|
changedFile = TRUE;
|
|
}
|
|
|
|
//
|
|
// Look for a '\n' in the file.
|
|
//
|
|
|
|
endOfLine = strchr(curLoc, '\n');
|
|
if (endOfLine == NULL) {
|
|
break;
|
|
}
|
|
|
|
curLoc = endOfLine + 1;
|
|
|
|
if (*curLoc == L'\0') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we changed the file, write out the new one.
|
|
//
|
|
|
|
if (changedFile) {
|
|
|
|
//
|
|
// Replace the old boot.ini with the new one.
|
|
//
|
|
|
|
TmpBootIni = SpDupStringW(BootIniPath);
|
|
|
|
if (!TmpBootIni) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
TmpBootIni[wcslen(TmpBootIni)-1] = L'$'; // make it boot.in$
|
|
|
|
INIT_OBJA(&Obja, &UnicodeString, TmpBootIni);
|
|
|
|
Status = ZwCreateFile(&Handle,
|
|
FILE_GENERIC_WRITE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
0, // no sharing
|
|
FILE_OVERWRITE_IF,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_WRITE_THROUGH,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPatchBootIni could not create %ws: %lx\n", TmpBootIni, Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = ZwWriteFile(Handle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
pNewFileData,
|
|
strlen(pNewFileData),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
ZwClose(Handle);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPatchBootIni could not ZwWriteFile: %lx\n", Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Now that we have written the tmp file, do the swap.
|
|
//
|
|
|
|
Status = SpDeleteFile(BootIniPath, NULL, NULL);
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPatchBootIni could not SpDeleteFile(%ws): %lx\n", BootIniPath, Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = SpRenameFile(TmpBootIni, BootIniPath, FALSE);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpPatchBootIni could not SpRenameFile(%ws,%ws): %lx\n", TmpBootIni, BootIniPath, Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (pFileData != NULL) {
|
|
SpMemFree(pFileData);
|
|
}
|
|
|
|
if (pNewFileData != NULL) {
|
|
SpMemFree(pNewFileData);
|
|
}
|
|
|
|
if (TmpBootIni != NULL) {
|
|
SpMemFree(TmpBootIni);
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
SpCopyNicFiles(
|
|
IN PWCHAR SetupPath,
|
|
IN PWCHAR DestPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine packages up information and sends it to the BINL server, getting back
|
|
a list of files to copy to support the given NIC. It then copies those files.
|
|
|
|
Arguments:
|
|
|
|
SetupPath - Setup path that supports the SysPrep image.
|
|
|
|
DestPath - Path to the winnt directory.
|
|
|
|
Return Value:
|
|
|
|
The NTSTATUS of the operation.
|
|
|
|
--*/
|
|
{
|
|
PSPUDP_PACKET pUdpPacket = NULL;
|
|
WCHAR UNALIGNED * pPacketEnd;
|
|
PSP_NETCARD_INFO_REQ pReqPacket;
|
|
PSP_NETCARD_INFO_RSP pRspPacket;
|
|
ULONG PacketSize;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG i,j;
|
|
PWCHAR pSrc, pDst, pTmp;
|
|
WCHAR SrcFqn[MAX_PATH];
|
|
WCHAR DstFqn[MAX_PATH];
|
|
|
|
//
|
|
// BINL expects the path to be a UNC w/o the architecture type, so take the current
|
|
// setup path, in the form of "\device\lanmanredirector\server\share\...\i386" and
|
|
// make it "\\server\share\..."
|
|
//
|
|
// First remove the architecture type, and the the leading stuff.
|
|
//
|
|
wcscpy(SrcFqn, SetupPath);
|
|
|
|
pTmp = &(SrcFqn[wcslen(SrcFqn)]);
|
|
|
|
while ((*pTmp != L'\\') && (pTmp != SrcFqn)) {
|
|
pTmp--;
|
|
}
|
|
|
|
if (*pTmp == L'\\') {
|
|
*pTmp = UNICODE_NULL;
|
|
}
|
|
|
|
pTmp = SrcFqn;
|
|
pTmp++;
|
|
while ((*pTmp != UNICODE_NULL) && (*pTmp != L'\\')) {
|
|
pTmp++;
|
|
}
|
|
if (*pTmp == L'\\') {
|
|
pTmp++;
|
|
}
|
|
while ((*pTmp != UNICODE_NULL) && (*pTmp != L'\\')) {
|
|
pTmp++;
|
|
}
|
|
if (*pTmp == L'\\') {
|
|
pTmp--;
|
|
*pTmp = L'\\';
|
|
}
|
|
|
|
//
|
|
// Allocate the packet
|
|
//
|
|
PacketSize = FIELD_OFFSET(SPUDP_PACKET, Data[0]) +
|
|
FIELD_OFFSET(SP_NETCARD_INFO_REQ, SetupPath[0]) +
|
|
(wcslen(pTmp) + 1) * sizeof(WCHAR);
|
|
|
|
pUdpPacket = (PSPUDP_PACKET)SpMemAllocNonPagedPool(PacketSize);
|
|
|
|
//
|
|
// Fill in the packet
|
|
//
|
|
RtlCopyMemory(&(pUdpPacket->Signature[0]), SetupRequestSignature, 4);
|
|
pUdpPacket->Length = PacketSize - FIELD_OFFSET(SPUDP_PACKET, RequestType);
|
|
pUdpPacket->RequestType = 0;
|
|
pUdpPacket->Status = STATUS_SUCCESS;
|
|
pUdpPacket->SequenceNumber = 1;
|
|
pUdpPacket->FragmentNumber = 1;
|
|
pUdpPacket->FragmentTotal = 1;
|
|
|
|
pReqPacket = (PSP_NETCARD_INFO_REQ)(&(pUdpPacket->Data[0]));
|
|
pReqPacket->Version = OSCPKT_NETCARD_REQUEST_VERSION;
|
|
RtlCopyMemory(&(pReqPacket->CardInfo), &RemoteSysPrepNetCardInfo, sizeof(NET_CARD_INFO));
|
|
wcscpy(&(pReqPacket->SetupPath[0]), pTmp);
|
|
|
|
#if defined(_AMD64_)
|
|
pReqPacket->Architecture = PROCESSOR_ARCHITECTURE_AMD64;
|
|
#elif defined(_IA64_)
|
|
pReqPacket->Architecture = PROCESSOR_ARCHITECTURE_IA64;
|
|
#elif defined(_X86_)
|
|
pReqPacket->Architecture = PROCESSOR_ARCHITECTURE_INTEL;
|
|
#else
|
|
#error "No Target Architecture"
|
|
#endif
|
|
|
|
|
|
//
|
|
// Open the Udp stack
|
|
//
|
|
Status = SpUdpConnect();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
//
|
|
// Send the request
|
|
//
|
|
Status = SpUdpSendAndReceiveDatagram(pUdpPacket,
|
|
PacketSize,
|
|
RemoteServerIpAddress,
|
|
BINL_DEFAULT_PORT,
|
|
SpSysPrepNicRcvFunc
|
|
);
|
|
|
|
SpUdpDisconnect();
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Get the received packet
|
|
//
|
|
SpMemFree(pUdpPacket);
|
|
pUdpPacket = (PSPUDP_PACKET)pGlobalResponsePacket;
|
|
|
|
Status = pUdpPacket->Status;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (GlobalResponsePacketLength <
|
|
(ULONG)(FIELD_OFFSET(SPUDP_PACKET, Data[0]) + FIELD_OFFSET(SP_NETCARD_INFO_RSP, MultiSzFiles[0]))) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CleanUp;
|
|
}
|
|
|
|
pRspPacket = (PSP_NETCARD_INFO_RSP)(&(pUdpPacket->Data[0]));
|
|
pPacketEnd = (WCHAR UNALIGNED *)(((PUCHAR)pGlobalResponsePacket) + GlobalResponsePacketLength);
|
|
|
|
//
|
|
// Now copy each file
|
|
//
|
|
pTmp = &(pRspPacket->MultiSzFiles[0]);
|
|
for (i = 0; i < pRspPacket->cFiles;) {
|
|
|
|
i++;
|
|
|
|
if (pTmp >= pPacketEnd) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
//
|
|
// Be careful about reading this data, since it's come in over the
|
|
// network. ie., make sure that the string length is reasonable
|
|
// before proceeding with processing the string.
|
|
//
|
|
pSrc = pTmp;
|
|
|
|
try {
|
|
j = wcslen(pSrc);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
j = sizeof(SrcFqn)/sizeof(WCHAR) + 1;
|
|
}
|
|
|
|
|
|
if (j + wcslen(SetupPath) + 1 > sizeof(SrcFqn)/sizeof(WCHAR)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CleanUp;
|
|
}
|
|
|
|
pDst = pTmp = pSrc + j + 1;
|
|
if (pTmp >= pPacketEnd) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CleanUp;
|
|
}
|
|
wcscpy(SrcFqn, SetupPath);
|
|
SpConcatenatePaths(SrcFqn, pSrc);
|
|
|
|
wcscpy(DstFqn, DestPath);
|
|
|
|
if (i == pRspPacket->cFiles) { // the last file in the list is the INF
|
|
SpConcatenatePaths(DstFqn, L"inf");
|
|
} else { // all the others go in system32\drivers
|
|
SpConcatenatePaths(DstFqn, L"system32\\drivers");
|
|
}
|
|
|
|
if (*pDst != UNICODE_NULL) {
|
|
try {
|
|
j = wcslen(pDst);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
j = sizeof(DstFqn)/sizeof(WCHAR) + 1;
|
|
}
|
|
|
|
if (j+wcslen(DstFqn)+1 > sizeof(DstFqn)/sizeof(WCHAR)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CleanUp;
|
|
}
|
|
|
|
pTmp = pDst + j + 1;
|
|
if (pTmp >= pPacketEnd) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto CleanUp;
|
|
}
|
|
|
|
SpConcatenatePaths(DstFqn, pDst);
|
|
} else {
|
|
SpConcatenatePaths(DstFqn, pSrc);
|
|
pTmp = pDst + 1;
|
|
}
|
|
|
|
Status = SpCopyFileUsingNames(SrcFqn, DstFqn, 0, COPY_DECOMPRESS_SYSPREP );
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto CleanUp;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
CleanUp:
|
|
|
|
if (pUdpPacket != NULL) {
|
|
SpMemFree(pUdpPacket);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SpSysPrepNicRcvFunc(
|
|
PVOID DataBuffer,
|
|
ULONG DataBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine receives datagrams from the server into the global variable
|
|
pGlobalResponsePacket, iff it is NULL, otherwise it is presumed to hold data
|
|
and the incoming packet is assumed to be a duplicate response packet.
|
|
|
|
NOTE: spudp.c guarantees singly-threaded callbacks, so we don't have to spin lock
|
|
here.
|
|
|
|
Arguments:
|
|
|
|
DataBuffer - The incoming data.
|
|
|
|
DataBufferLength - Length of the data in bytes.
|
|
|
|
Return Value:
|
|
|
|
The NTSTATUS of the operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSPUDP_PACKET pUdpPacket;
|
|
WCHAR UNALIGNED * pPacketEnd;
|
|
|
|
if ((pGlobalResponsePacket != NULL) || (DataBufferLength == 0)) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
pUdpPacket = (PSPUDP_PACKET)DataBuffer;
|
|
|
|
if (RtlCompareMemory(&(pUdpPacket->Signature[0]), SetupResponseSignature, 4) != 4) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
pGlobalResponsePacket = SpMemAlloc(DataBufferLength + sizeof(WCHAR));
|
|
|
|
RtlCopyMemory(pGlobalResponsePacket, DataBuffer, DataBufferLength);
|
|
pPacketEnd = (WCHAR UNALIGNED *)(((PUCHAR)pGlobalResponsePacket) + DataBufferLength);
|
|
*pPacketEnd = L'\0'; // NULL-terminate it
|
|
GlobalResponsePacketLength = DataBufferLength;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
SpSysPrepFailure(
|
|
ULONG ReasonNumber,
|
|
PVOID Parameter1,
|
|
PVOID Parameter2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Inform the user that we were unable to bring down the sysprep image
|
|
correctly.
|
|
|
|
This is a fatal condition.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Does not return.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ValidKeys[2] = { KEY_F3, 0 };
|
|
PWCHAR MessageText = NULL;
|
|
WCHAR blankMessage[1];
|
|
|
|
if (ReasonNumber > 0) {
|
|
|
|
// Get the message text.
|
|
//
|
|
|
|
if (Parameter1 == NULL) {
|
|
|
|
MessageText = SpRetreiveMessageText(NULL,ReasonNumber,NULL,0);
|
|
|
|
} else {
|
|
|
|
SpFormatMessage( TemporaryBuffer,
|
|
sizeof(TemporaryBuffer),
|
|
ReasonNumber,
|
|
Parameter1,
|
|
Parameter2
|
|
);
|
|
|
|
MessageText = SpDupStringW(TemporaryBuffer);
|
|
}
|
|
if (MessageText == NULL) {
|
|
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpSysPrepFailure: SpRetreiveMessageText %u returned NULL\n",ReasonNumber));
|
|
}
|
|
}
|
|
|
|
if (MessageText == NULL) {
|
|
|
|
blankMessage[0] = L'\0';
|
|
MessageText = &blankMessage[0];
|
|
}
|
|
|
|
CLEAR_CLIENT_SCREEN();
|
|
|
|
SpStartScreen( SP_SCRN_SYSPREP_FATAL_FAILURE,
|
|
3,
|
|
HEADER_HEIGHT+1,
|
|
FALSE,
|
|
FALSE,
|
|
DEFAULT_ATTRIBUTE,
|
|
MessageText
|
|
);
|
|
|
|
SpDisplayStatusOptions(DEFAULT_STATUS_ATTRIBUTE,SP_STAT_F3_EQUALS_EXIT,0);
|
|
|
|
SpWaitValidKey(ValidKeys,NULL,NULL);
|
|
|
|
SpDone(0,FALSE,FALSE);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpSysPrepSetShortFileName (
|
|
PWCHAR Source,
|
|
PWCHAR Dest
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Try to set the short filename out of the sysprep image.
|
|
|
|
This should be considered non-fatal if it fails since not all
|
|
files will have this information set for them.
|
|
|
|
Arguments:
|
|
|
|
Source :
|
|
|
|
Return Value:
|
|
|
|
The status code from setting the info. May not return if we hit a failure
|
|
and the user specifies to abort the setup.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
ULONG stringLength = 0;
|
|
ULONG FileNameInformationLength = 0;
|
|
PWCHAR fullName = NULL;
|
|
PWCHAR SFNBuffer = NULL;
|
|
|
|
HANDLE sourceHandle = NULL;
|
|
HANDLE streamHandle = NULL;
|
|
HANDLE destHandle = NULL;
|
|
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
MIRROR_SFN_STREAM mirrorHeader;
|
|
|
|
LARGE_INTEGER byteOffset;
|
|
ULONG bytesRead;
|
|
|
|
|
|
BOOLEAN haveSFN = FALSE;
|
|
BOOLEAN haveStream = FALSE;
|
|
BOOLEAN haveSourceAttributes = FALSE;
|
|
|
|
PFILE_NAME_INFORMATION FileNameInformation;
|
|
|
|
if ((Source == NULL) || (Dest == NULL)) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// alloc a buffer to hold the full file of the source including stream
|
|
|
|
while (*(Source+stringLength) != L'\0') {
|
|
stringLength++;
|
|
}
|
|
stringLength += sizeof( IMIRROR_SFN_STREAM_NAME ) + 1; // + 1 for size of null
|
|
stringLength *= sizeof(WCHAR);
|
|
|
|
fullName = SpMemAlloc( stringLength );
|
|
if (!fullName) {
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
|
|
wcscpy( fullName, Source );
|
|
wcscat( fullName, IMIRROR_SFN_STREAM_NAME );
|
|
|
|
//
|
|
// Open the source stream.
|
|
//
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,fullName);
|
|
|
|
Status = ZwCreateFile(
|
|
&streamHandle,
|
|
GENERIC_READ | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if ( ! NT_SUCCESS(Status) ) {
|
|
|
|
//
|
|
// for now, if a directory or file doesn't have our stream, it's ok.
|
|
// we'll just skip it.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
|
|
byteOffset.QuadPart = 0;
|
|
|
|
Status = ZwReadFile(streamHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(PCHAR) &mirrorHeader,
|
|
sizeof( mirrorHeader ),
|
|
&byteOffset,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status) ||
|
|
(IoStatusBlock.Information < sizeof( mirrorHeader ))) {
|
|
|
|
//
|
|
// we can't read the header correctly. just skip setting SFN.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
|
|
}
|
|
if (mirrorHeader.StreamVersion != IMIRROR_SFN_STREAM_VERSION) {
|
|
|
|
//
|
|
// we can't read the header correctly. just skip setting SFN.
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
|
|
haveStream = TRUE;
|
|
|
|
//
|
|
// allocate a buffer to hold the SFN. The size is embedded in the header.
|
|
// take off room for the structure and tack on two for a UNICODE_NULL at
|
|
// the end, just in case the stream doesn't have one.
|
|
//
|
|
|
|
if (mirrorHeader.StreamLength) {
|
|
|
|
SFNBuffer = SpMemAlloc( mirrorHeader.StreamLength - sizeof(MIRROR_SFN_STREAM) + 2 );
|
|
if (!SFNBuffer) {
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
|
|
byteOffset.QuadPart += sizeof( mirrorHeader );
|
|
|
|
//
|
|
// now we read the SFN since we know how long it is.
|
|
//
|
|
|
|
Status = ZwReadFile(streamHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
SFNBuffer,
|
|
mirrorHeader.StreamLength - sizeof(MIRROR_SFN_STREAM),
|
|
&byteOffset,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status) ||
|
|
(IoStatusBlock.Information < (mirrorHeader.StreamLength - sizeof(MIRROR_SFN_STREAM)))) {
|
|
|
|
//
|
|
// oh joy, we can't read the SFN correctly
|
|
//
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
|
|
haveSFN = TRUE;
|
|
//
|
|
// tack on a unicode NULL just in case.
|
|
//
|
|
SFNBuffer[(mirrorHeader.StreamLength - sizeof(MIRROR_SFN_STREAM))/sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
} else {
|
|
ASSERT(FALSE);
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,Dest);
|
|
|
|
Status = ZwCreateFile(
|
|
&destHandle,
|
|
FILE_READ_ATTRIBUTES |
|
|
FILE_WRITE_ATTRIBUTES |
|
|
FILE_READ_DATA |
|
|
FILE_WRITE_DATA |
|
|
DELETE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Maybe it's not a directory... Try for a file.
|
|
//
|
|
|
|
Status = ZwCreateFile(
|
|
&destHandle,
|
|
FILE_READ_ATTRIBUTES |
|
|
FILE_WRITE_ATTRIBUTES |
|
|
FILE_READ_DATA |
|
|
FILE_WRITE_DATA |
|
|
DELETE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
|
|
if( !NT_SUCCESS(Status) ) {
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
|
|
FileNameInformationLength = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) + ((wcslen(SFNBuffer)+1)*sizeof(WCHAR));
|
|
FileNameInformation = SpMemAlloc( FileNameInformationLength );
|
|
|
|
if (FileNameInformation) {
|
|
|
|
FileNameInformation->FileNameLength = wcslen(SFNBuffer) * sizeof(WCHAR);
|
|
wcscpy( FileNameInformation->FileName, SFNBuffer );
|
|
|
|
|
|
Status = ZwSetInformationFile( destHandle,
|
|
&IoStatusBlock,
|
|
FileNameInformation,
|
|
FileNameInformationLength,
|
|
FileShortNameInformation
|
|
);
|
|
|
|
SpMemFree( FileNameInformation );
|
|
|
|
// Keep quiet.
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
exit:
|
|
if (fullName) {
|
|
SpMemFree( fullName );
|
|
}
|
|
if (SFNBuffer) {
|
|
SpMemFree( SFNBuffer );
|
|
}
|
|
if (streamHandle) {
|
|
ZwClose( streamHandle );
|
|
}
|
|
if (sourceHandle) {
|
|
ZwClose( sourceHandle );
|
|
}
|
|
if (destHandle) {
|
|
ZwClose( destHandle );
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpSysPrepSetExtendedInfo (
|
|
PWCHAR Source,
|
|
PWCHAR Dest,
|
|
BOOLEAN Directory,
|
|
BOOLEAN RootDir
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Try to set the extended information out of the sysprep image. This
|
|
includes the acl and the compression info. If we encounter an error,
|
|
we may not only fail the operation but also reboot if the user chooses
|
|
to abandon the setup.
|
|
|
|
Arguments:
|
|
|
|
Source :
|
|
|
|
Return Value:
|
|
|
|
The status code from setting the info. May not return if we hit a failure
|
|
and the user specifies to abort the setup.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
ULONG stringLength = 0;
|
|
PWCHAR fullName = NULL;
|
|
PWCHAR rootWithSlash = NULL;
|
|
HANDLE sourceHandle = NULL;
|
|
HANDLE streamHandle = NULL;
|
|
HANDLE destHandle = NULL;
|
|
PCHAR sdBuffer = NULL;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
MIRROR_ACL_STREAM mirrorHeader;
|
|
LARGE_INTEGER byteOffset;
|
|
ULONG bytesRead;
|
|
ULONG ValidKeys[4] = { ASCI_ESC, KEY_F3, 0 };
|
|
ULONG WarnKeys[2] = { KEY_F3, 0 };
|
|
ULONG MnemonicKeys[] = { MnemonicContinueSetup, 0 };
|
|
BOOLEAN haveSecurityDescriptor = FALSE;
|
|
BOOLEAN haveStream = FALSE;
|
|
BOOLEAN haveSourceAttributes = FALSE;
|
|
FILE_BASIC_INFORMATION BasicFileInfo;
|
|
USHORT CompressionState;
|
|
|
|
if ((Source == NULL) || (Dest == NULL)) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!RootDir) {
|
|
SpSysPrepSetShortFileName(Source, Dest);
|
|
}
|
|
|
|
mirrorHeader.ExtendedAttributes = 0;
|
|
|
|
// alloc a buffer to hold the full file of the source including stream
|
|
|
|
while (*(Source+stringLength) != L'\0') {
|
|
stringLength++;
|
|
}
|
|
stringLength += sizeof( IMIRROR_ACL_STREAM_NAME ) + 1; // + 1 for size of null
|
|
stringLength *= sizeof(WCHAR);
|
|
|
|
fullName = SpMemAlloc( stringLength );
|
|
|
|
wcscpy( fullName, Source );
|
|
wcscat( fullName, IMIRROR_ACL_STREAM_NAME );
|
|
|
|
//
|
|
// Open the source stream.
|
|
//
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,fullName);
|
|
|
|
Status = ZwCreateFile(
|
|
&streamHandle,
|
|
GENERIC_READ | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if ( ! NT_SUCCESS(Status) ) {
|
|
|
|
//
|
|
// for now, if a directory or file doesn't have our stream, it's ok.
|
|
// we'll just copy the attributes from the source.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto setFileAttributes;
|
|
}
|
|
|
|
byteOffset.QuadPart = 0;
|
|
|
|
Status = ZwReadFile(streamHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(PCHAR) &mirrorHeader,
|
|
sizeof( mirrorHeader ),
|
|
&byteOffset,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status) ||
|
|
(IoStatusBlock.Information < sizeof( mirrorHeader ))) {
|
|
|
|
//
|
|
// oh joy, we can't read the header correctly. Let's ask the user
|
|
// if he wants to continue or abort.
|
|
//
|
|
|
|
failSetInfo:
|
|
SpStartScreen(
|
|
SP_SCRN_SYSPREP_SETACL_FAILED,
|
|
3,
|
|
HEADER_HEIGHT+1,
|
|
FALSE,
|
|
FALSE,
|
|
DEFAULT_ATTRIBUTE,
|
|
Dest
|
|
);
|
|
|
|
SpDisplayStatusOptions(
|
|
DEFAULT_STATUS_ATTRIBUTE,
|
|
SP_STAT_ESC_EQUALS_SKIP_FILE,
|
|
SP_STAT_F3_EQUALS_EXIT,
|
|
0
|
|
);
|
|
|
|
switch(SpWaitValidKey(ValidKeys,NULL,NULL)) {
|
|
|
|
case ASCI_ESC: // skip file
|
|
|
|
break;
|
|
|
|
case KEY_F3: // exit setup
|
|
|
|
SpConfirmExit();
|
|
goto failSetInfo;
|
|
}
|
|
|
|
CLEAR_CLIENT_SCREEN();
|
|
|
|
//
|
|
// we're skipping the file, delete it if it's not a directory since
|
|
// it isn't correctly formed.
|
|
//
|
|
|
|
if (destHandle) {
|
|
ZwClose( destHandle );
|
|
destHandle = NULL;
|
|
}
|
|
|
|
if ( ! Directory ) {
|
|
SpDeleteFile(Dest,NULL,NULL);
|
|
} else {
|
|
if (!RootDir) {
|
|
SpDeleteFileEx( Dest, NULL, NULL,
|
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN_FOR_BACKUP_INTENT );
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
if (mirrorHeader.StreamVersion != IMIRROR_ACL_STREAM_VERSION) {
|
|
|
|
//
|
|
// oh joy, we've hit a file we don't support.
|
|
//
|
|
goto failSetInfo;
|
|
}
|
|
|
|
haveStream = TRUE;
|
|
|
|
//
|
|
// allocate a buffer to hold the security descriptor.
|
|
//
|
|
|
|
if (mirrorHeader.SecurityDescriptorLength) {
|
|
|
|
sdBuffer = SpMemAlloc( mirrorHeader.SecurityDescriptorLength );
|
|
|
|
byteOffset.QuadPart += sizeof( mirrorHeader );
|
|
|
|
//
|
|
// now we read the security descriptor since we know how long it is.
|
|
//
|
|
|
|
Status = ZwReadFile(streamHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
sdBuffer,
|
|
mirrorHeader.SecurityDescriptorLength,
|
|
&byteOffset,
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status) ||
|
|
(IoStatusBlock.Information < mirrorHeader.SecurityDescriptorLength)) {
|
|
|
|
//
|
|
// oh joy, we can't read the SD correctly
|
|
//
|
|
goto failSetInfo;
|
|
}
|
|
|
|
haveSecurityDescriptor = TRUE;
|
|
}
|
|
setFileAttributes:
|
|
|
|
//
|
|
// we first open the source to get the file attributes and times that we're
|
|
// going to copy over to the dest.
|
|
//
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,Source);
|
|
|
|
Status = ZwCreateFile(
|
|
&sourceHandle,
|
|
FILE_READ_ATTRIBUTES,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_OPEN,
|
|
Directory ? ( FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT ) : FILE_NON_DIRECTORY_FILE,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
Status = ZwQueryInformationFile( sourceHandle,
|
|
&IoStatusBlock,
|
|
&BasicFileInfo,
|
|
sizeof(BasicFileInfo),
|
|
FileBasicInformation
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
haveSourceAttributes = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we open the target to write out the security descriptor and
|
|
// attributes.
|
|
//
|
|
|
|
if (RootDir) {
|
|
|
|
//
|
|
// append a \ to the end of the dest path
|
|
//
|
|
|
|
stringLength = 0;
|
|
while (*(Dest+stringLength) != L'\0') {
|
|
stringLength++;
|
|
}
|
|
stringLength += 2; // one for null, one for backslash
|
|
stringLength *= sizeof(WCHAR);
|
|
|
|
rootWithSlash = SpMemAlloc( stringLength );
|
|
|
|
wcscpy( rootWithSlash, Dest );
|
|
wcscat( rootWithSlash, L"\\" );
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,rootWithSlash);
|
|
|
|
} else {
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,Dest);
|
|
}
|
|
|
|
Status = ZwCreateFile(
|
|
&destHandle,
|
|
WRITE_OWNER |
|
|
WRITE_DAC |
|
|
ACCESS_SYSTEM_SECURITY |
|
|
FILE_READ_ATTRIBUTES |
|
|
FILE_WRITE_ATTRIBUTES |
|
|
FILE_READ_DATA |
|
|
FILE_WRITE_DATA,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// oh joy, we can't open the target correctly.
|
|
//
|
|
goto failSetInfo;
|
|
}
|
|
|
|
// NOTE: figure out what to do about reparse points and encrypted files
|
|
|
|
// if (mirrorHeader.ExtendedAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
|
// }
|
|
// if (mirrorHeader.ExtendedAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
|
// }
|
|
|
|
if (mirrorHeader.ExtendedAttributes & FILE_ATTRIBUTE_COMPRESSED) {
|
|
|
|
CompressionState = COMPRESSION_FORMAT_DEFAULT;
|
|
|
|
} else {
|
|
|
|
CompressionState = COMPRESSION_FORMAT_NONE;
|
|
}
|
|
|
|
try {
|
|
Status = ZwFsControlFile( destHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
FSCTL_SET_COMPRESSION,
|
|
&CompressionState,
|
|
sizeof(CompressionState),
|
|
NULL,
|
|
0
|
|
);
|
|
if (Status == STATUS_INVALID_DEVICE_REQUEST) {
|
|
|
|
//
|
|
// if this file system doesn't support compression for this
|
|
// object, we'll just ignore the error.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = STATUS_IN_PAGE_ERROR;
|
|
}
|
|
|
|
if ( NT_SUCCESS(Status) && ! haveSourceAttributes ) {
|
|
|
|
//
|
|
// if we don't have the source attributes, just grab them from the
|
|
// destination so that we have some attributes to manipulate.
|
|
//
|
|
|
|
Status = ZwQueryInformationFile( destHandle,
|
|
&IoStatusBlock,
|
|
&BasicFileInfo,
|
|
sizeof(BasicFileInfo),
|
|
FileBasicInformation
|
|
);
|
|
}
|
|
|
|
if (haveStream) {
|
|
|
|
//
|
|
// If this file has our stream, use the stream fields as the overriding
|
|
// values. They even override the source file's attributes on the server.
|
|
//
|
|
|
|
BasicFileInfo.FileAttributes = mirrorHeader.ExtendedAttributes;
|
|
BasicFileInfo.ChangeTime.QuadPart = mirrorHeader.ChangeTime.QuadPart;
|
|
}
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
if (Directory) {
|
|
|
|
BasicFileInfo.FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
|
|
|
} else if (BasicFileInfo.FileAttributes == 0) {
|
|
|
|
BasicFileInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
|
|
Status = ZwSetInformationFile( destHandle,
|
|
&IoStatusBlock,
|
|
&BasicFileInfo,
|
|
sizeof(BasicFileInfo),
|
|
FileBasicInformation
|
|
);
|
|
if (Status == STATUS_INVALID_PARAMETER && RootDir) {
|
|
|
|
//
|
|
// if this file system doesn't support setting attributes on the
|
|
// root of the volume, we'll ignore the error.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// post a warning that we couldn't set it. shouldn't be fatal.
|
|
//
|
|
|
|
SpStartScreen(
|
|
SP_SCRN_SYSPREP_COPY_FAILURE,
|
|
3,
|
|
HEADER_HEIGHT+1,
|
|
FALSE,
|
|
FALSE,
|
|
DEFAULT_ATTRIBUTE,
|
|
Status,
|
|
Dest
|
|
);
|
|
|
|
SpDisplayStatusOptions(
|
|
DEFAULT_STATUS_ATTRIBUTE,
|
|
SP_STAT_C_EQUALS_CONTINUE_SETUP,
|
|
SP_STAT_F3_EQUALS_EXIT,
|
|
0
|
|
);
|
|
|
|
switch(SpWaitValidKey(WarnKeys,NULL,MnemonicKeys)) {
|
|
|
|
case KEY_F3: // exit setup
|
|
|
|
SpConfirmExit();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (haveSecurityDescriptor) {
|
|
try {
|
|
|
|
// the state of the security descriptor is unknown, let's protect
|
|
// ourselves
|
|
|
|
Status = ZwSetSecurityObject( destHandle,
|
|
OWNER_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION |
|
|
DACL_SECURITY_INFORMATION |
|
|
SACL_SECURITY_INFORMATION,
|
|
(PSECURITY_DESCRIPTOR) sdBuffer
|
|
);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = STATUS_IN_PAGE_ERROR;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// oh joy, we can't write the SD correctly.
|
|
//
|
|
goto failSetInfo;
|
|
}
|
|
}
|
|
|
|
// endSetExtended:
|
|
if (rootWithSlash) {
|
|
SpMemFree( rootWithSlash );
|
|
}
|
|
if (fullName) {
|
|
SpMemFree( fullName );
|
|
}
|
|
if (sdBuffer) {
|
|
SpMemFree( sdBuffer );
|
|
}
|
|
if (streamHandle) {
|
|
ZwClose( streamHandle );
|
|
}
|
|
if (sourceHandle) {
|
|
ZwClose( sourceHandle );
|
|
}
|
|
if (destHandle) {
|
|
ZwClose( destHandle );
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
SpCopyEAsAndStreams (
|
|
PWCHAR SourceFile,
|
|
HANDLE SourceHandle OPTIONAL,
|
|
PWCHAR TargetFile,
|
|
HANDLE TargetHandle OPTIONAL,
|
|
BOOLEAN Directory
|
|
)
|
|
//
|
|
// This copies the EAs and streams from the source to the target. The
|
|
// source and dest handles are specified for files and optional for
|
|
// directories.
|
|
//
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
ULONG ValidKeys[4] = { ASCI_CR, ASCI_ESC, KEY_F3, 0 };
|
|
FILE_EA_INFORMATION eaInfo;
|
|
HANDLE tempSourceHandle = SourceHandle;
|
|
HANDLE tempTargetHandle = TargetHandle;
|
|
HANDLE StreamHandle;
|
|
HANDLE newStreamHandle;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING UnicodeString;
|
|
ULONG StreamInfoSize = 4096-8; // alloc a whole page. spmemalloc reserves 8 bytes
|
|
PFILE_STREAM_INFORMATION StreamInfoBase = NULL;
|
|
PFILE_STREAM_INFORMATION StreamInfo;
|
|
PUCHAR StreamBuffer = NULL;
|
|
UNICODE_STRING StreamName;
|
|
|
|
retryCopyEAs:
|
|
|
|
if (tempSourceHandle == NULL) {
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,SourceFile);
|
|
|
|
Status = ZwCreateFile(
|
|
&tempSourceHandle,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN_IF,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_FOR_BACKUP_INTENT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto EndCopyEAs;
|
|
}
|
|
}
|
|
|
|
if (tempTargetHandle == NULL) {
|
|
|
|
INIT_OBJA(&Obja,&UnicodeString,TargetFile);
|
|
|
|
Status = ZwCreateFile(
|
|
&tempTargetHandle,
|
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN_IF,
|
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_FOR_BACKUP_INTENT,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto EndCopyEAs;
|
|
}
|
|
}
|
|
|
|
//
|
|
// EAs can be on either FAT or NTFS.
|
|
//
|
|
|
|
Status = ZwQueryInformationFile( tempSourceHandle,
|
|
&IoStatusBlock,
|
|
&eaInfo,
|
|
sizeof( eaInfo ),
|
|
FileEaInformation
|
|
);
|
|
|
|
if (Status == STATUS_SUCCESS && eaInfo.EaSize > 0) {
|
|
|
|
//
|
|
// FileEaInformation, oddly enough, doesn't tell you how big a
|
|
// buffer you need to retrieve the EAs. Instead, it tells you
|
|
// how much room the EAs take up on the disk (in OS/2 format)!
|
|
// So we use the OS/2 size as a rough approximation of how large
|
|
// a buffer we need.
|
|
//
|
|
|
|
ULONG actualEaSize = eaInfo.EaSize;
|
|
PCHAR eaBuffer;
|
|
|
|
do {
|
|
actualEaSize *= 2;
|
|
eaBuffer = SpMemAlloc( actualEaSize );
|
|
|
|
Status = ZwQueryEaFile( tempSourceHandle,
|
|
&IoStatusBlock,
|
|
eaBuffer,
|
|
actualEaSize,
|
|
FALSE,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
TRUE );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SpMemFree( eaBuffer );
|
|
IoStatusBlock.Information = 0;
|
|
}
|
|
|
|
} while ( (Status == STATUS_BUFFER_OVERFLOW) ||
|
|
(Status == STATUS_BUFFER_TOO_SMALL) );
|
|
|
|
actualEaSize = (ULONG)IoStatusBlock.Information;
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
Status = ZwSetEaFile( tempTargetHandle,
|
|
&IoStatusBlock,
|
|
eaBuffer,
|
|
actualEaSize
|
|
);
|
|
}
|
|
SpMemFree( eaBuffer );
|
|
|
|
if (! NT_SUCCESS( Status )) {
|
|
goto EndCopyEAs;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Streams are only on NTFS and they're also only on files, not directories.
|
|
//
|
|
|
|
if (( RemoteSysPrepVolumeIsNtfs != TRUE ) || Directory ) {
|
|
|
|
goto EndCopyEAs;
|
|
}
|
|
|
|
do {
|
|
if (StreamInfoBase == NULL) {
|
|
StreamInfoBase = SpMemAlloc( StreamInfoSize );
|
|
}
|
|
Status = ZwQueryInformationFile(
|
|
tempSourceHandle,
|
|
&IoStatusBlock,
|
|
(PVOID) StreamInfoBase,
|
|
StreamInfoSize,
|
|
FileStreamInformation
|
|
);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SpMemFree( StreamInfoBase );
|
|
StreamInfoBase = NULL;
|
|
StreamInfoSize *= 2;
|
|
}
|
|
} while ( Status == STATUS_BUFFER_OVERFLOW ||
|
|
Status == STATUS_BUFFER_TOO_SMALL );
|
|
|
|
if ( NT_SUCCESS(Status) && IoStatusBlock.Information ) {
|
|
|
|
StreamInfo = StreamInfoBase;
|
|
|
|
for (;;) {
|
|
|
|
PWCHAR streamPtr;
|
|
USHORT remainingLength;
|
|
PWCHAR streamName;
|
|
|
|
//
|
|
// Build a string descriptor for the name of the stream.
|
|
//
|
|
|
|
StreamName.Buffer = &StreamInfo->StreamName[0];
|
|
StreamName.Length = (USHORT) StreamInfo->StreamNameLength;
|
|
StreamName.MaximumLength = StreamName.Length;
|
|
|
|
streamPtr = StreamName.Buffer;
|
|
|
|
if ((StreamName.Length > 0) && *streamPtr == L':') {
|
|
|
|
streamPtr++; // skip leading ":"
|
|
streamName = streamPtr; // remember start of stream name
|
|
remainingLength = StreamName.Length - sizeof(WCHAR);
|
|
|
|
while (remainingLength > 0 && *streamPtr != L':') {
|
|
streamPtr++;
|
|
remainingLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
if (remainingLength > 0) {
|
|
|
|
if ((remainingLength == (sizeof(L":$DATA")-sizeof(WCHAR))) &&
|
|
(RtlCompareMemory( streamPtr, L":$DATA", remainingLength )
|
|
== remainingLength)) {
|
|
|
|
//
|
|
// the attribute type on this is of type data so we
|
|
// have a data stream here. Now check that it is not
|
|
// the unnamed primary data stream and our own acl stream
|
|
// or the short file name stream.
|
|
//
|
|
if ((*streamName != L':') &&
|
|
((RtlCompareMemory(StreamName.Buffer,
|
|
IMIRROR_ACL_STREAM_NAME,
|
|
(sizeof(IMIRROR_ACL_STREAM_NAME)-sizeof(WCHAR)))
|
|
!= (sizeof(IMIRROR_ACL_STREAM_NAME)-sizeof(WCHAR))) &&
|
|
(RtlCompareMemory(StreamName.Buffer,
|
|
IMIRROR_SFN_STREAM_NAME,
|
|
(sizeof(IMIRROR_SFN_STREAM_NAME)-sizeof(WCHAR)))
|
|
!= (sizeof(IMIRROR_SFN_STREAM_NAME)-sizeof(WCHAR))))) {
|
|
//
|
|
// allocate a buffer to hold the stream data.
|
|
// Can't use TemporaryBuffer as it's used by
|
|
// SpCopyDirRecursiveCallback et al.
|
|
//
|
|
|
|
if (StreamBuffer == NULL) {
|
|
StreamBuffer = SpMemAlloc( StreamInfoSize );
|
|
}
|
|
|
|
//
|
|
// we chop off the ":DATA" suffix from the stream name
|
|
//
|
|
|
|
StreamName.Length -= remainingLength;
|
|
|
|
//
|
|
// Open the source stream.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&StreamName,
|
|
0,
|
|
tempSourceHandle,
|
|
NULL
|
|
);
|
|
Status = ZwCreateFile(
|
|
&StreamHandle,
|
|
GENERIC_READ | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_OPEN,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
if ( ! NT_SUCCESS(Status) ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Open the source stream.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&StreamName,
|
|
0,
|
|
tempTargetHandle,
|
|
NULL
|
|
);
|
|
Status = ZwCreateFile(
|
|
&newStreamHandle,
|
|
GENERIC_WRITE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ,
|
|
FILE_CREATE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0
|
|
);
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
LARGE_INTEGER byteOffset;
|
|
ULONG bytesRead;
|
|
|
|
byteOffset.QuadPart = 0;
|
|
|
|
while (NT_SUCCESS(Status)) {
|
|
|
|
Status = ZwReadFile(StreamHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
StreamBuffer,
|
|
StreamInfoSize,
|
|
&byteOffset,
|
|
NULL
|
|
);
|
|
|
|
if ( ! NT_SUCCESS(Status) ) {
|
|
|
|
if (Status == STATUS_END_OF_FILE) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
bytesRead = (ULONG)IoStatusBlock.Information;
|
|
try {
|
|
Status = ZwWriteFile(newStreamHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
StreamBuffer,
|
|
bytesRead,
|
|
&byteOffset,
|
|
NULL
|
|
);
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = STATUS_IN_PAGE_ERROR;
|
|
}
|
|
byteOffset.QuadPart += bytesRead;
|
|
}
|
|
ZwClose(newStreamHandle);
|
|
}
|
|
ZwClose(StreamHandle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS(Status) && StreamInfo->NextEntryOffset ) {
|
|
StreamInfo = (PFILE_STREAM_INFORMATION)((PCHAR) StreamInfo + StreamInfo->NextEntryOffset);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
EndCopyEAs:
|
|
|
|
if (tempSourceHandle != NULL && SourceHandle == NULL) {
|
|
ZwClose( tempSourceHandle );
|
|
}
|
|
if (tempTargetHandle != NULL && TargetHandle == NULL) {
|
|
ZwClose( tempTargetHandle );
|
|
}
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// this failed. let's ask the user if he wants to retry, skip, or
|
|
// abort.
|
|
//
|
|
repaint:
|
|
SpStartScreen(
|
|
SP_SCRN_COPY_FAILED,
|
|
3,
|
|
HEADER_HEIGHT+1,
|
|
FALSE,
|
|
FALSE,
|
|
DEFAULT_ATTRIBUTE,
|
|
TargetFile
|
|
);
|
|
|
|
SpDisplayStatusOptions(
|
|
DEFAULT_STATUS_ATTRIBUTE,
|
|
SP_STAT_ENTER_EQUALS_RETRY,
|
|
SP_STAT_ESC_EQUALS_SKIP_FILE,
|
|
SP_STAT_F3_EQUALS_EXIT,
|
|
0
|
|
);
|
|
|
|
switch(SpWaitValidKey(ValidKeys,NULL,NULL)) {
|
|
|
|
case ASCI_CR: // retry
|
|
|
|
SpCopyFilesScreenRepaint(SourceFile,TargetFile,TRUE);
|
|
goto retryCopyEAs;
|
|
|
|
case ASCI_ESC: // skip file
|
|
|
|
break;
|
|
|
|
case KEY_F3: // exit setup
|
|
|
|
SpConfirmExit();
|
|
goto repaint;
|
|
}
|
|
|
|
//
|
|
// we're skipping the file, delete it if it's not a directory since
|
|
// it isn't correctly formed.
|
|
//
|
|
|
|
if ( ! Directory ) {
|
|
SpDeleteFile(TargetFile,NULL,NULL);
|
|
}
|
|
|
|
//
|
|
// Need to completely repaint gauge, etc.
|
|
//
|
|
SpCopyFilesScreenRepaint(SourceFile,TargetFile,TRUE);
|
|
}
|
|
if (StreamInfoBase != NULL) {
|
|
SpMemFree(StreamInfoBase);
|
|
}
|
|
if (StreamBuffer != NULL) {
|
|
SpMemFree(StreamBuffer);
|
|
}
|
|
return Status;
|
|
}
|