/*++ Copyright (c) Microsoft Corporation Module Name: spcopy.c Abstract: File copy/decompression routines for text setup. Author: Ted Miller (tedm) 2-Aug-1993 Revision History: 02-Oct-1996 jimschm Added SpMoveWin9xFiles 12-Dec-1996 jimschm SpMoveWin9xFiles now moves paths based on WINNT.SIF instructions 24-Feb-1997 jimschm Added SpDeleteWin9xFiles --*/ #include "spprecmp.h" #pragma hdrstop #include "spcmdcon.h" #include "spasmcabs.h" // // This structure is used during an OEM preinstall. // It is used to form the list of files that were installed in the system, that // have a short target name, instead of the corresponding long target name. // typedef struct _FILE_TO_RENAME { struct _FILE_TO_RENAME *Next; // // Name of the file to be copied, as it exists on the source media // (short file name part only -- no paths). // PWSTR SourceFilename; // // Directory to which this file is to be copied. // PWSTR TargetDirectory; // // Name of file as it should exist on the target (long name). // PWSTR TargetFilename; } FILE_TO_RENAME, *PFILE_TO_RENAME; // // Structures used to hold lists of files and directories for SpCopyDirRecursive. // typedef struct _COPYDIR_FILE_NODE { LIST_ENTRY SiblingListEntry; WCHAR Name[1]; } COPYDIR_FILE_NODE, *PCOPYDIR_FILE_NODE; typedef struct _COPYDIR_DIRECTORY_NODE { LIST_ENTRY SiblingListEntry; LIST_ENTRY SubdirectoryList; LIST_ENTRY FileList; struct _COPYDIR_DIRECTORY_NODE *Parent; WCHAR Name[1]; } COPYDIR_DIRECTORY_NODE, *PCOPYDIR_DIRECTORY_NODE; // // List used on an OEM preinstall. // It contains the name of the files that need to be added to $$RENAME.TXT // PFILE_TO_RENAME RenameList = NULL; // // Remember whether or not we write out an ntbootdd.sys // BOOLEAN ForceBIOSBoot = FALSE; HARDWAREIDLIST *HardwareIDList = NULL; // // global variables for delayed driver CAB opening during // repair // extern PWSTR gszDrvInfDeviceName; extern PWSTR gszDrvInfDirName; extern HANDLE ghSif; #define FILE_ATTRIBUTES_RHS (FILE_ATTRIBUTE_READONLY | \ FILE_ATTRIBUTE_HIDDEN | \ FILE_ATTRIBUTE_SYSTEM | \ FILE_ATTRIBUTE_ARCHIVE) #define FILE_ATTRIBUTES_NONE 0 PVOID FileCopyGauge; PVOID FileDeleteGauge; PVOID _SetupLogFile = NULL; PVOID _LoggedOemFiles = NULL; extern PCMDCON_BLOCK gpCmdConsBlock; // // List of oem inf files installed as part of the installation of third party drivers // POEM_INF_FILE OemInfFileList = NULL; // // Name of the directory where OEM files need to be copied, if a catalog file (.cat) is part of // the third party driver package that the user provide using the F6 or F5 key. // PWSTR OemDirName = L"OemDir"; #if defined(REMOTE_BOOT) HANDLE SisRootHandle = NULL; #endif // defined(REMOTE_BOOT) VOID SpLogOneFile( IN PFILE_TO_COPY FileToCopy, IN PWSTR Sysroot, IN PWSTR DirectoryOnSourceDevice, IN PWSTR DiskDescription, IN PWSTR DiskTag, IN ULONG CheckSum ); BOOLEAN SpRemoveEntryFromCopyList( IN PDISK_FILE_LIST DiskFileLists, IN ULONG DiskCount, IN PWSTR TargetDirectory, IN PWSTR TargetFilename, IN PWSTR TargetDevicePath, IN BOOLEAN AbsoluteTargetDirectory ); PVOID SppRetrieveLoggedOemFiles( PVOID OldLogFile ); VOID SppMergeLoggedOemFiles( IN PVOID DestLogHandle, IN PVOID OemLogHandle, IN PWSTR SystemPartition, IN PWSTR SystemPartitionDirectory, IN PWSTR NtPartition ); BOOLEAN SppIsFileLoggedAsOemFile( IN PWSTR FilePath ); BOOLEAN SpDelEnumFile( IN PCWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Pointer ); VOID SppMergeRenameFiles( IN PWSTR SourceDevicePath, IN PWSTR NtPartition, IN PWSTR Sysroot ); VOID SppCopyOemDirectories( IN PWSTR SourceDevicePath, IN PWSTR NtPartition, IN PWSTR Sysroot ); NTSTATUS SpOpenFileInDriverCab( IN PCWSTR SourceFileName, IN PVOID SifHandle, OUT HANDLE *SourceHandle ); BOOLEAN pSpTimeFromDosTime( IN USHORT Date, IN USHORT Time, OUT PLARGE_INTEGER UtcTime ); VOID SpInitializeDriverInf( IN HANDLE MasterSifHandle, IN PWSTR SetupSourceDevicePath, IN PWSTR DirectoryOnSourceDevice ); BOOLEAN SpCreateDirectory( IN PCWSTR DevicePath, OPTIONAL IN PCWSTR RootDirectory, OPTIONAL IN PCWSTR Directory, IN ULONG DirAttrs OPTIONAL, IN ULONG CreateFlags OPTIONAL ) { UNICODE_STRING DevicePathString; UNICODE_STRING RootDirectoryString; UNICODE_STRING DirectoryString; BOOLEAN Result; RtlInitUnicodeString(&DevicePathString, DevicePath); RtlInitUnicodeString(&RootDirectoryString, RootDirectory); RtlInitUnicodeString(&DirectoryString, Directory); Result = SpCreateDirectory_Ustr(&DevicePathString, &RootDirectoryString, &DirectoryString, DirAttrs, CreateFlags); return Result; } BOOLEAN SpCreateDirectory_Ustr( IN PCUNICODE_STRING DevicePath, OPTIONAL IN PCUNICODE_STRING RootDirectory, OPTIONAL IN PCUNICODE_STRING Directory, IN ULONG DirAttrs OPTIONAL, IN ULONG CreateFlags OPTIONAL ) /*++ Routine Description: Create a directory. All containing directories are created to ensure that the directory can be created. For example, if the directory to be created is \a\b\c, then this routine will create \a, \a\b, and \a\b\c in that order. Arguments: DevicePath - supplies pathname to the device on which the directory is to be created. RootDirectory - if specified, supplies a fixed portion of the directory name, which may or may not have been already created. The directory being created will be concatenated to this value. Directory - supplies directory to be created on the device. You may use this to specify a full NT path (pass in NULL for DevicePath and RootDirectory). Return Value: None. Does not return if directry could not successfully be created. --*/ { UNICODE_STRING p_ustr; PWSTR p,q,r,EntirePath, z, NewName; OBJECT_ATTRIBUTES Obja; UNICODE_STRING UnicodeString; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; HANDLE Handle; ULONG ValidKeys[3] = { KEY_F3,ASCI_CR,0 }; ULONG DevicePartLen; BOOL TriedOnce; BOOLEAN SkippedFile = FALSE; static ULONG u = 0; const static UNICODE_STRING EmptyString = RTL_CONSTANT_STRING(L""); const static UNICODE_STRING JustBackslashString = RTL_CONSTANT_STRING(L"\\"); ASSERT (Directory); NewName = NULL; // // Do not bother attempting to create the root directory. // if (RtlEqualUnicodeString(Directory, &EmptyString, TRUE) || RtlEqualUnicodeString(Directory, &JustBackslashString, TRUE)) { return TRUE; } // // Fill up TemporaryBuffer with the full pathname of the directory being // created. If DevicePath is NULL, TemporaryBuffer will be filled with one // backslash. Because Directory is required, this ensures the path starts // with a backslash. // p = TemporaryBuffer; *p = 0; p_ustr = TemporaryBufferUnicodeString; ASSERT(p_ustr.Length == 0); SpConcatenatePaths_Ustr(&p_ustr,DevicePath); DevicePartLen = RTL_STRING_GET_LENGTH_CHARS(&p_ustr); if(RootDirectory) { SpConcatenatePaths_Ustr(&p_ustr,RootDirectory); } SpConcatenatePaths_Ustr(&p_ustr,Directory); // // Make a duplicate of the path being created. // RTL_STRING_NUL_TERMINATE(&p_ustr); EntirePath = SpDupStringW(p_ustr.Buffer); if (!EntirePath) { return FALSE; // ran out of memory } // // Make q point to the first character in the directory // part of the pathname (ie, 1 char past the end of the device name). // q = EntirePath + DevicePartLen; // // Note: It is possible for the device path to end in a '\', so we may need // to backup one character // if (*q != L'\\') { q--; } ASSERT(*q == L'\\'); // // Make r point to the first character in the directory // part of the pathname. This will be used to keep the status // line updated with the directory being created. // r = q; // // Make p point to the first character following the first // \ in the directory part of the full path. // p = q+1; do { // // find the next \ or the terminating 0. // q = wcschr(p,L'\\'); // // If we found \, terminate the string at that point. // if(q) { *q = 0; } do { if( !HeadlessTerminalConnected ) { if ((CreateFlags & CREATE_DIRECTORY_FLAG_NO_STATUS_TEXT_UI) == 0) { SpDisplayStatusText(SP_STAT_CREATING_DIRS,DEFAULT_STATUS_ATTRIBUTE,r); } } else { PWCHAR TempPtr = NULL; // // If we're headless, we need to be careful about displaying very long // file/directory names. For that reason, just display a little spinner. // switch( u % 4) { case 0: TempPtr = L"-"; break; case 1: TempPtr = L"\\"; break; case 2: TempPtr = L"|"; break; default: TempPtr = L"/"; break; } SpDisplayStatusText( SP_STAT_CREATING_DIRS,DEFAULT_STATUS_ATTRIBUTE, TempPtr ); u++; } // // Create or open the directory whose name is in EntirePath. // INIT_OBJA(&Obja,&UnicodeString,EntirePath); Handle = NULL; TriedOnce = FALSE; tryagain: Status = ZwCreateFile( &Handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL | DirAttrs, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0 ); // // If it's an obdirectory, obsymlink, device, or directory, then they just didn't pass // a long enough DevicePath. Let this by. // if (Status == STATUS_NOT_A_DIRECTORY) { // //Could be that a file exists by that name. Rename it out of the way // if( SpFileExists( EntirePath, FALSE ) && !TriedOnce){ z = TemporaryBuffer; wcscpy( z, EntirePath ); wcscat( z, L".SetupRenamedFile" ); NewName = SpDupStringW( z ); if( !NewName ) return FALSE; //out of memory - bugcheck (never gets here) - but this keeps PREFIX happy Status = SpRenameFile( EntirePath, NewName, FALSE ); if( NT_SUCCESS(Status)){ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Renamed file %ws to %ws\n", r, NewName)); TriedOnce = TRUE; goto tryagain; }else{ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to rename file %ws (%lx)\n", r, Status)); } } } if(!NT_SUCCESS(Status)) { BOOLEAN b = TRUE; KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Unable to create dir %ws (%lx)\n", r, Status)); if (CreateFlags & CREATE_DIRECTORY_FLAG_SKIPPABLE) { SkippedFile = TRUE; goto SkippedFileQuit; } // // Tell user we couldn't do it. Options are to retry or exit. // while(b) { SpStartScreen( SP_SCRN_DIR_CREATE_ERR, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, r ); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_RETRY, SP_STAT_F3_EQUALS_EXIT, 0 ); switch(SpWaitValidKey(ValidKeys,NULL,NULL)) { case ASCI_CR: b = FALSE; break; case KEY_F3: SpConfirmExit(); break; } } } } while(!NT_SUCCESS(Status)); if (Handle != NULL) ZwClose(Handle); // // Unterminate the current string if necessary. // if(q) { *q = L'\\'; p = q+1; } } while(*p && q); // *p catches string ending in '\' SkippedFileQuit: SpMemFree(EntirePath); if( NewName ) SpMemFree(NewName); return !SkippedFile; } VOID SpCreateDirStructWorker( IN PVOID SifHandle, IN PWSTR SifSection, IN PWSTR DevicePath, IN PWSTR RootDirectory, IN BOOLEAN Fatal ) /*++ Routine Description: Create a set of directories that are listed in a setup information file section. The expected format is as follows: [SectionName] shortname = directory shortname = directory . . . Arguments: SifHandle - supplies handle to loaded setup information file. SifSection - supplies name of section in the setup information file containing directories to be created. DevicePath - supplies pathname to the device on which the directory structure is to be created. RootDirectory - supplies a root directory, relative to which the directory structure will be created. Return Value: None. Does not return if directory structure could not be created. --*/ { ULONG Count; ULONG d; PWSTR Directory; // // Count the number of directories to be created. // Count = SpCountLinesInSection(SifHandle,SifSection); if(!Count) { if(Fatal) { SpFatalSifError(SifHandle,SifSection,NULL,0,0); } else { return; } } for(d=0; d] shortname = directory shortname = directory . . . Arguments: SifHandle - supplies handle to loaded setup information file. SifSection - supplies name of section in the setup information file containing directories to be created. DevicePath - supplies pathname to the device on which the directory structure is to be created. RootDirectory - supplies a root directory, relative to which the directory structure will be created. Return Value: None. Does not return if directory structure could not be created. --*/ { PWSTR p; // // Create the root directory. // SpCreateDirectory(DevicePath,NULL,RootDirectory,HideWinDir?FILE_ATTRIBUTE_HIDDEN:0,0); // // Create platform-indepdenent directories // SpCreateDirStructWorker(SifHandle,SifSection,DevicePath,RootDirectory,TRUE); // // Create platform-dependent directories // p = SpMakePlatformSpecificSectionName(SifSection); if (p) { SpCreateDirStructWorker(SifHandle,p,DevicePath,RootDirectory,FALSE); SpMemFree(p); } } VOID SpGetFileVersion( IN PVOID ImageBase, OUT PULONGLONG Version ) /*++ Routine Description: Get the version stamp out of the VS_FIXEDFILEINFO resource in a PE image. Arguments: ImageBase - supplies the address in memory where the file is mapped in. Version - receives 64bit version number, or 0 if the file is not a PE image or has no version data. Return Value: None. --*/ { PIMAGE_RESOURCE_DATA_ENTRY DataEntry; NTSTATUS Status; ULONG_PTR IdPath[3]; ULONG ResourceSize; struct { USHORT TotalSize; USHORT DataSize; USHORT Type; WCHAR Name[16]; // L"VS_VERSION_INFO" + unicode nul VS_FIXEDFILEINFO FixedFileInfo; } *Resource; *Version = 0; // // Do this to prevent the Ldr routines from faulting. // ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1); IdPath[0] = (ULONG_PTR)RT_VERSION; IdPath[1] = (ULONG_PTR)MAKEINTRESOURCE(VS_VERSION_INFO); IdPath[2] = 0; try { Status = LdrFindResource_U(ImageBase,IdPath,3,&DataEntry); } except(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_UNSUCCESSFUL; } if(!NT_SUCCESS(Status)) { return; } try { Status = LdrAccessResource(ImageBase,DataEntry,&Resource,&ResourceSize); } except(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_UNSUCCESSFUL; } if(!NT_SUCCESS(Status)) { return; } try { if((ResourceSize >= sizeof(*Resource)) && !_wcsicmp(Resource->Name,L"VS_VERSION_INFO")) { *Version = ((ULONGLONG)Resource->FixedFileInfo.dwFileVersionMS << 32) | (ULONGLONG)Resource->FixedFileInfo.dwFileVersionLS; } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: Warning: invalid version resource\n")); } } except(EXCEPTION_EXECUTE_HANDLER) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: Exception encountered processing bogus version resource\n")); } } #if defined(REMOTE_BOOT) NTSTATUS SpCopyFileForRemoteBoot( IN PWSTR SourceFilename, IN PWSTR TargetFilename, IN ULONG TargetAttributes, IN ULONG Flags, OUT PULONG Checksum ) /*++ Routine Description: Check to see if the target file already exists in the master tree on the remote boot server, and if it does, create a single-instance store link to the existing file instead of doing the copy. Arguments: SourceFilename - supplies fully qualified name of file in the NT namespace. TargetFilename - supplies fully qualified name of file in the NT namespace. TargetAttributes - if supplied (ie, non-0) supplies the attributes to be placed on the target on successful copy (ie, readonly, etc). Flags - bit mask specifying any special treatment necessary for the file. CheckSum - checksum of the file Return Value: NT Status value indicating outcome of NtWriteFile of the data. --*/ { NTSTATUS status; IO_STATUS_BLOCK ioStatusBlock; PSI_COPYFILE copyFile; ULONG copyFileSize; ULONG sourceLength; ULONG targetLength; HANDLE targetHandle; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING unicodeString; // // If the target file is not remote, then it must be on the local system // partition, and there's no use in trying an SIS copy. // // If there is no SIS root handle, there's no handle on which to issue the // SIS FSCTL. // if ( (_wcsnicmp(TargetFilename, L"\\Device\\LanmanRedirector", 24) != 0 ) || (SisRootHandle == NULL) ) { return STATUS_UNSUCCESSFUL; } // // Build the FSCTL command buffer. // sourceLength = (wcslen(SourceFilename) + 1) * sizeof(WCHAR); targetLength = (wcslen(TargetFilename) + 1) * sizeof(WCHAR); copyFileSize = FIELD_OFFSET(SI_COPYFILE, FileNameBuffer) + sourceLength + targetLength; copyFile = SpMemAlloc( copyFileSize ); copyFile->SourceFileNameLength = sourceLength; copyFile->DestinationFileNameLength = targetLength; copyFile->Flags = COPYFILE_SIS_REPLACE; RtlCopyMemory( copyFile->FileNameBuffer, SourceFilename, sourceLength ); RtlCopyMemory( copyFile->FileNameBuffer + (sourceLength / sizeof(WCHAR)), TargetFilename, targetLength ); // // Invoke the SIS CopyFile FsCtrl. // status = ZwFsControlFile( SisRootHandle, NULL, NULL, NULL, &ioStatusBlock, FSCTL_SIS_COPYFILE, copyFile, // Input buffer copyFileSize, // Input buffer length NULL, // Output buffer 0 ); // Output buffer length if ( NT_SUCCESS(status) ) { //KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SpCopyFileForRemoteBoot: SIS copy %ws->%ws succeeded\n", SourceFilename, TargetFilename )); // // Open the target file so that CSC knows about it and pins it. // INIT_OBJA(&objectAttributes, &unicodeString, TargetFilename); status = ZwOpenFile( &targetHandle, FILE_GENERIC_READ, &objectAttributes, &ioStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0 ); if ( NT_SUCCESS(status) ) { ZwClose(targetHandle); } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpCopyFileForRemoteBoot: SIS copy %ws->%ws succeeded, but open failed: %x\n", SourceFilename, TargetFilename, status )); } } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpCopyFileForRemoteBoot: SIS copy %ws->%ws failed: %x\n", SourceFilename, TargetFilename, status )); // // If it looks like SIS isn't active on the remote file system, close // the SIS root handle so that we can avoid repeatedly getting this // error. // // Note: NTFS returns STATUS_INVALID_PARAMETER. FAT returns // STATUS_INVALID_DEVICE_REQUEST. // if ( (status == STATUS_INVALID_PARAMETER) || (status == STATUS_INVALID_DEVICE_REQUEST) ) { ZwClose( SisRootHandle ); SisRootHandle = NULL; } } *Checksum = 0; SpMemFree( copyFile ); return status; } #endif // defined(REMOTE_BOOT) NTSTATUS SpCopyFileUsingNames( IN PWSTR SourceFilename, IN PWSTR TargetFilename, IN ULONG TargetAttributes, IN ULONG Flags ) /*++ Routine Description: Attempt to copy or decompress a file based on filenames. Arguments: SourceFilename - supplies fully qualified name of file in the NT namespace. TargetFilename - supplies fully qualified name of file in the NT namespace. TargetAttributes - if supplied (ie, non-0) supplies the attributes to be placed on the target on successful copy (ie, readonly, etc). Flags - bit mask specifying any special treatment necessary for the file. Return Value: NT Status value indicating outcome of NtWriteFile of the data. --*/ { NTSTATUS Status; HANDLE SourceHandle; HANDLE TargetHandle; BOOLEAN b; IO_STATUS_BLOCK IoStatusBlock; FILE_BASIC_INFORMATION BasicFileInfo; FILE_BASIC_INFORMATION BasicFileInfo2; BOOLEAN GotBasicInfo; ULONG FileSize; PVOID ImageBase; HANDLE SectionHandle; BOOLEAN IsCompressed; BOOLEAN InDriverCab; PWSTR TempFilename,TempSourcename; PFILE_RENAME_INFORMATION RenameFileInfo; OBJECT_ATTRIBUTES Obja; UNICODE_STRING UnicodeString; LARGE_INTEGER FileOffset; ULONGLONG SourceVersion; ULONGLONG TargetVersion; USHORT CompressionState; BOOLEAN Moved; BOOLEAN TargetExists; WCHAR SmashedSourceFilename[ACTUAL_MAX_PATH]; ULONG pathSize; #if 0 #ifdef _X86_ BOOL bUniprocFile = FALSE; // // If this file is on the list of files whose locks need to be smashed, // copy a file who's been smashed. We do this by prepending our up // directory name infront of the filename in SourceFilename. // if((Flags & COPY_SMASHLOCKS) && !SpInstallingMp() && !RemoteSysPrepSetup) { WCHAR *char_ptr; // // Find the last '\\' in the name. // char_ptr = SourceFilename + (wcslen(SourceFilename)) - 1; while( (char_ptr > SourceFilename) && (*char_ptr != L'\\') ) { char_ptr--; } // // Now insert our special directory name inside // the specified source file name. // if( *char_ptr == L'\\' ) { *char_ptr = 0; wcscpy( SmashedSourceFilename, SourceFilename ); *char_ptr = L'\\'; char_ptr++; wcscat( SmashedSourceFilename, L"\\UniProc\\" ); wcscat( SmashedSourceFilename, char_ptr ); KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Copying:\n\t%ws\n\tinstead of:\n\t%ws\n", SmashedSourceFilename, SourceFilename)); SourceFilename = SmashedSourceFilename; bUniprocFile = TRUE; } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: Unable to generate smashed source path for %ws\n", SourceFilename)); } } #endif // defined _x86_ #endif // 0 // // Open the source file if it's not open already. // Note that the name may not be the actual name on disk. // We also try to open the name with the _ appended. // InDriverCab = FALSE; if (RemoteSysPrepSetup && ((Flags & COPY_DECOMPRESS_SYSPREP) == 0)) { INIT_OBJA(&Obja,&UnicodeString,SourceFilename); Status = ZwCreateFile( &SourceHandle, FILE_GENERIC_READ, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, NULL, 0 ); } else { if (!PrivateInfHandle && g_UpdatesSifHandle) { TempSourcename = wcsrchr(SourceFilename,L'\\'); if (TempSourcename) { TempSourcename++; } else { TempSourcename = SourceFilename; } #if 0 #ifdef _X86_ // // If this file is on the list of files whose locks need to be smashed, // look in uniproc.cab first // if(bUniprocFile && g_UniprocSifHandle) { Status = SpOpenFileInDriverCab ( TempSourcename, g_UniprocSifHandle, &SourceHandle ); if (NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: using %ws from uniproc cab\n", TempSourcename)); InDriverCab = TRUE; Flags &= ~COPY_DELETESOURCE; } } #endif // defined _X86_ #endif // 0 if (!InDriverCab) { // // look in updates cab first // Status = SpOpenFileInDriverCab ( TempSourcename, g_UpdatesSifHandle, &SourceHandle ); if (NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: using %ws from updates cab\n", TempSourcename)); InDriverCab = TRUE; Flags &= ~COPY_DELETESOURCE; } } } if (!InDriverCab) { Status = SpOpenNameMayBeCompressed( SourceFilename, FILE_GENERIC_READ, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, &SourceHandle, &b ); if (!NT_SUCCESS(Status)) { // // if it's not the actual name and it's not compressed, it may be in the driver cab-file // TempSourcename = wcsrchr(SourceFilename,L'\\'); if (TempSourcename) { TempSourcename++; } else { TempSourcename = SourceFilename; } KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: temp source name: %ws\n", TempSourcename)); Status = SpOpenFileInDriverCab( TempSourcename, NULL, &SourceHandle ); InDriverCab = TRUE; } } } if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: Unable to open source file %ws (%x)\n",SourceFilename,Status)); return(Status); } // // Gather basic file info about the file. We only use the timestamp info. // If this fails this isn't fatal (we assume that if this fails, then // the copy will also fail; it not, the worst case is that the timestamps // might be wrong). // Status = ZwQueryInformationFile( SourceHandle, &IoStatusBlock, &BasicFileInfo, sizeof(BasicFileInfo), FileBasicInformation ); if(NT_SUCCESS(Status)) { GotBasicInfo = TRUE; } else { GotBasicInfo = FALSE; KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: Warning: unable to get basic file info for %ws (%x)\n",SourceFilename,Status)); } // // Get the source file size, map in the file, and determine whether it's compressed. // Status = SpGetFileSize(SourceHandle,&FileSize); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: unable to get size of %ws (%x)\n",SourceFilename,Status)); if (!InDriverCab) { ZwClose(SourceHandle); } return(Status); } if( FileSize == 0 ) { // // We'll soon indirectly call ZwCreateSection with a zero length. // This will fail, so let's deal with zero-length files up here so // they actually get copied. // // We know a couple of things that make our job much easier. // 1. We don't need to actually copy any data, just create an empty // file. // 2. The source file isn't compressed, so don't worry about // decompressing/renaming (by defintion, the smallest compressed // file is non-zero). // INIT_OBJA(&Obja,&UnicodeString,TargetFilename); Status = ZwCreateFile( &TargetHandle, FILE_GENERIC_WRITE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, // no sharing FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY, NULL, 0 ); if( NT_SUCCESS(Status) ) { // // if the source is off of a sysprep image, then we need to copy // EAs and alternate data streams too. we do this before setting // attributes so that read only bit isn't set. // Only do this if we're not grabbing additional drivers from the // flat image. // if (RemoteSysPrepSetup && ((Flags & COPY_DECOMPRESS_SYSPREP) == 0)) { Status = SpCopyEAsAndStreams( SourceFilename, SourceHandle, TargetFilename, TargetHandle, FALSE ); } if ( NT_SUCCESS(Status) ) { // // Try and set attributes on target. // BasicFileInfo.FileAttributes = TargetAttributes; ZwSetInformationFile( TargetHandle, &IoStatusBlock, &BasicFileInfo, sizeof(BasicFileInfo), FileBasicInformation ); } // // Close target file // ZwClose( TargetHandle ); // // Do we need to delete Source? // if( (Flags & COPY_DELETESOURCE) && !RemoteSysPrepSetup && !InDriverCab) { ZwClose(SourceHandle); SourceHandle = NULL; SpDeleteFile(SourceFilename,NULL,NULL); } } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: Failed to create zero-length file %ws\n",TargetFilename)); } // // Clean up this guy since we won't be needing him anymore. // if (SourceHandle != NULL) { if( !InDriverCab ) { ZwClose(SourceHandle); } } if (RemoteSysPrepSetup && NT_SUCCESS(Status) && ((Flags & COPY_DECOMPRESS_SYSPREP) == 0)) { Status = SpSysPrepSetExtendedInfo( SourceFilename, TargetFilename, FALSE, FALSE ); } return(Status); } Status = SpMapEntireFile(SourceHandle,&SectionHandle,&ImageBase,FALSE); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: unable to map file %ws (%x)\n",SourceFilename,Status)); if (!InDriverCab) { ZwClose(SourceHandle); } return(Status); } // // If we were told not to decompress, then treat any file like it is // uncompressed. // if (Flags & COPY_NODECOMP) { IsCompressed = FALSE; } else { if (InDriverCab) { IsCompressed = TRUE; } else { IsCompressed = SpdIsCompressed(ImageBase,FileSize); if (IsCompressed){ PWSTR ExtensionName; // // If cabinet file has one file only, IsCompressed is TRUE. // So we check extension whether this file is cabinet file or // compressed file. // ExtensionName = wcsrchr(SourceFilename, L'.'); if (ExtensionName && !_wcsicmp(ExtensionName, L".cab")) { IsCompressed = FALSE; } } } } // // Create a temporary filename to be used for the target. // pathSize = (wcslen(TargetFilename)+12) * sizeof(WCHAR); TempFilename = SpMemAlloc(pathSize); wcscpy(TempFilename,TargetFilename); wcscpy(wcsrchr(TempFilename,L'\\')+1,L"$$TEMP$$.~~~"); // // Allocate some space for the rename buffer. // RenameFileInfo = SpMemAlloc(sizeof(FILE_RENAME_INFORMATION) + pathSize ); // // Create the temporary file. We first try to do this via a move // if the source isn't compressed and we're going to delete the source file. // if (!IsCompressed && (Flags & COPY_DELETESOURCE) && !RemoteSysPrepSetup) { RenameFileInfo->ReplaceIfExists = TRUE; RenameFileInfo->RootDirectory = NULL; RenameFileInfo->FileNameLength = wcslen(TempFilename)*sizeof(WCHAR); wcscpy(RenameFileInfo->FileName,TempFilename); Status = ZwSetInformationFile( SourceHandle, &IoStatusBlock, RenameFileInfo, sizeof(FILE_RENAME_INFORMATION) + RenameFileInfo->FileNameLength, FileRenameInformation ); Moved = TRUE; } else { // // Force us to fall into the copy case below. // Status = STATUS_UNSUCCESSFUL; } INIT_OBJA(&Obja,&UnicodeString,TempFilename); if(!NT_SUCCESS(Status)) { Moved = FALSE; // // OK, move failed, try decompress/copy instead. // Start by creating the temporary file. // Status = ZwCreateFile( &TargetHandle, FILE_GENERIC_WRITE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, // no sharing FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_SEQUENTIAL_ONLY, NULL, 0 ); if(NT_SUCCESS(Status)) { if(IsCompressed && ( (!RemoteSysPrepSetup) || ((Flags & COPY_DECOMPRESS_SYSPREP) != 0))) { if (InDriverCab) { USHORT RealFileTime,RealFileDate; LARGE_INTEGER RealTime; ASSERT (TempSourcename != NULL ); // // remove the file from the driver cab... // Status = SpdDecompressFileFromDriverCab( TempSourcename, ImageBase, FileSize, TargetHandle, &RealFileDate, &RealFileTime ); // // ...now update the basic file information filetime... // if (GotBasicInfo) { SpTimeFromDosTime(RealFileDate,RealFileTime,&RealTime); BasicFileInfo.CreationTime = RealTime; } } else{ Status = SpdDecompressFile(ImageBase,FileSize,TargetHandle); } } else { ULONG remainingLength; ULONG writeLength; PUCHAR base; // // Guard the write with a try/except because if there is an i/o error, // memory management will raise an in-page exception. // FileOffset.QuadPart = 0; base = ImageBase; remainingLength = FileSize; try { while (remainingLength != 0) { writeLength = 60 * 1024; if (writeLength > remainingLength) { writeLength = remainingLength; } Status = ZwWriteFile( TargetHandle, NULL, NULL, NULL, &IoStatusBlock, base, writeLength, &FileOffset, NULL ); base += writeLength; FileOffset.LowPart += writeLength; remainingLength -= writeLength; if (!NT_SUCCESS(Status)) { break; } } } except(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_IN_PAGE_ERROR; } } // // if the source is off of a sysprep image, then we need to copy // EAs and alternate data streams too. // if ( NT_SUCCESS(Status) && RemoteSysPrepSetup && ((Flags & COPY_DECOMPRESS_SYSPREP) == 0)) { Status = SpCopyEAsAndStreams( SourceFilename, SourceHandle, TargetFilename, TargetHandle, FALSE ); } ZwClose(TargetHandle); } } SpUnmapFile(SectionHandle,ImageBase); if (!InDriverCab) { ZwClose(SourceHandle); } if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: unable to create temporary file %ws (%x)\n",TempFilename,Status)); SpMemFree(TempFilename); SpMemFree(RenameFileInfo); return(Status); } // // At this point we have a temporary target file that is now the source. // Open the file, map it in, and get its version. // Status = ZwCreateFile( &SourceHandle, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &Obja, &IoStatusBlock, NULL, 0, // don't bother with attributes 0, // no sharing FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if((Status == STATUS_ACCESS_DENIED) && Moved) { // // The only way this could have happened is if the source file // is uncompressed and the delete-source flag is set, since in // that case we could have moved the source file to the temp file. // In any other case we would have created the temp file by copying, // and there's no problem reopening the file since we just created // and closed it ourselves, above. // // Reset attributes and try again. The file might have been read-only. // This can happen when doing a winnt32 directly from a CD since the // RO attribute of files from the CD are preserved. // KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: SpCopyFileUsingNames: for file %ws, can't reopen temp file (access deined), trying again\n",SourceFilename)); Status = ZwCreateFile( &SourceHandle, FILE_WRITE_ATTRIBUTES, &Obja, &IoStatusBlock, NULL, 0, // don't bother with attributes FILE_SHARE_WRITE, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if(NT_SUCCESS(Status)) { RtlZeroMemory(&BasicFileInfo2,sizeof(BasicFileInfo2)); BasicFileInfo2.FileAttributes = FILE_ATTRIBUTE_NORMAL; Status = ZwSetInformationFile( SourceHandle, &IoStatusBlock, &BasicFileInfo2, sizeof(BasicFileInfo2), FileBasicInformation ); ZwClose(SourceHandle); if(NT_SUCCESS(Status)) { Status = ZwCreateFile( &SourceHandle, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &Obja, &IoStatusBlock, NULL, 0, // don't bother with attributes 0, // no sharing FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); } } } // // Read-only failured win out over sharing violations -- ie, we'll get back // ACCESS_DEINED first for files that are both RO and in-use. So break out // this block so it gets executed even if we tried again above because the // file might be read-only. // if((Status == STATUS_SHARING_VIOLATION) && Moved) { // // The only way this can happen is if the source file is uncompressed // and the delete-source flag is set. In this case we renamed the file // to the temp filename and now we can't open it for write. // In any other case we would have created the temp file by copying, // and so there's no problem opening the file since we just closed it. // // Rename the temp file back to the source file and try again without // the delete source flag set. This forces a copy instead of a move. // The rename better work or else we're completely hosed -- because // there's a file we can't overwrite with the name we want to use for // the temp file for all our copy operations! // KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: SpCopyFileUsingNames: temporary file %ws is in use -- trying recursive call\n",TempFilename)); Status = SpRenameFile(TempFilename,SourceFilename,FALSE); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SETUP: SpCopyFileUsingNames: unable to restore temp file to %ws (%x)\n",SourceFilename,Status)); } SpMemFree(TempFilename); SpMemFree(RenameFileInfo); if(NT_SUCCESS(Status)) { Status = SpCopyFileUsingNames( SourceFilename, TargetFilename, TargetAttributes, Flags & ~COPY_DELETESOURCE ); } return(Status); } if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: unable to reopen temporary file %ws (%x)\n",TempFilename,Status)); if(Moved) { SpRenameFile(TempFilename,SourceFilename,FALSE); } SpMemFree(TempFilename); SpMemFree(RenameFileInfo); return(Status); } Status = SpGetFileSize(SourceHandle,&FileSize); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: unable to get size of %ws (%x)\n",TempFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename,FALSE); } SpMemFree(TempFilename); SpMemFree(RenameFileInfo); return(Status); } Status = SpMapEntireFile(SourceHandle,&SectionHandle,&ImageBase,FALSE); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: unable to map file %ws (%x)\n",TempFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename,FALSE); } SpMemFree(TempFilename); SpMemFree(RenameFileInfo); return(Status); } SpGetFileVersion(ImageBase,&SourceVersion); SpUnmapFile(SectionHandle,ImageBase); // // See if the target file is there by attempting to open it. // If the file is there, get its version. // INIT_OBJA(&Obja,&UnicodeString,TargetFilename); Status = ZwCreateFile( &TargetHandle, FILE_GENERIC_READ, &Obja, &IoStatusBlock, NULL, 0, // don't bother with attributes FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, // open if exists, fail if not FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); TargetVersion = 0; if(NT_SUCCESS(Status)) { TargetExists = TRUE; // // If we're supposed to ignore versions, then keep the // target version at 0. This will guarantee that we'll overwrite // the target. We use the source filename here because it // allows more flexibility (such as with HALs, which all have // different source names but the same target name). // if(!(Flags & COPY_NOVERSIONCHECK)) { Status = SpGetFileSize(TargetHandle,&FileSize); if(NT_SUCCESS(Status)) { Status = SpMapEntireFile(TargetHandle,&SectionHandle,&ImageBase,FALSE); if(NT_SUCCESS(Status)) { SpGetFileVersion(ImageBase,&TargetVersion); SpUnmapFile(SectionHandle,ImageBase); } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: warning: unable to map file %ws (%x)\n",TargetFilename,Status)); } } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: warning: unable to get size of file %ws (%x)\n",TargetFilename,Status)); } } ZwClose(TargetHandle); } else { TargetExists = FALSE; } // // OK, now we have a temporary source file and maybe an existing // target file, and version numbers for both. We will replace or create // the target file if: // // - The target file doesn't have version data (this also catches the case // where the target file didn't exist) // // - The source version is newer than or equal to the target version. // // So that means we *won't* replace the target file only if both source and // target have version info and the source is older than the target. // // If the target version is 0 then the source version is always >= the target // so one simple test does everything we want. // #if 0 if(SourceVersion >= TargetVersion) { #else // // Quit version-checking. We need to install a stable OS. If we // version check, then we never know what we're going to end up with. // if(1) { #endif // if 0 // // Delete the existing target in preparation. // if(TargetExists) { SpDeleteFile(TargetFilename,NULL,NULL); } // // Rename temp file to actual target file. // RenameFileInfo->ReplaceIfExists = TRUE; RenameFileInfo->RootDirectory = NULL; RenameFileInfo->FileNameLength = wcslen(TargetFilename)*sizeof(WCHAR); ASSERT( RenameFileInfo->FileNameLength < pathSize ); wcscpy(RenameFileInfo->FileName,TargetFilename); Status = ZwSetInformationFile( SourceHandle, &IoStatusBlock, RenameFileInfo, sizeof(FILE_RENAME_INFORMATION) + RenameFileInfo->FileNameLength, FileRenameInformation ); SpMemFree(RenameFileInfo); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: unable to rename temp file to target %ws (%x)\n",TargetFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename,FALSE); } SpMemFree(TempFilename); return(Status); } // // If necessary, check if destination file is using NTFS compression, and // if so, uncompress it. // if(NT_SUCCESS(Status) && (Flags & COPY_FORCENOCOMP)) { Status = ZwQueryInformationFile( SourceHandle, &IoStatusBlock, &BasicFileInfo2, sizeof(BasicFileInfo2), FileBasicInformation ); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: unable to get basic file info on %ws (%x)\n",TargetFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename,FALSE); } SpMemFree(TempFilename); return(Status); } if(BasicFileInfo2.FileAttributes & FILE_ATTRIBUTE_COMPRESSED) { CompressionState = 0; Status = ZwFsControlFile( SourceHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_SET_COMPRESSION, &CompressionState, sizeof(CompressionState), NULL, 0 ); if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: SpCopyFileUsingNames: unable to make %ws uncompressed (%lx)\n",TargetFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename,FALSE); } SpMemFree(TempFilename); return(Status); } } } SpMemFree(TempFilename); // // Delete the source if necessary. If the source is not // compressed and the deletesource flag is set, then we moved // the source file and so the source file is already gone. // if(IsCompressed && (Flags & COPY_DELETESOURCE) && !RemoteSysPrepSetup && !InDriverCab) { PWSTR compname; // // Assume that the source name is on its compressed form, and attempt to // delete this file. // compname = SpGenerateCompressedName(SourceFilename); Status = SpDeleteFile(compname,NULL,NULL); SpMemFree(compname); if( Status == STATUS_OBJECT_NAME_NOT_FOUND ) { // // If we couldn't delete the file with the compressed name, then the file name // was probably on its uncompressed format. // SpDeleteFile(SourceFilename,NULL,NULL); } } // // Apply attributes and timestamp. // Ignore errors. // if(!GotBasicInfo) { RtlZeroMemory(&BasicFileInfo,sizeof(BasicFileInfo)); } // // Set the file attributes. Note that if the caller didn't specify any, // then 0 value will tell the I/O system to leave the attributes alone. // BasicFileInfo.FileAttributes = TargetAttributes; ZwSetInformationFile( SourceHandle, &IoStatusBlock, &BasicFileInfo, sizeof(BasicFileInfo), FileBasicInformation ); ZwClose(SourceHandle); Status = STATUS_SUCCESS; } else { // // Delete the temporary source. // ZwClose(SourceHandle); SpDeleteFile(TempFilename,NULL,NULL); SpMemFree(TempFilename); SpMemFree(RenameFileInfo); Status = STATUS_SUCCESS; } if (RemoteSysPrepSetup && NT_SUCCESS(Status) && ((Flags & COPY_DECOMPRESS_SYSPREP) == 0)) { Status = SpSysPrepSetExtendedInfo( SourceFilename, TargetFilename, FALSE, FALSE ); } return(Status); } VOID SpValidateAndChecksumFile( IN HANDLE FileHandle, OPTIONAL IN PWSTR Filename, OPTIONAL OUT PBOOLEAN IsNtImage, OUT PULONG Checksum, OUT PBOOLEAN Valid ) /*++ Routine Description: Calculate a checksum value for a file using the standard nt image checksum method. If the file is an nt image, validate the image using the partial checksum in the image header. If the file is not an nt image, it is simply defined as valid. If we encounter an i/o error while checksumming, then the file is declared invalid. Arguments: FileHandle - supplies handle of file to check (if not present, then Filename specifies the file to be opened and checked) Filename - supplies full NT path of file to check (if not present, then FileHandle must be specified) IsNtImage = Receives flag indicating whether the file is an NT image file. Checksum - receives 32-bit checksum value. Valid - receives flag indicating whether the file is a valid image (for nt images) and that we can read the image. Return Value: None. --*/ { NTSTATUS Status; PVOID BaseAddress; ULONG FileSize; HANDLE hFile = FileHandle, hSection; PIMAGE_NT_HEADERS NtHeaders; ULONG HeaderSum; // // Assume not an image and failure. // *IsNtImage = FALSE; *Checksum = 0; *Valid = FALSE; // // Open and map the file for read access. // Status = SpOpenAndMapFile( Filename, &hFile, &hSection, &BaseAddress, &FileSize, FALSE ); if(!NT_SUCCESS(Status)) { return; } NtHeaders = SpChecksumMappedFile(BaseAddress,FileSize,&HeaderSum,Checksum); // // If the file is not an image and we got this far (as opposed to encountering // an i/o error) then the checksum is declared valid. If the file is an image, // then its checksum may or may not be valid. // if(NtHeaders) { *IsNtImage = TRUE; *Valid = HeaderSum ? (*Checksum == HeaderSum) : TRUE; } else { *Valid = TRUE; } SpUnmapFile(hSection,BaseAddress); if(!FileHandle) { ZwClose(hFile); } } VOID SpCopyFileWithRetry( IN PFILE_TO_COPY FileToCopy, IN PWSTR SourceDevicePath, IN PWSTR DirectoryOnSourceDevice, IN PWSTR SourceDirectory, OPTIONAL IN PWSTR TargetRoot, OPTIONAL IN ULONG TargetFileAttributes, OPTIONAL IN PCOPY_DRAW_ROUTINE DrawScreen, IN PULONG FileCheckSum, OPTIONAL IN PBOOLEAN FileSkipped, OPTIONAL IN ULONG Flags ) /*++ Routine Description: This routine copies a single file, allowing retry is an error occurs during the copy. If the source file is LZ compressed, then it will be decompressed as it is copied to the target. If the file is not successfully copied, the user has the option to retry to copy or to skip copying that file after a profuse warning about how dangerous that is. Arguments: FileToCopy - supplies structure giving information about the file being copied. SourceDevicePath - supplies path to device on which the source media is mounted (ie, \device\floppy0, \device\cdrom0, etc). DirectoryOnSourceDevice - Supplies the directory on the source where the file is to be found. TargetRoot - if specified, supplies the directory on the target to which the file is to be copied. TargetFileAttributes - if supplied (ie, non-0) supplies the attributes to be placed on the target on successful copy (ie, readonly, etc). If not specified, the attributes will be set to FILE_ATTRIBUTE_NORMAL. DrawScreen - supplies address of a routine to be called to refresh the screen. FileCheckSum - if specified, will contain the check sum of the file copied. FileSkipped - if specified, will inform the caller if there was no attempt to copy the file. Flags - supplies flags to control special processing for this file, such as deleting the source file on successful copy or skip; smashing locks; specifying that the source file is oem; or to indicate that en oem file with the same name should be overwritten on upgrade. This value is ORed in with the Flags field of FileToCopy. Return Value: None. --*/ { PWSTR p = TemporaryBuffer; PWSTR FullSourceName,FullTargetName; NTSTATUS Status; ULONG ValidKeys[4] = { ASCI_CR, ASCI_ESC, KEY_F3, 0 }; BOOLEAN IsNtImage,IsValid; ULONG Checksum; BOOLEAN Failure; ULONG MsgId; BOOLEAN DoCopy; ULONG CopyFlags; BOOLEAN PreinstallRememberFile; // // Form the full NT path of the source file. // wcscpy(p,SourceDevicePath); SpConcatenatePaths(p,DirectoryOnSourceDevice); if(SourceDirectory) { SpConcatenatePaths(p,SourceDirectory); } SpConcatenatePaths(p,FileToCopy->SourceFilename); FullSourceName = SpDupStringW(p); // // Form the full NT path of the target file. // wcscpy(p,FileToCopy->TargetDevicePath); if(TargetRoot) { SpConcatenatePaths(p,TargetRoot); } SpConcatenatePaths(p,FileToCopy->TargetDirectory); // // On an OEM preinstall, if the target name is a long name, then use // the short name as a target name, and later on, if the copy succeeds, // add the file to RenameList, so that it can be added to $$rename.txt // if( !PreInstall || ( wcslen( FileToCopy->TargetFilename ) <= 8 + 1 + 3 ) ) { SpConcatenatePaths(p,FileToCopy->TargetFilename); PreinstallRememberFile = FALSE; } else { SpConcatenatePaths(p,FileToCopy->SourceFilename); PreinstallRememberFile = TRUE; } FullTargetName = SpDupStringW(p); // // Call out to the draw screen routine to indicate that // a new file is being copied. // DrawScreen(FullSourceName,FullTargetName,FALSE); // // Build up the copy flags value. // CopyFlags = Flags | FileToCopy->Flags; // // Set the file attributes if specified in inf file else // set the attributes as specified by the caller. // if (FileToCopy->FileAttributes != FILE_ATTRIBUTES_NONE){ TargetFileAttributes = FileToCopy->FileAttributes; } do { DoCopy = TRUE; // // Check the copy options field. The valid values here are // // - COPY_ALWAYS // - COPY_ONLY_IF_PRESENT // - COPY_ONLY_IF_NOT_PRESENT // - COPY_NEVER switch(CopyFlags & COPY_DISPOSITION_MASK) { case COPY_ONLY_IF_PRESENT: DoCopy = SpFileExists(FullTargetName, FALSE); break; case COPY_ONLY_IF_NOT_PRESENT: DoCopy = !SpFileExists(FullTargetName, FALSE); break; case COPY_NEVER: DoCopy = FALSE; case COPY_ALWAYS: default: break; } if(!DoCopy) { break; } // // In the upgrade case, check if the file being copied // replaces a third party file. // If it does, then ask what the user wants to do about it // if( !RepairWinnt && ( NTUpgrade == UpgradeFull ) && SpFileExists(FullTargetName, FALSE) ) { // // If necessary ask the user if he wants to overwrite the file. // Otherwise go ahead and copy the file. // if(!(CopyFlags & COPY_OVERWRITEOEMFILE)) { PWSTR TmpFilePath; BOOLEAN OverwriteFile; if(( TargetRoot == NULL ) || ( wcslen( FileToCopy->TargetDirectory ) == 0 ) ) { wcscpy( p, FileToCopy->TargetFilename ); } else { wcscpy( p, TargetRoot ); SpConcatenatePaths( p, FileToCopy->TargetDirectory ); SpConcatenatePaths(p,FileToCopy->TargetFilename); } TmpFilePath = SpDupStringW(p); OverwriteFile = TRUE; if( ( (CopyFlags & COPY_SOURCEISOEM) == 0 ) && SppIsFileLoggedAsOemFile( TmpFilePath ) ) { if( !UnattendedOperation ) { ULONG ValidKeys[3] = { ASCI_CR, ASCI_ESC, 0 }; BOOLEAN ActionSelected = FALSE; // ULONG Mnemonics[] = { MnemonicOverwrite, 0 }; // // Warn user that existing file is a third party file, // and ask if user wants to over write the file // while( !ActionSelected ) { SpStartScreen( SP_SCRN_OVERWRITE_OEM_FILE, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, FileToCopy->TargetFilename ); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_REPLACE_FILE, SP_STAT_ESC_EQUALS_SKIP_FILE, 0 ); switch(SpWaitValidKey(ValidKeys,NULL,NULL)) { case ASCI_CR: // don't overwrite OverwriteFile = TRUE; ActionSelected = TRUE; KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: OEM file %ls, will be overwritten.\n", FullTargetName )); break; case ASCI_ESC: // skip file OverwriteFile = FALSE; ActionSelected = TRUE; break; } } // // Need to completely repaint gauge, etc. // DrawScreen(FullSourceName,FullTargetName,TRUE); } else { // // On unattended upgrade, do what is in the script file // OverwriteFile = UnattendedOverwriteOem; } } SpMemFree( TmpFilePath ); if( !OverwriteFile ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: OEM file %ls, will not be overwritten.\n", FullTargetName )); if( ARGUMENT_PRESENT( FileSkipped ) ) { *FileSkipped = TRUE; } // // Free the source and target filenames. // SpMemFree(FullSourceName); SpMemFree(FullTargetName); return; } } } // // Copy the file. If there is a target root specified, assume // the file is being copied to the system partition and make // the file readonly, system, hidden. // #if defined(REMOTE_BOOT) // If this is a remote boot install, check to see if a copy of the // file already exists on the server, and if so, just make a link // to the file instead of copying it. // if (RemoteBootSetup) { Status = SpCopyFileForRemoteBoot( FullSourceName, FullTargetName, TargetFileAttributes, CopyFlags, &Checksum); IsValid = TRUE; // Checksum is known } else { Status = STATUS_UNSUCCESSFUL; } if (!NT_SUCCESS(Status)) #endif // defined(REMOTE_BOOT) { Status = SpCopyFileUsingNames( FullSourceName, FullTargetName, TargetFileAttributes, CopyFlags ); IsValid = FALSE; // Checksum is not known } // // If the file copied OK, verify the copy. // if(NT_SUCCESS(Status)) { if (!IsValid) { SpValidateAndChecksumFile(NULL,FullTargetName,&IsNtImage,&Checksum,&IsValid); } if( ARGUMENT_PRESENT( FileCheckSum ) ) { *FileCheckSum = Checksum; } // // If the image is valid, then the file really did copy OK. // if(IsValid) { Failure = FALSE; } else { // // If it's an nt image, then the verify failed. // If it's not an nt image, then the only way the verify // can fail is if we get an i/o error reading the file back, // which means it didn't really copy correctly. // MsgId = IsNtImage ? SP_SCRN_IMAGE_VERIFY_FAILED : SP_SCRN_COPY_FAILED; Failure = TRUE; PreinstallRememberFile = FALSE; } } else { if((Status == STATUS_OBJECT_NAME_NOT_FOUND) && (Flags & COPY_SKIPIFMISSING)) { Failure = FALSE; } else { Failure = TRUE; MsgId = SP_SCRN_COPY_FAILED; } PreinstallRememberFile = FALSE; } if(Failure) { // // The copy or verify failed. Give the user a message and allow retry. // repaint: SpStartScreen( MsgId, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, FileToCopy->SourceFilename ); 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 break; case ASCI_ESC: // skip file Failure = FALSE; break; case KEY_F3: // exit setup SpConfirmExit(); goto repaint; } // // Need to completely repaint gauge, etc. // DrawScreen(FullSourceName,FullTargetName,TRUE); } } while(Failure); if( ARGUMENT_PRESENT( FileSkipped ) ) { *FileSkipped = !DoCopy; } // // Free the source and target filenames. // SpMemFree(FullSourceName); SpMemFree(FullTargetName); // // In the preinstall mode, add the file to RenameList // if( PreInstall && PreinstallRememberFile ) { PFILE_TO_RENAME File; File = SpMemAlloc(sizeof(FILE_TO_RENAME)); File->SourceFilename = SpDupStringW(FileToCopy->SourceFilename); wcscpy(TemporaryBuffer,L"\\"); if(TargetRoot) { SpConcatenatePaths(TemporaryBuffer,TargetRoot); } SpConcatenatePaths(TemporaryBuffer,FileToCopy->TargetDirectory); File->TargetDirectory = SpDupStringW(TemporaryBuffer); File->TargetFilename = SpDupStringW((PWSTR)FileToCopy->TargetFilename); File->Next = RenameList; RenameList = File; } } VOID SpCopyFilesScreenRepaint( IN PWSTR FullSourcename, OPTIONAL IN PWSTR FullTargetname, OPTIONAL IN BOOLEAN RepaintEntireScreen ) { static ULONG u = 0; PWSTR p; UNREFERENCED_PARAMETER(FullTargetname); // // Repaint the entire screen if necessary. // if(RepaintEntireScreen) { SpStartScreen(SP_SCRN_SETUP_IS_COPYING,0,6,TRUE,FALSE,DEFAULT_ATTRIBUTE); if(FileCopyGauge) { SpDrawGauge(FileCopyGauge); } } // // Place the name of the file being copied on the rightmost // area of the status line. // if(FullSourcename) { if(RepaintEntireScreen) { SpvidClearScreenRegion( 0, VideoVars.ScreenHeight-STATUS_HEIGHT, VideoVars.ScreenWidth, STATUS_HEIGHT, DEFAULT_STATUS_BACKGROUND ); SpDisplayStatusActionLabel(SP_STAT_COPYING,12); } // // Isolate the filename part of the sourcename. // if(p = wcsrchr(FullSourcename,L'\\')) { p++; } else { p = FullSourcename; } if( !HeadlessTerminalConnected ) { SpDisplayStatusActionObject(p); } else { PWCHAR TempPtr = NULL; // // If we're headless, we need to be careful about displaying very long // file/directory names. For that reason, just display a little spinner. // switch( u % 4) { case 0: TempPtr = L"-"; break; case 1: TempPtr = L"\\"; break; case 2: TempPtr = L"|"; break; default: TempPtr = L"/"; break; } SpDisplayStatusActionObject( TempPtr ); u++; } } } VOID SpCopyFilesInCopyList( IN PVOID SifHandle, IN PDISK_FILE_LIST DiskFileLists, IN ULONG DiskCount, IN PWSTR SourceDevicePath, IN PWSTR DirectoryOnSourceDevice, IN PWSTR TargetRoot, IN PINCOMPATIBLE_FILE_LIST CompatibilityExceptionList OPTIONAL ) /*++ Routine Description: Iterate the copy list for each setup source disk and prompt for the disk and copy/decompress all the files on it. Arguments: SifHandle - supplies handle to setup information file. DiskFileLists - supplies the copy list, in the form of an array of structures, one per disk. DiskCount - supplies number of elements in the DiskFileLists array, ie, the number of setup disks. SourceDevicePath - supplies the path of the device from which files are to be copied (ie, \device\floppy0, etc). DirectoryOnSourceDevice - supplies the directory on the source device where files are to be found. TargetRoot - supplies root directory of target. All target directory specifications are relative to this directory on the target. CompatibilityExceptionList - Singly-linked list of PINCOMPATIBLE_FILE_ENTRY objects that should be skipped during copying. Optional, pass NULL if no exceptions are present. Return Value: None. --*/ { ULONG DiskNo; PDISK_FILE_LIST pDisk; PFILE_TO_COPY pFile; ULONG TotalFileCount; ULONG CheckSum; BOOLEAN FileSkipped; ULONG CopyFlags; NTSTATUS status; // // Compute the total number of files. // for(TotalFileCount=DiskNo=0; DiskNoFileCount == 0) { continue; } // // Prompt the user to insert the disk. // SpPromptForDisk( pDisk->Description, SourceDevicePath, pDisk->TagFile, FALSE, // no ignore disk in drive FALSE, // no allow escape TRUE, // warn multiple prompts NULL // don't care about redraw flag ); // // Passing the empty string as the first arg forces // the action area of the status line to be set up. // Not doing so results in the "Copying: xxxxx" to be // flush left on the status line instead of where // it belongs (flush right). // SpCopyFilesScreenRepaint(L"",NULL,TRUE); // // Copy each file on the source disk. // ASSERT(pDisk->FileList); for(pFile=pDisk->FileList; pFile; pFile=pFile->Next) { // // Copy the file. // // If the file is listed for lock smashing then we need to smash it // if installing UP on x86 (we don't bother with the latter // qualifications here). // // If there is an absolute target root specified, assume the // file is being copied to the system partition and make it // readonly/hidden/system. // // On upgrade, we need to know if the file is listed for oem overwrite. // // // "Copy" or "Move"?? // if( (WinntSetup || RemoteInstallSetup) && (!WinntFromCd) && (!NoLs) && (NTUpgrade != UpgradeFull) && (!IsFileFlagSet(SifHandle,pFile->TargetFilename,FILEFLG_DONTDELETESOURCE)) ) { // // We can delete the source (i.e. do a 'move') // CopyFlags = COPY_DELETESOURCE; } else { // // Do a 'copy' // CopyFlags = 0; } #if 0 #ifdef _X86_ // // Copy out of \uniproc (which contains lock-smashed binaries)? // if( IsFileFlagSet(SifHandle,pFile->TargetFilename,FILEFLG_SMASHLOCKS) ) { CopyFlags |= COPY_SMASHLOCKS; } #endif // defined _X86_ #endif // if 0 // // What do we do if we can't find a file?? // if( SkipMissingFiles ) { CopyFlags |= COPY_SKIPIFMISSING; } // // Do we overwrite files installed by the OEM? // if( (NTUpgrade == UpgradeFull) && (IsFileFlagSet(SifHandle,pFile->TargetFilename,FILEFLG_UPGRADEOVERWRITEOEM)) ) { CopyFlags |= COPY_OVERWRITEOEMFILE; } // // If the file is incompatible, and it's got the overwrite flag set, // the blow it away with our own one instead. // if ( SpIsFileIncompatible( CompatibilityExceptionList, pFile, pFile->AbsoluteTargetDirectory ? NULL : TargetRoot ) ) { if (IsFileFlagSet(SifHandle,pFile->SourceFilename,FILEFLG_UPGRADEOVERWRITEOEM) || ( pFile->Flags & FILEFLG_UPGRADEOVERWRITEOEM ) ) { CopyFlags = (CopyFlags & ~(COPY_DISPOSITION_MASK|COPY_OVERWRITEOEMFILE)); CopyFlags |= COPY_ALWAYS | COPY_NOVERSIONCHECK; KdPrintEx(( DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SETUP: OEM (or preexsting) file %ws is incompatible with gui-mode, set flag %08lx, forcing copy\n", pFile->TargetFilename, CopyFlags )); } } // // What about any privates? We don't want to ever 'move' // privates because they might be in the driver cab, in which // case, we want them in the ~LS directory when // we go into gui-mode setup. // if( (pSpIsFileInPrivateInf(pFile->TargetFilename)) ) { CopyFlags &= ~COPY_DELETESOURCE; } if(!WIN9X_OR_NT_UPGRADE || IsFileFlagSet(SifHandle,pFile->SourceFilename,FILEFLG_NOVERSIONCHECK)) { CopyFlags |= COPY_NOVERSIONCHECK; } SpCopyFileWithRetry( pFile, SourceDevicePath, DirectoryOnSourceDevice, pDisk->Directory, pFile->AbsoluteTargetDirectory ? NULL : TargetRoot, pFile->AbsoluteTargetDirectory ? FILE_ATTRIBUTES_RHS : 0, SpCopyFilesScreenRepaint, &CheckSum, &FileSkipped, CopyFlags ); // // Log the file // if( !FileSkipped ) { SpLogOneFile( pFile, pFile->AbsoluteTargetDirectory ? NULL : TargetRoot, NULL, // DirectoryOnSourceDevice, NULL, NULL, CheckSum ); } // // Advance the gauge. // SpTickGauge(FileCopyGauge); SendSetupProgressEvent(FileCopyEvent, OneFileCopyEvent, &((PGAS_GAUGE)FileCopyGauge)->CurrentPercentage); } } SendSetupProgressEvent(FileCopyEvent, FileCopyEndEvent, NULL); SpDestroyGauge(FileCopyGauge); FileCopyGauge = NULL; } NTSTATUS SpCreateIncompatibleFileEntry( OUT PINCOMPATIBLE_FILE_ENTRY *TargetEntry, IN PWSTR FileName, IN PWSTR VersionString, OPTIONAL IN PWSTR TargetAbsolutePath, OPTIONAL IN ULONG Flags OPTIONAL ) /*++ Routine Description: Allocates enough space to store the incompatible file entry data in a contiguous blob, copies the values into it, and returns the blob created. Layout (using null-terminated strings) is as follows: [ Allocation ] [ Header ][Filename][Version][TargetAbsolutePath] Arguments: TargetEntry - Pointer to a PINCOMPATIBLE_FILE_ENTRY that will be returned to the caller. FileName - Name of the file, no path VersionString - Full version string of the file TargetAbsolutePath - Absolute path on the target media that this file will live in Flags - Any flags to be stored Returns: STATUS_SUCCESS if TargetEntry contains a pointer to the allocated space STATUS_NO_MEMORY if the allocation failed STATUS_INVALID_PARAMETER_1 if TargetEntry was NULL STATUS_INVALID_PARAMETER_2 if FileName was NULL --*/ { ULONG WCharsNeeded = 0; ULONG ActualBytes = 0; PINCOMPATIBLE_FILE_ENTRY LocalEntry; PWSTR Cursor; if ( TargetEntry ) *TargetEntry = NULL; else return STATUS_INVALID_PARAMETER_1; // // Gather required sizes // if ( FileName != NULL ) WCharsNeeded += wcslen(FileName) + 1; else return STATUS_INVALID_PARAMETER_2; if ( VersionString != NULL ) WCharsNeeded += wcslen(VersionString) + 1; if ( TargetAbsolutePath != NULL ) WCharsNeeded += wcslen(TargetAbsolutePath) + 1; // // Allocate the space, point the cursor at where we'll be copying // the strings. // ActualBytes = ( sizeof(WCHAR) * WCharsNeeded ) + sizeof(INCOMPATIBLE_FILE_ENTRY); LocalEntry = SpMemAlloc( ActualBytes ); if ( LocalEntry == NULL ) { return STATUS_NO_MEMORY; } // // Blank it out, point the write cursor at the end // ZeroMemory(LocalEntry, ActualBytes); Cursor = (PWSTR)(LocalEntry + 1); // // Copy strings and set pointers // wcscpy(Cursor, FileName); LocalEntry->IncompatibleFileName = Cursor; Cursor += wcslen(FileName) + 1; if ( VersionString != NULL ) { wcscpy(Cursor, VersionString); LocalEntry->VersionString = Cursor; Cursor += wcslen(VersionString) + 1; } if ( TargetAbsolutePath != NULL ) { wcscpy(Cursor, TargetAbsolutePath); LocalEntry->FullPathOnTarget = Cursor; } *TargetEntry = LocalEntry; return STATUS_SUCCESS; } NTSTATUS SpFreeIncompatibleFileList( IN PINCOMPATIBLE_FILE_LIST FileListHead ) /*++ Routine Description: Cleans out the list of incompatible entries by freeing all the space that was allocated for the list. Arguments: FileListHead - Pointer to the list containing INCOMPATIBLE_FILE_ENTRY items Return values: STATUS_SUCCESS if the operation succeeds. STATUS_INVALID_PARAMETER if FileListHead is NULL --*/ { PINCOMPATIBLE_FILE_ENTRY IncListEntry; if ( !FileListHead ) return STATUS_INVALID_PARAMETER; while ( FileListHead->Head != NULL ) { // // Simple list removal, some bookkeeping // IncListEntry = FileListHead->Head; FileListHead->Head = IncListEntry->Next; FileListHead->EntryCount--; SpMemFree( IncListEntry ); } // // Toast the list structure as well. // FileListHead->Head = NULL; FileListHead->EntryCount = 0; return STATUS_SUCCESS; } BOOLEAN SpIsFileIncompatible( IN PINCOMPATIBLE_FILE_LIST FileList, IN PFILE_TO_COPY pFile, IN PWSTR TargetRoot OPTIONAL ) /*++ Routine Description: Find out whether the given target media path and file name are listed as "incompatible." Looks down FileList for an INCOMPATIBLE_FILE_ENTRY that contains FileName and TargetMediaPath. If TargetMediaPath is not specified, indicates TRUE if a member with the name FileName is listed instead. (Dangerous.) Arguments: FileList - Header node of a list created with SpInitializeCompatibilityOverwriteLists. pFile - File to copy structure, containing all the relevant information about this file to scan for. TargetRoot - Copy target root directory, optional Return Values: TRUE if the file "FileName" (with the optional path TargetMediaPath) is in the list of incompatible files. FALSE otherwise, or if FileList or FileName is NULL. Remarks: This routine could be made more robust in the face of bad parameters, but this was deemed superfluous, since there's exactly one caller of this API whose values are already checked. This function will then behave "properly" in all cases. --*/ { INCOMPATIBLE_FILE_ENTRY *Entry; BOOLEAN Found = FALSE; PWSTR TargetFileName; if ( ( FileList == NULL ) || ( FileList->EntryCount == 0 ) ) goto Exit; if ( pFile == NULL ) goto Exit; Entry = FileList->Head; // // Cache locally // TargetFileName = pFile->TargetFilename; // // Generate the target media path from the file object // #if 0 VOID SpCopyFileWithRetry( SpCopyFileWithRetry( IN PFILE_TO_COPY FileToCopy, pFile, IN PWSTR SourceDevicePath, SourceDevicePath, IN PWSTR DirectoryOnSourceDevice, DirectoryOnSourceDevice, IN PWSTR SourceDirectory, OPTIONAL pDisk->Directory, IN PWSTR TargetRoot, OPTIONAL pFile->AbsoluteTargetDirectory ? NULL : TargetRoot, IN ULONG TargetFileAttributes, OPTIONAL pFile->AbsoluteTargetDirectory ? ATTR_RHS : 0, IN PCOPY_DRAW_ROUTINE DrawScreen, SpCopyFilesScreenRepaint, IN PULONG FileCheckSum, OPTIONAL &CheckSum, IN PBOOLEAN FileSkipped, OPTIONAL &FileSkipped, IN ULONG Flags CopyFlags ); VOID SpCopyFileWithRetry( IN PFILE_TO_COPY FileToCopy, IN PWSTR SourceDevicePath, IN PWSTR DirectoryOnSourceDevice, IN PWSTR SourceDirectory, OPTIONAL IN PWSTR TargetRoot, OPTIONAL IN ULONG TargetFileAttributes, OPTIONAL IN PCOPY_DRAW_ROUTINE DrawScreen, IN PULONG FileCheckSum, OPTIONAL IN PBOOLEAN FileSkipped, OPTIONAL IN ULONG Flags ) // // Form the full NT path of the target file. // wcscpy(p,FileToCopy->TargetDevicePath); if(TargetRoot) { SpConcatenatePaths(p,TargetRoot); } SpConcatenatePaths(p,FileToCopy->TargetDirectory); // // On an OEM preinstall, if the target name is a long name, then use // the short name as a target name, and later on, if the copy succeeds, // add the file to RenameList, so that it can be added to $$rename.txt // if( !PreInstall || ( wcslen( FileToCopy->TargetFilename ) <= 8 + 1 + 3 ) ) { SpConcatenatePaths(p,FileToCopy->TargetFilename); PreinstallRememberFile = FALSE; } else { SpConcatenatePaths(p,FileToCopy->SourceFilename); PreinstallRememberFile = TRUE; } FullTargetName = SpDupStringW(p); #endif // if 0 // // Cycle through the list of incompatible file names, looking // for the one requested // while ( Entry != NULL ) { // // If the file names match, then check the media paths if // specified // if (_wcsicmp(TargetFileName, Entry->IncompatibleFileName) == 0) { // // CUT&PASTE CUT&PASTE CUT&PASTE CUT&PASTE CUT&PASTE CUT&PASTE // // This was clipped from SpCopyFileWithRetry's code that generates the // actual target path. I think the logic is supposed to look something // like the following: // // Target = // File.TargetDevicePath + // (File.AbsoluteTargetDirectory ? "" : TargetRoot) + // File.TargetDirectory + // ( (PreInstall || File.TargetFileName.Length > 12 ) ? File.SourceFilename : File.TargetFilename ) // PWSTR TargetMediaPath = (PWSTR)TemporaryBuffer; wcscpy( TargetMediaPath, pFile->TargetDevicePath ); if ( !pFile->AbsoluteTargetDirectory && ( TargetRoot != NULL ) ) { SpConcatenatePaths(TargetMediaPath, TargetRoot); } SpConcatenatePaths(TargetMediaPath,pFile->TargetDirectory); if ( !PreInstall || ( wcslen( pFile->TargetFilename ) <= 8 + 1 + 3 ) ) { SpConcatenatePaths(TargetMediaPath, pFile->TargetFilename); } else { SpConcatenatePaths(TargetMediaPath, pFile->SourceFilename); } // // When requesting an exact match, check it // if (Entry->FullPathOnTarget != NULL) { if (_wcsicmp(TargetMediaPath, Entry->FullPathOnTarget) == 0) { Found = TRUE; goto Exit; } // // Otherwise, the target media path was NULL, so just care if // the short names matched // } else { Found = TRUE; goto Exit; } } Entry = Entry->Next; } Exit: return Found; } NTSTATUS SpInitializeCompatibilityOverwriteLists( IN PVOID SifHandle, OUT PINCOMPATIBLE_FILE_LIST IncompatibleFileList ) /*++ Routine Description: Reads the list of files that were marked as "incompatible" or "wrong version" in Winnt32, and stored in the IncompatibleFilesToOverWrite section of the sif file. The data should be in the format [IncompatibleFilesToOverWrite] = , Arguments: SifHandle - Handle to the INF that this list will be loaded from. IncompatibleFileLists - Filled out to Returns: STATUS_SUCCESS on good completion. STATUS_INVALID_PARAMETER_1 if SifHandle is NULL. STATUS_INVALID_PARAMETER_2 if IncompatibleFileList is NULL. --*/ { NTSTATUS status = STATUS_SUCCESS; INCOMPATIBLE_FILE_LIST fileListHead; PINCOMPATIBLE_FILE_ENTRY fileListSingle = NULL; PWSTR SectionName = WINNT_OVERWRITE_EXISTING_W; PWSTR VersionString; PWSTR TargetMediaName; PWSTR FileName; PWSTR FullNtPathOfTargetName; ULONG SectionItemCount; ULONG i; if ( !SifHandle ) return STATUS_INVALID_PARAMETER_1; if ( !IncompatibleFileList ) return STATUS_INVALID_PARAMETER_2; // // Construct our local copy // IncompatibleFileList->Head = NULL; IncompatibleFileList->EntryCount = 0; fileListHead.Head = NULL; fileListHead.EntryCount = 0; SectionItemCount = SpCountLinesInSection(SifHandle, SectionName); for ( i = 0; i < SectionItemCount; i++ ) { FileName = SpGetKeyName( SifHandle, SectionName, i ); if (!FileName){ SpFatalSifError(SifHandle,SectionName,NULL,i,(ULONG)(-1)); } // // Get the version string // VersionString = SpGetSectionKeyIndex( SifHandle, SectionName, FileName, 0 ); // // And name on the target media, if specified. // TargetMediaName = SpGetSectionKeyIndex( SifHandle, SectionName, FileName, 1 ); // // We can't, unfortunately, just use the path we got from the sif // file, as it's a Win32 path (c:\foo\bar\zot.foom). We need a full // NT path (\Device\Harddisk0\Partition1\foo\bar\zot.foom) instead. // So, we convert with SpNtPathFromDosPath, which has the side- // effect of allocating space, which we don't necessarily care // about. // FullNtPathOfTargetName = SpNtPathFromDosPath( TargetMediaName ); // // Create the file list entry, and store it for // later use. // status = SpCreateIncompatibleFileEntry( &fileListSingle, FileName, VersionString, FullNtPathOfTargetName, 0 ); // // If that failed and we have a full path, it should get freed // before we fail this code path to avoid a leak. // if ( FullNtPathOfTargetName != NULL ) { SpMemFree(FullNtPathOfTargetName); FullNtPathOfTargetName = NULL; } if (!NT_SUCCESS(status)) goto Exit; // // Head insertion, indicate it was added // fileListSingle->Next = fileListHead.Head; fileListHead.Head = fileListSingle; fileListHead.EntryCount++; fileListSingle = NULL; } // // And store the list from here to there. // *IncompatibleFileList = fileListHead; Exit: // // Did we accidentally create one but fail to insert it? Hmm... // if( fileListSingle != NULL ) { SpMemFree(fileListSingle); fileListSingle = NULL; } // // If there was a failure and the list is nonempty, free its // entries. It will not have been copied over to IncompatibleFileLists // (the only point of failure is SpCreateIncompatibleFileEntry, which // if it fails leaps out to Exit: without assigning IFL.) // if ( fileListHead.EntryCount != 0 && !NT_SUCCESS(status)) { SpFreeIncompatibleFileList( &fileListHead ); } return status; } VOID SpInitializeFileLists( IN PVOID SifHandle, OUT PDISK_FILE_LIST *DiskFileLists, OUT PULONG DiskCount ) /*++ Routine Description: Initialize disk file lists. This involves looking in a given section in the sectup information file and fetching information for each disk specified there. The data is expected to be in the format [] = ,[,,] ... (Note that is the third field -- the 2 commas are not a typo -- field 2 is unused.) Arguments: SifHandle - supplies handle to loaded setup information file. DiskFileLists - receives pointer to an array of disk file list structures, one per line in SifSection. The caller must free this buffer when finished with it. DiskCount - receives number of elements in DiskFileLists array. Return Value: None. --*/ { unsigned pass; PWSTR mediaShortname,description,tagFile,directory; PDISK_FILE_LIST diskFileLists; PWSTR SectionName; ULONG TotalCount; ULONG SectionCount; ULONG i,u; BOOLEAN Found; diskFileLists = SpMemAlloc(0); TotalCount = 0; for(pass=0; pass<2; pass++) { // // On first pass do the platform-specific section. // SectionName = pass ? SIF_SETUPMEDIA : SpMakePlatformSpecificSectionName(SIF_SETUPMEDIA); // // Determine the number of media specifications // in the given section. // if (SectionName) { SectionCount = SpCountLinesInSection(SifHandle,SectionName); diskFileLists = SpMemRealloc( diskFileLists, (TotalCount+SectionCount) * sizeof(DISK_FILE_LIST) ); // // Zero out the new part of the buffer we just reallocated. // RtlZeroMemory( diskFileLists + TotalCount, SectionCount * sizeof(DISK_FILE_LIST) ); for(i=0; i