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

744 lines
20 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. cmdown.c
  5. Abstract:
  6. This module cleans up all the memory used by CM.
  7. Author:
  8. Dragos C. Sambotin (dragoss) 21-Feb-00
  9. Environment:
  10. This routine is intended to be called at system shutdown
  11. in order to detect memory leaks. It is supposed to free
  12. all registry data that is not freed by CmShutdownSystem.
  13. Revision History:
  14. --*/
  15. #include "cmp.h"
  16. //
  17. // externals
  18. //
  19. extern LIST_ENTRY CmpHiveListHead;
  20. extern PUCHAR CmpStashBuffer;
  21. extern PCM_KEY_HASH *CmpCacheTable;
  22. extern ULONG CmpDelayedCloseSize;
  23. extern CM_DELAYED_CLOSE_ENTRY *CmpDelayedCloseTable;
  24. extern PCM_NAME_HASH *CmpNameCacheTable;
  25. extern BOOLEAN HvShutdownComplete;
  26. extern BOOLEAN CmFirstTime;
  27. extern HIVE_LIST_ENTRY CmpMachineHiveList[];
  28. VOID
  29. CmpFreeAllMemory(
  30. VOID
  31. );
  32. VOID
  33. CmpDereferenceNameControlBlockWithLock(
  34. PCM_NAME_CONTROL_BLOCK Ncb
  35. );
  36. VOID
  37. CmpDumpKeyBodyList(
  38. IN PCM_KEY_CONTROL_BLOCK kcb,
  39. IN PULONG Count
  40. );
  41. #ifdef CM_SAVE_KCB_CACHE
  42. VOID
  43. CmpSaveKcbCache(
  44. VOID
  45. );
  46. #endif //CM_SAVE_KCB_CACHE
  47. #ifdef ALLOC_PRAGMA
  48. #pragma alloc_text(PAGE,CmpFreeAllMemory)
  49. #pragma alloc_text(PAGE,CmShutdownSystem)
  50. #ifdef CM_SAVE_KCB_CACHE
  51. #pragma alloc_text(PAGE,CmpSaveKcbCache)
  52. #endif //CM_SAVE_KCB_CACHE
  53. #endif
  54. VOID
  55. CmpFreeAllMemory(
  56. VOID
  57. )
  58. /*++
  59. Routine Description:
  60. - All hives are freed
  61. - KCB table is freed
  62. - Name hash table is freed
  63. - delay close table is freed - question: We need to clean/free all delayed close KCBs
  64. - all notifications/postblocks-aso.
  65. * equivalent with MmReleaseAllMemory
  66. Arguments:
  67. Return Value:
  68. --*/
  69. {
  70. PCMHIVE CmHive;
  71. LONG i;
  72. PCM_KEY_CONTROL_BLOCK KeyControlBlock;
  73. PCM_DELAYED_CLOSE_ENTRY DelayedEntry;
  74. PLIST_ENTRY NotifyPtr;
  75. PCM_NOTIFY_BLOCK NotifyBlock;
  76. PCM_POST_BLOCK PostBlock;
  77. PCM_KEY_HASH Current;
  78. PLIST_ENTRY AnchorAddr;
  79. ULONG Count;
  80. BOOLEAN MessageDisplayed;
  81. //
  82. // Iterate through the list of the hives in the system
  83. //
  84. while (IsListEmpty(&CmpHiveListHead) == FALSE) {
  85. //
  86. // Remove the hive from the list
  87. //
  88. CmHive = (PCMHIVE)RemoveHeadList(&CmpHiveListHead);
  89. CmHive = (PCMHIVE)CONTAINING_RECORD(CmHive,
  90. CMHIVE,
  91. HiveList);
  92. //
  93. // close hive handles (the ones that are open)
  94. //
  95. for (i=0; i<HFILE_TYPE_MAX; i++) {
  96. // these should be closed by CmShutdownSystem
  97. ASSERT( CmHive->FileHandles[i] == NULL );
  98. /*
  99. if (CmHive->FileHandles[i] != NULL) {
  100. CmCloseHandle(CmHive->FileHandles[i]);
  101. CmHive->FileHandles[i] = NULL;
  102. }
  103. */ }
  104. //
  105. // free the hive lock and view lock
  106. //
  107. ASSERT( CmHive->HiveLock != NULL );
  108. ExFreePool(CmHive->HiveLock);
  109. ASSERT( CmHive->ViewLock != NULL );
  110. ExFreePool(CmHive->ViewLock);
  111. /*
  112. DRAGOSS: we don't want ot do that! rather, we want to detect why we still
  113. have notifications at this point!!!!
  114. //
  115. // free notify-related stuff
  116. //
  117. NotifyPtr = &(CmHive->NotifyList);
  118. NotifyPtr = NotifyPtr->Flink;
  119. while( NotifyPtr != NULL ) {
  120. NotifyBlock = CONTAINING_RECORD(NotifyPtr, CM_NOTIFY_BLOCK, HiveList);
  121. // free post blocks; we assume that all threads have been terminated at this point
  122. while (IsListEmpty(&(NotifyBlock->PostList)) == FALSE) {
  123. PostBlock = (PCM_POST_BLOCK)RemoveHeadList(&(NotifyBlock->PostList));
  124. PostBlock = CONTAINING_RECORD(PostBlock,
  125. CM_POST_BLOCK,
  126. NotifyList);
  127. if( PostBlock->PostKeyBody ) {
  128. ExFreePool(PostBlock->PostKeyBody);
  129. }
  130. if( IsMasterPostBlock(PostBlock) ) {
  131. //
  132. // this members are allocated only for master post blocks
  133. //
  134. switch (PostBlockType(PostBlock)) {
  135. case PostSynchronous:
  136. ExFreePool(PostBlock->u->Sync.SystemEvent);
  137. break;
  138. case PostAsyncUser:
  139. ExFreePool(PostBlock->u->AsyncUser.Apc);
  140. break;
  141. case PostAsyncKernel:
  142. break;
  143. }
  144. ExFreePool(PostBlock->u);
  145. }
  146. ExFreePool(PostBlock);
  147. }
  148. NotifyPtr = NotifyPtr->Flink;
  149. ExFreePool(NotifyBlock);
  150. }
  151. */
  152. //
  153. // Spew in the debugger the names of the keynodes having notifies still set
  154. //
  155. NotifyPtr = &(CmHive->NotifyList);
  156. NotifyPtr = NotifyPtr->Flink;
  157. MessageDisplayed = FALSE;
  158. while( NotifyPtr != NULL ) {
  159. NotifyBlock = CONTAINING_RECORD(NotifyPtr, CM_NOTIFY_BLOCK, HiveList);
  160. AnchorAddr = &(NotifyBlock->PostList);
  161. PostBlock = (PCM_POST_BLOCK)(NotifyBlock->PostList.Flink);
  162. //
  163. // walk through the list and spew the keynames and postblock types.
  164. //
  165. while ( PostBlock != (PCM_POST_BLOCK)AnchorAddr ) {
  166. PostBlock = CONTAINING_RECORD(PostBlock,
  167. CM_POST_BLOCK,
  168. NotifyList);
  169. if( PostBlock->PostKeyBody ) {
  170. if( MessageDisplayed == FALSE ){
  171. MessageDisplayed = TRUE;
  172. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Dumping untriggered notifications for hive (%lx) (%.*S) \n\n",CmHive,
  173. HBASE_NAME_ALLOC / sizeof(WCHAR),CmHive->Hive.BaseBlock->FileName);
  174. }
  175. switch (PostBlockType(PostBlock)) {
  176. case PostSynchronous:
  177. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Synchronous ");
  178. break;
  179. case PostAsyncUser:
  180. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"AsyncUser ");
  181. break;
  182. case PostAsyncKernel:
  183. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"AsyncKernel ");
  184. break;
  185. }
  186. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"Notification, PostBlock %p not triggered on KCB %p\n",PostBlock,
  187. PostBlock->PostKeyBody->KeyBody->KeyControlBlock);
  188. }
  189. //
  190. // skip to the next element
  191. //
  192. PostBlock = (PCM_POST_BLOCK)(PostBlock->NotifyList.Flink);
  193. }
  194. NotifyPtr = NotifyPtr->Flink;
  195. }
  196. //
  197. // free security cache
  198. //
  199. CmpDestroySecurityCache (CmHive);
  200. //
  201. // free the hv level structure
  202. //
  203. HvFreeHive(&(CmHive->Hive));
  204. //
  205. // free the cm level structure
  206. //
  207. CmpFree(CmHive, sizeof(CMHIVE));
  208. }
  209. //
  210. // Now free the CM globals
  211. //
  212. // the stash buffer
  213. if( CmpStashBuffer != NULL ) {
  214. ExFreePool( CmpStashBuffer );
  215. }
  216. //
  217. // first, take care of all delayed closed KCBs
  218. // free their memory and dereference all the related.
  219. // name, hint, KeyHash
  220. //
  221. for (i=0; i<(LONG)CmpDelayedCloseSize; i++) {
  222. DelayedEntry = &(CmpDelayedCloseTable[i]);
  223. if( DelayedEntry->KeyControlBlock == NULL ) {
  224. //
  225. // this is a free entry
  226. //
  227. continue;
  228. }
  229. KeyControlBlock = DelayedEntry->KeyControlBlock;
  230. ASSERT( KeyControlBlock->DelayedCloseIndex == i );
  231. ASSERT( KeyControlBlock->RefCount == 0 );
  232. //
  233. // this will take care of other stuff kcb is pointing on.
  234. //
  235. CmpCleanUpKcbCacheWithLock(KeyControlBlock);
  236. }
  237. //
  238. // Spew open handles and associated processes
  239. //
  240. Count = 0;
  241. MessageDisplayed = FALSE;
  242. for (i=0; i<(LONG)CmpHashTableSize; i++) {
  243. Current = CmpCacheTable[i];
  244. while (Current) {
  245. KeyControlBlock = CONTAINING_RECORD(Current, CM_KEY_CONTROL_BLOCK, KeyHash);
  246. if( MessageDisplayed == FALSE ){
  247. MessageDisplayed = TRUE;
  248. DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"\nDumping open handles : \n\n");
  249. }
  250. CmpDumpKeyBodyList(KeyControlBlock,&Count);
  251. Current = Current->NextHash;
  252. }
  253. }
  254. if( Count != 0 ) {
  255. //
  256. // there some open handles; bugcheck
  257. //
  258. CM_BUGCHECK( REGISTRY_ERROR,HANDLES_STILL_OPEN_AT_SHUTDOWN,1,Count,0);
  259. }
  260. //
  261. // in case of private alloc, free pages
  262. //
  263. CmpDestroyCmPrivateAlloc();
  264. //
  265. // For the 3 tables below, the objects actually pointed from inside
  266. // should be cleaned up (freed) at the last handle closure time
  267. // the related handles are closed
  268. //
  269. // KCB cache table
  270. ASSERT( CmpCacheTable != NULL );
  271. ExFreePool(CmpCacheTable);
  272. // NameCacheTable
  273. ASSERT( CmpNameCacheTable != NULL );
  274. ExFreePool( CmpNameCacheTable );
  275. // DelayedCloseTable
  276. ASSERT( CmpDelayedCloseTable != NULL );
  277. ExFreePool( CmpDelayedCloseTable );
  278. }
  279. #ifdef CMP_STATS
  280. VOID CmpKcbStatDpcRoutine(IN PKDPC Dpc,IN PVOID DeferredContext,IN PVOID SystemArgument1,IN PVOID SystemArgument2);
  281. #endif
  282. #ifdef CM_SAVE_KCB_CACHE
  283. #define CACHE_DMP_FILE_NAME L"Cache.dmp"
  284. VOID
  285. CmpSaveKcbCache(
  286. VOID
  287. )
  288. /*++
  289. Routine Description:
  290. Saves the content of the kcb cache to \system32\config\cache.dmp
  291. Format of the file:
  292. [ULONG] NumberOfKeys
  293. [ULONG] Length
  294. [WCHAR*Length] Path
  295. [ULONG] Length
  296. [WCHAR*Length] Path
  297. [ULONG] Length
  298. [WCHAR*Length] Path
  299. [ULONG] Length
  300. [WCHAR*Length] Path
  301. [.................]
  302. Arguments:
  303. NONE
  304. Return Value:
  305. NONE
  306. --*/
  307. {
  308. UCHAR FileBuffer[MAX_NAME];
  309. UNICODE_STRING FileName;
  310. UNICODE_STRING TempName;
  311. HANDLE FileHandle;
  312. NTSTATUS Status;
  313. OBJECT_ATTRIBUTES ObjectAttributes;
  314. IO_STATUS_BLOCK IoStatus;
  315. ULONG KcbNo = 0;
  316. LARGE_INTEGER Offset;
  317. ULONG FileOffset;
  318. ULONG i;
  319. PCM_KEY_CONTROL_BLOCK KeyControlBlock;
  320. PCM_KEY_HASH Current;
  321. PUNICODE_STRING Name;
  322. ULONG Tmp;
  323. PCM_DELAYED_CLOSE_ENTRY DelayedEntry;
  324. PAGED_CODE();
  325. //
  326. // first, open the file.
  327. //
  328. FileName.MaximumLength = MAX_NAME;
  329. FileName.Length = 0;
  330. FileName.Buffer = (PWSTR)&(FileBuffer[0]);
  331. RtlInitUnicodeString(
  332. &TempName,
  333. INIT_SYSTEMROOT_HIVEPATH
  334. );
  335. RtlAppendStringToString((PSTRING)&FileName, (PSTRING)&TempName);
  336. RtlInitUnicodeString(
  337. &TempName,
  338. CACHE_DMP_FILE_NAME
  339. );
  340. RtlAppendStringToString((PSTRING)&FileName, (PSTRING)&TempName);
  341. InitializeObjectAttributes(
  342. &ObjectAttributes,
  343. &FileName,
  344. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  345. NULL,
  346. NULL
  347. );
  348. ASSERT_PASSIVE_LEVEL();
  349. Status = ZwCreateFile(
  350. &FileHandle,
  351. FILE_READ_DATA | FILE_WRITE_DATA,
  352. &ObjectAttributes,
  353. &IoStatus,
  354. NULL, // alloc size = none
  355. FILE_ATTRIBUTE_NORMAL,
  356. 0, // share nothing
  357. FILE_OPEN_IF,
  358. FILE_RANDOM_ACCESS,
  359. NULL, // eabuffer
  360. 0 // ealength
  361. );
  362. if( !NT_SUCCESS(Status) ) {
  363. // bad luck
  364. return;
  365. }
  366. //
  367. // write the number of kcbs (we'll rewrite it at the end).
  368. //
  369. Offset.LowPart = FileOffset = 0;
  370. Offset.HighPart = 0L;
  371. Status = ZwWriteFile(FileHandle,
  372. NULL,
  373. NULL,
  374. NULL,
  375. &IoStatus,
  376. &KcbNo,
  377. sizeof(ULONG),
  378. &Offset,
  379. NULL);
  380. if( !NT_SUCCESS(Status) ) {
  381. goto Exit;
  382. }
  383. FileOffset = Offset.LowPart + sizeof(ULONG);
  384. //
  385. // iterate through the cache and dump all kcbs
  386. //
  387. for (i=0; i<CmpHashTableSize; i++) {
  388. Current = CmpCacheTable[i];
  389. while (Current) {
  390. KeyControlBlock = CONTAINING_RECORD(Current, CM_KEY_CONTROL_BLOCK, KeyHash);
  391. Name = CmpConstructName(KeyControlBlock);
  392. if( Name ){
  393. Tmp = (ULONG)Name->Length;
  394. //
  395. // write off the length
  396. //
  397. Offset.LowPart = FileOffset;
  398. Status = ZwWriteFile(FileHandle,
  399. NULL,
  400. NULL,
  401. NULL,
  402. &IoStatus,
  403. &Tmp,
  404. sizeof(ULONG),
  405. &Offset,
  406. NULL);
  407. if( !NT_SUCCESS(Status) ) {
  408. goto Exit;
  409. }
  410. FileOffset = Offset.LowPart + sizeof(ULONG);
  411. //
  412. // and the buffer
  413. //
  414. Offset.LowPart = FileOffset;
  415. Status = ZwWriteFile(FileHandle,
  416. NULL,
  417. NULL,
  418. NULL,
  419. &IoStatus,
  420. Name->Buffer,
  421. Tmp,
  422. &Offset,
  423. NULL);
  424. if( !NT_SUCCESS(Status) ) {
  425. goto Exit;
  426. }
  427. FileOffset = Offset.LowPart + Tmp;
  428. //
  429. // record a new kcb and free the name
  430. //
  431. KcbNo++;
  432. ExFreePoolWithTag(Name, CM_NAME_TAG | PROTECTED_POOL);
  433. }
  434. Current = Current->NextHash;
  435. }
  436. }
  437. //
  438. // then, take care of all delayed closed KCBs
  439. //
  440. for (i=0; i<CmpDelayedCloseSize; i++) {
  441. DelayedEntry = &(CmpDelayedCloseTable[i]);
  442. if( DelayedEntry->KeyControlBlock == NULL ) {
  443. //
  444. // this is a free entry
  445. //
  446. continue;
  447. }
  448. KeyControlBlock = DelayedEntry->KeyControlBlock;
  449. ASSERT( KeyControlBlock->DelayedCloseIndex == i );
  450. ASSERT( KeyControlBlock->RefCount == 0 );
  451. Name = CmpConstructName(KeyControlBlock);
  452. if( Name ){
  453. Tmp = (ULONG)Name->Length;
  454. //
  455. // write off the length
  456. //
  457. Offset.LowPart = FileOffset;
  458. Status = ZwWriteFile(FileHandle,
  459. NULL,
  460. NULL,
  461. NULL,
  462. &IoStatus,
  463. &Tmp,
  464. sizeof(ULONG),
  465. &Offset,
  466. NULL);
  467. if( !NT_SUCCESS(Status) ) {
  468. goto Exit;
  469. }
  470. FileOffset = Offset.LowPart + sizeof(ULONG);
  471. //
  472. // and the buffer
  473. //
  474. Offset.LowPart = FileOffset;
  475. Status = ZwWriteFile(FileHandle,
  476. NULL,
  477. NULL,
  478. NULL,
  479. &IoStatus,
  480. Name->Buffer,
  481. Tmp,
  482. &Offset,
  483. NULL);
  484. if( !NT_SUCCESS(Status) ) {
  485. goto Exit;
  486. }
  487. FileOffset = Offset.LowPart + Tmp;
  488. //
  489. // record a new kcb and free the name
  490. //
  491. KcbNo++;
  492. ExFreePoolWithTag(Name, CM_NAME_TAG | PROTECTED_POOL);
  493. }
  494. }
  495. //
  496. // write the number of kcbs
  497. //
  498. Offset.LowPart = 0;
  499. Status = ZwWriteFile(FileHandle,
  500. NULL,
  501. NULL,
  502. NULL,
  503. &IoStatus,
  504. &KcbNo,
  505. sizeof(ULONG),
  506. &Offset,
  507. NULL);
  508. if( !NT_SUCCESS(Status) ) {
  509. goto Exit;
  510. }
  511. ZwFlushBuffersFile(
  512. FileHandle,
  513. &IoStatus
  514. );
  515. Exit:
  516. CmCloseHandle(FileHandle);
  517. }
  518. #endif //CM_SAVE_KCB_CACHE
  519. VOID
  520. CmShutdownSystem(
  521. VOID
  522. )
  523. /*++
  524. Routine Description:
  525. Shuts down the registry.
  526. Arguments:
  527. NONE
  528. Return Value:
  529. NONE
  530. --*/
  531. {
  532. PLIST_ENTRY p;
  533. PCMHIVE CmHive;
  534. NTSTATUS Status;
  535. PVOID RegistryRoot;
  536. PAGED_CODE();
  537. if (CmpRegistryRootHandle) {
  538. Status = ObReferenceObjectByHandle(CmpRegistryRootHandle,
  539. KEY_READ,
  540. NULL,
  541. KernelMode,
  542. &RegistryRoot,
  543. NULL);
  544. if (NT_SUCCESS(Status)) {
  545. // We want to dereference the object twice -- once for the
  546. // reference we just made, and once for the reference
  547. // fromCmpCreateRegistryRoot.
  548. ObDereferenceObject(RegistryRoot);
  549. ObDereferenceObject(RegistryRoot);
  550. }
  551. ObCloseHandle(CmpRegistryRootHandle, KernelMode);
  552. }
  553. CmpLockRegistryExclusive();
  554. //
  555. // Stop the workers; only if registry has been inited
  556. //
  557. if( CmFirstTime == FALSE ) {
  558. CmpShutdownWorkers();
  559. }
  560. //
  561. // shut down the registry
  562. //
  563. CmpDoFlushAll(TRUE);
  564. //
  565. // try to compress the system hive
  566. //
  567. CmCompressKey( &(CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive->Hive) );
  568. #ifdef CM_SAVE_KCB_CACHE
  569. //
  570. // dump the cache for perf warm-up at next boot
  571. //
  572. CmpSaveKcbCache();
  573. #endif //CM_SAVE_KCB_CACHE
  574. //
  575. // close all the hive files
  576. //
  577. p = CmpHiveListHead.Flink;
  578. while(p != &CmpHiveListHead) {
  579. CmHive = CONTAINING_RECORD(p, CMHIVE, HiveList);
  580. //
  581. // we need to unmap all views mapped for this hive first
  582. //
  583. CmpDestroyHiveViewList(CmHive);
  584. //
  585. // dereference the fileobject (if any).
  586. //
  587. CmpDropFileObjectForHive(CmHive);
  588. //
  589. // now we can safely close all the handles
  590. //
  591. CmpCmdHiveClose(CmHive);
  592. p=p->Flink;
  593. }
  594. #ifdef CMP_STATS
  595. // last chance to dump statistics
  596. if( CmFirstTime == FALSE ) {
  597. CmpKcbStatDpcRoutine(NULL,NULL,NULL,NULL);
  598. }
  599. #endif
  600. HvShutdownComplete = TRUE; // Tell HvSyncHive to ignore all
  601. // further requests
  602. if((PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_REGISTRY) && (CmFirstTime == FALSE)){
  603. //
  604. // Free aux memory used internally by CM
  605. //
  606. CmpFreeAllMemory();
  607. }
  608. CmpUnlockRegistry();
  609. return;
  610. }