/*++ Copyright (c) 1997 Microsoft Corporation Module Name: masys32.c Abstract: Functions to migrate a Win9x user's system directory to system32. Author: Mike Condra (mikeco) 25-Feb-1997 Revision History: ovidiut 09-Mar-1999 Added support to rename files on Win9x side jimschm 23-Sep-1998 Updated for new fileops code jimschm 02-Dec-1997 Removed rename of system32 if it already exists mikeco 23-Jun-1997 NT-style file- & fn-header comments --*/ #include "pch.h" #include "migdbp.h" #include "migappp.h" #define DBG_SYS32 "Sys32" PCTSTR pGetNewName ( PCTSTR FileName ) /*++ Routine Description: This function generates a new name for the file that is going to be renames. Arguments: FileName - Original file name Return Value: the new name for file --*/ { PCTSTR pattern; PTSTR result ; UINT count ; DWORD attrib ; pattern = JoinText (FileName, TEXT(".%03u")); result = JoinText (FileName, TEXT("XXXX")); count = 0; do { if (count == 999) { return result; } _stprintf (result, pattern, count); attrib = GetFileAttributes (result); count ++; } while (attrib != 0xFFFFFFFF); FreeText (pattern); return result; } BOOL pRenameSystem32File ( IN PCTSTR NewName, IN OUT PGROWBUFFER msgBufRename, OUT PBOOL FileDeleted ) /*++ Routine Description: pRenameSystem32File handles the special file %windir%\system32. If it exists and cannot be automatically renamed, 2 things may happen: - in unattended mode, the file will be deleted (this will be done by textmode setup) - otherwise the user is asked to take a decision: either rename the file or cancels Setup Arguments: DirName - name of NT dir to check msgBufRename - growbuffer to append a Message when renaming a file. msgBufDelete - growbuffer to append a Message when deleting a file (system32 only). Return Value: TRUE if the operation was successful and Setup can continue, FALSE if user cancelled --*/ { DWORD attrib; PCTSTR Message = NULL; PCTSTR button1 = NULL; PCTSTR button2 = NULL; BOOL Quit; BOOL b = FALSE; *FileDeleted = FALSE; while (!b && !((attrib = GetFileAttributes (g_System32Dir)) & FILE_ATTRIBUTE_DIRECTORY)) { // // rename this file now // if (SetFileAttributes (g_System32Dir, FILE_ATTRIBUTE_NORMAL)) { if (MoveFile (g_System32Dir, NewName)) { b = TRUE; SetFileAttributes (g_System32Dir, attrib); } else { DEBUGMSG (( DBG_SYS32, "CheckNtDirs: Unable to set normal attributes on file %s", g_System32Dir )); SetFileAttributes (g_System32Dir, attrib); } } if (!b) { if (!UNATTENDED()) { // // ask user to take a decision about this // Message = ParseMessageID (MSG_CANNOT_RENAME_FILE, &g_System32Dir); button1 = GetStringResource (MSG_RETRY_RENAME); button2 = GetStringResource (MSG_QUIT_SETUP); Quit = IDBUTTON1 != TwoButtonBox (g_ParentWnd, Message, button1, button2); FreeStringResource (Message); FreeStringResource (button1); FreeStringResource (button2); if (Quit) { SetLastError (ERROR_CANCELLED); DEBUGMSG (( DBG_SYS32, "CheckNtDirs: user cancelled Setup on renaming file %s", g_System32Dir )); return FALSE; } } else { // // suppose the admin would delete the file anyway; // that's exactly what textmode setup does, so leave it there and // return success // *FileDeleted = TRUE; b = TRUE; } } } return GetFileAttributes (g_System32Dir) & FILE_ATTRIBUTE_DIRECTORY; } BOOL pHandleSingleDir ( IN PCTSTR DirName, IN OUT PGROWBUFFER msgBufRename, IN OUT PGROWBUFFER msgBufDelete ) /*++ Routine Description: This function checks if a file is in one of NT5 dirs way. If so, the file is renamed and a Message is send to log. If there is a file named %windir%\system32, it is renamed at this point (special behaviour) and if this fails, Setup is cancelled. Arguments: DirName - name of NT dir to check msgBufRename - growbuffer to append a Message when renaming a file. msgBufDelete - growbuffer to append a Message when deleting a file (system32 only). Return Value: TRUE if the operation was successful and Setup can continue, FALSE if user cancelled --*/ { PCTSTR newFileName, FileNamePart; DWORD attributes; TCHAR msg[MAX_TCHAR_PATH * 2 + 5]; BOOL FileDeleted; attributes = GetFileAttributes (DirName); if (!(attributes & FILE_ATTRIBUTE_DIRECTORY)) { newFileName = pGetNewName (DirName); DEBUGMSG ((DBG_SYS32, "CheckNtDirs: Renaming %s to %s", DirName, newFileName)); FileDeleted = FALSE; // // special case: if DirName is g_System32Dir, rename the file right now // because textmode Setup doesn't have a chance to rename it before // it is already deleted and the System32 dir is created // if (StringIMatch (DirName, g_System32Dir)) { if (!pRenameSystem32File (newFileName, msgBufRename, &FileDeleted)) { return FALSE; } if (!FileDeleted) { FileNamePart = GetFileNameFromPath (newFileName); MYASSERT (FileNamePart); // // mark this info for undo on cancel // MemDbSetValueEx ( MEMDB_CATEGORY_CHG_FILE_PROPS, DirName, FileNamePart, NULL, attributes, NULL ); } } else { MemDbSetValueEx (MEMDB_CATEGORY_DIRS_COLLISION, DirName, NULL, NULL, 0, NULL); } // // append to the log // if (FileDeleted) { wsprintf (msg, TEXT("\n\t\t%s"), DirName); GrowBufAppendString (msgBufDelete, msg); } else { wsprintf (msg, TEXT("\n\t\t%s -> %s"), DirName, newFileName); GrowBufAppendString (msgBufRename, msg); } FreeText (newFileName); } return TRUE; } VOID pCheckProfilesDir ( IN OUT PGROWBUFFER msgBufRename ) /*++ Routine Description: pCheckProfilesDir makes sure that there is no directory named "g_ProfileDirNt". If there is, it is renamed, all files and folders within are marked for external move and a message is added to the user report. Arguments: msgBufRename - A grow buffer where the rename message will be appended, if this is the case Return Value: none --*/ { TCHAR msg[MAX_TCHAR_PATH * 2 + 5]; DWORD attrib; PCTSTR NewName; PTSTR p; TREE_ENUM TreeEnum; TCHAR NewDest[MAX_MBCHAR_PATH]; PCTSTR Message; PCTSTR Group; PCTSTR array[2]; MYASSERT (g_ProfileDirNt); attrib = GetFileAttributes (g_ProfileDirNt); if (attrib != INVALID_ATTRIBUTES) { MemDbSetValueEx (MEMDB_CATEGORY_DIRS_COLLISION, g_ProfileDirNt, NULL, NULL, 0, NULL); NewName = pGetNewName (g_ProfileDirNt); DEBUGMSG ((DBG_SYS32, "CheckNtDirs: Renaming %s to %s", g_ProfileDirNt, NewName)); MarkFileForMove (g_ProfileDirNt, NewName); wsprintf (msg, TEXT("\n\t\t%s -> %s"), g_ProfileDirNt, NewName); GrowBufAppendString (msgBufRename, msg); if (attrib & FILE_ATTRIBUTE_DIRECTORY) { // // mark all files in the tree for move // if (EnumFirstFileInTree (&TreeEnum, g_ProfileDirNt, NULL, TRUE)) { StringCopy (NewDest, NewName); p = AppendWack (NewDest); do { MYASSERT (*TreeEnum.SubPath != '\\'); StringCopy (p, TreeEnum.SubPath); if (!TreeEnum.Directory) { if (CanSetOperation (TreeEnum.FullPath, OPERATION_TEMP_PATH)) { // // remove old operation and set a new one // with the updated final dest // MarkFileForTemporaryMove (TreeEnum.FullPath, NewDest, g_TempDir); } else { if (CanSetOperation (TreeEnum.FullPath, OPERATION_FILE_MOVE)) { MarkFileForMove (TreeEnum.FullPath, NewDest); } } } else { if (CanSetOperation (TreeEnum.FullPath, OPERATION_FILE_MOVE_EXTERNAL)) { MarkFileForMoveExternal (TreeEnum.FullPath, NewDest); } } } while (EnumNextFileInTree (&TreeEnum)); } array[0] = g_ProfileDirNt; array[1] = NewName; Message = ParseMessageID (MSG_DIRECTORY_COLLISION_SUBCOMPONENT, array); if (Message) { Group = BuildMessageGroup ( MSG_INSTALL_NOTES_ROOT, MSG_DIRECTORY_COLLISION_SUBGROUP, Message ); if (Group) { MsgMgr_ObjectMsg_Add (TEXT("*RenameFolders"), Group, S_EMPTY); FreeText (Group); } FreeStringResource (Message); } } FreeText (NewName); } } BOOL pCheckNtDirs ( VOID ) /*++ Routine Description: This function makes sure that there is no file with the same name as one of the NT5 directories. Arguments: none Return Value: TRUE if check was successful; FALSE if Setup was cancelled by the user --*/ { MEMDB_ENUM enumDirs; GROWBUFFER msgBufRename = GROWBUF_INIT; GROWBUFFER msgBufDelete = GROWBUF_INIT; BOOL Success = TRUE; // // check first for g_ProfileDirNt // pCheckProfilesDir (&msgBufRename); if (MemDbEnumFirstValue ( &enumDirs, TEXT(MEMDB_CATEGORY_NT_DIRSA)TEXT("\\*"), MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY )) { do { if (!pHandleSingleDir (enumDirs.szName, &msgBufRename, &msgBufDelete)) { Success = FALSE; break; } } while (MemDbEnumNextValue (&enumDirs)); } if (Success) { // // warn user about what will happen // if (msgBufDelete.Buf) { LOG ((LOG_WARNING, (PCSTR)MSG_DIR_COLLISION_DELETE_LOG, msgBufDelete.Buf)); } if (msgBufRename.Buf) { LOG ((LOG_WARNING, (PCSTR)MSG_DIR_COLLISION_LOG, msgBufRename.Buf)); } } FreeGrowBuffer (&msgBufDelete); FreeGrowBuffer (&msgBufRename); return Success; } DWORD CheckNtDirs ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_CHECK_NT_DIRS; case REQUEST_RUN: if (!pCheckNtDirs ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: LOG ((LOG_ERROR, "Bad parameter while checking Nt Directories.")); } return 0; } BOOL pReadSystemFixedFiles ( IN OUT HASHTABLE SystemFixedFiles ) /*++ Routine Description: This function reads a section from Win95upg.inf with all modules that must remain in System directory. Arguments: none Return Value: TRUE if successfull, FALSE otherwise --*/ { INFCONTEXT context; TCHAR fileName[MAX_TCHAR_PATH]; BOOL result = TRUE; if (g_Win95UpgInf == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR, "Unable to read from WIN95UPG.INF")); SetLastError (ERROR_FILE_NOT_FOUND); return FALSE; } if (SetupFindFirstLine (g_Win95UpgInf, WINDIR_SYSTEM_FIXED_FILES, NULL, &context)) { do { if (SetupGetStringField ( &context, 1, fileName, MAX_TCHAR_PATH, NULL )) { HtAddString (SystemFixedFiles, fileName); } ELSE_DEBUGMSG ((DBG_ERROR, "File name not found in %s", WINDIR_SYSTEM_FIXED_FILES)); } while (SetupFindNextLine (&context, &context)); } return TRUE; } BOOL pReadSystemForcedMoveFiles ( VOID ) /*++ Routine Description: This function reads a section from Win95upg.inf with patterns for all modules that should be moved to System32 directory. Arguments: none Return Value: TRUE if successfull, FALSE otherwise --*/ { INFCONTEXT context; TCHAR filePattern[MAX_TCHAR_PATH]; BOOL result = TRUE; if (g_Win95UpgInf == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR, "Unable to read from WIN95UPG.INF")); SetLastError (ERROR_FILE_NOT_FOUND); return FALSE; } if (SetupFindFirstLine (g_Win95UpgInf, SYSTEM32_FORCED_MOVE, NULL, &context)) { do { if (SetupGetStringField ( &context, 1, filePattern, MAX_TCHAR_PATH, NULL )) { MemDbSetValueEx ( MEMDB_CATEGORY_SYSTEM32_FORCED_MOVE, filePattern, NULL, NULL, 0, NULL ); } ELSE_DEBUGMSG ((DBG_ERROR, "File name not found in %s", SYSTEM32_FORCED_MOVE)); } while (SetupFindNextLine (&context, &context)); } return TRUE; } VOID pMarkFileForSys32Move ( IN PCTSTR FileName, IN PCTSTR FullFileSpec, IN PCTSTR MovedFile, IN BOOL CheckExeType ) /*++ Routine Description: pMarkFileForSys32Move marks a file in %windir%\system to be moved to %windir%\system32. It takes into account all previous processing, so there is no operation collisions. Arguments: FileName - Specifies the src file name or sub-path from %windir%\system. FullFileSpec - Specifies the full path to the source file (which is supposed to be in the system dir) MovedFile - Specifies the destination path (which is supposed to be in the system32 dir) CheckExeType - Specifies TRUE if only 32-bit binaries should be moved. If TRUE and FullFileSpec does not point to a 32-bit binary, then memdb is queried for non-32-bit binaries that should be moved. Return Value: None. --*/ { TCHAR key [MEMDB_MAX]; // // Skip file if we already plan to move or delete it. // if (!CanSetOperation (FullFileSpec, OPERATION_FILE_MOVE)) { DEBUGMSG (( DBG_SYS32, "File already flagged for change: %s", FullFileSpec )); return; } if (!IsFileMarkedForChange (MovedFile)) { if (CheckExeType) { // // See if Win32 PE // if (GetModuleType (FullFileSpec) != W32_MODULE) { MemDbBuildKey (key, MEMDB_CATEGORY_SYSTEM32_FORCED_MOVE, FileName, NULL, NULL); if (!MemDbGetPatternValue (key, NULL)) { return; } } } } else { // // Move file during text mode because we know it is going to be // created. This allows text mode to compare versions before // overwriting. // // NOTE: We can be certain that the creation isn't from a file copy, // because we tested the source file above, and there is no // other reason why a file in system32 will be copied from // any other location than system or the NT sources. // // Also note that migration DLLs have not been processed yet. // RemoveOperationsFromPath (MovedFile, ALL_DEST_CHANGE_OPERATIONS); } // // All tests passed -- do the move // DEBUGMSG ((DBG_SYS32, "Moving %s to %s", FullFileSpec, MovedFile)); MarkFileForMove (FullFileSpec, MovedFile); } BOOL pMoveSystemDir ( VOID ) /*++ Routine Description: MoveSystemDir scans the %windir%\system directory for all 32-bit executables that are not excluded in win95upg.inf. Any matches are moved to system32. Arguments: none Return Value: TRUE if successfull, FALSE otherwise --*/ { TCHAR SystemDirPattern[MAX_TCHAR_PATH]; TCHAR FullFileSpec[MAX_TCHAR_PATH]; TCHAR MovedFile[MAX_TCHAR_PATH]; HANDLE hFind; WIN32_FIND_DATA fd; TCHAR key [MEMDB_MAX]; TREE_ENUM e; PTSTR p, q; PTSTR SubPathEnd; HASHTABLE systemFixedFiles; DWORD count = 0; DEBUGMSG ((DBG_SYS32, "Begining system to system32 processing")); systemFixedFiles = HtAlloc(); if (!pReadSystemFixedFiles (systemFixedFiles)) { HtFree (systemFixedFiles); return FALSE; } pReadSystemForcedMoveFiles (); // // Build the string %sysdir%\\*.* // StringCopy(SystemDirPattern, g_SystemDir); StringCat(SystemDirPattern, TEXT("\\*.*")); hFind = FindFirstFile (SystemDirPattern, &fd); if (INVALID_HANDLE_VALUE != hFind) { StringCopy (FullFileSpec, g_SystemDir); p = AppendWack (FullFileSpec); StringCopy (MovedFile, g_System32Dir); q = AppendWack (MovedFile); do { // // Reject "." and ".." // if (StringMatch(fd.cFileName, _T(".")) || StringMatch(fd.cFileName, _T(".."))) { continue; } // // See if is on list of files that stay in system dir // if (HtFindString (systemFixedFiles, fd.cFileName)) { continue; } // // If it's a directory, see if we should move it, and if so, // move it! // if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { MemDbBuildKey (key, MEMDB_CATEGORY_SYSTEM32_FORCED_MOVE, fd.cFileName, NULL, NULL); if (!MemDbGetPatternValue (key, NULL)) { continue; } // // To move a subdir, we enumerate all files in the tree, mark // each of them for move, and then follow the normal code path // to mark the dir itself to be moved. // StringCopy (p, fd.cFileName); StringCopy (q, fd.cFileName); SubPathEnd = AppendWack (q); if (EnumFirstFileInTree (&e, FullFileSpec, NULL, FALSE)) { do { StringCopy (SubPathEnd, e.SubPath); pMarkFileForSys32Move (q, e.FullPath, MovedFile, FALSE); } while (EnumNextFileInTree (&e)); } TickProgressBar (); } // // Make full file spec // StringCopy (p, fd.cFileName); StringCopy (q, fd.cFileName); pMarkFileForSys32Move (fd.cFileName, FullFileSpec, MovedFile, TRUE); count++; if (!(count % 128)) { TickProgressBar (); } } while (FindNextFile (hFind, &fd)); FindClose (hFind); } HtFree (systemFixedFiles); DEBUGMSG ((DBG_SYS32, "End of system to system32 processing")); return TRUE; } DWORD MoveSystemDir ( IN DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_MOVE_SYSTEM_DIR; case REQUEST_RUN: if (!pMoveSystemDir ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: LOG ((LOG_ERROR, "Bad parameter found while moving system directory.")); } return 0; } BOOL UndoChangedFileProps ( VOID ) /*++ Routine Description: UndoChangedFileProps enumerates all values in MEMDB_CATEGORY_CHG_FILE_PROPS and restore files to their original state (name, attributes). This function should be called when the user cancels the upgrade. Arguments: none Return Value: TRUE if all files were successfully set to their original attributes, FALSE otherwise --*/ { MEMDB_ENUM e; PTSTR FileNamePart, NewName, DirNameEnd; BOOL b = TRUE; if (MemDbGetValueEx ( &e, TEXT(MEMDB_CATEGORY_CHG_FILE_PROPS) TEXT("\\*"), NULL, NULL )) { do { FileNamePart = _tcsrchr (e.szName, TEXT('\\')); MYASSERT(FileNamePart); *FileNamePart = 0; FileNamePart++; DirNameEnd = _tcsrchr (e.szName, TEXT('\\')); MYASSERT(DirNameEnd); *DirNameEnd = 0; NewName = JoinPaths (e.szName, FileNamePart); *DirNameEnd = TEXT('\\'); if (!SetFileAttributes (NewName, FILE_ATTRIBUTE_NORMAL) || !MoveFile (NewName, e.szName) || !SetFileAttributes (e.szName, e.dwValue)) { b = FALSE; } FreePathString (NewName); } while (MemDbEnumNextValue (&e)); } return b; }