3822 lines
99 KiB
C
3822 lines
99 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
migmain.c
|
|
|
|
Abstract:
|
|
|
|
MigMain is called from w95upgnt.dll, which is called from SYSSETUP.DLL.
|
|
It is the main migration loop on the NT side of setup. MigMain loops
|
|
through all users on the Win95 side of configuration, migrates their
|
|
registry, creates their account, and fixes up their profile folders.
|
|
Then MigMain migrates all machine-specific settings such as link changes
|
|
and file deletion.
|
|
|
|
Author:
|
|
|
|
Jim Schmidt (jimschm) 12-Jul-1996
|
|
|
|
Revision History:
|
|
|
|
marcw 26-Mar-1999 More boot16 fixes -- hide msdos7 dir, fix to msdos.sys
|
|
marcw 18-Mar-1999 fixes for boot16 environment in localized case.
|
|
jimschm 23-Sep-1998 Changes for fileops & shell.c
|
|
calinn 23-Sep-1998 Changes for memdb fixup
|
|
jimschm 02-Jul-1998 Support for progress bar
|
|
jimschm 11-Jun-1998 Support for dynamic user profile paths in memdb
|
|
jimschm 05-May-1998 Migration of Default User if unattend option is enabled
|
|
jimschm 27-Apr-1998 Added icon preservation
|
|
jimschm 18-Mar-1998 Added pProcessAutoLogon
|
|
calinn 19-Nov-1997 Added pEnable16Boot, will create a 16 bit environment boot entry in boot.ini
|
|
jimschm 01-Oct-1997 Localized Everyone group
|
|
jimschm 13-Sep-1997 Reg Quota for beta 1 workaround
|
|
jimschm 21-Jul-1997 Use of fileops for ConvertWin9xPath (to be moved later)
|
|
jimschm 28-May-1997 Cleaned up
|
|
marcw 21-Mar-1997 added Pathmapping
|
|
jimschm 04-Feb-1997 Moved code into usermig.c and wkstamig.c
|
|
jimschm 15-Jan-1997 Plug-in spec modifications (now in migdlls.c)
|
|
jimschm 03-Jan-1997 Added g_UserName
|
|
jimschm 18-Dec-1996 Extracted code from miginf
|
|
mikeco O4-Dec-1996 Enumerate/modify PIF and LNK files
|
|
jimschm 23-Oct-1996 Joined ProcessUserInfs and ApplyChanges
|
|
jimschm 02-Oct-1996 Added default user migration
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
#include "migmainp.h"
|
|
|
|
#include "fileops.h"
|
|
#include "quota.h"
|
|
|
|
#ifndef UNICODE
|
|
#error UNICODE required
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
|
|
BOOL g_NoReloadsAllowed = FALSE;
|
|
|
|
#define DBG_VALIDNTFILES "NtFiles"
|
|
|
|
#endif
|
|
|
|
typedef BOOL (*PROFILE_PATH_PROVIDER)(OUT PTSTR AccountName, OUT PTSTR PathProfile);
|
|
|
|
//
|
|
// Globals for migmain.lib
|
|
//
|
|
|
|
HKEY g_hKeyRoot95, g_hKeyRootNT;
|
|
PCTSTR g_DomainUserName;
|
|
PCTSTR g_Win9xUserName;
|
|
PCTSTR g_FixedUserName;
|
|
PVOID g_HiveTable;
|
|
POOLHANDLE g_HivePool;
|
|
PVOID g_NulSessionTable;
|
|
PCTSTR g_EveryoneStr;
|
|
PCTSTR g_AdministratorsGroupStr;
|
|
PCTSTR g_PowerUsersGroupStr;
|
|
PCTSTR g_DomainUsersGroupStr;
|
|
PCTSTR g_NoneGroupStr;
|
|
TCHAR g_IconBin[MAX_TCHAR_PATH];
|
|
TCHAR g_DefaultUserName[MAX_USER_NAME];
|
|
TCHAR g_ComputerName[MAX_SERVER_NAME];
|
|
BOOL g_BlowAwayTempShellFolders = FALSE;
|
|
UINT g_Boot16 = BOOT16_AUTOMATIC;
|
|
|
|
//
|
|
// Buffer for GetString's messages
|
|
//
|
|
|
|
static TCHAR g_MsgBuf[2048];
|
|
|
|
//
|
|
// Flag identifying if the SKU is Personal
|
|
//
|
|
BOOL g_PersonalSKU = FALSE;
|
|
|
|
//
|
|
// Prototypes for migmain.c only
|
|
//
|
|
|
|
BOOL
|
|
pSetWin9xUpgValue (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
pCountUsers (
|
|
OUT PDWORD TotalUsersPtr,
|
|
OUT PDWORD ActiveUsersPtr
|
|
);
|
|
|
|
BOOL
|
|
pMigrateUsers (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
pRaiseRegistryQuota (
|
|
PCTSTR Win9xSystemDatSpec
|
|
);
|
|
|
|
VOID
|
|
pEnable16Boot (
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
pProcessAutoLogon (
|
|
BOOL Final
|
|
);
|
|
|
|
VOID
|
|
pFixUpMemDb (
|
|
VOID
|
|
);
|
|
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
MigMain_Entry (
|
|
IN HINSTANCE hinstDLL,
|
|
IN DWORD dwReason,
|
|
IN PVOID lpv
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MigMain_Entry is called at DLL initialization time
|
|
|
|
Arguments:
|
|
|
|
hinstDLL - (OS-supplied) Instance handle for the DLL
|
|
dwReason - (OS-supplied) Type of initialization or termination
|
|
lpv - (OS-supplied) Unused
|
|
|
|
Return Value:
|
|
|
|
TRUE because LIB always initializes properly.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Size;
|
|
OSVERSIONINFOEX osviex;
|
|
|
|
switch (dwReason) {
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
if(!pSetupInitializeUtils()) {
|
|
return FALSE;
|
|
}
|
|
g_hKeyRoot95 = g_hKeyRootNT = NULL;
|
|
|
|
g_HivePool = PoolMemInitNamedPool ("Hive path pool");
|
|
|
|
if (!g_HivePool) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Alloc string tables
|
|
g_HiveTable = pSetupStringTableInitializeEx (MAX_TCHAR_PATH,0);
|
|
if (!g_HiveTable) {
|
|
return FALSE;
|
|
}
|
|
|
|
g_NulSessionTable = pSetupStringTableInitializeEx (sizeof(PCWSTR), 0);
|
|
if (!g_NulSessionTable) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine if upgrading to Personal SKU
|
|
//
|
|
osviex.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
|
|
if (!GetVersionEx ((LPOSVERSIONINFO)&osviex)) {
|
|
MYASSERT (FALSE);
|
|
}
|
|
if (osviex.wProductType == VER_NT_WORKSTATION &&
|
|
(osviex.wSuiteMask & VER_SUITE_PERSONAL)
|
|
) {
|
|
g_PersonalSKU = TRUE;
|
|
}
|
|
|
|
#if 0
|
|
if (g_PersonalSKU) {
|
|
g_EveryoneStr = GetStringResource (MSG_EVERYONE_GROUP);
|
|
g_AdministratorsGroupStr = GetStringResource (MSG_OWNERS_GROUP);
|
|
g_PowerUsersGroupStr = GetStringResource (MSG_POWER_USERS_GROUP);
|
|
g_DomainUsersGroupStr = GetStringResource (MSG_DOMAIN_USERS_GROUP);
|
|
g_NoneGroupStr = GetStringResource (MSG_NONE_GROUP);
|
|
} else {
|
|
#endif
|
|
g_EveryoneStr = GetStringResource (MSG_EVERYONE_GROUP);
|
|
g_AdministratorsGroupStr = GetStringResource (MSG_ADMINISTRATORS_GROUP);
|
|
g_PowerUsersGroupStr = GetStringResource (MSG_POWER_USERS_GROUP);
|
|
g_DomainUsersGroupStr = GetStringResource (MSG_DOMAIN_USERS_GROUP);
|
|
g_NoneGroupStr = GetStringResource (MSG_NONE_GROUP);
|
|
|
|
Size = ARRAYSIZE(g_ComputerName);
|
|
if (!GetComputerName (g_ComputerName, &Size)) {
|
|
g_ComputerName[0] = 0;
|
|
}
|
|
|
|
MYASSERT (
|
|
g_ComputerName[0] &&
|
|
g_EveryoneStr &&
|
|
g_AdministratorsGroupStr &&
|
|
g_PowerUsersGroupStr &&
|
|
g_DomainUsersGroupStr &&
|
|
g_NoneGroupStr
|
|
);
|
|
|
|
FindAccountInit();
|
|
|
|
break;
|
|
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
if (g_HiveTable) {
|
|
pSetupStringTableDestroy (g_HiveTable);
|
|
}
|
|
|
|
if (g_NulSessionTable) {
|
|
pSetupStringTableDestroy (g_NulSessionTable);
|
|
}
|
|
|
|
if (g_HivePool) {
|
|
PoolMemDestroyPool (g_HivePool);
|
|
}
|
|
|
|
FreeStringResource (g_EveryoneStr);
|
|
FreeStringResource (g_AdministratorsGroupStr);
|
|
FreeStringResource (g_DomainUsersGroupStr);
|
|
|
|
FindAccountTerminate();
|
|
pSetupUninitializeUtils();
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
BOOL
|
|
pValidateNtFiles (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pValidateNtFiles validates the list of files that are supposed to be installed
|
|
by NT. We check for the flag set on Win95 side and for each entry we check to
|
|
see if the file is realy present (e.g. was installed by NT). If not then we delete
|
|
the entry.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
Always returns TRUE
|
|
|
|
--*/
|
|
|
|
{
|
|
MEMDB_ENUMW enumFiles;
|
|
WCHAR key[MEMDB_MAX];
|
|
PWSTR fileName;
|
|
TREE_ENUMW enumTree;
|
|
DWORD value;
|
|
|
|
if (MemDbEnumFirstValue (
|
|
&enumFiles,
|
|
TEXT(MEMDB_CATEGORY_NT_FILESA)TEXT("\\*"),
|
|
MEMDB_ALL_SUBLEVELS,
|
|
MEMDB_ENDPOINTS_ONLY
|
|
)) {
|
|
do {
|
|
if (MemDbBuildKeyFromOffsetW (enumFiles.dwValue, key, 1, NULL)) {
|
|
|
|
fileName = JoinPaths (key, enumFiles.szName);
|
|
|
|
if (!DoesFileExistW (fileName)) {
|
|
|
|
MemDbSetValueEx (
|
|
MEMDB_CATEGORY_NT_FILES_BAD,
|
|
enumFiles.szName,
|
|
NULL,
|
|
NULL,
|
|
enumFiles.dwValue,
|
|
NULL
|
|
);
|
|
}
|
|
FreePathString (fileName);
|
|
}
|
|
ELSE_DEBUGMSG ((DBG_WHOOPS, "NT_FILES : cannot find installation directory."));
|
|
}
|
|
while (MemDbEnumNextValue (&enumFiles));
|
|
}
|
|
|
|
// now we have in MEMDB_CATEGORY_NT_FILES_BAD all files that should be installed
|
|
// by NT but are not. Now we are going to scan the file system and see if they are
|
|
// installed in a different place.
|
|
if (EnumFirstFileInTreeEx (&enumTree, g_WinDrive, TEXT("*.*"), FALSE, FALSE, FILE_ENUM_ALL_LEVELS)) {
|
|
do {
|
|
MemDbBuildKey (key, MEMDB_CATEGORY_NT_FILES_BAD, enumTree.Name, NULL, NULL);
|
|
if (MemDbGetValue (key, &value) && (value != 0)) {
|
|
MemDbSetValue (key, 0);
|
|
MemDbBuildKeyFromOffsetW (value, key, 1, NULL);
|
|
DEBUGMSG ((
|
|
DBG_VALIDNTFILES,
|
|
"%s listed to be installed in %s but installed in %s",
|
|
enumTree.Name,
|
|
key,
|
|
enumTree.FullPath));
|
|
}
|
|
} while (EnumNextFileInTree (&enumTree));
|
|
}
|
|
|
|
MemDbBuildKey (key, MEMDB_CATEGORY_NT_FILES_BAD, TEXT("*"), NULL, NULL);
|
|
if (MemDbEnumFirstValue (
|
|
&enumFiles,
|
|
key,
|
|
MEMDB_ALL_SUBLEVELS,
|
|
MEMDB_ENDPOINTS_ONLY
|
|
)) {
|
|
do {
|
|
if (enumFiles.dwValue) {
|
|
MemDbBuildKeyFromOffsetW (enumFiles.dwValue, key, 1, NULL);
|
|
DEBUGMSG ((
|
|
DBG_VALIDNTFILES,
|
|
"%s listed to be installed in %s but never installed",
|
|
enumFiles.szName,
|
|
key,
|
|
enumTree.FullPath));
|
|
}
|
|
}
|
|
while (MemDbEnumNextValue (&enumFiles));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
DWORD
|
|
pGetState (
|
|
IN PCTSTR Item
|
|
)
|
|
{
|
|
DWORD Value;
|
|
TCHAR Node[MEMDB_MAX];
|
|
|
|
MemDbBuildKey (Node, MEMDB_CATEGORY_STATE, Item, NULL, NULL);
|
|
|
|
if (MemDbGetValue (Node, &Value)) {
|
|
return Value;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
MigMain_Init (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MigMain_Init is called for initialization, and has a better opportunity
|
|
to fail than MigMain_Entry (which is called during DllMain).
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE if initialization succeeded, or FALSE if an error occurred.
|
|
Call GetLastError() for error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc; // Temp: return code
|
|
TCHAR RelocWinDir[MAX_TCHAR_PATH];
|
|
TCHAR SrcResBin[MAX_TCHAR_PATH];
|
|
TCHAR IconFile[MAX_TCHAR_PATH];
|
|
ICON_EXTRACT_CONTEXT Context;
|
|
WORD CodePage;
|
|
LCID Locale;
|
|
TCHAR Node[MEMDB_MAX];
|
|
DWORD minorVersion;
|
|
|
|
#ifdef DEBUG
|
|
HANDLE hFile;
|
|
HKEY DebugKey = NULL;
|
|
CHAR Buf[32];
|
|
DWORD Value;
|
|
#endif
|
|
|
|
#ifdef PRERELEASE
|
|
//
|
|
// !!! This is for internal use only !!! It is used for auto stress.
|
|
//
|
|
|
|
if (g_ConfigOptions.AutoStress) {
|
|
SuppressAllLogPopups (TRUE);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// Dev: load c:\windbg.reg if it exists
|
|
//
|
|
|
|
#ifdef DEBUG
|
|
__try {
|
|
|
|
TCHAR WindbgRegPath[MAX_PATH] = TEXT("c:\\windbg.reg");
|
|
//
|
|
// Intentional hard-coded path!! This is for dev purposes only.
|
|
//
|
|
|
|
WindbgRegPath[0] = g_System32Dir[0];
|
|
|
|
if (!DoesFileExist (WindbgRegPath)) {
|
|
StringCopy (WindbgRegPath, TEXT("d:\\tools\\windbg.reg"));
|
|
}
|
|
|
|
hFile = CreateFile (
|
|
WindbgRegPath,
|
|
GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE) {
|
|
|
|
CloseHandle (hFile);
|
|
|
|
rc = TrackedRegOpenKey (HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windbg"), &DebugKey);
|
|
if (rc == ERROR_SUCCESS) {
|
|
DEBUGMSG ((DBG_VERBOSE, "Not restoring windbg.reg because it was already restored."));
|
|
__leave;
|
|
}
|
|
|
|
else {
|
|
rc = TrackedRegCreateKey (HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Windbg"), &DebugKey);
|
|
if (rc == ERROR_SUCCESS) {
|
|
if (!pSetupEnablePrivilege (SE_BACKUP_NAME, TRUE)) {
|
|
DEBUGMSG ((DBG_ERROR, "Windbg restore: pSetupEnablePrivilege SE_BACKUP_NAME failed"));
|
|
//__leave;
|
|
}
|
|
|
|
if (!pSetupEnablePrivilege (SE_RESTORE_NAME, TRUE)) {
|
|
DEBUGMSG ((DBG_ERROR, "Windbg restore: pSetupEnablePrivilege SE_RESTORE_NAME failed"));
|
|
//__leave;
|
|
}
|
|
|
|
rc = RegRestoreKey (DebugKey, WindbgRegPath, 0);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
DEBUGMSG ((DBG_WARNING, "Unable to restore windbg.reg, gle=%u", rc));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__finally {
|
|
if (DebugKey) {
|
|
CloseRegKey (DebugKey);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If debug.inf has a line UserEnv=1, then add a registry key to debug userenv.dll
|
|
//
|
|
|
|
if (GetPrivateProfileStringA (
|
|
"Debug",
|
|
"UserEnv",
|
|
"0",
|
|
Buf,
|
|
sizeof (Buf) / sizeof (Buf[0]),
|
|
g_DebugInfPath
|
|
)
|
|
) {
|
|
if (atoi (Buf)) {
|
|
rc = TrackedRegCreateKey (
|
|
HKEY_LOCAL_MACHINE,
|
|
TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
|
|
&DebugKey
|
|
);
|
|
|
|
if (rc == ERROR_SUCCESS) {
|
|
Value = 0x00010002;
|
|
|
|
RegSetValueEx (
|
|
DebugKey,
|
|
TEXT("UserEnvDebugLevel"),
|
|
0,
|
|
REG_DWORD,
|
|
(PBYTE) &Value,
|
|
sizeof (DWORD)
|
|
);
|
|
|
|
CloseRegKey (DebugKey);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Initialize the registry APIs
|
|
//
|
|
// We look in memdb for the location of .default
|
|
//
|
|
|
|
if (!MemDbLoad (GetMemDbDat())) {
|
|
LOG ((LOG_ERROR, "MigMain Init: MemDbLoad could not load %s", GetMemDbDat()));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get platform name
|
|
//
|
|
|
|
if (!MemDbGetEndpointValueEx (
|
|
MEMDB_CATEGORY_STATE,
|
|
MEMDB_ITEM_PLATFORM_NAME,
|
|
NULL,
|
|
g_Win95Name
|
|
)) {
|
|
LOG ((LOG_ERROR, "Could not find product name for OS being upgraded."));
|
|
StringCopy (g_Win95Name, TEXT("Windows 95"));
|
|
}
|
|
|
|
// Try Paths\Windir first
|
|
if (!MemDbGetEndpointValueEx (
|
|
MEMDB_CATEGORY_PATHS,
|
|
MEMDB_ITEM_RELOC_WINDIR,
|
|
NULL,
|
|
RelocWinDir
|
|
)) {
|
|
LOG ((LOG_ERROR, "Could not find relocated windir!"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// if upgrading from Millennium, also map classes.dat
|
|
//
|
|
MemDbBuildKey (Node, MEMDB_CATEGORY_STATE, MEMDB_ITEM_MINOR_VERSION, NULL, NULL);
|
|
if (!MemDbGetValue (Node, &minorVersion)) {
|
|
LOG ((LOG_ERROR, "Could not get previous OS version information!"));
|
|
minorVersion = 0;
|
|
}
|
|
rc = Win95RegInit (RelocWinDir, minorVersion == 90);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetLastError (rc);
|
|
LOG ((LOG_ERROR, "Init Processor: Win95RegInit failed (path: %s)", RelocWinDir));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Update locale
|
|
//
|
|
|
|
CodePage = (WORD) pGetState (MEMDB_ITEM_CODE_PAGE);
|
|
Locale = (LCID) pGetState (MEMDB_ITEM_LOCALE);
|
|
|
|
SetGlobalCodePage (CodePage, Locale);
|
|
|
|
//
|
|
// Prepare path to system.dat, then raise registry quota if necessary
|
|
//
|
|
|
|
StringCopy (AppendWack (RelocWinDir), TEXT("system.dat"));
|
|
pRaiseRegistryQuota (RelocWinDir);
|
|
|
|
//
|
|
// Copy migisol.exe to migicons.exe
|
|
//
|
|
|
|
wsprintf (g_IconBin, TEXT("%s\\migicons.exe"), g_System32Dir);
|
|
wsprintf (SrcResBin, TEXT("%s\\migisol.exe"), g_TempDir);
|
|
|
|
if (!CopyFile (SrcResBin, g_IconBin, FALSE)) {
|
|
LOG ((LOG_ERROR, "Can't copy %s to %s", SrcResBin, g_IconBin));
|
|
}
|
|
|
|
else {
|
|
//
|
|
// Insert all icons from migicons.dat into g_IconBin
|
|
//
|
|
|
|
__try {
|
|
|
|
wsprintf (IconFile, TEXT("%s\\%s"), g_TempDir, S_MIGICONS_DAT);
|
|
|
|
if (!BeginIconExtraction (&Context, g_IconBin)) {
|
|
LOG ((LOG_ERROR, "Can't begin icon extraction"));
|
|
__leave;
|
|
}
|
|
|
|
if (!OpenIconImageFile (&Context, IconFile, FALSE)) {
|
|
LOG ((LOG_ERROR, "Can't open %s", IconFile));
|
|
__leave;
|
|
}
|
|
|
|
while (CopyIcon (&Context, NULL, NULL, 0)) {
|
|
// empty
|
|
}
|
|
|
|
}
|
|
__finally {
|
|
if (!EndIconExtraction (&Context)) {
|
|
DEBUGMSG ((DBG_WARNING, "EndIconExtraction failed"));
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Validate MEMDB_CATEGORY_NT_FILES category. We need to find if the files
|
|
// that were supposed to be installed by NT are really there.
|
|
if (g_ConfigOptions.CheckNtFiles) {
|
|
pValidateNtFiles ();
|
|
}
|
|
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
MigMain_Migrate (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MigMain_Migrate is the main migration function in NT GUI mode setup.
|
|
w95upgnt.dll calls this function, and it is here that users are migrated,
|
|
the local machine settings are migrated, and files are adjusted appropriately.
|
|
|
|
See the file progress.c for a list of functions that are called.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE if migration succeeded, or FALSE if an error occurred. Call
|
|
GetLastError() for error code.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
BOOL Result;
|
|
|
|
InitializeProgressBar (
|
|
g_ProgressBar,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
PrepareMigrationProgressBar();
|
|
|
|
pProcessAutoLogon (FALSE);
|
|
|
|
g_BlowAwayTempShellFolders = TRUE;
|
|
Result = CallAllMigrationFunctions();
|
|
|
|
PushError();
|
|
|
|
if (Result) {
|
|
//
|
|
// Save logon prompt settings and set up auto-logon
|
|
//
|
|
|
|
pProcessAutoLogon (TRUE);
|
|
} else {
|
|
g_BlowAwayTempShellFolders = FALSE;
|
|
ClearAdminPassword();
|
|
}
|
|
|
|
//
|
|
// All done!
|
|
//
|
|
|
|
TerminateProgressBar();
|
|
|
|
PopError();
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ResolveDomains (
|
|
DWORD Request
|
|
)
|
|
{
|
|
DWORD rc = ERROR_SUCCESS;
|
|
TCHAR unattendFile[MAX_TCHAR_PATH];
|
|
TCHAR buffer[32];
|
|
|
|
switch (Request) {
|
|
|
|
case REQUEST_QUERYTICKS:
|
|
if (!IsMemberOfDomain()) {
|
|
return 1;
|
|
}
|
|
|
|
return TICKS_DOMAIN_SEARCH;
|
|
|
|
case REQUEST_RUN:
|
|
//
|
|
// If autologon is enabled, then force classic mode
|
|
//
|
|
|
|
wsprintf (unattendFile, TEXT("%s\\system32\\$winnt$.inf"), g_WinDir);
|
|
if (GetPrivateProfileString (
|
|
TEXT("GuiUnattended"),
|
|
TEXT("AutoLogon"),
|
|
TEXT(""),
|
|
buffer,
|
|
ARRAYSIZE(buffer),
|
|
unattendFile
|
|
)) {
|
|
|
|
if (StringIMatch (buffer, TEXT("Yes"))) {
|
|
DEBUGMSG ((DBG_VERBOSE, "Found autologon; forcing classic logon type"));
|
|
SetClassicLogonType();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Resolve the domains
|
|
//
|
|
|
|
if (!SearchDomainsForUserAccounts()) {
|
|
LOG ((LOG_ERROR, "An error occurred searching for user domains. The upgrade failed."));
|
|
rc = GetLastError();
|
|
} else {
|
|
//
|
|
// Fix up memdb for dynamic user profile paths
|
|
//
|
|
|
|
pFixUpMemDb();
|
|
}
|
|
|
|
if (IsMemberOfDomain()) {
|
|
TickProgressBarDelta (TICKS_DOMAIN_SEARCH);
|
|
} else {
|
|
TickProgressBar();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
PrepareEnvironment (
|
|
IN DWORD Request
|
|
)
|
|
{
|
|
DWORD rc = ERROR_SUCCESS;
|
|
|
|
switch (Request) {
|
|
|
|
case REQUEST_QUERYTICKS:
|
|
return TICKS_INIT;
|
|
|
|
case REQUEST_RUN:
|
|
//
|
|
// Disable Win 3.1 migration dialog
|
|
//
|
|
|
|
pSetWin9xUpgValue();
|
|
|
|
// Enable 16 bit environment boot
|
|
pEnable16Boot();
|
|
|
|
//
|
|
// Enable privileges (req'd for several things)
|
|
//
|
|
|
|
if (!pSetupEnablePrivilege (SE_BACKUP_NAME, TRUE)) {
|
|
LOG ((LOG_ERROR, "MigMain Migrate: pSetupEnablePrivilege SE_BACKUP_NAME failed"));
|
|
//rc = GetLastError();
|
|
//break;
|
|
}
|
|
|
|
if (!pSetupEnablePrivilege (SE_RESTORE_NAME, TRUE)) {
|
|
LOG ((LOG_ERROR, "MigMain Migrate: pSetupEnablePrivilege SE_RESTORE_NAME failed"));
|
|
//rc = GetLastError();
|
|
//break;
|
|
}
|
|
|
|
TickProgressBarDelta (TICKS_INIT);
|
|
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
BOOL
|
|
MigMain_Cleanup (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MigMain_Cleanup is called to perform file removal. We delete everything
|
|
that is in the memdb category DelFile, and we also try to clean up after
|
|
MSN and other empty Win9x directories. Before exiting we delete our
|
|
temporary directory.
|
|
|
|
This function is called very last in Setup and is part of syssetup's
|
|
cleanup.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE when all file deletes were successful, FALSE if an error occurred.
|
|
Call GetLastError for the reason of failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
PCTSTR TempDir;
|
|
TCHAR normalPath[] = S_SHELL_TEMP_NORMAL_PATH;
|
|
TCHAR longPath[] = S_SHELL_TEMP_LONG_PATH;
|
|
DRIVELETTERS drives;
|
|
UINT u;
|
|
|
|
#ifdef DEBUG
|
|
INT n = 0;
|
|
#endif
|
|
|
|
// Remove everything in memdb's DelFile category
|
|
b = DoFileDel();
|
|
|
|
//
|
|
// Clean up any remaining directories that are now empty, including shell
|
|
// folder temp dirs
|
|
//
|
|
|
|
InitializeDriveLetterStructure (&drives);
|
|
|
|
if (!g_BlowAwayTempShellFolders) {
|
|
|
|
for (u = 0 ; u < NUMDRIVELETTERS ; u++) {
|
|
if (drives.ExistsOnSystem[u] && drives.Type[u] == DRIVE_FIXED) {
|
|
normalPath[0] = drives.Letter[u];
|
|
longPath[0] = drives.Letter[u];
|
|
|
|
MemDbSetValueEx (MEMDB_CATEGORY_CLEAN_UP_DIR, normalPath, NULL, NULL, 1, NULL);
|
|
MemDbSetValueEx (MEMDB_CATEGORY_CLEAN_UP_DIR, longPath, NULL, NULL, 1, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
RemoveEmptyDirs();
|
|
|
|
if (!g_BlowAwayTempShellFolders) {
|
|
//
|
|
// Setup failed, clean up temp dir but leave it in place
|
|
//
|
|
|
|
for (u = 0 ; u < NUMDRIVELETTERS ; u++) {
|
|
if (drives.ExistsOnSystem[u] && drives.Type[u] == DRIVE_FIXED) {
|
|
normalPath[0] = drives.Letter[u];
|
|
longPath[0] = drives.Letter[u];
|
|
|
|
RemoveDirectory (normalPath);
|
|
if (DoesFileExist (normalPath)) {
|
|
LOG ((LOG_ERROR, (PCSTR) MSG_LEFT_TEMP_SHELL_FOLDERS, normalPath));
|
|
}
|
|
|
|
RemoveDirectory (longPath);
|
|
if (DoesFileExist (longPath)) {
|
|
LOG ((LOG_ERROR, (PCSTR) MSG_LEFT_TEMP_SHELL_FOLDERS, longPath));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} else {
|
|
//
|
|
// Setup was successful, blow away entire temp dir regardless of its content
|
|
//
|
|
|
|
for (u = 0 ; u < NUMDRIVELETTERS ; u++) {
|
|
if (drives.ExistsOnSystem[u] && drives.Type[u] == DRIVE_FIXED) {
|
|
normalPath[0] = drives.Letter[u];
|
|
longPath[0] = drives.Letter[u];
|
|
|
|
RemoveCompleteDirectory (normalPath);
|
|
DEBUGMSG_IF ((
|
|
DoesFileExist (normalPath),
|
|
DBG_ERROR,
|
|
"Temp dir cannot be removed: %s",
|
|
normalPath
|
|
));
|
|
|
|
RemoveCompleteDirectory (longPath);
|
|
DEBUGMSG_IF ((
|
|
DoesFileExist (longPath),
|
|
DBG_ERROR,
|
|
"Temp dir cannot be removed: %s",
|
|
longPath
|
|
));
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
n = GetPrivateProfileIntA ("debug", "keeptempfiles", n, g_DebugInfPath);
|
|
if (n) {
|
|
return b;
|
|
}
|
|
|
|
#endif
|
|
|
|
if (g_ConfigOptions.KeepTempFiles) {
|
|
return b;
|
|
}
|
|
|
|
//
|
|
// Blow away temp dir
|
|
//
|
|
|
|
TempDir = JoinPaths (g_WinDir, S_SETUP);
|
|
|
|
b = DeleteDirectoryContents (TempDir);
|
|
|
|
if (b) {
|
|
b = RemoveDirectory (TempDir);
|
|
|
|
if (!b) {
|
|
LOG ((LOG_ERROR, "Could not delete the tree %s.", TempDir));
|
|
}
|
|
}
|
|
else {
|
|
LOG ((LOG_ERROR, "Could not delete the contents of %s.", TempDir));
|
|
}
|
|
|
|
FreePathString (TempDir);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
GetMemDbDat (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a pointer to the path of the DAT file holding the Win9x memdb tree.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the Win32 path of ntsetup.dat.
|
|
|
|
--*/
|
|
|
|
{
|
|
static TCHAR FileName[MAX_TCHAR_PATH];
|
|
|
|
MYASSERT (!g_NoReloadsAllowed);
|
|
|
|
StringCopy (FileName, g_TempDir);
|
|
StringCopy (AppendWack (FileName), S_NTSETUPDAT);
|
|
|
|
return FileName;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
GetUserDatLocation (
|
|
IN PCTSTR User,
|
|
OUT PBOOL CreateOnlyFlag OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks in memdb to locate the user.dat file for the specified user. On
|
|
Win9x, migapp.lib writes a line to memdb giving the location of user.dat
|
|
for each user, and the default user. This function retrieves that
|
|
location to guarantee the same file is used on both NT and Win9x.
|
|
|
|
Arguments:
|
|
|
|
User - The fixed name of the user to process, or NULL for the default user.
|
|
|
|
CreateOnlyFlag - Receives the create-only flag specified on the Win9x side
|
|
of the upgrade. If this flag is TRUE, then the account
|
|
should not be migrated.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the Win32 path of user.dat for the given user.
|
|
If the entry does not exist, NULL will be returned, and the user
|
|
will not be processed.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
MEMDB_ENUM e;
|
|
static TCHAR UserDatLocation[MAX_TCHAR_PATH];
|
|
|
|
if (!MemDbGetValueEx (&e, MEMDB_CATEGORY_USER_DAT_LOC, User, NULL)) {
|
|
if (!StringIMatch (User, g_AdministratorStr)) {
|
|
DEBUGMSG ((DBG_WARNING, "'UserDatLocation' for %s does not exist.", User?User:S_DOT_DEFAULT));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
StringCopy (UserDatLocation, e.szName);
|
|
|
|
if (CreateOnlyFlag) {
|
|
*CreateOnlyFlag = (BOOL) e.dwValue;
|
|
}
|
|
|
|
return UserDatLocation;
|
|
}
|
|
|
|
|
|
VOID
|
|
pSaveVersionStr (
|
|
IN HKEY Key,
|
|
IN PCTSTR Name
|
|
)
|
|
{
|
|
TCHAR Data[MEMDB_MAX];
|
|
|
|
if (MemDbGetEndpointValueEx (MEMDB_CATEGORY_STATE, Name, NULL, Data)) {
|
|
RegSetValueEx (
|
|
Key,
|
|
Name,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE) Data,
|
|
SizeOfString (Data)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
pSaveVersionDword (
|
|
IN HKEY Key,
|
|
IN PCTSTR Name
|
|
)
|
|
{
|
|
DWORD Data;
|
|
TCHAR Node[MEMDB_MAX];
|
|
|
|
MemDbBuildKey (Node, MEMDB_CATEGORY_STATE, Name, NULL, NULL);
|
|
|
|
if (MemDbGetValue (Node, &Data)) {
|
|
RegSetValueEx (
|
|
Key,
|
|
Name,
|
|
0,
|
|
REG_DWORD,
|
|
(PBYTE) &Data,
|
|
sizeof (Data)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetWin9xUpgValue (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create the value entry Win9xUpg on
|
|
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon
|
|
This routine should always be called when setup is installing an NT system
|
|
on top of Win9x, otherwise NT will think it has to migrate Win 3.1.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the opearation succeeds.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Error;
|
|
HKEY Key;
|
|
DWORD Value;
|
|
HKEY VersionKey;
|
|
|
|
Key = OpenRegKeyStr (S_WINLOGON_REGKEY);
|
|
if (!Key) {
|
|
return FALSE;
|
|
}
|
|
|
|
Value = 1;
|
|
Error = RegSetValueEx (
|
|
Key,
|
|
S_WIN9XUPG_FLAG_VALNAME,
|
|
0,
|
|
REG_DWORD,
|
|
(PBYTE) &Value,
|
|
sizeof (DWORD)
|
|
);
|
|
|
|
//
|
|
// Save the version info
|
|
//
|
|
|
|
VersionKey = CreateRegKey (Key, TEXT("PrevOsVersion"));
|
|
|
|
if (VersionKey) {
|
|
|
|
pSaveVersionStr (VersionKey, MEMDB_ITEM_PLATFORM_NAME);
|
|
pSaveVersionStr (VersionKey, MEMDB_ITEM_VERSION_TEXT);
|
|
|
|
pSaveVersionDword (VersionKey, MEMDB_ITEM_MAJOR_VERSION);
|
|
pSaveVersionDword (VersionKey, MEMDB_ITEM_MINOR_VERSION);
|
|
pSaveVersionDword (VersionKey, MEMDB_ITEM_BUILD_NUMBER);
|
|
pSaveVersionDword (VersionKey, MEMDB_ITEM_PLATFORM_ID);
|
|
|
|
CloseRegKey (VersionKey);
|
|
}
|
|
|
|
CloseRegKey (Key);
|
|
if (Error != ERROR_SUCCESS) {
|
|
SetLastError (Error);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
GetString (
|
|
WORD wMsg
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Load the string resource given in wMsg and copy it to a global string
|
|
buffer. Return the a pointer to the buffer.
|
|
|
|
Arguments:
|
|
|
|
wMsg - The identifier of the message to load.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the loaded message, or NULL. The message must be
|
|
smaller than 2048 characters.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR String;
|
|
|
|
String = GetStringResource (wMsg);
|
|
if (!String) {
|
|
return TEXT("Error: String resource could not be loaded");
|
|
}
|
|
|
|
_tcssafecpy (g_MsgBuf, String, ARRAYSIZE(g_MsgBuf));
|
|
FreeStringResource (String);
|
|
|
|
return g_MsgBuf;
|
|
}
|
|
|
|
|
|
VOID
|
|
pCountUsers (
|
|
OUT PDWORD TotalUsersPtr,
|
|
OUT PDWORD ActiveUsersPtr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Counts all Win9x users, and determines how many of them are active
|
|
for migration. The count includes the Administrator account, the
|
|
logon prompt account, and optional default user account.
|
|
|
|
NOTE: Administrator may be counted twice in ActiveUsersPtr, once for
|
|
a real Win9x user named Administrator, and again for the
|
|
NT Administrator account that is always migrated. The caller
|
|
must handle this special case.
|
|
|
|
Arguments:
|
|
|
|
TotalUsersPtr - A DWORD that receives the total number of Win9x users,
|
|
including the NT-only users.
|
|
ActiveUsersPtr - A DWORD that receives the number of users that require
|
|
migration. Migration may or may not be enabled for any
|
|
user.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
USERPOSITION up;
|
|
TCHAR User[MAX_TCHAR_PATH];
|
|
DWORD rc;
|
|
PCTSTR UserDatLocation;
|
|
|
|
*ActiveUsersPtr = 0;
|
|
*TotalUsersPtr = 3; // include logon, default and administrator in the total
|
|
|
|
rc = Win95RegGetFirstUser (&up, User);
|
|
if (rc != ERROR_SUCCESS) {
|
|
*TotalUsersPtr = 0;
|
|
return;
|
|
}
|
|
|
|
while (Win95RegHaveUser (&up)) {
|
|
|
|
GetFixedUserName (User);
|
|
|
|
// see if this user requires migration
|
|
UserDatLocation = GetUserDatLocation (User, NULL);
|
|
if (UserDatLocation) {
|
|
*ActiveUsersPtr += 1;
|
|
}
|
|
|
|
// count all users, migrated and non-migrated
|
|
*TotalUsersPtr += 1;
|
|
|
|
Win95RegGetNextUser (&up, User);
|
|
}
|
|
|
|
// test migration requirement of default user and adminsistrator
|
|
UserDatLocation = GetUserDatLocation (g_AdministratorStr, NULL);
|
|
if (UserDatLocation) {
|
|
*ActiveUsersPtr += 1;
|
|
}
|
|
|
|
UserDatLocation = GetUserDatLocation (S_DOT_DEFAULT, NULL);
|
|
if (UserDatLocation) {
|
|
*ActiveUsersPtr += 1;
|
|
}
|
|
|
|
if (g_ConfigOptions.MigrateDefaultUser) {
|
|
*ActiveUsersPtr += 1;
|
|
}
|
|
|
|
DEBUGMSG ((DBG_VERBOSE, "pCountUsers: %u users, %u require migration", *TotalUsersPtr, *ActiveUsersPtr));
|
|
}
|
|
|
|
|
|
CONVERTPATH_RC
|
|
ConvertWin9xPath (
|
|
PTSTR PathBuf
|
|
)
|
|
{
|
|
TCHAR Buffer[MEMDB_MAX];
|
|
DWORD status;
|
|
|
|
status = GetFileInfoOnNt (PathBuf, Buffer, MEMDB_MAX);
|
|
|
|
if (status & FILESTATUS_REPLACED) {
|
|
if (status & FILESTATUS_MOVED) {
|
|
_tcssafecpy (PathBuf, Buffer, MAX_TCHAR_PATH);
|
|
return CONVERTPATH_REMAPPED;
|
|
}
|
|
return CONVERTPATH_NOT_REMAPPED;
|
|
}
|
|
if (status & FILESTATUS_MOVED) {
|
|
_tcssafecpy (PathBuf, Buffer, MAX_TCHAR_PATH);
|
|
return CONVERTPATH_REMAPPED;
|
|
}
|
|
if (status & FILESTATUS_DELETED) {
|
|
return CONVERTPATH_DELETED;
|
|
}
|
|
return CONVERTPATH_NOT_REMAPPED;
|
|
}
|
|
|
|
VOID
|
|
pRaiseRegistryQuota (
|
|
PCTSTR Win9xSystemDatSpec
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
SYSTEM_REGISTRY_QUOTA_INFORMATION RegQuotaInfo;
|
|
HANDLE FileHandle;
|
|
DWORD QuotaNeeded;
|
|
ULARGE_INTEGER FreeBytes, dc1, dc2;
|
|
LONGLONG FreeBytesNeeded;
|
|
HKEY SaveKey;
|
|
DWORD rc;
|
|
|
|
#ifdef PAGED_POOL_INCREASE
|
|
SYSTEM_POOL_INFORMATION PoolInfo;
|
|
|
|
//
|
|
// Obtain current system settings
|
|
//
|
|
|
|
Status = NtQuerySystemInformation (
|
|
SystemPagedPoolInformation,
|
|
(PVOID) &PoolInfo,
|
|
sizeof(PoolInfo),
|
|
NULL
|
|
);
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
LOG ((LOG_ERROR, "Cannot obtain PoolInfo"));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
pSetupEnablePrivilege (SE_INCREASE_QUOTA_NAME, TRUE);
|
|
|
|
Status = NtQuerySystemInformation (
|
|
SystemRegistryQuotaInformation,
|
|
(PVOID) &RegQuotaInfo,
|
|
sizeof(RegQuotaInfo),
|
|
NULL
|
|
);
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
LOG ((LOG_ERROR, "Cannot obtain RegQuotaInfo"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Obtain Win9x registry system.dat size
|
|
//
|
|
|
|
FileHandle = CreateFile (
|
|
Win9xSystemDatSpec,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL, // security attributes
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL // template file
|
|
);
|
|
|
|
if (FileHandle == INVALID_HANDLE_VALUE) {
|
|
LOG ((LOG_ERROR, "Cannot open %s; cannot raise registry quota", Win9xSystemDatSpec));
|
|
return;
|
|
}
|
|
|
|
QuotaNeeded = GetFileSize (FileHandle, NULL);
|
|
CloseHandle (FileHandle);
|
|
|
|
if (QuotaNeeded > 0x3fffffff) {
|
|
LOG ((LOG_ERROR, "Cannot obtain size for %s; cannot raise registry quota", Win9xSystemDatSpec));
|
|
return;
|
|
}
|
|
|
|
QuotaNeeded *= 6;
|
|
|
|
//
|
|
// Get free disk space on boot drive
|
|
//
|
|
|
|
if (!GetDiskFreeSpaceEx (
|
|
g_WinDir,
|
|
&FreeBytes,
|
|
&dc1,
|
|
&dc2
|
|
)) {
|
|
LOG ((LOG_ERROR, "Can't get free space on drive holding %s; cannot raise registry quota", g_WinDir));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Lots of disk space? Raise paged pool by 5 times the size of system.dat.
|
|
// Example: Win9x system.dat is 5M; must have 150M free to raise paged pool.
|
|
//
|
|
|
|
FreeBytesNeeded = (LONGLONG) QuotaNeeded * (LONGLONG) 6;
|
|
if (FreeBytes.QuadPart >= (DWORDLONG) FreeBytesNeeded) {
|
|
//
|
|
// Unimplemented: Raise the paged pool and return
|
|
//
|
|
|
|
DEBUGMSG ((DBG_WARNING, "RegQuota: Really should be raising paged pool -- this machine has %u bytes free", FreeBytes.LowPart));
|
|
|
|
}
|
|
|
|
//
|
|
// Last resort: raise the registry quota (if necessary)
|
|
//
|
|
|
|
if (RegQuotaInfo.RegistryQuotaAllowed < QuotaNeeded) {
|
|
DEBUGMSG ((DBG_VERBOSE, "Raising registry quota from %u to %u", RegQuotaInfo.RegistryQuotaAllowed, QuotaNeeded));
|
|
|
|
RegQuotaInfo.RegistryQuotaAllowed = QuotaNeeded;
|
|
|
|
Status = NtSetSystemInformation (
|
|
SystemRegistryQuotaInformation,
|
|
&RegQuotaInfo,
|
|
sizeof (RegQuotaInfo)
|
|
);
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
LOG ((LOG_ERROR, "Can't set raised registry quota"));
|
|
}
|
|
|
|
//
|
|
// Set a permanent value in the registry
|
|
//
|
|
|
|
SaveKey = OpenRegKeyStr (TEXT("HKLM\\System\\CurrentControlSet\\Control"));
|
|
if (SaveKey) {
|
|
rc = RegSetValueEx (
|
|
SaveKey,
|
|
TEXT("RegistrySizeLimit"),
|
|
0,
|
|
REG_DWORD,
|
|
(PBYTE) &QuotaNeeded,
|
|
sizeof (DWORD)
|
|
);
|
|
|
|
CloseRegKey (SaveKey);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
LOG ((LOG_ERROR, "Could not set HKLM\\System\\CurrentControlSet\\Control [RegistrySizeLimit]"));
|
|
}
|
|
}
|
|
ELSE_DEBUGMSG ((DBG_ERROR, "Can't open HKLM\\System\\CurrentControlSet\\Control"));
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
pCopyDosFile (
|
|
IN PCTSTR FileName,
|
|
IN BOOL InRootDir
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies a file from %windir%\setup\msdos7 into the designated DOS directory
|
|
|
|
Arguments:
|
|
|
|
FileName - file to copy (no path).
|
|
|
|
Return Value:
|
|
|
|
TRUE if succeeded, FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR sourcePath;
|
|
PTSTR sourceFileName;
|
|
PTSTR destPath;
|
|
PTSTR destFileName;
|
|
BOOL result;
|
|
|
|
sourcePath = JoinPaths (g_TempDir, S_BOOT16_DOS_DIR);
|
|
sourceFileName = JoinPaths (sourcePath, FileName);
|
|
|
|
if (InRootDir) {
|
|
destPath = NULL;
|
|
destFileName = JoinPaths (ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
FileName);
|
|
}
|
|
else {
|
|
destPath = JoinPaths (ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
S_BOOT16_DOS_DIR);
|
|
destFileName = JoinPaths (destPath, FileName);
|
|
}
|
|
|
|
SetFileAttributes (destFileName, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
result = CopyFile (sourceFileName, destFileName, FALSE);
|
|
|
|
FreePathString (sourcePath);
|
|
FreePathString (sourceFileName);
|
|
if (destPath != NULL) {
|
|
FreePathString (destPath);
|
|
}
|
|
FreePathString (destFileName);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
VOID
|
|
pWriteBoot16ConfigLines (
|
|
IN HANDLE File,
|
|
IN PCTSTR BaseSection,
|
|
IN PCTSTR DosPath,
|
|
IN BOOL Localized
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pWriteBoot16ConfigLines reads configuration lines from wkstamig.inf and
|
|
writes them to the specified file handle. The caller can control wether the
|
|
lines should contain first boot only items or not and can control wether to
|
|
read in the base dos lines (same for all languages) or special lines used
|
|
for specific languages.
|
|
|
|
Arguments:
|
|
|
|
File - An opened handle with appropriate access to the file where
|
|
the data should be written.
|
|
BaseSection - Contains the Base Section name to read from the INF. This
|
|
section may be modified with a code page if Localized is TRUE.
|
|
DosPath - Contains the full path to the dos boot files (typically
|
|
c:\msdos7)
|
|
Localized - Controls wether data from the localized section is read. If
|
|
this parameter is TRUE, then the code page will be appended to
|
|
the BaseSection string for purposes of reading from wkstamig.inf.
|
|
|
|
|
|
Return Value:
|
|
|
|
none
|
|
++*/
|
|
{
|
|
|
|
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
|
|
GROWLIST list = GROWLIST_INIT;
|
|
PTSTR line;
|
|
TCHAR codePageSection[MAX_TCHAR_PATH];
|
|
USHORT codePage;
|
|
PCTSTR infSection;
|
|
|
|
//
|
|
// Add boot16 line specific environment variables.
|
|
//
|
|
GrowListAppendString (&list, TEXT("BOOTDRIVE"));
|
|
GrowListAppendString (&list, g_BootDrive);
|
|
GrowListAppendString (&list, TEXT("BOOT16DIR"));
|
|
GrowListAppendString (&list, DosPath);
|
|
|
|
//
|
|
// Terminate the arg list with two NULLs
|
|
//
|
|
GrowListAppendEmptyItem (&list);
|
|
GrowListAppendEmptyItem (&list);
|
|
|
|
if (Localized) {
|
|
//
|
|
// Caller wants data from the localized section.
|
|
//
|
|
|
|
GetGlobalCodePage (&codePage, NULL);
|
|
wsprintf (codePageSection, TEXT("%s %u"), BaseSection, codePage);
|
|
infSection = codePageSection;
|
|
}
|
|
else {
|
|
|
|
infSection = BaseSection;
|
|
}
|
|
|
|
|
|
//
|
|
// Write lines from base section.
|
|
//
|
|
if (InfFindFirstLine (g_WkstaMigInf, infSection, NULL, &is)) {
|
|
|
|
do {
|
|
|
|
//
|
|
// Get the line from the section and expand any environment
|
|
// variables.
|
|
//
|
|
line = InfGetLineText (&is);
|
|
MYASSERT (line);
|
|
|
|
line = ExpandEnvironmentTextEx (line,GrowListGetStringPtrArray (&list));
|
|
MYASSERT (line);
|
|
|
|
//
|
|
// Write the line to the file.
|
|
//
|
|
WriteFileString (File, line);
|
|
WriteFileString (File, TEXT("\r\n"));
|
|
FreeText (line);
|
|
|
|
|
|
} while (InfFindNextLine (&is));
|
|
}
|
|
|
|
FreeGrowList (&list);
|
|
InfCleanUpInfStruct (&is);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
pCreateConfigFile(
|
|
IN PCTSTR DosPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a CONFIG.SYS file containing default settings.
|
|
|
|
Arguments:
|
|
|
|
DosPath - Contains the path to the dos files. (e.g. c:\msdos7)
|
|
|
|
Return Value:
|
|
|
|
TRUE if file was created, FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR configName = NULL;
|
|
HANDLE handle;
|
|
|
|
configName = JoinPaths (ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
S_BOOT16_CONFIG_FILE);
|
|
|
|
SetFileAttributes (configName, FILE_ATTRIBUTE_NORMAL);
|
|
handle = CreateFile (
|
|
configName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_NORMAL
|
|
,
|
|
NULL
|
|
);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Read lines from wkstamig.inf into this file.
|
|
//
|
|
pWriteBoot16ConfigLines (handle, S_BOOT16_CONFIGSYS_SECTION, DosPath, FALSE);
|
|
pWriteBoot16ConfigLines (handle, S_BOOT16_CONFIGSYS_SECTION, DosPath, TRUE);
|
|
|
|
CloseHandle (handle);
|
|
FreePathString (configName);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pCreateStartupFile(
|
|
IN PCTSTR DosPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates an AUTOEXEC.BAT file containing default settings.
|
|
|
|
Arguments:
|
|
|
|
DosPath - Contains the path to the dos files. (e.g. c:\msdos7)
|
|
|
|
Return Value:
|
|
|
|
TRUE if file was created, FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR startupName = NULL;
|
|
PCTSTR comment = NULL;
|
|
HANDLE handle;
|
|
PCTSTR args[2];
|
|
|
|
args[0] = DosPath;
|
|
args[1] = NULL;
|
|
|
|
startupName = JoinPaths (ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
S_BOOT16_STARTUP_FILE);
|
|
|
|
|
|
SetFileAttributes (startupName, FILE_ATTRIBUTE_NORMAL);
|
|
handle = CreateFile (
|
|
startupName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN,
|
|
NULL
|
|
);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
comment = ParseMessageID (MSG_BOOT16_STARTUP_COMMENT, args);
|
|
|
|
//
|
|
// Read lines from wkstamig.inf into this file.
|
|
//
|
|
pWriteBoot16ConfigLines (handle, S_BOOT16_AUTOEXEC_SECTION, DosPath, FALSE);
|
|
pWriteBoot16ConfigLines (handle, S_BOOT16_AUTOEXEC_SECTION, DosPath, TRUE);
|
|
|
|
//
|
|
// Write localized comment.
|
|
//
|
|
WriteFileString (handle, comment);
|
|
WriteFileString (handle, TEXT("\r\n"));
|
|
|
|
FreeStringResource (comment);
|
|
|
|
CloseHandle (handle);
|
|
FreePathString (startupName);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
pEliminateCollision (
|
|
IN PCTSTR FileSpec
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pEliminateCollision checks to see if the specified file spec already
|
|
exists. If it does, the file is renamed with a numeric .nnn extension. If
|
|
the file can't be renamed, it is removed.
|
|
|
|
Arguments:
|
|
|
|
FileSpec - Specifies the file spec that is going to be used for a new file.
|
|
If this file already exists, it is renamed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR p;
|
|
PCTSTR NewFileSpec;
|
|
UINT u;
|
|
BOOL b;
|
|
|
|
if (DoesFileExist (FileSpec)) {
|
|
NewFileSpec = DuplicatePathString (FileSpec, 0);
|
|
|
|
p = _tcsrchr (NewFileSpec, TEXT('.'));
|
|
if (!p || _tcschr (p, TEXT('\\'))) {
|
|
p = GetEndOfString (NewFileSpec);
|
|
}
|
|
|
|
u = 0;
|
|
do {
|
|
wsprintf (p, TEXT(".%03u"), u);
|
|
u++;
|
|
} while (DoesFileExist (NewFileSpec));
|
|
|
|
b = OurMoveFile (FileSpec, NewFileSpec);
|
|
|
|
LOG_IF ((
|
|
!b,
|
|
LOG_ERROR,
|
|
"Could not rename %s to %s; source file might be lost",
|
|
FileSpec,
|
|
NewFileSpec
|
|
));
|
|
|
|
if (!b) {
|
|
SetFileAttributes (FileSpec, FILE_ATTRIBUTE_NORMAL);
|
|
b = DeleteFile (FileSpec);
|
|
|
|
LOG_IF ((
|
|
!b,
|
|
LOG_ERROR,
|
|
"Could not remove %s to make room for a new file. The new file is lost.",
|
|
FileSpec
|
|
));
|
|
}
|
|
|
|
FreePathString (NewFileSpec);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
pRenameCfgFiles (
|
|
IN PCTSTR DosDrive
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Renames old CONFIG.SYS and AUTOEXEC.BAT to make room for automatically generated ones.
|
|
|
|
Arguments:
|
|
|
|
DosDirectory - Contains the directory where the msdos files live (typeically c:\msdos7)
|
|
|
|
Return Value:
|
|
|
|
TRUE if rename succeeded, FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR fileName1 = NULL;
|
|
PTSTR fileName2 = NULL;
|
|
|
|
fileName1 = JoinPaths (
|
|
ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
S_BOOT16_CONFIG_FILE
|
|
);
|
|
|
|
fileName2 = JoinPaths (
|
|
DosDrive,
|
|
S_BOOT16_CONFIGUPG_FILE
|
|
);
|
|
|
|
OurMoveFile (fileName1, fileName2);
|
|
SetFileAttributes (fileName2, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
FreePathString (fileName1);
|
|
FreePathString (fileName2);
|
|
|
|
fileName1 = JoinPaths (
|
|
ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
S_BOOT16_STARTUP_FILE
|
|
);
|
|
|
|
fileName2 = JoinPaths (
|
|
DosDrive,
|
|
S_BOOT16_STARTUPUPG_FILE
|
|
);
|
|
|
|
OurMoveFile (fileName1, fileName2);
|
|
SetFileAttributes (fileName2, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
FreePathString (fileName1);
|
|
FreePathString (fileName2);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
pCleanRootDir (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Blows away dos files in root directory.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR fileName = NULL;
|
|
|
|
fileName = JoinPaths (ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
S_BOOT16_SYSMAIN_FILE);
|
|
MarkFileForDelete (fileName);
|
|
FreePathString (fileName);
|
|
|
|
fileName = JoinPaths (ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
S_BOOT16_DOSINI_FILE);
|
|
MarkFileForDelete (fileName);
|
|
FreePathString (fileName);
|
|
}
|
|
|
|
#define IoFile TEXT("IO.SYS")
|
|
|
|
VOID
|
|
pEnable16Boot (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a 16 bit environment boot option.
|
|
First we will check to see if everything is OK, we have all the files we need etc.
|
|
Then create DOS directory, rename old AUTOEXEC and CONFIG, create new ones and
|
|
add an entry in BOOT.INI
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
TRUE if file was created, FALSE if not
|
|
|
|
--*/
|
|
|
|
{
|
|
PTSTR fileName = NULL;
|
|
PTSTR dosPath = NULL;
|
|
INFCONTEXT infContext;
|
|
DWORD oldFileAttr;
|
|
BOOL result = TRUE;
|
|
HANDLE fileHandle = INVALID_HANDLE_VALUE;
|
|
|
|
if (g_Boot16 == BOOT16_NO) {
|
|
pCleanRootDir ();
|
|
return;
|
|
}
|
|
|
|
__try {
|
|
|
|
//
|
|
// first thing. Copy IO.SYS in root directory (BOOTSECT.DOS should be there)
|
|
//
|
|
pCopyDosFile (IoFile, TRUE);
|
|
fileName = JoinPaths (ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
IoFile);
|
|
SetFileAttributes (fileName, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM);
|
|
FreePathString (fileName);
|
|
|
|
//
|
|
// Create DOS7 directory and copy dos files there
|
|
//
|
|
dosPath = JoinPaths (ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
S_BOOT16_DOS_DIR);
|
|
if (!CreateDirectory (dosPath, NULL) && (GetLastError()!=ERROR_ALREADY_EXISTS)) {
|
|
LOG ((LOG_ERROR,"BOOT16 : Unable to create DOS directory %s",dosPath));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// If we find autoexec.bat and config.sys rename them as *.upg
|
|
//
|
|
if (!pRenameCfgFiles (dosPath)) {
|
|
__leave;
|
|
}
|
|
|
|
|
|
|
|
if (g_WkstaMigInf == INVALID_HANDLE_VALUE) {
|
|
LOG ((LOG_ERROR,"BOOT16 : WKSTAMIG.INF is not opened"));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Read the section, for every file, we are trying to read it from our temp dir
|
|
// and copy it to the new DOS7 location
|
|
//
|
|
fileName = AllocPathString (MAX_TCHAR_PATH);
|
|
|
|
if (!SetupFindFirstLine (
|
|
g_WkstaMigInf,
|
|
S_BOOT16_SECTION,
|
|
NULL,
|
|
&infContext
|
|
)) {
|
|
LOG ((LOG_ERROR,"BOOT16 : Cannot read from %s section (WKSTAMIG.INF)",S_BOOT16_SECTION));
|
|
__leave;
|
|
}
|
|
|
|
do {
|
|
if (SetupGetStringField (
|
|
&infContext,
|
|
0,
|
|
fileName,
|
|
MAX_TCHAR_PATH/sizeof(fileName[0]),
|
|
NULL
|
|
)) {
|
|
|
|
pCopyDosFile (fileName, FALSE);
|
|
}
|
|
}
|
|
while (SetupFindNextLine (&infContext, &infContext));
|
|
|
|
//
|
|
// Hide the msdos7 directory (not our idea...)
|
|
//
|
|
SetFileAttributes (dosPath, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
|
|
|
|
|
|
|
|
FreePathString (fileName);
|
|
fileName = NULL;
|
|
|
|
//
|
|
// Next step, build MSDOS.SYS file.
|
|
//
|
|
fileName = JoinPaths (ISPC98() ? g_Win9xBootDrivePath : g_BootDrivePath,
|
|
S_BOOT16_DOSINI_FILE);
|
|
if (SetFileAttributes (fileName, FILE_ATTRIBUTE_NORMAL)) {
|
|
if (!DeleteFile (fileName)) {
|
|
LOG ((LOG_ERROR, "BOOT16 : Unable to delete %s",fileName));
|
|
__leave;
|
|
}
|
|
}
|
|
result &= WritePrivateProfileString (TEXT("Paths"), TEXT("WinDir"), dosPath, fileName);
|
|
result &= WritePrivateProfileString (TEXT("Paths"), TEXT("WinBootDir"), dosPath, fileName);
|
|
result &= WritePrivateProfileString (TEXT("Options"), TEXT("LOGO"), TEXT("0"), fileName);
|
|
result &= WritePrivateProfileString (TEXT("Options"), TEXT("BootGUI"), TEXT("0"), fileName);
|
|
if (!result) {
|
|
LOG((LOG_ERROR,"Unable to write to %s",fileName));
|
|
__leave;
|
|
}
|
|
|
|
FreePathString (fileName);
|
|
fileName = NULL;
|
|
|
|
//
|
|
// Generate config.sys and autoexec.bat files.
|
|
//
|
|
|
|
if (!pCreateConfigFile (dosPath)) {
|
|
LOG ((LOG_ERROR, "BOOT16 : Unable to create %s",S_BOOT16_CONFIG_FILE));
|
|
__leave;
|
|
}
|
|
|
|
if (!pCreateStartupFile (dosPath)) {
|
|
LOG ((LOG_ERROR, "BOOT16 : Unable to create %s",S_BOOT16_STARTUP_FILE));
|
|
__leave;
|
|
}
|
|
|
|
|
|
if ((!ISPC98()) || (g_BootDrivePath[0] == g_Win9xBootDrivePath[0])) {
|
|
|
|
//
|
|
// If boot16 is set to BOOT16_AUTOMATIC, we create a boot.dos file,
|
|
// but don't actually modify boot.ini. If it is BOOT16_YES, then
|
|
// we modify boot.ini
|
|
//
|
|
// The result is that DOS will not show up as a boot option unless
|
|
// there was a specific reason it was turned on originally. However,
|
|
// there will be a way to enable it if needed.
|
|
//
|
|
if (g_Boot16 == BOOT16_AUTOMATIC) {
|
|
|
|
fileName = JoinPaths (g_BootDrivePath, S_BOOT16_BOOTDOS_FILE);
|
|
fileHandle = CreateFile (
|
|
fileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE,
|
|
NULL
|
|
);
|
|
|
|
if (fileHandle != INVALID_HANDLE_VALUE) {
|
|
|
|
WriteFileString (fileHandle, ISPC98() ? L"C:\\" : g_BootDrivePath);
|
|
WriteFileString (fileHandle, TEXT("="));
|
|
WriteFileString (fileHandle, S_BOOT16_OS_ENTRY);
|
|
}
|
|
}
|
|
else {
|
|
|
|
fileName = JoinPaths (g_BootDrivePath, S_BOOT16_BOOTINI_FILE);
|
|
oldFileAttr = GetFileAttributes (fileName);
|
|
SetFileAttributes (fileName, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
if (!WritePrivateProfileString (
|
|
S_BOOT16_OS_SECTION,
|
|
ISPC98() ? L"C:\\" : g_BootDrivePath,
|
|
S_BOOT16_OS_ENTRY,
|
|
fileName
|
|
)) {
|
|
LOG((LOG_ERROR,"Unable to write to %s",fileName));
|
|
SetFileAttributes (fileName, oldFileAttr);
|
|
__leave;
|
|
}
|
|
|
|
SetFileAttributes (fileName, oldFileAttr);
|
|
}
|
|
}
|
|
|
|
}
|
|
__finally {
|
|
if (fileName != NULL) {
|
|
FreePathString (fileName);
|
|
fileName = NULL;
|
|
}
|
|
if (dosPath != NULL) {
|
|
FreePathString (dosPath);
|
|
dosPath = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
pCopyRegString (
|
|
IN HKEY DestKey,
|
|
IN HKEY SrcKey,
|
|
IN PCTSTR SrcValue
|
|
)
|
|
{
|
|
PCTSTR Data;
|
|
|
|
Data = GetRegValueString (SrcKey, SrcValue);
|
|
if (Data) {
|
|
RegSetValueEx (DestKey, SrcValue, 0, REG_SZ, (PBYTE) Data, SizeOfString (Data));
|
|
MemFree (g_hHeap, 0, Data);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef PRERELEASE
|
|
|
|
//
|
|
// !!! This is for internal use only !!! It is used for auto stress.
|
|
//
|
|
|
|
VOID
|
|
pTransferAutoStressVal (
|
|
IN HKEY StressKey,
|
|
IN PCTSTR ValueName
|
|
)
|
|
{
|
|
TCHAR Data[MEMDB_MAX];
|
|
LONG rc;
|
|
|
|
if (!MemDbGetEndpointValueEx (
|
|
MEMDB_CATEGORY_STATE,
|
|
ValueName,
|
|
NULL, // no field
|
|
Data
|
|
)) {
|
|
return;
|
|
}
|
|
|
|
rc = RegSetValueEx (
|
|
StressKey,
|
|
ValueName,
|
|
0,
|
|
REG_SZ,
|
|
(PBYTE) Data,
|
|
SizeOfString (Data)
|
|
);
|
|
|
|
DEBUGMSG_IF ((rc == ERROR_SUCCESS, DBG_VERBOSE, "Transferred autostress value %s", ValueName));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
VOID
|
|
pProcessAutoLogon (
|
|
BOOL Final
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pProcessAutoLogon copies the logon defaults to a special key, so the
|
|
migpwd.exe tool can restore them if it runs. Then, the function calls
|
|
AutoStartProcessing to set up RunOnce and AutoAdminLogon.
|
|
|
|
This function is called early in migration to save the clean install
|
|
autologon, and then again at the end to prepare migpwd.exe.
|
|
|
|
Arguments:
|
|
|
|
Final - Specifies FALSE if this is the early call, TRUE if it is the
|
|
final call.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY SrcKey, DestKey;
|
|
PCTSTR Data;
|
|
BOOL copyNow = FALSE;
|
|
static BOOL alreadyCopied = FALSE;
|
|
|
|
//
|
|
// If autologon is enabled, preserve it in Win9xUpg key, so that
|
|
// migpwd.exe will restore it.
|
|
//
|
|
|
|
SrcKey = OpenRegKeyStr (S_WINLOGON_REGKEY);
|
|
if (SrcKey) {
|
|
|
|
if (!Final) {
|
|
//
|
|
// Early in migration, we get the clean install autologon values.
|
|
// If autologon is enabled, preserve the settings.
|
|
//
|
|
|
|
Data = GetRegValueString (SrcKey, S_AUTOADMIN_LOGON_VALUE);
|
|
if (Data) {
|
|
|
|
if (_ttoi (Data)) {
|
|
//
|
|
// on PER, don't want to preserve this value;
|
|
// instead, we need to preserve the name of
|
|
// the Win9x username as migrated via wkstamig.inf (see below)
|
|
//
|
|
copyNow = !g_PersonalSKU;
|
|
}
|
|
|
|
MemFree (g_hHeap, 0, Data);
|
|
}
|
|
} else if (!alreadyCopied) {
|
|
|
|
//
|
|
// Near the end of migration, we get the default logon prompt
|
|
// settings via wkstamig.inf migration. We want the attended case
|
|
// to work properly (preserving default user name & password).
|
|
//
|
|
// But if we've already preserved autologon, then we don't get
|
|
// here.
|
|
//
|
|
|
|
copyNow = TRUE;
|
|
}
|
|
|
|
if (copyNow) {
|
|
|
|
MYASSERT (!alreadyCopied);
|
|
alreadyCopied = TRUE;
|
|
|
|
DestKey = CreateRegKeyStr (S_WIN9XUPG_KEY);
|
|
if (DestKey) {
|
|
pCopyRegString (DestKey, SrcKey, S_AUTOADMIN_LOGON_VALUE);
|
|
pCopyRegString (DestKey, SrcKey, S_DEFAULT_PASSWORD_VALUE);
|
|
pCopyRegString (DestKey, SrcKey, S_DEFAULT_USER_NAME_VALUE);
|
|
pCopyRegString (DestKey, SrcKey, S_DEFAULT_DOMAIN_NAME_VALUE);
|
|
CloseRegKey (DestKey);
|
|
}
|
|
}
|
|
|
|
CloseRegKey (SrcKey);
|
|
}
|
|
|
|
if (!Final) {
|
|
return;
|
|
}
|
|
|
|
AutoStartProcessing();
|
|
|
|
#ifdef PRERELEASE
|
|
//
|
|
// !!! This is for internal use only !!! It is used for auto stress.
|
|
//
|
|
|
|
if (g_ConfigOptions.AutoStress) {
|
|
HKEY StressKey;
|
|
|
|
StressKey = CreateRegKeyStr (S_AUTOSTRESS_KEY);
|
|
MYASSERT (StressKey);
|
|
|
|
pTransferAutoStressVal (StressKey, S_AUTOSTRESS_USER);
|
|
pTransferAutoStressVal (StressKey, S_AUTOSTRESS_PASSWORD);
|
|
pTransferAutoStressVal (StressKey, S_AUTOSTRESS_OFFICE);
|
|
pTransferAutoStressVal (StressKey, S_AUTOSTRESS_DBG);
|
|
pTransferAutoStressVal (StressKey, S_AUTOSTRESS_FLAGS);
|
|
|
|
CloseRegKey (StressKey);
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
PCTSTR
|
|
GetProfilePathForAllUsers (
|
|
VOID
|
|
)
|
|
{
|
|
PTSTR result = NULL;
|
|
DWORD size = 0;
|
|
|
|
if (!GetAllUsersProfileDirectory (NULL, &size) &&
|
|
ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
|
|
return NULL;
|
|
}
|
|
|
|
result = AllocPathString (size + 1);
|
|
if (!GetAllUsersProfileDirectory (result, &size)) {
|
|
FreePathString (result);
|
|
return NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
PCTSTR
|
|
pGetDefaultShellFolderLocationFromInf (
|
|
IN PCTSTR FolderName,
|
|
IN PCTSTR ProfilePath
|
|
)
|
|
{
|
|
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
|
|
PCTSTR data;
|
|
PCTSTR result = NULL;
|
|
|
|
MYASSERT (g_WkstaMigInf && g_WkstaMigInf != INVALID_HANDLE_VALUE);
|
|
|
|
if (InfFindFirstLine (g_WkstaMigInf, TEXT("ShellFolders.DefaultNtLocation"), FolderName, &is)) {
|
|
data = InfGetStringField (&is, 1);
|
|
if (data) {
|
|
result = StringSearchAndReplace (data, S_USERPROFILE_ENV, ProfilePath);
|
|
if (!result) {
|
|
result = DuplicatePathString (data, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
InfCleanUpInfStruct (&is);
|
|
|
|
return result;
|
|
}
|
|
|
|
VOID
|
|
pFixUpDynamicPaths (
|
|
PCTSTR Category
|
|
)
|
|
{
|
|
MEMDB_ENUM e;
|
|
TCHAR Pattern[MEMDB_MAX];
|
|
PTSTR p;
|
|
GROWBUFFER Roots = GROWBUF_INIT;
|
|
MULTISZ_ENUM e2;
|
|
TCHAR NewRoot[MEMDB_MAX];
|
|
TCHAR AllProfilePath[MAX_TCHAR_PATH];
|
|
PCTSTR ProfilePath;
|
|
DWORD Size;
|
|
PTSTR UserName;
|
|
HKEY sfKey = NULL;
|
|
PCTSTR sfPath = NULL;
|
|
PTSTR NtLocation;
|
|
PCTSTR tempExpand;
|
|
BOOL regFolder;
|
|
BOOL mkDir;
|
|
|
|
//
|
|
// Collect all the roots that need to be renamed
|
|
//
|
|
|
|
StringCopy (Pattern, Category);
|
|
p = AppendWack (Pattern);
|
|
StringCopy (p, TEXT("*"));
|
|
|
|
if (MemDbEnumFirstValue (&e, Pattern, MEMDB_THIS_LEVEL_ONLY, MEMDB_ALL_BUT_PROXY)) {
|
|
do {
|
|
if ((_tcsnextc (e.szName) == TEXT('>')) ||
|
|
(_tcsnextc (e.szName) == TEXT('<'))
|
|
) {
|
|
StringCopy (p, e.szName);
|
|
MultiSzAppend (&Roots, Pattern);
|
|
}
|
|
} while (MemDbEnumNextValue (&e));
|
|
}
|
|
|
|
//
|
|
// Now change each root
|
|
//
|
|
|
|
if (EnumFirstMultiSz (&e2, (PCTSTR) Roots.Buf)) {
|
|
do {
|
|
//
|
|
// Compute NewRoot
|
|
//
|
|
|
|
StringCopy (NewRoot, e2.CurrentString);
|
|
|
|
p = _tcschr (NewRoot, TEXT('<'));
|
|
|
|
if (p) {
|
|
|
|
UserName = _tcschr (p, TEXT('>'));
|
|
MYASSERT (UserName);
|
|
StringCopyAB (Pattern, _tcsinc (p), UserName);
|
|
UserName = _tcsinc (UserName);
|
|
|
|
regFolder = TRUE;
|
|
if (StringIMatch (Pattern, TEXT("Profiles"))) {
|
|
regFolder = FALSE;
|
|
}
|
|
if (StringIMatch (Pattern, TEXT("Common Profiles"))) {
|
|
regFolder = FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the profile root
|
|
//
|
|
|
|
if (StringIMatch (UserName, S_DOT_ALLUSERS)) {
|
|
Size = MAX_TCHAR_PATH;
|
|
if (regFolder) {
|
|
if (!GetAllUsersProfileDirectory (AllProfilePath, &Size)) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Cannot get All Users profile path."));
|
|
continue;
|
|
}
|
|
sfKey = OpenRegKeyStr (S_USHELL_FOLDERS_KEY_SYSTEM);
|
|
} else {
|
|
if (!GetProfilesDirectory (AllProfilePath, &Size)) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Cannot get All Users profile path."));
|
|
continue;
|
|
}
|
|
}
|
|
} else if (StringMatch (UserName, S_DEFAULT_USER)) {
|
|
Size = MAX_TCHAR_PATH;
|
|
if (regFolder) {
|
|
if (!GetDefaultUserProfileDirectory (AllProfilePath, &Size)) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Cannot get Default User profile path."));
|
|
continue;
|
|
}
|
|
sfKey = OpenRegKey (HKEY_CURRENT_USER, S_USHELL_FOLDERS_KEY_USER);
|
|
} else {
|
|
if (!GetProfilesDirectory (AllProfilePath, &Size)) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Cannot get All Users profile path."));
|
|
continue;
|
|
}
|
|
}
|
|
} else {
|
|
ProfilePath = GetProfilePathForUser (UserName);
|
|
if (!ProfilePath) {
|
|
DEBUGMSG ((DBG_WHOOPS, "Cannot get profile path for user:%s", UserName));
|
|
continue;
|
|
}
|
|
StringCopy (AllProfilePath, ProfilePath);
|
|
if (regFolder) {
|
|
sfKey = OpenRegKey (HKEY_CURRENT_USER, S_USHELL_FOLDERS_KEY_USER);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a specific reg folder is specified, get its path
|
|
//
|
|
|
|
mkDir = FALSE;
|
|
|
|
if (regFolder) {
|
|
if (!sfKey) {
|
|
DEBUGMSG ((DBG_ERROR, "Could not open Shell folders key."));
|
|
continue;
|
|
}
|
|
sfPath = GetRegValueString (sfKey, Pattern);
|
|
CloseRegKey (sfKey);
|
|
|
|
if (!sfPath || *sfPath == 0) {
|
|
|
|
DEBUGMSG ((DBG_WARNING, "Could not get Shell Folder path for: %s", Pattern));
|
|
|
|
tempExpand = pGetDefaultShellFolderLocationFromInf (Pattern, AllProfilePath);
|
|
if (!tempExpand) {
|
|
DEBUGMSG ((
|
|
DBG_WHOOPS,
|
|
"Shell folder %s is not in registry nor is it in [ShellFolders.DefaultNtLocation] of wkstamig.inf",
|
|
Pattern
|
|
));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Special case: Shell wants read-only on this folder. Create it now.
|
|
//
|
|
|
|
mkDir = TRUE;
|
|
|
|
} else {
|
|
tempExpand = StringSearchAndReplace (
|
|
sfPath,
|
|
S_USERPROFILE_ENV,
|
|
AllProfilePath
|
|
);
|
|
|
|
if (!tempExpand) {
|
|
tempExpand = DuplicatePathString (sfPath, 0);
|
|
}
|
|
}
|
|
|
|
if (sfPath) {
|
|
MemFree (g_hHeap, 0, sfPath);
|
|
}
|
|
} else {
|
|
tempExpand = DuplicatePathString (AllProfilePath, 0);
|
|
}
|
|
|
|
//
|
|
// Move symbolic name to full path
|
|
//
|
|
|
|
NtLocation = ExpandEnvironmentText (tempExpand);
|
|
|
|
if (mkDir) {
|
|
MakeSurePathExists (NtLocation, TRUE);
|
|
SetFileAttributes (NtLocation, FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
|
|
StringCopy (p, NtLocation);
|
|
|
|
MemDbMoveTree (e2.CurrentString, NewRoot);
|
|
|
|
FreeText (NtLocation);
|
|
|
|
FreePathString (tempExpand);
|
|
} else {
|
|
|
|
p = _tcschr (NewRoot, TEXT('>'));
|
|
MYASSERT (p);
|
|
|
|
if (StringIMatch (_tcsinc (p), S_DOT_ALLUSERS)) {
|
|
Size = MAX_TCHAR_PATH;
|
|
if (!GetAllUsersProfileDirectory (AllProfilePath, &Size)) {
|
|
DEBUGMSG ((DBG_WARNING, "Dynamic path for %s could not be resolved", e2.CurrentString));
|
|
}
|
|
else {
|
|
StringCopy (p, AllProfilePath);
|
|
MemDbMoveTree (e2.CurrentString, NewRoot);
|
|
}
|
|
} else if (StringMatch (_tcsinc (p), S_DEFAULT_USER)) {
|
|
Size = MAX_TCHAR_PATH;
|
|
if (!GetDefaultUserProfileDirectory (AllProfilePath, &Size)) {
|
|
DEBUGMSG ((DBG_WARNING, "Dynamic path for %s could not be resolved", e2.CurrentString));
|
|
}
|
|
else {
|
|
StringCopy (p, AllProfilePath);
|
|
MemDbMoveTree (e2.CurrentString, NewRoot);
|
|
}
|
|
} else {
|
|
ProfilePath = GetProfilePathForUser (_tcsinc (p));
|
|
if (ProfilePath) {
|
|
StringCopy (p, ProfilePath);
|
|
MemDbMoveTree (e2.CurrentString, NewRoot);
|
|
}
|
|
else {
|
|
DEBUGMSG ((DBG_WARNING, "Dynamic path for %s could not be resolved", e2.CurrentString));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
} while (EnumNextMultiSz (&e2));
|
|
}
|
|
|
|
FreeGrowBuffer (&Roots);
|
|
}
|
|
|
|
|
|
VOID
|
|
pFixUpMemDb (
|
|
VOID
|
|
)
|
|
{
|
|
MEMDB_ENUM e;
|
|
TCHAR node[MEMDB_MAX];
|
|
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_PATHROOT);
|
|
//pFixUpDynamicPaths (MEMDB_CATEGORY_DATA); OPTIMIZATION -- Data overlaps PathRoot
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_USERFILEMOVE_DEST);
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_SHELLFOLDERS_DEST);
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_SHELLFOLDERS_SRC);
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_LINKEDIT_TARGET);
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_LINKEDIT_WORKDIR);
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_LINKEDIT_ICONPATH);
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_LINKSTUB_TARGET);
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_LINKSTUB_WORKDIR);
|
|
pFixUpDynamicPaths (MEMDB_CATEGORY_LINKSTUB_ICONPATH);
|
|
|
|
//
|
|
// Enumerate each user in MyDocsMoveWarning, then update dynamic paths
|
|
//
|
|
|
|
// MyDocsMoveWarning\<user>\<path>
|
|
MemDbBuildKey (
|
|
node,
|
|
MEMDB_CATEGORY_MYDOCS_WARNING,
|
|
TEXT("*"),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (MemDbEnumFirstValue (&e, node, MEMDB_THIS_LEVEL_ONLY, MEMDB_ALL_MATCHES)) {
|
|
do {
|
|
MemDbBuildKey (
|
|
node,
|
|
MEMDB_CATEGORY_MYDOCS_WARNING,
|
|
e.szName, // <user>
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
pFixUpDynamicPaths (node);
|
|
|
|
} while (MemDbEnumNextValue (&e));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
EnumFirstUserToMigrate (
|
|
OUT PMIGRATE_USER_ENUM e,
|
|
IN DWORD Flags
|
|
)
|
|
{
|
|
ZeroMemory (e, sizeof (MIGRATE_USER_ENUM));
|
|
e->Flags = Flags;
|
|
|
|
pCountUsers (&e->TotalUsers, &e->ActiveUsers);
|
|
e->UserNumber = e->TotalUsers;
|
|
|
|
Win95RegGetFirstUser (&e->up, e->Win95RegName);
|
|
|
|
return EnumNextUserToMigrate (e);
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumNextUserToMigrate (
|
|
IN OUT PMIGRATE_USER_ENUM e
|
|
)
|
|
{
|
|
LONG rc;
|
|
PCTSTR Domain;
|
|
TCHAR Win9xAccount[MEMDB_MAX];
|
|
TCHAR EnumAccount[MAX_TCHAR_PATH];
|
|
USERPOSITION *AdminPosPtr;
|
|
USERPOSITION AdminPos;
|
|
BOOL Loop = TRUE;
|
|
PCTSTR UserDatLocation;
|
|
|
|
while (Loop) {
|
|
|
|
if (e->UserNumber == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
Loop = FALSE;
|
|
e->UserNumber--;
|
|
|
|
__try {
|
|
e->UserDoingTheUpgrade = FALSE;
|
|
|
|
if (e->UserNumber == INDEX_ADMINISTRATOR) {
|
|
|
|
_tcssafecpy (e->FixedUserName, g_AdministratorStr, MAX_USER_NAME);
|
|
StringCopy (e->Win9xUserName, e->FixedUserName);
|
|
e->AccountType = ADMINISTRATOR_ACCOUNT;
|
|
|
|
} else if (e->UserNumber == INDEX_LOGON_PROMPT) {
|
|
|
|
StringCopy (e->FixedUserName, S_DOT_DEFAULT);
|
|
StringCopy (e->Win9xUserName, e->FixedUserName);
|
|
e->AccountType = LOGON_USER_SETTINGS;
|
|
|
|
} else if (e->UserNumber == INDEX_DEFAULT_USER) {
|
|
//
|
|
// Do not process unless default user migration is enabled
|
|
//
|
|
|
|
if (!g_ConfigOptions.MigrateDefaultUser) {
|
|
Loop = (e->Flags & ENUM_ALL_USERS) == 0;
|
|
__leave;
|
|
}
|
|
|
|
StringCopy (e->FixedUserName, S_DEFAULT_USER);
|
|
StringCopy (e->Win9xUserName, e->FixedUserName);
|
|
e->AccountType = DEFAULT_USER_ACCOUNT;
|
|
|
|
} else {
|
|
|
|
_tcssafecpy (e->Win9xUserName, e->Win95RegName, MAX_USER_NAME);
|
|
StringCopy (e->FixedUserName, e->Win95RegName);
|
|
GetFixedUserName (e->FixedUserName);
|
|
e->AccountType = WIN9X_USER_ACCOUNT;
|
|
|
|
//
|
|
// Special case: Account named Administrator exists. In this
|
|
// case, we'd have two Administrator users unless
|
|
// one was skipped. So here is the test to skip
|
|
// if the user is named Administrator.
|
|
//
|
|
|
|
if (StringIMatch (e->Win9xUserName, g_AdministratorStr)) {
|
|
Loop = TRUE;
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
StringCopy (e->FixedDomainName, e->FixedUserName);
|
|
|
|
//
|
|
// See if we are to migrate this user, and if so, perpare
|
|
// the Win95 registry and call ProcessUser.
|
|
//
|
|
|
|
UserDatLocation = GetUserDatLocation (e->FixedUserName, &e->CreateOnly);
|
|
|
|
if (UserDatLocation && DoesFileExist (UserDatLocation)) {
|
|
e->Valid = TRUE;
|
|
StringCopy (e->UserDatLocation, UserDatLocation);
|
|
} else {
|
|
e->Valid = FALSE;
|
|
e->UserDatLocation[0] = 0;
|
|
}
|
|
|
|
if (e->Flags & ENUM_SET_WIN9X_HKR) {
|
|
//
|
|
// Make HKCU equal to the enumerated user
|
|
//
|
|
|
|
g_hKeyRoot95 = HKEY_CURRENT_USER;
|
|
}
|
|
|
|
if (e->Valid) {
|
|
|
|
//
|
|
// Is this user the user doing migration?
|
|
//
|
|
|
|
if (MemDbGetEndpointValueEx (
|
|
MEMDB_CATEGORY_ADMINISTRATOR_INFO,
|
|
MEMDB_ITEM_AI_USER_DOING_MIG,
|
|
NULL, // no field
|
|
Win9xAccount
|
|
)) {
|
|
//
|
|
// Win9xAccount is unfixed name, convert to fixed name then
|
|
// compare with the current enumerated user.
|
|
//
|
|
|
|
GetFixedUserName (Win9xAccount);
|
|
|
|
DEBUGMSG ((DBG_NAUSEA, "Comparing %s to %s", e->FixedUserName, Win9xAccount));
|
|
|
|
if (StringIMatch (e->FixedUserName, Win9xAccount)) {
|
|
e->UserDoingTheUpgrade = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Perform special init depending on the user type
|
|
//
|
|
|
|
if (e->AccountType == WIN9X_USER_ACCOUNT) {
|
|
|
|
if (e->Flags & ENUM_SET_WIN9X_HKR) {
|
|
//
|
|
// Map HKCU on Win95 to current user
|
|
//
|
|
|
|
rc = Win95RegSetCurrentUserNt (&e->up, e->UserDatLocation);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetLastError (rc);
|
|
LOG ((
|
|
LOG_ERROR,
|
|
"Migrate Users: Win95RegSetCurrentUserNt could not set user "
|
|
"to %s (user path %s)",
|
|
e->FixedUserName,
|
|
e->UserDatLocation
|
|
));
|
|
|
|
LOG ((LOG_ERROR, "Could not load %s", e->UserDatLocation));
|
|
Loop = (e->Flags & ENUM_ALL_USERS) == 0;
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
// Obtain the full user name
|
|
Domain = GetDomainForUser (e->FixedUserName);
|
|
if (Domain) {
|
|
StringCopy (e->FixedDomainName, Domain);
|
|
StringCopy (AppendWack (e->FixedDomainName), e->FixedUserName);
|
|
}
|
|
}
|
|
|
|
else if (e->AccountType == ADMINISTRATOR_ACCOUNT) {
|
|
|
|
//
|
|
// Map Win9x registry appropriate for the Administrator hive
|
|
//
|
|
|
|
if (e->Flags & ENUM_SET_WIN9X_HKR) {
|
|
AdminPosPtr = NULL;
|
|
|
|
// Obtain user account from memdb and find USERPOSITION for it
|
|
if (MemDbGetEndpointValueEx (
|
|
MEMDB_CATEGORY_ADMINISTRATOR_INFO,
|
|
MEMDB_ITEM_AI_ACCOUNT,
|
|
NULL, // no field
|
|
Win9xAccount
|
|
)) {
|
|
|
|
// Search Win9x user list for user
|
|
Win95RegGetFirstUser (&AdminPos, EnumAccount);
|
|
while (Win95RegHaveUser (&AdminPos)) {
|
|
GetFixedUserName (EnumAccount);
|
|
|
|
if (StringIMatch (Win9xAccount, EnumAccount)) {
|
|
AdminPosPtr = &AdminPos;
|
|
break;
|
|
}
|
|
|
|
Win95RegGetNextUser (&AdminPos, EnumAccount);
|
|
}
|
|
|
|
if (!AdminPosPtr) {
|
|
DEBUGMSG ((
|
|
DBG_WARNING,
|
|
"pMigrateUsers: Account %s not found",
|
|
Win9xAccount
|
|
));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Map HKCU on Win95 to match, or default user if no match or
|
|
// no memdb entry
|
|
//
|
|
|
|
rc = Win95RegSetCurrentUserNt (AdminPosPtr, e->UserDatLocation);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetLastError (rc);
|
|
LOG ((LOG_ERROR, "Could not load %s for Administrator", e->UserDatLocation));
|
|
Loop = (e->Flags & ENUM_ALL_USERS) == 0;
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (e->AccountType == LOGON_USER_SETTINGS || e->AccountType == DEFAULT_USER_ACCOUNT) {
|
|
|
|
//
|
|
// Map HKCU on Win95 to default user
|
|
//
|
|
|
|
if (e->Flags & ENUM_SET_WIN9X_HKR) {
|
|
rc = Win95RegSetCurrentUserNt (NULL, e->UserDatLocation);
|
|
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetLastError (rc);
|
|
LOG ((LOG_ERROR, "Could not load default user hive"));
|
|
Loop = (e->Flags & ENUM_ALL_USERS) == 0;
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* if (e->Valid) */
|
|
|
|
else {
|
|
Loop = (e->Flags & ENUM_ALL_USERS) == 0;
|
|
}
|
|
|
|
} /* try */
|
|
|
|
__finally {
|
|
//
|
|
// Get the next user for next time through loop, ignore errors
|
|
//
|
|
|
|
if (e->AccountType == WIN9X_USER_ACCOUNT) {
|
|
Win95RegGetNextUser (&e->up, e->Win95RegName);
|
|
}
|
|
}
|
|
} /* while (Loop) */
|
|
|
|
DEBUGMSG_IF ((
|
|
e->Flags & ENUM_SET_WIN9X_HKR,
|
|
DBG_VERBOSE,
|
|
"--- User Info ---\n"
|
|
" User Name: %s (%s)\n"
|
|
" Domain User Name: %s\n"
|
|
" Win95Reg Name: %s\n"
|
|
" User Hive: %s\n"
|
|
" Account Type: %s\n"
|
|
" Create Only: %s\n"
|
|
" Valid: %s\n"
|
|
" UserDoingTheUpgrade: %s\n",
|
|
e->Win9xUserName,
|
|
e->FixedUserName,
|
|
e->FixedDomainName,
|
|
e->Win95RegName,
|
|
e->UserDatLocation,
|
|
e->AccountType == WIN9X_USER_ACCOUNT ? TEXT("User") :
|
|
e->AccountType == ADMINISTRATOR_ACCOUNT ? TEXT("Administrator") :
|
|
e->AccountType == LOGON_USER_SETTINGS ? TEXT("Logon User") :
|
|
e->AccountType == DEFAULT_USER_ACCOUNT ? TEXT("Default User") : TEXT("Unknown"),
|
|
e->CreateOnly ? TEXT("Yes") : TEXT("No"),
|
|
e->Valid ? TEXT("Yes") : TEXT("No"),
|
|
e->UserDoingTheUpgrade ? TEXT("Yes") : TEXT("No")
|
|
));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
RunExternalProcesses (
|
|
IN HINF Inf,
|
|
IN PMIGRATE_USER_ENUM EnumPtr OPTIONAL
|
|
)
|
|
{
|
|
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
|
|
GROWLIST List = GROWLIST_INIT;
|
|
PCTSTR RawCmdLine;
|
|
PCTSTR ExpandedCmdLine;
|
|
BOOL ProcessResult;
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
DWORD rc;
|
|
|
|
GrowListAppendString (&List, TEXT("SYSTEMDIR"));
|
|
GrowListAppendString (&List, g_System32Dir);
|
|
|
|
if (EnumPtr) {
|
|
|
|
GrowListAppendString (&List, TEXT("USERNAME"));
|
|
GrowListAppendString (&List, EnumPtr->FixedUserName);
|
|
|
|
GrowListAppendString (&List, TEXT("USERNAMEWITHDOMAIN"));
|
|
GrowListAppendString (&List, EnumPtr->FixedDomainName);
|
|
|
|
GrowListAppendString (&List, TEXT("PREVOS_USERNAME"));
|
|
GrowListAppendString (&List, EnumPtr->Win9xUserName);
|
|
|
|
if (EnumPtr->AccountType != LOGON_USER_SETTINGS) {
|
|
|
|
GrowListAppendString (&List, TEXT("USERHIVEROOT"));
|
|
GrowListAppendString (&List, S_FULL_TEMP_USER_KEY);
|
|
|
|
} else {
|
|
|
|
GrowListAppendString (&List, TEXT("USERHIVEROOT"));
|
|
GrowListAppendString (&List, S_DEFAULT_USER_KEY);
|
|
|
|
}
|
|
|
|
if (EnumPtr->ExtraData) {
|
|
GrowListAppendString (&List, TEXT("USERPROFILE"));
|
|
GrowListAppendString (&List, EnumPtr->ExtraData->TempProfile);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Terminate the arg list with two NULLs
|
|
//
|
|
|
|
GrowListAppendEmptyItem (&List);
|
|
GrowListAppendEmptyItem (&List);
|
|
|
|
if (InfFindFirstLine (Inf, S_EXTERNAL_PROCESSES, NULL, (&is))) {
|
|
do {
|
|
//
|
|
// Get the command line
|
|
//
|
|
|
|
RawCmdLine = InfGetLineText (&is);
|
|
|
|
//
|
|
// Expand environment variables
|
|
//
|
|
|
|
ExpandedCmdLine = ExpandEnvironmentTextEx (
|
|
RawCmdLine,
|
|
GrowListGetStringPtrArray (&List)
|
|
);
|
|
|
|
//
|
|
// Launch the process
|
|
//
|
|
|
|
ZeroMemory (&si, sizeof (si));
|
|
si.cb = sizeof (si);
|
|
si.dwFlags = STARTF_FORCEOFFFEEDBACK;
|
|
|
|
ProcessResult = CreateProcess (
|
|
NULL,
|
|
(PTSTR) ExpandedCmdLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_DEFAULT_ERROR_MODE,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi
|
|
);
|
|
|
|
if (ProcessResult) {
|
|
|
|
CloseHandle (pi.hThread);
|
|
|
|
//
|
|
// Wait 60 seconds for the process to complete
|
|
//
|
|
|
|
rc = WaitForSingleObject (pi.hProcess, 60000);
|
|
if (rc != WAIT_OBJECT_0) {
|
|
TerminateProcess (pi.hProcess, 0);
|
|
DEBUGMSG ((DBG_ERROR, "Process %s timed out and was aborted", ExpandedCmdLine));
|
|
}
|
|
ELSE_DEBUGMSG ((DBG_VERBOSE, "External process completed: %s", ExpandedCmdLine));
|
|
|
|
CloseHandle (pi.hProcess);
|
|
|
|
}
|
|
ELSE_DEBUGMSG ((DBG_ERROR, "Cannot launch %s", ExpandedCmdLine));
|
|
|
|
FreeText (ExpandedCmdLine);
|
|
|
|
} while (InfFindNextLine (&is));
|
|
}
|
|
|
|
FreeGrowList (&List);
|
|
InfCleanUpInfStruct (&is);
|
|
}
|
|
|
|
|
|
DWORD
|
|
MigrateGhostSystemFiles (
|
|
IN DWORD Request
|
|
)
|
|
{
|
|
/*
|
|
TREE_ENUM e;
|
|
PCTSTR systemName;
|
|
DWORD status;
|
|
*/
|
|
|
|
if (Request == REQUEST_QUERYTICKS) {
|
|
return TICKS_GHOST_SYSTEM_MIGRATION;
|
|
} else if (Request != REQUEST_RUN) {
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
if (EnumFirstFileInTreeEx (&e, g_System32Dir, NULL, FALSE, FALSE, FILE_ENUM_THIS_LEVEL)) {
|
|
do {
|
|
systemName = JoinPaths (g_SystemDir, e.Name);
|
|
status = GetFileStatusOnNt (systemName);
|
|
|
|
if ((status & FILESTATUS_NTINSTALLED) &&
|
|
!(status & FILESTATUS_MOVED)
|
|
) {
|
|
if (!DoesFileExist (systemName)) {
|
|
MarkFileForMove (systemName, e.FullPath);
|
|
}
|
|
}
|
|
FreePathString (systemName);
|
|
|
|
} while (EnumNextFileInTree (&e));
|
|
}
|
|
*/
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
typedef struct _KNOWN_DIRS {
|
|
PCTSTR DirId;
|
|
PCTSTR Translation;
|
|
}
|
|
KNOWN_DIRS, *PKNOWN_DIRS;
|
|
|
|
KNOWN_DIRS g_KnownDirs [] = {
|
|
{TEXT("10"), g_WinDir},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
typedef struct {
|
|
PCTSTR ShellFolderName;
|
|
PCTSTR DirId;
|
|
PCTSTR ShellFolderNameDefault;
|
|
BOOL bUsed;
|
|
} SHELL_TO_DIRS, *PSHELL_TO_DIRS;
|
|
|
|
SHELL_TO_DIRS g_ShellToDirs[] = {
|
|
{TEXT("Administrative Tools"), TEXT("7501"), TEXT("7517\\Administrative Tools")},
|
|
{TEXT("Common Administrative Tools"), TEXT("7501"), TEXT("7517\\Administrative Tools")},
|
|
{TEXT("AppData"), TEXT("7502"), TEXT("Application Data")},
|
|
{TEXT("Common AppData"), TEXT("7502"), TEXT("Application Data")},
|
|
{TEXT("Cache"), TEXT("7503"), NULL},
|
|
{TEXT("Cookies"), TEXT("7504"), NULL},
|
|
{TEXT("Desktop"), TEXT("7505"), NULL},
|
|
{TEXT("Common Desktop"), TEXT("7505"), TEXT("Desktop")},
|
|
{TEXT("Favorites"), TEXT("7506"), NULL},
|
|
{TEXT("Common Favorites"), TEXT("7506"), TEXT("Favorites")},
|
|
{TEXT("Local Settings"), TEXT("7510"), NULL},
|
|
{TEXT("History"), TEXT("7508"), TEXT("7510\\History")},
|
|
{TEXT("Local AppData"), TEXT("7509"), TEXT("7510\\Application Data")},
|
|
{TEXT("Personal"), TEXT("7515"), TEXT("My Documents")},
|
|
{TEXT("Common Documents"), TEXT("7515"), TEXT("My Documents")},
|
|
{TEXT("My Music"), TEXT("7511"), TEXT("7515\\My Music")},
|
|
{TEXT("CommonMusic"), TEXT("7511"), TEXT("7515\\My Music")},
|
|
{TEXT("My Pictures"), TEXT("7512"), TEXT("7515\\My Pictures")},
|
|
{TEXT("CommonPictures"), TEXT("7512"), TEXT("7515\\My Pictures")},
|
|
{TEXT("My Video"), TEXT("7513"), TEXT("7515\\My Video")},
|
|
{TEXT("CommonVideo"), TEXT("7513"), TEXT("7515\\My Video")},
|
|
{TEXT("NetHood"), TEXT("7514"), NULL},
|
|
{TEXT("PrintHood"), TEXT("7516"), NULL},
|
|
{TEXT("Start Menu"), TEXT("7520"), NULL},
|
|
{TEXT("Common Start Menu"), TEXT("7520"), TEXT("Start Menu")},
|
|
{TEXT("Programs"), TEXT("7517"), TEXT("7520\\Programs")},
|
|
{TEXT("Common Programs"), TEXT("7517"), TEXT("7520\\Programs")},
|
|
{TEXT("Recent"), TEXT("7518"), NULL},
|
|
{TEXT("SendTo"), TEXT("7519"), NULL},
|
|
{TEXT("Startup"), TEXT("7521"), TEXT("7517\\Startup")},
|
|
{TEXT("Common Startup"), TEXT("7521"), TEXT("7517\\Startup")},
|
|
{TEXT("Templates"), TEXT("7522"), NULL},
|
|
{TEXT("Common Templates"), TEXT("7522"), TEXT("Templates")},
|
|
{TEXT("Fonts"), TEXT("7507"), TEXT("10\\Fonts")},
|
|
{NULL, NULL, NULL, FALSE}
|
|
};
|
|
|
|
GROWLIST g_KnownDirIds = GROWLIST_INIT;
|
|
GROWLIST g_KnownDirPaths = GROWLIST_INIT;
|
|
|
|
VOID
|
|
pAddKnownShellFolder (
|
|
IN PCTSTR ShellFolderName,
|
|
IN PCTSTR SrcPath
|
|
)
|
|
{
|
|
PSHELL_TO_DIRS p;
|
|
|
|
for (p = g_ShellToDirs ; p->ShellFolderName ; p++) {
|
|
if (StringIMatch (ShellFolderName, p->ShellFolderName)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!p->ShellFolderName) {
|
|
DEBUGMSG ((DBG_ERROR, "This system has an unsupported shell folder tag: %s", ShellFolderName));
|
|
return;
|
|
}
|
|
|
|
p->bUsed = TRUE;
|
|
|
|
GrowListAppendString (&g_KnownDirIds, p->DirId);
|
|
GrowListAppendString (&g_KnownDirPaths, SrcPath);
|
|
}
|
|
|
|
typedef struct {
|
|
PCTSTR sfName;
|
|
PCTSTR sfPath;
|
|
HKEY SfKey;
|
|
REGVALUE_ENUM SfKeyEnum;
|
|
BOOL UserSf;
|
|
} SF_ENUM, *PSF_ENUM;
|
|
|
|
BOOL
|
|
EnumFirstRegShellFolder (
|
|
IN OUT PSF_ENUM e,
|
|
IN BOOL UserSf
|
|
);
|
|
BOOL
|
|
EnumNextRegShellFolder (
|
|
IN OUT PSF_ENUM e
|
|
);
|
|
|
|
BOOL
|
|
pConvertDirName (
|
|
PCTSTR OldDirName,
|
|
PTSTR NewDirName,
|
|
PINT NameNumber
|
|
);
|
|
|
|
VOID
|
|
pInitKnownDirs (
|
|
IN BOOL bUser
|
|
)
|
|
{
|
|
SF_ENUM e;
|
|
PCTSTR profileForAllUsers;
|
|
PCTSTR profileForAllUsersVar = TEXT("%ALLUSERSPROFILE%");
|
|
PCTSTR sfPathPtr;
|
|
TCHAR shellPartialPath[MAX_PATH];
|
|
UINT charCount;
|
|
UINT charCountProfileVar;
|
|
UINT charCountProfile;
|
|
PSHELL_TO_DIRS p;
|
|
KNOWN_DIRS * pKnownDirs;
|
|
INT nameNumber;
|
|
|
|
for (p = g_ShellToDirs ; p->ShellFolderName; p++){
|
|
p->bUsed = FALSE;
|
|
}
|
|
|
|
if(bUser){
|
|
if (EnumFirstRegShellFolder(&e, TRUE)) {
|
|
do {
|
|
pAddKnownShellFolder(e.sfName, e.sfPath);
|
|
DEBUGMSG((DBG_VERBOSE, "USER: ShellFolderPath=%s\nCutedFolderPath=%s", e.sfPath, e.sfPath));
|
|
} while (EnumNextRegShellFolder(&e));
|
|
}
|
|
}
|
|
else{
|
|
profileForAllUsers = GetProfilePathForAllUsers();
|
|
MYASSERT(profileForAllUsers);
|
|
if(profileForAllUsers){
|
|
charCountProfile = TcharCount(profileForAllUsers);
|
|
}
|
|
|
|
charCountProfileVar = TcharCount(profileForAllUsersVar);
|
|
|
|
if (EnumFirstRegShellFolder(&e, FALSE)) {
|
|
do {
|
|
if(profileForAllUsers){
|
|
charCount = 0;
|
|
if(StringIMatchCharCount(e.sfPath, profileForAllUsers, charCountProfile)){
|
|
charCount = charCountProfile;
|
|
}
|
|
else{
|
|
if(StringIMatchCharCount(e.sfPath, profileForAllUsersVar, charCountProfileVar)){
|
|
charCount = charCountProfileVar;
|
|
}
|
|
}
|
|
|
|
StringCopy(shellPartialPath, TEXT("%USERPROFILE%"));
|
|
StringCat(shellPartialPath, &e.sfPath[charCount]);
|
|
sfPathPtr = shellPartialPath;
|
|
}
|
|
else{
|
|
sfPathPtr = e.sfPath;
|
|
}
|
|
DEBUGMSG((DBG_VERBOSE, "SYSTEM: ShellFolderPath=%s\r\nCutedFolderPath=%s", e.sfPath, shellPartialPath));
|
|
pAddKnownShellFolder(e.sfName, sfPathPtr);
|
|
} while (EnumNextRegShellFolder(&e));
|
|
}
|
|
|
|
FreePathString (profileForAllUsers);
|
|
}
|
|
|
|
for (pKnownDirs = g_KnownDirs ; pKnownDirs->DirId ; pKnownDirs++) {
|
|
GrowListAppendString (&g_KnownDirIds, pKnownDirs->DirId);
|
|
GrowListAppendString (&g_KnownDirPaths, pKnownDirs->Translation);
|
|
}
|
|
|
|
for (p = g_ShellToDirs ; p->ShellFolderName; p++){
|
|
if(p->bUsed){
|
|
continue;
|
|
}
|
|
|
|
shellPartialPath[0] = '\0';
|
|
|
|
nameNumber = 0;
|
|
pConvertDirName(p->DirId, shellPartialPath, &nameNumber);
|
|
if(!StringMatch (p->DirId, shellPartialPath)){
|
|
p->bUsed = TRUE;
|
|
continue;
|
|
}
|
|
|
|
if(p->ShellFolderNameDefault){
|
|
if(_istdigit(p->ShellFolderNameDefault[0])){
|
|
nameNumber = 0;
|
|
pConvertDirName(
|
|
p->ShellFolderNameDefault,
|
|
shellPartialPath,
|
|
&nameNumber);
|
|
}
|
|
else{
|
|
StringCopy(shellPartialPath, TEXT("%USERPROFILE%\\"));
|
|
StringCat(shellPartialPath, p->ShellFolderNameDefault);
|
|
}
|
|
}
|
|
else{
|
|
StringCopy(shellPartialPath, TEXT("%USERPROFILE%\\"));
|
|
StringCat(shellPartialPath, p->ShellFolderName);
|
|
}
|
|
|
|
pAddKnownShellFolder(p->ShellFolderName, shellPartialPath);
|
|
DEBUGMSG((DBG_VERBOSE, "REST: ShellFolderPath=%s\nCutedFolderPath=%s", p->ShellFolderName, shellPartialPath));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
pCleanUpKnownDirs (
|
|
VOID
|
|
)
|
|
{
|
|
FreeGrowList (&g_KnownDirPaths);
|
|
FreeGrowList (&g_KnownDirIds);
|
|
}
|
|
|
|
BOOL
|
|
pConvertDirName (
|
|
PCTSTR OldDirName,
|
|
PTSTR NewDirName,
|
|
PINT NameNumber
|
|
)
|
|
{
|
|
PCTSTR OldDirCurr = OldDirName;
|
|
PCTSTR OldDirNext;
|
|
BOOL match = FALSE;
|
|
INT index;
|
|
PCTSTR listStr;
|
|
|
|
if (*NameNumber == -1) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Extract the dir id, keeping a pointer to the subdir
|
|
//
|
|
|
|
NewDirName[0] = 0;
|
|
OldDirNext = _tcschr (OldDirCurr, '\\');
|
|
if (OldDirNext == NULL) {
|
|
OldDirNext = GetEndOfString (OldDirCurr);
|
|
}
|
|
|
|
StringCopyAB (NewDirName, OldDirCurr, OldDirNext);
|
|
|
|
//
|
|
// Find the next match in the known dir ID list
|
|
//
|
|
|
|
listStr = GrowListGetString (&g_KnownDirIds, *NameNumber);
|
|
|
|
while (listStr) {
|
|
|
|
*NameNumber += 1;
|
|
|
|
if (StringMatch (NewDirName, listStr)) {
|
|
listStr = GrowListGetString (&g_KnownDirPaths, (*NameNumber) - 1);
|
|
MYASSERT (listStr);
|
|
StringCopy (NewDirName, listStr);
|
|
break;
|
|
}
|
|
|
|
listStr = GrowListGetString (&g_KnownDirIds, *NameNumber);
|
|
}
|
|
|
|
//
|
|
// Cat the subpath to the output string and return
|
|
//
|
|
|
|
StringCat (NewDirName, OldDirNext);
|
|
|
|
if (!listStr) {
|
|
*NameNumber = -1;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
pUninstallUserProfileCleanupPreparation (
|
|
IN HINF Inf,
|
|
IN PTSTR UserNamePtr,
|
|
IN PCTSTR PathProfileRootPtr,
|
|
IN PCTSTR DocsAndSettingsRoot,
|
|
IN GROWLIST * ListOfLogicalPathsPtr,
|
|
IN OUT GROWLIST * ListOfPaths
|
|
)
|
|
{
|
|
INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
|
|
GROWLIST List = GROWLIST_INIT;
|
|
PTSTR rawDir;
|
|
TCHAR rawPath[MAX_PATH];
|
|
PTSTR ExpandedPath;
|
|
PTSTR fileName;
|
|
TCHAR shellPath[MAX_PATH];
|
|
INT nameNumber;
|
|
INT i;
|
|
INT listSize;
|
|
PCTSTR pathLogicalPath;
|
|
|
|
|
|
GrowListAppendString (&List, TEXT("USERPROFILE"));
|
|
GrowListAppendString (&List, PathProfileRootPtr);
|
|
|
|
GrowListAppendString (&List, TEXT("PROFILES"));
|
|
GrowListAppendString (&List, DocsAndSettingsRoot);
|
|
|
|
GrowListAppendString (&List, TEXT("USERNAME"));
|
|
GrowListAppendString (&List, UserNamePtr);
|
|
|
|
GrowListAppendEmptyItem (&List);
|
|
GrowListAppendEmptyItem (&List);
|
|
|
|
DEBUGMSG ((DBG_VERBOSE, "USERPROFILE.pathProfileRoot=%s\n", PathProfileRootPtr));
|
|
|
|
if (InfFindFirstLine (Inf, S_UNINSTALL_PROFILE_CLEAN_OUT, NULL, (&is))) {
|
|
do{
|
|
rawDir = InfGetStringField (&is, 1);
|
|
if(!rawDir || *rawDir == 0){
|
|
DEBUGMSG ((DBG_VERBOSE, "rawDir == NULL"));
|
|
continue;
|
|
}
|
|
|
|
StringCopy (rawPath, rawDir);
|
|
|
|
fileName = InfGetStringField (&is, 2);
|
|
if (fileName && *fileName) {
|
|
StringCopy (AppendWack(rawPath), fileName);
|
|
}
|
|
|
|
nameNumber = 0;
|
|
pConvertDirName(rawPath, shellPath, &nameNumber);
|
|
|
|
ExpandedPath = ExpandEnvironmentTextEx (
|
|
shellPath,
|
|
GrowListGetStringPtrArray (&List)
|
|
);
|
|
|
|
DEBUGMSG ((DBG_VERBOSE, "rawPath=%s\nExpandedPath=%s\nShellPath=%s", rawPath, ExpandedPath, shellPath));
|
|
|
|
GrowListAppendString (ListOfPaths, ExpandedPath);
|
|
|
|
FreeText (ExpandedPath);
|
|
|
|
} while (InfFindNextLine (&is));
|
|
}
|
|
|
|
if(ListOfLogicalPathsPtr){
|
|
for(i = 0, listSize = GrowListGetSize (ListOfLogicalPathsPtr); i < listSize; i++) {
|
|
pathLogicalPath = GrowListGetString(ListOfLogicalPathsPtr, i);
|
|
if(!pathLogicalPath){
|
|
continue;
|
|
}
|
|
|
|
nameNumber = 0;
|
|
pConvertDirName(pathLogicalPath, shellPath, &nameNumber);
|
|
|
|
ExpandedPath = ExpandEnvironmentTextEx (
|
|
shellPath,
|
|
GrowListGetStringPtrArray (&List)
|
|
);
|
|
|
|
GrowListAppendString (ListOfPaths, ExpandedPath);
|
|
|
|
FreeText (ExpandedPath);
|
|
}
|
|
}
|
|
|
|
FreeGrowList (&List);
|
|
InfCleanUpInfStruct (&is);
|
|
|
|
DEBUGMSG ((DBG_VERBOSE, "UninstallUserProfileCleanupPreparation end"));
|
|
}
|
|
|
|
BOOL
|
|
pGetProfilePathForAllUsers(
|
|
OUT PTSTR AccountName,
|
|
OUT PTSTR PathProfile
|
|
)
|
|
{
|
|
PCTSTR pathProfileForAllUser;
|
|
|
|
MYASSERT(AccountName && PathProfile);
|
|
if(!AccountName || !PathProfile){
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
pathProfileForAllUser = GetProfilePathForAllUsers();
|
|
if(!pathProfileForAllUser) {
|
|
return FALSE;
|
|
}
|
|
|
|
StringCopy (AccountName, S_ALL_USERS);
|
|
StringCopy (PathProfile, pathProfileForAllUser);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pGetProfilePathForDefaultUser(
|
|
OUT PTSTR AccountName,
|
|
OUT PTSTR PathProfile
|
|
)
|
|
{
|
|
DWORD bufferSize;
|
|
|
|
MYASSERT(AccountName && PathProfile);
|
|
if(!AccountName || !PathProfile){
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
bufferSize = MAX_PATH;
|
|
if(!GetDefaultUserProfileDirectory(PathProfile, &bufferSize) ||
|
|
!PathProfile[0]) {
|
|
return FALSE;
|
|
}
|
|
|
|
StringCopy (AccountName, S_DEFAULT_USER);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pGetProfilePathForUser(
|
|
IN PCTSTR UserName,
|
|
OUT PTSTR AccountName,
|
|
OUT PTSTR PathProfile
|
|
)
|
|
{
|
|
DWORD bufferSize;
|
|
|
|
MYASSERT(UserName && UserName[0] && AccountName && PathProfile);
|
|
if(!UserName || !UserName[0] || !AccountName || !PathProfile){
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
bufferSize = MAX_PATH;
|
|
if(!GetProfilesDirectory(PathProfile, &bufferSize) ||
|
|
!PathProfile[0]) {
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
StringCat(AppendWack(PathProfile), UserName);
|
|
|
|
StringCopy (AccountName, UserName);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
pGetProfilePathForLocalService(
|
|
OUT PTSTR AccountName,
|
|
OUT PTSTR PathProfile
|
|
)
|
|
{
|
|
return pGetProfilePathForUser(S_LOCALSERVICE_USER, AccountName, PathProfile);
|
|
}
|
|
|
|
BOOL
|
|
pGetProfilePathForNetworkService(
|
|
OUT PTSTR AccountName,
|
|
OUT PTSTR PathProfile
|
|
)
|
|
{
|
|
return pGetProfilePathForUser(S_NETWORKSERVICE_USER, AccountName, PathProfile);
|
|
}
|
|
|
|
BOOL
|
|
pGetProfilePathForMachineName(
|
|
OUT PTSTR AccountName,
|
|
OUT PTSTR PathProfile
|
|
)
|
|
{
|
|
TCHAR machineName[MAX_COMPUTERNAME_LENGTH + 2];
|
|
PTSTR machineNamePtr = ExpandEnvironmentTextEx (TEXT("%COMPUTERNAME%"), NULL);
|
|
BOOL bResult;
|
|
|
|
if(!machineNamePtr || machineNamePtr[0] == '%'){
|
|
MYASSERT(FALSE);
|
|
DEBUGMSG((DBG_VERBOSE, "ComputerName is NULL"));
|
|
return FALSE;
|
|
}
|
|
DEBUGMSG ((DBG_VERBOSE, "machineName=%s", machineNamePtr? machineNamePtr: TEXT("NULL")));
|
|
|
|
StringCopy(machineName, machineNamePtr);
|
|
StringCat(machineName, TEXT("$"));
|
|
|
|
return pGetProfilePathForUser(machineName, AccountName, PathProfile);
|
|
}
|
|
|
|
VOID
|
|
UninstallUserProfileCleanupPreparation (
|
|
IN HINF Inf,
|
|
IN PMIGRATE_USER_ENUM EnumPtr,
|
|
IN BOOL Playback
|
|
)
|
|
{
|
|
static GROWLIST listOfPaths = GROWLIST_INIT;
|
|
static PROFILE_PATH_PROVIDER profilePathProviders[] =
|
|
{
|
|
pGetProfilePathForAllUsers,
|
|
pGetProfilePathForDefaultUser,
|
|
pGetProfilePathForLocalService,
|
|
pGetProfilePathForNetworkService,
|
|
pGetProfilePathForMachineName
|
|
};
|
|
|
|
TCHAR accountName[MAX_PATH];
|
|
TCHAR pathProfile[MAX_PATH];
|
|
TCHAR docsAndSettingsRoot[MAX_PATH];
|
|
PCTSTR pathProfileRootPtr;
|
|
UINT i;
|
|
UINT listSize;
|
|
DWORD bufferSize;
|
|
INT stringLen;
|
|
INT cleanOutType;
|
|
TCHAR pathDir[MAX_PATH];
|
|
|
|
bufferSize = ARRAYSIZE (docsAndSettingsRoot);
|
|
if (!GetProfilesDirectory (docsAndSettingsRoot, &bufferSize)) {
|
|
DEBUGMSG ((DBG_ERROR, "Can't get Documents and Settings root"));
|
|
*docsAndSettingsRoot = 0;
|
|
}
|
|
|
|
if (EnumPtr) {
|
|
pathProfileRootPtr = GetProfilePathForUser(EnumPtr->FixedUserName);
|
|
if(pathProfileRootPtr) {
|
|
|
|
pInitKnownDirs(TRUE);
|
|
|
|
pUninstallUserProfileCleanupPreparation(
|
|
Inf,
|
|
EnumPtr->FixedUserName,
|
|
pathProfileRootPtr,
|
|
docsAndSettingsRoot,
|
|
&g_StartMenuItemsForCleanUpPrivate,
|
|
&listOfPaths
|
|
);
|
|
|
|
pCleanUpKnownDirs();
|
|
}
|
|
} else {
|
|
pInitKnownDirs(FALSE);
|
|
|
|
for(i = 0; i < ARRAYSIZE(profilePathProviders); i++){
|
|
if(profilePathProviders[i](accountName, pathProfile)){
|
|
pUninstallUserProfileCleanupPreparation(
|
|
Inf,
|
|
accountName,
|
|
pathProfile,
|
|
docsAndSettingsRoot,
|
|
&g_StartMenuItemsForCleanUpCommon,
|
|
&listOfPaths
|
|
);
|
|
}
|
|
}
|
|
|
|
pCleanUpKnownDirs();
|
|
}
|
|
|
|
if (Playback) {
|
|
for(i = 0, listSize = GrowListGetSize (&listOfPaths); i < listSize; i++) {
|
|
|
|
pathProfileRootPtr = GrowListGetString(&listOfPaths, i);
|
|
if (pathProfileRootPtr){
|
|
|
|
stringLen = TcharCount(pathProfileRootPtr);
|
|
if(stringLen > 2 && '*' == pathProfileRootPtr[stringLen - 1]){
|
|
MYASSERT('\\' == pathProfileRootPtr[stringLen - 2] || '/' == pathProfileRootPtr[stringLen - 2]);
|
|
StringCopyTcharCount(pathDir, pathProfileRootPtr, stringLen - 1);
|
|
pathProfileRootPtr = pathDir;
|
|
cleanOutType = BACKUP_AND_CLEAN_TREE;
|
|
}
|
|
else{
|
|
cleanOutType = BACKUP_FILE;
|
|
}
|
|
|
|
if (!MemDbSetValueEx (
|
|
MEMDB_CATEGORY_CLEAN_OUT,
|
|
pathProfileRootPtr,
|
|
NULL,
|
|
NULL,
|
|
cleanOutType,
|
|
NULL
|
|
)){
|
|
DEBUGMSG ((DBG_VERBOSE, "MemDbSetValueEx - failed"));
|
|
}
|
|
}
|
|
}
|
|
FreeGrowList (&listOfPaths);
|
|
|
|
FreeGrowList (&g_StartMenuItemsForCleanUpCommon);
|
|
FreeGrowList (&g_StartMenuItemsForCleanUpPrivate);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SetClassicLogonType (
|
|
VOID
|
|
)
|
|
{
|
|
static BOOL logonTypeChanged = FALSE;
|
|
DWORD d;
|
|
HKEY key;
|
|
LONG regResult;
|
|
|
|
if (!logonTypeChanged) {
|
|
key = OpenRegKeyStr (S_WINLOGON_REGKEY);
|
|
if (key) {
|
|
d = 0; // classic logon style
|
|
regResult = RegSetValueEx (
|
|
key,
|
|
TEXT("LogonType"),
|
|
0,
|
|
REG_DWORD,
|
|
(PCBYTE)(&d),
|
|
sizeof (d)
|
|
);
|
|
|
|
if (regResult == ERROR_SUCCESS) {
|
|
logonTypeChanged = TRUE;
|
|
LOG ((LOG_INFORMATION, "Logon type set to classic style because of MigrateUserAs answer file settings"));
|
|
}
|
|
|
|
CloseRegKey (key);
|
|
}
|
|
|
|
if (!logonTypeChanged) {
|
|
LOG ((LOG_ERROR, "Failed to set logon type to classic style; users will not appear in the logon menu"));
|
|
logonTypeChanged = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|