/*++ 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 #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; }