/*++ Copyright (c) 1996 Microsoft Corporation Module Name: wkstamig.c Abstract: The functions in this module are called to perform migration of system-wide settings. Author: Jim Schmidt (jimschm) 04-Feb-1997 Revision History: ovidiut 10-May-1999 Added DoIniActions jimschm 16-Dec-1998 Changed ATM font migration to use Adobe's APIs. jimschm 25-Nov-1998 ATM.INI migration; Win9x hive migration jimschm 23-Sep-1998 Consolidated memdb saves into usermig.c jimschm 19-Feb-1998 Added "none" group support, fixed share problems. calinn 12-Dec-1997 Added RestoreMMSettings_System --*/ #include "pch.h" #include "migmainp.h" #include "brfcasep.h" #include // // Constants, types, declarations // #define W95_ACCESS_READ 0x1 #define W95_ACCESS_WRITE 0x2 #define W95_ACCESS_CREATE 0x4 #define W95_ACCESS_EXEC 0x8 #define W95_ACCESS_DELETE 0x10 #define W95_ACCESS_ATRIB 0x20 #define W95_ACCESS_PERM 0x40 #define W95_ACCESS_FINDFIRST 0x80 #define W95_ACCESS_FULL 0xff #define W95_ACCESS_GROUP 0x8000 #define W95_GENERIC_READ (W95_ACCESS_READ|W95_ACCESS_FINDFIRST) #define W95_GENERIC_WRITE (W95_ACCESS_WRITE|W95_ACCESS_CREATE|W95_ACCESS_DELETE|W95_ACCESS_ATRIB) #define W95_GENERIC_FULL (W95_GENERIC_READ|W95_GENERIC_WRITE|W95_ACCESS_PERM) #define W95_GENERIC_NONE 0 #define S_DEFAULT_USER_NAME TEXT("DefaultUserName") #define S_DEFAULT_DOMAIN_NAME TEXT("DefaultDomainName") // from private\net\svcdlls\srvsvc\server #define SHARES_REGISTRY_PATH L"LanmanServer\\Shares" #define SHARES_SECURITY_REGISTRY_PATH L"LanmanServer\\Shares\\Security" #define CSCFLAGS_VARIABLE_NAME L"CSCFlags" #define MAXUSES_VARIABLE_NAME L"MaxUses" #define PATH_VARIABLE_NAME L"Path" #define PERMISSIONS_VARIABLE_NAME L"Permissions" #define REMARK_VARIABLE_NAME L"Remark" #define TYPE_VARIABLE_NAME L"Type" // Win9x-specific flags for NetShareEnum #define SHI50F_RDONLY 0x0001 #define SHI50F_FULL 0x0002 #define SHI50F_DEPENDSON (SHI50F_RDONLY|SHI50F_FULL) #define SHI50F_ACCESSMASK (SHI50F_RDONLY|SHI50F_FULL) #ifndef UNICODE #error UNICODE required #endif #define DBG_NETSHARES "NetShares" #define DBG_INIFILES "IniFiles" #define S_CLEANER_GUID TEXT("{C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}") #define S_CLEANER_ALL_FILES TEXT("*") typedef struct { // Enumeration return data TCHAR PfmFile[MAX_TCHAR_PATH]; TCHAR PfbFile[MAX_TCHAR_PATH]; TCHAR MmmFile[MAX_TCHAR_PATH]; TCHAR InfName[MAX_TCHAR_PATH]; // Internal state PTSTR KeyNames; POOLHANDLE Pool; } ATM_FONT_ENUM, *PATM_FONT_ENUM; typedef INT (WINAPI ATMADDFONTEXW) ( IN OUT PWSTR MenuName, IN OUT PWORD StyleAndType, IN PCWSTR MetricsFile, IN PCWSTR FontFile, IN PCWSTR MMMFile ); typedef ATMADDFONTEXW * PATMADDFONTEXW; PATMADDFONTEXW AtmAddFontEx; typedef VOID (SHUPDATERECYCLEBINICON_PROTOTYPE)(VOID); typedef SHUPDATERECYCLEBINICON_PROTOTYPE * SHUPDATERECYCLEBINICON_PROC; typedef struct { // enumeration output PCTSTR Source; PCTSTR Dest; // private members POOLHANDLE Pool; INFSTRUCT is; } HIVEFILE_ENUM, *PHIVEFILE_ENUM; #define MAX_KEY_NAME_LIST 32768 typedef struct { // enumeration output PCTSTR BrfcaseDb; // private MEMDB_ENUM mde; } BRIEFCASE_ENUM, *PBRIEFCASE_ENUM; // // Implementation // DWORD Simplify9xAccessFlags ( IN DWORD Win9xFlags ) /*++ Routine Description: Translates the Win9x LanMan flags into NT LanMan flags (for use with Net* APIs). Full permission requires: W95_ACCESS_READ W95_ACCESS_WRITE W95_ACCESS_CREATE W95_ACCESS_DELETE W95_ACCESS_ATRIB W95_ACCESS_FINDFIRST Read-only permission requires: W95_ACCESS_READ W95_ACCESS_FINDFIRST Change-only permission requires: W95_ACCESS_WRITE W95_ACCESS_CREATE W95_ACCESS_DELETE W95_ACCESS_ATRIB Any other combination results in The returned flags are currently converted to security flags based on the following mapping: 0 - Deny All Rights ACCESS_READ - Read-Only Rights ACCESS_WRITE - Change-Only Rights ACCESS_READ|ACCESS_WRITE - Full Rights See AddAclMember for details. Arguments: Flags - The set of Win9x flags as returned by an API on Win9x Return value: The NT equivalent of the flags. --*/ { DWORD NtFlags = 0; if (BITSARESET (Win9xFlags, W95_GENERIC_WRITE)) { NtFlags |= ACCESS_WRITE; } if (BITSARESET (Win9xFlags, W95_GENERIC_READ)) { NtFlags |= ACCESS_READ; } DEBUGMSG_IF (( !NtFlags, DBG_VERBOSE, "Unsupported permission %u was translated to disable permission", Win9xFlags )); return NtFlags; } NET_API_STATUS MigNetShareAdd ( IN PTSTR ServerName, IN DWORD Level, IN PBYTE Buf, OUT PDWORD ErrParam ) /*++ Routine Description: Our private version of NetShareAdd. The real NetShareAdd does not work in GUI mode. We emulate the real thing carefully because maybe some day it WILL work and we should use it. For now, we write directly to the registry. (This function is a reverse- engineer of the NetShareAdd function.) Arguments: ServerName - Always NULL Level - Always 2 Buf - A pointer to a caller-allocated SHARE_INFO_2 buffer cast as an PBYTE ErrParam - Not supported Return value: The Win32 result --*/ { SHARE_INFO_2 *psi; DWORD rc; HKEY hKey = NULL, hKeyShares = NULL; DWORD DontCare; GROWBUFFER GrowBuf = GROWBUF_INIT; // // This function is for compatibility with NetShareAdd, because one day // the real NetShareAdd might be improved to work in GUI mode setup. // if (Level != 2) { return ERROR_INVALID_LEVEL; } psi = (SHARE_INFO_2 *) Buf; rc = TrackedRegOpenKeyEx ( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services", 0, KEY_WRITE, &hKey ); if (rc != ERROR_SUCCESS) { goto cleanup; } rc = TrackedRegCreateKeyEx ( hKey, SHARES_REGISTRY_PATH, 0, S_EMPTY, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyShares, &DontCare ); if (rc != ERROR_SUCCESS) { goto cleanup; } // // Prepare multisz // if (!MultiSzAppendVal (&GrowBuf, CSCFLAGS_VARIABLE_NAME, 0)) { rc = GetLastError(); goto cleanup; } if (!MultiSzAppendVal (&GrowBuf, MAXUSES_VARIABLE_NAME, psi->shi2_max_uses)) { rc = GetLastError(); goto cleanup; } if (!psi->shi2_path || !(*psi->shi2_path)) { rc = ERROR_INVALID_PARAMETER; goto cleanup; } if (!MultiSzAppendString (&GrowBuf, PATH_VARIABLE_NAME, psi->shi2_path)) { rc = GetLastError(); goto cleanup; } if (!MultiSzAppendVal (&GrowBuf, PERMISSIONS_VARIABLE_NAME, psi->shi2_permissions)) { rc = GetLastError(); goto cleanup; } if (psi->shi2_remark && *psi->shi2_remark) { // Safety if (TcharCount (psi->shi2_remark) >= MAXCOMMENTSZ) { psi->shi2_remark[MAXCOMMENTSZ-1] = 0; } if (!MultiSzAppendString (&GrowBuf, REMARK_VARIABLE_NAME, psi->shi2_remark)) { rc = GetLastError(); goto cleanup; } } if (!MultiSzAppendVal (&GrowBuf, TYPE_VARIABLE_NAME, psi->shi2_type)) { rc = GetLastError(); goto cleanup; } // terminate multi-sz string chain if (!MultiSzAppend (&GrowBuf, S_EMPTY)) { rc = GetLastError(); goto cleanup; } // // Save to registry // rc = RegSetValueEx (hKeyShares, psi->shi2_netname, 0, REG_MULTI_SZ, GrowBuf.Buf, GrowBuf.End); cleanup: if (hKeyShares) { CloseRegKey (hKeyShares); } if (hKey) { CloseRegKey (hKey); } FreeGrowBuffer (&GrowBuf); return rc; } NET_API_STATUS MigNetShareSetInfo ( IN PTSTR Server, // ignored IN PTSTR NetName, IN DWORD Level, IN PBYTE Buf, OUT PDWORD ErrParam // ignored ) /*++ Routine Description: MigNetShareSetInfo implements a NetShareSetInfo emulation routine, because the real routine does not work properly in GUI mode setup. See SDK documentation for details. Arguments: Server - Unused NetName - Specifies the share name to create. Level - Specifies the API level (must be 1501) Buf - Specifies a filled SHARE_INFO_1501 structure. ErrParam - Unused Return Value: The Win32 status code. --*/ { SHARE_INFO_1501 *psi; DWORD rc; HKEY hKey; DWORD DontCare; TCHAR KeyName[MAX_TCHAR_PATH]; DWORD Len; if (Level != 1501) { return ERROR_INVALID_LEVEL; } psi = (SHARE_INFO_1501 *) Buf; // // Verify share exists // StringCopyW ( KeyName, L"SYSTEM\\CurrentControlSet\\Services\\" SHARES_REGISTRY_PATH ); rc = TrackedRegOpenKeyEx ( HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &hKey ); if (rc != ERROR_SUCCESS) { return rc; } rc = RegQueryValueEx (hKey, NetName, NULL, NULL, NULL, NULL); CloseRegKey (hKey); if (rc != ERROR_SUCCESS) { if (rc == ERROR_FILE_NOT_FOUND) { rc = ERROR_INVALID_SHARENAME; } return rc; } // // Save security descriptor as binary type in registry // StringCopy ( KeyName, L"SYSTEM\\CurrentControlSet\\Services\\" SHARES_SECURITY_REGISTRY_PATH ); rc = TrackedRegCreateKeyEx ( HKEY_LOCAL_MACHINE, KeyName, 0, S_EMPTY, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &DontCare ); if (rc != ERROR_SUCCESS) { return rc; } Len = GetSecurityDescriptorLength (psi->shi1501_security_descriptor); rc = RegSetValueEx ( hKey, NetName, 0, REG_BINARY, (PBYTE) psi->shi1501_security_descriptor, Len ); CloseRegKey (hKey); return rc; } BOOL pCreateNetShare ( IN PCTSTR NetName, IN PCTSTR Path, IN PCTSTR Remark, IN DWORD Type, IN DWORD Permissions ) /*++ Routine Description: pCreateNetShare is a wrapper to the Net APIs. Arguments: NetName - Specifies the share name Path - Specifies the local path to be shared Remark - Specifies the remark to register with the share Type - Specifies the share type Permissions - Specifies the Win9x share permissions, used only for logging errors. Return Value: TRUE if the share was created, FALSE otherwise. --*/ { SHARE_INFO_2 si2; DWORD rc; PWSTR UnicodePath; BOOL b = FALSE; UnicodePath = (PWSTR) CreateUnicode (Path); MYASSERT (UnicodePath); __try { // // Make NetShareAdd call // ZeroMemory (&si2, sizeof (si2)); si2.shi2_netname = (PTSTR) NetName; si2.shi2_type = (WORD) Type; si2.shi2_remark = (PTSTR) Remark; si2.shi2_permissions = 0; si2.shi2_max_uses = 0xffffffff; si2.shi2_path = UnicodePath; si2.shi2_passwd = NULL; rc = MigNetShareAdd (NULL, 2, (PBYTE) (&si2), NULL); if (rc != ERROR_SUCCESS) { SetLastError (rc); DEBUGMSG (( DBG_ERROR, "CreateShares: NetShareAdd failed for %s ('%s'), permissions=%x", NetName, Path, Permissions )); if (Permissions == W95_GENERIC_NONE) { LOG ((LOG_ERROR, (PCSTR)MSG_UNABLE_TO_CREATE_ACL_SHARE, NetName, Path)); } else if (Permissions != W95_GENERIC_READ) { LOG ((LOG_ERROR, (PCSTR)MSG_UNABLE_TO_CREATE_RO_SHARE, NetName, Path)); } else { LOG ((LOG_ERROR, (PCSTR)MSG_UNABLE_TO_CREATE_SHARE, NetName, Path)); } __leave; } b = TRUE; } __finally { DestroyUnicode (UnicodePath); } return b; } VOID LogUsersWhoFailed ( PBYTE AclMemberList, DWORD Members, PCTSTR Share, PCTSTR Path ) /*++ Routine Description: LogUsersWhoFailed implements logic to log the users who could not be added to a share. If there are a small number of users, a popup is given with each name. Otherwise, the share users are logged, and a popup tells the installer to look in the log for the list. Arguments: AclMemberList - Specifies the ACL data structure containing all the user names that need to be logged. Members - Specifies the number of members in AclMemberList. Share - Specifies the share name that could not be added. Path - Specifies the share path that could not be added. Return Value: None. --*/ { PACLMEMBER AclMember; DWORD d; DWORD GoodCount; DWORD BadCount; HWND Parent; GoodCount = 0; BadCount = 0; AclMember = (PACLMEMBER) AclMemberList; for (d = 0 ; d < Members ; d++) { if (AclMember->Failed) { BadCount++; } else { GoodCount++; } GetNextAclMember (&AclMember); } if (!BadCount) { return; } if (BadCount < 5) { Parent = g_ParentWnd; } else { if (!GoodCount) { LOG ((LOG_ERROR, (PCSTR)MSG_ALL_SIDS_BAD, Share, Path)); } else { LOG ((LOG_ERROR, (PCSTR)MSG_MANY_SIDS_BAD, BadCount, BadCount + GoodCount, Share, Path)); } Parent = NULL; } AclMember = (PACLMEMBER) AclMemberList; for (d = 0 ; d < Members ; d++) { if (AclMember->Failed) { LOG ((LOG_ERROR, (PCSTR)MSG_NO_USER_SID, AclMember->UserOrGroup, Share, Path)); } GetNextAclMember (&AclMember); } } BOOL SetShareAcl ( IN PCTSTR Share, IN PCTSTR Path, IN PBYTE AclMemberList, IN DWORD MemberCount ) /*++ Routine Description: SetShareAcl applies the access control list to a share that was previously created. Arguments: Share - Specifies the share name Path - Specifies the share path AclMemberList - Specifies the ACL data structure giving the user(s) with rights to the share MemberCount - Specifies the number of members in AclMemberList. Return Value: TRUE if the ACL was successfully applied to the share, FALSE otherwise. --*/ { BYTE Buf[8192]; PSECURITY_DESCRIPTOR pSD; SECURITY_DESCRIPTOR desc; PSID Sid; PACL Acl; SHARE_INFO_1501 shi1501; DWORD rc; DWORD Size; PWSTR UnicodeShare; pSD = (PSECURITY_DESCRIPTOR) Buf; // // Get Administrator's SID--they are the owner of the share // Sid = GetSidForUser (g_AdministratorsGroupStr); if (!Sid) { return FALSE; } // // Start building security descriptor // InitializeSecurityDescriptor (&desc, SECURITY_DESCRIPTOR_REVISION); if (!SetSecurityDescriptorOwner (&desc, Sid, FALSE)) { LOG ((LOG_ERROR, "Could not set %s as owner", g_AdministratorsGroupStr)); return FALSE; } // // Set the defaulted group to Domain\Domain Users (if it exists), // otherwise get SID of none // Sid = GetSidForUser (g_DomainUsersGroupStr); if (!Sid) { Sid = GetSidForUser (g_NoneGroupStr); } if (Sid) { SetSecurityDescriptorGroup (&desc, Sid, FALSE); } // // Create access allowed ACL from member list // Acl = CreateAclFromMemberList (AclMemberList, MemberCount); if (!Acl) { DEBUGMSG ((DBG_WARNING, "SetShareAcl failed because CreateAclFromMemberList failed")); return FALSE; } __try { UnicodeShare = (PWSTR) CreateUnicode (Share); MYASSERT (UnicodeShare); if (!SetSecurityDescriptorDacl (&desc, TRUE, Acl, FALSE)) { DEBUGMSG ((DBG_WARNING, "SetShareAcl failed because SetSecurityDescriptorDacl failed")); return FALSE; } // // Set security descriptor on share // Size = sizeof (Buf); if (!MakeSelfRelativeSD (&desc, pSD, &Size)) { LOG ((LOG_ERROR, "MakeSelfRelativeSD failed")); return FALSE; } ZeroMemory (&shi1501, sizeof (shi1501)); shi1501.shi1501_security_descriptor = pSD; rc = MigNetShareSetInfo (NULL, UnicodeShare, 1501, (PBYTE) &shi1501, NULL); if (rc != ERROR_SUCCESS) { SetLastError (rc); LOG ((LOG_ERROR, "NetShareSetInfo failed")); return FALSE; } } __finally { if (Acl) { FreeMemberListAcl (Acl); } DestroyUnicode (UnicodeShare); } return TRUE; } VOID DoCreateShares ( VOID ) /*++ Routine Description: DoCreateShares enumerates all the shares registered in memdb by WINNT32. For each enumeration, a share is created, and permissions or an ACL is applied. Arguments: None. Return Value: None. --*/ { MEMDB_ENUM e, e2; TCHAR Path[MEMDB_MAX]; TCHAR Remark[MEMDB_MAX]; // we overlap the Remark stack buffer instead of making another #define flagKey Remark TCHAR Password[MEMDB_MAX]; DWORD Flags; DWORD Members; DWORD shareType; GROWBUFFER NameList = GROWBUF_INIT; PCTSTR pathNT; // // Obtain shares from memdb // if (MemDbEnumItems (&e, MEMDB_CATEGORY_NETSHARES)) { do { // // Get share attributes // Flags = e.dwValue; if (!MemDbGetEndpointValueEx ( MEMDB_CATEGORY_NETSHARES, e.szName, MEMDB_FIELD_PATH, Path )) { DEBUGMSG ((DBG_WARNING, "DoCreateShares: No path found for %s", e.szName)); continue; } // IF YOU CHANGE CODE HERE: Note that flagKey is the same variable as Remark MemDbBuildKey (flagKey, MEMDB_CATEGORY_NETSHARES, e.szName, MEMDB_FIELD_TYPE, NULL); if (!MemDbGetValue (flagKey, &shareType)) { DEBUGMSG ((DBG_WARNING, "DoCreateShares: No type found for %s", e.szName)); continue; } // IF YOU CHANGE CODE HERE: Note that flagKey is the same variable as Remark if (!MemDbGetEndpointValueEx ( MEMDB_CATEGORY_NETSHARES, e.szName, MEMDB_FIELD_REMARK, Remark )) { Remark[0] = 0; } // // first check if the path changed // pathNT = GetPathStringOnNt (Path); // // Create the share and set appropriate security // if (Flags & SHI50F_ACLS) { // // Share has an ACL // if (pCreateNetShare (e.szName, pathNT, Remark, shareType, W95_GENERIC_NONE)) { // // For each user indexed, put them in an ACL member list // Members = 0; if (MemDbGetValueEx ( &e2, MEMDB_CATEGORY_NETSHARES, e.szName, MEMDB_FIELD_ACCESS_LIST )) { do { // // On Win9x, per-user flags have a 8 flags that control access. We translate // them to one of four flavors on NT: // // 1. Deny All Access: (Flags == 0) // 2. Read-Only Access: (Flags & W95_GENERIC_READ) && !(Flags & W95_GENERIC_WRITE) // 3. Change-Only Access: !(Flags & W95_GENERIC_READ) && (Flags & W95_GENERIC_WRITE) // 4. Full Access: (Flags & W95_GENERIC_FULL) == W95_GENERIC_FULL // DEBUGMSG ((DBG_NETSHARES, "Share %s user %s flags %u", e.szName, e2.szName, e2.dwValue)); if (AddAclMember ( &NameList, e2.szName, Simplify9xAccessFlags (e2.dwValue) )) { Members++; } } while (MemDbEnumNextValue (&e2)); } // // Convert member list into a real ACL and apply it to the share // if (NameList.Buf) { SetShareAcl (e.szName, pathNT, NameList.Buf, Members); LogUsersWhoFailed (NameList.Buf, Members, e.szName, pathNT); FreeGrowBuffer (&NameList); } } } else { // // Determine if a password is set // Password[0] = 0; if (Flags & SHI50F_RDONLY) { MemDbGetEndpointValueEx ( MEMDB_CATEGORY_NETSHARES, e.szName, MEMDB_FIELD_RO_PASSWORD, Password ); } if (!Password[0] && (Flags & SHI50F_FULL)) { MemDbGetEndpointValueEx ( MEMDB_CATEGORY_NETSHARES, e.szName, MEMDB_FIELD_RW_PASSWORD, Password ); } // // Enable all permissions for full access // Enable read-only permissions for read-only shares // Disable all permissions for no access // if (!Password[0]) { if (Flags & SHI50F_FULL) { Flags = W95_GENERIC_FULL; } else if (Flags & SHI50F_RDONLY) { Flags = W95_GENERIC_READ; } else if (Flags) { DEBUGMSG ((DBG_WHOOPS, "Flags (0x%X) is not 0, SHI50F_FULL or SHI50F_RDONLY", Flags)); Flags = W95_GENERIC_NONE; } } else { DEBUGMSG ((DBG_VERBOSE, "Password on share %s is not supported", e.szName)); Flags = W95_GENERIC_NONE; } // // We do not support share-level security with passwords. We // always create the share, but if a password exists, we // deny everyone access. // pCreateNetShare (e.szName, pathNT, Remark, shareType, Flags); Members = 0; if (AddAclMember ( &NameList, g_EveryoneStr, Simplify9xAccessFlags (Flags) )) { Members++; } // // Convert member list into a real ACL and apply it to the share // if (NameList.Buf) { SetShareAcl (e.szName, pathNT, NameList.Buf, Members); FreeGrowBuffer (&NameList); } } FreePathString (pathNT); } while (MemDbEnumNextValue (&e)); } } BOOL pUpdateRecycleBin ( VOID ) /*++ Routine Description: Calls SHUpdateRecycleBinIcon to reset the status of the recycle bin. This operation takes a few seconds as all hard drives are scanned, the recycle bin database is read, and each entry in the database is verified. Arguments: None Return value: TRUE - the operation was successful FALSE - the operation failed (either LoadLibrary or GetProcAddress) --*/ { SHUPDATERECYCLEBINICON_PROC Fn; HINSTANCE LibInst; BOOL b = TRUE; LibInst = LoadLibrary (S_SHELL32_DLL); if (!LibInst) { return FALSE; } Fn = (SHUPDATERECYCLEBINICON_PROC) GetProcAddress ( LibInst, S_ANSI_SHUPDATERECYCLEBINICON ); if (Fn) { // // Scan all hard disks and validate the recycle bin status // Fn(); } else { b = FALSE; } FreeLibrary (LibInst); return TRUE; } VOID pFixLogonDomainIfUserIsAdministrator ( VOID ) /*++ Routine Description: pFixLogonDomainIfUserIsAdministrator handles a special error case where the logon domain is not equivalent to the computer name, but the user is named Administrator. In this case, we change the default logon domain to be the computer name. Arguments: None Return value: none --*/ { PCTSTR AdministratorAcct; HKEY Key; PCTSTR Data; AdministratorAcct = GetStringResource (MSG_ADMINISTRATOR_ACCOUNT); if (AdministratorAcct) { Key = OpenRegKeyStr (S_WINLOGON_KEY); if (Key) { Data = GetRegValueString (Key, S_DEFAULT_USER_NAME); if (Data) { if (!StringCompare (Data, AdministratorAcct)) { // // Account name exactly matches our Administrator // string, so there is a good chance we wrote // this string. Therefore, we need to write the // computer name as the default domain. // if (g_ComputerName[0]) { RegSetValueEx ( Key, S_DEFAULT_DOMAIN_NAME, 0, REG_SZ, (PBYTE) g_ComputerName, SizeOfString (g_ComputerName) ); } } MemFree (g_hHeap, 0, Data); } CloseRegKey (Key); } FreeStringResource (AdministratorAcct); } } DWORD ProcessLocalMachine_First ( DWORD Request ) { if (Request == REQUEST_QUERYTICKS) { return TICKS_INI_ACTIONS_FIRST + TICKS_INI_MOVE + TICKS_INI_CONVERSION + TICKS_INI_MIGRATION; } // // We process the local machine in the following order: // // Initialization: // (1) Reload memdb // // Ini files conversion and mapping // DEBUGMSG ((DBG_INIFILES, "INI Files Actions.First - START")); DEBUGLOGTIME (("Starting function: DoIniActions")); if (!DoIniActions (INIACT_WKS_FIRST)) { LOG ((LOG_ERROR, "Process Local Machine: Could not perform one or more INI Files Actions.First")); } DEBUGLOGTIME (("Function complete: DoIniActions")); TickProgressBarDelta (TICKS_INI_ACTIONS_FIRST); DEBUGMSG ((DBG_INIFILES, "INI Files Actions.First - STOP")); DEBUGMSG ((DBG_INIFILES, "INI file moving - START")); DEBUGLOGTIME (("Starting function: MoveIniSettings")); if (!MoveIniSettings ()) { LOG ((LOG_ERROR, "Process Local Machine: Could not move one or more .INI files settings.")); return GetLastError(); } DEBUGLOGTIME (("Function complete: MoveIniSettings")); TickProgressBarDelta (TICKS_INI_MOVE); DEBUGMSG ((DBG_INIFILES, "INI file moving - STOP")); DEBUGMSG ((DBG_INIFILES, "INI file conversion - START")); DEBUGLOGTIME (("Starting function: ConvertIniFiles")); if (!ConvertIniFiles ()) { LOG ((LOG_ERROR, "Process Local Machine: Could not convert one or more .INI files.")); return GetLastError(); } DEBUGLOGTIME (("Function complete: ConvertIniFiles")); TickProgressBarDelta (TICKS_INI_CONVERSION); DEBUGMSG ((DBG_INIFILES, "INI file conversion - STOP")); DEBUGMSG ((DBG_INIFILES, "INI file migration - START")); DEBUGLOGTIME (("Starting function: ProcessIniFileMapping")); if (!ProcessIniFileMapping (FALSE)) { LOG ((LOG_ERROR, "Process Local Machine: Could not migrate one or more .INI files.")); return GetLastError(); } DEBUGLOGTIME (("Function complete: ProcessIniFileMapping")); TickProgressBarDelta (TICKS_INI_MIGRATION); DEBUGMSG ((DBG_INIFILES, "INI file migration - STOP")); return ERROR_SUCCESS; } VOID pTurnOffNetAccountWizard ( VOID ) /*++ Routine Description: pTurnOffNetAccountWizard removes the RunNetAccessWizard key to keep the network account wizard from appearing before the first logon. Arguments: None. Return Value: None. --*/ { HKEY Key; Key = OpenRegKeyStr (TEXT("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon")); if (Key) { RegDeleteValue (Key, TEXT("RunNetAccessWizard")); CloseRegKey (Key); } else { DEBUGMSG ((DBG_WARNING, "Could not open key for RunNetAccessWizard value")); } } typedef struct _OLE_CONTROL_DATA { LPWSTR FullPath; LPCWSTR RegType; } OLE_CONTROL_DATA, *POLE_CONTROL_DATA; DWORD RegisterIndividualOleControl( POLE_CONTROL_DATA OleControlData ) { PROCESS_INFORMATION processInfo; STARTUPINFO startupInfo; WCHAR cmdLine [MAX_PATH] = L""; WCHAR cmdOptions [MAX_PATH] = L""; DWORD WaitResult; BOOL b = TRUE; ZeroMemory (&startupInfo, sizeof (STARTUPINFO)); startupInfo.cb = sizeof (STARTUPINFO); if (OleControlData->RegType && (*OleControlData->RegType == L'B')) { // install and register wcscpy (cmdOptions, L"/s /i"); } else if (OleControlData->RegType && (*OleControlData->RegType == L'R')) { // register wcscpy (cmdOptions, L"/s"); } else if (OleControlData->RegType && (*OleControlData->RegType == L'I')) { // install wcscpy (cmdOptions, L"/s /i /n"); } else if ((OleControlData->RegType == NULL) || (*OleControlData->RegType == L'\0')) { // register wcscpy (cmdOptions, L"/s"); } wsprintf (cmdLine, L"%s\\regsvr32.exe %s %s", g_System32Dir, cmdOptions, OleControlData->FullPath); if (CreateProcess (NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &startupInfo, &processInfo)) { WaitResult = WaitForSingleObject (processInfo.hProcess, 1000 * 60 * 10 ); if (WaitResult == WAIT_TIMEOUT) { DEBUGMSG ((DBG_ERROR, "Timeout installing and/or registering OLE control %s", OleControlData->FullPath)); b = FALSE; } CloseHandle (processInfo.hProcess); CloseHandle (processInfo.hThread); } else { DEBUGMSG ((DBG_ERROR, "Create process failed: %s", cmdLine)); b = FALSE; } return b; } typedef struct _KNOWN_DIRS { PCWSTR DirId; PCWSTR Translation; } KNOWN_DIRSW, *PKNOWN_DIRSW; KNOWN_DIRSW g_KnownDirsW [] = { {L"10" , g_WinDir}, {L"11" , g_System32Dir}, {L"24" , g_WinDrive}, {L"16422" , g_ProgramFiles}, {L"16427" , g_ProgramFilesCommon}, {NULL, NULL} }; BOOL pConvertDirName ( PCWSTR OldDirName, PWSTR NewDirName ) { PCWSTR OldDirCurr = OldDirName; PCWSTR OldDirNext; PKNOWN_DIRSW p; NewDirName[0] = 0; OldDirNext = wcschr (OldDirCurr, L'\\'); if (OldDirNext == NULL) { OldDirNext = wcschr (OldDirCurr, 0); } StringCopyABW (NewDirName, OldDirCurr, OldDirNext); p = g_KnownDirsW; while (p->DirId!= NULL) { if (StringIMatchW (NewDirName, p->DirId)) { StringCopyW (NewDirName, p->Translation); break; } p++; } StringCatW (NewDirName, OldDirNext); return TRUE; } BOOL RegisterOleControls( VOID ) { INFCONTEXT InfLine; WCHAR DirId [MAX_PATH]; WCHAR SubDir [MAX_PATH]; WCHAR Filename [MAX_PATH]; WCHAR RegType [MAX_PATH]; WCHAR FullPathTemp[MAX_PATH]; WCHAR FullPath[MAX_PATH]; BOOL b; DWORD d; UINT Line; WCHAR OldCD[MAX_PATH]; OLE_CONTROL_DATA OleControlData; b = TRUE; Line = 0; // // Preserve current directory just in case // d = GetCurrentDirectory(MAX_PATH,OldCD); if(!d || (d >= MAX_PATH)) { OldCD[0] = 0; } if(SetupFindFirstLine(g_WkstaMigInf, L"Win9xUpg_OleControls", NULL, &InfLine)) { do { Line++; if (!SetupGetStringField (&InfLine, 1, DirId, MAX_PATH, NULL) || !SetupGetStringField (&InfLine, 2, SubDir, MAX_PATH, NULL) || !SetupGetStringField (&InfLine, 3, Filename, MAX_PATH, NULL) || !SetupGetStringField (&InfLine, 4, RegType, MAX_PATH, NULL) ) { DEBUGMSGW ((DBG_ERROR, "Bad line while registering controls %d", Line)); } else { DEBUGMSG ((DBG_VERBOSE, "SETUP: filename for file to register is %s", Filename)); // // Get full path to dll // if (pConvertDirName (DirId, FullPathTemp)) { wcscpy (FullPath, FullPathTemp); if (*SubDir) { wcscat (FullPath, L"\\"); wcscat (FullPath, SubDir); } SetCurrentDirectory(FullPath); wcscat (FullPath, L"\\"); wcscat (FullPath, Filename); OleControlData.FullPath = FullPath; OleControlData.RegType = RegType; RegisterIndividualOleControl (&OleControlData); } else { DEBUGMSG ((DBG_ERROR, "SETUP: dll skipped, bad dirid %s", DirId)); b = FALSE; } } } while(SetupFindNextLine(&InfLine,&InfLine)); } if(OldCD[0]) { SetCurrentDirectory(OldCD); } return(b); } DWORD ProcessLocalMachine_Last ( DWORD Request ) { DWORD rc; #ifdef VAR_PROGRESS_BAR CHAR SystemDatPath[MAX_MBCHAR_PATH]; WIN32_FIND_DATAA fd; HANDLE h; DWORD SizeKB; #endif static LONG g_TicksHklm; if (Request == REQUEST_QUERYTICKS) { #ifdef VAR_PROGRESS_BAR // // estimate g_TicksHklm function of size of file system.dat // StringCopyA (SystemDatPath, g_SystemHiveDir); StringCatA (SystemDatPath, "system.dat"); h = FindFirstFileA (SystemDatPath, &fd); if (h != INVALID_HANDLE_VALUE) { FindClose (h); MYASSERT (!fd.nFileSizeHigh); SizeKB = (fd.nFileSizeLow + 511) / 1024; DEBUGLOGTIME (("ProcessLocalMachine_Last: system.dat size = %lu KB", SizeKB)); // // statistics show that average time is 243 * (filesize in KB) - 372000 // I'll use 256 instead just to make sure the progress bar will not stop // at the end looking like it's hanged // The checked build is much slower (about 1.5 times) // #ifdef DEBUG g_TicksHklm = SizeKB * 400; #else g_TicksHklm = SizeKB * 256; #endif } else { // // what's wrong here? // MYASSERT (FALSE); g_TicksHklm = TICKS_HKLM; } #else // !defined VAR_PROGRESS_BAR g_TicksHklm = TICKS_HKLM; #endif return TICKS_INI_MERGE + g_TicksHklm + TICKS_SHARES + TICKS_LINK_EDIT + TICKS_DOSMIG_SYS + TICKS_UPDATERECYCLEBIN + TICKS_STF + TICKS_RAS + TICKS_TAPI + TICKS_MULTIMEDIA + TICKS_INI_ACTIONS_LAST; } // // We process the local machine in the following order: // // Initialization: // (1) Reload memdb // // Local machine registry preparation: // // (1) Process wkstamig.inf // (2) Merge Win95 registry with NT hive // // Process instructions written to memdb: // // (1) Create Win95 shares // (2) Process LinkEdit section // // // Load in default MemDb state, or at least delete everything if // memdb.dat does not exist. // MemDbLoad (GetMemDbDat()); DEBUGMSG ((DBG_INIFILES, "INI file merge - START")); DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: MergeIniSettings")); if (!MergeIniSettings ()) { LOG ((LOG_ERROR, "Process Local Machine: Could not merge one or more .INI files.")); return GetLastError(); } TickProgressBarDelta (TICKS_INI_MERGE); DEBUGMSG ((DBG_INIFILES, "INI file merge - STOP")); DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: MergeIniSettings")); // // Process local machine migration rules // DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: MergeRegistry")); if (!MergeRegistry (S_WKSTAMIG_INF, NULL)) { LOG ((LOG_ERROR, "Process Local Machine: MergeRegistry failed for wkstamig.inf")); return GetLastError(); } DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: MergeRegistry")); TickProgressBarDelta (g_TicksHklm); // // Process memdb nodes // DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: DoCreateShares")); DoCreateShares(); // we ignore all errors DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: DoCreateShares")); TickProgressBarDelta (TICKS_SHARES); DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: DoLinkEdit")); if (!DoLinkEdit()) { LOG ((LOG_ERROR, "Process Local Machine: DoLinkEdit failed.")); return GetLastError(); } DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: DoLinkEdit")); TickProgressBarDelta (TICKS_LINK_EDIT); // // Handle DOS system migration. // DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: DosMigNt_System")); __try { if (DosMigNt_System() != EXIT_SUCCESS) { LOG((LOG_ERROR, "Process Local Machine: DosMigNt_System failed.")); } } __except(TRUE) { DEBUGMSG ((DBG_WHOOPS, "Exception in DosMigNt_System")); } DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: DosMigNt_System")); TickProgressBarDelta (TICKS_DOSMIG_SYS); // // Make the recycled bin the correct status // DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: pUpdateRecycleBin")); if (!pUpdateRecycleBin ()) { LOG ((LOG_ERROR, "Process Local Machine: Could not update recycle bin.")); } DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: pUpdateRecycleBin")); TickProgressBarDelta (TICKS_UPDATERECYCLEBIN); // // Migrate all .STF files (ACME Setup) // DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: ProcessStfFiles")); if (!ProcessStfFiles()) { LOG ((LOG_ERROR, "Process Local Machine: Could not migrate one or more .STF files.")); } DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: ProcessStfFiles")); TickProgressBarDelta (TICKS_STF); DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: Ras_MigrateSystem")); if (!Ras_MigrateSystem()) { LOG ((LOG_ERROR, "Ras MigrateSystem: Error migrating system.")); } DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: Ras_MigrateSystem")); TickProgressBarDelta (TICKS_RAS); DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: Tapi_MigrateSystem")); if (!Tapi_MigrateSystem()) { LOG ((LOG_ERROR, "Tapi MigrateSystem: Error migrating system TAPI settings.")); } DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: Tapi_MigrateSystem")); TickProgressBarDelta (TICKS_TAPI); DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: RestoreMMSettings_System")); if (!RestoreMMSettings_System ()) { LOG ((LOG_ERROR, "Process Local Machine: Could not restore multimedia settings.")); return GetLastError(); } DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: RestoreMMSettings_System")); TickProgressBarDelta (TICKS_MULTIMEDIA); DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: DoIniActions.Last")); DEBUGMSG ((DBG_INIFILES, "INI Files Actions.Last - START")); if (!DoIniActions (INIACT_WKS_LAST)) { LOG ((LOG_ERROR, "Process Local Machine: Could not perform one or more INI Files Actions.Last")); } DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: DoIniActions.Last")); TickProgressBarDelta (TICKS_INI_ACTIONS_LAST); DEBUGMSG ((DBG_INIFILES, "INI Files Actions.Last - STOP")); DEBUGLOGTIME (("ProcessLocalMachine_Last: Starting function: RegisterOleControls")); RegisterOleControls (); DEBUGLOGTIME (("ProcessLocalMachine_Last: Function complete: RegisterOleControls")); // // Blow away the Network Account Wizard (so fast it doesn't need ticks) // pTurnOffNetAccountWizard(); // // Update security for Crypto group // rc = SetRegKeySecurity ( TEXT("HKLM\\Software\\Microsoft\\Cryptography\\MachineKeys"), SF_EVERYONE_FULL, NULL, NULL, TRUE ); return ERROR_SUCCESS; } BOOL pEnumWin9xHiveFileWorker ( IN OUT PHIVEFILE_ENUM EnumPtr ) /*++ Routine Description: pEnumWin9xHiveFileWorker parses wkstamig.inf to get the source path to a Win9x registry hive, and it gets the destination of where the hive should be migrated to. The source is tested, and if it doesn't exist, the function returns FALSE. Environment variables in both the source or dest are expanded before this function returns success. Arguments: EnumPtr - Specifies partially completed enumeration structure (the EnumPtr->is member must be valid). Receives the source and destination of the hive to be migrated. Return Value: Returns TRUE if a hive needs to be transfered from EnumPtr->Source to EnumPtr->Dest, otherwise FALSE. --*/ { PCTSTR Source; PCTSTR Dest; // // Get the source and dest from the INF // Source = InfGetStringField (&EnumPtr->is, 0); Dest = InfGetStringField (&EnumPtr->is, 1); if (!Source || !Dest) { DEBUGMSG ((DBG_WHOOPS, "wkstamig.inf HiveFilesToConvert is not correct")); return FALSE; } // // Expand the source and dest // if (EnumPtr->Source) { FreeText (EnumPtr->Source); } EnumPtr->Source = ExpandEnvironmentText (Source); if (EnumPtr->Dest) { FreeText (EnumPtr->Dest); } EnumPtr->Dest = ExpandEnvironmentText (Dest); // // The source must exist // if (!DoesFileExist (EnumPtr->Source)) { return FALSE; } return TRUE; } VOID pAbortHiveFileEnum ( IN OUT PHIVEFILE_ENUM EnumPtr ) /*++ Routine Description: pAbortHiveFileEnum cleans up the allocations from an active enumeration of Win9x hive files. This routine must be called by the enum first/next when the enumeration completes, or it must be called by the code using the enumeration. It is safe to call this routine in both places. Arguments: EnumPtr - Specifies the enumeration that needs to be aborted or that has completed successfully. Receives a zero'd struct. Return Value: None. --*/ { if (EnumPtr->Pool) { PoolMemDestroyPool (EnumPtr->Pool); } if (EnumPtr->Source) { FreeText (EnumPtr->Source); } if (EnumPtr->Dest) { FreeText (EnumPtr->Dest); } ZeroMemory (EnumPtr, sizeof (HIVEFILE_ENUM)); } BOOL pEnumNextWin9xHiveFile ( IN OUT PHIVEFILE_ENUM EnumPtr ) /*++ Routine Description: pEnumNextWin9xHiveFile continues the enumeration wksatmig.inf until either a hive needing migration is found, or no more INF entries are left. Arguments: EnumPtr - Specifies an enumeration structure that was initialized by pEnumFirstWin9xHiveFile. Receives the next hive file source & dest enumeration if one is available. Return Value: TRUE if a Win9x hive file needs to be migrated (its source and dest specified in EnumPtr). FALSE if no more hive files are to be processed. --*/ { do { if (!InfFindNextLine (&EnumPtr->is)) { pAbortHiveFileEnum (EnumPtr); return FALSE; } } while (!pEnumWin9xHiveFileWorker (EnumPtr)); return TRUE; } BOOL pEnumFirstWin9xHiveFile ( OUT PHIVEFILE_ENUM EnumPtr ) /*++ Routine Description: pEnumFirstWin9xHiveFile begins an enumeration of Win9x registry files that need to be migrated either to the NT registry, or to an NT registry hive file. Arguments: EnumPtr - Receives the source Win9x hive file and the destination (either a file or a registry path). Return Value: TRUE if a Win9x hive file was found and needs to be migrated, FALSE if no hive file migration is needed. --*/ { ZeroMemory (EnumPtr, sizeof (HIVEFILE_ENUM)); // // Begin the enumeration of the Hive Files section of wkstamig.inf // EnumPtr->Pool = PoolMemInitNamedPool ("Hive File Enum"); InitInfStruct (&EnumPtr->is, NULL, EnumPtr->Pool); if (!InfFindFirstLine (g_WkstaMigInf, S_WKSTAMIG_HIVE_FILES, NULL, &EnumPtr->is)) { pAbortHiveFileEnum (EnumPtr); return FALSE; } // // Attempt to return the first hive // if (pEnumWin9xHiveFileWorker (EnumPtr)) { return TRUE; } // // Hive does not exist, continue enumeration // return pEnumNextWin9xHiveFile (EnumPtr); } BOOL pTransferWin9xHiveToRegKey ( IN PCTSTR Win9xHive, IN PCTSTR NtRootKey ) /*++ Routine Description: pTransferWin9xHiveToRegKey maps in a Win9x hive file, enumerates all the keys and values, and transfers them to the NT registry. Arguments: Win9xHive - Specifies the registry hive file (a Win9x hive) NtRootKey - Specifies the path to the NT registry destination, such as HKLM\foo. Return Value: TRUE if the hive file was transferred without an error, FALSE otherwise. Use GetLastError to get the error code. --*/ { LONG rc; HKEY DestKey; BOOL b = FALSE; REGTREE_ENUM e; REGVALUE_ENUM e2; PCTSTR SubKey; HKEY DestSubKey; BOOL EnumAbort = FALSE; PBYTE DataBuf; GROWBUFFER Data = GROWBUF_INIT; DWORD Type; DWORD Size; BOOL CloseDestSubKey = FALSE; // // Map the hive into a temporary key // rc = Win95RegLoadKey ( HKEY_LOCAL_MACHINE, S_HIVE_TEMP, Win9xHive ); if (rc != ERROR_SUCCESS) { DEBUGMSG ((DBG_ERROR, "Can't load %s for transfer", Win9xHive)); return FALSE; } __try { DestKey = CreateRegKeyStr (NtRootKey); if (!DestKey) { DEBUGMSG ((DBG_ERROR, "Can't create %s", NtRootKey)); __leave; } if (EnumFirstRegKeyInTree95 (&e, TEXT("HKLM\\") S_HIVE_TEMP)) { EnumAbort = TRUE; do { // // Create the NT destination; if SubKey is empty, then // use the root key for the destination. // SubKey = (PCTSTR) ((PBYTE) e.FullKeyName + e.EnumBaseBytes); if (*SubKey) { DestSubKey = CreateRegKey (DestKey, SubKey); if (!DestSubKey) { DEBUGMSG ((DBG_ERROR, "Can't create subkey %s", SubKey)); __leave; } CloseDestSubKey = TRUE; } else { DestSubKey = DestKey; } // // Copy all values in 9x key to NT // if (EnumFirstRegValue95 (&e2, e.CurrentKey->KeyHandle)) { do { Data.End = 0; DataBuf = GrowBuffer (&Data, e2.DataSize); if (!DataBuf) { DEBUGMSG ((DBG_ERROR, "Data size is too big: %s", e2.DataSize)); __leave; } Size = e2.DataSize; rc = Win95RegQueryValueEx ( e2.KeyHandle, e2.ValueName, NULL, &Type, DataBuf, &Size ); if (rc != ERROR_SUCCESS) { DEBUGMSG (( DBG_ERROR, "Can't read enumerated value:\n" " %s\n" " %s [%s]", Win9xHive, e.FullKeyName, e2.ValueName )); __leave; } MYASSERT (Size == e2.DataSize); rc = RegSetValueEx ( DestSubKey, e2.ValueName, 0, e2.Type, DataBuf, Size ); if (rc != ERROR_SUCCESS) { DEBUGMSG (( DBG_ERROR, "Can't write enumerated value:\n" " %s\n" " %s\\%s [%s]", Win9xHive, NtRootKey, SubKey, e2.ValueName )); __leave; } } while (EnumNextRegValue95 (&e2)); } if (CloseDestSubKey) { CloseRegKey (DestSubKey); CloseDestSubKey = FALSE; } } while (EnumNextRegKeyInTree95 (&e)); EnumAbort = FALSE; } ELSE_DEBUGMSG ((DBG_WARNING, "%s is empty", Win9xHive)); b = TRUE; } __finally { PushError(); if (CloseDestSubKey) { CloseRegKey (DestSubKey); } if (EnumAbort) { AbortRegKeyTreeEnum95 (&e); } Win95RegUnLoadKey (HKEY_LOCAL_MACHINE, TEXT("$$$")); if (DestKey) { CloseRegKey (DestKey); } FreeGrowBuffer (&Data); PopError(); } return b; } BOOL pTransferWin9xHive ( IN PCTSTR Win9xHive, IN PCTSTR Destination ) /*++ Routine Description: pTransferWin9xHive transfers a Win9x registry hive file (foo.dat) to either an NT registry file, or a key in the NT registry. The source and destination can be the same file. Arguments: Win9xHive - Specifies the registry hive file path (a Win9x hive file). Destination - Specifies either a path or NT registry location where the Win9xHive should be transfered to. Return Value: TRUE if the hive was transfered, FALSE otherwise. Call GetLastError for an error code. --*/ { PCTSTR DestHive; BOOL ToHiveFile; HKEY Key; LONG rc; // // Determine if destination is a hive file or a reg location // if (_istalpha (Destination[0]) && Destination[1] == TEXT(':')) { ToHiveFile = TRUE; DestHive = TEXT("HKLM\\") S_TRANSFER_HIVE; } else { ToHiveFile = FALSE; DestHive = Destination; } // // Blast the Win9x hive data to the temp location // if (!pTransferWin9xHiveToRegKey (Win9xHive, DestHive)) { RegDeleteKey (HKEY_LOCAL_MACHINE, S_TRANSFER_HIVE); return FALSE; } // // Save the key if the destination is a hive file // if (ToHiveFile) { Key = OpenRegKeyStr (DestHive); if (!Key) { DEBUGMSG ((DBG_ERROR, "Transfer hive key %s does not exist", DestHive)); return FALSE; } rc = RegSaveKey (Key, Destination, NULL); CloseRegKey (Key); RegDeleteKey (HKEY_LOCAL_MACHINE, S_TRANSFER_HIVE); if (rc != ERROR_SUCCESS) { DEBUGMSG ((DBG_ERROR, "Win9x hive %s could not be saved to %s", Win9xHive, Destination)); return FALSE; } } // // Delete the source file if the destination is not the same // if (!ToHiveFile || !StringIMatch (Win9xHive, DestHive)) { // // By adding info to memdb, we must enforce a rule that // memdb cannot be reloaded. Otherwise we lose our // changes. // DeclareTemporaryFile (Win9xHive); #ifdef DEBUG g_NoReloadsAllowed = TRUE; #endif } return TRUE; } DWORD ConvertHiveFiles ( DWORD Request ) /*++ Routine Description: ConvertHiveFiles enumerates all the hive files on the system that need conversion, and calls pTransferWin9xHive to migrate them to the destination specified in wkstamig.inf. Arguments: Request - Specifies the progress bar-driven request. Return Value: If Request is REQUEST_QUERYTICKS, the return value is the number of ticks this routine is expected to take. Otherwise, the return value is ERROR_SUCCESS. --*/ { HIVEFILE_ENUM e; switch (Request) { case REQUEST_QUERYTICKS: return TICKS_HIVE_CONVERSION; case REQUEST_RUN: // // Enumerate all the hives that need to be processed // if (pEnumFirstWin9xHiveFile (&e)) { do { pTransferWin9xHive (e.Source, e.Dest); } while (pEnumNextWin9xHiveFile (&e)); } ELSE_DEBUGMSG ((DBG_NAUSEA, "ConvertHiveFiles: Nothing to do")); break; default: break; } return ERROR_SUCCESS; } BOOL pRegisterAtmFont ( IN PCTSTR PfmFile, IN PCTSTR PfbFile, IN PCTSTR MmmFile OPTIONAL ) /*++ Routine Description: pRegisterAtmFont calls the AtmFontExW API to register an Adobe PS font. Arguments: PfmFile - Specifies the path to the PFM file (the font metrics) PfbFile - Specifies the path to the PFB file (the font bits) MmmFile - Specifies the path to the MMM file (the new style metrics file) Return Value: TRUE if the font was registered, FALSE otherwise. --*/ { WORD StyleAndType = 0x2000; INT Result; if (AtmAddFontEx == NULL) { return FALSE; } Result = AtmAddFontEx ( NULL, &StyleAndType, PfmFile, PfbFile, MmmFile ); DEBUGMSG_IF (( Result != ERROR_SUCCESS, Result == - 1 ? DBG_WARNING : DBG_ERROR, "Font not added, result = %i.\n" " PFM: %s\n" " PFB: %s\n" " MMM: %s\n", Result, PfmFile, PfbFile, MmmFile )); return Result == ERROR_SUCCESS; } PCTSTR pGetAtmMultiSz ( POOLHANDLE Pool, PCTSTR InfName, PCTSTR SectionName, PCTSTR KeyName ) /*++ Routine Description: pGetAtmMultiSz returns a multi-sz of the ATM font file names in the order of PFM, PFB and MMM. Profile APIs are used because the key names have commas, and they are unquoted. Arguments: Pool - Specifies the pool where the multi-sz will allocate buffer space from. InfName - Specifies the full path to the INF, to be used with the profile APIs. SectionName - Specifies the section name in InfName that is being processed. KeyName - Specifies the key name to process Return Value: A pointer to a multi-sz allocated in the specified pool. --*/ { PTSTR MultiSz; PTSTR d; TCHAR FileBuf[MAX_TCHAR_PATH * 2]; UINT Bytes; GetPrivateProfileString ( SectionName, KeyName, TEXT(""), FileBuf, sizeof (FileBuf) / sizeof (FileBuf[0]), InfName ); // // Turn all commas into nuls // d = FileBuf; while (*d) { if (_tcsnextc (d) == TEXT(',')) { *d = 0; } d = _tcsinc (d); } // // Terminate the multi-sz // d++; *d = 0; d++; // // Transfer to a pool-based allocation and return it // Bytes = (UINT) ((PBYTE) d - (PBYTE) FileBuf); MultiSz = (PTSTR) PoolMemGetAlignedMemory (Pool, Bytes); CopyMemory (MultiSz, FileBuf, Bytes); return MultiSz; } BOOL pEnumAtmFontWorker ( IN OUT PATM_FONT_ENUM EnumPtr ) /*++ Routine Description: pEnumAtmFontWorker implements the logic of parsing ATM.INI to get the Adobe font names. This routine completes an enumeration started by pEnumFirstAtmFont or pEnumNextAtmFont. Arguments: EnumPtr - Specifies a partially completed enumeration structure, receives a fully completed structure. Return Value: TRUE if an ATM font was enumerated, FALSE otherwise. --*/ { PTSTR p; PCTSTR MultiSz; BOOL MetricFileExists; // // Get PFM, PFB and MMM files // MultiSz = pGetAtmMultiSz ( EnumPtr->Pool, EnumPtr->InfName, S_FONTS, EnumPtr->KeyNames ); if (!MultiSz) { return FALSE; } if (*MultiSz) { _tcssafecpy (EnumPtr->PfmFile, MultiSz, MAX_TCHAR_PATH); MultiSz = GetEndOfString (MultiSz) + 1; } if (*MultiSz) { _tcssafecpy (EnumPtr->PfbFile, MultiSz, MAX_TCHAR_PATH); } else { return FALSE; } MultiSz = pGetAtmMultiSz ( EnumPtr->Pool, EnumPtr->InfName, S_MMFONTS, EnumPtr->KeyNames ); if (MultiSz) { _tcssafecpy (EnumPtr->MmmFile, MultiSz, MAX_TCHAR_PATH); MultiSz = GetEndOfString (MultiSz) + 1; if (*MultiSz) { DEBUGMSG_IF (( !StringIMatch (MultiSz, EnumPtr->PfbFile), DBG_ERROR, "ATM.INI: MMFonts and Fonts specify two different PFBs: %s and %s", MultiSz, EnumPtr->PfbFile )); } } else { EnumPtr->MmmFile[0] = 0; } // // Special case: .MMM is listed in [Fonts] // p = _tcsrchr (EnumPtr->PfmFile, TEXT('.')); if (p && p < _tcschr (p, TEXT('\\'))) { p = NULL; } if (p && StringIMatch (p, TEXT(".mmm"))) { EnumPtr->PfmFile[0] = 0; } // // Special case: .MMM exists but is not listed in atm.ini // if (!EnumPtr->MmmFile[0]) { StringCopy (EnumPtr->MmmFile, EnumPtr->PfmFile); p = _tcsrchr (EnumPtr->MmmFile, TEXT('.')); if (p && p < _tcschr (p, TEXT('\\'))) { p = NULL; } if (p) { StringCopy (p, TEXT(".mmm")); if (!DoesFileExist (EnumPtr->MmmFile)) { EnumPtr->MmmFile[0] = 0; } } else { EnumPtr->MmmFile[0] = 0; } } // // Verify all files exist // MetricFileExists = FALSE; if (EnumPtr->PfmFile[0] && DoesFileExist (EnumPtr->PfmFile)) { MetricFileExists = TRUE; } if (EnumPtr->MmmFile[0] && DoesFileExist (EnumPtr->MmmFile)) { MetricFileExists = TRUE; } if (!DoesFileExist (EnumPtr->PfbFile) || !MetricFileExists) { DEBUGMSG (( DBG_VERBOSE, "At least one file is missing: %s, %s or %s", EnumPtr->PfmFile[0] ? EnumPtr->PfmFile : TEXT("(no PFM specified)"), EnumPtr->MmmFile[0] ? EnumPtr->MmmFile : TEXT("(no MMM specified)"), EnumPtr->PfbFile )); return FALSE; } return TRUE; } VOID pAbortAtmFontEnum ( IN OUT PATM_FONT_ENUM EnumPtr ) /*++ Routine Description: pAbortAtmFontEnum cleans up an enumeration structure after enumeration completes or if enumeration needs to be aborted. This routine can safely be called multiple times on the same structure. Arguments: EnumPtr - Specifies an initialized and possibly used enumeration structure. Return Value: None. --*/ { if (EnumPtr->Pool) { PoolMemDestroyPool (EnumPtr->Pool); } ZeroMemory (EnumPtr, sizeof (ATM_FONT_ENUM)); } BOOL pEnumNextAtmFont ( IN OUT PATM_FONT_ENUM EnumPtr ) /*++ Routine Description: pEnumNextAtmFont continues enumeration, returning either another set of ATM font paths, or FALSE. Arguments: EnumPtr - Specifies the enumeration structure started by pEnumNextAtmFont. Return Value: TRUE if another set of font paths is available, FALSE otherwise. --*/ { if (!EnumPtr->KeyNames || !(*EnumPtr->KeyNames)) { pAbortAtmFontEnum (EnumPtr); return FALSE; } // // Continue enumeration, looping until a font path set was found, // or there are no more atm.ini lines to enumerate. // do { EnumPtr->KeyNames = GetEndOfString (EnumPtr->KeyNames) + 1; if (!(*EnumPtr->KeyNames)) { pAbortAtmFontEnum (EnumPtr); return FALSE; } } while (!pEnumAtmFontWorker (EnumPtr)); return TRUE; } BOOL pEnumFirstAtmFont ( OUT PATM_FONT_ENUM EnumPtr ) /*++ Routine Description: pEnumFirstAtmFont begins the enumeration of font path sets in ATM.INI. Arguments: EnumPtr - Receivies the first set of font paths found (if any). Return Value: TRUE if a font path set was found, FALSE otherwise. --*/ { TCHAR AtmIni[MAX_TCHAR_PATH]; PTSTR FilePart; PTSTR KeyNames; UINT Bytes; // // Init structure // ZeroMemory (EnumPtr, sizeof (ATM_FONT_ENUM)); // // Find full path to atm.ini (usually in %windir%) // if (!SearchPath (NULL, TEXT("atm.ini"), NULL, MAX_TCHAR_PATH, AtmIni, &FilePart)) { DEBUGMSG ((DBG_VERBOSE, "ATM.INI not found in search path")); return FALSE; } StringCopy (EnumPtr->InfName, AtmIni); // // Establish processing pool and get all key names in [Fonts] // EnumPtr->Pool = PoolMemInitNamedPool ("ATM Font Enum"); MYASSERT (EnumPtr->Pool); KeyNames = MemAlloc (g_hHeap, 0, MAX_KEY_NAME_LIST); GetPrivateProfileString ( S_FONTS, NULL, TEXT(""), KeyNames, MAX_KEY_NAME_LIST, AtmIni ); Bytes = SizeOfMultiSz (KeyNames); EnumPtr->KeyNames = (PTSTR) PoolMemGetAlignedMemory (EnumPtr->Pool, Bytes); CopyMemory (EnumPtr->KeyNames, KeyNames, Bytes); MemFree (g_hHeap, 0, KeyNames); // // Begin enumeration // if (!(*EnumPtr->KeyNames)) { pAbortAtmFontEnum (EnumPtr); return FALSE; } if (pEnumAtmFontWorker (EnumPtr)) { return TRUE; } return pEnumNextAtmFont (EnumPtr); } DWORD MigrateAtmFonts ( DWORD Request ) /*++ Routine Description: MigrateAtmFonts is called by the progress bar to query ticks or to migrate ATM fonts. Arguments: Request - Specifies the reason the progress bar is calling the routine. Return Value: If Request is REQUEST_QUERYTICKS, then the return value is the number of estimated ticks needed to complete processing. Otherwise the return value is ERROR_SUCCESS. --*/ { ATM_FONT_ENUM e; static HANDLE AtmLib; TCHAR AtmIniPath[MAX_TCHAR_PATH]; if (Request == REQUEST_QUERYTICKS) { // // Dynamically load atmlib.dll // AtmAddFontEx = NULL; AtmLib = LoadLibrary (TEXT("atmlib.dll")); if (!AtmLib) { DEBUGMSG ((DBG_ERROR, "Cannot load entry point from atmlib.dll!")); } else { (FARPROC) AtmAddFontEx = GetProcAddress (AtmLib, "ATMAddFontExW"); DEBUGMSG_IF ((!AtmAddFontEx, DBG_ERROR, "Cannot get entry point ATMAddFontExW in atmlib.dll!")); } return AtmAddFontEx ? TICKS_ATM_MIGRATION : 0; } else if (Request != REQUEST_RUN) { return ERROR_SUCCESS; } if (AtmAddFontEx) { // // Do the ATM font migration // if (pEnumFirstAtmFont (&e)) { StringCopy (AtmIniPath, e.InfName); do { if (pRegisterAtmFont (e.PfmFile, e.PfbFile, e.MmmFile)) { DEBUGMSG ((DBG_VERBOSE, "ATM font registered %s", e.PfbFile)); } } while (pEnumNextAtmFont (&e)); DeclareTemporaryFile (AtmIniPath); #ifdef DEBUG g_NoReloadsAllowed = TRUE; #endif } // // Clean up use of atmlib.dll - we're finished // FreeLibrary (AtmLib); AtmLib = NULL; AtmAddFontEx = NULL; } return ERROR_SUCCESS; } DWORD RunSystemExternalProcesses ( IN DWORD Request ) { LONG Count; if (Request == REQUEST_QUERYTICKS) { // // Count the number of entries and multiply by a constant // Count = SetupGetLineCount (g_WkstaMigInf, S_EXTERNAL_PROCESSES); #ifdef PROGRESS_BAR DEBUGLOGTIME (("RunSystemExternalProcesses: ExternalProcesses=%ld", Count)); #endif if (Count < 1) { return 0; } return Count * TICKS_SYSTEM_EXTERN_PROCESSES; } if (Request != REQUEST_RUN) { return ERROR_SUCCESS; } // // Loop through the processes and run each of them // RunExternalProcesses (g_WkstaMigInf, NULL); return ERROR_SUCCESS; } BOOL pEnumFirstWin9xBriefcase ( OUT PBRIEFCASE_ENUM e ) { if (!MemDbGetValueEx (&e->mde, MEMDB_CATEGORY_BRIEFCASES, NULL, NULL)) { return FALSE; } e->BrfcaseDb = e->mde.szName; return TRUE; } BOOL pEnumNextWin9xBriefcase ( IN OUT PBRIEFCASE_ENUM e ) { if (!MemDbEnumNextValue (&e->mde)) { return FALSE; } e->BrfcaseDb = e->mde.szName; return TRUE; } BOOL pMigrateBriefcase ( IN PCTSTR BriefcaseDatabase, IN PCTSTR BriefcaseDir ) { HBRFCASE hbrfcase; PTSTR NtPath; BOOL Save, Success; TWINRESULT tr; BRFPATH_ENUM e; BOOL Result = TRUE; __try { g_BrfcasePool = PoolMemInitNamedPool ("Briefcase"); if (!g_BrfcasePool) { return FALSE; } tr = OpenBriefcase (BriefcaseDatabase, OB_FL_OPEN_DATABASE, NULL, &hbrfcase); if (tr == TR_SUCCESS) { if (EnumFirstBrfcasePath (hbrfcase, &e)) { Save = FALSE; Success = TRUE; do { if (StringIMatch (BriefcaseDir, e.PathString)) { // // ignore this path // continue; } NtPath = GetPathStringOnNt (e.PathString); MYASSERT (NtPath); if (!StringIMatch (NtPath, e.PathString)) { // // try to replace Win9x path with NT path // if (!ReplaceBrfcasePath (&e, NtPath)) { Success = FALSE; break; } Save = TRUE; } FreePathString (NtPath); } while (EnumNextBrfcasePath (&e)); if (!Success || Save && SaveBriefcase (hbrfcase) != TR_SUCCESS) { Result = FALSE; } } CloseBriefcase(hbrfcase); } } __finally { PoolMemDestroyPool (g_BrfcasePool); g_BrfcasePool = NULL; } return Result; } DWORD MigrateBriefcases ( DWORD Request ) /*++ Routine Description: MigrateBriefcases is called by the progress bar to query ticks or to migrate briefcases. Arguments: Request - Specifies the reason the progress bar is calling the routine. Return Value: If Request is REQUEST_QUERYTICKS, then the return value is the number of estimated ticks needed to complete processing. Otherwise the return value is ERROR_SUCCESS. --*/ { BRIEFCASE_ENUM e; TCHAR BrfcaseDir[MAX_PATH + 2]; PTSTR p; PTSTR BrfcaseDbOnNt; switch (Request) { case REQUEST_QUERYTICKS: return TICKS_MIGRATE_BRIEFCASES; case REQUEST_RUN: // // Enumerate all the briefcases that need to be processed // if (pEnumFirstWin9xBriefcase (&e)) { do { BrfcaseDbOnNt = GetPathStringOnNt (e.BrfcaseDb); MYASSERT (BrfcaseDbOnNt); // // get directory name first // if (CharCount (BrfcaseDbOnNt) <= MAX_PATH) { StringCopy (BrfcaseDir, BrfcaseDbOnNt); p = _tcsrchr (BrfcaseDir, TEXT('\\')); if (p) { *p = 0; if (!pMigrateBriefcase (BrfcaseDbOnNt, BrfcaseDir)) { LOG (( LOG_WARNING, (PCSTR)MSG_ERROR_MIGRATING_BRIEFCASE, BrfcaseDir )); } } } FreePathString (BrfcaseDbOnNt); } while (pEnumNextWin9xBriefcase (&e)); } ELSE_DEBUGMSG ((DBG_NAUSEA, "MigrateBriefcases: Nothing to do")); break; } return ERROR_SUCCESS; } DWORD RunSystemUninstallUserProfileCleanupPreparation ( IN DWORD Request ) { LONG Count; if (Request == REQUEST_QUERYTICKS) { // // Count the number of entries and multiply by a constant // Count = SetupGetLineCount (g_WkstaMigInf, S_UNINSTALL_PROFILE_CLEAN_OUT); #ifdef PROGRESS_BAR DEBUGLOGTIME (("RunSystemUninstallUserProfileCleanupPreparation: FileNumber=%ld", Count)); #endif if (Count < 1) { return 1; } return Count * TICKS_SYSTEM_UNINSTALL_CLEANUP; } if (Request != REQUEST_RUN) { return ERROR_SUCCESS; } // // Loop through the files and mark them to be deleted during uninstall // UninstallUserProfileCleanupPreparation (g_WkstaMigInf, NULL, TRUE); return ERROR_SUCCESS; } DWORD AddOptionsDiskCleaner ( DWORD Request ) { HKEY key = NULL; HKEY subKey = NULL; PCTSTR optionsPath; LONG rc; PCTSTR descText = NULL; DWORD d; if (Request == REQUEST_QUERYTICKS) { return 1; } if (Request != REQUEST_RUN) { return ERROR_SUCCESS; } optionsPath = JoinPaths (g_WinDir, TEXT("OPTIONS")); __try { if (!DoesFileExist (optionsPath)) { __leave; } key = OpenRegKeyStr (TEXT("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\VolumeCaches")); if (!key) { DEBUGMSG ((DBG_ERROR, "Can't open VolumeCaches")); __leave; } subKey = CreateRegKey (key, TEXT("Options Folder")); if (!subKey) { DEBUGMSG ((DBG_ERROR, "Can't create Options Folder")); __leave; } rc = RegSetValueEx ( subKey, TEXT(""), 0, REG_SZ, (PBYTE) S_CLEANER_GUID, sizeof (S_CLEANER_GUID) ); if (rc != ERROR_SUCCESS) { DEBUGMSG ((DBG_ERROR, "Can't write default value to Options Folder key")); } descText = GetStringResource (MSG_OPTIONS_CLEANER); rc = RegSetValueEx ( subKey, TEXT("Description"), 0, REG_SZ, (PBYTE) descText, SizeOfString (descText) ); if (rc != ERROR_SUCCESS) { DEBUGMSG ((DBG_ERROR, "Can't write Description value to Options Folder key")); } FreeStringResource (descText); descText = GetStringResource (MSG_OPTIONS_CLEANER_TITLE); rc = RegSetValueEx ( subKey, TEXT("Display"), 0, REG_SZ, (PBYTE) descText, SizeOfString (descText) ); if (rc != ERROR_SUCCESS) { DEBUGMSG ((DBG_ERROR, "Can't write Display value to Options Folder key")); } rc = RegSetValueEx ( subKey, TEXT("FileList"), 0, REG_SZ, (PBYTE) S_CLEANER_ALL_FILES, sizeof (S_CLEANER_ALL_FILES) ); if (rc != ERROR_SUCCESS) { DEBUGMSG ((DBG_ERROR, "Can't write FileList value to Options Folder key")); } rc = RegSetValueEx ( subKey, TEXT("Folder"), 0, REG_SZ, (PBYTE) optionsPath, SizeOfString (optionsPath) ); if (rc != ERROR_SUCCESS) { DEBUGMSG ((DBG_ERROR, "Can't write Folder value to Options Folder key")); } d = 0x17F; // see shell\applets\cleaner\dataclen\common.h DDEVCF_* flags rc = RegSetValueEx ( subKey, TEXT("Flags"), 0, REG_DWORD, (PBYTE) &d, sizeof (d) ); if (rc != ERROR_SUCCESS) { DEBUGMSG ((DBG_ERROR, "Can't write flags to Options Folder key")); } } __finally { FreePathString (optionsPath); if (key) { CloseRegKey (key); } if (subKey) { CloseRegKey (subKey); } FreeStringResource (descText); } return ERROR_SUCCESS; }