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

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