#include "precomp.h" #include "SetupSxs.h" #include "sputils.h" #pragma hdrstop // // Structure used to contain data about each directory // containing files we will copy from the source(s). // typedef struct _DIR { struct _DIR *Next; #ifndef SLOWER_WAY struct _DIR *Prev; #endif // // Symbol in main inf [Directories] section. // May be NULL. // LPCTSTR InfSymbol; // // Flags. // UINT Flags; // // In some cases files come from one directory on the source // but go to a different directory on the target. // LPCTSTR SourceName; LPCTSTR TargetName; } DIR, *PDIR; #define WINDOWS_DEFAULT_PFDOC_SIZE 81112 #define DIR_NEED_TO_FREE_SOURCENAME 0x00000001 #define DIR_ABSOLUTE_PATH 0x00000002 #define DIR_USE_SUBDIR 0x00000004 // If DIR_IS_PLATFORM_INDEPEND is passed to AddDirectory, then // all the files it enumerates will have their FILE_IN_PLATFORM_INDEPEND_DIR // flag set and then they will get copied to c:\$win_nt$.~ls instead of // c:\$win_nt$.~ls\ #define DIR_IS_PLATFORM_INDEPEND 0x00000008 #define DIR_SUPPORT_DYNAMIC_UPDATE 0x00000010 #define DIR_DOESNT_SUPPORT_PRIVATES 0x00000020 // // Dummy directory id we use for sections in the in (like [RootBootFiles]) // that don't have a directory specifier in the inf // #define DUMMY_DIRID TEXT("**") typedef struct _FIL { // // Size of file. // ULONGLONG Size; struct _FIL *Next; #ifndef SLOWER_WAY struct _FIL *Prev; #endif // // Directory information for the file. // PDIR Directory; // // Name of file on source. // LPCTSTR SourceName; // // Name of file on target. // LPCTSTR TargetName; UINT Flags; // // Bitmap used to track which threads have had a crack at // copying this file. // UINT ThreadBitmap; } FIL, *PFIL; #define FILE_NEED_TO_FREE_SOURCENAME 0x00000001 #define FILE_NEED_TO_FREE_TARGETNAME 0x00000002 #define FILE_ON_SYSTEM_PARTITION_ROOT 0x00000004 #define FILE_IN_PLATFORM_INDEPEND_DIR 0x00000008 #define FILE_PRESERVE_COMPRESSED_NAME 0x00000010 #define FILE_DECOMPRESS 0x00000020 #define FILE_IGNORE_COPY_ERROR 0x00000040 #define FILE_DO_NOT_COPY 0x00000080 #if defined(REMOTE_BOOT) #define FILE_ON_MACHINE_DIRECTORY_ROOT 0x00000100 // for remote boot #endif // defined(REMOTE_BOOT) // // This flag is really only meaningful on amd64/x86. It means that the file // is in the \$win_nt$.~bt directory on the system partition and not in // \$win_nt$.~ls. // #define FILE_IN_LOCAL_BOOT 0x80000000 // // This flag indicates that the file is not part of the product, // and should be migrated from the current NT system. When this flag is // set, the file should be moved to the $win_nt$.~bt directory (amd64/x86), // or to the $win_nt$.~ls\alpha directory (alpha). // This flag is not valid on Win95. // #define FILE_NT_MIGRATE 0x40000000 typedef struct _COPY_LIST { PDIR Directories; PFIL Files; UINT DirectoryCount; UINT FileCount; // // These members aren't initialized until we actually start // the copying. // CRITICAL_SECTION CriticalSection; BOOL ActiveCS; HANDLE StopCopyingEvent; HANDLE ListReadyEvent[MAX_SOURCE_COUNT]; HANDLE Threads[MAX_SOURCE_COUNT]; ULONGLONG SpaceOccupied[MAX_SOURCE_COUNT]; HWND hdlg; } COPY_LIST, *PCOPY_LIST; typedef struct _BUILD_LIST_THREAD_PARAMS { // // Copy list that gets built up by the thread. // It's a private list; the main thread merges all of these together // into the master list later. // COPY_LIST CopyList; TCHAR SourceRoot[MAX_PATH]; TCHAR CurrentDirectory[MAX_PATH]; TCHAR DestinationDirectory[MAX_PATH]; WIN32_FIND_DATA FindData; DWORD OptionalDirFlags; } BUILD_LIST_THREAD_PARAMS, *PBUILD_LIST_THREAD_PARAMS; // // Define structure used with the file copy error dialog. // typedef struct _COPY_ERR_DLG_PARAMS { LPCTSTR SourceFilename; LPCTSTR TargetFilename; UINT Win32Error; } COPY_ERR_DLG_PARAMS,*PCOPY_ERR_DLG_PARAMS; typedef struct _NAME_AND_SIZE_CAB { LPCTSTR Name; ULONGLONG Size; } NAME_AND_SIZE_CAB, *PNAME_AND_SIZE_CAB; COPY_LIST MasterCopyList; BOOL MainCopyStarted; // // Names of relevent inf sections. // LPCTSTR szDirectories = TEXT("Directories"); LPCTSTR szFiles = TEXT("Files"); LPCTSTR szDiskSpaceReq = TEXT("DiskSpaceRequirements"); LPCTSTR szPFDocSpaceReq = TEXT("PFDocSpace"); // // Amount of space we need when scanning all the // drives for a place to put the temporary files. // ULONGLONG MinDiskSpaceRequired; ULONGLONG MaxDiskSpaceRequired; TCHAR DiskDiagMessage[5000]; // // Amount of space occupied by the files in the master copy list, // on the local source drive. // DWORD LocalSourceDriveClusterSize; ULONGLONG TotalDataCopied = 0; DWORD BuildCopyListForOptionalDirThread( IN PVOID ThreadParam ); DWORD AddFilesInDirToCopyList( IN OUT PBUILD_LIST_THREAD_PARAMS Params ); PDIR AddDirectory( IN LPCTSTR InfSymbol, OPTIONAL IN OUT PCOPY_LIST CopyList, IN LPCTSTR SourceName, IN LPCTSTR TargetName, OPTIONAL IN UINT Flags ); PFIL AddFile( IN OUT PCOPY_LIST CopyList, IN LPCTSTR SourceFilename, IN LPCTSTR TargetFilename, OPTIONAL IN PDIR Directory, IN UINT Flags, IN ULONGLONG FileSize OPTIONAL ); DWORD AddSection( IN PVOID Inf, IN OUT PCOPY_LIST CopyList, IN LPCTSTR SectionName, OUT UINT *ErrorLine, IN UINT FileFlags, IN BOOL SimpleList, IN BOOL DoDriverCabPruning ); PDIR LookUpDirectory( IN PCOPY_LIST CopyList, IN LPCTSTR DirSymbol ); VOID TearDownCopyList( IN OUT PCOPY_LIST CopyList ); DWORD CopyOneFile( IN PFIL File, IN UINT SourceOrdinal, OUT PTSTR TargetFilename, IN INT CchTargetFilename, OUT ULONGLONG *SpaceOccupied ); INT_PTR CopyErrDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ); UINT DiamondCallback( IN PVOID Context, IN UINT Code, IN UINT_PTR Param1, IN UINT_PTR Param2 ); LRESULT DiskDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ); BOOL BuildCopyListWorker( IN HWND hdlg ) /*++ Routine Description: Worker routine to build the queue of file to be copied. Arguments: hdlg - window handle for any UI updates. Return Value: TRUE/FALSE. If the call succeeds, TRUE is returned and the global MasterCopyList structure is ready to be copied. --*/ { BOOL b; UINT u; UINT source; UINT thread; DWORD d = NO_ERROR; DWORD TempError; BUILD_LIST_THREAD_PARAMS BuildParams[MAX_OPTIONALDIRS]; BUILD_LIST_THREAD_PARAMS bltp; HANDLE BuildThreads[MAX_OPTIONALDIRS]; DWORD ThreadId; LPCTSTR Id,DirectoryName; PDIR DirectoryStruct; PFIL FileStruct; UINT ErrorLine; UINT i; TCHAR c; BOOL AllSourcesLocal; TCHAR floppynum[40]; LPCTSTR DummyDirectoryName; TCHAR buffer[MAX_PATH]; PTSTR p; TCHAR SourceDirectory[MAX_PATH]; WIN32_FIND_DATA fd; #ifdef PRERELEASE LONG lines1, lines2, l; TCHAR* dirNames; PDIR* dirStructs; PCTSTR src, dst; TCHAR tmp[MAX_PATH]; #endif TearDownCopyList(&MasterCopyList); DebugLog (Winnt32LogDetailedInformation, TEXT("Building Copy list."), 0); // // If NOLs was specified on the command line, and the user // did not specify to make a local source, and we have // exactly one source that is a hdd, then turn of ls and // use that drive. // if (MakeLocalSource && NoLs && !UserSpecifiedMakeLocalSource) { if (SourceCount == 1 && MyGetDriveType (*SourcePaths[0]) == DRIVE_FIXED) { MakeLocalSource = FALSE; DebugLog (Winnt32LogDetailedInformation, TEXT("Not making local source."), 0); } } #ifdef PRERELEASE if( !BuildCmdcons) { DebugLog (Winnt32LogDetailedInformation, TEXT("Adding SymbolDirs to copylist"), 0); // // for internal debugging, also copy all the .pdb files listed // in dosnet.inf // lines1 = InfGetSectionLineCount (MainInf, TEXT("SymbolDirs")); lines2 = InfGetSectionLineCount (MainInf, TEXT("SymbolFiles")); if (lines1 > 0 && lines2 > 0) { dirNames = (TCHAR*) MALLOC (lines1 * MAX_PATH * sizeof (TCHAR)); dirStructs = (PDIR*) MALLOC (lines1 * sizeof (PDIR)); if (dirNames && dirStructs) { u = 0; for (l = 0; l < lines1; l++) { lstrcpy (tmp, SourcePaths[0]); src = InfGetFieldByIndex (MainInf, TEXT("SymbolDirs"), l, 0); if (!src) { continue; } ConcatenatePaths (tmp, src, MAX_PATH); if (!GetFullPathName (tmp, MAX_PATH, SourceDirectory, NULL)) { continue; } if (!FileExists (SourceDirectory, &fd) || !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { continue; } dst = InfGetFieldByIndex (MainInf, TEXT("SymbolDirs"), l, 1); if (!dst) { dst = TEXT("symbols"); } dirStructs[u] = AddDirectory ( NULL, &MasterCopyList, SourceDirectory, dst, DIR_ABSOLUTE_PATH | DIR_NEED_TO_FREE_SOURCENAME ); lstrcpyn (&dirNames[u * MAX_PATH], SourceDirectory, MAX_PATH); u++; } for (l = 0; l < lines2; l++) { src = InfGetFieldByIndex (MainInf, TEXT("SymbolFiles"), l, 0); if (!src) { continue; } for (i = 0; i < u; i++) { BuildPath (tmp, &dirNames[i * MAX_PATH], src); if (!FileExists (tmp, &fd) || (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { continue; } if (dirStructs[i]) { AddFile (&MasterCopyList, src, NULL, dirStructs[i], FILE_IGNORE_COPY_ERROR, fd.nFileSizeLow); break; } } } } } } #endif // // copy any downloaded drivers under the local boot directory, // together with updates.cab/.sif if they are present // if (DynamicUpdateSuccessful ()) { if (g_DynUpdtStatus->UpdatesCabSource[0]) { lstrcpy (buffer, g_DynUpdtStatus->UpdatesCabSource); p = _tcsrchr (buffer, TEXT('\\')); if (!p) { d = ERROR_INVALID_PARAMETER; goto c1; } *p++ = 0; DirectoryStruct = AddDirectory ( NULL, &MasterCopyList, buffer, NULL, DIR_ABSOLUTE_PATH | DIR_NEED_TO_FREE_SOURCENAME | DIR_DOESNT_SUPPORT_PRIVATES ); if (!DirectoryStruct) { d = ERROR_NOT_ENOUGH_MEMORY; goto c1; } FileStruct = AddFile ( &MasterCopyList, p, NULL, DirectoryStruct, FILE_IN_LOCAL_BOOT | FILE_NEED_TO_FREE_SOURCENAME, 0 ); if (!FileStruct) { d = ERROR_NOT_ENOUGH_MEMORY; goto c1; } // // now copy the SIF file // if (!BuildSifName (g_DynUpdtStatus->UpdatesCabSource, buffer, ARRAYSIZE(buffer))) { d = ERROR_INVALID_PARAMETER; goto c1; } p = _tcsrchr (buffer, TEXT('\\')); if (!p) { d = ERROR_INVALID_PARAMETER; goto c1; } *p++ = 0; // // the directory is the same as before // FileStruct = AddFile ( &MasterCopyList, p, NULL, DirectoryStruct, FILE_IN_LOCAL_BOOT | FILE_NEED_TO_FREE_SOURCENAME, 0 ); if (!FileStruct) { d = ERROR_NOT_ENOUGH_MEMORY; goto c1; } } if (g_DynUpdtStatus->DuasmsSource[0]) { ZeroMemory (&bltp, sizeof(bltp)); lstrcpy (bltp.CurrentDirectory, g_DynUpdtStatus->DuasmsSource); lstrcpy (bltp.DestinationDirectory, S_SUBDIRNAME_DUASMS); bltp.OptionalDirFlags = OPTDIR_TEMPONLY | OPTDIR_ABSOLUTE | OPTDIR_IN_LOCAL_BOOT | OPTDIR_DOESNT_SUPPORT_PRIVATES; d = AddFilesInDirToCopyList (&bltp); if(d != NO_ERROR) { goto c1; } // // Merge the copy list into the master copy list. // MasterCopyList.FileCount += bltp.CopyList.FileCount; MasterCopyList.DirectoryCount += bltp.CopyList.DirectoryCount; if(MasterCopyList.Directories) { #ifndef SLOWER_WAY if (bltp.CopyList.Directories) { PVOID p; p = bltp.CopyList.Directories->Prev; bltp.CopyList.Directories->Prev = MasterCopyList.Directories->Prev; MasterCopyList.Directories->Prev->Next = bltp.CopyList.Directories; MasterCopyList.Directories->Prev = p; } #else for(DirectoryStruct=MasterCopyList.Directories; DirectoryStruct->Next; DirectoryStruct=DirectoryStruct->Next) { ; } DirectoryStruct->Next = bltp.CopyList.Directories; #endif } else { MasterCopyList.Directories = bltp.CopyList.Directories; } if(MasterCopyList.Files) { #ifndef SLOWER_WAY if (bltp.CopyList.Files) { PVOID p; p = bltp.CopyList.Files->Prev; bltp.CopyList.Files->Prev = MasterCopyList.Files->Prev; MasterCopyList.Files->Prev->Next = bltp.CopyList.Files; MasterCopyList.Files->Prev = p ; } #else for(FileStruct=MasterCopyList.Files; FileStruct->Next; FileStruct=FileStruct->Next) { ; } FileStruct->Next = bltp.CopyList.Files; #endif } else { MasterCopyList.Files = bltp.CopyList.Files; } } if (g_DynUpdtStatus->NewDriversList) { ZeroMemory(&bltp,sizeof(bltp)); lstrcpy (bltp.CurrentDirectory, g_DynUpdtStatus->SelectedDrivers); lstrcpy (bltp.DestinationDirectory, S_SUBDIRNAME_DRIVERS); bltp.OptionalDirFlags = OPTDIR_TEMPONLY | OPTDIR_ABSOLUTE | OPTDIR_IN_LOCAL_BOOT | OPTDIR_DOESNT_SUPPORT_PRIVATES; d = AddFilesInDirToCopyList (&bltp); if(d != NO_ERROR) { goto c1; } // // Merge the copy list into the master copy list. // MasterCopyList.FileCount += bltp.CopyList.FileCount; MasterCopyList.DirectoryCount += bltp.CopyList.DirectoryCount; if(MasterCopyList.Directories) { #ifndef SLOWER_WAY if (bltp.CopyList.Directories) { PVOID p; p = bltp.CopyList.Directories->Prev; bltp.CopyList.Directories->Prev = MasterCopyList.Directories->Prev; MasterCopyList.Directories->Prev->Next = bltp.CopyList.Directories; MasterCopyList.Directories->Prev = p; } #else for(DirectoryStruct=MasterCopyList.Directories; DirectoryStruct->Next; DirectoryStruct=DirectoryStruct->Next) { ; } DirectoryStruct->Next = bltp.CopyList.Directories; #endif } else { MasterCopyList.Directories = bltp.CopyList.Directories; } if(MasterCopyList.Files) { #ifndef SLOWER_WAY if (bltp.CopyList.Files) { PVOID p; p = bltp.CopyList.Files->Prev; bltp.CopyList.Files->Prev = MasterCopyList.Files->Prev; MasterCopyList.Files->Prev->Next = bltp.CopyList.Files; MasterCopyList.Files->Prev = p ; } #else for(FileStruct=MasterCopyList.Files; FileStruct->Next; FileStruct=FileStruct->Next) { ; } FileStruct->Next = bltp.CopyList.Files; #endif } else { MasterCopyList.Files = bltp.CopyList.Files; } } } // // Add the mandatory optional dirs to the list of optional dirs. // These are specified in txtsetup.sif. // DebugLog (Winnt32LogDetailedInformation, TEXT("Adding OptionalSrcDirs to optional dirs."), 0); u = 0; while(DirectoryName = InfGetFieldByIndex(MainInf,TEXT("OptionalSrcDirs"),u++,0)) { TCHAR TempString[MAX_PATH]; RememberOptionalDir(DirectoryName,OPTDIR_TEMPONLY |OPTDIR_ADDSRCARCH | OPTDIR_SUPPORT_DYNAMIC_UPDATE); #if defined(_WIN64) lstrcpy( TempString, TEXT("..\\I386\\")); ConcatenatePaths(TempString, DirectoryName, MAX_PATH); //Also check if an I386 equivalent WOW directory exists AddCopydirIfExists( TempString, OPTDIR_TEMPONLY | OPTDIR_PLATFORM_INDEP ); #endif } // // and Fusion side by side assemblies, driven by syssetup.inf, if the directories exists DebugLog (Winnt32LogDetailedInformation, TEXT("Adding AssemblyDirectories to optional dirs."), 0); // { TCHAR SideBySideInstallShareDirectory[MAX_PATH]; // source DWORD FileAttributes = 0; PCTSTR DirectoryName = NULL; u = 0; while (DirectoryName = InfGetFieldByIndex(MainInf, SXS_INF_ASSEMBLY_DIRECTORIES_SECTION_NAME, u++, 0)) { // // convention introduced specifically for side by side, so that // x86 files on amd64/ia64 might come from \i386\asms instead of \ia64\asms\i386, // depending on what dosnet.inf and syssetup.inf say: // a path that does not start with a slash is appended to \$win_nt$.~ls\processor // and \installShare\processor // (or cdromDriveLetter:\processor) // a path that does start with a slash is appended to \$win_nt$.~ls // and \installShare // (or cdromDriveLetter:\) DWORD FileAttributes; BOOL StartsWithSlash = (DirectoryName[0] == '\\' || DirectoryName[0] == '/'); lstrcpyn(SideBySideInstallShareDirectory, SourcePaths[0], MAX_PATH); if (StartsWithSlash) { DirectoryName += 1; // skip slash } else { ConcatenatePaths(SideBySideInstallShareDirectory, InfGetFieldByKey(MainInf, TEXT("Miscellaneous"), TEXT("DestinationPlatform"),0), MAX_PATH); } ConcatenatePaths(SideBySideInstallShareDirectory, DirectoryName, MAX_PATH ); // // The asms directory is optional because there might just be asms*.cab. // FileAttributes = GetFileAttributes(SideBySideInstallShareDirectory); if (FileAttributes != INVALID_FILE_ATTRIBUTES && (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { RememberOptionalDir(DirectoryName, OPTDIR_SIDE_BY_SIDE | OPTDIR_TEMPONLY | (StartsWithSlash ? OPTDIR_PLATFORM_INDEP : OPTDIR_ADDSRCARCH)); } } } thread = 0; if(MakeLocalSource && OptionalDirectoryCount) { // // Start optional directory threads going. // There will thus be as many threads as there are // optional source dirs. // DebugLog (Winnt32LogDetailedInformation, TEXT("Starting optional directory thread going..."), 0); ZeroMemory(BuildParams,sizeof(BuildParams)); source = 0; for(u=0; uNext) { // // we're not fetching the file size, so in the oem preinstall case // when there are oem boot files, the size check is not accurate. // if(!AddFile(&MasterCopyList,p->Filename,NULL,DirectoryStruct,FILE_IN_LOCAL_BOOT,0)) { d = ERROR_NOT_ENOUGH_MEMORY; break; } } } else { d = ERROR_NOT_ENOUGH_MEMORY; } } #endif // defined(_AMD64_) || defined(_X86_) } else { #ifdef UNICODE // Always true for ARC, never true for Win9x upgrade // // ARC case. Add setupldr. // FileStruct = AddFile( &MasterCopyList, SETUPLDR_FILENAME, NULL, LookUpDirectory(&MasterCopyList,DUMMY_DIRID), FILE_ON_SYSTEM_PARTITION_ROOT, 0 ); d = FileStruct ? NO_ERROR : ERROR_NOT_ENOUGH_MEMORY; #endif // UNICODE } // if (!IsArc()) } if(d != NO_ERROR) { goto c1; } if (AsrQuickTest) { // // Add asr.sif // FileStruct = AddFile( &MasterCopyList, TEXT("asr.sif"), NULL, DirectoryStruct, FILE_IN_LOCAL_BOOT, 0 ); d = FileStruct ? NO_ERROR : ERROR_NOT_ENOUGH_MEMORY; if(d != NO_ERROR) { DebugLog (Winnt32LogError, TEXT("ERROR: AsrQuitTest - could not add asr.sif!"), 0); goto c1; } } // // If there were any threads created to build the file lists for // optional directories, wait for them to terminate now. // If they were all successful then add the lists they created // to the master list. // if(thread) { WaitForMultipleObjects(thread,BuildThreads,TRUE,INFINITE); TempError = NO_ERROR; for(u=0; uPrev; BuildParams[u].CopyList.Directories->Prev = MasterCopyList.Directories->Prev; MasterCopyList.Directories->Prev->Next = BuildParams[u].CopyList.Directories; MasterCopyList.Directories->Prev = p; } #else for(DirectoryStruct=MasterCopyList.Directories; DirectoryStruct->Next; DirectoryStruct=DirectoryStruct->Next) { ; } DirectoryStruct->Next = BuildParams[u].CopyList.Directories; #endif } else { MasterCopyList.Directories = BuildParams[u].CopyList.Directories; } if(MasterCopyList.Files) { #ifndef SLOWER_WAY if (BuildParams[u].CopyList.Files) { PVOID p; p = BuildParams[u].CopyList.Files->Prev; BuildParams[u].CopyList.Files->Prev = MasterCopyList.Files->Prev; MasterCopyList.Files->Prev->Next = BuildParams[u].CopyList.Files; MasterCopyList.Files->Prev = p ; } #else for(FileStruct=MasterCopyList.Files; FileStruct->Next; FileStruct=FileStruct->Next) { ; } FileStruct->Next = BuildParams[u].CopyList.Files; #endif } else { MasterCopyList.Files = BuildParams[u].CopyList.Files; } ZeroMemory(&BuildParams[u].CopyList,sizeof(COPY_LIST)); } if(d != NO_ERROR) { goto c1; } } // // Success. // return(TRUE); c1: // // Clean up the copy list. // TearDownCopyList(&MasterCopyList); c0: // // Close thread handles and free per-thread copy lists that may still // be unmerged into the master list. // for(u=0; uOptionalDirFlags & OPTDIR_PLATFORM_INDEP) { Flags |= DIR_IS_PLATFORM_INDEPEND; } if (Params->OptionalDirFlags & OPTDIR_IN_LOCAL_BOOT) { Flags |= DIR_USE_SUBDIR; } if (Params->OptionalDirFlags & OPTDIR_SUPPORT_DYNAMIC_UPDATE) { Flags |= DIR_SUPPORT_DYNAMIC_UPDATE; } if (Params->OptionalDirFlags & OPTDIR_DOESNT_SUPPORT_PRIVATES) { Flags |= DIR_DOESNT_SUPPORT_PRIVATES; } // // Add the directory to the directory list. // Note that the directory is added in a form relative to the // source root. // // // Check for a floating $OEM$ directory // if( (DestinationDirectory=_tcsstr( Params->CurrentDirectory, WINNT_OEM_DIR )) && UserSpecifiedOEMShare ) { // // We need to manually specify the Target directory // name because it's not the same as the source. We // want the destination directory to look exactly like // the source from "$OEM$" down. // DirectoryDescriptor = AddDirectory( NULL, &Params->CopyList, Params->CurrentDirectory, DupString( DestinationDirectory ), Flags | DIR_ABSOLUTE_PATH ); } else if( Params->OptionalDirFlags & (OPTDIR_OVERLAY) ) { DirectoryDescriptor = AddDirectory( NULL, &Params->CopyList, TEXT("\\"), NULL, Flags | DIR_ABSOLUTE_PATH ); } else { DirectoryDescriptor = AddDirectory( NULL, &Params->CopyList, Params->CurrentDirectory, DupString( Params->DestinationDirectory ), ((Params->OptionalDirFlags & OPTDIR_ABSOLUTE)? DIR_ABSOLUTE_PATH : 0) | Flags ); } if(!DirectoryDescriptor) { return(ERROR_NOT_ENOUGH_MEMORY); } // // Windows 95 has a bug in some IDE CD-ROM drivers that causes FindFirstFile to fail // if used with a pattern of "*". It needs to use "*.*" instead. Appease its brokenness. // if (!ISNT()) { PatternMatch = TEXT("*.*"); } else { PatternMatch = TEXT("*"); } // // Form the search spec. We overload the SourceRoot member of // the parameters structure for this to avoid a stack-sucking // large local variable. // // // Go look at the absolute path given in CurrentDirectory if we're // processing a floating $OEM$ directory. // if (AlternateSourcePath[0]) { _tcscpy( tmp, AlternateSourcePath ); pchSrcLim = tmp + lstrlen(tmp); ConcatenatePaths( tmp, Params->CurrentDirectory, MAX_PATH ); ConcatenatePaths( tmp, PatternMatch, MAX_PATH ); FindHandle = FindFirstFile( tmp, &Params->FindData ); if (FindHandle != INVALID_HANDLE_VALUE) { *pchSrcLim = 0; _tcscpy( Params->SourceRoot, tmp ); } } else { FindHandle = INVALID_HANDLE_VALUE; } if (FindHandle == INVALID_HANDLE_VALUE) { if( DirectoryDescriptor->Flags & DIR_ABSOLUTE_PATH ) { pchSrcLim = Params->CurrentDirectory + lstrlen(Params->CurrentDirectory); ConcatenatePaths(Params->CurrentDirectory,PatternMatch,MAX_PATH); FindHandle = FindFirstFile(Params->CurrentDirectory,&Params->FindData); } else { pchSrcLim = Params->SourceRoot + lstrlen(Params->SourceRoot); ConcatenatePaths(Params->SourceRoot,Params->CurrentDirectory,MAX_PATH); ConcatenatePaths(Params->SourceRoot,PatternMatch,MAX_PATH); FindHandle = FindFirstFile(Params->SourceRoot,&Params->FindData); } *pchSrcLim = 0; } if(!FindHandle || (FindHandle == INVALID_HANDLE_VALUE)) { // // We might be failing on the $OEM$ directory. He's optional // so let's not fail for him. // if (Params->OptionalDirFlags & (OPTDIR_OEMSYS) && !UserSpecifiedOEMShare) { return(NO_ERROR); } else { DebugLog ( Winnt32LogError, TEXT("Unable to copy dir %1"), 0, Params->CurrentDirectory ); return(GetLastError()); } } pchSrcLim = Params->CurrentDirectory + lstrlen(Params->CurrentDirectory); pchDstLim = Params->DestinationDirectory + lstrlen(Params->DestinationDirectory); Flags = FILE_NEED_TO_FREE_SOURCENAME; if( !(Params->OptionalDirFlags & (OPTDIR_OVERLAY)) && (!(Params->OptionalDirFlags & OPTDIR_TEMPONLY) || (Params->OptionalDirFlags & (OPTDIR_OEMSYS))) ) { Flags |= FILE_IN_PLATFORM_INDEPEND_DIR; } if (Params->OptionalDirFlags & OPTDIR_PLATFORM_INDEP) { Flags |= FILE_IN_PLATFORM_INDEPEND_DIR; } if (Params->OptionalDirFlags & OPTDIR_IN_LOCAL_BOOT) { Flags |= FILE_IN_LOCAL_BOOT; } d = NO_ERROR; do { if(Params->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // // Directory. Ignore . and .. entries. // if( lstrcmp(Params->FindData.cFileName,TEXT(".")) && lstrcmp(Params->FindData.cFileName,TEXT("..")) && !(Params->OptionalDirFlags & (OPTDIR_OVERLAY)) ) { // // Restore the current directory name and then form // the name of the subdirectory and recurse into it. // *pchSrcLim = 0; ConcatenatePaths(Params->CurrentDirectory,Params->FindData.cFileName,MAX_PATH); *pchDstLim = 0; ConcatenatePaths(Params->DestinationDirectory,Params->FindData.cFileName,MAX_PATH); d = AddFilesInDirToCopyList(Params); } } else { FileDescriptor = AddFile( &Params->CopyList, Params->FindData.cFileName, NULL, DirectoryDescriptor, Flags, MAKEULONGLONG(Params->FindData.nFileSizeLow,Params->FindData.nFileSizeHigh) ); if(!FileDescriptor) { d = ERROR_NOT_ENOUGH_MEMORY; } } } while((d == NO_ERROR) && FindNextFile(FindHandle,&Params->FindData)); // // Check loop termination condition. If d is NO_ERROR, then FindNextFile // failed. We want to make sure it failed because it ran out of files // and not for some other reason. If we don't check this, the list of // files in the directory could wind up truncated without any indication // that something went wrong. // if(d == NO_ERROR) { d = GetLastError(); if(d == ERROR_NO_MORE_FILES) { d = NO_ERROR; } } FindClose(FindHandle); return(d); } PVOID PopulateDriverCacheStringTable( VOID ) /* This function populates a string table (hashing table) with the files listed in the driver cab (drvindex.inf). It assosciates a Boolean ExtraData with each element of value TRUE. Once done with that it goest through the [ForceCopyDriverCabFiles] section in dosnet.inf and marks those files as FALSE in the string table. Hence we now have a hash table that has TRUE marked for all files that don't need to be copied. The function FileToBeCopied can be used to query the string table. The caller is responsible for destroying the string table. Return values: Pointer to string table. */ { #define MAX_SECTION_NAME 256 TCHAR DriverInfName[MAX_PATH], Section[MAX_SECTION_NAME], FileName[MAX_PATH]; HINF InfHandle, DosnetInfHandle; DWORD i, Count = 0; PVOID StringTable = NULL; INFCONTEXT InfContext; INFCONTEXT LineContext; BOOL Err = FALSE, Present = TRUE, Absent = FALSE; LONG Hash = 0; InfHandle = NULL; DosnetInfHandle = NULL; FindPathToInstallationFile( DRVINDEX_INF, DriverInfName, MAX_PATH ); InfHandle = SetupapiOpenInfFile( DriverInfName, NULL, INF_STYLE_WIN4, NULL ); if (!InfHandle) { DebugLog (Winnt32LogError, TEXT("Unable to open INF file %1"), 0, DriverInfName); Err = TRUE; return(NULL); } if( (StringTable = pSetupStringTableInitializeEx(sizeof(BOOL), 0)) == NULL ){ DebugLog (Winnt32LogError, TEXT("Unable to create string table for %1"), 0, DriverInfName); Err = TRUE; goto cleanup; } // Populate the string table // // Now get the section names that we have to search. // if( SetupapiFindFirstLine( InfHandle, TEXT("Version"), TEXT("CabFiles"), &InfContext)){ Count = SetupapiGetFieldCount( &InfContext ); for( i=1; i<=Count; i++ ){ if(SetupapiGetStringField( &InfContext, i, Section, MAX_SECTION_NAME, 0)){ if( SetupapiFindFirstLine( InfHandle, Section, NULL, &LineContext )){ do{ if( SetupapiGetStringField( &LineContext, 0, FileName, MAX_PATH, 0)){ if( (-1 == pSetupStringTableAddStringEx( StringTable, FileName, (STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE), &Present, sizeof(BOOL)))){ DebugLog (Winnt32LogError, TEXT("Out of memory adding string %1 to DriverCache INF string table"), 0, FileName); Err = TRUE; goto cleanup; } } }while( SetupapiFindNextLine( &LineContext, &LineContext )); } }else{ DebugLog (Winnt32LogError, TEXT("Unable to get section name in INF %1"), 0, DriverInfName); Err = TRUE; goto cleanup; } } } // Remove entries pertaining to [ForceCopyDriverCabFiles] DosnetInfHandle = SetupapiOpenInfFile( FullInfName, NULL, INF_STYLE_WIN4, NULL ); if (!DosnetInfHandle) { DebugLog (Winnt32LogError, TEXT("Unable to open INF file %1"), 0, FullInfName); Err = TRUE; goto cleanup; } if( SetupapiFindFirstLine( DosnetInfHandle, TEXT("ForceCopyDriverCabFiles"), NULL, &LineContext )){ do{ if( SetupapiGetStringField( &LineContext, 0, FileName, MAX_PATH, 0)){ Hash = pSetupStringTableLookUpString( StringTable, FileName, STRTAB_CASE_INSENSITIVE); if (-1 != Hash ) { pSetupStringTableSetExtraData( StringTable, Hash, &Absent, sizeof(BOOL)); } } }while( SetupapiFindNextLine( &LineContext, &LineContext )); } cleanup: if( InfHandle != INVALID_HANDLE_VALUE){ SetupapiCloseInfFile( InfHandle ); } if( DosnetInfHandle != INVALID_HANDLE_VALUE){ SetupapiCloseInfFile( DosnetInfHandle ); } if( Err ){ if(StringTable) pSetupStringTableDestroy( StringTable ); StringTable = NULL; } return( StringTable ); } BOOL FileToBeCopied( IN PVOID StringTable, IN PTSTR FileName ) /* Function to check presence of a driver cab file in the string table. Arguments: StringTable - Pointer to initialized stringtable FileName - Name of file to look for Return value; TRUE - If the file is in the driver cab and not one of the files that are listed in [ForceCopyDriverCabFiles] else it returns FALSE. */ { BOOL Present = FALSE; if( (-1 != pSetupStringTableLookUpStringEx( StringTable, FileName, STRTAB_CASE_INSENSITIVE, &Present, sizeof(BOOL)))){ if( Present == TRUE ){ return( TRUE ); } } // // If we get here, we didn't find a match. // return( FALSE ); } DWORD AddSection( IN PVOID Inf, IN OUT PCOPY_LIST CopyList, IN LPCTSTR SectionName, OUT UINT *ErrorLine, IN UINT FileFlags, IN BOOL SimpleList, IN BOOL DoDriverCabPruning ) { LPCTSTR DirSymbol, TargetName; LPTSTR SourceName; unsigned Count; BOOL b; PDIR Directory; PVOID p; DWORD Err = NO_ERROR; PVOID DriverCacheStringTable = NULL; Count = 0; *ErrorLine = (UINT)(-1); // Open up drvindex.inf if we have to do driver cab pruning if( DoDriverCabPruning){ //Initialize sptils if(pSetupInitializeUtils()) { //POpulate our Driver Cab list string table for fast lookup later if( (DriverCacheStringTable = PopulateDriverCacheStringTable( )) == NULL){ return(ERROR_NOT_ENOUGH_MEMORY); } }else return(ERROR_NOT_ENOUGH_MEMORY); } if(SimpleList) { while((LPCTSTR)SourceName = InfGetFieldByIndex(Inf,SectionName,Count,0)) { if( Cancelled == TRUE ) { // // The user is trying to exit, and the clean up code // is waiting for us to finish. Break out. // break; } // If the file is present in drvindex.inf and not in the // [ForceCopyDriverCabFiles] section then don't add it to the copylist // // This is the section in dosnet.inf that we cross check against when making the filelists. // Files in this section are in the driver cab and also should reside in the local source // The idea here is that these files are once that are not in the FloppyFiles.x sections and yet // need to remain outside the driver cab. // if( DoDriverCabPruning){ if (FileToBeCopied( DriverCacheStringTable, SourceName )){ Count++; continue; } } TargetName = InfGetFieldByIndex(Inf,SectionName,Count,1); Directory = LookUpDirectory(CopyList,DUMMY_DIRID); Count++; if(!AddFile(CopyList,SourceName,TargetName,Directory,FileFlags,0)) { Err = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } } } else { #if defined(_X86_) TCHAR diskID[4]; wsprintf (diskID, TEXT("d%u"), MLSDiskID); #endif while((DirSymbol = InfGetFieldByIndex(Inf,SectionName,Count,0))) { if( Cancelled == TRUE ) { // // The user is trying to exit, and the clean up code // is waiting for us to finish. Break out. // break; } SourceName = (LPTSTR) InfGetFieldByIndex(Inf,SectionName,Count,1); if(NULL == SourceName) { *ErrorLine = Count; Err = ERROR_INVALID_DATA; DebugLog ( Winnt32LogError, TEXT("ERROR: Could not look up source name in section %1 line = %2!u!"), 0, SectionName, Count ); goto cleanup; } // // move this check here to help catch build errors in dosnet.inf // Directory = LookUpDirectory(CopyList,DirSymbol); if(!Directory) { *ErrorLine = Count; Err = ERROR_INVALID_DATA; DebugLog ( Winnt32LogError, TEXT("ERROR: Could not look up directory %1 in section %2 line = %3!u!"), 0, DirSymbol, SectionName, Count ); goto cleanup; } #if defined(_X86_) if (MLSDiskID) { // //restrict copy to files on this disk only // if (_tcsicmp (diskID, DirSymbol) != 0) { Count++; continue; } } #endif // If the file is present in drvindex.inf and not in the // [ForceCopyDriverCabFiles] section then don't add it to the copylist // // This is the section in dosnet.inf that we cross check against when making the filelists. // Files in this section are in the driver cab and also should reside in the local source // The idea here is that these files are once that are not in the FloppyFiles.x sections and yet // need to remain outside the driver cab. // if( DoDriverCabPruning){ if (FileToBeCopied( DriverCacheStringTable, SourceName )){ Count++; continue; } } TargetName = InfGetFieldByIndex(Inf,SectionName,Count,2); Count++; if(NumberOfLicensedProcessors && !(FileFlags & FILE_NEED_TO_FREE_SOURCENAME) && !TargetName && !lstrcmpi(SourceName,TEXT("SETUPREG.HIV"))) { TargetName = MALLOC(20*sizeof(TCHAR)); if(!TargetName) { Err = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } if (_sntprintf((PTSTR)TargetName,20,TEXT("IDW\\SETUP\\SETUP%uP.HIV"),NumberOfLicensedProcessors) < 0) { ((PTSTR)TargetName)[20 - 1] = 0; } p = AddFile( CopyList, TargetName, SourceName, Directory, FileFlags | FILE_NEED_TO_FREE_SOURCENAME, 0 ); } else { p = AddFile( CopyList, SourceName, TargetName, Directory, FileFlags, 0 ); } if(!p) { Err = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } } } cleanup: if( DriverCacheStringTable ) pSetupStringTableDestroy( DriverCacheStringTable ); if( DoDriverCabPruning){ pSetupUninitializeUtils(); } return(Err); } PDIR AddDirectory( IN LPCTSTR InfSymbol, OPTIONAL IN OUT PCOPY_LIST CopyList, IN LPCTSTR SourceName, IN LPCTSTR TargetName, OPTIONAL IN UINT Flags ) /*++ Routine Description: Add a directory to a copy list. No attempt is made to eliminate duplicates. Directories will be listed in the copy list in the order in which they were added. Arguments: InfSymbol - if specified, supplies the symbol from the [Directories] section of the master inf that identifies the directory. This pointer is used as-is; no copy of the string is made. CopyList - supplies the copy list to which the directory is added. SourceName - supplies name of directory on the source. If the DIR_NEED_TO_FREE_SOURCENAME flag is set in the Flags parameter then a copy is made of this string, otherwise this pointer is used as-is in the copy list. TargetName - if specified, supplies the name for the directory on the target. This name is used as-is (no copy is made). If not specified then the target name is the same as the source name. Flags - supplies flags that control the directory's entry in the copy list. Return Value: If successful, returns a pointer to the new FIL structure for the file. Otherwise returns NULL (the caller can assume out of memory). --*/ { PDIR dir; PDIR x; PDIR p; // // We assume the directory isn't already in the list. // Make a copy of the directory string and stick it in a DIR struct. // dir = MALLOC(sizeof(DIR)); if(!dir) { return(NULL); } ZeroMemory(dir,sizeof(DIR)); if(Flags & DIR_NEED_TO_FREE_SOURCENAME) { dir->SourceName = DupString(SourceName); if(!dir->SourceName) { FREE(dir); return(NULL); } } else { dir->SourceName = SourceName; } dir->InfSymbol = InfSymbol; dir->TargetName = TargetName ? TargetName : dir->SourceName; dir->Flags = Flags; DebugLog( Winnt32LogDetailedInformation, NULL, MSG_LOG_ADDED_DIR_TO_COPY_LIST, dir->SourceName, dir->TargetName, dir->InfSymbol ? dir->InfSymbol : TEXT("-") ); #ifndef SLOWER_WAY p = CopyList->Directories; if (p) { dir->Prev = p->Prev; dir->Next = NULL; p->Prev->Next = dir; p->Prev = dir; } else { CopyList->Directories = dir; dir->Prev = dir; dir->Next = NULL; } #else if(CopyList->Directories) { // // Preserve order. // for(p=CopyList->Directories; p->Next; p=p->Next) { ; } p->Next = dir; } else { CopyList->Directories = dir; } #endif CopyList->DirectoryCount++; return(dir); } PFIL AddFile( IN OUT PCOPY_LIST CopyList, IN LPCTSTR SourceFilename, IN LPCTSTR TargetFilename, OPTIONAL IN PDIR Directory, IN UINT Flags, IN ULONGLONG FileSize OPTIONAL ) /*++ Routine Description: Add a single file to a copy list, noting which directory the file is in as well as any flags, etc. No attempt is made to eliminate duplicates. Files will be listed in the copy list in the order in which they were added. Arguments: CopyList - supplies the copy list to which the file is added. SourceFilename - supplies the name of the file to be added. If the FILE_NEED_TO_FREE_SOURCENAME argument is specified, then this string is duplicated. Otherwise it is not duplicated and this pointer is stored directly in the copy list node. TargetFilename - if specified, then the file has a different name on the target than on the source and this is its name on the target. If the FILE_NEED_TO_FREE_TARGETNAME argument is specified, then this string is duplicated. Otherwise it is not duplicated and this pointer is stored directly in the copy list node. Directory - supplies a pointer to the directory structure for the directory in which the file lives. Flags - supplies FILE_xxx flags to control the file's entry in the list. FileSize - if specified, supplies the size of the file. Return Value: If successful, returns a pointer to the new FIL structure for the file. Otherwise returns NULL (the caller can assume out of memory). --*/ { PFIL fil; PFIL p; TCHAR FlagsText[500]; TCHAR SizeText[256]; // // Make a new FIL struct. // fil = MALLOC(sizeof(FIL)); if(!fil) { return(NULL); } ZeroMemory(fil,sizeof(FIL)); if(Flags & FILE_NEED_TO_FREE_SOURCENAME) { fil->SourceName = DupString(SourceFilename); if(!fil->SourceName) { FREE(fil); return(NULL); } } else { fil->SourceName = SourceFilename; } if(TargetFilename) { if (Flags & FILE_NEED_TO_FREE_TARGETNAME) { fil->TargetName = DupString(TargetFilename); if(!fil->TargetName) { if(Flags & FILE_NEED_TO_FREE_SOURCENAME) { FREE((PVOID)fil->SourceName); } FREE(fil); return(NULL); } } else { fil->TargetName = TargetFilename; } } else { fil->TargetName = fil->SourceName; Flags &= ~FILE_NEED_TO_FREE_TARGETNAME; } fil->Directory = Directory; fil->Flags = Flags; fil->Size = FileSize; if (Winnt32LogDetailedInformation < DebugLevel) { _sntprintf(FlagsText,ARRAYSIZE(FlagsText),TEXT("0x%x"),Flags); if(Flags & FILE_ON_SYSTEM_PARTITION_ROOT) { StringCchCat(FlagsText,ARRAYSIZE(FlagsText),TEXT(" FILE_ON_SYSTEM_PARTITION_ROOT")); } #if defined(REMOTE_BOOT) if(Flags & FILE_ON_MACHINE_DIRECTORY_ROOT) { StringCchCat(FlagsText,ARRAYSIZE(FlagsText),TEXT(" FILE_ON_MACHINE_DIRECTORY_ROOT")); } #endif // defined(REMOTE_BOOT) if(Flags & FILE_IN_LOCAL_BOOT) { StringCchCat(FlagsText,ARRAYSIZE(FlagsText),TEXT(" FILE_IN_LOCAL_BOOT")); } if(Flags & FILE_PRESERVE_COMPRESSED_NAME) { StringCchCat(FlagsText,ARRAYSIZE(FlagsText),TEXT(" FILE_PRESERVE_COMPRESSED_NAME")); } #if 0 if(Flags & FILE_DECOMPRESS) { StringCchCat(FlagsText,ARRAYSIZE(FlagsText),TEXT(" FILE_DECOMPRESS")); } #endif if (Flags & FILE_IGNORE_COPY_ERROR) { StringCchCat(FlagsText,ARRAYSIZE(FlagsText), TEXT("FILE_IGNORE_COPY_ERROR")); } if (!GetUserPrintableFileSizeString( fil->Size, SizeText, ARRAYSIZE(SizeText))) { MYASSERT (ARRAYSIZE(SizeText) >= ARRAYSIZE("0")); lstrcpy( SizeText, TEXT("0")); } DebugLog( Winnt32LogDetailedInformation, NULL, MSG_LOG_ADDED_FILE_TO_COPY_LIST, fil->SourceName, Directory->SourceName, SizeText, fil->TargetName, FlagsText ); } #ifndef SLOWER_WAY p = CopyList->Files; if (p) { fil->Prev = p->Prev; fil->Next = NULL; p->Prev->Next = fil; p->Prev = fil; } else { CopyList->Files = fil; fil->Prev = fil; fil->Next = NULL; } #else // // Hook into copy list. Preserve order. // if(CopyList->Files) { for(p=CopyList->Files; p->Next; p=p->Next) { ; } p->Next = fil; } else { CopyList->Files = fil; } #endif CopyList->FileCount++; return(fil); } BOOL RemoveFile ( IN OUT PCOPY_LIST CopyList, IN LPCTSTR SourceName, IN PDIR Directory, OPTIONAL IN DWORD SetFlags OPTIONAL ) /*++ Routine Description: Add a single file to a copy list, noting which directory the file is in as well as any flags, etc. No attempt is made to eliminate duplicates. Files will be listed in the copy list in the order in which they were added. Arguments: CopyList - supplies the copy list from which the file is removed. SourceFilename - supplies the name of the file to be removed. Directory - supplies a pointer to the directory structure for the directory in which the file lives. Flags - supplies FILE_xxx flags to match against the file's entry in the list. Return Value: TRUE if the specified file was found in the list and marked as removed FALSE otherwise --*/ { PFIL p; for (p = CopyList->Files; p; p = p->Next) { if (_tcsicmp (p->SourceName, SourceName) == 0 && (!Directory || Directory == p->Directory) && ((p->Flags & SetFlags) == SetFlags) ) { p->Flags |= FILE_DO_NOT_COPY; return TRUE; } } return FALSE; } PDIR LookUpDirectory( IN PCOPY_LIST CopyList, IN LPCTSTR DirSymbol ) /*++ Routine Description: Looks for an entry for a directory in a copy list that matches a given INF symbol. Arguments: CopyList - supplies the copy list in which the directory is to be searched for. DirSymbol - supplies the symbol that is expected to match an entry in the main inf's [Directories] section. Return Value: If the dir is found then the return value is a pointer to the directory node in the copy list. Otherwise NULL is returned. --*/ { PDIR dir; for(dir=CopyList->Directories; dir; dir=dir->Next) { if(dir->InfSymbol && !lstrcmpi(dir->InfSymbol,DirSymbol)) { return(dir); } } return(NULL); } VOID TearDownCopyList( IN OUT PCOPY_LIST CopyList ) /*++ Routine Description: Deletes a copy list and frees all associated memory. Arguments: CopyList - supplies a pointer to the copy list structure for the copy list to be freed. The COPY_LIST struct itself is NOT freed but all fields in it are zeroed out. Return Value: None. --*/ { PDIR dir; PFIL fil; PVOID p; dir = CopyList->Directories; while(dir) { p = dir->Next; // // Free the source and the target if necessary // if(dir->SourceName && (dir->Flags & DIR_NEED_TO_FREE_SOURCENAME)) { FREE((PVOID)dir->SourceName); } FREE(dir); dir = p; } fil = CopyList->Files; while(fil) { p = fil->Next; // // Free the source and the target if necessary // if(fil->SourceName && (fil->Flags & FILE_NEED_TO_FREE_SOURCENAME)) { FREE((PVOID)fil->SourceName); } if(fil->TargetName && (fil->Flags & FILE_NEED_TO_FREE_TARGETNAME)) { FREE((PVOID)fil->TargetName); } FREE(fil); fil = p; } ZeroMemory(CopyList,sizeof(COPY_LIST)); } BOOL GetMainInfValue ( IN PCTSTR Section, IN PCTSTR Key, IN DWORD FieldNumber, OUT PTSTR Buffer, IN DWORD BufChars ) { PCTSTR p; PTSTR end; if (MainInf) { p = InfGetFieldByKey (MainInf, Section, Key, FieldNumber); if (p) { lstrcpyn (Buffer, p, BufChars); } return p != NULL; } if (!FullInfName[0]) { if (!FindPathToWinnt32File (InfName, FullInfName, ARRAYSIZE(InfName))) { InfName[0] = 0; return FALSE; } } if (!GetPrivateProfileString ( Section, Key, TEXT(""), Buffer, BufChars, FullInfName )) { return FALSE; } MYASSERT (FieldNumber <= 1); end = _tcschr (Buffer, TEXT(',')); if (FieldNumber == 1) { if (!end) { return FALSE; } lstrcpyn (Buffer, end + 1, BufChars); } else { if (end) { *end = 0 ; } } return TRUE; } BOOL CheckCopyListSpace( IN TCHAR DriveLetter, IN DWORD BytesPerCluster, IN LONGLONG FreeSpace, OUT DWORD *RequiredMB, IN BOOL CheckBootFiles, IN BOOL CheckLocalSource, IN BOOL CheckWinntDirectorySpace, IN BOOL QuickTest, IN LONGLONG AdditionalPadding ) /*++ Routine Description: This routine scans the master copy list and determines, based on cluster size, whether the drive contains enough space to hold the files in that list. Note that the check is not exact because we can't predict exactly how much space the directories themselves might occupy, and we assume that none of the files already exist on the target, which is not always true (for example ntldr, ntdetect.com, etc, which are already on C:\ in the amd64/x86 case). We fudge by adding a meg to the requirements. Arguments: DriveLetter - supplies the drive letter of the drive being checked. The FILE_ON_SYSTEM_PARTITION_ROOT and FILE_IN_LOCAL_BOOT flags for nodes in the copy list require special handling based on the drive letter of the drive being scanned. BytesPerCluster - specifies the number of bytes per cluster on the drive. FreeSpace - supplies the number of free bytes on the drive. RequiredMB - receives the amount of space required on this drive, in MB. CheckLocalSource - Do we need to check for space on this drive for copying all the source local? CheckBootFiles - Do we need to check for space on this drive for copying all the boot files? CheckWinntDirectorySpace - Do we need to add in the space required for the final winnt directory? Return Value: If TRUE then the drive has enough space on it to hold the files listed in the copy list. If FALSE then it does not. --*/ { PFIL File; LONGLONG SpaceRequired = 0; LONGLONG SpaceLocalSource = 0; LONGLONG SpaceBootFiles = 0; LONGLONG SpacePadding = 0; LONGLONG SpaceWinDir = 0; LONGLONG RoundedSize; TCHAR ClusterSizeString[64]; TCHAR buffer[64]; PTSTR p; if( BytesPerCluster <= 512 ) { MYASSERT (ARRAYSIZE(ClusterSizeString) >= ARRAYSIZE("TempDirSpace512")); lstrcpy( ClusterSizeString,TEXT("TempDirSpace512") ); } else if( BytesPerCluster > (256 * 1024) ) { MYASSERT (ARRAYSIZE(ClusterSizeString) >= ARRAYSIZE("TempDirSpace32K")); lstrcpy( ClusterSizeString, TEXT("TempDirSpace32K") ); } else { if (FAILED (StringCchPrintf ( ClusterSizeString, ARRAYSIZE(ClusterSizeString), TEXT("TempDirSpace%uK"), BytesPerCluster/1024))) { MYASSERT (FALSE); } } // // ==================================================== // If appropriate, add in space needs for the ~LS directory. // ==================================================== // if( CheckLocalSource ) { BOOL WantThisFile; // // If we're checking local source, there are so many files that // we're going to add in a small fudge factor. // SpacePadding = AdditionalPadding; SpaceLocalSource = 1000000 + AdditionalPadding; // // Dosnet.inf has sizing info for each possible cluster size. // That info tells us how much the files in the [Files] section // take up on a drive with that cluster size. That's really // handy because then we don't have to hit the sources to actually // fetch each file's size. // // But the inf doesn't include optional directories, so we need to // traverse the copy list and add up all the (rounded) sizes, and // then add the total to the value from the inf. // // When we built the copy list, the files in the [Files] section // wound up with a Size of 0 since we don't go out to the source // to get the size. The files that were in optional dirs have their // actual sizes filled in. This allows us to do something a little // funky: we traverse the entire list without regard for whether a // file was in the [Files] section or was in an optional dir, since // the "0-size" files don't hose up the calculation. Then we add // that value to the relevent value from the inf. // for(File=MasterCopyList.Files; File; File=File->Next) { if(File->Flags & (FILE_IN_LOCAL_BOOT | FILE_ON_SYSTEM_PARTITION_ROOT #if defined(REMOTE_BOOT) | FILE_ON_MACHINE_DIRECTORY_ROOT #endif // defined(REMOTE_BOOT) )) { // // Special handling based on the system partition. // WantThisFile = CheckBootFiles; } else { WantThisFile = CheckLocalSource; } if(WantThisFile) { if(File->Size % BytesPerCluster) { RoundedSize = File->Size + (BytesPerCluster - (DWORD)(File->Size % BytesPerCluster)); } else { RoundedSize = File->Size; } SpaceLocalSource += RoundedSize; } } // // If appropriate, add in space needs for the ~LS directory. // Note that we go ahead an calculate LocalSourceSpace because // we may need that later on. // if (GetMainInfValue (szDiskSpaceReq, ClusterSizeString, 0, buffer, ARRAYSIZE(buffer)) || // // Strange cluster size or inf is broken. Try to use a default of 512 // since that most closely approximates the actual size of the files. // GetMainInfValue (szDiskSpaceReq, TEXT("TempDirSpace512"), 0, buffer, ARRAYSIZE(buffer)) ) { SpaceLocalSource += _tcstoul(buffer,NULL,10); } else { MYASSERT (FALSE); } } // // ==================================================== // If appropriate, add in space needs for the ~BT directory. // ==================================================== // if( CheckBootFiles ) { if( !IsArc() ) { // // Go get the space requirements for the boot files // from dosnet.inf. // if (GetMainInfValue (szDiskSpaceReq, ClusterSizeString, 1, buffer, ARRAYSIZE(buffer))) { SpaceBootFiles += _tcstoul(buffer,NULL,10); } else { // // Guess about 5Mb for amd64/x86 because we need the entire // ~BT directory. // SpaceBootFiles += (5*1024*1024); } } else { // // Guess that we'll need about 1.5Mb for ARC. // We can't assume that this will go to 0x0 just // because we're doing an upgrade because we might // be going from 4.0 to 5.0 (for example). In this // case we will create a new directory under the // \os tree to hold the hal, osloader, ... // SpaceBootFiles += (3*512*1024); } } // // ==================================================== // If appropriate, add in space needs for the install directory. // ==================================================== // // Note: This is for upgrade. // We also need to take in account the space requirements for Program Files, Documents and Settings if( CheckWinntDirectorySpace ) { if( BytesPerCluster <= 512 ) { MYASSERT (ARRAYSIZE(ClusterSizeString) >= ARRAYSIZE("WinDirSpace512")); lstrcpy( ClusterSizeString,TEXT("WinDirSpace512") ); } else if( BytesPerCluster > (256 * 1024) ) { MYASSERT (ARRAYSIZE(ClusterSizeString) >= ARRAYSIZE("WinDirSpace32K")); lstrcpy( ClusterSizeString, TEXT("WinDirSpace32K") ); } else { if (FAILED (StringCchPrintf ( ClusterSizeString, ARRAYSIZE(ClusterSizeString), TEXT("WinDirSpace%uK"), BytesPerCluster/1024))) { MYASSERT (FALSE); } } // // First we figure out how much a fresh install might take. // if (GetMainInfValue (szDiskSpaceReq, ClusterSizeString, 0, buffer, ARRAYSIZE(buffer))) { // // Multiply him by 1024 because the values in // txtsetup.sif are in Kb instead of bytes. // SpaceWinDir += (_tcstoul(buffer,NULL,10) * 1024); } else { // guess... SpaceWinDir += (924 * (1024 * 1024)); } // Lets take into account Program Files if (GetMainInfValue (szDiskSpaceReq, szPFDocSpaceReq, 0, buffer, ARRAYSIZE(buffer))) { // // Multiply him by 1024 because the values are in Kb instead of bytes. // SpaceWinDir += (_tcstoul(buffer,NULL,10) * 1024); } else { // guess... SpaceWinDir += (WINDOWS_DEFAULT_PFDOC_SIZE * 1024); } WinDirSpaceFor9x = SpaceWinDir; if( Upgrade ) { LPCTSTR q = 0; // // We're upgrading, so we need to figure out which // build we're running, then subtract off how much // a clean install of that build would have taken. // This will give us an idea of how much the %windir% // will grow. // if( ISNT() ) { BOOL b; // // NT case. // if( BuildNumber <= NT351 ) { b = GetMainInfValue (szDiskSpaceReq, TEXT("351WinDirSpace"), 0, buffer, ARRAYSIZE(buffer)); } else if( BuildNumber <= NT40 ) { b = GetMainInfValue (szDiskSpaceReq, TEXT("40WinDirSpace"), 0, buffer, ARRAYSIZE(buffer)); } else if( BuildNumber <= NT50 ) { b = GetMainInfValue (szDiskSpaceReq, TEXT("50WinDirSpace"), 0, buffer, ARRAYSIZE(buffer)); } else { b = GetMainInfValue (szDiskSpaceReq, TEXT("51WinDirSpace"), 0, buffer, ARRAYSIZE(buffer)); } if( b ) { // // Multiply him by 1024 because the values in // dosnet.inf are in Kb instead of bytes. // SpaceWinDir -= (_tcstoul(buffer,NULL,10) * 1024); } if( BuildNumber <= NT351 ) { b = GetMainInfValue (szDiskSpaceReq, TEXT("351PFDocSpace"), 0, buffer, ARRAYSIZE(buffer)); } else if( BuildNumber <= NT40 ) { b = GetMainInfValue (szDiskSpaceReq, TEXT("40PFDocSpace"), 0, buffer, ARRAYSIZE(buffer)); } else if( BuildNumber <= NT50 ) { b = GetMainInfValue (szDiskSpaceReq, TEXT("50PFDocSpace"), 0, buffer, ARRAYSIZE(buffer)); } else { b = GetMainInfValue (szDiskSpaceReq, TEXT("51PFDocSpace"), 0, buffer, ARRAYSIZE(buffer)); } if( b ) { // // Multiply him by 1024 because the values are in Kb instead of bytes. // SpaceWinDir -= (_tcstoul(buffer,NULL,10) * 1024); } // // Make sure we don't look bad... // At 85 MB, we are near the border line of gui-mode having enough space to run. // note:during gui-mode, there is 41MB pagefile // // if( SpaceWinDir < 0 ) { SpaceWinDir = (90 * (1024*024)); } } else { // // Win9X case. // // // Note that the Win9X upgrade DLL can do a much better job of // determining disk space requirements for the %windir% than // I can. We'll bypass this check if we're not on NT. // But, we need about 50MB disk space to run Win9x upgrage. // SpaceWinDir = 50<<20; //50MB } } // Upgrade } // CheckWinntDirectorySpace SpaceRequired = SpaceLocalSource + SpaceBootFiles + SpaceWinDir; if( CheckLocalSource ) { // // We need to remember how much space will be // required on the drive where we place the ~LS // directory because we send that to the upgrade // dll. // LocalSourceSpaceRequired = SpaceRequired; } *RequiredMB = (DWORD)((SpaceRequired+1048575) / (1024*1024)); DebugLog( QuickTest ? Winnt32LogDetailedInformation : Winnt32LogError, NULL, MSG_LOG_DISKSPACE_CHECK, DriveLetter, (ULONG)BytesPerCluster, (ULONG)(FreeSpace / (1024*1024)), (ULONG)((SpaceLocalSource+1048575) / (1024*1024)), (ULONG)(SpacePadding / (1024*1024)), (ULONG)((SpaceBootFiles+1048575) / (1024*1024)), (ULONG)((SpaceWinDir+1048575) / (1024*1024)), (ULONG)*RequiredMB ); return(SpaceRequired <= FreeSpace); } #define VALID_DRIVE (32) #define INVALID_DRIVE (64) #define NOT_ENOUGH_SPACE (128) ULONG CheckASingleDrive( IN TCHAR DriveLetter, OPTIONAL IN PCTSTR NtVolumeName, OPTIONAL OUT DWORD *ClusterSize, OUT DWORD *RequiredMb, OUT DWORD *AvailableMb, IN BOOL CheckBootFiles, IN BOOL CheckLocalSource, IN BOOL CheckFinalInstallDir, IN BOOL QuickTest, IN LONGLONG AdditionalPadding ) /*++ Routine Description: This routine examines a specific drive for its potential to hold some or all of the install files. First he runs through a series of checks to make sure the drive is appropriate. If we get through all of those, we go check for drive space requirements. Arguments: DriveLetter - supplies the drive letter of the drive being checked; may be 0 only if NtVolumeName is specified instead NtVolumeName - supplies the NT device name of the drive being checked; only used if DriveLetter is not specified ClusterSize - the clustersize on the we'll check. RequiredSpace - receives the amount of space required on this drive. AvailableSpace - receives the number of free bytes on the drive. CheckBootFiles - Do we need to check for space on this drive for copying all the boot files? CheckLocalSource - Do we need to check for space on this drive for copying all the source local? CheckFinalInstallDir - Do we need to add in the space required for the final winnt directory? Return Value: NOT_ENOUGH_SPACE - RequiredSpace > AvailableSpace INVALID_DRIVE - The drive is inappropriate for holding install source. E.g. it's a floppy, ... VALID_DRIVE - The drive is appropriate for holding install source AND RequiredSpace < AvailableSpace --*/ { TCHAR DriveName[MAX_PATH]; TCHAR Filesystem[256]; TCHAR VolumeName[MAX_PATH]; DWORD SerialNumber; DWORD MaxComponent; DWORD Flags; DWORD SectorsPerCluster = 0; DWORD BytesPerSector = 0; ULARGE_INTEGER FreeClusters = {0, 0}; ULARGE_INTEGER TotalClusters = {0, 0}; BOOL b; LONGLONG AvailableBytes; DWORD DriveType; MYASSERT (DriveLetter || ISNT()); if (!(DriveLetter || ISNT())) { return DRIVE_UNKNOWN; } if (DriveLetter) { DriveName[0] = DriveLetter; DriveName[1] = TEXT(':'); DriveName[2] = TEXT('\\'); DriveName[3] = 0; } else { #ifdef UNICODE MYASSERT (NtVolumeName); #else MYASSERT (FALSE); return ( DRIVE_UNKNOWN ); #endif } // // ==================================================== // Check for appropriate drive. // ==================================================== // // // Disallow a set of drives... // if (DriveLetter) { DriveType = MyGetDriveType(DriveLetter); if(DriveType == DRIVE_UNKNOWN || DriveType == DRIVE_RAMDISK || DriveType == DRIVE_NO_ROOT_DIR ) { return( DRIVE_UNKNOWN ); } } else { #ifdef UNICODE DriveType = MyGetDriveType2(NtVolumeName); if(DriveType == DRIVE_UNKNOWN || DriveType == DRIVE_RAMDISK || DriveType == DRIVE_NO_ROOT_DIR ) { return( DRIVE_UNKNOWN ); } #endif } // // Check drive type. Skip anything but hard drives. // if( CheckLocalSource) { if (DriveLetter) { if (MyGetDriveType(DriveLetter) != DRIVE_FIXED) { if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_DRIVE_NOT_HARD, DriveLetter ); } return( INVALID_DRIVE ); } } else { #ifdef UNICODE if (MyGetDriveType2(NtVolumeName) != DRIVE_FIXED) { if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_DRIVE_NOT_HARD2, NtVolumeName ); } return( INVALID_DRIVE ); } #endif } } // // Get filesystem. HPFS is disallowed. We make this check because // HPFS was still supported in NT3.51 and we have to upgrade NT 3.51. // Strictly speaking this check is not required on win95 but there's // no reason not to execute it either, so we avoid the #ifdef's. // if (DriveLetter) { b = GetVolumeInformation( DriveName, VolumeName,MAX_PATH, &SerialNumber, &MaxComponent, &Flags, Filesystem, ARRAYSIZE(Filesystem) ); if(!b || !lstrcmpi(Filesystem,TEXT("HPFS"))) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_DRIVE_NO_VOL_INFO, DriveLetter ); return( INVALID_DRIVE ); } } // // Check for FT and firmware accessibility. We rely on the underlying // routines to do the right thing on Win95. // // In the upgrade case, we can put the local source on an NTFT drive. // // Note that we can't do this for Alpha / ARC. // if( ( IsArc() || !Upgrade ) && IsDriveNTFT(DriveLetter, NtVolumeName) ) { if (!QuickTest) { DebugLog(Winnt32LogInformation,NULL,MSG_LOG_DRIVE_NTFT,DriveLetter); } return( INVALID_DRIVE ); } // Don't allow $win_nt$.~ls to go on a soft partition, because the // loader/textmode won't be able to find such partitions. // if(IsSoftPartition(DriveLetter, NtVolumeName)) { if (!QuickTest) { DebugLog(Winnt32LogInformation,NULL,MSG_LOG_DRIVE_VERITAS,DriveLetter); } return( INVALID_DRIVE ); } #if defined(_X86_) if( !ISNT() ) { // // If we're running on win95, then make sure we skip // any compressed volumes. // if( Flags & FS_VOL_IS_COMPRESSED) { return( INVALID_DRIVE ); } } #endif if (IsArc() && DriveLetter) { #ifdef UNICODE // Always true for ARC, never true for Win9x upgrade LPWSTR ArcPath; if(DriveLetterToArcPath (DriveLetter,&ArcPath) != NO_ERROR) { if (!QuickTest) { DebugLog(Winnt32LogInformation,NULL,MSG_LOG_DRIVE_NO_ARC,DriveLetter); } return( INVALID_DRIVE ); } FREE(ArcPath); #endif // UNICODE } // // Finally, get cluster size on the drive and free space stats. // Then go through the copy list and figure out whether the drive // has enough space. // if (DriveLetter) { b = Winnt32GetDiskFreeSpaceNew( DriveName, &SectorsPerCluster, &BytesPerSector, &FreeClusters, &TotalClusters ); } else { #ifdef UNICODE // Always true for ARC, never true for Win9x upgrade b = MyGetDiskFreeSpace ( NtVolumeName, &SectorsPerCluster, &BytesPerSector, &FreeClusters.LowPart, &TotalClusters.LowPart ); #endif // UNICODE } if(!b) { if (!QuickTest) { DebugLog(Winnt32LogWarning,NULL,MSG_LOG_DRIVE_CANT_GET_SPACE,DriveLetter,GetLastError()); } return( INVALID_DRIVE ); } // // Fill in some return parameters that are also helpful for the // next function call. // *ClusterSize = BytesPerSector * SectorsPerCluster; AvailableBytes = (LONGLONG)(*ClusterSize) * FreeClusters.QuadPart; *AvailableMb = (ULONG)(AvailableBytes / (1024 * 1024)); if( CheckCopyListSpace( DriveLetter, *ClusterSize, AvailableBytes, RequiredMb, CheckBootFiles, CheckLocalSource, CheckFinalInstallDir, QuickTest, AdditionalPadding) ) { return( VALID_DRIVE ); } else { return( NOT_ENOUGH_SPACE ); } } BOOL FindLocalSourceAndCheckSpaceWorker( IN HWND hdlg, IN BOOL QuickTest, IN LONGLONG AdditionalPadding ) /*++ Routine Description: Based on the master copy list, determine which drive has enough space to contain the local source. The check is sensitive to the cluster size on each drive. The alphabetically lowest local drive that is accessible from the firmware, not HPFS, not FT, and has enough space gets the local source. Arguments: hdlg - supplies the window handle of the window which will own any UI displayed by this routine. Return Value: Boolean value indicating outcome. If FALSE, the user will have been informed about why. If TRUE, global variables are set up: LocalSourceDrive LocalSourceDirectory LocalSourceWithPlatform If the global flag BlockOnNotEnoughSpace is set to FALSE, this routine will return TRUE regardless of wether or not a suitable drive was found. It is up to whoever sets this flag to ensure that this is the correct behavior. --*/ { TCHAR DriveLetter = 0; TCHAR WinntDriveLetter = 0; TCHAR MyLocalSourceDrive = 0; BOOL MakeBootSource = FALSE; ULONG CheckResult; ULONG ClusterSize; ULONG RequiredMb; ULONG AvailableMb; LPCTSTR q = 0; TCHAR platform[MAX_PATH]; // // ==================================================== // Check the system partition and make sure we can place any // boot files we need. // ==================================================== // // // Will we be creating a $WIN_NT$.~BT directory? // On ARC we still check for this space even if we don't need it, just in case. // there should always be at least 5M free on the system partition... // if( IsArc() || ((MakeBootMedia) && (Floppyless)) ) // // RISC always requires a small amount of space on the system // partition because we put the loader, hal, and (in the case // of ALPHA) the pal code. // { if (!QuickTest) { DebugLog( Winnt32LogInformation, TEXT( "\r\n\r\nExamining system partition for adequate space for temporary boot files.\r\n"), 0 ); } MakeBootSource = TRUE; // // use the drive letter // CheckResult = CheckASingleDrive ( SystemPartitionDriveLetter, #ifdef UNICODE SystemPartitionNtName, #else NULL, #endif &ClusterSize, &RequiredMb, &AvailableMb, TRUE, // Check boot files space FALSE, // Check local source space FALSE, // Check final install directory space QuickTest, AdditionalPadding ); if( CheckResult == NOT_ENOUGH_SPACE ) { if (SystemPartitionDriveLetter) { if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_SYSTEM_PARTITION_TOO_SMALL, SystemPartitionDriveLetter, AvailableMb, RequiredMb ); } } else { #ifdef UNICODE if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_SYSTEM_PARTITION_TOO_SMALL2, SystemPartitionNtName, AvailableMb, RequiredMb ); } #endif } if( BlockOnNotEnoughSpace) { if (!QuickTest) { // // We're dead and the user asked us to stop if we // can't fit, so put up a dialog telling him that // he needs more room on the system partition. // SendMessage(hdlg,WMX_ERRORMESSAGEUP,TRUE,0); if (SystemPartitionDriveLetter) { MessageBoxFromMessage( hdlg, MSG_SYSTEM_PARTITION_TOO_SMALL, FALSE, AppTitleStringId, MB_OK | MB_ICONWARNING, SystemPartitionDriveLetter, RequiredMb ); } else { #ifdef UNICODE MessageBoxFromMessage( hdlg, MSG_SYSTEM_PARTITION_TOO_SMALL2, FALSE, AppTitleStringId, MB_OK | MB_ICONWARNING, SystemPartitionNtName, RequiredMb ); #endif } SendMessage(hdlg,WMX_ERRORMESSAGEUP,FALSE,0); } return( FALSE ); } } else if( (CheckResult == INVALID_DRIVE) || (CheckResult == DRIVE_UNKNOWN) ) { if (!QuickTest) { if (SystemPartitionDriveLetter) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_SYSTEM_PARTITION_INVALID, SystemPartitionDriveLetter ); } else { #ifdef UNICODE DebugLog( Winnt32LogInformation, NULL, MSG_LOG_SYSTEM_PARTITION_INVALID2, SystemPartitionNtName ); #endif } SendMessage(hdlg,WMX_ERRORMESSAGEUP,TRUE,0); MessageBoxFromMessage( hdlg, MSG_SYSTEM_PARTITION_INVALID, FALSE, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,FALSE,0); } return( FALSE ); } else if( CheckResult == VALID_DRIVE ) { if (!QuickTest) { if (SystemPartitionDriveLetter) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_SYSTEM_PARTITION_VALID, SystemPartitionDriveLetter ); } else { #ifdef UNICODE DebugLog( Winnt32LogInformation, NULL, MSG_LOG_SYSTEM_PARTITION_VALID2, SystemPartitionNtName ); #endif } } } } // // ==================================================== // Check space for the final installation directory. // ==================================================== // if( Upgrade ) { TCHAR Text[MAX_PATH]; MinDiskSpaceRequired = 0x7FFFFFFF, MaxDiskSpaceRequired = 0; if (!QuickTest) { DebugLog( Winnt32LogInformation, TEXT( "\r\n\r\nExamining disk for adequate space expand the WinDir.\r\n"), 0 ); } // // Just check the drive where the current installation is. // MyGetWindowsDirectory( Text, MAX_PATH ); WinntDriveLetter = Text[0]; CheckResult = CheckASingleDrive( WinntDriveLetter, NULL, &ClusterSize, &RequiredMb, &AvailableMb, ((WinntDriveLetter == SystemPartitionDriveLetter) && MakeBootSource), FALSE, TRUE, QuickTest, AdditionalPadding ); if( CheckResult == NOT_ENOUGH_SPACE ) { if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_INSTALL_DRIVE_TOO_SMALL, WinntDriveLetter, AvailableMb, RequiredMb ); } // // If the BlockOnNotEnoughSpace flag is set, then we // will throw up a message box and exit setup. // if (BlockOnNotEnoughSpace) { if (!QuickTest) { SendMessage(hdlg,WMX_ERRORMESSAGEUP,TRUE,0); MessageBoxFromMessage( hdlg, MSG_INSTALL_DRIVE_TOO_SMALL, FALSE, AppTitleStringId, MB_OK | MB_ICONWARNING, RequiredMb ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,FALSE,0); } return( FALSE ); } } else if( (CheckResult == INVALID_DRIVE) || (CheckResult == DRIVE_UNKNOWN) ) { if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_INSTALL_DRIVE_INVALID, WinntDriveLetter ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,TRUE,0); MessageBoxFromMessage( hdlg, MSG_INSTALL_DRIVE_INVALID, FALSE, AppTitleStringId, MB_OK | MB_ICONWARNING, RequiredMb ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,FALSE,0); } return( FALSE ); } else if( CheckResult == VALID_DRIVE ) { // // We need to make one more check here. If the user // is upgrading a Domain Controller, then he'll likely // need another 250Mb of disk space for DCPROMO to run // post gui-mode setup. We need to check for that space // here. If we don't have it, we need to warn the user. // Note that we're only going to warn. // // Also note that we're only going to do this *IF* we // would have and enough disk space w/o this check. // if( (ISDC()) && ((RequiredMb + 250) > AvailableMb) && !QuickTest) { int i; i = MessageBoxFromMessage( hdlg, MSG_DCPROMO_DISKSPACE, FALSE, AppTitleStringId, MB_OKCANCEL | MB_ICONEXCLAMATION, ((RequiredMb + 250) - AvailableMb) + 1 ); if( i == IDCANCEL ) { return( FALSE ); } } if (!QuickTest) { // // Log that we a drive suitable for the install directory. // DebugLog( Winnt32LogInformation, NULL, MSG_LOG_INSTALL_DRIVE_OK, WinntDriveLetter ); } } } // // ==================================================== // Check space for the local source (i.e. the ~LS directory). // ==================================================== // if( MakeLocalSource ) { MinDiskSpaceRequired = 0x7FFFFFFF, MaxDiskSpaceRequired = 0; if (!QuickTest) { DebugLog( Winnt32LogInformation, TEXT( "\r\n\r\nExamining Disks for adequate space for temporary setup files.\r\n"), 0 ); } if( UserSpecifiedLocalSourceDrive ) { // // Just check the drive that the user chose. // CheckResult = CheckASingleDrive( UserSpecifiedLocalSourceDrive, NULL, &ClusterSize, &RequiredMb, &AvailableMb, ((UserSpecifiedLocalSourceDrive == SystemPartitionDriveLetter) && MakeBootSource), TRUE, // Check local source space (UserSpecifiedLocalSourceDrive == WinntDriveLetter), // Check final install directory space. QuickTest, AdditionalPadding ); if( CheckResult == NOT_ENOUGH_SPACE ) { MinDiskSpaceRequired = RequiredMb - 1; MaxDiskSpaceRequired = RequiredMb + 1; if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_LOCAL_SOURCE_TOO_SMALL, UserSpecifiedLocalSourceDrive, AvailableMb, RequiredMb ); } if( BlockOnNotEnoughSpace) { // // We're dead and the user asked us to stop if we // can't fit, so put up a dialog telling him that // he needs more room. // if (!QuickTest) { SendMessage(hdlg,WMX_ERRORMESSAGEUP,TRUE,0); MessageBoxFromMessage( hdlg, MSG_USER_LOCAL_SOURCE_TOO_SMALL, FALSE, AppTitleStringId, MB_OK | MB_ICONWARNING, UserSpecifiedLocalSourceDrive, (DWORD)MaxDiskSpaceRequired ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,FALSE,0); } return( FALSE ); } else { MyLocalSourceDrive = UserSpecifiedLocalSourceDrive; } } else if( (CheckResult == INVALID_DRIVE) || (CheckResult == DRIVE_UNKNOWN) ) { if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_LOCAL_SOURCE_INVALID, UserSpecifiedLocalSourceDrive ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,TRUE,0); MessageBoxFromMessage( hdlg, MSG_USER_LOCAL_SOURCE_INVALID, FALSE, AppTitleStringId, MB_OK | MB_ICONWARNING, UserSpecifiedLocalSourceDrive ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,FALSE,0); } return( FALSE ); } else if( CheckResult == VALID_DRIVE ) { if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_LOCAL_SOURCE_VALID, UserSpecifiedLocalSourceDrive ); } MyLocalSourceDrive = UserSpecifiedLocalSourceDrive; } } else { // // Check all drives. // for( DriveLetter = TEXT('A'); DriveLetter <= TEXT('Z'); DriveLetter++ ) { CheckResult = CheckASingleDrive( DriveLetter, NULL, &ClusterSize, &RequiredMb, &AvailableMb, ((DriveLetter == SystemPartitionDriveLetter) && MakeBootSource), TRUE, // Check local source space (DriveLetter == WinntDriveLetter), // Check final install directory space. QuickTest, AdditionalPadding ); if( CheckResult == NOT_ENOUGH_SPACE ) { DWORD Size; DWORD_PTR my_args[3]; TCHAR Text0[2048]; if( MinDiskSpaceRequired > RequiredMb ) MinDiskSpaceRequired = RequiredMb; if( MaxDiskSpaceRequired < RequiredMb ) MaxDiskSpaceRequired = RequiredMb; if (!QuickTest) { // // Log that we failed the check of this // drive for the local source files. // DebugLog( Winnt32LogInformation, NULL, MSG_LOG_LOCAL_SOURCE_TOO_SMALL, DriveLetter, AvailableMb, RequiredMb ); // // Log it to a buffer too. // my_args[0] = DriveLetter; my_args[1] = AvailableMb; my_args[2] = RequiredMb; Size = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, hInst, MSG_LOG_LOCAL_SOURCE_TOO_SMALL, 0, Text0, ARRAYSIZE(Text0), (va_list *)my_args ); StringCchCat (DiskDiagMessage, ARRAYSIZE(DiskDiagMessage), Text0 ); } } else if( CheckResult == INVALID_DRIVE ) { if (!QuickTest) { DWORD Size; DWORD_PTR my_args[1]; TCHAR Text0[2048]; DebugLog( Winnt32LogInformation, NULL, MSG_LOG_LOCAL_SOURCE_INVALID, DriveLetter ); // // Log it to a buffer too. // my_args[0] = DriveLetter; Size = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, hInst, MSG_LOG_LOCAL_SOURCE_INVALID, 0, Text0, ARRAYSIZE(Text0), (va_list *)my_args ); StringCchCat (DiskDiagMessage, ARRAYSIZE(DiskDiagMessage), Text0 ); } } else if( CheckResult == VALID_DRIVE ) { if (!QuickTest) { DebugLog( Winnt32LogInformation, NULL, MSG_LOG_LOCAL_SOURCE_VALID, DriveLetter ); } MyLocalSourceDrive = DriveLetter; break; } } // // See if we got it. We can't bypass this failure even // if the user has cleared BlockOnNotEnoughSpace because // we absolutely have to have a place to put local files. // The user can always get around this by either installing // from CD, or using /tempdrive and clearing BlockOnNotEnoughSpace. // if( MyLocalSourceDrive == 0 ) { // // We failed. Error-out. // // // Just so we don't look bad... // if( MinDiskSpaceRequired == MaxDiskSpaceRequired ) { MaxDiskSpaceRequired += 10; } if( MinDiskSpaceRequired > MaxDiskSpaceRequired ) { MinDiskSpaceRequired = 300; MaxDiskSpaceRequired = 500; } if (!QuickTest) { if( CheckUpgradeOnly ) { // // Just catch the message for the compatibility list. // SendMessage(hdlg,WMX_ERRORMESSAGEUP,TRUE,0); MessageBoxFromMessage( hdlg, MSG_NO_VALID_LOCAL_SOURCE, FALSE, AppTitleStringId, MB_OK | MB_ICONWARNING, (DWORD)MinDiskSpaceRequired, (DWORD)MaxDiskSpaceRequired ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,FALSE,0); } else { // // Put up a detailed dialog. // DialogBox( hInst, MAKEINTRESOURCE(IDD_DISKSPACE), hdlg, DiskDlgProc ); } } return( FALSE ); } } // // If we get here, then we found room for all our // needs. Set up some globals. // LocalSourceDrive = MyLocalSourceDrive; LocalSourceDriveOffset = MyLocalSourceDrive - TEXT('A'); LocalSourceDirectory[0] = MyLocalSourceDrive; LocalSourceDirectory[1] = TEXT(':'); LocalSourceDirectory[2] = TEXT('\\'); LocalSourceDirectory[3] = 0; if (!ConcatenatePaths(LocalSourceDirectory,LOCAL_SOURCE_DIR,ARRAYSIZE(LocalSourceDirectory))) { MYASSERT (FALSE); } MYASSERT (ARRAYSIZE(LocalSourceWithPlatform) >= ARRAYSIZE(LocalSourceDirectory)); lstrcpy(LocalSourceWithPlatform,LocalSourceDirectory); if (!GetMainInfValue (TEXT("Miscellaneous"), TEXT("DestinationPlatform"), 0, platform, ARRAYSIZE(platform))) { if (!QuickTest) { DebugLog( Winnt32LogSevereError, NULL, MSG_NO_PLATFORM, NULL ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,TRUE,0); MessageBoxFromMessage( hdlg, MSG_NO_PLATFORM, FALSE, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); SendMessage(hdlg,WMX_ERRORMESSAGEUP,FALSE,0); } return( FALSE ); } if (!ConcatenatePaths( LocalSourceWithPlatform, platform, ARRAYSIZE(LocalSourceWithPlatform) )) { MYASSERT (FALSE); } LocalSourceDriveClusterSize = ClusterSize; } return( TRUE ); } DWORD CopyWorkerThread( IN PVOID ThreadParameter ) /*++ Routine Description: Thread routine to copy files. There may be up to MAX_SOURCE_COUNT of these threads running simultaneously. Access to shared global data is controlled via a critical section, per- thread global data is accessed by using the threads "ordinal number" to access the appropriate member of the global data array. The copy thread treats the copy list as a LIFO queue. Each time the thread is ready to copy a file, it dequeues a file from the list. It then tries to copy the file. If this fails, a per-thread vector bit is set so that this thread doesn't attempt to copy the file again. It then puts the file back into the list (at the head) to allow another thread to attempt to copy the file. Arguments: ThreadParameter - this is actually an ordinal number to indicate which thread in the "array" of SourceCount threads is currently running Return Value: Ignored. --*/ { UINT SourceOrdinal; PFIL CopyEntry,Previous; HANDLE Events[2]; DWORD d; UINT ThreadBit; BOOL Requeue; TCHAR TargetFilename[MAX_PATH]; ULONGLONG SpaceOccupied; TCHAR SizeStr[25]; BOOL bDone = FALSE; SourceOrdinal = (UINT)((ULONG_PTR)ThreadParameter); ThreadBit = 1 << SourceOrdinal; // // Both of these are "manual reset" events, so they will remain signalled // until we reset them. // Events[0] = MasterCopyList.ListReadyEvent[SourceOrdinal]; Events[1] = MasterCopyList.StopCopyingEvent; // // Wait for user to cancel, for copying to be done, or the file list // to become ready/non-empty. // while(!Cancelled && (WaitForMultipleObjects(2,Events,FALSE,INFINITE) == WAIT_OBJECT_0)) { if(Cancelled) { break; } EnterCriticalSection(&MasterCopyList.CriticalSection); // // Locate the next file that this thread has not yet // tried to copy, if any. If the list is completely // empty then reset the list ready event. // for(Previous=NULL, CopyEntry=MasterCopyList.Files; CopyEntry && (CopyEntry->ThreadBitmap & ThreadBit); Previous=CopyEntry, CopyEntry=CopyEntry->Next) { ; } // // If we found an entry unlink it from the list. // if(CopyEntry) { if(Previous) { Previous->Next = CopyEntry->Next; } else { MasterCopyList.Files = CopyEntry->Next; } } else { // // No entry for this thread. Enter a state where we're waiting // for an entry to be requeued or for copying to be finished. // ResetEvent(Events[0]); } LeaveCriticalSection(&MasterCopyList.CriticalSection); if(Cancelled) { break; } // // If we got a file entry, go ahead and try to copy the file. // if(CopyEntry) { d = CopyOneFile(CopyEntry,SourceOrdinal,TargetFilename,ARRAYSIZE(TargetFilename),&SpaceOccupied); #ifdef TEST_EXCEPTION DoException( 3); #endif Requeue = FALSE; if(d == NO_ERROR) { MasterCopyList.SpaceOccupied[SourceOrdinal] += SpaceOccupied; TotalDataCopied += SpaceOccupied; } else { if (!Cancelled && !(CopyEntry->Flags & FILE_IGNORE_COPY_ERROR)) { // // Error. If this is the last thread to try to copy the file, // then we want to ask the user what to do. Otherwise requeue // the file so other copy threads can try to copy it. // if((CopyEntry->ThreadBitmap | ThreadBit) == (UINT)((1 << SourceCount)-1)) { MYASSERT (d != NO_ERROR); switch(FileCopyError(MasterCopyList.hdlg,CopyEntry->SourceName,TargetFilename,d,TRUE)) { case COPYERR_EXIT: // // FileCopyError() already set thhe stop-copying event // and set Cancelled to TRUE. We do something a little funky now, // namely we simulate a press of the cancel button on the wizard // so all abnormal terminations go through the same codepath. // PropSheet_PressButton(GetParent(MasterCopyList.hdlg),PSBTN_CANCEL); break; case COPYERR_SKIP: // // Requeue is aready set to FALSE, which will cause code // below to tell the main thread that another file is done. // Nothing more to do for this case. // break; case COPYERR_RETRY: // // Wipe the list of threads that have tried to copy the file // so all will take another crack at it. // CopyEntry->ThreadBitmap = 0; Requeue = TRUE; break; } } else { // // Tell ourselves that we've already tried to copy this file // and requeue it at the head of the list. // CopyEntry->ThreadBitmap |= ThreadBit; Requeue = TRUE; } } else { DebugLog ( Winnt32LogWarning, TEXT("Error %1!u! copying %2 to %3 - ignored"), 0, d, CopyEntry->SourceName, CopyEntry->TargetName ); } } if(Requeue) { EnterCriticalSection(&MasterCopyList.CriticalSection); CopyEntry->Next = MasterCopyList.Files; MasterCopyList.Files = CopyEntry; // // Want to set the event for every thread that might be // called on to copy this file. // for(d=0; dThreadBitmap & (1 << d))) { SetEvent(MasterCopyList.ListReadyEvent[d]); } } LeaveCriticalSection(&MasterCopyList.CriticalSection); if(Cancelled) { break; } } else { // // Inform the UI thread that another file is done. // Free the copy list entry and decrement the count // of files that have been processed. When that number // goes to 0, we are done. // PostMessage(MasterCopyList.hdlg,WMX_COPYPROGRESS,0,0); if(CopyEntry->SourceName && (CopyEntry->Flags & FILE_NEED_TO_FREE_SOURCENAME)) { FREE((PVOID)CopyEntry->SourceName); } if(CopyEntry->TargetName && (CopyEntry->Flags & FILE_NEED_TO_FREE_TARGETNAME)) { FREE((PVOID)CopyEntry->TargetName); } FREE(CopyEntry); if(!InterlockedDecrement(&MasterCopyList.FileCount)) { SetEvent(MasterCopyList.StopCopyingEvent); if (Cancelled) { break; } // // Sum up the total space occupied and write it into // size.sif in the local source. // if(MakeLocalSource) { SpaceOccupied = 0; for(d=0; dFlags & FILE_DO_NOT_COPY) { DebugLog ( Winnt32LogInformation, TEXT("Not copying %1"), 0, File->SourceName ); return NO_ERROR; } // // Form the full source and target names for this file, based on // information in the copy list entry and the source we're supposed to // be using for this file. // // Check to see if this directory's path has been tagged // as being an absolute path. If so, then we shouldn't // tack him onto the end of the SourcePath. Rather, we // can just take as he is. // if( !(File->Flags & FILE_NT_MIGRATE) ) { // // Generate a path to copy from (source path). // if (AlternateSourcePath[0] && !(File->Directory->Flags & DIR_DOESNT_SUPPORT_PRIVATES)) { //ConcatenatePaths(SourceFilename,File->Directory->SourceName,MAX_PATH); if (BuildPath(SourceFilename,AlternateSourcePath, File->SourceName)) { UsedAlternate = TRUE; } else { b = FALSE; } } else if (DynamicUpdateSuccessful () && g_DynUpdtStatus->UpdatesPath[0] && (File->Directory->Flags & DIR_SUPPORT_DYNAMIC_UPDATE) ) { // // Files in this directory support Dynamic Update // WIN32_FIND_DATA fd; if (BuildPath (SourceFilename, g_DynUpdtStatus->UpdatesPath, File->Directory->SourceName) && ConcatenatePaths (SourceFilename, File->SourceName, ARRAYSIZE(SourceFilename)) && FileExists (SourceFilename, &fd) && !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { UsedUpdated = TRUE; DebugLog ( Winnt32LogInformation, NULL, MSG_LOG_USE_UPDATED, SourceFilename, File->SourceName, SourceOrdinal ); } } if (!(UsedAlternate || UsedUpdated)) { if( File->Directory->Flags & DIR_ABSOLUTE_PATH ) { b = BuildPath (SourceFilename, File->Directory->SourceName, File->SourceName) != NULL; } else { b = BuildPath (SourceFilename,SourcePaths[SourceOrdinal], File->Directory->SourceName) != NULL && ConcatenatePaths (SourceFilename, File->SourceName, ARRAYSIZE(SourceFilename)); } } } else { b = MyGetWindowsDirectory (SourceFilename, ARRAYSIZE(SourceFilename)) > 0 && ConcatenatePaths (SourceFilename, File->Directory->SourceName, ARRAYSIZE(SourceFilename)); ConcatenatePaths (SourceFilename, File->SourceName, ARRAYSIZE(SourceFilename)); } if (!b) { DebugLog (Winnt32LogError, TEXT("Filename too long: [%1\\%2]"), 0, AlternateSourcePath, File->SourceName); return ERROR_INSUFFICIENT_BUFFER; } // // generate a target path // b = TRUE; if( !(File->Flags & FILE_NT_MIGRATE) ) { #if defined(REMOTE_BOOT) if(File->Flags & FILE_ON_MACHINE_DIRECTORY_ROOT) { MYASSERT(RemoteBoot); b = SUCCEEDED (StringCchCopy (TargetFilename, CchTargetFilename, MachineDirectory)); } else #endif // defined(REMOTE_BOOT) if(File->Flags & FILE_ON_SYSTEM_PARTITION_ROOT) { #if defined(REMOTE_BOOT) if (RemoteBoot) { b = MyGetWindowsDirectory(TargetFilename,ARRAYSIZE(TargetFilename)); if (b) { TargetFilename[3] = 0; } } else #endif // defined(REMOTE_BOOT) { b = BuildSystemPartitionPathToFile (TEXT(""), TargetFilename, CchTargetFilename); } } else { if(File->Flags & FILE_IN_LOCAL_BOOT) { b = SUCCEEDED (StringCchCopy ( TargetFilename, CchTargetFilename, IsArc() ? LocalSourceWithPlatform : LocalBootDirectory )); if (b && (File->Directory->Flags & DIR_USE_SUBDIR)) { b = ConcatenatePaths(TargetFilename,File->Directory->TargetName, CchTargetFilename); } } else { MYASSERT (LocalSourceDirectory[0]); if(File->Flags & FILE_IN_PLATFORM_INDEPEND_DIR) { b = SUCCEEDED (StringCchCopy ( TargetFilename, CchTargetFilename, LocalSourceDirectory)); } else { b = SUCCEEDED (StringCchCopy ( TargetFilename, CchTargetFilename, LocalSourceWithPlatform)); } b = b && ConcatenatePaths(TargetFilename,File->Directory->TargetName,CchTargetFilename); } } } else { b = SUCCEEDED (StringCchCopy ( TargetFilename, CchTargetFilename, IsArc() ? LocalSourceWithPlatform : LocalBootDirectory)); } b = b && ConcatenatePaths(TargetFilename,File->TargetName,CchTargetFilename); if (!b) { DebugLog (Winnt32LogError, TEXT("Buffer too small for full dest path of [%1]"), 0, File->TargetName); return ERROR_INSUFFICIENT_BUFFER; } // // We have the full source and destination paths. Try to do the actual copy. // try_again: SetDlgItemText( MasterCopyList.hdlg, IDT_SOURCE1+SourceOrdinal, _tcsrchr(TargetFilename,TEXT('\\')) + 1 ); // // Now see if the file can be located on the server with the compressed // form of the name or the name itself, depending on which was successful // last time. // TryCompressedFirst = (File->Flags & FILE_NT_MIGRATE) || UsedUpdated ? FALSE : (TlsGetValue(TlsIndex) != 0); if(TryCompressedFirst) { GenerateCompressedName(SourceFilename,ActualSource); FindHandle = FindFirstFile(ActualSource,&FindData); if(FindHandle && (FindHandle != INVALID_HANDLE_VALUE)) { // // Got the file, leave the name in ActualSource. // FindClose(FindHandle); } else { // // Don't have the file, try the actual filename. // If that works then remember the name in ActualSource. // FindHandle = FindFirstFile(SourceFilename,&FindData); if(FindHandle && (FindHandle != INVALID_HANDLE_VALUE)) { FindClose(FindHandle); MYASSERT (ARRAYSIZE(ActualSource) >= ARRAYSIZE(SourceFilename)); lstrcpy(ActualSource,SourceFilename); TryCompressedFirst = FALSE; } else { ActualSource[0] = 0; } } } else { FindHandle = FindFirstFile(SourceFilename,&FindData); if(FindHandle != INVALID_HANDLE_VALUE) { // // Found it -- remember the name in ActualSource. // FindClose(FindHandle); MYASSERT (ARRAYSIZE(ActualSource) >= ARRAYSIZE(SourceFilename)); lstrcpy(ActualSource,SourceFilename); } else { // // Try the compressed-form name. // GenerateCompressedName(SourceFilename,ActualSource); FindHandle = FindFirstFile(ActualSource,&FindData); if(FindHandle != INVALID_HANDLE_VALUE) { TryCompressedFirst = TRUE; FindClose(FindHandle); } else { // // Couldn't find the compressed form name either. // Indicate failure. // ActualSource[0] = 0; } } } // // At this point ActualSource[0] is 0 if we couldn't find the file. // if(!ActualSource[0]) { if (UsedAlternate) { if( File->Directory->Flags & DIR_ABSOLUTE_PATH ) { b = BuildPath( SourceFilename, File->Directory->SourceName,File->SourceName) != NULL; } else { b = BuildPath( SourceFilename,SourcePaths[SourceOrdinal],File->Directory->SourceName) != NULL && ConcatenatePaths(SourceFilename,File->SourceName,ARRAYSIZE(SourceFilename)); } if (!b) { DebugLog (Winnt32LogError, TEXT("Buffer too small for full source path of [%1]"), 0, File->SourceName); return ERROR_INSUFFICIENT_BUFFER; } UsedAlternate = FALSE; goto try_again; } return(ERROR_FILE_NOT_FOUND); } if( !(File->Flags & FILE_NT_MIGRATE) && !UsedUpdated ) { TlsSetValue(TlsIndex, UIntToPtr( TryCompressedFirst ) ); } if(TryCompressedFirst && (File->Flags & FILE_PRESERVE_COMPRESSED_NAME)) { // // Opened the compressed form of the source name, so use // a compressed form of the target name. Note that we're not // using the SourceFilename buffer anymore, so we use it // as temporary storage. // GenerateCompressedName(TargetFilename,SourceFilename); if (FAILED (StringCchCopy(TargetFilename,CchTargetFilename,SourceFilename))) { MYASSERT (FALSE); DebugLog (Winnt32LogError, TEXT("Buffer too small for full source path of [%1]"), 0, SourceFilename); return ERROR_INSUFFICIENT_BUFFER; } } // // Now go ahead and try to actually *copy* the file (gasp!) // To overcome net glitches, we retry once automatically. // // As a small touch, we try to preserve file attributes for files // that already exist on the system partition root. In other words // for a file like ntldr, if the user removed say RHS attribs // we try to leave it that way. // *(p = _tcsrchr(TargetFilename,TEXT('\\'))) = 0; d = CreateMultiLevelDirectory(TargetFilename); *p = TEXT('\\'); if(d != NO_ERROR) { DebugLog(Winnt32LogError,NULL,MSG_LOG_COPY_ERR,ActualSource,TargetFilename,SourceOrdinal,d); return(d); } OldAttributes = (File->Flags & FILE_ON_SYSTEM_PARTITION_ROOT) ? GetFileAttributes(TargetFilename) : (DWORD)(-1); SetFileAttributes(TargetFilename,FILE_ATTRIBUTE_NORMAL); // // ISSUE: the condition below is never TRUE because the flag FILE_DECOMPRESS // is never set. Besides, even if this was true, decompression would actually // fail on NT4 systems (because setupapi didn't support LZX compression) // #if 0 if(TryCompressedFirst && (File->Flags & FILE_DECOMPRESS)) { // // File existed with its compressed-form name and // we want to decompress it. Do that now, bypassing the usual // filecopy logic below. // NameAndSize.Name = TargetFilename; NameAndSize.Size = 0; if(!SetupapiCabinetRoutine(ActualSource,0,DiamondCallback,&NameAndSize)) { d = GetLastError(); DebugLog(Winnt32LogError,NULL,MSG_LOG_DECOMP_ERR,ActualSource,TargetFilename,SourceOrdinal,d); return(d); } // // Adjust file size so disk space checks are accurate // FindData.nFileSizeLow = LOULONG(NameAndSize.Size); FindData.nFileSizeHigh = HIULONG(NameAndSize.Size); } else { #endif if(!CopyFile(ActualSource,TargetFilename,FALSE)) { Sleep(500); if(!CopyFile(ActualSource,TargetFilename,FALSE)) { // // workaround for Win9x system bug: sometimes it fails to copy some files // use our own copy routine // if (!OurCopyFile (ActualSource,TargetFilename,FALSE)) { d = GetLastError(); DebugLog(Winnt32LogError,NULL,MSG_LOG_COPY_ERR,ActualSource,TargetFilename,SourceOrdinal,d); return(d); } else { #ifdef PRERELEASE // // log this info; at least we can track it and maybe we can find what's causing this // DebugLog(Winnt32LogWarning,TEXT("File %1 was successfully copied to %2 using OurCopyFile"),0,ActualSource,TargetFilename); #endif } } } #if 0 } #endif if(OldAttributes != (DWORD)(-1)) { // // API does nothing with the compression flag; strip it out. // SetFileAttributes(TargetFilename,OldAttributes & ~FILE_ATTRIBUTE_COMPRESSED); } DebugLog(Winnt32LogInformation,NULL,MSG_LOG_COPY_OK,ActualSource,TargetFilename,SourceOrdinal); // // Track size occupied on local source drive. // if( (LocalSourceDrive) && (MakeLocalSource) && ( (SystemPartitionDriveLetter == LocalSourceDrive) || !(File->Flags & (FILE_ON_SYSTEM_PARTITION_ROOT | FILE_IN_LOCAL_BOOT))) ) { DWORD Adjuster; ULONGLONG Value; Value = MAKEULONGLONG(0,FindData.nFileSizeHigh); Adjuster = ((FindData.nFileSizeLow % LocalSourceDriveClusterSize) != 0); Value += LocalSourceDriveClusterSize * ((FindData.nFileSizeLow/LocalSourceDriveClusterSize)+Adjuster); *SpaceOccupied = Value; } return(NO_ERROR); } UINT GetTotalFileCount( VOID ) { return(MasterCopyList.FileCount); } UINT FileCopyError( IN HWND ParentWindow, IN LPCTSTR SourceFilename, IN LPCTSTR TargetFilename, IN UINT Win32Error, IN BOOL MasterList ) /*++ Routine Description: This routine handles file copy errors, presenting them to the user for dispensation (skip, retry, exit). Arguments: ParentWindow - supplies window handle of window to be used as parent for the dialog that this routine displays. SourceFilename - supplies name of file that could not be copied. Only the final component of this name is used. TargetFilename - supplies the target filename for the file. This should be a fully qualified win32 path. Win32Error - supplies win32 error code that indicated reason for failure. MasterList - supplies a flag indicating whether the file being copied was on the master list or was just an individual file. If TRUE, copy errors are serialized and the master copy list stop copying event is set if the user chooses to cancel. Return Value: One of COPYERR_SKIP, COPYERR_EXIT, or COPYERR_RETRY. --*/ { UINT u; HANDLE Events[2]; COPY_ERR_DLG_PARAMS CopyErrDlgParams; LPCTSTR p; if(AutoSkipMissingFiles) { if(p = _tcsrchr(SourceFilename,TEXT('\\'))) { p++; } else { p = SourceFilename; } DebugLog(Winnt32LogWarning,NULL,MSG_LOG_SKIPPED_FILE,p); return(COPYERR_SKIP); } // // Multiple threads can potentially enter this routine simultaneously // but we only want a single error dialog up at once. Because each copy // thread is independent of the main thread running the wizard/ui, // we can block here. But we also need to wake up if the user cancels // copying from another thread, so we want on the stop copying event also. // if(MasterList) { Events[0] = UiMutex; Events[1] = MasterCopyList.StopCopyingEvent; u = WaitForMultipleObjects(2,Events,FALSE,INFINITE); if(Cancelled || (u != WAIT_OBJECT_0)) { // // Stop copying event. This means that some other thread is cancelling // setup. We just return skip since we don't need an extra guy running // around processing an exit request. // return(COPYERR_SKIP); } } // // OK, put up the actual UI. // CopyErrDlgParams.Win32Error = Win32Error; CopyErrDlgParams.SourceFilename = SourceFilename; CopyErrDlgParams.TargetFilename = TargetFilename; u = (UINT)DialogBoxParam( hInst, MAKEINTRESOURCE(IDD_COPYERROR), ParentWindow, CopyErrDlgProc, (LPARAM)&CopyErrDlgParams ); if(u == COPYERR_EXIT) { // // Set the cancelled flag before releasing the mutex. // This guarantees that if any other threads are waiting to stick up // a copy error, they'll hit the case above and return COPYERR_SKIP. // Cancelled = TRUE; if(MasterList) { SetEvent(MasterCopyList.StopCopyingEvent); } } if(MasterList) { ReleaseMutex(UiMutex); } return(u); } INT_PTR CopyErrDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { BOOL b; int i; static WarnedSkip; b = FALSE; switch(msg) { case WM_INITDIALOG: // // File not found and disk full get special treatment. // Others get the standard system message. // { TCHAR text1[500]; TCHAR text2[1000]; TCHAR text3[5000]; PCOPY_ERR_DLG_PARAMS Params; DWORD Flags; UINT Id; LPCTSTR Args[4]; LPCTSTR source; Params = (PCOPY_ERR_DLG_PARAMS)lParam; switch(Params->Win32Error) { case ERROR_FILE_NOT_FOUND: Flags = FORMAT_MESSAGE_FROM_HMODULE; Id = MSG_COPY_ERROR_NOSRC; break; case ERROR_HANDLE_DISK_FULL: case ERROR_DISK_FULL: Flags = FORMAT_MESSAGE_FROM_HMODULE; Id = MSG_COPY_ERROR_DISKFULL; break; default: Flags = FORMAT_MESSAGE_FROM_SYSTEM; Id = Params->Win32Error; break; } FormatMessage( Flags | FORMAT_MESSAGE_IGNORE_INSERTS, hInst, Id, 0, text1, ARRAYSIZE(text1), NULL ); FormatMessage( FORMAT_MESSAGE_FROM_HMODULE, hInst, MSG_COPY_ERROR_OPTIONS, 0, text2, ARRAYSIZE(text2), NULL ); if(source = _tcsrchr(Params->SourceFilename,TEXT('\\'))) { source++; } else { source = Params->SourceFilename; } Args[0] = source; Args[1] = Params->TargetFilename; Args[2] = text1; Args[3] = text2; FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, hInst, MSG_COPY_ERROR_TEMPLATE, 0, text3, ARRAYSIZE(text3), (va_list *)Args ); if (BatchMode) { // // Don't show the UI. Save the error message and pretend the // user hit Abort. // SaveTextForSMS(text3); EndDialog(hdlg,COPYERR_EXIT); } SetDlgItemText(hdlg,IDT_ERROR_TEXT,text3); } SetFocus(GetDlgItem(hdlg,IDRETRY)); break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDRETRY: if(HIWORD(wParam) == BN_CLICKED) { EndDialog(hdlg,COPYERR_RETRY); b = TRUE; } break; case IDIGNORE: if(HIWORD(wParam) == BN_CLICKED) { if(WarnedSkip) { i = IDYES; } else { i = MessageBoxFromMessage( hdlg, MSG_REALLY_SKIP, FALSE, AppTitleStringId, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 ); WarnedSkip = TRUE; } if(i == IDYES) { EndDialog(hdlg,COPYERR_SKIP); } b = TRUE; } break; case IDABORT: if(HIWORD(wParam) == BN_CLICKED) { i = MessageBoxFromMessage( hdlg, MSG_SURE_EXIT, FALSE, AppTitleStringId, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 ); if(i == IDYES) { EndDialog(hdlg,COPYERR_EXIT); } b = TRUE; } break; } break; } return(b); } UINT DiamondCallback( IN PVOID Context, IN UINT Code, IN UINT_PTR Param1, IN UINT_PTR Param2 ) { UINT u; PFILE_IN_CABINET_INFO_A FileInCabInfo; PNAME_AND_SIZE_CAB NameAndSize; if(Code == SPFILENOTIFY_FILEINCABINET) { // // Give setupapi the full target path of the file. // NameAndSize = Context; FileInCabInfo = (PFILE_IN_CABINET_INFO_A)Param1; #ifdef UNICODE u = WideCharToMultiByte( CP_ACP, 0, NameAndSize->Name, -1, FileInCabInfo->FullTargetName, ARRAYSIZE(FileInCabInfo->FullTargetName), NULL, NULL ); if(!u) { FileInCabInfo->Win32Error = GetLastError(); return(FILEOP_ABORT); } #else if (FAILED (StringCchCopy ( FileInCabInfo->FullTargetName, ARRAYSIZE(FileInCabInfo->FullTargetName), NameAndSize->Name))) { FileInCabInfo->Win32Error = ERROR_INSUFFICIENT_BUFFER; return(FILEOP_ABORT); } #endif // // BugBug: cabinet only returns a DWORD file size // NameAndSize->Size = (ULONGLONG)FileInCabInfo->FileSize; u = FILEOP_DOIT; } else { u = NO_ERROR; } return(u); } BOOL AddUnsupportedFilesToCopyList( IN HWND ParentWindow, IN PUNSUPORTED_DRIVER_INFO DriverList ) /*++ Routine Description: Adds unsupported, required drivers to be used during textmode setup. This would include 3rd party mass storage drivers, for instance. The files are simply appended to the master copy list. Arguments: ParentWindow - ParentWindow used for UI. DriverList - supplies the list of drivers to be added to the copy list. Return Value: If successful, returns a pointer to the new FIL structure for the file. Otherwise returns NULL (the caller can assume out of memory). --*/ { PUNSUPORTED_DRIVER_INFO p; ULONG Error; PUNSUPORTED_DRIVER_FILE_INFO q; PDIR r; PUNSUPORTED_DRIVER_INSTALL_INFO s; UNREFERENCED_PARAMETER(ParentWindow); for( p = DriverList; p != NULL; p = p->Next ) { for( q = p->FileList; q != NULL; q = q->Next ) { r = MALLOC( sizeof( DIR ) ); if( r == NULL ) { return( FALSE ); } r->Next = NULL; r->InfSymbol = NULL; r->Flags = 0; r->TargetName = NULL; r->SourceName = DupString( q->TargetDirectory ); if( r->SourceName == NULL) { FREE( r ); return( FALSE ); } if( !AddFile( &MasterCopyList, q->FileName, NULL, r, FILE_NT_MIGRATE | FILE_NEED_TO_FREE_SOURCENAME, 0 ) ) { FREE( (LPTSTR)(r->SourceName) ); FREE( r ); return( FALSE ); } // // now make sure the referenced file is not overwritten with an inbox driver // with the same name // RemoveFile (&MasterCopyList, q->FileName, NULL, FILE_IN_LOCAL_BOOT); } for( s = p->InstallList; s != NULL; s = s->Next ) { r = MALLOC( sizeof( DIR ) ); if( r == NULL ) { return( FALSE ); } r->Next = NULL; r->InfSymbol = NULL; r->Flags = 0; r->TargetName = NULL; r->SourceName = DupString( s->InfRelPath ); if( r->SourceName == NULL) { FREE( r ); return( FALSE ); } // // add the INF and the CAT (optional) // if( !AddFile( &MasterCopyList, s->InfFileName, s->InfOriginalFileName, r, FILE_NT_MIGRATE | FILE_NEED_TO_FREE_SOURCENAME | FILE_NEED_TO_FREE_TARGETNAME, 0 ) ) { FREE( (LPTSTR)(r->SourceName) ); FREE( r ); return( FALSE ); } if (s->CatalogRelPath && s->CatalogFileName) { r = MALLOC( sizeof( DIR ) ); if( r == NULL ) { return( FALSE ); } r->Next = NULL; r->InfSymbol = NULL; r->Flags = 0; r->TargetName = NULL; r->SourceName = DupString( s->CatalogRelPath ); if( r->SourceName == NULL) { FREE( r ); return( FALSE ); } // // add the INF and the CAT (optional) // if( !AddFile( &MasterCopyList, s->CatalogFileName, s->CatalogOriginalFileName, r, FILE_NT_MIGRATE | FILE_NEED_TO_FREE_SOURCENAME | FILE_NEED_TO_FREE_TARGETNAME, 0 ) ) { FREE( (LPTSTR)(r->SourceName) ); FREE( r ); return( FALSE ); } } } } return(TRUE); } BOOL AddGUIModeCompatibilityInfsToCopyList( VOID ) /*++ Routine Description: Adds the compatibility INF to the copy queue. The compatibility inf is used during GUI-setup to remove incompatible drivers. Arguments: None. Return Value: If successful, returns TRUE. --*/ { PDIR CompDir; PLIST_ENTRY Next_Link; PCOMPATIBILITY_DATA CompData; TCHAR InfLocation[MAX_PATH], *t; TCHAR relPath[MAX_PATH]; WIN32_FIND_DATA fd; Next_Link = CompatibilityData.Flink; if( Next_Link ){ while ((ULONG_PTR)Next_Link != (ULONG_PTR)&CompatibilityData) { CompData = CONTAINING_RECORD( Next_Link, COMPATIBILITY_DATA, ListEntry ); Next_Link = CompData->ListEntry.Flink; if(CompData->InfName && CompData->InfSection && *CompData->InfName && *CompData->InfSection) { BOOL b = FALSE; if (FileExists (CompData->InfName, &fd) && !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { b = SUCCEEDED (StringCchCopy (InfLocation, ARRAYSIZE(InfLocation), CompData->InfName)); if (b) { DebugLog( Winnt32LogInformation, TEXT("Using private compatibility inf %1"), 0, InfLocation ); } } else { b = BuildPath (relPath, TEXT("compdata"), CompData->InfName) && FindPathToWinnt32File (relPath, InfLocation, ARRAYSIZE(InfLocation)); } if (b) { b = FALSE; t = _tcsrchr (InfLocation, TEXT('\\')); if (t) { *t = 0; CompDir = AddDirectory( NULL, &MasterCopyList, InfLocation, TEXT("\\"), DIR_NEED_TO_FREE_SOURCENAME | DIR_ABSOLUTE_PATH ); if (CompDir && AddFile ( &MasterCopyList, t + 1, NULL, CompDir, (IsArc() ? 0 : FILE_IN_LOCAL_BOOT) | FILE_NEED_TO_FREE_SOURCENAME, 0 )) { b = TRUE; } } } if (!b) { DebugLog( Winnt32LogError, TEXT( "\r\n\r\nError encountered while trying to copy compatibility infs\r\n"), 0 ); return(FALSE); } } } } return( TRUE ); } LRESULT DiskDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { switch(msg) { case WM_INITDIALOG: // // Fill in the diagnostic list... // SetDlgItemText( hdlg, IDC_DISKDIAG, DiskDiagMessage ); return( TRUE ); case WM_COMMAND: if( (LOWORD(wParam) == IDOK) && (HIWORD(wParam) == BN_CLICKED)) { EndDialog(hdlg,TRUE); } return( TRUE ); case WM_CTLCOLOREDIT: SetBkColor( (HDC)wParam, GetSysColor(COLOR_BTNFACE)); return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); break; default: break; } return( FALSE ); }