Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4639 lines
135 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;
#ifdef _X86_
if (volumeInfo->PartitionActive) {
if (volumeInfo->IsBootDisk) {
//
// On an 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
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)TemporaryBuffer)->DataLength)) &&
(_wcsnicmp(L"{4D36E967-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, ((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength)) &&
(_wcsnicmp(L"{4D36E96A-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, ((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength)) &&
(_wcsnicmp(L"{4D36E96B-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, ((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength)) &&
(_wcsnicmp(L"{4D36E96F-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, ((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength)) &&
(_wcsnicmp(L"{4D36E97B-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, ((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength)) &&
(_wcsnicmp(L"{4D36E97D-E325-11CE-BFC1-08002BE10318}", (PWSTR)((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data, ((PKEY_VALUE_PARTIAL_INFORMATION)TemporaryBuffer)->DataLength)) ) {
// 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 type 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);
wcscpy(Path2, TemporaryBuffer);
//
// 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.
//
// 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.
//
#ifdef _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
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;
}