Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1330 lines
45 KiB

/*++
Copyright (c) Microsoft Corporation
Module Name:
spasmcabs.c
Abstract:
cab extraction in textmode setup
Author:
Jay Krell (JayKrell) May 2002
Revision History:
Jay Krell (JayKrell) June 2002
tested and cleanup error handling
general ui work:
put ui retry/skip/cancel ui upon errors
put leaf file name in progress
do not put directory names in progress
--*/
/*
[asmcabs]
asms01.cab = 1,124
asms02.cab = 1,124
urt1.cab = 1,1
urtabc.cab = 1,1
...
The first number is from [SourceDisksNames].
The second number is from [WinntDirectories].
The first number is generally 1 for \i386, \ia64, etc., but
55 for \i386 on Win64 is also expected.
The second number is generally either 1 for \windows or 124 for \windows\winsxs.
*/
#include "spprecmp.h"
#include "fdi.h"
#include "fcntl.h"
#include "crt/sys/stat.h"
#include <stdarg.h>
#include "ntrtlstringandbuffer.h"
#include "ntrtlpath.h"
#define SP_ASM_CABS_PRIVATE
#include "spasmcabs.h"
typedef struct _SP_ASMS_ERROR_INFORMATION {
BOOLEAN Success;
ERF FdiError;
NTSTATUS NtStatus;
RTL_UNICODE_STRING_BUFFER ErrorCabLeafFileName; // "asms01.cab"
RTL_UNICODE_STRING_BUFFER ErrorNtFilePath;
} SP_ASMS_ERROR_INFORMATION, *PSP_ASMS_ERROR_INFORMATION;
typedef const SP_ASMS_ERROR_INFORMATION *PCSP_ASMS_ERROR_INFORMATION;
VOID
SpAsmsInitErrorInfo(
PSP_ASMS_ERROR_INFORMATION ErrorInfo
)
{
RtlZeroMemory(ErrorInfo, sizeof(*ErrorInfo));
ASSERT(ErrorInfo->Success == FALSE);
ASSERT(ErrorInfo->FdiError.fError == FALSE);
RtlInitUnicodeStringBuffer(&ErrorInfo->ErrorCabLeafFileName, NULL, 0);
RtlInitUnicodeStringBuffer(&ErrorInfo->ErrorNtFilePath, NULL, 0);
}
VOID
SpAsmsFreeErrorInfo(
PSP_ASMS_ERROR_INFORMATION ErrorInfo
)
{
RtlFreeUnicodeStringBuffer(&ErrorInfo->ErrorCabLeafFileName);
RtlFreeUnicodeStringBuffer(&ErrorInfo->ErrorNtFilePath);
}
NTSTATUS
SpAsmsCabsTranslateFdiErrorToNtStatus(
int erfOper
)
//
// based on base\pnp\setupapi\diamond.c
//
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
//
// There is ERROR_INVALID_DATA used by setupapi, but no STATUS_INVALID_DATA.
//
const NTSTATUS STATUS_INVALID_DATA = STATUS_INVALID_PARAMETER;
const NTSTATUS STATUS_FILE_NOT_FOUND = STATUS_OBJECT_NAME_NOT_FOUND;
const NTSTATUS STATUS_NOT_ENOUGH_MEMORY = STATUS_NO_MEMORY;
switch(erfOper) {
case FDIERROR_NONE:
//
// We shouldn't see this -- if there was no error
// then FDICopy should have returned TRUE.
//
ASSERT(erfOper != FDIERROR_NONE);
NtStatus = STATUS_INVALID_DATA;
break;
case FDIERROR_CABINET_NOT_FOUND:
NtStatus = STATUS_FILE_NOT_FOUND;
break;
case FDIERROR_CORRUPT_CABINET:
NtStatus = STATUS_INVALID_DATA;
break;
case FDIERROR_ALLOC_FAIL:
NtStatus = STATUS_NOT_ENOUGH_MEMORY;
break;
case FDIERROR_TARGET_FILE:
case FDIERROR_USER_ABORT:
NtStatus = STATUS_INTERNAL_ERROR;
break;
case FDIERROR_NOT_A_CABINET:
case FDIERROR_UNKNOWN_CABINET_VERSION:
case FDIERROR_BAD_COMPR_TYPE:
case FDIERROR_MDI_FAIL:
case FDIERROR_RESERVE_MISMATCH:
case FDIERROR_WRONG_CABINET:
default:
//
// Cabinet is corrupt or not actually a cabinet, etc.
//
NtStatus = STATUS_INVALID_DATA;
break;
}
return NtStatus;
}
//
// These must match ntos\ex\pool.c
// We also free strings via RtlFreeUnicodeString which calls RtlFreeStringRoutine->ExFreePool.
//
PVOID
SpAllocateString(
IN SIZE_T NumberOfBytes
)
{
return ExAllocatePoolWithTag(PagedPool,NumberOfBytes,'grtS');
}
const PRTL_ALLOCATE_STRING_ROUTINE RtlAllocateStringRoutine = SpAllocateString;
const PRTL_FREE_STRING_ROUTINE RtlFreeStringRoutine = ExFreePool;
#if DBG
BOOLEAN SpAsmCabs_BreakOnError; // per function bool not doable
#define SP_ASMS_CAB_CALLBACK_EPILOG() \
do { if (CabResult == -1) { \
DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: %s: failed with status %lx\n", __FUNCTION__, NtStatus); \
DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: %s: ?? setupdd!SpAsmCabs_BreakOnError=1 to break\n", __FUNCTION__); \
if (SpAsmCabs_BreakOnError) { \
DbgBreakPoint(); \
} \
} } while(0)
#else
#define SP_ASMS_CAB_CALLBACK_EPILOG() /* nothing */
#endif
typedef struct _SP_EXTRACT_ASMCABS_GLOBAL_CONTEXT {
HANDLE FdiHandle;
PSP_ASMS_ERROR_INFORMATION ErrorInfo;
//
// These are shared by FdiCopyCallback and OpenFileForReadCallback.
// OpenFileForRead doesn't have a context parameter.
//
RTL_UNICODE_STRING_BUFFER UnicodeStringBuffer1;
RTL_UNICODE_STRING_BUFFER UnicodeStringBuffer2;
PVOID FileOpenUiCallbackContext OPTIONAL;
PSP_ASMCABS_FILE_OPEN_UI_CALLBACK FileOpenUiCallback;
} SP_EXTRACT_ASMCABS_GLOBAL_CONTEXT, *PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT;
typedef const SP_EXTRACT_ASMCABS_GLOBAL_CONTEXT *PCSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT;
PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT SpAsmCabsGlobalContext;
typedef struct _SP_EXTRACT_ASMCABS_FDICOPY_CONTEXT {
PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext;
//
// The paths in the cab are relative to this directory.
// The paths in the cab are merely appended to this path,
// with a slash between the two parts.
//
UNICODE_STRING DestinationRootDirectory; // "\Device\Harddisk0\Partition3\WINDOWS\WinSxS"
//
// LastDirectoryCreated is intended to reduce calls to "CreateDirectory".
// For every while we extract, we create all the directories in the path,
// but before we do that, we compare the directory of the file to the
// directory of the immediately previously extracted file. If they match,
// then we do not bother creating the directories again.
// (we are in a secure single threaded environment, the directories cannot
// disappear out from under us; if this were not the case, we would also
// hold open a handle to the directory -- not a bad perf optimization besides.)
//
RTL_UNICODE_STRING_BUFFER LastDirectoryCreated; // "\Device\Harddisk0\Partition3\WINDOWS\WinSxS\IA64_Microsoft.Windows.Common-Controls_6595b64144ccf1df_5.82.0.0_x-ww_B9C4A0A5"
} SP_EXTRACT_ASMCABS_FDICOPY_CONTEXT, *PSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT;
typedef const SP_EXTRACT_ASMCABS_FDICOPY_CONTEXT *PCSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT;
typedef struct _SP_EXTRACT_ASMCABS_FILE_CONTEXT {
//
// The "real" underlying NT kernel file handle, as you'd expect.
//
HANDLE NtFileHandle;
//
// We use this information to more closely emulate the behavior of
// diamond.c, which does its own pinning of seeks within the size
// of the file. Diamond.c uses memory mapped i/o. Perhaps we should too.
//
LARGE_INTEGER FileSize;
LARGE_INTEGER FileOffset;
//
// Like diamond.c, we try to set the filetime when we close a file,
// but we ignore errors, like diamond.c
//
LARGE_INTEGER FileTime;
//
// The path we used to open the file, for debugging and diagnostic
// purposes. Frequently asked question -- how do I get the path of
// an opened file? Answer -- you store it yourself when you open it.
//
RTL_UNICODE_STRING_BUFFER FilePath;
} SP_EXTRACT_ASMCABS_FILE_CONTEXT, *PSP_EXTRACT_ASMCABS_FILE_CONTEXT;
typedef const SP_EXTRACT_ASMCABS_FILE_CONTEXT *PCSP_EXTRACT_ASMCABS_FILE_CONTEXT;
NTSTATUS
SpAppendNtPathElement(
PRTL_UNICODE_STRING_BUFFER Path,
PCUNICODE_STRING Element
)
{
//
// RtlJoinMultiplePathPieces would be handy.
// ("piece" is proposed terminology for "one or more elements")
//
return RtlAppendPathElement(
RTL_APPEND_PATH_ELEMENT_ONLY_BACKSLASH_IS_SEPERATOR,
Path,
Element
);
}
PVOID
DIAMONDAPI
SpAsmCabsMemAllocCallback(
IN ULONG Size
)
{
return SpMemAlloc(Size);
}
VOID
DIAMONDAPI
SpAsmCabsMemFreeCallback(
IN PVOID Memory
)
{
if (Memory != NULL)
SpMemFree(Memory);
}
UINT
DIAMONDAPI
SpAsmCabsReadFileCallback(
IN INT_PTR Handle,
OUT PVOID pv,
IN UINT ByteCount
)
{
//
// diamond.c uses memory mapped i/o for reading, perhaps we should too.
//
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
UINT CabResult = (UINT)-1; // assume failure
PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)(PVOID)Handle;
LONG RealByteCount;
//
// pin the read to within the file like diamond.c does.
//
RealByteCount = (LONG)ByteCount;
if((MyFileHandle->FileOffset.QuadPart + RealByteCount) > MyFileHandle->FileSize.QuadPart) {
RealByteCount = (LONG)(MyFileHandle->FileSize.QuadPart - MyFileHandle->FileOffset.QuadPart);
}
if(RealByteCount < 0) {
RealByteCount = 0;
}
NtStatus = ZwReadFile(
MyFileHandle->NtFileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
pv,
RealByteCount,
&MyFileHandle->FileOffset,
NULL
);
if(NT_SUCCESS(NtStatus)) {
MyFileHandle->FileOffset.QuadPart += RealByteCount;
CabResult = RealByteCount;
} else {
#if DBG
DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: %s: Status %lx reading source target file\n", __FUNCTION__, NtStatus);
#endif
}
return CabResult;
}
UINT
DIAMONDAPI
SpAsmCabsWriteFileCallback(
IN INT_PTR Handle,
IN PVOID pv,
IN UINT ByteCount
)
{
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
UINT CabResult = (UINT)-1; // assume failure
PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)(PVOID)Handle;
const PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext = SpAsmCabsGlobalContext;
ASSERT(GlobalContext != NULL);
ASSERT(MyFileHandle != NULL);
NtStatus = ZwWriteFile(
MyFileHandle->NtFileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
pv,
ByteCount,
&MyFileHandle->FileOffset,
NULL
);
if(NT_SUCCESS(NtStatus)) {
MyFileHandle->FileOffset.QuadPart += ByteCount;
if (MyFileHandle->FileOffset.QuadPart > MyFileHandle->FileSize.QuadPart) {
MyFileHandle->FileSize = MyFileHandle->FileOffset;
}
CabResult = ByteCount;
} else {
const PUNICODE_STRING UnicodeString = &MyFileHandle->FilePath.String;
#if DBG
DbgPrintEx(DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: %s: Status %lx writing to target file %wZ\n", __FUNCTION__, NtStatus, UnicodeString);
#endif
if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&GlobalContext->ErrorInfo->ErrorNtFilePath, UnicodeString))) {
GlobalContext->ErrorInfo->ErrorNtFilePath.String.Length = 0;
}
}
return CabResult;
}
LONG
DIAMONDAPI
SpAsmCabsSeekFileCallback(
IN INT_PTR Handle,
IN long Distance32,
IN int SeekType
)
{
FILE_POSITION_INFORMATION CurrentPosition;
LARGE_INTEGER Distance;
PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)(PVOID)Handle;
LONG CabResult = -1; // assume failure
HANDLE NtFileHandle = MyFileHandle->NtFileHandle;
Distance.QuadPart = Distance32;
switch(SeekType) {
case SEEK_CUR:
CurrentPosition.CurrentByteOffset.QuadPart =
(MyFileHandle->FileOffset.QuadPart + Distance.QuadPart);
break;
case SEEK_END:
CurrentPosition.CurrentByteOffset.QuadPart =
(MyFileHandle->FileSize.QuadPart - Distance.QuadPart);
break;
case SEEK_SET:
CurrentPosition.CurrentByteOffset = Distance;
break;
}
//
// pin the seek to within the file like diamond.c does.
//
if(CurrentPosition.CurrentByteOffset.QuadPart < 0) {
CurrentPosition.CurrentByteOffset.QuadPart = 0;
}
if(CurrentPosition.CurrentByteOffset.QuadPart > MyFileHandle->FileSize.QuadPart) {
CurrentPosition.CurrentByteOffset = MyFileHandle->FileSize;
}
/* We don't need to do this since we specify the offset in the ReadFile/WriteFile calls.
{
IO_STATUS_BLOCK IoStatusBlock;
NtStatus = ZwSetInformationFile(
NtFileHandle,
&IoStatusBlock,
&CurrentPosition,
sizeof(CurrentPosition),
FilePositionInformation
);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
}
*/
MyFileHandle->FileOffset = CurrentPosition.CurrentByteOffset;
ASSERT(CurrentPosition.CurrentByteOffset.HighPart == 0);
CabResult = (LONG)CurrentPosition.CurrentByteOffset.QuadPart;
return CabResult;
}
INT_PTR
DIAMONDAPI
SpAsmCabsOpenFileForReadCallbackA(
IN PSTR FileName,
IN int oflag,
IN int pmode
)
{
ANSI_STRING AnsiString;
INT_PTR CabResult = -1; // assume failure
PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = NULL;
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
FILE_STANDARD_INFORMATION StandardInfo;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatusBlock;
PUNICODE_STRING ErrorNtFilePath = NULL;
const PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext = SpAsmCabsGlobalContext;
ASSERT(GlobalContext != NULL);
NtStatus = RtlInitAnsiStringEx(&AnsiString, FileName);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlEnsureUnicodeStringBufferSizeChars(&GlobalContext->UnicodeStringBuffer1, RTL_STRING_GET_LENGTH_CHARS(&AnsiString) + 1);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlAnsiStringToUnicodeString(&GlobalContext->UnicodeStringBuffer1.String, &AnsiString, FALSE);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = SpAsmCabsNewFile(&MyFileHandle);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
InitializeObjectAttributes(&Obja, &GlobalContext->UnicodeStringBuffer1.String, OBJ_CASE_INSENSITIVE, NULL, NULL);
RTL_STRING_NUL_TERMINATE(Obja.ObjectName);
NtStatus = ZwCreateFile(
&MyFileHandle->NtFileHandle,
FILE_GENERIC_READ,
&Obja,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (!NT_SUCCESS(NtStatus)) {
ErrorNtFilePath = Obja.ObjectName;
goto Exit;
}
//
// We don't want ui feedback for the .cab files here.
//
#if 0
if (SpAsmCabsGlobalContext->FileOpenUiCallback != NULL) {
(*SpAsmCabsGlobalContext->FileOpenUiCallback)(SpAsmCabsGlobalContext->FileOpenUiCallbackContext, Obja.ObjectName->Buffer);
}
#endif
NtStatus = ZwQueryInformationFile(
MyFileHandle->NtFileHandle,
&IoStatusBlock,
&StandardInfo,
sizeof(StandardInfo),
FileStandardInformation
);
if (!NT_SUCCESS(NtStatus)) {
ErrorNtFilePath = Obja.ObjectName;
goto Exit;
}
// ok if this fails
if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&MyFileHandle->FilePath, Obja.ObjectName))) {
MyFileHandle->FilePath.String.Length = 0;
}
MyFileHandle->FileSize = StandardInfo.EndOfFile;
CabResult = (INT_PTR)MyFileHandle;
MyFileHandle = NULL;
Exit:
if (!NT_SUCCESS(NtStatus)) {
GlobalContext->ErrorInfo->NtStatus = NtStatus;
if (ErrorNtFilePath != NULL) {
if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&GlobalContext->ErrorInfo->ErrorNtFilePath, ErrorNtFilePath))) {
GlobalContext->ErrorInfo->ErrorNtFilePath.String.Length = 0;
}
}
}
SpAsmCabsCloseFile(MyFileHandle);
SP_ASMS_CAB_CALLBACK_EPILOG();
return CabResult;
}
NTSTATUS
SpAsmCabsNewFile(
PSP_EXTRACT_ASMCABS_FILE_CONTEXT * MyFileHandle
)
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
ASSERT(MyFileHandle != NULL);
ASSERT(*MyFileHandle == NULL);
*MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)SpMemAlloc(sizeof(**MyFileHandle));
if (*MyFileHandle == NULL) {
NtStatus = STATUS_NO_MEMORY;
goto Exit;
}
RtlZeroMemory(*MyFileHandle, sizeof(**MyFileHandle));
RtlInitUnicodeStringBuffer(&(*MyFileHandle)->FilePath, NULL, 0);
NtStatus = STATUS_SUCCESS;
Exit:
return NtStatus;
}
VOID
SpAsmCabsCloseFile(
PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle
)
{
if (MyFileHandle != NULL
&& MyFileHandle != (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)INVALID_HANDLE_VALUE) {
HANDLE NtFileHandle = MyFileHandle->NtFileHandle;
if (NtFileHandle != NULL
&& NtFileHandle != INVALID_HANDLE_VALUE) {
MyFileHandle->NtFileHandle = NULL;
ZwClose(NtFileHandle);
}
SpMemFree(MyFileHandle);
}
}
int
DIAMONDAPI
SpAsmCabsCloseFileCallback(
IN INT_PTR Handle
)
{
SpAsmCabsCloseFile((PSP_EXTRACT_ASMCABS_FILE_CONTEXT)Handle);
return 0; // success
}
NTSTATUS
SpSplitFullPathAtDevice(
PCUNICODE_STRING FullPath,
PUNICODE_STRING Device,
PUNICODE_STRING Rest
)
//
// skip four slashes like SpCreateDirectoryForFileA.
// \device\harddiskn\partitionm\
//
{
SIZE_T i = 0;
SIZE_T j = 0;
SIZE_T Length = RTL_STRING_GET_LENGTH_CHARS(FullPath);
const PWSTR Buffer = FullPath->Buffer;
for (i = 0 ; i != 4 ; ++i )
{
for ( ; j != Length ; ++j )
{
if (Buffer[j] == '\\')
{
++j;
break;
}
}
}
ASSERT(j >= 4);
Device->Buffer = Buffer;
RTL_STRING_SET_LENGTH_CHARS_UNSAFE(Device, j - 1);
Rest->Buffer = Buffer + j;
RTL_STRING_SET_LENGTH_CHARS_UNSAFE(Rest, Length - j);
return STATUS_SUCCESS;
}
INT_PTR
DIAMONDAPI
SpExtractAsmCabsFdiCopyCallback(
IN FDINOTIFICATIONTYPE Operation,
IN PFDINOTIFICATION Parameters
)
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
INT_PTR CabResult = -1; // assume failure
PSP_EXTRACT_ASMCABS_FILE_CONTEXT MyFileHandle = NULL;
const PSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT FdiCopyContext = (PSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT)Parameters->pv;
const PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext = FdiCopyContext->GlobalContext;
IO_STATUS_BLOCK IoStatusBlock;
PUNICODE_STRING ErrorNtFilePath = NULL;
switch (Operation)
{
case fdintCOPY_FILE:
{
ANSI_STRING AnsiString;
OBJECT_ATTRIBUTES Obja;
UNICODE_STRING Directory;
NtStatus = RtlInitAnsiStringEx(&AnsiString, Parameters->psz1);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlEnsureUnicodeStringBufferSizeChars(&GlobalContext->UnicodeStringBuffer1, RTL_STRING_GET_LENGTH_CHARS(&AnsiString) + 1);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlAnsiStringToUnicodeString(&GlobalContext->UnicodeStringBuffer1.String, &AnsiString, FALSE);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlAssignUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer2, &FdiCopyContext->DestinationRootDirectory);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = SpAppendNtPathElement(&GlobalContext->UnicodeStringBuffer2, &GlobalContext->UnicodeStringBuffer1.String);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
InitializeObjectAttributes(
&Obja,
&GlobalContext->UnicodeStringBuffer2.String,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
ErrorNtFilePath = Obja.ObjectName;
NtStatus = SpDeleteFileOrEmptyDirectory(0, Obja.ObjectName);
if (NtStatus == STATUS_OBJECT_PATH_NOT_FOUND
|| NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
NtStatus = STATUS_SUCCESS;
}
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
Directory = *Obja.ObjectName;
NtStatus = RtlRemoveLastNtPathElement(0, &Directory);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
//
// remove last character if it is a backslash
//
while (Directory.Length != 0 && RTL_STRING_GET_LAST_CHAR(&Directory) == '\\') {
Directory.Length -= sizeof(Directory.Buffer[0]);
Directory.MaximumLength -= sizeof(Directory.Buffer[0]);
}
if (!RtlEqualUnicodeString(&Directory, &FdiCopyContext->LastDirectoryCreated.String, TRUE)) {
//
// oops...need it split up for the setup utility function actually..
//
UNICODE_STRING DirectoryDevice;
UNICODE_STRING DirectoryTail;
NtStatus = SpSplitFullPathAtDevice(&Directory, &DirectoryDevice, &DirectoryTail);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus =
SpCreateDirectory_Ustr(
&DirectoryDevice,
NULL,
&DirectoryTail,
0, // DirAttrs
CREATE_DIRECTORY_FLAG_NO_STATUS_TEXT_UI
);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlAssignUnicodeStringBuffer(&FdiCopyContext->LastDirectoryCreated, &Directory);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
}
NtStatus = SpAsmCabsNewFile(&MyFileHandle);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = ZwCreateFile(
&MyFileHandle->NtFileHandle,
FILE_GENERIC_WRITE,
&Obja,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
0, // no sharing
FILE_OVERWRITE_IF, // allow overwrite
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
ErrorNtFilePath = NULL;
if (SpAsmCabsGlobalContext->FileOpenUiCallback != NULL) {
(*SpAsmCabsGlobalContext->FileOpenUiCallback)(SpAsmCabsGlobalContext->FileOpenUiCallbackContext, Obja.ObjectName->Buffer);
}
// ok if this fails
if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&MyFileHandle->FilePath, Obja.ObjectName))) {
MyFileHandle->FilePath.String.Length = 0;
}
//
// attribs, date, and time are all available in
// fdintCLOSE_FILE_INFO, but diamond.c keeps them around
// from when the open is done.
//
MyFileHandle->FileSize.QuadPart = Parameters->cb;
SpTimeFromDosTime(
Parameters->date,
Parameters->time,
&MyFileHandle->FileTime);
CabResult = (INT_PTR)MyFileHandle;
MyFileHandle = NULL;
}
break;
case fdintCLOSE_FILE_INFO:
{
FILE_BASIC_INFORMATION FileBasicDetails;
//
// Try to set file's last-modifed time, but ignore
// errors like diamond.c does.
//
MyFileHandle = (PSP_EXTRACT_ASMCABS_FILE_CONTEXT)Parameters->hf;
ASSERT(MyFileHandle != NULL);
NtStatus = ZwQueryInformationFile(
MyFileHandle->NtFileHandle,
&IoStatusBlock,
&FileBasicDetails,
sizeof(FileBasicDetails),
FileBasicInformation );
if (NT_SUCCESS(NtStatus)) {
FileBasicDetails.LastWriteTime = MyFileHandle->FileTime;
ZwSetInformationFile(
MyFileHandle->NtFileHandle,
&IoStatusBlock,
&FileBasicDetails,
sizeof(FileBasicDetails),
FileBasicInformation);
}
SpAsmCabsCloseFile(MyFileHandle);
MyFileHandle = NULL;
CabResult = TRUE; // keep FDI going
}
break;
default:
CabResult = 0;
break;
}
NtStatus = STATUS_SUCCESS;
Exit:
if (!NT_SUCCESS(NtStatus)) {
GlobalContext->ErrorInfo->NtStatus = NtStatus;
if (ErrorNtFilePath != NULL) {
if (!NT_SUCCESS(RtlAssignUnicodeStringBuffer(&GlobalContext->ErrorInfo->ErrorNtFilePath, ErrorNtFilePath))) {
GlobalContext->ErrorInfo->ErrorNtFilePath.String.Length = 0;
}
}
}
SpAsmCabsCloseFile(MyFileHandle);
SP_ASMS_CAB_CALLBACK_EPILOG();
return CabResult;
}
NTSTATUS
SpExtractAssemblyCabinetsInternalNoRetryOrUi(
HANDLE SifHandle,
IN PCWSTR SourceDevicePath, // \device\harddisk0\partition2
IN PCWSTR DirectoryOnSourceDevice, // \$win_nt$.~ls
IN PCWSTR SysrootDevice, // \Device\Harddisk0\Partition2
IN PCWSTR Sysroot, // \WINDOWS.2
PSP_ASMS_ERROR_INFORMATION ErrorInfo,
PSP_ASMCABS_FILE_OPEN_UI_CALLBACK FileOpenUiCallback OPTIONAL,
PVOID FileOpenUiCallbackContext OPTIONAL
)
{
const static WCHAR ConstSectionName[] = L"asmcabs";
const PWSTR SectionName = (PWSTR)ConstSectionName;
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
ULONG LineIndex = 0;
ULONG LineCount = 0;
ULONG LineNumber = 0;
SP_EXTRACT_ASMCABS_GLOBAL_CONTEXT xGlobalContext;
const PSP_EXTRACT_ASMCABS_GLOBAL_CONTEXT GlobalContext = &xGlobalContext;
SP_EXTRACT_ASMCABS_FDICOPY_CONTEXT xFdiCopyContext;
const PSP_EXTRACT_ASMCABS_FDICOPY_CONTEXT FdiCopyContext = &xFdiCopyContext;
BOOL FdiCopyResult = FALSE;
UNICODE_STRING SysrootDeviceString; // \device\harddisk\partition
UNICODE_STRING SysrootString; // \windows
PWSTR CabFileName = NULL; // asms02.cab
UNICODE_STRING CabFileNameString = { 0 }; // asms02.cab
RTL_ANSI_STRING_BUFFER CabFileNameBufferA; // asms02.cab
PWSTR CabMediaShortName = NULL; // "1", "2", etc.
PWSTR CabSetupRelativeDirectory = NULL; // \ia64
UNICODE_STRING CabSetupRelativeDirectoryString; // \ia64
RTL_UNICODE_STRING_BUFFER CabDirectoryBuffer; // \device\harddisk\partition\$win_nt$.~ls\ia64
RTL_ANSI_STRING_BUFFER CabDirectoryBufferA; // \device\harddisk\partition\$win_nt$.~ls\ia64
UNICODE_STRING SourceDevicePathString; // \device\harddisk\partition
UNICODE_STRING DirectoryOnSourceDeviceString; // \$win_nt$.~ls
PWSTR DestinationDirectoryNumber = NULL;
PWSTR RelativeDestinationDirectory = NULL;
UNICODE_STRING RelativeDestinationDirectoryString;
RTL_UNICODE_STRING_BUFFER DestinationDirectoryBuffer;
if (!RTL_VERIFY(SourceDevicePath != NULL)
|| !RTL_VERIFY(DirectoryOnSourceDevice != NULL)
|| !RTL_VERIFY(SysrootDevice != NULL)
|| !RTL_VERIFY(ErrorInfo != NULL)
|| !RTL_VERIFY(Sysroot != NULL)) {
return STATUS_INVALID_PARAMETER;
}
ErrorInfo->FdiError.fError = FALSE;
ErrorInfo->Success = FALSE;
ErrorInfo->NtStatus = STATUS_SUCCESS;
SpAsmCabsGlobalContext = GlobalContext;
RtlZeroMemory(GlobalContext, sizeof(*GlobalContext));
RtlZeroMemory(FdiCopyContext, sizeof(*FdiCopyContext));
FdiCopyContext->GlobalContext = GlobalContext;
GlobalContext->ErrorInfo = ErrorInfo;
GlobalContext->FileOpenUiCallback = FileOpenUiCallback;
GlobalContext->FileOpenUiCallbackContext = FileOpenUiCallbackContext;
RtlInitUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer1, NULL, 0);
RtlInitUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer2, NULL, 0);
RtlInitUnicodeStringBuffer(&FdiCopyContext->LastDirectoryCreated, NULL, 0);
RtlInitUnicodeStringBuffer(&CabDirectoryBuffer, NULL, 0);
RtlInitUnicodeStringBuffer(&DestinationDirectoryBuffer, NULL, 0);
RtlInitAnsiStringBuffer(&CabFileNameBufferA, NULL, 0);
RtlInitAnsiStringBuffer(&CabDirectoryBufferA, NULL, 0);
NtStatus = RtlInitUnicodeStringEx(&SourceDevicePathString, SourceDevicePath);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlInitUnicodeStringEx(&DirectoryOnSourceDeviceString, DirectoryOnSourceDevice);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlInitUnicodeStringEx(&SysrootDeviceString, SysrootDevice);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlInitUnicodeStringEx(&SysrootString, Sysroot);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
LineCount = SpCountLinesInSection(SifHandle, SectionName);
if(LineCount == 0) {
// optional for now
//SpFatalSifError(SifHandle, SectionName,NULL,0,0);
goto Success;
}
GlobalContext->FdiHandle =
FDICreate(
SpAsmCabsMemAllocCallback,
SpAsmCabsMemFreeCallback,
SpAsmCabsOpenFileForReadCallbackA,
SpAsmCabsReadFileCallback,
SpAsmCabsWriteFileCallback,
SpAsmCabsCloseFileCallback,
SpAsmCabsSeekFileCallback,
cpuUNKNOWN, // ignored
&ErrorInfo->FdiError
);
if (GlobalContext->FdiHandle == NULL) {
goto FdiError;
}
for ( LineNumber = 0 ; LineNumber != LineCount ; ++LineNumber ) {
//
// get the filename
//
CabFileName = SpGetKeyName(SifHandle, SectionName, LineNumber);
if (CabFileName == NULL) {
SpFatalSifError(SifHandle, SectionName, NULL, LineNumber, 0);
goto Exit;
}
if (FileOpenUiCallback != NULL) {
(*FileOpenUiCallback)(FileOpenUiCallbackContext, CabFileName);
}
NtStatus = RtlInitUnicodeStringEx(&CabFileNameString, CabFileName);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlAssignAnsiStringBufferFromUnicode(&CabFileNameBufferA, CabFileName);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
RTL_STRING_NUL_TERMINATE(&CabFileNameBufferA.String);
//
// get the source directory information, prompt for media, etc.
//
CabMediaShortName = SpGetSectionLineIndex(SifHandle, SectionName, LineNumber, 0);
if (CabMediaShortName == NULL) {
SpFatalSifError(SifHandle, SectionName, CabFileName, LineNumber, 0);
goto Exit;
}
SpPromptForSetupMedia(SifHandle, CabMediaShortName, SourceDevicePathString.Buffer);
SpGetSourceMediaInfo(SifHandle, CabMediaShortName, NULL, NULL, &CabSetupRelativeDirectory);
if (CabSetupRelativeDirectory == NULL) {
SpFatalSifError(SifHandle, SectionName, CabFileName, LineNumber, 0);
goto Exit;
}
NtStatus = RtlInitUnicodeStringEx(&CabSetupRelativeDirectoryString, CabSetupRelativeDirectory);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlEnsureUnicodeStringBufferSizeChars(
&CabDirectoryBuffer,
RTL_STRING_GET_LENGTH_CHARS(&SourceDevicePathString)
+ 1 // slash
+ RTL_STRING_GET_LENGTH_CHARS(&DirectoryOnSourceDeviceString)
+ 1 // slash
+ RTL_STRING_GET_LENGTH_CHARS(&CabSetupRelativeDirectoryString)
+ 2 // slash and nul
);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlAssignUnicodeStringBuffer(&CabDirectoryBuffer, &SourceDevicePathString);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = SpAppendNtPathElement(&CabDirectoryBuffer, &DirectoryOnSourceDeviceString);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = SpAppendNtPathElement(&CabDirectoryBuffer, &CabSetupRelativeDirectoryString);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
//
// Fdi hands us back the concatenation of the path and filename, so make sure there
// is a slash in there.
//
NtStatus = RtlUnicodeStringBufferEnsureTrailingNtPathSeperator(&CabDirectoryBuffer);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlAssignAnsiStringBufferFromUnicodeString(&CabDirectoryBufferA, &CabDirectoryBuffer.String);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
RTL_STRING_NUL_TERMINATE(&CabDirectoryBufferA.String);
//
// get the destination directory information
//
DestinationDirectoryNumber = SpGetSectionLineIndex(SifHandle, SectionName, LineNumber, 1);
if (DestinationDirectoryNumber == NULL) {
SpFatalSifError(SifHandle, SectionName, CabFileName, LineNumber, 1);
goto Exit;
}
RelativeDestinationDirectory = SpLookUpTargetDirectory(SifHandle, DestinationDirectoryNumber);
if (RelativeDestinationDirectory == NULL) {
SpFatalSifError(SifHandle, SectionName, CabFileName, LineNumber, 1);
goto Exit;
}
NtStatus = RtlInitUnicodeStringEx(&RelativeDestinationDirectoryString, RelativeDestinationDirectory);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlEnsureUnicodeStringBufferSizeChars(
&DestinationDirectoryBuffer,
RTL_STRING_GET_LENGTH_CHARS(&SysrootDeviceString)
+ 1 // slash
+ RTL_STRING_GET_LENGTH_CHARS(&SysrootString)
+ 1 // slash
+ RTL_STRING_GET_LENGTH_CHARS(&RelativeDestinationDirectoryString)
+ 2 // slash and nul
);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = RtlAssignUnicodeStringBuffer(&DestinationDirectoryBuffer, &SysrootDeviceString);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = SpAppendNtPathElement(&DestinationDirectoryBuffer, &SysrootString);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
NtStatus = SpAppendNtPathElement(&DestinationDirectoryBuffer, &RelativeDestinationDirectoryString);
if (!NT_SUCCESS(NtStatus)) {
goto Exit;
}
FdiCopyContext->DestinationRootDirectory = DestinationDirectoryBuffer.String;
ErrorInfo->FdiError.fError = FALSE;
ErrorInfo->Success = FALSE;
ErrorInfo->NtStatus = STATUS_SUCCESS;
FdiCopyResult =
FDICopy(
GlobalContext->FdiHandle,
CabFileNameBufferA.String.Buffer, // asms02.cab
CabDirectoryBufferA.String.Buffer, // "\device\harddisk0\partition2\$win_nt$.~ls\ia64"
0,
SpExtractAsmCabsFdiCopyCallback,
NULL,
FdiCopyContext);
if (!FdiCopyResult) {
NTSTATUS NestedStatus = STATUS_INTERNAL_ERROR;
FdiError:
NestedStatus = RtlAssignUnicodeStringBuffer(&ErrorInfo->ErrorCabLeafFileName, &CabFileNameString);
if (!NT_SUCCESS(NestedStatus)) {
ErrorInfo->ErrorCabLeafFileName.String.Length = 0;
}
if (ErrorInfo->NtStatus == STATUS_SUCCESS) {
if (ErrorInfo->FdiError.fError) {
ErrorInfo->NtStatus = SpAsmsCabsTranslateFdiErrorToNtStatus(ErrorInfo->FdiError.erfOper);
} else {
ErrorInfo->NtStatus = STATUS_INTERNAL_ERROR;
}
}
goto Exit;
}
}
Success:
ErrorInfo->FdiError.fError = FALSE;
ErrorInfo->Success = TRUE;
ErrorInfo->NtStatus = STATUS_SUCCESS;
Exit:
RtlFreeUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer1);
RtlFreeUnicodeStringBuffer(&GlobalContext->UnicodeStringBuffer2);
RtlFreeUnicodeStringBuffer(&FdiCopyContext->LastDirectoryCreated);
RtlFreeUnicodeStringBuffer(&CabDirectoryBuffer);
RtlFreeUnicodeStringBuffer(&DestinationDirectoryBuffer);
RtlFreeAnsiStringBuffer(&CabFileNameBufferA);
RtlFreeAnsiStringBuffer(&CabDirectoryBufferA);
if (GlobalContext->FdiHandle != NULL) {
//
// From experience, we know that FDIDestroy access violates
// on a NULL FdiHandle.
//
FDIDestroy(GlobalContext->FdiHandle);
GlobalContext->FdiHandle = NULL;
}
SpAsmCabsGlobalContext = NULL;
return STATUS_SUCCESS;
}
typedef struct _SP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT {
BOOLEAN RedrawEntireScreen;
} SP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT, *PSP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT;
typedef const SP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT *PCSP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT;
VOID
CALLBACK
SpAsmsCabFileOpenUiCallback(
PVOID VoidContext,
PCWSTR FileName
)
{
const PSP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT Context = (PSP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT)VoidContext;
ASSERT(Context != NULL);
//
// SpCopyFilesScreenRepaint takes a path with or without backslashes
// and puts on the screen the leaf filename in the lower right.
//
// The last parameter is "redraw whole screen" and after
// any error it should be TRUE. The result with it always
// false is slightly not great.
//
SpCopyFilesScreenRepaint((PWSTR)FileName, NULL, Context->RedrawEntireScreen);
Context->RedrawEntireScreen = FALSE;
}
NTSTATUS
SpExtractAssemblyCabinets(
HANDLE SifHandle,
IN PCWSTR SourceDevicePath, // \device\harddisk0\partition2
IN PCWSTR DirectoryOnSourceDevice, // \$win_nt$.~ls
IN PCWSTR SysrootDevice, // \Device\Harddisk0\Partition2
IN PCWSTR Sysroot // \WINDOWS.2
)
//
// Wrapper for SpExtractAsmCabs that provides more ui, including
// retry/skip/abort FOR THE WHOLE OPERATION, not per .cab (presently
// we only have on .cab anyway, and the main recoverable error we
// anticipate is the CD being ejected; hopefully we'll play into diskspace
// calculations).
//
{
NTSTATUS NtStatus = STATUS_INTERNAL_ERROR;
BOOLEAN QueueInited = FALSE;
BOOLEAN RedrawScreen = FALSE;
const static ULONG ValidKeys[4] = { ASCI_CR, ASCI_ESC, KEY_F3, 0 };
RTL_UNICODE_STRING_BUFFER FileNameInErrorMessage;
BOOLEAN PutSeperatorInErrorMessage = FALSE;
// perhaps just a slash here is better ui
const static UNICODE_STRING SeperatorInErrorMessageString = RTL_CONSTANT_STRING(L"\\...\\");
USHORT PrefixLength = 0;
SP_ASMS_ERROR_INFORMATION xErrorInfo;
const PSP_ASMS_ERROR_INFORMATION ErrorInfo = &xErrorInfo;
SP_ASMS_CAB_FILE_OPEN_UI_CALLBACK_CONTEXT CabFileOpenUiCallbackContext = { 0 };
if (!RTL_VERIFY(SourceDevicePath != NULL)
|| !RTL_VERIFY(DirectoryOnSourceDevice != NULL)
|| !RTL_VERIFY(SysrootDevice != NULL)
|| !RTL_VERIFY(Sysroot != NULL)) {
return STATUS_INVALID_PARAMETER;
}
SpAsmsInitErrorInfo(ErrorInfo);
RtlInitUnicodeStringBuffer(&FileNameInErrorMessage, NULL, 0);
TryAgain:
if (RedrawScreen) {
SpCopyFilesScreenRepaint(NULL, NULL, TRUE);
}
RedrawScreen = TRUE;
ErrorInfo->FdiError.fError = FALSE;
ErrorInfo->Success = FALSE;
ErrorInfo->NtStatus = STATUS_SUCCESS;
ErrorInfo->ErrorCabLeafFileName.String.Length = 0;
ErrorInfo->ErrorNtFilePath.String.Length = 0;
FileNameInErrorMessage.String.Length = 0;
SpExtractAssemblyCabinetsInternalNoRetryOrUi(
SifHandle,
SourceDevicePath,
DirectoryOnSourceDevice,
SysrootDevice,
Sysroot,
ErrorInfo,
SpAsmsCabFileOpenUiCallback,
&CabFileOpenUiCallbackContext
);
if (ErrorInfo->Success) {
goto Exit;
}
//
// If we failed and we retry, we want the next redraw
// to redraw the entire screen. (This seems
// redundant with the local RedrawScreen.)
//
CabFileOpenUiCallbackContext.RedrawEntireScreen = TRUE;
//
// The copy or verify failed. Give the user a message and allow retry.
//
//
// the file name in the error messages is given
// as foo.cab\leaf_path_in_cab
//
// This is just a convention invented here.
// Another idea would be foo.cab(leaf_path)
// or just foo.cab
// or just leaf_path
// or foo.cab(full_path_in_cab)
// or foo.cab\full_path_in_cab)
// or destination_directory\full_path_in_cab
//
FileNameInErrorMessage.String.Length = 0;
// setup ui likes nul terminals and unicode_string_buffer always
// has room for them
FileNameInErrorMessage.String.Buffer[0] = 0;
PutSeperatorInErrorMessage = FALSE;
if (ErrorInfo->ErrorCabLeafFileName.String.Length != 0) {
RtlAppendUnicodeStringBuffer(
&FileNameInErrorMessage,
&ErrorInfo->ErrorCabLeafFileName.String
);
PutSeperatorInErrorMessage = TRUE;
}
if (ErrorInfo->ErrorNtFilePath.String.Length != 0) {
if (PutSeperatorInErrorMessage) {
NtStatus =
RtlAppendUnicodeStringBuffer(
&FileNameInErrorMessage,
&SeperatorInErrorMessageString
);
}
PrefixLength = 0;
NtStatus =
RtlFindCharInUnicodeString(
RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
&ErrorInfo->ErrorNtFilePath.String,
&RtlNtPathSeperatorString,
&PrefixLength);
if (NtStatus == STATUS_NOT_FOUND) {
PrefixLength = 0;
NtStatus = STATUS_SUCCESS;
}
if (NT_SUCCESS(NtStatus)) {
UNICODE_STRING Leaf;
Leaf.Buffer = (PWSTR)(PrefixLength + (PUCHAR)ErrorInfo->ErrorNtFilePath.String.Buffer);
Leaf.Length = (ErrorInfo->ErrorNtFilePath.String.Length - PrefixLength);
Leaf.MaximumLength = Leaf.Length;
//
// remove first character if it is a seperator
//
if (!RTL_STRING_IS_EMPTY(&Leaf)) {
if (Leaf.Buffer[0] == RtlNtPathSeperatorString.Buffer[0]) {
Leaf.Buffer += 1;
Leaf.Length -= sizeof(Leaf.Buffer[0]);
Leaf.MaximumLength -= sizeof(Leaf.Buffer[0]);
}
RtlAppendUnicodeStringBuffer(
&FileNameInErrorMessage,
&Leaf
);
}
}
}
SpStartScreen(
SP_SCRN_COPY_FAILED,
3,
HEADER_HEIGHT+1,
FALSE,
FALSE,
DEFAULT_ATTRIBUTE,
FileNameInErrorMessage.String.Buffer
);
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
goto TryAgain;
case ASCI_ESC: // skip file
break;
case KEY_F3: // exit setup
SpConfirmExit();
goto TryAgain;
}
SpCopyFilesScreenRepaint(NULL, NULL, TRUE);
Exit:
SpAsmsFreeErrorInfo(ErrorInfo);
RtlFreeUnicodeStringBuffer(&FileNameInErrorMessage);
return STATUS_SUCCESS;
}