/*++ Copyright (c) 1993 Microsoft Corporation Module Name: spcopy.c Abstract: File copy/decompression routines for text setup. Author: Ted Miller (tedm) 2-Aug-1993 Revision History: --*/ #include "spprecmp.h" #pragma hdrstop typedef struct _DISK_FILE_LIST { PWSTR MediaShortname; PWSTR Description; PWSTR TagFile; PWSTR Directory; ULONG FileCount; PFILE_TO_COPY FileList; } DISK_FILE_LIST, *PDISK_FILE_LIST; // // 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; // // 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; #define ATTR_RHS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE) PVOID FileCopyGauge; PVOID _SetupLogFile = NULL; PVOID _LoggedOemFiles = NULL; 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 PWSTR DirName, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ret, IN PVOID Pointer ); VOID SpCopyDirRecursive( IN PWSTR SrcPath, IN PWSTR DestDevPath, IN PWSTR DestDirPath ); VOID SppMergeRenameFiles( IN PWSTR SourceDevicePath, IN PWSTR NtPartition, IN PWSTR Sysroot ); VOID SppCopyOemDirectories( IN PWSTR SourceDevicePath, IN PWSTR NtPartition, IN PWSTR Sysroot ); VOID SpCreateDirectory( IN PWSTR DevicePath, IN PWSTR RootDirectory, OPTIONAL IN PWSTR Directory ) /*++ 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 must have already been created. The directory being created will be concatenated to this value. Directory - supplies directory to be created on the device. Return Value: None. Does not return if directry could not successfully be created. --*/ { PWSTR p,q,r,EntirePath; OBJECT_ATTRIBUTES Obja; UNICODE_STRING UnicodeString; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; HANDLE Handle; ULONG ValidKeys[3] = { KEY_F3,ASCI_CR,0 }; ULONG DevicePartLen; // // Do not bother attempting to create the root directory. // if((Directory[0] == 0) || ((Directory[0] == L'\\') && (Directory[1] == 0))) { return; } // // Fill up TemporaryBuffer with the full pathname // of the directory being created. // p = (PWSTR)TemporaryBuffer; *p = 0; SpConcatenatePaths(p,DevicePath); DevicePartLen = wcslen(p); if(RootDirectory) { SpConcatenatePaths(p,RootDirectory); } SpConcatenatePaths(p,Directory); // // Make a duplicate of the path being created. // EntirePath = SpDupStringW(p); // // 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; 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 { SpDisplayStatusText(SP_STAT_CREATING_DIRS,DEFAULT_STATUS_ATTRIBUTE,r); // // Create or open the directory whose name is in EntirePath. // INIT_OBJA(&Obja,&UnicodeString,EntirePath); Status = ZwCreateFile( &Handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0 ); if(!NT_SUCCESS(Status)) { BOOLEAN b = TRUE; KdPrint(("SETUP: Unable to create dir %ws (%lx)\n", r, Status)); // // 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)); ZwClose(Handle); // // Unterminate the current string if necessary. // if(q) { *q = L'\\'; p = q+1; } } while(*p && q); // *p catches string ending in '\' SpMemFree(EntirePath); } 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); // // Create platform-indepdenent directories // SpCreateDirStructWorker(SifHandle,SifSection,DevicePath,RootDirectory,TRUE); // // Create platform-depdenent directories // p = SpMakePlatformSpecificSectionName(SifSection); 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 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)ImageBase | 1); IdPath[0] = (ULONG)RT_VERSION; IdPath[1] = (ULONG)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; } if((ResourceSize >= sizeof(*Resource)) && !_wcsicmp(Resource->Name,L"VS_VERSION_INFO")) { *Version = ((ULONGLONG)Resource->FixedFileInfo.dwFileVersionMS << 32) | (ULONGLONG)Resource->FixedFileInfo.dwFileVersionLS; } else { KdPrint(("SETUP: Warning: invalid version resource\n")); } } 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; PWSTR TempFilename; PFILE_RENAME_INFORMATION RenameFileInfo; OBJECT_ATTRIBUTES Obja; UNICODE_STRING UnicodeString; LARGE_INTEGER LargeZero; ULONGLONG SourceVersion; ULONGLONG TargetVersion; USHORT CompressionState; BOOLEAN Moved; BOOLEAN TargetExists; // // 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. // Status = SpOpenNameMayBeCompressed( SourceFilename, FILE_GENERIC_READ, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, &SourceHandle, &b ); if(!NT_SUCCESS(Status)) { KdPrint(("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; KdPrint(("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)) { KdPrint(("SETUP: SpCopyFileUsingNames: unable to get size of %ws (%x)\n",SourceFilename,Status)); ZwClose(SourceHandle); return(Status); } Status = SpMapEntireFile(SourceHandle,&SectionHandle,&ImageBase,FALSE); if(!NT_SUCCESS(Status)) { KdPrint(("SETUP: SpCopyFileUsingNames: unable to map file %ws (%x)\n",SourceFilename,Status)); ZwClose(SourceHandle); return(Status); } IsCompressed = SpdIsCompressed(ImageBase,FileSize); // // Create a temporary filename to be used for the target. // TempFilename = SpMemAlloc((wcslen(TargetFilename)+12)*sizeof(WCHAR)); wcscpy(TempFilename,TargetFilename); wcscpy(wcsrchr(TempFilename,L'\\')+1,L"$$TEMP$$.~~~"); // // Allocate some space for the rename buffer. // RenameFileInfo = SpMemAlloc(1000); // // 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)) { 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, NULL, 0 ); if(NT_SUCCESS(Status)) { if(IsCompressed) { Status = SpdDecompressFile(ImageBase,FileSize,TargetHandle); } else { // // Guard the write with a try/except because if there is an i/o error, // memory management will raise an in-page exception. // LargeZero.QuadPart = 0; try { Status = ZwWriteFile( TargetHandle, NULL, NULL, NULL, &IoStatusBlock, ImageBase, FileSize, &LargeZero, NULL ); } except(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_IN_PAGE_ERROR; } } ZwClose(TargetHandle); } } SpUnmapFile(SectionHandle,ImageBase); ZwClose(SourceHandle); if(!NT_SUCCESS(Status)) { KdPrint(("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. // KdPrint(("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! // KdPrint(("SETUP: SpCopyFileUsingNames: temporary file %ws is in use -- trying recursive call\n",TempFilename)); Status = SpRenameFile(TempFilename,SourceFilename); if(!NT_SUCCESS(Status)) { KdPrint(("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)) { KdPrint(("SETUP: SpCopyFileUsingNames: unable to reopen temporary file %ws (%x)\n",TempFilename,Status)); if(Moved) { SpRenameFile(TempFilename,SourceFilename); } SpMemFree(TempFilename); SpMemFree(RenameFileInfo); return(Status); } Status = SpGetFileSize(SourceHandle,&FileSize); if(!NT_SUCCESS(Status)) { KdPrint(("SETUP: SpCopyFileUsingNames: unable to get size of %ws (%x)\n",TempFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename); } SpMemFree(TempFilename); SpMemFree(RenameFileInfo); return(Status); } Status = SpMapEntireFile(SourceHandle,&SectionHandle,&ImageBase,FALSE); if(!NT_SUCCESS(Status)) { KdPrint(("SETUP: SpCopyFileUsingNames: unable to map file %ws (%x)\n",TempFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename); } 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 { KdPrint(("SETUP: SpCopyFileUsingNames: warning: unable to map file %ws (%x)\n",TargetFilename,Status)); } } else { KdPrint(("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(SourceVersion >= TargetVersion) { // // 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); wcscpy(RenameFileInfo->FileName,TargetFilename); Status = ZwSetInformationFile( SourceHandle, &IoStatusBlock, RenameFileInfo, sizeof(FILE_RENAME_INFORMATION) + RenameFileInfo->FileNameLength, FileRenameInformation ); SpMemFree(RenameFileInfo); if(!NT_SUCCESS(Status)) { KdPrint(("SETUP: SpCopyFileUsingNames: unable to rename temp file to target %ws (%x)\n",TargetFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename); } SpMemFree(TempFilename); return(Status); } #ifdef _X86_ // // If this file is on the list of files whose locks need to be smashed, // go smash locks. Note that the subroutine checks to see whether smashing // is really necessary (ie, if we're installing uniprocessor). // if(Flags & COPY_SMASHLOCKS) { BOOLEAN IsNtImage,IsValid; ULONG Checksum; SpValidateAndChecksumFile(SourceHandle,NULL,&IsNtImage,&Checksum,&IsValid); // // If the image is valid, then smash the locks // if(IsNtImage && IsValid) { SpMashemSmashem(SourceHandle,NULL,NULL,NULL); } else { // // It's not an nt image, or not a valid nt image, // so set Status to avoid the forcenocomp stuff below. // Status = STATUS_IMAGE_CHECKSUM_MISMATCH; } } #endif // // 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)) { KdPrint(("SETUP: SpCopyFileUsingNames: unable to get basic file info on %ws (%x)\n",TargetFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename); } 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)) { KdPrint(("SETUP: SpCopyFileUsingNames: unable to make %ws uncompressed (%lx)\n",TargetFilename,Status)); ZwClose(SourceHandle); if(Moved) { SpRenameFile(TempFilename,SourceFilename); } 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)) { 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; } 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 = (PWSTR)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; 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[4] = { ASCI_CR, ASCI_ESC, KEY_F1, 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, SP_STAT_F1_EQUALS_HELP, 0 ); switch(SpWaitValidKey(ValidKeys,NULL,NULL)) { case ASCI_CR: // don't overwrite OverwriteFile = TRUE; ActionSelected = TRUE; KdPrint(( "SETUP: OEM file %ls, will be overwritten.\n", FullTargetName )); break; case ASCI_ESC: // skip file OverwriteFile = FALSE; ActionSelected = TRUE; break; case KEY_F1: // display help SpHelp( SP_HELP_OVERWRITE_OEM_FILE, NULL, SPHELP_HELPTEXT ); 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 ) { KdPrint(( "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. // Status = SpCopyFileUsingNames( FullSourceName, FullTargetName, TargetFileAttributes, CopyFlags ); // // If the file copied OK, verify the copy. // if(NT_SUCCESS(Status)) { 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((PWSTR)TemporaryBuffer,L"\\"); if(TargetRoot) { SpConcatenatePaths((PWSTR)TemporaryBuffer,TargetRoot); } SpConcatenatePaths((PWSTR)TemporaryBuffer,FileToCopy->TargetDirectory); File->TargetDirectory = SpDupStringW((PWSTR)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 ) { 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; } SpDisplayStatusActionObject(p); } } VOID SpCopyFilesInCopyList( IN PVOID SifHandle, IN PDISK_FILE_LIST DiskFileLists, IN ULONG DiskCount, IN PWSTR SourceDevicePath, IN PWSTR DirectoryOnSourceDevice, IN PWSTR TargetRoot ) /*++ 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. Return Value: None. --*/ { ULONG DiskNo; PDISK_FILE_LIST pDisk; PFILE_TO_COPY pFile; ULONG TotalFileCount; ULONG CheckSum; BOOLEAN FileSkipped; ULONG CopyFlags; #ifdef _FASTRECOVER_ PWSTR CopySourceDevicePath = SourceDevicePath; #endif // // Compute the total number of files. // for(TotalFileCount=DiskNo=0; DiskNoFileCount == 0) { continue; } #ifdef _FASTRECOVER_ //wfc CopySourceDevicePath = !_wcsnicmp(pDisk->TagFile,L"\\flop",5) ? L"\\device\\floppy0" : SourceDevicePath; #endif // // Prompt the user to insert the disk. // SpPromptForDisk( pDisk->Description, #ifdef _FASTRECOVER_ CopySourceDevicePath, #else SourceDevicePath, #endif 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. // CopyFlags = ( (WinntSetup && (NTUpgrade != UpgradeFull)) ? COPY_DELETESOURCE : 0) #ifdef _X86_ | (IsFileFlagSet(SifHandle,pFile->TargetFilename,FILEFLG_SMASHLOCKS) ? COPY_SMASHLOCKS : 0) #endif | (SkipMissingFiles ? COPY_SKIPIFMISSING : 0) | ( ((NTUpgrade == UpgradeFull) && IsFileFlagSet(SifHandle,pFile->TargetFilename,FILEFLG_UPGRADEOVERWRITEOEM)) ? COPY_OVERWRITEOEMFILE : 0); if((NTUpgrade != UpgradeFull) || IsFileFlagSet(SifHandle,pFile->SourceFilename,FILEFLG_NOVERSIONCHECK)) { CopyFlags |= COPY_NOVERSIONCHECK; } SpCopyFileWithRetry( pFile, #ifdef _FASTRECOVER_ CopySourceDevicePath, #else SourceDevicePath, #endif DirectoryOnSourceDevice, pDisk->Directory, pFile->AbsoluteTargetDirectory ? NULL : TargetRoot, pFile->AbsoluteTargetDirectory ? ATTR_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); } } SpDestroyGauge(FileCopyGauge); FileCopyGauge = NULL; } 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. // 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; iNext; SpMemFree(Entry); Entry = Next; } } SpMemFree(*DiskFileLists); *DiskFileLists = NULL; } BOOLEAN SpCreateEntryInCopyList( IN PVOID SifHandle, IN PDISK_FILE_LIST DiskFileLists, IN ULONG DiskCount, IN ULONG DiskNumber, IN PWSTR SourceFilename, IN PWSTR TargetDirectory, IN PWSTR TargetFilename, IN PWSTR TargetDevicePath, IN BOOLEAN AbsoluteTargetDirectory, IN ULONG CopyFlags ) /*++ Routine Description: Adds an entry to a disk's file copy list after first verifying that the file is not already on the disk copy list. Arguments: SifHandle - supplies handle to loaded text setup information file (txtsetup.sif). DiskFileLists - supplies an array of file lists, one for each distribution disk in the product. DiskCount - supplies number of elements in the DiskFileLists array. SourceFilename - supplies the name of the file as it exists on the distribution media. TargetDirectory - supplies the directory on the target media into which the file will be copied. TargetFilename - supplies the name of the file as it will exist in the target tree. TargetDevicePath - supplies the NT name of the device onto which the file is to be copied (ie, \device\harddisk1\partition2, etc). AbsoluteTargetDirectory - indicates whether TargetDirectory is a path from the root, or relative to a root to specified later. CopyFlags - COPY_ALWAYS : always copied COPY_ONLY_IF_PRESENT : copied only if present on the targetReturn Value: COPY_ONLY_IF_NOT_PRESENT : not copied if present on the target COPY_NEVER : never copied Return Value: TRUE if a new copy list entry was created; FALSE if not (ie, the file was already on the copy list). --*/ { PDISK_FILE_LIST pDiskList; PFILE_TO_COPY pListEntry; UNREFERENCED_PARAMETER(DiskCount); pDiskList = &DiskFileLists[DiskNumber]; for(pListEntry=pDiskList->FileList; pListEntry; pListEntry=pListEntry->Next) { if(!_wcsicmp(pListEntry->TargetFilename,TargetFilename) && !_wcsicmp(pListEntry->SourceFilename,SourceFilename) && !_wcsicmp(pListEntry->TargetDirectory,TargetDirectory) && !_wcsicmp(pListEntry->TargetDevicePath,TargetDevicePath) && (pListEntry->AbsoluteTargetDirectory == AbsoluteTargetDirectory) // && ( (pListEntry->CopyOptions == COPY_ALWAYS) // || (CopyOptions == COPY_ALWAYS) // || (CopyOptions == pListEntry->CopyOptions) // ) ) { // // Return code indicates that we did not add a new entry. // return(FALSE); } } // // File not already found; create new entry // and link into relevent disk's file list. // pListEntry = SpMemAlloc(sizeof(FILE_TO_COPY)); pListEntry->SourceFilename = SourceFilename; pListEntry->TargetDirectory = TargetDirectory; pListEntry->TargetFilename = TargetFilename; pListEntry->TargetDevicePath = TargetDevicePath; pListEntry->AbsoluteTargetDirectory = AbsoluteTargetDirectory; pListEntry->Flags = CopyFlags; pListEntry->Next = pDiskList->FileList; pDiskList->FileList = pListEntry; pDiskList->FileCount++; // // Return code indicates that we added a new entry. // return(TRUE); } VOID SpAddMasterFileSectionToCopyList( IN PVOID SifHandle, IN PDISK_FILE_LIST DiskFileLists, IN ULONG DiskCount, IN PWSTR TargetDevicePath, IN PWSTR AbsoluteTargetDirectory, IN ULONG CopyOptionsIndex ) /*++ Routine Description: Adds files listed in a setup information master file section to the copy list. Each line in the section is expected to be in a standard format: [Section] = , , , , Arguments: SifHandle - supplies handle to loaded setup information file. DiskFileLists - supplies an array of file lists, one for each distribution disk in the product. DiskCount - supplies number of elements in the DiskFileLists array. TargetDevicePath - supplies the NT name of the device onto which the files are to be copied (ie, \device\harddisk1\partition2, etc). AbsoluteTargetDirectory - If specified, supplies the directory into which the files are to be copied on the target; overrides values specified on the lines in []. This allows the caller to specify an absolute directory for the files instead of using indirection via a target directory shortname. CopyOptionsIndex - This specifies which index to look up to get the copy options field. If the field is not present it is assumed that this this file is not to be copied. Use: INDEX_UPGRADE for upgrade copy options INDEX_WINNTFILE for fresh installation copy options --*/ { ULONG Count,u,u1,CopyOptions; PWSTR CopyOptionsString, sourceFilename,targetFilename,targetDirSpec,mediaShortname,TargetDirectory; BOOLEAN fAbsoluteTargetDirectory; PWSTR section; unsigned i; for(i=0; i<2; i++) { section = i ? SpMakePlatformSpecificSectionName(SIF_FILESONSETUPMEDIA) : SIF_FILESONSETUPMEDIA; // // Determine the number of files listed in the section. // This value may be zero. // Count = SpCountLinesInSection(SifHandle,section); if (fAbsoluteTargetDirectory = (AbsoluteTargetDirectory != NULL)) { TargetDirectory = AbsoluteTargetDirectory; } for(u=0; u,[,] Arguments: SifHandle - supplies handle to loaded setup information file. DiskFileLists - supplies an array of file lists, one for each distribution disk in the product. DiskCount - supplies number of elements in the DiskFileLists array. SectionName - supplies the name of the section that lists the files being added to the copy list. TargetDevicePath - supplies the NT name of the device onto which the files are to be copied (ie, \device\harddisk1\partition2, etc). TargetDirectory - If specified, supplies the directory into which the files are to be copied on the target; overrides values specified on the lines in []. This allows the caller to specify an absolute directory for the files instead of using indirection via a target directory shortname. CopyOptions - COPY_ALWAYS : always copied COPY_ONLY_IF_PRESENT : copied only if present on the targetReturn Value: COPY_ONLY_IF_NOT_PRESENT : not copied if present on the target COPY_NEVER : never copied CheckForNoComp - if true, then check each file to see if it must exist uncompressed on an NTFS partition supporting compression (ie, NTLDR on x86). --*/ { ULONG Count,u; // // Determine the number of files listed in the section. // This value may be zero. // Count = SpCountLinesInSection(SifHandle,SectionName); for(u=0; uThirdPartyOptionSelected) { SpAddSingleFileToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_HAL, pHw->IdString, 0, #ifdef _X86_ TargetDevicePath, NULL, #else SystemPartition, SystemPartitionDirectory, #endif COPY_ALWAYS, FALSE ); } #ifdef _X86_ // // If a third party computer was not specified, then there will be a // detect module specified in the [ntdetect] section of the inf file // for the computer. // If a third-party computer was specified, then there may or may not // be a detect module. If there is no detect module specified, then // copy the 'standard' one. // { PWSTR NtDetectId = NULL; if(!pHw->ThirdPartyOptionSelected) { NtDetectId = pHw->IdString; } else { if(!IS_FILETYPE_PRESENT(pHw->FileTypeBits,HwFileDetect)) { NtDetectId = SIF_STANDARD; } } if(NtDetectId) { SpAddSingleFileToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_NTDETECT, NtDetectId, 0, SystemPartition, SystemPartitionDirectory, COPY_ALWAYS, FALSE ); } } #endif } VOID SpAddConditionalFilesToCopyList( IN PVOID SifHandle, IN PDISK_FILE_LIST DiskFileLists, IN ULONG DiskCount, IN PWSTR TargetDevicePath, IN PWSTR SystemPartition, IN PWSTR SystemPartitionDirectory, IN BOOLEAN Uniprocessor ) /*++ Routine Description: Add files to the copy list that are copied based on the configuration of the machine and user selections. This may include: - the up or mp kernel. - abiosdsk - vga files [x86 only] - files for computer, keyboard, mouse, display, and layout - scsi miniport drivers - mouse and keyboard class drivers - the HAL - the detect module [x86 only] Arguments: SifHandle - supplies handle to loaded setup information file. DiskFileLists - supplies an array of file lists, one for each distribution disk in the product. DiskCount - supplies number of elements in the DiskFileLists array. TargetDevicePath - supplies the NT name of the device that will hold the nt tree. SystemPartition - supplies the NT name of the device that will hold the system partition. SystemPartitionDirectoty - supplies the directory on the system partition into which files that go on the system partition will be copied. Uniprocessor - if true, then we are installing/upgrading a UP system. Note that this a different question than the number of processors in the system. Return Value: None. --*/ { ULONG i; PHARDWARE_COMPONENT pHw; PWSTR SectionName; // // Add the hal, kernel and ntdetect to the copy list // SpAddHalKrnlDetToCopyList( SifHandle, DiskFileLists, DiskCount, TargetDevicePath, SystemPartition, SystemPartitionDirectory, Uniprocessor ); // // If there are any abios disks, copy the abios disk driver. // if(AbiosDisksExist) { SpAddSingleFileToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_SPECIALFILES, SIF_ABIOSDISK, 0, TargetDevicePath, NULL, COPY_ALWAYS, FALSE ); } // // Always copy vga files. // SpAddSectionFilesToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_VGAFILES, TargetDevicePath, NULL, COPY_ALWAYS, FALSE ); // // Add the correct device driver files to the copy list. // for(i=0; iNext ) { // // No files to copy here for third-party options. // This is handled elsewhere. // if(pHw->ThirdPartyOptionSelected) { continue; } // // Get the name of the section containing files for this device. // SectionName = SpGetSectionKeyIndex( SifHandle, NonlocalizedComponentNames[i], pHw->IdString, INDEX_FILESECTION ); if(!SectionName) { SpFatalSifError( SifHandle, NonlocalizedComponentNames[i], pHw->IdString, 0, INDEX_FILESECTION ); } // // Add that section's files to the copy list. // SpAddSectionFilesToCopyList( SifHandle, DiskFileLists, DiskCount, SectionName, TargetDevicePath, NULL, COPY_ALWAYS, FALSE ); } } // // Add the keyboard layout dll to the copy list. // if( !PreInstall || (PreinstallHardwareComponents[HwComponentLayout] == NULL) ) { pHw = HardwareComponents[HwComponentLayout]; } else { pHw = PreinstallHardwareComponents[HwComponentLayout]; } // if(!pHw->ThirdPartyOptionSelected) { SpAddSingleFileToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_KEYBOARDLAYOUTFILES, pHw->IdString, 0, TargetDevicePath, NULL, COPY_ALWAYS, FALSE ); } // // Add scsi miniport drivers to the copy list. // Because miniport drivers are only a single file, // we just use the filename specified in [SCSI.Load] -- // no need for separate [files.xxxx] sections. // if( !PreInstall || ( PreinstallScsiHardware == NULL ) ) { pHw = ScsiHardware; } else { pHw = PreinstallScsiHardware; } for( ; pHw; pHw=pHw->Next) { if(!pHw->ThirdPartyOptionSelected) { SpAddSingleFileToCopyList( SifHandle, DiskFileLists, DiskCount, L"SCSI.Load", pHw->IdString, 0, TargetDevicePath, NULL, COPY_ALWAYS, FALSE ); } } // // If not being replaced by third-party ones, add keyboard and mouse // class drivers. // Note that in the pre-install case, keyboard and class drivers will // be added if at least one retail mouse or keyborad driver are // to be pre-installed. // if( !PreInstall || ( PreinstallHardwareComponents[HwComponentMouse] == NULL ) ) { pHw=HardwareComponents[HwComponentMouse]; } else { pHw=PreinstallHardwareComponents[HwComponentMouse]; } for( ;pHw;pHw=pHw->Next ) { if(!pHw->ThirdPartyOptionSelected || !IS_FILETYPE_PRESENT(pHw->FileTypeBits,HwFileClass)) { SpAddSingleFileToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_SPECIALFILES, SIF_MOUSECLASS, 0, TargetDevicePath, NULL, COPY_ALWAYS, FALSE ); // // We don't need to continue to look at the other mouse drivers // since we have already added the class driver // break; } } if( !PreInstall || ( PreinstallHardwareComponents[HwComponentKeyboard] == NULL ) ) { pHw=HardwareComponents[HwComponentKeyboard]; } else { pHw=PreinstallHardwareComponents[HwComponentKeyboard]; } for( ;pHw;pHw=pHw->Next ) { if(!pHw->ThirdPartyOptionSelected || !IS_FILETYPE_PRESENT(pHw->FileTypeBits,HwFileClass)) { SpAddSingleFileToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_SPECIALFILES, SIF_KEYBOARDCLASS, 0, TargetDevicePath, NULL, COPY_ALWAYS, FALSE ); // // We don't need to continue to look at the other keyboard drivers // since we have already added the class driver // break; } } } VOID SpCopyThirdPartyDrivers( IN PWSTR SourceDevicePath, IN PWSTR SysrootDevice, IN PWSTR Sysroot, IN PWSTR SyspartDevice, IN PWSTR SyspartDirectory, IN PDISK_FILE_LIST DiskFileLists, IN ULONG DiskCount ) { ULONG component; PHARDWARE_COMPONENT pHw; PHARDWARE_COMPONENT_FILE pHwFile; FILE_TO_COPY FileDescriptor; PWSTR TargetRoot; PWSTR InfNameBases[HwComponentMax+1] = { L"cpt", L"vio", L"kbd", L"lay", L"ptr", L"scs" }; ULONG InfCounts[HwComponentMax+1] = { 0,0,0,0,0,0 }; WCHAR InfFilename[20]; ULONG CheckSum; BOOLEAN FileSkipped; ULONG TargetFileAttribs; ULONG CopyFlags; for(component=0; component<=HwComponentMax; component++) { // // If we're upgrading, then we only want to copy third-party HALs or SCSI // drivers (if supplied) // if((NTUpgrade == UpgradeFull) && !((component == HwComponentComputer) || (component == HwComponentMax))) { continue; } // // Handle scsi specially. // pHw = (component==HwComponentMax) ? ( ( !PreInstall || ( PreinstallScsiHardware == NULL ) )? ScsiHardware : PreinstallScsiHardware ) : ( ( !PreInstall || ( PreinstallHardwareComponents[component] == NULL ) )? HardwareComponents[component] : PreinstallHardwareComponents[component] ); // // Look at each instance of this component. // for( ; pHw; pHw=pHw->Next) { // // Skip this device if not a third-party selection. // if(!pHw->ThirdPartyOptionSelected) { continue; } // // Loop through the list of files associated with this selection. // for(pHwFile=pHw->Files; pHwFile; pHwFile=pHwFile->Next) { // // Assume the file goes on the nt drive (as opposed to // the system partition drive) and that the target name // is the same as the source name. Also, assume no special // attributes (ie, FILE_ATTRIBUTE_NORMAL) // FileDescriptor.Next = NULL; FileDescriptor.SourceFilename = pHwFile->Filename; FileDescriptor.TargetDevicePath = SysrootDevice; FileDescriptor.TargetFilename = FileDescriptor.SourceFilename; FileDescriptor.Flags = COPY_ALWAYS; TargetFileAttribs = 0; switch(pHwFile->FileType) { // // Driver, port, and class type files are all device drivers // and are treated the same -- they get copied to the // system32\drivers directory. // case HwFileDriver: case HwFilePort: case HwFileClass: TargetRoot = Sysroot; FileDescriptor.TargetDirectory = L"system32\\drivers"; break; // // Dlls get copied to the system32 directory. // case HwFileDll: TargetRoot = Sysroot; FileDescriptor.TargetDirectory = L"system32"; break; // // Inf files get copied to the system32 directory and are // renamed based on the component. // case HwFileInf: if(InfCounts[component] < 99) { InfCounts[component]++; // names start at 1 swprintf( InfFilename, L"oem%s%02d.inf", InfNameBases[component], InfCounts[component] ); FileDescriptor.TargetFilename = InfFilename; } TargetRoot = Sysroot; FileDescriptor.TargetDirectory = L"inf"; break; // // Hal files are renamed to hal.dll and copied to the system32 // directory (x86) or the system partition (non-x86). // case HwFileHal: #ifdef _X86_ TargetRoot = Sysroot; FileDescriptor.TargetDirectory = L"system32"; #else TargetRoot = NULL; FileDescriptor.TargetDevicePath = SyspartDevice; FileDescriptor.TargetDirectory = SyspartDirectory; TargetFileAttribs = ATTR_RHS; #endif FileDescriptor.TargetFilename = L"hal.dll"; break; // // Detect modules are renamed to ntdetect.com and copied to // the root of the system partition (C:). // case HwFileDetect: TargetRoot = NULL; FileDescriptor.TargetDevicePath = SyspartDevice; FileDescriptor.TargetDirectory = SyspartDirectory; FileDescriptor.TargetFilename = L"ntdetect.com"; TargetFileAttribs = ATTR_RHS; break; } if( !PreInstall ) { // // Prompt for the disk. // SpPromptForDisk( pHwFile->DiskDescription, SourceDevicePath, pHwFile->DiskTagFile, FALSE, // don't ignore disk in drive FALSE, // don't allow escape FALSE, // don't warn about 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 the file. // SpCopyFileWithRetry( &FileDescriptor, SourceDevicePath, (PreInstall)? PreinstallOemSourcePath : pHwFile->Directory, NULL, TargetRoot, TargetFileAttribs, SpCopyFilesScreenRepaint, &CheckSum, &FileSkipped, COPY_SOURCEISOEM ); // // Log the file // if( !FileSkipped ) { SpLogOneFile( &FileDescriptor, TargetRoot, pHwFile->Directory, pHwFile->DiskDescription, pHwFile->DiskTagFile, CheckSum ); } // // Remove the file from the copy list so that it won't be overwritten // SpRemoveEntryFromCopyList( DiskFileLists, DiskCount, FileDescriptor.TargetDirectory, FileDescriptor.TargetFilename, FileDescriptor.TargetDevicePath, FileDescriptor.AbsoluteTargetDirectory ); } } } #ifdef _ALPHA_ if(OemPalFilename) { // // Prompt for the OEM PAL disk. // SpPromptForDisk( OemPalDiskDescription, SourceDevicePath, OemPalFilename, FALSE, // don't ignore disk in drive FALSE, // don't allow escape FALSE, // don't warn about multiple prompts NULL // don't care about redraw flag ); SpCopyFilesScreenRepaint(L"",NULL,TRUE); // // Copy the file. // FileDescriptor.Next = NULL; FileDescriptor.SourceFilename = OemPalFilename; FileDescriptor.TargetFilename = FileDescriptor.SourceFilename; FileDescriptor.Flags = COPY_ALWAYS; FileDescriptor.TargetDevicePath = SyspartDevice; FileDescriptor.TargetDirectory = SyspartDirectory; SpCopyFileWithRetry( &FileDescriptor, SourceDevicePath, L"", NULL, NULL, ATTR_RHS, SpCopyFilesScreenRepaint, &CheckSum, &FileSkipped, COPY_SOURCEISOEM ); // // Log the file // if(!FileSkipped) { SpLogOneFile( &FileDescriptor, NULL, L"", OemPalDiskDescription, OemPalFilename, CheckSum ); } // // Remove the file from the copy list so that it won't be overwritten // SpRemoveEntryFromCopyList( DiskFileLists, DiskCount, FileDescriptor.TargetDirectory, FileDescriptor.TargetFilename, FileDescriptor.TargetDevicePath, FileDescriptor.AbsoluteTargetDirectory ); } #endif } #ifdef _X86_ VOID SpCopyNtbootddScreenRepaint( IN PWSTR FullSourcename, OPTIONAL IN PWSTR FullTargetname, OPTIONAL IN BOOLEAN RepaintEntireScreen ) { UNREFERENCED_PARAMETER(FullSourcename); UNREFERENCED_PARAMETER(FullTargetname); UNREFERENCED_PARAMETER(RepaintEntireScreen); // // Just put up a message indicating that we are setting up // boot params. // CLEAR_CLIENT_SCREEN(); SpDisplayStatusText(SP_STAT_DOING_NTBOOTDD,DEFAULT_STATUS_ATTRIBUTE); } VOID SpCreateNtbootddSys( IN PDISK_REGION NtPartitionRegion, IN PWSTR NtPartitionDevicePath, IN PWSTR Sysroot, IN PWSTR SystemPartitionDevicePath ) /*++ Routine Description: Create c:\ntbootdd.sys if necessary. The scsi miniport driver file fill be copied from the drivers directory (where it was copied during the earlier file copy phase) to c:\ntbootdd.sys. Arguments: NtPartitionRegion - supplies the region descriptor for the disk region onto which the user chose to install Windows NT. NtPartitionDevicePath - supplies the nt namespace pathname for the partition onto which the user chose to install Windows NT. Sysroot - supplies the directory on the target partition. SystemPartitionDevicePath - supplies the nt device path of the partition onto which to copy ntbootdd.sys (ie, C:\). Return Value: None. --*/ { PWSTR MiniportDriverBasename; PWSTR MiniportDriverFilename; FILE_TO_COPY Descriptor; PWSTR DriversDirectory,p; ULONG CheckSum; BOOLEAN FileSkipped; // // If the Nt Partition is not on a scsi disk, there's nothing to do. // MiniportDriverBasename = HardDisks[NtPartitionRegion->DiskNumber].ScsiMiniportShortname; if(*MiniportDriverBasename == 0) { return; } // // If it's on a scsi disk that is visible through the BIOS, // nothing to do. // p = SpNtToArc(NtPartitionDevicePath,PrimaryArcPath); if(p) { if(!_wcsnicmp(p,L"multi(",6) && !SpIsRegionBeyondCylinder1024(NtPartitionRegion)) { SpMemFree(p); return; } SpMemFree(p); } // // Form the name of the scsi miniport driver. // wcscpy((PWSTR)TemporaryBuffer,MiniportDriverBasename); wcscat((PWSTR)TemporaryBuffer,L".sys"); MiniportDriverFilename = SpDupStringW((PWSTR)TemporaryBuffer); // // Form the full path to the drivers directory. // wcscpy((PWSTR)TemporaryBuffer,Sysroot); SpConcatenatePaths((PWSTR)TemporaryBuffer,L"system32\\drivers"); DriversDirectory = SpDupStringW((PWSTR)TemporaryBuffer); // // When we are in upgrade mode we may or may not have the scsi // miniport that we used for this boot of setup to recognise // the target drive. We should check to see if this file does // exist before trying the copy // if(NTUpgrade == UpgradeFull) { PWSTR Driver; wcscpy((PWSTR)TemporaryBuffer, NtPartitionDevicePath); SpConcatenatePaths((PWSTR)TemporaryBuffer, DriversDirectory); SpConcatenatePaths((PWSTR)TemporaryBuffer, MiniportDriverFilename); Driver = SpDupStringW((PWSTR)TemporaryBuffer); if(!SpFileExists(Driver, FALSE)) { SpMemFree(Driver); SpMemFree(MiniportDriverFilename); SpMemFree(DriversDirectory); return; } SpMemFree(Driver); } // // // Fill in the fields of the file descriptor. // Descriptor.SourceFilename = MiniportDriverFilename; Descriptor.TargetDevicePath = SystemPartitionDevicePath; Descriptor.TargetDirectory = L""; Descriptor.TargetFilename = L"NTBOOTDD.SYS"; Descriptor.Flags = COPY_ALWAYS; // // Copy the file. // SpCopyFileWithRetry( &Descriptor, NtPartitionDevicePath, DriversDirectory, NULL, NULL, ATTR_RHS, SpCopyNtbootddScreenRepaint, &CheckSum, &FileSkipped, 0 ); // // Log the file // if( !FileSkipped ) { SpLogOneFile( &Descriptor, Sysroot, NULL, NULL, NULL, CheckSum ); } // // Clean up. // SpMemFree(MiniportDriverFilename); SpMemFree(DriversDirectory); } #endif VOID SpCopyFiles( IN PVOID SifHandle, IN PDISK_REGION SystemPartitionRegion, IN PDISK_REGION NtPartitionRegion, IN PWSTR Sysroot, IN PWSTR SystemPartitionDirectory, IN PWSTR SourceDevicePath, IN PWSTR DirectoryOnSourceDevice, IN PWSTR ThirdPartySourceDevicePath ) { PDISK_FILE_LIST DiskFileLists; ULONG DiskCount; PWSTR NtPartition,SystemPartition; PWSTR p; BOOLEAN Uniprocessor; ULONG n; CLEAR_CLIENT_SCREEN(); Uniprocessor = !SpInstallingMp(); // // Skip copying if directed to do so in the setup information file. // if((p = SpGetSectionKeyIndex(SifHandle,SIF_SETUPDATA,SIF_DONTCOPY,0)) && SpStringToLong(p,NULL,10)) { KdPrint(("SETUP: DontCopy flag is set in .sif; skipping file copying\n")); return; } // // Initialize the diamond decompression engine. // SpdInitialize(); // // Get the device path of the nt partition. // SpNtNameFromRegion( NtPartitionRegion, (PWSTR)TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); NtPartition = SpDupStringW((PWSTR)TemporaryBuffer); // // Get the device path of the system partition. // SpNtNameFromRegion( SystemPartitionRegion, (PWSTR)TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); SystemPartition = SpDupStringW((PWSTR)TemporaryBuffer); // // Create the system partition directory. // SpCreateDirectory(SystemPartition,NULL,SystemPartitionDirectory); // // Create the nt tree. // SpCreateDirectoryStructureFromSif(SifHandle,SIF_NTDIRECTORIES,NtPartition,Sysroot); // // We may be installing into an old tree, so delete all files // in the system32\config subdirectory (unless we're upgrading). // if(NTUpgrade != UpgradeFull) { wcscpy((PWSTR)TemporaryBuffer, NtPartition); SpConcatenatePaths((PWSTR)TemporaryBuffer, Sysroot); SpConcatenatePaths((PWSTR)TemporaryBuffer, L"system32\\config"); p = SpDupStringW((PWSTR)TemporaryBuffer); // // Enumerate and delete all files in system32\config subdirectory. // SpEnumFiles(p, SpDelEnumFile, &n, NULL); SpMemFree(p); } else { // // We go off and try to load the setup.log file for the // installation we're about to upgrade. We do this because we // need to transfer any loggged OEM files to our new setup.log. // Otherwise, these entries would be lost in our new log file, // and we would have an unrepairable installation if the OEM files // lost were vital for booting. // ULONG RootDirLength; NTSTATUS Status; PVOID Inf; // // We first find out if the repair directory exists. If it does exist // load setup.log from the repair directory. Otherwise, load setup.log // from the WinNt directory // wcscpy((PWSTR)TemporaryBuffer, NtPartition); SpConcatenatePaths((PWSTR)TemporaryBuffer, Sysroot); RootDirLength = wcslen((PWSTR)TemporaryBuffer); SpConcatenatePaths((PWSTR)TemporaryBuffer, SETUP_REPAIR_DIRECTORY); SpConcatenatePaths((PWSTR)TemporaryBuffer, SETUP_LOG_FILENAME); if(!SpFileExists((PWSTR)TemporaryBuffer, FALSE)) { ((PWSTR)TemporaryBuffer)[RootDirLength] = L'\0'; SpConcatenatePaths((PWSTR)TemporaryBuffer, SETUP_LOG_FILENAME); } p = SpDupStringW((PWSTR)TemporaryBuffer); // // Attempt to load old setup.log. If we can't, it's no big deal, We just // won't have any old logged OEM files to merge in. // Status = SpLoadSetupTextFile(p, NULL, 0, &Inf, &n); if(!NT_SUCCESS(Status)) { KdPrint(("SETUP: SpCopyFiles: can't load old setup.log (%lx)\n", Status)); } else { // // We found setup.log, so go and pull out anything pertinent. // _LoggedOemFiles = SppRetrieveLoggedOemFiles(Inf); SpFreeTextFile(Inf); } SpMemFree(p); // // Prepare fonts for upgrade. // wcscpy((PWSTR)TemporaryBuffer,NtPartition); SpConcatenatePaths((PWSTR)TemporaryBuffer,Sysroot); SpConcatenatePaths((PWSTR)TemporaryBuffer,L"SYSTEM"); p = SpDupStringW((PWSTR)TemporaryBuffer); SpPrepareFontsForUpgrade(p); SpMemFree(p); } SpDisplayStatusText(SP_STAT_BUILDING_COPYLIST,DEFAULT_STATUS_ATTRIBUTE); // // Create the buffer for the log file. // _SetupLogFile = SpNewSetupTextFile(); if( _SetupLogFile == NULL ) { KdPrint(("SETUP: Unable to create buffer for setup.log \n")); } // // Generate media descriptors for the source media. // SpInitializeFileLists( SifHandle, &DiskFileLists, &DiskCount ); if(NTUpgrade != UpgradeFull) { SpAddMasterFileSectionToCopyList( SifHandle, DiskFileLists, DiskCount, NtPartition, NULL, INDEX_WINNTFILE ); // // Add the section of system partition files that are always copied. // SpAddSectionFilesToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_SYSPARTCOPYALWAYS, SystemPartition, SystemPartitionDirectory, COPY_ALWAYS, (BOOLEAN)(SystemPartitionRegion->Filesystem == FilesystemNtfs) ); // // Add conditional files to the copy list. // SpAddConditionalFilesToCopyList( SifHandle, DiskFileLists, DiskCount, NtPartition, SystemPartition, SystemPartitionDirectory, Uniprocessor ); } else { PHARDWARE_COMPONENT pHw; // // Add the section of system partition files that are always copied. // SpAddSectionFilesToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_SYSPARTCOPYALWAYS, SystemPartition, SystemPartitionDirectory, COPY_ALWAYS, (BOOLEAN)(SystemPartitionRegion->Filesystem == FilesystemNtfs) ); // // Add the detected scsi miniport drivers to the copy list. // Note that they are always copied to the target. // These files have to be added to the copy list, before the ones marked // as COPY_ONLY_IF_PRESENT. This is because in most cases, these files // will be listed in [Files] with COPY_ONLY_IF_PRESENT set, and the // function that creates entries in the copy list, will not create more // than one entry for the same file. So if we add the file to the copy // list, with COPY_ONLY_IF_PRESENT, there will be no way to replace // or overwrite this entry in the list, and the file will end up not // being copied. // // we just use the filename specified in [SCSI.Load] -- // no need for separate [files.xxxx] sections. // if( !PreInstall || ( PreinstallScsiHardware == NULL ) ) { pHw = ScsiHardware; } else { pHw = PreinstallScsiHardware; } for( ; pHw; pHw=pHw->Next) { if(!pHw->ThirdPartyOptionSelected) { SpAddSingleFileToCopyList( SifHandle, DiskFileLists, DiskCount, L"SCSI.Load", pHw->IdString, 0, NtPartition, NULL, COPY_ALWAYS, FALSE ); } } // // Add the files in the master file list with the copy options // specified in each line on the INDEX_UPGRADE index. The options // specify whether the file is to be copied at all or copied always // or copied only if there on the target or not copied if there on // the target. // SpAddMasterFileSectionToCopyList( SifHandle, DiskFileLists, DiskCount, NtPartition, NULL, INDEX_UPGRADE ); // // Add the section of files that are upgraded only if it is not // a Win31 upgrade // if(WinUpgradeType != UpgradeWin31) { SpAddSectionFilesToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_FILESUPGRADEWIN31, NtPartition, NULL, COPY_ALWAYS, FALSE ); } // // Add the files for kernel, hal and detect module, these are // handled specially because they involve renamed files (it is // not possible to find out just by looking at the target file // how to upgrade it). // NOTE: This does not handle third-party HAL's (they get copied // by SpCopyThirdPartyDrivers() below). // SpAddHalKrnlDetToCopyList( SifHandle, DiskFileLists, DiskCount, NtPartition, SystemPartition, SystemPartitionDirectory, Uniprocessor ); // // Add the new hive files so that our config stuff can get at them // to extract new configuration information. These new hive files // are renamed on the target so that they don't overwrite the // existing hives. SpAddSectionFilesToCopyList( SifHandle, DiskFileLists, DiskCount, SIF_FILESNEWHIVES, NtPartition, NULL, COPY_ALWAYS, FALSE ); } // // Copy third-party files. // We do this here just in case there is some error in the setup information // file -- we'd have caught it by now, before we start copying files to the // user's hard drive. // NOTE: SpCopyThirdPartyDrivers has a check to make sure it only copies the // HAL and PAL if we're in an upgrade (in which case, we want to leave the other // drivers alone). // SpCopyThirdPartyDrivers( ThirdPartySourceDevicePath, NtPartition, Sysroot, SystemPartition, SystemPartitionDirectory, DiskFileLists, DiskCount ); #if 0 KdPrint( ("SETUP: Sysroot = %ls \n", Sysroot ) ); KdPrint( ("SETUP: SystemPartitionDirectory = %ls \n", SystemPartitionDirectory )); KdPrint( ("SETUP: SourceDevicePath = %ls \n", SourceDevicePath )); KdPrint( ("SETUP: DirectoryOnSourceDevice = %ls \n", DirectoryOnSourceDevice )); KdPrint( ("SETUP: ThirdPartySourceDevicePath = %ls \n", ThirdPartySourceDevicePath )); // SpCreateSetupLogFile( DiskFileLists, DiskCount, NtPartitionRegion, Sysroot, DirectoryOnSourceDevice ); #endif // // Copy files in the copy list. // SpCopyFilesInCopyList( SifHandle, DiskFileLists, DiskCount, SourceDevicePath, DirectoryOnSourceDevice, Sysroot ); #ifdef _X86_ // // Take care of ntbootdd.sys. // SpCreateNtbootddSys( NtPartitionRegion, NtPartition, Sysroot, SystemPartition ); #endif if( PreInstall ) { SppCopyOemDirectories( SourceDevicePath, NtPartition, Sysroot ); } // // Create the log file in disk // if( _SetupLogFile != NULL ) { PWSTR p; PWSTR TempName; PWSTR Values[] = { SIF_NEW_REPAIR_NT_VERSION }; // // Merge in the OEM files retrived from the previous setup.log // if(_LoggedOemFiles) { SppMergeLoggedOemFiles(_SetupLogFile, _LoggedOemFiles, SystemPartition, ( *SystemPartitionDirectory != (WCHAR)'\0' )? SystemPartitionDirectory : ( PWSTR )L"\\", NtPartition ); SpFreeTextFile(_LoggedOemFiles); } // // Add signature // SpAddLineToSection( _SetupLogFile, SIF_NEW_REPAIR_SIGNATURE, SIF_NEW_REPAIR_VERSION_KEY, Values, 1 ); // // Add section that contains the paths // Values[0] = SystemPartition; SpAddLineToSection( _SetupLogFile, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_SYSTEM_PARTITION_DEVICE, Values, 1 ); Values[0] = ( *SystemPartitionDirectory != (WCHAR)'\0' )? SystemPartitionDirectory : ( PWSTR )L"\\"; SpAddLineToSection( _SetupLogFile, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_SYSTEM_PARTITION_DIRECTORY, Values, 1 ); Values[0] = NtPartition; SpAddLineToSection( _SetupLogFile, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_TARGET_DEVICE, Values, 1 ); Values[0] = Sysroot; SpAddLineToSection( _SetupLogFile, SIF_NEW_REPAIR_PATHS, SIF_NEW_REPAIR_PATHS_TARGET_DIRECTORY, Values, 1 ); // // Flush to disk // TempName = SpMemAlloc( ( wcslen( SETUP_REPAIR_DIRECTORY ) + 1 + wcslen( SETUP_LOG_FILENAME ) + 1 ) * sizeof( WCHAR ) ); if( TempName != NULL ) { wcscpy( TempName, SETUP_REPAIR_DIRECTORY ); SpConcatenatePaths(TempName, SETUP_LOG_FILENAME ); SpWriteSetupTextFile(_SetupLogFile,NtPartition,Sysroot,TempName); } else { KdPrint( ("SETUP: Out of memory. Unable to save = %ls \n", SETUP_LOG_FILENAME )); } SpMemFree( TempName ); SpFreeTextFile( _SetupLogFile ); _SetupLogFile = NULL; } // // Free the media descriptors. // SpFreeCopyLists(&DiskFileLists,DiskCount); SpMemFree(NtPartition); SpMemFree(SystemPartition); // // Terminate diamond. // SpdTerminate(); } VOID SppDeleteFilesInSection( IN PVOID SifHandle, IN PWSTR SifSection, IN PDISK_REGION NtPartitionRegion, IN PWSTR Sysroot ) /*++ Routine Description: This routine enumerates files listed in the given section and deletes them from the system tree. Arguments: SifHandle - supplies handle to loaded setup information file. SifSection - section containing files to delete NtPartitionRegion - region descriptor for volume on which nt resides. Sysroot - root directory for nt. Return Value: None. --*/ { ULONG Count,u; PWSTR filename, dirordinal, targetdir, ntdir; NTSTATUS Status; CLEAR_CLIENT_SCREEN(); // // Get the device path of the nt partition. // SpNtNameFromRegion( NtPartitionRegion, (PWSTR)TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); SpConcatenatePaths((PWSTR)TemporaryBuffer,Sysroot); ntdir = SpDupStringW((PWSTR)TemporaryBuffer); // // Determine the number of files listed in the section. // This value may be zero. // Count = SpCountLinesInSection(SifHandle,SifSection); for(u=0; uFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { return TRUE; // continue processing } // // We have to make a copy of the filename, because the info struct // we get isn't NULL-terminated. // wcsncpy( (PWSTR)TemporaryBuffer, FileInfo->FileName, FileInfo->FileNameLength ); ((PWSTR)TemporaryBuffer)[FileInfo->FileNameLength >> 1] = UNICODE_NULL; FileName = SpDupStringW((PWSTR)TemporaryBuffer); // // display status bar // SpDisplayStatusText( SP_STAT_DELETING_FILE, DEFAULT_STATUS_ATTRIBUTE, FileName ); // // Ignore return status of delete // SpDeleteFile(DirName, FileName, NULL); SpMemFree(FileName); return TRUE; // continue processing } VOID SpLogOneFile( IN PFILE_TO_COPY FileToCopy, IN PWSTR Sysroot, IN PWSTR DirectoryOnSourceDevice, IN PWSTR DiskDescription, IN PWSTR DiskTag, IN ULONG CheckSum ) { PWSTR Values[ 5 ]; LPWSTR NtPath; ULONG ValueCount; PFILE_TO_COPY p; WCHAR CheckSumString[ 9 ]; if( _SetupLogFile == NULL ) { return; } Values[ 1 ] = CheckSumString; Values[ 2 ] = DirectoryOnSourceDevice; Values[ 3 ] = DiskDescription; Values[ 4 ] = DiskTag; swprintf( CheckSumString, ( LPWSTR )L"%lx", CheckSum ); p = FileToCopy; #if 0 KdPrint( ("SETUP: Source Name = %ls, \t\tTargetDirectory = %ls \t\tTargetName = %ls\t\tTargetDevice = %ls, \tAbsoluteDirectory = %d \n", p->SourceFilename, p->TargetDirectory, p->TargetFilename, p->TargetDevicePath, p->AbsoluteTargetDirectory )); #endif Values[0] = p->SourceFilename; ValueCount = ( DirectoryOnSourceDevice == NULL )? 2 : 5; if( ( Sysroot == NULL ) || ( wcslen( p->TargetDirectory ) == 0 ) ) { SpAddLineToSection( _SetupLogFile, SIF_NEW_REPAIR_SYSPARTFILES, p->TargetFilename, Values, ValueCount ); } else { NtPath = SpDupStringW( Sysroot ); NtPath = SpMemRealloc( NtPath, sizeof( WCHAR ) * ( wcslen( Sysroot ) + wcslen( p->TargetDirectory ) + wcslen( p->TargetFilename ) + 2 + // for possible two extra back slashes 1 // for the terminating NULL ) ); SpConcatenatePaths( NtPath, p->TargetDirectory ); SpConcatenatePaths( NtPath, p->TargetFilename ); SpAddLineToSection( _SetupLogFile, SIF_NEW_REPAIR_WINNTFILES, NtPath, Values, ValueCount ); SpMemFree( NtPath ); } } PVOID SppRetrieveLoggedOemFiles( PVOID OldLogFile ) { PVOID NewLogFile; BOOLEAN OldFormatSetupLogFile, FilesRetrieved = FALSE; PWSTR SectionName[2]; ULONG FileCount, SectionIndex, i; PWSTR TargetFileName; PWSTR OemDiskDescription, OemDiskTag, OemSourceDirectory; PWSTR Values[5]; // // Create a new setup.log file to merge the OEM files into // NewLogFile = SpNewSetupTextFile(); if(!NewLogFile) { KdPrint(("SETUP: Unable to create new setup.log buffer for OEM merging.\n")); return NULL; } // // Determine whether setup.log has the new or old style // if(OldFormatSetupLogFile = !IsSetupLogFormatNew(OldLogFile)) { SectionName[0] = SIF_REPAIRSYSPARTFILES; SectionName[1] = SIF_REPAIRWINNTFILES; } else { SectionName[0] = SIF_NEW_REPAIR_SYSPARTFILES; SectionName[1] = SIF_NEW_REPAIR_WINNTFILES; } if(OldFormatSetupLogFile) { // // I don't know if we even want to mess with this. // The format of setup.log in NT 3.1 makes it impossible // to identify any OEM files except for SCSI files, and // even then the tagfile name is lost. I would have to use // the driver filename itself as a substitute for the tagfile // name (which is what NT 3.1 repair did--UGGHH!!) // } else { // // Retrieve logged OEM files first from system partition, then // from winnt directory. // for(SectionIndex = 0; SectionIndex < 2; SectionIndex++) { FileCount = SpCountLinesInSection(OldLogFile, SectionName[SectionIndex]); for(i=0; iFileList; pListEntry; pListEntry=pListEntry->Next) { if(!_wcsicmp(pListEntry->TargetFilename,TargetFilename) && !_wcsicmp(pListEntry->TargetDirectory,TargetDirectory) && !_wcsicmp(pListEntry->TargetDevicePath,TargetDevicePath) && (pListEntry->AbsoluteTargetDirectory == AbsoluteTargetDirectory)) { pListEntry->Flags &= ~COPY_DISPOSITION_MASK; pListEntry->Flags |= COPY_NEVER; KdPrint(( "SETUP: SpRemoveEntryFromCopyList() removed %ls from copy list \n", TargetFilename )); return( TRUE ); } } } // KdPrint(( "SETUP: SpRemoveEntryFromCopyList() failed to remove %ls from copy list \n", TargetFilename )); return( FALSE ); } NTSTATUS SpMoveFileOrDirectory( IN PWSTR SrcPath, IN PWSTR DestPath ) /*++ Routine Description: This routine attempts to move a source file or directory, to a target file or directory. Note: This function will fail if the source and destination paths do not point to the same volume. Arguments: SrcPath: Absolute path to the source file or directory. This path should include the path to the source device. DestPath: Absolute path to the destination file or directory. This path should include the path to the source device. Return Value: NTSTATUS --*/ { OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING SrcName; HANDLE hSrc; NTSTATUS Status; WCHAR RenameFileInfoBuffer[ MAX_PATH + 20 ]; PFILE_RENAME_INFORMATION RenameFileInfo; // // Initialize names and attributes. // INIT_OBJA(&Obja,&SrcName,SrcPath); Status = ZwCreateFile( &hSrc, FILE_GENERIC_READ, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, 0, NULL, 0 ); if( !NT_SUCCESS( Status ) ) { KdPrint(("SETUP: Unable to open source file %ws. Status = %lx\n",SrcPath, Status)); return( Status ); } RenameFileInfo = (PFILE_RENAME_INFORMATION)RenameFileInfoBuffer; RenameFileInfo->ReplaceIfExists = TRUE; RenameFileInfo->RootDirectory = NULL; RenameFileInfo->FileNameLength = wcslen( DestPath )*sizeof( WCHAR ); RtlMoveMemory(RenameFileInfo->FileName,DestPath,(wcslen( DestPath )+1)*sizeof(WCHAR)); Status = ZwSetInformationFile( hSrc, &IoStatusBlock, RenameFileInfo, sizeof(RenameFileInfoBuffer), FileRenameInformation ); if(!NT_SUCCESS(Status)) { KdPrint(("SETUP: unable to set attribute on %ws. Status = %lx\n",SrcPath, Status)); } ZwClose(hSrc); return( Status ); } BOOLEAN SppCopyDirRecursiveCallback( IN PWSTR SrcPath, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT PULONG ReturnData, IN PVOID DestPath ) /*++ Routine Description: This routine is called by the file enumerator as a callback for each file or subdirectory found in the source directory. If FileInfo represents a file, then we copy the file to the destination. If FileInfo represents a directory, then we copy the directory recursivelly. Arguments: SrcPath - Absolute path to the source directory. This path should contain the path to the source device. FileInfo - supplies find data for a file in the source dir. ReturnData - receives an error code if an error occurs. We ignore errors in this routine and thus we always just fill this in with NO_ERROR. DestPath - Absolute path to the destination directory. This path must contain the path to the destination device. Return Value: Always TRUE. --*/ { PWSTR p, q; PWSTR temp; ULONG Len; NTSTATUS Status; *ReturnData = NO_ERROR; // // Build the new SrcPath // Note how we use the temporary buffer. Be careful if you // change this code. // temp = (PWSTR)(TemporaryBuffer + (sizeof(TemporaryBuffer)/2)); Len = FileInfo->FileNameLength/sizeof(WCHAR); wcsncpy(temp,FileInfo->FileName,Len); temp[Len] = 0; wcscpy((PWSTR)TemporaryBuffer,SrcPath); SpConcatenatePaths((PWSTR)TemporaryBuffer,temp); p = SpDupStringW((PWSTR)TemporaryBuffer); if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // // This is a directory // if( (wcscmp( temp, L"." ) != 0) && (wcscmp( temp, L".." ) != 0) ) { q = SpDupStringW(temp); SpCopyDirRecursive( p, (PWSTR)DestPath, q ); SpMemFree(q); } } else { // // It is a file. Copy the file // wcscpy((PWSTR)TemporaryBuffer,(PWSTR)DestPath); SpConcatenatePaths((PWSTR)TemporaryBuffer,temp); q = SpDupStringW((PWSTR)TemporaryBuffer); SpCopyFilesScreenRepaint(p, NULL, FALSE); // if( SpFileExists( p, FALSE ) ) { // SpDeleteFile( p, NULL, NULL ); // } Status = SpCopyFileUsingNames( p, q, 0, COPY_DELETESOURCE ); if( !NT_SUCCESS( Status ) ) { KdPrint(("SETUP: unable copy %ws. Status = %lx\n", p, Status)); SpCopyFilesScreenRepaint(L"", NULL, TRUE); } SpMemFree(q); } SpMemFree(p); return(TRUE); } VOID SpCopyDirRecursive( IN PWSTR SrcPath, IN PWSTR DestDevPath, IN PWSTR DestDirPath ) /*++ Routine Description: This routine recursively copies a src directory to a destination directory. Arguments: SrcPath: Absolute path to the source directory. This path should include the path to the source device. DestDevPath: Path to the destination device. DestDirPath: Path to the destination directory. Return Value: None. --*/ { ULONG n; PWSTR p; PWSTR r; NTSTATUS Status; // // Create the target directory // wcscpy((PWSTR)TemporaryBuffer, DestDevPath); SpConcatenatePaths((PWSTR)TemporaryBuffer, DestDirPath); p = SpDupStringW((PWSTR)TemporaryBuffer); if( !SpFileExists( p, TRUE ) ) { // // If the directory doesn't exist, then try to move (rename) the // source directory. // Status = SpMoveFileOrDirectory( SrcPath, p ); if( NT_SUCCESS( Status ) ) { SpMemFree(p); return; } // // If unable to rename the source directory, then create the // target directory // SpCreateDirectory( DestDevPath, NULL, DestDirPath ); SpCopyFilesScreenRepaint(L"", NULL, TRUE); } // // Enumerate and copy all files in the source directory to the // destination directory. // SpEnumFiles(SrcPath, SppCopyDirRecursiveCallback, &n, p); SpMemFree(p); } VOID SppCopyOemDirectories( IN PWSTR SourceDevicePath, IN PWSTR NtPartition, IN PWSTR Sysroot ) /*++ Routine Description: This routine recursively copies a src directory to a destination directory. Arguments: SourceDevicePath: Path to the device that contains the source. NtPartition: Path to the drive that contains the system. Systroot: Directory where the system is installed. Return Value: None. --*/ { PWSTR r, s, t; WCHAR Drive[3]; PDISK_REGION TargetRegion; // // Check if the subdirectory $OEM$\\$$ exists on the source directory. // If it exists, then tree copy the directory on top of %SystemRoot% // wcscpy((PWSTR)TemporaryBuffer, SourceDevicePath); SpConcatenatePaths( (PWSTR)TemporaryBuffer, PreinstallOemSourcePath ); r = wcsrchr( (PWSTR)TemporaryBuffer, (WCHAR)'\\' ); if( r != NULL ) { *r = (WCHAR)'\0'; } SpConcatenatePaths( (PWSTR)TemporaryBuffer, WINNT_OEM_FILES_SYSROOT_W ); r = SpDupStringW((PWSTR)TemporaryBuffer); if( SpFileExists( r, TRUE ) ) { SpCopyFilesScreenRepaint(L"", NULL, TRUE); SpCopyDirRecursive( r, NtPartition, Sysroot ); } SpMemFree( r ); // // Copy the subdirectories $OEM$\ to the root of each // corresponding drive. // These directories are: // // $OEM$\C // $OEM$\D // $OEM$\E // . // . // . // $OEM$\Z // // wcscpy((PWSTR)TemporaryBuffer, SourceDevicePath); SpConcatenatePaths( (PWSTR)TemporaryBuffer, PreinstallOemSourcePath ); r = wcsrchr( (PWSTR)TemporaryBuffer, (WCHAR)'\\' ); if( r != NULL ) { *r = (WCHAR)'\0'; } SpConcatenatePaths( (PWSTR)TemporaryBuffer, L"\\C" ); r = SpDupStringW((PWSTR)TemporaryBuffer); s = wcsrchr( r, (WCHAR)'\\' ); s++; Drive[1] = (WCHAR)':'; Drive[2] = (WCHAR)'\0'; for( Drive[0] = (WCHAR)'C'; Drive[0] <= (WCHAR)'Z'; Drive[0] = Drive[0] + 1) { // // If the subdirectory $OEM$\ exists on the source, // and if there is a FAT or NTFS partition in the target machine that // has the same drive letter specification, then tree copy // $OEM$\ to the corresponding partition in the target // machine. // *s = Drive[0]; if( SpFileExists( r, TRUE ) ) { if( ( ( TargetRegion = SpRegionFromDosName( Drive ) ) != NULL ) && TargetRegion->PartitionedSpace && ( ( TargetRegion->Filesystem == FilesystemFat ) || ( TargetRegion->Filesystem == FilesystemNtfs ) ) ) { SpNtNameFromRegion( TargetRegion, (PWSTR)TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalCurrent ); t = SpDupStringW((PWSTR)TemporaryBuffer); SpCopyDirRecursive( r, t, L"" ); SpMemFree( t ); } } } SpMemFree( r ); // // Merge %SystemRoot%\$$rename.txt with $$rename.txt in the root of the // NT partition. // SppMergeRenameFiles( SourceDevicePath, NtPartition, Sysroot ); } VOID SppMergeRenameFiles( IN PWSTR SourceDevicePath, IN PWSTR NtPartition, IN PWSTR Sysroot ) /*++ Routine Description: This routine recursively copies a src directory to a destination directory. Arguments: SourceDevicePath: Path to the device that contains the source. NtPartition: Path to the drive that contains the system. Systroot: Directory where the system is installed. Return Value: None. --*/ { PWSTR r, s; PDISK_REGION TargetRegion; NTSTATUS Status; PVOID RootRenameFile; PVOID SysrootRenameFile; ULONG ErrorLine; ULONG SectionCount; ULONG LineCount; ULONG i,j; PWSTR SectionName; PWSTR NewSectionName; PWSTR KeyName; PWSTR Values[1]; PFILE_TO_RENAME File; // // Build the ful path to %sysroot%\$$rename.txt // wcscpy((PWSTR)TemporaryBuffer, NtPartition); SpConcatenatePaths( (PWSTR)TemporaryBuffer, Sysroot ); SpConcatenatePaths( (PWSTR)TemporaryBuffer, WINNT_OEM_LFNLIST_W ); s = SpDupStringW((PWSTR)TemporaryBuffer); // // Load %sysroot%\$$rename.txt, if one exists // if( SpFileExists( s, FALSE ) ) { // // Load Sysroot\$$rename.txt // Status = SpLoadSetupTextFile( s, NULL, 0, &SysrootRenameFile, &ErrorLine ); if( !NT_SUCCESS( Status ) ) { KdPrint(("SETUP: Unable to load file %ws. Status = %lx \n", s, Status )); goto merge_rename_exit; } } else { SysrootRenameFile = NULL; } // // If there is a $$rename.txt on sysroot, then it needs to be merged // (or appended) to the one in the NtPartition. // If RenameList is not empty, then the files in this list need to be // added to $$rename.txt on the NtPartition. // Otherwise, don't do any merge. // if( ( SysrootRenameFile != NULL ) || ( RenameList != NULL ) ) { // // Find out if the NtPartition contains a $$rename.txt // wcscpy((PWSTR)TemporaryBuffer, NtPartition); SpConcatenatePaths( (PWSTR)TemporaryBuffer, WINNT_OEM_LFNLIST_W ); r = SpDupStringW((PWSTR)TemporaryBuffer); if( !SpFileExists( r, FALSE ) ) { // // If the NT partition doesn't contain $$rename.txt, then // create a new $$rename.txt in memory // RootRenameFile = SpNewSetupTextFile(); if( RootRenameFile == NULL ) { KdPrint(("SETUP: SpNewSetupTextFile() failed \n")); if( SysrootRenameFile != NULL ) { SpFreeTextFile( SysrootRenameFile ); } SpMemFree( r ); goto merge_rename_exit; } } else { // // Load $$rename on the NTPartition // Status = SpLoadSetupTextFile( r, NULL, 0, &RootRenameFile, &ErrorLine ); if( !NT_SUCCESS( Status ) ) { KdPrint(("SETUP: Unable to load file %ws. Status = %lx \n", r, Status )); if( SysrootRenameFile != NULL ) { SpFreeTextFile( SysrootRenameFile ); } SpMemFree( r ); goto merge_rename_exit; } } if( SysrootRenameFile != NULL ) { // // Add the section of Sysroot\$$rename.txt to $$rename.txt in memory // Note that we need to prepend Sysroot to the section name // SectionCount = SpCountSectionsInFile( SysrootRenameFile ); for( i = 0; i < SectionCount; i++ ) { SectionName = SpGetSectionName( SysrootRenameFile, i ); if( SectionName != NULL ) { wcscpy((PWSTR)TemporaryBuffer, L"\\"); SpConcatenatePaths( (PWSTR)TemporaryBuffer, Sysroot); SpConcatenatePaths( (PWSTR)TemporaryBuffer, SectionName ); NewSectionName = SpDupStringW((PWSTR)TemporaryBuffer); LineCount = SpCountLinesInSection( SysrootRenameFile, SectionName ); for( j = 0; j < LineCount; j++ ) { KeyName = SpGetKeyName( SysrootRenameFile, SectionName, j ); Values[0] = SpGetSectionKeyIndex( SysrootRenameFile, SectionName, KeyName, 0 ); SpAddLineToSection( RootRenameFile, NewSectionName, KeyName, Values, 1 ); } SpMemFree( NewSectionName ); } } // // $$rename.txt on Sysroot is no longer needed // SpFreeTextFile( SysrootRenameFile ); SpDeleteFile( s, NULL, NULL ); } // // Add the files in RenameList to \$$rename.txt // if( RenameList != NULL ) { do { File = RenameList; RenameList = File->Next; Values[0] = File->TargetFilename; SpAddLineToSection( RootRenameFile, File->TargetDirectory, File->SourceFilename, Values, 1 ); SpMemFree( File->SourceFilename ); SpMemFree( File->TargetFilename ); SpMemFree( File->TargetDirectory ); SpMemFree( File ); } while( RenameList != NULL ); } // // Create a new \$$rename.txt // SpWriteSetupTextFile( RootRenameFile, r, NULL, NULL ); // // $$rename.txt on memory is no longer needed // SpFreeTextFile( RootRenameFile ); } merge_rename_exit: SpMemFree( s ); }