/*++ Copyright (c) 1997 Microsoft Corporation Module Name: sysmig.c Abstract: System migration functions for Win95 Author: Jim Schmidt (jimschm) 13-Feb-1997 Revision History: jimschm 09-Mar-2001 Redesigned file list code to support LDID enumeration in a clear way marcw 18-Mar-1999 Boot16 now set to BOOT16_YES unless it has been explicitly set to BOOT16_NO or the partition will be converted to NTFS. jimschm 23-Sep-1998 Updated for new fileops code jimschm 12-May-1998 Added .386 warning calinn 19-Nov-1997 Added pSaveDosFiles, will move DOS files out of the way during upgrade marcw 21-Jul-1997 Added end-processing of special keys. (e.g. HKLM\Software\Microsoft\CurrentVersion\RUN) jimschm 20-Jun-1997 Hooked up user loop and saved user names to memdb --*/ #include "pch.h" #include "sysmigp.h" #include "progbar.h" #include "regops.h" #include "oleregp.h" #include typedef struct TAG_DIRPATH { struct TAG_DIRPATH *Next; TCHAR DirPath[]; } DIRIDPATH, *PDIRIDPATH; typedef struct TAG_DIRID { struct TAG_DIRID *Next; PDIRIDPATH FirstDirPath; UINT DirId; } DIRIDMAP, *PDIRIDMAP; typedef struct { PDIRIDPATH LastMatch; PCTSTR SubPath; PTSTR ResultBuffer; } DIRNAME_ENUM, *PDIRNAME_ENUM; UINT *g_Boot16; UINT g_ProgressBarTime; PDIRIDMAP g_HeadDirId; PDIRIDMAP g_TailDirId; POOLHANDLE g_DirIdPool; PDIRIDMAP g_LastIdPtr; BOOL pWarnAboutOldDrivers ( VOID ); VOID pAddNtFile ( IN PCTSTR Dir, OPTIONAL IN PCTSTR FileName, OPTIONAL IN BOOL BackupThisFile, IN BOOL CleanThisFile, IN BOOL OsFile ); BOOL WINAPI SysMig_Entry ( IN HINSTANCE hinstDLL, IN DWORD dwReason, IN LPVOID lpv ) /*++ Routine Description: SysMig_Entry is a DllMain-like init funciton, called by w95upg\dll. This function is called at process attach and detach. Arguments: hinstDLL - (OS-supplied) instance handle for the DLL dwReason - (OS-supplied) indicates attach or detatch from process or thread lpv - unused Return Value: Return value is always TRUE (indicating successful init). --*/ { switch (dwReason) { case DLL_PROCESS_ATTACH: g_DirIdPool = PoolMemInitNamedPool ("FileList"); break; case DLL_PROCESS_DETACH: TerminateCacheFolderTracking(); if (g_DirIdPool) { PoolMemDestroyPool (g_DirIdPool); } break; } return TRUE; } BOOL pPreserveShellIcons ( VOID ) /*++ Routine Description: This routine scans the Shell Icons for references to files that are expected to be deleted. If a reference is found, the file is removed from the deleted list, and marked to be moved to %windir%\migicons\shl. Arguments: none Return Value: none --*/ { REGVALUE_ENUM e; HKEY ShellIcons; PCTSTR Data; TCHAR ArgZero[MAX_CMDLINE]; DWORD Binary = 0; INT IconIndex; PCTSTR p; // // Scan all ProgIDs, looking for default icons that are currently // set for deletion. Once found, save the icon. // ShellIcons = OpenRegKeyStr (S_SHELL_ICONS_REG_KEY); if (ShellIcons) { if (EnumFirstRegValue (&e, ShellIcons)) { do { Data = (PCTSTR) GetRegValueDataOfType (ShellIcons, e.ValueName, REG_SZ); if (Data) { ExtractArgZero (Data, ArgZero); if (FILESTATUS_UNCHANGED != GetFileStatusOnNt (ArgZero)) { p = _tcschr (Data, TEXT(',')); if (p) { IconIndex = _ttoi (_tcsinc (p)); } else { IconIndex = 0; } // // Extract will fail only if the icon is known good // if (ExtractIconIntoDatFile ( ArgZero, IconIndex, &g_IconContext, NULL )) { DEBUGMSG ((DBG_SYSMIG, "Preserving shell icon file %s", ArgZero)); } } MemFree (g_hHeap, 0, Data); } } while (EnumNextRegValue (&e)); } CloseRegKey (ShellIcons); } return TRUE; } BOOL pMoveStaticFiles ( VOID ) { BOOL rSuccess = TRUE; INFSTRUCT is = INITINFSTRUCT_POOLHANDLE; PCTSTR from; PCTSTR to; PCTSTR fromExpanded; PCTSTR toExpanded; PCTSTR toFinalDest; PTSTR Pattern; FILE_ENUM e; // // Cycle through all of the entries in the static move files section. // if (InfFindFirstLine(g_Win95UpgInf,S_STATIC_MOVE_FILES,NULL,&is)) { do { // // For each entry, check to see if the file exists. If it does, // add it into the memdb move file section. // from = InfGetStringField(&is,0); to = InfGetStringField(&is,1); if (from && to) { fromExpanded = ExpandEnvironmentText(from); toExpanded = ExpandEnvironmentText(to); Pattern = _tcsrchr (fromExpanded, TEXT('\\')); // // full path please // MYASSERT (Pattern); if (!Pattern) { continue; } *Pattern = 0; Pattern++; if (EnumFirstFile (&e, fromExpanded, Pattern)) { do { if (!StringIMatch (e.FileName, Pattern)) { // // pattern specified // toFinalDest = JoinPaths (toExpanded, e.FileName); } else { toFinalDest = toExpanded; } if (!IsFileMarkedAsHandled (e.FullPath)) { // // Remove general operations, then mark for move // RemoveOperationsFromPath ( e.FullPath, OPERATION_FILE_DELETE| OPERATION_FILE_MOVE| OPERATION_FILE_MOVE_BY_NT| OPERATION_FILE_MOVE_SHELL_FOLDER| OPERATION_CREATE_FILE ); MarkFileForMove (e.FullPath, toFinalDest); } if (toFinalDest != toExpanded) { FreePathString (toFinalDest); } } while (EnumNextFile (&e)); } --Pattern; *Pattern = TEXT('\\'); FreeText (toExpanded); FreeText (fromExpanded); } } while (InfFindNextLine(&is)); } InfCleanUpInfStruct(&is); return rSuccess; } DWORD MoveStaticFiles ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_MOVE_STATIC_FILES; case REQUEST_RUN: if (!pMoveStaticFiles ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in MoveStaticFiles.")); } return 0; } BOOL pCopyStaticFiles ( VOID ) { BOOL rSuccess = TRUE; INFSTRUCT is = INITINFSTRUCT_POOLHANDLE; PCTSTR from; PCTSTR to; PCTSTR fromExpanded; PCTSTR toExpanded; PCTSTR toFinalDest; PTSTR Pattern; FILE_ENUM e; // // Cycle through all of the entries in the static copy files section. // if (InfFindFirstLine(g_Win95UpgInf,S_STATIC_COPY_FILES,NULL,&is)) { do { // // For each entry, check to see if the file exists. If it does, // add it into the memdb copy file section. // from = InfGetStringField(&is,0); to = InfGetStringField(&is,1); if (from && to) { fromExpanded = ExpandEnvironmentText(from); toExpanded = ExpandEnvironmentText(to); Pattern = _tcsrchr (fromExpanded, TEXT('\\')); // // full path please // MYASSERT (Pattern); if (!Pattern) { continue; } *Pattern = 0; Pattern++; if (EnumFirstFile (&e, fromExpanded, Pattern)) { do { if (!StringIMatch (e.FileName, Pattern)) { // // pattern specified // toFinalDest = JoinPaths (toExpanded, e.FileName); } else { toFinalDest = toExpanded; } if (!IsFileMarkedForOperation (e.FullPath, OPERATION_FILE_DELETE)) { MarkFileForCopy (e.FullPath, toFinalDest); } if (toFinalDest != toExpanded) { FreePathString (toFinalDest); } } while (EnumNextFile (&e)); } --Pattern; *Pattern = TEXT('\\'); FreeText (toExpanded); FreeText (fromExpanded); } } while (InfFindNextLine(&is)); } InfCleanUpInfStruct(&is); return rSuccess; } DWORD CopyStaticFiles ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_COPY_STATIC_FILES; case REQUEST_RUN: if (!pCopyStaticFiles ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in CopyStaticFiles.")); } return 0; } DWORD PreserveShellIcons ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_PRESERVE_SHELL_ICONS; case REQUEST_RUN: if (!pPreserveShellIcons ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in PreserveShellIcons")); } return 0; } PCTSTR GetWindowsInfDir( VOID ) { PTSTR WindowsInfDir = NULL; /* NTBUG9:419428 - This registry entry is a semi-colon list of INF paths, and it can contain Win9x source media INFs on OEM machines. WindowsInfDir = (PTSTR) GetRegData (S_WINDOWS_CURRENTVERSION, S_DEVICEPATH); */ if (!WindowsInfDir) { WindowsInfDir = (PTSTR) MemAlloc (g_hHeap, 0, SizeOfString (g_WinDir) + sizeof (S_INF)); StringCopy (WindowsInfDir, g_WinDir); StringCopy (AppendWack (WindowsInfDir), S_INF); } return WindowsInfDir; } #ifndef SM_CMONITORS #define SM_CMONITORS 80 #endif BOOL pProcessMiscMessages ( VOID ) /*++ Routine Description: pProcessMiscMessages performs runtime tests for items that are incompatible, and it adds messages when the tests succeed. Arguments: None. Return Value: Always TRUE. --*/ { PCTSTR Group; PCTSTR Message; OSVERSIONINFO Version; WORD CodePage; LCID Locale; if (GetSystemMetrics (SM_CMONITORS) > 1) { // // On Win95 and OSR2, GetSystemMetrics returns 0. // Group = BuildMessageGroup (MSG_INSTALL_NOTES_ROOT, MSG_MULTI_MONITOR_UNSUPPORTED_SUBGROUP, NULL); Message = GetStringResource (MSG_MULTI_MONITOR_UNSUPPORTED); if (Message && Group) { MsgMgr_ObjectMsg_Add (TEXT("*MultiMonitor"), Group, Message); } FreeText (Group); FreeStringResource (Message); } pWarnAboutOldDrivers(); // // Save platform info // Version.dwOSVersionInfoSize = sizeof (Version); GetVersionEx (&Version); MemDbSetValueEx ( MEMDB_CATEGORY_STATE, MEMDB_ITEM_MAJOR_VERSION, NULL, NULL, Version.dwMajorVersion, NULL ); MemDbSetValueEx ( MEMDB_CATEGORY_STATE, MEMDB_ITEM_MINOR_VERSION, NULL, NULL, Version.dwMinorVersion, NULL ); MemDbSetValueEx ( MEMDB_CATEGORY_STATE, MEMDB_ITEM_BUILD_NUMBER, NULL, NULL, Version.dwBuildNumber, NULL ); MemDbSetValueEx ( MEMDB_CATEGORY_STATE, MEMDB_ITEM_PLATFORM_ID, NULL, NULL, Version.dwPlatformId, NULL ); MemDbSetValueEx ( MEMDB_CATEGORY_STATE, MEMDB_ITEM_VERSION_TEXT, NULL, Version.szCSDVersion, 0, NULL ); GetGlobalCodePage (&CodePage, &Locale); MemDbSetValueEx ( MEMDB_CATEGORY_STATE, MEMDB_ITEM_CODE_PAGE, NULL, NULL, CodePage, NULL ); MemDbSetValueEx ( MEMDB_CATEGORY_STATE, MEMDB_ITEM_LOCALE, NULL, NULL, Locale, NULL ); // // Bad hard disk warning // if (!g_ConfigOptions.GoodDrive && HwComp_ReportIncompatibleController()) { // // Turn on boot loader flag // WriteInfKey (WINNT_DATA, WINNT_D_WIN95UNSUPHDC, S_ONE); } return TRUE; } DWORD ProcessMiscMessages ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_MISC_MESSAGES; case REQUEST_RUN: if (!pProcessMiscMessages()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in ProcessSpecialKeys")); } return 0; } BOOL pDeleteWinDirWackInf ( VOID ) { PCTSTR WindowsInfDir; FILE_ENUM e; DWORD count = 0; // // Delete all contents of c:\windows\inf. // WindowsInfDir = GetWindowsInfDir(); if (!WindowsInfDir) { return FALSE; } if (EnumFirstFile (&e, WindowsInfDir, NULL)) { do { MarkFileForDelete (e.FullPath); count++; if (!(count % 32)) { TickProgressBar (); } } while (EnumNextFile (&e)); } MemFree (g_hHeap, 0, WindowsInfDir); return TRUE; } DWORD DeleteWinDirWackInf ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_DELETE_WIN_DIR_WACK_INF; case REQUEST_RUN: if (!pDeleteWinDirWackInf ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in DeleteWinDirWackInf")); } return 0; } BOOL pMoveWindowsIniFiles ( VOID ) { WIN32_FIND_DATA fd; HANDLE FindHandle; TCHAR WinDirPattern[MAX_TCHAR_PATH]; TCHAR FullPath[MAX_TCHAR_PATH]; TCHAR Key[MEMDB_MAX]; INFCONTEXT context; DWORD result; BOOL b = FALSE; // // build suppression table // if (SetupFindFirstLine (g_Win95UpgInf, S_INI_FILES_IGNORE, NULL, &context)) { do { if (SetupGetStringField (&context, 0, Key, MEMDB_MAX, NULL)) { MemDbSetValueEx ( MEMDB_CATEGORY_INIFILES_IGNORE, Key, NULL, NULL, 0, NULL ); } } while (SetupFindNextLine (&context, &context)); } // // Scan %windir% for files // wsprintf (WinDirPattern, TEXT("%s\\*.ini"), g_WinDir); FindHandle = FindFirstFile (WinDirPattern, &fd); if (FindHandle != INVALID_HANDLE_VALUE) { __try { do { // // don't move and process specific INI files // MemDbBuildKey (Key, MEMDB_CATEGORY_INIFILES_IGNORE, fd.cFileName, NULL, NULL); if (!MemDbGetValue (Key, &result)) { wsprintf (FullPath, TEXT("%s\\%s"), g_WinDir, fd.cFileName); if (CanSetOperation (FullPath, OPERATION_TEMP_PATH)) { // // see bug 317646 // #ifdef DEBUG if (StringIMatch (fd.cFileName, TEXT("netcfg.ini"))) { continue; } #endif MarkFileForTemporaryMove (FullPath, FullPath, g_TempDir); MarkFileForBackup (FullPath); } } ELSE_DEBUGMSG ((DBG_NAUSEA, "Ini File Ignored : %s\\%s", g_WinDir, fd.cFileName)); } while (FindNextFile (FindHandle, &fd)); b = TRUE; } __finally { FindClose (FindHandle); } } return b; } DWORD MoveWindowsIniFiles ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_MOVE_INI_FILES; case REQUEST_RUN: if (!pMoveWindowsIniFiles ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in MoveWindowsIniFiles")); } return 0; } PTSTR pFindDosFile ( IN PTSTR FileName ) { WIN32_FIND_DATA findFileData; PTSTR fullPathName = NULL; PTSTR fullFileName = NULL; HANDLE findHandle; fullPathName = AllocPathString (MAX_TCHAR_PATH); fullFileName = AllocPathString (MAX_TCHAR_PATH); _tcsncpy (fullPathName, g_WinDir, MAX_TCHAR_PATH/sizeof (fullPathName [0])); fullFileName = JoinPaths (fullPathName, FileName); findHandle = FindFirstFile (fullFileName, &findFileData); if (findHandle != INVALID_HANDLE_VALUE) { FindClose (&findFileData); FreePathString (fullPathName); return fullFileName; } FreePathString (fullFileName); StringCat (fullPathName, S_BOOT16_COMMAND_DIR); fullFileName = JoinPaths (fullPathName, FileName); findHandle = FindFirstFile (fullFileName, &findFileData); if (findHandle != INVALID_HANDLE_VALUE) { FindClose (&findFileData); FreePathString (fullPathName); return fullFileName; } FreePathString (fullPathName); FreePathString (fullFileName); return NULL; } BOOL pSaveDosFile ( IN PTSTR FileName, IN PTSTR FullFileName, IN PTSTR TempPath ) { PTSTR newFileName = NULL; newFileName = JoinPaths (TempPath, FileName); if (!CopyFile (FullFileName, newFileName, FALSE)) { DEBUGMSG ((DBG_WARNING, "BOOT16 : Cannot copy %s to %s", FullFileName, newFileName)); } FreePathString (newFileName); return TRUE; } VOID pReportNoBoot16 ( VOID ) /* This function will report that BOOT16 option will not be available because the file system is going to be converted to NTFS. */ { PCTSTR ReportEntry; PCTSTR ReportTitle; PCTSTR Message; PCTSTR Group; PTSTR argArray[1]; ReportEntry = GetStringResource (MSG_INSTALL_NOTES_ROOT); if (ReportEntry) { argArray [0] = g_Win95Name; ReportTitle = (PCTSTR)ParseMessageID (MSG_NO_BOOT16_WARNING_SUBGROUP, argArray); if (ReportTitle) { Message = (PCTSTR)ParseMessageID (MSG_NO_BOOT16_WARNING, argArray); if (Message) { Group = JoinPaths (ReportEntry, ReportTitle); if (Group) { MsgMgr_ObjectMsg_Add (TEXT("*NoBoot16"), Group, Message); FreePathString (Group); } FreeStringResourcePtrA (&Message); } FreeStringResourcePtrA (&ReportTitle); } FreeStringResource (ReportEntry); } } #define S_IOFILE TEXT("IO.SYS") #define S_MSDOSFILE TEXT("MSDOS.SYS") #define S_CONFIG_SYS TEXT("CONFIG.SYS") #define S_AUTOEXEC_BAT TEXT("AUTOEXEC.BAT") VOID pMarkDosFileForChange ( IN PCTSTR FileName ) { pAddNtFile (g_BootDrivePath, FileName, TRUE, TRUE, TRUE); } BOOL pSaveDosFiles ( VOID ) { HINF WkstaMigInf = INVALID_HANDLE_VALUE; PTSTR boot16TempPath = NULL; INFCONTEXT infContext; PTSTR fileName = NULL; PTSTR fullFileName = NULL; PTSTR wkstaMigSource = NULL; PTSTR wkstaMigTarget = NULL; DWORD result; TCHAR dir[MAX_PATH]; // // For restore purposes, mark MSDOS environment as a Win9x OS file // pMarkDosFileForChange (S_IOFILE); pMarkDosFileForChange (S_MSDOSFILE); pMarkDosFileForChange (S_AUTOEXEC_BAT); pMarkDosFileForChange (S_CONFIG_SYS); // // Now create a backup dir // if ((*g_Boot16 == BOOT16_YES) && (*g_ForceNTFSConversion)) { WriteInfKey (S_WIN9XUPGUSEROPTIONS, TEXT("boot16"), S_NO); // // We no longer report the no boot16 message. // //pReportNoBoot16 (); // return TRUE; } if (*g_Boot16 == BOOT16_NO) { WriteInfKey (S_WIN9XUPGUSEROPTIONS, TEXT("boot16"), S_NO); } else if (*g_Boot16 == BOOT16_YES) { WriteInfKey (S_WIN9XUPGUSEROPTIONS, TEXT("boot16"), S_YES); } else { WriteInfKey (S_WIN9XUPGUSEROPTIONS, TEXT("boot16"), S_BOOT16_AUTOMATIC); } __try { //prepare our temporary directory for saving dos7 files boot16TempPath = JoinPaths (g_TempDir, S_BOOT16_DOS_DIR); if (!CreateDirectory (boot16TempPath, NULL) && (GetLastError()!=ERROR_ALREADY_EXISTS)) { LOG ((LOG_ERROR,"BOOT16 : Unable to create temporary directory %s",boot16TempPath)); __leave; } fileName = AllocPathString (MAX_TCHAR_PATH); //load the files needed for booting in a 16 bit environment. The files are listed //in wkstamig.inf section [Win95-DOS files] wkstaMigSource = JoinPaths (SOURCEDIRECTORY(0), S_WKSTAMIG_INF); wkstaMigTarget = JoinPaths (g_TempDir, S_WKSTAMIG_INF); result = SetupDecompressOrCopyFile (wkstaMigSource, wkstaMigTarget, 0); if ((result != ERROR_SUCCESS) && (result != ERROR_ALREADY_EXISTS)) { LOG ((LOG_ERROR,"BOOT16 : Unable to decompress WKSTAMIG.INF")); __leave; } WkstaMigInf = InfOpenInfFile (wkstaMigTarget); if (WkstaMigInf == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR,"BOOT16 : WKSTAMIG.INF could not be opened")); __leave; } //read the section, for every file we are trying to find it in either %windir% or //%windir%\command. If we find it, we'll just copy it to a safe place if (!SetupFindFirstLine ( WkstaMigInf, S_BOOT16_SECTION, NULL, &infContext )) { LOG ((LOG_ERROR,"Cannot read from %s section (WKSTAMIG.INF)",S_BOOT16_SECTION)); return TRUE; } do { if (SetupGetStringField ( &infContext, 0, fileName, MAX_TCHAR_PATH/sizeof(fileName[0]), NULL )) { //see if we can find this file either in %windir% or in %windir%\command fullFileName = pFindDosFile (fileName); if (fullFileName != NULL) { pSaveDosFile (fileName, fullFileName, boot16TempPath); FreePathString (fullFileName); fullFileName = NULL; } } } while (SetupFindNextLine (&infContext, &infContext)); //OK, now save io.sys. fullFileName = AllocPathString (MAX_TCHAR_PATH); StringCopy (fullFileName, g_BootDrivePath); StringCat (fullFileName, S_IOFILE); pSaveDosFile (S_IOFILE, fullFileName, boot16TempPath); FreePathString (fullFileName); fullFileName = NULL; } __finally { if (WkstaMigInf != INVALID_HANDLE_VALUE) { InfCloseInfFile (WkstaMigInf); } if (boot16TempPath) { FreePathString (boot16TempPath); } if (wkstaMigSource) { FreePathString (wkstaMigSource); } if (wkstaMigTarget) { DeleteFile (wkstaMigTarget); FreePathString (wkstaMigTarget); } if (fileName) { FreePathString (fileName); } } return TRUE; } DWORD SaveDosFiles ( IN DWORD Request ) { if (REPORTONLY()) { return 0; } switch (Request) { case REQUEST_QUERYTICKS: return TICKS_SAVE_DOS_FILES; case REQUEST_RUN: if (!pSaveDosFiles ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in SaveDosFiles")); } return 0; } DWORD InitWin95Registry ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_INIT_WIN95_REGISTRY; case REQUEST_RUN: return Win95RegInit (g_WinDir, ISMILLENNIUM()); default: DEBUGMSG ((DBG_ERROR, "Bad parameter in InitWin95Registry")); } return 0; } PDIRIDMAP pFindDirId ( IN UINT DirId, IN PDIRIDMAP BestGuess, OPTIONAL IN BOOL Create ) { PDIRIDMAP map; MYASSERT (Create || (g_HeadDirId && g_TailDirId)); // // Find the existing dir ID. Check the caller's best guess first. // if (BestGuess && BestGuess->DirId == DirId) { return BestGuess; } map = g_HeadDirId; while (map) { if (map->DirId == DirId) { return map; } map = map->Next; } if (!Create) { return NULL; } // // Insert a new dir ID struct at the end of the list // map = (PDIRIDMAP) PoolMemGetAlignedMemory (g_DirIdPool, sizeof (DIRIDMAP)); if (g_TailDirId) { g_TailDirId->Next = map; } else { g_HeadDirId = map; } g_TailDirId = map; map->Next = NULL; map->FirstDirPath = NULL; map->DirId = DirId; return map; } VOID pInsertDirIdPath ( IN UINT DirId, IN PCTSTR DirPath, IN OUT PDIRIDMAP *BestGuess ) { PDIRIDPATH pathStruct; PDIRIDPATH existingPathStruct; PDIRIDMAP dirIdMap; // // Locate the dir ID structure, then append the DirPath to the // list of paths for the ID // dirIdMap = pFindDirId (DirId, *BestGuess, TRUE); MYASSERT (dirIdMap); *BestGuess = dirIdMap; existingPathStruct = dirIdMap->FirstDirPath; while (existingPathStruct) { if (StringIMatch (existingPathStruct->DirPath, DirPath)) { return; } existingPathStruct = existingPathStruct->Next; } pathStruct = (PDIRIDPATH) PoolMemGetAlignedMemory ( g_DirIdPool, sizeof (DIRIDPATH) + SizeOfString (DirPath) ); pathStruct->Next = dirIdMap->FirstDirPath; dirIdMap->FirstDirPath = pathStruct; StringCopy (pathStruct->DirPath, DirPath); } BOOL pConvertFirstDirName ( OUT PDIRNAME_ENUM EnumPtr, IN PCTSTR DirNameWithId, OUT PTSTR DirNameWithPath, IN OUT PDIRIDMAP *LastDirIdMatch, IN BOOL Convert11To1501 ) { UINT id; PDIRIDMAP idToPath; EnumPtr->ResultBuffer = DirNameWithPath; EnumPtr->LastMatch = NULL; // // Find the dir ID in the list of all dir IDs // id = _tcstoul (DirNameWithId, (PTSTR *) (&EnumPtr->SubPath), 10); if (!id) { DEBUGMSG ((DBG_WARNING, "Dir ID %s is not valid", DirNameWithId)); return FALSE; } DEBUGMSG_IF (( EnumPtr->SubPath[0] != TEXT('\\') && EnumPtr->SubPath[0], DBG_WHOOPS, "Error in filelist.dat: non-numeric characters following LDID: %s", DirNameWithId )); if (Convert11To1501 && id == 11) { id = 1501; } idToPath = pFindDirId (id, *LastDirIdMatch, FALSE); if (!idToPath || !(idToPath->FirstDirPath)) { DEBUGMSG ((DBG_WARNING, "Dir ID %s is not in the list and might not exist on the system", DirNameWithId)); return FALSE; } *LastDirIdMatch = idToPath; EnumPtr->LastMatch = idToPath->FirstDirPath; wsprintf (EnumPtr->ResultBuffer, TEXT("%s%s"), EnumPtr->LastMatch->DirPath, EnumPtr->SubPath); return TRUE; } BOOL pConvertNextDirName ( IN OUT PDIRNAME_ENUM EnumPtr ) { if (EnumPtr->LastMatch) { EnumPtr->LastMatch = EnumPtr->LastMatch->Next; } if (!EnumPtr->LastMatch) { return FALSE; } wsprintf (EnumPtr->ResultBuffer, TEXT("%s%s"), EnumPtr->LastMatch->DirPath, EnumPtr->SubPath); return TRUE; } typedef struct _KNOWN_DIRS { PCSTR DirId; PCSTR *Translation; } KNOWN_DIRS, *PKNOWN_DIRS; KNOWN_DIRS g_KnownDirs [] = { {"10" , &g_WinDir}, {"11" , &g_System32Dir}, {"12" , &g_DriversDir}, {"17" , &g_InfDir}, {"18" , &g_HelpDir}, {"20" , &g_FontsDir}, {"21" , &g_ViewersDir}, {"23" , &g_ColorDir}, {"24" , &g_WinDrive}, {"25" , &g_SharedDir}, {"30" , &g_BootDrive}, {"50" , &g_SystemDir}, {"51" , &g_SpoolDir}, {"52" , &g_SpoolDriversDir}, {"53" , &g_ProfileDirNt}, {"54" , &g_BootDrive}, {"55" , &g_PrintProcDir}, {"1501" , &g_SystemDir}, {"1501" , &g_System32Dir}, {"7523" , &g_ProfileDir}, {"7523" , &g_CommonProfileDir}, {"16422", &g_ProgramFilesDir}, {"16427", &g_ProgramFilesCommonDir}, {"66002", &g_System32Dir}, {"66003", &g_ColorDir}, {NULL, NULL} }; typedef struct { PCSTR ShellFolderName; PCSTR DirId; } SHELL_TO_DIRS, *PSHELL_TO_DIRS; SHELL_TO_DIRS g_ShellToDirs[] = { {"Administrative Tools", "7501"}, {"Common Administrative Tools", "7501"}, {"AppData", "7502"}, {"Common AppData", "7502"}, {"Cache", "7503"}, {"Cookies", "7504"}, {"Desktop", "7505"}, {"Common Desktop", "7505"}, {"Favorites", "7506"}, {"Common Favorites", "7506"}, {"Fonts", "7507"}, {"History", "7508"}, {"Local AppData", "7509"}, {"Local Settings", "7510"}, {"My Music", "7511"}, {"CommonMusic", "7511"}, {"My Pictures", "7512"}, {"CommonPictures", "7512"}, {"My Video", "7513"}, {"CommonVideo", "7513"}, {"NetHood", "7514"}, {"Personal", "7515"}, {"Common Personal", "7515"}, {"Common Documents", "7515"}, {"PrintHood", "7516"}, {"Programs", "7517"}, {"Common Programs", "7517"}, {"Recent", "7518"}, {"SendTo", "7519"}, {"Start Menu", "7520"}, {"Common Start Menu", "7520"}, {"Startup", "7521"}, {"Common Startup", "7521"}, {"Templates", "7522"}, {"Common Templates", "7522"}, {"Profiles", "7523"}, {"Common Profiles", "7523"}, {NULL, NULL} }; VOID pAddKnownShellFolder ( IN PCTSTR ShellFolderName, IN PCTSTR SrcPath ) { PSHELL_TO_DIRS p; // // Translate shell folder name into a dir ID // for (p = g_ShellToDirs ; p->ShellFolderName ; p++) { if (StringIMatch (ShellFolderName, p->ShellFolderName)) { break; } } if (!p->ShellFolderName) { DEBUGMSG ((DBG_ERROR, "This system has an unsupported shell folder tag: %s", ShellFolderName)); return; } // // Record dir ID to path match in grow list // pInsertDirIdPath (_tcstoul (p->DirId, NULL, 10), SrcPath, &g_LastIdPtr); } VOID pInitKnownDirs ( VOID ) { USERENUM eUser; SF_ENUM e; PKNOWN_DIRS p; // // Add all fixed known dirs to grow lists // for (p = g_KnownDirs ; p->DirId ; p++) { pInsertDirIdPath (_tcstoul (p->DirId, NULL, 10), *(p->Translation), &g_LastIdPtr); } // // Enumerate all users and put their shell folders in a known dirs struct // if (EnumFirstUser (&eUser, ENUMUSER_ENABLE_NAME_FIX)) { do { if (!(eUser.AccountType & INVALID_ACCOUNT)) { if (eUser.AccountType & NAMED_USER) { // // Process the shell folders for this migrated user // if (EnumFirstRegShellFolder (&e, &eUser)) { do { pAddKnownShellFolder (e.sfName, e.sfPath); } while (EnumNextRegShellFolder (&e)); } } } } while (!CANCELLED() && EnumNextUser (&eUser)); if (EnumFirstRegShellFolder (&e, NULL)) { do { pAddKnownShellFolder (e.sfName, e.sfPath); } while (!CANCELLED() && EnumNextRegShellFolder (&e)); } } } VOID pCleanUpKnownDirs ( VOID ) { if (g_DirIdPool) { PoolMemDestroyPool (g_DirIdPool); g_DirIdPool = NULL; g_HeadDirId = NULL; g_TailDirId = NULL; } } VOID pAddNtFile ( IN PCTSTR Dir, OPTIONAL IN PCTSTR FileName, OPTIONAL IN BOOL BackupThisFile, IN BOOL CleanThisFile, IN BOOL OsFile ) { TCHAR copyOfFileName[MAX_PATH]; PTSTR p; PCTSTR fullPath; BOOL freePath = FALSE; DWORD offset; TCHAR key[MEMDB_MAX]; DWORD value; if (!Dir || !FileName) { if (!Dir) { MYASSERT (FileName); fullPath = FileName; } else { fullPath = Dir; } StringCopy (copyOfFileName, fullPath); p = _tcsrchr (copyOfFileName, TEXT('\\')); if (p) { *p = 0; Dir = copyOfFileName; FileName = p + 1; } else { DEBUGMSG ((DBG_WHOOPS, "Incomplete file name passed as NT OS file: %s", fullPath)); return; } } else { fullPath = NULL; } MYASSERT (Dir); MYASSERT (FileName); if (OsFile) { MemDbSetValueEx ( MEMDB_CATEGORY_NT_DIRS, Dir, NULL, NULL, 0, &offset ); MemDbSetValueEx ( MEMDB_CATEGORY_NT_FILES, FileName, NULL, NULL, offset, NULL ); } if (BackupThisFile || CleanThisFile) { if (!fullPath) { fullPath = JoinPaths (Dir, FileName); freePath = TRUE; } if (BackupThisFile) { // // If the file exists, back it up (and don't clean it) // if (DoesFileExist (fullPath)) { MarkFileForBackup (fullPath); CleanThisFile = FALSE; } } if (CleanThisFile) { // // Clean will cause the NT-installed file to be deleted // if (!DoesFileExist (fullPath)) { MemDbBuildKey ( key, MEMDB_CATEGORY_CLEAN_OUT, fullPath, NULL, NULL ); if (MemDbGetValue (key, &value)) { if (value) { DEBUGMSG (( DBG_WARNING, "File %s already in uninstall cleanout list with type %u", fullPath, value )); } return; } MemDbSetValue (key, 0); } } if (freePath) { FreePathString (fullPath); } } } VOID pAddNtPath ( IN PCTSTR DirName, IN BOOL ForceAsOsFile, IN BOOL WholeTree, IN BOOL ForceDirClean, IN PCTSTR FilePattern, OPTIONAL IN BOOL ForceFileClean OPTIONAL ) { TREE_ENUM e; TCHAR rootDir[MAX_PATH]; PTSTR p; BOOL b; UINT type; TCHAR key[MEMDB_MAX]; DWORD value; MYASSERT (!_tcschr (DirName, TEXT('*'))); if (IsDriveExcluded (DirName)) { DEBUGMSG ((DBG_VERBOSE, "Skipping %s because it is excluded", DirName)); return; } if (!IsDriveAccessible (DirName)) { DEBUGMSG ((DBG_VERBOSE, "Skipping %s because it is not accessible", DirName)); return; } if (!WholeTree) { b = EnumFirstFileInTreeEx (&e, DirName, FilePattern, FALSE, FALSE, 1); } else { b = EnumFirstFileInTree (&e, DirName, FilePattern, FALSE); } if (b) { do { if (e.Directory) { continue; } StringCopy (rootDir, e.FullPath); p = _tcsrchr (rootDir, TEXT('\\')); if (p) { *p = 0; // // Limit the file size to 5MB // if (e.FindData->nFileSizeHigh == 0 && e.FindData->nFileSizeLow <= 5242880 ) { pAddNtFile (rootDir, e.Name, TRUE, ForceFileClean, ForceAsOsFile); } ELSE_DEBUGMSG (( DBG_WARNING, "Not backing up big file %s. It is %I64u bytes.", e.FullPath, (ULONGLONG) e.FindData->nFileSizeHigh << 32 | (ULONGLONG) e.FindData->nFileSizeLow )); } } while (EnumNextFileInTree (&e)); } if (ForceDirClean) { type = WholeTree ? BACKUP_AND_CLEAN_TREE : BACKUP_AND_CLEAN_SUBDIR; } else if (WholeTree) { type = BACKUP_SUBDIRECTORY_TREE; } else { type = BACKUP_SUBDIRECTORY_FILES; } MemDbBuildKey ( key, MEMDB_CATEGORY_CLEAN_OUT, DirName, NULL, NULL ); if (MemDbGetValue (key, &value)) { if (!value && type) { DEBUGMSG (( DBG_WARNING, "NT File %s already in uninstall cleanout list, overriding with type %u", DirName, type )); } else { if (value != type) { DEBUGMSG (( DBG_WARNING, "NT File %s already in uninstall cleanout list with type %u", DirName, value )); } return; } } MemDbSetValue (key, type); } VOID pAddEmptyDirsTree ( IN PCTSTR RootDir ) { TREE_ENUM e; DWORD attribs; TCHAR key[MEMDB_MAX]; DWORD value; if (IsDriveExcluded (RootDir)) { DEBUGMSG ((DBG_VERBOSE, "Skipping empty dir tree of %s because it is excluded", RootDir)); return; } if (!IsDriveAccessible (RootDir)) { DEBUGMSG ((DBG_VERBOSE, "Skipping empty dir tree of %s because it is not accessible", RootDir)); return; } if (DoesFileExist (RootDir)) { if (EnumFirstFileInTreeEx ( &e, RootDir, NULL, FALSE, FALSE, FILE_ENUM_ALL_LEVELS )) { do { if (e.Directory) { AddDirPathToEmptyDirsCategory (e.FullPath, TRUE, FALSE); } } while (EnumNextFileInTree (&e)); } else { attribs = GetFileAttributes (RootDir); if (attribs == FILE_ATTRIBUTE_DIRECTORY || attribs == INVALID_ATTRIBUTES ) { attribs = 0; } MemDbBuildKey ( key, MEMDB_CATEGORY_EMPTY_DIRS, RootDir, NULL, NULL ); if (MemDbGetValue (key, &value)) { if (value) { DEBUGMSG_IF (( value != attribs, DBG_ERROR, "Ignoring conflict in empty dirs for %s", RootDir )); return; } } MemDbSetValue (key, attribs); } } ELSE_DEBUGMSG ((DBG_SYSMIG, "Uninstall: dir does not exist: %s", RootDir)); } BOOL ReadNtFilesEx ( IN PCSTR FileListName, //optional, if null default is opened IN BOOL ConvertPath ) { PCSTR fileListName = NULL; PCSTR fileListTmp = NULL; HANDLE fileHandle = NULL; HANDLE mapHandle = NULL; PCSTR filePointer = NULL; PCSTR dirPointer = NULL; PCSTR filePtr = NULL; DWORD offset; DWORD version; BOOL result = FALSE; CHAR dirName [MEMDB_MAX]; PSTR p; UINT u; INFSTRUCT is = INITINFSTRUCT_POOLHANDLE; PCTSTR fileName; PCTSTR fileLoc; PCTSTR dirId; PCTSTR field3; PCTSTR field4; BOOL forceAsOsFile; BOOL forceDirClean; DIRNAME_ENUM nameEnum; BOOL treeMode; HINF drvIndex; MEMDB_ENUM memdbEnum; DWORD fileAttributes; PCTSTR fullPath; PCTSTR systemPath; BOOL b; PDIRIDMAP lastMatch = NULL; UINT ticks = 0; __try { pInitKnownDirs(); // // add to this list the dirs listed in [WinntDirectories] section of txtsetup.sif // if (InfFindFirstLine(g_TxtSetupSif, S_WINNTDIRECTORIES, NULL, &is)) { // // all locations are relative to %windir% // prepare %windir%\ // StringCopy (dirName, g_WinDir); p = GetEndOfString (dirName); *p++ = TEXT('\\'); do { ticks++; if ((ticks & 255) == 0) { if (!TickProgressBarDelta (TICKS_READ_NT_FILES / 50)) { __leave; } } // // For each entry, add the dir in memdb // fileLoc = InfGetStringField(&is, 1); // // ignore special entry "\" // if (fileLoc && !StringMatch(fileLoc, TEXT("\\"))) { StringCopy (p, fileLoc); MemDbSetValueEx ( MEMDB_CATEGORY_NT_DIRS, dirName, NULL, NULL, 0, NULL ); pAddNtFile (NULL, dirName, FALSE, TRUE, FALSE); } } while (InfFindNextLine(&is)); } if (FileListName != NULL) { filePointer = MapFileIntoMemory (FileListName, &fileHandle, &mapHandle); } else { for (u = 0 ; !fileListName && u < SOURCEDIRECTORYCOUNT() ; u++) { fileListName = JoinPaths (SOURCEDIRECTORY(u), S_FILELIST_UNCOMPRESSED); if (DoesFileExist (fileListName)) { break; } FreePathString (fileListName); fileListName = JoinPaths (SOURCEDIRECTORY(u), S_FILELIST_COMPRESSED); if (DoesFileExist (fileListName)) { fileListTmp = JoinPaths (g_TempDir, S_FILELIST_UNCOMPRESSED); if (SetupDecompressOrCopyFile (fileListName, fileListTmp, 0) == NO_ERROR) { FreePathString (fileListName); fileListName = fileListTmp; break; } DEBUGMSG ((DBG_ERROR, "Can't copy %s to %s", fileListName, fileListTmp)); __leave; } FreePathString (fileListName); fileListName = NULL; } if (!fileListName) { SetLastError (ERROR_FILE_NOT_FOUND); DEBUGMSG ((DBG_ERROR, "Can't find %s", fileListName)); __leave; } filePointer = MapFileIntoMemory (fileListName, &fileHandle, &mapHandle); if (!fileListTmp) { FreePathString (fileListName); } } filePtr = filePointer; if (filePointer == NULL) { DEBUGMSG ((DBG_ERROR, "Invalid file format: %s", fileListName)); __leave; } version = *((PDWORD) filePointer); filePointer += sizeof (DWORD); __try { if (version >= 1) { while (*filePointer != 0) { ticks++; if ((ticks & 255) == 0) { if (!TickProgressBarDelta (TICKS_READ_NT_FILES / 50)) { __leave; } } dirPointer = filePointer; filePointer = GetEndOfString (filePointer) + 1; if (ConvertPath) { // // First loop: add the OS file exactly as it is in filelist.dat // if (pConvertFirstDirName (&nameEnum, dirPointer, dirName, &lastMatch, FALSE)) { do { pAddNtFile (dirName, filePointer, FALSE, FALSE, TRUE); } while (pConvertNextDirName (&nameEnum)); } // // Second loop: add the file for backup, implementing the special system/system32 hack // if (pConvertFirstDirName (&nameEnum, dirPointer, dirName, &lastMatch, TRUE)) { do { pAddNtFile (dirName, filePointer, TRUE, FALSE, FALSE); } while (pConvertNextDirName (&nameEnum)); } } else { pAddNtFile (dirPointer, filePointer, TRUE, FALSE, TRUE); } filePointer = GetEndOfString (filePointer) + 1; } if (version >= 2) { filePointer ++; while (*filePointer != 0) { ticks++; if ((ticks & 255) == 0) { if (!TickProgressBarDelta (TICKS_READ_NT_FILES / 50)) { __leave; } } MemDbSetValueEx ( MEMDB_CATEGORY_NT_FILES_EXCEPT, filePointer, NULL, NULL, 0, NULL ); filePointer = GetEndOfString (filePointer) + 1; } if (version >= 3) { ticks++; if ((ticks & 255) == 0) { if (!TickProgressBarDelta (TICKS_READ_NT_FILES / 50)) { __leave; } } filePointer ++; while (*filePointer != 0) { dirPointer = filePointer; filePointer = GetEndOfString (filePointer) + 1; if (ConvertPath) { if (pConvertFirstDirName (&nameEnum, dirPointer, dirName, &lastMatch, TRUE)) { do { pAddNtFile (dirName, filePointer, TRUE, FALSE, FALSE); } while (pConvertNextDirName (&nameEnum)); } } else { pAddNtFile (dirPointer, filePointer, TRUE, FALSE, FALSE); } filePointer = GetEndOfString (filePointer) + 1; } } } } } __except (EXCEPTION_EXECUTE_HANDLER){ LOG ((LOG_ERROR, "Access violation while reading NT file list.")); __leave; } if (CANCELLED()) { __leave; } // so far so good. Let's read static installed section from win95upg.inf MYASSERT (g_Win95UpgInf); // // Cycle through all of the entries in the StaticInstalledFiles section. // if (InfFindFirstLine(g_Win95UpgInf,S_STATIC_INSTALLED_FILES,NULL,&is)) { do { ticks++; if ((ticks & 255) == 0) { if (!TickProgressBarDelta (TICKS_READ_NT_FILES / 50)) { __leave; } } // // For each entry, add the file with it's location in memdb // fileName = InfGetStringField(&is,1); fileLoc = InfGetStringField(&is,2); if (fileName && fileLoc) { if (ConvertPath) { if (pConvertFirstDirName (&nameEnum, fileLoc, dirName, &lastMatch, FALSE)) { do { pAddNtFile (dirName, fileName, TRUE, FALSE, TRUE); } while (pConvertNextDirName (&nameEnum)); } } else { pAddNtFile (fileLoc, fileName, TRUE, FALSE, TRUE); } } } while (InfFindNextLine(&is)); } // // Add the files in drvindex.inf // drvIndex = InfOpenInfInAllSources (TEXT("drvindex.inf")); if (drvIndex == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR, "Can't open drvindex.inf.")); return FALSE; } if (InfFindFirstLine (drvIndex, TEXT("driver"), NULL, &is)) { do { ticks++; if ((ticks & 255) == 0) { if (!TickProgressBarDelta (TICKS_READ_NT_FILES / 50)) { __leave; } } fileName = InfGetStringField (&is, 1); // // Is this drive file already listed in the file list? // wsprintf (dirName, MEMDB_CATEGORY_NT_FILES TEXT("\\%s"), fileName); if (MemDbGetValue (dirName, NULL)) { DEBUGMSG ((DBG_SYSMIG, "%s is listed in drivers and in filelist.dat", fileName)); } else { // // Add this file // pAddNtFile (g_DriversDir, fileName, TRUE, TRUE, TRUE); } } while (InfFindNextLine (&is)); } InfCloseInfFile (drvIndex); // // This code marks files to be backed up, because they aren't being caught // through the regular mechanisms of setup. // if (InfFindFirstLine (g_Win95UpgInf, TEXT("Backup"), NULL, &is)) { do { ticks++; if ((ticks & 255) == 0) { if (!TickProgressBarDelta (TICKS_READ_NT_FILES / 50)) { __leave; } } InfResetInfStruct (&is); dirId = InfGetStringField (&is, 1); fileName = InfGetStringField (&is, 2); field3 = InfGetStringField (&is, 3); field4 = InfGetStringField (&is, 4); if (dirId && *dirId == 0) { dirId = NULL; } if (fileName && *fileName == 0) { fileName = NULL; } if (field3 && *field3 == 0) { field3 = NULL; } if (field4 && *field4 == 0) { field4 = NULL; } if (!dirId) { continue; } #ifdef DEBUG if (!fileName) { p = _tcsrchr (dirId, TEXT('\\')); if (!p) { p = (PTSTR) dirId; } p = _tcschr (p, TEXT('.')); if (p) { DEBUGMSG ((DBG_WHOOPS, "%s should be a dir spec, but it looks like it has a file.", dirId)); } } #endif if (field3) { forceAsOsFile = _ttoi (field3) != 0; } else { forceAsOsFile = FALSE; } if (field4) { forceDirClean = _ttoi (field4) != 0; } else { forceDirClean = FALSE; } treeMode = FALSE; p = _tcsrchr (dirId, TEXT('\\')); if (p && p[1] == TEXT('*') && !p[2]) { *p = 0; treeMode = TRUE; } else { p = NULL; } if (ConvertPath) { if (pConvertFirstDirName (&nameEnum, dirId, dirName, &lastMatch, FALSE)) { do { if (fileName && !treeMode) { if (_tcsrchr (fileName, TEXT('*')) || _tcsrchr (fileName, TEXT('?'))) { // //Add files that match "fileName" pattern from "dirName" directory only // pAddNtPath (dirName, forceAsOsFile, FALSE, FALSE, fileName, TRUE); } else { // //Add only one file "fileName" // pAddNtFile (dirName, fileName, TRUE, TRUE, forceAsOsFile); } } else { if (INVALID_ATTRIBUTES == GetFileAttributes (dirName)) { if (dirName[0] && dirName[1] == TEXT(':')) { pAddNtPath (dirName, FALSE, treeMode, forceDirClean, NULL, FALSE); } } else { // //Add all files that match "fileName" pattern from whole tree starting from "dirName" // pAddNtPath (dirName, forceAsOsFile, treeMode, forceDirClean, fileName, FALSE); } } } while (pConvertNextDirName (&nameEnum)); } } } while (InfFindNextLine (&is)); } // // In some cases, NT components create empty directories for future use. // Some of them aren't ever used. Because setup does not know about // them, we list the special cases in a win95upg.inf section called // [Uninstall.Delete]. // // For each entry, record the files or empty directories that need to be // removed in an uninstall. If an directory is specified but is not empty, // then it won't be altered during uninstall. // if (InfFindFirstLine (g_Win95UpgInf, TEXT("Uninstall.Delete"), NULL, &is)) { do { ticks++; if ((ticks & 255) == 0) { if (!TickProgressBarDelta (TICKS_READ_NT_FILES / 50)) { __leave; } } dirId = InfGetStringField (&is, 1); fileName = InfGetStringField (&is, 2); if (!dirId || *dirId == 0) { continue; } if (fileName && *fileName == 0) { fileName = NULL; } if (ConvertPath) { if (pConvertFirstDirName (&nameEnum, dirId, dirName, &lastMatch, FALSE)) { do { pAddNtFile (dirName, fileName, FALSE, TRUE, FALSE); } while (pConvertNextDirName (&nameEnum)); } } } while (InfFindNextLine (&is)); } if (InfFindFirstLine (g_Win95UpgInf, TEXT("Uninstall.KeepEmptyDirs"), NULL, &is)) { do { ticks++; if ((ticks & 255) == 0) { if (!TickProgressBarDelta (TICKS_READ_NT_FILES / 50)) { __leave; } } dirId = InfGetStringField (&is, 1); if (!dirId || *dirId == 0) { continue; } if (ConvertPath) { if (pConvertFirstDirName (&nameEnum, dirId, dirName, &lastMatch, FALSE)) { do { pAddEmptyDirsTree (dirName); } while (pConvertNextDirName (&nameEnum)); } } } while (InfFindNextLine (&is)); } result = TRUE; } __finally { UnmapFile ((PVOID)filePtr, fileHandle, mapHandle); if (fileListTmp) { DeleteFile (fileListTmp); FreePathString (fileListTmp); fileListTmp = NULL; } InfCleanUpInfStruct(&is); pCleanUpKnownDirs(); } return CANCELLED() ? FALSE : result; } DWORD ReadNtFiles ( IN DWORD Request ) { DWORD ticks = 0; switch (Request) { case REQUEST_QUERYTICKS: return TICKS_READ_NT_FILES; case REQUEST_RUN: ProgressBar_SetComponentById (MSG_PREPARING_LIST); ProgressBar_SetSubComponent (NULL); if (!ReadNtFilesEx (NULL, TRUE)) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in ReadNtFiles")); } return 0; } BOOL pIsDriverKnown ( IN PCTSTR DriverFileName, IN PCTSTR FullPath, IN BOOL DeleteMeansKnown ) { HANDLE h; DWORD Status; // // Does DriverFileName have an extension? We require one. // If no dot exists, then we assume this is something an OEM added. // if (!_tcschr (DriverFileName, TEXT('.'))) { return TRUE; } // // Is this file in migdb? // if (IsKnownMigDbFile (DriverFileName)) { return TRUE; } // // Is it going to be processed? // Status = GetFileStatusOnNt (FullPath); if (Status != FILESTATUS_UNCHANGED) { // // If marked for delete, and DeleteMeansKnown is FALSE, then // we consider the file unknown because it is simply being // deleted as a cleanup step. // // If DeleteMeansKnown is TRUE, then the caller assumes that // a file marked for delete is a known driver. // if (!(Status == FILESTATUS_DELETED) || DeleteMeansKnown) { return TRUE; } } // // Make sure this is a NE header (or the more common case, the LE // header) // h = OpenNeFile (FullPath); if (!h) { DEBUGMSG ((DBG_WARNING, "%s is not a NE file", FullPath)); // // Is this a PE file? If so, the last error will be // ERROR_BAD_EXE_FORMAT. // if (GetLastError() == ERROR_BAD_EXE_FORMAT) { return FALSE; } DEBUGMSG ((DBG_WARNING, "%s is not a PE file", FullPath)); return TRUE; } CloseNeFile (h); return FALSE; } BOOL pWarnAboutOldDrivers ( VOID ) { HINF Inf; TCHAR Path[MAX_TCHAR_PATH]; INFSTRUCT is = INITINFSTRUCT_GROWBUFFER; BOOL b = FALSE; PCTSTR Data; PCTSTR DriverFile; BOOL OldDriverFound = FALSE; PCTSTR Group; PCTSTR Message; TCHAR FullPath[MAX_TCHAR_PATH]; GROWBUFFER FileList = GROWBUF_INIT; PTSTR p; wsprintf (Path, TEXT("%s\\system.ini"), g_WinDir); Inf = InfOpenInfFile (Path); if (Inf != INVALID_HANDLE_VALUE) { if (InfFindFirstLine (Inf, TEXT("386Enh"), NULL, &is)) { do { Data = InfGetStringField (&is, 1); if (Data) { // // Determine if device driver is known // if (_tcsnextc (Data) != TEXT('*')) { DriverFile = GetFileNameFromPath (Data); if (!_tcschr (Data, TEXT(':'))) { if (!SearchPath ( NULL, DriverFile, NULL, MAX_TCHAR_PATH, FullPath, NULL )) { _tcssafecpy (FullPath, Data, MAX_TCHAR_PATH); } } else { _tcssafecpy (FullPath, Data, MAX_TCHAR_PATH); } if (!pIsDriverKnown (DriverFile, FullPath, TRUE)) { // // Unidentified driver; log it and turn on // incompatibility message. // p = (PTSTR) GrowBuffer (&FileList, ByteCount (FullPath) + 7 * sizeof (TCHAR)); if (p) { wsprintf (p, TEXT(" %s\r\n"), FullPath); FileList.End -= sizeof (TCHAR); } OldDriverFound = TRUE; MsgMgr_LinkObjectWithContext (TEXT("*386ENH"), Data); } else { DEBUGMSG ((DBG_NAUSEA, "Driver %s is known", Data)); } } } } while (InfFindNextLine (&is)); } ELSE_DEBUGMSG ((DBG_ERROR, "pWarnAboutOldDrivers: Cannot open %s", Path)); InfCloseInfFile (Inf); InfCleanUpInfStruct (&is); b = TRUE; } /*NTBUG9:155050 if (OldDriverFound) { LOG ((LOG_INFORMATION, (PCSTR)MSG_386ENH_DRIVER_LOG, FileList.Buf)); Group = BuildMessageGroup (MSG_INCOMPATIBLE_HARDWARE_ROOT, MSG_OLD_DRIVER_FOUND_SUBGROUP, NULL); Message = GetStringResource (MSG_OLD_DRIVER_FOUND_MESSAGE); if (Message && Group) { MsgMgr_ContextMsg_Add (TEXT("*386ENH"), Group, Message); } FreeText (Group); FreeStringResource (Message); } */ FreeGrowBuffer (&FileList); return b; } DWORD MoveSystemRegistry ( IN DWORD Request ) { PCTSTR path = NULL; switch (Request) { case REQUEST_QUERYTICKS: return TICKS_MOVE_SYSTEMREGISTRY; case REQUEST_RUN: path = JoinPaths (g_WinDir, S_SYSTEMDAT); MarkHiveForTemporaryMove (path, g_TempDir, NULL, TRUE, FALSE); FreePathString (path); // // on Millennium, also save classes.dat hive // path = JoinPaths (g_WinDir, S_CLASSESDAT); MarkHiveForTemporaryMove (path, g_TempDir, NULL, TRUE, FALSE); FreePathString (path); return ERROR_SUCCESS; } return 0; } VOID pProcessJoystick ( PJOYSTICK_ENUM EnumPtr ) { PCTSTR Group; TCHAR FullPath[MAX_TCHAR_PATH]; // // Is this joystick compatible? // if (!_tcschr (EnumPtr->JoystickDriver, TEXT('\\'))) { if (!SearchPath (NULL, EnumPtr->JoystickDriver, NULL, MAX_TCHAR_PATH, FullPath, NULL)) { StringCopy (FullPath, EnumPtr->JoystickDriver); } } else { StringCopy (FullPath, EnumPtr->JoystickDriver); } if (!pIsDriverKnown (GetFileNameFromPath (FullPath), FullPath, FALSE)) { LOG (( LOG_INFORMATION, "Joystick driver for %s is not known: %s", EnumPtr->JoystickName, FullPath )); Group = BuildMessageGroup ( MSG_INCOMPATIBLE_HARDWARE_ROOT, MSG_JOYSTICK_SUBGROUP, EnumPtr->JoystickName ); MsgMgr_ObjectMsg_Add ( FullPath, Group, NULL ); FreeText (Group); } } DWORD ReportIncompatibleJoysticks ( IN DWORD Request ) { JOYSTICK_ENUM e; switch (Request) { case REQUEST_QUERYTICKS: return TICKS_JOYSTICK_DETECTION; case REQUEST_RUN: if (EnumFirstJoystick (&e)) { do { pProcessJoystick (&e); } while (EnumNextJoystick (&e)); } return ERROR_SUCCESS; } return 0; } DWORD TwainCheck ( DWORD Request ) { TWAINDATASOURCE_ENUM e; PCTSTR Group; if (Request == REQUEST_QUERYTICKS) { return TICKS_TWAIN; } else if (Request != REQUEST_RUN) { return 0; } if (EnumFirstTwainDataSource (&e)) { do { if (!TreatAsGood (e.DataSourceModule) && !pIsDriverKnown ( GetFileNameFromPath (e.DataSourceModule), e.DataSourceModule, FALSE )) { // // Nobody handled the file. Generate a warning. // Group = BuildMessageGroup ( MSG_INCOMPATIBLE_HARDWARE_ROOT, MSG_TWAIN_SUBGROUP, e.DisplayName ); MsgMgr_ObjectMsg_Add ( e.DataSourceModule, Group, NULL ); MarkFileForDelete (e.DataSourceModule); FreeText (Group); } } while (EnumNextTwainDataSource (&e)); } return ERROR_SUCCESS; } DWORD ProcessRecycleBins ( DWORD Request ) { ACCESSIBLE_DRIVE_ENUM e; TREE_ENUM eFiles; BOOL recycleFound; UINT filesDeleted; TCHAR recycledInfo[] = TEXT("x:\\recycled\\INFO"); TCHAR recyclerInfo[] = TEXT("x:\\recycler\\INFO"); TCHAR recycledInfo2[] = TEXT("x:\\recycled\\INFO2"); TCHAR recyclerInfo2[] = TEXT("x:\\recycler\\INFO2"); TCHAR recycled[] = TEXT("x:\\recycled"); TCHAR recycler[] = TEXT("x:\\recycler"); PTSTR dir; PCTSTR args[1]; PCTSTR message; PCTSTR group; if (Request == REQUEST_QUERYTICKS) { return TICKS_RECYCLEBINS; } else if (Request != REQUEST_RUN) { return 0; } recycleFound = FALSE; filesDeleted = 0; // // Enumerate through each of the accessible drives looking for // a directory called RECYCLED or RECYCLER on the root. // if (GetFirstAccessibleDriveEx (&e, TRUE)) { do { dir = NULL; // // See if there is any recycle information to examine on // this drive. // recycledInfo[0] = *e->Drive; recyclerInfo[0] = *e->Drive; recycledInfo2[0] = *e->Drive; recyclerInfo2[0] = *e->Drive; recycler[0] = *e->Drive; recycled[0] = *e->Drive; if (DoesFileExist (recycledInfo) || DoesFileExist (recycledInfo2)) { dir = recycled; } else if (DoesFileExist(recyclerInfo) || DoesFileExist (recyclerInfo2)) { dir = recycler; } if (dir) { if (IsDriveExcluded (dir)) { DEBUGMSG ((DBG_VERBOSE, "Skipping recycle dir %s because it is excluded", dir)); dir = NULL; } else if (!IsDriveAccessible (dir)) { DEBUGMSG ((DBG_VERBOSE, "Skipping recycle dir %s because it is not accessible", dir)); dir = NULL; } } if (dir && EnumFirstFileInTree (&eFiles, dir, NULL, FALSE)) { // // We have work to do, Enumerate the files and mark them for // deletion. // do { // // Mark the file for deletion, tally up the saved bytes, and free the space on the drive. // filesDeleted++; FreeSpace (eFiles.FullPath,(eFiles.FindData->nFileSizeHigh * MAXDWORD) + eFiles.FindData->nFileSizeLow); // // only display Recycle Bin warning if there are visible files there // if (!(eFiles.FindData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)) { recycleFound = TRUE; } } while (EnumNextFileInTree (&eFiles)); // // We are going to delete all of this directory. // MemDbSetValueEx (MEMDB_CATEGORY_FULL_DIR_DELETES, dir, NULL, NULL, 0, NULL); } } while (GetNextAccessibleDrive (&e)); } if (recycleFound) { // // We need to provide the user with a message. // wsprintf(recycled,"%d",filesDeleted); args[0] = recycled; group = BuildMessageGroup (MSG_INSTALL_NOTES_ROOT, MSG_RECYCLE_BIN_SUBGROUP, NULL); message = ParseMessageID (MSG_RECYCLED_FILES_WILL_BE_DELETED, args); if (message && group) { MsgMgr_ObjectMsg_Add (TEXT("*RECYCLEBIN"), group, message); FreeText (group); FreeStringResource (message); } } return 0; } DWORD AnswerFileDetection ( IN DWORD Request ) { INFSTRUCT is = INITINFSTRUCT_GROWBUFFER; TCHAR KeyStr[MAX_REGISTRY_KEY]; TCHAR EncodedKeyStr[MAX_ENCODED_RULE]; TCHAR ValueName[MAX_REGISTRY_VALUE_NAME]; TCHAR EncodedValueName[MAX_ENCODED_RULE]; PTSTR ValueDataPattern = NULL; PBYTE ValueData = NULL; PTSTR ValueDataStr = NULL; PCTSTR p; PTSTR q; HKEY Key = NULL; BOOL DefaultValue; TCHAR SectionName[MAX_INF_SECTION_NAME]; TCHAR InfKey[MAX_INF_KEY_NAME]; TCHAR InfKeyData[MAX_INF_KEY_NAME]; DWORD Type; DWORD Size; BOOL Match; UINT u; switch (Request) { case REQUEST_QUERYTICKS: return TICKS_ANSWER_FILE_DETECTION; case REQUEST_RUN: if (InfFindFirstLine (g_Win95UpgInf, S_ANSWER_FILE_DETECTION, NULL, &is)) { do { __try { // // The first field has the key and optional value, encoded // in the standard usermig.inf and wkstamig.inf syntax // DefaultValue = FALSE; p = InfGetStringField (&is, 1); if (!p || *p == 0) { continue; } StackStringCopy (EncodedKeyStr, p); q = _tcschr (EncodedKeyStr, TEXT('[')); if (q) { StringCopy (EncodedValueName, SkipSpace (q + 1)); *q = 0; q = _tcschr (EncodedValueName, TEXT(']')); if (q) { *q = 0; } ELSE_DEBUGMSG (( DBG_WHOOPS, "Unmatched square brackets in %s (see [%s])", p, S_ANSWER_FILE_DETECTION )); if (*EncodedValueName == 0) { DefaultValue = TRUE; } else { q = (PTSTR) SkipSpaceR (EncodedValueName, NULL); if (q) { *_mbsinc (q) = 0; } } } else { *EncodedValueName = 0; } q = (PTSTR) SkipSpaceR (EncodedKeyStr, NULL); if (q) { *_mbsinc (q) = 0; } DecodeRuleChars (KeyStr, EncodedKeyStr); DecodeRuleChars (ValueName, EncodedValueName); // // The second field has the optional value data. If it // is empty, then the value data is not tested. // p = InfGetStringField (&is, 2); if (p && *p) { ValueDataPattern = AllocText (CharCount (p) + 1); StringCopy (ValueDataPattern, p); } else { ValueDataPattern = NULL; } // // The third field has the section name // p = InfGetStringField (&is, 3); if (!p || *p == 0) { DEBUGMSG ((DBG_WHOOPS, "Section %s lacks a section name", S_ANSWER_FILE_DETECTION)); continue; } StackStringCopy (SectionName, p); // // The fourth field gives the INF key name // p = InfGetStringField (&is, 4); if (!p || *p == 0) { DEBUGMSG ((DBG_WHOOPS, "Section %s lacks an INF key", S_ANSWER_FILE_DETECTION)); continue; } StackStringCopy (InfKey, p); // // The fifth field is optional and gives the INF value name. // The default is 1. // p = InfGetStringField (&is, 5); if (p && *p != 0) { StackStringCopy (InfKeyData, p); } else { StringCopy (InfKeyData, TEXT("1")); } // // Data is gathered. Now test the rule. // DEBUGMSG (( DBG_NAUSEA, "Testing answer file setting.\n" "Key: %s\n" "Value Name: %s\n" "Value Data: %s\n" "Section: %s\n" "Key: %s\n" "Key Value: %s", KeyStr, *ValueName ? ValueName : DefaultValue ? TEXT("") : TEXT(""), ValueDataPattern ? ValueDataPattern : TEXT(""), SectionName, InfKey, InfKeyData )); Match = FALSE; Key = OpenRegKeyStr (KeyStr); if (Key) { // // Test the value name // if (*ValueName || DefaultValue) { if (GetRegValueTypeAndSize (Key, ValueName, &Type, &Size)) { // // Test the value data // if (ValueDataPattern) { // // Get the data // ValueData = GetRegValueData (Key, ValueName); if (!ValueData) { MYASSERT (FALSE); continue; } // // Create the string // switch (Type) { case REG_SZ: case REG_EXPAND_SZ: ValueDataStr = DuplicateText ((PCTSTR) ValueData); break; case REG_DWORD: ValueDataStr = AllocText (11); wsprintf (ValueDataStr, TEXT("0x%08X"), *((PDWORD) ValueData)); break; default: ValueDataStr = AllocText (3 * (max (1, Size))); q = ValueDataStr; for (u = 0 ; u < Size ; u++) { if (u) { *q++ = TEXT(' '); } wsprintf (q, TEXT("%02X"), ValueData[u]); q += 2; } *q = 0; break; } // // Pattern-match the string // if (IsPatternMatch (ValueDataPattern, ValueDataStr)) { DEBUGMSG ((DBG_NAUSEA, "Key, value name and value data found")); Match = TRUE; } ELSE_DEBUGMSG (( DBG_NAUSEA, "Value data %s did not match %s", ValueDataStr, ValueDataPattern )); } else { DEBUGMSG ((DBG_NAUSEA, "Key and value name found")); Match = TRUE; } } ELSE_DEBUGMSG ((DBG_NAUSEA, "Value name not found, rc=%u", GetLastError())); } else { DEBUGMSG ((DBG_NAUSEA, "Key found")); Match = TRUE; } } ELSE_DEBUGMSG ((DBG_NAUSEA, "Key not found, rc=%u", GetLastError())); if (Match) { WriteInfKey (SectionName, InfKey, InfKeyData); } } __finally { if (Key) { CloseRegKey (Key); Key = NULL; } FreeText (ValueDataPattern); ValueDataPattern = NULL; if (ValueData) { MemFree (g_hHeap, 0, ValueData); ValueData = NULL; } FreeText (ValueDataStr); ValueDataStr = NULL; } } while (InfFindNextLine (&is)); } InfCleanUpInfStruct (&is); return ERROR_SUCCESS; } return 0; } VOID pAppendIniFiles ( IN HINF Inf, IN PCTSTR Section, IN PCTSTR MemDbCategory ) /*++ Routine Description: pAppendIniFiles reads from the specified INF from given section and appends INI patterns to the multisz list IniFiles. Arguments: Inf - Specifies the source INF handle Section - Specifies the section in that INF MemDbCategory - Specifies the category in which to store INI patterns from that section Return Value: none --*/ { INFCONTEXT ctx; TCHAR Field[MEMDB_MAX]; TCHAR IniPattern[MAX_PATH]; PTSTR IniPath; if (SetupFindFirstLine (Inf, Section, NULL, &ctx)) { do { // // INI file name is in the first value // if (SetupGetStringField (&ctx, 1, Field, MEMDB_MAX, NULL) && Field[0]) { // // now convert env vars // if (ExpandEnvironmentStrings (Field, IniPattern, MAX_PATH) > MAX_PATH) { DEBUGMSG (( DBG_ERROR, "pAppendIniFiles: Invalid INI dir name in wkstamig.inf section [%s]; name too long", Section )); MYASSERT (FALSE); continue; } IniPath = IniPattern; // // to speed up things while scanning file system, only check filenames // with extension .INI; that means this section should only contain // filenames with .INI extension (if a file with a different extension // is needed, GatherIniFiles needs to be modified together // with this function, i.e. to create here a list of extensions to be // searched for) // MYASSERT (StringIMatch(GetDotExtensionFromPath (IniPattern), TEXT(".INI"))); // // check for empty directory name // if (!_tcschr (IniPattern, TEXT('\\'))) { // // no dir name provided, assume %windir% // reuse Field // StringCopy (Field, g_WinDir); // // construct new path // StringCopy (AppendWack (Field), IniPattern); IniPath = Field; } // // append filename to provided grow buffer // MemDbSetValueEx (MemDbCategory, IniPath, NULL, NULL, 0, NULL); } } while (SetupFindNextLine (&ctx, &ctx)); } } BOOL pCreateIniCategories ( ) /*++ Routine Description: pCreateIniCategories appends to multisz buffers that will hold the patterns of INI files on which actions will be later performed on NT. Arguments: none Return Value: TRUE if success, FALSE if failure. --*/ { HINF WkstaMigInf = INVALID_HANDLE_VALUE; PTSTR wkstaMigSource = NULL; PTSTR wkstaMigTarget = NULL; DWORD result; BOOL b = FALSE; __try { wkstaMigSource = JoinPaths (SOURCEDIRECTORY(0), S_WKSTAMIG_INF); wkstaMigTarget = JoinPaths (g_TempDir, S_WKSTAMIG_INF); result = SetupDecompressOrCopyFile (wkstaMigSource, wkstaMigTarget, 0); if ((result != ERROR_SUCCESS) && (result != ERROR_ALREADY_EXISTS)) { LOG ((LOG_ERROR, "INI ACTIONS: Unable to decompress %s", wkstaMigSource)); __leave; } WkstaMigInf = InfOpenInfFile (wkstaMigTarget); if (WkstaMigInf == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR, "INI ACTIONS: %s could not be opened", wkstaMigTarget)); __leave; } pAppendIniFiles (WkstaMigInf, S_INIFILES_ACTIONS_FIRST, MEMDB_CATEGORY_INIFILES_ACT_FIRST); pAppendIniFiles (WkstaMigInf, S_INIFILES_ACTIONS_LAST, MEMDB_CATEGORY_INIFILES_ACT_LAST); b = TRUE; } __finally { result = GetLastError (); if (WkstaMigInf != INVALID_HANDLE_VALUE) { InfCloseInfFile (WkstaMigInf); } if (wkstaMigTarget) { DeleteFile (wkstaMigTarget); FreePathString (wkstaMigTarget); } if (wkstaMigSource) { FreePathString (wkstaMigSource); } SetLastError (result); } return b; } DWORD InitIniProcessing ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_INITINIPROCESSING; case REQUEST_RUN: if (!pCreateIniCategories ()) { return GetLastError (); } return ERROR_SUCCESS; } return 0; }