/*++ Copyright (c) 2000 Microsoft Corporation Module Name: uninst.c Abstract: Implements the code to launch the uninstall. Author: Jim Schmidt (jimschm) 28-Nov-2000 Revision History: --*/ #include "pch.h" #include "undop.h" #include "resource.h" // // Types // typedef enum { BACKUP_DOESNT_EXIST, BACKUP_IN_PROGRESS, BACKUP_SKIPPED_BY_USER, BACKUP_COMPLETE } JOURNALSTATUS; // // Global Variables // DRIVELETTERS g_DriveLetters; TCHAR g_BootDrv = UNKNOWN_DRIVE; #define WMX_STOP (WM_APP+96) // // Code // VOID pAddSifEntry ( IN HINF Inf, IN PINFSECTION Section, IN PCTSTR Key, IN PCTSTR Data ) { PTSTR quotedData; quotedData = AllocText (CharCount (Data) + 2); wsprintf (quotedData, TEXT("\"%s\""), Data); AddInfLineToTable (Inf, Section, Key, quotedData, 0); FreeText (quotedData); } BOOL pEnablePrivilege( IN PTSTR PrivilegeName, IN BOOL Enable ) { HANDLE Token; BOOL b; TOKEN_PRIVILEGES NewPrivileges; LUID Luid; if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) { return(FALSE); } if(!LookupPrivilegeValue(NULL,PrivilegeName,&Luid)) { CloseHandle(Token); return(FALSE); } NewPrivileges.PrivilegeCount = 1; NewPrivileges.Privileges[0].Luid = Luid; NewPrivileges.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0; b = AdjustTokenPrivileges( Token, FALSE, &NewPrivileges, 0, NULL, NULL ); CloseHandle(Token); return(b); } VOID pWriteDrvLtrFiles ( VOID ) { BOOL rf = TRUE; DWORD index; HANDLE signatureFile; TCHAR signatureFilePath[MAX_PATH * 2]; DWORD signatureFilePathLength; DWORD bytesWritten; InitializeDriveLetterStructure (&g_DriveLetters); // // Hard drive information is actually written to a special signature file // on the root directory of each fixed hard drive. The information is // nothing special -- just the drive number (0 = A, etc.) // lstrcpy (signatureFilePath,TEXT("*:\\$DRVLTR$.~_~")); signatureFilePathLength = lstrlen(signatureFilePath); for (index = 0; index < NUMDRIVELETTERS; index++) { if (g_DriveLetters.ExistsOnSystem[index] && g_DriveLetters.Type[index] == DRIVE_FIXED) { *signatureFilePath = (TCHAR) index + TEXT('A'); signatureFile = CreateFile( signatureFilePath, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL ); if (signatureFile != INVALID_HANDLE_VALUE) { WriteFile (signatureFile, &index, sizeof(DWORD), &bytesWritten, NULL); CloseHandle (signatureFile); } } } } VOID pAppendSystemRestore ( IN HANDLE FileHandle, IN PCWSTR SystemGuid ) { INT index; WCHAR outputStr[MAX_PATH]; DWORD dontCare; DEBUGMSG ((DBG_VERBOSE, "Enumerating sr drives")); for (index = 0; index < NUMDRIVELETTERS; index++) { if (g_DriveLetters.ExistsOnSystem[index] && g_DriveLetters.Type[index] == DRIVE_FIXED ) { wsprintfW ( outputStr, L"%c:\\System Volume Information\\_restore%s\r\n", (WCHAR) index + L'A', SystemGuid ); DEBUGMSGW ((DBG_VERBOSE, "Adding %s to deldirs.txt file", outputStr)); SetFilePointer (FileHandle, 0, NULL, FILE_END); WriteFile (FileHandle, outputStr, ByteCountW (outputStr), &dontCare, NULL); } } } VOID pAppendSystemRestoreToDelDirs ( IN PCTSTR DelDirsTxtPath ) { HANDLE delDirsFile = INVALID_HANDLE_VALUE; HKEY key = NULL; PCWSTR data = NULL; __try { key = OpenRegKeyStr (TEXT("HKLM\\System\\CurrentControlSet\\Services\\sr\\Parameters")); if (!key) { DEBUGMSG ((DBG_ERROR, "Can't open SR Parameters key")); __leave; } data = GetRegValueStringW (key, L"MachineGuid"); if (!data) { DEBUGMSG ((DBG_ERROR, "Can't get SR MachineGuid")); __leave; } delDirsFile = CreateFile ( DelDirsTxtPath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (delDirsFile == INVALID_HANDLE_VALUE) { DEBUGMSG ((DBG_ERROR, "Can't open %s for writing", DelDirsTxtPath)); __leave; } pAppendSystemRestore (delDirsFile, data); } __finally { if (delDirsFile != INVALID_HANDLE_VALUE) { CloseHandle (delDirsFile); } if (data) { FreeMem (data); } if (key) { CloseRegKey (key); } } } BOOL WINAPI pCabNotificationSeekForDrive( IN PCTSTR FileName ) { if(!FileName){ MYASSERT(FALSE); return FALSE; } MYASSERT(UNKNOWN_DRIVE == g_BootDrv); g_BootDrv = (TCHAR)FileName[0]; return -1; } BOOL GetBootDrive( IN PCTSTR BackUpPath, IN PCTSTR Path ) { OCABHANDLE cabHandle; if(UNKNOWN_DRIVE != g_BootDrv){ return TRUE; } cabHandle = CabOpenCabinet (Path); if (cabHandle) { SetCurrentDirectory (BackUpPath); CabExtractAllFilesEx ( cabHandle, NULL, (PCABNOTIFICATIONW)pCabNotificationSeekForDrive); CabCloseCabinet (cabHandle); MYASSERT(UNKNOWN_DRIVE != g_BootDrv); } else { return FALSE; } return TRUE; } DWORD WINAPI pLongOperationUi ( PVOID DontCare ) { HWND h; RECT parentRect; MSG msg; TCHAR textBuf[256]; HKEY key; LONG rc; DWORD size; HFONT font; HDC hdc; LOGFONT lf; SIZE textSize; RECT rect; HWND parent; TCHAR title[128]; // // Get dialog title // if (!LoadString (g_hInst, IDS_TITLE, title, ARRAYSIZE(title))) { return 0; } // // Get display text // key = OpenRegKeyStr (S_WIN9XUPG_KEY); if (!key) { return 0; } size = sizeof (textBuf); rc = RegQueryValueEx (key, S_UNINSTALL_DISP_STR, NULL, NULL, (PBYTE) textBuf, &size); CloseRegKey (key); if (rc != ERROR_SUCCESS) { return 0; } // // Create font object and compute width/height // hdc = CreateDC (TEXT("DISPLAY"), NULL, NULL, NULL); ZeroMemory (&lf, sizeof (lf)); lf.lfHeight = -MulDiv (9, GetDeviceCaps (hdc, LOGPIXELSY), 72); StringCopy (lf.lfFaceName, TEXT("MS Shell Dlg")); font = CreateFontIndirect (&lf); SelectObject (hdc, font); GetTextExtentPoint32 (hdc, textBuf, TcharCount (textBuf), &textSize); DeleteDC (hdc); // // Compute window position // GetWindowRect (GetDesktopWindow(), &parentRect); rect.left = (parentRect.right - parentRect.left) / 2; rect.right = rect.left; rect.left -= textSize.cx * 2; rect.right += textSize.cx * 2; rect.top = (parentRect.bottom - parentRect.top) / 2; rect.bottom = rect.top; rect.top -= textSize.cy * 4; rect.bottom += textSize.cy * 4; parent = CreateWindow ( TEXT("STATIC"), title, WS_OVERLAPPED|WS_BORDER|WS_CAPTION|SS_WHITERECT, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, g_hInst, NULL ); // // Create static control // GetClientRect (parent, &rect); // // Create window & let it run until verify is done // h = CreateWindow ( TEXT("STATIC"), textBuf, WS_CHILD|SS_CENTER|SS_CENTERIMAGE, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, parent, NULL, g_hInst, NULL ); SendMessage (h, WM_SETFONT, (WPARAM) font, 0); ShowWindow (parent, SW_SHOW); ShowWindow (h, SW_SHOW); UpdateWindow (parent); while (GetMessage (&msg, NULL, 0, 0)) { if (msg.message == WMX_STOP) { DestroyWindow (parent); break; } TranslateMessage (&msg); DispatchMessage (&msg); } DeleteObject (font); return 0; } DWORD pStartLongOperationUi ( VOID ) { HANDLE h; DWORD threadId; h = CreateThread ( NULL, 0, pLongOperationUi, NULL, 0, &threadId ); if (!h) { return 0; } CloseHandle (h); return threadId; } VOID pKillLongOperationUi ( IN DWORD ThreadId ) { if (ThreadId) { PostThreadMessage (ThreadId, WMX_STOP, 0, 0); } } BOOL CheckCabForAllFilesAvailability( IN PCTSTR CabPath ) { OCABHANDLE cabHandle; BOOL result = FALSE; DWORD threadId; threadId = pStartLongOperationUi(); cabHandle = CabOpenCabinet (CabPath); if (cabHandle) { result = CabVerifyCabinet (cabHandle); CabCloseCabinet (cabHandle); } pKillLongOperationUi (threadId); return result; } BOOL DoUninstall ( VOID ) { HINF inf = INVALID_HANDLE_VALUE; PINFSECTION section; PCTSTR path = NULL; PCTSTR path2 = NULL; PCTSTR sifPath = NULL; TCHAR bootPath[4] = TEXT("?:\\"); BOOL error = TRUE; OCABHANDLE cabHandle; PINFLINE infLine; DWORD attribs; PCTSTR backUpPath; PTSTR updatedData; BOOL cantSave; TCHAR textModeBootIniEntry[] = TEXT("?:\\$win_nt$.~bt\\bootsect.dat"); TCHAR bootDirPath[MAX_PATH] = TEXT(""); HANDLE fileHandle; DWORD bytesWritten; JOURNALSTATUS journalStatus; BOOL result; __try { backUpPath = GetUndoDirPath(); SetCursor (LoadCursor (NULL, IDC_WAIT)); // // Retrieve directory path of backup directory // if(!backUpPath) { LOG ((LOG_WARNING, "Uninstall Cleanup: Failed to retrieve directory path")); __leave; } // // Recreate "backup.$$$" with correct content, if file was not in place. // path = JoinPaths (backUpPath, TEXT("backup.$$$")); if(0xffffffff == GetFileAttributes(path)){ MYASSERT(ERROR_FILE_NOT_FOUND == GetLastError()); fileHandle = CreateFile(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(INVALID_HANDLE_VALUE != fileHandle){ journalStatus = BACKUP_COMPLETE; result = WriteFile(fileHandle, &journalStatus, sizeof(journalStatus), &bytesWritten, NULL); CloseHandle(fileHandle); if(!result){ LOG ((LOG_WARNING, "Uninstall Cleanup: Failed to write to backup.$$$")); __leave; } } else { LOG ((LOG_WARNING, "Uninstall Cleanup: Failed to create file backup.$$$")); __leave; } } FreePathString (path); // // Write a drive signature file to each drive // pWriteDrvLtrFiles(); // // Prepare boot path, clean out existing $win_nt$.~bt // directory // path = JoinPaths (backUpPath, TEXT("boot.cab")); if(GetBootDrive(backUpPath, path)){ bootPath[0] = g_BootDrv; textModeBootIniEntry[0] = g_BootDrv; } else{ LOG ((LOG_WARNING, "Uninstall Cleanup: Unable to open %s", path)); __leave; } FreePathString (path); path = JoinPaths (bootPath, TEXT("$win_nt$.~bt")); RemoveCompleteDirectory (path); StringCopy(bootDirPath, path); FreePathString (path); path = NULL; // // Extract the boot files from the boot.cab // path = JoinPaths (backUpPath, TEXT("boot.cab")); cabHandle = CabOpenCabinet (path); if (cabHandle) { SetCurrentDirectory (backUpPath); CabExtractAllFiles (cabHandle, NULL); CabCloseCabinet (cabHandle); } else { LOG ((LOG_WARNING, "Uninstall Cleanup: Unable to open %s", path)); __leave; } // // Verify the CAB expanded properly // FreePathString (path); path = NULL; path = JoinPaths (bootPath, TEXT("$win_nt$.~bt")); if (!DoesFileExist (path)) { LOG ((LOG_ERROR, "Files did not expand properly or boot.cab is damaged")); __leave; } FreePathString (path); path = NULL; path = JoinPaths (backUpPath, S_ROLLBACK_DELFILES_TXT); if (!DoesFileExist (path)) { LOG ((LOG_ERROR, "delfiles.txt not found")); __leave; } FreePathString (path); path = NULL; path = JoinPaths (backUpPath, S_ROLLBACK_DELDIRS_TXT); if (!DoesFileExist (path)) { LOG ((LOG_ERROR, "deldirs.txt not found")); __leave; } FreePathString (path); path = NULL; path = JoinPaths (backUpPath, S_ROLLBACK_MKDIRS_TXT); if (!DoesFileExist (path)) { LOG ((LOG_ERROR, "mkdirs.txt not found")); __leave; } FreePathString (path); path = NULL; path = JoinPaths (backUpPath, S_ROLLBACK_MOVED_TXT); if (!DoesFileExist (path)) { LOG ((LOG_ERROR, "moved.txt not found")); __leave; } FreePathString (path); path = NULL; // // Modify winnt.sif so that text mode knows what to do // sifPath = JoinPaths (bootPath, TEXT("$win_nt$.~bt\\winnt.sif")); inf = OpenInfFile (sifPath); if (inf == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR, "Can't open %s", sifPath)); __leave; } section = FindInfSectionInTable (inf, TEXT("data")); if (section) { // // Write keys to winnt.sif... // if (!FindLineInInfSection (inf, section, TEXT("Rollback"))) { pAddSifEntry (inf, section, TEXT("Rollback"), TEXT("1")); } // // ...change %windir%\setup\uninstall\*.txt to %backuppath%\*.txt // // moved.txt infLine = FindLineInInfSection (inf, section, WINNT_D_ROLLBACK_MOVE); if (infLine) { DeleteLineInInfSection (inf, infLine); } path = JoinPaths (backUpPath, S_ROLLBACK_MOVED_TXT); pAddSifEntry (inf, section, WINNT_D_ROLLBACK_MOVE, path); FreePathString (path); path = NULL; // delfiles.txt infLine = FindLineInInfSection (inf, section, WINNT_D_ROLLBACK_DELETE); if (infLine) { DeleteLineInInfSection (inf, infLine); } path = JoinPaths (backUpPath, S_ROLLBACK_DELFILES_TXT); pAddSifEntry (inf, section, WINNT_D_ROLLBACK_DELETE, path); FreePathString (path); path = NULL; // deldirs.txt infLine = FindLineInInfSection (inf, section, WINNT_D_ROLLBACK_DELETE_DIR); if (infLine) { DeleteLineInInfSection (inf, infLine); } path = JoinPaths (backUpPath, S_ROLLBACK_DELDIRS_TXT); pAddSifEntry (inf, section, WINNT_D_ROLLBACK_DELETE_DIR, path); // // append System Restore directories to deldirs.txt // DEBUGMSG ((DBG_VERBOSE, "Appending SR dirs to %s", path)); pAppendSystemRestoreToDelDirs (path); FreePathString (path); path = NULL; // mkdirs.txt infLine = FindLineInInfSection (inf, section, S_ROLLBACK_MK_DIRS); if (infLine) { DeleteLineInInfSection (inf, infLine); } path = JoinPaths (backUpPath, S_ROLLBACK_MKDIRS_TXT); pAddSifEntry (inf, section, S_ROLLBACK_MK_DIRS, path); FreePathString (path); path = NULL; // // ...write changed winnt.sif file // if (!SaveInfFile (inf, sifPath)) { LOG ((LOG_ERROR, "Unable to update winnt.sif")); __leave; } // // clone txtsetup.sif // path = JoinPaths (bootPath, TEXT("$win_nt$.~bt\\txtsetup.sif")); path2 = JoinPaths (bootPath, TEXT("txtsetup.sif")); SetFileAttributes (path2, FILE_ATTRIBUTE_NORMAL); if (!CopyFile (path, path2, FALSE)) { LOG ((LOG_ERROR, "Can't copy %s to %s", path, path2)); __leave; } FreePathString (path); path = NULL; FreePathString (path2); path2 = NULL; } else { LOG ((LOG_ERROR, "[data] not found in winnt.sif")); } CloseInfFile (inf); FreePathString (sifPath); sifPath = NULL; // // Edit the current boot.ini // sifPath = JoinPaths (bootPath, TEXT("boot.ini")); inf = OpenInfFile (sifPath); if (inf == INVALID_HANDLE_VALUE) { LOG ((LOG_ERROR, "Can't open %s", sifPath)); __leave; } section = FindInfSectionInTable (inf, TEXT("Operating Systems")); if (!section) { LOG ((LOG_ERROR, "[Operating Systems] not found in boot.ini")); __leave; } // // Add /rollback to textmode entry // infLine = FindLineInInfSection (inf, section, textModeBootIniEntry); if (infLine) { if(!_tcsistr (infLine->Data, TEXT("/rollback"))){ updatedData = AllocText (TcharCount (infLine->Data) + 10); StringCopy (updatedData, infLine->Data); StringCat (updatedData, TEXT(" /rollback")); AddInfLineToTable (inf, section, infLine->Key, updatedData, infLine->LineFlags); DeleteLineInInfSection (inf, infLine); FreeText (updatedData); } } else { updatedData = AllocText (256 + sizeof (TEXT("\" /rollback\"")) / sizeof (TCHAR)); StringCopy (updatedData, TEXT("\"")); if (!LoadString (g_hInst, IDS_TITLE, GetEndOfString (updatedData), 256)) { DEBUGMSG ((DBG_ERROR, "Can't load boot.ini text")); __leave; } StringCat (updatedData, TEXT("\" /rollback")); AddInfLineToTable (inf, section, textModeBootIniEntry, updatedData, 0); FreeText (updatedData); } // // Set timeout to zero and set default // section = FindInfSectionInTable (inf, TEXT("boot loader")); if (!section) { LOG ((LOG_ERROR, "[Boot Loader] not found in boot.ini")); __leave; } infLine = FindLineInInfSection (inf, section, TEXT("timeout")); if (infLine) { DeleteLineInInfSection (inf, infLine); } AddInfLineToTable (inf, section, TEXT("timeout"), TEXT("0"), 0); infLine = FindLineInInfSection (inf, section, TEXT("default")); if (infLine) { DeleteLineInInfSection (inf, infLine); } AddInfLineToTable (inf, section, TEXT("default"), textModeBootIniEntry, 0); // // Save changes // attribs = GetFileAttributes (sifPath); SetFileAttributes (sifPath, FILE_ATTRIBUTE_NORMAL); cantSave = (SaveInfFile (inf, sifPath) == FALSE); if (attribs != INVALID_ATTRIBUTES) { SetFileAttributes (sifPath, attribs); } if (cantSave) { LOG ((LOG_ERROR, "Unable to update boot.ini")); __leave; } // // Rollback environment is ready to go // error = FALSE; } __finally { FreePathString (path); FreePathString (path2); FreePathString (sifPath); if (backUpPath) { MemFree (g_hHeap, 0, backUpPath); } if (inf != INVALID_HANDLE_VALUE) { CloseInfFile (inf); } } // // Shutdown on success // if (!error) { pEnablePrivilege(SE_SHUTDOWN_NAME,TRUE); ExitWindowsEx (EWX_REBOOT, 0); } else { RemoveCompleteDirectory (bootDirPath); return FALSE; } return TRUE; }