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.

2605 lines
83 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. hiveload.c
  5. Abstract:
  6. This module implements procedures to read a hive into memory, applying
  7. logs, etc.
  8. NOTE: Alternate image loading is not supported here, that is
  9. done by the boot loader.
  10. Author:
  11. Bryan M. Willman (bryanwi) 30-Mar-92
  12. Environment:
  13. Revision History:
  14. Dragos C. Sambotin (dragoss) 25-Jan-99
  15. Implementation of bin-size chunk loading of hives.
  16. Dragos C. Sambotin (dragoss) 10-Apr-99
  17. 64K IO reads when loading the hive
  18. --*/
  19. #include "cmp.h"
  20. typedef enum _RESULT {
  21. NotHive,
  22. Fail,
  23. NoMemory,
  24. HiveSuccess,
  25. RecoverHeader,
  26. RecoverData,
  27. SelfHeal
  28. } RESULT;
  29. RESULT
  30. HvpGetHiveHeader(
  31. PHHIVE Hive,
  32. PHBASE_BLOCK *BaseBlock,
  33. PLARGE_INTEGER TimeStamp
  34. );
  35. RESULT
  36. HvpGetLogHeader(
  37. PHHIVE Hive,
  38. PHBASE_BLOCK *BaseBlock,
  39. PLARGE_INTEGER TimeStamp
  40. );
  41. RESULT
  42. HvpRecoverData(
  43. PHHIVE Hive
  44. );
  45. NTSTATUS
  46. HvpReadFileImageAndBuildMap(
  47. PHHIVE Hive,
  48. ULONG Length
  49. );
  50. NTSTATUS
  51. HvpMapFileImageAndBuildMap(
  52. PHHIVE Hive,
  53. ULONG Length
  54. );
  55. VOID
  56. HvpDelistBinFreeCells(
  57. PHHIVE Hive,
  58. PHBIN Bin,
  59. HSTORAGE_TYPE Type
  60. );
  61. NTSTATUS
  62. HvpRecoverWholeHive(PHHIVE Hive,
  63. ULONG FileOffset);
  64. #ifdef ALLOC_PRAGMA
  65. #pragma alloc_text(PAGE,HvMapHive)
  66. #pragma alloc_text(PAGE,HvLoadHive)
  67. #pragma alloc_text(PAGE,HvpGetHiveHeader)
  68. #pragma alloc_text(PAGE,HvpGetLogHeader)
  69. #pragma alloc_text(PAGE,HvpRecoverData)
  70. #pragma alloc_text(PAGE,HvpReadFileImageAndBuildMap)
  71. #pragma alloc_text(PAGE,HvpMapFileImageAndBuildMap)
  72. #pragma alloc_text(PAGE,HvpRecoverWholeHive)
  73. #pragma alloc_text(PAGE,HvCloneHive)
  74. #pragma alloc_text(PAGE,HvShrinkHive)
  75. #endif
  76. extern PUCHAR CmpStashBuffer;
  77. extern ULONG CmpStashBufferSize;
  78. extern struct {
  79. PHHIVE Hive;
  80. ULONG Status;
  81. ULONG Space;
  82. HCELL_INDEX MapPoint;
  83. PHBIN BinPoint;
  84. } HvCheckHiveDebug;
  85. extern struct {
  86. PHHIVE Hive;
  87. ULONG FileOffset;
  88. ULONG FailPoint; // look in HvpRecoverData for exact point of failure
  89. } HvRecoverDataDebug;
  90. #if 0
  91. VOID
  92. HvDumpFileObjectState(
  93. IN HANDLE FileHandle
  94. )
  95. {
  96. NTSTATUS Status;
  97. PFILE_OBJECT FileObject;
  98. Status = ObReferenceObjectByHandle ( FileHandle,
  99. FILE_READ_DATA | FILE_WRITE_DATA,
  100. IoFileObjectType,
  101. KernelMode,
  102. (PVOID *)(&FileObject),
  103. NULL );
  104. if (!NT_SUCCESS(Status)) {
  105. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"[HvDumpFileObjectState] Could not reference file object status = %x\n",Status));
  106. } else {
  107. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"[HvDumpFileObjectState] FileObject = %p \n",FileObject));
  108. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL," \t SharedCacheMap = %p \n",FileObject->SectionObjectPointer->SharedCacheMap));
  109. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL," \t DataSectionObject = %p \n",FileObject->SectionObjectPointer->DataSectionObject));
  110. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL," \t ImageSectionObject = %p \n\n",FileObject->SectionObjectPointer->ImageSectionObject));
  111. ObDereferenceObject((PVOID)(FileObject));
  112. }
  113. }
  114. #endif //0
  115. // Dragos: Modified functions:
  116. NTSTATUS
  117. HvMapHive(
  118. PHHIVE Hive
  119. )
  120. /*++
  121. Routine Description:
  122. Hive must be fully initialized, in particular, file handles
  123. must be set up. This routine is not intended for loading hives
  124. from images already in memory.
  125. This routine will apply whatever fixes are available for errors
  126. in the hive image. In particular, if a log exists, and is applicable,
  127. this routine will automatically apply it.
  128. The difference from HvLoadHive is that this routine is NOT loading the
  129. hive into memory. It instead maps view of the hive in memory and does
  130. the bin enlisting and hive checking stuff.
  131. If errors are detected, the memory hive-loading is performed, log is applied
  132. and then bins are discarded.
  133. ALGORITHM:
  134. call HvpGetHiveHeader()
  135. if (NoMemory or NoHive)
  136. return failure
  137. if (RecoverData or RecoverHeader) and (no log)
  138. return falure
  139. if (RecoverHeader)
  140. call HvpGetLogHeader
  141. if (fail)
  142. return failure
  143. fix up baseblock
  144. Read Data
  145. if (RecoverData or RecoverHeader)
  146. HvpRecoverData
  147. return STATUS_REGISTRY_RECOVERED
  148. clean up sequence numbers
  149. return success OR STATUS_REGISTRY_RECOVERED
  150. If STATUS_REGISTRY_RECOVERED is returned, then
  151. If (Log) was used, DirtyVector and DirtyCount are set,
  152. caller is expected to flush the changes (using a
  153. NEW log file)
  154. Arguments:
  155. Hive - supplies a pointer to the hive control structure for the
  156. hive of interest
  157. TailDisplay - array containing the tail ends of the free cell lists - optional
  158. Return Value:
  159. STATUS:
  160. STATUS_INSUFFICIENT_RESOURCES - memory alloc failure, etc
  161. STATUS_NOT_REGISTRY_FILE - bad signatures and the like
  162. STATUS_REGISTRY_CORRUPT - bad signatures in the log,
  163. bad stuff in both in alternate,
  164. inconsistent log
  165. STATUS_REGISTRY_IO_FAILED - data read failed
  166. STATUS_RECOVERED - successfully recovered the hive,
  167. a semi-flush of logged data
  168. is necessary.
  169. STATUS_SUCCESS - it worked, no recovery needed
  170. --*/
  171. {
  172. PHBASE_BLOCK BaseBlock;
  173. ULONG result1;
  174. ULONG result2;
  175. NTSTATUS status;
  176. LARGE_INTEGER TimeStamp;
  177. #if DBG
  178. UNICODE_STRING HiveName;
  179. #endif
  180. ASSERT(Hive->Signature == HHIVE_SIGNATURE);
  181. #if 0
  182. HvDumpFileObjectState(((PCMHIVE)Hive)->FileHandles[HFILE_TYPE_PRIMARY]);
  183. #endif
  184. BaseBlock = NULL;
  185. result1 = HvpGetHiveHeader(Hive, &BaseBlock, &TimeStamp);
  186. //
  187. // bomb out for total errors
  188. //
  189. if (result1 == NoMemory) {
  190. status = STATUS_INSUFFICIENT_RESOURCES;
  191. goto Exit1;
  192. }
  193. if (result1 == NotHive) {
  194. status = STATUS_NOT_REGISTRY_FILE;
  195. goto Exit1;
  196. }
  197. //
  198. // if recovery needed, and no log, bomb out
  199. //
  200. if ( ((result1 == RecoverData) ||
  201. (result1 == RecoverHeader)) &&
  202. (Hive->Log == FALSE) )
  203. {
  204. status = STATUS_REGISTRY_CORRUPT;
  205. goto Exit1;
  206. }
  207. //
  208. // need to recover header using log, so try to get it from log
  209. //
  210. if (result1 == RecoverHeader) {
  211. result2 = HvpGetLogHeader(Hive, &BaseBlock, &TimeStamp);
  212. if (result2 == NoMemory) {
  213. status = STATUS_INSUFFICIENT_RESOURCES;
  214. goto Exit1;
  215. }
  216. if (result2 == Fail) {
  217. status = STATUS_REGISTRY_CORRUPT;
  218. goto Exit1;
  219. }
  220. BaseBlock->Type = HFILE_TYPE_PRIMARY;
  221. if( result2 == SelfHeal ) {
  222. //
  223. // tag as self heal so we can fire a warning later on.
  224. //
  225. BaseBlock->BootType = HBOOT_SELFHEAL;
  226. } else {
  227. BaseBlock->BootType = 0;
  228. }
  229. } else {
  230. BaseBlock->BootType = 0;
  231. }
  232. Hive->BaseBlock = BaseBlock;
  233. Hive->Version = Hive->BaseBlock->Minor;
  234. #if DBG
  235. RtlInitUnicodeString(&HiveName, (PCWSTR)Hive->BaseBlock->FileName);
  236. #endif
  237. status = HvpAdjustHiveFreeDisplay(Hive,BaseBlock->Length,Stable);
  238. if( !NT_SUCCESS(status) ) {
  239. goto Exit1;
  240. }
  241. //
  242. // at this point, we have a sane baseblock. we know for sure that the
  243. // pimary registry file is valid, so we don't need any data recovery
  244. //
  245. #if 0
  246. HvDumpFileObjectState(((PCMHIVE)Hive)->FileHandles[HFILE_TYPE_PRIMARY]);
  247. #endif
  248. #if DBG
  249. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Aquiring FileObject for hive (%p) (%.*S) ...",Hive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer));
  250. #endif
  251. status = CmpAquireFileObjectForFile((PCMHIVE)Hive,((PCMHIVE)Hive)->FileHandles[HFILE_TYPE_PRIMARY],&(((PCMHIVE)Hive)->FileObject));
  252. #if DBG
  253. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL," Status = %lx\n",status));
  254. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Initializing HiveViewList for hive (%p) (%.*S) \n\n",Hive,HiveName.Length / sizeof(WCHAR),HiveName.Buffer));
  255. #endif
  256. if( !NT_SUCCESS(status) ) {
  257. //
  258. // if status is STATUS_RETRY, top level routine will try to load it in the old fashioned way
  259. //
  260. goto Exit1;
  261. }
  262. #if 0
  263. HvDumpFileObjectState(((PCMHIVE)Hive)->FileHandles[HFILE_TYPE_PRIMARY]);
  264. #endif
  265. CmpPrefetchHiveFile( ((PCMHIVE)Hive)->FileObject,BaseBlock->Length);
  266. #ifdef CM_MAP_NO_READ
  267. //
  268. // we need to make sure all the cell's data is faulted in inside a
  269. // try/except block, as the IO to fault the data in can throw exceptions
  270. // STATUS_INSUFFICIENT_RESOURCES, in particular
  271. //
  272. try {
  273. #endif //CM_MAP_NO_READ
  274. status = HvpMapFileImageAndBuildMap(Hive,BaseBlock->Length);
  275. //
  276. // if STATUS_REGISTRY_CORRUPT and RecoverData don't bail out, keep recovering
  277. //
  278. if( !NT_SUCCESS(status) ) {
  279. //
  280. // need recovery but none available (RecoverHeader implies recover data).
  281. //
  282. if( (status != STATUS_REGISTRY_CORRUPT) && (status != STATUS_REGISTRY_RECOVERED) ) {
  283. goto Exit2;
  284. }
  285. if( (status == STATUS_REGISTRY_CORRUPT) && (result1 != RecoverData) && (result1 != RecoverHeader) ) {
  286. goto Exit2;
  287. }
  288. //
  289. // in case the above call returns STATUS_REGISTRY_RECOVERED, we should be sefl healing the hive
  290. //
  291. ASSERT( (status != STATUS_REGISTRY_RECOVERED) || CmDoSelfHeal() );
  292. }
  293. //
  294. // apply data recovery if we need it
  295. //
  296. if ( (result1 == RecoverHeader) || // -> implies recover data
  297. (result1 == RecoverData) )
  298. {
  299. result2 = HvpRecoverData(Hive);
  300. if (result2 == NoMemory) {
  301. status = STATUS_INSUFFICIENT_RESOURCES;
  302. goto Exit2;
  303. }
  304. if (result2 == Fail) {
  305. status = STATUS_REGISTRY_CORRUPT;
  306. goto Exit2;
  307. }
  308. status = STATUS_REGISTRY_RECOVERED;
  309. }
  310. #ifdef CM_MAP_NO_READ
  311. } except (EXCEPTION_EXECUTE_HANDLER) {
  312. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"HvMapHive: exception thrown ehile faulting in data, code:%08lx\n", GetExceptionCode()));
  313. status = GetExceptionCode();
  314. goto Exit2;
  315. }
  316. #endif //CM_MAP_NO_READ
  317. BaseBlock->Sequence2 = BaseBlock->Sequence1;
  318. return status;
  319. Exit2:
  320. //
  321. // Clean up the bins already allocated
  322. //
  323. HvpFreeAllocatedBins( Hive );
  324. //
  325. // Clean up the directory table
  326. //
  327. HvpCleanMap( Hive );
  328. Exit1:
  329. if (BaseBlock != NULL) {
  330. (Hive->Free)(BaseBlock, Hive->BaseBlockAlloc);
  331. }
  332. Hive->BaseBlock = NULL;
  333. Hive->DirtyCount = 0;
  334. return status;
  335. }
  336. /*++++++++++++++++++++++++++++++++++++++++
  337. This routine loads the hive into paged pool. We might not need it anymore!
  338. Support will be dropped as we see fit.
  339. ----------------------------------------*/
  340. NTSTATUS
  341. HvLoadHive(
  342. PHHIVE Hive
  343. )
  344. /*++
  345. Routine Description:
  346. Hive must be fully initialized, in particular, file handles
  347. must be set up. This routine is not intended for loading hives
  348. from images already in memory.
  349. This routine will apply whatever fixes are available for errors
  350. in the hive image. In particular, if a log exists, and is applicable,
  351. this routine will automatically apply it.
  352. ALGORITHM:
  353. call HvpGetHiveHeader()
  354. if (NoMemory or NoHive)
  355. return failure
  356. if (RecoverData or RecoverHeader) and (no log)
  357. return falure
  358. if (RecoverHeader)
  359. call HvpGetLogHeader
  360. if (fail)
  361. return failure
  362. fix up baseblock
  363. Read Data
  364. if (RecoverData or RecoverHeader)
  365. HvpRecoverData
  366. return STATUS_REGISTRY_RECOVERED
  367. clean up sequence numbers
  368. return success OR STATUS_REGISTRY_RECOVERED
  369. If STATUS_REGISTRY_RECOVERED is returned, then
  370. If (Log) was used, DirtyVector and DirtyCount are set,
  371. caller is expected to flush the changes (using a
  372. NEW log file)
  373. Arguments:
  374. Hive - supplies a pointer to the hive control structure for the
  375. hive of interest
  376. TailDisplay - array containing the tail ends of the free cell lists - optional
  377. Return Value:
  378. STATUS:
  379. STATUS_INSUFFICIENT_RESOURCES - memory alloc failure, etc
  380. STATUS_NOT_REGISTRY_FILE - bad signatures and the like
  381. STATUS_REGISTRY_CORRUPT - bad signatures in the log,
  382. bad stuff in both in alternate,
  383. inconsistent log
  384. STATUS_REGISTRY_IO_FAILED - data read failed
  385. STATUS_RECOVERED - successfully recovered the hive,
  386. a semi-flush of logged data
  387. is necessary.
  388. STATUS_SUCCESS - it worked, no recovery needed
  389. --*/
  390. {
  391. PHBASE_BLOCK BaseBlock;
  392. ULONG result1;
  393. ULONG result2;
  394. NTSTATUS status;
  395. LARGE_INTEGER TimeStamp;
  396. ASSERT(Hive->Signature == HHIVE_SIGNATURE);
  397. BaseBlock = NULL;
  398. result1 = HvpGetHiveHeader(Hive, &BaseBlock, &TimeStamp);
  399. //
  400. // bomb out for total errors
  401. //
  402. if (result1 == NoMemory) {
  403. status = STATUS_INSUFFICIENT_RESOURCES;
  404. goto Exit1;
  405. }
  406. if (result1 == NotHive) {
  407. status = STATUS_NOT_REGISTRY_FILE;
  408. goto Exit1;
  409. }
  410. //
  411. // if recovery needed, and no log, bomb out
  412. //
  413. if ( ((result1 == RecoverData) ||
  414. (result1 == RecoverHeader)) &&
  415. (Hive->Log == FALSE) )
  416. {
  417. status = STATUS_REGISTRY_CORRUPT;
  418. goto Exit1;
  419. }
  420. //
  421. // need to recover header using log, so try to get it from log
  422. //
  423. if (result1 == RecoverHeader) {
  424. result2 = HvpGetLogHeader(Hive, &BaseBlock, &TimeStamp);
  425. if (result2 == NoMemory) {
  426. status = STATUS_INSUFFICIENT_RESOURCES;
  427. goto Exit1;
  428. }
  429. if (result2 == Fail) {
  430. status = STATUS_REGISTRY_CORRUPT;
  431. goto Exit1;
  432. }
  433. BaseBlock->Type = HFILE_TYPE_PRIMARY;
  434. if( result2 == SelfHeal ) {
  435. //
  436. // tag as self heal so we can fire a warning later on.
  437. //
  438. BaseBlock->BootType = HBOOT_SELFHEAL;
  439. } else {
  440. BaseBlock->BootType = 0;
  441. }
  442. } else {
  443. BaseBlock->BootType = 0;
  444. }
  445. Hive->BaseBlock = BaseBlock;
  446. Hive->Version = Hive->BaseBlock->Minor;
  447. status = HvpAdjustHiveFreeDisplay(Hive,BaseBlock->Length,Stable);
  448. if( !NT_SUCCESS(status) ) {
  449. goto Exit1;
  450. }
  451. //
  452. // at this point, we have a sane baseblock. we may or may not still
  453. // need to apply data recovery
  454. //
  455. status = HvpReadFileImageAndBuildMap(Hive,BaseBlock->Length);
  456. //
  457. // if STATUS_REGISTRY_CORRUPT and RecoverData don't bail out, keep recovering
  458. //
  459. if( !NT_SUCCESS(status) ) {
  460. //
  461. // need recovery but none available (RecoverHeader implies recover data).
  462. //
  463. if( (status != STATUS_REGISTRY_CORRUPT) && (status != STATUS_REGISTRY_RECOVERED) ) {
  464. goto Exit2;
  465. }
  466. if( (status == STATUS_REGISTRY_CORRUPT) && (result1 != RecoverData) && (result1 != RecoverHeader) ) {
  467. goto Exit2;
  468. }
  469. //
  470. // in case the above call returns STATUS_REGISTRY_RECOVERED, we should be self healing the hive
  471. //
  472. ASSERT( (status != STATUS_REGISTRY_RECOVERED) || CmDoSelfHeal() );
  473. }
  474. //
  475. // apply data recovery if we need it
  476. //
  477. if ( (result1 == RecoverHeader) || // -> implies recover data
  478. (result1 == RecoverData) )
  479. {
  480. result2 = HvpRecoverData(Hive);
  481. if (result2 == NoMemory) {
  482. status = STATUS_INSUFFICIENT_RESOURCES;
  483. goto Exit2;
  484. }
  485. if (result2 == Fail) {
  486. status = STATUS_REGISTRY_CORRUPT;
  487. goto Exit2;
  488. }
  489. status = STATUS_REGISTRY_RECOVERED;
  490. }
  491. BaseBlock->Sequence2 = BaseBlock->Sequence1;
  492. return status;
  493. Exit2:
  494. //
  495. // Clean up the bins already allocated
  496. //
  497. HvpFreeAllocatedBins( Hive );
  498. //
  499. // Clean up the directory table
  500. //
  501. HvpCleanMap( Hive );
  502. Exit1:
  503. if (BaseBlock != NULL) {
  504. (Hive->Free)(BaseBlock, Hive->BaseBlockAlloc);
  505. }
  506. Hive->BaseBlock = NULL;
  507. Hive->DirtyCount = 0;
  508. return status;
  509. }
  510. NTSTATUS
  511. HvpReadFileImageAndBuildMap(
  512. PHHIVE Hive,
  513. ULONG Length
  514. )
  515. /*++
  516. Routine Description:
  517. Read the hive from the file and allocate storage for the hive
  518. image in chunks of HBINs. Build the hive map "on the fly".
  519. Optimized to read chunks of 64K from the file.
  520. Arguments:
  521. Hive - supplies a pointer to the hive control structure for the
  522. hive of interest
  523. Length - the length of the hive, in bytes
  524. TailDisplay - array containing the tail ends of the free cell lists - optional
  525. Return Value:
  526. STATUS:
  527. STATUS_INSUFFICIENT_RESOURCES - memory alloc failure, etc
  528. STATUS_REGISTRY_IO_FAILED - data read failed
  529. STATUS_REGISTRY_CORRUPT - base block is corrupt
  530. STATUS_SUCCESS - it worked
  531. --*/
  532. {
  533. ULONG FileOffset;
  534. NTSTATUS Status = STATUS_SUCCESS;
  535. PHBIN Bin; // current bin
  536. ULONG BinSize = 0; // size of the current bin
  537. ULONG BinOffset = 0; // current offset inside current bin
  538. ULONG BinFileOffset; // physical offset of the bin in the file (used for consistency checking)
  539. ULONG BinDataInBuffer;// the amount of data needed to be copied in the current bin available in the buffer
  540. ULONG BinDataNeeded; //
  541. PUCHAR IOBuffer;
  542. ULONG IOBufferSize; // valid data in IOBuffer (only at the end of the file this is different than IO_BUFFER_SIZE)
  543. ULONG IOBufferOffset; // current offset inside IOBuffer
  544. NTSTATUS Status2 = STATUS_SUCCESS; // used to force recoverData upon exit
  545. BOOLEAN MarkBinDirty;
  546. //
  547. // Init the map
  548. //
  549. Status = HvpInitMap(Hive);
  550. if( !NT_SUCCESS(Status) ) {
  551. //
  552. // return failure
  553. //
  554. return Status;
  555. }
  556. //
  557. // Allocate a IO_BUFFER_SIZE for I/O operations from paged pool.
  558. // It will be freed at the end of the function.
  559. //
  560. IOBuffer = (PUCHAR)ExAllocatePool(PagedPool, IO_BUFFER_SIZE);
  561. if (IOBuffer == NULL) {
  562. Status = STATUS_INSUFFICIENT_RESOURCES;
  563. HvpCleanMap( Hive );
  564. return Status;
  565. }
  566. //
  567. // Start right after the hive header
  568. //
  569. FileOffset = HBLOCK_SIZE;
  570. BinFileOffset = FileOffset;
  571. Bin = NULL;
  572. //
  573. // outer loop : reads IO_BUFFER_SIZE chunks from the file
  574. //
  575. while( FileOffset < (Length + HBLOCK_SIZE) ) {
  576. //
  577. // we are at the begining of the IO buffer
  578. //
  579. IOBufferOffset = 0;
  580. //
  581. // the buffer size will be either IO_BufferSize, or the amount
  582. // uread from the file (when this is smaller than IO_BUFFER_SIZE)
  583. //
  584. IOBufferSize = Length + HBLOCK_SIZE - FileOffset;
  585. IOBufferSize = ( IOBufferSize > IO_BUFFER_SIZE ) ? IO_BUFFER_SIZE : IOBufferSize;
  586. ASSERT( (IOBufferSize % HBLOCK_SIZE) == 0 );
  587. //
  588. // read data from the file
  589. //
  590. if ( ! (Hive->FileRead)(
  591. Hive,
  592. HFILE_TYPE_PRIMARY,
  593. &FileOffset,
  594. (PVOID)IOBuffer,
  595. IOBufferSize
  596. )
  597. )
  598. {
  599. Status = STATUS_REGISTRY_IO_FAILED;
  600. goto ErrorExit;
  601. }
  602. //
  603. // inner loop: breaks the buffer into bins
  604. //
  605. while( IOBufferOffset < IOBufferSize ) {
  606. MarkBinDirty = FALSE;
  607. if( Bin == NULL ) {
  608. //
  609. // this is the beginning of a new bin
  610. // perform bin validation and allocate the bin
  611. //
  612. // temporary bin points to the current location inside the buffer
  613. Bin = (PHBIN)(IOBuffer + IOBufferOffset);
  614. //
  615. // Check the validity of the bin header
  616. //
  617. BinSize = Bin->Size;
  618. if ( (BinSize > Length) ||
  619. (BinSize < HBLOCK_SIZE) ||
  620. (Bin->Signature != HBIN_SIGNATURE) ||
  621. (Bin->FileOffset != (BinFileOffset - HBLOCK_SIZE) )) {
  622. //
  623. // Bin is bogus
  624. //
  625. Bin = (PHBIN)(Hive->Allocate)(HBLOCK_SIZE, TRUE,CM_FIND_LEAK_TAG30);
  626. if (Bin == NULL) {
  627. Status = STATUS_INSUFFICIENT_RESOURCES;
  628. goto ErrorExit;
  629. }
  630. //
  631. // copy the data already read in the first HBLOCK of the bin
  632. //
  633. RtlCopyMemory(Bin,(IOBuffer + IOBufferOffset), HBLOCK_SIZE);
  634. Status2 = STATUS_REGISTRY_CORRUPT;
  635. HvCheckHiveDebug.Hive = Hive;
  636. HvCheckHiveDebug.Status = 0xA001;
  637. HvCheckHiveDebug.Space = Length;
  638. HvCheckHiveDebug.MapPoint = BinFileOffset - HBLOCK_SIZE;
  639. HvCheckHiveDebug.BinPoint = Bin;
  640. //goto ErrorExit;
  641. //
  642. // DO NOT EXIT; Fix this bin header and go on. RecoverData should fix it.
  643. // If not, CmCheckRegistry called later will prevent loading of an invalid hive
  644. //
  645. // NOTE: Still, mess the signature, to make sure that if this particular bin doesn't get recovered,
  646. // we'll fail the hive loading request.
  647. //
  648. if( CmDoSelfHeal() ) {
  649. //
  650. // put the correct signature, fileoffset and binsize in place;
  651. // HvEnlistBinInMap will take care of the cells consistency.
  652. //
  653. Bin->Signature = HBIN_SIGNATURE;
  654. Bin->FileOffset = BinFileOffset - HBLOCK_SIZE;
  655. if ( ((Bin->FileOffset + BinSize) > Length) ||
  656. (BinSize < HBLOCK_SIZE) ||
  657. (BinSize % HBLOCK_SIZE) ) {
  658. BinSize = Bin->Size = HBLOCK_SIZE;
  659. }
  660. //
  661. // signal back to the caller that we have altered the hive.
  662. //
  663. Status2 = STATUS_REGISTRY_RECOVERED;
  664. CmMarkSelfHeal(Hive);
  665. //
  666. // mark the bin dirty after enlisting.
  667. //
  668. MarkBinDirty = TRUE;
  669. } else {
  670. Bin->Signature = 0; //TRICK!!!!
  671. BinSize = Bin->Size = HBLOCK_SIZE;
  672. Bin->FileOffset = BinOffset;
  673. //
  674. // simulate as the entire bin is a used cell
  675. //
  676. ((PHCELL)((PUCHAR)Bin + sizeof(HBIN)))->Size = sizeof(HBIN) - BinSize; //TRICK!!!!
  677. }
  678. //
  679. // Now that we have the entire bin in memory, Enlist It!
  680. //
  681. Status = HvpEnlistBinInMap(Hive, Length, Bin, BinFileOffset - HBLOCK_SIZE, NULL);
  682. if( CmDoSelfHeal() && ((Status == STATUS_REGISTRY_RECOVERED) || MarkBinDirty) ) {
  683. //
  684. // self heal: enlist fixed the bin
  685. //
  686. Status2 = STATUS_REGISTRY_RECOVERED;
  687. Status = STATUS_SUCCESS;
  688. CmMarkSelfHeal(Hive);
  689. //
  690. // we are in self-heal mode and we have changed data in the bin; mark it all dirty.
  691. //
  692. HvMarkDirty(Hive,BinOffset,BinSize,TRUE);
  693. HvMarkDirty(Hive, 0, sizeof(HBIN),TRUE); // force header of 1st bin dirty
  694. }
  695. if( !NT_SUCCESS(Status) ) {
  696. goto ErrorExit;
  697. }
  698. //
  699. // Adjust the offsets
  700. //
  701. BinFileOffset += Bin->Size;
  702. IOBufferOffset += Bin->Size;
  703. //
  704. // another bin is on his way
  705. //
  706. Bin = NULL;
  707. } else {
  708. //
  709. // bin is valid; allocate a pool chunk of the right size
  710. //
  711. Bin = (PHBIN)(Hive->Allocate)(BinSize, TRUE,CM_FIND_LEAK_TAG31);
  712. if (Bin == NULL) {
  713. Status = STATUS_INSUFFICIENT_RESOURCES;
  714. goto ErrorExit;
  715. }
  716. //
  717. // the chunk is allocated; set the offset inside the bin and continue
  718. // the next iteration of the inner loop will start by copying data in this bin
  719. //
  720. BinOffset = 0;
  721. }
  722. } else {
  723. //
  724. // if we are here, the bin is allocated, the BinSize and BinOffset are set
  725. // We have to calculate how much for this bin is available in the buffer,
  726. // and copy it. If we finished with this bin, enlist it and mark the begining of a new one
  727. //
  728. ASSERT( Bin != NULL );
  729. BinDataInBuffer = (IOBufferSize - IOBufferOffset);
  730. BinDataNeeded = (BinSize - BinOffset);
  731. if( BinDataInBuffer >= BinDataNeeded ) {
  732. //
  733. // we have available more than what we need; Finish the bin
  734. //
  735. RtlCopyMemory(((PUCHAR)Bin + BinOffset),(IOBuffer + IOBufferOffset), BinDataNeeded);
  736. //
  737. // enlist it
  738. //
  739. Status = HvpEnlistBinInMap(Hive, Length, Bin, BinFileOffset - HBLOCK_SIZE, NULL);
  740. if( CmDoSelfHeal() && (Status == STATUS_REGISTRY_RECOVERED) ) {
  741. //
  742. // self heal: enlist fixed the bin
  743. //
  744. Status2 = STATUS_REGISTRY_RECOVERED;
  745. Status = STATUS_SUCCESS;
  746. CmMarkSelfHeal(Hive);
  747. //
  748. // we are in self-heal mode and we have changed data in the bin; mark it all dirty.
  749. //
  750. HvMarkDirty(Hive,BinOffset,BinSize,TRUE);
  751. HvMarkDirty(Hive, 0, sizeof(HBIN),TRUE); // force header of 1st bin dirty
  752. }
  753. if( !NT_SUCCESS(Status) ) {
  754. goto ErrorExit;
  755. }
  756. //
  757. // Adjust the offsets
  758. //
  759. BinFileOffset += BinSize;
  760. IOBufferOffset += BinDataNeeded;
  761. //
  762. // mark the begining of a new bin
  763. //
  764. Bin = NULL;
  765. } else {
  766. //
  767. // we do not have all bin data in the buffer
  768. // copy what we can
  769. //
  770. RtlCopyMemory(((PUCHAR)Bin + BinOffset),(IOBuffer + IOBufferOffset), BinDataInBuffer);
  771. //
  772. // adjust the offsets; this should be the last iteration of the inner loop
  773. //
  774. BinOffset += BinDataInBuffer;
  775. IOBufferOffset += BinDataInBuffer;
  776. //
  777. // if we are here, the buffer must have beed exausted
  778. //
  779. ASSERT( IOBufferOffset == IOBufferSize );
  780. }
  781. }
  782. }
  783. }
  784. //
  785. // if we got here, we shouldn't have a bin under construction
  786. //
  787. ASSERT( Bin == NULL );
  788. //
  789. // Free the buffer used for I/O operations
  790. //
  791. ExFreePool(IOBuffer);
  792. Status = NT_SUCCESS(Status)?Status2:Status;
  793. return Status;
  794. ErrorExit:
  795. //
  796. // Free the buffer used for I/O operations
  797. //
  798. ExFreePool(IOBuffer);
  799. return Status;
  800. }
  801. RESULT
  802. HvpGetHiveHeader(
  803. PHHIVE Hive,
  804. PHBASE_BLOCK *BaseBlock,
  805. PLARGE_INTEGER TimeStamp
  806. )
  807. /*++
  808. Routine Description:
  809. Examine the base block sector and possibly the first sector of
  810. the first bin, and decide what (if any) recovery needs to be applied
  811. based on what we find there.
  812. ALGORITHM:
  813. read BaseBlock from offset 0
  814. if ( (I/O error) OR
  815. (checksum wrong) )
  816. {
  817. read bin block from offset HBLOCK_SIZE (4k)
  818. if (2nd I/O error)
  819. return NotHive
  820. }
  821. check bin sign., offset.
  822. if (OK)
  823. return RecoverHeader, TimeStamp=from Link field
  824. } else {
  825. return NotHive
  826. }
  827. }
  828. if (wrong type or signature or version or format)
  829. return NotHive
  830. }
  831. if (seq1 != seq2) {
  832. return RecoverData, TimeStamp=BaseBlock->TimeStamp, valid BaseBlock
  833. }
  834. return ReadData, valid BaseBlock
  835. Arguments:
  836. Hive - supplies a pointer to the hive control structure for the
  837. hive of interest
  838. BaseBlock - supplies pointer to variable to receive pointer to
  839. HBASE_BLOCK, if we can successfully read one.
  840. TimeStamp - pointer to variable to receive time stamp (serial number)
  841. of hive, be it from the baseblock or from the Link field
  842. of the first bin.
  843. Return Value:
  844. RESULT code
  845. --*/
  846. {
  847. PHBASE_BLOCK buffer;
  848. BOOLEAN rc;
  849. ULONG FileOffset;
  850. ULONG Alignment;
  851. ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster));
  852. //
  853. // allocate buffer to hold base block
  854. //
  855. *BaseBlock = NULL;
  856. buffer = (PHBASE_BLOCK)((Hive->Allocate)(Hive->BaseBlockAlloc, TRUE,CM_FIND_LEAK_TAG32));
  857. if (buffer == NULL) {
  858. return NoMemory;
  859. }
  860. //
  861. // Make sure the buffer we got back is cluster-aligned. If not, try
  862. // harder to get an aligned buffer.
  863. //
  864. Alignment = Hive->Cluster * HSECTOR_SIZE - 1;
  865. if (((ULONG_PTR)buffer & Alignment) != 0) {
  866. (Hive->Free)(buffer, Hive->BaseBlockAlloc);
  867. buffer = (PHBASE_BLOCK)((Hive->Allocate)(PAGE_SIZE, TRUE,CM_FIND_LEAK_TAG33));
  868. if (buffer == NULL) {
  869. return NoMemory;
  870. }
  871. Hive->BaseBlockAlloc = PAGE_SIZE;
  872. }
  873. RtlZeroMemory((PVOID)buffer, sizeof(HBASE_BLOCK));
  874. //
  875. // attempt to read base block
  876. //
  877. FileOffset = 0;
  878. rc = (Hive->FileRead)(Hive,
  879. HFILE_TYPE_PRIMARY,
  880. &FileOffset,
  881. (PVOID)buffer,
  882. HSECTOR_SIZE * Hive->Cluster);
  883. if ( (rc == FALSE) ||
  884. (HvpHeaderCheckSum(buffer) != buffer->CheckSum)) {
  885. //
  886. // base block is toast, try the first block in the first bin
  887. //
  888. FileOffset = HBLOCK_SIZE;
  889. rc = (Hive->FileRead)(Hive,
  890. HFILE_TYPE_PRIMARY,
  891. &FileOffset,
  892. (PVOID)buffer,
  893. HSECTOR_SIZE * Hive->Cluster);
  894. if ( (rc == FALSE) ||
  895. ( ((PHBIN)buffer)->Signature != HBIN_SIGNATURE) ||
  896. ( ((PHBIN)buffer)->FileOffset != 0)
  897. )
  898. {
  899. //
  900. // the bin is toast too, punt
  901. //
  902. (Hive->Free)(buffer, Hive->BaseBlockAlloc);
  903. return NotHive;
  904. }
  905. //
  906. // base block is bogus, but bin is OK, so tell caller
  907. // to look for a log file and apply recovery
  908. //
  909. *TimeStamp = ((PHBIN)buffer)->TimeStamp;
  910. (Hive->Free)(buffer, Hive->BaseBlockAlloc);
  911. return RecoverHeader;
  912. }
  913. //
  914. // base block read OK, but is it valid?
  915. //
  916. if ( (buffer->Signature != HBASE_BLOCK_SIGNATURE) ||
  917. (buffer->Type != HFILE_TYPE_PRIMARY) ||
  918. (buffer->Major != HSYS_MAJOR) ||
  919. (buffer->Minor > HSYS_MINOR_SUPPORTED) ||
  920. ((buffer->Major == 1) && (buffer->Minor == 0)) ||
  921. (buffer->Format != HBASE_FORMAT_MEMORY)
  922. )
  923. {
  924. //
  925. // file is simply not a valid hive
  926. //
  927. (Hive->Free)(buffer, Hive->BaseBlockAlloc);
  928. return NotHive;
  929. }
  930. //
  931. // see if recovery is necessary
  932. //
  933. *BaseBlock = buffer;
  934. *TimeStamp = buffer->TimeStamp;
  935. if ( (buffer->Sequence1 != buffer->Sequence2) ) {
  936. return RecoverData;
  937. }
  938. return HiveSuccess;
  939. }
  940. RESULT
  941. HvpGetLogHeader(
  942. PHHIVE Hive,
  943. PHBASE_BLOCK *BaseBlock,
  944. PLARGE_INTEGER TimeStamp
  945. )
  946. /*++
  947. Routine Description:
  948. Read and validate log file header. Return it if it's valid.
  949. ALGORITHM:
  950. read header
  951. if ( (I/O error) or
  952. (wrong signature,
  953. wrong type,
  954. seq mismatch
  955. wrong checksum,
  956. wrong timestamp
  957. )
  958. return Fail
  959. }
  960. return baseblock, OK
  961. Arguments:
  962. Hive - supplies a pointer to the hive control structure for the
  963. hive of interest
  964. BaseBlock - supplies pointer to variable to receive pointer to
  965. HBASE_BLOCK, if we can successfully read one.
  966. TimeStamp - pointer to variable holding TimeStamp, which must
  967. match the one in the log file.
  968. Return Value:
  969. RESULT
  970. --*/
  971. {
  972. PHBASE_BLOCK buffer;
  973. BOOLEAN rc;
  974. ULONG FileOffset;
  975. ASSERT(sizeof(HBASE_BLOCK) == HBLOCK_SIZE);
  976. ASSERT(sizeof(HBASE_BLOCK) >= (HSECTOR_SIZE * Hive->Cluster));
  977. //
  978. // allocate buffer to hold base block
  979. //
  980. *BaseBlock = NULL;
  981. buffer = (PHBASE_BLOCK)((Hive->Allocate)(Hive->BaseBlockAlloc, TRUE,CM_FIND_LEAK_TAG34));
  982. if (buffer == NULL) {
  983. return NoMemory;
  984. }
  985. RtlZeroMemory((PVOID)buffer, HSECTOR_SIZE);
  986. //
  987. // attempt to read base block
  988. //
  989. FileOffset = 0;
  990. rc = (Hive->FileRead)(Hive,
  991. HFILE_TYPE_LOG,
  992. &FileOffset,
  993. (PVOID)buffer,
  994. HSECTOR_SIZE * Hive->Cluster);
  995. if ( (rc == FALSE) ||
  996. (buffer->Signature != HBASE_BLOCK_SIGNATURE) ||
  997. (buffer->Type != HFILE_TYPE_LOG) ||
  998. (buffer->Sequence1 != buffer->Sequence2) ||
  999. (HvpHeaderCheckSum(buffer) != buffer->CheckSum) ||
  1000. (TimeStamp->LowPart != buffer->TimeStamp.LowPart) ||
  1001. (TimeStamp->HighPart != buffer->TimeStamp.HighPart)) {
  1002. if( CmDoSelfHeal() ) {
  1003. //
  1004. // We are in self healing mode; Fix the header and go on
  1005. //
  1006. FILE_FS_SIZE_INFORMATION FsSizeInformation;
  1007. IO_STATUS_BLOCK IoStatusBlock;
  1008. FILE_END_OF_FILE_INFORMATION FileInfo;
  1009. ULONG Cluster;
  1010. NTSTATUS Status;
  1011. Status = ZwQueryVolumeInformationFile(
  1012. ((PCMHIVE)Hive)->FileHandles[HFILE_TYPE_PRIMARY],
  1013. &IoStatusBlock,
  1014. &FsSizeInformation,
  1015. sizeof(FILE_FS_SIZE_INFORMATION),
  1016. FileFsSizeInformation
  1017. );
  1018. if (!NT_SUCCESS(Status)) {
  1019. Cluster = 1;
  1020. } else if (FsSizeInformation.BytesPerSector > HBLOCK_SIZE) {
  1021. (Hive->Free)(buffer, Hive->BaseBlockAlloc);
  1022. return Fail;
  1023. }
  1024. Cluster = FsSizeInformation.BytesPerSector / HSECTOR_SIZE;
  1025. Cluster = (Cluster < 1) ? 1 : Cluster;
  1026. Status = ZwQueryInformationFile(
  1027. ((PCMHIVE)Hive)->FileHandles[HFILE_TYPE_PRIMARY],
  1028. &IoStatusBlock,
  1029. (PVOID)&FileInfo,
  1030. sizeof(FILE_END_OF_FILE_INFORMATION),
  1031. FileEndOfFileInformation
  1032. );
  1033. if(!NT_SUCCESS(Status)) {
  1034. (Hive->Free)(buffer, Hive->BaseBlockAlloc);
  1035. return Fail;
  1036. }
  1037. buffer->Signature = HBASE_BLOCK_SIGNATURE;
  1038. buffer->Sequence1 = buffer->Sequence2 = 1;
  1039. buffer->Cluster = Cluster;
  1040. buffer->Length = FileInfo.EndOfFile.LowPart - HBLOCK_SIZE;
  1041. buffer->CheckSum = HvpHeaderCheckSum(buffer);
  1042. *BaseBlock = buffer;
  1043. return SelfHeal;
  1044. } else {
  1045. //
  1046. // Log is unreadable, invalid, or doesn't apply the right hive
  1047. //
  1048. (Hive->Free)(buffer, Hive->BaseBlockAlloc);
  1049. return Fail;
  1050. }
  1051. }
  1052. *BaseBlock = buffer;
  1053. return HiveSuccess;
  1054. }
  1055. NTSTATUS
  1056. HvpMapFileImageAndBuildMap(
  1057. PHHIVE Hive,
  1058. ULONG Length
  1059. )
  1060. /*++
  1061. Routine Description:
  1062. map views of the file in memory and initialize the bin map.
  1063. we are based on the assumption that no bin is crossing the CM_VIEW_SIZE boundary.
  1064. asserts and validation code should be added later on this matter.
  1065. Arguments:
  1066. Hive - supplies a pointer to the hive control structure for the
  1067. hive of interest
  1068. Length - the length of the hive, in bytes
  1069. Return Value:
  1070. STATUS:
  1071. STATUS_INSUFFICIENT_RESOURCES - memory alloc failure, etc
  1072. STATUS_REGISTRY_IO_FAILED - data read failed
  1073. STATUS_REGISTRY_CORRUPT - base block is corrupt
  1074. STATUS_SUCCESS - it worked
  1075. --*/
  1076. {
  1077. NTSTATUS Status = STATUS_SUCCESS;
  1078. ULONG FileOffset = 0;
  1079. ULONG BinOffset = 0;
  1080. PCM_VIEW_OF_FILE CmView;
  1081. PHMAP_ENTRY Me;
  1082. PHBIN Bin; // current bin
  1083. ULONG BinSize; // size of the current bin
  1084. NTSTATUS Status2 = STATUS_SUCCESS; // used to force recoverData upon exit
  1085. BOOLEAN MarkBinDirty;
  1086. //
  1087. // Init the map
  1088. //
  1089. Status = HvpInitMap(Hive);
  1090. if( !NT_SUCCESS(Status) ) {
  1091. //
  1092. // return failure
  1093. //
  1094. return Status;
  1095. }
  1096. //
  1097. // mark all entries in the map as invalid
  1098. //
  1099. // I moved this in HvpAllocateMap.
  1100. //
  1101. while( BinOffset < Length) {
  1102. Status = CmpMapCmView((PCMHIVE)Hive,BinOffset,&CmView,FALSE/*map not initialized yet*/);
  1103. if( !NT_SUCCESS(Status) ) {
  1104. goto ErrorExit;
  1105. }
  1106. //
  1107. // touch the view
  1108. //
  1109. CmpTouchView((PCMHIVE)Hive,CmView,BinOffset);
  1110. //
  1111. // iterate through the map (starting with this offset)
  1112. // the stop condition is when we get an invalid bin
  1113. // (valid bins should be mapped in view)
  1114. //
  1115. while((Me = HvpGetCellMap(Hive, BinOffset)) != NULL) {
  1116. //
  1117. // attention here ! Bins crossing the CM_VIEW_SIZE boundary
  1118. // should be allocated from paged pool !!!!!
  1119. //
  1120. if( (Me->BinAddress & HMAP_INVIEW) == 0 ) {
  1121. //
  1122. // we have reached the end of the view
  1123. //
  1124. break;
  1125. }
  1126. Bin = (PHBIN)Me->BlockAddress;
  1127. MarkBinDirty = FALSE;
  1128. //
  1129. // we should be here at the begining of a new bin
  1130. //
  1131. BinSize = Bin->Size;
  1132. if ( (BinSize > Length) ||
  1133. (BinSize < HBLOCK_SIZE) ||
  1134. (Bin->Signature != HBIN_SIGNATURE) ||
  1135. (Bin->FileOffset != BinOffset ) ) {
  1136. //
  1137. // Bin is bogus
  1138. //
  1139. Status2 = STATUS_REGISTRY_CORRUPT;
  1140. HvCheckHiveDebug.Hive = Hive;
  1141. HvCheckHiveDebug.Status = 0xA001;
  1142. HvCheckHiveDebug.Space = Length;
  1143. HvCheckHiveDebug.MapPoint = BinOffset;
  1144. HvCheckHiveDebug.BinPoint = Bin;
  1145. //goto ErrorExit;
  1146. //
  1147. // DO NOT EXIT; Fix this bin header and go on. RecoverData should fix it.
  1148. // If not, CmCheckRegistry called later will prevent loading of an invalid hive
  1149. //
  1150. // NOTE: Still, mess the signature, to make sure that if this particular bin doesn't get recovered,
  1151. // we'll fail the hive loading request.
  1152. //
  1153. if( CmDoSelfHeal() ) {
  1154. //
  1155. // put the correct signature, fileoffset and binsize in place;
  1156. // HvEnlistBinInMap will take care of the cells consistency.
  1157. //
  1158. Bin->Signature = HBIN_SIGNATURE;
  1159. Bin->FileOffset = BinOffset;
  1160. if ( ((BinOffset + BinSize) > Length) ||
  1161. (BinSize < HBLOCK_SIZE) ||
  1162. (BinSize % HBLOCK_SIZE) ) {
  1163. BinSize = Bin->Size = HBLOCK_SIZE;
  1164. }
  1165. //
  1166. // signal back to the caller that we have altered the hive.
  1167. //
  1168. Status2 = STATUS_REGISTRY_RECOVERED;
  1169. CmMarkSelfHeal(Hive);
  1170. //
  1171. // remember to mark the bin dirty after we enlist it
  1172. //
  1173. MarkBinDirty = TRUE;
  1174. } else {
  1175. Bin->Signature = 0; //TRICK!!!!
  1176. BinSize = Bin->Size = HBLOCK_SIZE;
  1177. Bin->FileOffset = BinOffset;
  1178. //
  1179. // simulate as the entire bin is a used cell
  1180. //
  1181. ((PHCELL)((PUCHAR)Bin + sizeof(HBIN)))->Size = sizeof(HBIN) - BinSize; //TRICK!!!!
  1182. }
  1183. }
  1184. //
  1185. // Bins crossing the CM_VIEW_SIZE boundary problem.
  1186. // We fix it here, by loading the entire bin
  1187. // into paged pool
  1188. //
  1189. if( HvpCheckViewBoundary(BinOffset,BinOffset+BinSize-1) == FALSE ) {
  1190. //
  1191. // it is ilegal to fall through here if we did the trick above.
  1192. //
  1193. ASSERT( Bin->Signature == HBIN_SIGNATURE );
  1194. //
  1195. // load it in the old fashioned way (into paged pool)
  1196. //
  1197. CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"HvpMapFileImageAndBuildMap: Bin crossing CM_VIEW_SIZE boundary at BinOffset = %lx BinSize = %lx\n",BinOffset,BinSize));
  1198. // first, allocate the bin
  1199. Bin = (PHBIN)(Hive->Allocate)(BinSize, TRUE,CM_FIND_LEAK_TAG35);
  1200. if (Bin == NULL) {
  1201. Status = STATUS_INSUFFICIENT_RESOURCES;
  1202. goto ErrorExit;
  1203. }
  1204. //
  1205. // read data from the file
  1206. //
  1207. FileOffset = BinOffset + HBLOCK_SIZE;
  1208. if ( ! (Hive->FileRead)(
  1209. Hive,
  1210. HFILE_TYPE_PRIMARY,
  1211. &FileOffset,
  1212. (PVOID)Bin,
  1213. BinSize
  1214. )
  1215. )
  1216. {
  1217. (Hive->Free)(Bin, BinSize);
  1218. Status = STATUS_REGISTRY_IO_FAILED;
  1219. goto ErrorExit;
  1220. }
  1221. ASSERT( (FileOffset - HBLOCK_SIZE) == (BinOffset + BinSize) );
  1222. //
  1223. // enlist the bin as in paged pool
  1224. //
  1225. Status = HvpEnlistBinInMap(Hive, Length, Bin, BinOffset, NULL);
  1226. } else {
  1227. //
  1228. // Now that we have the entire bin mapped in memory, Enlist It!
  1229. //
  1230. Status = HvpEnlistBinInMap(Hive, Length, Bin, BinOffset, CmView);
  1231. }
  1232. //
  1233. // account for self healing
  1234. //
  1235. if( CmDoSelfHeal() && ((Status == STATUS_REGISTRY_RECOVERED) || MarkBinDirty) ) {
  1236. Status2 = STATUS_REGISTRY_RECOVERED;
  1237. Status = STATUS_SUCCESS;
  1238. CmMarkSelfHeal(Hive);
  1239. //
  1240. // we are in self-heal mode and we have changed data in the bin; mark it all dirty.
  1241. //
  1242. HvMarkDirty(Hive,BinOffset,BinSize,TRUE);
  1243. HvMarkDirty(Hive, 0, sizeof(HBIN),TRUE); // force header of 1st bin dirty
  1244. }
  1245. //
  1246. // advance to the new bin
  1247. //
  1248. BinOffset += BinSize;
  1249. if( !NT_SUCCESS(Status) ) {
  1250. goto ErrorExit;
  1251. }
  1252. }
  1253. }
  1254. Status = NT_SUCCESS(Status)?Status2:Status;
  1255. return Status;
  1256. ErrorExit:
  1257. //
  1258. // DO NOT Clean up the directory table, as we may want to recover the hive
  1259. //
  1260. //if( Status != STATUS_REGISTRY_CORRUPT ) {
  1261. // HvpFreeAllocatedBins( Hive );
  1262. // HvpCleanMap( Hive );
  1263. //}
  1264. return Status;
  1265. }
  1266. RESULT
  1267. HvpRecoverData(
  1268. PHHIVE Hive
  1269. )
  1270. /*++
  1271. Routine Description:
  1272. Apply the corrections in the log file to the hive memory image.
  1273. ALGORITHM:
  1274. compute size of dirty vector
  1275. read in dirty vector
  1276. if (i/o error)
  1277. return Fail
  1278. skip first cluster of data (already processed as log)
  1279. sweep vector, looking for runs of bits
  1280. address of first bit is used to compute memory offset
  1281. length of run is length of block to read
  1282. assert always a cluster multiple
  1283. file offset kept by running counter
  1284. read
  1285. if (i/o error)
  1286. return fail
  1287. return success
  1288. NOTE: This routine works with hives mapped OR loaded into paged pool
  1289. Arguments:
  1290. Hive - supplies a pointer to the hive control structure for the
  1291. hive of interest
  1292. Return Value:
  1293. RESULT
  1294. --*/
  1295. {
  1296. ULONG Cluster;
  1297. ULONG ClusterSize;
  1298. ULONG HeaderLength;
  1299. ULONG VectorSize;
  1300. PULONG Vector;
  1301. ULONG FileOffset;
  1302. BOOLEAN rc;
  1303. ULONG Current;
  1304. ULONG Start;
  1305. ULONG End;
  1306. ULONG Address;
  1307. PUCHAR MemoryBlock;
  1308. RTL_BITMAP BitMap;
  1309. ULONG Length;
  1310. ULONG DirtyVectorSignature = 0;
  1311. ULONG RequestedReadBufferSize;
  1312. ULONG i;
  1313. PHMAP_ENTRY Me;
  1314. PHBIN Bin;
  1315. PHBIN NewBin;
  1316. PUCHAR SectorImage;
  1317. PUCHAR Source;
  1318. PHBIN SourceBin;
  1319. ULONG SectorOffsetInBin;
  1320. ULONG SectorOffsetInBlock;
  1321. ULONG BlockOffsetInBin;
  1322. ULONG NumberOfSectors;
  1323. PCM_VIEW_OF_FILE CmView;
  1324. NTSTATUS Status;
  1325. //
  1326. // compute size of dirty vector, read and check signature, read vector
  1327. //
  1328. Cluster = Hive->Cluster;
  1329. ClusterSize = Cluster * HSECTOR_SIZE;
  1330. HeaderLength = ROUND_UP(HLOG_HEADER_SIZE, ClusterSize);
  1331. Length = Hive->BaseBlock->Length;
  1332. VectorSize = (Length / HSECTOR_SIZE) / 8; // VectorSize == Bytes
  1333. FileOffset = ROUND_UP(HLOG_HEADER_SIZE, HeaderLength);
  1334. HvRecoverDataDebug.Hive = Hive;
  1335. HvRecoverDataDebug.FailPoint = 0;
  1336. //
  1337. // we need to align the reads at sector size too
  1338. //
  1339. RequestedReadBufferSize = VectorSize + sizeof(DirtyVectorSignature);
  1340. LOCK_STASH_BUFFER();
  1341. if( CmpStashBufferSize < RequestedReadBufferSize ) {
  1342. PUCHAR TempBuffer = ExAllocatePoolWithTag(PagedPool, ROUND_UP(RequestedReadBufferSize,PAGE_SIZE),CM_STASHBUFFER_TAG);
  1343. if (TempBuffer == NULL) {
  1344. HvRecoverDataDebug.FailPoint = 1;
  1345. UNLOCK_STASH_BUFFER();
  1346. return Fail;
  1347. }
  1348. if( CmpStashBuffer != NULL ) {
  1349. ExFreePool( CmpStashBuffer );
  1350. }
  1351. CmpStashBuffer = TempBuffer;
  1352. CmpStashBufferSize = ROUND_UP(RequestedReadBufferSize,PAGE_SIZE);
  1353. }
  1354. //
  1355. // get the signature and dirty vector at one time
  1356. //
  1357. RequestedReadBufferSize = ROUND_UP(RequestedReadBufferSize,ClusterSize);
  1358. ASSERT( RequestedReadBufferSize <= CmpStashBufferSize);
  1359. ASSERT( (RequestedReadBufferSize % HSECTOR_SIZE) == 0 );
  1360. rc = (Hive->FileRead)(
  1361. Hive,
  1362. HFILE_TYPE_LOG,
  1363. &FileOffset,
  1364. (PVOID)CmpStashBuffer,
  1365. RequestedReadBufferSize
  1366. );
  1367. if (rc == FALSE) {
  1368. HvRecoverDataDebug.FailPoint = 2;
  1369. UNLOCK_STASH_BUFFER();
  1370. if( CmDoSelfHeal() ) {
  1371. //
  1372. // .LOG is bad too. attempt to load at the extent of some data loss.
  1373. //
  1374. CmMarkSelfHeal(Hive);
  1375. return SelfHeal;
  1376. } else {
  1377. return Fail;
  1378. }
  1379. }
  1380. //
  1381. // check the signature
  1382. //
  1383. DirtyVectorSignature = *((ULONG *)CmpStashBuffer);
  1384. if (DirtyVectorSignature != HLOG_DV_SIGNATURE) {
  1385. UNLOCK_STASH_BUFFER();
  1386. HvRecoverDataDebug.FailPoint = 3;
  1387. if( CmDoSelfHeal() ) {
  1388. //
  1389. // .LOG is bad too. attempt to load at the extent of some data loss.
  1390. //
  1391. CmMarkSelfHeal(Hive);
  1392. return SelfHeal;
  1393. } else {
  1394. return Fail;
  1395. }
  1396. }
  1397. //
  1398. // get the actual vector
  1399. //
  1400. Vector = (PULONG)((Hive->Allocate)(ROUND_UP(VectorSize,sizeof(ULONG)), TRUE,CM_FIND_LEAK_TAG36));
  1401. if (Vector == NULL) {
  1402. UNLOCK_STASH_BUFFER();
  1403. HvRecoverDataDebug.FailPoint = 4;
  1404. return NoMemory;
  1405. }
  1406. RtlCopyMemory(Vector,CmpStashBuffer + sizeof(DirtyVectorSignature),VectorSize);
  1407. UNLOCK_STASH_BUFFER();
  1408. FileOffset = ROUND_UP(FileOffset, ClusterSize);
  1409. //
  1410. // step through the diry map, reading in the corresponding file bytes
  1411. //
  1412. Current = 0;
  1413. VectorSize = VectorSize * 8; // VectorSize == bits
  1414. RtlInitializeBitMap(&BitMap, Vector, VectorSize);
  1415. if( RtlNumberOfSetBits(&BitMap) == VectorSize ) {
  1416. //
  1417. // the entire hive is marked as dirty; easier to start from scratch
  1418. //
  1419. if( !NT_SUCCESS(HvpRecoverWholeHive(Hive,FileOffset)) ) {
  1420. goto ErrorExit;
  1421. }
  1422. goto Done;
  1423. }
  1424. while (Current < VectorSize) {
  1425. //
  1426. // find next contiguous block of entries to read in
  1427. //
  1428. for (i = Current; i < VectorSize; i++) {
  1429. if (RtlCheckBit(&BitMap, i) == 1) {
  1430. break;
  1431. }
  1432. }
  1433. Start = i;
  1434. for ( ; i < VectorSize; i++) {
  1435. if (RtlCheckBit(&BitMap, i) == 0) {
  1436. break;
  1437. }
  1438. }
  1439. End = i;
  1440. Current = End;
  1441. //
  1442. // Start == number of 1st sector, End == number of Last sector + 1
  1443. //
  1444. Length = (End - Start) * HSECTOR_SIZE;
  1445. if( 0 == Length ) {
  1446. // no more dirty blocks.
  1447. break;
  1448. }
  1449. //
  1450. // allocate a buffer to read the whole run from the file; This is a temporary
  1451. // block that'll be freed immediately, so don't charge quota for it.
  1452. //
  1453. MemoryBlock = (PUCHAR)ExAllocatePoolWithTag(PagedPool, Length, CM_POOL_TAG);
  1454. if( MemoryBlock == NULL ) {
  1455. HvRecoverDataDebug.FailPoint = 5;
  1456. goto ErrorExit;
  1457. }
  1458. rc = (Hive->FileRead)(
  1459. Hive,
  1460. HFILE_TYPE_LOG,
  1461. &FileOffset,
  1462. (PVOID)MemoryBlock,
  1463. Length
  1464. );
  1465. ASSERT((FileOffset % ClusterSize) == 0);
  1466. if (rc == FALSE) {
  1467. ExFreePool(MemoryBlock);
  1468. HvRecoverDataDebug.FailPoint = 6;
  1469. HvRecoverDataDebug.FileOffset = FileOffset;
  1470. if( CmDoSelfHeal() ) {
  1471. //
  1472. // .LOG is bad too. attempt to load at the extent of some data loss.
  1473. //
  1474. CmMarkSelfHeal(Hive);
  1475. //
  1476. // clear off what we have missed
  1477. //
  1478. RtlClearBits(&BitMap,FileOffset/HSECTOR_SIZE,(Hive->BaseBlock->Length - FileOffset)/HSECTOR_SIZE);
  1479. goto Done;
  1480. } else {
  1481. goto ErrorExit;
  1482. }
  1483. }
  1484. Source = MemoryBlock;
  1485. //
  1486. // copy recovered data in the right locations inside the in-memory bins
  1487. //
  1488. while( Start < End ) {
  1489. Address = Start * HSECTOR_SIZE;
  1490. Me = HvpGetCellMap(Hive, Address);
  1491. VALIDATE_CELL_MAP(__LINE__,Me,Hive,Address);
  1492. if( (Me->BinAddress & (HMAP_INVIEW|HMAP_INPAGEDPOOL)) == 0 ) {
  1493. //
  1494. // bin is not in memory, neither in paged pool ==> map it
  1495. //
  1496. if( !NT_SUCCESS(CmpMapThisBin((PCMHIVE)Hive,Address,FALSE)) ) {
  1497. ExFreePool(MemoryBlock);
  1498. HvRecoverDataDebug.FailPoint = 7;
  1499. HvRecoverDataDebug.FileOffset = Address;
  1500. goto ErrorExit;
  1501. }
  1502. }
  1503. if( Me->BinAddress & HMAP_INVIEW ) {
  1504. //
  1505. // pin the view (if not already pinned), as changes have
  1506. // to be flushed to the disk.
  1507. //
  1508. ASSERT( Me->CmView != NULL );
  1509. if( IsListEmpty(&(Me->CmView->PinViewList)) == TRUE ) {
  1510. //
  1511. // the view is not already pinned. pin it
  1512. //
  1513. ASSERT_VIEW_MAPPED( Me->CmView );
  1514. if( !NT_SUCCESS(CmpPinCmView ((PCMHIVE)Hive,Me->CmView)) ) {
  1515. //
  1516. // could not pin view
  1517. //
  1518. ExFreePool(MemoryBlock);
  1519. HvRecoverDataDebug.FailPoint = 10;
  1520. HvRecoverDataDebug.FileOffset = Address;
  1521. goto ErrorExit;
  1522. }
  1523. } else {
  1524. //
  1525. // view is already pinned; do nothing
  1526. //
  1527. ASSERT_VIEW_PINNED( Me->CmView );
  1528. }
  1529. CmView = Me->CmView;
  1530. } else {
  1531. CmView = NULL;
  1532. }
  1533. Bin = (PHBIN)HBIN_BASE(Me->BinAddress);
  1534. //
  1535. // compute the memory address where data should be copied
  1536. //
  1537. SectorOffsetInBin = Address - Bin->FileOffset;
  1538. if( ( SectorOffsetInBin == 0 ) && ( ((PHBIN)Source)->Size > Bin->Size ) ){
  1539. //
  1540. // Bin in the log file is bigger than the one in memory;
  1541. // two or more bins must have been coalesced
  1542. //
  1543. ASSERT( Me->BinAddress & HMAP_NEWALLOC );
  1544. SourceBin = (PHBIN)Source;
  1545. //
  1546. // new bin must have the right offset
  1547. //
  1548. ASSERT(Address == SourceBin->FileOffset);
  1549. ASSERT( SourceBin->Signature == HBIN_SIGNATURE );
  1550. //
  1551. // entire bin should be dirty
  1552. //
  1553. ASSERT( (SourceBin->FileOffset + SourceBin->Size) <= End * HSECTOR_SIZE );
  1554. if( Me->BinAddress & HMAP_INPAGEDPOOL ) {
  1555. //
  1556. // Allocate the right size for the new bin
  1557. //
  1558. NewBin = (PHBIN)(Hive->Allocate)(SourceBin->Size, TRUE,CM_FIND_LEAK_TAG37);
  1559. if (NewBin == NULL) {
  1560. HvRecoverDataDebug.FailPoint = 8;
  1561. goto ErrorExit;
  1562. }
  1563. } else {
  1564. //
  1565. // bin is mapped in the system cache
  1566. //
  1567. ASSERT( Me->BinAddress & HMAP_INVIEW );
  1568. NewBin = Bin;
  1569. }
  1570. //
  1571. // Copy the old data into the new bin and free old bins
  1572. //
  1573. while(Bin->FileOffset < (Address + SourceBin->Size)) {
  1574. //
  1575. // Delist this bin free cells
  1576. //
  1577. HvpDelistBinFreeCells(Hive,Bin,Stable);
  1578. if( Me->BinAddress & HMAP_INPAGEDPOOL ) {
  1579. RtlCopyMemory((PUCHAR)NewBin + (Bin->FileOffset - Address),Bin, Bin->Size);
  1580. }
  1581. //
  1582. // Advance to the new bin
  1583. //
  1584. if( (Bin->FileOffset + Bin->Size) < Hive->BaseBlock->Length ) {
  1585. Me = HvpGetCellMap(Hive, Bin->FileOffset + Bin->Size);
  1586. VALIDATE_CELL_MAP(__LINE__,Me,Hive,Bin->FileOffset + Bin->Size);
  1587. if( Me->BinAddress & HMAP_INPAGEDPOOL ) {
  1588. //
  1589. // Free the old bin
  1590. //
  1591. (Hive->Free)(Bin, Bin->Size);
  1592. }
  1593. //
  1594. // the new address must be the begining of a new allocation
  1595. //
  1596. ASSERT( Me->BinAddress & HMAP_NEWALLOC );
  1597. Bin = (PHBIN)HBIN_BASE(Me->BinAddress);
  1598. } else {
  1599. //
  1600. // we are at the end of the hive here; just break out of the loop
  1601. //
  1602. ASSERT( (Address + SourceBin->Size) == Hive->BaseBlock->Length );
  1603. ASSERT( (Bin->FileOffset + Bin->Size) == Hive->BaseBlock->Length );
  1604. //
  1605. // Free the old bin
  1606. //
  1607. if( Me->BinAddress & HMAP_INPAGEDPOOL ) {
  1608. (Hive->Free)(Bin, Bin->Size);
  1609. }
  1610. //
  1611. // debug purposes only
  1612. //
  1613. ASSERT( (Bin = NULL) == NULL );
  1614. // bail out of while loop
  1615. break;
  1616. }
  1617. }
  1618. #if DBG
  1619. //
  1620. // validation: bin size increase must come from coalescing of former bins
  1621. // (i.e. bins are never split!!!)
  1622. //
  1623. if( Bin != NULL ) {
  1624. ASSERT( Bin->FileOffset == (Address + SourceBin->Size));
  1625. }
  1626. #endif
  1627. //
  1628. // Now overwrite the modified data !
  1629. //
  1630. while( (Address < (SourceBin->FileOffset + SourceBin->Size)) && (Start < End) ) {
  1631. RtlCopyMemory((PUCHAR)NewBin + (Address - SourceBin->FileOffset),Source, HSECTOR_SIZE);
  1632. //
  1633. // skip to the next sector
  1634. //
  1635. Start++;
  1636. Source += HSECTOR_SIZE;
  1637. Address += HSECTOR_SIZE;
  1638. }
  1639. //
  1640. // first sector of the new bin is always restaured from the log file!
  1641. //
  1642. ASSERT(NewBin->FileOffset == SourceBin->FileOffset);
  1643. ASSERT(NewBin->Size == SourceBin->Size);
  1644. } else {
  1645. //
  1646. // Normal case: sector recovery somewhere in the middle of the bin
  1647. //
  1648. //
  1649. // Offset should fall within bin memory layout
  1650. //
  1651. ASSERT( SectorOffsetInBin < Bin->Size );
  1652. if(Me->BinAddress & HMAP_DISCARDABLE) {
  1653. //
  1654. // bin is free (discarded); That means it is entirely present in the log file.
  1655. //
  1656. ASSERT( SectorOffsetInBin == 0 );
  1657. SectorImage = (PUCHAR)Bin;
  1658. } else {
  1659. BlockOffsetInBin = (ULONG)((PUCHAR)Me->BlockAddress - (PUCHAR)Bin);
  1660. SectorOffsetInBlock = SectorOffsetInBin - BlockOffsetInBin;
  1661. //
  1662. // sanity check; address should be the same relative to either begining of the bin or begining of the block
  1663. //
  1664. ASSERT(((PUCHAR)Me->BlockAddress + SectorOffsetInBlock) == ((PUCHAR)Bin + SectorOffsetInBin));
  1665. SectorImage = (PUCHAR)((PUCHAR)Me->BlockAddress + SectorOffsetInBlock);
  1666. }
  1667. DbgPrint("HvpRecoverData: SectorOffsetInBin = %lx,SectorImage = %p, Bin = %p, Source = %p\n",
  1668. SectorOffsetInBin,SectorImage,Bin,Source);
  1669. if( SectorImage == (PUCHAR)Bin ) {
  1670. //
  1671. // we are at the beggining of a bin. Check the validity of the data in the .LOG
  1672. //
  1673. PHBIN LogBin = (PHBIN)Source;
  1674. if ( (LogBin->Size < HBLOCK_SIZE) ||
  1675. (LogBin->Signature != HBIN_SIGNATURE) ||
  1676. (Bin->FileOffset != LogBin->FileOffset ) ) {
  1677. //
  1678. // Bin in .LOG is not valid. All we can do now is throw it away and hope the self healing process
  1679. // will successfully recover the hive.
  1680. //
  1681. if( CmDoSelfHeal() ) {
  1682. CmMarkSelfHeal(Hive);
  1683. ExFreePool(MemoryBlock);
  1684. // clear off the remaining sirty bits
  1685. RtlClearBits(&BitMap,Bin->FileOffset/HSECTOR_SIZE,
  1686. (Hive->BaseBlock->Length - Bin->FileOffset)/HSECTOR_SIZE);
  1687. goto Done;
  1688. }
  1689. }
  1690. }
  1691. //
  1692. // Delist this bin free cells
  1693. //
  1694. HvpDelistBinFreeCells(Hive,Bin,Stable);
  1695. //
  1696. // both source and destination should be valid at this point
  1697. //
  1698. ASSERT( SectorImage < ((PUCHAR)Bin + Bin->Size) );
  1699. ASSERT( Source < (MemoryBlock + Length) );
  1700. NumberOfSectors = 0;
  1701. while( ( (SectorImage + (NumberOfSectors * HSECTOR_SIZE)) < (PUCHAR)((PUCHAR)Bin + Bin->Size) ) &&
  1702. ( (Start + NumberOfSectors ) < End ) ) {
  1703. //
  1704. // we are still inside the same bin;
  1705. // deal with all sectors inside the same bin at once
  1706. //
  1707. NumberOfSectors++;
  1708. }
  1709. //
  1710. // finally, copy the memory
  1711. //
  1712. RtlCopyMemory(SectorImage,Source, NumberOfSectors * HSECTOR_SIZE);
  1713. NewBin = Bin;
  1714. //
  1715. // skip to the next sector
  1716. //
  1717. Start += NumberOfSectors;
  1718. Source += NumberOfSectors * HSECTOR_SIZE;
  1719. }
  1720. //
  1721. // rebuild the map anyway
  1722. //
  1723. Status = HvpEnlistBinInMap(Hive, Length, NewBin, NewBin->FileOffset, CmView);
  1724. if( !NT_SUCCESS(Status) ) {
  1725. HvRecoverDataDebug.FailPoint = 9;
  1726. HvRecoverDataDebug.FileOffset = NewBin->FileOffset;
  1727. if( CmDoSelfHeal() && (Status == STATUS_REGISTRY_RECOVERED) ) {
  1728. //
  1729. // .LOG is bad too, but enlisting fixed the bin
  1730. //
  1731. CmMarkSelfHeal(Hive);
  1732. } else {
  1733. goto ErrorExit;
  1734. }
  1735. goto ErrorExit;
  1736. }
  1737. }
  1738. //
  1739. // get rid of the temporary pool
  1740. //
  1741. ExFreePool(MemoryBlock);
  1742. }
  1743. Done:
  1744. //
  1745. // put correct dirty vector in Hive so that recovered data
  1746. // can be correctly flushed
  1747. //
  1748. if (Hive->DirtyVector.Buffer != NULL) {
  1749. Hive->Free((PVOID)(Hive->DirtyVector.Buffer), Hive->DirtyAlloc);
  1750. }
  1751. RtlInitializeBitMap(&(Hive->DirtyVector), Vector, VectorSize);
  1752. Hive->DirtyCount = RtlNumberOfSetBits(&Hive->DirtyVector);
  1753. Hive->DirtyAlloc = ROUND_UP(VectorSize/8,sizeof(ULONG));
  1754. HvMarkDirty(Hive, 0, sizeof(HBIN),TRUE); // force header of 1st bin dirty
  1755. return HiveSuccess;
  1756. ErrorExit:
  1757. //
  1758. // free the dirty vector and return failure
  1759. //
  1760. (Hive->Free)(Vector, ROUND_UP(VectorSize/8,sizeof(ULONG)));
  1761. return Fail;
  1762. }
  1763. NTSTATUS
  1764. HvpRecoverWholeHive(PHHIVE Hive,
  1765. ULONG FileOffset
  1766. )
  1767. /*++
  1768. Routine Description:
  1769. We have the whole hive inside the log. Redo the mapping and copy from the log
  1770. to the actual storage.
  1771. Arguments:
  1772. Hive - supplies a pointer to the hive control structure for the
  1773. hive of interest
  1774. FileOffset - where the actual hive data starts in the log file.
  1775. Return Value:
  1776. NTSTATUS
  1777. --*/
  1778. {
  1779. NTSTATUS Status = STATUS_SUCCESS;
  1780. ULONG BinOffset = 0;
  1781. PCM_VIEW_OF_FILE CmView = NULL;
  1782. BOOLEAN rc;
  1783. PHMAP_ENTRY Me;
  1784. PHBIN Bin; // current bin
  1785. PHBIN LogBin;
  1786. ULONG LogBinSize; // size of the current bin
  1787. ULONG Length;
  1788. LOGICAL MappedHive;
  1789. PFREE_HBIN FreeBin;
  1790. ULONG i;
  1791. PCM_VIEW_OF_FILE EnlistCmView;
  1792. //
  1793. // free the bins that may have been allocated from paged pool.
  1794. //
  1795. HvpFreeAllocatedBins( Hive );
  1796. CmpDestroyHiveViewList((PCMHIVE)Hive);
  1797. //
  1798. // free all free bins.
  1799. //
  1800. while( !IsListEmpty(&(Hive->Storage[Stable].FreeBins)) ) {
  1801. FreeBin = (PFREE_HBIN)RemoveHeadList(&(Hive->Storage[Stable].FreeBins));
  1802. FreeBin = CONTAINING_RECORD(FreeBin,
  1803. FREE_HBIN,
  1804. ListEntry);
  1805. (Hive->Free)(FreeBin, sizeof(FREE_HBIN));
  1806. }
  1807. //
  1808. // invalidate all free cell hints;
  1809. //
  1810. #ifdef HV_TRACK_FREE_SPACE
  1811. Hive->Storage[Stable].FreeStorage = 0;
  1812. #endif
  1813. Hive->Storage[Stable].FreeSummary = 0;
  1814. for (i = 0; i < HHIVE_FREE_DISPLAY_SIZE; i++) {
  1815. RtlClearAllBits(&(Hive->Storage[Stable].FreeDisplay[i].Display));
  1816. }
  1817. //
  1818. // we'll use CmpStashBuffer to read from the log.
  1819. //
  1820. MappedHive = ( ((PCMHIVE)Hive)->FileObject != NULL );
  1821. Length = Hive->BaseBlock->Length;
  1822. BinOffset = 0;
  1823. while( BinOffset < Length) {
  1824. Me = HvpGetCellMap(Hive, BinOffset);
  1825. if( MappedHive && !(Me->BinAddress & HMAP_INVIEW) ) {
  1826. //
  1827. // first, pin the old view (if any)
  1828. //
  1829. if( CmView ) {
  1830. //
  1831. // pin the view (is already marked dirty)
  1832. //
  1833. if( IsListEmpty(&(CmView->PinViewList)) == TRUE ) {
  1834. //
  1835. // the view is not already pinned. pin it
  1836. //
  1837. ASSERT_VIEW_MAPPED( CmView );
  1838. Status = CmpPinCmView ((PCMHIVE)Hive,CmView);
  1839. if( !NT_SUCCESS(Status)) {
  1840. //
  1841. // could not pin view
  1842. //
  1843. HvRecoverDataDebug.FailPoint = 13;
  1844. HvRecoverDataDebug.FileOffset = FileOffset;
  1845. return Status;
  1846. }
  1847. } else {
  1848. //
  1849. // view is already pinned; do nothing
  1850. //
  1851. ASSERT_VIEW_PINNED( CmView );
  1852. }
  1853. }
  1854. Status = CmpMapCmView((PCMHIVE)Hive,BinOffset,&CmView,FALSE/*map not initialized yet*/);
  1855. if( !NT_SUCCESS(Status) ) {
  1856. HvRecoverDataDebug.FailPoint = 10;
  1857. HvRecoverDataDebug.FileOffset = FileOffset;
  1858. return Status;
  1859. }
  1860. }
  1861. rc = (Hive->FileRead)(
  1862. Hive,
  1863. HFILE_TYPE_LOG,
  1864. &FileOffset,
  1865. (PVOID)CmpStashBuffer,
  1866. HBLOCK_SIZE
  1867. );
  1868. if (rc == FALSE) {
  1869. HvRecoverDataDebug.FailPoint = 11;
  1870. HvRecoverDataDebug.FileOffset = FileOffset;
  1871. return STATUS_REGISTRY_IO_FAILED;
  1872. }
  1873. LogBin = (PHBIN)CmpStashBuffer;
  1874. LogBinSize = LogBin->Size;
  1875. if( (LogBin->Signature != HBIN_SIGNATURE) ||
  1876. (LogBin->FileOffset != BinOffset) ) {
  1877. HvRecoverDataDebug.FailPoint = 17;
  1878. HvRecoverDataDebug.FileOffset = FileOffset;
  1879. return STATUS_REGISTRY_IO_FAILED;
  1880. }
  1881. //
  1882. // Bins crossing the CM_VIEW_SIZE boundary problem.
  1883. // We fix it here, by loading the entire bin
  1884. // into paged pool
  1885. //
  1886. FileOffset -= HBLOCK_SIZE;
  1887. if( (!MappedHive) || (HvpCheckViewBoundary(BinOffset,BinOffset+LogBinSize-1) == FALSE) ) {
  1888. //
  1889. // load it in the old fashioned way (into paged pool)
  1890. //
  1891. // first, allocate the bin
  1892. Bin = (PHBIN)(Hive->Allocate)(LogBinSize, TRUE,CM_FIND_LEAK_TAG35);
  1893. if (Bin == NULL) {
  1894. HvRecoverDataDebug.FailPoint = 12;
  1895. HvRecoverDataDebug.FileOffset = FileOffset;
  1896. return STATUS_INSUFFICIENT_RESOURCES;
  1897. }
  1898. //
  1899. // this will enlist the bin as in paged pool
  1900. //
  1901. EnlistCmView = NULL;
  1902. } else {
  1903. ASSERT(Me->BinAddress & HMAP_INVIEW);
  1904. ASSERT(Me->CmView == CmView );
  1905. Bin = (PHBIN)Me->BlockAddress;
  1906. EnlistCmView = CmView;
  1907. }
  1908. //
  1909. // read data from the file
  1910. //
  1911. if ( ! (Hive->FileRead)(
  1912. Hive,
  1913. HFILE_TYPE_LOG,
  1914. &FileOffset,
  1915. (PVOID)Bin,
  1916. LogBinSize
  1917. )
  1918. )
  1919. {
  1920. HvRecoverDataDebug.FailPoint = 14;
  1921. HvRecoverDataDebug.FileOffset = FileOffset;
  1922. return STATUS_REGISTRY_IO_FAILED;
  1923. }
  1924. //
  1925. // enlist the bin;
  1926. //
  1927. Status = HvpEnlistBinInMap(Hive, Length, Bin, BinOffset, CmView);
  1928. if( !NT_SUCCESS(Status) ) {
  1929. HvRecoverDataDebug.FailPoint = 15;
  1930. HvRecoverDataDebug.FileOffset = FileOffset;
  1931. return Status;
  1932. }
  1933. //
  1934. // advance to the new bin
  1935. //
  1936. BinOffset += LogBinSize;
  1937. }
  1938. if( CmView ) {
  1939. //
  1940. // pin the view (is already marked dirty)
  1941. //
  1942. if( IsListEmpty(&(CmView->PinViewList)) == TRUE ) {
  1943. //
  1944. // the view is not already pinned. pin it
  1945. //
  1946. ASSERT_VIEW_MAPPED( CmView );
  1947. Status = CmpPinCmView ((PCMHIVE)Hive,CmView);
  1948. if( !NT_SUCCESS(Status)) {
  1949. //
  1950. // could not pin view
  1951. //
  1952. HvRecoverDataDebug.FailPoint = 16;
  1953. HvRecoverDataDebug.FileOffset = FileOffset;
  1954. return Status;
  1955. }
  1956. } else {
  1957. //
  1958. // view is already pinned; do nothing
  1959. //
  1960. ASSERT_VIEW_PINNED( CmView );
  1961. }
  1962. }
  1963. return STATUS_SUCCESS;
  1964. }
  1965. NTSTATUS
  1966. HvCloneHive(PHHIVE SourceHive,
  1967. PHHIVE DestHive,
  1968. PULONG NewLength
  1969. )
  1970. /*++
  1971. Routine Description:
  1972. Duplicates the bins from the source hive to the destination hive.
  1973. Allocates the map, and recomputes the PhysicalOffset for each bin.
  1974. It does not touch the freedisplay.
  1975. Arguments:
  1976. SourceHive -
  1977. DestHive -
  1978. Return Value:
  1979. TBS
  1980. --*/
  1981. {
  1982. ULONG Length;
  1983. NTSTATUS Status;
  1984. ULONG MapSlots;
  1985. ULONG Tables;
  1986. PHMAP_TABLE t = NULL;
  1987. PHMAP_DIRECTORY d = NULL;
  1988. ULONG FileOffset;
  1989. ULONG ShiftOffset;
  1990. PHMAP_ENTRY Me;
  1991. PFREE_HBIN FreeBin;
  1992. ULONG BinSize;
  1993. PHBIN Bin,NewBin;
  1994. Length = DestHive->BaseBlock->Length = SourceHive->BaseBlock->Length;
  1995. //
  1996. // Compute size of data region to be mapped
  1997. //
  1998. if ((Length % HBLOCK_SIZE) != 0 ) {
  1999. Status = STATUS_REGISTRY_CORRUPT;
  2000. goto ErrorExit1;
  2001. }
  2002. MapSlots = Length / HBLOCK_SIZE;
  2003. if( MapSlots > 0 ) {
  2004. Tables = (MapSlots-1) / HTABLE_SLOTS;
  2005. } else {
  2006. Tables = 0;
  2007. }
  2008. DestHive->Storage[Stable].Length = Length;
  2009. //
  2010. // allocate and build structure for map
  2011. //
  2012. if (Tables == 0) {
  2013. //
  2014. // Just 1 table, no need for directory
  2015. //
  2016. t = (DestHive->Allocate)(sizeof(HMAP_TABLE), FALSE,CM_FIND_LEAK_TAG23);
  2017. if (t == NULL) {
  2018. Status = STATUS_INSUFFICIENT_RESOURCES;
  2019. goto ErrorExit1;
  2020. }
  2021. RtlZeroMemory(t, sizeof(HMAP_TABLE));
  2022. DestHive->Storage[Stable].Map =
  2023. (PHMAP_DIRECTORY)&(DestHive->Storage[Stable].SmallDir);
  2024. DestHive->Storage[Stable].SmallDir = t;
  2025. } else {
  2026. //
  2027. // Need directory and multiple tables
  2028. //
  2029. d = (PHMAP_DIRECTORY)(DestHive->Allocate)(sizeof(HMAP_DIRECTORY), FALSE,CM_FIND_LEAK_TAG24);
  2030. if (d == NULL) {
  2031. Status = STATUS_INSUFFICIENT_RESOURCES;
  2032. goto ErrorExit1;
  2033. }
  2034. RtlZeroMemory(d, sizeof(HMAP_DIRECTORY));
  2035. //
  2036. // Allocate tables and fill in dir
  2037. //
  2038. if (HvpAllocateMap(DestHive, d, 0, Tables) == FALSE) {
  2039. Status = STATUS_INSUFFICIENT_RESOURCES;
  2040. goto ErrorExit2;
  2041. }
  2042. DestHive->Storage[Stable].Map = d;
  2043. DestHive->Storage[Stable].SmallDir = 0;
  2044. }
  2045. //
  2046. // Now we have to allocate the memory for the HBINs and fill in
  2047. // the map appropriately. We'll keep track of the freebins
  2048. // and update the Spare field in each bin accordingly.
  2049. //
  2050. // temporary mark the hive as read only, so we won't enlist the free cells
  2051. DestHive->ReadOnly = TRUE;
  2052. FileOffset = ShiftOffset = 0;
  2053. while(FileOffset < Length) {
  2054. Me = HvpGetCellMap(SourceHive, FileOffset);
  2055. if( (Me->BinAddress & (HMAP_INPAGEDPOOL|HMAP_INVIEW)) == 0) {
  2056. //
  2057. // view is not mapped, neither in paged pool
  2058. // try to map it.
  2059. //
  2060. // do not touch the view as we have no interest in it afterwards
  2061. //
  2062. if( !NT_SUCCESS(CmpMapThisBin((PCMHIVE)SourceHive,FileOffset,FALSE)) ) {
  2063. Status = STATUS_INSUFFICIENT_RESOURCES;
  2064. goto ErrorExit2;
  2065. }
  2066. }
  2067. if( Me->BinAddress & HMAP_DISCARDABLE ) {
  2068. //
  2069. // bin is discardable. If it is not discarded yet, save it as it is
  2070. // else, allocate, initialize and save a fake bin
  2071. //
  2072. FreeBin = (PFREE_HBIN)Me->BlockAddress;
  2073. BinSize = FreeBin->Size;
  2074. //
  2075. // all we need to do here is to keep track of shifting offset
  2076. //
  2077. ShiftOffset += BinSize;
  2078. //
  2079. // we leave "holes" (map filled with 0); we'll detect them later and shrink the map.
  2080. //
  2081. } else {
  2082. #ifdef CM_MAP_NO_READ
  2083. //
  2084. // we need to make sure all the cell's data is faulted in inside a
  2085. // try/except block, as the IO to fault the data in can throw exceptions
  2086. // STATUS_INSUFFICIENT_RESOURCES, in particular
  2087. //
  2088. try {
  2089. #endif //CM_MAP_NO_READ
  2090. Bin = (PHBIN)HBIN_BASE(Me->BinAddress);
  2091. ASSERT( Bin->Signature == HBIN_SIGNATURE );
  2092. ASSERT( Bin->FileOffset == FileOffset );
  2093. BinSize = Bin->Size;
  2094. //
  2095. // Allocate the new bin
  2096. //
  2097. NewBin = (PHBIN)(DestHive->Allocate)(BinSize, TRUE,CM_FIND_LEAK_TAG35);
  2098. if (NewBin == NULL) {
  2099. Status = STATUS_INSUFFICIENT_RESOURCES;
  2100. goto ErrorExit2;
  2101. }
  2102. //
  2103. // copy data over the new bin and update the Spare field
  2104. //
  2105. RtlCopyMemory(NewBin,Bin,BinSize);
  2106. NewBin->Spare = ShiftOffset;
  2107. Status = HvpEnlistBinInMap(DestHive, Length, NewBin, FileOffset, NULL);
  2108. if( !NT_SUCCESS(Status) ) {
  2109. goto ErrorExit2;
  2110. }
  2111. #ifdef CM_MAP_NO_READ
  2112. } except (EXCEPTION_EXECUTE_HANDLER) {
  2113. Status = GetExceptionCode();
  2114. goto ErrorExit2;
  2115. }
  2116. #endif //CM_MAP_NO_READ
  2117. }
  2118. FileOffset += BinSize;
  2119. }
  2120. DestHive->ReadOnly = FALSE;
  2121. *NewLength = Length - ShiftOffset;
  2122. return STATUS_SUCCESS;
  2123. ErrorExit2:
  2124. if (d != NULL) {
  2125. //
  2126. // directory was built and allocated, so clean it up
  2127. //
  2128. HvpFreeMap(DestHive, d, 0, Tables);
  2129. (DestHive->Free)(d, sizeof(HMAP_DIRECTORY));
  2130. }
  2131. ErrorExit1:
  2132. return Status;
  2133. }
  2134. NTSTATUS
  2135. HvShrinkHive(PHHIVE Hive,
  2136. ULONG NewLength
  2137. )
  2138. /*++
  2139. Routine Description:
  2140. Initialize free display and move free bins at the end.
  2141. Renlist all bins. Update/shrink the map and the length of the hive.
  2142. Arguments:
  2143. Hive -
  2144. NewLength -
  2145. Return Value:
  2146. TBS
  2147. --*/
  2148. {
  2149. NTSTATUS Status;
  2150. ULONG Offset;
  2151. ULONG Length;
  2152. PHMAP_ENTRY Me;
  2153. PHBIN Bin;
  2154. ULONG OldTable;
  2155. ULONG NewTable;
  2156. PAGED_CODE();
  2157. Status = HvpAdjustHiveFreeDisplay(Hive,NewLength,Stable);
  2158. if( !NT_SUCCESS(Status) ) {
  2159. goto ErrorExit;
  2160. }
  2161. //
  2162. // iterate through the map and move bins toward the beggining.
  2163. //
  2164. Offset = 0;
  2165. Length = Hive->BaseBlock->Length;
  2166. while( Offset < Length ) {
  2167. Me = HvpGetCellMap(Hive, Offset);
  2168. if( Me->BinAddress & HMAP_INPAGEDPOOL ) {
  2169. //
  2170. // we only care about bins in paged pool
  2171. //
  2172. Bin = (PHBIN)HBIN_BASE(Me->BinAddress);
  2173. ASSERT( Bin->Signature == HBIN_SIGNATURE );
  2174. ASSERT( Bin->FileOffset == Offset );
  2175. //
  2176. // shift the bin and enlist it again.
  2177. //
  2178. Bin->FileOffset -= Bin->Spare;
  2179. Status = HvpEnlistBinInMap(Hive, Length, Bin, Bin->FileOffset, NULL);
  2180. if( !NT_SUCCESS(Status) ) {
  2181. goto ErrorExit;
  2182. }
  2183. Offset += Bin->Size;
  2184. } else {
  2185. //
  2186. // advance carefully.
  2187. //
  2188. Offset += HBLOCK_SIZE;
  2189. }
  2190. }
  2191. //
  2192. // now shrink the map and update the length
  2193. //
  2194. OldTable = ( (Length-1) / HBLOCK_SIZE ) / HTABLE_SLOTS;
  2195. NewTable = ( (NewLength-1) / HBLOCK_SIZE ) / HTABLE_SLOTS;
  2196. ASSERT( OldTable >= NewTable );
  2197. HvpFreeMap(Hive, Hive->Storage[Stable].Map, NewTable+1, OldTable);
  2198. Hive->Storage[Stable].Length = NewLength;
  2199. Hive->BaseBlock->Length = NewLength;
  2200. ErrorExit:
  2201. return Status;
  2202. }