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.

5229 lines
155 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. cmapi.c
  5. Abstract:
  6. This module contains CM level entry points for the registry.
  7. Author:
  8. Bryan M. Willman (bryanwi) 30-Aug-1991
  9. Revision History:
  10. --*/
  11. #include "cmp.h"
  12. extern BOOLEAN CmpNoWrite;
  13. extern LIST_ENTRY CmpHiveListHead;
  14. extern BOOLEAN CmpProfileLoaded;
  15. extern BOOLEAN CmpWasSetupBoot;
  16. extern UNICODE_STRING CmSymbolicLinkValueName;
  17. extern ULONG CmpGlobalQuotaAllowed;
  18. extern ULONG CmpGlobalQuotaWarning;
  19. extern PCMHIVE CmpMasterHive;
  20. extern HIVE_LIST_ENTRY CmpMachineHiveList[];
  21. VOID
  22. CmpDereferenceNameControlBlockWithLock(
  23. PCM_NAME_CONTROL_BLOCK Ncb
  24. );
  25. //
  26. // procedures private to this file
  27. //
  28. NTSTATUS
  29. CmpSetValueKeyExisting(
  30. IN PHHIVE Hive,
  31. IN HCELL_INDEX OldChild,
  32. IN PCM_KEY_VALUE Value,
  33. IN ULONG Type,
  34. IN PVOID Data,
  35. IN ULONG DataSize,
  36. IN ULONG StorageType,
  37. IN ULONG TempData
  38. );
  39. NTSTATUS
  40. CmpSetValueKeyNew(
  41. IN PHHIVE Hive,
  42. IN PCM_KEY_NODE Parent,
  43. IN PUNICODE_STRING ValueName,
  44. IN ULONG Index,
  45. IN ULONG Type,
  46. IN PVOID Data,
  47. IN ULONG DataSize,
  48. IN ULONG StorageType,
  49. IN ULONG TempData
  50. );
  51. VOID
  52. CmpRemoveKeyHash(
  53. IN PCM_KEY_HASH KeyHash
  54. );
  55. PCM_KEY_CONTROL_BLOCK
  56. CmpInsertKeyHash(
  57. IN PCM_KEY_HASH KeyHash,
  58. IN BOOLEAN FakeKey
  59. );
  60. #if DBG
  61. ULONG
  62. CmpUnloadKeyWorker(
  63. PCM_KEY_CONTROL_BLOCK Current,
  64. PVOID Context1,
  65. PVOID Context2
  66. );
  67. #endif
  68. ULONG
  69. CmpCompressKeyWorker(
  70. PCM_KEY_CONTROL_BLOCK Current,
  71. PVOID Context1,
  72. PVOID Context2
  73. );
  74. NTSTATUS
  75. CmpDuplicateKey(
  76. PHHIVE Hive,
  77. HCELL_INDEX OldKeyCell,
  78. PHCELL_INDEX NewKeyCell
  79. );
  80. VOID
  81. CmpDestroyTemporaryHive(
  82. PCMHIVE CmHive
  83. );
  84. BOOLEAN
  85. CmpCompareNewValueDataAgainstKCBCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  86. PUNICODE_STRING ValueName,
  87. ULONG Type,
  88. PVOID Data,
  89. ULONG DataSize
  90. );
  91. BOOLEAN
  92. CmpGetValueDataFromCache(
  93. IN PHHIVE Hive,
  94. IN PPCM_CACHED_VALUE ContainingList,
  95. IN PCELL_DATA ValueKey,
  96. IN BOOLEAN ValueCached,
  97. OUT PUCHAR *DataPointer,
  98. OUT PBOOLEAN Allocated,
  99. OUT PHCELL_INDEX CellToRelease
  100. );
  101. BOOLEAN
  102. CmpCompareNewValueDataAgainstKCBCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  103. PUNICODE_STRING ValueName,
  104. ULONG Type,
  105. PVOID Data,
  106. ULONG DataSize
  107. );
  108. BOOLEAN
  109. CmpGetValueDataFromCache(
  110. IN PHHIVE Hive,
  111. IN PPCM_CACHED_VALUE ContainingList,
  112. IN PCELL_DATA ValueKey,
  113. IN BOOLEAN ValueCached,
  114. OUT PUCHAR *DataPointer,
  115. OUT PBOOLEAN Allocated,
  116. OUT PHCELL_INDEX CellToRelease
  117. );
  118. BOOLEAN
  119. CmpIsHiveAlreadyLoaded( IN HANDLE KeyHandle,
  120. IN POBJECT_ATTRIBUTES SourceFile,
  121. OUT PCMHIVE *CmHive
  122. );
  123. NTSTATUS
  124. static
  125. __forceinline
  126. CmpCheckReplaceHive( IN PHHIVE Hive,
  127. OUT PHCELL_INDEX Key
  128. );
  129. BOOLEAN
  130. CmpDoFlushNextHive(
  131. BOOLEAN ForceFlush,
  132. PBOOLEAN PostWarning,
  133. PULONG DirtyCount
  134. );
  135. #ifdef ALLOC_PRAGMA
  136. #pragma alloc_text(PAGE,CmDeleteValueKey)
  137. #pragma alloc_text(PAGE,CmEnumerateKey)
  138. #pragma alloc_text(PAGE,CmEnumerateValueKey)
  139. #pragma alloc_text(PAGE,CmFlushKey)
  140. #pragma alloc_text(PAGE,CmQueryKey)
  141. #pragma alloc_text(PAGE,CmQueryValueKey)
  142. #pragma alloc_text(PAGE,CmQueryMultipleValueKey)
  143. #pragma alloc_text(PAGE,CmSetValueKey)
  144. #pragma alloc_text(PAGE,CmpSetValueKeyExisting)
  145. #pragma alloc_text(PAGE,CmpSetValueKeyNew)
  146. #pragma alloc_text(PAGE,CmSetLastWriteTimeKey)
  147. #pragma alloc_text(PAGE,CmSetKeyUserFlags)
  148. #pragma alloc_text(PAGE,CmLoadKey)
  149. #pragma alloc_text(PAGE,CmUnloadKey)
  150. #ifdef NT_UNLOAD_KEY_EX
  151. #pragma alloc_text(PAGE,CmUnloadKeyEx)
  152. #endif //NT_UNLOAD_KEY_EX
  153. #pragma alloc_text(PAGE,CmpDoFlushAll)
  154. #pragma alloc_text(PAGE,CmpDoFlushNextHive)
  155. #pragma alloc_text(PAGE,CmReplaceKey)
  156. #ifdef WRITE_PROTECTED_REGISTRY_POOL
  157. #pragma alloc_text(PAGE,CmpMarkAllBinsReadOnly)
  158. #endif //WRITE_PROTECTED_REGISTRY_POOL
  159. #ifdef NT_RENAME_KEY
  160. #pragma alloc_text(PAGE,CmRenameKey)
  161. #endif //NT_RENAME_KEY
  162. #pragma alloc_text(PAGE,CmLockKcbForWrite)
  163. #if DBG
  164. #pragma alloc_text(PAGE,CmpUnloadKeyWorker)
  165. #endif
  166. #pragma alloc_text(PAGE,CmMoveKey)
  167. #pragma alloc_text(PAGE,CmpDuplicateKey)
  168. #pragma alloc_text(PAGE,CmCompressKey)
  169. #pragma alloc_text(PAGE,CmpCompressKeyWorker)
  170. #pragma alloc_text(PAGE,CmpCompareNewValueDataAgainstKCBCache)
  171. #pragma alloc_text(PAGE,CmpIsHiveAlreadyLoaded)
  172. #pragma alloc_text(PAGE,CmpCheckReplaceHive)
  173. #endif
  174. NTSTATUS
  175. CmDeleteValueKey(
  176. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  177. IN UNICODE_STRING ValueName // RAW
  178. )
  179. /*++
  180. Routine Description:
  181. One of the value entries of a registry key may be removed with this call.
  182. The value entry with ValueName matching ValueName is removed from the key.
  183. If no such entry exists, an error is returned.
  184. Arguments:
  185. KeyControlBlock - pointer to kcb for key to operate on
  186. ValueName - The name of the value to be deleted. NULL is a legal name.
  187. Return Value:
  188. NTSTATUS - Result code from call, among the following:
  189. <TBS>
  190. --*/
  191. {
  192. NTSTATUS status;
  193. PCM_KEY_NODE pcell = NULL;
  194. PCHILD_LIST plist;
  195. PCM_KEY_VALUE Value = NULL;
  196. ULONG targetindex;
  197. HCELL_INDEX ChildCell;
  198. PHHIVE Hive;
  199. HCELL_INDEX Cell;
  200. LARGE_INTEGER systemtime;
  201. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmDeleteValueKey\n"));
  202. status = STATUS_OBJECT_NAME_NOT_FOUND;
  203. ChildCell = HCELL_NIL;
  204. CmpLockRegistryExclusive();
  205. #ifdef CHECK_REGISTRY_USECOUNT
  206. CmpCheckRegistryUseCount();
  207. #endif //CHECK_REGISTRY_USECOUNT
  208. PERFINFO_REG_DELETE_VALUE(KeyControlBlock, &ValueName);
  209. //
  210. // no edits, not even this one, on keys marked for deletion
  211. //
  212. if (KeyControlBlock->Delete) {
  213. #ifdef CHECK_REGISTRY_USECOUNT
  214. CmpCheckRegistryUseCount();
  215. #endif //CHECK_REGISTRY_USECOUNT
  216. CmpUnlockRegistry();
  217. // Mark the hive as read only
  218. CmpMarkAllBinsReadOnly(Hive);
  219. return STATUS_KEY_DELETED;
  220. }
  221. Hive = KeyControlBlock->KeyHive;
  222. Cell = KeyControlBlock->KeyCell;
  223. try {
  224. pcell = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
  225. if( pcell == NULL ) {
  226. //
  227. // we couldn't map a view for the bin containing this cell
  228. //
  229. status = STATUS_INSUFFICIENT_RESOURCES;
  230. leave;
  231. }
  232. // Mark the hive as read only
  233. CmpMarkAllBinsReadOnly(Hive);
  234. plist = &(pcell->ValueList);
  235. if (plist->Count != 0) {
  236. //
  237. // The parent has at least one value, map in the list of
  238. // values and call CmpFindChildInList
  239. //
  240. //
  241. // plist -> the CHILD_LIST structure
  242. // pchild -> the child node structure being examined
  243. //
  244. if( CmpFindNameInList(Hive,
  245. plist,
  246. &ValueName,
  247. &targetindex,
  248. &ChildCell) == FALSE ) {
  249. // Mark the hive as read only
  250. CmpMarkAllBinsReadOnly(Hive);
  251. status = STATUS_INSUFFICIENT_RESOURCES;
  252. leave;
  253. }
  254. if (ChildCell != HCELL_NIL) {
  255. //
  256. // 1. the desired target was found
  257. // 2. ChildCell is it's HCELL_INDEX
  258. // 3. targetaddress points to it
  259. // 4. targetindex is it's index
  260. //
  261. //
  262. // attempt to mark all relevent cells dirty
  263. //
  264. if (!(HvMarkCellDirty(Hive, Cell) &&
  265. HvMarkCellDirty(Hive, pcell->ValueList.List) &&
  266. HvMarkCellDirty(Hive, ChildCell)))
  267. {
  268. // Mark the hive as read only
  269. CmpMarkAllBinsReadOnly(Hive);
  270. status = STATUS_NO_LOG_SPACE;
  271. leave;
  272. }
  273. Value = (PCM_KEY_VALUE)HvGetCell(Hive,ChildCell);
  274. if( Value == NULL ) {
  275. //
  276. // could not map view inside
  277. // this is impossible as we just dirtied the view
  278. //
  279. ASSERT( FALSE );
  280. // Mark the hive as read only
  281. CmpMarkAllBinsReadOnly(Hive);
  282. status = STATUS_INSUFFICIENT_RESOURCES;
  283. leave;
  284. }
  285. if( !CmpMarkValueDataDirty(Hive,Value) ) {
  286. // Mark the hive as read only
  287. CmpMarkAllBinsReadOnly(Hive);
  288. status = STATUS_NO_LOG_SPACE;
  289. leave;
  290. }
  291. // sanity
  292. ASSERT_CELL_DIRTY(Hive,pcell->ValueList.List);
  293. ASSERT_CELL_DIRTY(Hive,ChildCell);
  294. if( !NT_SUCCESS(CmpRemoveValueFromList(Hive,targetindex,plist)) ) {
  295. //
  296. // bail out !
  297. //
  298. status = STATUS_INSUFFICIENT_RESOURCES;
  299. leave;
  300. }
  301. if( CmpFreeValue(Hive, ChildCell) == FALSE ) {
  302. //
  303. // we couldn't map a view inside above call
  304. //
  305. status = STATUS_INSUFFICIENT_RESOURCES;
  306. leave;
  307. }
  308. KeQuerySystemTime(&systemtime);
  309. pcell->LastWriteTime = systemtime;
  310. // cache it in the kcb too.
  311. KeyControlBlock->KcbLastWriteTime = systemtime;
  312. // some sanity asserts
  313. ASSERT( pcell->MaxValueNameLen == KeyControlBlock->KcbMaxValueNameLen );
  314. ASSERT( pcell->MaxValueDataLen == KeyControlBlock->KcbMaxValueDataLen );
  315. ASSERT_CELL_DIRTY(Hive,Cell);
  316. if (pcell->ValueList.Count == 0) {
  317. pcell->MaxValueNameLen = 0;
  318. pcell->MaxValueDataLen = 0;
  319. // update the kcb cache too
  320. KeyControlBlock->KcbMaxValueNameLen = 0;
  321. KeyControlBlock->KcbMaxValueDataLen = 0;
  322. }
  323. //
  324. // We are changing the KCB cache. Since the registry is locked exclusively,
  325. // we do not need a KCB lock.
  326. //
  327. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  328. //
  329. // Invalidate and rebuild the cache
  330. //
  331. CmpCleanUpKcbValueCache(KeyControlBlock);
  332. CmpSetUpKcbValueCache(KeyControlBlock,plist->Count,plist->List);
  333. CmpReportNotify(
  334. KeyControlBlock,
  335. KeyControlBlock->KeyHive,
  336. KeyControlBlock->KeyCell,
  337. REG_NOTIFY_CHANGE_LAST_SET
  338. );
  339. status = STATUS_SUCCESS;
  340. } else {
  341. status = STATUS_OBJECT_NAME_NOT_FOUND;
  342. }
  343. }
  344. } finally {
  345. if(pcell != NULL){
  346. HvReleaseCell(Hive, Cell);
  347. }
  348. if(Value != NULL){
  349. ASSERT( ChildCell != HCELL_NIL );
  350. HvReleaseCell(Hive, ChildCell);
  351. }
  352. #ifdef CHECK_REGISTRY_USECOUNT
  353. CmpCheckRegistryUseCount();
  354. #endif //CHECK_REGISTRY_USECOUNT
  355. CmpUnlockRegistry();
  356. }
  357. // Mark the hive as read only
  358. CmpMarkAllBinsReadOnly(Hive);
  359. return status;
  360. }
  361. NTSTATUS
  362. CmEnumerateKey(
  363. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  364. IN ULONG Index,
  365. IN KEY_INFORMATION_CLASS KeyInformationClass,
  366. IN PVOID KeyInformation,
  367. IN ULONG Length,
  368. IN PULONG ResultLength
  369. )
  370. /*++
  371. Routine Description:
  372. Enumerate sub keys, return data on Index'th entry.
  373. CmEnumerateKey returns the name of the Index'th sub key of the open
  374. key specified. The value STATUS_NO_MORE_ENTRIES will be
  375. returned if value of Index is larger than the number of sub keys.
  376. Note that Index is simply a way to select among child keys. Two calls
  377. to CmEnumerateKey with the same Index are NOT guaranteed to return
  378. the same results.
  379. If KeyInformation is not long enough to hold all requested data,
  380. STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
  381. set to the number of bytes actually required.
  382. Arguments:
  383. KeyControlBlock - pointer to the KCB that describes the key
  384. Index - Specifies the (0-based) number of the sub key to be returned.
  385. KeyInformationClass - Specifies the type of information returned in
  386. Buffer. One of the following types:
  387. KeyBasicInformation - return last write time, title index, and name.
  388. (see KEY_BASIC_INFORMATION structure)
  389. KeyNodeInformation - return last write time, title index, name, class.
  390. (see KEY_NODE_INFORMATION structure)
  391. KeyInformation -Supplies pointer to buffer to receive the data.
  392. Length - Length of KeyInformation in bytes.
  393. ResultLength - Number of bytes actually written into KeyInformation.
  394. Return Value:
  395. NTSTATUS - Result code from call, among the following:
  396. <TBS>
  397. --*/
  398. {
  399. NTSTATUS status;
  400. HCELL_INDEX childcell;
  401. PHHIVE Hive;
  402. HCELL_INDEX Cell;
  403. PCM_KEY_NODE Node;
  404. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmEnumerateKey\n"));
  405. CmpLockRegistry();
  406. PERFINFO_REG_ENUM_KEY(KeyControlBlock, Index);
  407. if (KeyControlBlock->Delete) {
  408. CmpUnlockRegistry();
  409. return STATUS_KEY_DELETED;
  410. }
  411. Hive = KeyControlBlock->KeyHive;
  412. Cell = KeyControlBlock->KeyCell;
  413. // Mark the hive as read only
  414. CmpMarkAllBinsReadOnly(Hive);
  415. //
  416. // fetch the child of interest
  417. //
  418. Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
  419. if( Node == NULL ) {
  420. //
  421. // we couldn't map a view for the bin containing this cell
  422. //
  423. CmpUnlockRegistry();
  424. CmpMarkAllBinsReadOnly(Hive);
  425. return STATUS_INSUFFICIENT_RESOURCES;
  426. }
  427. childcell = CmpFindSubKeyByNumber(Hive, Node, Index);
  428. // release this cell here as we don't need this Node anymore
  429. HvReleaseCell(Hive, Cell);
  430. if (childcell == HCELL_NIL) {
  431. //
  432. // no such child, clean up and return error
  433. //
  434. // we cannot return STATUS_INSUFFICIENT_RESOURCES because of Iop
  435. // subsystem which treats INSUFFICIENT RESOURCES as no fatal error
  436. //
  437. CmpUnlockRegistry();
  438. // Mark the hive as read only
  439. CmpMarkAllBinsReadOnly(Hive);
  440. return STATUS_NO_MORE_ENTRIES;
  441. }
  442. Node = (PCM_KEY_NODE)HvGetCell(Hive,childcell);
  443. if( Node == NULL ) {
  444. //
  445. // we couldn't map a view for the bin containing this cell
  446. //
  447. CmpMarkAllBinsReadOnly(Hive);
  448. CmpUnlockRegistry();
  449. return STATUS_INSUFFICIENT_RESOURCES;
  450. }
  451. try {
  452. //
  453. // call a worker to perform data transfer
  454. //
  455. status = CmpQueryKeyData(Hive,
  456. Node,
  457. KeyInformationClass,
  458. KeyInformation,
  459. Length,
  460. ResultLength
  461. #if defined(CMP_STATS) || defined(CMP_KCB_CACHE_VALIDATION)
  462. ,
  463. NULL
  464. #endif
  465. );
  466. } except (EXCEPTION_EXECUTE_HANDLER) {
  467. HvReleaseCell(Hive, childcell);
  468. CmpUnlockRegistry();
  469. status = GetExceptionCode();
  470. // Mark the hive as read only
  471. CmpMarkAllBinsReadOnly(Hive);
  472. return status;
  473. }
  474. HvReleaseCell(Hive, childcell);
  475. CmpUnlockRegistry();
  476. // Mark the hive as read only
  477. CmpMarkAllBinsReadOnly(Hive);
  478. return status;
  479. }
  480. NTSTATUS
  481. CmEnumerateValueKey(
  482. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  483. IN ULONG Index,
  484. IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
  485. IN PVOID KeyValueInformation,
  486. IN ULONG Length,
  487. IN PULONG ResultLength
  488. )
  489. /*++
  490. Routine Description:
  491. The value entries of an open key may be enumerated.
  492. CmEnumerateValueKey returns the name of the Index'th value
  493. entry of the open key specified by KeyHandle. The value
  494. STATUS_NO_MORE_ENTRIES will be returned if value of Index is
  495. larger than the number of sub keys.
  496. Note that Index is simply a way to select among value
  497. entries. Two calls to NtEnumerateValueKey with the same Index
  498. are NOT guaranteed to return the same results.
  499. If KeyValueInformation is not long enough to hold all requested data,
  500. STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
  501. set to the number of bytes actually required.
  502. Arguments:
  503. KeyControlBlock - pointer to the KCB that describes the key
  504. Index - Specifies the (0-based) number of the sub key to be returned.
  505. KeyValueInformationClass - Specifies the type of information returned
  506. in Buffer. One of the following types:
  507. KeyValueBasicInformation - return time of last write,
  508. title index, and name. (See KEY_VALUE_BASIC_INFORMATION)
  509. KeyValueFullInformation - return time of last write,
  510. title index, name, class. (See KEY_VALUE_FULL_INFORMATION)
  511. KeyValueInformation -Supplies pointer to buffer to receive the data.
  512. Length - Length of KeyValueInformation in bytes.
  513. ResultLength - Number of bytes actually written into KeyValueInformation.
  514. Return Value:
  515. NTSTATUS - Result code from call, among the following:
  516. <TBS>
  517. --*/
  518. {
  519. NTSTATUS status;
  520. PHHIVE Hive;
  521. PCM_KEY_NODE Node;
  522. PCELL_DATA ChildList;
  523. PCM_KEY_VALUE ValueData = NULL;
  524. BOOLEAN IndexCached;
  525. BOOLEAN ValueCached = FALSE;
  526. PPCM_CACHED_VALUE ContainingList = NULL;
  527. HCELL_INDEX ValueDataCellToRelease = HCELL_NIL;
  528. HCELL_INDEX ValueListToRelease = HCELL_NIL;
  529. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmEnumerateValueKey\n"));
  530. //
  531. // lock the parent cell
  532. //
  533. CmpLockRegistry();
  534. PERFINFO_REG_ENUM_VALUE(KeyControlBlock, Index);
  535. if (KeyControlBlock->Delete) {
  536. CmpUnlockRegistry();
  537. return STATUS_KEY_DELETED;
  538. }
  539. Hive = KeyControlBlock->KeyHive;
  540. Node = (PCM_KEY_NODE)HvGetCell(Hive, KeyControlBlock->KeyCell);
  541. if( Node == NULL ) {
  542. //
  543. // we couldn't map a view for the bin containing this cell
  544. //
  545. CmpUnlockRegistry();
  546. return STATUS_INSUFFICIENT_RESOURCES;
  547. }
  548. //
  549. // fetch the child of interest
  550. //
  551. //
  552. // Do it using the cache
  553. //
  554. if (Index >= KeyControlBlock->ValueCache.Count) {
  555. //
  556. // No such child, clean up and return error.
  557. //
  558. HvReleaseCell(Hive, KeyControlBlock->KeyCell);
  559. CmpUnlockRegistry();
  560. return(STATUS_NO_MORE_ENTRIES);
  561. }
  562. // Mark the hive as read only
  563. CmpMarkAllBinsReadOnly(Hive);
  564. BEGIN_KCB_LOCK_GUARD;
  565. CmpLockKCBTreeExclusive();
  566. if (KeyControlBlock->ExtFlags & CM_KCB_SYM_LINK_FOUND) {
  567. //
  568. // The value list is now set to the KCB for symbolic link,
  569. // Clean it up and set the value right before we do the query.
  570. //
  571. CmpCleanUpKcbValueCache(KeyControlBlock);
  572. CmpSetUpKcbValueCache(KeyControlBlock,Node->ValueList.Count,Node->ValueList.List);
  573. }
  574. ChildList = CmpGetValueListFromCache(Hive, &(KeyControlBlock->ValueCache), &IndexCached, &ValueListToRelease);
  575. if( ChildList == NULL ) {
  576. //
  577. // couldn't map view; treat it as insufficient resources
  578. //
  579. if( ValueListToRelease != HCELL_NIL ) {
  580. HvReleaseCell(Hive,ValueListToRelease);
  581. }
  582. HvReleaseCell(Hive, KeyControlBlock->KeyCell);
  583. // Mark the hive as read only
  584. CmpMarkAllBinsReadOnly(Hive);
  585. CmpUnlockKCBTree();
  586. CmpUnlockRegistry();
  587. return(STATUS_INSUFFICIENT_RESOURCES);
  588. }
  589. ValueData = CmpGetValueKeyFromCache(Hive, ChildList, Index, &ContainingList, IndexCached, &ValueCached,&ValueDataCellToRelease);
  590. if( ValueData == NULL ) {
  591. //
  592. // couldn't map view; treat it as insufficient resources
  593. //
  594. if( ValueListToRelease != HCELL_NIL ) {
  595. HvReleaseCell(Hive,ValueListToRelease);
  596. }
  597. HvReleaseCell(Hive, KeyControlBlock->KeyCell);
  598. if( ValueDataCellToRelease != HCELL_NIL ) {
  599. HvReleaseCell(Hive,ValueDataCellToRelease);
  600. }
  601. // Mark the hive as read only
  602. CmpMarkAllBinsReadOnly(Hive);
  603. CmpUnlockKCBTree();
  604. CmpUnlockRegistry();
  605. return(STATUS_INSUFFICIENT_RESOURCES);
  606. }
  607. END_KCB_LOCK_GUARD;
  608. // Trying to catch the BAD guy who writes over our pool.
  609. CmpMakeValueCacheReadWrite(ValueCached,CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList));
  610. try {
  611. //
  612. // call a worker to perform data transfer; we are touching user-mode address; do it in a try/except
  613. //
  614. status = CmpQueryKeyValueData(Hive,
  615. ContainingList,
  616. ValueData,
  617. ValueCached,
  618. KeyValueInformationClass,
  619. KeyValueInformation,
  620. Length,
  621. ResultLength);
  622. } except (EXCEPTION_EXECUTE_HANDLER) {
  623. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"CmEnumerateValueKey: code:%08lx\n", GetExceptionCode()));
  624. status = GetExceptionCode();
  625. }
  626. // Trying to catch the BAD guy who writes over our pool.
  627. CmpMakeValueCacheReadOnly(ValueCached,CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList));
  628. if( ValueListToRelease != HCELL_NIL ) {
  629. HvReleaseCell(Hive,ValueListToRelease);
  630. }
  631. HvReleaseCell(Hive, KeyControlBlock->KeyCell);
  632. if( ValueDataCellToRelease != HCELL_NIL ) {
  633. HvReleaseCell(Hive,ValueDataCellToRelease);
  634. }
  635. CmpUnlockKCBTree();
  636. CmpUnlockRegistry();
  637. // Mark the hive as read only
  638. CmpMarkAllBinsReadOnly(Hive);
  639. return status;
  640. }
  641. NTSTATUS
  642. CmFlushKey(
  643. IN PHHIVE Hive,
  644. IN HCELL_INDEX Cell
  645. )
  646. /*++
  647. Routine Description:
  648. Forces changes made to a key to disk.
  649. CmFlushKey will not return to its caller until any changed data
  650. associated with the key has been written out.
  651. WARNING: CmFlushKey will flush the entire registry tree, and thus will
  652. burn cycles and I/O.
  653. Arguments:
  654. Hive - supplies a pointer to the hive control structure for the hive
  655. Cell - supplies index of node to whose sub keys are to be found
  656. Return Value:
  657. NTSTATUS - Result code from call, among the following:
  658. <TBS>
  659. --*/
  660. {
  661. PCMHIVE CmHive;
  662. NTSTATUS status = STATUS_SUCCESS;
  663. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmFlushKey\n"));
  664. UNREFERENCED_PARAMETER (Cell);
  665. //
  666. // If writes are not working, lie and say we succeeded, will
  667. // clean up in a short time. Only early system init code
  668. // will ever know the difference.
  669. //
  670. if (CmpNoWrite) {
  671. return STATUS_SUCCESS;
  672. }
  673. // Mark the hive as read only
  674. CmpMarkAllBinsReadOnly(Hive);
  675. CmHive = CONTAINING_RECORD(Hive, CMHIVE, Hive);
  676. //
  677. // Don't flush the master hive. If somebody asks for a flushkey on
  678. // the master hive, do a CmpDoFlushAll instead. CmpDoFlushAll flushes
  679. // every hive except the master hive, which is what they REALLY want.
  680. //
  681. if (CmHive == CmpMasterHive) {
  682. CmpDoFlushAll(FALSE);
  683. } else {
  684. DCmCheckRegistry(CONTAINING_RECORD(Hive, CMHIVE, Hive));
  685. CmLockHive (CmHive);
  686. CmLockHiveViews (CmHive);
  687. if( HvHiveWillShrink( &(CmHive->Hive) ) ) {
  688. //
  689. // we may end up here is when the hive shrinks and we need
  690. // exclusive access over the registry, as we are going to CcPurge !
  691. //
  692. CmUnlockHiveViews (CmHive);
  693. CmUnlockHive (CmHive);
  694. CmpUnlockRegistry();
  695. CmpLockRegistryExclusive();
  696. #ifdef CHECK_REGISTRY_USECOUNT
  697. CmpCheckRegistryUseCount();
  698. #endif //CHECK_REGISTRY_USECOUNT
  699. CmLockHive (CmHive);
  700. if( CmHive->UseCount != 0) {
  701. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  702. CmpFixHiveUsageCount(CmHive);
  703. ASSERT( CmHive->UseCount == 0 );
  704. }
  705. } else {
  706. //
  707. // release the views
  708. //
  709. CmUnlockHiveViews (CmHive);
  710. }
  711. if (! HvSyncHive(Hive)) {
  712. status = STATUS_REGISTRY_IO_FAILED;
  713. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmFlushKey: HvSyncHive failed\n"));
  714. }
  715. CmUnlockHive (CmHive);
  716. }
  717. // Mark the hive as read only
  718. CmpMarkAllBinsReadOnly(Hive);
  719. return status;
  720. }
  721. NTSTATUS
  722. CmQueryKey(
  723. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  724. IN KEY_INFORMATION_CLASS KeyInformationClass,
  725. IN PVOID KeyInformation,
  726. IN ULONG Length,
  727. IN PULONG ResultLength
  728. )
  729. /*++
  730. Routine Description:
  731. Data about the class of a key, and the numbers and sizes of its
  732. children and value entries may be queried with CmQueryKey.
  733. NOTE: The returned lengths are guaranteed to be at least as
  734. long as the described values, but may be longer in
  735. some circumstances.
  736. Arguments:
  737. KeyControlBlock - pointer to the KCB that describes the key
  738. KeyInformationClass - Specifies the type of information
  739. returned in Buffer. One of the following types:
  740. KeyBasicInformation - return last write time, title index, and name.
  741. (See KEY_BASIC_INFORMATION)
  742. KeyNodeInformation - return last write time, title index, name, class.
  743. (See KEY_NODE_INFORMATION)
  744. KeyFullInformation - return all data except for name and security.
  745. (See KEY_FULL_INFORMATION)
  746. KeyInformation -Supplies pointer to buffer to receive the data.
  747. Length - Length of KeyInformation in bytes.
  748. ResultLength - Number of bytes actually written into KeyInformation.
  749. Return Value:
  750. NTSTATUS - Result code from call, among the following:
  751. <TBS>
  752. --*/
  753. {
  754. NTSTATUS status = STATUS_UNSUCCESSFUL;
  755. PCM_KEY_NODE Node = NULL;
  756. PUNICODE_STRING Name = NULL;
  757. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmQueryKey\n"));
  758. CmpLockRegistry();
  759. PERFINFO_REG_QUERY_KEY(KeyControlBlock);
  760. // Mark the hive as read only
  761. CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
  762. try {
  763. //
  764. // request for the FULL path of the key
  765. //
  766. if( KeyInformationClass == KeyNameInformation ) {
  767. if (KeyControlBlock->Delete ) {
  768. //
  769. // special case: return key deleted status, but still fill the full name of the key.
  770. //
  771. status = STATUS_KEY_DELETED;
  772. } else {
  773. status = STATUS_SUCCESS;
  774. }
  775. if( KeyControlBlock->NameBlock ) {
  776. Name = CmpConstructName(KeyControlBlock);
  777. if (Name == NULL) {
  778. status = STATUS_INSUFFICIENT_RESOURCES;
  779. } else {
  780. ULONG requiredlength;
  781. ULONG minimumlength;
  782. USHORT NameLength;
  783. LONG leftlength;
  784. PKEY_INFORMATION pbuffer = (PKEY_INFORMATION)KeyInformation;
  785. NameLength = Name->Length;
  786. requiredlength = FIELD_OFFSET(KEY_NAME_INFORMATION, Name) + NameLength;
  787. minimumlength = FIELD_OFFSET(KEY_NAME_INFORMATION, Name);
  788. *ResultLength = requiredlength;
  789. if (Length < minimumlength) {
  790. status = STATUS_BUFFER_TOO_SMALL;
  791. } else {
  792. //
  793. // Fill in the length of the name
  794. //
  795. pbuffer->KeyNameInformation.NameLength = NameLength;
  796. //
  797. // Now copy the full name into the user buffer, if enough space
  798. //
  799. leftlength = Length - minimumlength;
  800. requiredlength = NameLength;
  801. if (leftlength < (LONG)requiredlength) {
  802. requiredlength = leftlength;
  803. status = STATUS_BUFFER_OVERFLOW;
  804. }
  805. //
  806. // If not enough space, copy how much we can and return overflow
  807. //
  808. RtlCopyMemory(
  809. &(pbuffer->KeyNameInformation.Name[0]),
  810. Name->Buffer,
  811. requiredlength
  812. );
  813. }
  814. }
  815. }
  816. } else if(KeyControlBlock->Delete ) {
  817. //
  818. // key already deleted
  819. //
  820. status = STATUS_KEY_DELETED;
  821. } else if( KeyInformationClass == KeyFlagsInformation ) {
  822. //
  823. // we only want to get the user defined flags;
  824. //
  825. PKEY_INFORMATION pbuffer = (PKEY_INFORMATION)KeyInformation;
  826. ULONG requiredlength;
  827. requiredlength = sizeof(KEY_FLAGS_INFORMATION);
  828. *ResultLength = requiredlength;
  829. if (Length < requiredlength) {
  830. status = STATUS_BUFFER_TOO_SMALL;
  831. } else {
  832. pbuffer->KeyFlagsInformation.UserFlags = (ULONG)((USHORT)KeyControlBlock->Flags >> KEY_USER_FLAGS_SHIFT);
  833. status = STATUS_SUCCESS;
  834. }
  835. } else {
  836. //
  837. // call a worker to perform data transfer
  838. //
  839. if( KeyInformationClass == KeyCachedInformation ) {
  840. //
  841. // call the fast version
  842. //
  843. status = CmpQueryKeyDataFromCache( KeyControlBlock,
  844. KeyInformationClass,
  845. KeyInformation,
  846. Length,
  847. ResultLength );
  848. } else {
  849. //
  850. // old'n plain slow version
  851. //
  852. Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
  853. if( Node == NULL ) {
  854. //
  855. // we couldn't map a view for the bin containing this cell
  856. //
  857. status = STATUS_INSUFFICIENT_RESOURCES;
  858. } else {
  859. status = CmpQueryKeyData(KeyControlBlock->KeyHive,
  860. Node,
  861. KeyInformationClass,
  862. KeyInformation,
  863. Length,
  864. ResultLength
  865. #if defined(CMP_STATS) || defined(CMP_KCB_CACHE_VALIDATION)
  866. ,
  867. KeyControlBlock
  868. #endif
  869. );
  870. }
  871. }
  872. }
  873. } finally {
  874. if( Node != NULL ) {
  875. HvReleaseCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
  876. }
  877. if( Name != NULL ) {
  878. ExFreePoolWithTag(Name, CM_NAME_TAG | PROTECTED_POOL);
  879. }
  880. CmpUnlockRegistry();
  881. }
  882. // Mark the hive as read only
  883. CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
  884. return status;
  885. }
  886. NTSTATUS
  887. CmQueryValueKey(
  888. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  889. IN UNICODE_STRING ValueName,
  890. IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
  891. IN PVOID KeyValueInformation,
  892. IN ULONG Length,
  893. IN PULONG ResultLength
  894. )
  895. /*++
  896. Routine Description:
  897. The ValueName, TitleIndex, Type, and Data for any one of a key's
  898. value entries may be queried with CmQueryValueKey.
  899. If KeyValueInformation is not long enough to hold all requested data,
  900. STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be
  901. set to the number of bytes actually required.
  902. Arguments:
  903. KeyControlBlock - pointer to the KCB that describes the key
  904. ValueName - The name of the value entry to return data for.
  905. KeyValueInformationClass - Specifies the type of information
  906. returned in KeyValueInformation. One of the following types:
  907. KeyValueBasicInformation - return time of last write, title
  908. index, and name. (See KEY_VALUE_BASIC_INFORMATION)
  909. KeyValueFullInformation - return time of last write, title
  910. index, name, class. (See KEY_VALUE_FULL_INFORMATION)
  911. KeyValueInformation -Supplies pointer to buffer to receive the data.
  912. Length - Length of KeyValueInformation in bytes.
  913. ResultLength - Number of bytes actually written into KeyValueInformation.
  914. Return Value:
  915. NTSTATUS - Result code from call, among the following:
  916. <TBS>
  917. --*/
  918. {
  919. NTSTATUS status;
  920. PCM_KEY_VALUE ValueData = NULL;
  921. ULONG Index;
  922. BOOLEAN ValueCached = FALSE;
  923. PPCM_CACHED_VALUE ContainingList = NULL;
  924. HCELL_INDEX ValueDataCellToRelease = HCELL_NIL;
  925. PAGED_CODE();
  926. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmQueryValueKey\n"));
  927. CmpLockRegistry();
  928. PERFINFO_REG_QUERY_VALUE(KeyControlBlock, &ValueName);
  929. if (KeyControlBlock->Delete) {
  930. CmpUnlockRegistry();
  931. return STATUS_KEY_DELETED;
  932. }
  933. // Mark the hive as read only
  934. CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
  935. BEGIN_KCB_LOCK_GUARD;
  936. // try shared first
  937. CmpLockKCBTree();
  938. if (KeyControlBlock->ExtFlags & CM_KCB_SYM_LINK_FOUND) {
  939. // upgrade lock to exclusive (referenced so can't be deleted)
  940. CmpUnlockKCBTree();
  941. CmpLockKCBTreeExclusive();
  942. if (KeyControlBlock->ExtFlags & CM_KCB_SYM_LINK_FOUND) {
  943. //
  944. // The value list is now set to the KCB for symbolic link,
  945. // Clean it up and set the value right before we do the query.
  946. //
  947. CmpCleanUpKcbValueCache(KeyControlBlock);
  948. {
  949. PCM_KEY_NODE Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
  950. if( Node == NULL ) {
  951. //
  952. // we couldn't map a view for the bin containing this cell
  953. //
  954. CmpUnlockKCBTree();
  955. CmpUnlockRegistry();
  956. // Mark the hive as read only
  957. CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
  958. return STATUS_INSUFFICIENT_RESOURCES;
  959. }
  960. CmpSetUpKcbValueCache(KeyControlBlock,Node->ValueList.Count,Node->ValueList.List);
  961. HvReleaseCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
  962. }
  963. }
  964. }
  965. CmpLockKCB(KeyControlBlock);
  966. //
  967. // Find the data
  968. //
  969. ValueData = CmpFindValueByNameFromCache(KeyControlBlock->KeyHive,
  970. &(KeyControlBlock->ValueCache),
  971. &ValueName,
  972. &ContainingList,
  973. &Index,
  974. &ValueCached,
  975. &ValueDataCellToRelease
  976. );
  977. END_KCB_LOCK_GUARD;
  978. if (ValueData) {
  979. // Trying to catch the BAD guy who writes over our pool.
  980. CmpMakeValueCacheReadWrite(ValueCached,CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList));
  981. try {
  982. //
  983. // call a worker to perform data transfer; we are touching user-mode address; do it in a try/except
  984. //
  985. status = CmpQueryKeyValueData(KeyControlBlock->KeyHive,
  986. ContainingList,
  987. ValueData,
  988. ValueCached,
  989. KeyValueInformationClass,
  990. KeyValueInformation,
  991. Length,
  992. ResultLength);
  993. } except (EXCEPTION_EXECUTE_HANDLER) {
  994. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"CmQueryValueKey: code:%08lx\n", GetExceptionCode()));
  995. status = GetExceptionCode();
  996. }
  997. // Trying to catch the BAD guy who writes over our pool.
  998. CmpMakeValueCacheReadOnly(ValueCached,CMP_GET_CACHED_ADDRESS(KeyControlBlock->ValueCache.ValueList));
  999. } else {
  1000. status = STATUS_OBJECT_NAME_NOT_FOUND;
  1001. }
  1002. if(ValueDataCellToRelease != HCELL_NIL) {
  1003. HvReleaseCell(KeyControlBlock->KeyHive,ValueDataCellToRelease);
  1004. }
  1005. CmpUnlockKCB(KeyControlBlock);
  1006. CmpUnlockKCBTree();
  1007. CmpUnlockRegistry();
  1008. // Mark the hive as read only
  1009. CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
  1010. return status;
  1011. }
  1012. NTSTATUS
  1013. CmQueryMultipleValueKey(
  1014. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  1015. IN PKEY_VALUE_ENTRY ValueEntries,
  1016. IN ULONG EntryCount,
  1017. IN PVOID ValueBuffer,
  1018. IN OUT PULONG BufferLength,
  1019. IN OPTIONAL PULONG ResultLength
  1020. )
  1021. /*++
  1022. Routine Description:
  1023. Multiple values of any key may be queried atomically with
  1024. this api.
  1025. Arguments:
  1026. KeyControlBlock - Supplies the key to be queried.
  1027. ValueEntries - Returns an array of KEY_VALUE_ENTRY structures, one for each value.
  1028. EntryCount - Supplies the number of entries in the ValueNames and ValueEntries arrays
  1029. ValueBuffer - Returns the value data for each value.
  1030. BufferLength - Supplies the length of the ValueBuffer array in bytes.
  1031. Returns the length of the ValueBuffer array that was filled in.
  1032. ResultLength - if present, Returns the length in bytes of the ValueBuffer
  1033. array required to return the requested values of this key.
  1034. Return Value:
  1035. NTSTATUS
  1036. --*/
  1037. {
  1038. PHHIVE Hive;
  1039. NTSTATUS Status;
  1040. ULONG i;
  1041. UNICODE_STRING CurrentName;
  1042. HCELL_INDEX ValueCell = HCELL_NIL;
  1043. PCM_KEY_VALUE ValueNode;
  1044. ULONG RequiredLength = 0;
  1045. ULONG UsedLength = 0;
  1046. ULONG DataLength;
  1047. BOOLEAN BufferFull = FALSE;
  1048. BOOLEAN Small;
  1049. KPROCESSOR_MODE PreviousMode;
  1050. PCM_KEY_NODE Node;
  1051. PAGED_CODE();
  1052. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmQueryMultipleValueKey\n"));
  1053. CmpLockRegistry();
  1054. if (KeyControlBlock->Delete) {
  1055. CmpUnlockRegistry();
  1056. return STATUS_KEY_DELETED;
  1057. }
  1058. Hive = KeyControlBlock->KeyHive;
  1059. Status = STATUS_SUCCESS;
  1060. Node = (PCM_KEY_NODE)HvGetCell(Hive, KeyControlBlock->KeyCell);
  1061. if( Node == NULL ) {
  1062. //
  1063. // we couldn't map a view for the bin containing this cell
  1064. //
  1065. CmpUnlockRegistry();
  1066. return STATUS_INSUFFICIENT_RESOURCES;
  1067. }
  1068. // Mark the hive as read only
  1069. CmpMarkAllBinsReadOnly(Hive);
  1070. PreviousMode = KeGetPreviousMode();
  1071. try {
  1072. for (i=0; i < EntryCount; i++) {
  1073. //
  1074. // find the data
  1075. //
  1076. if (PreviousMode == UserMode) {
  1077. CurrentName = ProbeAndReadUnicodeString(ValueEntries[i].ValueName);
  1078. ProbeForRead(CurrentName.Buffer,CurrentName.Length,sizeof(WCHAR));
  1079. } else {
  1080. CurrentName = *(ValueEntries[i].ValueName);
  1081. }
  1082. PERFINFO_REG_QUERY_MULTIVALUE(KeyControlBlock, &CurrentName);
  1083. ValueCell = CmpFindValueByName(Hive,
  1084. Node,
  1085. &CurrentName);
  1086. if (ValueCell != HCELL_NIL) {
  1087. ValueNode = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell);
  1088. if( ValueNode == NULL ) {
  1089. //
  1090. // we couldn't map a view for the bin containing this cell
  1091. //
  1092. ValueCell = HCELL_NIL;
  1093. Status = STATUS_INSUFFICIENT_RESOURCES;
  1094. break;
  1095. }
  1096. Small = CmpIsHKeyValueSmall(DataLength, ValueNode->DataLength);
  1097. //
  1098. // Round up UsedLength and RequiredLength to a ULONG boundary
  1099. //
  1100. UsedLength = (UsedLength + sizeof(ULONG)-1) & ~(sizeof(ULONG)-1);
  1101. RequiredLength = (RequiredLength + sizeof(ULONG)-1) & ~(sizeof(ULONG)-1);
  1102. //
  1103. // If there is enough room for this data value in the buffer,
  1104. // fill it in now. Otherwise, mark the buffer as full. We must
  1105. // keep iterating through the values in order to determine the
  1106. // RequiredLength.
  1107. //
  1108. if ((UsedLength + DataLength <= *BufferLength) &&
  1109. (!BufferFull)) {
  1110. PCELL_DATA Buffer;
  1111. BOOLEAN BufferAllocated;
  1112. HCELL_INDEX CellToRelease;
  1113. //
  1114. // get the data from source, regardless of the size
  1115. //
  1116. if( CmpGetValueData(Hive,ValueNode,&DataLength,&Buffer,&BufferAllocated,&CellToRelease) == FALSE ) {
  1117. //
  1118. // insufficient resources; return NULL
  1119. //
  1120. ASSERT( BufferAllocated == FALSE );
  1121. ASSERT( Buffer == NULL );
  1122. Status = STATUS_INSUFFICIENT_RESOURCES;
  1123. break;
  1124. }
  1125. RtlCopyMemory((PUCHAR)ValueBuffer + UsedLength,
  1126. Buffer,
  1127. DataLength);
  1128. //
  1129. // cleanup the temporary buffer
  1130. //
  1131. if( BufferAllocated == TRUE ) {
  1132. ExFreePool( Buffer );
  1133. }
  1134. //
  1135. // release the buffer in case we are using hive storage
  1136. //
  1137. if( CellToRelease != HCELL_NIL ) {
  1138. HvReleaseCell(Hive,CellToRelease);
  1139. }
  1140. ValueEntries[i].Type = ValueNode->Type;
  1141. ValueEntries[i].DataLength = DataLength;
  1142. ValueEntries[i].DataOffset = UsedLength;
  1143. UsedLength += DataLength;
  1144. } else {
  1145. BufferFull = TRUE;
  1146. Status = STATUS_BUFFER_OVERFLOW;
  1147. }
  1148. RequiredLength += DataLength;
  1149. HvReleaseCell(Hive, ValueCell);
  1150. ValueCell = HCELL_NIL;
  1151. } else {
  1152. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1153. break;
  1154. }
  1155. }
  1156. if (NT_SUCCESS(Status) ||
  1157. (Status == STATUS_BUFFER_OVERFLOW)) {
  1158. *BufferLength = UsedLength;
  1159. if (ARGUMENT_PRESENT(ResultLength)) {
  1160. *ResultLength = RequiredLength;
  1161. }
  1162. }
  1163. } finally {
  1164. if( ValueCell != HCELL_NIL) {
  1165. HvReleaseCell(Hive, ValueCell);
  1166. }
  1167. HvReleaseCell(Hive, KeyControlBlock->KeyCell);
  1168. CmpUnlockRegistry();
  1169. }
  1170. // Mark the hive as read only
  1171. CmpMarkAllBinsReadOnly(Hive);
  1172. return Status;
  1173. }
  1174. NTSTATUS
  1175. CmSetValueKey(
  1176. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  1177. IN PUNICODE_STRING ValueName,
  1178. IN ULONG Type,
  1179. IN PVOID Data,
  1180. IN ULONG DataSize
  1181. )
  1182. /*++
  1183. Routine Description:
  1184. A value entry may be created or replaced with CmSetValueKey.
  1185. If a value entry with a Value ID (i.e. name) matching the
  1186. one specified by ValueName exists, it is deleted and replaced
  1187. with the one specified. If no such value entry exists, a new
  1188. one is created. NULL is a legal Value ID. While Value IDs must
  1189. be unique within any given key, the same Value ID may appear
  1190. in many different keys.
  1191. Arguments:
  1192. KeyControlBlock - pointer to kcb for the key to operate on
  1193. ValueName - The unique (relative to the containing key) name
  1194. of the value entry. May be NULL.
  1195. Type - The integer type number of the value entry.
  1196. Data - Pointer to buffer with actual data for the value entry.
  1197. DataSize - Size of Data buffer.
  1198. Return Value:
  1199. NTSTATUS - Result code from call, among the following:
  1200. <TBS>
  1201. --*/
  1202. {
  1203. NTSTATUS status;
  1204. PCM_KEY_NODE parent = NULL;
  1205. HCELL_INDEX oldchild = 0;
  1206. ULONG count;
  1207. PHHIVE Hive = NULL;
  1208. HCELL_INDEX Cell;
  1209. ULONG StorageType;
  1210. ULONG TempData;
  1211. BOOLEAN found;
  1212. PCM_KEY_VALUE Value = NULL;
  1213. LARGE_INTEGER systemtime;
  1214. ULONG mustChange=FALSE;
  1215. ULONG ChildIndex;
  1216. HCELL_INDEX ParentToRelease = HCELL_NIL;
  1217. HCELL_INDEX ChildToRelease = HCELL_NIL;
  1218. PERFINFO_REG_SET_VALUE_DECL();
  1219. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmSetValueKey\n"));
  1220. CmpLockRegistry();
  1221. ASSERT(sizeof(ULONG) == CM_KEY_VALUE_SMALL);
  1222. PERFINFO_REG_SET_VALUE(KeyControlBlock);
  1223. // Mark the hive as read only
  1224. CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
  1225. while (TRUE) {
  1226. //
  1227. // Check that we are not being asked to add a value to a key
  1228. // that has been deleted
  1229. //
  1230. if (KeyControlBlock->Delete == TRUE) {
  1231. status = STATUS_KEY_DELETED;
  1232. goto Exit;
  1233. }
  1234. //
  1235. // Check to see if this is a symbolic link node. If so caller
  1236. // is only allowed to create/change the SymbolicLinkValue
  1237. // value name
  1238. //
  1239. #ifdef CMP_KCB_CACHE_VALIDATION
  1240. {
  1241. PCM_KEY_NODE Node;
  1242. Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
  1243. if( Node == NULL ) {
  1244. //
  1245. // we couldn't map a view for the bin containing this cell
  1246. //
  1247. status = STATUS_INSUFFICIENT_RESOURCES;
  1248. goto Exit;
  1249. }
  1250. ASSERT( Node->Flags == KeyControlBlock->Flags );
  1251. HvReleaseCell(KeyControlBlock->KeyHive, KeyControlBlock->KeyCell);
  1252. }
  1253. #endif
  1254. if (KeyControlBlock->Flags & KEY_SYM_LINK &&
  1255. (( (Type != REG_LINK)
  1256. #ifdef CM_DYN_SYM_LINK
  1257. && (Type != REG_DYN_LINK)
  1258. #endif //CM_DYN_SYM_LINK
  1259. ) ||
  1260. ValueName == NULL ||
  1261. !RtlEqualUnicodeString(&CmSymbolicLinkValueName, ValueName, TRUE)))
  1262. {
  1263. //
  1264. // Disallow attempts to manipulate any value names under a symbolic link
  1265. // except for the "SymbolicLinkValue" value name or type other than REG_LINK
  1266. //
  1267. // Mark the hive as read only
  1268. CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
  1269. status = STATUS_ACCESS_DENIED;
  1270. goto Exit;
  1271. }
  1272. if( mustChange == FALSE ) {
  1273. //
  1274. // first iteration; look inside the kcb cache
  1275. //
  1276. if( CmpCompareNewValueDataAgainstKCBCache(KeyControlBlock,ValueName,Type,Data,DataSize) == TRUE ) {
  1277. //
  1278. // the value is in the cache and is the same; make this call a noop
  1279. //
  1280. status = STATUS_SUCCESS;
  1281. goto Exit;
  1282. }
  1283. //
  1284. // To Get here, we must either be changing a value, or setting a new one
  1285. //
  1286. mustChange=TRUE;
  1287. } else {
  1288. //
  1289. // second iteration; look inside the hive
  1290. //
  1291. //
  1292. // get reference to parent key,
  1293. //
  1294. Hive = KeyControlBlock->KeyHive;
  1295. Cell = KeyControlBlock->KeyCell;
  1296. if( ParentToRelease != HCELL_NIL ) {
  1297. HvReleaseCell(Hive,ParentToRelease);
  1298. ParentToRelease = HCELL_NIL;
  1299. }
  1300. parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
  1301. if( parent == NULL ) {
  1302. //
  1303. // we couldn't map a view for the bin containing this cell
  1304. //
  1305. status = STATUS_INSUFFICIENT_RESOURCES;
  1306. goto Exit;
  1307. }
  1308. ParentToRelease = Cell;
  1309. //
  1310. // try to find an existing value entry by the same name
  1311. //
  1312. count = parent->ValueList.Count;
  1313. found = FALSE;
  1314. if (count > 0) {
  1315. if( CmpFindNameInList(Hive,
  1316. &parent->ValueList,
  1317. ValueName,
  1318. &ChildIndex,
  1319. &oldchild) == FALSE ) {
  1320. //
  1321. // we couldn't map a view for the bin containing this cell
  1322. //
  1323. status = STATUS_INSUFFICIENT_RESOURCES;
  1324. goto Exit;
  1325. }
  1326. if (oldchild != HCELL_NIL) {
  1327. if( ChildToRelease != HCELL_NIL ) {
  1328. HvReleaseCell(Hive,ChildToRelease);
  1329. ChildToRelease = HCELL_NIL;
  1330. }
  1331. Value = (PCM_KEY_VALUE)HvGetCell(Hive,oldchild);
  1332. if( Value == NULL ) {
  1333. //
  1334. // could no map view
  1335. //
  1336. status = STATUS_INSUFFICIENT_RESOURCES;
  1337. goto Exit;
  1338. }
  1339. ChildToRelease = oldchild;
  1340. found = TRUE;
  1341. }
  1342. } else {
  1343. //
  1344. // empty list; add it first
  1345. //
  1346. ChildIndex = 0;
  1347. }
  1348. //
  1349. // Performance Hack:
  1350. // If a Set is asking us to set a key to the current value (IE does this a lot)
  1351. // drop it (and, therefore, the last modified time) on the floor, but return success
  1352. // this stops the page from being dirtied, and us having to flush the registry.
  1353. //
  1354. //
  1355. break;
  1356. }
  1357. //
  1358. // We're going through these gyrations so that if someone does come in and try and delete the
  1359. // key we're setting we're safe. Once we know we have to change the key, take the
  1360. // Exclusive (write) lock then restart
  1361. //
  1362. //
  1363. CmpUnlockRegistry();
  1364. CmpLockRegistryExclusive();
  1365. #ifdef CHECK_REGISTRY_USECOUNT
  1366. CmpCheckRegistryUseCount();
  1367. #endif //CHECK_REGISTRY_USECOUNT
  1368. }// while
  1369. ASSERT( mustChange == TRUE );
  1370. // It's a different or new value, mark it dirty, since we'll
  1371. // at least set its time stamp
  1372. if (! HvMarkCellDirty(Hive, Cell)) {
  1373. status = STATUS_NO_LOG_SPACE;
  1374. goto Exit;
  1375. }
  1376. StorageType = HvGetCellType(Cell);
  1377. //
  1378. // stash small data if relevent
  1379. //
  1380. TempData = 0;
  1381. if ((DataSize <= CM_KEY_VALUE_SMALL) &&
  1382. (DataSize > 0))
  1383. {
  1384. try {
  1385. RtlCopyMemory( // yes, move memory, could be 1 byte
  1386. &TempData, // at the end of a page.
  1387. Data,
  1388. DataSize
  1389. );
  1390. } except (EXCEPTION_EXECUTE_HANDLER) {
  1391. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmSetValueKey: code:%08lx\n", GetExceptionCode()));
  1392. status = GetExceptionCode();
  1393. goto Exit;
  1394. }
  1395. }
  1396. if (found) {
  1397. //
  1398. // ----- Existing Value Entry Path -----
  1399. //
  1400. //
  1401. // An existing value entry of the specified name exists,
  1402. // set our data into it.
  1403. //
  1404. status = CmpSetValueKeyExisting(Hive,
  1405. oldchild,
  1406. Value,
  1407. Type,
  1408. Data,
  1409. DataSize,
  1410. StorageType,
  1411. TempData);
  1412. PERFINFO_REG_SET_VALUE_EXIST();
  1413. } else {
  1414. //
  1415. // ----- New Value Entry Path -----
  1416. //
  1417. //
  1418. // Either there are no existing value entries, or the one
  1419. // specified is not in the list. In either case, create and
  1420. // fill a new one, and add it to the list
  1421. //
  1422. status = CmpSetValueKeyNew(Hive,
  1423. parent,
  1424. ValueName,
  1425. ChildIndex,
  1426. Type,
  1427. Data,
  1428. DataSize,
  1429. StorageType,
  1430. TempData);
  1431. PERFINFO_REG_SET_VALUE_NEW();
  1432. }
  1433. if (NT_SUCCESS(status)) {
  1434. // sanity assert
  1435. ASSERT( parent->MaxValueNameLen == KeyControlBlock->KcbMaxValueNameLen );
  1436. if (parent->MaxValueNameLen < ValueName->Length) {
  1437. parent->MaxValueNameLen = ValueName->Length;
  1438. // update the kcb cache too
  1439. KeyControlBlock->KcbMaxValueNameLen = ValueName->Length;
  1440. }
  1441. //sanity assert
  1442. ASSERT( parent->MaxValueDataLen == KeyControlBlock->KcbMaxValueDataLen );
  1443. if (parent->MaxValueDataLen < DataSize) {
  1444. parent->MaxValueDataLen = DataSize;
  1445. // update the kcb cache too
  1446. KeyControlBlock->KcbMaxValueDataLen = parent->MaxValueDataLen;
  1447. }
  1448. KeQuerySystemTime(&systemtime);
  1449. parent->LastWriteTime = systemtime;
  1450. // update the kcb cache too.
  1451. KeyControlBlock->KcbLastWriteTime = systemtime;
  1452. //
  1453. // Update the cache, no need for KCB lock as the registry is locked exclusively.
  1454. //
  1455. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  1456. if( found && (CMP_IS_CELL_CACHED(KeyControlBlock->ValueCache.ValueList)) ) {
  1457. //
  1458. // invalidate only the entry we changed.
  1459. //
  1460. PULONG_PTR CachedList = (PULONG_PTR) CMP_GET_CACHED_CELLDATA(KeyControlBlock->ValueCache.ValueList);
  1461. if (CMP_IS_CELL_CACHED(CachedList[ChildIndex])) {
  1462. ExFreePool((PVOID) CMP_GET_CACHED_ADDRESS(CachedList[ChildIndex]));
  1463. }
  1464. CachedList[ChildIndex] = oldchild;
  1465. } else {
  1466. //
  1467. // rebuild ALL KCB cache
  1468. //
  1469. CmpCleanUpKcbValueCache(KeyControlBlock);
  1470. CmpSetUpKcbValueCache(KeyControlBlock,parent->ValueList.Count,parent->ValueList.List);
  1471. }
  1472. CmpReportNotify(KeyControlBlock,
  1473. KeyControlBlock->KeyHive,
  1474. KeyControlBlock->KeyCell,
  1475. REG_NOTIFY_CHANGE_LAST_SET);
  1476. }
  1477. Exit:
  1478. PERFINFO_REG_SET_VALUE_DONE(ValueName);
  1479. if( ParentToRelease != HCELL_NIL && Hive != NULL) {
  1480. HvReleaseCell(Hive,ParentToRelease);
  1481. }
  1482. if( ChildToRelease != HCELL_NIL && Hive != NULL) {
  1483. HvReleaseCell(Hive,ChildToRelease);
  1484. }
  1485. CmpUnlockRegistry();
  1486. // Mark the hive as read only
  1487. CmpMarkAllBinsReadOnly(KeyControlBlock->KeyHive);
  1488. return status;
  1489. }
  1490. NTSTATUS
  1491. CmpSetValueKeyExisting(
  1492. IN PHHIVE Hive,
  1493. IN HCELL_INDEX OldChild,
  1494. IN PCM_KEY_VALUE Value,
  1495. IN ULONG Type,
  1496. IN PVOID Data,
  1497. IN ULONG DataSize,
  1498. IN ULONG StorageType,
  1499. IN ULONG TempData
  1500. )
  1501. /*++
  1502. Routine Description:
  1503. Helper for CmSetValueKey, implements the case where the value entry
  1504. being set already exists.
  1505. Arguments:
  1506. Hive - hive of interest
  1507. OldChild - hcell_index of the value entry body to which we are to
  1508. set new data
  1509. Type - The integer type number of the value entry.
  1510. Data - Pointer to buffer with actual data for the value entry.
  1511. DataSize - Size of Data buffer.
  1512. StorageType - stable or volatile
  1513. TempData - small values are passed here
  1514. Return Value:
  1515. STATUS_SUCCESS if it worked, appropriate status code if it did not
  1516. Note:
  1517. For new hives format, we have the following cases:
  1518. New Data Old Data
  1519. -------- --------
  1520. 1. small small
  1521. 2. small normal
  1522. 3. small bigdata
  1523. 4. normal small
  1524. 5. normal normal
  1525. 6. normal bigdata
  1526. 7. bigdata small
  1527. 8. bigdata normal
  1528. 9. bigdata bigdata
  1529. --*/
  1530. {
  1531. HCELL_INDEX DataCell;
  1532. HCELL_INDEX OldDataCell;
  1533. PCELL_DATA pdata;
  1534. HCELL_INDEX NewCell;
  1535. ULONG OldRealSize;
  1536. USHORT OldSizeType; // 0 - small
  1537. USHORT NewSizeType; // 1 - normal
  1538. // 2 - bigdata
  1539. HANDLE hSecure = 0;
  1540. NTSTATUS status = STATUS_SUCCESS;
  1541. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  1542. //
  1543. // value entry by the specified name already exists
  1544. // oldchild is hcell_index of its value entry body
  1545. // which we will always edit, so mark it dirty
  1546. //
  1547. if (! HvMarkCellDirty(Hive, OldChild)) {
  1548. return STATUS_NO_LOG_SPACE;
  1549. }
  1550. if(CmpIsHKeyValueSmall(OldRealSize, Value->DataLength) == TRUE ) {
  1551. //
  1552. // old data was small
  1553. //
  1554. OldSizeType = 0;
  1555. } else if( CmpIsHKeyValueBig(Hive,OldRealSize) == TRUE ) {
  1556. //
  1557. // old data was big
  1558. //
  1559. OldSizeType = 2;
  1560. } else {
  1561. //
  1562. // old data was normal
  1563. //
  1564. OldSizeType = 1;
  1565. }
  1566. if( DataSize <= CM_KEY_VALUE_SMALL ) {
  1567. //
  1568. // new data is small
  1569. //
  1570. NewSizeType = 0;
  1571. } else if( CmpIsHKeyValueBig(Hive,DataSize) == TRUE ) {
  1572. //
  1573. // new data is big
  1574. //
  1575. NewSizeType = 2;
  1576. } else {
  1577. //
  1578. // new data is normal
  1579. //
  1580. NewSizeType = 1;
  1581. }
  1582. //
  1583. // this will handle all cases and will make sure data is marked dirty
  1584. //
  1585. if( !CmpMarkValueDataDirty(Hive,Value) ) {
  1586. return STATUS_NO_LOG_SPACE;
  1587. }
  1588. //
  1589. // cases 1,2,3
  1590. //
  1591. if( NewSizeType == 0 ) {
  1592. if( ((OldSizeType == 1) && (OldRealSize > 0) ) ||
  1593. (OldSizeType == 2)
  1594. ) {
  1595. CmpFreeValueData(Hive,Value->Data,OldRealSize);
  1596. }
  1597. //
  1598. // write our new small data into value entry body
  1599. //
  1600. Value->DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
  1601. Value->Data = TempData;
  1602. Value->Type = Type;
  1603. return STATUS_SUCCESS;
  1604. }
  1605. //
  1606. // secure the user buffer so we don't get inconsistencies.
  1607. // ONLY if we are called with a user mode buffer !!!
  1608. //
  1609. if ( (ULONG_PTR)Data <= (ULONG_PTR)MM_HIGHEST_USER_ADDRESS ) {
  1610. hSecure = MmSecureVirtualMemory(Data,DataSize, PAGE_READONLY);
  1611. if (hSecure == 0) {
  1612. return STATUS_INVALID_PARAMETER;
  1613. }
  1614. }
  1615. //
  1616. // store it to be freed if the allocation succeeds
  1617. //
  1618. OldDataCell = Value->Data;
  1619. //
  1620. // cases 4,5,6
  1621. //
  1622. if( NewSizeType == 1 ){
  1623. if( (OldSizeType == 1) && (OldRealSize > 0)) {
  1624. //
  1625. // we already have a cell; see if we can reuse it !
  1626. //
  1627. DataCell = Value->Data;
  1628. ASSERT(DataCell != HCELL_NIL);
  1629. pdata = HvGetCell(Hive, DataCell);
  1630. if( pdata == NULL ) {
  1631. //
  1632. // we couldn't map a view for the bin containing this cell
  1633. //
  1634. status = STATUS_INSUFFICIENT_RESOURCES;
  1635. goto Exit;
  1636. }
  1637. // release it right here, as the registry is locked exclusively, so we don't care
  1638. HvReleaseCell(Hive, DataCell);
  1639. ASSERT(HvGetCellSize(Hive, pdata) > 0);
  1640. if (DataSize <= (ULONG)(HvGetCellSize(Hive, pdata))) {
  1641. //
  1642. // The existing data cell is big enough to hold the new data.
  1643. //
  1644. //
  1645. // we'll keep this cell
  1646. //
  1647. NewCell = DataCell;
  1648. } else {
  1649. //
  1650. // grow the existing cell
  1651. //
  1652. NewCell = HvReallocateCell(Hive,DataCell,DataSize);
  1653. if (NewCell == HCELL_NIL) {
  1654. status = STATUS_INSUFFICIENT_RESOURCES;
  1655. goto Exit;
  1656. }
  1657. }
  1658. } else {
  1659. //
  1660. // allocate a new cell
  1661. //
  1662. NewCell = HvAllocateCell(Hive, DataSize, StorageType,(HvGetCellType(OldChild)==StorageType)?OldChild:HCELL_NIL);
  1663. if (NewCell == HCELL_NIL) {
  1664. status = STATUS_INSUFFICIENT_RESOURCES;
  1665. goto Exit;
  1666. }
  1667. }
  1668. //
  1669. // now we have a cell that can accomodate the data
  1670. //
  1671. pdata = HvGetCell(Hive, NewCell);
  1672. if( pdata == NULL ) {
  1673. //
  1674. // we couldn't map a view for the bin containing this cell
  1675. //
  1676. // this shouldn't happen as we just allocated/ reallocated/ marked dirty this cell
  1677. //
  1678. ASSERT( FALSE );
  1679. status = STATUS_INSUFFICIENT_RESOURCES;
  1680. goto Exit;
  1681. }
  1682. // release it right here, as the registry is locked exclusively, so we don't care
  1683. HvReleaseCell(Hive, NewCell);
  1684. //
  1685. // copy the actual data
  1686. //
  1687. RtlCopyMemory(pdata,Data,DataSize);
  1688. Value->Data = NewCell;
  1689. Value->DataLength = DataSize;
  1690. Value->Type = Type;
  1691. // sanity
  1692. ASSERT_CELL_DIRTY(Hive,NewCell);
  1693. if( OldSizeType == 2 ) {
  1694. //
  1695. // old data was big; free it
  1696. //
  1697. ASSERT( OldDataCell != NewCell );
  1698. CmpFreeValueData(Hive,OldDataCell,OldRealSize);
  1699. }
  1700. status = STATUS_SUCCESS;
  1701. goto Exit;
  1702. }
  1703. //
  1704. // cases 7,8,9
  1705. //
  1706. if( NewSizeType == 2 ) {
  1707. if( OldSizeType == 2 ) {
  1708. //
  1709. // data was previously big; grow it!
  1710. //
  1711. status =CmpSetValueDataExisting(Hive,Data,DataSize,StorageType,OldDataCell);
  1712. if( !NT_SUCCESS(status) ) {
  1713. goto Exit;
  1714. }
  1715. NewCell = OldDataCell;
  1716. } else {
  1717. //
  1718. // data was small or normal.
  1719. // allocate and copy to a new big data cell;
  1720. // then free the old cell
  1721. //
  1722. status = CmpSetValueDataNew(Hive,Data,DataSize,StorageType,OldChild,&NewCell);
  1723. if( !NT_SUCCESS(status) ) {
  1724. //
  1725. // We have bombed out loading user data, clean up and exit.
  1726. //
  1727. goto Exit;
  1728. }
  1729. if( (OldSizeType != 0) && (OldRealSize != 0) ) {
  1730. //
  1731. // there is something to free
  1732. //
  1733. HvFreeCell(Hive, Value->Data);
  1734. }
  1735. }
  1736. Value->DataLength = DataSize;
  1737. Value->Data = NewCell;
  1738. Value->Type = Type;
  1739. // sanity
  1740. ASSERT_CELL_DIRTY(Hive,NewCell);
  1741. status = STATUS_SUCCESS;
  1742. goto Exit;
  1743. }
  1744. //
  1745. // we shouldn't get here
  1746. //
  1747. ASSERT( FALSE );
  1748. Exit:
  1749. if( hSecure) {
  1750. MmUnsecureVirtualMemory(hSecure);
  1751. }
  1752. return status;
  1753. }
  1754. NTSTATUS
  1755. CmpSetValueKeyNew(
  1756. IN PHHIVE Hive,
  1757. IN PCM_KEY_NODE Parent,
  1758. IN PUNICODE_STRING ValueName,
  1759. IN ULONG Index,
  1760. IN ULONG Type,
  1761. IN PVOID Data,
  1762. IN ULONG DataSize,
  1763. IN ULONG StorageType,
  1764. IN ULONG TempData
  1765. )
  1766. /*++
  1767. Routine Description:
  1768. Helper for CmSetValueKey, implements the case where the value entry
  1769. being set does not exist. Will create new value entry and data,
  1770. place in list (which may be created)
  1771. Arguments:
  1772. Hive - hive of interest
  1773. Parent - pointer to key node value entry is for
  1774. ValueName - The unique (relative to the containing key) name
  1775. of the value entry. May be NULL.
  1776. Index - where in the list should this value be inserted
  1777. Type - The integer type number of the value entry.
  1778. Data - Pointer to buffer with actual data for the value entry.
  1779. DataSize - Size of Data buffer.
  1780. StorageType - stable or volatile
  1781. TempData - small data values passed here
  1782. Return Value:
  1783. STATUS_SUCCESS if it worked, appropriate status code if it did not
  1784. --*/
  1785. {
  1786. PCELL_DATA pvalue;
  1787. HCELL_INDEX ValueCell;
  1788. NTSTATUS Status;
  1789. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  1790. //
  1791. // Either Count == 0 (no list) or our entry is simply not in
  1792. // the list. Create a new value entry body, and data. Add to list.
  1793. // (May create the list.)
  1794. //
  1795. if (Parent->ValueList.Count != 0) {
  1796. ASSERT(Parent->ValueList.List != HCELL_NIL);
  1797. if (! HvMarkCellDirty(Hive, Parent->ValueList.List)) {
  1798. return STATUS_NO_LOG_SPACE;
  1799. }
  1800. }
  1801. //
  1802. // allocate the body of the value entry, and the data
  1803. //
  1804. ValueCell = HvAllocateCell(
  1805. Hive,
  1806. CmpHKeyValueSize(Hive, ValueName),
  1807. StorageType,
  1808. HCELL_NIL
  1809. );
  1810. if (ValueCell == HCELL_NIL) {
  1811. return STATUS_INSUFFICIENT_RESOURCES;
  1812. }
  1813. //
  1814. // map in the body, and fill in its fixed portion
  1815. //
  1816. pvalue = HvGetCell(Hive, ValueCell);
  1817. if( pvalue == NULL ) {
  1818. //
  1819. // we couldn't map a view for the bin containing this cell
  1820. //
  1821. //
  1822. // normally this shouldn't happen as we just allocated ValueCell
  1823. // i.e. the bin containing ValueCell should be mapped in memory at this point.
  1824. //
  1825. ASSERT( FALSE );
  1826. HvFreeCell(Hive, ValueCell);
  1827. return STATUS_INSUFFICIENT_RESOURCES;
  1828. }
  1829. // release it right here, as the registry is locked exclusively, so we don't care
  1830. HvReleaseCell(Hive, ValueCell);
  1831. // sanity
  1832. ASSERT_CELL_DIRTY(Hive,ValueCell);
  1833. pvalue->u.KeyValue.Signature = CM_KEY_VALUE_SIGNATURE;
  1834. //
  1835. // fill in the variable portions of the new value entry, name and
  1836. // and data are copied from caller space, could fault.
  1837. //
  1838. try {
  1839. //
  1840. // fill in the name
  1841. //
  1842. pvalue->u.KeyValue.NameLength = CmpCopyName(Hive,
  1843. pvalue->u.KeyValue.Name,
  1844. ValueName);
  1845. } except (EXCEPTION_EXECUTE_HANDLER) {
  1846. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmSetValueKey: code:%08lx\n", GetExceptionCode()));
  1847. //
  1848. // We have bombed out loading user data, clean up and exit.
  1849. //
  1850. HvFreeCell(Hive, ValueCell);
  1851. return GetExceptionCode();
  1852. }
  1853. if (pvalue->u.KeyValue.NameLength < ValueName->Length) {
  1854. pvalue->u.KeyValue.Flags = VALUE_COMP_NAME;
  1855. } else {
  1856. pvalue->u.KeyValue.Flags = 0;
  1857. }
  1858. //
  1859. // fill in the data
  1860. //
  1861. if (DataSize > CM_KEY_VALUE_SMALL) {
  1862. Status = CmpSetValueDataNew(Hive,Data,DataSize,StorageType,ValueCell,&(pvalue->u.KeyValue.Data));
  1863. if( !NT_SUCCESS(Status) ) {
  1864. //
  1865. // We have bombed out loading user data, clean up and exit.
  1866. //
  1867. HvFreeCell(Hive, ValueCell);
  1868. return Status;
  1869. }
  1870. pvalue->u.KeyValue.DataLength = DataSize;
  1871. // sanity
  1872. ASSERT_CELL_DIRTY(Hive,pvalue->u.KeyValue.Data);
  1873. } else {
  1874. pvalue->u.KeyValue.DataLength = DataSize + CM_KEY_VALUE_SPECIAL_SIZE;
  1875. pvalue->u.KeyValue.Data = TempData;
  1876. }
  1877. pvalue->u.KeyValue.Type = Type;
  1878. if( !NT_SUCCESS(CmpAddValueToList(Hive,ValueCell,Index,StorageType,&(Parent->ValueList)) ) ) {
  1879. // out of space, free all allocated stuff
  1880. // this will free embeded cigdata cell info too (if any)
  1881. CmpFreeValue(Hive,ValueCell);
  1882. return STATUS_INSUFFICIENT_RESOURCES;
  1883. }
  1884. return STATUS_SUCCESS;
  1885. }
  1886. NTSTATUS
  1887. CmSetLastWriteTimeKey(
  1888. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  1889. IN PLARGE_INTEGER LastWriteTime
  1890. )
  1891. /*++
  1892. Routine Description:
  1893. The LastWriteTime associated with a key node can be set with
  1894. CmSetLastWriteTimeKey
  1895. Arguments:
  1896. KeyControlBlock - pointer to kcb for the key to operate on
  1897. LastWriteTime - new time for key
  1898. Return Value:
  1899. NTSTATUS - Result code from call, among the following:
  1900. <TBS>
  1901. --*/
  1902. {
  1903. PCM_KEY_NODE parent;
  1904. PHHIVE Hive;
  1905. HCELL_INDEX Cell;
  1906. NTSTATUS status = STATUS_SUCCESS;
  1907. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmSetLastWriteTimeKey\n"));
  1908. CmpLockRegistryExclusive();
  1909. //
  1910. // Check that we are not being asked to modify a key
  1911. // that has been deleted
  1912. //
  1913. if (KeyControlBlock->Delete == TRUE) {
  1914. status = STATUS_KEY_DELETED;
  1915. goto Exit;
  1916. }
  1917. Hive = KeyControlBlock->KeyHive;
  1918. Cell = KeyControlBlock->KeyCell;
  1919. parent = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
  1920. if( parent == NULL ) {
  1921. //
  1922. // we couldn't map a view for the bin containing this cell
  1923. //
  1924. status = STATUS_INSUFFICIENT_RESOURCES;
  1925. goto Exit;
  1926. }
  1927. // release the cell right here, as the registry is locked exclusively, so we don't care
  1928. HvReleaseCell(Hive, Cell);
  1929. if (! HvMarkCellDirty(Hive, Cell)) {
  1930. status = STATUS_NO_LOG_SPACE;
  1931. goto Exit;
  1932. }
  1933. parent->LastWriteTime = *LastWriteTime;
  1934. // update the kcb cache too.
  1935. KeyControlBlock->KcbLastWriteTime = *LastWriteTime;
  1936. Exit:
  1937. CmpUnlockRegistry();
  1938. return status;
  1939. }
  1940. NTSTATUS
  1941. CmSetKeyUserFlags(
  1942. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  1943. IN ULONG UserFlags
  1944. )
  1945. /*++
  1946. Routine Description:
  1947. Sets the user defined flags for the key; At this point there are only
  1948. 4 bits reserved for user defined flags. kcb and knode must be kept in
  1949. sync.
  1950. Arguments:
  1951. KeyControlBlock - pointer to kcb for the key to operate on
  1952. UserFlags - user defined flags to be set on this key.
  1953. Return Value:
  1954. NTSTATUS - Result code from call, among the following:
  1955. <TBS>
  1956. --*/
  1957. {
  1958. PCM_KEY_NODE Node;
  1959. PHHIVE Hive;
  1960. HCELL_INDEX Cell;
  1961. LARGE_INTEGER LastWriteTime;
  1962. NTSTATUS status = STATUS_SUCCESS;
  1963. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmSetKeyUserFlags\n"));
  1964. CmpLockRegistryExclusive();
  1965. //
  1966. // Check that we are not being asked to modify a key
  1967. // that has been deleted
  1968. //
  1969. if (KeyControlBlock->Delete == TRUE) {
  1970. status = STATUS_KEY_DELETED;
  1971. goto Exit;
  1972. }
  1973. if( UserFlags & (~((ULONG)KEY_USER_FLAGS_VALID_MASK)) ) {
  1974. //
  1975. // number of user defined flags exceeded; punt
  1976. //
  1977. status = STATUS_INVALID_PARAMETER;
  1978. goto Exit;
  1979. }
  1980. Hive = KeyControlBlock->KeyHive;
  1981. Cell = KeyControlBlock->KeyCell;
  1982. Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
  1983. if( Node == NULL ) {
  1984. //
  1985. // we couldn't map a view for the bin containing this cell
  1986. //
  1987. status = STATUS_INSUFFICIENT_RESOURCES;
  1988. goto Exit;
  1989. }
  1990. // release the cell right here, as the registry is locked exclusively, so we don't care
  1991. HvReleaseCell(Hive, Cell);
  1992. if (! HvMarkCellDirty(Hive, Cell)) {
  1993. status = STATUS_NO_LOG_SPACE;
  1994. goto Exit;
  1995. }
  1996. //
  1997. // shift/(pack) the user defined flags and
  1998. // update knode and kcb cache
  1999. //
  2000. // first, erase the old flags
  2001. Node->Flags &= KEY_USER_FLAGS_CLEAR_MASK;
  2002. Node->Flags |= (USHORT)(UserFlags<<KEY_USER_FLAGS_SHIFT);
  2003. // update the kcb cache
  2004. KeyControlBlock->Flags = Node->Flags;
  2005. //
  2006. // we need to update the LstWriteTime as well
  2007. //
  2008. KeQuerySystemTime(&LastWriteTime);
  2009. Node->LastWriteTime = LastWriteTime;
  2010. // update the kcb cache too.
  2011. KeyControlBlock->KcbLastWriteTime = LastWriteTime;
  2012. Exit:
  2013. CmpUnlockRegistry();
  2014. return status;
  2015. }
  2016. BOOLEAN
  2017. CmpIsHiveAlreadyLoaded( IN HANDLE KeyHandle,
  2018. IN POBJECT_ATTRIBUTES SourceFile,
  2019. OUT PCMHIVE *CmHive
  2020. )
  2021. /*++
  2022. Routine Description:
  2023. Checks if the SourceFile is already loaded in the same spot as KeyHandle.
  2024. Arguments:
  2025. KeyHandle - should be the root of a hive. We'll query the name of the primary file
  2026. and compare it against the name of SourceFile
  2027. SourceFile - specifies a file. while file could be remote,
  2028. that is strongly discouraged.
  2029. Return Value:
  2030. TRUE/FALSE
  2031. --*/
  2032. {
  2033. NTSTATUS status;
  2034. PCM_KEY_BODY KeyBody;
  2035. BOOLEAN Result = FALSE; // pesimistic
  2036. PAGED_CODE();
  2037. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  2038. status = ObReferenceObjectByHandle(KeyHandle,
  2039. 0,
  2040. CmpKeyObjectType,
  2041. KernelMode,
  2042. (PVOID *)(&KeyBody),
  2043. NULL);
  2044. if(!NT_SUCCESS(status)) {
  2045. return FALSE;
  2046. }
  2047. if( KeyBody->KeyControlBlock->Delete ) {
  2048. return FALSE;
  2049. }
  2050. *CmHive = (PCMHIVE)CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive);
  2051. //
  2052. // should be the root of a hive
  2053. //
  2054. if( !(KeyBody->KeyControlBlock->Flags & KEY_HIVE_ENTRY) || // not root of a hive
  2055. ((*CmHive)->FileUserName.Buffer == NULL)// no name captured
  2056. ) {
  2057. goto ExitCleanup;
  2058. }
  2059. if( RtlCompareUnicodeString(&((*CmHive)->FileUserName),
  2060. SourceFile->ObjectName,
  2061. TRUE) == 0 ) {
  2062. //
  2063. // same file; same spot
  2064. //
  2065. Result = TRUE;
  2066. //
  2067. // unfreeze the hive;hive will become just a regular hive from now on
  2068. // it is safe to do this because we hold an extra refcount on the root of the hive
  2069. // as we have specifically opened the root to check if it's already loaded
  2070. //
  2071. if( IsHiveFrozen(*CmHive) ) {
  2072. (*CmHive)->Frozen = FALSE;
  2073. if( (*CmHive)->UnloadWorkItem != NULL ) {
  2074. ExFreePool( (*CmHive)->UnloadWorkItem );
  2075. (*CmHive)->UnloadWorkItem = NULL;
  2076. }
  2077. if( (*CmHive)->RootKcb ) {
  2078. CmpDereferenceKeyControlBlockWithLock((*CmHive)->RootKcb);
  2079. (*CmHive)->RootKcb = NULL;
  2080. }
  2081. }
  2082. }
  2083. ExitCleanup:
  2084. ObDereferenceObject((PVOID)KeyBody);
  2085. return Result;
  2086. }
  2087. NTSTATUS
  2088. CmLoadKey(
  2089. IN POBJECT_ATTRIBUTES TargetKey,
  2090. IN POBJECT_ATTRIBUTES SourceFile,
  2091. IN ULONG Flags,
  2092. IN PCM_KEY_BODY KeyBody
  2093. )
  2094. /*++
  2095. Routine Description:
  2096. A hive (file in the format created by NtSaveKey) may be linked
  2097. into the active registry with this call. UNLIKE NtRestoreKey,
  2098. the file specified to NtLoadKey will become the actual backing
  2099. store of part of the registry (that is, it will NOT be copied.)
  2100. The file may have an associated .log file.
  2101. If the hive file is marked as needing a .log file, and one is
  2102. not present, the call will fail.
  2103. The name specified by SourceFile must be such that ".log" can
  2104. be appended to it to generate the name of the log file. Thus,
  2105. on FAT file systems, the hive file may not have an extension.
  2106. This call is used by logon to make the user's profile available
  2107. in the registry. It is not intended for use doing backup,
  2108. restore, etc. Use NtRestoreKey for that.
  2109. N.B. This routine assumes that the object attributes for the file
  2110. to be opened have been captured into kernel space so that
  2111. they can safely be passed to the worker thread to open the file
  2112. and do the actual I/O.
  2113. Arguments:
  2114. TargetKey - specifies the path to a key to link the hive to.
  2115. path must be of the form "\registry\user\<username>"
  2116. SourceFile - specifies a file. while file could be remote,
  2117. that is strongly discouraged.
  2118. Flags - specifies any flags that should be used for the load operation.
  2119. The only valid flag is REG_NO_LAZY_FLUSH.
  2120. Return Value:
  2121. NTSTATUS - values TBS.
  2122. --*/
  2123. {
  2124. PCMHIVE NewHive;
  2125. NTSTATUS Status;
  2126. BOOLEAN Allocate;
  2127. BOOLEAN RegistryLockAquired;
  2128. SECURITY_QUALITY_OF_SERVICE ServiceQos;
  2129. SECURITY_CLIENT_CONTEXT ClientSecurityContext;
  2130. HANDLE KeyHandle;
  2131. PCMHIVE OtherHive = NULL;
  2132. CM_PARSE_CONTEXT ParseContext;
  2133. if( KeyBody != NULL ) {
  2134. OtherHive = (PCMHIVE)CONTAINING_RECORD(KeyBody->KeyControlBlock->KeyHive, CMHIVE, Hive);
  2135. if( ! (OtherHive->Flags & CM_CMHIVE_FLAG_UNTRUSTED) ) {
  2136. //
  2137. // deny attempts to join the TRUSTED class of trust
  2138. //
  2139. return STATUS_INVALID_PARAMETER;
  2140. }
  2141. }
  2142. //
  2143. // Obtain the security context here so we can use it
  2144. // later to impersonate the user, which we will do
  2145. // if we cannot access the file as SYSTEM. This
  2146. // usually occurs if the file is on a remote machine.
  2147. //
  2148. ServiceQos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  2149. ServiceQos.ImpersonationLevel = SecurityImpersonation;
  2150. ServiceQos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  2151. ServiceQos.EffectiveOnly = TRUE;
  2152. Status = SeCreateClientSecurity(CONTAINING_RECORD(KeGetCurrentThread(),ETHREAD,Tcb),
  2153. &ServiceQos,
  2154. FALSE,
  2155. &ClientSecurityContext);
  2156. if (!NT_SUCCESS(Status)) {
  2157. return(Status);
  2158. }
  2159. RtlZeroMemory(&ParseContext,sizeof(CM_PARSE_CONTEXT));
  2160. ParseContext.CreateOperation = FALSE;
  2161. //
  2162. // we open the root of the hive here. if it already exists,this will prevent it from going
  2163. // away from under us while we are doing the "already loaded" check (due to delay unload logic)
  2164. //
  2165. Status = ObOpenObjectByName(TargetKey,
  2166. CmpKeyObjectType,
  2167. KernelMode,
  2168. NULL,
  2169. KEY_READ,
  2170. (PVOID)&ParseContext,
  2171. &KeyHandle);
  2172. if(!NT_SUCCESS(Status)) {
  2173. KeyHandle = NULL;
  2174. }
  2175. //
  2176. // Do not lock the registry; Instead set the RegistryLockAquired member
  2177. // of REGISTRY_COMMAND so CmpWorker can lock it after opening the hive files
  2178. //
  2179. //CmpLockRegistryExclusive();
  2180. //
  2181. RegistryLockAquired = FALSE;
  2182. Allocate = TRUE;
  2183. Status = CmpCmdHiveOpen( SourceFile, // FileAttributes
  2184. &ClientSecurityContext, // ImpersonationContext
  2185. &Allocate, // Allocate
  2186. &RegistryLockAquired, // RegistryLockAquired
  2187. &NewHive, // NewHive
  2188. CM_CHECK_REGISTRY_CHECK_CLEAN //CheckFlags
  2189. );
  2190. SeDeleteClientSecurity( &ClientSecurityContext );
  2191. if (!NT_SUCCESS(Status)) {
  2192. if( KeyHandle != NULL ) {
  2193. PCMHIVE LoadedHive = NULL;
  2194. //
  2195. // lock the registry exclusive while we are checking attempt to load same file into the same spot
  2196. //
  2197. if( !RegistryLockAquired ) {
  2198. CmpLockRegistryExclusive();
  2199. RegistryLockAquired = TRUE;
  2200. }
  2201. //
  2202. // check if the same file is loaded in the same spot
  2203. //
  2204. if( CmpIsHiveAlreadyLoaded(KeyHandle,SourceFile,&LoadedHive) ) {
  2205. ASSERT( LoadedHive );
  2206. if( OtherHive != NULL ) {
  2207. //
  2208. // unjoin the existing class (if any) and join the new one
  2209. //
  2210. CmpUnJoinClassOfTrust(LoadedHive);
  2211. CmpJoinClassOfTrust(LoadedHive,OtherHive);
  2212. LoadedHive->Flags |= CM_CMHIVE_FLAG_UNTRUSTED;
  2213. }
  2214. Status = STATUS_SUCCESS;
  2215. }
  2216. }
  2217. if( RegistryLockAquired ) {
  2218. // if CmpWorker has locked the registry, unlock it now.
  2219. CmpUnlockRegistry();
  2220. }
  2221. if( KeyHandle != NULL ) {
  2222. ZwClose(KeyHandle);
  2223. }
  2224. return(Status);
  2225. } else {
  2226. //
  2227. // if we got here, CmpWorker should have locked the registry exclusive.
  2228. //
  2229. ASSERT( RegistryLockAquired );
  2230. }
  2231. //
  2232. // if this is a NO_LAZY_FLUSH hive, set the appropriate bit.
  2233. //
  2234. if (Flags & REG_NO_LAZY_FLUSH) {
  2235. NewHive->Hive.HiveFlags |= HIVE_NOLAZYFLUSH;
  2236. }
  2237. //
  2238. // mark the hive as untrusted
  2239. //
  2240. NewHive->Flags |= CM_CMHIVE_FLAG_UNTRUSTED;
  2241. if( OtherHive != NULL ) {
  2242. //
  2243. // join the same class of trust with the otherhive
  2244. //
  2245. CmpJoinClassOfTrust(NewHive,OtherHive);
  2246. }
  2247. //
  2248. // We now have a succesfully loaded and initialized CmHive, so we
  2249. // just need to link that into the appropriate spot in the master hive.
  2250. //
  2251. Status = CmpLinkHiveToMaster(TargetKey->ObjectName,
  2252. TargetKey->RootDirectory,
  2253. NewHive,
  2254. Allocate,
  2255. TargetKey->SecurityDescriptor);
  2256. if (NT_SUCCESS(Status)) {
  2257. //
  2258. // add new hive to hivelist
  2259. //
  2260. CmpAddToHiveFileList(NewHive);
  2261. //
  2262. // flush the hive right here if just created; this is to avoid situations where
  2263. // the lazy flusher doesn't get a chance to flush the hive, or it can't (because
  2264. // the hive is a no_lazy_flush hive and it is never explicitly flushed)
  2265. //
  2266. if( Allocate == TRUE ) {
  2267. HvSyncHive(&(NewHive->Hive));
  2268. }
  2269. } else {
  2270. LOCK_HIVE_LIST();
  2271. CmpRemoveEntryList(&(NewHive->HiveList));
  2272. UNLOCK_HIVE_LIST();
  2273. CmpCheckForOrphanedKcbs((PHHIVE)NewHive);
  2274. CmpDestroyHiveViewList(NewHive);
  2275. CmpDestroySecurityCache (NewHive);
  2276. CmpDropFileObjectForHive(NewHive);
  2277. CmpUnJoinClassOfTrust(NewHive);
  2278. HvFreeHive((PHHIVE)NewHive);
  2279. //
  2280. // Close the hive files
  2281. //
  2282. CmpCmdHiveClose(NewHive);
  2283. //
  2284. // free the cm level structure
  2285. //
  2286. ASSERT( NewHive->HiveLock );
  2287. ExFreePool(NewHive->HiveLock);
  2288. ASSERT( NewHive->ViewLock );
  2289. ExFreePool(NewHive->ViewLock);
  2290. CmpFree(NewHive, sizeof(CMHIVE));
  2291. }
  2292. //
  2293. // We've given user chance to log on, so turn on quota
  2294. //
  2295. if ((CmpProfileLoaded == FALSE) &&
  2296. (CmpWasSetupBoot == FALSE)) {
  2297. CmpProfileLoaded = TRUE;
  2298. CmpSetGlobalQuotaAllowed();
  2299. }
  2300. #ifdef CHECK_REGISTRY_USECOUNT
  2301. CmpCheckRegistryUseCount();
  2302. #endif //CHECK_REGISTRY_USECOUNT
  2303. CmpUnlockRegistry();
  2304. if( KeyHandle != NULL ) {
  2305. ZwClose(KeyHandle);
  2306. }
  2307. return(Status);
  2308. }
  2309. #if DBG
  2310. ULONG
  2311. CmpUnloadKeyWorker(
  2312. PCM_KEY_CONTROL_BLOCK Current,
  2313. PVOID Context1,
  2314. PVOID Context2
  2315. )
  2316. {
  2317. PUNICODE_STRING ConstructedName;
  2318. UNREFERENCED_PARAMETER (Context2);
  2319. if (Current->KeyHive == Context1) {
  2320. ConstructedName = CmpConstructName(Current);
  2321. if (ConstructedName) {
  2322. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"%wZ\n", ConstructedName));
  2323. ExFreePoolWithTag(ConstructedName, CM_NAME_TAG | PROTECTED_POOL);
  2324. }
  2325. }
  2326. return KCB_WORKER_CONTINUE; // always keep searching
  2327. }
  2328. #endif
  2329. NTSTATUS
  2330. CmUnloadKey(
  2331. IN PHHIVE Hive,
  2332. IN HCELL_INDEX Cell,
  2333. IN PCM_KEY_CONTROL_BLOCK Kcb,
  2334. IN ULONG Flags
  2335. )
  2336. /*++
  2337. Routine Description:
  2338. Unlinks a hive from its location in the registry, closes its file
  2339. handles, and deallocates all its memory.
  2340. There must be no key control blocks currently referencing the hive
  2341. to be unloaded.
  2342. Arguments:
  2343. Hive - Supplies a pointer to the hive control structure for the
  2344. hive to be unloaded
  2345. Cell - supplies the HCELL_INDEX for the root cell of the hive.
  2346. Kcb - Supplies the key control block
  2347. Flags - REG_FORCE_UNLOAD will first mark open handles as invalid
  2348. and then unload the hive.
  2349. Return Value:
  2350. NTSTATUS
  2351. --*/
  2352. {
  2353. PCMHIVE CmHive;
  2354. LOGICAL Success;
  2355. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmUnloadKey\n"));
  2356. //
  2357. // Make sure the cell passed in is the root cell of the hive.
  2358. //
  2359. if((Cell != Hive->BaseBlock->RootCell) || ((PCMHIVE)Hive == CmpMasterHive)) {
  2360. return(STATUS_INVALID_PARAMETER);
  2361. }
  2362. //
  2363. // Make sure there are no open references to key control blocks
  2364. // for this hive. If there are none, then we can unload the hive.
  2365. //
  2366. CmHive = CONTAINING_RECORD(Hive, CMHIVE, Hive);
  2367. if(Kcb->RefCount != 1) {
  2368. if( Flags == REG_FORCE_UNLOAD ) {
  2369. //
  2370. // this will mark open handles as invalid.
  2371. //
  2372. CmpSearchForOpenSubKeys(Kcb, SearchAndDeref,NULL);
  2373. } else {
  2374. Success = (CmpSearchForOpenSubKeys(Kcb,SearchIfExist,NULL) == 0);
  2375. Success = Success && (Kcb->RefCount == 1);
  2376. if( Success == FALSE) {
  2377. #if DBG
  2378. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"List of keys open against hive unload was attempted on:\n"));
  2379. CmpSearchKeyControlBlockTree(
  2380. CmpUnloadKeyWorker,
  2381. Hive,
  2382. NULL
  2383. );
  2384. #endif
  2385. return STATUS_CANNOT_DELETE;
  2386. }
  2387. ASSERT( Kcb->RefCount == 1 );
  2388. }
  2389. }
  2390. //
  2391. // Flush any dirty data to disk. If this fails, too bad.
  2392. //
  2393. CmFlushKey(Hive, Cell);
  2394. //
  2395. // Remove the hive from the HiveFileList
  2396. //
  2397. CmpRemoveFromHiveFileList((PCMHIVE)Hive);
  2398. //
  2399. // Unlink from master hive, remove from list
  2400. //
  2401. Success = CmpDestroyHive(Hive, Cell);
  2402. if (Success) {
  2403. //
  2404. // signal the user event (if any), then do the cleanup (i.e. deref the event
  2405. // and the artificial refcount we set on the root kcb)
  2406. //
  2407. if( CmHive->UnloadEvent != NULL ) {
  2408. KeSetEvent(CmHive->UnloadEvent,0,FALSE);
  2409. ObDereferenceObject(CmHive->UnloadEvent);
  2410. }
  2411. CmpDestroyHiveViewList(CmHive);
  2412. CmpDestroySecurityCache (CmHive);
  2413. CmpDropFileObjectForHive(CmHive);
  2414. CmpUnJoinClassOfTrust(CmHive);
  2415. HvFreeHive(Hive);
  2416. //
  2417. // Close the hive files
  2418. //
  2419. CmpCmdHiveClose(CmHive);
  2420. //
  2421. // free the cm level structure
  2422. //
  2423. ASSERT( CmHive->HiveLock );
  2424. ExFreePool(CmHive->HiveLock);
  2425. ASSERT( CmHive->ViewLock );
  2426. ExFreePool(CmHive->ViewLock);
  2427. CmpFree(CmHive, sizeof(CMHIVE));
  2428. return(STATUS_SUCCESS);
  2429. } else {
  2430. return(STATUS_INSUFFICIENT_RESOURCES);
  2431. }
  2432. }
  2433. #ifdef NT_UNLOAD_KEY_EX
  2434. NTSTATUS
  2435. CmUnloadKeyEx(
  2436. IN PCM_KEY_CONTROL_BLOCK kcb,
  2437. IN PKEVENT UserEvent
  2438. )
  2439. /*++
  2440. Routine Description:
  2441. First tries to unlink the hive, by calling the sync version
  2442. If the hive cannot be unloaded (there are open handles inside it),
  2443. reference the root of the hive (i.e. kcb) and freeze the hive.
  2444. Arguments:
  2445. Kcb - Supplies the key control block
  2446. UserEvent - the event to be signaled after the hive was unloaded
  2447. (only if late - unload is needed)
  2448. Return Value:
  2449. STATUS_PENDING - the hive was frozen and it'll be unloaded later
  2450. STATUS_SUCCESS - the hive was successfully sync-unloaded (no need
  2451. to signal for UserEvent)
  2452. <other> - an error occured, operation failed
  2453. --*/
  2454. {
  2455. PCMHIVE CmHive;
  2456. HCELL_INDEX Cell;
  2457. NTSTATUS Status;
  2458. PAGED_CODE();
  2459. Cell = kcb->KeyCell;
  2460. CmHive = (PCMHIVE)CONTAINING_RECORD(kcb->KeyHive, CMHIVE, Hive);
  2461. if( IsHiveFrozen(CmHive) ) {
  2462. //
  2463. // don't let them hurt themselves by calling it twice
  2464. //
  2465. return STATUS_TOO_LATE;
  2466. }
  2467. //
  2468. // first, try out he sync routine; this may or may not unload the hive,
  2469. // but at least will kick kcbs with refcount = 0 out of cache
  2470. //
  2471. Status = CmUnloadKey(&(CmHive->Hive),Cell,kcb,0);
  2472. if( Status != STATUS_CANNOT_DELETE ) {
  2473. //
  2474. // the hive was either unloaded, or some bad thing happened
  2475. //
  2476. return Status;
  2477. }
  2478. ASSERT( kcb->RefCount > 1 );
  2479. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  2480. //
  2481. // Prepare for late-unloading:
  2482. // 1. reference the kcb, to make sure it won't go away without us noticing
  2483. // (we have the registry locked in exclusive mode, so we don't need to lock the kcbtree
  2484. //
  2485. if (!CmpReferenceKeyControlBlock(kcb)) {
  2486. return STATUS_INSUFFICIENT_RESOURCES;
  2487. }
  2488. //
  2489. // parse the kcb tree and mark all open kcbs inside this hive and "no delay close"
  2490. //
  2491. CmpSearchForOpenSubKeys(kcb,SearchAndTagNoDelayClose,NULL);
  2492. kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE;
  2493. //
  2494. // 2. Freeze the hive
  2495. //
  2496. CmHive->RootKcb = kcb;
  2497. CmHive->Frozen = TRUE;
  2498. CmHive->UnloadEvent = UserEvent;
  2499. return STATUS_PENDING;
  2500. }
  2501. #endif //NT_UNLOAD_KEY_EX
  2502. // define in cmworker.c
  2503. extern BOOLEAN CmpForceForceFlush;
  2504. BOOLEAN
  2505. CmpDoFlushAll(
  2506. BOOLEAN ForceFlush
  2507. )
  2508. /*++
  2509. Routine Description:
  2510. Flush all hives.
  2511. Runs down list of Hives and applies HvSyncHive to them.
  2512. NOTE: Hives which are marked as HV_NOLAZYFLUSH are *NOT* flushed
  2513. by this call. You must call HvSyncHive explicitly to flush
  2514. a hive marked as HV_NOLAZYFLUSH.
  2515. Arguments:
  2516. ForceFlush - used as a contingency plan when a prior exception left
  2517. some hive in a used state. When set to TRUE, assumes the
  2518. registry is locked exclusive. It also repairs the broken
  2519. hives.
  2520. - When FALSE saves only the hives with UseCount == 0.
  2521. Return Value:
  2522. NONE
  2523. Notes:
  2524. If any of the hives is about to shrink CmpForceForceFlush is set to TRUE,
  2525. otherwise, it is set to FALSE
  2526. --*/
  2527. {
  2528. NTSTATUS Status;
  2529. PLIST_ENTRY p;
  2530. PCMHIVE h;
  2531. BOOLEAN Result = TRUE;
  2532. /*
  2533. ULONG rc;
  2534. */
  2535. extern PCMHIVE CmpMasterHive;
  2536. //
  2537. // If writes are not working, lie and say we succeeded, will
  2538. // clean up in a short time. Only early system init code
  2539. // will ever know the difference.
  2540. //
  2541. if (CmpNoWrite) {
  2542. return TRUE;
  2543. }
  2544. CmpForceForceFlush = FALSE;
  2545. //
  2546. // traverse list of hives, sync each one
  2547. //
  2548. LOCK_HIVE_LIST();
  2549. p = CmpHiveListHead.Flink;
  2550. while (p != &CmpHiveListHead) {
  2551. h = CONTAINING_RECORD(p, CMHIVE, HiveList);
  2552. if (!(h->Hive.HiveFlags & HIVE_NOLAZYFLUSH)) {
  2553. //
  2554. //Lock the hive before we flush it.
  2555. //-- since we now allow multiple readers
  2556. // during a flush (a flush is considered a read)
  2557. // we have to force a serialization on the vector table
  2558. //
  2559. CmLockHive (h);
  2560. if( (ForceFlush == TRUE) && (h->UseCount != 0) ) {
  2561. //
  2562. // hive was left in an instable state by a prior exception raised
  2563. // somewhere inside a CM function.
  2564. //
  2565. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  2566. CmpFixHiveUsageCount(h);
  2567. ASSERT( h->UseCount == 0 );
  2568. }
  2569. if( (ForceFlush == TRUE) || (!HvHiveWillShrink((PHHIVE)h)) ) {
  2570. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpDoFlushAll hive = %p ForceFlush = %lu IsHiveShrinking = %lu BaseLength = %lx StableLength = %lx\n",
  2571. h,(ULONG)ForceFlush,(ULONG)HvHiveWillShrink((PHHIVE)h),((PHHIVE)h)->BaseBlock->Length,((PHHIVE)h)->Storage[Stable].Length));
  2572. Status = HvSyncHive((PHHIVE)h);
  2573. if( !NT_SUCCESS( Status ) ) {
  2574. Result = FALSE;
  2575. }
  2576. } else {
  2577. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpDoFlushAll: Fail to flush hive %p because is shrinking\n",h));
  2578. Result = FALSE;
  2579. //
  2580. // another unsuccessful attempt to save this hive, because we needed the reglock exclusive
  2581. //
  2582. CmpForceForceFlush = TRUE;
  2583. }
  2584. CmUnlockHive (h);
  2585. //
  2586. // WARNNOTE - the above means that a lazy flush or
  2587. // or shutdown flush did not work. we don't
  2588. // know why. there is noone to report an error
  2589. // to, so continue on and hope for the best.
  2590. // (in theory, worst that can happen is user changes
  2591. // are lost.)
  2592. //
  2593. }
  2594. p = p->Flink;
  2595. }
  2596. UNLOCK_HIVE_LIST();
  2597. return Result;
  2598. }
  2599. extern ULONG CmpLazyFlushCount;
  2600. extern ULONG CmpLazyFlushHiveCount;
  2601. BOOLEAN
  2602. CmpDoFlushNextHive(
  2603. BOOLEAN ForceFlush,
  2604. PBOOLEAN PostWarning,
  2605. PULONG DirtyCount
  2606. )
  2607. /*++
  2608. Routine Description:
  2609. Flush next hive in list with FlushCount != CmpLazyFlushCount
  2610. Runs in the context of the CmpWorkerThread.
  2611. Runs down list of Hives until it finds the first one with that was not yet flushed
  2612. by the lazy flusher (ie. has its flush count lesser than the lazy flusher count)
  2613. NOTE: Hives which are marked as HV_NOLAZYFLUSH are *NOT* flushed
  2614. by this call. You must call HvSyncHive explicitly to flush
  2615. a hive marked as HV_NOLAZYFLUSH.
  2616. Arguments:
  2617. ForceFlush - used as a contingency plan when a prior exception left
  2618. some hive in a used state. When set to TRUE, assumes the
  2619. registry is locked exclusive. It also repairs the broken
  2620. hives.
  2621. - When FALSE saves only the hives with UseCount == 0.
  2622. Return Value:
  2623. TRUE - if there are more hives to flush
  2624. FALSE - otherwise
  2625. Notes:
  2626. If any of the hives is about to shrink CmpForceForceFlush is set to TRUE,
  2627. otherwise, it is set to FALSE
  2628. --*/
  2629. {
  2630. NTSTATUS Status;
  2631. PLIST_ENTRY p;
  2632. PCMHIVE h;
  2633. BOOLEAN Result;
  2634. ULONG HiveCount = CmpLazyFlushHiveCount;
  2635. extern PCMHIVE CmpMasterHive;
  2636. *PostWarning = FALSE;
  2637. *DirtyCount = 0;
  2638. //
  2639. // If writes are not working, lie and say we succeeded, will
  2640. // clean up in a short time. Only early system init code
  2641. // will ever know the difference.
  2642. //
  2643. if (CmpNoWrite) {
  2644. return TRUE;
  2645. }
  2646. //
  2647. // flush at least one hive
  2648. //
  2649. if( !HiveCount ) {
  2650. HiveCount = 1;
  2651. }
  2652. CmpForceForceFlush = FALSE;
  2653. //
  2654. // traverse list of hives, sync each one
  2655. //
  2656. LOCK_HIVE_LIST();
  2657. p = CmpHiveListHead.Flink;
  2658. while (p != &CmpHiveListHead) {
  2659. h = CONTAINING_RECORD(p, CMHIVE, HiveList);
  2660. if (!(h->Hive.HiveFlags & HIVE_NOLAZYFLUSH) && // lazy flush is notspecifically disabled on this hive
  2661. (h->FlushCount != CmpLazyFlushCount) // and it was not already flushed during this iteration
  2662. ) {
  2663. #if 0
  2664. {
  2665. UNICODE_STRING HiveName;
  2666. RtlInitUnicodeString(&HiveName, (PCWSTR)h->Hive.BaseBlock->FileName);
  2667. DbgPrint("CmpDoFlushNextHive : Hive = (%32.*S); FC = %lx ...",HiveName.Length / sizeof(WCHAR),HiveName.Buffer,h->FlushCount);
  2668. }
  2669. #endif
  2670. Result = TRUE;
  2671. //
  2672. //Lock the hive before we flush it.
  2673. //-- since we now allow multiple readers
  2674. // during a flush (a flush is considered a read)
  2675. // we have to force a serialization on the vector table
  2676. //
  2677. CmLockHive (h);
  2678. if( (h->Hive.DirtyCount == 0) || (h->Hive.HiveFlags & HIVE_VOLATILE) ) {
  2679. //
  2680. // if the hive is volatile or has no dirty data, just skip it.
  2681. // silently update the flush count
  2682. //
  2683. h->FlushCount = CmpLazyFlushCount;
  2684. #if 0
  2685. DbgPrint(" skipping it ...");
  2686. #endif
  2687. } else {
  2688. if( (ForceFlush == TRUE) && (h->UseCount != 0) ) {
  2689. //
  2690. // hive was left in an instable state by a prior exception raised
  2691. // somewhere inside a CM function.
  2692. //
  2693. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  2694. CmpFixHiveUsageCount(h);
  2695. ASSERT( h->UseCount == 0 );
  2696. }
  2697. if( (ForceFlush == TRUE) || (!HvHiveWillShrink((PHHIVE)h)) ) {
  2698. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpDoFlushAll hive = %p ForceFlush = %lu IsHiveShrinking = %lu BaseLength = %lx StableLength = %lx\n",
  2699. h,(ULONG)ForceFlush,(ULONG)HvHiveWillShrink((PHHIVE)h),((PHHIVE)h)->BaseBlock->Length,((PHHIVE)h)->Storage[Stable].Length));
  2700. Status = HvSyncHive((PHHIVE)h);
  2701. if( !NT_SUCCESS( Status ) ) {
  2702. *PostWarning = TRUE;
  2703. Result = FALSE;
  2704. }
  2705. } else {
  2706. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpDoFlushAll: Fail to flush hive %p because is shrinking\n",h));
  2707. Result = FALSE;
  2708. //
  2709. // another unsuccessful attempt to save this hive, because we needed the reglock exclusive
  2710. //
  2711. CmpForceForceFlush = TRUE;
  2712. }
  2713. if( Result == TRUE ) {
  2714. #if 0
  2715. DbgPrint(" flushed sucessfuly");
  2716. DbgPrint(" \t GLFC = %lx\n",CmpLazyFlushCount);
  2717. #endif
  2718. //
  2719. // we have successfully flushed current hive hive
  2720. //
  2721. h->FlushCount = CmpLazyFlushCount;
  2722. HiveCount--;
  2723. if( !HiveCount) {
  2724. //
  2725. // skip to the next one and break out of the loop, so we can detect whether the last one was flushed out
  2726. //
  2727. CmUnlockHive (h);
  2728. p = p->Flink;
  2729. break;
  2730. }
  2731. } else {
  2732. //
  2733. // do not update flush count for this one as we want to attempt to flush it at next iteration
  2734. //
  2735. #if 0
  2736. DbgPrint(" failed to flush ");
  2737. #endif
  2738. }
  2739. }
  2740. CmUnlockHive (h);
  2741. #if 0
  2742. DbgPrint(" \t GLFC = %lx\n",CmpLazyFlushCount);
  2743. #endif
  2744. } else if( (h->Hive.DirtyCount != 0) && // hive has dirty data
  2745. (!(h->Hive.HiveFlags & HIVE_VOLATILE)) && // is not volatile
  2746. (!(h->Hive.HiveFlags & HIVE_NOLAZYFLUSH))){ // and lazy flush is enabled
  2747. //
  2748. // count dirty count for this hive; we'll need to fire another lazy flusher
  2749. // to take this into account, even if we made it to the end of the list
  2750. //
  2751. // sanity; this has already been flushed
  2752. ASSERT( h->FlushCount == CmpLazyFlushCount );
  2753. *DirtyCount += h->Hive.DirtyCount;
  2754. }
  2755. p = p->Flink;
  2756. }
  2757. if( p == &CmpHiveListHead ) {
  2758. //
  2759. // we have flushed out everything; caller must update globalflush count
  2760. //
  2761. Result = FALSE;
  2762. } else {
  2763. Result = TRUE;
  2764. }
  2765. UNLOCK_HIVE_LIST();
  2766. return Result;
  2767. }
  2768. NTSTATUS
  2769. CmReplaceKey(
  2770. IN PHHIVE Hive,
  2771. IN HCELL_INDEX Cell,
  2772. IN PUNICODE_STRING NewHiveName,
  2773. IN PUNICODE_STRING OldFileName
  2774. )
  2775. /*++
  2776. Routine Description:
  2777. Renames the hive file for a running system and replaces it with a new
  2778. file. The new file is not actually used until the next boot.
  2779. Arguments:
  2780. Hive - Supplies a hive control structure for the hive to be replaced.
  2781. Cell - Supplies the HCELL_INDEX of the root cell of the hive to be
  2782. replaced.
  2783. NewHiveName - Supplies the name of the file which is to be installed
  2784. as the new hive.
  2785. OldFileName - Supplies the name of the file which the existing hive
  2786. file is to be renamed to.
  2787. Return Value:
  2788. NTSTATUS
  2789. --*/
  2790. {
  2791. CHAR ObjectInfoBuffer[512];
  2792. NTSTATUS Status;
  2793. NTSTATUS Status2;
  2794. OBJECT_ATTRIBUTES Attributes;
  2795. PCMHIVE NewHive;
  2796. PCMHIVE CmHive;
  2797. POBJECT_NAME_INFORMATION NameInfo;
  2798. ULONG OldQuotaAllowed;
  2799. ULONG OldQuotaWarning;
  2800. BOOLEAN Allocate;
  2801. BOOLEAN RegistryLockAquired;
  2802. UNREFERENCED_PARAMETER (Cell);
  2803. CmpLockRegistryExclusive();
  2804. #ifdef CHECK_REGISTRY_USECOUNT
  2805. CmpCheckRegistryUseCount();
  2806. #endif //CHECK_REGISTRY_USECOUNT
  2807. if (Hive->HiveFlags & HIVE_HAS_BEEN_REPLACED) {
  2808. CmpUnlockRegistry();
  2809. return STATUS_FILE_RENAMED;
  2810. }
  2811. //
  2812. // temporarily disable registry quota as we will be giving this memory back immediately!
  2813. //
  2814. OldQuotaAllowed = CmpGlobalQuotaAllowed;
  2815. OldQuotaWarning = CmpGlobalQuotaWarning;
  2816. CmpGlobalQuotaAllowed = CM_WRAP_LIMIT;
  2817. CmpGlobalQuotaWarning = CM_WRAP_LIMIT;
  2818. //
  2819. // First open the new hive file and check to make sure it is valid.
  2820. //
  2821. InitializeObjectAttributes(&Attributes,
  2822. NewHiveName,
  2823. OBJ_CASE_INSENSITIVE,
  2824. NULL,
  2825. NULL);
  2826. Allocate = FALSE;
  2827. RegistryLockAquired = TRUE;
  2828. Status = CmpCmdHiveOpen( &Attributes, // FileAttributes
  2829. NULL, // ImpersonationContext
  2830. &Allocate, // Allocate
  2831. &RegistryLockAquired, // RegistryLockAquired
  2832. &NewHive, // NewHive
  2833. CM_CHECK_REGISTRY_CHECK_CLEAN // CheckFlags
  2834. );
  2835. if (!NT_SUCCESS(Status)) {
  2836. goto ErrorExit;
  2837. }
  2838. ASSERT(Allocate == FALSE);
  2839. if( Hive == (PHHIVE)(CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive) ) {
  2840. //
  2841. // Somebody attempts to replace the system hive: do the WPA test
  2842. //
  2843. HCELL_INDEX Src,Dest;
  2844. Status = CmpCheckReplaceHive(Hive,&Src);
  2845. if( !NT_SUCCESS(Status) ) {
  2846. goto ErrorCleanup;
  2847. }
  2848. Status = CmpCheckReplaceHive((PHHIVE)NewHive,&Dest);
  2849. if( !NT_SUCCESS(Status) ) {
  2850. goto ErrorCleanup;
  2851. }
  2852. ASSERT( Src != HCELL_NIL );
  2853. ASSERT( Dest != HCELL_NIL );
  2854. //
  2855. // now stuff the current WPA subtree into the new hive
  2856. //
  2857. if( !CmpSyncTrees(Hive, Src, (PHHIVE)NewHive, Dest, FALSE ) ) {
  2858. Status = STATUS_REGISTRY_CORRUPT;
  2859. goto ErrorCleanup;
  2860. }
  2861. //
  2862. // commit the changes we've made in the destination hive
  2863. //
  2864. if( !HvSyncHive((PHHIVE)NewHive) ) {
  2865. Status = STATUS_REGISTRY_CORRUPT;
  2866. goto ErrorCleanup;
  2867. }
  2868. }
  2869. //
  2870. // The new hive exists, and is consistent, and we have it open.
  2871. // Now rename the current hive file.
  2872. //
  2873. CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive);
  2874. Status = CmpCmdRenameHive( CmHive, // CmHive
  2875. (POBJECT_NAME_INFORMATION)ObjectInfoBuffer, // OldName
  2876. OldFileName, // NewName
  2877. sizeof(ObjectInfoBuffer) // NameInfoLength
  2878. );
  2879. if (!NT_SUCCESS(Status)) {
  2880. //
  2881. // rename failed, close the files associated with the new hive
  2882. //
  2883. goto ErrorCleanup;
  2884. }
  2885. //
  2886. // The existing hive was successfully renamed, so try to rename the
  2887. // new file to what the old hive file was named. (which was returned
  2888. // into ObjectInfoBuffer by the worker thread)
  2889. //
  2890. Hive->HiveFlags |= HIVE_HAS_BEEN_REPLACED;
  2891. NameInfo = (POBJECT_NAME_INFORMATION)ObjectInfoBuffer;
  2892. Status = CmpCmdRenameHive( NewHive, // CmHive
  2893. NULL, // OldName
  2894. &NameInfo->Name,// NewName
  2895. 0 // NameInfoLength
  2896. );
  2897. if (!NT_SUCCESS(Status)) {
  2898. //
  2899. // We are in trouble now. We have renamed the existing hive file,
  2900. // but we couldn't rename the new hive file! Try to rename the
  2901. // existing hive file back to where it was.
  2902. //
  2903. CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive);
  2904. Status2 = CmpCmdRenameHive( CmHive, // CmHive
  2905. NULL, // OldName
  2906. &NameInfo->Name, // NewName
  2907. 0 // NameInfoLength
  2908. );
  2909. if (!NT_SUCCESS(Status2)) {
  2910. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmReplaceKey: renamed existing hive file, but couldn't\n"));
  2911. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK," rename new hive file (%08lx) ",Status));
  2912. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK," or replace old hive file (%08lx)!\n",Status2));
  2913. //
  2914. // WARNNOTE:
  2915. // To get into this state, the user must have relevent
  2916. // privileges, deliberately mess with system in an attempt
  2917. // to defeat it, AND get it done in a narrow timing window.
  2918. //
  2919. // Further, if it's a user profile, the system will
  2920. // still come up.
  2921. //
  2922. // Therefore, return an error code and go on.
  2923. //
  2924. Status = STATUS_REGISTRY_CORRUPT;
  2925. }
  2926. } else {
  2927. //
  2928. // flush file buffers (we are particulary interested in ValidDataLength to be updated on-disk)
  2929. //
  2930. IO_STATUS_BLOCK IoStatus;
  2931. Status = ZwFlushBuffersFile(NewHive->FileHandles[HFILE_TYPE_PRIMARY],&IoStatus);
  2932. if (!NT_SUCCESS(Status)) {
  2933. //
  2934. // failed to set ValidDataLength, close the files associated with the new hive
  2935. //
  2936. //
  2937. // We are in trouble now. We have renamed the existing hive file,
  2938. // but we couldn't rename the new hive file! Try to rename the
  2939. // existing hive file back to where it was.
  2940. //
  2941. CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive);
  2942. Status2 = CmpCmdRenameHive( CmHive, // CmHive
  2943. NULL, // OldName
  2944. &NameInfo->Name, // NewName
  2945. 0 // NameInfoLength
  2946. );
  2947. if (!NT_SUCCESS(Status2)) {
  2948. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmReplaceKey: renamed existing hive file, but couldn't\n"));
  2949. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK," rename new hive file (%08lx) ",Status));
  2950. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK," or replace old hive file (%08lx)!\n",Status2));
  2951. //
  2952. // WARNNOTE:
  2953. // To get into this state, the user must have relevent
  2954. // privileges, deliberately mess with system in an attempt
  2955. // to defeat it, AND get it done in a narrow timing window.
  2956. //
  2957. // Further, if it's a user profile, the system will
  2958. // still come up.
  2959. //
  2960. // Therefore, return an error code and go on.
  2961. //
  2962. Status = STATUS_REGISTRY_CORRUPT;
  2963. }
  2964. }
  2965. }
  2966. //
  2967. // All of the renaming is done. However, we are holding an in-memory
  2968. // image of the new hive. Release it, since it will not actually
  2969. // be used until next boot.
  2970. //
  2971. // Do not close the open file handles to the new hive, we need to
  2972. // keep it locked exclusively until the system is rebooted to prevent
  2973. // people from mucking with it.
  2974. //
  2975. ErrorCleanup:
  2976. LOCK_HIVE_LIST();
  2977. CmpRemoveEntryList(&(NewHive->HiveList));
  2978. UNLOCK_HIVE_LIST();
  2979. CmpDestroyHiveViewList(NewHive);
  2980. CmpDestroySecurityCache(NewHive);
  2981. CmpDropFileObjectForHive(NewHive);
  2982. CmpUnJoinClassOfTrust(NewHive);
  2983. HvFreeHive((PHHIVE)NewHive);
  2984. //
  2985. // only close handles on error
  2986. //
  2987. if( !NT_SUCCESS(Status) ) {
  2988. CmpCmdHiveClose(NewHive);
  2989. }
  2990. ASSERT( NewHive->HiveLock );
  2991. ExFreePool(NewHive->HiveLock);
  2992. ASSERT( NewHive->ViewLock );
  2993. ExFreePool(NewHive->ViewLock);
  2994. CmpFree(NewHive, sizeof(CMHIVE));
  2995. ErrorExit:
  2996. //
  2997. // Set global quota back to what it was.
  2998. //
  2999. CmpGlobalQuotaAllowed = OldQuotaAllowed;
  3000. CmpGlobalQuotaWarning = OldQuotaWarning;
  3001. #ifdef CHECK_REGISTRY_USECOUNT
  3002. CmpCheckRegistryUseCount();
  3003. #endif //CHECK_REGISTRY_USECOUNT
  3004. CmpUnlockRegistry();
  3005. return(Status);
  3006. }
  3007. #ifdef NT_RENAME_KEY
  3008. ULONG
  3009. CmpComputeKcbConvKey(
  3010. PCM_KEY_CONTROL_BLOCK KeyControlBlock
  3011. );
  3012. NTSTATUS
  3013. CmRenameKey(
  3014. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  3015. IN UNICODE_STRING NewKeyName // RAW
  3016. )
  3017. /*++
  3018. Routine Description:
  3019. Changes the name of the key to the given one.
  3020. What needs to be done:
  3021. 1. Allocate a cell big enough to accomodate new knode
  3022. 2. make a duplicate of the index in subkeylist of kcb's parent
  3023. 3. replace parent's subkeylist with the duplicate
  3024. 4. add new subkey to parent
  3025. 5. remove old subkey
  3026. 6. free storage.
  3027. Arguments:
  3028. KeyControlBlock - pointer to kcb for key to operate on
  3029. NewKeyName - The new name to be given to this key
  3030. Return Value:
  3031. NTSTATUS - Result code from call, among the following:
  3032. <TBS>
  3033. Comments:
  3034. What do we do with symbolic links?
  3035. --*/
  3036. {
  3037. NTSTATUS Status;
  3038. PHHIVE Hive;
  3039. HCELL_INDEX Cell;
  3040. PCM_KEY_NODE Node;
  3041. PCM_KEY_NODE ParentNode;
  3042. ULONG NodeSize;
  3043. HCELL_INDEX NewKeyCell = HCELL_NIL;
  3044. HSTORAGE_TYPE StorageType;
  3045. HCELL_INDEX OldSubKeyList = HCELL_NIL;
  3046. PCM_KEY_NODE NewKeyNode;
  3047. PCM_KEY_INDEX Index;
  3048. ULONG i;
  3049. LARGE_INTEGER TimeStamp;
  3050. ULONG NameLength;
  3051. PCM_NAME_CONTROL_BLOCK OldNcb = NULL;
  3052. ULONG ConvKey;
  3053. WCHAR *Cp;
  3054. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmRenameKey\n"));
  3055. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  3056. //
  3057. // validate new name
  3058. //
  3059. if( NewKeyName.Length > REG_MAX_KEY_NAME_LENGTH ) {
  3060. return STATUS_INVALID_PARAMETER;
  3061. }
  3062. try {
  3063. Cp = NewKeyName.Buffer;
  3064. for (i=0; i<NewKeyName.Length; i += sizeof(WCHAR)) {
  3065. if( *Cp == OBJ_NAME_PATH_SEPARATOR ) {
  3066. return STATUS_INVALID_PARAMETER;
  3067. }
  3068. ++Cp;
  3069. }
  3070. } except (EXCEPTION_EXECUTE_HANDLER) {
  3071. Status = GetExceptionCode();
  3072. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!NtRenameKey: code:%08lx\n", Status));
  3073. return Status;
  3074. }
  3075. //
  3076. // no edits, on keys marked for deletion
  3077. //
  3078. if (KeyControlBlock->Delete) {
  3079. return STATUS_KEY_DELETED;
  3080. }
  3081. //
  3082. // see if the newName is not already a subkey of parentKcb
  3083. //
  3084. Hive = KeyControlBlock->KeyHive;
  3085. Cell = KeyControlBlock->KeyCell;
  3086. StorageType = HvGetCellType(Cell);
  3087. //
  3088. // OBS. we could have worked with the kcb tree instead, but if this is not
  3089. // going to work, we are in trouble anyway, so it's better to find out soon
  3090. //
  3091. Node = (PCM_KEY_NODE)HvGetCell(Hive,Cell);
  3092. if( Node == NULL ) {
  3093. //
  3094. // cannot map view
  3095. //
  3096. return STATUS_INSUFFICIENT_RESOURCES;
  3097. }
  3098. // release the cell right here, as the registry is locked exclusively, so we don't care
  3099. HvReleaseCell(Hive, Cell);
  3100. //
  3101. // cannot rename the root of a hive; or anything in the master hive !!!
  3102. //
  3103. if((Hive == &CmpMasterHive->Hive) || (KeyControlBlock->ParentKcb == NULL) || (KeyControlBlock->ParentKcb->KeyHive == &CmpMasterHive->Hive) ) {
  3104. return STATUS_ACCESS_DENIED;
  3105. }
  3106. ParentNode = (PCM_KEY_NODE)HvGetCell(Hive,Node->Parent);
  3107. if( ParentNode == NULL ) {
  3108. //
  3109. // cannot map view
  3110. //
  3111. return STATUS_INSUFFICIENT_RESOURCES;
  3112. }
  3113. // release the cell right here, as the registry is locked exclusively, so we don't care
  3114. HvReleaseCell(Hive, Node->Parent);
  3115. try {
  3116. if( CmpFindSubKeyByName(Hive,ParentNode,&NewKeyName) != HCELL_NIL ) {
  3117. //
  3118. // a subkey with this name already exists
  3119. //
  3120. return STATUS_CANNOT_DELETE;
  3121. }
  3122. //
  3123. // since we are in try-except, compute the new node size
  3124. //
  3125. NodeSize = CmpHKeyNodeSize(Hive, &NewKeyName);
  3126. } except (EXCEPTION_EXECUTE_HANDLER) {
  3127. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmRenameKey: code:%08lx\n", GetExceptionCode()));
  3128. return GetExceptionCode();
  3129. }
  3130. //
  3131. // 1. Allocate the new knode cell and copy the data from the old one, updating
  3132. // the name.
  3133. //
  3134. // mark the parent dirty, as we will modify its SubkeyLists
  3135. //
  3136. if(!HvMarkCellDirty(Hive, Node->Parent)) {
  3137. return STATUS_NO_LOG_SPACE;
  3138. }
  3139. //
  3140. // mark the index dirty as we are going to free it on success
  3141. //
  3142. if ( !CmpMarkIndexDirty(Hive, Node->Parent, Cell) ) {
  3143. return STATUS_NO_LOG_SPACE;
  3144. }
  3145. //
  3146. // mark key_node as dirty as we are going to free it if we succeed
  3147. //
  3148. if(!HvMarkCellDirty(Hive, Cell)) {
  3149. return STATUS_NO_LOG_SPACE;
  3150. }
  3151. OldSubKeyList = ParentNode->SubKeyLists[StorageType];
  3152. if( (OldSubKeyList == HCELL_NIL) || (!HvMarkCellDirty(Hive, OldSubKeyList)) ) {
  3153. return STATUS_NO_LOG_SPACE;
  3154. }
  3155. Index = (PCM_KEY_INDEX)HvGetCell(Hive,OldSubKeyList);
  3156. if( Index == NULL ) {
  3157. //
  3158. // this is a bad joke; we just marked this dirty
  3159. //
  3160. ASSERT( FALSE );
  3161. return STATUS_INSUFFICIENT_RESOURCES;
  3162. }
  3163. // release the cell right here, as the registry is locked exclusively, so we don't care
  3164. HvReleaseCell(Hive, OldSubKeyList);
  3165. //
  3166. // mark all the index cells dirty
  3167. //
  3168. if( Index->Signature == CM_KEY_INDEX_ROOT ) {
  3169. //
  3170. // it's a root
  3171. //
  3172. for(i=0;i<Index->Count;i++) {
  3173. // common sense
  3174. ASSERT( (Index->List[i] != 0) && (Index->List[i] != HCELL_NIL) );
  3175. if(!HvMarkCellDirty(Hive, Index->List[i])) {
  3176. return STATUS_NO_LOG_SPACE;
  3177. }
  3178. }
  3179. }
  3180. NewKeyCell = HvAllocateCell(
  3181. Hive,
  3182. NodeSize,
  3183. StorageType,
  3184. Cell // in the same vicinity
  3185. );
  3186. if( NewKeyCell == HCELL_NIL ) {
  3187. return STATUS_INSUFFICIENT_RESOURCES;
  3188. }
  3189. NewKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,NewKeyCell);
  3190. if( NewKeyNode == NULL ) {
  3191. //
  3192. // cannot map view; this shouldn't happen as we just allocated
  3193. // this cell (i.e. it should be dirty/pinned into memory)
  3194. //
  3195. ASSERT( FALSE );
  3196. Status = STATUS_INSUFFICIENT_RESOURCES;
  3197. goto ErrorExit;
  3198. }
  3199. // release the cell right here, as the registry is locked exclusively, so we don't care
  3200. HvReleaseCell(Hive, NewKeyCell);
  3201. //
  3202. // copy old keynode info onto the new cell and update the name
  3203. //
  3204. // first everything BUT the name
  3205. RtlCopyMemory(NewKeyNode,Node,FIELD_OFFSET(CM_KEY_NODE, Name));
  3206. // second, the new name
  3207. try {
  3208. NewKeyNode->NameLength = CmpCopyName( Hive,
  3209. NewKeyNode->Name,
  3210. &NewKeyName);
  3211. NameLength = NewKeyName.Length;
  3212. if (NewKeyNode->NameLength < NameLength ) {
  3213. NewKeyNode->Flags |= KEY_COMP_NAME;
  3214. } else {
  3215. NewKeyNode->Flags &= ~KEY_COMP_NAME;
  3216. }
  3217. } except (EXCEPTION_EXECUTE_HANDLER) {
  3218. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmRenameKey: code:%08lx\n", GetExceptionCode()));
  3219. Status = GetExceptionCode();
  3220. goto ErrorExit;
  3221. }
  3222. // third, the timestamp
  3223. KeQuerySystemTime(&TimeStamp);
  3224. NewKeyNode->LastWriteTime = TimeStamp;
  3225. //
  3226. // at this point we have the new key_node all built up.
  3227. //
  3228. //
  3229. // 2.3. Make a duplicate of the parent's subkeylist and replace the original
  3230. //
  3231. ParentNode->SubKeyLists[StorageType] = CmpDuplicateIndex(Hive,OldSubKeyList,StorageType);
  3232. if( ParentNode->SubKeyLists[StorageType] == HCELL_NIL ) {
  3233. Status = STATUS_INSUFFICIENT_RESOURCES;
  3234. goto ErrorExit;
  3235. }
  3236. //
  3237. // 4. Add new subkey to the parent. This will take care of index
  3238. // grow and rebalance problems.
  3239. // Note: the index is at this point a duplicate, so if we fail, we still have the
  3240. // original one handy to recover
  3241. //
  3242. if( !CmpAddSubKey(Hive,Node->Parent,NewKeyCell) ) {
  3243. Status = STATUS_INSUFFICIENT_RESOURCES;
  3244. goto ErrorExit;
  3245. }
  3246. //
  3247. // 5. remove old subkey;
  3248. //
  3249. if( !CmpRemoveSubKey(Hive,Node->Parent,Cell) ) {
  3250. Status = STATUS_INSUFFICIENT_RESOURCES;
  3251. goto ErrorExit;
  3252. }
  3253. //
  3254. // 5'. update the parent on each and every son.
  3255. //
  3256. if( !CmpUpdateParentForEachSon(Hive,NewKeyCell) ) {
  3257. Status = STATUS_INSUFFICIENT_RESOURCES;
  3258. goto ErrorExit;
  3259. }
  3260. //
  3261. // update the NCB in the kcb; at the end of this function, the kcbs underneath this
  3262. // will eventually get rehashed
  3263. //
  3264. OldNcb = KeyControlBlock->NameBlock;
  3265. try {
  3266. KeyControlBlock->NameBlock = CmpGetNameControlBlock (&NewKeyName);
  3267. } except (EXCEPTION_EXECUTE_HANDLER) {
  3268. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_EXCEPTION,"!!CmRenameKey: code:%08lx\n", GetExceptionCode()));
  3269. Status = GetExceptionCode();
  3270. goto ErrorExit;
  3271. }
  3272. //
  3273. // 6. At this point we have it all done. We just need to free the old index and key_cell
  3274. //
  3275. //
  3276. // free old index
  3277. //
  3278. Index = (PCM_KEY_INDEX)HvGetCell(Hive,OldSubKeyList);
  3279. if( Index == NULL ) {
  3280. //
  3281. // this is a bad joke; we just marked this dirty
  3282. //
  3283. ASSERT( FALSE );
  3284. Status = STATUS_INSUFFICIENT_RESOURCES;
  3285. goto ErrorExit;
  3286. }
  3287. // release the cell right here, as the registry is locked exclusively, so we don't care
  3288. HvReleaseCell(Hive, OldSubKeyList);
  3289. if( Index->Signature == CM_KEY_INDEX_ROOT ) {
  3290. //
  3291. // it's a root
  3292. //
  3293. for(i=0;i<Index->Count;i++) {
  3294. // common sense
  3295. ASSERT( (Index->List[i] != 0) && (Index->List[i] != HCELL_NIL) );
  3296. HvFreeCell(Hive, Index->List[i]);
  3297. }
  3298. } else {
  3299. //
  3300. // should be a leaf
  3301. //
  3302. ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) ||
  3303. (Index->Signature == CM_KEY_FAST_LEAF) ||
  3304. (Index->Signature == CM_KEY_HASH_LEAF)
  3305. );
  3306. ASSERT(Index->Count != 0);
  3307. }
  3308. HvFreeCell(Hive, OldSubKeyList);
  3309. //
  3310. // free old cell
  3311. //
  3312. HvFreeCell(Hive,Cell);
  3313. //
  3314. // update the node KeyCell for this kcb and the timestamp on the kcb;
  3315. //
  3316. KeyControlBlock->KeyCell = NewKeyCell;
  3317. KeyControlBlock->KcbLastWriteTime = TimeStamp;
  3318. //
  3319. // and one last "little" thing: update parent's maxnamelen and reset parents cache
  3320. //
  3321. CmpCleanUpSubKeyInfo (KeyControlBlock->ParentKcb);
  3322. if (ParentNode->MaxNameLen < NameLength) {
  3323. ParentNode->MaxNameLen = NameLength;
  3324. KeyControlBlock->ParentKcb->KcbMaxNameLen = (USHORT)NameLength;
  3325. }
  3326. //
  3327. // rehash this kcb
  3328. //
  3329. ConvKey = CmpComputeKcbConvKey(KeyControlBlock);
  3330. if( ConvKey != KeyControlBlock->ConvKey ) {
  3331. //
  3332. // rehash the kcb by removing it from hash, and then inserting it
  3333. // again with th new ConvKey
  3334. //
  3335. CmpRemoveKeyHash(&(KeyControlBlock->KeyHash));
  3336. KeyControlBlock->ConvKey = ConvKey;
  3337. CmpInsertKeyHash(&(KeyControlBlock->KeyHash),FALSE);
  3338. }
  3339. //
  3340. // Aditional work: take care of the kcb subtree; this cannot fail, punt
  3341. //
  3342. CmpSearchForOpenSubKeys(KeyControlBlock,SearchAndRehash,NULL);
  3343. //
  3344. // last, dereference the OldNcb for this kcb
  3345. //
  3346. ASSERT( OldNcb != NULL );
  3347. CmpDereferenceNameControlBlockWithLock(OldNcb);
  3348. return STATUS_SUCCESS;
  3349. ErrorExit:
  3350. if( OldSubKeyList != HCELL_NIL ) {
  3351. //
  3352. // we have attempted (maybe even succedded) to duplicate parent's index)
  3353. //
  3354. if( ParentNode->SubKeyLists[StorageType] != HCELL_NIL ) {
  3355. //
  3356. // we need to free this as it is a duplicate
  3357. //
  3358. Index = (PCM_KEY_INDEX)HvGetCell(Hive,ParentNode->SubKeyLists[StorageType]);
  3359. if( Index == NULL ) {
  3360. //
  3361. // could not map view;this shouldn't happen as we just allocated this cell
  3362. //
  3363. ASSERT( FALSE );
  3364. } else {
  3365. // release the cell right here, as the registry is locked exclusively, so we don't care
  3366. HvReleaseCell(Hive, ParentNode->SubKeyLists[StorageType]);
  3367. if( Index->Signature == CM_KEY_INDEX_ROOT ) {
  3368. //
  3369. // it's a root
  3370. //
  3371. for(i=0;i<Index->Count;i++) {
  3372. // common sense
  3373. ASSERT( (Index->List[i] != 0) && (Index->List[i] != HCELL_NIL) );
  3374. HvFreeCell(Hive, Index->List[i]);
  3375. }
  3376. } else {
  3377. //
  3378. // should be a leaf
  3379. //
  3380. ASSERT((Index->Signature == CM_KEY_INDEX_LEAF) ||
  3381. (Index->Signature == CM_KEY_FAST_LEAF) ||
  3382. (Index->Signature == CM_KEY_HASH_LEAF)
  3383. );
  3384. ASSERT(Index->Count != 0);
  3385. }
  3386. HvFreeCell(Hive, ParentNode->SubKeyLists[StorageType]);
  3387. }
  3388. }
  3389. //
  3390. // restore the parent's index
  3391. //
  3392. ParentNode->SubKeyLists[StorageType] = OldSubKeyList;
  3393. }
  3394. ASSERT( NewKeyCell != HCELL_NIL );
  3395. HvFreeCell(Hive,NewKeyCell);
  3396. if( OldNcb != NULL ) {
  3397. KeyControlBlock->NameBlock = OldNcb;
  3398. }
  3399. return Status;
  3400. }
  3401. #endif
  3402. NTSTATUS
  3403. CmMoveKey(
  3404. IN PCM_KEY_CONTROL_BLOCK KeyControlBlock
  3405. )
  3406. /*++
  3407. Routine Description:
  3408. Moves all the cells related to this kcb above the specified fileoffset.
  3409. What needs to be done:
  3410. 1. mark all data that we are going to touch dirty
  3411. 2. Duplicate the key_node (and values and all cells involved)
  3412. 3. Update the parent for all children
  3413. 4. replace the new Key_cell in the parent's subkeylist
  3414. 5. Update the kcb and the kcb cache
  3415. 6. remove old subkey
  3416. WARNING:
  3417. after 3 we cannot fail anymore. if we do, we'll leak cells.
  3418. Arguments:
  3419. KeyControlBlock - pointer to kcb for key to operate on
  3420. Return Value:
  3421. NTSTATUS - Result code from call, among the following:
  3422. <TBS>
  3423. --*/
  3424. {
  3425. NTSTATUS Status;
  3426. PHHIVE Hive;
  3427. HCELL_INDEX OldKeyCell;
  3428. HCELL_INDEX NewKeyCell = HCELL_NIL;
  3429. HCELL_INDEX ParentKeyCell;
  3430. HSTORAGE_TYPE StorageType;
  3431. PCM_KEY_NODE OldKeyNode;
  3432. PCM_KEY_NODE ParentKeyNode;
  3433. PCM_KEY_NODE NewKeyNode;
  3434. PCM_KEY_INDEX ParentIndex;
  3435. PCM_KEY_INDEX OldIndex;
  3436. ULONG i,j;
  3437. HCELL_INDEX LeafCell;
  3438. PCM_KEY_INDEX Leaf;
  3439. PCM_KEY_FAST_INDEX FastIndex;
  3440. PHCELL_INDEX ParentIndexLocation = NULL;
  3441. PAGED_CODE();
  3442. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmMoveKey\n"));
  3443. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  3444. //
  3445. // no edits, on keys marked for deletion
  3446. //
  3447. if (KeyControlBlock->Delete) {
  3448. return STATUS_KEY_DELETED;
  3449. }
  3450. //
  3451. // see if the newName is not already a subkey of parentKcb
  3452. //
  3453. Hive = KeyControlBlock->KeyHive;
  3454. OldKeyCell = KeyControlBlock->KeyCell;
  3455. StorageType = HvGetCellType(OldKeyCell);
  3456. if( StorageType != Stable ) {
  3457. //
  3458. // nop the volatiles
  3459. //
  3460. return STATUS_SUCCESS;
  3461. }
  3462. if( OldKeyCell == Hive->BaseBlock->RootCell ) {
  3463. //
  3464. // this works only for stable keys.
  3465. //
  3466. return STATUS_INVALID_PARAMETER;
  3467. }
  3468. //
  3469. // 1. mark all data that we are going to touch dirty
  3470. //
  3471. // parent's index, as we will replace the key node cell in it
  3472. // we only search in the Stable storage. It is supposed to be there
  3473. //
  3474. OldKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,OldKeyCell);
  3475. if( OldKeyNode == NULL ) {
  3476. //
  3477. // cannot map view
  3478. //
  3479. return STATUS_INSUFFICIENT_RESOURCES;
  3480. }
  3481. if (! CmpMarkKeyDirty(Hive, OldKeyCell
  3482. #if DBG
  3483. ,FALSE
  3484. #endif //DBG
  3485. )) {
  3486. HvReleaseCell(Hive, OldKeyCell);
  3487. return STATUS_NO_LOG_SPACE;
  3488. }
  3489. // release the cell right here, as the registry is locked exclusively, and the key_cell is marked as dirty
  3490. HvReleaseCell(Hive, OldKeyCell);
  3491. if( OldKeyNode->Flags & KEY_SYM_LINK ) {
  3492. //
  3493. // we do not compact links
  3494. //
  3495. return STATUS_INVALID_PARAMETER;
  3496. }
  3497. if( OldKeyNode->SubKeyLists[Stable] != HCELL_NIL ) {
  3498. //
  3499. // mark the index dirty
  3500. //
  3501. OldIndex = (PCM_KEY_INDEX)HvGetCell(Hive, OldKeyNode->SubKeyLists[Stable]);
  3502. if( OldIndex == NULL ) {
  3503. //
  3504. // we couldn't map the bin containing this cell
  3505. //
  3506. return STATUS_INSUFFICIENT_RESOURCES;
  3507. }
  3508. HvReleaseCell(Hive, OldKeyNode->SubKeyLists[Stable]);
  3509. if( !HvMarkCellDirty(Hive, OldKeyNode->SubKeyLists[Stable]) ) {
  3510. return STATUS_NO_LOG_SPACE;
  3511. }
  3512. if(OldIndex->Signature == CM_KEY_INDEX_ROOT) {
  3513. for (i = 0; i < OldIndex->Count; i++) {
  3514. if( !HvMarkCellDirty(Hive, OldIndex->List[i]) ) {
  3515. return STATUS_NO_LOG_SPACE;
  3516. }
  3517. }
  3518. }
  3519. }
  3520. ParentKeyCell = OldKeyNode->Parent;
  3521. //
  3522. // now in the parent's spot
  3523. //
  3524. ParentKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,ParentKeyCell);
  3525. if( ParentKeyNode == NULL ) {
  3526. //
  3527. // cannot map view
  3528. //
  3529. return STATUS_INSUFFICIENT_RESOURCES;
  3530. }
  3531. if( !HvMarkCellDirty(Hive, ParentKeyCell) ) {
  3532. HvReleaseCell(Hive, ParentKeyCell);
  3533. return STATUS_NO_LOG_SPACE;
  3534. }
  3535. // release the cell right here, as the registry is locked exclusively, so we don't care
  3536. // Key_cell is marked dirty to keep the parent knode mapped
  3537. HvReleaseCell(Hive, ParentKeyCell);
  3538. ParentIndex = (PCM_KEY_INDEX)HvGetCell(Hive, ParentKeyNode->SubKeyLists[Stable]);
  3539. if( ParentIndex == NULL ) {
  3540. //
  3541. // we couldn't map the bin containing this cell
  3542. //
  3543. return STATUS_INSUFFICIENT_RESOURCES;
  3544. }
  3545. HvReleaseCell(Hive, ParentKeyNode->SubKeyLists[Stable]);
  3546. if(ParentIndex->Signature == CM_KEY_INDEX_ROOT) {
  3547. //
  3548. // step through root, till we find the right leaf
  3549. //
  3550. for (i = 0; i < ParentIndex->Count; i++) {
  3551. LeafCell = ParentIndex->List[i];
  3552. Leaf = (PCM_KEY_INDEX)HvGetCell(Hive, LeafCell);
  3553. if( Leaf == NULL ) {
  3554. //
  3555. // we couldn't map the bin containing this cell
  3556. //
  3557. return STATUS_INSUFFICIENT_RESOURCES;
  3558. }
  3559. HvReleaseCell(Hive, LeafCell);
  3560. if ( (Leaf->Signature == CM_KEY_FAST_LEAF) ||
  3561. (Leaf->Signature == CM_KEY_HASH_LEAF)
  3562. ) {
  3563. FastIndex = (PCM_KEY_FAST_INDEX)Leaf;
  3564. for(j=0;j<FastIndex->Count;j++) {
  3565. if( FastIndex->List[j].Cell == OldKeyCell ) {
  3566. //
  3567. // found it! remember the locations we want to update later and break the loop
  3568. //
  3569. if( !HvMarkCellDirty(Hive, LeafCell) ) {
  3570. return STATUS_NO_LOG_SPACE;
  3571. }
  3572. ParentIndexLocation = &(FastIndex->List[j].Cell);
  3573. break;
  3574. }
  3575. }
  3576. if( ParentIndexLocation != NULL ) {
  3577. break;
  3578. }
  3579. } else {
  3580. for(j=0;j<Leaf->Count;j++) {
  3581. if( Leaf->List[j] == OldKeyCell ) {
  3582. //
  3583. // found it! remember the locations we want to update later and break the loop
  3584. //
  3585. if( !HvMarkCellDirty(Hive, LeafCell) ) {
  3586. return STATUS_NO_LOG_SPACE;
  3587. }
  3588. ParentIndexLocation = &(Leaf->List[j]);
  3589. break;
  3590. }
  3591. }
  3592. if( ParentIndexLocation != NULL ) {
  3593. break;
  3594. }
  3595. }
  3596. }
  3597. } else if ( (ParentIndex->Signature == CM_KEY_FAST_LEAF) ||
  3598. (ParentIndex->Signature == CM_KEY_HASH_LEAF)
  3599. ) {
  3600. FastIndex = (PCM_KEY_FAST_INDEX)ParentIndex;
  3601. for(j=0;j<FastIndex->Count;j++) {
  3602. if( FastIndex->List[j].Cell == OldKeyCell ) {
  3603. //
  3604. // found it! remember the locations we want to update later and break the loop
  3605. //
  3606. if( !HvMarkCellDirty(Hive, ParentKeyNode->SubKeyLists[Stable]) ) {
  3607. return STATUS_NO_LOG_SPACE;
  3608. }
  3609. ParentIndexLocation = &(FastIndex->List[j].Cell);
  3610. break;
  3611. }
  3612. }
  3613. } else {
  3614. for(j=0;j<ParentIndex->Count;j++) {
  3615. if( ParentIndex->List[j] == OldKeyCell ) {
  3616. //
  3617. // found it! remember the locations we want to update later and break the loop
  3618. //
  3619. if( !HvMarkCellDirty(Hive, ParentKeyNode->SubKeyLists[Stable]) ) {
  3620. return STATUS_NO_LOG_SPACE;
  3621. }
  3622. ParentIndexLocation = &(ParentIndex->List[j]);
  3623. break;
  3624. }
  3625. }
  3626. }
  3627. // we should've find it !!!
  3628. ASSERT( ParentIndexLocation != NULL );
  3629. //
  3630. // 2. Duplicate the key_node (and values and all cells involved)
  3631. //
  3632. Status = CmpDuplicateKey(Hive,OldKeyCell,&NewKeyCell);
  3633. if( !NT_SUCCESS(Status) ) {
  3634. return Status;
  3635. }
  3636. // sanity
  3637. ASSERT( (NewKeyCell != HCELL_NIL) && (StorageType == (HSTORAGE_TYPE)HvGetCellType(NewKeyCell)));
  3638. //
  3639. // 3. update the parent on each and every son.
  3640. //
  3641. if( !CmpUpdateParentForEachSon(Hive,NewKeyCell) ) {
  3642. Status = STATUS_INSUFFICIENT_RESOURCES;
  3643. goto ErrorExit;
  3644. }
  3645. //
  3646. // 4. replace the new Key_cell in the parent's subkeylist
  3647. // From now on, WE CANNOT fails. we have everything marked dirty
  3648. // we just update some fields. no resources required !
  3649. // If we fail to free some cells, too bad, we'll leak some cells.
  3650. //
  3651. *ParentIndexLocation = NewKeyCell;
  3652. //
  3653. // 5. Update the kcb and the kcb cache
  3654. //
  3655. CmpCleanUpSubKeyInfo(KeyControlBlock->ParentKcb);
  3656. KeyControlBlock->KeyCell = NewKeyCell;
  3657. CmpRebuildKcbCache(KeyControlBlock);
  3658. //
  3659. // 6. remove old subkey
  3660. //
  3661. // First the Index; it's already marked dirty (i.e. PINNED)
  3662. //
  3663. if( OldKeyNode->SubKeyLists[Stable] != HCELL_NIL ) {
  3664. OldIndex = (PCM_KEY_INDEX)HvGetCell(Hive, OldKeyNode->SubKeyLists[Stable]);
  3665. ASSERT( OldIndex != NULL );
  3666. HvReleaseCell(Hive, OldKeyNode->SubKeyLists[Stable]);
  3667. if(OldIndex->Signature == CM_KEY_INDEX_ROOT) {
  3668. for (i = 0; i < OldIndex->Count; i++) {
  3669. HvFreeCell(Hive, OldIndex->List[i]);
  3670. }
  3671. }
  3672. HvFreeCell(Hive,OldKeyNode->SubKeyLists[Stable]);
  3673. }
  3674. OldKeyNode->SubKeyCounts[Stable] = 0;
  3675. OldKeyNode->SubKeyCounts[Volatile] = 0;
  3676. CmpFreeKeyByCell(Hive,OldKeyCell,FALSE);
  3677. return STATUS_SUCCESS;
  3678. ErrorExit:
  3679. //
  3680. // we need to free the new knode allocated
  3681. //
  3682. NewKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,NewKeyCell);
  3683. // must be dirty
  3684. ASSERT( NewKeyNode != NULL );
  3685. HvReleaseCell(Hive, NewKeyCell);
  3686. if( NewKeyNode->SubKeyLists[Stable] != HCELL_NIL ) {
  3687. OldIndex = (PCM_KEY_INDEX)HvGetCell(Hive, NewKeyNode->SubKeyLists[Stable]);
  3688. ASSERT( OldIndex != NULL );
  3689. HvReleaseCell(Hive, NewKeyNode->SubKeyLists[Stable]);
  3690. if(OldIndex->Signature == CM_KEY_INDEX_ROOT) {
  3691. for (i = 0; i < OldIndex->Count; i++) {
  3692. HvFreeCell(Hive, OldIndex->List[i]);
  3693. }
  3694. }
  3695. HvFreeCell(Hive,NewKeyNode->SubKeyLists[Stable]);
  3696. }
  3697. NewKeyNode->SubKeyCounts[Stable] = 0;
  3698. NewKeyNode->SubKeyCounts[Volatile] = 0;
  3699. CmpFreeKeyByCell(Hive,NewKeyCell,FALSE);
  3700. return Status;
  3701. }
  3702. NTSTATUS
  3703. CmpDuplicateKey(
  3704. PHHIVE Hive,
  3705. HCELL_INDEX OldKeyCell,
  3706. PHCELL_INDEX NewKeyCell
  3707. )
  3708. /*++
  3709. Routine Description:
  3710. Makes an exact clone of OldKeyCell key_node in the
  3711. space above AboveFileOffset.
  3712. Operates on Stable storage ONLY!!!
  3713. Arguments:
  3714. Return Value:
  3715. NTSTATUS - Result code from call, among the following:
  3716. <TBS>
  3717. --*/
  3718. {
  3719. PCM_KEY_NODE OldKeyNode;
  3720. PCM_KEY_NODE NewKeyNode;
  3721. PRELEASE_CELL_ROUTINE TargetReleaseCellRoutine;
  3722. PAGED_CODE();
  3723. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  3724. ASSERT( HvGetCellType(OldKeyCell) == Stable );
  3725. OldKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,OldKeyCell);
  3726. if( OldKeyNode == NULL ) {
  3727. //
  3728. // cannot map view
  3729. //
  3730. return STATUS_INSUFFICIENT_RESOURCES;
  3731. }
  3732. //
  3733. // since the registry is locked exclusively here, we don't need to lock/release cells
  3734. // while copying the trees; So, we just set the release routines to NULL and restore after
  3735. // the copy is complete; this saves some pain
  3736. //
  3737. TargetReleaseCellRoutine = Hive->ReleaseCellRoutine;
  3738. Hive->ReleaseCellRoutine = NULL;
  3739. *NewKeyCell = CmpCopyKeyPartial(Hive,OldKeyCell,Hive,OldKeyNode->Parent,TRUE);
  3740. Hive->ReleaseCellRoutine = TargetReleaseCellRoutine;
  3741. if( *NewKeyCell == HCELL_NIL ) {
  3742. HvReleaseCell(Hive, OldKeyCell);
  3743. return STATUS_INSUFFICIENT_RESOURCES;
  3744. }
  3745. NewKeyNode = (PCM_KEY_NODE)HvGetCell(Hive,*NewKeyCell);
  3746. if( NewKeyNode == NULL ) {
  3747. //
  3748. // cannot map view
  3749. //
  3750. HvReleaseCell(Hive, OldKeyCell);
  3751. CmpFreeKeyByCell(Hive,*NewKeyCell,FALSE);
  3752. return STATUS_INSUFFICIENT_RESOURCES;
  3753. }
  3754. //
  3755. // now we have the key_cell duplicated. Values and security has also been taken care of
  3756. // Go ahead and duplicate the Index.
  3757. //
  3758. if( OldKeyNode->SubKeyLists[Stable] != HCELL_NIL ) {
  3759. NewKeyNode->SubKeyLists[Stable] = CmpDuplicateIndex(Hive,OldKeyNode->SubKeyLists[Stable],Stable);
  3760. if( NewKeyNode->SubKeyLists[Stable] == HCELL_NIL ) {
  3761. HvReleaseCell(Hive, OldKeyCell);
  3762. CmpFreeKeyByCell(Hive,*NewKeyCell,FALSE);
  3763. HvReleaseCell(Hive, *NewKeyCell);
  3764. return STATUS_INSUFFICIENT_RESOURCES;
  3765. }
  3766. } else {
  3767. ASSERT( OldKeyNode->SubKeyCounts[Stable] == 0 );
  3768. NewKeyNode->SubKeyLists[Stable] = HCELL_NIL;
  3769. }
  3770. NewKeyNode->SubKeyCounts[Stable] = OldKeyNode->SubKeyCounts[Stable];
  3771. NewKeyNode->SubKeyLists[Volatile] = OldKeyNode->SubKeyLists[Volatile];
  3772. NewKeyNode->SubKeyCounts[Volatile] = OldKeyNode->SubKeyCounts[Volatile];
  3773. HvReleaseCell(Hive, *NewKeyCell);
  3774. HvReleaseCell(Hive, OldKeyCell);
  3775. return STATUS_SUCCESS;
  3776. }
  3777. #ifdef WRITE_PROTECTED_REGISTRY_POOL
  3778. VOID
  3779. CmpMarkAllBinsReadOnly(
  3780. PHHIVE Hive
  3781. )
  3782. /*++
  3783. Routine Description:
  3784. Marks the memory allocated for all the stable bins in this hive as read only.
  3785. Arguments:
  3786. Hive - supplies a pointer to the hive control structure for the
  3787. hive of interest
  3788. Return Value:
  3789. NONE (It should work!)
  3790. --*/
  3791. {
  3792. PHMAP_ENTRY t;
  3793. PHBIN Bin;
  3794. HCELL_INDEX p;
  3795. ULONG Length;
  3796. //
  3797. // we are only interested in the stable storage
  3798. //
  3799. Length = Hive->Storage[Stable].Length;
  3800. p = 0;
  3801. //
  3802. // for each bin in the space
  3803. //
  3804. while (p < Length) {
  3805. t = HvpGetCellMap(Hive, p);
  3806. VALIDATE_CELL_MAP(__LINE__,t,Hive,p);
  3807. Bin = (PHBIN)HBIN_BASE(t->BinAddress);
  3808. if (t->BinAddress & HMAP_NEWALLOC) {
  3809. //
  3810. // Mark it as read Only
  3811. //
  3812. HvpChangeBinAllocation(Bin,TRUE);
  3813. }
  3814. // next one, please
  3815. p = (ULONG)p + Bin->Size;
  3816. }
  3817. }
  3818. #endif //WRITE_PROTECTED_REGISTRY_POOL
  3819. ULONG
  3820. CmpCompressKeyWorker(
  3821. PCM_KEY_CONTROL_BLOCK Current,
  3822. PVOID Context1,
  3823. PVOID Context2
  3824. )
  3825. {
  3826. PLIST_ENTRY pListHead;
  3827. PCM_KCB_REMAP_BLOCK kcbRemapBlock;
  3828. //PLIST_ENTRY AnchorAddr;
  3829. if (Current->KeyHive == Context1) {
  3830. pListHead = (PLIST_ENTRY)Context2;
  3831. ASSERT( pListHead );
  3832. /*
  3833. //
  3834. // check if we didn't already recorded this kcb
  3835. //
  3836. AnchorAddr = pListHead;
  3837. kcbRemapBlock = (PCM_KCB_REMAP_BLOCK)(pListHead->Flink);
  3838. while ( kcbRemapBlock != (PCM_KCB_REMAP_BLOCK)AnchorAddr ) {
  3839. kcbRemapBlock = CONTAINING_RECORD(
  3840. kcbRemapBlock,
  3841. CM_KCB_REMAP_BLOCK,
  3842. RemapList
  3843. );
  3844. if( kcbRemapBlock->KeyControlBlock == Current ) {
  3845. //
  3846. // we already have this kcb
  3847. //
  3848. return KCB_WORKER_CONTINUE;
  3849. }
  3850. //
  3851. // skip to the next element
  3852. //
  3853. kcbRemapBlock = (PCM_KCB_REMAP_BLOCK)(kcbRemapBlock->RemapList.Flink);
  3854. }
  3855. */
  3856. kcbRemapBlock = (PCM_KCB_REMAP_BLOCK)ExAllocatePool(PagedPool, sizeof(CM_KCB_REMAP_BLOCK));
  3857. if( kcbRemapBlock == NULL ) {
  3858. return KCB_WORKER_ERROR;
  3859. }
  3860. kcbRemapBlock->KeyControlBlock = Current;
  3861. kcbRemapBlock->NewCellIndex = HCELL_NIL;
  3862. kcbRemapBlock->OldCellIndex = Current->KeyCell;
  3863. kcbRemapBlock->ValueCount = 0;
  3864. kcbRemapBlock->ValueList = HCELL_NIL;
  3865. InsertTailList(pListHead,&(kcbRemapBlock->RemapList));
  3866. }
  3867. return KCB_WORKER_CONTINUE; // always keep searching
  3868. }
  3869. NTSTATUS
  3870. CmCompressKey(
  3871. IN PHHIVE Hive
  3872. )
  3873. /*++
  3874. Routine Description:
  3875. Compresses the kcb, by means of simulating an "in-place" SaveKey
  3876. What needs to be done:
  3877. 1. iterate through the kcb tree and make a list of all the kcbs
  3878. that need to be changed (their keycell will change during the process)
  3879. 2. iterate through the cache and compute an array of security cells.
  3880. We'll need it to map security cells into the new hive.
  3881. 3. Save the hive into a temporary hive, preserving
  3882. the volatile info in keynodes and updating the cell mappings.
  3883. 4. Update the cache by adding volatile security cells from the old hive.
  3884. 5. Dump temporary (compressed) hive over to the old file.
  3885. 6. Switch hive data from the compressed one to the existing one and update
  3886. the kcb KeyCell and security mapping
  3887. 7. Invalidate the map and drop paged bins.
  3888. 8. Free storage for the new hive (OK if we fail)
  3889. Arguments:
  3890. Hive - Hive to operate on
  3891. Return Value:
  3892. NTSTATUS - Result code from call, among the following:
  3893. <TBS>
  3894. --*/
  3895. {
  3896. NTSTATUS Status = STATUS_SUCCESS;
  3897. HCELL_INDEX KeyCell;
  3898. PCMHIVE CmHive;
  3899. PCM_KCB_REMAP_BLOCK RemapBlock;
  3900. PCMHIVE NewHive = NULL;
  3901. HCELL_INDEX LinkCell;
  3902. PCM_KEY_NODE LinkNode;
  3903. PCM_KNODE_REMAP_BLOCK KnodeRemapBlock;
  3904. ULONG OldLength;
  3905. PAGED_CODE();
  3906. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_CM,"CmCompressKey\n"));
  3907. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  3908. if( HvAutoCompressCheck(Hive) == FALSE ) {
  3909. return STATUS_SUCCESS;
  3910. }
  3911. KeyCell = Hive->BaseBlock->RootCell;
  3912. CmHive = CONTAINING_RECORD(Hive, CMHIVE, Hive);
  3913. //
  3914. // Make sure the cell passed in is the root cell of the hive.
  3915. //
  3916. if ( CmHive == CmpMasterHive ) {
  3917. return STATUS_INVALID_PARAMETER;
  3918. }
  3919. //
  3920. // 0. Get the cells we need to relink the compressed hive
  3921. //
  3922. LinkNode = (PCM_KEY_NODE)HvGetCell(Hive,KeyCell);
  3923. if( LinkNode == NULL ) {
  3924. return STATUS_INSUFFICIENT_RESOURCES;
  3925. }
  3926. LinkCell = LinkNode->Parent;
  3927. HvReleaseCell(Hive,KeyCell);
  3928. LinkNode = (PCM_KEY_NODE)HvGetCell((PHHIVE)CmpMasterHive,LinkCell);
  3929. // master storage is paged pool
  3930. ASSERT(LinkNode != NULL);
  3931. HvReleaseCell((PHHIVE)CmpMasterHive,LinkCell);
  3932. OldLength = Hive->BaseBlock->Length;
  3933. //
  3934. // 1. iterate through the kcb tree and make a list of all the kcbs
  3935. // that need to be changed (their keycell will change during the process)
  3936. //
  3937. ASSERT( IsListEmpty(&(CmHive->KcbConvertListHead)) );
  3938. //
  3939. // this will kick all kcb with refcount == 0 out of cache, so we can use
  3940. // CmpSearchKeyControlBlockTree for recording referenced kcbs
  3941. //
  3942. CmpCleanUpKCBCacheTable();
  3943. //CmpSearchForOpenSubKeys(KeyControlBlock,SearchIfExist);
  3944. if( !CmpSearchKeyControlBlockTree(CmpCompressKeyWorker,(PVOID)Hive,(PVOID)(&(CmHive->KcbConvertListHead))) ) {
  3945. Status = STATUS_INSUFFICIENT_RESOURCES;
  3946. goto Exit;
  3947. }
  3948. //
  3949. // 2. iterate through the cache and compute an array of security cells.
  3950. // We'll need it to map security cells into the new hive.
  3951. //
  3952. if( !CmpBuildSecurityCellMappingArray(CmHive) ) {
  3953. Status = STATUS_INSUFFICIENT_RESOURCES;
  3954. goto Exit;
  3955. }
  3956. //
  3957. // 3. Save the hive into a temporary hive , preserving
  3958. // the volatile info in keynodes and updating the cell mappings.
  3959. //
  3960. Status = CmpShiftHiveFreeBins(CmHive,&NewHive);
  3961. if( !NT_SUCCESS(Status) ) {
  3962. goto Exit;
  3963. }
  3964. //
  3965. // 5. Dump temporary (compressed) hive over to the old file.
  3966. //
  3967. Status = CmpOverwriteHive(CmHive,NewHive,LinkCell);
  3968. if (!NT_SUCCESS(Status)) {
  3969. goto Exit;
  3970. }
  3971. //
  3972. // From this point on, we WILL NOT FAIL!
  3973. //
  3974. //
  3975. // get the root node and link it into the master storage
  3976. //
  3977. LinkNode->ChildHiveReference.KeyCell = NewHive->Hive.BaseBlock->RootCell;
  3978. //
  3979. // 6. Switch hive data from the compressed one to the existing one and update
  3980. // the kcb KeyCell and security mapping
  3981. // This should better NOT fail!!! If it does, we are doomed, as we have partial
  3982. // data => bugcheck
  3983. //
  3984. CmpSwitchStorageAndRebuildMappings(CmHive,NewHive);
  3985. //
  3986. // 7. Invalidate the map and drop paged bins. If system hive, check for the hysteresis callback.
  3987. //
  3988. HvpDropAllPagedBins(&(CmHive->Hive));
  3989. if( OldLength < CmHive->Hive.BaseBlock->Length ) {
  3990. CmpUpdateSystemHiveHysteresis(&(CmHive->Hive),CmHive->Hive.BaseBlock->Length,OldLength);
  3991. }
  3992. Exit:
  3993. //
  3994. // 8. Free storage for the new hive (OK if we fail)
  3995. //
  3996. if( NewHive != NULL ) {
  3997. CmpDestroyTemporaryHive(NewHive);
  3998. }
  3999. if( CmHive->CellRemapArray != NULL ) {
  4000. ExFreePool(CmHive->CellRemapArray);
  4001. CmHive->CellRemapArray = NULL;
  4002. }
  4003. //
  4004. // remove all remap blocks and free them
  4005. //
  4006. while (IsListEmpty(&(CmHive->KcbConvertListHead)) == FALSE) {
  4007. RemapBlock = (PCM_KCB_REMAP_BLOCK)RemoveHeadList(&(CmHive->KcbConvertListHead));
  4008. RemapBlock = CONTAINING_RECORD(
  4009. RemapBlock,
  4010. CM_KCB_REMAP_BLOCK,
  4011. RemapList
  4012. );
  4013. ExFreePool(RemapBlock);
  4014. }
  4015. while (IsListEmpty(&(CmHive->KnodeConvertListHead)) == FALSE) {
  4016. KnodeRemapBlock = (PCM_KNODE_REMAP_BLOCK)RemoveHeadList(&(CmHive->KnodeConvertListHead));
  4017. KnodeRemapBlock = CONTAINING_RECORD(
  4018. KnodeRemapBlock,
  4019. CM_KNODE_REMAP_BLOCK,
  4020. RemapList
  4021. );
  4022. ExFreePool(KnodeRemapBlock);
  4023. }
  4024. return Status;
  4025. }
  4026. NTSTATUS
  4027. CmLockKcbForWrite(PCM_KEY_CONTROL_BLOCK KeyControlBlock)
  4028. /*++
  4029. Routine Description:
  4030. Tags the kcb as being read-only and no-delay-close
  4031. Arguments:
  4032. KeyControlBlock
  4033. Return Value:
  4034. TBS
  4035. --*/
  4036. {
  4037. PAGED_CODE();
  4038. CmpLockKCBTreeExclusive();
  4039. ASSERT_KCB(KeyControlBlock);
  4040. if( KeyControlBlock->Delete ) {
  4041. CmpUnlockKCBTree();
  4042. return STATUS_KEY_DELETED;
  4043. }
  4044. //
  4045. // sanity check in case we are called twice
  4046. //
  4047. ASSERT( ((KeyControlBlock->ExtFlags&CM_KCB_READ_ONLY_KEY) && (KeyControlBlock->ExtFlags&CM_KCB_NO_DELAY_CLOSE)) ||
  4048. (!(KeyControlBlock->ExtFlags&CM_KCB_READ_ONLY_KEY))
  4049. );
  4050. //
  4051. // tag the kcb as read-only; also make it no-delay close so it can revert to the normal state after all handles are closed.
  4052. //
  4053. KeyControlBlock->ExtFlags |= (CM_KCB_READ_ONLY_KEY|CM_KCB_NO_DELAY_CLOSE);
  4054. //
  4055. // add an artificial refcount on this kcb. This will keep the kcb (and the read only flag set in memory for as long as the system is up)
  4056. //
  4057. InterlockedIncrement( (PLONG)&KeyControlBlock->RefCount );
  4058. CmpUnlockKCBTree();
  4059. return STATUS_SUCCESS;
  4060. }
  4061. BOOLEAN
  4062. CmpCompareNewValueDataAgainstKCBCache( PCM_KEY_CONTROL_BLOCK KeyControlBlock,
  4063. PUNICODE_STRING ValueName,
  4064. ULONG Type,
  4065. PVOID Data,
  4066. ULONG DataSize
  4067. )
  4068. /*++
  4069. Routine Description:
  4070. Most of the SetValue calls are noops (i.e. they are setting the same
  4071. value name to the same value data). By comparing against the data already
  4072. in the kcb cache (i.e. faulted in) we can save page faults.
  4073. Arguments:
  4074. KeyControlBlock - pointer to kcb for the key to operate on
  4075. ValueName - The unique (relative to the containing key) name
  4076. of the value entry. May be NULL.
  4077. Type - The integer type number of the value entry.
  4078. Data - Pointer to buffer with actual data for the value entry.
  4079. DataSize - Size of Data buffer.
  4080. Return Value:
  4081. TRUE - same value with the same data exist in the cache.
  4082. --*/
  4083. {
  4084. PCM_KEY_VALUE Value;
  4085. ULONG Index;
  4086. BOOLEAN ValueCached;
  4087. PPCM_CACHED_VALUE ContainingList;
  4088. HCELL_INDEX ValueDataCellToRelease = HCELL_NIL;
  4089. BOOLEAN Result = FALSE;
  4090. PUCHAR datapointer = NULL;
  4091. BOOLEAN BufferAllocated = FALSE;
  4092. HCELL_INDEX CellToRelease = HCELL_NIL;
  4093. ULONG compareSize;
  4094. ULONG realsize;
  4095. BOOLEAN small;
  4096. PAGED_CODE();
  4097. BEGIN_KCB_LOCK_GUARD;
  4098. CmpLockKCBTreeExclusive();
  4099. if( KeyControlBlock->Flags & KEY_SYM_LINK ) {
  4100. //
  4101. // need to rebuild the value cache, so we could runt the same code
  4102. //
  4103. PCM_KEY_NODE Node = (PCM_KEY_NODE)HvGetCell(KeyControlBlock->KeyHive,KeyControlBlock->KeyCell);
  4104. if( Node == NULL ) {
  4105. //
  4106. // we couldn't map the bin containing this cell
  4107. //
  4108. goto Exit;
  4109. }
  4110. CmpCleanUpKcbValueCache(KeyControlBlock);
  4111. CmpSetUpKcbValueCache(KeyControlBlock,Node->ValueList.Count,Node->ValueList.List);
  4112. HvReleaseCell(KeyControlBlock->KeyHive,KeyControlBlock->KeyCell);
  4113. }
  4114. Value = CmpFindValueByNameFromCache(KeyControlBlock->KeyHive,
  4115. &(KeyControlBlock->ValueCache),
  4116. ValueName,
  4117. &ContainingList,
  4118. &Index,
  4119. &ValueCached,
  4120. &ValueDataCellToRelease
  4121. );
  4122. if(Value) {
  4123. if( (Type == Value->Type) && (DataSize == (Value->DataLength & ~CM_KEY_VALUE_SPECIAL_SIZE)) ) {
  4124. small = CmpIsHKeyValueSmall(realsize, Value->DataLength);
  4125. if (small == TRUE) {
  4126. datapointer = (PUCHAR)(&(Value->Data));
  4127. } else if( CmpGetValueDataFromCache(KeyControlBlock->KeyHive, ContainingList,(PCELL_DATA)Value,
  4128. ValueCached,&datapointer,&BufferAllocated,&CellToRelease) == FALSE ){
  4129. //
  4130. // we couldn't map view for cell; treat it as insufficient resources problem
  4131. //
  4132. ASSERT( datapointer == NULL );
  4133. ASSERT( BufferAllocated == FALSE );
  4134. goto Exit;
  4135. }
  4136. //
  4137. // compare data
  4138. //
  4139. if (DataSize > 0) {
  4140. try {
  4141. compareSize = (ULONG)RtlCompareMemory ((PVOID)datapointer,Data,(DataSize & ~CM_KEY_VALUE_SPECIAL_SIZE));
  4142. } except (EXCEPTION_EXECUTE_HANDLER) {
  4143. goto Exit;
  4144. }
  4145. } else {
  4146. compareSize = 0;
  4147. }
  4148. if (compareSize == DataSize) {
  4149. Result = TRUE;
  4150. }
  4151. }
  4152. }
  4153. Exit:
  4154. CmpUnlockKCBTree();
  4155. END_KCB_LOCK_GUARD;
  4156. if(ValueDataCellToRelease != HCELL_NIL) {
  4157. HvReleaseCell(KeyControlBlock->KeyHive,ValueDataCellToRelease);
  4158. }
  4159. if( BufferAllocated == TRUE ) {
  4160. ExFreePool(datapointer);
  4161. }
  4162. if(CellToRelease != HCELL_NIL) {
  4163. HvReleaseCell(KeyControlBlock->KeyHive,CellToRelease);
  4164. }
  4165. return Result;
  4166. }
  4167. NTSTATUS
  4168. static
  4169. __forceinline
  4170. CmpCheckReplaceHive( IN PHHIVE Hive,
  4171. OUT PHCELL_INDEX Key
  4172. )
  4173. {
  4174. HCELL_INDEX RootCell;
  4175. UNICODE_STRING Name;
  4176. NTSTATUS Status = STATUS_SUCCESS;
  4177. PRELEASE_CELL_ROUTINE TargetReleaseCellRoutine;
  4178. WCHAR Buffer[4];
  4179. PAGED_CODE();
  4180. ASSERT_CM_LOCK_OWNED_EXCLUSIVE();
  4181. //
  4182. // disable refcounting
  4183. //
  4184. TargetReleaseCellRoutine = Hive->ReleaseCellRoutine;
  4185. Hive->ReleaseCellRoutine = NULL;
  4186. Buffer[3] = 0;
  4187. *Key = HCELL_NIL;
  4188. Buffer[1] = (WCHAR)'P';
  4189. RootCell = Hive->BaseBlock->RootCell;
  4190. Buffer[2] = (WCHAR)'A';
  4191. if( RootCell == HCELL_NIL ) {
  4192. //
  4193. // could not find root cell. Bogus.
  4194. //
  4195. Status = STATUS_REGISTRY_CORRUPT;
  4196. goto Exit;
  4197. }
  4198. Buffer[0] = (WCHAR)'W';
  4199. RtlInitUnicodeString(&Name, Buffer);
  4200. RootCell = CmpFindSubKeyByName(Hive,
  4201. (PCM_KEY_NODE)HvGetCell(Hive,RootCell),
  4202. &Name);
  4203. if( RootCell != HCELL_NIL ) {
  4204. //
  4205. // found it.
  4206. //
  4207. *Key = RootCell;
  4208. } else {
  4209. //
  4210. // WPA key should be present; it's created by GUI mode.
  4211. //
  4212. Status = STATUS_REGISTRY_CORRUPT;
  4213. goto Exit;
  4214. }
  4215. Exit:
  4216. Hive->ReleaseCellRoutine = TargetReleaseCellRoutine;
  4217. return Status;
  4218. }