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.

1831 lines
52 KiB

  1. /*++
  2. File Description:
  3. This file contains all the functions required to add a registry entry
  4. to force execution of the system clone worker upon reboot.
  5. --*/
  6. #include <stdio.h>
  7. #include <nt.h>
  8. #include <ntrtl.h>
  9. #include <nturtl.h>
  10. #include <ntpoapi.h>
  11. #include <ntdddisk.h>
  12. #include <windows.h>
  13. #include <shlwapi.h>
  14. #include <stdlib.h>
  15. #include <lmcons.h>
  16. #include <lmerr.h>
  17. #include <lmjoin.h>
  18. #include <regstr.h>
  19. #include "sysprep.h"
  20. #include "msg.h"
  21. #include "resource.h"
  22. #include <tchar.h>
  23. #include <opklib.h>
  24. #include <ntverp.h>
  25. #include <spsyslib.h>
  26. #include <sysprep_.c>
  27. #include <winbom.h>
  28. // External functions
  29. //
  30. extern void uiDialogTopRight(HWND hwndDlg);
  31. extern HWND ghwndOemResetDlg;
  32. //
  33. // Does the user want a new SID?
  34. //
  35. BOOL NoSidGen = FALSE;
  36. BOOL SetupClPresent = TRUE;
  37. //
  38. // Does the user want confirmation?
  39. //
  40. BOOL QuietMode = FALSE;
  41. //
  42. // Do PnP re-enumeration?
  43. //
  44. BOOL PnP = FALSE;
  45. //
  46. // Do we shutdown when we're done?
  47. //
  48. BOOL NoReboot = FALSE;
  49. //
  50. // Instead of shutting down, do we reboot?
  51. //
  52. BOOL Reboot = FALSE;
  53. //
  54. // Clean out the critical devices database?
  55. //
  56. BOOL Clean = FALSE;
  57. //
  58. // Force the shutdown instead of trying to poweroff?
  59. //
  60. BOOL ForceShutdown = FALSE;
  61. //
  62. // Generating an Image for Factory Preinstallation.
  63. //
  64. BOOL Factory = FALSE;
  65. //
  66. // Reseal a machine after running FACTORY.EXE
  67. //
  68. BOOL Reseal = FALSE;
  69. // Per/Pro SKUs defaults to OOBE, Server SKUs always use MiniSetup.
  70. // Pro SKU can override OOBE with -mini to use MiniSetup also
  71. // via sysprep.inf
  72. BOOL bMiniSetup = FALSE;
  73. //
  74. // Just do an audit boot if this switch is passed in. ( '-audit' )
  75. //
  76. BOOL Audit = FALSE;
  77. //
  78. // Rollback
  79. //
  80. BOOL bActivated = FALSE;
  81. //
  82. // Build list of pnpids in [sysprepmassstorage] section in sysprep.inf
  83. //
  84. BOOL BuildMSD = FALSE;
  85. //
  86. // Internal Define(s):
  87. //
  88. #define SYSPREP_LOG _T("SYSPREP.LOG") // Sysprep log file
  89. #define SYSPREP_MUTEX _T("SYSPREP-APP-5c9fbbd0-ee0e-11d2-9a21-0000f81edacc") // GUID used to determine if sysprep is currently running
  90. #define SYSPREP_LOCK_SLEEP 100 // Number of miliseconds to sleep in LockApplication function
  91. #define SYSPREP_LOCK_SLEEP_COUNT 10 // Number of times to sleep during LockApplication function
  92. // Path to the sysprep directory.
  93. //
  94. TCHAR g_szSysprepDir[MAX_PATH] = NULLSTR;
  95. // Path to the SYSPREP.EXE.
  96. //
  97. TCHAR g_szSysprepPath[MAX_PATH] = NULLSTR;
  98. // Path to the Sysprep log file.
  99. //
  100. TCHAR g_szLogFile[MAX_PATH] = NULLSTR;
  101. // Path to the Winbom file.
  102. //
  103. TCHAR g_szWinBOMPath[MAX_PATH] = NULLSTR;
  104. // Public functions
  105. //
  106. BOOL FProcessSwitches();
  107. // Local functions
  108. static BOOL RenameWinbom();
  109. static INT CleanupPhantomDevices();
  110. static VOID CleanUpDevices();
  111. #if !defined(_WIN64)
  112. static BOOL SaveDiskSignature();
  113. #endif // !defined(_WIN64)
  114. //
  115. // UI stuff...
  116. //
  117. HINSTANCE ghInstance;
  118. UINT AppTitleStringId = IDS_APPTITLE;
  119. HANDLE ghWaitEvent = NULL, ghWaitThread = NULL;
  120. BOOL gbScreenSaver = FALSE;
  121. void StartWaitThread();
  122. void EndWaitThread();
  123. void DisableScreenSaver(BOOL *pScreenSaver);
  124. void EnableScreenSaver(BOOL *pScreenSaver);
  125. int
  126. MessageBoxFromMessageV(
  127. IN DWORD MessageId,
  128. IN DWORD CaptionStringId,
  129. IN UINT Style,
  130. IN va_list *Args
  131. )
  132. {
  133. TCHAR Caption[512];
  134. TCHAR Buffer[5000];
  135. if(!LoadString(ghInstance,CaptionStringId,Caption,sizeof(Caption)/sizeof(TCHAR))) {
  136. Caption[0] = 0;
  137. }
  138. if( !FormatMessage( FORMAT_MESSAGE_FROM_HMODULE,
  139. ghInstance,
  140. MessageId,
  141. 0,
  142. Buffer,
  143. sizeof(Buffer) / sizeof(TCHAR),
  144. Args ) ) {
  145. return GetLastError();
  146. } else {
  147. return(MessageBox(NULL,Buffer,Caption,Style));
  148. }
  149. }
  150. int
  151. MessageBoxFromMessage(
  152. IN DWORD MessageId,
  153. IN DWORD CaptionStringId,
  154. IN UINT Style,
  155. ...
  156. )
  157. {
  158. va_list arglist;
  159. int i = IDOK; // Default return value of "OK".
  160. // If we're in the middle of a Wait thread kill it
  161. //
  162. EndWaitThread();
  163. if ( !QuietMode )
  164. {
  165. va_start(arglist,Style);
  166. i = MessageBoxFromMessageV(MessageId,CaptionStringId,Style,&arglist);
  167. va_end(arglist);
  168. }
  169. return(i);
  170. }
  171. /*++
  172. ===============================================================================
  173. Routine Description:
  174. This routine will attempt to disjoin a user from a domain, if he
  175. is already in a domain
  176. Arguments:
  177. none
  178. Return Value:
  179. TRUE - Everything is okay.
  180. FALSE - Something bad happened.
  181. ===============================================================================
  182. --*/
  183. BOOL UnjoinNetworkDomain
  184. (
  185. void
  186. )
  187. {
  188. if (IsDomainMember())
  189. {
  190. // He's a member of some domain. Let's try and remove him
  191. // from the domain.
  192. if (NO_ERROR != NetUnjoinDomain( NULL, NULL, NULL, 0 ))
  193. {
  194. return FALSE;
  195. }
  196. }
  197. return TRUE;
  198. }
  199. /*++
  200. ===============================================================================
  201. Routine Description:
  202. This routine will setup the setup for operation on the "factory floor"
  203. The purpose here is to run a process which will facilitate the installation
  204. of updated drivers for new devices, and to boot quickly into full GUI mode
  205. for application pre-install/config, as well as to customize the system.
  206. Arguments:
  207. none
  208. Return Value:
  209. TRUE if no errors, FALSE otherise
  210. ===============================================================================
  211. --*/
  212. BOOL SetupForFactoryFloor
  213. (
  214. void
  215. )
  216. {
  217. TCHAR szFactory[MAX_PATH] = NULLSTR,
  218. szSysprep[MAX_PATH] = NULLSTR,
  219. szSystem[MAX_PATH] = NULLSTR;
  220. LPTSTR lpFilePart = NULLSTR;
  221. // Make sure we have the right privileges
  222. //
  223. pSetupEnablePrivilege(SE_RESTORE_NAME,TRUE);
  224. pSetupEnablePrivilege(SE_BACKUP_NAME,TRUE);
  225. // We need the path to sysprep.exe and factory.exe.
  226. //
  227. if ( !( GetModuleFileName(NULL, szSysprep, AS(szSysprep)) && szSysprep[0] &&
  228. GetFullPathName(szSysprep, AS(szFactory), szFactory, &lpFilePart) && szFactory[0] && lpFilePart ) )
  229. {
  230. return FALSE;
  231. }
  232. // Replace the sysprep.exe filename with factory.exe.
  233. //
  234. StringCchCopy ( lpFilePart, AS ( szFactory ) - ( lpFilePart - szFactory ), TEXT( "factory.exe" ) );
  235. // Make sure that sysprep.exe and factory.exe are on the system drive.
  236. //
  237. if ( ( ExpandEnvironmentStrings(TEXT("%SystemDrive%"), szSystem, AS(szSystem)) ) &&
  238. ( szSystem[0] ) &&
  239. ( szSystem[0] != szSysprep[0] ) )
  240. {
  241. // Well that sucks, we should try and copy the files over to the %SystemDrive%\sysprep folder.
  242. //
  243. AddPath(szSystem, TEXT("sysprep"));
  244. lpFilePart = szSystem + lstrlen(szSystem);
  245. CreateDirectory(szSystem, NULL);
  246. // First copy factory locally.
  247. //
  248. AddPath(szSystem, TEXT("factory.exe"));
  249. CopyFile(szFactory, szSystem, FALSE);
  250. StringCchCopy ( szFactory, AS ( szFactory ), szSystem );
  251. // Now try to copy sysprep.exe.
  252. //
  253. *lpFilePart = TEXT('\0');
  254. AddPath(szSystem, TEXT("sysprep.exe"));
  255. CopyFile(szSysprep, szSystem, FALSE);
  256. //lstrcpy(szSysprep, szSystem);
  257. }
  258. if (!SetFactoryStartup(szFactory))
  259. return FALSE;
  260. // Clear out any previous Factory.exe state settings
  261. RegDeleteKey(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Factory\\State");
  262. // Remove any setting before Factory
  263. //
  264. NukeMruList();
  265. // Rearm
  266. //
  267. if (!IsIA64() && !bActivated && (ERROR_SUCCESS != ReArm())) {
  268. // Display warning that grace period limit has reached and cannot
  269. // re-active grace period, and we continue thru.
  270. //
  271. MessageBoxFromMessage( MSG_REARM_ERROR,
  272. AppTitleStringId,
  273. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  274. }
  275. return TRUE;
  276. }
  277. INT_PTR WaitDlgProc
  278. (
  279. IN HWND hwndDlg,
  280. IN UINT msg,
  281. IN WPARAM wParam,
  282. IN LPARAM lParam
  283. )
  284. {
  285. switch (msg)
  286. {
  287. case WM_INITDIALOG:
  288. {
  289. // Centers the wait dialog in parent or screen
  290. //
  291. HWND hwndParent = GetParent(hwndDlg);
  292. CenterDialogEx(hwndParent, hwndDlg);
  293. // If no parent then make sure this is visible
  294. //
  295. if (hwndParent == NULL)
  296. SetForegroundWindow(hwndDlg);
  297. // Play the animation
  298. //
  299. Animate_Open(GetDlgItem(hwndDlg,IDC_ANIMATE),MAKEINTRESOURCE(IDA_CLOCK_AVI));
  300. Animate_Play(GetDlgItem(hwndDlg,IDC_ANIMATE),0,-1,-1);
  301. }
  302. break;
  303. }
  304. return (BOOL) FALSE;
  305. }
  306. DWORD WaitThread(LPVOID lpVoid)
  307. {
  308. HWND hwnd;
  309. if ( hwnd = CreateDialog(ghInstance, MAKEINTRESOURCE(IDD_WAIT), ghwndOemResetDlg, (DLGPROC) WaitDlgProc) )
  310. {
  311. MSG msg;
  312. HANDLE hEvent = (HANDLE) lpVoid;
  313. ShowWindow(hwnd, SW_SHOWNORMAL);
  314. while ( MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT) == (WAIT_OBJECT_0 + 1) )
  315. {
  316. while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
  317. {
  318. TranslateMessage(&msg);
  319. DispatchMessage(&msg);
  320. }
  321. }
  322. DestroyWindow(hwnd);
  323. }
  324. else
  325. GetLastError();
  326. return 0;
  327. }
  328. void StartWaitThread()
  329. {
  330. // Create a dialog to show progress is being made.
  331. //
  332. DWORD dwThread;
  333. // Disable the toplevel Oemreset dialog
  334. //
  335. if (ghwndOemResetDlg)
  336. EnableWindow(ghwndOemResetDlg, FALSE);
  337. if ( ghWaitEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("SYSPREP_EVENT_WAIT")))
  338. ghWaitThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) WaitThread, (LPVOID) ghWaitEvent, 0, &dwThread);
  339. }
  340. void EndWaitThread()
  341. {
  342. // Kill the Status Dialog.
  343. //
  344. if ( ghWaitEvent )
  345. SetEvent(ghWaitEvent);
  346. // Try and let the thread terminate nicely.
  347. //
  348. if ( ghWaitThread )
  349. WaitForSingleObject(ghWaitThread, 2000);
  350. // Clear the handles
  351. //
  352. ghWaitEvent = NULL;
  353. ghWaitThread = NULL;
  354. // Enable the toplevel OemReset dialog
  355. //
  356. if (ghwndOemResetDlg)
  357. EnableWindow(ghwndOemResetDlg, TRUE);
  358. }
  359. /*++
  360. ===============================================================================
  361. Routine Description:
  362. This is the error callback handler for SetDefaultOEMApps()
  363. ===============================================================================
  364. --*/
  365. void ReportSetDefaultOEMAppsError(LPCTSTR pszAppName, LPCTSTR pszIniVar)
  366. {
  367. MessageBoxFromMessage( MSG_SETDEFAULTS_NOTFOUND,
  368. AppTitleStringId,
  369. MB_OK | MB_ICONERROR | MB_TASKMODAL,
  370. pszAppName, pszIniVar);
  371. }
  372. /*++
  373. ===============================================================================
  374. Routine Description:
  375. This routine will perform the tasks necessary to reseal the machine,
  376. readying it to be shipped to the end user.
  377. Arguments:
  378. BOOL fIgnoreFactory - ignores if factory floor was run
  379. Return Value:
  380. TRUE if no errors, FALSE otherwise
  381. ===============================================================================
  382. --*/
  383. BOOL ResealMachine
  384. (
  385. void
  386. )
  387. {
  388. // Make sure privileges have been set
  389. //
  390. pSetupEnablePrivilege(SE_RESTORE_NAME,TRUE);
  391. pSetupEnablePrivilege(SE_BACKUP_NAME,TRUE);
  392. // Prepare the machine to be hardware independent.
  393. //
  394. if (!FPrepareMachine()) {
  395. MessageBoxFromMessage( MSG_REGISTRY_ERROR,
  396. AppTitleStringId,
  397. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  398. return FALSE;
  399. }
  400. //
  401. // Cleanup registry no matter what since factorymode=yes can set this
  402. // and winbom.ini can set this and sysprep -factory can set this, or else
  403. // PnP will hang on FactoryPreInstallInProgress being set.
  404. //
  405. CleanupRegistry();
  406. // Clean up the factory mess.
  407. //
  408. RegDelete(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", L"AutoAdminLogon");
  409. SHDeleteKey(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Factory");
  410. // Rearm
  411. //
  412. if (!IsIA64() && !bActivated && (ERROR_SUCCESS != ReArm())) {
  413. // Display warning that grace period limit has reached and cannot
  414. // re-active grace period, and we continue thru.
  415. //
  416. MessageBoxFromMessage( MSG_REARM_ERROR,
  417. AppTitleStringId,
  418. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  419. }
  420. #if defined(_WIN64)
  421. //
  422. // For EFI machines set the boot timeout to 5 seconds so that developers get a chance to see
  423. // the boot menu and have the option to boot to the EFI shell, CD, or other menu options,
  424. // for development purposes.
  425. //
  426. ChangeBootTimeout(5);
  427. #else
  428. ChangeBootTimeout(0); // reset the timeout to 0 secs
  429. #endif // !defined(_WIN64)
  430. //
  431. // First part of reseal.
  432. //
  433. AdjustFiles();
  434. //
  435. // Second part of reseal.
  436. //
  437. // This is common reseal code used by both Riprep and Sysprep.
  438. // These happen whether or not factory floor was run before.
  439. //
  440. if (!FCommonReseal()) {
  441. MessageBoxFromMessage( MSG_COMMON_ERROR,
  442. AppTitleStringId,
  443. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  444. return FALSE;
  445. }
  446. // ISSUE-2000/06/06-DONALDM
  447. // We need to handle the network configuration problem for factory more cleanly
  448. // We need to define what the network state is when factory first comes up, and
  449. // what the network state is for final customer delivery. Simply disjoining from
  450. // a domain during reseal is probably not enough...
  451. //
  452. // if( !UnjoinNetworkDomain())
  453. // {
  454. // // We failed to disjoin. Our only option is to
  455. // // inform the user and bail.
  456. // MessageBoxFromMessage( MSG_DOMAIN_INCOMPATIBILITY,
  457. // AppTitleStringId,
  458. // MB_OK | MB_ICONSTOP | MB_TASKMODAL );
  459. // return FALSE;
  460. // }
  461. //
  462. // Set default middleware applications.
  463. //
  464. if (!SetDefaultOEMApps(g_szWinBOMPath))
  465. {
  466. // SetDefaultApplications will do its own MessageBoxFromMessage
  467. // with more detailed information
  468. return FALSE;
  469. }
  470. // Call functions in published SYSPREP_.C file that we skiped when the
  471. // FACTORY option was selected
  472. // ISSUE-2000/06/05-DONALDM - We need to really decide about how to handle network settings for
  473. // the factory case. I think we don't need this call, becase we should have
  474. // already dealt with networking settings when FACTORY.EXE ran.
  475. //
  476. // RemoveNetworkSettings(NULL);
  477. return TRUE;
  478. }
  479. // Macro for processing command line options.
  480. // Setting bVar to 1 (not to 'TRUE') because we need it for mutually exclusive option checks below.
  481. //
  482. #define CHECK_PARAM(lpCmdLine, lpOption, bVar) if ( LSTRCMPI(lpCmdLine, lpOption) == 0 ) bVar = 1
  483. //
  484. // Parse command line parameters
  485. //
  486. static BOOL ParseCmdLine()
  487. {
  488. DWORD dwArgs;
  489. LPTSTR *lpArgs;
  490. BOOL bError = FALSE;
  491. BOOL bHelp = FALSE;
  492. if ( (dwArgs = GetCommandLineArgs(&lpArgs) ) && lpArgs )
  493. {
  494. LPTSTR lpArg;
  495. DWORD dwArg;
  496. // We want to skip over the first argument (it is the path
  497. // to the command being executed.
  498. //
  499. if ( dwArgs > 1 )
  500. {
  501. dwArg = 1;
  502. lpArg = *(lpArgs + dwArg);
  503. }
  504. else
  505. lpArg = NULL;
  506. // Loop through all the arguments.
  507. //
  508. while ( lpArg && !bError )
  509. {
  510. // Now we check to see if the first char is a dash or forward slash.
  511. //
  512. if ( *lpArg == _T('-') || *lpArg == _T('/'))
  513. {
  514. LPTSTR lpOption = CharNext(lpArg);
  515. // This is where you add command line options that start with a dash (-).
  516. //
  517. CHECK_PARAM( lpOption, _T("quiet"), QuietMode);
  518. CHECK_PARAM( lpOption, _T("nosidgen"), NoSidGen);
  519. CHECK_PARAM( lpOption, _T("pnp"), PnP);
  520. CHECK_PARAM( lpOption, _T("noreboot"), NoReboot);
  521. CHECK_PARAM( lpOption, _T("reboot"), Reboot);
  522. CHECK_PARAM( lpOption, _T("clean"), Clean);
  523. CHECK_PARAM( lpOption, _T("forceshutdown"), ForceShutdown);
  524. CHECK_PARAM( lpOption, _T("factory"), Factory);
  525. CHECK_PARAM( lpOption, _T("reseal"), Reseal);
  526. CHECK_PARAM( lpOption, _T("mini"), bMiniSetup);
  527. CHECK_PARAM( lpOption, _T("audit"), Audit);
  528. CHECK_PARAM( lpOption, _T("activated"), bActivated);
  529. CHECK_PARAM( lpOption, _T("bmsd"), BuildMSD);
  530. CHECK_PARAM( lpOption, _T("?"), bHelp);
  531. }
  532. else if ( *lpArg )
  533. {
  534. bError = TRUE;
  535. }
  536. // Setup the pointer to the next argument in the command line.
  537. //
  538. if ( ++dwArg < dwArgs )
  539. lpArg = *(lpArgs + dwArg);
  540. else
  541. lpArg = NULL;
  542. }
  543. // Make sure to free the two buffers allocated by the GetCommandLineArgs() function.
  544. //
  545. FREE(*lpArgs);
  546. FREE(lpArgs);
  547. }
  548. if (bError || bHelp)
  549. {
  550. // Set the quiet switch in this case so we display the error.
  551. // Note that we return FALSE and exit the application following this.
  552. //
  553. QuietMode = FALSE;
  554. MessageBoxFromMessage( MSG_USAGE,
  555. AppTitleStringId,
  556. MB_OK | MB_TASKMODAL );
  557. return FALSE;
  558. }
  559. //
  560. // Now look at the switches passed in and make sure that they are consistent.
  561. // If they are not, display an error message and quit, unless we're in quiet
  562. // mode where we do not display any error messages.
  563. //
  564. //
  565. // Check that the shutdown options are not conflicting with each other.
  566. if ( (NoReboot + Reboot + ForceShutdown) > 1 )
  567. {
  568. bError = TRUE;
  569. }
  570. // These top-level options are exclusive: -bmsd, -clean, -audit, -factory, -reseal.
  571. //
  572. else if ( (BuildMSD + Clean + Audit + Factory + Reseal) > 1 )
  573. {
  574. bError = TRUE;
  575. }
  576. // For Clean or BuildMSD none of the options except -quiet are valid.
  577. //
  578. else if ( Clean || BuildMSD )
  579. {
  580. if ( NoSidGen || PnP || NoReboot || Reboot || ForceShutdown || bMiniSetup || bActivated )
  581. {
  582. bError = TRUE;
  583. }
  584. }
  585. else if ( Audit )
  586. {
  587. if ( NoSidGen || PnP || bMiniSetup || bActivated )
  588. {
  589. bError = TRUE;
  590. }
  591. }
  592. else if ( Factory )
  593. {
  594. if ( PnP || bMiniSetup )
  595. {
  596. bError = TRUE;
  597. }
  598. }
  599. else if ( Reseal )
  600. {
  601. // If -pnp is specified -mini must have been specified unless we're running on server or ia64 (because
  602. // later we force bMiniSetup to be true on server and ia64.
  603. //
  604. if ( PnP && !bMiniSetup && !(IsServerSKU() || IsIA64()) )
  605. {
  606. bError = TRUE;
  607. }
  608. }
  609. // If there was some inconsistency in the switches specified put up
  610. // an error message.
  611. if ( bError )
  612. {
  613. // Reset the quiet switch in this case so we display the error.
  614. // Note that we return FALSE and exit the application following this.
  615. //
  616. QuietMode = FALSE;
  617. MessageBoxFromMessage( MSG_USAGE_COMBINATIONS,
  618. AppTitleStringId,
  619. MB_OK | MB_TASKMODAL | MB_ICONERROR);
  620. return FALSE;
  621. }
  622. // Force MiniSetup on IA64 and Servers.
  623. //
  624. if (IsIA64() || IsServerSKU())
  625. {
  626. bMiniSetup = TRUE;
  627. }
  628. else if ( IsPersonalSKU() )
  629. {
  630. if ( bMiniSetup )
  631. {
  632. // Can't specify mini-setup for personal sku
  633. //
  634. MessageBoxFromMessage( MSG_NO_MINISETUP,
  635. AppTitleStringId,
  636. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  637. bMiniSetup = FALSE;
  638. }
  639. if ( PnP )
  640. {
  641. // Can't specify -pnp because we're not running mini-setup on personal sku.
  642. //
  643. MessageBoxFromMessage( MSG_NO_PNP,
  644. AppTitleStringId,
  645. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  646. PnP = FALSE;
  647. }
  648. }
  649. //
  650. // If we're cleaning up the critical device database,
  651. // then we'll be wanting to set some additional flags.
  652. //
  653. if (Clean || BuildMSD)
  654. {
  655. QuietMode = TRUE;
  656. NoReboot = TRUE;
  657. }
  658. return !bError;
  659. }
  660. BOOL
  661. IsFactoryPresent(
  662. VOID
  663. )
  664. /*++
  665. ===============================================================================
  666. Routine Description:
  667. This routine tests to see if FACTORY.EXE is present on the machine.
  668. FACTORY.EXE will be required to run on reboot, so if it's not here,
  669. we need to know.
  670. Arguments:
  671. None.
  672. Return Value:
  673. TRUE - FACTORY.EXE is present.
  674. FALSE - FACTORY.EXE is not present.
  675. ===============================================================================
  676. --*/
  677. {
  678. WCHAR FileName[MAX_PATH];
  679. // Attempt to locate FACTORY.EXE
  680. //
  681. if (GetModuleFileName(NULL, FileName, MAX_PATH)) {
  682. if (PathRemoveFileSpec(FileName)) {
  683. OPKAddPathN(FileName, TEXT("FACTORY.EXE"), AS ( FileName ));
  684. if (FileExists(FileName))
  685. return TRUE;
  686. }
  687. }
  688. return FALSE;
  689. }
  690. void PowerOff(BOOL fForceShutdown)
  691. {
  692. SYSTEM_POWER_CAPABILITIES spc;
  693. ULONG uiFlags = EWX_POWEROFF;
  694. ZeroMemory(&spc, sizeof(spc));
  695. // Make sure we have privilege to shutdown
  696. //
  697. pSetupEnablePrivilege(SE_SHUTDOWN_NAME,TRUE);
  698. //
  699. // Use flag else query system for power capabilities
  700. //
  701. if (fForceShutdown)
  702. uiFlags = EWX_SHUTDOWN;
  703. else if (NT_SUCCESS(NtPowerInformation(SystemPowerCapabilities,
  704. NULL,
  705. 0,
  706. &spc,
  707. sizeof(spc))))
  708. {
  709. //
  710. // spc.SystemS1 == sleep 1
  711. // spc.SystemS2 == sleep 2
  712. // spc.SystemS3 == sleep 3
  713. // spc.SystemS4 == hibernate support
  714. // spc.SystemS5 == poweroff support
  715. //
  716. if (spc.SystemS5)
  717. {
  718. // ACPI capable
  719. uiFlags = EWX_POWEROFF;
  720. }
  721. else
  722. {
  723. // Non-ACPI
  724. uiFlags = EWX_SHUTDOWN;
  725. }
  726. }
  727. ExitWindowsEx(uiFlags|EWX_FORCE, SYSPREP_SHUTDOWN_FLAGS);
  728. }
  729. int APIENTRY WinMain( HINSTANCE hInstance,
  730. HINSTANCE hPrevInstance,
  731. LPSTR lpCmdLine,
  732. int nCmdShow )
  733. /*++
  734. ===============================================================================
  735. Routine Description:
  736. This routine is the main entry point for the program.
  737. We do a bit of error checking, then, if all goes well, we update the
  738. registry to enable execution of our second half.
  739. ===============================================================================
  740. --*/
  741. {
  742. DWORD dwVal;
  743. HKEY hKey;
  744. LPTSTR lpFilePart = NULL;
  745. INITCOMMONCONTROLSEX icex;
  746. LPTSTR lpAppName = NULL;
  747. ghInstance = hInstance;
  748. SetErrorMode(SEM_FAILCRITICALERRORS);
  749. memset(&icex, 0, sizeof(icex));
  750. icex.dwSize = sizeof(icex);
  751. icex.dwICC = ICC_PROGRESS_CLASS|ICC_ANIMATE_CLASS;
  752. InitCommonControlsEx(&icex);
  753. // We need the path to sysprep.exe and where it is located.
  754. //
  755. GetModuleFileName(NULL, g_szSysprepPath, AS(g_szSysprepPath));
  756. if ( GetFullPathName(g_szSysprepPath, AS(g_szSysprepDir), g_szSysprepDir, &lpFilePart) && g_szSysprepDir[0] && lpFilePart )
  757. {
  758. // Chop off the file name.
  759. //
  760. *lpFilePart = NULLCHR;
  761. }
  762. // If either of those file, we must quit (can't imagine that every happening).
  763. //
  764. if ( ( g_szSysprepPath[0] == NULLCHR ) || ( g_szSysprepDir[0] == NULLCHR ) )
  765. {
  766. // TODO: Log this failure.
  767. //
  768. // LogFile(WINBOM_LOGFILE, _T("\n"));
  769. return 0;
  770. }
  771. // Need the full path to the log file.
  772. //
  773. StringCchCopy ( g_szLogFile, AS ( g_szLogFile ), g_szSysprepDir);
  774. AddPath(g_szLogFile, SYSPREP_LOG);
  775. // Attempt to aquire a lock on the application
  776. //
  777. if ( !LockApplication(TRUE) )
  778. {
  779. // Let the user know that we are busy
  780. //
  781. MessageBoxFromMessage( MSG_ALREADY_RUNNING,
  782. AppTitleStringId,
  783. MB_OK | MB_ICONINFORMATION | MB_SYSTEMMODAL );
  784. return 0;
  785. }
  786. //
  787. // Check to see if we are allowed to run on this build of the OS
  788. //
  789. if ( !OpklibCheckVersion( VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE ) )
  790. {
  791. MessageBoxFromMessage( MSG_NOT_ALLOWED,
  792. AppTitleStringId,
  793. MB_OK | MB_ICONERROR | MB_SYSTEMMODAL );
  794. return 0;
  795. }
  796. // Ensure that the user has privilege/access to run this app.
  797. if(!pSetupIsUserAdmin()
  798. || !pSetupDoesUserHavePrivilege(SE_SHUTDOWN_NAME)
  799. || !pSetupDoesUserHavePrivilege(SE_BACKUP_NAME)
  800. || !pSetupDoesUserHavePrivilege(SE_RESTORE_NAME)
  801. || !pSetupDoesUserHavePrivilege(SE_SYSTEM_ENVIRONMENT_NAME))
  802. {
  803. MessageBoxFromMessage( MSG_NOT_AN_ADMINISTRATOR,
  804. AppTitleStringId,
  805. MB_OK | MB_ICONSTOP | MB_TASKMODAL );
  806. LockApplication(FALSE);
  807. return 0;
  808. }
  809. // Check the command line
  810. if( !ParseCmdLine() )
  811. {
  812. LockApplication(FALSE);
  813. return 0;
  814. }
  815. // Determines whether we can run SidGen. If not quit the application
  816. //
  817. // Make sure setupcl.exe is present in the system32 directory, if we need
  818. // to use it.
  819. if( !(SetupClPresent = IsSetupClPresent()) && !NoSidGen )
  820. {
  821. MessageBoxFromMessage( MSG_NO_SUPPORT,
  822. AppTitleStringId,
  823. MB_OK | MB_ICONSTOP | MB_TASKMODAL );
  824. LockApplication(FALSE);
  825. return 1;
  826. }
  827. // Put up a dialog to identify ourselves and make sure the user
  828. // really wants to do this.
  829. if ( IDCANCEL == MessageBoxFromMessage( MSG_IDENTIFY_SYSPREP,
  830. AppTitleStringId,
  831. MB_OKCANCEL| MB_ICONEXCLAMATION | MB_SYSTEMMODAL )
  832. )
  833. {
  834. LockApplication(FALSE);
  835. return 0;
  836. }
  837. // Allocate memory for window
  838. //
  839. if ( (lpAppName = AllocateString(NULL, IDS_APPNAME)) && *lpAppName )
  840. {
  841. ghwndOemResetDlg = FindWindow(NULL, lpAppName);
  842. // Free up the allocated memory
  843. //
  844. FREE(lpAppName);
  845. }
  846. DisableScreenSaver(&gbScreenSaver);
  847. //
  848. // Call RenameWinbom() once to initialize it. First time it is called it will check the factory
  849. // state registry key for the current winbom.ini that we are using. The second time it gets called it
  850. // will actually perform the rename if necessary. Make sure that the first time this gets called
  851. // for intialization it is before LocateWinBom() because LocateWinBom() populates the registry with
  852. // the winbom it finds.
  853. //
  854. RenameWinbom();
  855. // Need full path to winbom too. It is not an error if the file is
  856. // not found. (It is optional.)
  857. //
  858. LocateWinBom(g_szWinBOMPath, AS(g_szWinBOMPath), g_szSysprepDir, INI_VAL_WBOM_TYPE_FACTORY, LOCATE_NORMAL);
  859. // Process switches
  860. //
  861. if ( !FProcessSwitches() && !ghwndOemResetDlg)
  862. {
  863. ShowOemresetDialog(hInstance);
  864. }
  865. EnableScreenSaver(&gbScreenSaver);
  866. // Unlock application and free up memory
  867. //
  868. LockApplication(FALSE);
  869. return 0;
  870. }
  871. // Factory Preinstall now also prepares the machine
  872. //
  873. BOOL FDoFactoryPreinstall()
  874. {
  875. HKEY hKey;
  876. DWORD dwVal;
  877. if (!IsFactoryPresent()) {
  878. MessageBoxFromMessage( MSG_NO_FACTORYEXE,
  879. AppTitleStringId,
  880. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  881. return FALSE;
  882. }
  883. // Setup factory.exe for factory floor
  884. //
  885. if (!SetupForFactoryFloor())
  886. {
  887. MessageBoxFromMessage( MSG_SETUPFACTORYFLOOR_ERROR,
  888. AppTitleStringId,
  889. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  890. return FALSE;
  891. }
  892. // Prepare machine to be hardware independent for factory floor
  893. //
  894. if (!FPrepareMachine()) {
  895. MessageBoxFromMessage( MSG_REGISTRY_ERROR,
  896. AppTitleStringId,
  897. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  898. return FALSE;
  899. }
  900. // Set the boot timeout for boot on factory floor
  901. if (!ChangeBootTimeout( 1 ))
  902. return FALSE;
  903. return TRUE;
  904. }
  905. // Prepares the machine to be hardware independent
  906. //
  907. BOOL FPrepareMachine()
  908. {
  909. TCHAR szSysprepInf[MAX_PATH] = TEXT("");
  910. //
  911. // Make sure we've got the required privileges to update the registry.
  912. //
  913. pSetupEnablePrivilege(SE_RESTORE_NAME,TRUE);
  914. pSetupEnablePrivilege(SE_BACKUP_NAME,TRUE);
  915. // Build path to sysprep.inf from where sysprep.exe is located
  916. //
  917. if (GetModuleFileName(NULL, szSysprepInf, MAX_PATH))
  918. {
  919. PathRemoveFileSpec(szSysprepInf);
  920. OPKAddPathN(szSysprepInf, TEXT("sysprep.inf"), AS ( szSysprepInf ) );
  921. }
  922. // Disable System Restore
  923. //
  924. DisableSR();
  925. // Make sure we're not a member of a domain. If we are, then try and
  926. // force the unjoin.
  927. //
  928. if( !UnjoinNetworkDomain())
  929. {
  930. // We failed to disjoin. Our only option is to
  931. // inform the user and bail.
  932. MessageBoxFromMessage( MSG_DOMAIN_INCOMPATIBILITY,
  933. AppTitleStringId,
  934. MB_OK | MB_ICONSTOP | MB_TASKMODAL );
  935. return FALSE;
  936. }
  937. #if !defined(_WIN64)
  938. // Set the boot disk signature in the registry. The mount manager uses this
  939. // to avoid a PNP pop-up after imaging.
  940. //
  941. if ( !SaveDiskSignature() )
  942. {
  943. return FALSE;
  944. }
  945. #endif // !defined(_WIN64)
  946. // Determine if we should set the BigLba support in registry
  947. //
  948. if ( !SetBigLbaSupport(szSysprepInf) )
  949. {
  950. return FALSE;
  951. }
  952. // Determine if we should remove the tapi settings
  953. //
  954. if ( !RemoveTapiSettings(szSysprepInf) )
  955. {
  956. return FALSE;
  957. }
  958. // Set OEMDuplicatorString
  959. if (!SetOEMDuplicatorString(szSysprepInf))
  960. return FALSE;
  961. // If we want to regenerate the SID's on the next boot do it.
  962. //
  963. if ( NoSidGen )
  964. {
  965. // Remember that we didn't generate SIDs
  966. //
  967. RegSetDword(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_SIDGEN, 0);
  968. }
  969. else
  970. {
  971. if ( PrepForSidGen() )
  972. {
  973. // Write out registry value so that we know that we've regenerated SIDs.
  974. //
  975. RegSetDword(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_SIDGEN, 1);
  976. // Set this registry key, only UpdateSecurityKeys can remove this key
  977. //
  978. RegSetDword(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_SIDGENHISTORY, 1);
  979. }
  980. else
  981. {
  982. return FALSE;
  983. }
  984. }
  985. // If Mass Storage Devices were installed, clean up the ones not being used.
  986. // Note: We only want to CleanUpDevices() if we are resealing. This is the equivalent of
  987. // automatically running "sysprep -clean" on reseal if we know that we need to do it.
  988. //
  989. if ( RegCheck(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_MASS_STORAGE))
  990. {
  991. if ( Reseal )
  992. {
  993. // Clean the critical device database, since we might have put some
  994. // HDC and network drivers in there during factory floor from PopulateDeviceDatabase()
  995. CleanUpDevices();
  996. // Remove this key because we just ran CleanUpDevices().
  997. //
  998. RegDelete(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_MASS_STORAGE);
  999. }
  1000. }
  1001. else
  1002. {
  1003. BOOL fPopulated = FALSE;
  1004. // Set up Hardware independence for mass storage controllers.
  1005. //
  1006. BuildMassStorageSection(FALSE);
  1007. if (!PopulateDeviceDatabase(&fPopulated))
  1008. return FALSE;
  1009. // Write out signature value to know that we have built the mass-storage section.
  1010. //
  1011. if ( fPopulated && !RegSetDword(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_MASS_STORAGE, 1) )
  1012. return FALSE;
  1013. }
  1014. // Remove network settings/card last so any errors during Device Database won't loose
  1015. // networking.
  1016. //
  1017. if (!RemoveNetworkSettings(szSysprepInf))
  1018. return FALSE;
  1019. return TRUE;
  1020. }
  1021. // Reseal and Factory should behave the same according to the shutdown path.
  1022. //
  1023. void DoShutdownTypes()
  1024. {
  1025. pSetupEnablePrivilege(SE_SHUTDOWN_NAME,TRUE);
  1026. if (Reboot)
  1027. ExitWindowsEx(EWX_REBOOT|EWX_FORCE, SYSPREP_SHUTDOWN_FLAGS);
  1028. else if (NoReboot)
  1029. PostQuitMessage(0);
  1030. else
  1031. PowerOff(ForceShutdown); // Default
  1032. }
  1033. // Process action switches return TRUE if processed
  1034. //
  1035. BOOL FProcessSwitches()
  1036. {
  1037. // There are currently 4 basic operating modes for SYSPREP:
  1038. // 1) Factory floor mode. This mode is new for Whistler and will not completly
  1039. // clone the system, but will prep the system for OEM factory floor installation
  1040. // 2) Clean mode. In this mode, sysprep will clean up the critical device database
  1041. // 3) Reseal mode. This is the complement to factory mode which will "complete" the
  1042. // cloning process after factory floor mode has been used.
  1043. // 4) "Audit" mode. The system just executes an audit boot. Used to restart the system
  1044. // at the end of factory.exe processing.
  1045. // These are just flags for reseal
  1046. //
  1047. if (Reseal)
  1048. {
  1049. StartWaitThread();
  1050. // Ensure that we're running on the right OS.
  1051. //
  1052. if( !CheckOSVersion() )
  1053. {
  1054. MessageBoxFromMessage( MSG_OS_INCOMPATIBILITY,
  1055. AppTitleStringId,
  1056. MB_OK | MB_ICONSTOP | MB_TASKMODAL );
  1057. return TRUE;
  1058. }
  1059. // Reseal the machine
  1060. //
  1061. if (!ResealMachine()) {
  1062. MessageBoxFromMessage( MSG_RESEAL_ERROR,
  1063. AppTitleStringId,
  1064. MB_OK | MB_ICONSTOP | MB_TASKMODAL );
  1065. return TRUE;
  1066. }
  1067. // Rename the current winbom so we don't use it again.
  1068. //
  1069. RenameWinbom();
  1070. // Shutdown or reboot?
  1071. DoShutdownTypes();
  1072. EndWaitThread();
  1073. return TRUE;
  1074. }
  1075. else if (Factory)
  1076. {
  1077. StartWaitThread();
  1078. // Set Factory to start on next boot and prepare for imaging
  1079. //
  1080. if (!FDoFactoryPreinstall())
  1081. return TRUE;
  1082. // Rename the current winbom so we don't use it again.
  1083. //
  1084. RenameWinbom();
  1085. // Shutdown or reboot?
  1086. DoShutdownTypes();
  1087. EndWaitThread();
  1088. return TRUE;
  1089. }
  1090. else if (Clean)
  1091. {
  1092. CleanUpDevices();
  1093. // Remove this key because we just ran CleanUpDevices().
  1094. //
  1095. RegDelete(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_MASS_STORAGE);
  1096. return TRUE;
  1097. }
  1098. else if (Audit)
  1099. {
  1100. // Prepare for pseudo factory but get back to audit.
  1101. //
  1102. if ( RegCheck(HKLM, REGSTR_PATH_SYSTEM_SETUP, REGSTR_VALUE_AUDIT) )
  1103. {
  1104. TCHAR szFactoryPath[MAX_PATH] = NULLSTR;
  1105. // Going into Audit mode requires Factory.exe and winbom.ini
  1106. // to exist.
  1107. //
  1108. if (FGetFactoryPath(szFactoryPath)) {
  1109. SetFactoryStartup(szFactoryPath);
  1110. DoShutdownTypes();
  1111. }
  1112. else {
  1113. LogFile(g_szLogFile, MSG_NO_FACTORYEXE);
  1114. MessageBoxFromMessage( MSG_NO_FACTORYEXE,
  1115. IDS_APPTITLE,
  1116. MB_OK | MB_ICONERROR | MB_TASKMODAL );
  1117. }
  1118. }
  1119. else
  1120. {
  1121. LogFile(g_szLogFile, IDS_ERR_FACTORYMODE);
  1122. }
  1123. return TRUE;
  1124. }
  1125. else if (BuildMSD)
  1126. {
  1127. StartWaitThread();
  1128. BuildMassStorageSection(TRUE /* Force build */);
  1129. EndWaitThread();
  1130. return TRUE;
  1131. }
  1132. // Return False to show the UI
  1133. //
  1134. Reseal = Factory = Clean = Audit = 0;
  1135. return FALSE;
  1136. }
  1137. BOOL LockApplication(BOOL bState)
  1138. {
  1139. static HANDLE hMutex;
  1140. BOOL bReturn = FALSE,
  1141. bBail = FALSE;
  1142. DWORD dwSleepCount = 0;
  1143. // We want to lock the application
  1144. //
  1145. if ( bState )
  1146. {
  1147. // Check to see if we can create the mutex and that the mutex did not
  1148. // already exist
  1149. //
  1150. while ( !bReturn && (dwSleepCount < SYSPREP_LOCK_SLEEP_COUNT) && !bBail)
  1151. {
  1152. SetLastError(ERROR_SUCCESS);
  1153. if ( hMutex = CreateMutex(NULL, FALSE, SYSPREP_MUTEX) )
  1154. {
  1155. if ( GetLastError() == ERROR_ALREADY_EXISTS )
  1156. {
  1157. CloseHandle(hMutex);
  1158. hMutex = NULL;
  1159. dwSleepCount++;
  1160. Sleep(SYSPREP_LOCK_SLEEP);
  1161. }
  1162. else
  1163. {
  1164. // Application successfully created lock
  1165. //
  1166. bReturn = TRUE;
  1167. }
  1168. }
  1169. else
  1170. {
  1171. bBail = TRUE;
  1172. }
  1173. }
  1174. }
  1175. else if ( hMutex )
  1176. {
  1177. CloseHandle(hMutex);
  1178. hMutex = NULL;
  1179. bReturn = TRUE;
  1180. }
  1181. // Return whether or not the lock/unlock was successful
  1182. //
  1183. return bReturn;
  1184. }
  1185. //
  1186. // Shutdown or Reboot the machine
  1187. //
  1188. VOID ShutdownOrReboot(UINT uFlags, DWORD dwReserved)
  1189. {
  1190. // Enable privileges for shutdown
  1191. //
  1192. EnablePrivilege(SE_SHUTDOWN_NAME, TRUE);
  1193. // Shutdown or Reboot the machine
  1194. //
  1195. ExitWindowsEx(uFlags|EWX_FORCE, dwReserved);
  1196. }
  1197. // Remember the Screen Saver state and to disable it during Sysprep
  1198. //
  1199. void DisableScreenSaver(BOOL *pScreenSaver)
  1200. {
  1201. SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, (PVOID)pScreenSaver, 0);
  1202. if (*pScreenSaver == TRUE)
  1203. {
  1204. SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, SPIF_SENDWININICHANGE);
  1205. }
  1206. }
  1207. // Remember the Screen Saver state and to re-enable it after Sysprep
  1208. //
  1209. void EnableScreenSaver(BOOL *pScreenSaver)
  1210. {
  1211. if (*pScreenSaver == TRUE)
  1212. {
  1213. SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE);
  1214. }
  1215. }
  1216. // Rename the old winbom when going into factory or reseal so that we don't use it again by mistake.
  1217. static BOOL RenameWinbom()
  1218. {
  1219. BOOL bRet = TRUE;
  1220. static LPTSTR lpszWinbom = NULL;
  1221. static BOOL bInitialized = FALSE;
  1222. if ( !bInitialized )
  1223. {
  1224. // Only bother to try if we are in audit mode and there
  1225. // is a winbom in use.
  1226. //
  1227. if ( RegCheck(HKLM, _T("SYSTEM\\Setup"), _T("AuditInProgress")) )
  1228. {
  1229. lpszWinbom = RegGetExpand(HKLM, _T("SOFTWARE\\Microsoft\\Factory\\State"), _T("Winbom"));
  1230. }
  1231. bInitialized = TRUE;
  1232. }
  1233. else if ( lpszWinbom )
  1234. {
  1235. // Make sure the winbom in the registry exists.
  1236. //
  1237. if ( *lpszWinbom && FileExists(lpszWinbom) )
  1238. {
  1239. LPTSTR lpszExtension;
  1240. TCHAR szBackup[MAX_PATH];
  1241. DWORD dwExtra;
  1242. // At this point, if we don't rename the file then it
  1243. // means there was an error.
  1244. //
  1245. bRet = FALSE;
  1246. // Copy the full path to the winbom into our own buffer.
  1247. //
  1248. lstrcpyn(szBackup, lpszWinbom, AS(szBackup));
  1249. // Get a pointer to the extension of the file name.
  1250. //
  1251. if ( lpszExtension = StrRChr(szBackup, NULL, _T('.')) )
  1252. {
  1253. // Set the extension pointer to after the '.' character.
  1254. //
  1255. lpszExtension = CharNext(lpszExtension);
  1256. // See how many characters are in the current extension.
  1257. //
  1258. if ( (dwExtra = lstrlen(lpszExtension)) < 3 )
  1259. {
  1260. // There is less then a 3 character extension, so
  1261. // we need some extra space for our 3 digit one.
  1262. //
  1263. dwExtra = 3 - dwExtra;
  1264. }
  1265. else
  1266. {
  1267. // If there are already at least 3 characters in
  1268. // the exension, then no more space is required.
  1269. //
  1270. dwExtra = 0;
  1271. }
  1272. }
  1273. else
  1274. {
  1275. // No extension, so we need 4 characters extra for
  1276. // the '.' and the 3 digit extension.
  1277. //
  1278. dwExtra = 4;
  1279. }
  1280. // Make sure there is enough room for our extension to be
  1281. // added to our buffer.
  1282. //
  1283. if ( ( lstrlen(lpszWinbom) < AS(szBackup) ) &&
  1284. ( lstrlen(szBackup) + dwExtra < AS(szBackup) ) )
  1285. {
  1286. DWORD dwNum = 0;
  1287. // If there is no extension, add the dot.
  1288. //
  1289. if ( NULL == lpszExtension )
  1290. {
  1291. // Add our '.' to the end of the string, and set the
  1292. // extension pointer past it.
  1293. //
  1294. lpszExtension = szBackup + lstrlen(szBackup);
  1295. *lpszExtension = _T('.');
  1296. lpszExtension = CharNext(lpszExtension);
  1297. }
  1298. // Try to find out new file name. Keep increasing our
  1299. // number from 000 until we find a name that doesn't exist.
  1300. //
  1301. do
  1302. {
  1303. StringCchPrintf ( lpszExtension, AS ( szBackup ) - ( szBackup - lpszExtension), _T("%3.3d"), dwNum);
  1304. }
  1305. while ( ( FileExists(szBackup) ) &&
  1306. ( ++dwNum < 1000 ) );
  1307. // If we found a name that doesn't exist, rename
  1308. // the winbom.
  1309. //
  1310. if ( dwNum < 1000 )
  1311. {
  1312. // If the move works, then return success.
  1313. //
  1314. bRet = MoveFile(lpszWinbom, szBackup);
  1315. }
  1316. }
  1317. }
  1318. // Free the buffer allocated.
  1319. //
  1320. FREE(lpszWinbom);
  1321. }
  1322. // Return TRUE if we didn't need to rename the winbom,
  1323. // or we were able to do so successfully.
  1324. //
  1325. return bRet;
  1326. }
  1327. #if !defined(_WIN64)
  1328. static BOOL SaveDiskSignature()
  1329. {
  1330. BOOL bRet = FALSE;
  1331. WCHAR szBuf[MAX_PATH] = NULLSTR;
  1332. HANDLE hDisk;
  1333. DWORD dwBytesReturned = 0;
  1334. TCHAR cDriveLetter;
  1335. szBuf[0] = NULLCHR;
  1336. if ( GetWindowsDirectory(szBuf, AS(szBuf)) && szBuf[0] )
  1337. {
  1338. // We only need the drive letter from this.
  1339. cDriveLetter = szBuf[0];
  1340. StringCchPrintf ( szBuf, AS ( szBuf ), _T("\\\\.\\%c:"), cDriveLetter);
  1341. }
  1342. else
  1343. {
  1344. return FALSE;
  1345. }
  1346. // Attempt to open the file
  1347. //
  1348. hDisk = CreateFile( szBuf,
  1349. GENERIC_READ,
  1350. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1351. NULL,
  1352. OPEN_EXISTING,
  1353. FILE_ATTRIBUTE_NORMAL,
  1354. NULL
  1355. );
  1356. // Check to see if we were able to open the disk
  1357. //
  1358. if ( INVALID_HANDLE_VALUE == hDisk )
  1359. {
  1360. bRet = FALSE;
  1361. DbgPrint("SaveDiskSignature(): Unable to open file on %ws. Error (%lx)\n", szBuf, GetLastError());
  1362. }
  1363. else
  1364. {
  1365. PDRIVE_LAYOUT_INFORMATION_EX pLayoutInfoEx = NULL;
  1366. ULONG lengthLayoutEx = 0;
  1367. DbgPrint("SaveDiskSignature(): Successfully opened file on %ws\n", szBuf);
  1368. lengthLayoutEx = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + (sizeof(PARTITION_INFORMATION_EX) * 128);
  1369. pLayoutInfoEx = (PDRIVE_LAYOUT_INFORMATION_EX) MALLOC( lengthLayoutEx );
  1370. if ( pLayoutInfoEx )
  1371. {
  1372. // Attempt to get the drive layout
  1373. //
  1374. bRet = DeviceIoControl( hDisk,
  1375. IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
  1376. NULL,
  1377. 0,
  1378. pLayoutInfoEx,
  1379. lengthLayoutEx,
  1380. &dwBytesReturned,
  1381. NULL
  1382. );
  1383. // Check the status of the drive layout
  1384. //
  1385. if ( bRet )
  1386. { // Only do this on MBR disks
  1387. //
  1388. if ( PARTITION_STYLE_MBR == pLayoutInfoEx->PartitionStyle )
  1389. {
  1390. // Only set this value on MBR disks.
  1391. //
  1392. if ( !RegSetDword(HKEY_LOCAL_MACHINE, REGSTR_PATH_SYSTEM_SETUP, REGSTR_VAL_DISKSIG, pLayoutInfoEx->Mbr.Signature) )
  1393. {
  1394. DbgPrint("SaveDiskSignature(): Cannot write disk signature to registry\n.");
  1395. bRet = FALSE;
  1396. }
  1397. }
  1398. else
  1399. { // bRet = TRUE at this point.
  1400. DbgPrint("SaveDiskSignature(): Not supported on GPT disks.\n");
  1401. }
  1402. }
  1403. else
  1404. {
  1405. DbgPrint("SaveDiskSignature(): Unable to open IOCTL on %ws. Error (%lx)\n", szBuf, GetLastError());
  1406. }
  1407. // Clean up. Macro checks for NULL;
  1408. //
  1409. FREE( pLayoutInfoEx );
  1410. }
  1411. else
  1412. {
  1413. bRet = FALSE;
  1414. }
  1415. CloseHandle( hDisk );
  1416. }
  1417. return bRet;
  1418. }
  1419. #endif // !defined(_WIN64)
  1420. //
  1421. // Helper function for CleanupPhantomDevices. Decides whether it is ok to remove
  1422. // certain PNP devices.
  1423. //
  1424. BOOL
  1425. CanDeviceBeRemoved(
  1426. HDEVINFO DeviceInfoSet,
  1427. PSP_DEVINFO_DATA DeviceInfoData,
  1428. PTSTR DeviceInstanceId
  1429. )
  1430. {
  1431. BOOL bCanBeRemoved = TRUE;
  1432. if (_tcsicmp(DeviceInstanceId, TEXT("HTREE\\ROOT\\0")) == 0) {
  1433. //
  1434. // The device has the DeviceInstanceId of HTREE\ROOT\0 then it is the
  1435. // root of the device tree and can NOT be removed!
  1436. //
  1437. bCanBeRemoved = FALSE;
  1438. } else if (_tcsnicmp(DeviceInstanceId, TEXT("SW\\"), lstrlen(TEXT("SW\\"))) == 0) {
  1439. //
  1440. // If the DeviceInstanceId starts with SW\\ then it is a swenum (software
  1441. // enumerated) device and should not be removed.
  1442. //
  1443. bCanBeRemoved = FALSE;
  1444. } else if (IsEqualGUID(&(DeviceInfoData->ClassGuid), &GUID_DEVCLASS_LEGACYDRIVER)) {
  1445. //
  1446. // If the device is of class GUID_DEVCLASS_LEGACYDRIVER then do not
  1447. // uninstall it.
  1448. //
  1449. bCanBeRemoved = FALSE;
  1450. }
  1451. return bCanBeRemoved;
  1452. }
  1453. //
  1454. // Cleans up phantom PNP devices. This is useful for cleaning up devices that existed
  1455. // on the machine that was imaged but do not exist on the target machine.
  1456. //
  1457. static INT
  1458. CleanupPhantomDevices(
  1459. VOID
  1460. )
  1461. {
  1462. HDEVINFO DeviceInfoSet;
  1463. HDEVINFO InterfaceDeviceInfoSet;
  1464. SP_DEVINFO_DATA DeviceInfoData;
  1465. SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
  1466. INT DevicesRemoved = 0;
  1467. INT MemberIndex, InterfaceMemberIndex;
  1468. DWORD Status, Problem;
  1469. CONFIGRET cr;
  1470. TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
  1471. //
  1472. // Get a list of all the devices on this machine, including present (live)
  1473. // and not present (phantom) devices.
  1474. //
  1475. DeviceInfoSet = SetupDiGetClassDevs(NULL,
  1476. NULL,
  1477. NULL,
  1478. DIGCF_ALLCLASSES
  1479. );
  1480. if (DeviceInfoSet != INVALID_HANDLE_VALUE) {
  1481. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  1482. MemberIndex = 0;
  1483. //
  1484. // Enumerate through the list of devices.
  1485. //
  1486. while (SetupDiEnumDeviceInfo(DeviceInfoSet,
  1487. MemberIndex++,
  1488. &DeviceInfoData
  1489. )) {
  1490. //
  1491. // Check if this device is a Phantom
  1492. //
  1493. cr = CM_Get_DevNode_Status(&Status,
  1494. &Problem,
  1495. DeviceInfoData.DevInst,
  1496. 0
  1497. );
  1498. if ((cr == CR_NO_SUCH_DEVINST) ||
  1499. (cr == CR_NO_SUCH_VALUE)) {
  1500. //
  1501. // This is a phantom. Now get the DeviceInstanceId so we
  1502. // can display/log this as output.
  1503. //
  1504. if (SetupDiGetDeviceInstanceId(DeviceInfoSet,
  1505. &DeviceInfoData,
  1506. DeviceInstanceId,
  1507. sizeof(DeviceInstanceId)/sizeof(TCHAR),
  1508. NULL)) {
  1509. if (CanDeviceBeRemoved(DeviceInfoSet,
  1510. &DeviceInfoData,
  1511. DeviceInstanceId)) {
  1512. #ifdef DEBUG_LOGLOG
  1513. LOG_Write(L"CLEANUP: %s will be removed.\n", DeviceInstanceId);
  1514. #endif
  1515. //
  1516. // Call DIF_REMOVE to remove the device's hardware
  1517. // and software registry keys.
  1518. //
  1519. if (SetupDiCallClassInstaller(DIF_REMOVE,
  1520. DeviceInfoSet,
  1521. &DeviceInfoData
  1522. )) {
  1523. DevicesRemoved++;
  1524. } else {
  1525. #ifdef DEBUG_LOGLOG
  1526. LOG_Write(L"CLEANUP: Error 0x%X removing phantom\n", GetLastError());
  1527. #endif
  1528. }
  1529. }
  1530. }
  1531. }
  1532. }
  1533. SetupDiDestroyDeviceInfoList(DeviceInfoSet);
  1534. }
  1535. return DevicesRemoved;
  1536. }
  1537. // Cleans unused services and phantom PNP devices.
  1538. //
  1539. static VOID CleanUpDevices()
  1540. {
  1541. // Cleanup the services that we installed in the [SysprepMassStorage] section.
  1542. //
  1543. CleanDeviceDatabase();
  1544. // Cleanup phantom devices.
  1545. //
  1546. CleanupPhantomDevices();
  1547. }