/*++ Copyright (c) 1996 Microsoft Corporation Module Name: filemig.c Abstract: Contains utility functions to migrate file system settings. Author: Jim Schmidt (jimschm) 12-Jul-1996 Revision History: jimschm 08-Jul-1999 Added FileSearchAndReplace jimschm 23-Sep-1998 Changed for new shell.c & progress.c calinn 29-Jan-1998 Fixed DoFileDel messages jimschm 21-Nov-1997 PC-98 changes jimschm 14-Nov-1997 FileCopy now makes the dest dir if it doesn't exist jimschm 18-Jul-1997 Now supports FileCopy and FileDel changes mikeco 09-Apr-1997 Mods to MoveProfileDir jimschm 18-Dec-1996 Extracted code from miginf jimschm 23-Oct-1996 Joined ProcessUserInfs and ApplyChanges mikeco 04-Dec-1996 Enumerate/modify PIF and LNK files jimschm 02-Oct-1996 Added default user migration --*/ #include "pch.h" #include "migmainp.h" #include "persist.h" #include "uninstall.h" #ifndef UNICODE #error UNICODE reqired #endif #define DBG_FILEMIG "FileMig" #define BACKUP_FILE_NUMBER 2 #define BOOT_FILES_ADDITIONAL_PADDING (1<<20) #define UNDO_FILES_ADDITIONAL_PADDING (1<<20) #define MAX_INT_CHAR 11 PERSISTENCE_IMPLEMENTATION(DRIVE_LAYOUT_INFORMATION_EX_PERSISTENCE); PERSISTENCE_IMPLEMENTATION(DISKINFO_PERSISTENCE); PERSISTENCE_IMPLEMENTATION(DRIVEINFO_PERSISTENCE); PERSISTENCE_IMPLEMENTATION(FILEINTEGRITYINFO_PERSISTENCE); PERSISTENCE_IMPLEMENTATION(BACKUPIMAGEINFO_PERSISTENCE); GROWLIST g_StartMenuItemsForCleanUpCommon = GROWLIST_INIT; GROWLIST g_StartMenuItemsForCleanUpPrivate = GROWLIST_INIT; BOOL OurMoveFileExA ( IN PCSTR ExistingFile, IN PCSTR DestinationFile, IN DWORD Flags ) { PCWSTR unicodeExistingFile; PCWSTR unicodeDestinationFile; BOOL b; unicodeExistingFile = ConvertAtoW (ExistingFile); unicodeDestinationFile = ConvertAtoW (DestinationFile); b = OurMoveFileExW (unicodeExistingFile, unicodeDestinationFile, Flags); FreeConvertedStr (unicodeExistingFile); FreeConvertedStr (unicodeDestinationFile); return b; } BOOL OurMoveFileExW ( IN PCWSTR ExistingFile, IN PCWSTR DestinationFile, IN DWORD Flags ) { PCWSTR longExistingFile; PCWSTR longDestinationFile; BOOL b; longExistingFile = JoinPathsW (L"\\\\?", ExistingFile); longDestinationFile = JoinPathsW (L"\\\\?", DestinationFile); MakeSurePathExists (longDestinationFile, FALSE); DEBUGMSG ((DBG_VERBOSE, "Trying to move %s to %s", longExistingFile, longDestinationFile)); b = MoveFileExW (longExistingFile, longDestinationFile, Flags); FreePathStringW (longExistingFile); FreePathStringW (longDestinationFile); return b; } BOOL OurCopyFileW ( IN PCWSTR ExistingFile, IN PCWSTR DestinationFile ) { PCWSTR longExistingFile; PCWSTR longDestinationFile; BOOL b; longExistingFile = JoinPathsW (L"\\\\?", ExistingFile); longDestinationFile = JoinPathsW (L"\\\\?", DestinationFile); DEBUGMSG ((DBG_VERBOSE, "Trying to copy %s to %s", longExistingFile, longDestinationFile)); MakeSurePathExists (longDestinationFile, FALSE); b = CopyFileW (longExistingFile, longDestinationFile, FALSE); FreePathStringW (longExistingFile); FreePathStringW (longDestinationFile); return b; } BOOL pFileSearchAndReplaceWorker ( IN PBYTE MapStart, IN PBYTE MapEnd, IN HANDLE OutFile, IN PTOKENSET TokenSet ); BOOL pCopyFileWithVersionCheck ( IN PCTSTR Src, IN PCTSTR Dest ) { DWORD Attributes; DWORD rc; Attributes = GetLongPathAttributes (Src); if (Attributes == INVALID_ATTRIBUTES) { SetLastError (ERROR_FILE_NOT_FOUND); LOG ((LOG_ERROR, "Copy File With Version Check: File not found: %s", Src)); return FALSE; } MakeSureLongPathExists (Dest, FALSE); // FALSE == not path only SetLongPathAttributes (Dest, FILE_ATTRIBUTE_NORMAL); rc = SetupDecompressOrCopyFile ( Src, Dest, FILE_COMPRESSION_NONE ); if (rc != ERROR_SUCCESS) { SetLastError (rc); LOG ((LOG_ERROR, "Cannot copy %s to %s", Src, Dest)); return FALSE; } SetLongPathAttributes (Dest, Attributes); return TRUE; } BOOL pCopyTempRelocToDest ( VOID ) /*++ Routine Description: pCopyTempRelocToDest enumerates the DirAttribs category and establishes a path for each directory listed. It then enumerates the RelocTemp category and copies each file to its one or more destinations. Arguments: none Return Value: TRUE if copy succeeded, or FALSE if an error occurred. Call GetLastError() for error code. --*/ { FILEOP_ENUM eOp; FILEOP_PROP_ENUM eOpProp; TCHAR srcPath [MEMDB_MAX]; PCTSTR extPtr; if (EnumFirstPathInOperation (&eOp, OPERATION_TEMP_PATH)) { do { srcPath [0] = 0; if (EnumFirstFileOpProperty (&eOpProp, eOp.Sequencer, OPERATION_TEMP_PATH)) { do { if (srcPath [0]) { // // if the dest file is an INI file, // don't copy it; // the merging mechanism will combine them later // extPtr = GetFileExtensionFromPath (eOpProp.Property); if (extPtr && StringIMatch (extPtr, TEXT("INI"))) { continue; } MakeSureLongPathExists (eOpProp.Property, FALSE); if (!pCopyFileWithVersionCheck (srcPath, eOpProp.Property)) { // // don't stop here; continue with remaining files // break; } } else { StringCopy (srcPath, eOpProp.Property); } } while (EnumNextFileOpProperty (&eOpProp)); } } while (EnumNextPathInOperation (&eOp)); } return TRUE; } DWORD DoCopyFile ( DWORD Request ) /*++ Routine Description: DoCopyFile performs a file copy for each file listed in the file copy operation. Arguments: Request - Specifies REQUEST_QUERYTICKS if a tick estimate is needed, or REQUEST_RUN if processing should be preformed. Return Value: Tick count (REQUEST_QUERYTICKS), or Win32 status code (REQUEST_RUN). --*/ { FILEOP_ENUM OpEnum; TCHAR DestPath[MAX_TCHAR_PATH]; if (Request == REQUEST_QUERYTICKS) { return TICKS_COPYFILE; } // // Perform rest of temporary file relocation // pCopyTempRelocToDest(); // // Copy files into directories // if (EnumFirstPathInOperation (&OpEnum, OPERATION_FILE_COPY)) { do { // // Get dest // if (GetPathProperty (OpEnum.Path, OPERATION_FILE_COPY, 0, DestPath)) { MakeSureLongPathExists (DestPath, FALSE); pCopyFileWithVersionCheck (OpEnum.Path, DestPath); } } while (EnumNextPathInOperation (&OpEnum)); } TickProgressBarDelta (TICKS_COPYFILE); return ERROR_SUCCESS; } PCTSTR g_LnkStubDataFile = NULL; HANDLE g_LnkStubDataHandle = INVALID_HANDLE_VALUE; BOOL g_LnkStubBadData = FALSE; VOID pInitLnkStubData ( VOID ) { INT maxSequencer; DWORD offset = 0; DWORD bytesWritten; MemDbGetValue (MEMDB_CATEGORY_LINKSTUB_MAXSEQUENCE, &maxSequencer); g_LnkStubDataFile = JoinPaths (g_WinDir, S_LNKSTUB_DAT); g_LnkStubDataHandle = CreateFile ( g_LnkStubDataFile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (g_LnkStubDataHandle != INVALID_HANDLE_VALUE) { // let's write empty data for all possible sequencers // there is a DWORD entry for each sequencer (1 based) while (maxSequencer) { if (!WriteFile ( g_LnkStubDataHandle, &offset, sizeof (DWORD), &bytesWritten, NULL )) { g_LnkStubBadData = TRUE; return; } maxSequencer--; } } else { g_LnkStubBadData = TRUE; } } VOID pDoneLnkStubData ( VOID ) { CloseHandle (g_LnkStubDataHandle); g_LnkStubDataHandle = INVALID_HANDLE_VALUE; if (g_LnkStubBadData) { DeleteFile (g_LnkStubDataFile); } FreePathString (g_LnkStubDataFile); g_LnkStubDataFile = NULL; } VOID pWriteLnkStubData ( IN PCTSTR NewLinkPath, IN PCTSTR NewTarget, IN PCTSTR NewArgs, IN PCTSTR NewWorkDir, IN PCTSTR NewIconPath, IN INT NewIconNr, IN INT ShowMode, IN INT Sequencer, IN DWORD Announcement, IN DWORD Availability, IN PGROWBUFFER ReqFilesList ) { DWORD offset; DWORD bytesWritten; WIN32_FIND_DATA findData; MULTISZ_ENUM multiSzEnum; TCHAR stub[]=TEXT(""); PCTSTR reqFilePath = NULL; PCTSTR oldFileSpec = NULL; PTSTR oldFilePtr = NULL; if ((!g_LnkStubBadData) && (Sequencer > 0)) { if (SetFilePointer (g_LnkStubDataHandle, (Sequencer - 1) * sizeof (DWORD), NULL, FILE_BEGIN) == 0xFFFFFFFF) { g_LnkStubBadData = TRUE; return; } offset = GetFileSize (g_LnkStubDataHandle, NULL); if (offset == 0xFFFFFFFF) { g_LnkStubBadData = TRUE; return; } if (!WriteFile ( g_LnkStubDataHandle, &offset, sizeof (DWORD), &bytesWritten, NULL )) { g_LnkStubBadData = TRUE; return; } if (SetFilePointer (g_LnkStubDataHandle, 0, NULL, FILE_END) == 0xFFFFFFFF) { g_LnkStubBadData = TRUE; return; } // // NOTE: Format of lnkstub.dat is below. lnkstub\lnkstub.c must match. // if (!WriteFile (g_LnkStubDataHandle, NewLinkPath, SizeOfString (NewLinkPath), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, NewTarget, SizeOfString (NewTarget), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, NewArgs, SizeOfString (NewArgs), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, NewWorkDir, SizeOfString (NewWorkDir), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, NewIconPath, SizeOfString (NewIconPath), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, &NewIconNr, sizeof (INT), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, &ShowMode, sizeof (INT), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, &Announcement, sizeof (DWORD), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, &Availability, sizeof (DWORD), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!DoesFileExistEx (NewTarget, &findData)) { findData.ftLastWriteTime.dwLowDateTime = 0; findData.ftLastWriteTime.dwHighDateTime = 0; } if (!WriteFile (g_LnkStubDataHandle, &findData.ftLastWriteTime.dwLowDateTime, sizeof (DWORD), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, &findData.ftLastWriteTime.dwHighDateTime, sizeof (DWORD), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (EnumFirstMultiSz (&multiSzEnum, (PTSTR)ReqFilesList->Buf)) { do { if (!WriteFile ( g_LnkStubDataHandle, multiSzEnum.CurrentString, SizeOfString (multiSzEnum.CurrentString), &bytesWritten, NULL )) { g_LnkStubBadData = TRUE; return; } oldFileSpec = DuplicatePathString (NewTarget, 0); oldFilePtr = (PTSTR)GetFileNameFromPath (oldFileSpec); if (oldFilePtr) { *oldFilePtr = 0; } reqFilePath = JoinPaths (oldFileSpec, multiSzEnum.CurrentString); if (!DoesFileExistEx (reqFilePath, &findData)) { findData.ftLastWriteTime.dwLowDateTime = 0; findData.ftLastWriteTime.dwHighDateTime = 0; } if (!WriteFile (g_LnkStubDataHandle, &findData.ftLastWriteTime.dwLowDateTime, sizeof (DWORD), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } if (!WriteFile (g_LnkStubDataHandle, &findData.ftLastWriteTime.dwHighDateTime, sizeof (DWORD), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } FreePathString (reqFilePath); FreePathString (oldFileSpec); } while ((!g_LnkStubBadData) && EnumNextMultiSz (&multiSzEnum)); } if (!WriteFile (g_LnkStubDataHandle, stub, SizeOfString (stub), &bytesWritten, NULL)) { g_LnkStubBadData = TRUE; return; } } } BOOL RestoreInfoFromDefaultPif ( IN PCTSTR UserName, IN HKEY KeyRoot ) { TCHAR key [MEMDB_MAX]; MEMDB_ENUM e; DWORD value1, value2; HKEY cmdKey; cmdKey = OpenRegKey (KeyRoot, S_CMDATTRIB_KEY); if (!cmdKey) { cmdKey = CreateRegKey (KeyRoot, S_CMDATTRIB_KEY); } if (cmdKey) { MemDbBuildKey (key, MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_FULLSCREEN, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { value1 = _ttoi (e.szName); RegSetValueEx (cmdKey, S_CMD_FULLSCREEN, 0, REG_DWORD, (PCBYTE)&value1, sizeof (DWORD)); } MemDbBuildKey (key, MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_XSIZE, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { value1 = _ttoi (e.szName); MemDbBuildKey (key, MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_YSIZE, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { value2 = _ttoi (e.szName); value2 = _rotl (value2, sizeof (DWORD) * 8 / 2); value1 |= value2; RegSetValueEx (cmdKey, S_CMD_WINDOWSIZE, 0, REG_DWORD, (PCBYTE)&value1, sizeof (DWORD)); } } MemDbBuildKey (key, MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_QUICKEDIT, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { value1 = _ttoi (e.szName); RegSetValueEx (cmdKey, S_CMD_QUICKEDIT, 0, REG_DWORD, (PCBYTE)&value1, sizeof (DWORD)); } MemDbBuildKey (key, MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_FONTNAME, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { RegSetValueEx (cmdKey, S_CMD_FACENAME, 0, REG_SZ, (PCBYTE)e.szName, SizeOfString (e.szName)); } MemDbBuildKey (key, MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_XFONTSIZE, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { value1 = _ttoi (e.szName); MemDbBuildKey (key, MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_YFONTSIZE, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { value2 = _ttoi (e.szName); value2 = _rotl (value2, sizeof (DWORD) * 8 / 2); value1 |= value2; RegSetValueEx (cmdKey, S_CMD_FONTSIZE, 0, REG_DWORD, (PCBYTE)&value1, sizeof (DWORD)); } } MemDbBuildKey (key, MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_FONTWEIGHT, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { value1 = _ttoi (e.szName); RegSetValueEx (cmdKey, S_CMD_FONTWEIGHT, 0, REG_DWORD, (PCBYTE)&value1, sizeof (DWORD)); } MemDbBuildKey (key, MEMDB_CATEGORY_DEFAULT_PIF, MEMDB_CATEGORY_LINKEDIT_FONTFAMILY, TEXT("*"), NULL); if (MemDbEnumFirstValue (&e, key, MEMDB_ALL_SUBLEVELS, MEMDB_ENDPOINTS_ONLY)) { value1 = _ttoi (e.szName); RegSetValueEx (cmdKey, S_CMD_FONTFAMILY, 0, REG_DWORD, (PCBYTE)&value1, sizeof (DWORD)); } CloseRegKey (cmdKey); } return TRUE; } BOOL DoLinkEdit ( VOID ) /*++ Routine Description: DoLinkEdit adjusts all PIFs and LNKs that need their targets, working directories or icon paths changed. Arguments: none Return Value: TRUE if link editing succeeded, or FALSE if an error occurred. --*/ { FILEOP_ENUM eOp; FILEOP_PROP_ENUM eOpProp; BOOL forceToShowNormal; BOOL ConvertToLnk; PTSTR NewTarget; PTSTR NewArgs; PTSTR NewWorkDir; PTSTR NewIconPath; PTSTR NewLinkPath; INT NewIconNr; INT Sequencer; DWORD Announcement; DWORD Availability; INT ShowMode; LNK_EXTRA_DATA ExtraData; CONVERTPATH_RC C_Result; TCHAR tempArgs[MAX_TCHAR_PATH * sizeof (TCHAR)]; GROWBUFFER reqFilesList = GROWBUF_INIT; if (EnumFirstPathInOperation (&eOp, OPERATION_LINK_EDIT)) { do { DEBUGMSG ((DBG_VERBOSE, "eOp.Path=%s", eOp.Path)); NewTarget = NULL; NewArgs = NULL; NewWorkDir = NULL; NewIconPath = NULL; NewIconNr = 0; ConvertToLnk = FALSE; forceToShowNormal = FALSE; ZeroMemory (&ExtraData, sizeof (LNK_EXTRA_DATA)); if (EnumFirstFileOpProperty (&eOpProp, eOp.Sequencer, OPERATION_LINK_EDIT)) { do { if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_TARGET)) { NewTarget = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_ARGS)) { StringCopyTcharCount (tempArgs, eOpProp.Property, ARRAYSIZE(tempArgs)); C_Result = ConvertWin9xPath (tempArgs); NewArgs = DuplicatePathString (tempArgs, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_WORKDIR)) { NewWorkDir = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_ICONPATH)) { NewIconPath = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_ICONNUMBER)) { NewIconNr = _tcstoul (eOpProp.Property, NULL, 16); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_FULLSCREEN)) { ConvertToLnk = TRUE; ExtraData.FullScreen = _ttoi (eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_XSIZE)) { ConvertToLnk = TRUE; ExtraData.xSize = _ttoi (eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_YSIZE)) { ConvertToLnk = TRUE; ExtraData.ySize = _ttoi (eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_QUICKEDIT)) { ConvertToLnk = TRUE; ExtraData.QuickEdit = _ttoi (eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_FONTNAME)) { ConvertToLnk = TRUE; StringCopy (ExtraData.FontName, eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_XFONTSIZE)) { ConvertToLnk = TRUE; ExtraData.xFontSize = _ttoi (eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_YFONTSIZE)) { ConvertToLnk = TRUE; ExtraData.yFontSize = _ttoi (eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_FONTWEIGHT)) { ConvertToLnk = TRUE; ExtraData.FontWeight = _ttoi (eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_FONTFAMILY)) { ConvertToLnk = TRUE; ExtraData.FontFamily = _ttoi (eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_CODEPAGE)) { ConvertToLnk = TRUE; ExtraData.CurrentCodePage = (WORD)_ttoi (eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKEDIT_SHOWNORMAL)) { ConvertToLnk = TRUE; forceToShowNormal = TRUE; } } while (EnumNextFileOpProperty (&eOpProp)); } NewLinkPath = GetPathStringOnNt (eOp.Path); DEBUGMSG ((DBG_VERBOSE, "Editing shell link %s", NewLinkPath)); if (!ModifyShellLink( NewLinkPath, NewTarget, NewArgs, NewWorkDir, NewIconPath, NewIconNr, ConvertToLnk, &ExtraData, forceToShowNormal )) { LOG ((LOG_ERROR, "Shell Link %s could not be modified", eOp.Path)); } FreePathString (NewLinkPath); } while (EnumNextPathInOperation (&eOp)); } if (EnumFirstPathInOperation (&eOp, OPERATION_LINK_STUB)) { pInitLnkStubData (); do { NewTarget = NULL; NewArgs = NULL; NewWorkDir = NULL; NewIconPath = NULL; NewIconNr = 0; Sequencer = 0; Announcement = 0; Availability = 0; ShowMode = SW_NORMAL; if (EnumFirstFileOpProperty (&eOpProp, eOp.Sequencer, OPERATION_LINK_STUB)) { do { if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_TARGET)) { NewTarget = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_ARGS)) { StringCopyTcharCount (tempArgs, eOpProp.Property, ARRAYSIZE(tempArgs)); C_Result = ConvertWin9xPath (tempArgs); NewArgs = DuplicatePathString (tempArgs, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_WORKDIR)) { NewWorkDir = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_ICONPATH)) { NewIconPath = DuplicatePathString (eOpProp.Property, 0); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_ICONNUMBER)) { NewIconNr = _tcstoul (eOpProp.Property, NULL, 16); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_SEQUENCER)) { Sequencer = _tcstoul (eOpProp.Property, NULL, 16); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_ANNOUNCEMENT)) { Announcement = _tcstoul (eOpProp.Property, NULL, 16); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_REPORTAVAIL)) { Availability = _tcstoul (eOpProp.Property, NULL, 16); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_REQFILE)) { MultiSzAppend (&reqFilesList, eOpProp.Property); } if (StringIMatch (eOpProp.PropertyName, MEMDB_CATEGORY_LINKSTUB_SHOWMODE)) { ShowMode = _tcstoul (eOpProp.Property, NULL, 16); } } while (EnumNextFileOpProperty (&eOpProp)); } NewLinkPath = GetPathStringOnNt (eOp.Path); pWriteLnkStubData ( NewLinkPath, NewTarget, NewArgs, NewWorkDir, NewIconPath, NewIconNr, ShowMode, Sequencer, Announcement, Availability, &reqFilesList ); FreeGrowBuffer (&reqFilesList); } while (EnumNextPathInOperation (&eOp)); pDoneLnkStubData (); } return TRUE; } PCTSTR pGetFileNameFromPath ( PCTSTR FileSpec ) { PCTSTR p; p = _tcsrchr (FileSpec, TEXT('\\')); if (p) { p = _tcsinc (p); } else { p = _tcsrchr (FileSpec, TEXT(':')); if (p) { p = _tcsinc (p); } } if (!p) { p = FileSpec; } return p; } BOOL DoFileDel ( VOID ) /*++ Routine Description: DoFileDel deletes all files marked to be deleted by us (not by an external module). Arguments: none Return Value: TRUE if the delete operation succeeded, or FALSE if an error occurred. Call GetLastError() for error code. --*/ { FILEOP_ENUM e; HKEY Key; PDWORD ValuePtr; BOOL DoDelete; PCTSTR SharedFileName; DWORD attr; PCTSTR disableName; PCTSTR newLocation; GROWLIST disableList = GROWLIST_INIT; PCTSTR srcPath; UINT count; UINT u; // // Enumerate each file in filedel. This is used for cleanup purposes, not // for migration purposes. It is called just before syssetup.dll // terminates. // if (EnumFirstPathInOperation (&e, OPERATION_CLEANUP)) { do { // // Check registry for use count // DoDelete = TRUE; Key = OpenRegKeyStr (S_REG_SHARED_DLLS); if (Key) { // // Test SharedDlls for full path, then file name only // SharedFileName = e.Path; ValuePtr = (PDWORD) GetRegValueDataOfType (Key, e.Path, REG_DWORD); if (!ValuePtr) { SharedFileName = pGetFileNameFromPath (e.Path); ValuePtr = (PDWORD) GetRegValueDataOfType ( Key, SharedFileName, REG_DWORD ); } // // Match found. Is use count reasonable and greater than one? // if (ValuePtr) { if (*ValuePtr < 0x10000 && *ValuePtr > 1) { *ValuePtr -= 1; RegSetValueEx ( Key, SharedFileName, 0, REG_DWORD, (PBYTE) ValuePtr, sizeof (DWORD) ); DEBUGMSG (( DBG_FILEMIG, "%s not deleted; share count decremented to %u", SharedFileName, *ValuePtr )); } else { RegDeleteValue (Key, SharedFileName); } DoDelete = FALSE; MemFree (g_hHeap, 0, ValuePtr); } CloseRegKey (Key); } if (DoDelete) { attr = GetLongPathAttributes (e.Path); if (attr != INVALID_ATTRIBUTES) { DEBUGMSG ((DBG_FILEMIG, "Deleting %s", e.Path)); if (GetLongPathAttributes (e.Path) & FILE_ATTRIBUTE_DIRECTORY) { SetLongPathAttributes (e.Path, FILE_ATTRIBUTE_DIRECTORY); DEBUGMSG ((DBG_FILEMIG, "Removing %s", e.Path)); DeleteDirectoryContents (e.Path); if (!SetLongPathAttributes (e.Path, FILE_ATTRIBUTE_NORMAL) || !RemoveLongDirectoryPath (e.Path) ) { LOG ((LOG_ERROR, "RemoveDirectory failed for %s", e.Path)); } } else { DEBUGMSG ((DBG_FILEMIG, "Deleting %s", e.Path)); if (!SetLongPathAttributes (e.Path, FILE_ATTRIBUTE_NORMAL) || !DeleteLongPath (e.Path) ) { LOG ((LOG_ERROR, "DeleteFile failed for %s", e.Path)); } } DEBUGMSG ((DBG_FILEMIG, "Done deleting %s", e.Path)); } } } while (EnumNextPathInOperation (&e)); } SetLastError (ERROR_SUCCESS); return TRUE; } INT CALLBACK pRemoveEmptyDirsProc ( PCTSTR FullFileSpec, PCTSTR DontCare, WIN32_FIND_DATA *FindDataPtr, DWORD EnumTreeID, PVOID Param, PDWORD CurrentDirData ) /*++ Routine Description: pRemoveEmptyDirsProc is called for every directory in the tree being enumerated (see pRemoveEmptyDirsInTree below). This enum proc calls RemoveLongDirectoryPath, regardless if files exist in it or not. RemoveLongDirectoryPath will fail if it is not empty. Arguments: FullFileSpec - The Win32 path and directory name of the item being enumerated FindDataPtr - A pointer to the WIN32_FIND_DATA structure for the item EnumTreeID - Unused Param - A BOOL indicating FALSE if we should only remove empty dirs that we deleted something from, or TRUE if we should delete the empty dir in any case. Return Value: TRUE if the delete operation succeeded, or FALSE if an error occurred. Call GetLastError() for error code. --*/ { if ((FindDataPtr->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { return CALLBACK_CONTINUE; } // // Did we delete any files from this directory? // if (!Param) { if (!TestPathsForOperations (FullFileSpec, ALL_DELETE_OPERATIONS)) { DEBUGMSG ((DBG_NAUSEA, "We did not delete anything from %s", FullFileSpec)); return CALLBACK_CONTINUE; } if (IsDirectoryMarkedAsEmpty (FullFileSpec)) { DEBUGMSG ((DBG_NAUSEA, "This directory was empty to begin with: %s", FullFileSpec)); return CALLBACK_CONTINUE; } } // // Yes, delete the directory. If it is not empty, RemoveLongDirectoryPath will fail. // DEBUGMSG ((DBG_NAUSEA, "Trying to remove empty directory %s", FullFileSpec)); if (!SetLongPathAttributes (FullFileSpec, FILE_ATTRIBUTE_DIRECTORY)) { return CALLBACK_CONTINUE; } if (RemoveLongDirectoryPath (FullFileSpec)) { DEBUGMSG ((DBG_NAUSEA, "%s was removed", FullFileSpec)); } else { DEBUGMSG ((DBG_NAUSEA, "%s was not removed", FullFileSpec)); SetLongPathAttributes (FullFileSpec, FindDataPtr->dwFileAttributes); } return CALLBACK_CONTINUE; } BOOL pRemoveEmptyDirsInTree ( PCTSTR TreePath, BOOL CleanAll ) /*++ Routine Description: pRemoveEmptyDirsInTree calls EnumerateTree to scan all directories in TreePath, deleting those that are empty. Arguments: TreePath - A full path to the root of the tree to enumerate. The path must not have any wildcards. CleanAll - Specifies TRUE if the empty dir should be cleaned in all cases, or FALSE if it should be cleaned only if modified by a delete operation. Return Value: TRUE if the delete operation succeeded, or FALSE if an error occurred. Call GetLastError() for error code. --*/ { BOOL b; b = EnumerateTree ( TreePath, // Starting path pRemoveEmptyDirsProc, // Enumeration Proc NULL, // Error-logging proc 0, // MemDb exclusion node--not used (PVOID) CleanAll, // EnumProc param ENUM_ALL_LEVELS, // Level NULL, // exclusion INF file--not used FILTER_DIRECTORIES|FILTER_DIRS_LAST // Attributes filter ); if (!b) { LOG ((LOG_ERROR, "pRemoveEmptyDirsInTree: EnumerateTree failed")); } return b; } BOOL RemoveEmptyDirs ( VOID ) /*++ Routine Description: RemoveEmptyDirs sweeps through the directories in CleanUpDirs and blows away any subdirectory that has no files. Arguments: none Return Value: Always TRUE. --*/ { MEMDB_ENUM e; if (MemDbGetValueEx (&e, MEMDB_CATEGORY_CLEAN_UP_DIR, NULL, NULL)) { do { pRemoveEmptyDirsInTree (e.szName, e.dwValue); } while (MemDbEnumNextValue (&e)); } return TRUE; } VOID pFixSelfRelativePtr ( PTOKENSET Base, PCVOID *Ptr ) { if (*Ptr != NULL) { *Ptr = (PBYTE) *Ptr - TOKEN_BASE_OFFSET + (UINT) Base + sizeof (TOKENSET) + (Base->ArgCount * sizeof (TOKENARG)); } } BOOL pFileSearchAndReplaceA ( IN PCSTR FilePath, IN OUT PTOKENSET TokenSet ) /*++ Routine Description: pFileSearchAndReplace does all the initialization work necessary to update the contents of a file. It also converts a self-relative token set into an absolute token set. That means the offsets in the struct are converted to pointers. After everything is prepared, pFileSearchAndReplaceWorker is called to modify the file. Arguments: FilePath - Specifies the file to process TokenSet - Specifies the token set to apply to FilePath. Receives its pointers updated, if necessary. Return Value: TRUE if the file was successfully updated. FALSE otherwise. --*/ { HANDLE InFile = INVALID_HANDLE_VALUE; HANDLE OutFile = INVALID_HANDLE_VALUE; CHAR TempDir[MAX_MBCHAR_PATH]; CHAR TempPath[MAX_MBCHAR_PATH]; PBYTE MapStart = NULL; PBYTE MapEnd; DWORD Attribs; HANDLE Map = NULL; BOOL b = FALSE; UINT u; __try { // // Detect a TokenSet struct that needs its offsets fixed // if (TokenSet->SelfRelative) { pFixSelfRelativePtr (TokenSet, &TokenSet->CharsToIgnore); for (u = 0 ; u < TokenSet->ArgCount ; u++) { pFixSelfRelativePtr (TokenSet, &TokenSet->Args[u].DetectPattern); pFixSelfRelativePtr (TokenSet, &TokenSet->Args[u].SearchList); pFixSelfRelativePtr (TokenSet, &TokenSet->Args[u].ReplaceWith); } TokenSet->SelfRelative = FALSE; } DEBUGMSG ((DBG_VERBOSE, "URL mode: %s", TokenSet->UrlMode ? TEXT("YES") : TEXT ("NO"))); // // Save original attributes // Attribs = GetFileAttributesA (FilePath); if (Attribs == INVALID_ATTRIBUTES) { DEBUGMSGA ((DBG_ERROR, "Can't get attributes of %s", FilePath)); __leave; } // // Open the source file // InFile = CreateFileA ( FilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (InFile == INVALID_HANDLE_VALUE) { DEBUGMSGA ((DBG_ERROR, "Can't open %s", FilePath)); __leave; } // // Get a destination file name // GetTempPathA (ARRAYSIZE(TempDir), TempDir); GetTempFileNameA (TempDir, "xx$", 0, TempPath); OutFile = CreateFileA ( TempPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (OutFile == INVALID_HANDLE_VALUE) { DEBUGMSGA ((DBG_ERROR, "Can't create %s", TempPath)); __leave; } // // Create file mapping // Map = CreateFileMapping ( InFile, NULL, PAGE_READONLY, 0, 0, NULL ); if (!Map) { DEBUGMSGA ((DBG_ERROR, "Can't create file mapping for %s", FilePath)); __leave; } // // Map a view of the source file // MapStart = MapViewOfFile (Map, FILE_MAP_READ, 0, 0, 0); if (!MapStart) { DEBUGMSGA ((DBG_ERROR, "Can't map view of file for %s", FilePath)); __leave; } MapEnd = MapStart + GetFileSize (InFile, NULL); // // Now do the search and replace // if (!pFileSearchAndReplaceWorker ( MapStart, MapEnd, OutFile, TokenSet )) { __leave; } // // Close the handles // UnmapViewOfFile (MapStart); CloseHandle (Map); CloseHandle (OutFile); CloseHandle (InFile); MapStart = NULL; Map = NULL; OutFile = INVALID_HANDLE_VALUE; InFile = INVALID_HANDLE_VALUE; // // Remove the original file, and replace it with the new copy // SetFileAttributesA (FilePath, FILE_ATTRIBUTE_NORMAL); // // MOVEFILE_REPLACE_EXISTING does not work with non-normal attributes // if (!OurMoveFileExA (TempPath, FilePath, MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING)) { DEBUGMSGA ((DBG_ERROR, "Can't move %s to %s", TempPath, FilePath)); __leave; } if (!SetFileAttributesA (FilePath, Attribs)) { DEBUGMSGA ((DBG_WARNING, "Can't set attributes on %s", FilePath)); } b = TRUE; } __finally { if (MapStart) { UnmapViewOfFile (MapStart); } if (Map) { CloseHandle (Map); } if (OutFile != INVALID_HANDLE_VALUE) { CloseHandle (OutFile); DeleteFileA (TempPath); } if (InFile != INVALID_HANDLE_VALUE) { CloseHandle (InFile); } } return b; } VOID pConvertUrlToText ( IN PCSTR Source, OUT PSTR Buffer // caller must ensure buffer is able to hold the entire Source ) { PSTR dest; PCSTR src; src = Source; dest = Buffer; while (*src) { if (*src == '%' && GetHexDigit(src[1]) != -1 && GetHexDigit(src[2]) != -1) { *dest++ = GetHexDigit(src[1]) << 4 | GetHexDigit(src[2]); src += 3; } else { *dest++ = *src++; } } *dest = 0; } CHAR pMakeHex ( IN UINT Digit ) { MYASSERT (Digit < 16); if (Digit < 10) { Digit += '0'; } else { Digit += 'A' - 10; } return (CHAR) Digit; } UINT pConvertTextToUrl ( IN PCSTR Text, OUT PSTR Buffer, IN UINT BufferTchars ) { PSTR dest; PCSTR src; PSTR maxDest; PCSTR unsafeChars = "<>\"#{}|^~[]'"; UINT result = 0; src = Text; dest = Buffer; maxDest = Buffer + BufferTchars - 1; while (*src && dest < maxDest) { if (*src < 0x21 || *src > 0x7e || strchr (unsafeChars, *src)) { if (dest + 3 >= maxDest) { break; } *dest++ = '%'; *dest++ = pMakeHex (((UINT) (*src)) >> 4); *dest++ = pMakeHex (((UINT) (*src)) & 0x0F); src++; } else if (*src == '\\') { *dest++ = '/'; src++; } else { *dest++ = *src++; } } if (dest <= maxDest) { *dest++ = 0; result = dest - Buffer; } else if (BufferTchars) { *maxDest = 0; } return result; } BOOL pFileSearchAndReplaceWorker ( IN PBYTE MapStart, IN PBYTE MapEnd, IN HANDLE OutFile, IN PTOKENSET TokenSet ) /*++ Routine Description: pFileSearchAndReplaceWorker implements a general search and replace mechanism. It parses a memory mapped file, and writes it to a destination file, updating it as necessary. After parsing a line, this function strips out the characters to be ignored (if any), and then tests the line against each detection pattern. If a detection pattern is matched, then the search/replace pair(s) are processed, and the paths are updated if specified. Arguments: MapStart - Specifies the first byte of the memory mapped file MapEnd - Specifies one byte after the end of the memory mapped file OutFile - Specifies a handle to a file that is open for writing TokenSet - Specifies the set of tokens to process. This includes global settings, and detect/search/replace sets. Return Value: TRUE if the function successfully processed the file, FALSE otherwise. --*/ { PBYTE Start; PBYTE End; PBYTE Eol; BOOL b = FALSE; GROWBUFFER Buf = GROWBUF_INIT; GROWBUFFER Dest = GROWBUF_INIT; GROWBUFFER quoteless = GROWBUF_INIT; GROWBUFFER SpcList = GROWBUF_INIT; UINT u; UINT Count; PSTR p; PCSTR q; PCSTR SrcBuf; BOOL Detected; PTOKENARG Arg; MULTISZ_ENUMA e; PCSTR NewStr; PCSTR ReplaceStr; PCSTR *Element; PSTR EndStr; DWORD Status; CHAR NewPath[MAX_MBCHAR_PATH]; UINT Len; PBYTE Output; UINT OutputBytes; DWORD DontCare; MBCHAR ch; INT i; UINT reservedTchars; PSTR reservedDest; UINT removedDblQuotes; PCSTR initialPos; UINT inQuotes; // // Initialize the structure // for (u = 0 ; u < TokenSet->ArgCount ; u++) { Arg = &TokenSet->Args[u]; Arg->DetectPatternStruct = NULL; } __try { // // Parse the detect patterns // for (u = 0 ; u < TokenSet->ArgCount ; u++) { Arg = &TokenSet->Args[u]; Arg->DetectPatternStruct = CreateParsedPatternA ( Arg->DetectPattern ); if (!Arg->DetectPatternStruct) { DEBUGMSGA ((DBG_ERROR, "File pattern syntax error: %s", Arg->DetectPattern)); __leave; } } // // Identify each line, and then parse the line // Start = MapStart; while (Start < MapEnd) { // // Find the line // End = Start; while (End < MapEnd && *End && *End != '\r' && *End != '\n') { End++; } Eol = End; if (End < MapEnd && *End == '\r') { while (End < MapEnd && *End == '\r') { End++; } } if (End < MapEnd && *End == '\n') { End++; } if (End > Start) { // // OK we now have a line. Copy it into Buf, removing // the characters we don't care about. // Buf.End = 0; Dest.End = 0; Detected = FALSE; p = (PSTR) GrowBuffer (&Buf, Eol - Start + sizeof (CHAR)); if (TokenSet->CharsToIgnore) { q = Start; while (q < End) { if (!_mbschr (TokenSet->CharsToIgnore, _mbsnextc (q))) { _copymbchar (p, q); p = _mbsinc (p); } q = _mbsinc (q); } *p = 0; for (u = 0 ; u < TokenSet->ArgCount ; u++) { Arg = &TokenSet->Args[u]; Detected = TestParsedPatternA ( Arg->DetectPatternStruct, (PCSTR) Buf.Buf ); if (Detected) { break; } } } else { for (u = 0 ; u < TokenSet->ArgCount ; u++) { Arg = &TokenSet->Args[u]; Detected = TestParsedPatternABA ( Arg->DetectPatternStruct, (PCSTR) Start, (PCSTR) Eol ); if (Detected) { break; } } } if (Detected) { // // Copy the line into a work buffer // Buf.End = 0; p = (PSTR) GrowBuffer (&Buf, (End - Start + 1) * sizeof (CHAR)); StringCopyABA (p, (PCSTR) Start, (PCSTR) End); Output = Buf.Buf; OutputBytes = Buf.End - sizeof (CHAR); DEBUGMSGA ((DBG_NAUSEA, "Copied line to work buffer: %s", p)); // // Perform search and replace on the line // if (Arg->SearchList) { ReplaceStr = Arg->ReplaceWith; if (EnumFirstMultiSzA (&e, Arg->SearchList)) { do { NewStr = StringSearchAndReplaceA ( (PCSTR) Buf.Buf, e.CurrentString, ReplaceStr ); if (NewStr) { Buf.End = 0; GrowBufCopyStringA (&Buf, NewStr); FreePathStringA (NewStr); OutputBytes = Buf.End - sizeof (CHAR); } ReplaceStr = GetEndOfStringA (ReplaceStr) + 1; } while (EnumNextMultiSzA (&e)); } } // // Perform path update // if (Arg->UpdatePath) { DEBUGMSG ((DBG_NAUSEA, "Updating path")); Dest.End = 0; SrcBuf = (PCSTR) Buf.Buf; inQuotes = 0; while (*SrcBuf) { if ((SrcBuf[1] == ':' && (SrcBuf[2] == '\\' || SrcBuf[2] == '/')) && (SrcBuf[3] != '/' && SrcBuf[3] != '\\') ) { quoteless.End = 0; GrowBuffer ("eless, SizeOfStringA (SrcBuf)); // // Convert from URL to file system char set // if (TokenSet->UrlMode) { DEBUGMSGA ((DBG_NAUSEA, "URL conversion input: %s", SrcBuf)); pConvertUrlToText (SrcBuf, (PSTR) quoteless.Buf); q = (PCSTR) quoteless.Buf; DEBUGMSGA ((DBG_NAUSEA, "URL conversion result: %s", q)); } else { q = SrcBuf; } // // Remove all dbl quotes from buffer, flip // forward slashes into backslashes, stop at // first non-file system character // or at the first quote if the text // is between quotes already // p = (PSTR) quoteless.Buf; initialPos = q; DEBUGMSGA ((DBG_NAUSEA, "CMD line cleanup input: %s", q)); removedDblQuotes = 0; while (*q) { ch = _mbsnextc (q); if (ch == ':' || ch == '|' || ch == '?' || ch == '*' || ch == '<' || ch == '>') { if (q != &initialPos[1]) { break; } } if (ch != '\"') { if (ch != '/') { if (IsLeadByte (*q) && q[1]) { *p++ = *q++; } *p++ = *q++; } else { *p++ = '\\'; q++; } } else { // // check parser's status // if (inQuotes & 1) { // // done with the filename // break; } q++; removedDblQuotes++; } } *p = 0; DEBUGMSGA ((DBG_NAUSEA, "CMD line cleanup result: %s", quoteless.Buf)); // // Build a list of spaces // SpcList.End = 0; initialPos = (PCSTR) quoteless.Buf; q = quoteless.Buf + 2; EndStr = p; while (q < EndStr) { ch = _mbsnextc (q); if (isspace (ch)) { Element = (PCSTR *) GrowBuffer (&SpcList, sizeof (PCSTR)); *Element = q; while (q + 1 < EndStr && isspace (_mbsnextc (q + 1))) { q++; } } q = _mbsinc (q); } if (q == EndStr || !SpcList.End) { Element = (PCSTR *) GrowBuffer (&SpcList, sizeof (PCSTR)); *Element = EndStr; } // // Test all paths by using the longest possibility first, // and then by truncating the path at the spaces. // Count = SpcList.End / sizeof (PCSTR); MYASSERT (Count > 0); Element = (PCSTR *) SpcList.Buf; for (i = Count - 1 ; i >= 0 ; i--) { p = (PSTR) (Element[i]); ch = *p; *p = 0; DEBUGMSGA ((DBG_NAUSEA, "Testing path: %s", initialPos)); Status = GetFileInfoOnNtA (initialPos, NewPath, MAX_MBCHAR_PATH); DEBUGMSGA ((DBG_NAUSEA, "Results: %x/%s", Status, NewPath)); *p = (CHAR)ch; if (Status != FILESTATUS_UNCHANGED && Status != FILESTATUS_BACKUP) { break; } } *EndStr = 0; // // If there is a new path, update the destination // if (Status != FILESTATUS_UNCHANGED && Status != FILESTATUS_BACKUP) { if (TokenSet->UrlMode) { reservedTchars = (TcharCountA (NewPath) * 3) + 1; reservedDest = GrowBufferReserve (&Dest, reservedTchars * sizeof (CHAR)); Dest.End += pConvertTextToUrl (NewPath, reservedDest, reservedTchars) / sizeof (CHAR); DEBUGMSGA ((DBG_NAUSEA, "URL conversion output: %s", reservedDest)); } else { GrowBufAppendStringA (&Dest, NewPath); } SrcBuf += (Element[i] - initialPos) + removedDblQuotes; Dest.End -= sizeof (CHAR); } else { // // No changed path here; copy char by char // if (IsLeadByte (*SrcBuf) && SrcBuf[1]) { Len = 2; } else { Len = 1; } p = GrowBuffer (&Dest, Len); CopyMemory (p, SrcBuf, Len); SrcBuf = (PCSTR) ((PBYTE) SrcBuf + Len); } } else { // // keep track of quotes // if (*SrcBuf == '\"') { inQuotes++; } // // This is not a path, copy the character to Dest // if (IsLeadByte (*SrcBuf) && SrcBuf[1]) { Len = 2; } else { Len = 1; } p = GrowBuffer (&Dest, Len); CopyMemory (p, SrcBuf, Len); SrcBuf = (PCSTR) ((PBYTE) SrcBuf + Len); } } Output = Dest.Buf; OutputBytes = Dest.End; } } else { // // The line does not change // Output = Start; OutputBytes = End - Start; } // // Write the line // if (!WriteFile (OutFile, Output, OutputBytes, &DontCare, NULL)) { DEBUGMSG ((DBG_ERROR, "File search/replace: Can't write to output file")); __leave; } // // Write a nul if it is found in the file // if (End < MapEnd && *End == 0) { if (!WriteFile (OutFile, End, 1, &DontCare, NULL)) { DEBUGMSG ((DBG_ERROR, "File search/replace: Can't write nul to output file")); __leave; } End++; } } else if (End < MapEnd) { DEBUGMSG ((DBG_WHOOPS, "Parse error in pFileSearchAndReplaceWorker")); break; } Start = End; } b = TRUE; } __finally { FreeGrowBuffer (&Buf); FreeGrowBuffer (&Dest); FreeGrowBuffer (&SpcList); FreeGrowBuffer ("eless); for (u = 0 ; u < TokenSet->ArgCount ; u++) { Arg = &TokenSet->Args[u]; DestroyParsedPatternA (Arg->DetectPatternStruct); } } return b; } BOOL pIsOkToEdit ( IN PCSTR AnsiPath, OUT PSTR NewPath OPTIONAL ) /*++ Routine Description: pIsOkToEdit checks an ansi file name to see if it is handled by a migration DLL, or if it is deleted. If neither of those cases apply, the file can be edited. Optionally the function returns the final path for the file. Arguments: AnsiPath - Specifies the path to test NewPath - Receives the final path for the file, which may be the same as AnsiPath, or may be different. Return Value: TRUE if the file can be edited, FALSE otherwise. --*/ { DWORD Status; // // Is this file marked as handled? // if (IsFileMarkedAsHandledA (AnsiPath)) { return FALSE; } Status = GetFileInfoOnNtA (AnsiPath, NewPath, MEMDB_MAX); return !(Status & FILESTATUS_DELETED); } BOOL pProcessFileEdit ( VOID ) /*++ Routine Description: pProcessFileEdit enumerates all the files that can be edited, and calls pFileSearchAndReplace for each, using the token sets created on the Win9x side of setup. Arguments: None. Return Value: TRUE on success, FALSE on error. --*/ { MEMDB_ENUMA e; PTOKENSET PathsOnlySet; BOOL b = TRUE; GROWBUFFER TokenSetCopy = GROWBUF_INIT; PTOKENSET Buf; CHAR NewPath[MEMDB_MAX]; DWORD Result; Result = GetLastError(); // // Create a set that will update the paths of any file // PathsOnlySet = (PTOKENSET) MemAlloc (g_hHeap, 0, sizeof (TOKENSET) + sizeof (TOKENARG)); PathsOnlySet->ArgCount = 1; PathsOnlySet->CharsToIgnore = NULL; PathsOnlySet->UrlMode = FALSE; PathsOnlySet->SelfRelative = FALSE; PathsOnlySet->Args[0].DetectPattern = "*"; PathsOnlySet->Args[0].SearchList = NULL; PathsOnlySet->Args[0].ReplaceWith = NULL; PathsOnlySet->Args[0].UpdatePath = TRUE; if (MemDbGetValueExA (&e, MEMDB_CATEGORY_FILEEDITA, NULL, NULL)) { do { if (!pIsOkToEdit (e.szName, NewPath)) { continue; } DEBUGMSGA ((DBG_VERBOSE, "Editing %s.", NewPath)); if (e.bBinary) { TokenSetCopy.End = 0; Buf = (PTOKENSET) GrowBuffer (&TokenSetCopy, e.BinarySize); CopyMemory (Buf, e.BinaryPtr, e.BinarySize); if (!pFileSearchAndReplaceA (NewPath, Buf)) { DEBUGMSGA ((DBG_ERROR, "Could not edit %s", NewPath)); b = FALSE; Result = GetLastError(); } FreeGrowBuffer (&TokenSetCopy); } else { if (!pFileSearchAndReplaceA (NewPath, PathsOnlySet)) { DEBUGMSGA ((DBG_ERROR, "Could not edit %s", NewPath)); b = FALSE; Result = GetLastError(); } } } while (MemDbEnumNextValueA (&e)); } MemFree (g_hHeap, 0, PathsOnlySet); SetLastError (Result); return b; } DWORD DoFileEdit ( DWORD Request ) /*++ Routine Description: DoFileEdit is called by the progress bar manager to query ticks or do the file editing. If querying ticks, then the function determines how many files will be edited, and multiplies that by a constant to get the tick size. Otherwise the function edits all the files queued for this operation. Arguments: Request - Specifies the request being made by the progress bar manager Return Value: If Request is REQUEST_QUERYTICKS, the return value indicates the number of ticks. Otherwise, the return value is a Win32 result code. --*/ { MEMDB_ENUMA e; UINT u; if (Request == REQUEST_QUERYTICKS) { u = 0; if (MemDbGetValueExA (&e, MEMDB_CATEGORY_FILEEDITA, NULL, NULL)) { do { if (pIsOkToEdit (e.szName, NULL)) { u++; } } while (MemDbEnumNextValueA (&e)); } return u * TICKS_FILE_EDIT; } if (Request != REQUEST_RUN) { return 0; } if (!pProcessFileEdit()) { return GetLastError(); } return ERROR_SUCCESS; } VOID pWriteLine ( IN HANDLE Handle, IN PCWSTR RootDir, OPTIONAL IN PCWSTR String ) { DWORD dontCare; PCWSTR fullPath; if (RootDir) { fullPath = JoinPathsW (RootDir, String); } else { fullPath = String; } WriteFile (Handle, fullPath, ByteCountW (fullPath), &dontCare, NULL); if (fullPath != String) { FreePathStringW (fullPath); } WriteFile (Handle, L"\r\n", 4, &dontCare, NULL); } DWORD RemoveBootIniCancelOption ( DWORD Request ) { HINF inf = INVALID_HANDLE_VALUE; PCTSTR bootIni; PCTSTR bootIniTmp; DWORD result = ERROR_SUCCESS; PINFLINE osLine; BOOL changed = FALSE; DWORD attribs; if (Request == REQUEST_QUERYTICKS) { return 50; } if (Request != REQUEST_RUN) { return 0; } bootIni = JoinPaths (g_BootDrivePath, TEXT("boot.ini")); __try { // // Open boot.ini for editing // inf = OpenInfFile (bootIni); if (inf == INVALID_HANDLE_VALUE) { DEBUGMSG ((DBG_ERROR, "Can't open %s", bootIni)); result = GetLastError(); __leave; } // // Scan boot.ini for a textmode option that has /rollback. Delete it. // osLine = GetFirstLineInSectionStr (inf, TEXT("Operating Systems")); if (!osLine) { DEBUGMSG ((DBG_ERROR, "No lines found in [Operating Systems] in %s", bootIni)); result = ERROR_FILE_NOT_FOUND; __leave; } // // Loop until all lines with /rollback are gone // do { do { // // Check this line for a /rollback option // if (_tcsistr (osLine->Data, TEXT("/rollback"))) { DEBUGMSG ((DBG_FILEMIG, "Found rollback option: %s", osLine->Data)); break; } } while (osLine = GetNextLineInSection (osLine)); if (osLine) { if (!DeleteLineInInfSection (inf, osLine)) { MYASSERT (FALSE); break; } DEBUGMSG ((DBG_FILEMIG, "Line sucessfully removed")); changed = TRUE; osLine = GetFirstLineInSectionStr (inf, TEXT("Operating Systems")); } } while (osLine); // // If we changed the file, then write it to disk. Keep the original // boot.ini file in case we fail to save. // attribs = GetFileAttributes (bootIni); SetFileAttributes (bootIni, FILE_ATTRIBUTE_NORMAL); MYASSERT (attribs != INVALID_ATTRIBUTES); bootIniTmp = JoinPaths (g_BootDrivePath, TEXT("boot.~t")); SetFileAttributes (bootIniTmp, FILE_ATTRIBUTE_NORMAL); DeleteFile (bootIniTmp); if (!MoveFile (bootIni, bootIniTmp)) { LOG ((LOG_ERROR, (PCSTR) MSG_BOOT_INI_MOVE_FAILED, bootIni, bootIniTmp)); result = GetLastError(); } else { DEBUGMSG ((DBG_FILEMIG, "Moved %s to %s", bootIni, bootIniTmp)); if (!SaveInfFile (inf, bootIni)) { LOG ((LOG_ERROR, (PCSTR) MSG_BOOT_INI_SAVE_FAILED, bootIni)); result = GetLastError(); SetFileAttributes (bootIni, FILE_ATTRIBUTE_NORMAL); DeleteFile (bootIni); if (!MoveFile (bootIniTmp, bootIni)) { // // This should not happen, because we just successfully // moved the original to the tmp; we should be able to // move the temp back to the original. If we fail, the pc // becomes unbootable. But what can we do? // LOG ((LOG_ERROR, (PCSTR) MSG_BOOT_INI_MOVE_FAILED, bootIniTmp, bootIni)); } } else { // // boot.ini was successfully updated. Remove the original copy. // DeleteFile (bootIniTmp); MYASSERT (result == ERROR_SUCCESS); DEBUGMSG ((DBG_FILEMIG, "%s was saved", bootIni)); } } // // restore attributes on original if possible. // SetFileAttributes (bootIni, attribs); FreePathString (bootIniTmp); // result already set above } __finally { if (inf != INVALID_HANDLE_VALUE) { CloseInfFile (inf); } FreePathString (bootIni); } return result; } ULONGLONG pGetFileSize( IN PCTSTR FilePath ) { ULARGE_INTEGER FileSize = {0, 0}; GetFileSizeFromFilePath(FilePath, &FileSize); return FileSize.QuadPart; } BOOL pMapHiveOfUserDoingTheUpgrade ( VOID ) { MIGRATE_USER_ENUM e; PTSTR profilePath; TCHAR hiveFile[MAX_TCHAR_PATH]; LONG rc; HKEY newHkcu = NULL; TCHAR rootKey[] = TEXT("HKU\\") S_TEMP_USER_KEY; BOOL result = FALSE; BOOL hiveLoaded = FALSE; __try { // // Find Administrator // if (EnumFirstUserToMigrate (&e, ENUM_ALL_USERS)) { do { if (e.UserDoingTheUpgrade) { break; } } while (EnumNextUserToMigrate (&e)); if (e.UserDoingTheUpgrade) { DEBUGMSG ((DBG_VERBOSE, "%s is the user doing the upgrade", e.FixedUserName)); // // Load the hive // if (-1 == pSetupStringTableLookUpStringEx ( g_HiveTable, e.FixedUserName, STRTAB_CASE_INSENSITIVE, hiveFile, sizeof (hiveFile) )) { DEBUGMSG ((DBG_WHOOPS, "Can't find NT hive for %s", e.FixedUserName)); __leave; } rc = RegUnLoadKey (HKEY_USERS, S_TEMP_USER_KEY); if (rc != ERROR_SUCCESS) { DumpOpenKeys (); SetLastError (rc); DEBUGMSG_IF (( rc != ERROR_INVALID_PARAMETER, DBG_WARNING, "Can't unload temp user key" )); } rc = RegLoadKey (HKEY_USERS, S_TEMP_USER_KEY, hiveFile); if (rc != ERROR_SUCCESS) { LOG (( LOG_ERROR, "Uninstall: Can't load user hive for %s (%s)", e.FixedUserName, hiveFile )); __leave; } hiveLoaded = TRUE; newHkcu = OpenRegKeyStr (rootKey); if (newHkcu) { rc = RegOverridePredefKey (HKEY_CURRENT_USER, newHkcu); if (rc != ERROR_SUCCESS) { LOG ((LOG_ERROR, "Uninstall: Can't override HKCU")); __leave; } } else { LOG (( LOG_ERROR, "Uninstall: Can't open user hive for %s (%s)", e.FixedUserName, hiveFile )); __leave; } } else { DEBUGMSG ((DBG_ERROR, "Can't find migration user")); __leave; } } else { DEBUGMSG ((DBG_WHOOPS, "No users were enumerated")); __leave; } result = TRUE; } __finally { if (newHkcu) { CloseRegKey (newHkcu); } if (hiveLoaded && !result) { RegOverridePredefKey (HKEY_CURRENT_USER, NULL); RegUnLoadKey (HKEY_USERS, S_TEMP_USER_KEY); } } return result; } VOID pUnmapHiveOfUserDoingTheUpgrade ( VOID ) { RegOverridePredefKey (HKEY_CURRENT_USER, NULL); RegUnLoadKey (HKEY_USERS, S_TEMP_USER_KEY); } BOOL pClearPasswordsFromWinntSif( PCTSTR WinntSifDir ) { TCHAR pathWinntSif[MAX_PATH]; TCHAR password[MAX_PATH]; BOOL bResult = TRUE; UINT i; if(!WinntSifDir || (_tcslen(WinntSifDir) + 1/*'\\'*/ + ARRAYSIZE(WINNT_SIF_FILE)) > ARRAYSIZE(pathWinntSif)){ MYASSERT(FALSE); return FALSE; } _tcscpy(pathWinntSif, WinntSifDir); _tcscat(AppendWack(pathWinntSif), WINNT_SIF_FILE); { struct{ PCTSTR Section; PCTSTR PasswordKey; } passwordList[] = { {S_PAGE_IDENTIFICATION, S_DOMAIN_ADMIN_PW}, // DomainAdminPassword {WINNT_GUIUNATTENDED, TEXT("AdminPassword")}, // AdminPassword {TEXT("Win9xUpg.UserOptions"), TEXT("UserPassword")}, // UserPassword {TEXT("Win9xUpg.UserOptions"), S_DEFAULT_PASSWORD_VALUE} // DefaultPassword }; for(i = 0; i < ARRAYSIZE(passwordList); i++){ if(GetPrivateProfileString(passwordList[i].Section, passwordList[i].PasswordKey, TEXT(""), password, ARRAYSIZE(password), pathWinntSif) && password[0] != '\0'){ if(!WritePrivateProfileString(passwordList[i].Section, passwordList[i].PasswordKey, TEXT(""), pathWinntSif)){ MYASSERT(FALSE); bResult = FALSE; } } } } return bResult; } DWORD WriteBackupInfo ( DWORD Request ) /*++ Routine Description: WriteBackupInfo outputs files to allow rollback to work properly. It also moves the text mode rollback environment into %windir%\undo. Arguments: Request - Specifies the request being made by the progress bar manager Return Value: If Request is REQUEST_QUERYTICKS, the return value indicates the number of ticks. Otherwise, the return value is a Win32 result code. --*/ { UINT u; TCHAR src[MAX_PATH]; TCHAR cabPath[MAX_PATH]; PCSTR ansiTempDir; HKEY key; HKEY subKey; PCTSTR msg; HANDLE delDirsHandle; HANDLE delFilesHandle; PCTSTR path; DWORD dontCare; TREE_ENUM treeEnum; LONG rc; CCABHANDLE cabHandle; BOOL res; TCHAR pathForFile[MAX_PATH]; DWORD i; WIN32_FILE_ATTRIBUTE_DATA dataOfFile; static LPCTSTR arrayOfFilesName[] = {TEXT("boot.cab"), TEXT("backup.cab")}; PSTR ansiString; BOOL validUninstall = TRUE; ULARGE_INTEGER AmountOfSpaceForDelFiles; ULARGE_INTEGER AmountOfSpaceForBackupFiles; INFCONTEXT ic; ULARGE_INTEGER tempLargeInteger; TCHAR keyPath[MEMDB_MAX]; DWORD value; GROWBUFFER appList = GROWBUF_INIT; GROWBUFFER appMultiSz = GROWBUF_INIT; PINSTALLEDAPPW installedApp; UINT count; ULONGLONG *ullPtr; BYTE * backupImageInfoPtr = NULL; UINT sizeOfbackupImageInfo; BACKUPIMAGEINFO backupImageInfo; FILEINTEGRITYINFO fileIntegrityInfo[BACKUP_FILE_NUMBER]; WCHAR fileNameOfFileIntegrityInfo[ARRAYSIZE(fileIntegrityInfo)][MAX_PATH]; BACKUPIMAGEINFO testbackupImageInfo; DRIVEINFO drivesInfo[MAX_DRIVE_NUMBER]; WCHAR * FileSystemName = NULL; WCHAR * VolumeNTPath = NULL; BOOL unmapUser; if (Request == REQUEST_QUERYTICKS) { return 50; } if (Request != REQUEST_RUN) { return 0; } if (!g_ConfigOptions.EnableBackup) { DEBUGMSG ((DBG_ERROR, "Backup is not enabled")); return ERROR_SUCCESS; } ELSE_DEBUGMSG ((DBG_FILEMIG, "Backup is enabled")); if(!g_ConfigOptions.PathForBackup) { DEBUGMSG ((DBG_ERROR, "Path For Backup does not specified")); return ERROR_INVALID_PARAMETER; } ELSE_DEBUGMSG ((DBG_FILEMIG, "Path For Backup is %s", g_ConfigOptions.PathForBackup)); FileSystemName = MemAlloc(g_hHeap, 0, MAX_DRIVE_NUMBER * MAX_PATH); if(!FileSystemName){ DEBUGMSG ((DBG_ERROR, "WriteBackupInfo: Can't allocate memory for FileSystemName")); return ERROR_NOT_ENOUGH_MEMORY; } VolumeNTPath = MemAlloc(g_hHeap, 0, MAX_DRIVE_NUMBER * MAX_PATH); if(!VolumeNTPath){ MemFree(g_hHeap, 0, FileSystemName); DEBUGMSG ((DBG_ERROR, "WriteBackupInfo: Can't allocate memory for VolumeNTPath")); return ERROR_NOT_ENOUGH_MEMORY; } // //Init BACKUPIMAGEINFO structure // for(i = 0; i < ARRAYSIZE(drivesInfo); i++){ drivesInfo[i].FileSystemName = &FileSystemName[i * MAX_PATH]; drivesInfo[i].VolumeNTPath = &VolumeNTPath[i * MAX_PATH]; } backupImageInfo.NumberOfDrives = 0; backupImageInfo.DrivesInfo = drivesInfo; backupImageInfo.NumberOfFiles = BACKUP_FILE_NUMBER; backupImageInfo.FilesInfo = fileIntegrityInfo; for(i = 0; i < ARRAYSIZE(fileIntegrityInfo); i++){ fileIntegrityInfo[i].FileName = fileNameOfFileIntegrityInfo[i]; } // // Complete the backup image by writing a list of files that are new with // the upgraded OS, or moved in the upgrade process. // AmountOfSpaceForDelFiles.QuadPart = 0; ansiTempDir = CreateDbcs (g_TempDir); WriteBackupFilesA (FALSE, ansiTempDir, NULL, NULL, 0, 0, &AmountOfSpaceForDelFiles, NULL); DestroyDbcs (ansiTempDir); DEBUGMSG((DBG_FILEMIG, "AmountOfSpaceForDelFiles is %d MB", (UINT)AmountOfSpaceForDelFiles.QuadPart>>20)); AmountOfSpaceForBackupFiles.QuadPart = 0; value = 0; MemDbBuildKey (keyPath, MEMDB_CATEGORY_STATE, MEMDB_ITEM_ROLLBACK_SPACE, NULL, NULL); if(MemDbGetValue (keyPath, &value)){ AmountOfSpaceForBackupFiles.QuadPart = value; AmountOfSpaceForBackupFiles.QuadPart <<= 20; } ELSE_DEBUGMSG((DBG_FILEMIG, "Can't read MEMDB_ITEM_ROLLBACK_SPACE")); DEBUGMSG((DBG_FILEMIG, "AmountOfSpaceForBackupFiles is %d MB", (UINT)AmountOfSpaceForBackupFiles.QuadPart>>20)); if(AmountOfSpaceForBackupFiles.QuadPart > AmountOfSpaceForDelFiles.QuadPart){ backupImageInfo.BackupFilesDiskSpace.QuadPart = AmountOfSpaceForBackupFiles.QuadPart - AmountOfSpaceForDelFiles.QuadPart; } else{ backupImageInfo.BackupFilesDiskSpace.QuadPart = 0; } // // Prepare boot.cab. Some errors are ignored, such as the inability to // create a backup dir, or set its attributes. If these cases were to // occur, subsequent errors are reported. // // As serious errors are encountered, we log them and turn off the // Add/Remove Programs option. We continue so we can capture all of // the possible problems. // wsprintf (src, TEXT("%s$win_nt$.~bt"), g_BootDrivePath); if (!CreateDirectory (g_ConfigOptions.PathForBackup, NULL)) { if (GetLastError() != ERROR_ALREADY_EXISTS) { LOG ((LOG_ERROR, "WriteBackupInfo: Can't create %s directory", g_ConfigOptions.PathForBackup)); } } res = SetFileAttributes (g_ConfigOptions.PathForBackup, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); if(!res) { DEBUGMSG ((DBG_ERROR, "Can't set attributes to %s directory", g_ConfigOptions.PathForBackup)); } key = OpenRegKeyStr (S_REGKEY_WIN_SETUP); if (key != NULL) { if(ERROR_SUCCESS == RegSetValueEx ( key, S_REG_KEY_UNDO_PATH, 0, REG_SZ, (PBYTE)g_ConfigOptions.PathForBackup, SizeOfString (g_ConfigOptions.PathForBackup))){ res = TRUE; } else { res = FALSE; } CloseRegKey (key); } else { res = FALSE; } if (!res) { LOG ((LOG_ERROR, "WriteBackupInfo:Can't set %s value to %s key in registry, uninstall will be disabled", S_REG_KEY_UNDO_PATH, S_REGKEY_WIN_SETUP)); validUninstall = FALSE; } if(!pClearPasswordsFromWinntSif(src)){ DEBUGMSG ((DBG_ERROR, "WriteBackupInfo:pClearPasswordsFromWinntSif failed")); } if (validUninstall) { PCTSTR pathBootCab = JoinPaths (g_ConfigOptions.PathForBackup, TEXT("boot.cab")); DeleteFile(pathBootCab); FreePathString(pathBootCab); cabHandle = CabCreateCabinet (g_ConfigOptions.PathForBackup, TEXT("boot.cab"), TEXT("dontcare"), 0); } else { cabHandle = NULL; } backupImageInfo.BootFilesDiskSpace.QuadPart = 0; backupImageInfo.UndoFilesDiskSpace.QuadPart = 0; if (!cabHandle) { LOG ((LOG_ERROR, "WriteBackupInfo:Can't create CAB file for ~bt in %s, uninstall will be disabled", g_ConfigOptions.PathForBackup)); validUninstall = FALSE; } else { if (EnumFirstFileInTree (&treeEnum, src, NULL, FALSE)) { do { if (treeEnum.Directory) { continue; } tempLargeInteger.LowPart = treeEnum.FindData->nFileSizeLow; tempLargeInteger.HighPart = treeEnum.FindData->nFileSizeHigh; backupImageInfo.BootFilesDiskSpace.QuadPart += tempLargeInteger.QuadPart; if (!CabAddFileToCabinet (cabHandle, treeEnum.FullPath, treeEnum.FullPath)) { LOG ((LOG_ERROR, "WriteBackupInfo:Can't add %s to boot.cab, uninstall will be disabled", src)); validUninstall = FALSE; } } while (EnumNextFileInTree (&treeEnum)); } wsprintf (src, TEXT("%s\\uninstall\\moved.txt"), g_TempDir); wsprintf (cabPath, TEXT("%s\\moved.txt"), g_ConfigOptions.PathForBackup); if (!CabAddFileToCabinet (cabHandle, src, cabPath)) { LOG ((LOG_ERROR, "WriteBackupInfo:Can't add %s to boot.cab, uninstall will be disabled", src)); validUninstall = FALSE; } backupImageInfo.UndoFilesDiskSpace.QuadPart += pGetFileSize(src); wsprintf (src, TEXT("%s\\uninstall\\delfiles.txt"), g_TempDir); wsprintf (cabPath, TEXT("%s\\delfiles.txt"), g_ConfigOptions.PathForBackup); if (!CabAddFileToCabinet (cabHandle, src, cabPath)) { LOG ((LOG_ERROR, "WriteBackupInfo:Can't add %s to boot.cab, uninstall will be disabled", src)); validUninstall = FALSE; } backupImageInfo.UndoFilesDiskSpace.QuadPart += pGetFileSize(src); wsprintf (src, TEXT("%s\\uninstall\\deldirs.txt"), g_TempDir); wsprintf (cabPath, TEXT("%s\\deldirs.txt"), g_ConfigOptions.PathForBackup); if (!CabAddFileToCabinet (cabHandle, src, cabPath)) { LOG ((LOG_ERROR, "WriteBackupInfo:Can't add %s to boot.cab, uninstall will be disabled", src)); validUninstall = FALSE; } backupImageInfo.UndoFilesDiskSpace.QuadPart += pGetFileSize(src); wsprintf (src, TEXT("%s\\uninstall\\mkdirs.txt"), g_TempDir); wsprintf (cabPath, TEXT("%s\\mkdirs.txt"), g_ConfigOptions.PathForBackup); if (!CabAddFileToCabinet (cabHandle, src, cabPath)) { LOG ((LOG_ERROR, "WriteBackupInfo:Can't add %s to boot.cab, uninstall will be disabled", src)); validUninstall = FALSE; } backupImageInfo.UndoFilesDiskSpace.QuadPart += pGetFileSize(src); //wsprintf (src, TEXT("%s\\uninstall\\boot.ini"), g_TempDir); //wsprintf (cabPath, TEXT("%sboot.ini"), g_BootDrivePath); //if (!CabAddFileToCabinet (cabHandle, src, cabPath)) { // DEBUGMSG ((DBG_ERROR, "Can't add %s to boot.cab", src)); // validUninstall = FALSE; //} //backupImageInfo.BootFilesDiskSpace.QuadPart += pGetFileSize(src); wsprintf (src, TEXT("%s\\uninstall\\$ldr$"), g_TempDir); wsprintf (cabPath, TEXT("%s$ldr$"), g_BootDrivePath); if (!CabAddFileToCabinet (cabHandle, src, cabPath)) { LOG ((LOG_ERROR, "WriteBackupInfo:Can't add %s to boot.cab, uninstall will be disabled", src)); validUninstall = FALSE; } backupImageInfo.BootFilesDiskSpace.QuadPart += pGetFileSize(src); wsprintf (src, TEXT("%s\\system32\\autochk.exe"), g_WinDir); wsprintf (cabPath, TEXT("%s$win_nt$.~bt\\i386\\autochk.exe"), g_BootDrivePath); if (!CabAddFileToCabinet (cabHandle, src, cabPath)) { // // This is only a warning, because text mode will prompt for the // CD when autochk.exe can't be found. // LOG ((LOG_WARNING, "WriteBackupInfo:Can't add %s to boot.cab, uninstall will be disabled", src)); } backupImageInfo.BootFilesDiskSpace.QuadPart += pGetFileSize(src); backupImageInfo.BootFilesDiskSpace.QuadPart += BOOT_FILES_ADDITIONAL_PADDING; backupImageInfo.UndoFilesDiskSpace.QuadPart += backupImageInfo.BootFilesDiskSpace.QuadPart + UNDO_FILES_ADDITIONAL_PADDING; if (!CabFlushAndCloseCabinet (cabHandle)) { LOG ((LOG_ERROR, "WriteBackupInfo:Can't write CAB file for ~bt, uninstall will be disabled")); validUninstall = FALSE; } } // // Create and write undo integrity info to registry // if (validUninstall) { backupImageInfo.FilesInfo[0].IsCab = TRUE; GetIntegrityInfo(TEXT("boot.cab"), g_ConfigOptions.PathForBackup, &backupImageInfo.FilesInfo[0]); backupImageInfo.FilesInfo[1].IsCab = TRUE; GetIntegrityInfo(TEXT("backup.cab"), g_ConfigOptions.PathForBackup, &backupImageInfo.FilesInfo[1]); if(GetUndoDrivesInfo(drivesInfo, &backupImageInfo.NumberOfDrives, g_BootDrivePath[0], g_WinDir[0], g_ConfigOptions.PathForBackup[0])){ if(GetDisksInfo(&backupImageInfo.DisksInfo, &backupImageInfo.NumberOfDisks)){ if(Persist_Success == PERSIST_STORE(&backupImageInfoPtr, &sizeOfbackupImageInfo, BACKUPIMAGEINFO, BACKUPIMAGEINFO_VERSION, &backupImageInfo)){ key = OpenRegKeyStr (S_REGKEY_WIN_SETUP); if (key) { RegSetValueEx ( key, S_REG_KEY_UNDO_INTEGRITY, 0, REG_BINARY, (PBYTE)backupImageInfoPtr, sizeOfbackupImageInfo ); DEBUGMSG(( DBG_VERBOSE, "Boot files size is %d KB, Undo file size is %d KB, Backup files size is %d KB", (DWORD)backupImageInfo.BootFilesDiskSpace.QuadPart>>10, (DWORD)backupImageInfo.UndoFilesDiskSpace.QuadPart>>10, (DWORD)backupImageInfo.BackupFilesDiskSpace.QuadPart>>10)); CloseRegKey (key); } else { LOG((LOG_ERROR, "WriteBackupInfo:Could not write to registry, uninstall will be disabled")); validUninstall = FALSE; } PERSIST_RELEASE_BUFFER(backupImageInfoPtr); } else{ LOG((LOG_ERROR, "WriteBackupInfo:Could not marshall BACKUPIMAGEINFO structure, GetLastError() == %d, uninstall will be disabled", GetLastError())); validUninstall = FALSE; } } else { LOG((LOG_ERROR, "WriteBackupInfo:GetDisksInfo failed, uninstall will be disabled")); validUninstall = FALSE; } } else{ LOG ((LOG_ERROR, "WriteBackupInfo:GetUndoDrivesInfo failed, uninstall will be disabled")); validUninstall = FALSE; } if(backupImageInfo.DisksInfo){ FreeDisksInfo(backupImageInfo.DisksInfo, backupImageInfo.NumberOfDisks); } } // // Establish Add/Remove Programs entry // if (validUninstall) { key = CreateRegKeyStr (TEXT("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall")); if (key) { subKey = CreateRegKey (key, TEXT("Windows")); CloseRegKey (key); if (subKey) { msg = GetStringResource (MSG_UNINSTALL_DISPLAY_STRING); RegSetValueEx (subKey, TEXT("DisplayName"), 0, REG_SZ, (PBYTE) msg, SizeOfString (msg)); FreeStringResource (msg); msg = TEXT("%SYSTEMROOT%\\system32\\osuninst.exe"); rc = RegSetValueEx (subKey, TEXT("UninstallString"), 0, REG_EXPAND_SZ, (PBYTE) msg, SizeOfString (msg)); SetLastError (rc); rc = RegSetValueEx (subKey, TEXT("DisplayIcon"), 0, REG_EXPAND_SZ, (PBYTE) msg, SizeOfString (msg)); SetLastError (rc); rc = RegSetValueEx ( subKey, TEXT("InstallLocation"), 0, REG_EXPAND_SZ, (PBYTE) g_ConfigOptions.PathForBackup, SizeOfString (g_ConfigOptions.PathForBackup)); SetLastError (rc); DEBUGMSG_IF ((rc != ERROR_SUCCESS, DBG_ERROR, "Can't create Add/Remove Programs value")); CloseRegKey (subKey); } else { LOG ((LOG_ERROR, "Can't create Add/Remove Programs subkey")); validUninstall = FALSE; } } else { validUninstall = FALSE; LOG ((LOG_ERROR, "Can't create Add/Remove Programs subkey")); } } if(VolumeNTPath){ MemFree(g_hHeap, 0, VolumeNTPath); } if(FileSystemName){ MemFree(g_hHeap, 0, FileSystemName); } // // Save progress text to the registry // if (validUninstall) { key = CreateRegKeyStr (S_WIN9XUPG_KEY); if (key) { msg = GetStringResource (MSG_OLEREG); RegSetValueEx (key, S_UNINSTALL_DISP_STR, 0, REG_SZ, (PBYTE) msg, SizeOfString (msg)); FreeStringResource (msg); CloseRegKey (key); } } // // Write list of installed apps to the registry // if (validUninstall) { CoInitialize (NULL); // // Map in the default user's hive. Use this for HKCU. // unmapUser = pMapHiveOfUserDoingTheUpgrade(); // // Get the installed apps. // installedApp = GetInstalledAppsW (&appList, &count); // // Unmap the hive. // if (unmapUser) { pUnmapHiveOfUserDoingTheUpgrade(); } // // Record the apps in the registry. // if (installedApp) { for (u = 0 ; u < count ; u++) { DEBUGMSG (( DBG_FILEMIG, "App previously installed: %ws (%I64X)", installedApp->DisplayName, installedApp->Checksum )); GrowBufCopyStringW (&appMultiSz, installedApp->DisplayName); ullPtr = (ULONGLONG *) GrowBuffer (&appMultiSz, sizeof (ULONGLONG)); *ullPtr = installedApp->Checksum; installedApp++; } GrowBufCopyStringW (&appMultiSz, L""); key = OpenRegKeyStr (S_REGKEY_WIN_SETUP); if (key) { rc = RegSetValueEx ( key, S_REG_KEY_UNDO_APP_LIST, 0, REG_BINARY, appMultiSz.Buf, appMultiSz.End ); if (rc != ERROR_SUCCESS) { SetLastError (rc); DEBUGMSG ((DBG_ERROR, "Can't write list of installed apps to registry value")); } CloseRegKey (key); } ELSE_DEBUGMSG ((DBG_ERROR, "Can't write list of installed apps to registry")); } ELSE_DEBUGMSG ((DBG_ERROR, "Can't get list of installed apps.")); } FreeGrowBuffer (&appList); FreeGrowBuffer (&appMultiSz); DEBUGMSG_IF ((!validUninstall, DBG_ERROR, "Uninstall is not available because of a previous error")); return ERROR_SUCCESS; } DWORD DisableFiles ( DWORD Request ) /*++ Routine Description: DisableFiles runs code to ensure a Win9x file is removed from processing, usually because it is suspected of causing problems. This function renames all files marked for OPERATION_FILE_DISABLED adding a .disabled at the end. Arguments: Request - Specifies the progress bar request, which is either REQUEST_QUERYTICKS or REQUEST_RUN. Return Value: The number of ticks (REQUEST_QUERYTICKS) or the status code (REQUEST_RUN). --*/ { FILEOP_ENUM e; DWORD attr; PCTSTR disableName; PCTSTR newLocation; GROWLIST disableList = GROWLIST_INIT; PCTSTR srcPath; UINT count; UINT u; if (Request == REQUEST_QUERYTICKS) { return 50; } if (Request != REQUEST_RUN) { return 0; } // // Enumerate each file in OPERATION_FILE_DISABLED and put it in a grow // list, because we will then modify the operations so that uninstall // works properly. // if (EnumFirstPathInOperation (&e, OPERATION_FILE_DISABLED)) { do { GrowListAppendString (&disableList, e.Path); } while (EnumNextPathInOperation (&e)); } // // Now process each file // count = GrowListGetSize (&disableList); for (u = 0 ; u < count ; u++) { srcPath = GrowListGetString (&disableList, u); newLocation = GetPathStringOnNt (srcPath); attr = GetLongPathAttributes (newLocation); if (attr != INVALID_ATTRIBUTES) { SetLongPathAttributes (newLocation, FILE_ATTRIBUTE_NORMAL); disableName = JoinText (newLocation, TEXT(".disabled")); RemoveOperationsFromPath (srcPath, ALL_MOVE_OPERATIONS|ALL_DELETE_OPERATIONS); MarkFileForMoveByNt (srcPath, disableName); if (!OurMoveFile (newLocation, disableName)) { if (GetLastError() == ERROR_ALREADY_EXISTS) { // // Restart case -- we already moved this file // SetLongPathAttributes (newLocation, FILE_ATTRIBUTE_NORMAL); DeleteLongPath (newLocation); } ELSE_DEBUGMSG ((DBG_ERROR, "Cannot rename %s to %s", newLocation, disableName)); } FreeText (disableName); SetLongPathAttributes (newLocation, attr); } FreePathString (newLocation); } FreeGrowList (&disableList); return ERROR_SUCCESS; } VOID pUninstallStartMenuCleanupPreparation ( VOID ) { INFSTRUCT is = INITINFSTRUCT_POOLHANDLE; INFSTRUCT isLinks = INITINFSTRUCT_POOLHANDLE; PCTSTR temp; BOOL isCommonGroup; TCHAR itemFullPath[MAX_PATH]; PCTSTR itemGroupPtr; TCHAR itemGroupPath[MAX_PATH]; HINF InfSysSetupHandle; PINFSECTION sectionWkstaMigInf = NULL; PINFSECTION sectionUserMigInf = NULL; InfSysSetupHandle = InfOpenInfFile (TEXT("syssetup.inf")); if(!InfSysSetupHandle){ LOG((LOG_ERROR,"Can't open syssetup.inf for UninstallStartMenuCleanupPreparation.")); MYASSERT(FALSE); return; } // //[StartMenu.StartMenuItems] // if (InfFindFirstLine (InfSysSetupHandle, TEXT("StartMenu.StartMenuItems"), NULL, &is)) { do { StringCopy(itemFullPath, TEXT("7520")); temp = InfGetStringField (&isLinks, 0); if (!temp || *temp == 0) { continue; } StringCat(AppendWack(itemFullPath), temp); StringCat(itemFullPath, TEXT(".lnk")); GrowListAppendString (&g_StartMenuItemsForCleanUpCommon, itemFullPath); GrowListAppendString (&g_StartMenuItemsForCleanUpPrivate, itemFullPath); DEBUGMSG ((DBG_VERBOSE,"UninstallStartMenuCleanupPreparation: %s", itemFullPath)); } while (InfFindNextLine (&is)); } // //[StartMenuGroups] // if (InfFindFirstLine (InfSysSetupHandle, TEXT("StartMenuGroups"), NULL, &is)) { do { itemGroupPtr = InfGetStringField (&is, 1); if (!itemGroupPtr || *itemGroupPtr == 0) { continue; } temp = InfGetStringField (&is, 2); if (!temp || *temp == 0) { continue; } if('0' == temp[0]){ isCommonGroup = TRUE; } else{ isCommonGroup = FALSE; } temp = InfGetStringField (&is, 0); if (!temp || *temp == 0) { continue; } StringCopy(itemGroupPath, TEXT("7517")); StringCat(AppendWack(itemGroupPath), itemGroupPtr); if (InfFindFirstLine (InfSysSetupHandle, temp, NULL, &isLinks)) { do { StringCopy(itemFullPath, itemGroupPath); temp = InfGetStringField (&isLinks, 0); if (!temp || *temp == 0) { continue; } StringCat(AppendWack(itemFullPath), temp); StringCat(itemFullPath, TEXT(".lnk")); GrowListAppendString (&g_StartMenuItemsForCleanUpCommon, itemFullPath); if(!isCommonGroup){ GrowListAppendString (&g_StartMenuItemsForCleanUpPrivate, itemFullPath); } DEBUGMSG ((DBG_VERBOSE,"UninstallStartMenuCleanupPreparation: %s", itemFullPath)); } while (InfFindNextLine (&isLinks)); } } while (InfFindNextLine (&is)); } InfCleanUpInfStruct (&is); InfCleanUpInfStruct (&isLinks); InfCloseInfFile (InfSysSetupHandle); } DWORD UninstallStartMenuCleanupPreparation( DWORD Request ) /*++ Routine Description: UninstallStartMenuCleanupPreparation mark files from Start Menu sections from syssetup.inf to clean up. Arguments: Request - Specifies the request being made by the progress bar manager Return Value: If Request is REQUEST_QUERYTICKS, the return value indicates the number of ticks. Otherwise, the return value is a Win32 result code. --*/ { if (Request == REQUEST_QUERYTICKS) { return 3; } if (Request != REQUEST_RUN) { return 0; } pUninstallStartMenuCleanupPreparation(); return ERROR_SUCCESS; }