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.

2453 lines
68 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. BOOTCFG.CXX
  5. Abstract:
  6. Contains functions used for managing the system control sets in the
  7. system portion of the registry.
  8. ScCheckLastKnownGood
  9. ScRevertToLastKnownGood
  10. NotifyBootConfigStatus
  11. ScGetTopKeys
  12. ScGetCtrlSetIds
  13. ScDeleteRegTree
  14. ScBuildCtrlSetName
  15. ScGetCtrlSetHandle
  16. ScDeleteTree
  17. ScCopyKeyRecursive
  18. ScCopyKeyValues
  19. ScDeleteRegServiceEntry
  20. ScGatherOrphanIds
  21. ScDeleteCtrlSetOrphans
  22. ScMatchInArray
  23. ScStartCtrlSetCleanupThread
  24. ScCleanupThread
  25. ScRunAcceptBootPgm
  26. ScAcceptTheBoot
  27. Author:
  28. Dan Lafferty (danl) 19-Apr-1992
  29. Environment:
  30. User Mode - Win32
  31. Notes:
  32. Revision History:
  33. 24-Aug-1998 Elliot Shmukler (t-ellios)
  34. Most of the LKG related work has now been moved into the Kernel.
  35. The tree copies & clone deletions formerly performed by functions
  36. in this file has now been replaced by calls to NtInitializeRegistry.
  37. 28-Jun-1995 AnirudhS
  38. SetupInProgress: This function is now called from more than one place.
  39. Cache the return value so we only examine the registry once.
  40. 04-Feb-1994 Danl
  41. RevertToLastKnownGood: If the boot has been accepted, then we won't
  42. allow a revert.
  43. 15-Jun-1993 Danl
  44. Ignore LastKnownGood adjustments if setup is still running.
  45. Use the SystemSetupInProgress value in the registry to determine
  46. if is running.
  47. 01-Apr-1993 Danl
  48. Add ability to take ownership if we cannot open one of the keys due
  49. to an access denied error.
  50. 08-Feb-1993 Danl
  51. Changed the clearing of the LKG_ENV_VAR so that it is done whenever
  52. we are booting LKG. Reguardless of whether or not it is the last
  53. boot. Prior to this, it was only cleared when a revert occured, and
  54. not on the first boot.
  55. 04-Feb-1993 Danl
  56. Use NtUnloadKey to delete the clone tree. The clone tree is now
  57. in a separate hive. So this is allowed.
  58. 18-Jan-1993 Danl
  59. Make use of the LastKnownGood Environment Variable. Now we do
  60. not alter the default control set when we need to revert. We
  61. just set the Environment Variable to True, and reboot. Phase2
  62. and ScCheckLastKnownGood do the right thing.
  63. 19-Apr-1992 danl
  64. Created
  65. --*/
  66. //
  67. // INCLUDES
  68. //
  69. #include "precomp.hxx"
  70. #include <stdlib.h> // ultoa
  71. #include "scsec.h" // ScAccessValidate()
  72. #include "bootcfg.h" // ScRegDeleteTree()
  73. #include "scconfig.h" // ScOpenServicesKey()
  74. #include <svcslib.h> // SetupInProgress()
  75. #include <ntsetup.h> // REGSTR_VALUE_OOBEINPROGRESS
  76. #include <bootstatus.h>
  77. //
  78. // DEFINES
  79. //
  80. #define SYSTEM_KEY L"system"
  81. #define SELECT_KEY L"select"
  82. #define SERVICES_KEY L"System\\CurrentControlSet\\Services"
  83. #define ACCEPT_BOOT_KEY L"System\\CurrentControlSet\\Control\\BootVerificationProgram"
  84. #define SETUP_PROG_KEY L"Setup"
  85. #define CURRENT_VALUE_NAME L"Current"
  86. #define DEFAULT_VALUE_NAME L"Default"
  87. #define LKG_VALUE_NAME L"LastKnownGood"
  88. #define FAILED_VALUE_NAME L"Failed"
  89. #define IMAGE_PATH_NAME L"ImagePath"
  90. #define SETUP_PROG_VALUE_NAME L"SystemSetupInProgress"
  91. #define CTRL_SET_NAME_TEMPLATE L"ControlSet000"
  92. #define CTRL_SET_NAME_CHAR_COUNT 13
  93. #define CTRL_SET_NAME_NUM_OFFSET 10
  94. #define CTRL_SET_NAME_BYTES ((CTRL_SET_CHAR_COUNT+1) * sizeof(WCHAR))
  95. #define CLONE_SECURITY_INFORMATION (OWNER_SECURITY_INFORMATION | \
  96. GROUP_SECURITY_INFORMATION | \
  97. DACL_SECURITY_INFORMATION | \
  98. SACL_SECURITY_INFORMATION)
  99. //
  100. // STANDARD access is obtained for the system and select keys.
  101. // We read and write to these keys.
  102. //
  103. #define SC_STANDARD_KEY_ACCESS KEY_READ | \
  104. READ_CONTROL | \
  105. WRITE_OWNER | \
  106. KEY_WRITE
  107. //
  108. // CLONE access is obtained for the top level clone key
  109. // We must be able to copy and delete clone trees.
  110. //
  111. #define SC_CLONE_KEY_ACCESS KEY_READ | \
  112. READ_CONTROL | \
  113. WRITE_OWNER | \
  114. DELETE | \
  115. ACCESS_SYSTEM_SECURITY
  116. //
  117. // CONTROL_SET access is obtained for the top level control sets.
  118. // We must be able to copy and delete control sets.
  119. // NOTE: SE_SECURITY_PRIVILEGE is required to get ACCESS_SYSTEM_SECURITY.
  120. //
  121. #define SC_CONTROL_SET_KEY_ACCESS KEY_READ | \
  122. KEY_WRITE | \
  123. DELETE | \
  124. READ_CONTROL | \
  125. WRITE_OWNER | \
  126. ACCESS_SYSTEM_SECURITY
  127. //
  128. // COPY access is obtained for each subkey in a control set as it is being
  129. // copied.
  130. // NOTE: SE_SECURITY_PRIVILEGE is required to get ACCESS_SYSTEM_SECURITY.
  131. //
  132. #define SC_COPY_KEY_ACCESS KEY_READ | \
  133. READ_CONTROL | \
  134. ACCESS_SYSTEM_SECURITY
  135. //
  136. // DELETE access is obtained for each subkey in a control set that is being
  137. // deleted.
  138. //
  139. #define SC_DELETE_KEY_ACCESS DELETE | \
  140. KEY_READ
  141. //
  142. // CREATE access is the access used for all keys created by this
  143. // process.
  144. //
  145. #define SC_CREATE_KEY_ACCESS KEY_WRITE | \
  146. WRITE_OWNER | \
  147. WRITE_DAC | \
  148. ACCESS_SYSTEM_SECURITY
  149. //
  150. // Control Set IDs are stored in an array of DWORDs. The array has the
  151. // following offsets for each ID:
  152. //
  153. #define CURRENT_ID 0
  154. #define DEFAULT_ID 1
  155. #define LKG_ID 2
  156. #define FAILED_ID 3
  157. #define NUM_IDS 4
  158. //
  159. // Macros
  160. //
  161. #define SET_LKG_ENV_VAR(pString) \
  162. { \
  163. UNICODE_STRING Name,Value; \
  164. \
  165. RtlInitUnicodeString(&Name, L"LastKnownGood"); \
  166. RtlInitUnicodeString(&Value,pString); \
  167. \
  168. status = RtlNtStatusToDosError(NtSetSystemEnvironmentValue(&Name,&Value)); \
  169. }
  170. //
  171. // GLOBALS
  172. //
  173. //
  174. // This flag is set when ScCheckLastKnownGood is called. It is later
  175. // checked when either ScRevertToLastKnownGood or NotifyBootConfigStatus
  176. // is called. TRUE indicates that we know we are booting LastKnownGood.
  177. //
  178. DWORD ScGlobalLastKnownGood;
  179. BOOL ScGlobalBootAccepted = FALSE;
  180. CRITICAL_SECTION ScBootConfigCriticalSection;
  181. LPDWORD ScGlobalOrphanIds = NULL;
  182. //
  183. // LOCAL FUNCTION PROTOTYPES
  184. //
  185. DWORD
  186. ScGetTopKeys(
  187. PHKEY SystemKey,
  188. PHKEY SelectKey
  189. );
  190. DWORD
  191. ScGetCtrlSetIds(
  192. HKEY SelectKey,
  193. LPDWORD IdArray
  194. );
  195. BOOL
  196. ScBuildCtrlSetName(
  197. LPWSTR ControlSetName,
  198. DWORD ControlId
  199. );
  200. HKEY
  201. ScGetCtrlSetHandle(
  202. HKEY SystemKey,
  203. DWORD ControlId,
  204. LPWSTR ControlSetName
  205. );
  206. VOID
  207. ScDeleteTree(
  208. IN HKEY KeyHandle
  209. );
  210. VOID
  211. ScCopyKeyRecursive(
  212. HKEY ParentKey,
  213. PHKEY DestKeyPtr,
  214. HKEY SourceKey,
  215. LPWSTR DestKeyName
  216. );
  217. VOID
  218. ScCopyKeyValues(
  219. HKEY DestKey,
  220. HKEY SourceKey,
  221. DWORD NumberOfValues,
  222. DWORD MaxValueNameLength,
  223. DWORD MaxValueDataLength
  224. );
  225. VOID
  226. ScDeleteRegTree(
  227. HKEY ParentKey,
  228. HKEY KeyToDelete,
  229. LPWSTR NameOfKeyToDelete
  230. );
  231. VOID
  232. ScGatherOrphanIds(
  233. HKEY SystemKey,
  234. LPDWORD *OrphanIdPtr,
  235. LPDWORD idArray
  236. );
  237. BOOL
  238. ScMatchInArray(
  239. DWORD Value,
  240. LPDWORD IdArray
  241. );
  242. VOID
  243. ScStartCtrlSetCleanupThread();
  244. DWORD
  245. ScCleanupThread();
  246. DWORD
  247. ScAcceptTheBoot(
  248. VOID
  249. );
  250. DWORD
  251. ScGetNewCtrlSetId(
  252. LPDWORD IdArray,
  253. LPDWORD NewIdPtr
  254. );
  255. BOOL
  256. ScCheckLastKnownGood(
  257. VOID
  258. )
  259. /*++
  260. Routine Description:
  261. This function is called early in the service controller initialization.
  262. Its purpose is to protect the LastKnownGood control set. If this
  263. function finds that the control set that we are booting is the
  264. LastKnownGood control set, it will save the clone tree to a new
  265. control set and make this LastKnownGood. The clone tree in this case
  266. is an unchanged version of LKG. The Current control is not! Current
  267. may have been modified by drivers that were started before the service
  268. controller was started.
  269. Phase 2 of the boot procedure is always responsible for actually
  270. doing the revert to LastKnownGood. We determine that we have reverted
  271. by noting that Current and LKG will be the same control sets, and
  272. Default will be different. If Default is the same (all three control
  273. sets are the same), then it is the very first boot, and we don't consider
  274. it a failure case. If Phase 2 is causing the boot from LastKnownGood,
  275. then we want to set
  276. Failed to Default and
  277. Current to LKG and
  278. Set the LKG environment variable to FALSE.
  279. The assumption here is that Phase2 is using LastKnownGood because
  280. The Default Control Set was not acceptable.
  281. Arguments:
  282. TRUE - If all the necessary operations were successful.
  283. FALSE - If any of the control set manipulation could not be completed
  284. successfully.
  285. Return Value:
  286. Note:
  287. --*/
  288. {
  289. DWORD status;
  290. BOOL retStat;
  291. HKEY systemKey=0;
  292. HKEY selectKey=0;
  293. HKEY failedKey=0;
  294. HKEY newKey=0;
  295. DWORD idArray[NUM_IDS];
  296. WCHAR failedKeyName[CTRL_SET_NAME_CHAR_COUNT+1];
  297. DWORD savedLkgId;
  298. DWORD newId;
  299. ULONG privileges[5];
  300. //
  301. // Initialize the Critical section that will synchronize access to
  302. // these routines. The service controller could call
  303. // ScRevertToLastKnownGood at the same time that someone calls
  304. // NotifyBootConfigStatus(). This could cause the control set pointers
  305. // to get corrupted. So access to these functions is restricted by
  306. // a critical section. It is initialized here because this function
  307. // must be called prior to starting any services, or starting the
  308. // RPC server. Therefore we can't get asynchronous calls to these
  309. // routines at this time.
  310. //
  311. InitializeCriticalSection(&ScBootConfigCriticalSection);
  312. //
  313. // This thread gets SE_SECURITY_PRIVILEGE for copying security
  314. // descriptors and deleting keys.
  315. //
  316. privileges[0] = SE_BACKUP_PRIVILEGE;
  317. privileges[1] = SE_RESTORE_PRIVILEGE;
  318. privileges[2] = SE_SECURITY_PRIVILEGE;
  319. privileges[3] = SE_TAKE_OWNERSHIP_PRIVILEGE;
  320. privileges[4] = SE_SYSTEM_ENVIRONMENT_PRIVILEGE;
  321. status = ScGetPrivilege( 5, privileges);
  322. if (status != NO_ERROR) {
  323. SC_LOG1(ERROR, "ScCheckLastKnownGood: ScGetPrivilege Failed %d\n",
  324. status);
  325. return(FALSE);
  326. }
  327. //
  328. // Get the System, Select, and Clone Keys
  329. //
  330. status = ScGetTopKeys(&systemKey, &selectKey);
  331. if (status != NO_ERROR) {
  332. SC_LOG0(ERROR,"ScCheckLastKnownGood: ScGetTopKeys failed\n");
  333. retStat = FALSE;
  334. goto CleanExit;
  335. }
  336. //
  337. // Get the ControlSetIds stored in the \system\select key.
  338. //
  339. status = ScGetCtrlSetIds(
  340. selectKey,
  341. idArray);
  342. if (status != NO_ERROR) {
  343. ScRegCloseKey(systemKey);
  344. ScRegCloseKey(selectKey);
  345. SC_LOG0(ERROR,"ScCheckLastKnownGood: ScGetCtrlSetIds Failed\n");
  346. retStat = FALSE;
  347. goto CleanExit;
  348. }
  349. //
  350. // Scan for Orphaned Control Sets.
  351. // This is required prior to calling ScGetNewCtrlSetId (which
  352. // avoids the orphaned numbers).
  353. //
  354. ScGatherOrphanIds(systemKey,&ScGlobalOrphanIds,idArray);
  355. if ((SetupInProgress(systemKey, NULL)) ||
  356. (idArray[CURRENT_ID] != idArray[LKG_ID])) {
  357. //
  358. // We are not booting from LastKnownGood, so we don't do
  359. // anything except make sure the LKG_FLAG not set.
  360. //
  361. ScGlobalLastKnownGood = 0;
  362. ScRegCloseKey(systemKey);
  363. ScRegCloseKey(selectKey);
  364. retStat = TRUE;
  365. goto CleanExit;
  366. }
  367. else {
  368. //
  369. // We Must be booting the LastKnownGood configuration.
  370. // Put LkgControlSetId into SavedLkgControlSetId.
  371. //
  372. SC_LOG0(TRACE,"ScCheckLastKnownGood, We are booting LKG\n");
  373. savedLkgId = idArray[LKG_ID];
  374. //
  375. // Set the LKG environment variable to FALSE - so Phase 2
  376. // does not automatically revert again.
  377. //
  378. SET_LKG_ENV_VAR(L"False");
  379. if (status != NO_ERROR) {
  380. SC_LOG1(ERROR,"ScCheckLastKnownGood: Couldn't clear LKG "
  381. "environment variable %d\n",status);
  382. }
  383. //
  384. // Copy the Clone tree into a non-volatile node (new ControlSetId).
  385. //
  386. SC_LOG0(TRACE,"ScCheckLastKnownGood, Copy Clone to new ctrl set\n");
  387. status = ScGetNewCtrlSetId( idArray, &newId);
  388. if(status == NO_ERROR)
  389. {
  390. status = RtlNtStatusToDosError(NtInitializeRegistry(REG_INIT_BOOT_ACCEPTED_BASE +
  391. (USHORT)newId));
  392. }
  393. if (status != NO_ERROR) {
  394. SC_LOG0(ERROR,"ScCheckLastKnownGood: ScGetNewCtrlSetId Failed\n");
  395. SC_LOG0(ERROR,"SERIOUS ERROR - Unable to copy control set that "
  396. "is to be saved as LastKnownGood\n");
  397. }
  398. else {
  399. SC_LOG0(TRACE,"ScCheckLastKnownGood, Copy Clone is complete\n");
  400. //
  401. // Set LkgControlSetId to this new ControlSetId.
  402. //
  403. SC_LOG0(TRACE,"ScCheckLastKnownGood, Set LKG to this new ctrl set\n");
  404. idArray[LKG_ID] = newId;
  405. status = ScRegSetValueExW(
  406. selectKey, // hKey
  407. LKG_VALUE_NAME, // lpValueName
  408. 0, // dwValueTitle (OPTIONAL)
  409. REG_DWORD, // dwType
  410. (LPBYTE)&(idArray[LKG_ID]), // lpData
  411. sizeof(DWORD)); // cbData
  412. ScRegCloseKey(newKey);
  413. if (status != NO_ERROR) {
  414. SC_LOG1(ERROR,"ScCheckLastKnownGood: ScRegSetValueEx (lkgValue) "
  415. "failed %d\n",status);
  416. SC_LOG1(ERROR,"Semi-SERIOUS ERROR - Unable to Set Select Value "
  417. "For LastKnownGood.\nThe new ControlSet%d should "
  418. "be LKG\n",newId);
  419. }
  420. else {
  421. //
  422. // Since we already generated a LKG, we don't want to allow the
  423. // user or the boot verfication program to try to go through the
  424. // motions of generating it again. So we set the global flag that
  425. // indicates that the boot was accepted as LKG.
  426. //
  427. ScGlobalBootAccepted = TRUE;
  428. //
  429. // Set Global LKG_FLAG to indicate that we are running LKG, and
  430. // whether or not we are here because we reverted. The only
  431. // reason we would be here without reverting is because it is the
  432. // very first boot. But in the very first boot, FAILED is 0.
  433. //
  434. ScGlobalLastKnownGood |= RUNNING_LKG;
  435. if (idArray[FAILED_ID] != 0) {
  436. ScGlobalLastKnownGood |= REVERTED_TO_LKG;
  437. }
  438. } //endif - Set LKG Id to NetCtrlSet ID;
  439. } //endif - MakeNewCtrlSet == TRUE;
  440. //
  441. // If the DefaultControlSetId is the same as the original
  442. // LkgControlSetId, then Phase2 of the boot must have reverted
  443. // to Last Known Good.
  444. //
  445. if (idArray[DEFAULT_ID] != savedLkgId) {
  446. //
  447. // We are booting LastKnownGood because it was set that way
  448. // by Phase2 of the boot. In this case, we want to set the
  449. // FailedControlSetId to the DefaultControlSetId. Then we
  450. // want to set the DefaultControlSetId to the CurrentControlSetId.
  451. //
  452. // NOTE: On the very first boot, we don't go through this path
  453. // because current=default=lkg.
  454. //
  455. SC_LOG0(TRACE,"ScCheckLastKnownGood, Phase 2 caused LKG"
  456. " so we delete the failed tree and put\n"
  457. " Default->Failed\n"
  458. " Lkg -> Default\n");
  459. if (idArray[FAILED_ID] != 0) {
  460. SC_LOG0(TRACE,"ScCheckLastKnownGood: Deleting Old Failed Tree\n");
  461. failedKey = ScGetCtrlSetHandle(
  462. systemKey,
  463. idArray[FAILED_ID],
  464. failedKeyName);
  465. ScDeleteRegTree(systemKey, failedKey, failedKeyName);
  466. }
  467. //
  468. // Put the DefaultId into the Failed value.
  469. //
  470. idArray[FAILED_ID] = idArray[DEFAULT_ID];
  471. status = ScRegSetValueExW(
  472. selectKey, // hKey
  473. FAILED_VALUE_NAME, // lpValueName
  474. 0, // dwValueTitle (OPTIONAL)
  475. REG_DWORD, // dwType
  476. (LPBYTE)&(idArray[FAILED_ID]), // lpData
  477. sizeof(DWORD)); // cbData
  478. if (status != NO_ERROR) {
  479. SC_LOG1(ERROR,"ScCheckLastKnownGood: ScRegSetValueEx (failedValue) failed %d\n",
  480. status);
  481. }
  482. //
  483. // Put the CurrentId into the Default Value.
  484. //
  485. idArray[DEFAULT_ID] = idArray[CURRENT_ID];
  486. status = ScRegSetValueExW(
  487. selectKey, // hKey
  488. DEFAULT_VALUE_NAME, // lpValueName
  489. 0, // dwValueTitle (OPTIONAL)
  490. REG_DWORD, // dwType
  491. (LPBYTE)&(idArray[CURRENT_ID]), // lpData
  492. sizeof(DWORD)); // cbData
  493. if (status != NO_ERROR) {
  494. SC_LOG1(ERROR,"ScCheckLastKnownGood: ScRegSetValueEx (DefaultValue) failed %d\n",
  495. status);
  496. ScRegCloseKey(selectKey);
  497. ScRegCloseKey(systemKey);
  498. retStat = FALSE;
  499. goto CleanExit;
  500. }
  501. }
  502. ScRegCloseKey(systemKey);
  503. ScRegCloseKey(selectKey);
  504. }
  505. retStat = TRUE;
  506. CleanExit:
  507. //
  508. // If the code above was successful then mark the boot as having been
  509. // successful.
  510. //
  511. if(retStat) {
  512. HANDLE bootStatusData;
  513. BOOL b = TRUE;
  514. status = RtlLockBootStatusData(&bootStatusData);
  515. if(NT_SUCCESS(status)) {
  516. RtlGetSetBootStatusData(bootStatusData,
  517. FALSE,
  518. RtlBsdItemBootGood,
  519. &b,
  520. sizeof(BOOL),
  521. NULL);
  522. RtlUnlockBootStatusData(bootStatusData);
  523. }
  524. }
  525. //
  526. // Restore privileges for the current thread.
  527. //
  528. (VOID)ScReleasePrivilege();
  529. //
  530. // Remove any control sets that need to be deleted (clone or orphans).
  531. // This is performed by a seperate thread.
  532. //
  533. if (ScGlobalOrphanIds != NULL) {
  534. ScStartCtrlSetCleanupThread();
  535. }
  536. return(retStat);
  537. }
  538. DWORD
  539. ScRevertToLastKnownGood(
  540. VOID
  541. )
  542. /*++
  543. Routine Description:
  544. This function attempts to revert to the last known good control set.
  545. It does this in the following manner:
  546. If not running LastKnownGood:
  547. Set the LKG environment variable so that phase 2 of the boot
  548. procedure will cause the revert to happen. Then shutdown the
  549. system so it will boot again.
  550. Arguments:
  551. Return Value:
  552. Note:
  553. --*/
  554. {
  555. DWORD status;
  556. NTSTATUS ntStatus;
  557. ULONG privileges[6];
  558. //
  559. // If we are not currently running LastKnownGood, then set the tree we
  560. // are booting from (clone) to failed. Set the Default to point to
  561. // LastKnownGood. Then reboot.
  562. //
  563. if (!(ScGlobalLastKnownGood & RUNNING_LKG)) {
  564. EnterCriticalSection(&ScBootConfigCriticalSection);
  565. if (ScGlobalBootAccepted) {
  566. //
  567. // If the boot has already been accepted, then we don't want
  568. // to allow a forced revert.
  569. //
  570. LeaveCriticalSection(&ScBootConfigCriticalSection);
  571. return(ERROR_BOOT_ALREADY_ACCEPTED);
  572. }
  573. SC_LOG0(TRACE,"ScRevertToLastKnownGood: Reverting...\n");
  574. //
  575. // This thread gets SE_SECURITY_PRIVILEGE for copying security
  576. // descriptors and deleting keys.
  577. //
  578. privileges[0] = SE_BACKUP_PRIVILEGE;
  579. privileges[1] = SE_RESTORE_PRIVILEGE;
  580. privileges[2] = SE_SECURITY_PRIVILEGE;
  581. privileges[3] = SE_SHUTDOWN_PRIVILEGE;
  582. privileges[4] = SE_SYSTEM_ENVIRONMENT_PRIVILEGE;
  583. privileges[5] = SE_TAKE_OWNERSHIP_PRIVILEGE;
  584. status = ScGetPrivilege( 6, privileges);
  585. if (status != NO_ERROR) {
  586. SC_LOG1(ERROR, "ScRevertToLastKnownGood: ScGetPrivilege Failed %d\n",
  587. status);
  588. LeaveCriticalSection(&ScBootConfigCriticalSection);
  589. return(status);
  590. }
  591. //
  592. // Set the LKG environment variable to True - so Phase 2
  593. // will automatically revert, or put up the screen asking if the
  594. // user wants to revert.
  595. //
  596. SET_LKG_ENV_VAR(L"True");
  597. if (status != NO_ERROR) {
  598. //
  599. // If we could not set the environment variable that causes
  600. // the revert, there is no reason to reboot. Otherwise, we
  601. // we would reboot continuously.
  602. //
  603. // WE SHOULD LOG AN EVENT HERE - that says that we should
  604. // reboot, but we didn't.
  605. //
  606. SC_LOG1(ERROR,"RevertToLastKnownGood: Couldn't set LKG "
  607. "environment variable %d\n",status);
  608. (VOID)ScReleasePrivilege();
  609. LeaveCriticalSection(&ScBootConfigCriticalSection);
  610. return(NO_ERROR);
  611. }
  612. //
  613. // Re-boot.
  614. //
  615. SC_LOG0(ERROR,"Reverted To LastKnownGood. Now Rebooting...\n");
  616. ScLogEvent(NEVENT_REVERTED_TO_LASTKNOWNGOOD);
  617. //
  618. // Just prior to shutting down, sleep for 5 seconds so that the
  619. // system has time to flush the events to disk.
  620. //
  621. Sleep(5000);
  622. LeaveCriticalSection(&ScBootConfigCriticalSection);
  623. ntStatus = NtShutdownSystem(ShutdownReboot);
  624. if (!NT_SUCCESS(ntStatus)) {
  625. SC_LOG1(ERROR,"NtShutdownSystem Failed 0x%lx\n",ntStatus);
  626. }
  627. //
  628. // Restore privileges for the current thread.
  629. //
  630. (VOID)ScReleasePrivilege();
  631. return(RtlNtStatusToDosError(ntStatus));
  632. }
  633. //
  634. // Otherwise... just return back to the caller.
  635. //
  636. return(ERROR_ALREADY_RUNNING_LKG);
  637. }
  638. DWORD
  639. RNotifyBootConfigStatus(
  640. IN LPWSTR lpMachineName,
  641. IN DWORD BootAcceptable
  642. )
  643. /*++
  644. Routine Description:
  645. If we are not currently booted with Last Known Good, this function
  646. will revert to Last Known Good if the boot is not acceptable. Or it
  647. will save the boot configuration that we last booted from as the
  648. Last Known Good. This is the configuration that we will fall back
  649. to if a future boot fails.
  650. Arguments:
  651. BootAcceptable - This indicates whether or not the boot was acceptable.
  652. Return Value:
  653. TRUE - This is only returned if the boot is acceptable, and we
  654. successfully replaced Last Known Good with the current boot
  655. configuration.
  656. FALSE - This is returned if an error occured when attempting to replace
  657. Last Known Good or if the system is currently booted from Last
  658. Known Good.
  659. Note:
  660. --*/
  661. {
  662. DWORD status=NO_ERROR;
  663. SC_HANDLE_STRUCT scManagerHandle;
  664. UNREFERENCED_PARAMETER(lpMachineName); // This should always be null.
  665. //
  666. // Perform a security check to see if the caller has
  667. // SC_MANAGER_MODIFY_BOOT_CONFIG access.
  668. //
  669. scManagerHandle.Signature = SC_SIGNATURE;
  670. scManagerHandle.Type.ScManagerObject.DatabaseName = NULL;
  671. status = ScAccessValidate(&scManagerHandle,SC_MANAGER_MODIFY_BOOT_CONFIG);
  672. if (status != NO_ERROR) {
  673. return(status);
  674. }
  675. if (ScGlobalLastKnownGood & RUNNING_LKG) {
  676. //
  677. // If we are already booting LastKnownGood, then return false.
  678. //
  679. return(ERROR_ALREADY_RUNNING_LKG);
  680. }
  681. if (BootAcceptable) {
  682. SC_LOG0(TRACE,"NotifyBootConfigStatus: Boot is Acceptable\n");
  683. //
  684. // Must enter critical section before progressing.
  685. //
  686. EnterCriticalSection(&ScBootConfigCriticalSection);
  687. if (ScGlobalBootAccepted) {
  688. LeaveCriticalSection(&ScBootConfigCriticalSection);
  689. return(ERROR_BOOT_ALREADY_ACCEPTED);
  690. }
  691. //
  692. // If Auto-Start is not complete yet, then we just want to mark
  693. // to boot as accepted and operate on it after auto-start completion.
  694. // We also want to set the ScGlobalBootAccepted flag so that
  695. // further requests to accept the boot will be ignored.
  696. //
  697. if (!(ScGlobalLastKnownGood & AUTO_START_DONE)) {
  698. SC_LOG0(BOOT,"RNotifyBootConfigStatus: Boot Accepted, but Auto-start "
  699. "is not complete. Defer acceptance\n");
  700. ScGlobalLastKnownGood |= ACCEPT_DEFERRED;
  701. ScGlobalBootAccepted = TRUE;
  702. }
  703. else {
  704. SC_LOG0(BOOT,"RNotifyBootConfigStatus: Boot Accepted and Auto-start "
  705. "is complete\n");
  706. status = ScAcceptTheBoot();
  707. }
  708. LeaveCriticalSection(&ScBootConfigCriticalSection);
  709. return(status);
  710. }
  711. else {
  712. //
  713. // The Boot was not acceptable.
  714. //
  715. // NOTE: We should never return from the call to
  716. // ScRevertToLastKnownGood.
  717. //
  718. //
  719. SC_LOG0(TRACE,"NotifyBootConfigStatus: Boot is Not Acceptable. Revert!\n");
  720. return(ScRevertToLastKnownGood());
  721. }
  722. }
  723. DWORD
  724. ScGetTopKeys(
  725. PHKEY SystemKey,
  726. PHKEY SelectKey
  727. )
  728. /*++
  729. Routine Description:
  730. This function opens handles to the SystemKey, and the SelectKey.
  731. Arguments:
  732. Return Value:
  733. Note:
  734. --*/
  735. {
  736. DWORD status;
  737. //
  738. // Get the System Key
  739. //
  740. status = ScRegOpenKeyExW(
  741. HKEY_LOCAL_MACHINE, // hKey
  742. SYSTEM_KEY, // lpSubKey
  743. 0L, // ulOptions (reserved)
  744. SC_STANDARD_KEY_ACCESS, // desired access
  745. SystemKey); // Newly Opened Key Handle
  746. if (status != NO_ERROR) {
  747. SC_LOG1(ERROR,"ScGetTopKeys: ScRegOpenKeyEx (system key) failed %d\n",status);
  748. return (status);
  749. }
  750. //
  751. // Get the Select Key
  752. //
  753. status = ScRegOpenKeyExW(
  754. *SystemKey, // hKey
  755. SELECT_KEY, // lpSubKey
  756. 0L, // ulOptions (reserved)
  757. SC_STANDARD_KEY_ACCESS, // desired access
  758. SelectKey); // Newly Opened Key Handle
  759. if (status != NO_ERROR) {
  760. SC_LOG1(ERROR,"ScGetTopKeys: ScRegOpenKeyEx (select key) failed %d\n",status);
  761. ScRegCloseKey(*SystemKey);
  762. return (status);
  763. }
  764. return(NO_ERROR);
  765. }
  766. DWORD
  767. ScGetCtrlSetIds(
  768. HKEY SelectKey,
  769. LPDWORD IdArray
  770. )
  771. /*++
  772. Routine Description:
  773. This function obtains all the important Control Set IDs from the
  774. \system\select portion of the registry. These IDs are in the form
  775. of a DWORD that is used to build the Key name for that control set.
  776. For instance the DWORD=003 is used to build the string
  777. "control_set_003".
  778. If a control set for one of these is not present, a 0 is returned
  779. for that ID.
  780. Arguments:
  781. SelectKey - This is the Key Handle for the \system\select portion of
  782. the registry.
  783. IdArray - This is an array of DWORDs where each element is an ID.
  784. This array contains elements for Current, Default, LKG, and Failed.
  785. Return Value:
  786. NO_ERROR - If the operation was successful.
  787. OTHER - Any error that can be returned from RegQueryValueEx could be
  788. returned here if we fail to get an ID for Current, Default, or
  789. LKG. We expect Failed To be empty to start with.
  790. Note:
  791. --*/
  792. {
  793. DWORD status;
  794. DWORD bufferSize;
  795. //
  796. // Get the Current Id
  797. //
  798. bufferSize = sizeof(DWORD);
  799. status = ScRegQueryValueExW (
  800. SelectKey, // hKey
  801. CURRENT_VALUE_NAME, // lpValueName
  802. NULL, // lpTitleIndex
  803. NULL, // lpType
  804. (LPBYTE)&IdArray[CURRENT_ID], // lpData
  805. &bufferSize); // lpcbData
  806. if (status != NO_ERROR) {
  807. SC_LOG1(ERROR,"ScGetCtrlSetIds,ScRegQueryValueEx(current) failed %d\n",status);
  808. IdArray[CURRENT_ID] = 0;
  809. return(status);
  810. }
  811. //
  812. // Get the DefaultID
  813. //
  814. bufferSize = sizeof(DWORD);
  815. status = ScRegQueryValueExW (
  816. SelectKey, // hKey
  817. DEFAULT_VALUE_NAME, // lpValueName
  818. NULL, // lpTitleIndex
  819. NULL, // lpType
  820. (LPBYTE)&IdArray[DEFAULT_ID], // lpData
  821. &bufferSize); // lpcbData
  822. if (status != NO_ERROR) {
  823. SC_LOG1(ERROR,"ScGetCtrlSetIds,ScRegQueryValueEx(default) failed %d\n",status);
  824. IdArray[DEFAULT_ID] = 0;
  825. return(status);
  826. }
  827. //
  828. // Get the LKG Id
  829. //
  830. bufferSize = sizeof(DWORD);
  831. status = ScRegQueryValueExW (
  832. SelectKey, // hKey
  833. LKG_VALUE_NAME, // lpValueName
  834. NULL, // lpTitleIndex
  835. NULL, // lpType
  836. (LPBYTE)&IdArray[LKG_ID], // lpData
  837. &bufferSize); // lpcbData
  838. if (status != NO_ERROR) {
  839. SC_LOG1(ERROR,"ScGetCtrlSetIds,ScRegQueryValueEx(LKG) failed %d\n",status);
  840. IdArray[LKG_ID] = 0;
  841. return(status);
  842. }
  843. //
  844. // Get the Failed Id
  845. //
  846. bufferSize = sizeof(DWORD);
  847. status = ScRegQueryValueExW (
  848. SelectKey, // hKey
  849. FAILED_VALUE_NAME, // lpValueName
  850. NULL, // lpTitleIndex
  851. NULL, // lpType
  852. (LPBYTE)&IdArray[FAILED_ID], // lpData
  853. &bufferSize); // lpcbData
  854. if (status != NO_ERROR) {
  855. SC_LOG1(ERROR,"ScGetCtrlSetIds,ScRegQueryValueEx(Failed) failed %d\n",status);
  856. IdArray[FAILED_ID] = 0;
  857. }
  858. return(NO_ERROR);
  859. }
  860. VOID
  861. ScDeleteRegTree(
  862. HKEY ParentKey,
  863. HKEY KeyToDelete,
  864. LPWSTR NameOfKeyToDelete
  865. )
  866. /*++
  867. Routine Description:
  868. This function walks through a Key Tree and deletes all the sub-keys
  869. contained within. It then closes the top level Key Handle, and deletes
  870. that key (which is a subkey of the parent).
  871. This function also closes the handle for the key being deleted.
  872. Arguments:
  873. ParentKey - This is the handle to the parent key whose sub-key is being
  874. deleted.
  875. KeyToDelete - A handle to the key that is to be deleted.
  876. NameOfKeyToDelete - This is a pointer to a string that Identifies the
  877. name of the key that is to be deleted.
  878. Return Value:
  879. none.
  880. Note:
  881. --*/
  882. {
  883. DWORD status;
  884. if (KeyToDelete == NULL)
  885. {
  886. return;
  887. }
  888. //
  889. // Delete the tree.
  890. //
  891. ScDeleteTree(KeyToDelete);
  892. ScRegCloseKey(KeyToDelete);
  893. status = ScRegDeleteKeyW(ParentKey, NameOfKeyToDelete);
  894. if (status != NO_ERROR) {
  895. SC_LOG1(ERROR,"ScDeleteRegTree, ScRegDeleteKey failed %d\n",status);
  896. }
  897. return;
  898. }
  899. BOOL
  900. ScBuildCtrlSetName(
  901. LPWSTR ControlSetName,
  902. DWORD ControlId
  903. )
  904. /*++
  905. Routine Description:
  906. Arguments:
  907. Return Value:
  908. Note:
  909. --*/
  910. {
  911. DWORD NumOffset = CTRL_SET_NAME_NUM_OFFSET;
  912. //
  913. // Build the name. NumOffset is the array offset of where the
  914. // number portion of the name is to be stored. The number initially
  915. // contains 000. And the offset points to the first zero. If only
  916. // two digits are to be stored, the offset is first incremented to
  917. // point to where the last two digits go.
  918. //
  919. if (ControlId > 999) {
  920. SC_LOG1(ERROR, "ScBuildCtrlSetName,ControlId Too Large -- %d\n",ControlId);
  921. return(FALSE);
  922. }
  923. if (ControlId < 100) {
  924. NumOffset++;
  925. }
  926. if (ControlId < 10) {
  927. NumOffset++;
  928. }
  929. wcscpy(ControlSetName, CTRL_SET_NAME_TEMPLATE);
  930. //
  931. // The above checks should assure that the _ultow call will not
  932. // overflow the buffer.
  933. //
  934. _ultow(ControlId, &(ControlSetName[NumOffset]), 10);
  935. return(TRUE);
  936. }
  937. HKEY
  938. ScGetCtrlSetHandle(
  939. HKEY SystemKey,
  940. DWORD ControlId,
  941. LPWSTR ControlSetName
  942. )
  943. /*++
  944. Routine Description:
  945. This function uses the ControlId to create the name of the control set
  946. to open. Then it opens a Key (handle) to this control set.
  947. Then name was well as the key handle are returned.
  948. Arguments:
  949. SystemKey - This is the handle for the System Key. The Control Sets
  950. are sub-keys for this key.
  951. ControlId - This is the ID for the Control Set for which we are
  952. desiring a handle (key).
  953. KeyName - This is a pointer to a location where the name of the key
  954. is to be placed.
  955. Return Value:
  956. HKEY - This is the Key handle for the control set in question. If the
  957. control set does not exist, a NULL is returned.
  958. Note:
  959. --*/
  960. {
  961. DWORD status;
  962. HKEY ctrlSetKey;
  963. //
  964. // Build the Control Set Name
  965. //
  966. if (!ScBuildCtrlSetName(ControlSetName, ControlId)) {
  967. return(NULL);
  968. }
  969. //
  970. // Open the Key for this name.
  971. //
  972. SC_LOG1(TRACE,"ScGetCtrlSetHandle: ControlSetName = "FORMAT_LPWSTR"\n",
  973. ControlSetName);
  974. //
  975. // Get the ControlSetName
  976. //
  977. status = ScRegOpenKeyExW(
  978. SystemKey, // hKey
  979. ControlSetName, // lpSubKey
  980. 0L, // ulOptions (reserved)
  981. SC_CONTROL_SET_KEY_ACCESS, // desired access
  982. &ctrlSetKey); // Newly Opened Key Handle
  983. if (status != NO_ERROR) {
  984. SC_LOG2(ERROR,"ScGetCtrlSetHandle: ScRegOpenKeyEx (%ws) failed %d\n",
  985. ControlSetName,
  986. status);
  987. return (NULL);
  988. }
  989. return(ctrlSetKey);
  990. }
  991. DWORD
  992. ScGetNewCtrlSetId(
  993. LPDWORD IdArray,
  994. LPDWORD NewIdPtr
  995. )
  996. /*++
  997. Routine Description:
  998. This routine computes the new control set ID to be used for
  999. the LKG control set
  1000. Arguments:
  1001. IdArray - Supplies the ID array filled in by ScGetCtrlSetIds
  1002. NewIdPtr - Returns a free ID to be used for the LKG control set
  1003. Return Value:
  1004. Either NO_ERROR if successful or ERROR_NO_MORE_ITEMS if there
  1005. are no more free IDs (should never happen)
  1006. --*/
  1007. {
  1008. DWORD newId, i;
  1009. BOOL inArray;
  1010. for(newId = 1; newId < 1000; newId++)
  1011. {
  1012. inArray = FALSE;
  1013. for(i = 0; i < NUM_IDS; i++)
  1014. {
  1015. if(IdArray[i] == newId)
  1016. {
  1017. inArray = TRUE;
  1018. break;
  1019. }
  1020. }
  1021. if (!inArray && !ScMatchInArray(newId, ScGlobalOrphanIds))
  1022. {
  1023. *NewIdPtr = newId;
  1024. return NO_ERROR;
  1025. }
  1026. }
  1027. return ERROR_NO_MORE_ITEMS;
  1028. }
  1029. VOID
  1030. ScDeleteTree(
  1031. IN HKEY KeyHandle
  1032. )
  1033. /*++
  1034. Routine Description:
  1035. This function recursively deletes all keys under the key handle that
  1036. is passed in.
  1037. Arguments:
  1038. KeyHandle - This is the handle for the Key Tree that is being deleted.
  1039. Return Value:
  1040. none.
  1041. Note:
  1042. This was cut & pasted from ..\..\winreg\tools\crdel\crdel.c
  1043. The only modifications were changing TSTR to WSTR and calling the
  1044. UNICODE version of the functions.
  1045. --*/
  1046. {
  1047. DWORD status;
  1048. DWORD Index;
  1049. HKEY ChildHandle;
  1050. DWORD bytesReturned;
  1051. BYTE buffer[ sizeof( KEY_FULL_INFORMATION) + sizeof( WCHAR) * MAX_PATH];
  1052. DWORD NumberOfSubKeys;
  1053. PWCHAR KeyName;
  1054. status = NtQueryKey(
  1055. (HANDLE)KeyHandle,
  1056. KeyFullInformation,
  1057. (PVOID)buffer,
  1058. sizeof( buffer),
  1059. &bytesReturned
  1060. );
  1061. if ( status != STATUS_SUCCESS) {
  1062. SC_LOG1(ERROR, "ScDeleteTree: NtQueryKey Failed 0x%x\n",status);
  1063. return;
  1064. }
  1065. NumberOfSubKeys = ((PKEY_FULL_INFORMATION)buffer)->SubKeys;
  1066. KeyName = (PWCHAR)buffer;
  1067. for( Index = 0; Index < NumberOfSubKeys; Index++ ) {
  1068. status = ScRegEnumKeyW(
  1069. KeyHandle,
  1070. 0,
  1071. KeyName,
  1072. sizeof( buffer)
  1073. );
  1074. if (status != NO_ERROR) {
  1075. SC_LOG1(ERROR, "ScDeleteTree: ScRegEnumKeyW Failed %d\n",status);
  1076. return;
  1077. }
  1078. status = ScRegOpenKeyExW(
  1079. KeyHandle,
  1080. KeyName,
  1081. REG_OPTION_RESERVED,
  1082. SC_DELETE_KEY_ACCESS,
  1083. &ChildHandle
  1084. );
  1085. if (status != NO_ERROR) {
  1086. SC_LOG2(ERROR, "ScDeleteTree: ScRegOpenKeyExW (%ws) Failed %d\n",
  1087. KeyName,
  1088. status);
  1089. return;
  1090. }
  1091. ScDeleteTree( ChildHandle );
  1092. status = ScRegDeleteKeyW(
  1093. KeyHandle,
  1094. KeyName);
  1095. NtClose( (HANDLE)ChildHandle);
  1096. if ( status != NO_ERROR) {
  1097. SC_LOG1(ERROR, "ScDeleteTree: ScRegDeleteKeyW Failed 0x%x\n", status);
  1098. return;
  1099. }
  1100. }
  1101. }
  1102. VOID
  1103. ScDeleteRegServiceEntry(
  1104. LPWSTR ServiceName
  1105. )
  1106. /*++
  1107. Routine Description:
  1108. Arguments:
  1109. Return Value:
  1110. --*/
  1111. {
  1112. DWORD status;
  1113. HKEY parentKey;
  1114. HKEY keyToDelete;
  1115. ULONG privileges[4];
  1116. LPWSTR ServicesKeyPath = SERVICES_KEY;
  1117. //*******************************
  1118. // Delete the registry node for
  1119. // This service.
  1120. //*******************************
  1121. privileges[0] = SE_BACKUP_PRIVILEGE;
  1122. privileges[1] = SE_RESTORE_PRIVILEGE;
  1123. privileges[2] = SE_SECURITY_PRIVILEGE;
  1124. privileges[3] = SE_TAKE_OWNERSHIP_PRIVILEGE;
  1125. status = ScGetPrivilege( 4, privileges);
  1126. if (status != NO_ERROR) {
  1127. SC_LOG1(ERROR, "ScDeleteRegServiceEntry: ScGetPrivilege Failed %d\n",
  1128. status);
  1129. return;
  1130. }
  1131. //
  1132. // Open the "services" section of the CurrentControlSet.
  1133. //
  1134. status = ScRegOpenKeyExW(
  1135. HKEY_LOCAL_MACHINE, // hKey
  1136. ServicesKeyPath, // lpSubKey
  1137. 0L, // ulOptions (reserved)
  1138. SC_DELETE_KEY_ACCESS, // desired access
  1139. &parentKey); // Newly Opened Key Handle
  1140. if (status != NO_ERROR) {
  1141. SC_LOG2(ERROR,"ScDeleteRegServiceEntry: "
  1142. "ScRegOpenKeyEx (%ws) failed %d\n",ServicesKeyPath,
  1143. status);
  1144. //
  1145. // Restore privileges for the current thread.
  1146. //
  1147. (VOID)ScReleasePrivilege();
  1148. return;
  1149. }
  1150. //
  1151. // Get Key for the Tree we are to delete
  1152. //
  1153. status = ScRegOpenKeyExW(
  1154. parentKey, // hKey
  1155. ServiceName, // lpSubKey
  1156. 0L, // ulOptions (reserved)
  1157. SC_DELETE_KEY_ACCESS, // desired access
  1158. &keyToDelete); // Newly Opened Key Handle
  1159. if (status != NO_ERROR) {
  1160. SC_LOG2(ERROR,"ScDeleteRegServiceEntry: "
  1161. "ScRegOpenKeyEx (%ws) failed %d\n",ServiceName,
  1162. status);
  1163. ScRegCloseKey(parentKey);
  1164. //
  1165. // Restore privileges for the current thread.
  1166. //
  1167. (VOID)ScReleasePrivilege();
  1168. return;
  1169. }
  1170. //
  1171. // Delete the Key.
  1172. // NOTE: ScDeleteRegTree will also close the handle to the keyToDelete.
  1173. //
  1174. ScDeleteRegTree(parentKey, keyToDelete, ServiceName);
  1175. ScRegCloseKey(parentKey);
  1176. //
  1177. // Restore privileges for the current thread.
  1178. //
  1179. (VOID)ScReleasePrivilege();
  1180. return;
  1181. }
  1182. VOID
  1183. ScGatherOrphanIds(
  1184. HKEY SystemKey,
  1185. LPDWORD *OrphanIdPtr,
  1186. LPDWORD idArray
  1187. )
  1188. /*++
  1189. Routine Description:
  1190. This function searches through the system key to find any orphan control
  1191. set ids. If any are found, they are packed into an array of ids that
  1192. are passed back to the caller.
  1193. NOTE: This function allocates memory for *OrphanIdPtr if orphans
  1194. exist. It is the responsibility of the caller to free this memory.
  1195. Arguments:
  1196. SystemKey - This is an open handle to the system key.
  1197. OrphanIdPtr - This is a pointer to a location for the pointer to
  1198. the array of Orphan IDs. If there are no orphans, then this pointer
  1199. is NULL on return from this routine.
  1200. idArray - This is the array of IDs that are used in the select key.
  1201. Return Value:
  1202. Note:
  1203. --*/
  1204. {
  1205. DWORD enumStatus;
  1206. DWORD status;
  1207. WCHAR KeyName[ MAX_PATH ];
  1208. DWORD KeyNameLength = MAX_PATH;
  1209. DWORD i=0;
  1210. DWORD j=0;
  1211. DWORD numOrphans=0;
  1212. DWORD num;
  1213. LPDWORD tempIdArray;
  1214. DWORD matchInArray;
  1215. WCHAR ClassName[ MAX_PATH ];
  1216. DWORD ClassNameLength=MAX_PATH;
  1217. DWORD NumberOfSubKeys;
  1218. DWORD MaxSubKeyLength;
  1219. DWORD MaxClassLength;
  1220. DWORD NumberOfValues;
  1221. DWORD MaxValueNameLength;
  1222. DWORD MaxValueDataLength;
  1223. DWORD SecurityDescriptorLength;
  1224. FILETIME LastWriteTime;
  1225. //
  1226. // If the pointer points to something - free it. and make the pointer
  1227. // NULL.
  1228. //
  1229. LocalFree(*OrphanIdPtr);
  1230. *OrphanIdPtr = NULL;
  1231. //
  1232. // Find out how many subkeys there are in the system key.
  1233. // This will tell us the maximum size of array required to store
  1234. // potential orphan control set IDs.
  1235. //
  1236. status = ScRegQueryInfoKeyW(
  1237. SystemKey,
  1238. ClassName,
  1239. &ClassNameLength,
  1240. NULL,
  1241. &NumberOfSubKeys,
  1242. &MaxSubKeyLength,
  1243. &MaxClassLength,
  1244. &NumberOfValues,
  1245. &MaxValueNameLength,
  1246. &MaxValueDataLength,
  1247. &SecurityDescriptorLength,
  1248. &LastWriteTime
  1249. );
  1250. if (status != NO_ERROR) {
  1251. SC_LOG1(ERROR, "ScGatherOrphanIds: ScRegQueryInfoKey Failed %d\n",status);
  1252. return;
  1253. }
  1254. //
  1255. // Allocate a buffer for the orphan control set IDs. This buffer is
  1256. // initialized to 0 to guanantee that the array if IDs will be terminated
  1257. // by a 0.
  1258. //
  1259. tempIdArray = (LPDWORD)LocalAlloc(LMEM_ZEROINIT, sizeof(DWORD) * (NumberOfSubKeys+1));
  1260. if (tempIdArray == NULL) {
  1261. SC_LOG0(ERROR, "ScGatherOrphanIds:LocalAlloc Failed\n");
  1262. return;
  1263. }
  1264. do {
  1265. enumStatus = ScRegEnumKeyW(
  1266. SystemKey,
  1267. i,
  1268. KeyName,
  1269. KeyNameLength);
  1270. if (enumStatus == NO_ERROR) {
  1271. //
  1272. // We have a key name, is it a control set?
  1273. //
  1274. if ((wcslen(KeyName) == (CTRL_SET_NAME_CHAR_COUNT)) &&
  1275. (!wcsncmp(
  1276. CTRL_SET_NAME_TEMPLATE,
  1277. KeyName,
  1278. CTRL_SET_NAME_NUM_OFFSET))) {
  1279. //
  1280. // It appears to be a control set, now get the number
  1281. // and see if it is in the array of ids from the select
  1282. // key.
  1283. //
  1284. num = (DWORD)_wtol(KeyName+CTRL_SET_NAME_NUM_OFFSET);
  1285. matchInArray = FALSE;
  1286. for (j=0; j<NUM_IDS; j++) {
  1287. if (num == idArray[j]) {
  1288. matchInArray = TRUE;
  1289. break;
  1290. }
  1291. }
  1292. //
  1293. // It's not in the array of ids from the select key.
  1294. // Add it to the number of orphans.
  1295. //
  1296. if ((matchInArray == FALSE) && (num < 1000)) {
  1297. if (numOrphans < NumberOfSubKeys) {
  1298. tempIdArray[numOrphans] = num;
  1299. numOrphans++;
  1300. }
  1301. }
  1302. }
  1303. }
  1304. i++;
  1305. }
  1306. while (enumStatus == NO_ERROR);
  1307. if (numOrphans > 0) {
  1308. *OrphanIdPtr = tempIdArray;
  1309. }
  1310. else {
  1311. *OrphanIdPtr = NULL;
  1312. LocalFree(tempIdArray);
  1313. }
  1314. return;
  1315. }
  1316. VOID
  1317. ScDeleteCtrlSetOrphans(
  1318. VOID
  1319. )
  1320. /*++
  1321. Routine Description:
  1322. This function deletes orphaned control sets if any exist. The control
  1323. set numbers for these orphaned sets are pointed to by a global
  1324. memory pointer. If this pointer is non-null, then there are control sets
  1325. to delete. After deletion, the memory pointed to by this pointer is
  1326. freed.
  1327. NOTE: The necessary privileges are expected to be held prior to calling
  1328. this function.
  1329. Arguments:
  1330. none
  1331. Return Value:
  1332. none
  1333. --*/
  1334. {
  1335. DWORD status;
  1336. DWORD i;
  1337. HKEY systemKey;
  1338. HKEY keyToDelete;
  1339. LPWSTR SystemKeyPath = SYSTEM_KEY;
  1340. WCHAR nameOfKeyToDelete[CTRL_SET_NAME_CHAR_COUNT+1];
  1341. if (ScGlobalOrphanIds != NULL) {
  1342. //
  1343. // Open the SYSTEM key in the registry.
  1344. //
  1345. status = ScRegOpenKeyExW(
  1346. HKEY_LOCAL_MACHINE, // hKey
  1347. SystemKeyPath, // lpSubKey
  1348. 0L, // ulOptions (reserved)
  1349. SC_DELETE_KEY_ACCESS, // desired access
  1350. &systemKey); // Newly Opened Key Handle
  1351. if (status != NO_ERROR) {
  1352. SC_LOG2(ERROR,"ScDeleteCtrlSetOrphans: "
  1353. "ScRegOpenKeyEx (%ws) failed %d\n",SystemKeyPath,
  1354. status);
  1355. return;
  1356. }
  1357. for (i=0; ScGlobalOrphanIds[i]!=0; i++) {
  1358. //
  1359. // Use the ID number to get the name and key handle for the
  1360. // KeyToDelete.
  1361. //
  1362. keyToDelete = ScGetCtrlSetHandle(
  1363. systemKey,
  1364. ScGlobalOrphanIds[i],
  1365. nameOfKeyToDelete);
  1366. //
  1367. // Delete the entire tree. Then go onto the next ID.
  1368. //
  1369. SC_LOG1(TRACE,
  1370. "ScDeleteCtrlSetOrphans, Delete orphan control set %d\n",
  1371. ScGlobalOrphanIds[i]);
  1372. ScDeleteRegTree(systemKey, keyToDelete, nameOfKeyToDelete);
  1373. SC_LOG0(TRACE,"ScDeleteCtrlSetOrphans, Finished Deleting orphan control set\n");
  1374. }
  1375. //
  1376. // Free memory for IDs, and set the global pointer to NULL.
  1377. //
  1378. LocalFree(ScGlobalOrphanIds);
  1379. ScGlobalOrphanIds = NULL;
  1380. }
  1381. return;
  1382. }
  1383. BOOL
  1384. ScMatchInArray(
  1385. DWORD Value,
  1386. LPDWORD Array
  1387. )
  1388. /*++
  1389. Routine Description:
  1390. This function scans through a null terminated array of DWORDs looking
  1391. for a match with the DWORD value that is passed in.
  1392. Arguments:
  1393. Value - The DWORD value that we are looking for.
  1394. Array - The pointer to the Array of DWORDs that we are scanning through.
  1395. Return Value:
  1396. TRUE - If a the Value is found in the Array.
  1397. FALSE - If it is not found.
  1398. --*/
  1399. {
  1400. DWORD i;
  1401. if (Array != NULL) {
  1402. for(i=0; Array[i] != 0; i++) {
  1403. if (Value == Array[i]) {
  1404. return(TRUE);
  1405. }
  1406. }
  1407. }
  1408. return(FALSE);
  1409. }
  1410. VOID
  1411. ScStartCtrlSetCleanupThread(
  1412. )
  1413. /*++
  1414. Routine Description:
  1415. This function starts a thread that will delete delete any orphaned control sets.
  1416. Arguments:
  1417. NONE.
  1418. Return Value:
  1419. none
  1420. --*/
  1421. {
  1422. DWORD status;
  1423. HANDLE threadHandle;
  1424. DWORD threadId;
  1425. threadHandle = CreateThread (
  1426. NULL, // Thread Attributes.
  1427. 0L, // Stack Size
  1428. (LPTHREAD_START_ROUTINE)ScCleanupThread,// lpStartAddress
  1429. (LPVOID)0L, // lpParameter
  1430. 0L, // Creation Flags
  1431. &threadId); // lpThreadId
  1432. if (threadHandle == (HANDLE) NULL) {
  1433. SC_LOG1(ERROR,"ScStartCtrlSetCleanupThread:CreateThread failed %d\n",
  1434. GetLastError());
  1435. //
  1436. // If we couldn't create the thread for some reason, then just
  1437. // go ahead and to the cleanup with this thread. This may make
  1438. // booting the system slow, but it's the best I can do.
  1439. //
  1440. status = ScCleanupThread();
  1441. }
  1442. else {
  1443. CloseHandle(threadHandle);
  1444. }
  1445. }
  1446. DWORD
  1447. ScCleanupThread(
  1448. )
  1449. /*++
  1450. Routine Description:
  1451. This functions looks through the system key to see if
  1452. there are any orphan control sets to delete. If found, the orphans
  1453. are deleted. Orphaned control sets are control sets that exist in
  1454. the system key, but are not referenced in the \system\select key.
  1455. NOTE: This function should only be called when no other threads are
  1456. creating control sets. Otherwise, this function may see a new control
  1457. set that is not yet in the select key, and attempt to delete it.
  1458. Arguments:
  1459. NONE.
  1460. Return Value:
  1461. none.
  1462. --*/
  1463. {
  1464. DWORD status;
  1465. HKEY systemKey=0;
  1466. HKEY selectKey=0;
  1467. DWORD idArray[NUM_IDS];
  1468. ULONG privileges[4];
  1469. //
  1470. // This thread gets SE_SECURITY_PRIVILEGE for copying security
  1471. // descriptors and deleting keys.
  1472. //
  1473. privileges[0] = SE_BACKUP_PRIVILEGE;
  1474. privileges[1] = SE_RESTORE_PRIVILEGE;
  1475. privileges[2] = SE_SECURITY_PRIVILEGE;
  1476. privileges[3] = SE_TAKE_OWNERSHIP_PRIVILEGE;
  1477. status = ScGetPrivilege( 4, privileges);
  1478. if (status != NO_ERROR) {
  1479. SC_LOG1(ERROR, "ScCheckLastKnownGood: ScGetPrivilege Failed %d\n",
  1480. status);
  1481. return(FALSE);
  1482. }
  1483. EnterCriticalSection(&ScBootConfigCriticalSection);
  1484. //
  1485. // Get the System, Select, and Clone Keys
  1486. //
  1487. status = ScGetTopKeys(&systemKey, &selectKey);
  1488. if (status != NO_ERROR) {
  1489. SC_LOG0(ERROR,"ScCleanupThread: ScGetTopKeys failed\n");
  1490. LeaveCriticalSection(&ScBootConfigCriticalSection);
  1491. goto CleanExit;
  1492. }
  1493. //
  1494. // Get the ControlSetIds stored in the \system\select key.
  1495. //
  1496. status = ScGetCtrlSetIds(
  1497. selectKey,
  1498. idArray);
  1499. if (status != NO_ERROR) {
  1500. SC_LOG0(ERROR,"ScCleanupThread: ScGetCtrlSetIds Failed\n");
  1501. LeaveCriticalSection(&ScBootConfigCriticalSection);
  1502. goto CleanExit;
  1503. }
  1504. //
  1505. // Scan for Orphaned Control Sets.
  1506. //
  1507. ScGatherOrphanIds(systemKey,&ScGlobalOrphanIds,idArray);
  1508. LeaveCriticalSection(&ScBootConfigCriticalSection);
  1509. if (ScGlobalOrphanIds != NULL) {
  1510. ScDeleteCtrlSetOrphans();
  1511. }
  1512. CleanExit:
  1513. if (systemKey != 0) {
  1514. ScRegCloseKey(systemKey);
  1515. }
  1516. if (selectKey != 0) {
  1517. ScRegCloseKey(selectKey);
  1518. }
  1519. (VOID)ScReleasePrivilege();
  1520. return(0);
  1521. }
  1522. VOID
  1523. ScRunAcceptBootPgm(
  1524. VOID
  1525. )
  1526. /*++
  1527. Routine Description:
  1528. This function is called after the Service Controller has finished
  1529. auto-starting all the auto-start services. If the boot has already
  1530. been accepted (for instance, WinLogon already called
  1531. NotifyBootConfigStatus()), then at this point we can accept the boot.
  1532. If the boot has not yet been accepted, this function looks in the
  1533. ACCEPT_BOOT_KEY portion of the registry to
  1534. see if there is a value containing the image path of the boot verify
  1535. program to execute. The program can have any name or path. If it
  1536. is in the registry, this function will run it.
  1537. This function is called when the service controller thinks that the
  1538. boot has completed successfully. It is up to the exec'd program
  1539. to decide if this is true or not, and take appropriate action if
  1540. necessary. The default boot verify program will simply accept the
  1541. boot as is.
  1542. Arguments:
  1543. none
  1544. Return Value:
  1545. none
  1546. --*/
  1547. {
  1548. DWORD status;
  1549. LPWSTR AcceptBootKeyPath = ACCEPT_BOOT_KEY;
  1550. HKEY AcceptBootKey;
  1551. DWORD ValueType;
  1552. LPWSTR pTempImagePath;
  1553. LPWSTR pImagePath;
  1554. PROCESS_INFORMATION processInfo;
  1555. STARTUPINFOW StartupInfo;
  1556. DWORD bufferSize;
  1557. DWORD charCount;
  1558. //
  1559. // Check to see if the boot has already been accepted.
  1560. //
  1561. EnterCriticalSection(&ScBootConfigCriticalSection);
  1562. ScGlobalLastKnownGood |= AUTO_START_DONE;
  1563. if (ScGlobalLastKnownGood & ACCEPT_DEFERRED) {
  1564. SC_LOG0(BOOT,"ScRunAcceptBootPgm: Boot Acceptance was deferred. Accept "
  1565. "it now\n");
  1566. ScAcceptTheBoot();
  1567. LeaveCriticalSection(&ScBootConfigCriticalSection);
  1568. return;
  1569. }
  1570. LeaveCriticalSection(&ScBootConfigCriticalSection);
  1571. //
  1572. // Open the \CurrentControlSet\Control\AcceptBootPgm Key
  1573. //
  1574. //
  1575. // Get the System Key
  1576. //
  1577. status = ScRegOpenKeyExW(
  1578. HKEY_LOCAL_MACHINE, // hKey
  1579. AcceptBootKeyPath, // lpSubKey
  1580. 0L, // ulOptions (reserved)
  1581. KEY_READ, // desired access
  1582. &AcceptBootKey); // Newly Opened Key Handle
  1583. if (status != NO_ERROR) {
  1584. SC_LOG2(TRACE,"ScRunAcceptBootPgm: ScRegOpenKeyEx (%ws) failed %d\n",
  1585. AcceptBootKeyPath, status);
  1586. return;
  1587. }
  1588. //
  1589. // If the ImagePath value is there, then run the specified
  1590. // program.
  1591. //
  1592. bufferSize = MAX_PATH * sizeof(WCHAR);
  1593. pTempImagePath = (LPWSTR)LocalAlloc(LMEM_FIXED, bufferSize*2);
  1594. if (pTempImagePath == NULL) {
  1595. SC_LOG0(TRACE,"ScRunAcceptBootPgm,LocalAlloc failed \n");
  1596. return;
  1597. }
  1598. pImagePath = pTempImagePath + MAX_PATH;
  1599. status = ScRegQueryValueExW (
  1600. AcceptBootKey, // hKey
  1601. IMAGE_PATH_NAME, // lpValueName
  1602. NULL, // lpTitleIndex
  1603. &ValueType, // lpType
  1604. (LPBYTE)pTempImagePath, // lpData
  1605. &bufferSize); // lpcbData
  1606. if (status != NO_ERROR) {
  1607. SC_LOG1(TRACE,"ScRunAcceptBootPgm,ScRegQueryValueEx failed %d\n",status);
  1608. ScRegCloseKey(AcceptBootKey);
  1609. LocalFree(pTempImagePath);
  1610. return;
  1611. }
  1612. SC_LOG1(TRACE,"ScRunAcceptBootPgm:Executing the %ws program\n",pTempImagePath);
  1613. if ((ValueType == REG_SZ) || (ValueType == REG_EXPAND_SZ))
  1614. {
  1615. WCHAR wszEmpty[1] = {0};
  1616. if (ValueType == REG_EXPAND_SZ)
  1617. {
  1618. charCount = ExpandEnvironmentStringsW (
  1619. pTempImagePath,
  1620. pImagePath,
  1621. MAX_PATH);
  1622. if (charCount > MAX_PATH)
  1623. {
  1624. SC_LOG0(ERROR,"ScRunAcceptBootPgm: ImagePath is too big\n");
  1625. LocalFree(pTempImagePath);
  1626. return;
  1627. }
  1628. }
  1629. else
  1630. {
  1631. pImagePath = pTempImagePath;
  1632. }
  1633. //
  1634. // Exec the program.
  1635. //
  1636. StartupInfo.cb = sizeof(STARTUPINFOW); // size
  1637. StartupInfo.lpReserved = NULL; // lpReserved
  1638. StartupInfo.lpDesktop = NULL; // DeskTop
  1639. StartupInfo.lpTitle = NULL; // Title
  1640. StartupInfo.dwX = 0; // X (position)
  1641. StartupInfo.dwY = 0; // Y (position)
  1642. StartupInfo.dwXSize = 0; // XSize (dimension)
  1643. StartupInfo.dwYSize = 0; // YSize (dimension)
  1644. StartupInfo.dwXCountChars = 0; // XCountChars
  1645. StartupInfo.dwYCountChars = 0; // YCountChars
  1646. StartupInfo.dwFillAttribute = 0; // FillAttributes
  1647. StartupInfo.dwFlags = STARTF_FORCEOFFFEEDBACK;
  1648. // Flags - should be STARTF_TASKNOTCLOSABLE
  1649. StartupInfo.wShowWindow = SW_HIDE; // ShowWindow
  1650. StartupInfo.cbReserved2 = 0L; // cbReserved
  1651. StartupInfo.lpReserved2 = NULL; // lpReserved
  1652. if (!CreateProcessW (
  1653. pImagePath, // Fully qualified image name
  1654. wszEmpty, // Command Line
  1655. NULL, // Process Attributes
  1656. NULL, // Thread Attributes
  1657. FALSE, // Inherit Handles
  1658. DETACHED_PROCESS, // Creation Flags
  1659. NULL, // Pointer to Environment block
  1660. NULL, // Pointer to Current Directory
  1661. &StartupInfo, // Startup Info
  1662. &processInfo)) // ProcessInformation
  1663. {
  1664. status = GetLastError();
  1665. SC_LOG1(ERROR,
  1666. "ScRunAcceptBootPgm: CreateProcess failed " FORMAT_DWORD "\n",
  1667. status);
  1668. }
  1669. }
  1670. LocalFree(pTempImagePath);
  1671. ScRegCloseKey(AcceptBootKey);
  1672. return;
  1673. }
  1674. DWORD
  1675. ScAcceptTheBoot(
  1676. VOID
  1677. )
  1678. /*++
  1679. Routine Description:
  1680. This function does the actual work of accepting the current boot as
  1681. the LKG configuration.
  1682. NOTE: Before the function is called, the ScBootConfigCriticalSection
  1683. is expected to be entered.
  1684. Arguments:
  1685. Return Value:
  1686. --*/
  1687. {
  1688. DWORD status;
  1689. HKEY systemKey=0;
  1690. HKEY selectKey=0;
  1691. DWORD idArray[NUM_IDS];
  1692. DWORD newId;
  1693. ULONG privileges[4];
  1694. //
  1695. // This thread gets SE_SECURITY_PRIVILEGE for copying security
  1696. // descriptors and deleting keys.
  1697. //
  1698. privileges[0] = SE_BACKUP_PRIVILEGE;
  1699. privileges[1] = SE_RESTORE_PRIVILEGE;
  1700. privileges[2] = SE_SECURITY_PRIVILEGE;
  1701. privileges[3] = SE_TAKE_OWNERSHIP_PRIVILEGE;
  1702. status = ScGetPrivilege( 4, privileges);
  1703. if (status != NO_ERROR) {
  1704. SC_LOG1(ERROR, "RNotifyBootConfigStatus: ScGetPrivilege Failed %d\n",
  1705. status);
  1706. return(status);
  1707. }
  1708. //
  1709. // Get the System, Select, and Clone Keys
  1710. //
  1711. status = ScGetTopKeys(&systemKey, &selectKey);
  1712. if (status != NO_ERROR) {
  1713. SC_LOG0(ERROR,"ScAcceptTheBoot: ScGetTopKeys failed\n");
  1714. SetLastError(status);
  1715. //
  1716. // Restore privileges for the current thread.
  1717. //
  1718. (VOID)ScReleasePrivilege();
  1719. return(status);
  1720. }
  1721. //
  1722. // Get the ControlSetIds stored in the \system\select key.
  1723. //
  1724. status = ScGetCtrlSetIds(
  1725. selectKey,
  1726. idArray);
  1727. if (status != NO_ERROR) {
  1728. SC_LOG0(ERROR,"ScAcceptTheBoot: ScGetCtrlSetIds Failed\n");
  1729. goto CleanExit;
  1730. }
  1731. //
  1732. // Don't commit the LKG profile if this is safe mode, unless we actually
  1733. // booted into the LKG profile.
  1734. //
  1735. if (g_SafeBootEnabled) {
  1736. if (idArray[LKG_ID] != idArray[CURRENT_ID]) {
  1737. ScGlobalBootAccepted = TRUE;
  1738. status = NO_ERROR;
  1739. SC_LOG0(TRACE,"ScAcceptTheBoot: Safe mode boot, not committing LKG\n");
  1740. goto CleanExit;
  1741. }
  1742. }
  1743. //
  1744. // Scan for Orphaned Control Sets.
  1745. // This is required prior to calling ScMakeNewCtrlSet (which
  1746. // avoids the orphaned numbers).
  1747. //
  1748. ScGatherOrphanIds(systemKey,&ScGlobalOrphanIds,idArray);
  1749. //
  1750. // Delete the LastKnownGood ControlSet if there are no other
  1751. // references to that control set.
  1752. //
  1753. SC_LOG0(TRACE,"ScAcceptTheBoot: Delete LKG ControlSet if no ref\n");
  1754. if ( (idArray[LKG_ID] != idArray[FAILED_ID]) &&
  1755. (idArray[LKG_ID] != idArray[DEFAULT_ID]) &&
  1756. (idArray[LKG_ID] != idArray[CURRENT_ID])) {
  1757. newId = idArray[LKG_ID];
  1758. }
  1759. else
  1760. {
  1761. status = ScGetNewCtrlSetId(idArray, &newId);
  1762. if(status != NO_ERROR)
  1763. {
  1764. SC_LOG0(ERROR, "ScAcceptTheBoot: Could Not Get New Control Set Id.\n");
  1765. goto CleanExit;
  1766. }
  1767. }
  1768. //
  1769. // Accept the boot and save the boot configuration as LKG.
  1770. //
  1771. status = RtlNtStatusToDosError(NtInitializeRegistry(REG_INIT_BOOT_ACCEPTED_BASE +
  1772. (USHORT)newId));
  1773. if(status != NO_ERROR)
  1774. {
  1775. SC_LOG1(ERROR, "ScAcceptTheBoot: NtInitializeRegistry Failed with %d",
  1776. status);
  1777. goto CleanExit;
  1778. }
  1779. //
  1780. // Make this control set the LastKnownGood Control Set.
  1781. // This is the ControlSet that we last booted from.
  1782. //
  1783. if(newId != idArray[LKG_ID])
  1784. {
  1785. //
  1786. // We only need to do anything if we did not overwrite the old LKG
  1787. // with NtInitializeRegistry.
  1788. //
  1789. idArray[LKG_ID] = newId;
  1790. status = ScRegSetValueExW(
  1791. selectKey, // hKey
  1792. LKG_VALUE_NAME, // lpValueName
  1793. 0, // dwValueTitle (OPTIONAL)
  1794. REG_DWORD, // dwType
  1795. (LPBYTE)&(idArray[LKG_ID]), // lpData
  1796. sizeof(DWORD)); // cbData
  1797. if (status != NO_ERROR) {
  1798. SC_LOG1(ERROR,"ScAcceptTheBoot: ScRegSetValueEx (LkgValue) failed %d\n",
  1799. status);
  1800. goto CleanExit;
  1801. }
  1802. }
  1803. //
  1804. // Commit this boot by deleting anything we would undo since previous boot.
  1805. //
  1806. status = ScLastGoodFileCleanup();
  1807. if (status != NO_ERROR) {
  1808. SC_LOG1(ERROR,"ScAcceptTheBoot: LastGoodFileCleanup failed %d\n",
  1809. status);
  1810. goto CleanExit;
  1811. }
  1812. ScGlobalBootAccepted = TRUE;
  1813. status = NO_ERROR;
  1814. SC_LOG0(TRACE,"ScAcceptTheBoot: Done\n");
  1815. CleanExit:
  1816. if (systemKey != 0) {
  1817. ScRegCloseKey(systemKey);
  1818. }
  1819. if (selectKey != 0) {
  1820. ScRegCloseKey(selectKey);
  1821. }
  1822. //
  1823. // Restore privileges for the current thread.
  1824. //
  1825. (VOID)ScReleasePrivilege();
  1826. return(status);
  1827. }
  1828. BOOL
  1829. SetupInProgress(
  1830. HKEY SystemKey,
  1831. LPDWORD pdwOOBEMode OPTIONAL
  1832. )
  1833. /*++
  1834. Routine Description:
  1835. Checks a registry location to determine if Setup is in Progress.
  1836. \HKEY_LOCAL_MACHINE\System\Setup
  1837. value=DWORD SystemSetupInProgress
  1838. The value is cached so that the registry is examined only on the
  1839. first call to this function.
  1840. Arguments:
  1841. SystemKey - open handle to HKEY_LOCAL_MACHINE\System.
  1842. This is ignored in all except the first call to this function.
  1843. Return Value:
  1844. TRUE - If Setup is in progress
  1845. FALSE - If Setup isn't in progress
  1846. --*/
  1847. {
  1848. static DWORD TheValue=0xffffffff; // 0=false, 1=true,
  1849. // 0xffffffff=uninitialized
  1850. static DWORD IsOOBE;
  1851. DWORD status = NO_ERROR;
  1852. DWORD BytesRequired = sizeof(TheValue);
  1853. HKEY KeyHandle;
  1854. if (TheValue == 0xffffffff)
  1855. {
  1856. //
  1857. // First call
  1858. //
  1859. SC_ASSERT(SystemKey != NULL);
  1860. TheValue = 0;
  1861. IsOOBE = 0;
  1862. status = ScRegOpenKeyExW(
  1863. SystemKey,
  1864. SETUP_PROG_KEY,
  1865. 0L,
  1866. KEY_READ,
  1867. &KeyHandle);
  1868. if (status == NO_ERROR)
  1869. {
  1870. //
  1871. // There are two registry values that may be set here:
  1872. //
  1873. // 1. OobeInProgress -- if it exists and is non-zero,
  1874. // this is an OOBE boot.
  1875. //
  1876. // 2. SystemSetupInProgress -- if it exists and is
  1877. // non-zero AND it's not an OOBE boot, it's
  1878. // GUI-mode setup. If OOBE's in progress,
  1879. // don't even bother checking this one (it may
  1880. // or may not be set depending on whether we're
  1881. // in retail OOBE or mini-setup OOBE) and return
  1882. // FALSE from SetupInProgress (along with the
  1883. // appropriate OOBE value).
  1884. //
  1885. status = ScRegQueryValueExW(
  1886. KeyHandle,
  1887. REGSTR_VALUE_OOBEINPROGRESS,
  1888. NULL,
  1889. NULL,
  1890. (LPBYTE) &IsOOBE,
  1891. &BytesRequired);
  1892. if (IsOOBE != 0)
  1893. {
  1894. SC_ASSERT(status == NO_ERROR);
  1895. }
  1896. if (IsOOBE == 0)
  1897. {
  1898. status = ScRegQueryValueExW(
  1899. KeyHandle,
  1900. SETUP_PROG_VALUE_NAME,
  1901. NULL,
  1902. NULL,
  1903. (LPBYTE) &TheValue,
  1904. &BytesRequired);
  1905. if (TheValue != 0)
  1906. {
  1907. SC_ASSERT(status == NO_ERROR);
  1908. TheValue = 1;
  1909. }
  1910. }
  1911. ScRegCloseKey(KeyHandle);
  1912. }
  1913. }
  1914. SC_LOG(TRACE,"SetupInProgress = %d (0=FALSE,else TRUE)\n", TheValue);
  1915. if (pdwOOBEMode)
  1916. {
  1917. SC_LOG(TRACE, "SetupInProgress: OOBE mode = %d (0 = none, 1 = OOBE, 2 = SP install)\n", IsOOBE);
  1918. *pdwOOBEMode = IsOOBE;
  1919. }
  1920. return TheValue;
  1921. }