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.

1321 lines
39 KiB

  1. //
  2. // REGDBLK.C
  3. //
  4. // Copyright (C) Microsoft Corporation, 1995
  5. //
  6. #include "pch.h"
  7. DECLARE_DEBUG_COUNT(g_RgDatablockLockCount);
  8. // Don't let a FREE_RECORD shrink less than this value.
  9. #define MINIMUM_FREE_RECORD_LENGTH (sizeof(KEY_RECORD) + sizeof(VALUE_RECORD))
  10. //
  11. // RgAllocDatablockInfoBuffers
  12. //
  13. // Allocates the buffers associated with a DATABLOCK_INFO structure. The
  14. // size of the datablock buffer is determined by the BlockSize member.
  15. //
  16. int
  17. INTERNAL
  18. RgAllocDatablockInfoBuffers(
  19. LPDATABLOCK_INFO lpDatablockInfo
  20. )
  21. {
  22. lpDatablockInfo-> lpDatablockHeader = (LPDATABLOCK_HEADER)
  23. RgAllocMemory(lpDatablockInfo-> BlockSize);
  24. if (!IsNullPtr(lpDatablockInfo-> lpDatablockHeader)) {
  25. lpDatablockInfo-> lpKeyRecordTable = (LPKEY_RECORD_TABLE_ENTRY)
  26. RgSmAllocMemory(sizeof(KEY_RECORD_TABLE_ENTRY) *
  27. KEY_RECORDS_PER_DATABLOCK);
  28. if (!IsNullPtr(lpDatablockInfo-> lpKeyRecordTable))
  29. return ERROR_SUCCESS;
  30. RgFreeDatablockInfoBuffers(lpDatablockInfo);
  31. }
  32. return ERROR_OUTOFMEMORY;
  33. }
  34. //
  35. // RgFreeDatablockInfoBuffers
  36. //
  37. // Frees the buffers associated with a DATABLOCK_INFO structure.
  38. //
  39. VOID
  40. INTERNAL
  41. RgFreeDatablockInfoBuffers(
  42. LPDATABLOCK_INFO lpDatablockInfo
  43. )
  44. {
  45. if (!IsNullPtr(lpDatablockInfo-> lpDatablockHeader)) {
  46. RgFreeMemory(lpDatablockInfo-> lpDatablockHeader);
  47. lpDatablockInfo-> lpDatablockHeader = NULL;
  48. }
  49. if (!IsNullPtr(lpDatablockInfo-> lpKeyRecordTable)) {
  50. RgSmFreeMemory(lpDatablockInfo-> lpKeyRecordTable);
  51. lpDatablockInfo-> lpKeyRecordTable = NULL;
  52. }
  53. }
  54. //
  55. // RgBuildKeyRecordTable
  56. //
  57. // Builds a KEY_RECORD index table for the given datablock.
  58. //
  59. // A datablock consists of a header followed by a series of variable-sized
  60. // KEY_RECORDs, each with a unique id. To make lookups fast, an index table is
  61. // used to map from the unique id to that KEY_RECORD's location.
  62. //
  63. // As we walk over each KEY_RECORD, we do checks to validate the structure of
  64. // the datablock, so the error code should be checked for corruption.
  65. //
  66. int
  67. INTERNAL
  68. RgBuildKeyRecordTable(
  69. LPDATABLOCK_INFO lpDatablockInfo
  70. )
  71. {
  72. LPDATABLOCK_HEADER lpDatablockHeader;
  73. UINT Offset;
  74. UINT BytesRemaining;
  75. LPKEY_RECORD lpKeyRecord;
  76. DWORD DatablockAddress;
  77. ZeroMemory(lpDatablockInfo-> lpKeyRecordTable,
  78. sizeof(KEY_RECORD_TABLE_ENTRY) * KEY_RECORDS_PER_DATABLOCK);
  79. lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
  80. Offset = sizeof(DATABLOCK_HEADER);
  81. BytesRemaining = lpDatablockInfo-> BlockSize - sizeof(DATABLOCK_HEADER);
  82. while (BytesRemaining) {
  83. lpKeyRecord = (LPKEY_RECORD) ((LPBYTE) lpDatablockHeader + Offset);
  84. DatablockAddress = lpKeyRecord-> DatablockAddress;
  85. if ((lpKeyRecord-> AllocatedSize == 0) || (lpKeyRecord-> AllocatedSize >
  86. BytesRemaining) || ((DatablockAddress != REG_NULL) &&
  87. (LOWORD(DatablockAddress) >= KEY_RECORDS_PER_DATABLOCK))) {
  88. TRACE(("RgBuildKeyRecordTable: invalid key record detected\n"));
  89. TRACE(("lpdh=%x\n", lpDatablockHeader));
  90. TRACE(("lpkr=%x\n", lpKeyRecord));
  91. TRACE(("as=%x\n", lpKeyRecord-> AllocatedSize));
  92. TRACE(("br=%x\n", BytesRemaining));
  93. TRACE(("dba=%x\n", DatablockAddress));
  94. TRAP();
  95. // Old code tries to reclaim some of the data.
  96. return ERROR_BADDB;
  97. }
  98. if (DatablockAddress != REG_NULL) {
  99. lpDatablockInfo-> lpKeyRecordTable[LOWORD(DatablockAddress)] =
  100. (KEY_RECORD_TABLE_ENTRY) Offset;
  101. }
  102. Offset += SmallDword(lpKeyRecord-> AllocatedSize);
  103. BytesRemaining -= SmallDword(lpKeyRecord-> AllocatedSize);
  104. }
  105. return ERROR_SUCCESS;
  106. }
  107. //
  108. // RgLockDatablock
  109. //
  110. // Locks the specified datablock in memory, indicating that it is about to be
  111. // used. If the datablock is not currently in memory, then it is brought in.
  112. // Unlocked datablocks are freed as necessary to make room for this new
  113. // datablock.
  114. //
  115. // IMPORTANT: Locking a datablock only means that it's guaranteed to be kept
  116. // in memory. It does not mean that pointers contained in a DATABLOCK_INFO
  117. // structure will remain the same: routines that could change the
  118. // DATABLOCK_INFO pointers are labeled "IMPORTANT" as well.
  119. //
  120. // lpFileInfo, registry file containing the datablock.
  121. // BlockIndex, index of the datablock.
  122. //
  123. int
  124. INTERNAL
  125. RgLockDatablock(
  126. LPFILE_INFO lpFileInfo,
  127. UINT BlockIndex
  128. )
  129. {
  130. int ErrorCode;
  131. LPDATABLOCK_INFO lpDatablockInfo;
  132. HFILE hFile = HFILE_ERROR;
  133. if (BlockIndex >= lpFileInfo-> FileHeader.BlockCount) {
  134. TRACE(("RgLockDatablock: invalid datablock number\n"));
  135. return ERROR_BADDB;
  136. }
  137. lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
  138. //
  139. // Is the datablock currently in memory?
  140. //
  141. if (!(lpDatablockInfo-> Flags & DIF_PRESENT)) {
  142. NOISE(("RgLockDatablock: "));
  143. NOISE((lpFileInfo-> FileName));
  144. NOISE((", block %d\n", BlockIndex));
  145. ASSERT(lpDatablockInfo-> FileOffset != -1);
  146. if ((ErrorCode = RgAllocDatablockInfoBuffers(lpDatablockInfo)) !=
  147. ERROR_SUCCESS)
  148. goto CleanupAfterError;
  149. NOISE((" lpDatablockHeader=%lx\n", lpDatablockInfo-> lpDatablockHeader));
  150. NOISE((" lpKeyRecordTable=%lx\n", lpDatablockInfo-> lpKeyRecordTable));
  151. if ((hFile = RgOpenFile(lpFileInfo-> FileName, OF_READ)) == HFILE_ERROR)
  152. goto CleanupAfterFileError;
  153. if (!RgSeekFile(hFile, lpDatablockInfo-> FileOffset))
  154. goto CleanupAfterFileError;
  155. if (!RgReadFile(hFile, lpDatablockInfo-> lpDatablockHeader,
  156. (UINT) lpDatablockInfo-> BlockSize))
  157. goto CleanupAfterFileError;
  158. if (!RgIsValidDatablockHeader(lpDatablockInfo-> lpDatablockHeader)) {
  159. ErrorCode = ERROR_BADDB;
  160. goto CleanupAfterError;
  161. }
  162. if ((ErrorCode = RgBuildKeyRecordTable(lpDatablockInfo)) !=
  163. ERROR_SUCCESS)
  164. goto CleanupAfterError;
  165. RgCloseFile(hFile);
  166. }
  167. lpDatablockInfo-> Flags |= (DIF_ACCESSED | DIF_PRESENT);
  168. lpDatablockInfo-> LockCount++;
  169. INCREMENT_DEBUG_COUNT(g_RgDatablockLockCount);
  170. return ERROR_SUCCESS;
  171. CleanupAfterFileError:
  172. ErrorCode = ERROR_REGISTRY_IO_FAILED;
  173. CleanupAfterError:
  174. if (hFile != HFILE_ERROR)
  175. RgCloseFile(hFile);
  176. RgFreeDatablockInfoBuffers(lpDatablockInfo);
  177. DEBUG_OUT(("RgLockDatablock() returning error %d\n", ErrorCode));
  178. return ErrorCode;
  179. }
  180. //
  181. // RgUnlockDatablock
  182. //
  183. // Unlocks the datablock, indicating that the datablock is no longer in active
  184. // use. After a datablock has been unlocked, the datablock may be freed after
  185. // flushing to disk if dirty.
  186. //
  187. VOID
  188. INTERNAL
  189. RgUnlockDatablock(
  190. LPFILE_INFO lpFileInfo,
  191. UINT BlockIndex,
  192. BOOL fMarkDirty
  193. )
  194. {
  195. LPDATABLOCK_INFO lpDatablockInfo;
  196. ASSERT(BlockIndex < lpFileInfo-> FileHeader.BlockCount);
  197. lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
  198. ASSERT(lpDatablockInfo-> LockCount > 0);
  199. lpDatablockInfo-> LockCount--;
  200. if (fMarkDirty) {
  201. lpDatablockInfo-> Flags |= DIF_DIRTY;
  202. lpFileInfo-> Flags |= FI_DIRTY;
  203. RgDelayFlush();
  204. }
  205. DECREMENT_DEBUG_COUNT(g_RgDatablockLockCount);
  206. }
  207. //
  208. // RgLockKeyRecord
  209. //
  210. // Wraps RgLockDatablock, returning the address of the specified KEY_RECORD
  211. // structure.
  212. //
  213. int
  214. INTERNAL
  215. RgLockKeyRecord(
  216. LPFILE_INFO lpFileInfo,
  217. UINT BlockIndex,
  218. BYTE KeyRecordIndex,
  219. LPKEY_RECORD FAR* lplpKeyRecord
  220. )
  221. {
  222. int ErrorCode;
  223. LPDATABLOCK_INFO lpDatablockInfo;
  224. if ((ErrorCode = RgLockDatablock(lpFileInfo, BlockIndex)) ==
  225. ERROR_SUCCESS) {
  226. lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
  227. if (IsNullKeyRecordTableEntry(lpDatablockInfo->
  228. lpKeyRecordTable[KeyRecordIndex])) {
  229. RgUnlockDatablock(lpFileInfo, BlockIndex, FALSE);
  230. TRACE(("RgLockKeyRecord: invalid datablock address %x:%x\n",
  231. BlockIndex, KeyRecordIndex));
  232. ErrorCode = ERROR_BADDB;
  233. }
  234. else {
  235. *lplpKeyRecord = RgIndexKeyRecordPtr(lpDatablockInfo,
  236. KeyRecordIndex);
  237. }
  238. }
  239. return ErrorCode;
  240. }
  241. //
  242. // RgCompactDatablock
  243. //
  244. // Compacts the datablock by pushing all KEY_RECORDS together and leaving a
  245. // single FREEKEY_RECORD at the end.
  246. //
  247. // The datablock must be marked dirty by the caller, if desired.
  248. //
  249. // Returns TRUE if any action was taken.
  250. //
  251. BOOL
  252. INTERNAL
  253. RgCompactDatablock(
  254. LPDATABLOCK_INFO lpDatablockInfo
  255. )
  256. {
  257. LPDATABLOCK_HEADER lpDatablockHeader;
  258. LPFREEKEY_RECORD lpFreeKeyRecord;
  259. LPBYTE lpSource;
  260. LPBYTE lpDestination;
  261. UINT Offset;
  262. UINT BlockSize;
  263. UINT BytesToPushDown;
  264. lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
  265. // Only need to compact if there's a free record in this datablock.
  266. if (lpDatablockHeader-> FirstFreeOffset == REG_NULL)
  267. return FALSE;
  268. lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
  269. SmallDword(lpDatablockHeader-> FirstFreeOffset));
  270. // Only need to compact if the all the free bytes aren't already at the end
  271. // of the datablock (datablocks can't be greater than 64K-1, so no overflow
  272. // is possible).
  273. if ((SmallDword(lpDatablockHeader-> FirstFreeOffset) +
  274. SmallDword(lpFreeKeyRecord-> AllocatedSize) >= lpDatablockInfo->
  275. BlockSize) && (lpFreeKeyRecord-> NextFreeOffset == REG_NULL))
  276. return FALSE;
  277. NOISE(("RgCompactDatablock: block %d\n", lpDatablockHeader-> BlockIndex));
  278. lpSource = NULL;
  279. lpDestination = NULL;
  280. Offset = sizeof(DATABLOCK_HEADER);
  281. BlockSize = lpDatablockInfo-> BlockSize;
  282. while (Offset < BlockSize) {
  283. // Advance to the next free record or the end of the block.
  284. for (;;) {
  285. lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
  286. Offset);
  287. if (Offset >= BlockSize || IsKeyRecordFree(lpFreeKeyRecord)) {
  288. //
  289. // If lpSource is valid, then we can push down the bytes from
  290. // lpSource through lpFreeKeyRecord to lpDestination.
  291. //
  292. if (!IsNullPtr(lpSource)) {
  293. BytesToPushDown = (LPBYTE) lpFreeKeyRecord -
  294. (LPBYTE) lpSource;
  295. MoveMemory(lpDestination, lpSource, BytesToPushDown);
  296. lpDestination += BytesToPushDown;
  297. }
  298. if (IsNullPtr(lpDestination))
  299. lpDestination = (LPBYTE) lpFreeKeyRecord;
  300. break;
  301. }
  302. Offset += SmallDword(lpFreeKeyRecord-> AllocatedSize);
  303. }
  304. // Advance to the next key record.
  305. while (Offset < BlockSize) {
  306. lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
  307. Offset);
  308. if (!IsKeyRecordFree(lpFreeKeyRecord)) {
  309. lpSource = (LPBYTE) lpFreeKeyRecord;
  310. break;
  311. }
  312. Offset += SmallDword(lpFreeKeyRecord-> AllocatedSize);
  313. }
  314. }
  315. // lpDestination now points at the end of the datablock where the giant
  316. // free record is to be placed. Initialize this record and patch up the
  317. // datablock header.
  318. lpDatablockHeader-> FirstFreeOffset = (LPBYTE) lpDestination -
  319. (LPBYTE) lpDatablockHeader;
  320. ((LPFREEKEY_RECORD) lpDestination)-> AllocatedSize = lpDatablockInfo->
  321. FreeBytes;
  322. ((LPFREEKEY_RECORD) lpDestination)-> DatablockAddress = REG_NULL;
  323. ((LPFREEKEY_RECORD) lpDestination)-> NextFreeOffset = REG_NULL;
  324. // The key record table is now invalid, so we must refresh its contents.
  325. RgBuildKeyRecordTable(lpDatablockInfo);
  326. return TRUE;
  327. }
  328. //
  329. // RgCreateDatablock
  330. //
  331. // Creates a new datablock at the end of the file of the specified length (plus
  332. // padding to align the block).
  333. //
  334. // The datablock is locked, so RgUnlockDatablock must be called on the last
  335. // datablock in the file.
  336. //
  337. int
  338. INTERNAL
  339. RgCreateDatablock(
  340. LPFILE_INFO lpFileInfo,
  341. UINT Length
  342. )
  343. {
  344. UINT BlockCount;
  345. LPDATABLOCK_INFO lpDatablockInfo;
  346. LPDATABLOCK_HEADER lpDatablockHeader;
  347. LPFREEKEY_RECORD lpFreeKeyRecord;
  348. BlockCount = lpFileInfo-> FileHeader.BlockCount;
  349. if (BlockCount >= DATABLOCKS_PER_FILE)
  350. return ERROR_OUTOFMEMORY;
  351. if (BlockCount >= lpFileInfo-> DatablockInfoAllocCount) {
  352. // lpDatablockInfo is too small to hold the info for a new datablock,
  353. // so we must grow it a bit.
  354. if (IsNullPtr((lpDatablockInfo = (LPDATABLOCK_INFO)
  355. RgSmReAllocMemory(lpFileInfo-> lpDatablockInfo, (BlockCount +
  356. DATABLOCK_INFO_SLACK_ALLOC) * sizeof(DATABLOCK_INFO)))))
  357. return ERROR_OUTOFMEMORY;
  358. lpFileInfo-> lpDatablockInfo = lpDatablockInfo;
  359. lpFileInfo-> DatablockInfoAllocCount += DATABLOCK_INFO_SLACK_ALLOC;
  360. }
  361. lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockCount);
  362. Length = RgAlignBlockSize(Length + sizeof(DATABLOCK_HEADER));
  363. lpDatablockInfo-> BlockSize = Length;
  364. if (RgAllocDatablockInfoBuffers(lpDatablockInfo) != ERROR_SUCCESS)
  365. return ERROR_OUTOFMEMORY;
  366. lpDatablockInfo-> FreeBytes = Length - sizeof(DATABLOCK_HEADER);
  367. lpDatablockInfo-> FirstFreeIndex = 0;
  368. lpDatablockInfo-> FileOffset = -1; // Set during file flush
  369. lpDatablockInfo-> Flags = DIF_PRESENT | DIF_ACCESSED | DIF_DIRTY;
  370. lpDatablockInfo-> LockCount = 1;
  371. lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
  372. lpDatablockHeader-> Signature = DH_SIGNATURE;
  373. lpDatablockHeader-> BlockSize = Length;
  374. lpDatablockHeader-> FreeBytes = lpDatablockInfo-> FreeBytes;
  375. lpDatablockHeader-> Flags = DHF_HASBLOCKNUMBERS;
  376. lpDatablockHeader-> BlockIndex = (WORD) BlockCount;
  377. lpDatablockHeader-> FirstFreeOffset = sizeof(DATABLOCK_HEADER);
  378. lpDatablockHeader-> MaxAllocatedIndex = 0;
  379. // lpDatablockHeader-> FirstFreeIndex is copied back on the flush.
  380. // lpDatablockHeader-> Reserved is worthless because it was randomly set
  381. // to a pointer in the old code.
  382. lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
  383. sizeof(DATABLOCK_HEADER));
  384. lpFreeKeyRecord-> AllocatedSize = lpDatablockInfo-> FreeBytes;
  385. lpFreeKeyRecord-> DatablockAddress = REG_NULL;
  386. lpFreeKeyRecord-> NextFreeOffset = REG_NULL;
  387. lpFileInfo-> FileHeader.BlockCount++;
  388. lpFileInfo-> FileHeader.Flags |= FHF_DIRTY;
  389. // Extending a datablock does not necessarily mean "rewrite the
  390. // whole file again", but it works for now...
  391. lpFileInfo-> Flags |= FI_DIRTY | FI_EXTENDED;
  392. RgDelayFlush();
  393. INCREMENT_DEBUG_COUNT(g_RgDatablockLockCount);
  394. // We must initialize the key record table, so we might as well let
  395. // RgBuildKeyRecordTable check the validity of what we just created...
  396. return RgBuildKeyRecordTable(lpDatablockInfo);
  397. }
  398. //
  399. // RgExtendDatablock
  400. //
  401. // Extends the given datablock to the specified size. If successful, then the
  402. // resulting datablock will be compacted with a single FREEKEY_RECORD at the
  403. // end of the datablock which will include the added space.
  404. //
  405. int
  406. INTERNAL
  407. RgExtendDatablock(
  408. LPFILE_INFO lpFileInfo,
  409. UINT BlockIndex,
  410. UINT Length
  411. )
  412. {
  413. LPDATABLOCK_INFO lpDatablockInfo;
  414. DWORD NewBlockSize;
  415. LPDATABLOCK_HEADER lpNewDatablockHeader;
  416. LPFREEKEY_RECORD lpFreeKeyRecord;
  417. ASSERT(BlockIndex < lpFileInfo-> FileHeader.BlockCount);
  418. lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
  419. ASSERT(lpDatablockInfo-> Flags & DIF_PRESENT);
  420. // Check if enough free bytes already exist: if so, no need to extend.
  421. if (lpDatablockInfo-> FreeBytes >= Length) {
  422. DEBUG_OUT(("RgExtendDatablock: unexpectedly called\n"));
  423. return ERROR_SUCCESS;
  424. }
  425. NewBlockSize = RgAlignBlockSize(lpDatablockInfo-> BlockSize + Length -
  426. lpDatablockInfo-> FreeBytes);
  427. if (NewBlockSize > MAXIMUM_DATABLOCK_SIZE) {
  428. TRACE(("RgExtendDatablock: datablock too big\n"));
  429. return ERROR_OUTOFMEMORY;
  430. }
  431. NOISE(("RgExtendDatablock: block %d\n", BlockIndex));
  432. NOISE(("block size=%x, new block size=%x\n", lpDatablockInfo-> BlockSize,
  433. NewBlockSize));
  434. if (IsNullPtr((lpNewDatablockHeader = (LPDATABLOCK_HEADER)
  435. RgReAllocMemory(lpDatablockInfo-> lpDatablockHeader, (UINT)
  436. NewBlockSize))))
  437. return ERROR_OUTOFMEMORY;
  438. lpDatablockInfo-> lpDatablockHeader = lpNewDatablockHeader;
  439. RgCompactDatablock(lpDatablockInfo);
  440. if (lpNewDatablockHeader-> FirstFreeOffset == REG_NULL) {
  441. lpNewDatablockHeader-> FirstFreeOffset = lpDatablockInfo-> BlockSize;
  442. lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpNewDatablockHeader +
  443. SmallDword(lpNewDatablockHeader-> FirstFreeOffset));
  444. lpFreeKeyRecord-> DatablockAddress = REG_NULL;
  445. lpFreeKeyRecord-> NextFreeOffset = REG_NULL;
  446. }
  447. else {
  448. lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpNewDatablockHeader +
  449. SmallDword(lpNewDatablockHeader-> FirstFreeOffset));
  450. }
  451. lpDatablockInfo-> FreeBytes += (UINT) NewBlockSize - lpDatablockInfo->
  452. BlockSize;
  453. lpFreeKeyRecord-> AllocatedSize = lpDatablockInfo-> FreeBytes;
  454. lpDatablockInfo-> BlockSize = (UINT) NewBlockSize;
  455. lpDatablockInfo-> Flags |= (DIF_DIRTY | DIF_EXTENDED);
  456. // Extending a datablock does not necessarily mean "rewrite the
  457. // whole file again", but it works for now...
  458. lpFileInfo-> Flags |= FI_DIRTY | FI_EXTENDED;
  459. RgDelayFlush();
  460. return ERROR_SUCCESS;
  461. }
  462. //
  463. // RgAllocKeyRecordFromDatablock
  464. //
  465. // Creates an uninitialized KEY_RECORD of the desired size from the provided
  466. // datablock. On exit, only AllocatedSize is valid.
  467. //
  468. // The datablock referred to by lpDatablockInfo must have been locked to
  469. // guarantee that the its data is actually present. The datablock is not
  470. // dirtied.
  471. //
  472. // IMPORTANT: Any datablock may be relocated as a result of calling this
  473. // routine. All pointers to datablocks should be refetched.
  474. //
  475. int
  476. INTERNAL
  477. RgAllocKeyRecordFromDatablock(
  478. LPFILE_INFO lpFileInfo,
  479. UINT BlockIndex,
  480. UINT Length,
  481. LPKEY_RECORD FAR* lplpKeyRecord
  482. )
  483. {
  484. LPDATABLOCK_INFO lpDatablockInfo;
  485. LPDATABLOCK_HEADER lpDatablockHeader;
  486. LPFREEKEY_RECORD lpFreeKeyRecord;
  487. UINT AllocatedSize;
  488. LPFREEKEY_RECORD lpNewFreeKeyRecord;
  489. UINT ExtraBytes;
  490. ASSERT(BlockIndex < lpFileInfo-> FileHeader.BlockCount);
  491. lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
  492. ASSERT(lpDatablockInfo-> Flags & DIF_PRESENT);
  493. if (Length > lpDatablockInfo-> FreeBytes)
  494. return ERROR_OUTOFMEMORY;
  495. RgCompactDatablock(lpDatablockInfo);
  496. lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
  497. lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
  498. SmallDword(lpDatablockHeader-> FirstFreeOffset));
  499. AllocatedSize = SmallDword(lpFreeKeyRecord-> AllocatedSize);
  500. if (Length > AllocatedSize) {
  501. TRACE(("RgAllocKeyRecordFromDatablock() detected corruption?\n"));
  502. return ERROR_OUTOFMEMORY;
  503. }
  504. ExtraBytes = AllocatedSize - Length;
  505. //
  506. // If we were to break this FREEKEY_RECORD into two records, would the
  507. // second chunk be too small? If so, then don't do it. Just give back
  508. // the full allocated size to the caller.
  509. //
  510. if (ExtraBytes >= MINIMUM_FREE_RECORD_LENGTH) {
  511. lpNewFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpFreeKeyRecord +
  512. Length);
  513. lpDatablockHeader-> FirstFreeOffset += Length;
  514. lpFreeKeyRecord-> AllocatedSize = Length;
  515. // IMPORTANT: Note that lpNewFreeKeyRecord and lpFreeKeyRecord may
  516. // overlap so we have to be careful when changing these fields!
  517. lpNewFreeKeyRecord-> NextFreeOffset = lpFreeKeyRecord-> NextFreeOffset;
  518. lpNewFreeKeyRecord-> DatablockAddress = REG_NULL;
  519. lpNewFreeKeyRecord-> AllocatedSize = ExtraBytes;
  520. }
  521. else {
  522. Length = AllocatedSize;
  523. lpDatablockHeader-> FirstFreeOffset = lpFreeKeyRecord-> NextFreeOffset;
  524. }
  525. // Adjust the number of free bytes in this datablock. At this point,
  526. // Length is equal to the size of the newly formed record.
  527. lpDatablockInfo-> FreeBytes -= Length;
  528. *lplpKeyRecord = (LPKEY_RECORD) lpFreeKeyRecord;
  529. return ERROR_SUCCESS;
  530. }
  531. //
  532. // RgAllocKeyRecordIndex
  533. //
  534. // Allocates a key record index from the provided datablock. If no indexs
  535. // are available in the datablock, then KEY_RECORDS_PER_DATABLOCK is returned.
  536. //
  537. // The datablock referred to by lpDatablockInfo must have been locked to
  538. // guarantee that the its data is actually present. The datablock is not
  539. // dirtied.
  540. //
  541. UINT
  542. INTERNAL
  543. RgAllocKeyRecordIndex(
  544. LPDATABLOCK_INFO lpDatablockInfo
  545. )
  546. {
  547. LPDATABLOCK_HEADER lpDatablockHeader;
  548. UINT KeyRecordIndex;
  549. UINT NextFreeIndex;
  550. LPKEY_RECORD_TABLE_ENTRY lpKeyRecordTableEntry;
  551. lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
  552. KeyRecordIndex = lpDatablockInfo-> FirstFreeIndex;
  553. NextFreeIndex = KeyRecordIndex + 1;
  554. ASSERT(KeyRecordIndex < KEY_RECORDS_PER_DATABLOCK);
  555. ASSERT(IsNullKeyRecordTableEntry(lpDatablockInfo->
  556. lpKeyRecordTable[KeyRecordIndex]));
  557. if (KeyRecordIndex > lpDatablockHeader-> MaxAllocatedIndex)
  558. lpDatablockHeader-> MaxAllocatedIndex = (WORD) KeyRecordIndex;
  559. else {
  560. // Find the next free hole in the key record table or leave ourselves
  561. // at the end of the table.
  562. for (lpKeyRecordTableEntry =
  563. &lpDatablockInfo-> lpKeyRecordTable[NextFreeIndex]; NextFreeIndex <=
  564. lpDatablockHeader-> MaxAllocatedIndex; NextFreeIndex++,
  565. lpKeyRecordTableEntry++) {
  566. if (IsNullKeyRecordTableEntry(*lpKeyRecordTableEntry))
  567. break;
  568. }
  569. }
  570. lpDatablockInfo-> FirstFreeIndex = NextFreeIndex;
  571. return KeyRecordIndex;
  572. }
  573. //
  574. // RgAllocKeyRecord
  575. //
  576. //
  577. // IMPORTANT: Any datablock may be relocated as a result of calling this
  578. // routine. All pointers to datablocks should be refetched.
  579. //
  580. int
  581. INTERNAL
  582. RgAllocKeyRecord(
  583. LPFILE_INFO lpFileInfo,
  584. UINT Length,
  585. LPKEY_RECORD FAR* lplpKeyRecord
  586. )
  587. {
  588. BOOL fExtendDatablock;
  589. UINT BlockIndex;
  590. LPDATABLOCK_INFO lpDatablockInfo;
  591. UINT KeyRecordIndex;
  592. if (lpFileInfo-> FileHeader.BlockCount == 0)
  593. goto MakeNewDatablock;
  594. //
  595. // Find a datablock that can satisfy the allocation request. Two passes
  596. // may be made over this routine-- during the second pass, datablocks may
  597. // be extended.
  598. //
  599. fExtendDatablock = FALSE;
  600. DoSecondPass:
  601. BlockIndex = lpFileInfo-> FileHeader.BlockCount;
  602. // We overindex by one, but this gets decremented at the start of the loop.
  603. lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
  604. while (BlockIndex--) {
  605. lpDatablockInfo--;
  606. // Are there any more ids available in this datablock?
  607. if (lpDatablockInfo-> FirstFreeIndex >= KEY_RECORDS_PER_DATABLOCK)
  608. continue;
  609. if (fExtendDatablock) {
  610. // Can we grow this datablock without exceeding the maximum size?
  611. if ((DWORD) (lpDatablockInfo-> BlockSize - lpDatablockInfo->
  612. FreeBytes) + Length > MAXIMUM_DATABLOCK_SIZE)
  613. continue;
  614. }
  615. else {
  616. // Is there enough free space in this datablock for this record?
  617. if (Length > lpDatablockInfo-> FreeBytes)
  618. continue;
  619. }
  620. if (RgLockDatablock(lpFileInfo, BlockIndex) == ERROR_SUCCESS) {
  621. if (!fExtendDatablock || RgExtendDatablock(lpFileInfo, BlockIndex,
  622. Length) == ERROR_SUCCESS) {
  623. if (RgAllocKeyRecordFromDatablock(lpFileInfo, BlockIndex,
  624. Length, lplpKeyRecord) == ERROR_SUCCESS)
  625. goto AllocatedKeyRecord;
  626. }
  627. RgUnlockDatablock(lpFileInfo, BlockIndex, FALSE);
  628. }
  629. }
  630. // If we haven't already tried to extend some datablock, make another
  631. // pass over the blocks to do so.
  632. if (!fExtendDatablock) {
  633. fExtendDatablock = TRUE;
  634. goto DoSecondPass;
  635. }
  636. //
  637. // No datablock has enough space to satisfy the request, so attempt to
  638. // create a new one at the end of the file.
  639. //
  640. MakeNewDatablock:
  641. if (RgCreateDatablock(lpFileInfo, Length) == ERROR_SUCCESS) {
  642. BlockIndex = lpFileInfo-> FileHeader.BlockCount - 1;
  643. lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
  644. if (RgAllocKeyRecordFromDatablock(lpFileInfo, BlockIndex, Length,
  645. lplpKeyRecord) == ERROR_SUCCESS) {
  646. AllocatedKeyRecord:
  647. KeyRecordIndex = RgAllocKeyRecordIndex(lpDatablockInfo);
  648. (*lplpKeyRecord)-> DatablockAddress = MAKELONG(KeyRecordIndex,
  649. BlockIndex);
  650. lpDatablockInfo-> lpKeyRecordTable[KeyRecordIndex] =
  651. (KEY_RECORD_TABLE_ENTRY) ((LPBYTE) (*lplpKeyRecord) -
  652. (LPBYTE) lpDatablockInfo-> lpDatablockHeader);
  653. return ERROR_SUCCESS;
  654. }
  655. RgUnlockDatablock(lpFileInfo, BlockIndex, FALSE);
  656. }
  657. return ERROR_OUTOFMEMORY;
  658. }
  659. //
  660. // RgExtendKeyRecord
  661. //
  662. // Attempts to extend the given KEY_RECORD by combining it with an adjacent
  663. // FREE_RECORD.
  664. //
  665. // The datablock referred to by lpDatablockInfo must have been locked to
  666. // guarantee that the its data is actually present. The datablock is not
  667. // dirtied.
  668. //
  669. // Returns ERROR_SUCCESS if the KEY_RECORD could be extended, else
  670. // ERROR_OUTOFMEMORY.
  671. //
  672. int
  673. INTERNAL
  674. RgExtendKeyRecord(
  675. LPFILE_INFO lpFileInfo,
  676. UINT BlockIndex,
  677. UINT Length,
  678. LPKEY_RECORD lpKeyRecord
  679. )
  680. {
  681. LPDATABLOCK_INFO lpDatablockInfo;
  682. LPDATABLOCK_HEADER lpDatablockHeader;
  683. LPFREEKEY_RECORD lpFreeKeyRecord;
  684. UINT AllocatedSize;
  685. UINT FreeSizeAllocation;
  686. UINT ExtraBytes;
  687. LPFREEKEY_RECORD lpTempFreeKeyRecord;
  688. DWORD NewFreeOffset; // May be REG_NULL
  689. UINT FreeOffset;
  690. DWORD Offset; // May be REG_NULL
  691. ASSERT(BlockIndex < lpFileInfo-> FileHeader.BlockCount);
  692. lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
  693. lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
  694. AllocatedSize = SmallDword(lpKeyRecord-> AllocatedSize);
  695. lpFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpKeyRecord +
  696. AllocatedSize);
  697. FreeOffset = (LPBYTE) lpFreeKeyRecord - (LPBYTE) lpDatablockHeader;
  698. // Check if this key record is at the very end of the datablock and that
  699. // lpFreeKeyRecord is really a free key record.
  700. if (FreeOffset >= lpDatablockInfo-> BlockSize ||
  701. !IsKeyRecordFree(lpFreeKeyRecord))
  702. return ERROR_OUTOFMEMORY;
  703. ASSERT(Length >= AllocatedSize);
  704. FreeSizeAllocation = Length - AllocatedSize;
  705. AllocatedSize = SmallDword(lpFreeKeyRecord-> AllocatedSize);
  706. if (FreeSizeAllocation > AllocatedSize)
  707. return ERROR_OUTOFMEMORY;
  708. ExtraBytes = AllocatedSize - FreeSizeAllocation;
  709. //
  710. // If we were to break this FREEKEY_RECORD into two records, would the
  711. // second chunk be too small? If so, then don't do it. Just give back
  712. // the full allocated size to the caller.
  713. //
  714. if (ExtraBytes >= MINIMUM_FREE_RECORD_LENGTH) {
  715. NewFreeOffset = FreeOffset + FreeSizeAllocation;
  716. lpTempFreeKeyRecord = (LPFREEKEY_RECORD) ((LPBYTE) lpFreeKeyRecord +
  717. FreeSizeAllocation);
  718. // IMPORTANT: Note that lpNewFreeKeyRecord and lpFreeKeyRecord may
  719. // overlap so we have to be careful when changing these fields!
  720. lpTempFreeKeyRecord-> NextFreeOffset = lpFreeKeyRecord-> NextFreeOffset;
  721. lpTempFreeKeyRecord-> DatablockAddress = REG_NULL;
  722. lpTempFreeKeyRecord-> AllocatedSize = ExtraBytes;
  723. }
  724. else {
  725. NewFreeOffset = lpFreeKeyRecord-> NextFreeOffset;
  726. // The key record's allocated length will also include all of the extra
  727. // bytes.
  728. FreeSizeAllocation += ExtraBytes;
  729. }
  730. lpKeyRecord-> AllocatedSize += FreeSizeAllocation;
  731. lpDatablockInfo-> FreeBytes -= FreeSizeAllocation;
  732. //
  733. // Unlink the free record that we just extended into and possibly link in
  734. // the new FREEKEY_RECORD if a split occurred.
  735. //
  736. Offset = lpDatablockHeader-> FirstFreeOffset;
  737. if (Offset == FreeOffset) {
  738. lpDatablockHeader-> FirstFreeOffset = NewFreeOffset;
  739. }
  740. else {
  741. while (Offset != REG_NULL) {
  742. lpTempFreeKeyRecord =
  743. (LPFREEKEY_RECORD) ((LPBYTE) lpDatablockHeader +
  744. SmallDword(Offset));
  745. Offset = lpTempFreeKeyRecord-> NextFreeOffset;
  746. if (Offset == FreeOffset) {
  747. lpTempFreeKeyRecord-> NextFreeOffset = NewFreeOffset;
  748. break;
  749. }
  750. }
  751. }
  752. return ERROR_SUCCESS;
  753. }
  754. //
  755. // RgFreeKeyRecord
  756. //
  757. // The datablock referred to by lpDatablockInfo must have been locked to
  758. // guarantee that the its data is actually present. The datablock is not
  759. // dirtied.
  760. //
  761. VOID
  762. INTERNAL
  763. RgFreeKeyRecord(
  764. LPDATABLOCK_INFO lpDatablockInfo,
  765. LPKEY_RECORD lpKeyRecord
  766. )
  767. {
  768. LPDATABLOCK_HEADER lpDatablockHeader;
  769. lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
  770. ((LPFREEKEY_RECORD) lpKeyRecord)-> DatablockAddress = REG_NULL;
  771. ((LPFREEKEY_RECORD) lpKeyRecord)-> NextFreeOffset = lpDatablockHeader->
  772. FirstFreeOffset;
  773. lpDatablockHeader-> FirstFreeOffset = (LPBYTE) lpKeyRecord - (LPBYTE)
  774. lpDatablockHeader;
  775. lpDatablockInfo-> FreeBytes += SmallDword(((LPFREEKEY_RECORD) lpKeyRecord)->
  776. AllocatedSize);
  777. }
  778. //
  779. // RgFreeKeyRecordIndex
  780. //
  781. // The datablock referred to by lpDatablockInfo must have been locked to
  782. // guarantee that the its data is actually present. The datablock is not
  783. // dirtied.
  784. //
  785. // We don't bother updated MaxAllocatedIndex because it's only really useful
  786. // if we're always freeing from the maximum index to zero. This is very
  787. // rarely the case, so no point in keeping that test around or touching the
  788. // datablock header page just to do it.
  789. //
  790. VOID
  791. INTERNAL
  792. RgFreeKeyRecordIndex(
  793. LPDATABLOCK_INFO lpDatablockInfo,
  794. UINT KeyRecordIndex
  795. )
  796. {
  797. ASSERT(lpDatablockInfo-> lpDatablockHeader-> MaxAllocatedIndex >=
  798. KeyRecordIndex);
  799. if (lpDatablockInfo-> FirstFreeIndex > KeyRecordIndex)
  800. lpDatablockInfo-> FirstFreeIndex = KeyRecordIndex;
  801. lpDatablockInfo-> lpKeyRecordTable[KeyRecordIndex] =
  802. NULL_KEY_RECORD_TABLE_ENTRY;
  803. }
  804. //
  805. // RgWriteDatablocks
  806. //
  807. // Writes all dirty datablocks to the file specified by the file handle.
  808. //
  809. int
  810. INTERNAL
  811. RgWriteDatablocks(
  812. LPFILE_INFO lpFileInfo,
  813. HFILE hSourceFile,
  814. HFILE hDestinationFile
  815. )
  816. {
  817. UINT BlockIndex;
  818. LPDATABLOCK_INFO lpDatablockInfo;
  819. LPDATABLOCK_HEADER lpDatablockHeader;
  820. LONG FileOffset;
  821. lpDatablockInfo = lpFileInfo-> lpDatablockInfo;
  822. FileOffset = lpFileInfo-> FileHeader.Size;
  823. for (BlockIndex = 0; BlockIndex < lpFileInfo-> FileHeader.BlockCount;
  824. BlockIndex++, lpDatablockInfo++) {
  825. if (lpDatablockInfo-> Flags & DIF_PRESENT) {
  826. // The block is currently in memory. If we're either extending
  827. // the file or the block is dirty, then write out our in-memory
  828. // copy to disk.
  829. if (hSourceFile != HFILE_ERROR || lpDatablockInfo-> Flags &
  830. DIF_DIRTY) {
  831. NOISE(("writing datablock #%d of ", BlockIndex));
  832. NOISE((lpFileInfo-> FileName));
  833. NOISE(("\n"));
  834. lpDatablockHeader = lpDatablockInfo-> lpDatablockHeader;
  835. // Copy back the fields that we've been maintaining in the
  836. // DATABLOCK_INFO structure.
  837. lpDatablockHeader-> BlockSize = lpDatablockInfo-> BlockSize;
  838. lpDatablockHeader-> FreeBytes = lpDatablockInfo-> FreeBytes;
  839. lpDatablockHeader-> FirstFreeIndex = (WORD) lpDatablockInfo->
  840. FirstFreeIndex;
  841. // The checksum is not currently calculated, so we must clear
  842. // the flag so we don't confuse Win95.
  843. lpDatablockHeader-> Flags &= ~DHF_HASCHECKSUM;
  844. if (!RgSeekFile(hDestinationFile, FileOffset))
  845. return ERROR_REGISTRY_IO_FAILED;
  846. if (!RgWriteFile(hDestinationFile, lpDatablockHeader,
  847. lpDatablockInfo-> BlockSize))
  848. return ERROR_REGISTRY_IO_FAILED;
  849. }
  850. }
  851. else {
  852. // The block is not currently in memory. If we're extending the
  853. // file, then we must write out this datablock. The overhead is
  854. // too great to lock the datablock down, so just copy it from the
  855. // original file to the extended file.
  856. if (hSourceFile != HFILE_ERROR) {
  857. if (RgCopyFileBytes(hSourceFile, lpDatablockInfo-> FileOffset,
  858. hDestinationFile, FileOffset, lpDatablockInfo->
  859. BlockSize) != ERROR_SUCCESS)
  860. return ERROR_REGISTRY_IO_FAILED;
  861. }
  862. }
  863. FileOffset += lpDatablockInfo-> BlockSize;
  864. }
  865. return ERROR_SUCCESS;
  866. }
  867. //
  868. // RgWriteDatablocksComplete
  869. //
  870. // Called after a file has been successfully written. We can now safely clear
  871. // all dirty flags and update our state information with the knowledge that
  872. // the file is in a consistent state.
  873. //
  874. VOID
  875. INTERNAL
  876. RgWriteDatablocksComplete(
  877. LPFILE_INFO lpFileInfo
  878. )
  879. {
  880. UINT BlockIndex;
  881. LPDATABLOCK_INFO lpDatablockInfo;
  882. LONG FileOffset;
  883. lpDatablockInfo = lpFileInfo-> lpDatablockInfo;
  884. FileOffset = lpFileInfo-> FileHeader.Size;
  885. for (BlockIndex = 0; BlockIndex < lpFileInfo-> FileHeader.BlockCount;
  886. BlockIndex++, lpDatablockInfo++) {
  887. lpDatablockInfo-> Flags &= ~DIF_DIRTY;
  888. lpDatablockInfo-> FileOffset = FileOffset;
  889. FileOffset += lpDatablockInfo-> BlockSize;
  890. }
  891. }
  892. //
  893. // RgSweepDatablocks
  894. //
  895. // Makes a pass through all the present datablocks of the given FILE_INFO
  896. // structure and discards datablocks that have not been accessed since the last
  897. // sweep.
  898. //
  899. VOID
  900. INTERNAL
  901. RgSweepDatablocks(
  902. LPFILE_INFO lpFileInfo
  903. )
  904. {
  905. UINT BlocksLeft;
  906. LPDATABLOCK_INFO lpDatablockInfo;
  907. for (BlocksLeft = lpFileInfo-> FileHeader.BlockCount, lpDatablockInfo =
  908. lpFileInfo-> lpDatablockInfo; BlocksLeft > 0; BlocksLeft--,
  909. lpDatablockInfo++) {
  910. if (((lpDatablockInfo-> Flags & (DIF_PRESENT | DIF_ACCESSED |
  911. DIF_DIRTY)) == DIF_PRESENT) && (lpDatablockInfo-> LockCount == 0)) {
  912. NOISE(("discarding datablock #%d of ",
  913. lpFileInfo-> FileHeader.BlockCount - BlocksLeft));
  914. NOISE((lpFileInfo-> FileName));
  915. NOISE(("\n"));
  916. RgFreeDatablockInfoBuffers(lpDatablockInfo);
  917. lpDatablockInfo-> Flags = 0;
  918. }
  919. // Reset the accessed bit for the next sweep.
  920. lpDatablockInfo-> Flags &= ~DIF_ACCESSED;
  921. }
  922. }
  923. //
  924. // RgIsValidDatablockHeader
  925. //
  926. // Returns TRUE if lpDatablockHeader is a valid DATABLOCK_HEADER structure.
  927. //
  928. BOOL
  929. INTERNAL
  930. RgIsValidDatablockHeader(
  931. LPDATABLOCK_HEADER lpDatablockHeader
  932. )
  933. {
  934. if (lpDatablockHeader-> Signature != DH_SIGNATURE ||
  935. HIWORD(lpDatablockHeader-> BlockSize) != 0)
  936. return FALSE;
  937. return TRUE;
  938. }
  939. #ifdef VXD
  940. #pragma VxD_RARE_CODE_SEG
  941. #endif
  942. //
  943. // RgInitDatablockInfo
  944. //
  945. // Initializes fields in the provided FILE_INFO related to the datablocks.
  946. //
  947. int
  948. INTERNAL
  949. RgInitDatablockInfo(
  950. LPFILE_INFO lpFileInfo,
  951. HFILE hFile
  952. )
  953. {
  954. UINT BlockCount;
  955. UINT BlockIndex;
  956. LPDATABLOCK_INFO lpDatablockInfo;
  957. LONG FileOffset;
  958. DATABLOCK_HEADER DatablockHeader;
  959. BlockCount = lpFileInfo-> FileHeader.BlockCount;
  960. if (IsNullPtr((lpDatablockInfo = (LPDATABLOCK_INFO)
  961. RgSmAllocMemory((BlockCount + DATABLOCK_INFO_SLACK_ALLOC) *
  962. sizeof(DATABLOCK_INFO)))))
  963. return ERROR_OUTOFMEMORY;
  964. ZeroMemory(lpDatablockInfo, BlockCount * sizeof(DATABLOCK_INFO));
  965. lpFileInfo-> lpDatablockInfo = lpDatablockInfo;
  966. lpFileInfo-> DatablockInfoAllocCount = BlockCount +
  967. DATABLOCK_INFO_SLACK_ALLOC;
  968. FileOffset = lpFileInfo-> FileHeader.Size;
  969. for (BlockIndex = 0; BlockIndex < BlockCount; BlockIndex++,
  970. lpDatablockInfo++) {
  971. if (!RgSeekFile(hFile, FileOffset))
  972. return ERROR_REGISTRY_IO_FAILED;
  973. if (!RgReadFile(hFile, &DatablockHeader, sizeof(DATABLOCK_HEADER)))
  974. return ERROR_REGISTRY_IO_FAILED;
  975. if (!RgIsValidDatablockHeader(&DatablockHeader))
  976. return ERROR_BADDB;
  977. // Following fields already zeroed by above ZeroMemory.
  978. // lpDatablockInfo-> lpDatablockHeader = NULL;
  979. // lpDatablockInfo-> lpKeyRecordTable = NULL;
  980. // lpDatablockInfo-> Flags = 0;
  981. // lpDatablockInfo-> LockCount = 0;
  982. lpDatablockInfo-> FileOffset = FileOffset;
  983. // Cache these fields from the datablock header. These fields should
  984. // not be considered valid when the datablock is physically in memory.
  985. lpDatablockInfo-> BlockSize = SmallDword(DatablockHeader.BlockSize);
  986. lpDatablockInfo-> FreeBytes = SmallDword(DatablockHeader.FreeBytes);
  987. lpDatablockInfo-> FirstFreeIndex = DatablockHeader.FirstFreeIndex;
  988. NOISE(("DB#%d fileoff=%lx, size=%x free=%x 1stindex=%d\n", BlockIndex,
  989. FileOffset, lpDatablockInfo-> BlockSize, lpDatablockInfo->
  990. FreeBytes, lpDatablockInfo-> FirstFreeIndex));
  991. FileOffset += lpDatablockInfo-> BlockSize;
  992. }
  993. return ERROR_SUCCESS;
  994. }