/*++ Copyright (c) 2000 Microsoft Corporation Module Name: opk.c Abstract: Common modules shared by OPK tools. Note: Source Depot requires that we publish the .h (E:\NT\admin\published\ntsetup\opklib.w) and .lib (E:\NT\public\internal\admin\lib). Author: Brian Ku (briank) 06/20/2000 Stephen Lodwick (stelo) 06/28/2000 Revision History: --*/ #include #include #include #include #include // // Local Define(s): // #define FILE_WINBOM_INI _T("WINBOM.INI") #define MAX_NAME 50 #define REG_KEY_FACTORY _T("SOFTWARE\\Microsoft\\Factory") #define REG_KEY_FACTORY_STATE REG_KEY_FACTORY _T("\\State") #define REG_KEY_SETUP_SETUP REGSTR_PATH_SETUP REGSTR_KEY_SETUP #define REG_VAL_FACTORY_WINBOM _T("WinBOM") #define REG_VAL_FACTORY_USERNAME _T("UserName") #define REG_VAL_FACTORY_PASSWORD _T("Password") #define REG_VAL_FACTORY_DOMAIN _T("Domain") #define REG_VAL_DEVICEPATH _T("DevicePath") #define REG_VAL_SOURCEPATH _T("SourcePath") #define REG_VAL_SPSOURCEPATH _T("ServicePackSourcePath") #define DIR_SYSTEMROOT _T("%SystemDrive%\\") // This has to have the trailing backslash, don't remove. #define NET_TIMEOUT 30000 // Time out to wait for net to start in milliseconds. // // Local Type Define(s): // typedef struct _STRLIST { LPTSTR lpszData; struct _STRLIST * lpNext; } STRLIST, *PSTRLIST, *LPSTRLIST; // // Local variables // static WCHAR NameOrgName[MAX_NAME+1]; static WCHAR NameOrgOrg[MAX_NAME+1]; static LPTSTR CleanupDirs [] = { {_T("Win9xmig")}, {_T("Win9xupg")}, {_T("Winntupg")} }; // // Internal Fuction Prototype(s): // static BOOL CheckWinbomRegKey(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpszShare, DWORD cbShare, LPTSTR lpszUser, DWORD cbUser, LPTSTR lpszPass, DWORD cbPass, LPTSTR lpFactoryMode, LPTSTR lpKey, BOOL bNetwork, LPBOOL lpbExists); static BOOL SearchRemovableDrives(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpFactoryMode, UINT uDriveType); static BOOL WinBOMExists(LPTSTR lpWinBom, LPTSTR lpMode); static void SavePathList(HKEY hKeyRoot, LPTSTR lpszSubKey, LPSTRLIST lpStrList, BOOL bWrite); static BOOL AddPathToList(LPTSTR lpszExpanded, LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted); static void EnumeratePath(LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted); static BOOL AddPathsToList(LPTSTR lpszBegin, LPTSTR lpszRoot, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted, BOOL bRecursive); /*++ Routine Description: Enable or disable a given named privilege. Arguments: PrivilegeName - supplies the name of a system privilege. Enable - flag indicating whether to enable or disable the privilege. Return Value: Boolean value indicating whether the operation was successful. --*/ BOOL EnablePrivilege( IN PCTSTR PrivilegeName, IN BOOL Enable ) { HANDLE Token; BOOL bRet; TOKEN_PRIVILEGES NewPrivileges; LUID Luid; if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) { return(FALSE); } if(!LookupPrivilegeValue(NULL,PrivilegeName,&Luid)) { CloseHandle(Token); return(FALSE); } NewPrivileges.PrivilegeCount = 1; NewPrivileges.Privileges[0].Luid = Luid; NewPrivileges.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0; bRet = AdjustTokenPrivileges( Token, FALSE, &NewPrivileges, 0, NULL, NULL ); // // The return value of AdjustTokenPrivileges() can be true even though we didn't set all // the privileges that we asked for. We need to call GetLastError() to make sure the call succeeded. // bRet = bRet && ( ERROR_SUCCESS == GetLastError() ); CloseHandle(Token); return(bRet); } /*++ =============================================================================== Routine Description: This routine will clean up any registry changes that we made to facilitate the factory pre-install process Arguments: none Return Value: =============================================================================== --*/ void CleanupRegistry ( void ) { HKEY hSetupKey; DWORD dwResult; DWORD dwValue = 0; // Open HKLM\System\Setup dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\Setup", 0, KEY_ALL_ACCESS, &hSetupKey); if (NO_ERROR == dwResult) { // Set the SystemSetupInProgress Value to 0. RegSetValueEx(hSetupKey, L"SystemSetupInProgress", 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD)); dwValue = 0; RegSetValueEx(hSetupKey, L"SetupType", 0, REG_DWORD, (CONST BYTE *)&dwValue, sizeof(DWORD)); // Delete the FactoryPreInstall value RegDeleteValue(hSetupKey, L"FactoryPreInstallInProgress"); RegDeleteValue(hSetupKey, L"AuditInProgress"); // Close the setup reg key RegCloseKey(hSetupKey); } return; } // // Get Organization and Owner names from registry // void GetNames(TCHAR szNameOrgOrg[], TCHAR szNameOrgName[]) { HKEY hKey = NULL; DWORD dwLen = 0; if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), &hKey)) { dwLen = MAX_NAME; RegQueryValueEx(hKey, TEXT("RegisteredOrganization"), 0, 0, (LPBYTE)szNameOrgOrg, &dwLen); dwLen = MAX_NAME; RegQueryValueEx(hKey, TEXT("RegisteredOwner"), 0, 0, (LPBYTE)szNameOrgName, &dwLen); RegCloseKey(hKey); } } // // Strip out non alphabets from guid // DWORD StripDash(TCHAR *pszGuid) { TCHAR *pszOrg, *pszTemp = pszGuid; pszOrg = pszGuid; while (pszGuid && *pszGuid != TEXT('\0')) { if (*pszTemp != TEXT('-') && *pszTemp != TEXT('{') && *pszTemp != TEXT('}')) *pszGuid++ = *pszTemp++; else pszTemp++; } if (pszOrg) return (DWORD)lstrlen(pszOrg); return 0; } // // GenUniqueName - Create a random computer name with a base name of 8 chars // VOID GenUniqueName( OUT PWSTR GeneratedString, IN DWORD DesiredStrLen ) { GUID guid; DWORD total = 0, length = 0; TCHAR szGuid[MAX_PATH]; // If we have a valid out param // if (GeneratedString) { static PCWSTR UsableChars = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // // How many characters will come from the org/name string. // DWORD BaseLength = 8; DWORD i,j; DWORD UsableCount; if( DesiredStrLen < BaseLength ) { BaseLength = DesiredStrLen - 1; } // // Get the Organization and Owner name from registry // GetNames(NameOrgOrg, NameOrgName); if( NameOrgOrg[0] ) { wcscpy( GeneratedString, NameOrgOrg ); } else if( NameOrgName[0] ) { wcscpy( GeneratedString, NameOrgName ); } else { wcscpy( GeneratedString, TEXT("X") ); for( i = 1; i < BaseLength; i++ ) { wcscat( GeneratedString, TEXT("X") ); } } // // Get him upper-case for our filter... // CharUpper(GeneratedString); // // Now we want to put a '-' at the end // of our GeneratedString. We'd like it to // be placed in the BASE_LENGTH character, but // the string may be shorter than that, or may // even have a ' ' in it. Figure out where to // put the '-' now. // for( i = 0; i <= BaseLength; i++ ) { // // Check for a short string. // if( (GeneratedString[i] == 0 ) || (GeneratedString[i] == L' ') || (!wcschr(UsableChars, GeneratedString[i])) || (i == BaseLength ) ) { GeneratedString[i] = L'-'; GeneratedString[i+1] = 0; break; } } // // Special case the scenario where we had no usable // characters. // if( GeneratedString[0] == L'-' ) { GeneratedString[0] = 0; } total = lstrlen(GeneratedString); // Loop until we have meet the desired string length // while (total < DesiredStrLen) { // Create a unique guid to be used in the string // CoCreateGuid(&guid); StringFromGUID2(&guid, szGuid, AS(szGuid)); // Remove the curly brace and dashes to generate the string // length = StripDash(szGuid); total += length; if (!lstrlen(GeneratedString)) { if (DesiredStrLen < total) lstrcpyn(GeneratedString, szGuid, DesiredStrLen+1); /* +1 for NULL */ else lstrcpy(GeneratedString, szGuid); } else if (total < DesiredStrLen) lstrcat(GeneratedString, szGuid); else _tcsncat(GeneratedString, szGuid, (length - (total - DesiredStrLen))); } CharUpper(GeneratedString); // Assert if (total != DesiredStrLen) // } } /****************************************************************************\ BOOL // Returns TRUE if any credentials are found and // are going to be returned. GetCredentials( // Tries to get user credentials from a few // different places. LPTSTR lpszUsername, // Pointer to a string buffer that will recieve // the user name for the credentials found. DWORD cbUsername, // Size, in characters, of the lpszUsername // string buffer. LPTSTR lpszPassword, // Pointer to a string buffer that will recieve // the password for the credentials found. DWORD cbPassword, // Size, in characters, of the lpszPassword // string buffer. LPTSTR lpFileName, // Optional pointer to the file name that will // contain the credentials. If this is NULL or // an empty string, the know registry key will // be checked instead for the credentials. LPTSTR lpAlternate // Optional pointer to the alternate section to // check first if lpFileName is vallid, or the // the optional registry key to check instead of // the normal known one. ); \****************************************************************************/ BOOL GetCredentials(LPTSTR lpszUsername, DWORD cbUsername, LPTSTR lpszPassword, DWORD cbPassword, LPTSTR lpFileName, LPTSTR lpAlternate) { BOOL bRet = FALSE; TCHAR szUsername[UNLEN + 1] = NULLSTR, szPassword[PWLEN + 1] = NULLSTR, szDomain[DNLEN + 1] = NULLSTR; LPSTR lpUserName; HKEY hKey; DWORD dwType, dwSize; BOOL bAlternate = ( lpAlternate && *lpAlternate ); // Make sure there is a filename, otherwise we check the registry. // if ( lpFileName && *lpFileName ) { // First try the alternate key for a user name. // if ( bAlternate ) { GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_USERNAME, NULLSTR, szUsername, AS(szUsername), lpFileName); } // If none found, try the normal section. If they happen // to pass the normal section in as the alternate section then // we will check it twice if no key exists, no big deal. // if ( NULLCHR == szUsername[0] ) { lpAlternate = WBOM_FACTORY_SECTION; GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_USERNAME, NULLSTR, szUsername, AS(szUsername), lpFileName); } // Make sure we found a user name. // if ( szUsername[0] ) { // If there is now backslash in the username, and there is a domain key, use that as the domain // if ( ((StrChr( szUsername, CHR_BACKSLASH )) == NULL) && (GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_DOMAIN, NULLSTR, szDomain, AS(szDomain), lpFileName)) && szDomain[0] ) { // Copy the "domain\username" string into the returning buffer // lstrcpyn(lpszUsername, szDomain, cbUsername); AddPathN(lpszUsername, szUsername, cbUsername); } else { // Copy the username into the returning buffer // lstrcpyn(lpszUsername, szUsername, cbUsername); } // We found the credentials // bRet = TRUE; // Get the password // if ( GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_PASSWORD, NULLSTR, szPassword, AS(szPassword), lpFileName) ) { // Copy the password into the returning buffer // lstrcpyn(lpszPassword, szPassword, cbPassword); } else *lpszPassword = NULLCHR; } } else if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, bAlternate ? lpAlternate : REG_KEY_FACTORY, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS ) { // Check the registry key to see if it has user credentials. // dwSize = sizeof(szUsername); if ( ( RegQueryValueEx(hKey, REG_VAL_FACTORY_USERNAME, NULL, &dwType, (LPBYTE) szUsername, &dwSize) == ERROR_SUCCESS ) && ( dwType == REG_SZ ) && ( szUsername[0] ) ) { // Check the registry key to see if it has user credentials. // dwSize = sizeof(szDomain); if ( ( StrChr(szUsername, CHR_BACKSLASH) == NULL ) && ( RegQueryValueEx(hKey, REG_VAL_FACTORY_DOMAIN, NULL, &dwType, (LPBYTE) szDomain, &dwSize) == ERROR_SUCCESS ) && ( dwType == REG_SZ ) && ( szDomain[0] ) ) { // Copy the domain and username into the returning buffer. // AddPathN(szDomain, szUsername, AS(szDomain)); lstrcpyn(lpszUsername, szDomain, cbUsername); } else { // Copy the username into the returning buffer. // lstrcpyn(lpszUsername, szUsername, cbUsername); } // We found the credentials // bRet = TRUE; // Check the registry key to see if it has user credentials. // dwSize = sizeof(szPassword); if ( ( RegQueryValueEx(hKey, REG_VAL_FACTORY_PASSWORD, NULL, &dwType, (LPBYTE) szPassword, &dwSize) == ERROR_SUCCESS ) && ( dwType == REG_SZ ) ) { // Copy the password into the returning buffer // lstrcpyn(lpszPassword, szPassword, cbPassword); } else { // No password specified, just return and empty string. // *lpszPassword = NULLCHR; } } // Always remember to close the key. // RegCloseKey(hKey); } return bRet; } NET_API_STATUS FactoryNetworkConnectEx(LPTSTR lpszPath, LPTSTR lpszWinBOMPath, LPTSTR lpAlternateSection, LPTSTR lpszUsername, DWORD cbUsername, LPTSTR lpszPassword, DWORD cbPassword, BOOL bState) { NET_API_STATUS nErr, nRet = 0; static BOOL bFirst = TRUE; BOOL bJustStarted = FALSE; TCHAR szUsername[UNLEN + DNLEN + 2] = NULLSTR, szPassword[PWLEN + 1] = NULLSTR, szBuffer[MAX_PATH], szWinbomShare[MAX_PATH] = NULLSTR; LPTSTR lpSearch; DWORD dwStart; // Get the credentials for the current section // if ( bState ) { // Make sure we pass in a username buffer big enough to hold the "domain\username" string. // GetCredentials(szUsername, AS(szUsername), szPassword, AS(szPassword), lpszWinBOMPath, lpAlternateSection); } // Get just the share pare of the winbom path if it is a UNC. // if ( lpszWinBOMPath ) { GetUncShare(lpszWinBOMPath, szWinbomShare, AS(szWinbomShare)); } // Determine all of the UNC paths in the string supplied // lpSearch = lpszPath; while ( lpSearch = StrStr(lpSearch, _T("\\\\")) ) { // See if this is a UNC share. // if ( GetUncShare(lpSearch, szBuffer, AS(szBuffer)) && szBuffer[0] ) { // We can not connect or disconnect from the share where the winbom is. // if ( ( NULLCHR == szWinbomShare[0] ) || ( lstrcmpi(szBuffer, szWinbomShare) != 0 ) ) { // Connect/disconnect from the share and // nErr = 0; dwStart = GetTickCount(); do { if ( nErr ) { Sleep(100); } if ( NERR_WkstaNotStarted == (nErr = ConnectNetworkResource(szBuffer, szUsername, szPassword, bState)) ) { // Wierd bug here we are hacking around. If we just wait till the network starts, sometimes the first // call it gives us a wierd error. So if we run into the not started error, then we keep retrying on // any error until we time out. // bJustStarted = TRUE; } #ifdef DBG LogFileStr(_T("c:\\sysprep\\winbom.log"), _T("FactoryNetworkConnect(%s)=%d [%d,%d]\n"), szBuffer, nErr, dwStart, GetTickCount()); #endif // DBG } while ( ( bFirst && bJustStarted && nErr ) && ( (GetTickCount() - dwStart) < NET_TIMEOUT ) ); // If we hit an error and it is the first one, // return it. // if ( nErr && ( 0 == nRet ) ) { nRet = nErr; } // Once we have tried to connect to a network resource, we set // this so we don't ever time out again as long as we are still // running. // bFirst = FALSE; } // Move the pointer past the share name. // lpSearch += lstrlen(szBuffer); } else { // Go past the double backslash even though it is not a UNC path. // lpSearch += 2; } } // Might need to return the credentials we used. // if ( lpszUsername && cbUsername ) { lstrcpyn(lpszUsername, szUsername, cbUsername); } if ( lpszPassword && cbPassword ) { lstrcpyn(lpszPassword, szPassword, cbPassword); } // Return the net error, or 0 if everything worked. // return nRet; } NET_API_STATUS FactoryNetworkConnect(LPTSTR lpszPath, LPTSTR lpszWinBOMPath, LPTSTR lpAlternateSection, BOOL bState) { return FactoryNetworkConnectEx(lpszPath, lpszWinBOMPath, lpAlternateSection, NULL, 0, NULL, 0, bState); } /*++ =============================================================================== Routine Description: This routine will locate the WINBOM.INI file. The search algorithm will be: * Check the registry key * Check local floppy drives * Check local CD-ROM drives * Check the sysprep folder * Check the root of the boot volume Arguments: lpWinBOMPath - return buffer where the winbom path will be copied. cbWinbBOMPath - size of return buffer in characters. lpFactoryPath - the sysprep folder where factory.exe is. Return Value: TRUE - WINBOM.INI was found FALSE - WINBOM.INI could not be found =============================================================================== --*/ BOOL LocateWinBom(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpFactoryPath, LPTSTR lpFactoryMode, DWORD dwFlags) { BOOL bFound = FALSE, bRunningFromCd, bNetwork = !(GET_FLAG(dwFlags, LOCATE_NONET)); TCHAR szWinBom[MAX_PATH] = NULLSTR; TCHAR szNewShare[MAX_PATH], szNewUser[256], szNewPass[256], szCurShare[MAX_PATH] = NULLSTR, szCurUser[256] = NULLSTR, szCurPass[256] = NULLSTR; // Set the error mode so no drives display error messages ("please insert floppy") // SetErrorMode(SEM_FAILCRITICALERRORS); // Always set the return buffer to an empty string first. // *lpWinBOMPath = NULLCHR; // This neat little flag is used when we think that we already found a // winbom sometime this boot (like when factory runs from the run key) and // we want to make sure we use the same winbom instead of searching again // and maybe getting a different one. // if ( GET_FLAG(dwFlags, LOCATE_AGAIN) ) { BOOL bDone; // Try our state winbom key to see if we have a winbom that we are already using. // bFound = CheckWinbomRegKey(szWinBom, AS(szWinBom), szCurShare, AS(szCurShare), szCurUser, AS(szCurUser), szCurPass, AS(szCurPass), lpFactoryMode, REG_KEY_FACTORY_STATE, bNetwork, &bDone); // Now if the registry key existed, we don't want to do anything more. Just use // what we got right now and be done with it. // if ( bDone ) { // Check to see if we have anything to return. // if ( bFound ) { // Copy the path we found into the return buffer. // lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath); } // Set the error mode back to system default // SetErrorMode(0); // Return right now if we found it or not. // return bFound; } } // Check to see if the system drive is a CD-ROM (which pretty much // means that we are running in WinPE and we should search this drive // last). // ExpandEnvironmentStrings(DIR_SYSTEMROOT, szWinBom, AS(szWinBom)); bRunningFromCd = ( GetDriveType(szWinBom) == DRIVE_CDROM ); szWinBom[0] = NULLCHR; // Check the magic registry key first as an option to override were the winbom is. // bFound = CheckWinbomRegKey(szWinBom, AS(szWinBom), szCurShare, AS(szCurShare), szCurUser, AS(szCurUser), szCurPass, AS(szCurPass), lpFactoryMode, REG_KEY_FACTORY, bNetwork, NULL); // Walk through the drives first checking if the drive is removeable and NOT CDROM. // Check for the presence of the WinBOM file and quit if it is found. // if ( !bFound ) { bFound = SearchRemovableDrives(szWinBom, AS(szWinBom), lpFactoryMode, DRIVE_REMOVABLE); } // Walk through the drives again this time checking if the drive IS a CD-ROM. // Check for the presence of the WinBOM file and quit if it is found. Also // only do this if the OS is not running from a CD. This is so in the WinPE // case the person can put a winbom on the hard drive and it will be used // before the one that is always on the CD-ROM that WinPE boots from. // if ( !bFound ) { bFound = SearchRemovableDrives(szWinBom, AS(szWinBom), lpFactoryMode, bRunningFromCd ? DRIVE_FIXED : DRIVE_CDROM); } // Now if still not found, check the same directory as factory. // if ( !bFound ) { lstrcpyn(szWinBom, lpFactoryPath, AS(szWinBom)); AddPath(szWinBom, FILE_WINBOM_INI); bFound = WinBOMExists(szWinBom, lpFactoryMode); } // Now if still not found, check the root of the system drive. // if ( !bFound ) { ExpandEnvironmentStrings(DIR_SYSTEMROOT, szWinBom, AS(szWinBom)); lstrcat(szWinBom, FILE_WINBOM_INI); bFound = WinBOMExists(szWinBom, lpFactoryMode); } // Now if we skipped the CD-ROM search above, do it now if we still // don't have a winbom. // if ( !bFound && bRunningFromCd ) { bFound = SearchRemovableDrives(szWinBom, AS(szWinBom), lpFactoryMode, DRIVE_CDROM); } // Make sure we found a WinBOM and look for a NewWinbom key. // if ( bFound ) { DWORD dwLimit = 10; // Must be greater than zero. BOOL bAgain; LPTSTR lpszNewWinbom; // Copy the path we found into the return buffer. // lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath); // Now do the loop to search for possible NewWinBom keys. // do { // Reset the bool so we can check for alternate WinBOMs. // bAgain = FALSE; // See if the NewWinBom key exists in the winbom we found. // if ( lpszNewWinbom = IniGetExpand(lpWinBOMPath, INI_SEC_WBOM_FACTORY, INI_KEY_WBOM_FACTORY_NEWWINBOM, NULL) ) { LPTSTR lpShareRemove; BOOL bSame = FALSE; // The NewWinBom key might be a UNC, so see if we need to connect // to the share. // szNewShare[0] = NULLCHR; szNewUser[0] = NULLCHR; szNewPass[0] = NULLCHR; if ( bNetwork && GetUncShare(lpszNewWinbom, szNewShare, AS(szNewShare)) && szNewShare[0] ) { // Only really need to connect if the we are not already // connected. // if ( lstrcmpi(szNewShare, szCurShare) != 0 ) { FactoryNetworkConnectEx(szNewShare, lpWinBOMPath, NULL, szNewUser, AS(szNewUser), szNewPass, AS(szNewPass), TRUE); } else { bSame = TRUE; } } // Now make sure the winbom we found really exists and is a vallid // winbom we can use. // if ( WinBOMExists(lpszNewWinbom, lpFactoryMode) ) { // Copy the new winbom path we found into the return buffer. // lstrcpyn(lpWinBOMPath, lpszNewWinbom, cbWinbBOMPath); bAgain = TRUE; lpShareRemove = szCurShare; } else { lpShareRemove = szNewShare; } // Do any share cleanup that needs to happen. // if ( bNetwork && *lpShareRemove && !bSame ) { FactoryNetworkConnect(lpShareRemove, NULL, NULL, FALSE); *lpShareRemove = NULLCHR; } // Also save the share info if there was one so we can cleanup // later if we find another winbom. // if ( bAgain ) { lstrcpyn(szCurShare, szNewShare, AS(szCurShare)); lstrcpyn(szCurUser, szNewUser, AS(szCurUser)); lstrcpyn(szCurPass, szNewPass, AS(szCurPass)); } // Clean up the ini key we allocated. // FREE(lpszNewWinbom); } } while ( --dwLimit && bAgain ); } // Save the winbom we are using (or empty string if not using one) to our state // key so other programs or instances of factory running this boot know what winbom // to use. // RegSetString(HKLM, REG_KEY_FACTORY_STATE, REG_VAL_FACTORY_WINBOM, lpWinBOMPath); // We may want to save the credentials that we used to get to this winbom in our // state key so we can get them back and reconnect, but for now if we just don't // ever disconnect from the share where the winbom is, we should be fine. Only need // to worry about this if the caller of the function wanted to disconnect this // network resource when they were done. If we do that, these keys will have to // be written. But we will have to put in a bunch of code so that CheckWinbomRegKey() // returns the credentials used, and when doing our NewWinBom search that we also // save the credentials we finally end up using. This is a lot of work, and I see // no need for this now. Just make sure if you call this function that you do NOT // call FactoryNetworkConnect() to remove the net connection. // // This didn't work, because when we log on we loose our net connection so we need // the credentials to reconnect after logon. So I went through all the work I mentioned // above to make this work. // RegSetString(HKLM, REG_KEY_FACTORY_STATE, REG_VAL_FACTORY_USERNAME, szCurUser); RegSetString(HKLM, REG_KEY_FACTORY_STATE, REG_VAL_FACTORY_PASSWORD, szCurPass); // Set the error mode back to system default // SetErrorMode(0); // Return if we found it or not. // return bFound; } BOOL SetFactoryStartup(LPCTSTR lpFactory) { HKEY hKey; BOOL bRet = TRUE; // Now make sure we are also setup as a setup program to run before we log on. // if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SYSTEM\\Setup"), 0, KEY_ALL_ACCESS, &hKey ) == ERROR_SUCCESS ) { TCHAR szFileName[MAX_PATH + 32] = NULLSTR; DWORD dwVal; // // Setup the control flags for the SETUP key // The Setting used are: // CmdLine = c:\sysprep\factory.exe -setup // SetupType = 2 (No reboot) // SystemSetupInProgress = 0 (no service restrictions) // MiniSetupInProgress = 0 (Not doing a mini setup) // FactoryPreInstallInProgress = 1 (Delay pnp driver installs) // AuditInProgress = 1 (general key to determine if the OEM is auditing the machine) // lstrcpyn(szFileName, lpFactory, AS(szFileName)); lstrcat(szFileName, _T(" -setup")); if ( RegSetValueEx(hKey, _T("CmdLine"), 0, REG_SZ, (CONST LPBYTE) szFileName, ( lstrlen(szFileName) + 1 ) * sizeof(TCHAR)) != ERROR_SUCCESS ) bRet = FALSE; dwVal = SETUPTYPE_NOREBOOT; if ( RegSetValueEx(hKey, TEXT("SetupType"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS ) bRet = FALSE; dwVal = 0; if ( RegSetValueEx(hKey, TEXT("SystemSetupInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS ) bRet = FALSE; dwVal = 0; if ( RegSetValueEx(hKey, TEXT("MiniSetupInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS ) bRet = FALSE; dwVal = 1; if ( RegSetValueEx(hKey, TEXT("FactoryPreInstallInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS ) bRet = FALSE; dwVal = 1; if ( RegSetValueEx(hKey, TEXT("AuditInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS ) bRet = FALSE; RegCloseKey(hKey); } else bRet = FALSE; return bRet; } BOOL UpdateDevicePathEx(HKEY hKeyRoot, LPTSTR lpszSubKey, LPTSTR lpszNewPath, LPTSTR lpszRoot, BOOL bRecursive) { LPSTRLIST lpSorted = NULL, lpUnsorted = NULL; LPTSTR lpszDevicePath; // First add any paths already in the registry to the lists. // if ( lpszDevicePath = RegGetString(hKeyRoot, lpszSubKey, REG_VAL_DEVICEPATH) ) { AddPathsToList(lpszDevicePath, NULL, &lpSorted, &lpUnsorted, FALSE); FREE(lpszDevicePath); } // Now add any they wanted to the list. // AddPathsToList(lpszNewPath, lpszRoot, &lpSorted, &lpUnsorted, bRecursive); // Now that we are done, we can free our sorted list. // SavePathList(hKeyRoot, lpszSubKey, lpSorted, FALSE); // Now save our final list back to the registry and free // it. // SavePathList(hKeyRoot, lpszSubKey, lpUnsorted, TRUE); return TRUE; } BOOL UpdateDevicePath(LPTSTR lpszNewPath, LPTSTR lpszRoot, BOOL bRecursive) { return ( UpdateDevicePathEx( HKLM, REGSTR_PATH_SETUP, lpszNewPath, lpszRoot ? lpszRoot : DIR_SYSTEMROOT, bRecursive ) ); } BOOL UpdateSourcePath(LPTSTR lpszSourcePath) { BOOL bRet = FALSE; if ( lpszSourcePath && *lpszSourcePath ) { if (bRet = RegSetString(HKLM, REG_KEY_SETUP_SETUP, REG_VAL_SOURCEPATH, lpszSourcePath)) { bRet = RegSetString(HKLM, REG_KEY_SETUP_SETUP, REG_VAL_SPSOURCEPATH, lpszSourcePath); } } return bRet; } // // Internal Function(s): // static BOOL CheckWinbomRegKey(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpszShare, DWORD cbShare, LPTSTR lpszUser, DWORD cbUser, LPTSTR lpszPass, DWORD cbPass, LPTSTR lpFactoryMode, LPTSTR lpKey, BOOL bNetwork, LPBOOL lpbExists) { HKEY hKey; BOOL bFound = FALSE, bExists = FALSE; TCHAR szWinBom[MAX_PATH] = NULLSTR; // Check the registry key to see if it knows about a winbom to use. // if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpKey, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS ) { DWORD dwType, dwSize = sizeof(szWinBom); // Try to get the value. // if ( ( RegQueryValueEx(hKey, REG_VAL_FACTORY_WINBOM, NULL, &dwType, (LPBYTE) szWinBom, &dwSize) == ERROR_SUCCESS ) && ( dwType == REG_SZ ) ) { // Now the key must have existed. If there was something // in the key, lets try to use it. // if ( szWinBom[0] ) { TCHAR szShare[MAX_PATH] = NULLSTR, szUser[256] = NULLSTR, szPass[256] = NULLSTR; // Throw some networking support in here. // if ( bNetwork && GetUncShare(szWinBom, szShare, AS(szShare)) && szShare[0] ) { FactoryNetworkConnectEx(szShare, NULL, lpKey, szUser, AS(szUser), szPass, AS(szPass), TRUE); } // Check to see if the winbom is actually there still. // If not, then it is bad and we should just act like // the key didn't even exist. // if ( WinBOMExists(szWinBom, lpFactoryMode) ) { // If found, return the winbom in the supplied buffer. // lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath); bFound = bExists = TRUE; // See what we might need to return. // if ( lpszShare && cbShare ) { lstrcpyn(lpszShare, szShare, cbShare); } if ( lpszUser && cbUser ) { lstrcpyn(lpszUser, szUser, cbUser); } if ( lpszPass && cbPass ) { lstrcpyn(lpszPass, szPass, cbPass); } } else if ( bNetwork && szShare[0] ) { // Clean up our net connection. // FactoryNetworkConnect(szShare, NULL, NULL, FALSE); } } else { // There wasn't anything in the key, but it did exist. // This most likely means we didn't find the winbom the // first time around, so we may need to know that now. // bExists = TRUE; } } // Always remember to close the key. // RegCloseKey(hKey); } // If they want to know if the key existed, then return it to them. // if ( lpbExists ) { *lpbExists = bExists; } // If we found a winbom, return true. // return bFound; } static BOOL SearchRemovableDrives(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpFactoryMode, UINT uDriveType) { DWORD dwDrives; TCHAR szWinBom[MAX_PATH], szDrive[] = _T("_:\\"); BOOL bFound = FALSE; // Loop through all the dirves on the system. // for ( szDrive[0] = _T('A'), dwDrives = GetLogicalDrives(); ( szDrive[0] <= _T('Z') ) && dwDrives && !bFound; szDrive[0]++, dwDrives >>= 1 ) { // First check to see if the first bit is set (which means // this drive exists in the system). Then make sure it is // a drive type that we want to check for a winbom. // if ( ( dwDrives & 0x1 ) && ( GetDriveType(szDrive) == uDriveType ) ) { // See if there is a wINBOM.INI file on the drive. // lstrcpyn(szWinBom, szDrive, AS(szWinBom)); lstrcat(szWinBom, FILE_WINBOM_INI); if ( WinBOMExists(szWinBom, lpFactoryMode) ) { // Return the path to the winbom in the supplied buffer. // lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath); bFound = TRUE; } } } return bFound; } static BOOL WinBOMExists(LPTSTR lpWinBom, LPTSTR lpMode) { BOOL bRet = FALSE; // First the file must exists. // if ( FileExists(lpWinBom) ) { TCHAR szModes[256] = NULLSTR; // See if there is even a mode string in this winbom (has to be or // we will automatically use it). // if ( lpMode && *lpMode && GetPrivateProfileString(WBOM_FACTORY_SECTION, INI_KEY_WBOM_FACTORY_TYPE, NULLSTR, szModes, AS(szModes), lpWinBom) && szModes[0] ) { LPTSTR lpCheck = szModes, lpNext; // Loop through ever comma delimited field in the value we got // from the winbom (there is always at least one). // do { // See if there is another mode field in this string. // if ( lpNext = StrChr(lpCheck, _T(',')) ) *lpNext++ = NULLCHR; // Make sure there are no spaces around the field. // StrTrm(lpCheck, _T(' ')); // If the mode we are in matches the one in the winbom, then // we are good to go. // if ( lstrcmpi(lpMode, lpCheck) == 0 ) bRet = TRUE; // Set the check pointer to the next // field. // lpCheck = lpNext; } while ( !bRet && lpCheck ); // It would be nice to log if we don't use this winbom because of this // setting, but we can't really do that because we need to winbom to // init logging. // /* if ( !bRet ) { // Log here. } */ } else bRet = TRUE; } return bRet; } static void SavePathList(HKEY hKeyRoot, LPTSTR lpszSubKey, LPSTRLIST lpStrList, BOOL bWrite) { LPSTRLIST lpStrListNode; DWORD cbDevicePath = 256, dwLength = 0, dwOldSize; LPTSTR lpszDevicePath; // Initialize our intial buffer we are going to use to // write to the registry. // if ( bWrite ) { lpszDevicePath = (LPTSTR) MALLOC(cbDevicePath * sizeof(TCHAR)); } // Loop through the list. // while ( lpStrList ) { // Save a pointer to the current node. // lpStrListNode = lpStrList; // Advanced to the next node. // lpStrList = lpStrList->lpNext; // If we are saving this list to the registry, then // we need to add to our buffer. // if ( bWrite && lpszDevicePath ) { // Make sure our buffer is still big enough. // The two extra are for the possible semi-colon // we might add and one more to be safe. We // don't have to worry about the null terminator // because we do less than or equal to our current // buffer size. // dwOldSize = cbDevicePath; dwLength += lstrlen(lpStrListNode->lpszData); while ( cbDevicePath <= (dwLength + 2) ) { cbDevicePath *= 2; } // If it wasn't big enough, we need to reallocate it. // if ( cbDevicePath > dwOldSize ) { LPTSTR lpszTmpDevicePath = (LPTSTR) REALLOC(lpszDevicePath, cbDevicePath * sizeof(TCHAR)); // // Make sure the REALLOC succeeded before reassigning the memory // if ( lpszTmpDevicePath ) { lpszDevicePath = lpszTmpDevicePath; } } // Make sure we still have a buffer. // if ( lpszDevicePath ) { // If we already have added a path, tack on a semicolon. // if ( *lpszDevicePath ) { lstrcat(lpszDevicePath, _T(";")); dwLength++; } // Now add our path. // lstrcat(lpszDevicePath, lpStrListNode->lpszData); } } // Free the data in this node. // FREE(lpStrListNode->lpszData); // Free the node itself. // FREE(lpStrListNode); } // If we have the data, save it to the registry. // if ( bWrite && lpszDevicePath ) { RegSetExpand(hKeyRoot, lpszSubKey, REG_VAL_DEVICEPATH, lpszDevicePath); FREE(lpszDevicePath); } } static BOOL AddPathToList(LPTSTR lpszExpanded, LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted) { LPSTRLIST lpSortedNode, lpUnsortedNode; BOOL bQuit = FALSE; // Loop until we get to the end or find a string that is bigger than // ours. // while ( *lplpSorted && !bQuit ) { // If we do this, we don't have to do the complicated // indirection. // lpSortedNode = *lplpSorted; // Compare the nodes string to the one we want to add. // switch ( CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, lpszExpanded, -1, lpSortedNode->lpszData, -1) ) { case CSTR_EQUAL: // If the are the same, we just return FALSE because we do // not need to add it. // return FALSE; case CSTR_LESS_THAN: // If our string is less than the one in this node, we need // to stop so we can insert ourself before it. // bQuit = TRUE; break; default: // Default, just try the next item in the list. // lplpSorted = &(lpSortedNode->lpNext); } } // Now we need to advance the pointer of the unsorted list to the // end so we can add ours. // while ( *lplpUnsorted ) { lpUnsortedNode = *lplpUnsorted; lplpUnsorted = &(lpUnsortedNode->lpNext); } // Allocate our nodes. If anything fails, we have to return false. // if ( NULL == (lpSortedNode = (LPSTRLIST) MALLOC(sizeof(STRLIST))) ) { return FALSE; } if ( NULL == (lpUnsortedNode = (LPSTRLIST) MALLOC(sizeof(STRLIST))) ) { FREE(lpSortedNode); return FALSE; } // Set the data in the sorted node and insert the list since we // know where that goes right now. // lpSortedNode->lpszData = lpszExpanded; lpSortedNode->lpNext = *lplpSorted; *lplpSorted = lpSortedNode; // Now set the data in the unsorted node and insert it at the end // of that list. // lpUnsortedNode->lpszData = lpszPath; lpUnsortedNode->lpNext = NULL; *lplpUnsorted = lpUnsortedNode; return TRUE; } static void EnumeratePath(LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted) { WIN32_FIND_DATA FileFound; HANDLE hFile; LPTSTR lpszNewPath, lpszExpandedPath; DWORD cbNewPath; BOOL bAdded = FALSE; // Process all the files and directories in the directory passed in. // if ( (hFile = FindFirstFile(_T("*"), &FileFound)) != INVALID_HANDLE_VALUE ) { do { // First check to see if this is a a directory. // if ( ( FileFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) && ( lstrcmp(FileFound.cFileName, _T(".")) != 0 ) && ( lstrcmp(FileFound.cFileName, _T("..")) != 0 ) && ( SetCurrentDirectory(FileFound.cFileName) ) ) { // Need the size for the new path... which is the length of the // old path, plus new path, and 3 extra for the joining backslash, // null terminator, and another one more to be safe. // cbNewPath = lstrlen(lpszPath) + lstrlen(FileFound.cFileName) + 3; if ( lpszNewPath = (LPTSTR) MALLOC(cbNewPath * sizeof(TCHAR)) ) { // Create our new path (note this one is not expanded out, // it may contain environment variables. // lstrcpyn(lpszNewPath, lpszPath, cbNewPath); AddPathN(lpszNewPath, FileFound.cFileName, cbNewPath); // Make sure we can expand out the buffer. // if ( lpszExpandedPath = AllocateExpand(lpszNewPath) ) { // Now add the path to the list. // bAdded = AddPathToList(lpszExpandedPath, lpszNewPath, lplpSorted, lplpUnsorted); // If the path didn't get added, we have to free the buffer. // if ( !bAdded ) { FREE(lpszExpandedPath); } } // Continue the recursive search // EnumeratePath(lpszNewPath, lplpSorted, lplpUnsorted); // If the path didn't get added, we have to free the buffer. // if ( !bAdded ) { FREE(lpszNewPath); } } // Set the current directory to parent, to continue. // SetCurrentDirectory(_T("..")); } } while ( FindNextFile(hFile, &FileFound) ); FindClose(hFile); } } static BOOL AddPathsToList(LPTSTR lpszBegin, LPTSTR lpszRoot, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted, BOOL bRecursive) { BOOL bRet = TRUE, bAddBackslash = FALSE, bAdded; LPTSTR lpszEnd, lpszPath, lpszExpanded, lpszCat; DWORD dwSize, dwBackslash; // If they don't pass in the root we don't do anything. // if ( lpszRoot ) { if ( NULLCHR == *lpszRoot ) { lpszRoot = NULL; } else if ( _T('\\') != *CharPrev(lpszRoot, lpszRoot + lstrlen(lpszRoot)) ) { // The root path passed in doesn't have a backslash at // the end so we set this so that we know we have to add // one each time we add a path. // bAddBackslash = TRUE; } } // Loop through all the semicolon separated paths in the // buffer passed to use. // do { // Find the beginning of the path past all // the semicolons. // while ( _T(';') == *lpszBegin ) { lpszBegin++; } if ( *lpszBegin ) { // Find the end of the path which is the next // semicolon or the end of the string, whichever // comes first. // lpszEnd = lpszBegin; while ( *lpszEnd && ( _T(';') != *lpszEnd ) ) { lpszEnd++; } // See if our new path has a backslash at the // beginning of it. // dwBackslash = 0; if ( _T('\\') == *lpszBegin ) { // If it does and we don't want to add one, // then advance the pointer past it. // if ( !bAddBackslash ) { lpszBegin++; } } else if ( bAddBackslash ) { // Set this so we know to add the backslash and // allocate the extra space for it. // dwBackslash = 1; } // Figure out the size we need for the path we are going // to create. It is the length of the new string, plus // the root if one was passed in, plus 1 for the backslash // if we need to add one, plus 2 extra (one for the null // terminator and one just to be safe). // dwSize = ((int) (lpszEnd - lpszBegin)) + dwBackslash + 2; if ( lpszRoot ) { dwSize += lstrlen(lpszRoot); } // Now allocate our path buffer. // if ( lpszPath = (LPTSTR) MALLOC(dwSize * sizeof(TCHAR)) ) { // Reset this so if anything doesn't work we know to // free our allocated memory. // bAdded = FALSE; // Copy the path into our buffer. // lpszCat = lpszPath; if ( lpszRoot ) { lstrcpy(lpszCat, lpszRoot); lpszCat += lstrlen(lpszCat); } if ( dwBackslash ) { *lpszCat++ = _T('\\'); } lstrcpyn(lpszCat, lpszBegin, (int) (lpszEnd - lpszBegin) + 1); if ( lpszExpanded = AllocateExpand(lpszPath) ) { // Add it to our lists. // bAdded = AddPathToList(lpszExpanded, lpszPath, lplpSorted, lplpUnsorted); // If this is a recursive add, we try to enumerate all the // sub directories and add them as well. // if ( ( bRecursive ) && ( DirectoryExists(lpszExpanded) ) && ( SetCurrentDirectory(lpszExpanded) ) ) { EnumeratePath(lpszPath, lplpSorted, lplpUnsorted); } // If it wasn't added to the list, then free the memory. // if ( !bAdded ) { FREE(lpszExpanded); } } // If it wasn't added to the list, then free the memory. // if ( !bAdded ) { FREE(lpszPath); } } // Reset the beginning to the next string. // lpszBegin = lpszEnd; } } while ( *lpszBegin ); return bRet; } VOID CleanupSourcesDir(LPTSTR lpszSourcesDir) { UINT i = 0; LPTSTR lpEnd = NULL; // If we have a valid sources if ( lpszSourcesDir && *lpszSourcesDir && DirectoryExists(lpszSourcesDir) ) { lpEnd = lpszSourcesDir + lstrlen(lpszSourcesDir); for (i = 0; ( i < AS(CleanupDirs) ); i++) { AddPath(lpszSourcesDir, CleanupDirs[i]); DeletePath(lpszSourcesDir); *lpEnd = NULLCHR; } } } // External functions // typedef BOOL ( *OpkCheckVersion ) ( DWORD dwMajorVersion, DWORD dwQFEVersion ); // // Wrapper around the syssetup OPKCheckVersion() function. // BOOL OpklibCheckVersion( DWORD dwMajorVersion, DWORD dwQFEVersion ) { BOOL bRet = TRUE; // Allow tool to run by default, in case we can't load syssetup or find the entry point. HINSTANCE hInstSysSetup = NULL; OpkCheckVersion pOpkCheckVersion = NULL; hInstSysSetup = LoadLibrary( _T("syssetup.dll") ); if ( hInstSysSetup ) { pOpkCheckVersion = (OpkCheckVersion) GetProcAddress( hInstSysSetup, "OpkCheckVersion" ); if ( pOpkCheckVersion ) { bRet = pOpkCheckVersion( dwMajorVersion, dwQFEVersion ); } FreeLibrary( hInstSysSetup ); } return bRet; }