windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/w95upg/migapp/masys32.c
2020-09-26 16:20:57 +08:00

916 lines
22 KiB
C

/*++
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;
}