Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1699 lines
54 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. opk.c
  5. Abstract:
  6. Common modules shared by OPK tools. Note: Source Depot requires that we
  7. publish the .h (E:\NT\admin\published\ntsetup\opklib.w) and .lib
  8. (E:\NT\public\internal\admin\lib).
  9. Author:
  10. Brian Ku (briank) 06/20/2000
  11. Stephen Lodwick (stelo) 06/28/2000
  12. Revision History:
  13. --*/
  14. #include <pch.h>
  15. #include <objbase.h>
  16. #include <tchar.h>
  17. #include <regstr.h>
  18. #include <winbom.h>
  19. //
  20. // Local Define(s):
  21. //
  22. #define FILE_WINBOM_INI _T("WINBOM.INI")
  23. #define MAX_NAME 50
  24. #define REG_KEY_FACTORY _T("SOFTWARE\\Microsoft\\Factory")
  25. #define REG_KEY_FACTORY_STATE REG_KEY_FACTORY _T("\\State")
  26. #define REG_KEY_SETUP_SETUP REGSTR_PATH_SETUP REGSTR_KEY_SETUP
  27. #define REG_VAL_FACTORY_WINBOM _T("WinBOM")
  28. #define REG_VAL_FACTORY_USERNAME _T("UserName")
  29. #define REG_VAL_FACTORY_PASSWORD _T("Password")
  30. #define REG_VAL_FACTORY_DOMAIN _T("Domain")
  31. #define REG_VAL_DEVICEPATH _T("DevicePath")
  32. #define REG_VAL_SOURCEPATH _T("SourcePath")
  33. #define REG_VAL_SPSOURCEPATH _T("ServicePackSourcePath")
  34. #define DIR_SYSTEMROOT _T("%SystemDrive%\\") // This has to have the trailing backslash, don't remove.
  35. #define NET_TIMEOUT 30000 // Time out to wait for net to start in milliseconds.
  36. //
  37. // Local Type Define(s):
  38. //
  39. typedef struct _STRLIST
  40. {
  41. LPTSTR lpszData;
  42. struct _STRLIST * lpNext;
  43. }
  44. STRLIST, *PSTRLIST, *LPSTRLIST;
  45. //
  46. // Local variables
  47. //
  48. static WCHAR NameOrgName[MAX_NAME+1];
  49. static WCHAR NameOrgOrg[MAX_NAME+1];
  50. static LPTSTR CleanupDirs [] =
  51. {
  52. {_T("Win9xmig")},
  53. {_T("Win9xupg")},
  54. {_T("Winntupg")}
  55. };
  56. //
  57. // Internal Fuction Prototype(s):
  58. //
  59. static BOOL CheckWinbomRegKey(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath,
  60. LPTSTR lpszShare, DWORD cbShare,
  61. LPTSTR lpszUser, DWORD cbUser,
  62. LPTSTR lpszPass, DWORD cbPass,
  63. LPTSTR lpFactoryMode, LPTSTR lpKey,
  64. BOOL bNetwork, LPBOOL lpbExists);
  65. static BOOL SearchRemovableDrives(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpFactoryMode, UINT uDriveType);
  66. static BOOL WinBOMExists(LPTSTR lpWinBom, LPTSTR lpMode);
  67. static void SavePathList(HKEY hKeyRoot, LPTSTR lpszSubKey, LPSTRLIST lpStrList, BOOL bWrite);
  68. static BOOL AddPathToList(LPTSTR lpszExpanded, LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted);
  69. static void EnumeratePath(LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted);
  70. static BOOL AddPathsToList(LPTSTR lpszBegin, LPTSTR lpszRoot, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted, BOOL bRecursive);
  71. /*++
  72. Routine Description:
  73. Enable or disable a given named privilege.
  74. Arguments:
  75. PrivilegeName - supplies the name of a system privilege.
  76. Enable - flag indicating whether to enable or disable the privilege.
  77. Return Value:
  78. Boolean value indicating whether the operation was successful.
  79. --*/
  80. BOOL
  81. EnablePrivilege(
  82. IN PCTSTR PrivilegeName,
  83. IN BOOL Enable
  84. )
  85. {
  86. HANDLE Token;
  87. BOOL bRet;
  88. TOKEN_PRIVILEGES NewPrivileges;
  89. LUID Luid;
  90. if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) {
  91. return(FALSE);
  92. }
  93. if(!LookupPrivilegeValue(NULL,PrivilegeName,&Luid)) {
  94. CloseHandle(Token);
  95. return(FALSE);
  96. }
  97. NewPrivileges.PrivilegeCount = 1;
  98. NewPrivileges.Privileges[0].Luid = Luid;
  99. NewPrivileges.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
  100. bRet = AdjustTokenPrivileges(
  101. Token,
  102. FALSE,
  103. &NewPrivileges,
  104. 0,
  105. NULL,
  106. NULL
  107. );
  108. //
  109. // The return value of AdjustTokenPrivileges() can be true even though we didn't set all
  110. // the privileges that we asked for. We need to call GetLastError() to make sure the call succeeded.
  111. //
  112. bRet = bRet && ( ERROR_SUCCESS == GetLastError() );
  113. CloseHandle(Token);
  114. return(bRet);
  115. }
  116. /*++
  117. ===============================================================================
  118. Routine Description:
  119. This routine will clean up any registry changes that we made to facilitate
  120. the factory pre-install process
  121. Arguments:
  122. none
  123. Return Value:
  124. ===============================================================================
  125. --*/
  126. void CleanupRegistry
  127. (
  128. void
  129. )
  130. {
  131. HKEY hSetupKey;
  132. DWORD dwResult;
  133. DWORD dwValue = 0;
  134. // Open HKLM\System\Setup
  135. dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  136. L"SYSTEM\\Setup",
  137. 0,
  138. KEY_ALL_ACCESS,
  139. &hSetupKey);
  140. if (NO_ERROR == dwResult)
  141. {
  142. // Set the SystemSetupInProgress Value to 0.
  143. RegSetValueEx(hSetupKey,
  144. L"SystemSetupInProgress",
  145. 0,
  146. REG_DWORD,
  147. (LPBYTE) &dwValue,
  148. sizeof(DWORD));
  149. dwValue = 0;
  150. RegSetValueEx(hSetupKey,
  151. L"SetupType",
  152. 0,
  153. REG_DWORD,
  154. (CONST BYTE *)&dwValue,
  155. sizeof(DWORD));
  156. // Delete the FactoryPreInstall value
  157. RegDeleteValue(hSetupKey, L"FactoryPreInstallInProgress");
  158. RegDeleteValue(hSetupKey, L"AuditInProgress");
  159. // Close the setup reg key
  160. RegCloseKey(hSetupKey);
  161. }
  162. return;
  163. }
  164. //
  165. // Get Organization and Owner names from registry
  166. //
  167. void GetNames(TCHAR szNameOrgOrg[], TCHAR szNameOrgName[])
  168. {
  169. HKEY hKey = NULL;
  170. DWORD dwLen = 0;
  171. if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), &hKey)) {
  172. dwLen = MAX_NAME;
  173. RegQueryValueEx(hKey, TEXT("RegisteredOrganization"), 0, 0, (LPBYTE)szNameOrgOrg, &dwLen);
  174. dwLen = MAX_NAME;
  175. RegQueryValueEx(hKey, TEXT("RegisteredOwner"), 0, 0, (LPBYTE)szNameOrgName, &dwLen);
  176. RegCloseKey(hKey);
  177. }
  178. }
  179. //
  180. // Strip out non alphabets from guid
  181. //
  182. DWORD StripDash(TCHAR *pszGuid)
  183. {
  184. TCHAR *pszOrg, *pszTemp = pszGuid;
  185. pszOrg = pszGuid;
  186. while (pszGuid && *pszGuid != TEXT('\0')) {
  187. if (*pszTemp != TEXT('-') && *pszTemp != TEXT('{') && *pszTemp != TEXT('}'))
  188. *pszGuid++ = *pszTemp++;
  189. else
  190. pszTemp++;
  191. }
  192. if (pszOrg)
  193. return (DWORD)lstrlen(pszOrg);
  194. return 0;
  195. }
  196. //
  197. // GenUniqueName - Create a random computer name with a base name of 8 chars
  198. //
  199. VOID GenUniqueName(
  200. OUT PWSTR GeneratedString,
  201. IN DWORD DesiredStrLen
  202. )
  203. {
  204. GUID guid;
  205. DWORD total = 0, length = 0;
  206. TCHAR szGuid[MAX_PATH];
  207. // If we have a valid out param
  208. //
  209. if (GeneratedString) {
  210. static PCWSTR UsableChars = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  211. //
  212. // How many characters will come from the org/name string.
  213. //
  214. DWORD BaseLength = 8;
  215. DWORD i,j;
  216. DWORD UsableCount;
  217. if( DesiredStrLen < BaseLength ) {
  218. BaseLength = DesiredStrLen - 1;
  219. }
  220. //
  221. // Get the Organization and Owner name from registry
  222. //
  223. GetNames(NameOrgOrg, NameOrgName);
  224. if( NameOrgOrg[0] ) {
  225. wcscpy( GeneratedString, NameOrgOrg );
  226. } else if( NameOrgName[0] ) {
  227. wcscpy( GeneratedString, NameOrgName );
  228. } else {
  229. wcscpy( GeneratedString, TEXT("X") );
  230. for( i = 1; i < BaseLength; i++ ) {
  231. wcscat( GeneratedString, TEXT("X") );
  232. }
  233. }
  234. //
  235. // Get him upper-case for our filter...
  236. //
  237. CharUpper(GeneratedString);
  238. //
  239. // Now we want to put a '-' at the end
  240. // of our GeneratedString. We'd like it to
  241. // be placed in the BASE_LENGTH character, but
  242. // the string may be shorter than that, or may
  243. // even have a ' ' in it. Figure out where to
  244. // put the '-' now.
  245. //
  246. for( i = 0; i <= BaseLength; i++ ) {
  247. //
  248. // Check for a short string.
  249. //
  250. if( (GeneratedString[i] == 0 ) ||
  251. (GeneratedString[i] == L' ') ||
  252. (!wcschr(UsableChars, GeneratedString[i])) ||
  253. (i == BaseLength )
  254. ) {
  255. GeneratedString[i] = L'-';
  256. GeneratedString[i+1] = 0;
  257. break;
  258. }
  259. }
  260. //
  261. // Special case the scenario where we had no usable
  262. // characters.
  263. //
  264. if( GeneratedString[0] == L'-' ) {
  265. GeneratedString[0] = 0;
  266. }
  267. total = lstrlen(GeneratedString);
  268. // Loop until we have meet the desired string length
  269. //
  270. while (total < DesiredStrLen) {
  271. // Create a unique guid to be used in the string
  272. //
  273. CoCreateGuid(&guid);
  274. StringFromGUID2(&guid, szGuid, AS(szGuid));
  275. // Remove the curly brace and dashes to generate the string
  276. //
  277. length = StripDash(szGuid);
  278. total += length;
  279. if (!lstrlen(GeneratedString)) {
  280. if (DesiredStrLen < total)
  281. lstrcpyn(GeneratedString, szGuid, DesiredStrLen+1); /* +1 for NULL */
  282. else
  283. lstrcpy(GeneratedString, szGuid);
  284. }
  285. else if (total < DesiredStrLen)
  286. lstrcat(GeneratedString, szGuid);
  287. else
  288. _tcsncat(GeneratedString, szGuid, (length - (total - DesiredStrLen)));
  289. }
  290. CharUpper(GeneratedString);
  291. // Assert if (total != DesiredStrLen)
  292. //
  293. }
  294. }
  295. /****************************************************************************\
  296. BOOL // Returns TRUE if any credentials are found and
  297. // are going to be returned.
  298. GetCredentials( // Tries to get user credentials from a few
  299. // different places.
  300. LPTSTR lpszUsername, // Pointer to a string buffer that will recieve
  301. // the user name for the credentials found.
  302. DWORD cbUsername, // Size, in characters, of the lpszUsername
  303. // string buffer.
  304. LPTSTR lpszPassword, // Pointer to a string buffer that will recieve
  305. // the password for the credentials found.
  306. DWORD cbPassword, // Size, in characters, of the lpszPassword
  307. // string buffer.
  308. LPTSTR lpFileName, // Optional pointer to the file name that will
  309. // contain the credentials. If this is NULL or
  310. // an empty string, the know registry key will
  311. // be checked instead for the credentials.
  312. LPTSTR lpAlternate // Optional pointer to the alternate section to
  313. // check first if lpFileName is vallid, or the
  314. // the optional registry key to check instead of
  315. // the normal known one.
  316. );
  317. \****************************************************************************/
  318. BOOL GetCredentials(LPTSTR lpszUsername, DWORD cbUsername, LPTSTR lpszPassword, DWORD cbPassword, LPTSTR lpFileName, LPTSTR lpAlternate)
  319. {
  320. BOOL bRet = FALSE;
  321. TCHAR szUsername[UNLEN + 1] = NULLSTR,
  322. szPassword[PWLEN + 1] = NULLSTR,
  323. szDomain[DNLEN + 1] = NULLSTR;
  324. LPSTR lpUserName;
  325. HKEY hKey;
  326. DWORD dwType,
  327. dwSize;
  328. BOOL bAlternate = ( lpAlternate && *lpAlternate );
  329. // Make sure there is a filename, otherwise we check the registry.
  330. //
  331. if ( lpFileName && *lpFileName )
  332. {
  333. // First try the alternate key for a user name.
  334. //
  335. if ( bAlternate )
  336. {
  337. GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_USERNAME, NULLSTR, szUsername, AS(szUsername), lpFileName);
  338. }
  339. // If none found, try the normal section. If they happen
  340. // to pass the normal section in as the alternate section then
  341. // we will check it twice if no key exists, no big deal.
  342. //
  343. if ( NULLCHR == szUsername[0] )
  344. {
  345. lpAlternate = WBOM_FACTORY_SECTION;
  346. GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_USERNAME, NULLSTR, szUsername, AS(szUsername), lpFileName);
  347. }
  348. // Make sure we found a user name.
  349. //
  350. if ( szUsername[0] )
  351. {
  352. // If there is now backslash in the username, and there is a domain key, use that as the domain
  353. //
  354. if ( ((StrChr( szUsername, CHR_BACKSLASH )) == NULL) &&
  355. (GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_DOMAIN, NULLSTR, szDomain, AS(szDomain), lpFileName)) && szDomain[0]
  356. )
  357. {
  358. // Copy the "domain\username" string into the returning buffer
  359. //
  360. lstrcpyn(lpszUsername, szDomain, cbUsername);
  361. AddPathN(lpszUsername, szUsername, cbUsername);
  362. }
  363. else
  364. {
  365. // Copy the username into the returning buffer
  366. //
  367. lstrcpyn(lpszUsername, szUsername, cbUsername);
  368. }
  369. // We found the credentials
  370. //
  371. bRet = TRUE;
  372. // Get the password
  373. //
  374. if ( GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_PASSWORD, NULLSTR, szPassword, AS(szPassword), lpFileName) )
  375. {
  376. // Copy the password into the returning buffer
  377. //
  378. lstrcpyn(lpszPassword, szPassword, cbPassword);
  379. }
  380. else
  381. *lpszPassword = NULLCHR;
  382. }
  383. }
  384. else if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, bAlternate ? lpAlternate : REG_KEY_FACTORY, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS )
  385. {
  386. // Check the registry key to see if it has user credentials.
  387. //
  388. dwSize = sizeof(szUsername);
  389. if ( ( RegQueryValueEx(hKey, REG_VAL_FACTORY_USERNAME, NULL, &dwType, (LPBYTE) szUsername, &dwSize) == ERROR_SUCCESS ) &&
  390. ( dwType == REG_SZ ) &&
  391. ( szUsername[0] ) )
  392. {
  393. // Check the registry key to see if it has user credentials.
  394. //
  395. dwSize = sizeof(szDomain);
  396. if ( ( StrChr(szUsername, CHR_BACKSLASH) == NULL ) &&
  397. ( RegQueryValueEx(hKey, REG_VAL_FACTORY_DOMAIN, NULL, &dwType, (LPBYTE) szDomain, &dwSize) == ERROR_SUCCESS ) &&
  398. ( dwType == REG_SZ ) &&
  399. ( szDomain[0] ) )
  400. {
  401. // Copy the domain and username into the returning buffer.
  402. //
  403. AddPathN(szDomain, szUsername, AS(szDomain));
  404. lstrcpyn(lpszUsername, szDomain, cbUsername);
  405. }
  406. else
  407. {
  408. // Copy the username into the returning buffer.
  409. //
  410. lstrcpyn(lpszUsername, szUsername, cbUsername);
  411. }
  412. // We found the credentials
  413. //
  414. bRet = TRUE;
  415. // Check the registry key to see if it has user credentials.
  416. //
  417. dwSize = sizeof(szPassword);
  418. if ( ( RegQueryValueEx(hKey, REG_VAL_FACTORY_PASSWORD, NULL, &dwType, (LPBYTE) szPassword, &dwSize) == ERROR_SUCCESS ) &&
  419. ( dwType == REG_SZ ) )
  420. {
  421. // Copy the password into the returning buffer
  422. //
  423. lstrcpyn(lpszPassword, szPassword, cbPassword);
  424. }
  425. else
  426. {
  427. // No password specified, just return and empty string.
  428. //
  429. *lpszPassword = NULLCHR;
  430. }
  431. }
  432. // Always remember to close the key.
  433. //
  434. RegCloseKey(hKey);
  435. }
  436. return bRet;
  437. }
  438. NET_API_STATUS FactoryNetworkConnectEx(LPTSTR lpszPath, LPTSTR lpszWinBOMPath, LPTSTR lpAlternateSection, LPTSTR lpszUsername, DWORD cbUsername, LPTSTR lpszPassword, DWORD cbPassword, BOOL bState)
  439. {
  440. NET_API_STATUS nErr,
  441. nRet = 0;
  442. static BOOL bFirst = TRUE;
  443. BOOL bJustStarted = FALSE;
  444. TCHAR szUsername[UNLEN + DNLEN + 2] = NULLSTR,
  445. szPassword[PWLEN + 1] = NULLSTR,
  446. szBuffer[MAX_PATH],
  447. szWinbomShare[MAX_PATH] = NULLSTR;
  448. LPTSTR lpSearch;
  449. DWORD dwStart;
  450. // Get the credentials for the current section
  451. //
  452. if ( bState )
  453. {
  454. // Make sure we pass in a username buffer big enough to hold the "domain\username" string.
  455. //
  456. GetCredentials(szUsername, AS(szUsername), szPassword, AS(szPassword), lpszWinBOMPath, lpAlternateSection);
  457. }
  458. // Get just the share pare of the winbom path if it is a UNC.
  459. //
  460. if ( lpszWinBOMPath )
  461. {
  462. GetUncShare(lpszWinBOMPath, szWinbomShare, AS(szWinbomShare));
  463. }
  464. // Determine all of the UNC paths in the string supplied
  465. //
  466. lpSearch = lpszPath;
  467. while ( lpSearch = StrStr(lpSearch, _T("\\\\")) )
  468. {
  469. // See if this is a UNC share.
  470. //
  471. if ( GetUncShare(lpSearch, szBuffer, AS(szBuffer)) && szBuffer[0] )
  472. {
  473. // We can not connect or disconnect from the share where the winbom is.
  474. //
  475. if ( ( NULLCHR == szWinbomShare[0] ) ||
  476. ( lstrcmpi(szBuffer, szWinbomShare) != 0 ) )
  477. {
  478. // Connect/disconnect from the share and
  479. //
  480. nErr = 0;
  481. dwStart = GetTickCount();
  482. do
  483. {
  484. if ( nErr )
  485. {
  486. Sleep(100);
  487. }
  488. if ( NERR_WkstaNotStarted == (nErr = ConnectNetworkResource(szBuffer, szUsername, szPassword, bState)) )
  489. {
  490. // Wierd bug here we are hacking around. If we just wait till the network starts, sometimes the first
  491. // call it gives us a wierd error. So if we run into the not started error, then we keep retrying on
  492. // any error until we time out.
  493. //
  494. bJustStarted = TRUE;
  495. }
  496. #ifdef DBG
  497. LogFileStr(_T("c:\\sysprep\\winbom.log"), _T("FactoryNetworkConnect(%s)=%d [%d,%d]\n"), szBuffer, nErr, dwStart, GetTickCount());
  498. #endif // DBG
  499. }
  500. while ( ( bFirst && bJustStarted && nErr ) &&
  501. ( (GetTickCount() - dwStart) < NET_TIMEOUT ) );
  502. // If we hit an error and it is the first one,
  503. // return it.
  504. //
  505. if ( nErr && ( 0 == nRet ) )
  506. {
  507. nRet = nErr;
  508. }
  509. // Once we have tried to connect to a network resource, we set
  510. // this so we don't ever time out again as long as we are still
  511. // running.
  512. //
  513. bFirst = FALSE;
  514. }
  515. // Move the pointer past the share name.
  516. //
  517. lpSearch += lstrlen(szBuffer);
  518. }
  519. else
  520. {
  521. // Go past the double backslash even though it is not a UNC path.
  522. //
  523. lpSearch += 2;
  524. }
  525. }
  526. // Might need to return the credentials we used.
  527. //
  528. if ( lpszUsername && cbUsername )
  529. {
  530. lstrcpyn(lpszUsername, szUsername, cbUsername);
  531. }
  532. if ( lpszPassword && cbPassword )
  533. {
  534. lstrcpyn(lpszPassword, szPassword, cbPassword);
  535. }
  536. // Return the net error, or 0 if everything worked.
  537. //
  538. return nRet;
  539. }
  540. NET_API_STATUS FactoryNetworkConnect(LPTSTR lpszPath, LPTSTR lpszWinBOMPath, LPTSTR lpAlternateSection, BOOL bState)
  541. {
  542. return FactoryNetworkConnectEx(lpszPath, lpszWinBOMPath, lpAlternateSection, NULL, 0, NULL, 0, bState);
  543. }
  544. /*++
  545. ===============================================================================
  546. Routine Description:
  547. This routine will locate the WINBOM.INI file. The search algorithm will be:
  548. * Check the registry key
  549. * Check local floppy drives
  550. * Check local CD-ROM drives
  551. * Check the sysprep folder
  552. * Check the root of the boot volume
  553. Arguments:
  554. lpWinBOMPath - return buffer where the winbom path will be copied.
  555. cbWinbBOMPath - size of return buffer in characters.
  556. lpFactoryPath - the sysprep folder where factory.exe is.
  557. Return Value:
  558. TRUE - WINBOM.INI was found
  559. FALSE - WINBOM.INI could not be found
  560. ===============================================================================
  561. --*/
  562. BOOL LocateWinBom(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpFactoryPath, LPTSTR lpFactoryMode, DWORD dwFlags)
  563. {
  564. BOOL bFound = FALSE,
  565. bRunningFromCd,
  566. bNetwork = !(GET_FLAG(dwFlags, LOCATE_NONET));
  567. TCHAR szWinBom[MAX_PATH] = NULLSTR;
  568. TCHAR szNewShare[MAX_PATH],
  569. szNewUser[256],
  570. szNewPass[256],
  571. szCurShare[MAX_PATH] = NULLSTR,
  572. szCurUser[256] = NULLSTR,
  573. szCurPass[256] = NULLSTR;
  574. // Set the error mode so no drives display error messages ("please insert floppy")
  575. //
  576. SetErrorMode(SEM_FAILCRITICALERRORS);
  577. // Always set the return buffer to an empty string first.
  578. //
  579. *lpWinBOMPath = NULLCHR;
  580. // This neat little flag is used when we think that we already found a
  581. // winbom sometime this boot (like when factory runs from the run key) and
  582. // we want to make sure we use the same winbom instead of searching again
  583. // and maybe getting a different one.
  584. //
  585. if ( GET_FLAG(dwFlags, LOCATE_AGAIN) )
  586. {
  587. BOOL bDone;
  588. // Try our state winbom key to see if we have a winbom that we are already using.
  589. //
  590. bFound = CheckWinbomRegKey(szWinBom, AS(szWinBom), szCurShare, AS(szCurShare), szCurUser, AS(szCurUser), szCurPass, AS(szCurPass), lpFactoryMode, REG_KEY_FACTORY_STATE, bNetwork, &bDone);
  591. // Now if the registry key existed, we don't want to do anything more. Just use
  592. // what we got right now and be done with it.
  593. //
  594. if ( bDone )
  595. {
  596. // Check to see if we have anything to return.
  597. //
  598. if ( bFound )
  599. {
  600. // Copy the path we found into the return buffer.
  601. //
  602. lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath);
  603. }
  604. // Set the error mode back to system default
  605. //
  606. SetErrorMode(0);
  607. // Return right now if we found it or not.
  608. //
  609. return bFound;
  610. }
  611. }
  612. // Check to see if the system drive is a CD-ROM (which pretty much
  613. // means that we are running in WinPE and we should search this drive
  614. // last).
  615. //
  616. ExpandEnvironmentStrings(DIR_SYSTEMROOT, szWinBom, AS(szWinBom));
  617. bRunningFromCd = ( GetDriveType(szWinBom) == DRIVE_CDROM );
  618. szWinBom[0] = NULLCHR;
  619. // Check the magic registry key first as an option to override were the winbom is.
  620. //
  621. bFound = CheckWinbomRegKey(szWinBom, AS(szWinBom), szCurShare, AS(szCurShare), szCurUser, AS(szCurUser), szCurPass, AS(szCurPass), lpFactoryMode, REG_KEY_FACTORY, bNetwork, NULL);
  622. // Walk through the drives first checking if the drive is removeable and NOT CDROM.
  623. // Check for the presence of the WinBOM file and quit if it is found.
  624. //
  625. if ( !bFound )
  626. {
  627. bFound = SearchRemovableDrives(szWinBom, AS(szWinBom), lpFactoryMode, DRIVE_REMOVABLE);
  628. }
  629. // Walk through the drives again this time checking if the drive IS a CD-ROM.
  630. // Check for the presence of the WinBOM file and quit if it is found. Also
  631. // only do this if the OS is not running from a CD. This is so in the WinPE
  632. // case the person can put a winbom on the hard drive and it will be used
  633. // before the one that is always on the CD-ROM that WinPE boots from.
  634. //
  635. if ( !bFound )
  636. {
  637. bFound = SearchRemovableDrives(szWinBom, AS(szWinBom), lpFactoryMode, bRunningFromCd ? DRIVE_FIXED : DRIVE_CDROM);
  638. }
  639. // Now if still not found, check the same directory as factory.
  640. //
  641. if ( !bFound )
  642. {
  643. lstrcpyn(szWinBom, lpFactoryPath, AS(szWinBom));
  644. AddPath(szWinBom, FILE_WINBOM_INI);
  645. bFound = WinBOMExists(szWinBom, lpFactoryMode);
  646. }
  647. // Now if still not found, check the root of the system drive.
  648. //
  649. if ( !bFound )
  650. {
  651. ExpandEnvironmentStrings(DIR_SYSTEMROOT, szWinBom, AS(szWinBom));
  652. lstrcat(szWinBom, FILE_WINBOM_INI);
  653. bFound = WinBOMExists(szWinBom, lpFactoryMode);
  654. }
  655. // Now if we skipped the CD-ROM search above, do it now if we still
  656. // don't have a winbom.
  657. //
  658. if ( !bFound && bRunningFromCd )
  659. {
  660. bFound = SearchRemovableDrives(szWinBom, AS(szWinBom), lpFactoryMode, DRIVE_CDROM);
  661. }
  662. // Make sure we found a WinBOM and look for a NewWinbom key.
  663. //
  664. if ( bFound )
  665. {
  666. DWORD dwLimit = 10; // Must be greater than zero.
  667. BOOL bAgain;
  668. LPTSTR lpszNewWinbom;
  669. // Copy the path we found into the return buffer.
  670. //
  671. lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath);
  672. // Now do the loop to search for possible NewWinBom keys.
  673. //
  674. do
  675. {
  676. // Reset the bool so we can check for alternate WinBOMs.
  677. //
  678. bAgain = FALSE;
  679. // See if the NewWinBom key exists in the winbom we found.
  680. //
  681. if ( lpszNewWinbom = IniGetExpand(lpWinBOMPath, INI_SEC_WBOM_FACTORY, INI_KEY_WBOM_FACTORY_NEWWINBOM, NULL) )
  682. {
  683. LPTSTR lpShareRemove;
  684. BOOL bSame = FALSE;
  685. // The NewWinBom key might be a UNC, so see if we need to connect
  686. // to the share.
  687. //
  688. szNewShare[0] = NULLCHR;
  689. szNewUser[0] = NULLCHR;
  690. szNewPass[0] = NULLCHR;
  691. if ( bNetwork && GetUncShare(lpszNewWinbom, szNewShare, AS(szNewShare)) && szNewShare[0] )
  692. {
  693. // Only really need to connect if the we are not already
  694. // connected.
  695. //
  696. if ( lstrcmpi(szNewShare, szCurShare) != 0 )
  697. {
  698. FactoryNetworkConnectEx(szNewShare, lpWinBOMPath, NULL, szNewUser, AS(szNewUser), szNewPass, AS(szNewPass), TRUE);
  699. }
  700. else
  701. {
  702. bSame = TRUE;
  703. }
  704. }
  705. // Now make sure the winbom we found really exists and is a vallid
  706. // winbom we can use.
  707. //
  708. if ( WinBOMExists(lpszNewWinbom, lpFactoryMode) )
  709. {
  710. // Copy the new winbom path we found into the return buffer.
  711. //
  712. lstrcpyn(lpWinBOMPath, lpszNewWinbom, cbWinbBOMPath);
  713. bAgain = TRUE;
  714. lpShareRemove = szCurShare;
  715. }
  716. else
  717. {
  718. lpShareRemove = szNewShare;
  719. }
  720. // Do any share cleanup that needs to happen.
  721. //
  722. if ( bNetwork && *lpShareRemove && !bSame )
  723. {
  724. FactoryNetworkConnect(lpShareRemove, NULL, NULL, FALSE);
  725. *lpShareRemove = NULLCHR;
  726. }
  727. // Also save the share info if there was one so we can cleanup
  728. // later if we find another winbom.
  729. //
  730. if ( bAgain )
  731. {
  732. lstrcpyn(szCurShare, szNewShare, AS(szCurShare));
  733. lstrcpyn(szCurUser, szNewUser, AS(szCurUser));
  734. lstrcpyn(szCurPass, szNewPass, AS(szCurPass));
  735. }
  736. // Clean up the ini key we allocated.
  737. //
  738. FREE(lpszNewWinbom);
  739. }
  740. }
  741. while ( --dwLimit && bAgain );
  742. }
  743. // Save the winbom we are using (or empty string if not using one) to our state
  744. // key so other programs or instances of factory running this boot know what winbom
  745. // to use.
  746. //
  747. RegSetString(HKLM, REG_KEY_FACTORY_STATE, REG_VAL_FACTORY_WINBOM, lpWinBOMPath);
  748. // We may want to save the credentials that we used to get to this winbom in our
  749. // state key so we can get them back and reconnect, but for now if we just don't
  750. // ever disconnect from the share where the winbom is, we should be fine. Only need
  751. // to worry about this if the caller of the function wanted to disconnect this
  752. // network resource when they were done. If we do that, these keys will have to
  753. // be written. But we will have to put in a bunch of code so that CheckWinbomRegKey()
  754. // returns the credentials used, and when doing our NewWinBom search that we also
  755. // save the credentials we finally end up using. This is a lot of work, and I see
  756. // no need for this now. Just make sure if you call this function that you do NOT
  757. // call FactoryNetworkConnect() to remove the net connection.
  758. //
  759. // This didn't work, because when we log on we loose our net connection so we need
  760. // the credentials to reconnect after logon. So I went through all the work I mentioned
  761. // above to make this work.
  762. //
  763. RegSetString(HKLM, REG_KEY_FACTORY_STATE, REG_VAL_FACTORY_USERNAME, szCurUser);
  764. RegSetString(HKLM, REG_KEY_FACTORY_STATE, REG_VAL_FACTORY_PASSWORD, szCurPass);
  765. // Set the error mode back to system default
  766. //
  767. SetErrorMode(0);
  768. // Return if we found it or not.
  769. //
  770. return bFound;
  771. }
  772. BOOL SetFactoryStartup(LPCTSTR lpFactory)
  773. {
  774. HKEY hKey;
  775. BOOL bRet = TRUE;
  776. // Now make sure we are also setup as a setup program to run before we log on.
  777. //
  778. if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SYSTEM\\Setup"), 0, KEY_ALL_ACCESS, &hKey ) == ERROR_SUCCESS )
  779. {
  780. TCHAR szFileName[MAX_PATH + 32] = NULLSTR;
  781. DWORD dwVal;
  782. //
  783. // Setup the control flags for the SETUP key
  784. // The Setting used are:
  785. // CmdLine = c:\sysprep\factory.exe -setup
  786. // SetupType = 2 (No reboot)
  787. // SystemSetupInProgress = 0 (no service restrictions)
  788. // MiniSetupInProgress = 0 (Not doing a mini setup)
  789. // FactoryPreInstallInProgress = 1 (Delay pnp driver installs)
  790. // AuditInProgress = 1 (general key to determine if the OEM is auditing the machine)
  791. //
  792. lstrcpyn(szFileName, lpFactory, AS(szFileName));
  793. lstrcat(szFileName, _T(" -setup"));
  794. if ( RegSetValueEx(hKey, _T("CmdLine"), 0, REG_SZ, (CONST LPBYTE) szFileName, ( lstrlen(szFileName) + 1 ) * sizeof(TCHAR)) != ERROR_SUCCESS )
  795. bRet = FALSE;
  796. dwVal = SETUPTYPE_NOREBOOT;
  797. if ( RegSetValueEx(hKey, TEXT("SetupType"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
  798. bRet = FALSE;
  799. dwVal = 0;
  800. if ( RegSetValueEx(hKey, TEXT("SystemSetupInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
  801. bRet = FALSE;
  802. dwVal = 0;
  803. if ( RegSetValueEx(hKey, TEXT("MiniSetupInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
  804. bRet = FALSE;
  805. dwVal = 1;
  806. if ( RegSetValueEx(hKey, TEXT("FactoryPreInstallInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
  807. bRet = FALSE;
  808. dwVal = 1;
  809. if ( RegSetValueEx(hKey, TEXT("AuditInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
  810. bRet = FALSE;
  811. RegCloseKey(hKey);
  812. }
  813. else
  814. bRet = FALSE;
  815. return bRet;
  816. }
  817. BOOL UpdateDevicePathEx(HKEY hKeyRoot, LPTSTR lpszSubKey, LPTSTR lpszNewPath, LPTSTR lpszRoot, BOOL bRecursive)
  818. {
  819. LPSTRLIST lpSorted = NULL,
  820. lpUnsorted = NULL;
  821. LPTSTR lpszDevicePath;
  822. // First add any paths already in the registry to the lists.
  823. //
  824. if ( lpszDevicePath = RegGetString(hKeyRoot, lpszSubKey, REG_VAL_DEVICEPATH) )
  825. {
  826. AddPathsToList(lpszDevicePath, NULL, &lpSorted, &lpUnsorted, FALSE);
  827. FREE(lpszDevicePath);
  828. }
  829. // Now add any they wanted to the list.
  830. //
  831. AddPathsToList(lpszNewPath, lpszRoot, &lpSorted, &lpUnsorted, bRecursive);
  832. // Now that we are done, we can free our sorted list.
  833. //
  834. SavePathList(hKeyRoot, lpszSubKey, lpSorted, FALSE);
  835. // Now save our final list back to the registry and free
  836. // it.
  837. //
  838. SavePathList(hKeyRoot, lpszSubKey, lpUnsorted, TRUE);
  839. return TRUE;
  840. }
  841. BOOL UpdateDevicePath(LPTSTR lpszNewPath, LPTSTR lpszRoot, BOOL bRecursive)
  842. {
  843. return ( UpdateDevicePathEx( HKLM,
  844. REGSTR_PATH_SETUP,
  845. lpszNewPath,
  846. lpszRoot ? lpszRoot : DIR_SYSTEMROOT,
  847. bRecursive ) );
  848. }
  849. BOOL UpdateSourcePath(LPTSTR lpszSourcePath)
  850. {
  851. BOOL bRet = FALSE;
  852. if ( lpszSourcePath && *lpszSourcePath )
  853. {
  854. if (bRet = RegSetExpand(HKLM, REG_KEY_SETUP_SETUP, REG_VAL_SOURCEPATH, lpszSourcePath))
  855. {
  856. bRet = RegSetExpand(HKLM, REG_KEY_SETUP_SETUP, REG_VAL_SPSOURCEPATH, lpszSourcePath);
  857. }
  858. }
  859. return bRet;
  860. }
  861. //
  862. // Internal Function(s):
  863. //
  864. static BOOL CheckWinbomRegKey(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath,
  865. LPTSTR lpszShare, DWORD cbShare,
  866. LPTSTR lpszUser, DWORD cbUser,
  867. LPTSTR lpszPass, DWORD cbPass,
  868. LPTSTR lpFactoryMode, LPTSTR lpKey,
  869. BOOL bNetwork, LPBOOL lpbExists)
  870. {
  871. HKEY hKey;
  872. BOOL bFound = FALSE,
  873. bExists = FALSE;
  874. TCHAR szWinBom[MAX_PATH] = NULLSTR;
  875. // Check the registry key to see if it knows about a winbom to use.
  876. //
  877. if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpKey, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS )
  878. {
  879. DWORD dwType,
  880. dwSize = sizeof(szWinBom);
  881. // Try to get the value.
  882. //
  883. if ( ( RegQueryValueEx(hKey, REG_VAL_FACTORY_WINBOM, NULL, &dwType, (LPBYTE) szWinBom, &dwSize) == ERROR_SUCCESS ) &&
  884. ( dwType == REG_SZ ) )
  885. {
  886. // Now the key must have existed. If there was something
  887. // in the key, lets try to use it.
  888. //
  889. if ( szWinBom[0] )
  890. {
  891. TCHAR szShare[MAX_PATH] = NULLSTR,
  892. szUser[256] = NULLSTR,
  893. szPass[256] = NULLSTR;
  894. // Throw some networking support in here.
  895. //
  896. if ( bNetwork && GetUncShare(szWinBom, szShare, AS(szShare)) && szShare[0] )
  897. {
  898. FactoryNetworkConnectEx(szShare, NULL, lpKey, szUser, AS(szUser), szPass, AS(szPass), TRUE);
  899. }
  900. // Check to see if the winbom is actually there still.
  901. // If not, then it is bad and we should just act like
  902. // the key didn't even exist.
  903. //
  904. if ( WinBOMExists(szWinBom, lpFactoryMode) )
  905. {
  906. // If found, return the winbom in the supplied buffer.
  907. //
  908. lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath);
  909. bFound = bExists = TRUE;
  910. // See what we might need to return.
  911. //
  912. if ( lpszShare && cbShare )
  913. {
  914. lstrcpyn(lpszShare, szShare, cbShare);
  915. }
  916. if ( lpszUser && cbUser )
  917. {
  918. lstrcpyn(lpszUser, szUser, cbUser);
  919. }
  920. if ( lpszPass && cbPass )
  921. {
  922. lstrcpyn(lpszPass, szPass, cbPass);
  923. }
  924. }
  925. else if ( bNetwork && szShare[0] )
  926. {
  927. // Clean up our net connection.
  928. //
  929. FactoryNetworkConnect(szShare, NULL, NULL, FALSE);
  930. }
  931. }
  932. else
  933. {
  934. // There wasn't anything in the key, but it did exist.
  935. // This most likely means we didn't find the winbom the
  936. // first time around, so we may need to know that now.
  937. //
  938. bExists = TRUE;
  939. }
  940. }
  941. // Always remember to close the key.
  942. //
  943. RegCloseKey(hKey);
  944. }
  945. // If they want to know if the key existed, then return it to them.
  946. //
  947. if ( lpbExists )
  948. {
  949. *lpbExists = bExists;
  950. }
  951. // If we found a winbom, return true.
  952. //
  953. return bFound;
  954. }
  955. static BOOL SearchRemovableDrives(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpFactoryMode, UINT uDriveType)
  956. {
  957. DWORD dwDrives;
  958. TCHAR szWinBom[MAX_PATH],
  959. szDrive[] = _T("_:\\");
  960. BOOL bFound = FALSE;
  961. // Loop through all the dirves on the system.
  962. //
  963. for ( szDrive[0] = _T('A'), dwDrives = GetLogicalDrives();
  964. ( szDrive[0] <= _T('Z') ) && dwDrives && !bFound;
  965. szDrive[0]++, dwDrives >>= 1 )
  966. {
  967. // First check to see if the first bit is set (which means
  968. // this drive exists in the system). Then make sure it is
  969. // a drive type that we want to check for a winbom.
  970. //
  971. if ( ( dwDrives & 0x1 ) &&
  972. ( GetDriveType(szDrive) == uDriveType ) )
  973. {
  974. // See if there is a wINBOM.INI file on the drive.
  975. //
  976. lstrcpyn(szWinBom, szDrive, AS(szWinBom));
  977. lstrcat(szWinBom, FILE_WINBOM_INI);
  978. if ( WinBOMExists(szWinBom, lpFactoryMode) )
  979. {
  980. // Return the path to the winbom in the supplied buffer.
  981. //
  982. lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath);
  983. bFound = TRUE;
  984. }
  985. }
  986. }
  987. return bFound;
  988. }
  989. static BOOL WinBOMExists(LPTSTR lpWinBom, LPTSTR lpMode)
  990. {
  991. BOOL bRet = FALSE;
  992. // First the file must exists.
  993. //
  994. if ( FileExists(lpWinBom) )
  995. {
  996. TCHAR szModes[256] = NULLSTR;
  997. // See if there is even a mode string in this winbom (has to be or
  998. // we will automatically use it).
  999. //
  1000. if ( lpMode &&
  1001. *lpMode &&
  1002. GetPrivateProfileString(WBOM_FACTORY_SECTION, INI_KEY_WBOM_FACTORY_TYPE, NULLSTR, szModes, AS(szModes), lpWinBom) &&
  1003. szModes[0] )
  1004. {
  1005. LPTSTR lpCheck = szModes,
  1006. lpNext;
  1007. // Loop through ever comma delimited field in the value we got
  1008. // from the winbom (there is always at least one).
  1009. //
  1010. do
  1011. {
  1012. // See if there is another mode field in this string.
  1013. //
  1014. if ( lpNext = StrChr(lpCheck, _T(',')) )
  1015. *lpNext++ = NULLCHR;
  1016. // Make sure there are no spaces around the field.
  1017. //
  1018. StrTrm(lpCheck, _T(' '));
  1019. // If the mode we are in matches the one in the winbom, then
  1020. // we are good to go.
  1021. //
  1022. if ( lstrcmpi(lpMode, lpCheck) == 0 )
  1023. bRet = TRUE;
  1024. // Set the check pointer to the next
  1025. // field.
  1026. //
  1027. lpCheck = lpNext;
  1028. }
  1029. while ( !bRet && lpCheck );
  1030. // It would be nice to log if we don't use this winbom because of this
  1031. // setting, but we can't really do that because we need to winbom to
  1032. // init logging.
  1033. //
  1034. /*
  1035. if ( !bRet )
  1036. {
  1037. // Log here.
  1038. }
  1039. */
  1040. }
  1041. else
  1042. bRet = TRUE;
  1043. }
  1044. return bRet;
  1045. }
  1046. static void SavePathList(HKEY hKeyRoot, LPTSTR lpszSubKey, LPSTRLIST lpStrList, BOOL bWrite)
  1047. {
  1048. LPSTRLIST lpStrListNode;
  1049. DWORD cbDevicePath = 256,
  1050. dwLength = 0,
  1051. dwOldSize;
  1052. LPTSTR lpszDevicePath;
  1053. // Initialize our intial buffer we are going to use to
  1054. // write to the registry.
  1055. //
  1056. if ( bWrite )
  1057. {
  1058. lpszDevicePath = (LPTSTR) MALLOC(cbDevicePath * sizeof(TCHAR));
  1059. }
  1060. // Loop through the list.
  1061. //
  1062. while ( lpStrList )
  1063. {
  1064. // Save a pointer to the current node.
  1065. //
  1066. lpStrListNode = lpStrList;
  1067. // Advanced to the next node.
  1068. //
  1069. lpStrList = lpStrList->lpNext;
  1070. // If we are saving this list to the registry, then
  1071. // we need to add to our buffer.
  1072. //
  1073. if ( bWrite && lpszDevicePath )
  1074. {
  1075. // Make sure our buffer is still big enough.
  1076. // The two extra are for the possible semi-colon
  1077. // we might add and one more to be safe. We
  1078. // don't have to worry about the null terminator
  1079. // because we do less than or equal to our current
  1080. // buffer size.
  1081. //
  1082. dwOldSize = cbDevicePath;
  1083. dwLength += lstrlen(lpStrListNode->lpszData);
  1084. while ( cbDevicePath <= (dwLength + 2) )
  1085. {
  1086. cbDevicePath *= 2;
  1087. }
  1088. // If it wasn't big enough, we need to reallocate it.
  1089. //
  1090. if ( cbDevicePath > dwOldSize )
  1091. {
  1092. LPTSTR lpszTmpDevicePath = (LPTSTR) REALLOC(lpszDevicePath, cbDevicePath * sizeof(TCHAR));
  1093. //
  1094. // Make sure the REALLOC succeeded before reassigning the memory
  1095. //
  1096. if ( lpszTmpDevicePath )
  1097. {
  1098. lpszDevicePath = lpszTmpDevicePath;
  1099. }
  1100. }
  1101. // Make sure we still have a buffer.
  1102. //
  1103. if ( lpszDevicePath )
  1104. {
  1105. // If we already have added a path, tack on a semicolon.
  1106. //
  1107. if ( *lpszDevicePath )
  1108. {
  1109. lstrcat(lpszDevicePath, _T(";"));
  1110. dwLength++;
  1111. }
  1112. // Now add our path.
  1113. //
  1114. lstrcat(lpszDevicePath, lpStrListNode->lpszData);
  1115. }
  1116. }
  1117. // Free the data in this node.
  1118. //
  1119. FREE(lpStrListNode->lpszData);
  1120. // Free the node itself.
  1121. //
  1122. FREE(lpStrListNode);
  1123. }
  1124. // If we have the data, save it to the registry.
  1125. //
  1126. if ( bWrite && lpszDevicePath )
  1127. {
  1128. RegSetExpand(hKeyRoot, lpszSubKey, REG_VAL_DEVICEPATH, lpszDevicePath);
  1129. FREE(lpszDevicePath);
  1130. }
  1131. }
  1132. static BOOL AddPathToList(LPTSTR lpszExpanded, LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted)
  1133. {
  1134. LPSTRLIST lpSortedNode,
  1135. lpUnsortedNode;
  1136. BOOL bQuit = FALSE;
  1137. // Loop until we get to the end or find a string that is bigger than
  1138. // ours.
  1139. //
  1140. while ( *lplpSorted && !bQuit )
  1141. {
  1142. // If we do this, we don't have to do the complicated
  1143. // indirection.
  1144. //
  1145. lpSortedNode = *lplpSorted;
  1146. // Compare the nodes string to the one we want to add.
  1147. //
  1148. switch ( CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, lpszExpanded, -1, lpSortedNode->lpszData, -1) )
  1149. {
  1150. case CSTR_EQUAL:
  1151. // If the are the same, we just return FALSE because we do
  1152. // not need to add it.
  1153. //
  1154. return FALSE;
  1155. case CSTR_LESS_THAN:
  1156. // If our string is less than the one in this node, we need
  1157. // to stop so we can insert ourself before it.
  1158. //
  1159. bQuit = TRUE;
  1160. break;
  1161. default:
  1162. // Default, just try the next item in the list.
  1163. //
  1164. lplpSorted = &(lpSortedNode->lpNext);
  1165. }
  1166. }
  1167. // Now we need to advance the pointer of the unsorted list to the
  1168. // end so we can add ours.
  1169. //
  1170. while ( *lplpUnsorted )
  1171. {
  1172. lpUnsortedNode = *lplpUnsorted;
  1173. lplpUnsorted = &(lpUnsortedNode->lpNext);
  1174. }
  1175. // Allocate our nodes. If anything fails, we have to return false.
  1176. //
  1177. if ( NULL == (lpSortedNode = (LPSTRLIST) MALLOC(sizeof(STRLIST))) )
  1178. {
  1179. return FALSE;
  1180. }
  1181. if ( NULL == (lpUnsortedNode = (LPSTRLIST) MALLOC(sizeof(STRLIST))) )
  1182. {
  1183. FREE(lpSortedNode);
  1184. return FALSE;
  1185. }
  1186. // Set the data in the sorted node and insert the list since we
  1187. // know where that goes right now.
  1188. //
  1189. lpSortedNode->lpszData = lpszExpanded;
  1190. lpSortedNode->lpNext = *lplpSorted;
  1191. *lplpSorted = lpSortedNode;
  1192. // Now set the data in the unsorted node and insert it at the end
  1193. // of that list.
  1194. //
  1195. lpUnsortedNode->lpszData = lpszPath;
  1196. lpUnsortedNode->lpNext = NULL;
  1197. *lplpUnsorted = lpUnsortedNode;
  1198. return TRUE;
  1199. }
  1200. static void EnumeratePath(LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted)
  1201. {
  1202. WIN32_FIND_DATA FileFound;
  1203. HANDLE hFile;
  1204. LPTSTR lpszNewPath,
  1205. lpszExpandedPath;
  1206. DWORD cbNewPath;
  1207. BOOL bAdded = FALSE;
  1208. // Process all the files and directories in the directory passed in.
  1209. //
  1210. if ( (hFile = FindFirstFile(_T("*"), &FileFound)) != INVALID_HANDLE_VALUE )
  1211. {
  1212. do
  1213. {
  1214. // First check to see if this is a a directory.
  1215. //
  1216. if ( ( FileFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) &&
  1217. ( lstrcmp(FileFound.cFileName, _T(".")) != 0 ) &&
  1218. ( lstrcmp(FileFound.cFileName, _T("..")) != 0 ) &&
  1219. ( SetCurrentDirectory(FileFound.cFileName) ) )
  1220. {
  1221. // Need the size for the new path... which is the length of the
  1222. // old path, plus new path, and 3 extra for the joining backslash,
  1223. // null terminator, and another one more to be safe.
  1224. //
  1225. cbNewPath = lstrlen(lpszPath) + lstrlen(FileFound.cFileName) + 3;
  1226. if ( lpszNewPath = (LPTSTR) MALLOC(cbNewPath * sizeof(TCHAR)) )
  1227. {
  1228. // Create our new path (note this one is not expanded out,
  1229. // it may contain environment variables.
  1230. //
  1231. lstrcpyn(lpszNewPath, lpszPath, cbNewPath);
  1232. AddPathN(lpszNewPath, FileFound.cFileName, cbNewPath);
  1233. // Make sure we can expand out the buffer.
  1234. //
  1235. if ( lpszExpandedPath = AllocateExpand(lpszNewPath) )
  1236. {
  1237. // Now add the path to the list.
  1238. //
  1239. bAdded = AddPathToList(lpszExpandedPath, lpszNewPath, lplpSorted, lplpUnsorted);
  1240. // If the path didn't get added, we have to free the buffer.
  1241. //
  1242. if ( !bAdded )
  1243. {
  1244. FREE(lpszExpandedPath);
  1245. }
  1246. }
  1247. // Continue the recursive search
  1248. //
  1249. EnumeratePath(lpszNewPath, lplpSorted, lplpUnsorted);
  1250. // If the path didn't get added, we have to free the buffer.
  1251. //
  1252. if ( !bAdded )
  1253. {
  1254. FREE(lpszNewPath);
  1255. }
  1256. }
  1257. // Set the current directory to parent, to continue.
  1258. //
  1259. SetCurrentDirectory(_T(".."));
  1260. }
  1261. }
  1262. while ( FindNextFile(hFile, &FileFound) );
  1263. FindClose(hFile);
  1264. }
  1265. }
  1266. static BOOL AddPathsToList(LPTSTR lpszBegin, LPTSTR lpszRoot, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted, BOOL bRecursive)
  1267. {
  1268. BOOL bRet = TRUE,
  1269. bAddBackslash = FALSE,
  1270. bAdded;
  1271. LPTSTR lpszEnd,
  1272. lpszPath,
  1273. lpszExpanded,
  1274. lpszCat;
  1275. DWORD dwSize,
  1276. dwBackslash;
  1277. // If they don't pass in the root we don't do anything.
  1278. //
  1279. if ( lpszRoot )
  1280. {
  1281. if ( NULLCHR == *lpszRoot )
  1282. {
  1283. lpszRoot = NULL;
  1284. }
  1285. else if ( _T('\\') != *CharPrev(lpszRoot, lpszRoot + lstrlen(lpszRoot)) )
  1286. {
  1287. // The root path passed in doesn't have a backslash at
  1288. // the end so we set this so that we know we have to add
  1289. // one each time we add a path.
  1290. //
  1291. bAddBackslash = TRUE;
  1292. }
  1293. }
  1294. // Loop through all the semicolon separated paths in the
  1295. // buffer passed to use.
  1296. //
  1297. do
  1298. {
  1299. // Find the beginning of the path past all
  1300. // the semicolons.
  1301. //
  1302. while ( _T(';') == *lpszBegin )
  1303. {
  1304. lpszBegin++;
  1305. }
  1306. if ( *lpszBegin )
  1307. {
  1308. // Find the end of the path which is the next
  1309. // semicolon or the end of the string, whichever
  1310. // comes first.
  1311. //
  1312. lpszEnd = lpszBegin;
  1313. while ( *lpszEnd && ( _T(';') != *lpszEnd ) )
  1314. {
  1315. lpszEnd++;
  1316. }
  1317. // See if our new path has a backslash at the
  1318. // beginning of it.
  1319. //
  1320. dwBackslash = 0;
  1321. if ( _T('\\') == *lpszBegin )
  1322. {
  1323. // If it does and we don't want to add one,
  1324. // then advance the pointer past it.
  1325. //
  1326. if ( !bAddBackslash )
  1327. {
  1328. lpszBegin++;
  1329. }
  1330. }
  1331. else if ( bAddBackslash )
  1332. {
  1333. // Set this so we know to add the backslash and
  1334. // allocate the extra space for it.
  1335. //
  1336. dwBackslash = 1;
  1337. }
  1338. // Figure out the size we need for the path we are going
  1339. // to create. It is the length of the new string, plus
  1340. // the root if one was passed in, plus 1 for the backslash
  1341. // if we need to add one, plus 2 extra (one for the null
  1342. // terminator and one just to be safe).
  1343. //
  1344. dwSize = ((int) (lpszEnd - lpszBegin)) + dwBackslash + 2;
  1345. if ( lpszRoot )
  1346. {
  1347. dwSize += lstrlen(lpszRoot);
  1348. }
  1349. // Now allocate our path buffer.
  1350. //
  1351. if ( lpszPath = (LPTSTR) MALLOC(dwSize * sizeof(TCHAR)) )
  1352. {
  1353. // Reset this so if anything doesn't work we know to
  1354. // free our allocated memory.
  1355. //
  1356. bAdded = FALSE;
  1357. // Copy the path into our buffer.
  1358. //
  1359. lpszCat = lpszPath;
  1360. if ( lpszRoot )
  1361. {
  1362. lstrcpy(lpszCat, lpszRoot);
  1363. lpszCat += lstrlen(lpszCat);
  1364. }
  1365. if ( dwBackslash )
  1366. {
  1367. *lpszCat++ = _T('\\');
  1368. }
  1369. lstrcpyn(lpszCat, lpszBegin, (int) (lpszEnd - lpszBegin) + 1);
  1370. if ( lpszExpanded = AllocateExpand(lpszPath) )
  1371. {
  1372. // Add it to our lists.
  1373. //
  1374. bAdded = AddPathToList(lpszExpanded, lpszPath, lplpSorted, lplpUnsorted);
  1375. // If this is a recursive add, we try to enumerate all the
  1376. // sub directories and add them as well.
  1377. //
  1378. if ( ( bRecursive ) &&
  1379. ( DirectoryExists(lpszExpanded) ) &&
  1380. ( SetCurrentDirectory(lpszExpanded) ) )
  1381. {
  1382. EnumeratePath(lpszPath, lplpSorted, lplpUnsorted);
  1383. }
  1384. // If it wasn't added to the list, then free the memory.
  1385. //
  1386. if ( !bAdded )
  1387. {
  1388. FREE(lpszExpanded);
  1389. }
  1390. }
  1391. // If it wasn't added to the list, then free the memory.
  1392. //
  1393. if ( !bAdded )
  1394. {
  1395. FREE(lpszPath);
  1396. }
  1397. }
  1398. // Reset the beginning to the next string.
  1399. //
  1400. lpszBegin = lpszEnd;
  1401. }
  1402. }
  1403. while ( *lpszBegin );
  1404. return bRet;
  1405. }
  1406. VOID CleanupSourcesDir(LPTSTR lpszSourcesDir)
  1407. {
  1408. UINT i = 0;
  1409. LPTSTR lpEnd = NULL;
  1410. // If we have a valid sources
  1411. if ( lpszSourcesDir &&
  1412. *lpszSourcesDir &&
  1413. DirectoryExists(lpszSourcesDir)
  1414. )
  1415. {
  1416. lpEnd = lpszSourcesDir + lstrlen(lpszSourcesDir);
  1417. for (i = 0; ( i < AS(CleanupDirs) ); i++)
  1418. {
  1419. AddPath(lpszSourcesDir, CleanupDirs[i]);
  1420. DeletePath(lpszSourcesDir);
  1421. *lpEnd = NULLCHR;
  1422. }
  1423. }
  1424. }
  1425. // External functions
  1426. //
  1427. typedef BOOL ( *OpkCheckVersion ) ( DWORD dwMajorVersion, DWORD dwQFEVersion );
  1428. //
  1429. // Wrapper around the syssetup OPKCheckVersion() function.
  1430. //
  1431. BOOL OpklibCheckVersion( DWORD dwMajorVersion, DWORD dwQFEVersion )
  1432. {
  1433. BOOL bRet = TRUE; // Allow tool to run by default, in case we can't load syssetup or find the entry point.
  1434. HINSTANCE hInstSysSetup = NULL;
  1435. OpkCheckVersion pOpkCheckVersion = NULL;
  1436. hInstSysSetup = LoadLibrary( _T("syssetup.dll") );
  1437. if ( hInstSysSetup )
  1438. {
  1439. pOpkCheckVersion = (OpkCheckVersion) GetProcAddress( hInstSysSetup, "OpkCheckVersion" );
  1440. if ( pOpkCheckVersion )
  1441. {
  1442. bRet = pOpkCheckVersion( dwMajorVersion, dwQFEVersion );
  1443. }
  1444. FreeLibrary( hInstSysSetup );
  1445. }
  1446. return bRet;
  1447. }