Leaked source code of windows server 2003
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.

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