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.

902 lines
22 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. cmgquota.c
  5. Abstract:
  6. The module contains CM routines to support Global Quota
  7. Global Quota has little to do with NT's standard per-process/user
  8. quota system. Global Quota is waying of controlling the aggregate
  9. resource usage of the entire registry. It is used to manage space
  10. consumption by objects which user apps create, but which are persistent
  11. and therefore cannot be assigned to the quota of a user app.
  12. Global Quota prevents the registry from consuming all of paged
  13. pool, and indirectly controls how much disk it can consume.
  14. Like the release 1 file systems, a single app can fill all the
  15. space in the registry, but at least it cannot kill the system.
  16. Memory objects used for known short times and protected by
  17. serialization, or billable as quota objects, are not counted
  18. in the global quota.
  19. Author:
  20. Bryan M. Willman (bryanwi) 13-Jan-1993
  21. Revision History:
  22. Dragos C Sambotin (dragoss) 04-Nov-1999
  23. Charge quota only for bins in paged pool (volatile storage and bins crossing
  24. the CM_VIEW_SIZE boundary).
  25. --*/
  26. #include "cmp.h"
  27. VOID
  28. CmpSystemHiveHysteresisWorker(
  29. IN PVOID WorkItem
  30. );
  31. VOID
  32. CmpRaiseSelfHealWarningWorker(
  33. IN PVOID Arg
  34. );
  35. #ifdef ALLOC_PRAGMA
  36. #pragma alloc_text(PAGE,CmpClaimGlobalQuota)
  37. #pragma alloc_text(PAGE,CmpReleaseGlobalQuota)
  38. #pragma alloc_text(PAGE,CmpSetGlobalQuotaAllowed)
  39. #pragma alloc_text(PAGE,CmpQuotaWarningWorker)
  40. #pragma alloc_text(PAGE,CmQueryRegistryQuotaInformation)
  41. #pragma alloc_text(PAGE,CmSetRegistryQuotaInformation)
  42. #pragma alloc_text(PAGE,CmpCanGrowSystemHive)
  43. #pragma alloc_text(PAGE,CmpSystemQuotaWarningWorker)
  44. #pragma alloc_text(INIT,CmpComputeGlobalQuotaAllowed)
  45. #pragma alloc_text(PAGE,CmpSystemHiveHysteresisWorker)
  46. #pragma alloc_text(PAGE,CmpUpdateSystemHiveHysteresis)
  47. #pragma alloc_text(PAGE,CmRegisterSystemHiveLimitCallback)
  48. #pragma alloc_text(PAGE,CmpRaiseSelfHealWarning)
  49. #pragma alloc_text(PAGE,CmpRaiseSelfHealWarningForSystemHives)
  50. #pragma alloc_text(PAGE,CmpRaiseSelfHealWarningWorker)
  51. #endif
  52. //
  53. // Registry control values
  54. //
  55. #define CM_DEFAULT_RATIO (3)
  56. #define CM_LIMIT_RATIO(x) ((x / 10) * 8)
  57. #define CM_MINIMUM_GLOBAL_QUOTA (16 *1024 * 1024)
  58. //
  59. // Percent of used registry quota that triggers a hard error
  60. // warning popup.
  61. //
  62. #define CM_REGISTRY_WARNING_LEVEL (95)
  63. //
  64. // System hive hard quota limit
  65. //
  66. // For an x86 3GB system we set the limit at 12MB for now. Needs some MM changes before we
  67. // bump this up.
  68. // For an x86 non-3GB system, we set the limit at 1/4 of physical memory
  69. // For IA-64 we set the limit at 32MB
  70. //
  71. #define _200MB (200 *1024 * 1024)
  72. #if defined(_X86_)
  73. #define CM_SYSTEM_HIVE_LIMIT_SIZE (MmVirtualBias ? (12 * 1024 * 1024) : (min(MmNumberOfPhysicalPages / 4, _200MB >> PAGE_SHIFT) * PAGE_SIZE))
  74. #else
  75. #define CM_SYSTEM_HIVE_LIMIT_SIZE (32 * 1024 * 1024)
  76. #endif
  77. #define CM_SYSTEM_HIVE_WARNING_SIZE ((CM_SYSTEM_HIVE_LIMIT_SIZE*9)/10)
  78. extern ULONG CmRegistrySizeLimit;
  79. extern ULONG CmRegistrySizeLimitLength;
  80. extern ULONG CmRegistrySizeLimitType;
  81. extern ULONG MmSizeOfPagedPoolInBytes;
  82. //
  83. // Maximum number of bytes of Global Quota the registry may use.
  84. // Set to largest positive number for use in boot. Will be set down
  85. // based on pool and explicit registry values.
  86. //
  87. extern ULONG CmpGlobalQuota;
  88. extern ULONG CmpGlobalQuotaAllowed;
  89. //
  90. // Mark that will trigger the low-on-quota popup
  91. //
  92. extern ULONG CmpGlobalQuotaWarning;
  93. //
  94. // Indicate whether the popup has been triggered yet or not.
  95. //
  96. extern BOOLEAN CmpQuotaWarningPopupDisplayed;
  97. extern BOOLEAN CmpSystemQuotaWarningPopupDisplayed;
  98. //
  99. // GQ actually in use
  100. //
  101. extern ULONG CmpGlobalQuotaUsed;
  102. extern HIVE_LIST_ENTRY CmpMachineHiveList[];
  103. VOID
  104. CmQueryRegistryQuotaInformation(
  105. IN PSYSTEM_REGISTRY_QUOTA_INFORMATION RegistryQuotaInformation
  106. )
  107. /*++
  108. Routine Description:
  109. Returns the registry quota information
  110. Arguments:
  111. RegistryQuotaInformation - Supplies pointer to buffer that will return
  112. the registry quota information.
  113. Return Value:
  114. None.
  115. --*/
  116. {
  117. RegistryQuotaInformation->RegistryQuotaAllowed = CmpGlobalQuota;
  118. RegistryQuotaInformation->RegistryQuotaUsed = CmpGlobalQuotaUsed;
  119. RegistryQuotaInformation->PagedPoolSize = MmSizeOfPagedPoolInBytes;
  120. }
  121. VOID
  122. CmSetRegistryQuotaInformation(
  123. IN PSYSTEM_REGISTRY_QUOTA_INFORMATION RegistryQuotaInformation
  124. )
  125. /*++
  126. Routine Description:
  127. Sets the registry quota information. The caller is assumed to have
  128. completed the necessary security checks already.
  129. Arguments:
  130. RegistryQuotaInformation - Supplies pointer to buffer that provides
  131. the new registry quota information.
  132. Return Value:
  133. None.
  134. --*/
  135. {
  136. CmpGlobalQuota = RegistryQuotaInformation->RegistryQuotaAllowed;
  137. //
  138. // Sanity checks against insane values
  139. //
  140. if (CmpGlobalQuota > CM_WRAP_LIMIT) {
  141. CmpGlobalQuota = CM_WRAP_LIMIT;
  142. }
  143. if (CmpGlobalQuota < CM_MINIMUM_GLOBAL_QUOTA) {
  144. CmpGlobalQuota = CM_MINIMUM_GLOBAL_QUOTA;
  145. }
  146. //
  147. // Recompute the warning level
  148. //
  149. CmpGlobalQuotaWarning = CM_REGISTRY_WARNING_LEVEL * (CmpGlobalQuota / 100);
  150. CmpGlobalQuotaAllowed = CmpGlobalQuota;
  151. }
  152. VOID
  153. CmpQuotaWarningWorker(
  154. IN PVOID WorkItem
  155. )
  156. /*++
  157. Routine Description:
  158. Displays hard error popup that indicates the registry quota is
  159. running out.
  160. Arguments:
  161. WorkItem - Supplies pointer to the work item. This routine will
  162. free the work item.
  163. Return Value:
  164. None.
  165. --*/
  166. {
  167. NTSTATUS Status;
  168. ULONG Response;
  169. ExFreePool(WorkItem);
  170. Status = ExRaiseHardError(STATUS_REGISTRY_QUOTA_LIMIT,
  171. 0,
  172. 0,
  173. NULL,
  174. OptionOk,
  175. &Response);
  176. }
  177. BOOLEAN
  178. CmpClaimGlobalQuota(
  179. IN ULONG Size
  180. )
  181. /*++
  182. Routine Description:
  183. If CmpGlobalQuotaUsed + Size >= CmpGlobalQuotaAllowed, return
  184. false. Otherwise, increment CmpGlobalQuotaUsed, in effect claiming
  185. the requested GlobalQuota.
  186. Arguments:
  187. Size - number of bytes of GlobalQuota caller wants to claim
  188. Return Value:
  189. TRUE - Claim succeeded, and has been counted in Used GQ
  190. FALSE - Claim failed, nothing counted in GQ.
  191. --*/
  192. {
  193. #if 0
  194. //
  195. // We shouldn't come to this, unless we have leaks;
  196. // There is no quota anymore, remember?
  197. //
  198. LONG available;
  199. PWORK_QUEUE_ITEM WorkItem;
  200. //
  201. // compute available space, then see if size <. This prevents overflows.
  202. // Note that this must be signed. Since quota is not enforced until logon,
  203. // it is possible for the available bytes to be negative.
  204. //
  205. available = (LONG)CmpGlobalQuotaAllowed - (LONG)CmpGlobalQuotaUsed;
  206. if ((LONG)Size < available) {
  207. CmpGlobalQuotaUsed += Size;
  208. if ((CmpGlobalQuotaUsed > CmpGlobalQuotaWarning) &&
  209. (!CmpQuotaWarningPopupDisplayed) &&
  210. (ExReadyForErrors)) {
  211. //
  212. // Queue work item to display popup
  213. //
  214. WorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
  215. if (WorkItem != NULL) {
  216. CmpQuotaWarningPopupDisplayed = TRUE;
  217. ExInitializeWorkItem(WorkItem,
  218. CmpQuotaWarningWorker,
  219. WorkItem);
  220. ExQueueWorkItem(WorkItem, DelayedWorkQueue);
  221. }
  222. }
  223. return TRUE;
  224. } else {
  225. return FALSE;
  226. }
  227. #endif //0
  228. CmpGlobalQuotaUsed += Size;
  229. return TRUE;
  230. }
  231. VOID
  232. CmpReleaseGlobalQuota(
  233. IN ULONG Size
  234. )
  235. /*++
  236. Routine Description:
  237. If Size <= CmpGlobalQuotaUsed, then decrement it. Else BugCheck.
  238. Arguments:
  239. Size - number of bytes of GlobalQuota caller wants to release
  240. Return Value:
  241. NONE.
  242. --*/
  243. {
  244. if (Size > CmpGlobalQuotaUsed) {
  245. CM_BUGCHECK(REGISTRY_ERROR,QUOTA_ERROR,1,0,0);
  246. }
  247. CmpGlobalQuotaUsed -= Size;
  248. }
  249. VOID
  250. CmpComputeGlobalQuotaAllowed(
  251. VOID
  252. )
  253. /*++
  254. Routine Description:
  255. Compute CmpGlobalQuota based on:
  256. (a) Size of paged pool
  257. (b) Explicit user registry commands to set registry GQ
  258. Return Value:
  259. NONE.
  260. --*/
  261. {
  262. ULONG PagedLimit;
  263. PagedLimit = CM_LIMIT_RATIO(MmSizeOfPagedPoolInBytes);
  264. if ((CmRegistrySizeLimitLength != 4) ||
  265. (CmRegistrySizeLimitType != REG_DWORD) ||
  266. (CmRegistrySizeLimit == 0))
  267. {
  268. //
  269. // If no value at all, or value of wrong type, or set to
  270. // zero, use internally computed default
  271. //
  272. CmpGlobalQuota = MmSizeOfPagedPoolInBytes / CM_DEFAULT_RATIO;
  273. } else if (CmRegistrySizeLimit >= PagedLimit) {
  274. //
  275. // If more than computed upper bound, use computed upper bound
  276. //
  277. CmpGlobalQuota = PagedLimit;
  278. } else {
  279. //
  280. // Use the set size
  281. //
  282. CmpGlobalQuota = CmRegistrySizeLimit;
  283. }
  284. if (CmpGlobalQuota > CM_WRAP_LIMIT) {
  285. CmpGlobalQuota = CM_WRAP_LIMIT;
  286. }
  287. if (CmpGlobalQuota < CM_MINIMUM_GLOBAL_QUOTA) {
  288. CmpGlobalQuota = CM_MINIMUM_GLOBAL_QUOTA;
  289. }
  290. CmpGlobalQuotaWarning = CM_REGISTRY_WARNING_LEVEL * (CmpGlobalQuota / 100);
  291. return;
  292. }
  293. VOID
  294. CmpSetGlobalQuotaAllowed(
  295. VOID
  296. )
  297. /*++
  298. Routine Description:
  299. Enables registry quota
  300. NOTE: Do NOT put this in init segment, we call it after
  301. that code has been freed!
  302. Return Value:
  303. NONE.
  304. --*/
  305. {
  306. CmpGlobalQuotaAllowed = CmpGlobalQuota;
  307. }
  308. BOOLEAN
  309. CmpCanGrowSystemHive(
  310. IN PHHIVE Hive,
  311. IN ULONG NewLength
  312. )
  313. /*++
  314. Routine Description:
  315. Checks if the system hive is allowed to grow with the specified amount
  316. of data (using the hard quota limit on the system hive)
  317. Return Value:
  318. NONE.
  319. --*/
  320. {
  321. PCMHIVE CmHive;
  322. PWORK_QUEUE_ITEM WorkItem;
  323. PAGED_CODE();
  324. CmHive = (PCMHIVE)CONTAINING_RECORD(Hive,CMHIVE,Hive);
  325. if( CmHive != CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive ) {
  326. //
  327. // not the system hive, bail out
  328. //
  329. return TRUE;
  330. }
  331. // account for the header.
  332. NewLength += HBLOCK_SIZE;
  333. if( NewLength > CM_SYSTEM_HIVE_LIMIT_SIZE ) {
  334. //
  335. // this is bad; we may not be able to boot next time !!!
  336. //
  337. return FALSE;
  338. }
  339. if( (NewLength > CM_SYSTEM_HIVE_WARNING_SIZE) &&
  340. (!CmpSystemQuotaWarningPopupDisplayed) &&
  341. (ExReadyForErrors)
  342. ) {
  343. //
  344. // we're above the warning level, queue work item to display popup
  345. //
  346. WorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
  347. if (WorkItem != NULL) {
  348. CmpSystemQuotaWarningPopupDisplayed = TRUE;
  349. ExInitializeWorkItem(WorkItem,
  350. CmpSystemQuotaWarningWorker,
  351. WorkItem);
  352. ExQueueWorkItem(WorkItem, DelayedWorkQueue);
  353. }
  354. }
  355. return TRUE;
  356. }
  357. VOID
  358. CmpSystemQuotaWarningWorker(
  359. IN PVOID WorkItem
  360. )
  361. /*++
  362. Routine Description:
  363. Displays hard error popup that indicates the hard quota limit
  364. on the system hive is running out.
  365. Arguments:
  366. WorkItem - Supplies pointer to the work item. This routine will
  367. free the work item.
  368. Return Value:
  369. None.
  370. --*/
  371. {
  372. NTSTATUS Status;
  373. ULONG Response;
  374. ExFreePool(WorkItem);
  375. Status = ExRaiseHardError(STATUS_REGISTRY_QUOTA_LIMIT,
  376. 0,
  377. 0,
  378. NULL,
  379. OptionOk,
  380. &Response);
  381. }
  382. //
  383. // Pnp private API
  384. //
  385. ULONG CmpSystemHiveHysteresisLow = 0;
  386. ULONG CmpSystemHiveHysteresisHigh = 0;
  387. PVOID CmpSystemHiveHysteresisContext = NULL;
  388. PCM_HYSTERESIS_CALLBACK CmpSystemHiveHysteresisCallback = NULL;
  389. ULONG CmpSystemHiveHysteresisHitRatio = 0;
  390. BOOLEAN CmpSystemHiveHysteresisLowSeen = FALSE;
  391. BOOLEAN CmpSystemHiveHysteresisHighSeen = FALSE;
  392. VOID
  393. CmpSystemHiveHysteresisWorker(
  394. IN PVOID WorkItem
  395. )
  396. /*++
  397. Routine Description:
  398. Calls the hysteresis callback
  399. Arguments:
  400. WorkItem - Supplies pointer to the work item. This routine will
  401. free the work item.
  402. Return Value:
  403. None.
  404. --*/
  405. {
  406. PCM_HYSTERESIS_CALLBACK Callback;
  407. ExFreePool(WorkItem);
  408. Callback = CmpSystemHiveHysteresisCallback;
  409. if( Callback ) {
  410. (*Callback)(CmpSystemHiveHysteresisContext,CmpSystemHiveHysteresisHitRatio);
  411. }
  412. }
  413. VOID
  414. CmpUpdateSystemHiveHysteresis( PHHIVE Hive,
  415. ULONG NewLength,
  416. ULONG OldLength
  417. )
  418. {
  419. PCMHIVE CmHive;
  420. PWORK_QUEUE_ITEM WorkItem;
  421. ULONG CurrentRatio;
  422. BOOLEAN DoWorkItem = FALSE;
  423. PAGED_CODE();
  424. CmHive = (PCMHIVE)CONTAINING_RECORD(Hive,CMHIVE,Hive);
  425. if( (!CmpSystemHiveHysteresisCallback) || (CmHive != CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive) ) {
  426. //
  427. // not the system hive, bail out
  428. //
  429. return;
  430. }
  431. ASSERT( NewLength != OldLength );
  432. //
  433. // compute current ratio; acount for the header first
  434. //
  435. CurrentRatio = NewLength + HBLOCK_SIZE;
  436. CurrentRatio *= 100;
  437. CurrentRatio /= CM_SYSTEM_HIVE_LIMIT_SIZE;
  438. if( NewLength > OldLength ) {
  439. //
  440. // hive is growing
  441. //
  442. if( (CmpSystemHiveHysteresisHighSeen == FALSE) && (CurrentRatio > CmpSystemHiveHysteresisHigh) ) {
  443. //
  444. // we reached high; see if low has already been hit and queue work item
  445. //
  446. CmpSystemHiveHysteresisHighSeen = TRUE;
  447. if( TRUE == CmpSystemHiveHysteresisLowSeen ) {
  448. //
  449. // low to high; queue workitem
  450. //
  451. CmpSystemHiveHysteresisHitRatio = CurrentRatio;
  452. DoWorkItem = TRUE;
  453. }
  454. }
  455. } else {
  456. //
  457. // hive is shrinking
  458. //
  459. if( (FALSE == CmpSystemHiveHysteresisLowSeen) && (CurrentRatio < CmpSystemHiveHysteresisLow ) ) {
  460. //
  461. // we reached low; see if low has been hit and queue work item
  462. //
  463. CmpSystemHiveHysteresisLowSeen = TRUE;
  464. if( TRUE == CmpSystemHiveHysteresisHighSeen ) {
  465. //
  466. // high to low; queue workitem
  467. //
  468. CmpSystemHiveHysteresisHitRatio = CurrentRatio;
  469. DoWorkItem = TRUE;
  470. }
  471. }
  472. }
  473. if( DoWorkItem ) {
  474. ASSERT( CmpSystemHiveHysteresisLowSeen && CmpSystemHiveHysteresisHighSeen );
  475. WorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
  476. if (WorkItem != NULL) {
  477. ExInitializeWorkItem(WorkItem,
  478. CmpSystemHiveHysteresisWorker,
  479. WorkItem);
  480. ExQueueWorkItem(WorkItem, DelayedWorkQueue);
  481. }
  482. //
  483. // reset state so we can fire again later
  484. //
  485. CmpSystemHiveHysteresisLowSeen = FALSE;
  486. CmpSystemHiveHysteresisHighSeen = FALSE;
  487. }
  488. }
  489. ULONG
  490. CmRegisterSystemHiveLimitCallback(
  491. ULONG Low,
  492. ULONG High,
  493. PVOID Ref,
  494. PCM_HYSTERESIS_CALLBACK Callback
  495. )
  496. /*++
  497. Routine Description:
  498. This routine registers a hysteresis for the system hive limit ratio.
  499. We will call the callback :
  500. a. the system hive goes above High from below Low
  501. b. the system hive goes below Low from above High
  502. Arguments:
  503. Low, High - specifies the hysteresis
  504. Ref - Context to give back to the callback
  505. Callback - callback routine.
  506. Return Value:
  507. current ratio 0 - 100
  508. --*/
  509. {
  510. ULONG Length;
  511. PAGED_CODE();
  512. if( CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive ) {
  513. Length = CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive->Hive.BaseBlock->Length + HBLOCK_SIZE;
  514. Length *= 100;
  515. Length /= CM_SYSTEM_HIVE_LIMIT_SIZE;
  516. } else {
  517. Length = 0;
  518. }
  519. //
  520. // allow only one call per system uptime.
  521. //
  522. if( CmpSystemHiveHysteresisCallback == NULL ) {
  523. CmpSystemHiveHysteresisLow = Low;
  524. CmpSystemHiveHysteresisHigh = High;
  525. CmpSystemHiveHysteresisContext = Ref;
  526. CmpSystemHiveHysteresisCallback = Callback;
  527. //
  528. // set state vars
  529. //
  530. if( Length <= Low ) {
  531. CmpSystemHiveHysteresisLowSeen = TRUE;
  532. } else {
  533. CmpSystemHiveHysteresisLowSeen = FALSE;
  534. }
  535. if( Length >= High) {
  536. CmpSystemHiveHysteresisHighSeen = TRUE;
  537. } else {
  538. CmpSystemHiveHysteresisHighSeen = FALSE;
  539. }
  540. }
  541. return Length;
  542. }
  543. VOID
  544. CmpHysteresisTest(PVOID Ref, ULONG Level)
  545. {
  546. UNREFERENCED_PARAMETER (Ref);
  547. DbgPrint("CmpHysteresisTest called with level = %lu \n",Level);
  548. }
  549. LIST_ENTRY CmpSelfHealQueueListHead;
  550. FAST_MUTEX CmpSelfHealQueueLock;
  551. BOOLEAN CmpSelfHealWorkerActive = FALSE;
  552. #define LOCK_SELF_HEAL_QUEUE() ExAcquireFastMutex(&CmpSelfHealQueueLock)
  553. #define UNLOCK_SELF_HEAL_QUEUE() ExReleaseFastMutex(&CmpSelfHealQueueLock)
  554. typedef struct {
  555. PWORK_QUEUE_ITEM WorkItem;
  556. LIST_ENTRY SelfHealQueueListEntry;
  557. UNICODE_STRING HiveName;
  558. //
  559. // variable length; name goes here
  560. //
  561. } CM_SELF_HEAL_WORK_ITEM_PARAMETER, *PCM_SELF_HEAL_WORK_ITEM_PARAMETER;
  562. VOID
  563. CmpRaiseSelfHealWarningWorker(
  564. IN PVOID Arg
  565. )
  566. {
  567. PVOID ErrorParameters;
  568. ULONG ErrorResponse;
  569. PCM_SELF_HEAL_WORK_ITEM_PARAMETER Param;
  570. Param = (PCM_SELF_HEAL_WORK_ITEM_PARAMETER)Arg;
  571. ErrorParameters = &(Param->HiveName);
  572. ExRaiseHardError(
  573. STATUS_REGISTRY_HIVE_RECOVERED,
  574. 1,
  575. 1,
  576. (PULONG_PTR)&ErrorParameters,
  577. OptionOk,
  578. &ErrorResponse
  579. );
  580. //
  581. // free what we have allocated
  582. //
  583. ExFreePool(Param->WorkItem);
  584. ExFreePool(Param);
  585. //
  586. // see if there are other self heal warnings to be posted.
  587. //
  588. LOCK_SELF_HEAL_QUEUE();
  589. CmpSelfHealWorkerActive = FALSE;
  590. if( IsListEmpty(&CmpSelfHealQueueListHead) == FALSE ) {
  591. //
  592. // remove head and queue it.
  593. //
  594. Param = (PCM_SELF_HEAL_WORK_ITEM_PARAMETER)RemoveHeadList(&CmpSelfHealQueueListHead);
  595. Param = CONTAINING_RECORD(
  596. Param,
  597. CM_SELF_HEAL_WORK_ITEM_PARAMETER,
  598. SelfHealQueueListEntry
  599. );
  600. ExQueueWorkItem(Param->WorkItem, DelayedWorkQueue);
  601. CmpSelfHealWorkerActive = TRUE;
  602. }
  603. UNLOCK_SELF_HEAL_QUEUE();
  604. }
  605. VOID
  606. CmpRaiseSelfHealWarning(
  607. IN PUNICODE_STRING HiveName
  608. )
  609. /*++
  610. Routine Description:
  611. Raise a hard error informing the use the specified hive has been self healed and
  612. it might not be entirely consitent
  613. Arguments:
  614. Parameter - the hive name.
  615. Return Value:
  616. None.
  617. --*/
  618. {
  619. PCM_SELF_HEAL_WORK_ITEM_PARAMETER Param;
  620. PAGED_CODE();
  621. //
  622. // we're above the warning level, queue work item to display popup
  623. //
  624. Param = ExAllocatePool(NonPagedPool, sizeof(CM_SELF_HEAL_WORK_ITEM_PARAMETER) + HiveName->Length);
  625. if( Param ) {
  626. Param->WorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
  627. if(Param->WorkItem != NULL) {
  628. Param->HiveName.Length = Param->HiveName.MaximumLength = HiveName->Length;
  629. Param->HiveName.Buffer = (PWSTR)(((PUCHAR)Param) + sizeof(CM_SELF_HEAL_WORK_ITEM_PARAMETER));
  630. RtlCopyMemory(Param->HiveName.Buffer,HiveName->Buffer,HiveName->Length);
  631. ExInitializeWorkItem(Param->WorkItem,
  632. CmpRaiseSelfHealWarningWorker,
  633. Param);
  634. LOCK_SELF_HEAL_QUEUE();
  635. if( !CmpSelfHealWorkerActive ) {
  636. //
  637. // no work item currently; ok to queue one.
  638. //
  639. ExQueueWorkItem(Param->WorkItem, DelayedWorkQueue);
  640. CmpSelfHealWorkerActive = TRUE;
  641. } else {
  642. //
  643. // add it to the end of the list. It'll be picked up when the current work item
  644. // completes
  645. //
  646. InsertTailList(
  647. &CmpSelfHealQueueListHead,
  648. &(Param->SelfHealQueueListEntry)
  649. );
  650. }
  651. UNLOCK_SELF_HEAL_QUEUE();
  652. } else {
  653. ExFreePool(Param);
  654. }
  655. }
  656. }
  657. VOID
  658. CmpRaiseSelfHealWarningForSystemHives( )
  659. /*++
  660. Routine Description:
  661. Walks the system hivelist and raises a hard error in the event one of the hives has been self healed.
  662. Intended to be called after controlset has been saved, from inside NtInitializeRegistry
  663. (i.e. we have an UI available so it will not stop the machine).
  664. Arguments:
  665. Return Value:
  666. None.
  667. --*/
  668. {
  669. ULONG i;
  670. UNICODE_STRING Name;
  671. PAGED_CODE();
  672. for (i = 0; i < CM_NUMBER_OF_MACHINE_HIVES; i++) {
  673. if( !(CmpMachineHiveList[i].HHiveFlags & HIVE_VOLATILE) && (((PHHIVE)(CmpMachineHiveList[i].CmHive2))->BaseBlock->BootType & HBOOT_SELFHEAL) ) {
  674. RtlInitUnicodeString(
  675. &Name,
  676. CmpMachineHiveList[i].Name
  677. );
  678. CmpRaiseSelfHealWarning( &Name );
  679. }
  680. }
  681. }