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.

14004 lines
395 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. BitmpSup.c
  5. Abstract:
  6. This module implements the general bitmap allocation & deallocation
  7. routines for Ntfs. It is defined into two main parts the first
  8. section handles the bitmap file for clusters on the disk. The
  9. second part is for bitmap attribute allocation (e.g., the mft bitmap).
  10. So unlike other modules this one has local procedure prototypes and
  11. definitions followed by the exported bitmap file routines, followed
  12. by the local bitmap file routines, and then followed by the bitmap
  13. attribute routines, followed by the local bitmap attribute allocation
  14. routines.
  15. Author:
  16. Gary Kimura [GaryKi] 23-Nov-1991
  17. Revision History:
  18. --*/
  19. #include "NtfsProc.h"
  20. #ifdef NTFS_FRAGMENT_DISK
  21. BOOLEAN NtfsFragmentDisk = FALSE;
  22. ULONG NtfsFragmentLength = 2;
  23. BOOLEAN NtfsFragmentMft = FALSE;
  24. #endif
  25. #ifdef NTFS_CHECK_CACHED_RUNS
  26. BOOLEAN NtfsDoVerifyCachedRuns = FALSE;
  27. #endif
  28. #define NTFS_MFT_ZONE_DEFAULT_SHIFT (3)
  29. #define BITMAP_VOLATILE_FREE_COUNT (0x400)
  30. //
  31. // Define stack overflow threshhold.
  32. //
  33. #define OVERFLOW_RECORD_THRESHHOLD (0xF00)
  34. //
  35. // A mask of single bits used to clear and set bits in a byte
  36. //
  37. static UCHAR BitMask[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
  38. //
  39. // Local debug trace level
  40. //
  41. #define Dbg (DEBUG_TRACE_BITMPSUP)
  42. //
  43. // Define a tag for general pool allocations from this module
  44. //
  45. #undef MODULE_POOL_TAG
  46. #define MODULE_POOL_TAG ('BFtN')
  47. #define MIN3(A,B,C) ((((A) < (B)) && ((A) < (C))) ? (A) : ((((B) < (A)) && ((B) < (C))) ? (B) : (C)))
  48. #define CollectAllocateClusterStats(VCB,SIZE,HINT) { \
  49. (VCB)->Statistics->Ntfs.Allocate.Calls += 1; \
  50. (VCB)->Statistics->Ntfs.Allocate.Clusters += (ULONG)(SIZE); \
  51. if (HINT) { (VCB)->Statistics->Ntfs.Allocate.Hints += 1; } \
  52. }
  53. #define IncrementAllocateClusterStats(VCB) { \
  54. (VCB)->Statistics->Ntfs.Allocate.RunsReturned += 1; \
  55. }
  56. #define IncrementHintHonoredStats(VCB,SIZE) { \
  57. (VCB)->Statistics->Ntfs.Allocate.HintsHonored += 1; \
  58. (VCB)->Statistics->Ntfs.Allocate.HintsClusters += (ULONG)(SIZE); \
  59. }
  60. #define IncrementCacheHitStats(VCB,SIZE) { \
  61. (VCB)->Statistics->Ntfs.Allocate.Cache += 1; \
  62. (VCB)->Statistics->Ntfs.Allocate.CacheClusters += (ULONG)(SIZE); \
  63. }
  64. #define IncrementCacheMissStats(VCB,SIZE) { \
  65. (VCB)->Statistics->Ntfs.Allocate.CacheMiss += 1; \
  66. (VCB)->Statistics->Ntfs.Allocate.CacheMissClusters += (ULONG)(SIZE); \
  67. }
  68. //
  69. // Local routines to manage the cached free clusters.
  70. //
  71. BOOLEAN
  72. NtfsLookupCachedLcn (
  73. IN PNTFS_CACHED_RUNS CachedRuns,
  74. IN LCN Lcn,
  75. OUT PLCN StartingLcn,
  76. OUT PLONGLONG RunLength,
  77. OUT PUSHORT Index OPTIONAL
  78. );
  79. BOOLEAN
  80. NtfsGetNextCachedLcn (
  81. IN PNTFS_CACHED_RUNS CachedRuns,
  82. IN USHORT Index,
  83. OUT PLCN StartingLcn,
  84. OUT PLONGLONG RunLength
  85. );
  86. BOOLEAN
  87. NtfsLookupCachedLcnByLength (
  88. IN PNTFS_CACHED_RUNS CachedRuns,
  89. IN LONGLONG Length,
  90. IN BOOLEAN AllowShorter,
  91. IN LCN Lcn,
  92. OUT PLCN StartingLcn,
  93. OUT PLONGLONG RunLength,
  94. OUT PUSHORT Index OPTIONAL
  95. );
  96. VOID
  97. NtfsInsertCachedLcn (
  98. IN PNTFS_CACHED_RUNS CachedRuns,
  99. IN LCN Lcn,
  100. IN LONGLONG Length
  101. );
  102. VOID
  103. NtfsRemoveCachedLcn (
  104. IN PNTFS_CACHED_RUNS CachedRuns,
  105. IN LCN Lcn,
  106. IN LONGLONG Length
  107. );
  108. //
  109. // The following are the internal routines we use to manage this.
  110. //
  111. BOOLEAN
  112. NtfsGrowCachedRuns (
  113. IN PNTFS_CACHED_RUNS CachedRuns
  114. );
  115. VOID
  116. NtfsCompactCachedRuns (
  117. IN PNTFS_CACHED_RUNS CachedRuns,
  118. IN USHORT FirstIndex,
  119. IN USHORT LastIndex,
  120. IN BOOLEAN LcnSortedList
  121. );
  122. VOID
  123. NtfsAddCachedRunMult (
  124. IN PIRP_CONTEXT IrpContext,
  125. IN PVCB Vcb,
  126. IN LCN StartingLcn,
  127. IN RTL_BITMAP_RUN *RunArray,
  128. IN ULONG RunCount
  129. );
  130. VOID
  131. NtfsDeleteCachedRun (
  132. IN PNTFS_CACHED_RUNS CachedRuns,
  133. IN USHORT LcnIndex,
  134. IN USHORT LenIndex
  135. );
  136. VOID
  137. NtfsGrowLengthInCachedLcn (
  138. IN PNTFS_CACHED_RUNS CachedRuns,
  139. IN PNTFS_LCN_CLUSTER_RUN ThisEntry,
  140. IN USHORT LcnIndex
  141. );
  142. VOID
  143. NtfsShrinkLengthInCachedLcn (
  144. IN PNTFS_CACHED_RUNS CachedRuns,
  145. IN PNTFS_LCN_CLUSTER_RUN ThisEntry,
  146. IN USHORT LcnIndex
  147. );
  148. USHORT
  149. NtfsGetCachedLengthInsertionPoint (
  150. IN PNTFS_CACHED_RUNS CachedRuns,
  151. IN LCN Lcn,
  152. IN LONGLONG Length
  153. );
  154. VOID
  155. NtfsInsertCachedRun (
  156. IN PNTFS_CACHED_RUNS CachedRuns,
  157. IN LCN Lcn,
  158. IN LONGLONG Length,
  159. IN USHORT LcnIndex
  160. );
  161. BOOLEAN
  162. NtfsPositionCachedLcn (
  163. IN PNTFS_CACHED_RUNS CachedRuns,
  164. IN LCN Lcn,
  165. OUT PUSHORT Index
  166. );
  167. BOOLEAN
  168. NtfsPositionCachedLcnByLength (
  169. IN PNTFS_CACHED_RUNS CachedRuns,
  170. IN LONGLONG RunLength,
  171. IN PLCN Lcn OPTIONAL,
  172. IN PUSHORT StartIndex OPTIONAL,
  173. IN BOOLEAN SearchForward,
  174. OUT PUSHORT RunIndex
  175. );
  176. #ifdef NTFS_CHECK_CACHED_RUNS
  177. VOID
  178. NtfsVerifyCachedLcnRuns (
  179. IN PNTFS_CACHED_RUNS CachedRuns,
  180. IN USHORT FirstIndex,
  181. IN USHORT LastIndex,
  182. IN BOOLEAN SkipSortCheck,
  183. IN BOOLEAN SkipBinCheck
  184. );
  185. VOID
  186. NtfsVerifyCachedLenRuns (
  187. IN PNTFS_CACHED_RUNS CachedRuns,
  188. IN USHORT FirstIndex,
  189. IN USHORT LastIndex,
  190. IN BOOLEAN SkipSortCheck
  191. );
  192. VOID
  193. NtfsVerifyCachedRuns (
  194. IN PNTFS_CACHED_RUNS CachedRuns,
  195. IN BOOLEAN SkipSortCheck,
  196. IN BOOLEAN SkipBinCheck
  197. );
  198. #endif
  199. //
  200. // Macros to manipulate the cached run structures.
  201. //
  202. //
  203. // VOID
  204. // NtfsModifyCachedBinArray (
  205. // IN PNTFS_CACHED_RUNS CachedRuns,
  206. // IN LONGLONG OldLength
  207. // IN LONGLONG NewLength
  208. // );
  209. //
  210. #define NtfsModifyCachedBinArray(C,OL,NL) { \
  211. ASSERT( (NL) != 0 ); \
  212. ASSERT( (OL) != 0 ); \
  213. if ((OL) <= (C)->Bins) { \
  214. (C)->BinArray[ (OL) - 1 ] -= 1; \
  215. } \
  216. if ((NL) <= (C)->Bins) { \
  217. (C)->BinArray[ (NL) - 1 ] += 1; \
  218. } \
  219. }
  220. //
  221. // Some local manifest constants
  222. //
  223. #define BYTES_PER_PAGE (PAGE_SIZE)
  224. #define BITS_PER_PAGE (BYTES_PER_PAGE * 8)
  225. //
  226. // Local procedure prototypes for direct bitmap manipulation
  227. //
  228. VOID
  229. NtfsAllocateBitmapRun (
  230. IN PIRP_CONTEXT IrpContext,
  231. IN PVCB Vcb,
  232. IN LCN StartingLcn,
  233. IN LONGLONG ClusterCount,
  234. IN BOOLEAN FromCachedRuns
  235. );
  236. VOID
  237. NtfsFreeBitmapRun (
  238. IN PIRP_CONTEXT IrpContext,
  239. IN PVCB Vcb,
  240. IN LCN StartingLcn,
  241. IN OUT PLONGLONG ClusterCount
  242. );
  243. BOOLEAN
  244. NtfsFindFreeBitmapRun (
  245. IN PIRP_CONTEXT IrpContext,
  246. IN PVCB Vcb,
  247. IN LONGLONG NumberToFind,
  248. IN LCN StartingSearchHint,
  249. IN BOOLEAN ReturnAnyLength,
  250. IN BOOLEAN IgnoreMftZone,
  251. OUT PLCN ReturnedLcn,
  252. OUT PLONGLONG ClusterCountFound
  253. );
  254. BOOLEAN
  255. NtfsScanBitmapRange (
  256. IN PIRP_CONTEXT IrpContext,
  257. IN PVCB Vcb,
  258. IN LCN StartLcn,
  259. IN LCN BeyondLcn,
  260. IN LONGLONG NumberToFind,
  261. OUT PLCN ReturnedLcn,
  262. OUT PLONGLONG ClusterCountFound
  263. );
  264. BOOLEAN
  265. NtfsAddRecentlyDeallocated (
  266. IN PVCB Vcb,
  267. IN LCN Lcn,
  268. IN OUT PRTL_BITMAP Bitmap
  269. );
  270. //
  271. // The following two prototype are macros for calling map or pin data
  272. //
  273. // VOID
  274. // NtfsMapPageInBitmap (
  275. // IN PIRP_CONTEXT IrpContext,
  276. // IN PVCB Vcb,
  277. // IN LCN Lcn,
  278. // OUT PLCN StartingLcn,
  279. // IN OUT PRTL_BITMAP Bitmap,
  280. // OUT PBCB *BitmapBcb,
  281. // );
  282. //
  283. // VOID
  284. // NtfsPinPageInBitmap (
  285. // IN PIRP_CONTEXT IrpContext,
  286. // IN PVCB Vcb,
  287. // IN LCN Lcn,
  288. // OUT PLCN StartingLcn,
  289. // IN OUT PRTL_BITMAP Bitmap,
  290. // OUT PBCB *BitmapBcb,
  291. // );
  292. //
  293. #define NtfsMapPageInBitmap(A,B,C,D,E,F) NtfsMapOrPinPageInBitmap(A,B,C,D,E,F,FALSE)
  294. #define NtfsPinPageInBitmap(A,B,C,D,E,F) NtfsMapOrPinPageInBitmap(A,B,C,D,E,F,TRUE)
  295. VOID
  296. NtfsMapOrPinPageInBitmap (
  297. IN PIRP_CONTEXT IrpContext,
  298. IN PVCB Vcb,
  299. IN LCN Lcn,
  300. OUT PLCN StartingLcn,
  301. IN OUT PRTL_BITMAP Bitmap,
  302. OUT PBCB *BitmapBcb,
  303. IN BOOLEAN AlsoPinData
  304. );
  305. //
  306. // Local procedure prototype for doing read ahead on our cached
  307. // run information
  308. //
  309. VOID
  310. NtfsReadAheadCachedBitmap (
  311. IN PIRP_CONTEXT IrpContext,
  312. IN PVCB Vcb,
  313. IN LCN StartingLcn
  314. );
  315. //
  316. // Local procedure prototypes for routines that help us find holes
  317. // that need to be filled with MCBs
  318. //
  319. BOOLEAN
  320. NtfsGetNextHoleToFill (
  321. IN PIRP_CONTEXT IrpContext,
  322. IN PNTFS_MCB Mcb,
  323. IN VCN StartingVcn,
  324. IN VCN EndingVcn,
  325. OUT PVCN VcnToFill,
  326. OUT PLONGLONG ClusterCountToFill,
  327. OUT PLCN PrecedingLcn
  328. );
  329. LONGLONG
  330. NtfsScanMcbForRealClusterCount (
  331. IN PIRP_CONTEXT IrpContext,
  332. IN PNTFS_MCB Mcb,
  333. IN VCN StartingVcn,
  334. IN VCN EndingVcn
  335. );
  336. //
  337. // A local procedure prototype for masking out recently deallocated records
  338. //
  339. BOOLEAN
  340. NtfsAddDeallocatedRecords (
  341. IN PVCB Vcb,
  342. IN PSCB Scb,
  343. IN ULONG StartingIndexOfBitmap,
  344. IN OUT PRTL_BITMAP Bitmap
  345. );
  346. //
  347. // Local procedure prototypes for managing the Mft zone.
  348. //
  349. LCN
  350. NtfsInitializeMftZone (
  351. IN PIRP_CONTEXT IrpContext,
  352. IN PVCB Vcb
  353. );
  354. BOOLEAN
  355. NtfsReduceMftZone (
  356. IN PIRP_CONTEXT IrpContext,
  357. IN PVCB Vcb
  358. );
  359. //
  360. // Local procedure prototype to check the stack usage in the record
  361. // package.
  362. //
  363. VOID
  364. NtfsCheckRecordStackUsage (
  365. IN PIRP_CONTEXT IrpContext
  366. );
  367. //
  368. // Local procedure prototype to check for a continuos volume bitmap run
  369. //
  370. VOID
  371. NtfsRunIsClear (
  372. IN PIRP_CONTEXT IrpContext,
  373. IN PVCB Vcb,
  374. IN LCN StartingLcn,
  375. IN LONGLONG RunLength
  376. );
  377. //
  378. // Local procedure prototypes for managing windows of deleted entries.
  379. //
  380. VOID
  381. NtfsAddDelWindow (
  382. IN PNTFS_CACHED_RUNS CachedRuns,
  383. IN USHORT FirstIndex,
  384. IN USHORT LastIndex,
  385. IN BOOLEAN LcnList
  386. );
  387. PNTFS_DELETED_RUNS
  388. NtfsGetDelWindow (
  389. IN PNTFS_CACHED_RUNS CachedRuns,
  390. IN USHORT FirstIndex,
  391. IN USHORT LastIndex,
  392. IN BOOLEAN LcnList,
  393. OUT PUSHORT WindowIndex OPTIONAL
  394. );
  395. VOID
  396. NtfsShrinkDelWindow (
  397. IN PNTFS_CACHED_RUNS CachedRuns,
  398. IN BOOLEAN ShrinkFromStart,
  399. IN BOOLEAN LcnWindow,
  400. IN USHORT WindowIndex
  401. );
  402. VOID
  403. NtfsDeleteDelWindow (
  404. IN PNTFS_CACHED_RUNS CachedRuns,
  405. IN BOOLEAN LcnWindow,
  406. IN USHORT WindowIndex
  407. );
  408. VOID
  409. NtfsMakeSpaceCachedLcn (
  410. IN PNTFS_CACHED_RUNS CachedRuns,
  411. IN LCN StartingLcn,
  412. IN RTL_BITMAP_RUN *RunArray,
  413. IN ULONG RunCount,
  414. IN PUSHORT LcnSorted OPTIONAL
  415. );
  416. //
  417. // Local procedure prototype for dumping cached bitmap information
  418. //
  419. #ifdef NTFSDBG
  420. ULONG
  421. NtfsDumpCachedMcbInformation (
  422. IN PVCB Vcb
  423. );
  424. #else
  425. #define NtfsDumpCachedMcbInformation(V) (0)
  426. #endif // NTFSDBG
  427. #ifdef ALLOC_PRAGMA
  428. #pragma alloc_text(PAGE, NtfsAddBadCluster)
  429. #pragma alloc_text(PAGE, NtfsAddCachedRun)
  430. #pragma alloc_text(PAGE, NtfsAddCachedRunMult)
  431. #pragma alloc_text(PAGE, NtfsAddDeallocatedRecords)
  432. #pragma alloc_text(PAGE, NtfsAddDelWindow)
  433. #pragma alloc_text(PAGE, NtfsAddRecentlyDeallocated)
  434. #pragma alloc_text(PAGE, NtfsAllocateBitmapRun)
  435. #pragma alloc_text(PAGE, NtfsAllocateClusters)
  436. #pragma alloc_text(PAGE, NtfsAllocateMftReservedRecord)
  437. #pragma alloc_text(PAGE, NtfsAllocateRecord)
  438. #pragma alloc_text(PAGE, NtfsGrowLengthInCachedLcn)
  439. #pragma alloc_text(PAGE, NtfsShrinkLengthInCachedLcn)
  440. #pragma alloc_text(PAGE, NtfsCheckRecordStackUsage)
  441. #pragma alloc_text(PAGE, NtfsCleanupClusterAllocationHints)
  442. #pragma alloc_text(PAGE, NtfsCompactCachedRuns)
  443. #pragma alloc_text(PAGE, NtfsCreateMftHole)
  444. #pragma alloc_text(PAGE, NtfsDeallocateClusters)
  445. #pragma alloc_text(PAGE, NtfsDeallocateRecord)
  446. #pragma alloc_text(PAGE, NtfsDeallocateRecordsComplete)
  447. #pragma alloc_text(PAGE, NtfsDeleteCachedRun)
  448. #pragma alloc_text(PAGE, NtfsDeleteDelWindow)
  449. #pragma alloc_text(PAGE, NtfsFindFreeBitmapRun)
  450. #pragma alloc_text(PAGE, NtfsFindMftFreeTail)
  451. #pragma alloc_text(PAGE, NtfsFreeBitmapRun)
  452. #pragma alloc_text(PAGE, NtfsGetCachedLengthInsertionPoint)
  453. #pragma alloc_text(PAGE, NtfsGetDelWindow)
  454. #pragma alloc_text(PAGE, NtfsGetNextCachedLcn)
  455. #pragma alloc_text(PAGE, NtfsGetNextHoleToFill)
  456. #pragma alloc_text(PAGE, NtfsGrowCachedRuns)
  457. #pragma alloc_text(PAGE, NtfsInitializeCachedRuns)
  458. #pragma alloc_text(PAGE, NtfsInitializeClusterAllocation)
  459. #pragma alloc_text(PAGE, NtfsInitializeMftZone)
  460. #pragma alloc_text(PAGE, NtfsInitializeRecordAllocation)
  461. #pragma alloc_text(PAGE, NtfsInsertCachedLcn)
  462. #pragma alloc_text(PAGE, NtfsInsertCachedRun)
  463. #pragma alloc_text(PAGE, NtfsIsRecordAllocated)
  464. #pragma alloc_text(PAGE, NtfsLookupCachedLcn)
  465. #pragma alloc_text(PAGE, NtfsLookupCachedLcnByLength)
  466. #pragma alloc_text(PAGE, NtfsMakeSpaceCachedLcn)
  467. #pragma alloc_text(PAGE, NtfsMapOrPinPageInBitmap)
  468. #pragma alloc_text(PAGE, NtfsModifyBitsInBitmap)
  469. #pragma alloc_text(PAGE, NtfsPositionCachedLcn)
  470. #pragma alloc_text(PAGE, NtfsPositionCachedLcnByLength)
  471. #pragma alloc_text(PAGE, NtfsPreAllocateClusters)
  472. #pragma alloc_text(PAGE, NtfsReadAheadCachedBitmap)
  473. #pragma alloc_text(PAGE, NtfsReduceMftZone)
  474. #pragma alloc_text(PAGE, NtfsReinitializeCachedRuns)
  475. #pragma alloc_text(PAGE, NtfsRemoveCachedLcn)
  476. #pragma alloc_text(PAGE, NtfsReserveMftRecord)
  477. #pragma alloc_text(PAGE, NtfsRestartClearBitsInBitMap)
  478. #pragma alloc_text(PAGE, NtfsRestartSetBitsInBitMap)
  479. #pragma alloc_text(PAGE, NtfsRunIsClear)
  480. #pragma alloc_text(PAGE, NtfsScanBitmapRange)
  481. #pragma alloc_text(PAGE, NtfsScanEntireBitmap)
  482. #pragma alloc_text(PAGE, NtfsScanMcbForRealClusterCount)
  483. #pragma alloc_text(PAGE, NtfsScanMftBitmap)
  484. #pragma alloc_text(PAGE, NtfsShrinkDelWindow)
  485. #pragma alloc_text(PAGE, NtfsUninitializeCachedRuns)
  486. #pragma alloc_text(PAGE, NtfsUninitializeRecordAllocation)
  487. #ifdef NTFS_CHECK_CACHED_RUNS
  488. #pragma alloc_text(PAGE, NtfsVerifyCachedLcnRuns)
  489. #pragma alloc_text(PAGE, NtfsVerifyCachedLenRuns)
  490. #pragma alloc_text(PAGE, NtfsVerifyCachedRuns)
  491. #endif
  492. #endif
  493. VOID
  494. NtfsInitializeClusterAllocation (
  495. IN PIRP_CONTEXT IrpContext,
  496. IN PVCB Vcb
  497. )
  498. /*++
  499. Routine Description:
  500. This routine initializes the cluster allocation structures within the
  501. specified Vcb. It reads in as necessary the bitmap and scans it for
  502. free space and builds the free space mcb based on this scan.
  503. This procedure is multi-call save. That is, it can be used to
  504. reinitialize the cluster allocation without first calling the
  505. uninitialize cluster allocation routine.
  506. Arguments:
  507. Vcb - Supplies the Vcb being initialized
  508. Return Value:
  509. None.
  510. --*/
  511. {
  512. LONGLONG ClusterCount;
  513. ASSERT_IRP_CONTEXT( IrpContext );
  514. ASSERT_VCB( Vcb );
  515. PAGED_CODE();
  516. DebugTrace( +1, Dbg, ("NtfsInitializeClusterAllocation\n") );
  517. NtfsAcquireExclusiveScb( IrpContext, Vcb->BitmapScb );
  518. try {
  519. //
  520. // The bitmap file currently doesn't have a paging IO resource.
  521. // Create one here so that we won't serialize synchronization
  522. // of the bitmap package with the lazy writer.
  523. //
  524. Vcb->BitmapScb->Header.PagingIoResource =
  525. Vcb->BitmapScb->Fcb->PagingIoResource = NtfsAllocateEresource();
  526. //
  527. // We didn't mark the Scb for the volume bitmap as MODIFIED_NO_WRITE
  528. // when creating it. Do so now.
  529. //
  530. SetFlag( Vcb->BitmapScb->ScbState, SCB_STATE_MODIFIED_NO_WRITE );
  531. //
  532. // Now call a bitmap routine to scan the entire bitmap. This
  533. // routine will compute the number of free clusters in the
  534. // bitmap and set the largest free runs that we find into the
  535. // cached bitmap structures.
  536. //
  537. NtfsScanEntireBitmap( IrpContext, Vcb, FALSE );
  538. //
  539. // Our last operation is to set the hint lcn which is used by
  540. // our allocation routine as a hint on where to find free space.
  541. // In the running system it is the last lcn that we've allocated.
  542. // But for startup we'll put it to be the first free run that
  543. // is stored in the free space mcb.
  544. //
  545. NtfsGetNextCachedLcn( &Vcb->CachedRuns,
  546. 0,
  547. &Vcb->LastBitmapHint,
  548. &ClusterCount );
  549. NtfsInitializeMftZone( IrpContext, Vcb );
  550. } finally {
  551. DebugUnwind( NtfsInitializeClusterAllocation );
  552. NtfsReleaseScb( IrpContext, Vcb->BitmapScb );
  553. }
  554. DebugTrace( -1, Dbg, ("NtfsInitializeClusterAllocation -> VOID\n") );
  555. return;
  556. }
  557. BOOLEAN
  558. NtfsAllocateClusters (
  559. IN PIRP_CONTEXT IrpContext,
  560. IN PVCB Vcb,
  561. IN OUT PSCB Scb,
  562. IN VCN OriginalStartingVcn,
  563. IN BOOLEAN AllocateAll,
  564. IN LONGLONG ClusterCount,
  565. IN PLCN TargetLcn OPTIONAL,
  566. IN OUT PLONGLONG DesiredClusterCount
  567. )
  568. /*++
  569. Routine Description:
  570. This routine allocates disk space. It fills in the unallocated holes in
  571. input mcb with allocated clusters from starting Vcn to the cluster count.
  572. The basic algorithm used by this procedure is as follows:
  573. 1. Compute the EndingVcn from the StartingVcn and cluster count
  574. 2. Compute the real number of clusters needed to allocate by scanning
  575. the mcb from starting to ending vcn seeing where the real holes are
  576. 3. If the real cluster count is greater than the known free cluster count
  577. then the disk is full
  578. 4. Call a routine that takes a starting Vcn, ending Vcn, and the Mcb and
  579. returns the first hole that needs to be filled and while there is a hole
  580. to be filled...
  581. 5. Check if the run preceding the hole that we are trying to fill
  582. has an ending Lcn and if it does then with that Lcn see if we
  583. get a cache hit, if we do then allocate the cluster
  584. 6. If we are still looking then enumerate through the cached free runs
  585. and if we find a suitable one. Allocate the first suitable run we find that
  586. satisfies our request. Also in the loop remember the largest
  587. suitable run we find.
  588. 8. If we are still looking then bite the bullet and scan the bitmap on
  589. the disk for a free run using either the preceding Lcn as a hint if
  590. available or the stored last bitmap hint in the Vcb.
  591. 9. At this point we've located a run of clusters to allocate. To do the
  592. actual allocation we allocate the space from the bitmap, decrement
  593. the number of free clusters left, and update the hint.
  594. 10. Before going back to step 4 we move the starting Vcn to be the point
  595. one after the run we've just allocated.
  596. 11. With the allocation complete we update the last bitmap hint stored in
  597. the Vcb to be the last Lcn we've allocated, and we call a routine
  598. to do the read ahead in the cached bitmap at the ending lcn.
  599. Arguments:
  600. Vcb - Supplies the Vcb used in this operation
  601. Scb - Supplies an Scb whose Mcb contains the current retrieval information
  602. for the file and on exit will contain the updated retrieval
  603. information
  604. StartingVcn - Supplies a starting cluster for us to begin allocation
  605. AllocateAll - If TRUE, allocate all the clusters here. Don't break
  606. up request.
  607. ClusterCount - Supplies the number of clusters to allocate
  608. TargetLcn - If supplied allocate at this lcn rather than searching for free space
  609. used by the movefile defragging code
  610. DesiredClusterCount - Supplies the number of clusters we would like allocated
  611. and will allocate if it doesn't require additional runs. On return
  612. this value is the number of clusters allocated.
  613. Return Value:
  614. FALSE - if no clusters were allocated (they were already allocated)
  615. TRUE - if clusters were allocated
  616. Important Note:
  617. This routine will stop after allocating MAXIMUM_RUNS_AT_ONCE runs, in order
  618. to limit the size of allocating transactions. The caller must be aware that
  619. he may not get all of the space he asked for if the disk is real fragmented.
  620. --*/
  621. {
  622. VCN StartingVcn = OriginalStartingVcn;
  623. VCN EndingVcn;
  624. VCN DesiredEndingVcn;
  625. PNTFS_MCB Mcb = &Scb->Mcb;
  626. LONGLONG RemainingDesiredClusterCount;
  627. VCN VcnToFill;
  628. LONGLONG ClusterCountToFill;
  629. LCN PrecedingLcn;
  630. BOOLEAN FoundClustersToAllocate;
  631. LCN FoundLcn;
  632. LONGLONG FoundClusterCount;
  633. LONGLONG LargestBitmapClusterCount = 0;
  634. BOOLEAN FromCachedRuns;
  635. USHORT RunIndex;
  636. LCN HintLcn;
  637. ULONG LoopCount = 0;
  638. ULONG RunCount = 0;
  639. BOOLEAN ClustersAllocated = FALSE;
  640. BOOLEAN GotAHoleToFill = TRUE;
  641. BOOLEAN FoundRun = FALSE;
  642. BOOLEAN ExtendingMft = FALSE;
  643. BOOLEAN AllocateFromBitmap = FALSE;
  644. ASSERT_IRP_CONTEXT( IrpContext );
  645. ASSERT_VCB( Vcb );
  646. PAGED_CODE();
  647. DebugTrace( +1, Dbg, ("NtfsAllocateClusters\n") );
  648. DebugTrace( 0, Dbg, ("StartVcn = %0I64x\n", StartingVcn) );
  649. DebugTrace( 0, Dbg, ("ClusterCount = %0I64x\n", ClusterCount) );
  650. DebugTrace( 0, Dbg, ("DesiredClusterCount = %0I64x\n", *DesiredClusterCount) );
  651. NtfsAcquireExclusiveScb( IrpContext, Vcb->BitmapScb );
  652. try {
  653. if (FlagOn( Vcb->VcbState, VCB_STATE_RELOAD_FREE_CLUSTERS )) {
  654. NtfsScanEntireBitmap( IrpContext, Vcb, FALSE );
  655. }
  656. //
  657. // Check to see if we are defragmenting
  658. //
  659. if (ARGUMENT_PRESENT( TargetLcn )) {
  660. FoundLcn = *TargetLcn;
  661. //
  662. // Ensure that the run is NOT already allocated
  663. //
  664. NtfsRunIsClear( IrpContext, Vcb, FoundLcn, ClusterCount );
  665. //
  666. // Get the allocation data from the Scb
  667. //
  668. VcnToFill = OriginalStartingVcn;
  669. FoundClusterCount = ClusterCount;
  670. *DesiredClusterCount = ClusterCount;
  671. GotAHoleToFill = FALSE;
  672. ClustersAllocated = TRUE;
  673. FoundRun = TRUE;
  674. FromCachedRuns = FALSE;
  675. //
  676. // We already have the allocation so skip over the allocation section
  677. //
  678. goto Defragment;
  679. }
  680. //
  681. // Compute the ending vcn, and the cluster count of how much we really
  682. // need to allocate (based on what is already allocated). Then check if we
  683. // have space on the disk.
  684. //
  685. EndingVcn = (StartingVcn + ClusterCount) - 1;
  686. ClusterCount = NtfsScanMcbForRealClusterCount( IrpContext, Mcb, StartingVcn, EndingVcn );
  687. if ((ClusterCount + IrpContext->DeallocatedClusters) > Vcb->FreeClusters) {
  688. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  689. }
  690. //
  691. // Let's see if it is ok to allocate clusters for this Scb now,
  692. // in case compressed files have over-reserved the space. This
  693. // calculation is done in such a way as to guarantee we do not
  694. // have either of the terms subtracting through zero, even if
  695. // we were to over-reserve the free space on the disk due to a
  696. // hot fix or something. Always satisfy this request if we are
  697. // in the paging IO write path because we know we are using clusters
  698. // already reserved for this stream.
  699. //
  700. NtfsAcquireReservedClusters( Vcb );
  701. //
  702. // Do the fast test to see if there is even a chance of failing the reservation test
  703. // or if we will allocate this space anyway.
  704. // If there is no Irp or this is the Usn journal then allocate the space anyway.
  705. //
  706. if ((ClusterCount + Vcb->TotalReserved > Vcb->FreeClusters) &&
  707. #ifdef BRIANDBG
  708. !NtfsIgnoreReserved &&
  709. #endif
  710. (IrpContext->OriginatingIrp != NULL) &&
  711. !FlagOn( Scb->Fcb->FcbState, FCB_STATE_USN_JOURNAL )) {
  712. //
  713. // If this is not a write then fail this unless this is an fsctl which
  714. // may have reserved space.
  715. //
  716. if (IrpContext->MajorFunction != IRP_MJ_WRITE) {
  717. //
  718. // If this is an Fsctl for a data file then account for the reservation.
  719. // All other non-writes will fail because we already checked whether
  720. // they conflicted with the volume reservation.
  721. //
  722. if ((IrpContext->MajorFunction != IRP_MJ_FILE_SYSTEM_CONTROL) ||
  723. (Scb->Header.NodeTypeCode != NTFS_NTC_SCB_DATA) ||
  724. (ClusterCount + Vcb->TotalReserved - LlClustersFromBytesTruncate( Vcb, Scb->ScbType.Data.TotalReserved ) > Vcb->FreeClusters)) {
  725. NtfsReleaseReservedClusters( Vcb );
  726. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  727. }
  728. //
  729. // If we are in user write path then check the reservation. Otherwise
  730. // satisfy the request. It will be some other stream which supports the
  731. // write (i.e. Mft record for a secondary file record).
  732. //
  733. } else if ((Scb->Header.NodeTypeCode == NTFS_NTC_SCB_DATA) &&
  734. !FlagOn( IrpContext->OriginatingIrp->Flags, IRP_PAGING_IO ) &&
  735. (ClusterCount + Vcb->TotalReserved - LlClustersFromBytesTruncate( Vcb, Scb->ScbType.Data.TotalReserved ) > Vcb->FreeClusters)) {
  736. NtfsReleaseReservedClusters( Vcb );
  737. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  738. }
  739. }
  740. NtfsReleaseReservedClusters( Vcb );
  741. //
  742. // We need to check that the request won't fail because of clusters
  743. // in the recently deallocated lists.
  744. //
  745. if (Vcb->FreeClusters < (Vcb->DeallocatedClusters + ClusterCount)) {
  746. NtfsRaiseStatus( IrpContext, STATUS_LOG_FILE_FULL, NULL, NULL );
  747. }
  748. //
  749. // Remember if we are extending the Mft.
  750. //
  751. if ((Scb == Vcb->MftScb) &&
  752. (LlBytesFromClusters( Vcb, StartingVcn ) == (ULONGLONG) Scb->Header.AllocationSize.QuadPart)) {
  753. ExtendingMft = TRUE;
  754. }
  755. //
  756. // Now compute the desired ending vcn and the real desired cluster count
  757. //
  758. DesiredEndingVcn = (StartingVcn + *DesiredClusterCount) - 1;
  759. RemainingDesiredClusterCount = NtfsScanMcbForRealClusterCount( IrpContext, Mcb, StartingVcn, DesiredEndingVcn );
  760. //
  761. // While there are holes to fill we will do the following loop
  762. //
  763. while ((AllocateAll || (LoopCount < MAXIMUM_RUNS_AT_ONCE))
  764. &&
  765. (GotAHoleToFill = NtfsGetNextHoleToFill( IrpContext,
  766. Mcb,
  767. StartingVcn,
  768. DesiredEndingVcn,
  769. &VcnToFill,
  770. &ClusterCountToFill,
  771. &PrecedingLcn))) {
  772. //
  773. // Assume we will find this in the cached runs array.
  774. //
  775. FromCachedRuns = TRUE;
  776. //
  777. // If this is our first time through the loop then record out bitmap stats
  778. // then always bump up the run count stat.
  779. //
  780. if (!ClustersAllocated) {
  781. CollectAllocateClusterStats( Vcb,
  782. RemainingDesiredClusterCount,
  783. PrecedingLcn != UNUSED_LCN );
  784. }
  785. IncrementAllocateClusterStats( Vcb );
  786. //
  787. // First indicate that we haven't found anything suitable yet
  788. //
  789. FoundClustersToAllocate = FALSE;
  790. //
  791. // Remember that we are will be allocating clusters.
  792. //
  793. ClustersAllocated = TRUE;
  794. //
  795. // Initialize HintLcn to a value that sorts lower than any other
  796. // Lcn. If we have no PrecedingLcn to use as a hint, the
  797. // allocation will preferentially use an Lcn that is as small
  798. // as possible for the desired cluster count. This will left
  799. // pack things as much as possible.
  800. //
  801. HintLcn = UNUSED_LCN;
  802. //
  803. // Check if the preceding lcn is anything other than -1 then with
  804. // that as a hint check if we have a cache hit on a free run
  805. //
  806. if (PrecedingLcn != UNUSED_LCN) {
  807. if (NtfsLookupCachedLcn( &Vcb->CachedRuns,
  808. PrecedingLcn + 1,
  809. &FoundLcn,
  810. &FoundClusterCount,
  811. NULL )) {
  812. //
  813. // Increment the stats and say we've found something to allocate
  814. //
  815. IncrementHintHonoredStats( Vcb, MIN3(FoundClusterCount, RemainingDesiredClusterCount, ClusterCountToFill));
  816. #ifdef NTFS_FRAGMENT_DISK
  817. if (NtfsFragmentMft &&
  818. (Scb == Vcb->MftScb) &&
  819. (FoundClusterCount > 1)) {
  820. FoundLcn += 1;
  821. FoundClusterCount -= 1;
  822. }
  823. #endif
  824. if ((Scb->AttributeTypeCode == $INDEX_ALLOCATION) &&
  825. (FoundClusterCount * Vcb->BytesPerCluster < Scb->ScbType.Index.BytesPerIndexBuffer)) {
  826. } else {
  827. FoundClustersToAllocate = TRUE;
  828. }
  829. }
  830. if (!FoundClustersToAllocate && !ExtendingMft ) {
  831. //
  832. // Set up the hint LCN for the lookup by length
  833. // call below.
  834. //
  835. HintLcn = PrecedingLcn + 1;
  836. }
  837. }
  838. //
  839. // If we are still looking to allocate something then hit the cache.
  840. // Skip this for the Mft zone as we are willing to go to disk for it.
  841. //
  842. while (!FoundClustersToAllocate &&
  843. !ExtendingMft &&
  844. NtfsLookupCachedLcnByLength( &Vcb->CachedRuns,
  845. RemainingDesiredClusterCount,
  846. (BOOLEAN)(Scb->AttributeTypeCode != $INDEX_ALLOCATION),
  847. HintLcn,
  848. &FoundLcn,
  849. &FoundClusterCount,
  850. &RunIndex )) {
  851. if ((FoundLcn < Vcb->MftZoneEnd) &&
  852. ((FoundLcn + FoundClusterCount) > Vcb->MftZoneStart)) {
  853. //
  854. // This run overlaps the Mft zone. Remove the zone from
  855. // the cache.
  856. //
  857. NtfsRemoveCachedLcn( &Vcb->CachedRuns,
  858. Vcb->MftZoneStart,
  859. Vcb->MftZoneEnd - Vcb->MftZoneStart );
  860. //
  861. // Retry the lookup.
  862. //
  863. continue;
  864. }
  865. //
  866. // This run will do.
  867. //
  868. FoundClustersToAllocate = TRUE;
  869. }
  870. //
  871. // this code tries to prevent the paging file allocations
  872. // from becoming fragmented.
  873. //
  874. // if the clusters we just found are smaller than half
  875. // the of the remaining cluster to allocate then we force
  876. // a look at the bitmap.
  877. //
  878. if (FlagOn( Scb->Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
  879. FoundClustersToAllocate &&
  880. FoundClusterCount < (RemainingDesiredClusterCount >> 1)) {
  881. if (LargestBitmapClusterCount > 0) {
  882. if (LargestBitmapClusterCount >= RemainingDesiredClusterCount) {
  883. FoundClustersToAllocate = FALSE;
  884. }
  885. } else {
  886. FoundClustersToAllocate = FALSE;
  887. }
  888. }
  889. //
  890. // Check if we've allocated from our cache and increment the stats
  891. //
  892. if (FoundClustersToAllocate) {
  893. IncrementCacheHitStats( Vcb,
  894. MIN3( FoundClusterCount,
  895. RemainingDesiredClusterCount,
  896. ClusterCountToFill ));
  897. //
  898. // We've done everything we can with the cached bitmap information so
  899. // now bite the bullet and scan the bitmap for a free cluster. If
  900. // we have an hint lcn then use it otherwise use the hint stored in the
  901. // vcb. But never use a hint that is part of the mft zone, and because
  902. // the mft always has a preceding lcn we know we'll hint in the zone
  903. // for the mft.
  904. //
  905. } else {
  906. BOOLEAN AllocatedFromZone;
  907. BOOLEAN ReturnAnyLength;
  908. //
  909. // The clusters aren't coming from the cached runs array.
  910. //
  911. FromCachedRuns = FALSE;
  912. //
  913. // First check if we have already satisfied the core requirements
  914. // and are now just going for the desired ending vcn. If so then
  915. // we will not waste time hitting the disk
  916. //
  917. if (StartingVcn > EndingVcn) {
  918. //
  919. // Set the loop count to MAXIMUM_RUNS_AT_ONCE to indicate we bailed early
  920. // without finding all of the requested clusters.
  921. //
  922. LoopCount = MAXIMUM_RUNS_AT_ONCE;
  923. break;
  924. }
  925. if (PrecedingLcn != UNUSED_LCN) {
  926. HintLcn = PrecedingLcn + 1;
  927. ReturnAnyLength = TRUE;
  928. } else {
  929. //
  930. // We shouldn't be here if we are extending the Mft.
  931. //
  932. ASSERT( !ExtendingMft );
  933. HintLcn = Vcb->LastBitmapHint;
  934. ReturnAnyLength = FALSE;
  935. if ((HintLcn >= Vcb->MftZoneStart) &&
  936. (HintLcn < Vcb->MftZoneEnd)) {
  937. HintLcn = Vcb->MftZoneEnd;
  938. }
  939. }
  940. AllocatedFromZone = NtfsFindFreeBitmapRun( IrpContext,
  941. Vcb,
  942. ClusterCountToFill,
  943. HintLcn,
  944. ReturnAnyLength,
  945. ExtendingMft,
  946. &FoundLcn,
  947. &FoundClusterCount );
  948. if (LargestBitmapClusterCount == 0) {
  949. //
  950. // remember the first cluster count that we get from
  951. // the bitmap as this will be the largest. this is used
  952. // to optimize the pagefile case.
  953. //
  954. LargestBitmapClusterCount = FoundClusterCount;
  955. }
  956. AllocateFromBitmap = TRUE;
  957. IncrementCacheMissStats(Vcb, MIN3(FoundClusterCount, RemainingDesiredClusterCount, ClusterCountToFill));
  958. if (FoundClusterCount == 0) {
  959. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  960. }
  961. //
  962. // Check if we need to reduce the zone.
  963. //
  964. if (!ExtendingMft) {
  965. if (AllocatedFromZone) {
  966. //
  967. // If there is space to reduce the zone then do so now
  968. // and rescan the bitmap.
  969. //
  970. if (NtfsReduceMftZone( IrpContext, Vcb )) {
  971. FoundClusterCount = 0;
  972. NtfsFindFreeBitmapRun( IrpContext,
  973. Vcb,
  974. ClusterCountToFill,
  975. Vcb->MftZoneEnd,
  976. FALSE,
  977. FALSE,
  978. &FoundLcn,
  979. &FoundClusterCount );
  980. if (FoundClusterCount == 0) {
  981. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  982. }
  983. }
  984. }
  985. //
  986. // We are extending the Mft. If we didn't get a contiguous run then
  987. // set up a new zone.
  988. //
  989. } else if (PrecedingLcn + 1 != FoundLcn) {
  990. NtfsScanEntireBitmap( IrpContext, Vcb, TRUE );
  991. ASSERT( Vcb->CachedRuns.Used != 0 );
  992. FoundLcn = NtfsInitializeMftZone( IrpContext, Vcb );
  993. NtfsFindFreeBitmapRun( IrpContext,
  994. Vcb,
  995. ClusterCountToFill,
  996. FoundLcn,
  997. TRUE,
  998. TRUE,
  999. &FoundLcn,
  1000. &FoundClusterCount );
  1001. }
  1002. }
  1003. //
  1004. // At this point we have found a run to allocate denoted by the
  1005. // values in FoundLcn and FoundClusterCount. We need to trim back
  1006. // the cluster count to be the amount we really need and then
  1007. // do the allocation. To do the allocation we zap the bitmap,
  1008. // decrement the free count, and add the run to the mcb we're
  1009. // using
  1010. //
  1011. #ifdef NTFS_FRAGMENT_DISK
  1012. if (NtfsFragmentDisk && ((ULONG) FoundClusterCount > NtfsFragmentLength)) {
  1013. FoundLcn += 1;
  1014. FoundClusterCount = NtfsFragmentLength;
  1015. } else if (NtfsFragmentMft &&
  1016. (Scb == Vcb->MftScb) &&
  1017. (FoundClusterCount > NtfsFragmentLength)) {
  1018. FoundLcn += 1;
  1019. FoundClusterCount = NtfsFragmentLength;
  1020. }
  1021. #endif
  1022. if (FoundClusterCount > RemainingDesiredClusterCount) {
  1023. FoundClusterCount = RemainingDesiredClusterCount;
  1024. }
  1025. if (FoundClusterCount > ClusterCountToFill) {
  1026. FoundClusterCount = ClusterCountToFill;
  1027. }
  1028. ASSERT( Vcb->FreeClusters >= FoundClusterCount );
  1029. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MODIFIED_BITMAP );
  1030. Defragment:
  1031. NtfsAllocateBitmapRun( IrpContext, Vcb, FoundLcn, FoundClusterCount, FromCachedRuns );
  1032. //
  1033. // Modify the total allocated for this file.
  1034. //
  1035. NtfsAcquireReservedClusters( Vcb );
  1036. Scb->TotalAllocated += (LlBytesFromClusters( Vcb, FoundClusterCount ));
  1037. NtfsReleaseReservedClusters( Vcb );
  1038. //
  1039. // Adjust the count of free clusters. Only store the change in
  1040. // the top level irp context in case of aborts.
  1041. //
  1042. Vcb->FreeClusters -= FoundClusterCount;
  1043. IrpContext->FreeClusterChange -= FoundClusterCount;
  1044. ASSERT_LCN_RANGE_CHECKING( Vcb, (FoundLcn + FoundClusterCount) );
  1045. ASSERT( FoundClusterCount != 0 );
  1046. NtfsAddNtfsMcbEntry( Mcb, VcnToFill, FoundLcn, FoundClusterCount, FALSE );
  1047. //
  1048. // If this is the Mft file then put these into our AddedClusters Mcb
  1049. // as well.
  1050. //
  1051. if (Mcb == &Vcb->MftScb->Mcb) {
  1052. FsRtlAddLargeMcbEntry( &Vcb->MftScb->ScbType.Mft.AddedClusters,
  1053. VcnToFill,
  1054. FoundLcn,
  1055. FoundClusterCount );
  1056. }
  1057. //
  1058. // And update the last bitmap hint, but only if we used the hint to begin with
  1059. //
  1060. if (PrecedingLcn == UNUSED_LCN) {
  1061. Vcb->LastBitmapHint = FoundLcn;
  1062. }
  1063. //
  1064. // Now move the starting Vcn to the Vcn that we've just filled plus the
  1065. // found cluster count
  1066. //
  1067. StartingVcn = VcnToFill + FoundClusterCount;
  1068. //
  1069. // Decrement the remaining desired cluster count by the amount we just allocated
  1070. //
  1071. RemainingDesiredClusterCount = RemainingDesiredClusterCount - FoundClusterCount;
  1072. LoopCount += 1;
  1073. RunCount += 1;
  1074. if (FoundRun == TRUE) {
  1075. break;
  1076. }
  1077. }
  1078. //
  1079. // Now we need to compute the total cluster that we've just allocated
  1080. // We'll call get next hole to fill. If the result is false then we
  1081. // allocated everything. If the result is true then we do some quick
  1082. // math to get the size allocated
  1083. //
  1084. if (GotAHoleToFill && NtfsGetNextHoleToFill( IrpContext,
  1085. Mcb,
  1086. OriginalStartingVcn,
  1087. DesiredEndingVcn,
  1088. &VcnToFill,
  1089. &ClusterCountToFill,
  1090. &PrecedingLcn)) {
  1091. //
  1092. // If this is a sparse file and we didn't get all that we asked for
  1093. // then trim the allocation back to a compression boundary.
  1094. //
  1095. if ((LoopCount >= MAXIMUM_RUNS_AT_ONCE) &&
  1096. !AllocateAll &&
  1097. (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ) == ATTRIBUTE_FLAG_SPARSE )) {
  1098. ULONG ClustersPerCompressionMask;
  1099. ClustersPerCompressionMask = (1 << Scb->CompressionUnitShift) - 1;
  1100. //
  1101. // We should end on a compression unit boundary.
  1102. //
  1103. if ((ULONG) VcnToFill & ClustersPerCompressionMask) {
  1104. //
  1105. // Back up to a compression unit boundary.
  1106. //
  1107. StartingVcn = VcnToFill & ~((LONGLONG) ClustersPerCompressionMask);
  1108. ASSERT( StartingVcn > OriginalStartingVcn );
  1109. NtfsDeallocateClusters( IrpContext,
  1110. Vcb,
  1111. Scb,
  1112. StartingVcn,
  1113. VcnToFill - 1,
  1114. &Scb->TotalAllocated );
  1115. //
  1116. // We don't want these clusters to be reflected in the clusters
  1117. // deallocated for this transaction. Otherwise our caller may
  1118. // assume he can get them with a log file full.
  1119. //
  1120. IrpContext->DeallocatedClusters -= (VcnToFill - StartingVcn);
  1121. VcnToFill = StartingVcn;
  1122. }
  1123. }
  1124. *DesiredClusterCount = VcnToFill - OriginalStartingVcn;
  1125. }
  1126. //
  1127. // At this point we've allocated everything we were asked to do
  1128. // so now call a routine to read ahead into our cache the disk
  1129. // information at the last lcn we allocated. But only do the readahead
  1130. // if we allocated clusters and we couldn't satisfy the request in one
  1131. // run.
  1132. //
  1133. if (ClustersAllocated &&
  1134. ((RunCount > 1) || AllocateFromBitmap) &&
  1135. (FoundLcn + FoundClusterCount < Vcb->TotalClusters)) {
  1136. NtfsReadAheadCachedBitmap( IrpContext, Vcb, FoundLcn + FoundClusterCount );
  1137. }
  1138. } finally {
  1139. DebugUnwind( NtfsAllocateClusters );
  1140. DebugTrace( 0, Dbg, ("%d\n", NtfsDumpCachedMcbInformation(Vcb)) );
  1141. NtfsReleaseScb(IrpContext, Vcb->BitmapScb);
  1142. }
  1143. DebugTrace( -1, Dbg, ("NtfsAllocateClusters -> %08lx\n", ClustersAllocated) );
  1144. return ClustersAllocated;
  1145. }
  1146. VOID
  1147. NtfsAddBadCluster (
  1148. IN PIRP_CONTEXT IrpContext,
  1149. IN PVCB Vcb,
  1150. IN LCN Lcn
  1151. )
  1152. /*++
  1153. Routine Description:
  1154. This routine helps append a bad cluster to the bad cluster file.
  1155. It marks it as allocated in the volume bitmap and also adds
  1156. the Lcn to the MCB for the bad cluster file.
  1157. Arguments:
  1158. Vcb - Supplies the Vcb used in this operation
  1159. Lcn - Supplies the Lcn of the new bad cluster
  1160. Return:
  1161. None.
  1162. --*/
  1163. {
  1164. PNTFS_MCB Mcb;
  1165. LONGLONG FoundLcn;
  1166. LONGLONG FoundClusters;
  1167. PDEALLOCATED_CLUSTERS Clusters;
  1168. ASSERT_IRP_CONTEXT( IrpContext );
  1169. ASSERT_VCB( Vcb );
  1170. PAGED_CODE();
  1171. DebugTrace( +1, Dbg, ("NtfsAddBadCluster\n") );
  1172. DebugTrace( 0, Dbg, ("Lcn = %0I64x\n", Lcn) );
  1173. //
  1174. // Reference the bad cluster mcb and grab exclusive access to the
  1175. // bitmap scb
  1176. //
  1177. Mcb = &Vcb->BadClusterFileScb->Mcb;
  1178. NtfsAcquireExclusiveScb( IrpContext, Vcb->BitmapScb );
  1179. try {
  1180. //
  1181. // We are given the bad Lcn so all we need to do is
  1182. // allocate it in the bitmap, and take care of some
  1183. // bookkeeping
  1184. //
  1185. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MODIFIED_BITMAP );
  1186. NtfsAllocateBitmapRun( IrpContext, Vcb, Lcn, 1, FALSE );
  1187. //
  1188. // Go ahead and remove this cluster from the recently deallocated arrays.
  1189. // We don't want to give this back to the bitmap package.
  1190. //
  1191. // Best odds are that these are in the active deallocated clusters.
  1192. //
  1193. Clusters = (PDEALLOCATED_CLUSTERS)Vcb->DeallocatedClusterListHead.Flink;
  1194. do {
  1195. if (FsRtlLookupLargeMcbEntry( &Clusters->Mcb,
  1196. Lcn,
  1197. &FoundLcn,
  1198. &FoundClusters,
  1199. NULL,
  1200. NULL,
  1201. NULL ) &&
  1202. (FoundLcn != UNUSED_LCN)) {
  1203. FsRtlRemoveLargeMcbEntry( &Clusters->Mcb,
  1204. Lcn,
  1205. 1 );
  1206. //
  1207. // Removing one from Dealloc and Vcb. Operation above
  1208. // could fail leaving entry in Deallocated cluster. OK because the
  1209. // entry is still deallocated this operation will abort.
  1210. //
  1211. Clusters->ClusterCount -= 1;
  1212. Vcb->DeallocatedClusters -= 1;
  1213. break;
  1214. }
  1215. Clusters = (PDEALLOCATED_CLUSTERS)Clusters->Link.Flink;
  1216. } while ( &Clusters->Link != &Vcb->DeallocatedClusterListHead );
  1217. Vcb->FreeClusters -= 1;
  1218. IrpContext->FreeClusterChange -= 1;
  1219. ASSERT_LCN_RANGE_CHECKING( Vcb, (Lcn + 1) );
  1220. //
  1221. // Vcn == Lcn in the bad cluster file.
  1222. //
  1223. NtfsAddNtfsMcbEntry( Mcb, Lcn, Lcn, (LONGLONG)1, FALSE );
  1224. } finally {
  1225. DebugUnwind( NtfsAddBadCluster );
  1226. NtfsReleaseScb(IrpContext, Vcb->BitmapScb);
  1227. }
  1228. DebugTrace( -1, Dbg, ("NtfsAddBadCluster -> VOID\n") );
  1229. return;
  1230. }
  1231. BOOLEAN
  1232. NtfsDeallocateClusters (
  1233. IN PIRP_CONTEXT IrpContext,
  1234. IN PVCB Vcb,
  1235. IN PSCB Scb,
  1236. IN VCN StartingVcn,
  1237. IN VCN EndingVcn,
  1238. OUT PLONGLONG TotalAllocated OPTIONAL
  1239. )
  1240. /*++
  1241. Routine Description:
  1242. This routine deallocates (i.e., frees) disk space. It free any clusters that
  1243. are specified as allocated in the input mcb with the specified range of starting
  1244. vcn to ending vcn inclusive.
  1245. The basic algorithm used by this procedure is as follows:
  1246. 1. With a Vcn value beginning at starting vcn and progressing to ending vcn
  1247. do the following steps...
  1248. 2. Lookup the Mcb entry at the vcn this will yield an lcn and a cluster count
  1249. if the entry exists (even if it is a hole). If the entry does not exist
  1250. then we are completely done because we have run off the end of allocation.
  1251. 3. If the entry is a hole (i.e., Lcn == -1) then add the cluster count to
  1252. Vcn and go back to step 1.
  1253. 4. At this point we have a real run of clusters that need to be deallocated but
  1254. the cluster count might put us over the ending vcn so adjust the cluster
  1255. count to keep us within the ending vcn.
  1256. 5. Now deallocate the clusters from the bitmap, and increment the free cluster
  1257. count stored in the vcb.
  1258. 6. Add (i.e., change) any cached bitmap information concerning this run to indicate
  1259. that it is now free.
  1260. 7. Remove the run from the mcb.
  1261. 8. Add the cluster count that we've just freed to Vcn and go back to step 1.
  1262. Arguments:
  1263. Vcb - Supplies the vcb used in this operation
  1264. Mcb - Supplies the mcb describing the runs to be deallocated
  1265. StartingVcn - Supplies the vcn to start deallocating at in the input mcb
  1266. EndingVcn - Supplies the vcn to end deallocating at in the input mcb
  1267. TotalAllocated - If specified we will modifify the total allocated clusters
  1268. for this file.
  1269. Return Value:
  1270. FALSE - if nothing was deallocated.
  1271. TRUE - if some space was deallocated.
  1272. --*/
  1273. {
  1274. VCN Vcn;
  1275. LCN Lcn;
  1276. LONGLONG ClusterCount;
  1277. LONGLONG ClustersRemoved = 0;
  1278. BOOLEAN ClustersDeallocated = FALSE;
  1279. LCN LastLcnAdded;
  1280. BOOLEAN RaiseLogFull;
  1281. ASSERT_IRP_CONTEXT( IrpContext );
  1282. ASSERT_VCB( Vcb );
  1283. PAGED_CODE();
  1284. DebugTrace( +1, Dbg, ("NtfsDeallocateClusters\n") );
  1285. DebugTrace( 0, Dbg, ("StartingVcn = %016I64x\n", StartingVcn) );
  1286. DebugTrace( 0, Dbg, ("EndingVcn = %016I64\n", EndingVcn) );
  1287. NtfsAcquireExclusiveScb( IrpContext, Vcb->BitmapScb );
  1288. try {
  1289. if (FlagOn( Vcb->VcbState, VCB_STATE_RELOAD_FREE_CLUSTERS )) {
  1290. NtfsScanEntireBitmap( IrpContext, Vcb, FALSE );
  1291. }
  1292. //
  1293. // The following loop scans through the mcb from starting vcn to ending vcn
  1294. // with a step of cluster count.
  1295. //
  1296. for (Vcn = StartingVcn; Vcn <= EndingVcn; Vcn = Vcn + ClusterCount) {
  1297. //
  1298. // Get the run information from the Mcb, and if this Vcn isn't specified
  1299. // in the mcb then return now to our caller
  1300. //
  1301. if (!NtfsLookupNtfsMcbEntry( &Scb->Mcb, Vcn, &Lcn, &ClusterCount, NULL, NULL, NULL, NULL )) {
  1302. try_return( NOTHING );
  1303. }
  1304. //
  1305. // Make sure that the run we just looked up is not a hole otherwise
  1306. // if it is a hole we'll just continue with out loop continue with our
  1307. // loop
  1308. //
  1309. if (Lcn != UNUSED_LCN) {
  1310. PDEALLOCATED_CLUSTERS CurrentClusters;
  1311. ASSERT_LCN_RANGE_CHECKING( Vcb, (Lcn + ClusterCount) );
  1312. //
  1313. // Now we have a real run to deallocate, but it might be too large
  1314. // to check for that the vcn plus cluster count must be less than
  1315. // or equal to the ending vcn plus 1.
  1316. //
  1317. if ((Vcn + ClusterCount) > EndingVcn) {
  1318. ClusterCount = (EndingVcn - Vcn) + 1;
  1319. }
  1320. //
  1321. // And to hold us off from reallocating the clusters right away we'll
  1322. // add this run to the recently deallocated mcb in the vcb. If this fails
  1323. // because we are growing the mapping then change the code to
  1324. // LOG_FILE_FULL to empty the mcb.
  1325. //
  1326. RaiseLogFull = FALSE;
  1327. try {
  1328. CurrentClusters = NtfsGetDeallocatedClusters( IrpContext, Vcb );
  1329. FsRtlAddLargeMcbEntry( &CurrentClusters->Mcb,
  1330. Lcn,
  1331. Lcn,
  1332. ClusterCount );
  1333. } except ((GetExceptionCode() == STATUS_INSUFFICIENT_RESOURCES) ?
  1334. EXCEPTION_EXECUTE_HANDLER :
  1335. EXCEPTION_CONTINUE_SEARCH) {
  1336. RaiseLogFull = TRUE;
  1337. }
  1338. if (RaiseLogFull) {
  1339. NtfsRaiseStatus( IrpContext, STATUS_LOG_FILE_FULL, NULL, NULL );
  1340. }
  1341. //
  1342. // Correct here because we increment only if successfully
  1343. // adding the clusters. It is also added to dealloc and Vcb together.
  1344. //
  1345. CurrentClusters->ClusterCount += ClusterCount;
  1346. Vcb->DeallocatedClusters += ClusterCount;
  1347. IrpContext->DeallocatedClusters += ClusterCount;
  1348. ClustersRemoved = ClusterCount;
  1349. LastLcnAdded = Lcn + ClusterCount;
  1350. //
  1351. // Now zap the bitmap, increment the free cluster count, and change
  1352. // the cached information on this run to indicate that it is now free
  1353. //
  1354. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MODIFIED_BITMAP );
  1355. NtfsFreeBitmapRun( IrpContext, Vcb, Lcn, &ClustersRemoved);
  1356. ASSERT( ClustersRemoved == 0 );
  1357. ClustersDeallocated = TRUE;
  1358. //
  1359. // Reserve newly freed clusters if necc. to maintain balance for
  1360. // mapped data files
  1361. //
  1362. if (($DATA == Scb->AttributeTypeCode) &&
  1363. (Scb->CompressionUnit != 0) &&
  1364. FlagOn( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE )) {
  1365. LONGLONG FileOffset;
  1366. ULONG ByteCount;
  1367. LONGLONG TempL;
  1368. TempL= NtfsCalculateNeededReservedSpace( Scb );
  1369. if (Scb->ScbType.Data.TotalReserved <= TempL) {
  1370. FileOffset = LlBytesFromClusters( Vcb, Vcn );
  1371. ByteCount = BytesFromClusters( Vcb, ClusterCount );
  1372. //
  1373. // If we're deallocating beyond allocation size as a result of DeallocateInternal
  1374. // optimization (split and remove at back) compensate.
  1375. //
  1376. if (FileOffset >= Scb->Header.AllocationSize.QuadPart ) {
  1377. FileOffset = Scb->Header.AllocationSize.QuadPart - ByteCount;
  1378. }
  1379. //
  1380. // Round attempted reservation down to needed amount if its larger
  1381. //
  1382. if (ByteCount > TempL - Scb->ScbType.Data.TotalReserved) {
  1383. ByteCount = (ULONG)(TempL - Scb->ScbType.Data.TotalReserved);
  1384. }
  1385. NtfsReserveClusters( IrpContext, Scb, FileOffset, ByteCount );
  1386. }
  1387. }
  1388. //
  1389. // Adjust the count of free clusters and adjust the IrpContext
  1390. // field for the change this transaction.
  1391. //
  1392. Vcb->FreeClusters += ClusterCount;
  1393. //
  1394. // If we had shrunk the Mft zone and there is at least 1/16
  1395. // of the volume now available, then grow the zone back.
  1396. // Acquire MftScb so we can can manipulate its mcb. Use ex routines so we
  1397. // always drop it at the end in the finally clause. If we can't get it
  1398. // we'll just skip resizing the zone
  1399. //
  1400. if (FlagOn( Vcb->VcbState, VCB_STATE_REDUCED_MFT ) &&
  1401. (Int64ShraMod32( Vcb->TotalClusters, 4 ) < Vcb->FreeClusters)) {
  1402. if (NtfsAcquireResourceExclusive( IrpContext, Vcb->MftScb, FALSE )) {
  1403. try {
  1404. NtfsScanEntireBitmap( IrpContext, Vcb, TRUE );
  1405. NtfsInitializeMftZone( IrpContext, Vcb );
  1406. } finally {
  1407. NtfsReleaseResource( IrpContext, Vcb->MftScb );
  1408. }
  1409. }
  1410. }
  1411. IrpContext->FreeClusterChange += ClusterCount;
  1412. //
  1413. // Modify the total allocated amount if the pointer is specified.
  1414. //
  1415. if (ARGUMENT_PRESENT( TotalAllocated )) {
  1416. NtfsAcquireReservedClusters( Vcb );
  1417. *TotalAllocated -= (LlBytesFromClusters( Vcb, ClusterCount ));
  1418. if (*TotalAllocated < 0) {
  1419. *TotalAllocated = 0;
  1420. }
  1421. NtfsReleaseReservedClusters( Vcb );
  1422. }
  1423. //
  1424. // Now remove this entry from the mcb and go back to the top of the
  1425. // loop
  1426. //
  1427. NtfsRemoveNtfsMcbEntry( &Scb->Mcb, Vcn, ClusterCount );
  1428. //
  1429. // If this is the Mcb for the Mft file then remember this in the
  1430. // RemovedClusters Mcb.
  1431. //
  1432. if (&Scb->Mcb == &Vcb->MftScb->Mcb) {
  1433. FsRtlAddLargeMcbEntry( &Vcb->MftScb->ScbType.Mft.RemovedClusters,
  1434. Vcn,
  1435. Lcn,
  1436. ClusterCount );
  1437. }
  1438. }
  1439. }
  1440. try_exit: NOTHING;
  1441. } finally {
  1442. DebugUnwind( NtfsDeallocateClusters );
  1443. DebugTrace( 0, Dbg, ("%d\n", NtfsDumpCachedMcbInformation(Vcb)) );
  1444. //
  1445. // Remove the entries from the recently deallocated entries
  1446. // if we didn't modify the bitmap. ClustersRemoved contains
  1447. // the number we didn't insert in the last attempt to free bits
  1448. // in the bitmap.
  1449. //
  1450. if (ClustersRemoved != 0) {
  1451. PDEALLOCATED_CLUSTERS Clusters = (PDEALLOCATED_CLUSTERS) Vcb->DeallocatedClusterListHead.Flink;
  1452. FsRtlRemoveLargeMcbEntry( &Clusters->Mcb,
  1453. LastLcnAdded - ClustersRemoved,
  1454. ClustersRemoved );
  1455. //
  1456. // This should be OK. We are backing out an insert above.
  1457. // Whatever space needed should be present because we are reverting to
  1458. // a known state.
  1459. //
  1460. Clusters->ClusterCount -= ClustersRemoved;
  1461. Vcb->DeallocatedClusters -= ClustersRemoved;
  1462. }
  1463. NtfsReleaseScb( IrpContext, Vcb->BitmapScb );
  1464. }
  1465. DebugTrace( -1, Dbg, ("NtfsDeallocateClusters -> %02lx\n", ClustersDeallocated) );
  1466. return ClustersDeallocated;
  1467. }
  1468. VOID
  1469. NtfsPreAllocateClusters (
  1470. IN PIRP_CONTEXT IrpContext,
  1471. IN PVCB Vcb,
  1472. IN LCN StartingLcn,
  1473. IN LONGLONG ClusterCount,
  1474. OUT PBOOLEAN AcquiredBitmap,
  1475. OUT PBOOLEAN AcquiredMft
  1476. )
  1477. /*++
  1478. Routine Description:
  1479. This routine pre-allocates clusters in the bitmap within the specified range.
  1480. All changes are made only in memory and neither logged nor written to disk.
  1481. We allow exceptions to flow out possibly with all the files acquired. At the end we hold
  1482. the bitmap and mft exclusive to mark the reservation if we succeed
  1483. Arguments:
  1484. Vcb - Supplies the vcb used in this operation
  1485. StartingLcn - Supplies the starting Lcn index within the bitmap to
  1486. start allocating (i.e., setting to 1).
  1487. ClusterCount - Supplies the number of bits to set to 1 within the bitmap.
  1488. AcquiredBitmap - set to true if we leave with bitmap acquired
  1489. AcquiredMft - set to true if we leave with the mft acquired
  1490. Return Value:
  1491. None.
  1492. --*/
  1493. {
  1494. PAGED_CODE()
  1495. NtfsAcquireExclusiveScb( IrpContext, Vcb->MftScb );
  1496. *AcquiredMft = TRUE;
  1497. NtfsAcquireExclusiveScb( IrpContext, Vcb->BitmapScb );
  1498. *AcquiredBitmap = TRUE;
  1499. NtfsRunIsClear( IrpContext, Vcb, StartingLcn, ClusterCount );
  1500. }
  1501. VOID
  1502. NtfsScanEntireBitmap (
  1503. IN PIRP_CONTEXT IrpContext,
  1504. IN PVCB Vcb,
  1505. IN LOGICAL CachedRunsOnly
  1506. )
  1507. /*++
  1508. Routine Description:
  1509. This routine scans in the entire bitmap, It computes the number of free clusters
  1510. available, and at the same time remembers the largest free runs that it
  1511. then inserts into the cached bitmap structure.
  1512. Arguments:
  1513. Vcb - Supplies the vcb used by this operation
  1514. CachedRunsOnly - Indicates that we only want to look for the longest runs.
  1515. Return Value:
  1516. None.
  1517. --*/
  1518. {
  1519. LCN Lcn;
  1520. RTL_BITMAP Bitmap;
  1521. PBCB BitmapBcb;
  1522. BOOLEAN StuffAdded = FALSE;
  1523. ASSERT_IRP_CONTEXT( IrpContext );
  1524. ASSERT_VCB( Vcb );
  1525. PAGED_CODE();
  1526. DebugTrace( +1, Dbg, ("NtfsScanEntireBitmap\n") );
  1527. BitmapBcb = NULL;
  1528. try {
  1529. //
  1530. // If we are only reloading cached runs then check if there is any real work to do.
  1531. // We don't want to constantly rescan the bitmap if we are growing the Mft and never
  1532. // have any suitable runs available.
  1533. //
  1534. if (CachedRunsOnly) {
  1535. USHORT RunIndex;
  1536. BOOLEAN FoundRun;
  1537. //
  1538. // If there hasn't been a lot of activity freeing clusters then
  1539. // don't do this work unless the cached run structure is empty.
  1540. //
  1541. if (Vcb->ClustersRecentlyFreed < BITMAP_VOLATILE_FREE_COUNT) {
  1542. //
  1543. // Determine if there is a cached run that is at least as
  1544. // large as LongestFreedRun.
  1545. //
  1546. FoundRun = NtfsPositionCachedLcnByLength( &Vcb->CachedRuns,
  1547. Vcb->CachedRuns.LongestFreedRun,
  1548. NULL,
  1549. NULL,
  1550. TRUE,
  1551. &RunIndex );
  1552. if (!FoundRun &&
  1553. (RunIndex < Vcb->CachedRuns.Used) &&
  1554. (Vcb->CachedRuns.LengthArray[ RunIndex ] != NTFS_CACHED_RUNS_DEL_INDEX) ) {
  1555. //
  1556. // RunIndex points to a larger entry.
  1557. //
  1558. FoundRun = TRUE;
  1559. ASSERT( FoundRun ||
  1560. (RunIndex >= Vcb->CachedRuns.Used) ||
  1561. (Vcb->CachedRuns.LengthArray[ RunIndex ] == NTFS_CACHED_RUNS_DEL_INDEX) );
  1562. }
  1563. if (FoundRun) {
  1564. //
  1565. // Use the entries we already have.
  1566. //
  1567. leave;
  1568. }
  1569. }
  1570. //
  1571. // Set the current total free space to zero and the following loop will compute
  1572. // the actual number of free clusters.
  1573. //
  1574. } else {
  1575. Vcb->FreeClusters = 0;
  1576. }
  1577. NtfsReinitializeCachedRuns( &Vcb->CachedRuns );
  1578. //
  1579. // For every bitmap page we read it in and check how many free clusters there are.
  1580. // While we have the page in memory we also scan for a large chunks of free space.
  1581. //
  1582. for (Lcn = 0; Lcn < Vcb->TotalClusters; Lcn = Lcn + Bitmap.SizeOfBitMap) {
  1583. LCN StartingLcn;
  1584. RTL_BITMAP_RUN RunArray[64];
  1585. ULONG RunArrayIndex;
  1586. //
  1587. // Read in the bitmap page and make sure that we haven't messed up the math
  1588. //
  1589. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE; }
  1590. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  1591. NtfsMapPageInBitmap( IrpContext, Vcb, Lcn, &StartingLcn, &Bitmap, &BitmapBcb );
  1592. ASSERTMSG("Math wrong for bits per page of bitmap", (Lcn == StartingLcn));
  1593. //
  1594. // Compute the number of clear bits in the bitmap each clear bit denotes
  1595. // a free cluster.
  1596. //
  1597. if (!CachedRunsOnly) {
  1598. Vcb->FreeClusters += RtlNumberOfClearBits( &Bitmap );
  1599. }
  1600. //
  1601. // Now bias the bitmap with the RecentlyDeallocatedMcb.
  1602. //
  1603. StuffAdded = NtfsAddRecentlyDeallocated( Vcb, StartingLcn, &Bitmap );
  1604. //
  1605. // Find the 64 longest free runs in the bitmap and add them to the
  1606. // cached bitmap.
  1607. //
  1608. RunArrayIndex = RtlFindClearRuns( &Bitmap, RunArray, 64, TRUE );
  1609. if (RunArrayIndex > 0) {
  1610. NtfsAddCachedRunMult( IrpContext,
  1611. Vcb,
  1612. Lcn,
  1613. RunArray,
  1614. RunArrayIndex );
  1615. }
  1616. }
  1617. Vcb->ClustersRecentlyFreed = 0;
  1618. Vcb->CachedRuns.LongestFreedRun = 0;
  1619. } finally {
  1620. DebugUnwind( NtfsScanEntireBitmap );
  1621. if (!AbnormalTermination() && !CachedRunsOnly) {
  1622. ClearFlag( Vcb->VcbState, VCB_STATE_RELOAD_FREE_CLUSTERS );
  1623. }
  1624. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); }
  1625. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  1626. }
  1627. DebugTrace( -1, Dbg, ("NtfsScanEntireBitmap -> VOID\n") );
  1628. return;
  1629. }
  1630. VOID
  1631. NtfsModifyBitsInBitmap (
  1632. IN PIRP_CONTEXT IrpContext,
  1633. IN PVCB Vcb,
  1634. IN LONGLONG FirstBit,
  1635. IN LONGLONG BeyondFinalBit,
  1636. IN ULONG RedoOperation,
  1637. IN ULONG UndoOperation
  1638. )
  1639. /*++
  1640. Routine Description:
  1641. This routine is called to directly modify a specific range of bits in the volume bitmap.
  1642. It should only be called by someone who is directly manipulating the volume bitmap
  1643. (i.e. ExtendVolume).
  1644. Arguments:
  1645. Vcb - This is the volume being modified.
  1646. FirstBit - First bit in the bitmap to set.
  1647. BeyondFinalBit - Indicates where to stop modifying.
  1648. RedoOperation - Indicates whether we are setting or clearing the bits.
  1649. UndoOperation - Indicates whether we need to back out the Redo operation above.
  1650. Return Value:
  1651. None.
  1652. --*/
  1653. {
  1654. RTL_BITMAP Bitmap;
  1655. PBCB BitmapBcb = NULL;
  1656. LONGLONG CurrentLcn;
  1657. LONGLONG BaseLcn;
  1658. BITMAP_RANGE BitmapRange;
  1659. PVOID UndoBuffer = NULL;
  1660. ULONG UndoBufferLength = 0;
  1661. PAGED_CODE();
  1662. //
  1663. // Use a try-finally to facilate cleanup.
  1664. //
  1665. try {
  1666. //
  1667. // Loop and perform the necessary operations on each affected page
  1668. // in the bitmap.
  1669. //
  1670. for (CurrentLcn = FirstBit; CurrentLcn < BeyondFinalBit; CurrentLcn = BaseLcn + Bitmap.SizeOfBitMap) {
  1671. //
  1672. // Read in the page of the bitmap.
  1673. //
  1674. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  1675. NtfsPinPageInBitmap( IrpContext, Vcb, CurrentLcn, &BaseLcn, &Bitmap, &BitmapBcb );
  1676. //
  1677. // Determine how many bits to clear on the current page.
  1678. //
  1679. BitmapRange.BitMapOffset = (ULONG) (CurrentLcn - BaseLcn);
  1680. BitmapRange.NumberOfBits = BITS_PER_PAGE - BitmapRange.BitMapOffset;
  1681. if (BitmapRange.NumberOfBits > (ULONG) (BeyondFinalBit - CurrentLcn)) {
  1682. BitmapRange.NumberOfBits = (ULONG) (BeyondFinalBit - CurrentLcn);
  1683. }
  1684. //
  1685. // Write the log record to clear or set the bits.
  1686. //
  1687. if (UndoOperation != Noop) {
  1688. ASSERT( (UndoOperation == SetBitsInNonresidentBitMap) ||
  1689. (UndoOperation == ClearBitsInNonresidentBitMap) );
  1690. UndoBuffer = &BitmapRange;
  1691. UndoBufferLength = sizeof( BITMAP_RANGE );
  1692. }
  1693. (VOID)
  1694. NtfsWriteLog( IrpContext,
  1695. Vcb->BitmapScb,
  1696. BitmapBcb,
  1697. RedoOperation,
  1698. &BitmapRange,
  1699. sizeof( BITMAP_RANGE ),
  1700. UndoOperation,
  1701. UndoBuffer,
  1702. UndoBufferLength,
  1703. Int64ShraMod32( BaseLcn, 3 ),
  1704. 0,
  1705. 0,
  1706. Bitmap.SizeOfBitMap >> 3 );
  1707. //
  1708. // Call the appropriate routine to modify the bits.
  1709. //
  1710. if (RedoOperation == SetBitsInNonresidentBitMap) {
  1711. NtfsRestartSetBitsInBitMap( IrpContext,
  1712. &Bitmap,
  1713. BitmapRange.BitMapOffset,
  1714. BitmapRange.NumberOfBits );
  1715. } else {
  1716. NtfsRestartClearBitsInBitMap( IrpContext,
  1717. &Bitmap,
  1718. BitmapRange.BitMapOffset,
  1719. BitmapRange.NumberOfBits );
  1720. }
  1721. }
  1722. } finally {
  1723. DebugUnwind( NtfsModifyBitsInBitmap );
  1724. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  1725. }
  1726. return;
  1727. }
  1728. BOOLEAN
  1729. NtfsCreateMftHole (
  1730. IN PIRP_CONTEXT IrpContext,
  1731. IN PVCB Vcb
  1732. )
  1733. /*++
  1734. Routine Description:
  1735. This routine is called to create a hole within the Mft.
  1736. Arguments:
  1737. Vcb - Vcb for volume.
  1738. Return Value:
  1739. None.
  1740. --*/
  1741. {
  1742. BOOLEAN FoundHole = FALSE;
  1743. PBCB BitmapBcb = NULL;
  1744. BOOLEAN StuffAdded = FALSE;
  1745. RTL_BITMAP Bitmap;
  1746. PUCHAR BitmapBuffer;
  1747. ULONG SizeToMap;
  1748. ULONG BitmapOffset;
  1749. ULONG BitmapSize;
  1750. ULONG BitmapIndex;
  1751. ULONG StartIndex;
  1752. ULONG HoleCount;
  1753. ULONG MftVcn;
  1754. ULONG MftClusterCount;
  1755. PAGED_CODE();
  1756. //
  1757. // Use a try-finally to facilitate cleanup.
  1758. //
  1759. try {
  1760. //
  1761. // Compute the number of records in the Mft file and the full range to
  1762. // pin in the Mft bitmap.
  1763. //
  1764. BitmapIndex = (ULONG) LlFileRecordsFromBytes( Vcb, Vcb->MftScb->Header.FileSize.QuadPart );
  1765. //
  1766. // Knock this index down to a hole boundary.
  1767. //
  1768. BitmapIndex &= Vcb->MftHoleInverseMask;
  1769. //
  1770. // Compute the values for the bitmap.
  1771. //
  1772. BitmapSize = (BitmapIndex + 7) / 8;
  1773. //
  1774. // Convert the index to the number of bits on this page.
  1775. //
  1776. BitmapIndex &= (BITS_PER_PAGE - 1);
  1777. if (BitmapIndex == 0) {
  1778. BitmapIndex = BITS_PER_PAGE;
  1779. }
  1780. //
  1781. // Set the Vcn count to the full size of the bitmap.
  1782. //
  1783. BitmapOffset = (ULONG) ROUND_TO_PAGES( BitmapSize );
  1784. //
  1785. // Loop through all of the pages of the Mft bitmap looking for an appropriate
  1786. // hole.
  1787. //
  1788. while (BitmapOffset != 0) {
  1789. //
  1790. // Move to the beginning of this page.
  1791. //
  1792. BitmapOffset -= BITS_PER_PAGE;
  1793. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE; }
  1794. //
  1795. // Compute the number of bytes to map in the current page.
  1796. //
  1797. SizeToMap = BitmapSize - BitmapOffset;
  1798. if (SizeToMap > PAGE_SIZE) {
  1799. SizeToMap = PAGE_SIZE;
  1800. }
  1801. //
  1802. // Unmap any pages from a previous page and map the current page.
  1803. //
  1804. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  1805. //
  1806. // Initialize the bitmap for this page.
  1807. //
  1808. NtfsMapStream( IrpContext,
  1809. Vcb->MftBitmapScb,
  1810. BitmapOffset,
  1811. SizeToMap,
  1812. &BitmapBcb,
  1813. &BitmapBuffer );
  1814. RtlInitializeBitMap( &Bitmap, (PULONG) BitmapBuffer, SizeToMap * 8 );
  1815. StuffAdded = NtfsAddDeallocatedRecords( Vcb,
  1816. Vcb->MftScb,
  1817. BitmapOffset * 8,
  1818. &Bitmap );
  1819. //
  1820. // Walk through the current page looking for a hole. Continue
  1821. // until we find a hole or have reached the beginning of the page.
  1822. //
  1823. do {
  1824. //
  1825. // Go back one Mft index and look for a clear run.
  1826. //
  1827. BitmapIndex -= 1;
  1828. HoleCount = RtlFindLastBackwardRunClear( &Bitmap,
  1829. BitmapIndex,
  1830. &BitmapIndex );
  1831. //
  1832. // If we couldn't find any run then break out of the loop.
  1833. //
  1834. if (HoleCount == 0) {
  1835. break;
  1836. //
  1837. // If this is too small to make a hole then continue on.
  1838. //
  1839. } else if (HoleCount < Vcb->MftHoleGranularity) {
  1840. BitmapIndex &= Vcb->MftHoleInverseMask;
  1841. continue;
  1842. }
  1843. //
  1844. // Round up the starting index for this clear run and
  1845. // adjust the hole count.
  1846. //
  1847. StartIndex = (BitmapIndex + Vcb->MftHoleMask) & Vcb->MftHoleInverseMask;
  1848. HoleCount -= (StartIndex - BitmapIndex);
  1849. //
  1850. // Round the hole count down to a hole boundary.
  1851. //
  1852. HoleCount &= Vcb->MftHoleInverseMask;
  1853. //
  1854. // If we couldn't find enough records for a hole then
  1855. // go to a previous index.
  1856. //
  1857. if (HoleCount < Vcb->MftHoleGranularity) {
  1858. BitmapIndex &= Vcb->MftHoleInverseMask;
  1859. continue;
  1860. }
  1861. //
  1862. // Convert the hole count to a cluster count.
  1863. //
  1864. if (Vcb->FileRecordsPerCluster == 0) {
  1865. HoleCount <<= Vcb->MftToClusterShift;
  1866. } else {
  1867. HoleCount = 1;
  1868. }
  1869. //
  1870. // Loop by finding the run at the given Vcn and walk through
  1871. // subsequent runs looking for a hole.
  1872. //
  1873. do {
  1874. PVOID RangePtr;
  1875. ULONG McbIndex;
  1876. VCN ThisVcn;
  1877. LCN ThisLcn;
  1878. LONGLONG ThisClusterCount;
  1879. //
  1880. // Find the starting Vcn for this hole and initialize
  1881. // the cluster count for the current hole.
  1882. //
  1883. ThisVcn = StartIndex + (BitmapOffset * 3);
  1884. if (Vcb->FileRecordsPerCluster == 0) {
  1885. ThisVcn <<= Vcb->MftToClusterShift;
  1886. } else {
  1887. ThisVcn >>= Vcb->MftToClusterShift;
  1888. }
  1889. MftVcn = (ULONG) ThisVcn;
  1890. MftClusterCount = 0;
  1891. //
  1892. // Lookup the run at the current Vcn.
  1893. //
  1894. NtfsLookupNtfsMcbEntry( &Vcb->MftScb->Mcb,
  1895. ThisVcn,
  1896. &ThisLcn,
  1897. &ThisClusterCount,
  1898. NULL,
  1899. NULL,
  1900. &RangePtr,
  1901. &McbIndex );
  1902. //
  1903. // Now walk through this bitmap run and look for a run we
  1904. // can deallocate to create a hole.
  1905. //
  1906. do {
  1907. //
  1908. // Go to the next run in the Mcb.
  1909. //
  1910. McbIndex += 1;
  1911. //
  1912. // If this run extends beyond the end of the of the
  1913. // hole then truncate the clusters in this run.
  1914. //
  1915. if (ThisClusterCount > HoleCount) {
  1916. ThisClusterCount = HoleCount;
  1917. HoleCount = 0;
  1918. } else {
  1919. HoleCount -= (ULONG) ThisClusterCount;
  1920. }
  1921. //
  1922. // Check if this run is a hole then clear the count
  1923. // of clusters.
  1924. //
  1925. if (ThisLcn == UNUSED_LCN) {
  1926. //
  1927. // We want to skip this hole. If we have found a
  1928. // hole then we are done. Otherwise we want to
  1929. // find the next range in the Mft starting at the point beyond
  1930. // the current run (which is a hole). Nothing to do if we don't
  1931. // have enough clusters for a full hole.
  1932. //
  1933. if (!FoundHole &&
  1934. (HoleCount >= Vcb->MftClustersPerHole)) {
  1935. //
  1936. // Find the Vcn after the current Mft run.
  1937. //
  1938. ThisVcn += ThisClusterCount;
  1939. //
  1940. // If this isn't on a hole boundary then
  1941. // round up to a hole boundary. Adjust the
  1942. // available clusters for a hole.
  1943. //
  1944. MftVcn = (ULONG) (ThisVcn + Vcb->MftHoleClusterMask);
  1945. MftVcn = (ULONG) ThisVcn & Vcb->MftHoleClusterInverseMask;
  1946. //
  1947. // Now subtract this from the HoleClusterCount.
  1948. //
  1949. HoleCount -= MftVcn - (ULONG) ThisVcn;
  1950. //
  1951. // We need to convert the Vcn at this point to an Mft record
  1952. // number.
  1953. //
  1954. if (Vcb->FileRecordsPerCluster == 0) {
  1955. StartIndex = MftVcn >> Vcb->MftToClusterShift;
  1956. } else {
  1957. StartIndex = MftVcn << Vcb->MftToClusterShift;
  1958. }
  1959. }
  1960. break;
  1961. //
  1962. // We found a run to deallocate.
  1963. //
  1964. } else {
  1965. //
  1966. // Add these clusters to the clusters already found.
  1967. // Set the flag indicating we found a hole if there
  1968. // are enough clusters to create a hole.
  1969. //
  1970. MftClusterCount += (ULONG) ThisClusterCount;
  1971. if (MftClusterCount >= Vcb->MftClustersPerHole) {
  1972. FoundHole = TRUE;
  1973. }
  1974. }
  1975. } while ((HoleCount != 0) &&
  1976. NtfsGetSequentialMcbEntry( &Vcb->MftScb->Mcb,
  1977. &RangePtr,
  1978. McbIndex,
  1979. &ThisVcn,
  1980. &ThisLcn,
  1981. &ThisClusterCount ));
  1982. } while (!FoundHole && (HoleCount >= Vcb->MftClustersPerHole));
  1983. //
  1984. // Round down to a hole boundary for the next search for
  1985. // a hole candidate.
  1986. //
  1987. BitmapIndex &= Vcb->MftHoleInverseMask;
  1988. } while (!FoundHole && (BitmapIndex >= Vcb->MftHoleGranularity));
  1989. //
  1990. // If we found a hole then deallocate the clusters and record
  1991. // the hole count change.
  1992. //
  1993. if (FoundHole) {
  1994. IO_STATUS_BLOCK IoStatus;
  1995. LONGLONG MftFileOffset;
  1996. //
  1997. // We want to flush the data in the Mft out to disk in
  1998. // case a lazywrite comes in during a window where we have
  1999. // removed the allocation but before a possible abort.
  2000. //
  2001. MftFileOffset = LlBytesFromClusters( Vcb, MftVcn );
  2002. //
  2003. // Round the cluster count and hole count down to a hole boundary.
  2004. //
  2005. MftClusterCount &= Vcb->MftHoleClusterInverseMask;
  2006. if (Vcb->FileRecordsPerCluster == 0) {
  2007. HoleCount = MftClusterCount >> Vcb->MftToClusterShift;
  2008. } else {
  2009. HoleCount = MftClusterCount << Vcb->MftToClusterShift;
  2010. }
  2011. CcFlushCache( &Vcb->MftScb->NonpagedScb->SegmentObject,
  2012. (PLARGE_INTEGER) &MftFileOffset,
  2013. BytesFromClusters( Vcb, MftClusterCount ),
  2014. &IoStatus );
  2015. ASSERT( IoStatus.Status == STATUS_SUCCESS );
  2016. //
  2017. // Remove the clusters from the Mcb for the Mft.
  2018. //
  2019. NtfsDeleteAllocation( IrpContext,
  2020. Vcb->MftScb->FileObject,
  2021. Vcb->MftScb,
  2022. MftVcn,
  2023. (LONGLONG) MftVcn + (MftClusterCount - 1),
  2024. TRUE,
  2025. FALSE );
  2026. //
  2027. // Record the change to the hole count.
  2028. //
  2029. Vcb->MftHoleRecords += HoleCount;
  2030. Vcb->MftScb->ScbType.Mft.HoleRecordChange += HoleCount;
  2031. //
  2032. // Exit the loop.
  2033. //
  2034. break;
  2035. }
  2036. //
  2037. // Look at all of the bits on the previous page.
  2038. //
  2039. BitmapIndex = BITS_PER_PAGE;
  2040. }
  2041. } finally {
  2042. DebugUnwind( NtfsCreateMftHole );
  2043. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); }
  2044. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2045. }
  2046. return FoundHole;
  2047. }
  2048. BOOLEAN
  2049. NtfsFindMftFreeTail (
  2050. IN PIRP_CONTEXT IrpContext,
  2051. IN PVCB Vcb,
  2052. OUT PLONGLONG FileOffset
  2053. )
  2054. /*++
  2055. Routine Description:
  2056. This routine is called to find the file offset where the run of free records at
  2057. the end of the Mft file begins. If we can't find a minimal run of file records
  2058. we won't perform truncation.
  2059. Arguments:
  2060. Vcb - This is the Vcb for the volume being defragged.
  2061. FileOffset - This is the offset where the truncation may begin.
  2062. Return Value:
  2063. BOOLEAN - TRUE if there is an acceptable candidate for truncation at the end of
  2064. the file FALSE otherwise.
  2065. --*/
  2066. {
  2067. ULONG FinalIndex;
  2068. ULONG BaseIndex;
  2069. ULONG ThisIndex;
  2070. RTL_BITMAP Bitmap;
  2071. PULONG BitmapBuffer;
  2072. BOOLEAN StuffAdded = FALSE;
  2073. BOOLEAN MftTailFound = FALSE;
  2074. PBCB BitmapBcb = NULL;
  2075. PAGED_CODE();
  2076. //
  2077. // Use a try-finally to facilite cleanup.
  2078. //
  2079. try {
  2080. //
  2081. // Find the page and range of the last page of the Mft bitmap.
  2082. //
  2083. FinalIndex = (ULONG)Int64ShraMod32(Vcb->MftScb->Header.FileSize.QuadPart, Vcb->MftShift) - 1;
  2084. BaseIndex = FinalIndex & ~(BITS_PER_PAGE - 1);
  2085. Bitmap.SizeOfBitMap = FinalIndex - BaseIndex + 1;
  2086. //
  2087. // Pin this page. If the last bit is not clear then return immediately.
  2088. //
  2089. NtfsMapStream( IrpContext,
  2090. Vcb->MftBitmapScb,
  2091. (LONGLONG)(BaseIndex / 8),
  2092. (Bitmap.SizeOfBitMap + 7) / 8,
  2093. &BitmapBcb,
  2094. &BitmapBuffer );
  2095. RtlInitializeBitMap( &Bitmap, BitmapBuffer, Bitmap.SizeOfBitMap );
  2096. StuffAdded = NtfsAddDeallocatedRecords( Vcb,
  2097. Vcb->MftScb,
  2098. BaseIndex,
  2099. &Bitmap );
  2100. //
  2101. // If the last bit isn't clear then there is nothing we can do.
  2102. //
  2103. if (RtlCheckBit( &Bitmap, Bitmap.SizeOfBitMap - 1 ) == 1) {
  2104. try_return( NOTHING );
  2105. }
  2106. //
  2107. // Find the final free run of the page.
  2108. //
  2109. RtlFindLastBackwardRunClear( &Bitmap, Bitmap.SizeOfBitMap - 1, &ThisIndex );
  2110. //
  2111. // This Index is a relative value. Adjust by the page offset.
  2112. //
  2113. ThisIndex += BaseIndex;
  2114. //
  2115. // Round up the index to a trucate/extend granularity value.
  2116. //
  2117. ThisIndex += Vcb->MftHoleMask;
  2118. ThisIndex &= Vcb->MftHoleInverseMask;
  2119. if (ThisIndex <= FinalIndex) {
  2120. //
  2121. // Convert this value to a file offset and return it to our caller.
  2122. //
  2123. *FileOffset = LlBytesFromFileRecords( Vcb, ThisIndex );
  2124. MftTailFound = TRUE;
  2125. }
  2126. try_exit: NOTHING;
  2127. } finally {
  2128. DebugUnwind( NtfsFindMftFreeTail );
  2129. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); }
  2130. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2131. }
  2132. return MftTailFound;
  2133. }
  2134. //
  2135. // Local support routine
  2136. //
  2137. VOID
  2138. NtfsAllocateBitmapRun (
  2139. IN PIRP_CONTEXT IrpContext,
  2140. IN PVCB Vcb,
  2141. IN LCN StartingLcn,
  2142. IN LONGLONG ClusterCount,
  2143. IN BOOLEAN FromCachedRuns
  2144. )
  2145. /*++
  2146. Routine Description:
  2147. This routine allocates clusters in the bitmap within the specified range.
  2148. Arguments:
  2149. Vcb - Supplies the vcb used in this operation
  2150. StartingLcn - Supplies the starting Lcn index within the bitmap to
  2151. start allocating (i.e., setting to 1).
  2152. ClusterCount - Supplies the number of bits to set to 1 within the
  2153. bitmap.
  2154. FromCachedRuns - Indicates the clusters came from cached information. Allows
  2155. us to handle the case where the cached runs are corrupt.
  2156. Return Value:
  2157. None.
  2158. --*/
  2159. {
  2160. LCN BaseLcn;
  2161. RTL_BITMAP Bitmap;
  2162. PBCB BitmapBcb;
  2163. ULONG BitOffset;
  2164. ULONG BitsToSet;
  2165. BITMAP_RANGE BitmapRange;
  2166. ASSERT_IRP_CONTEXT( IrpContext );
  2167. ASSERT_VCB( Vcb );
  2168. PAGED_CODE();
  2169. DebugTrace( +1, Dbg, ("NtfsAllocateBitmapRun\n") );
  2170. DebugTrace( 0, Dbg, ("StartingLcn = %016I64x\n", StartingLcn) );
  2171. DebugTrace( 0, Dbg, ("ClusterCount = %016I64x\n", ClusterCount) );
  2172. BitmapBcb = NULL;
  2173. try {
  2174. //
  2175. // While the cluster count is greater than zero then we
  2176. // will loop through reading in a page in the bitmap
  2177. // setting bits, and then updating cluster count,
  2178. // and starting lcn
  2179. //
  2180. while (ClusterCount > 0) {
  2181. //
  2182. // Read in the base containing the starting lcn this will return
  2183. // a base lcn for the start of the bitmap
  2184. //
  2185. NtfsPinPageInBitmap( IrpContext, Vcb, StartingLcn, &BaseLcn, &Bitmap, &BitmapBcb );
  2186. //
  2187. // Compute the bit offset within the bitmap of the first bit
  2188. // we are to set, and also compute the number of bits we need to
  2189. // set, which is the minimum of the cluster count and the
  2190. // number of bits left in the bitmap from BitOffset.
  2191. //
  2192. BitOffset = (ULONG)(StartingLcn - BaseLcn);
  2193. if (ClusterCount <= (Bitmap.SizeOfBitMap - BitOffset)) {
  2194. BitsToSet = (ULONG)ClusterCount;
  2195. } else {
  2196. BitsToSet = Bitmap.SizeOfBitMap - BitOffset;
  2197. }
  2198. //
  2199. // We can only make this check if it is not restart, because we have
  2200. // no idea whether the update is applied or not. Raise corrupt if
  2201. // already set to prevent cross-links.
  2202. //
  2203. #ifdef NTFS_CHECK_BITMAP
  2204. if ((Vcb->BitmapCopy != NULL) &&
  2205. !NtfsCheckBitmap( Vcb,
  2206. (ULONG) BaseLcn + BitOffset,
  2207. BitsToSet,
  2208. FALSE )) {
  2209. NtfsBadBitmapCopy( IrpContext, (ULONG) BaseLcn + BitOffset, BitsToSet );
  2210. }
  2211. #endif
  2212. //
  2213. // We hit an unexpected bit set in the bitmap. The assumption here is that
  2214. // we got the bit from the cached run information. If so then simply remove
  2215. // these clusters from the cached run information.
  2216. //
  2217. if (!RtlAreBitsClear( &Bitmap, BitOffset, BitsToSet )) {
  2218. if (FromCachedRuns) {
  2219. //
  2220. // Clear out the lists.
  2221. //
  2222. #ifdef NTFS_CHECK_CACHED_RUNS
  2223. ASSERT( FALSE );
  2224. #endif
  2225. NtfsReinitializeCachedRuns( &Vcb->CachedRuns );
  2226. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  2227. }
  2228. ASSERTMSG("Cannot set bits that are not clear ", FALSE );
  2229. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  2230. }
  2231. //
  2232. // Now log this change as well.
  2233. //
  2234. BitmapRange.BitMapOffset = BitOffset;
  2235. BitmapRange.NumberOfBits = BitsToSet;
  2236. (VOID)
  2237. NtfsWriteLog( IrpContext,
  2238. Vcb->BitmapScb,
  2239. BitmapBcb,
  2240. SetBitsInNonresidentBitMap,
  2241. &BitmapRange,
  2242. sizeof(BITMAP_RANGE),
  2243. ClearBitsInNonresidentBitMap,
  2244. &BitmapRange,
  2245. sizeof(BITMAP_RANGE),
  2246. Int64ShraMod32( BaseLcn, 3 ),
  2247. 0,
  2248. 0,
  2249. Bitmap.SizeOfBitMap >> 3 );
  2250. //
  2251. // Now that we've logged the change go ahead and remove it from the
  2252. // free run Mcb. Do it after it appears in a log record so that
  2253. // it won't be allocated to another file.
  2254. //
  2255. (VOID)NtfsAddCachedRun( IrpContext,
  2256. Vcb,
  2257. StartingLcn,
  2258. BitsToSet,
  2259. RunStateAllocated );
  2260. //
  2261. // Now set the bits by calling the same routine used at restart.
  2262. //
  2263. NtfsRestartSetBitsInBitMap( IrpContext,
  2264. &Bitmap,
  2265. BitOffset,
  2266. BitsToSet );
  2267. #ifdef NTFS_CHECK_BITMAP
  2268. if (Vcb->BitmapCopy != NULL) {
  2269. ULONG BitmapPage;
  2270. ULONG StartBit;
  2271. BitmapPage = ((ULONG) (BaseLcn + BitOffset)) / (PAGE_SIZE * 8);
  2272. StartBit = ((ULONG) (BaseLcn + BitOffset)) & ((PAGE_SIZE * 8) - 1);
  2273. RtlSetBits( Vcb->BitmapCopy + BitmapPage, StartBit, BitsToSet );
  2274. }
  2275. #endif
  2276. //
  2277. // Unpin the Bcb now before possibly looping back.
  2278. //
  2279. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2280. //
  2281. // Now decrement the cluster count and increment the starting lcn accordling
  2282. //
  2283. ClusterCount -= BitsToSet;
  2284. StartingLcn += BitsToSet;
  2285. }
  2286. } finally {
  2287. DebugUnwind( NtfsAllocateBitmapRun );
  2288. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2289. }
  2290. DebugTrace( -1, Dbg, ("NtfsAllocateBitmapRun -> VOID\n") );
  2291. return;
  2292. }
  2293. VOID
  2294. NtfsRestartSetBitsInBitMap (
  2295. IN PIRP_CONTEXT IrpContext,
  2296. IN PRTL_BITMAP Bitmap,
  2297. IN ULONG BitMapOffset,
  2298. IN ULONG NumberOfBits
  2299. )
  2300. /*++
  2301. Routine Description:
  2302. This routine is common to normal operation and restart, and sets a range of
  2303. bits within a single page (as determined by the system which wrote the log
  2304. record) of the volume bitmap.
  2305. Arguments:
  2306. Bitmap - The bit map structure in which to set the bits
  2307. BitMapOffset - Bit offset to set
  2308. NumberOfBits - Number of bits to set
  2309. Return Value:
  2310. None.
  2311. --*/
  2312. {
  2313. UNREFERENCED_PARAMETER( IrpContext );
  2314. PAGED_CODE();
  2315. //
  2316. // Now set the bits and mark the bcb dirty.
  2317. //
  2318. RtlSetBits( Bitmap, BitMapOffset, NumberOfBits );
  2319. }
  2320. //
  2321. // Local support routine
  2322. //
  2323. VOID
  2324. NtfsFreeBitmapRun (
  2325. IN PIRP_CONTEXT IrpContext,
  2326. IN PVCB Vcb,
  2327. IN LCN StartingLcn,
  2328. IN OUT PLONGLONG ClusterCount
  2329. )
  2330. /*++
  2331. Routine Description:
  2332. This routine frees clusters in the bitmap within the specified range.
  2333. Arguments:
  2334. Vcb - Supplies the vcb used in this operation
  2335. StartingLcn - Supplies the starting Lcn index within the bitmap to
  2336. start freeing (i.e., setting to 0).
  2337. ClusterCount - On entry supplies the number of bits to set to 0 within the
  2338. bitmap. On exit contains the number of bits left to insert. This is
  2339. used in the error case to correct the recently deallocated bitmap.
  2340. Return Value:
  2341. None.
  2342. --*/
  2343. {
  2344. LCN BaseLcn;
  2345. RTL_BITMAP Bitmap;
  2346. PBCB BitmapBcb;
  2347. ULONG BitOffset;
  2348. ULONG BitsToClear;
  2349. BITMAP_RANGE BitmapRange;
  2350. ASSERT_IRP_CONTEXT( IrpContext );
  2351. ASSERT_VCB( Vcb );
  2352. PAGED_CODE();
  2353. DebugTrace( +1, Dbg, ("NtfsFreeBitmapRun\n") );
  2354. DebugTrace( 0, Dbg, ("StartingLcn = %016I64\n", StartingLcn) );
  2355. DebugTrace( 0, Dbg, ("ClusterCount = %016I64x\n", *ClusterCount) );
  2356. BitmapBcb = NULL;
  2357. try {
  2358. //
  2359. // Keep track of how volatile the bitmap package is.
  2360. //
  2361. Vcb->ClustersRecentlyFreed += *ClusterCount;
  2362. if (*ClusterCount > Vcb->CachedRuns.LongestFreedRun) {
  2363. Vcb->CachedRuns.LongestFreedRun = *ClusterCount;
  2364. }
  2365. //
  2366. // While the cluster count is greater than zero then we
  2367. // will loop through reading in a page in the bitmap
  2368. // clearing bits, and then updating cluster count,
  2369. // and starting lcn
  2370. //
  2371. while (*ClusterCount > 0) {
  2372. //
  2373. // Read in the base containing the starting lcn this will return
  2374. // a base lcn for the start of the bitmap
  2375. //
  2376. NtfsPinPageInBitmap( IrpContext, Vcb, StartingLcn, &BaseLcn, &Bitmap, &BitmapBcb );
  2377. //
  2378. // Compute the bit offset within the bitmap of the first bit
  2379. // we are to clear, and also compute the number of bits we need to
  2380. // clear, which is the minimum of the cluster count and the
  2381. // number of bits left in the bitmap from BitOffset.
  2382. //
  2383. BitOffset = (ULONG)(StartingLcn - BaseLcn);
  2384. if (*ClusterCount <= Bitmap.SizeOfBitMap - BitOffset) {
  2385. BitsToClear = (ULONG)(*ClusterCount);
  2386. } else {
  2387. BitsToClear = Bitmap.SizeOfBitMap - BitOffset;
  2388. }
  2389. //
  2390. // We can only make this check if it is not restart, because we have
  2391. // no idea whether the update is applied or not. Raise corrupt if
  2392. // these bits aren't set.
  2393. //
  2394. #ifdef NTFS_CHECK_BITMAP
  2395. if ((Vcb->BitmapCopy != NULL) &&
  2396. !NtfsCheckBitmap( Vcb,
  2397. (ULONG) BaseLcn + BitOffset,
  2398. BitsToClear,
  2399. TRUE )) {
  2400. NtfsBadBitmapCopy( IrpContext, (ULONG) BaseLcn + BitOffset, BitsToClear );
  2401. }
  2402. #endif
  2403. //
  2404. // Check if the bits are incorrectly clear.
  2405. //
  2406. if (!RtlAreBitsSet( &Bitmap, BitOffset, BitsToClear )) {
  2407. //
  2408. // Correct thing to do is to ignore the error since the bits are already clear.
  2409. //
  2410. NOTHING;
  2411. //
  2412. // Don't log if the bits are already correct. Otherwise we could set them in the
  2413. // abort path.
  2414. //
  2415. } else {
  2416. //
  2417. // Now log this change as well.
  2418. //
  2419. BitmapRange.BitMapOffset = BitOffset;
  2420. BitmapRange.NumberOfBits = BitsToClear;
  2421. (VOID)
  2422. NtfsWriteLog( IrpContext,
  2423. Vcb->BitmapScb,
  2424. BitmapBcb,
  2425. ClearBitsInNonresidentBitMap,
  2426. &BitmapRange,
  2427. sizeof(BITMAP_RANGE),
  2428. SetBitsInNonresidentBitMap,
  2429. &BitmapRange,
  2430. sizeof(BITMAP_RANGE),
  2431. Int64ShraMod32( BaseLcn, 3 ),
  2432. 0,
  2433. 0,
  2434. Bitmap.SizeOfBitMap >> 3 );
  2435. //
  2436. // Now clear the bits by calling the same routine used at restart.
  2437. //
  2438. NtfsRestartClearBitsInBitMap( IrpContext,
  2439. &Bitmap,
  2440. BitOffset,
  2441. BitsToClear );
  2442. #ifdef NTFS_CHECK_BITMAP
  2443. if (Vcb->BitmapCopy != NULL) {
  2444. ULONG BitmapPage;
  2445. ULONG StartBit;
  2446. BitmapPage = ((ULONG) (BaseLcn + BitOffset)) / (PAGE_SIZE * 8);
  2447. StartBit = ((ULONG) (BaseLcn + BitOffset)) & ((PAGE_SIZE * 8) - 1);
  2448. RtlClearBits( Vcb->BitmapCopy + BitmapPage, StartBit, BitsToClear );
  2449. }
  2450. #endif
  2451. }
  2452. //
  2453. // Unpin the Bcb now before possibly looping back.
  2454. //
  2455. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2456. //
  2457. // Now decrement the cluster count and increment the starting lcn accordling
  2458. //
  2459. *ClusterCount -= BitsToClear;
  2460. StartingLcn += BitsToClear;
  2461. }
  2462. } finally {
  2463. DebugUnwind( NtfsFreeBitmapRun );
  2464. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2465. }
  2466. DebugTrace( -1, Dbg, ("NtfsFreeBitmapRun -> VOID\n") );
  2467. return;
  2468. }
  2469. VOID
  2470. NtfsRestartClearBitsInBitMap (
  2471. IN PIRP_CONTEXT IrpContext,
  2472. IN PRTL_BITMAP Bitmap,
  2473. IN ULONG BitMapOffset,
  2474. IN ULONG NumberOfBits
  2475. )
  2476. /*++
  2477. Routine Description:
  2478. This routine is common to normal operation and restart, and clears a range of
  2479. bits within a single page (as determined by the system which wrote the log
  2480. record) of the volume bitmap.
  2481. Arguments:
  2482. Bitmap - Bitmap structure in which to clear the bits
  2483. BitMapOffset - Bit offset to clear
  2484. NumberOfBits - Number of bits to clear
  2485. Return Value:
  2486. None.
  2487. --*/
  2488. {
  2489. UNREFERENCED_PARAMETER( IrpContext );
  2490. PAGED_CODE();
  2491. //
  2492. // Now clear the bits and mark the bcb dirty.
  2493. //
  2494. RtlClearBits( Bitmap, BitMapOffset, NumberOfBits );
  2495. }
  2496. //
  2497. // Local support routine
  2498. //
  2499. BOOLEAN
  2500. NtfsFindFreeBitmapRun (
  2501. IN PIRP_CONTEXT IrpContext,
  2502. IN PVCB Vcb,
  2503. IN LONGLONG NumberToFind,
  2504. IN LCN StartingSearchHint,
  2505. IN BOOLEAN ReturnAnyLength,
  2506. IN BOOLEAN IgnoreMftZone,
  2507. OUT PLCN ReturnedLcn,
  2508. OUT PLONGLONG ClusterCountFound
  2509. )
  2510. /*++
  2511. Routine Description:
  2512. This routine searches the bitmap for free clusters based on the
  2513. hint, and number needed. This routine is actually pretty dumb in
  2514. that it doesn't try for the best fit, we'll assume the caching worked
  2515. and already would have given us a good fit.
  2516. Arguments:
  2517. Vcb - Supplies the vcb used in this operation
  2518. NumberToFind - Supplies the number of clusters that we would
  2519. really like to find
  2520. StartingSearchHint - Supplies an Lcn to start the search from
  2521. ReturnAnyLength - If TRUE then we are more interested in finding
  2522. a run which begins with the StartingSearchHint rather than
  2523. one which matches the length of the run. Case in point is when
  2524. we are trying to append to an existing file (the Mft is a
  2525. critical case).
  2526. ReturnedLcn - Recieves the Lcn of the free run of clusters that
  2527. we were able to find
  2528. IgnoreMftZone - If TRUE then don't adjust the request around the Mft zone.
  2529. ClusterCountFound - Receives the number of clusters in this run
  2530. Return Value:
  2531. BOOLEAN - TRUE if clusters allocated from zone. FALSE otherwise.
  2532. --*/
  2533. {
  2534. RTL_BITMAP Bitmap;
  2535. PVOID BitmapBuffer;
  2536. PBCB BitmapBcb;
  2537. BOOLEAN AllocatedFromZone = FALSE;
  2538. BOOLEAN StuffAdded;
  2539. ULONG Count;
  2540. ULONG RequestedCount;
  2541. ULONG FoundCount;
  2542. //
  2543. // As we walk through the bitmap pages we need to remember
  2544. // exactly where we are in the bitmap stream. We walk through
  2545. // the volume bitmap a page at a time but the current bitmap
  2546. // contained within the current page but may not be the full
  2547. // page.
  2548. //
  2549. // Lcn - Lcn used to find the bitmap page to pin. This Lcn
  2550. // will lie within the page to pin.
  2551. //
  2552. // BaseLcn - Bit offset of the start of the current bitmap in
  2553. // the bitmap stream.
  2554. //
  2555. // LcnFromHint - Bit offset of the start of the page after
  2556. // the page which contains the StartingSearchHint.
  2557. //
  2558. // BitOffset - Offset of found bits from the beginning
  2559. // of the current bitmap.
  2560. //
  2561. LCN Lcn = StartingSearchHint;
  2562. LCN BaseLcn;
  2563. LCN LcnFromHint;
  2564. ULONG BitOffset;
  2565. ULONG StartIndex;
  2566. RTL_BITMAP_RUN RunArray[16];
  2567. ULONG RunArrayIndex;
  2568. ASSERT_IRP_CONTEXT( IrpContext );
  2569. ASSERT_VCB( Vcb );
  2570. PAGED_CODE();
  2571. DebugTrace( +1, Dbg, ("NtfsFindFreeBitmapRun\n") );
  2572. DebugTrace( 0, Dbg, ("NumberToFind = %016I64x\n", NumberToFind) );
  2573. DebugTrace( 0, Dbg, ("StartingSearchHint = %016I64x\n", StartingSearchHint) );
  2574. BitmapBcb = NULL;
  2575. StuffAdded = FALSE;
  2576. try {
  2577. //
  2578. // First trim the number of clusters that we are being asked
  2579. // for to fit in a ulong
  2580. //
  2581. if (NumberToFind > MAXULONG) {
  2582. RequestedCount = Count = MAXULONG;
  2583. } else {
  2584. RequestedCount = Count = (ULONG)NumberToFind;
  2585. }
  2586. //
  2587. // Let's not go past the end of the volume.
  2588. //
  2589. if (Lcn < Vcb->TotalClusters) {
  2590. //
  2591. // Now read in the first bitmap based on the search hint, this will return
  2592. // a base lcn that we can use to compute the real bit off for our hint. We also
  2593. // must bias the bitmap by whatever has been recently deallocated.
  2594. //
  2595. NtfsMapPageInBitmap( IrpContext, Vcb, Lcn, &BaseLcn, &Bitmap, &BitmapBcb );
  2596. LcnFromHint = BaseLcn + Bitmap.SizeOfBitMap;
  2597. StuffAdded = NtfsAddRecentlyDeallocated( Vcb, BaseLcn, &Bitmap );
  2598. BitmapBuffer = Bitmap.Buffer;
  2599. //
  2600. // We don't want to look in the Mft zone if it is at the beginning
  2601. // of this page unless our caller told us to skip any zone checks. Adjust the
  2602. // bitmap so we skip this range.
  2603. //
  2604. if (!IgnoreMftZone &&
  2605. (BaseLcn < Vcb->MftZoneEnd) && (Lcn > Vcb->MftZoneEnd)) {
  2606. //
  2607. // Find the number of bits to swallow. We know this will
  2608. // a multible of bytes since the Mft zone end is always
  2609. // on a ulong boundary.
  2610. //
  2611. BitOffset = (ULONG) (Vcb->MftZoneEnd - BaseLcn);
  2612. //
  2613. // Adjust the bitmap size and buffer to skip this initial
  2614. // range in the Mft zone.
  2615. //
  2616. Bitmap.Buffer = Add2Ptr( Bitmap.Buffer, BitOffset / 8 );
  2617. Bitmap.SizeOfBitMap -= BitOffset;
  2618. BaseLcn = Vcb->MftZoneEnd;
  2619. }
  2620. //
  2621. // The bit offset is from the base of this bitmap to our starting Lcn.
  2622. //
  2623. BitOffset = (ULONG)(Lcn - BaseLcn);
  2624. //
  2625. // Now search the bitmap for a clear number of bits based on our hint
  2626. // If we the returned bitoffset is not -1 then we have a hit.
  2627. //
  2628. if (ReturnAnyLength) {
  2629. //
  2630. // We'd like to find a contiguous run. If we don't then go back and
  2631. // ask for a longer run.
  2632. //
  2633. StartIndex = RtlFindClearBits( &Bitmap, 1, BitOffset );
  2634. if ((StartIndex != -1) &&
  2635. (StartIndex != BitOffset)) {
  2636. BitOffset = RtlFindClearBits( &Bitmap, Count, BitOffset );
  2637. } else {
  2638. BitOffset = StartIndex;
  2639. }
  2640. //
  2641. // We didn't find a contiguous length
  2642. //
  2643. } else {
  2644. BitOffset = RtlFindClearBits( &Bitmap, Count, BitOffset );
  2645. }
  2646. if (BitOffset != -1) {
  2647. //
  2648. // We found a run. If the starting Lcn is our input hint AND
  2649. // we will accept any length then walk forward in the bitmap
  2650. // and find the real length of the run.
  2651. //
  2652. *ReturnedLcn = BitOffset + BaseLcn;
  2653. if (ReturnAnyLength &&
  2654. (*ReturnedLcn == StartingSearchHint)) {
  2655. Count = 0;
  2656. while (TRUE) {
  2657. FoundCount = RtlFindNextForwardRunClear( &Bitmap,
  2658. BitOffset,
  2659. &StartIndex );
  2660. //
  2661. // Verify that we found something and that the offset
  2662. // begins with out start hint.
  2663. //
  2664. if (FoundCount &&
  2665. (BitOffset == StartIndex)) {
  2666. Count += FoundCount;
  2667. if (Count >= RequestedCount) {
  2668. Count = RequestedCount;
  2669. break;
  2670. }
  2671. } else {
  2672. break;
  2673. }
  2674. //
  2675. // Break out if we found enough or the run doesn't
  2676. // extend to the end of the bitmap or we are at
  2677. // the last page of the bitmap.
  2678. //
  2679. if ((StartIndex + FoundCount != Bitmap.SizeOfBitMap) ||
  2680. (BaseLcn + Bitmap.SizeOfBitMap >= Vcb->TotalClusters)) {
  2681. break;
  2682. }
  2683. Lcn = BaseLcn + Bitmap.SizeOfBitMap;
  2684. if (StuffAdded) { NtfsFreePool( BitmapBuffer ); StuffAdded = FALSE; }
  2685. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2686. NtfsMapPageInBitmap( IrpContext, Vcb, Lcn, &BaseLcn, &Bitmap, &BitmapBcb );
  2687. ASSERTMSG("Math wrong for bits per page of bitmap", (Lcn == BaseLcn));
  2688. StuffAdded = NtfsAddRecentlyDeallocated( Vcb, BaseLcn, &Bitmap );
  2689. BitmapBuffer = Bitmap.Buffer;
  2690. BitOffset = 0;
  2691. }
  2692. }
  2693. *ClusterCountFound = Count;
  2694. //
  2695. // While we have the bitmap let's grab some long runs
  2696. //
  2697. RunArrayIndex = RtlFindClearRuns( &Bitmap, RunArray, 16, TRUE );
  2698. if (RunArrayIndex > 0) {
  2699. NtfsAddCachedRunMult( IrpContext,
  2700. Vcb,
  2701. BaseLcn,
  2702. RunArray,
  2703. RunArrayIndex );
  2704. }
  2705. leave;
  2706. }
  2707. //
  2708. // Well the first try didn't succeed so now just grab the longest free run in the
  2709. // current bitmap, and while we're at it will populate the cached run information
  2710. //
  2711. RunArrayIndex = RtlFindClearRuns( &Bitmap, RunArray, 16, TRUE );
  2712. if (RunArrayIndex > 0) {
  2713. USHORT LocalOffset;
  2714. *ReturnedLcn = RunArray[0].StartingIndex + BaseLcn;
  2715. *ClusterCountFound = RunArray[0].NumberOfBits;
  2716. //
  2717. // There is no point in adding a free run for a range that is
  2718. // about to be consumed, although it won't affect correctness.
  2719. //
  2720. if (*ClusterCountFound > NumberToFind) {
  2721. //
  2722. // Trim off the part of the free run that will be
  2723. // consumed by the caller.
  2724. //
  2725. RunArray[0].StartingIndex += (ULONG)NumberToFind;
  2726. RunArray[0].NumberOfBits -= (ULONG)NumberToFind;
  2727. LocalOffset = 0;
  2728. //
  2729. // Only return the requested amount to the caller.
  2730. //
  2731. *ClusterCountFound = NumberToFind;
  2732. } else {
  2733. //
  2734. // Skip the first entry since the caller will use all of
  2735. // it.
  2736. //
  2737. LocalOffset = 1;
  2738. }
  2739. if (RunArrayIndex > LocalOffset) {
  2740. NtfsAddCachedRunMult( IrpContext,
  2741. Vcb,
  2742. BaseLcn,
  2743. RunArray + LocalOffset,
  2744. RunArrayIndex - LocalOffset );
  2745. }
  2746. leave;
  2747. }
  2748. //
  2749. // Well the current bitmap is full so now simply scan the disk looking
  2750. // for anything that is free, starting with the next bitmap.
  2751. // And again bias the bitmap with recently deallocated clusters.
  2752. // We won't even bother looking for the longest free runs we'll take
  2753. // whatever we can get.
  2754. //
  2755. if (StuffAdded) { NtfsFreePool( BitmapBuffer ); StuffAdded = FALSE; }
  2756. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2757. Lcn = BaseLcn + Bitmap.SizeOfBitMap;
  2758. //
  2759. // If this is the Mft then scan from the current point to volume end,
  2760. // then back to the beginning.
  2761. //
  2762. if (IgnoreMftZone) {
  2763. //
  2764. // Look in the following ranges. Break out if we find anything.
  2765. //
  2766. // - Current point to end of volume
  2767. // - Start of volume to current
  2768. //
  2769. if (NtfsScanBitmapRange( IrpContext,
  2770. Vcb,
  2771. Lcn,
  2772. Vcb->TotalClusters,
  2773. NumberToFind,
  2774. ReturnedLcn,
  2775. ClusterCountFound )) {
  2776. if ((*ReturnedLcn < Vcb->MftZoneEnd) &&
  2777. (*ReturnedLcn >= Vcb->MftZoneStart)) {
  2778. AllocatedFromZone = TRUE;
  2779. }
  2780. leave;
  2781. }
  2782. if (NtfsScanBitmapRange( IrpContext,
  2783. Vcb,
  2784. 0,
  2785. Lcn,
  2786. NumberToFind,
  2787. ReturnedLcn,
  2788. ClusterCountFound )) {
  2789. if ((*ReturnedLcn < Vcb->MftZoneEnd) &&
  2790. (*ReturnedLcn >= Vcb->MftZoneStart)) {
  2791. AllocatedFromZone = TRUE;
  2792. }
  2793. leave;
  2794. }
  2795. //
  2796. // No luck.
  2797. //
  2798. *ClusterCountFound = 0;
  2799. leave;
  2800. }
  2801. }
  2802. //
  2803. // Check if we are starting before the Mft zone.
  2804. //
  2805. if (Lcn < Vcb->MftZoneStart) {
  2806. //
  2807. // Look in the following ranges. Break out if we find anything.
  2808. //
  2809. // - Current point to Zone start
  2810. // - Zone end to end of volume
  2811. // - Start of volume to current
  2812. //
  2813. if (NtfsScanBitmapRange( IrpContext,
  2814. Vcb,
  2815. Lcn,
  2816. Vcb->MftZoneStart,
  2817. NumberToFind,
  2818. ReturnedLcn,
  2819. ClusterCountFound )) {
  2820. leave;
  2821. }
  2822. if (NtfsScanBitmapRange( IrpContext,
  2823. Vcb,
  2824. Vcb->MftZoneEnd,
  2825. Vcb->TotalClusters,
  2826. NumberToFind,
  2827. ReturnedLcn,
  2828. ClusterCountFound )) {
  2829. leave;
  2830. }
  2831. if (NtfsScanBitmapRange( IrpContext,
  2832. Vcb,
  2833. 0,
  2834. Lcn,
  2835. NumberToFind,
  2836. ReturnedLcn,
  2837. ClusterCountFound )) {
  2838. leave;
  2839. }
  2840. //
  2841. // Check if we are beyond the Mft zone.
  2842. //
  2843. } else if (Lcn > Vcb->MftZoneEnd) {
  2844. //
  2845. // Look in the following ranges. Break out if we find anything.
  2846. //
  2847. // - Current point to end of volume
  2848. // - Start of volume to Zone start
  2849. // - Zone end to current point.
  2850. //
  2851. if (NtfsScanBitmapRange( IrpContext,
  2852. Vcb,
  2853. Lcn,
  2854. Vcb->TotalClusters,
  2855. NumberToFind,
  2856. ReturnedLcn,
  2857. ClusterCountFound )) {
  2858. leave;
  2859. }
  2860. if (NtfsScanBitmapRange( IrpContext,
  2861. Vcb,
  2862. 0,
  2863. Vcb->MftZoneStart,
  2864. NumberToFind,
  2865. ReturnedLcn,
  2866. ClusterCountFound )) {
  2867. leave;
  2868. }
  2869. if (NtfsScanBitmapRange( IrpContext,
  2870. Vcb,
  2871. Vcb->MftZoneEnd,
  2872. Lcn,
  2873. NumberToFind,
  2874. ReturnedLcn,
  2875. ClusterCountFound )) {
  2876. leave;
  2877. }
  2878. //
  2879. // We are starting within the zone. Skip over the zone to check it last.
  2880. //
  2881. } else {
  2882. //
  2883. // Look in the following ranges. Break out if we find anything.
  2884. //
  2885. // - End of zone to end of volume
  2886. // - Start of volume to start of zone
  2887. //
  2888. if (NtfsScanBitmapRange( IrpContext,
  2889. Vcb,
  2890. Vcb->MftZoneEnd,
  2891. Vcb->TotalClusters,
  2892. NumberToFind,
  2893. ReturnedLcn,
  2894. ClusterCountFound )) {
  2895. leave;
  2896. }
  2897. if (NtfsScanBitmapRange( IrpContext,
  2898. Vcb,
  2899. 0,
  2900. Vcb->MftZoneStart,
  2901. NumberToFind,
  2902. ReturnedLcn,
  2903. ClusterCountFound )) {
  2904. leave;
  2905. }
  2906. }
  2907. //
  2908. // We didn't find anything. Let's examine the zone explicitly.
  2909. //
  2910. if (NtfsScanBitmapRange( IrpContext,
  2911. Vcb,
  2912. Vcb->MftZoneStart,
  2913. Vcb->MftZoneEnd,
  2914. NumberToFind,
  2915. ReturnedLcn,
  2916. ClusterCountFound )) {
  2917. AllocatedFromZone = TRUE;
  2918. leave;
  2919. }
  2920. //
  2921. // No luck.
  2922. //
  2923. *ClusterCountFound = 0;
  2924. } finally {
  2925. DebugUnwind( NtfsFindFreeBitmapRun );
  2926. if (StuffAdded) { NtfsFreePool( BitmapBuffer ); }
  2927. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2928. }
  2929. DebugTrace( 0, Dbg, ("ReturnedLcn <- %016I64x\n", *ReturnedLcn) );
  2930. DebugTrace( 0, Dbg, ("ClusterCountFound <- %016I64x\n", *ClusterCountFound) );
  2931. DebugTrace( -1, Dbg, ("NtfsFindFreeBitmapRun -> VOID\n") );
  2932. return AllocatedFromZone;
  2933. }
  2934. //
  2935. // Local support routine
  2936. //
  2937. BOOLEAN
  2938. NtfsScanBitmapRange (
  2939. IN PIRP_CONTEXT IrpContext,
  2940. IN PVCB Vcb,
  2941. IN LCN StartLcn,
  2942. IN LCN BeyondLcn,
  2943. IN LONGLONG NumberToFind,
  2944. OUT PLCN ReturnedLcn,
  2945. OUT PLONGLONG ClusterCountFound
  2946. )
  2947. /*++
  2948. Routine Description:
  2949. This routine will scan a range of the bitmap looking for a free run.
  2950. It is called when we need to limit the bits we are willing to consider
  2951. at a time, typically to skip over the Mft zone.
  2952. Arguments:
  2953. Vcb - Volume being scanned.
  2954. StartLcn - First Lcn in the bitmap to consider.
  2955. BeyondLcn - First Lcn in the bitmap past the range we want to consider.
  2956. NumberToFind - Supplies the number of clusters that we would
  2957. really like to find
  2958. ReturnedLcn - Start of free range if found.
  2959. ClusterCountFound - Length of free range if found.
  2960. Return Value:
  2961. BOOLEAN - TRUE if a bitmap range was found. FALSE otherwise.
  2962. --*/
  2963. {
  2964. BOOLEAN FreeRangeFound = FALSE;
  2965. RTL_BITMAP Bitmap;
  2966. PVOID BitmapBuffer;
  2967. ULONG BitOffset;
  2968. PBCB BitmapBcb = NULL;
  2969. BOOLEAN StuffAdded = FALSE;
  2970. LCN BaseLcn;
  2971. RTL_BITMAP_RUN RunArray[16];
  2972. ULONG RunArrayIndex;
  2973. PAGED_CODE();
  2974. DebugTrace( +1, Dbg, ("NtfsScanBitmapRange...\n") );
  2975. //
  2976. // The end Lcn might be beyond the end of the bitmap.
  2977. //
  2978. if (BeyondLcn > Vcb->TotalClusters) {
  2979. BeyondLcn = Vcb->TotalClusters;
  2980. }
  2981. //
  2982. // Use a try-finally to facilitate cleanup.
  2983. //
  2984. try {
  2985. //
  2986. // Now search the rest of the bitmap starting with right after the mft zone
  2987. // followed by the mft zone (or the beginning of the disk). Again take whatever
  2988. // we can get and not bother with the longest runs.
  2989. //
  2990. while (StartLcn < BeyondLcn) {
  2991. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  2992. NtfsMapPageInBitmap( IrpContext, Vcb, StartLcn, &BaseLcn, &Bitmap, &BitmapBcb );
  2993. StuffAdded = NtfsAddRecentlyDeallocated( Vcb, BaseLcn, &Bitmap );
  2994. BitmapBuffer = Bitmap.Buffer;
  2995. //
  2996. // Check if we don't want to use the entire page.
  2997. //
  2998. if ((BaseLcn + Bitmap.SizeOfBitMap) > BeyondLcn) {
  2999. Bitmap.SizeOfBitMap = (ULONG) (BeyondLcn - BaseLcn);
  3000. }
  3001. //
  3002. // Now adjust the starting Lcn if not at the beginning
  3003. // of the bitmap page. We know this will be a multiple
  3004. // of bytes since the MftZoneEnd is always on a ulong
  3005. // boundary in the bitmap.
  3006. //
  3007. if (BaseLcn != StartLcn) {
  3008. BitOffset = (ULONG) (StartLcn - BaseLcn);
  3009. Bitmap.SizeOfBitMap -= BitOffset;
  3010. Bitmap.Buffer = Add2Ptr( Bitmap.Buffer, BitOffset / 8 );
  3011. BaseLcn = StartLcn;
  3012. }
  3013. RunArrayIndex = RtlFindClearRuns( &Bitmap, RunArray, 16, TRUE );
  3014. if (RunArrayIndex > 0) {
  3015. USHORT LocalOffset;
  3016. *ReturnedLcn = RunArray[0].StartingIndex + BaseLcn;
  3017. *ClusterCountFound = RunArray[0].NumberOfBits;
  3018. FreeRangeFound = TRUE;
  3019. //
  3020. // There is no point in adding a free run for a range that is
  3021. // about to be consumed, although it won't affect correctness.
  3022. //
  3023. if (*ClusterCountFound > NumberToFind) {
  3024. //
  3025. // Trim off the part of the free run that will be
  3026. // consumed by the caller.
  3027. //
  3028. RunArray[0].StartingIndex += (ULONG)NumberToFind;
  3029. RunArray[0].NumberOfBits -= (ULONG)NumberToFind;
  3030. LocalOffset = 0;
  3031. } else {
  3032. //
  3033. // Skip the first entry since the caller will use all of
  3034. // it.
  3035. //
  3036. LocalOffset = 1;
  3037. }
  3038. if (RunArrayIndex > LocalOffset) {
  3039. NtfsAddCachedRunMult( IrpContext,
  3040. Vcb,
  3041. BaseLcn,
  3042. RunArray + LocalOffset,
  3043. RunArrayIndex - LocalOffset );
  3044. }
  3045. leave;
  3046. }
  3047. StartLcn = BaseLcn + Bitmap.SizeOfBitMap;
  3048. if (StuffAdded) { NtfsFreePool( BitmapBuffer ); StuffAdded = FALSE; }
  3049. }
  3050. } finally {
  3051. DebugUnwind( NtfsScanBitmapRange );
  3052. if (StuffAdded) { NtfsFreePool( BitmapBuffer ); StuffAdded = FALSE; }
  3053. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  3054. DebugTrace( -1, Dbg, ("NtfsScanBitmapRange -> %08lx\n", FreeRangeFound) );
  3055. }
  3056. return FreeRangeFound;
  3057. }
  3058. //
  3059. // Local support routine
  3060. //
  3061. BOOLEAN
  3062. NtfsAddRecentlyDeallocated (
  3063. IN PVCB Vcb,
  3064. IN LCN StartingBitmapLcn,
  3065. IN OUT PRTL_BITMAP Bitmap
  3066. )
  3067. /*++
  3068. Routine Description:
  3069. This routine will modify the input bitmap by removing from it
  3070. any clusters that are in the recently deallocated mcb. If we
  3071. do add stuff then we will not modify the bitmap buffer itself but
  3072. will allocate a new copy for the bitmap.
  3073. We will always protect the boot sector on the disk by marking the
  3074. first 8K as allocated. This will prevent us from overwriting the
  3075. boot sector if the volume becomes corrupted.
  3076. Arguments:
  3077. Vcb - Supplies the Vcb used in this operation
  3078. StartingBitmapLcn - Supplies the Starting Lcn of the bitmap
  3079. Bitmap - Supplies the bitmap being modified
  3080. Return Value:
  3081. BOOLEAN - TRUE if the bitmap has been modified and FALSE
  3082. otherwise.
  3083. --*/
  3084. {
  3085. BOOLEAN Results;
  3086. PVOID NewBuffer;
  3087. LCN EndingBitmapLcn;
  3088. PLARGE_MCB Mcb;
  3089. ULONG i;
  3090. VCN StartingVcn;
  3091. LCN StartingLcn;
  3092. LCN EndingLcn;
  3093. LONGLONG ClusterCount;
  3094. PDEALLOCATED_CLUSTERS DeallocatedClusters;
  3095. ULONG StartingBit;
  3096. ULONG EndingBit;
  3097. PAGED_CODE();
  3098. DebugTrace( +1, Dbg, ("NtfsAddRecentlyDeallocated...\n") );
  3099. //
  3100. // Until shown otherwise we will assume that we haven't updated anything
  3101. //
  3102. Results = FALSE;
  3103. //
  3104. // If this is the first page of the bitmap then mark the first 8K as
  3105. // allocated. This will prevent us from accidentally allocating out
  3106. // of the boot sector even if the bitmap is corrupt.
  3107. //
  3108. if ((StartingBitmapLcn == 0) &&
  3109. !RtlAreBitsSet( Bitmap, 0, ClustersFromBytes( Vcb, 0x2000 ))) {
  3110. NewBuffer = NtfsAllocatePool(PagedPool, (Bitmap->SizeOfBitMap+7)/8 );
  3111. RtlCopyMemory( NewBuffer, Bitmap->Buffer, (Bitmap->SizeOfBitMap+7)/8 );
  3112. Bitmap->Buffer = NewBuffer;
  3113. Results = TRUE;
  3114. //
  3115. // Now mark the bits as allocated.
  3116. //
  3117. RtlSetBits( Bitmap, 0, ClustersFromBytes( Vcb, 0x2000 ));
  3118. }
  3119. //
  3120. // Now compute the ending bitmap lcn for the bitmap
  3121. //
  3122. EndingBitmapLcn = StartingBitmapLcn + (Bitmap->SizeOfBitMap - 1);
  3123. //
  3124. // For every run in the recently deallocated mcb we will check if it is real and
  3125. // then check if the run in contained in the bitmap.
  3126. //
  3127. // There are really six cases to consider:
  3128. //
  3129. // StartingBitmapLcn EndingBitmapLcn
  3130. // +=================================+
  3131. //
  3132. //
  3133. // 1 -------+ EndingLcn
  3134. //
  3135. // 2 StartingLcn +--------
  3136. //
  3137. // 3 -------------------+ EndingLcn
  3138. //
  3139. // 4 StartingLcn +-------------------------
  3140. //
  3141. // 5 ---------------------------------------------------------------
  3142. //
  3143. // 6 EndingLcn +-------------------+ StartingLcn
  3144. //
  3145. //
  3146. // 1. EndingLcn is before StartingBitmapLcn which means we haven't
  3147. // reached the bitmap yet.
  3148. //
  3149. // 2. StartingLcn is after EndingBitmapLcn which means we've gone
  3150. // beyond the bitmap
  3151. //
  3152. // 3, 4, 5, 6. There is some overlap between the bitmap and
  3153. // the run.
  3154. //
  3155. DeallocatedClusters = (PDEALLOCATED_CLUSTERS)Vcb->DeallocatedClusterListHead.Flink;
  3156. do {
  3157. //
  3158. // Skip this Mcb if it has no entries.
  3159. //
  3160. if (DeallocatedClusters->ClusterCount != 0) {
  3161. Mcb = &DeallocatedClusters->Mcb;
  3162. for (i = 0; FsRtlGetNextLargeMcbEntry( Mcb, i, &StartingVcn, &StartingLcn, &ClusterCount ); i += 1) {
  3163. if (StartingVcn == StartingLcn) {
  3164. //
  3165. // Compute the ending lcn as the starting lcn minus cluster count plus 1.
  3166. //
  3167. EndingLcn = (StartingLcn + ClusterCount) - 1;
  3168. //
  3169. // Check if we haven't reached the bitmap yet.
  3170. //
  3171. if (EndingLcn < StartingBitmapLcn) {
  3172. NOTHING;
  3173. //
  3174. // Check if we've gone beyond the bitmap
  3175. //
  3176. } else if (EndingBitmapLcn < StartingLcn) {
  3177. break;
  3178. //
  3179. // Otherwise we overlap with the bitmap in some way
  3180. //
  3181. } else {
  3182. //
  3183. // First check if we have never set bit in the bitmap. and if so then
  3184. // now is the time to make an private copy of the bitmap buffer
  3185. //
  3186. if (Results == FALSE) {
  3187. NewBuffer = NtfsAllocatePool(PagedPool, (Bitmap->SizeOfBitMap+7)/8 );
  3188. RtlCopyMemory( NewBuffer, Bitmap->Buffer, (Bitmap->SizeOfBitMap+7)/8 );
  3189. Bitmap->Buffer = NewBuffer;
  3190. Results = TRUE;
  3191. }
  3192. //
  3193. // Now compute the begining and ending bit that we need to set in the bitmap
  3194. //
  3195. StartingBit = (StartingLcn < StartingBitmapLcn ?
  3196. 0 :
  3197. (ULONG)(StartingLcn - StartingBitmapLcn));
  3198. EndingBit = (EndingLcn > EndingBitmapLcn ?
  3199. Bitmap->SizeOfBitMap - 1 :
  3200. (ULONG)(EndingLcn - StartingBitmapLcn));
  3201. //
  3202. // And set those bits
  3203. //
  3204. RtlSetBits( Bitmap, StartingBit, EndingBit - StartingBit + 1 );
  3205. }
  3206. }
  3207. }
  3208. }
  3209. DeallocatedClusters = (PDEALLOCATED_CLUSTERS)DeallocatedClusters->Link.Flink;
  3210. } while (&DeallocatedClusters->Link != &Vcb->DeallocatedClusterListHead );
  3211. DebugTrace( -1, Dbg, ("NtfsAddRecentlyDeallocated -> %08lx\n", Results) );
  3212. return Results;
  3213. }
  3214. //
  3215. // Local support routine
  3216. //
  3217. VOID
  3218. NtfsMapOrPinPageInBitmap (
  3219. IN PIRP_CONTEXT IrpContext,
  3220. IN PVCB Vcb,
  3221. IN LCN Lcn,
  3222. OUT PLCN StartingLcn,
  3223. IN OUT PRTL_BITMAP Bitmap,
  3224. OUT PBCB *BitmapBcb,
  3225. IN BOOLEAN AlsoPinData
  3226. )
  3227. /*++
  3228. Routine Description:
  3229. This routine reads in a single page of the bitmap file and returns
  3230. an initialized bitmap variable for that page
  3231. Arguments:
  3232. Vcb - Supplies the vcb used in this operation
  3233. Lcn - Supplies the Lcn index in the bitmap that we want to read in
  3234. In other words, this routine reads in the bitmap page containing
  3235. the lcn index
  3236. StartingLcn - Receives the base lcn index of the bitmap that we've
  3237. just read in.
  3238. Bitmap - Receives an initialized bitmap. The memory for the bitmap
  3239. header must be supplied by the caller
  3240. BitmapBcb - Receives the Bcb for the bitmap buffer
  3241. AlsoPinData - Indicates if this routine should also pin the page
  3242. in memory, used if we need to modify the page
  3243. Return Value:
  3244. None.
  3245. --*/
  3246. {
  3247. ULONG BitmapSize;
  3248. PVOID Buffer;
  3249. ASSERT_IRP_CONTEXT( IrpContext );
  3250. ASSERT_VCB( Vcb );
  3251. PAGED_CODE();
  3252. DebugTrace( +1, Dbg, ("NtfsMapOrPinPageInBitmap\n") );
  3253. DebugTrace( 0, Dbg, ("Lcn = %016I64x\n", Lcn) );
  3254. //
  3255. // Compute the starting lcn index of the page we're after
  3256. //
  3257. *StartingLcn = Lcn & ~(BITS_PER_PAGE-1);
  3258. //
  3259. // Compute how many bits there are in the page we need to read
  3260. //
  3261. BitmapSize = (ULONG)(Vcb->TotalClusters - *StartingLcn);
  3262. if (BitmapSize > BITS_PER_PAGE) {
  3263. BitmapSize = BITS_PER_PAGE;
  3264. }
  3265. //
  3266. // Now either Pin or Map the bitmap page, we will add 7 to the bitmap
  3267. // size before dividing it by 8. That way we will ensure we get the last
  3268. // byte read in. For example a bitmap size of 1 through 8 reads in 1 byte
  3269. //
  3270. if (AlsoPinData) {
  3271. NtfsPinStream( IrpContext,
  3272. Vcb->BitmapScb,
  3273. Int64ShraMod32( *StartingLcn, 3 ),
  3274. (BitmapSize+7)/8,
  3275. BitmapBcb,
  3276. &Buffer );
  3277. } else {
  3278. NtfsMapStream( IrpContext,
  3279. Vcb->BitmapScb,
  3280. Int64ShraMod32( *StartingLcn, 3 ),
  3281. (BitmapSize+7)/8,
  3282. BitmapBcb,
  3283. &Buffer );
  3284. }
  3285. //
  3286. // And initialize the bitmap
  3287. //
  3288. RtlInitializeBitMap( Bitmap, Buffer, BitmapSize );
  3289. DebugTrace( 0, Dbg, ("StartingLcn <- %016I64x\n", *StartingLcn) );
  3290. DebugTrace( -1, Dbg, ("NtfsMapOrPinPageInBitmap -> VOID\n") );
  3291. return;
  3292. }
  3293. BOOLEAN
  3294. NtfsAddCachedRun (
  3295. IN PIRP_CONTEXT IrpContext,
  3296. IN PVCB Vcb,
  3297. IN LCN StartingLcn,
  3298. IN LONGLONG ClusterCount,
  3299. IN NTFS_RUN_STATE RunState
  3300. )
  3301. /*++
  3302. Routine Description:
  3303. This procedure adds a new run to the cached free space
  3304. bitmap information.
  3305. Arguments:
  3306. Vcb - Supplies the vcb for this operation
  3307. StartingLcn - Supplies the lcn for the run being added
  3308. ClusterCount - Supplies the number of clusters in the run being added
  3309. RunState - Supplies the state of the run being added. This state
  3310. must be either free or allocated.
  3311. Return Value:
  3312. BOOLEAN - TRUE if more entries can be added to the list, FALSE otherwise.
  3313. --*/
  3314. {
  3315. ASSERT_IRP_CONTEXT( IrpContext );
  3316. ASSERT_VCB( Vcb );
  3317. PAGED_CODE();
  3318. DebugTrace( +1, Dbg, ("NtfsAddCachedRun\n") );
  3319. DebugTrace( 0, Dbg, ("StartingLcn = %016I64x\n", StartingLcn) );
  3320. DebugTrace( 0, Dbg, ("ClusterCount = %016I64x\n", ClusterCount) );
  3321. //
  3322. // Based on whether we are adding a free or allocated run we
  3323. // setup or local variables to a point to the right
  3324. // vcb variables
  3325. //
  3326. if (RunState == RunStateFree) {
  3327. //
  3328. // We better not be setting Lcn 0 free.
  3329. //
  3330. if (StartingLcn == 0) {
  3331. ASSERT( FALSE );
  3332. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  3333. }
  3334. //
  3335. // Sanity check that we aren't adding bits beyond the end of the
  3336. // bitmap.
  3337. //
  3338. ASSERT( StartingLcn + ClusterCount <= Vcb->TotalClusters );
  3339. NtfsInsertCachedLcn( &Vcb->CachedRuns,
  3340. StartingLcn,
  3341. ClusterCount );
  3342. } else {
  3343. //
  3344. // Now remove the run from the cached runs because it can potentially already be
  3345. // there.
  3346. //
  3347. NtfsRemoveCachedLcn( &Vcb->CachedRuns,
  3348. StartingLcn,
  3349. ClusterCount );
  3350. }
  3351. DebugTrace( -1, Dbg, ("NtfsAddCachedRun -> VOID\n") );
  3352. return ((Vcb->CachedRuns.Avail - Vcb->CachedRuns.Used + Vcb->CachedRuns.DelLcnCount) > 0);
  3353. }
  3354. #if 0
  3355. VOID
  3356. NtfsMakeSpaceCachedLcn (
  3357. IN PNTFS_CACHED_RUNS CachedRuns,
  3358. IN LCN StartingLcn,
  3359. IN RTL_BITMAP_RUN *RunArray,
  3360. IN ULONG RunCount,
  3361. IN PUSHORT LcnSorted OPTIONAL
  3362. )
  3363. /*++
  3364. Routine Description:
  3365. This procedure attempts to make space in the Lcn-sorted array for RunCount
  3366. new entries in the given Lcn range. This routine will not delete any
  3367. existing entries to create the space because we don't know at this time
  3368. how many will actually end up being inserted into the list. They may not
  3369. be inserted because their run lengths are too small relative to the
  3370. entries already in the list. This call is used because it is more
  3371. efficient to create space once for all the entries than to do so
  3372. individually. In effect, this routine moves windows of deleted entries
  3373. to the desired Lcn position.
  3374. Arguments:
  3375. CachedRuns - Pointer to a cached run structure.
  3376. StartingLcn - Supplies the base Lcn for the runs being added
  3377. RunArray - The bit position and length of each of the free runs.
  3378. The array will be sorted according to length.
  3379. RunCount - Supplies the number of runs being added
  3380. LcnSorted - An optional array of RunCount indices that gives the Lcn
  3381. sort order.
  3382. Return Value:
  3383. None.
  3384. --*/
  3385. {
  3386. PAGED_CODE();
  3387. DebugTrace( +1, Dbg, ("NtfsMakeSpaceCachedLcn\n") );
  3388. DebugTrace( -1, Dbg, ("NtfsMakeSpaceCachedLcn -> VOID\n") );
  3389. return;
  3390. }
  3391. #endif /* 0 */
  3392. VOID
  3393. NtfsAddCachedRunMult (
  3394. IN PIRP_CONTEXT IrpContext,
  3395. IN PVCB Vcb,
  3396. IN LCN StartingLcn,
  3397. IN PRTL_BITMAP_RUN RunArray,
  3398. IN ULONG RunCount
  3399. )
  3400. /*++
  3401. Routine Description:
  3402. This procedure adds multiple new runs to the cached free space
  3403. bitmap information. It is assumed that the new runs fall
  3404. in a close range of Lcn values. As a rule, these runs come from
  3405. a single page of the bitmap.
  3406. Arguments:
  3407. Vcb - Supplies the vcb for this operation
  3408. StartingLcn - Supplies the base Lcn for the runs being added
  3409. RunArray - The bit position and length of each of the free runs.
  3410. The array will be sorted according to length.
  3411. RunCount - Supplies the number of runs being added
  3412. Return Value:
  3413. None.
  3414. --*/
  3415. {
  3416. USHORT Index1;
  3417. PUSHORT LcnSorted = NULL;
  3418. ASSERT_IRP_CONTEXT( IrpContext );
  3419. ASSERT_VCB( Vcb );
  3420. PAGED_CODE();
  3421. DebugTrace( +1, Dbg, ("NtfsAddCachedRunMult\n") );
  3422. DebugTrace( 0, Dbg, ("StartingLcn = %016I64x\n", StartingLcn) );
  3423. DebugTrace( 0, Dbg, ("RunArray = %08lx\n", RunArray) );
  3424. DebugTrace( 0, Dbg, ("RunCount = %08lx\n", RunCount) );
  3425. #if 0
  3426. //
  3427. // Sort the entries by Lcn. It is often the case that at startup we are
  3428. // adding entries that will all fall at the end of the Lcn-sorted list.
  3429. // However, if the entries are not added in Lcn-sorted order there will
  3430. // likely be some moving around of entries in the Lcn-sorted list that
  3431. // could be avoided.
  3432. //
  3433. LcnSorted = NtfsAllocatePoolNoRaise( PagedPool, sizeof( USHORT ) * RunCount );
  3434. if (LcnSorted != NULL) {
  3435. USHORT Index2;
  3436. //
  3437. // Bubble sort the elements.
  3438. //
  3439. for (Index1 = 1, LcnSorted[0] = 0;
  3440. Index1 < RunCount;
  3441. Index1 += 1) {
  3442. for (Index2 = 0; Index2 < Index1; Index2 += 1) {
  3443. if (RunArray[Index1].StartingIndex < RunArray[LcnSorted[Index2]].StartingIndex) {
  3444. //
  3445. // Move the entries from Index2 through Index1 - 1 to the
  3446. // right to make space for the current entry.
  3447. //
  3448. RtlMoveMemory( LcnSorted + Index2 + 1,
  3449. LcnSorted + Index2,
  3450. sizeof( USHORT ) * (Index1 - Index2) );
  3451. break;
  3452. }
  3453. }
  3454. //
  3455. // Write the index into the correctly sorted location.
  3456. //
  3457. LcnSorted[Index2] = Index1;
  3458. }
  3459. }
  3460. //
  3461. // Make space in the Lcn-sorted array for these new entries.
  3462. // This is done in advance because it is more efficient to create
  3463. // space once for all the entries than to do so individually.
  3464. // The following routine will not delete any existing entries to
  3465. // create the space because we don't know at this time how many will
  3466. // actually end up being inserted into the list. They may not be
  3467. // inserted because their run lengths are too small relative to the
  3468. // entries already in the list.
  3469. //
  3470. NtfsMakeSpaceCachedLcn( &Vcb->CachedRuns,
  3471. StartingLcn,
  3472. RunArray,
  3473. RunCount,
  3474. LcnSorted );
  3475. #endif /* 0 */
  3476. //
  3477. // Insert the new entries.
  3478. //
  3479. for (Index1 = 0; Index1 < RunCount; Index1 += 1) {
  3480. //
  3481. // If not sorted then do the generic insert. The gain for the sorted case
  3482. // that we won't have to do a memory copy for entries we just inserted.
  3483. //
  3484. if (LcnSorted != NULL) {
  3485. (VOID) NtfsAddCachedRun( IrpContext,
  3486. Vcb,
  3487. StartingLcn + RunArray[ LcnSorted[ Index1 ]].StartingIndex,
  3488. (LONGLONG)RunArray[ LcnSorted[ Index1 ]].NumberOfBits,
  3489. RunStateFree );
  3490. } else {
  3491. (VOID) NtfsAddCachedRun( IrpContext,
  3492. Vcb,
  3493. StartingLcn + RunArray[ Index1 ].StartingIndex,
  3494. (LONGLONG)RunArray[ Index1 ].NumberOfBits,
  3495. RunStateFree );
  3496. }
  3497. }
  3498. if (LcnSorted != NULL) {
  3499. NtfsFreePool( LcnSorted );
  3500. }
  3501. DebugTrace( -1, Dbg, ("NtfsAddCachedRunMult -> VOID\n") );
  3502. return;
  3503. }
  3504. //
  3505. // Local support routine
  3506. //
  3507. VOID
  3508. NtfsReadAheadCachedBitmap (
  3509. IN PIRP_CONTEXT IrpContext,
  3510. IN PVCB Vcb,
  3511. IN LCN StartingLcn
  3512. )
  3513. /*++
  3514. Routine Description:
  3515. This routine does a read ahead of the bitmap into the cached bitmap
  3516. starting at the specified starting lcn.
  3517. Arguments:
  3518. Vcb - Supplies the vcb to use in this operation
  3519. StartingLcn - Supplies the starting lcn to use in this read ahead
  3520. operation.
  3521. Return Value:
  3522. None.
  3523. --*/
  3524. {
  3525. RTL_BITMAP Bitmap;
  3526. PBCB BitmapBcb;
  3527. BOOLEAN StuffAdded;
  3528. LCN BaseLcn;
  3529. ULONG Index;
  3530. LONGLONG Size;
  3531. RTL_BITMAP_RUN RunArray[16];
  3532. ULONG RunArrayIndex;
  3533. ASSERT_IRP_CONTEXT( IrpContext );
  3534. ASSERT_VCB( Vcb );
  3535. PAGED_CODE();
  3536. DebugTrace( +1, Dbg, ("NtfsReadAheadCachedBitmap\n") );
  3537. DebugTrace( 0, Dbg, ("StartingLcn = %016I64x\n", StartingLcn) );
  3538. BitmapBcb = NULL;
  3539. StuffAdded = FALSE;
  3540. try {
  3541. //
  3542. // Check if the lcn index is already in the cached runs info and if it is then
  3543. // our read ahead is done.
  3544. //
  3545. if (NtfsLookupCachedLcn( &Vcb->CachedRuns,
  3546. StartingLcn,
  3547. &BaseLcn,
  3548. &BaseLcn,
  3549. NULL )) {
  3550. try_return( NOTHING );
  3551. }
  3552. //
  3553. // Map in the page containing the starting lcn and compute the bit index for the
  3554. // starting lcn within the bitmap. And bias the bitmap with recently deallocated
  3555. // clusters.
  3556. //
  3557. NtfsMapPageInBitmap( IrpContext, Vcb, StartingLcn, &BaseLcn, &Bitmap, &BitmapBcb );
  3558. StuffAdded = NtfsAddRecentlyDeallocated( Vcb, BaseLcn, &Bitmap );
  3559. Index = (ULONG)(StartingLcn - BaseLcn);
  3560. //
  3561. // Now if the index is clear then we can build up the hint at the starting index, we
  3562. // scan through the bitmap checking the size of the run and then adding the free run
  3563. // to the cached free space mcb
  3564. //
  3565. if (RtlCheckBit( &Bitmap, Index ) == 0) {
  3566. Size = RtlFindNextForwardRunClear( &Bitmap, Index, &Index );
  3567. (VOID) NtfsAddCachedRun( IrpContext, Vcb, StartingLcn, (LONGLONG)Size, RunStateFree );
  3568. }
  3569. //
  3570. // While we have the bitmap loaded we will scan it for a few longest runs
  3571. //
  3572. RunArrayIndex = RtlFindClearRuns( &Bitmap, RunArray, 16, TRUE );
  3573. if (RunArrayIndex > 0) {
  3574. NtfsAddCachedRunMult( IrpContext,
  3575. Vcb,
  3576. BaseLcn,
  3577. RunArray,
  3578. RunArrayIndex );
  3579. }
  3580. try_exit: NOTHING;
  3581. } finally {
  3582. DebugUnwind( NtfsReadAheadCachedBitmap );
  3583. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); }
  3584. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  3585. }
  3586. DebugTrace( -1, Dbg, ("NtfsReadAheadCachedBitmap -> VOID\n") );
  3587. return;
  3588. }
  3589. //
  3590. // Local support routine
  3591. //
  3592. BOOLEAN
  3593. NtfsGetNextHoleToFill (
  3594. IN PIRP_CONTEXT IrpContext,
  3595. IN PNTFS_MCB Mcb,
  3596. IN VCN StartingVcn,
  3597. IN VCN EndingVcn,
  3598. OUT PVCN VcnToFill,
  3599. OUT PLONGLONG ClusterCountToFill,
  3600. OUT PLCN PrecedingLcn
  3601. )
  3602. /*++
  3603. Routine Description:
  3604. This routine takes a specified range within an mcb and returns the to
  3605. caller the first run that is not allocated to any lcn within the range
  3606. Arguments:
  3607. Mcb - Supplies the mcb to use in this operation
  3608. StartingVcn - Supplies the starting vcn to search from
  3609. EndingVcn - Supplies the ending vcn to search to
  3610. VcnToFill - Receives the first Vcn within the range that is unallocated
  3611. ClusterCountToFill - Receives the size of the free run
  3612. PrecedingLcn - Receives the Lcn of the allocated cluster preceding the
  3613. free run. If the free run starts at Vcn 0 then the preceding lcn
  3614. is -1.
  3615. Return Value:
  3616. BOOLEAN - TRUE if there is another hole to fill and FALSE otherwise
  3617. --*/
  3618. {
  3619. BOOLEAN Result;
  3620. BOOLEAN McbHit;
  3621. LCN Lcn;
  3622. LONGLONG MaximumRunSize;
  3623. LONGLONG LlTemp1;
  3624. ASSERT_IRP_CONTEXT( IrpContext );
  3625. PAGED_CODE();
  3626. DebugTrace( +1, Dbg, ("NtfsGetNextHoleToFill\n") );
  3627. DebugTrace( 0, Dbg, ("StartingVcn = %016I64x\n", StartingVcn) );
  3628. DebugTrace( 0, Dbg, ("EndingVcn = %016I64x\n", EndingVcn) );
  3629. //
  3630. // We'll first assume that there is not a hole to fill unless
  3631. // the following loop finds one to fill
  3632. //
  3633. Result = FALSE;
  3634. for (*VcnToFill = StartingVcn;
  3635. *VcnToFill <= EndingVcn;
  3636. *VcnToFill += *ClusterCountToFill) {
  3637. //
  3638. // Check if the hole is already filled and it so then do nothing but loop back up
  3639. // to the top of our loop and try again
  3640. //
  3641. if ((McbHit = NtfsLookupNtfsMcbEntry( Mcb, *VcnToFill, &Lcn, ClusterCountToFill, NULL, NULL, NULL, NULL )) &&
  3642. (Lcn != UNUSED_LCN)) {
  3643. NOTHING;
  3644. } else {
  3645. //
  3646. // We have a hole to fill so now compute the maximum size hole that
  3647. // we are allowed to fill and then check if we got an miss on the lookup
  3648. // and need to set cluster count or if the size we got back is too large
  3649. //
  3650. MaximumRunSize = (EndingVcn - *VcnToFill) + 1;
  3651. if (!McbHit || (*ClusterCountToFill > MaximumRunSize)) {
  3652. *ClusterCountToFill = MaximumRunSize;
  3653. }
  3654. //
  3655. // Now set the preceding lcn to either -1 if there isn't a preceding vcn or
  3656. // set it to the lcn of the preceding vcn
  3657. //
  3658. if (*VcnToFill == 0) {
  3659. *PrecedingLcn = UNUSED_LCN;
  3660. } else {
  3661. LlTemp1 = *VcnToFill - 1;
  3662. if (!NtfsLookupNtfsMcbEntry( Mcb, LlTemp1, PrecedingLcn, NULL, NULL, NULL, NULL, NULL )) {
  3663. *PrecedingLcn = UNUSED_LCN;
  3664. }
  3665. }
  3666. //
  3667. // We found a hole so set our result to TRUE and break out of the loop
  3668. //
  3669. Result = TRUE;
  3670. break;
  3671. }
  3672. }
  3673. DebugTrace( 0, Dbg, ("VcnToFill <- %016I64x\n", *VcnToFill) );
  3674. DebugTrace( 0, Dbg, ("ClusterCountToFill <- %016I64x\n", *ClusterCountToFill) );
  3675. DebugTrace( 0, Dbg, ("PrecedingLcn <- %016I64x\n", *PrecedingLcn) );
  3676. DebugTrace( -1, Dbg, ("NtfsGetNextHoleToFill -> %08lx\n", Result) );
  3677. return Result;
  3678. }
  3679. //
  3680. // Local support routine
  3681. //
  3682. LONGLONG
  3683. NtfsScanMcbForRealClusterCount (
  3684. IN PIRP_CONTEXT IrpContext,
  3685. IN PNTFS_MCB Mcb,
  3686. IN VCN StartingVcn,
  3687. IN VCN EndingVcn
  3688. )
  3689. /*++
  3690. Routine Description:
  3691. This routine scans the input mcb within the specified range and returns
  3692. to the caller the exact number of clusters that a really free (i.e.,
  3693. not mapped to any Lcn) within the range.
  3694. Arguments:
  3695. Mcb - Supplies the Mcb used in this operation
  3696. StartingVcn - Supplies the starting vcn to search from
  3697. EndingVcn - Supplies the ending vcn to search to
  3698. Return Value:
  3699. LONGLONG - Returns the number of unassigned clusters from
  3700. StartingVcn to EndingVcn inclusive within the mcb.
  3701. --*/
  3702. {
  3703. LONGLONG FreeCount;
  3704. VCN Vcn;
  3705. LCN Lcn;
  3706. LONGLONG RunSize;
  3707. ASSERT_IRP_CONTEXT( IrpContext );
  3708. PAGED_CODE();
  3709. DebugTrace( +1, Dbg, ("NtfsScanMcbForRealClusterCount\n") );
  3710. DebugTrace( 0, Dbg, ("StartingVcn = %016I64x\n", StartingVcn) );
  3711. DebugTrace( 0, Dbg, ("EndingVcn = %016I64x\n", EndingVcn) );
  3712. //
  3713. // First compute free count as if the entire run is already unallocated
  3714. // and the in the following loop we march through the mcb looking for
  3715. // actual allocation and decrementing the free count appropriately
  3716. //
  3717. FreeCount = (EndingVcn - StartingVcn) + 1;
  3718. for (Vcn = StartingVcn; Vcn <= EndingVcn; Vcn = Vcn + RunSize) {
  3719. //
  3720. // Lookup the mcb entry and if we get back false then we're overrun
  3721. // the mcb and therefore nothing else above it can be allocated.
  3722. //
  3723. if (!NtfsLookupNtfsMcbEntry( Mcb, Vcn, &Lcn, &RunSize, NULL, NULL, NULL, NULL )) {
  3724. break;
  3725. }
  3726. //
  3727. // If the lcn we got back is not -1 then this run is actually already
  3728. // allocated, so first check if the run size puts us over the ending
  3729. // vcn and adjust as necessary and then decrement the free count
  3730. // by the run size
  3731. //
  3732. if (Lcn != UNUSED_LCN) {
  3733. if (RunSize > ((EndingVcn - Vcn) + 1)) {
  3734. RunSize = (EndingVcn - Vcn) + 1;
  3735. }
  3736. FreeCount = FreeCount - RunSize;
  3737. }
  3738. }
  3739. DebugTrace( -1, Dbg, ("NtfsScanMcbForRealClusterCount -> %016I64x\n", FreeCount) );
  3740. return FreeCount;
  3741. }
  3742. //
  3743. // Local support routine, only defined with ntfs debug version
  3744. //
  3745. #ifdef NTFSDBG
  3746. ULONG
  3747. NtfsDumpCachedMcbInformation (
  3748. IN PVCB Vcb
  3749. )
  3750. /*++
  3751. Routine Description:
  3752. This routine dumps out the cached bitmap information
  3753. Arguments:
  3754. Vcb - Supplies the Vcb used by this operation
  3755. Return Value:
  3756. ULONG - 1.
  3757. --*/
  3758. {
  3759. DbgPrint("Dump BitMpSup Information, Vcb@ %08lx\n", Vcb);
  3760. DbgPrint("TotalCluster: %016I64x\n", Vcb->TotalClusters);
  3761. DbgPrint("FreeClusters: %016I64x\n", Vcb->FreeClusters);
  3762. return 1;
  3763. }
  3764. #endif // NTFSDBG
  3765. //
  3766. // The rest of this module implements the record allocation routines
  3767. //
  3768. VOID
  3769. NtfsInitializeRecordAllocation (
  3770. IN PIRP_CONTEXT IrpContext,
  3771. IN PSCB DataScb,
  3772. IN PATTRIBUTE_ENUMERATION_CONTEXT BitmapAttribute,
  3773. IN ULONG BytesPerRecord,
  3774. IN ULONG ExtendGranularity,
  3775. IN ULONG TruncateGranularity,
  3776. IN OUT PRECORD_ALLOCATION_CONTEXT RecordAllocationContext
  3777. )
  3778. /*++
  3779. Routine Description:
  3780. This routine initializes the record allocation context used for
  3781. allocating and deallocating fixed sized records from a data stream.
  3782. Note that the bitmap attribute size must always be at least a multiple
  3783. of 32 bits. However the data scb does not need to contain that many
  3784. records. If in the course of allocating a new record we discover that
  3785. the data scb is too small we will then add allocation to the data scb.
  3786. Arguments:
  3787. DataScb - Supplies the Scb representing the data stream that is being
  3788. divided into fixed sized records with each bit in the bitmap corresponding
  3789. to one record in the data stream
  3790. BitmapAttribute - Supplies the enumeration context for the bitmap
  3791. attribute. The attribute can either be resident or nonresident
  3792. and this routine will handle both cases properly.
  3793. BytesPerRecord - Supplies the size of the homogenous records that
  3794. that the data stream is being divided into.
  3795. ExtendGranularity - Supplies the number of records (i.e., allocation units
  3796. to extend the data scb by each time).
  3797. TruncateGranularity - Supplies the number of records to use when truncating
  3798. the data scb. That is if the end of the data stream contains the
  3799. specified number of free records then we truncate.
  3800. RecordAllocationContext - Supplies the memory for an context record that is
  3801. utilized by this record allocation routines.
  3802. Return Value:
  3803. None.
  3804. --*/
  3805. {
  3806. PATTRIBUTE_RECORD_HEADER AttributeRecordHeader;
  3807. RTL_BITMAP Bitmap;
  3808. ULONG ClearLength;
  3809. ULONG ClearIndex;
  3810. ASSERT_IRP_CONTEXT( IrpContext );
  3811. ASSERT_SCB( DataScb );
  3812. PAGED_CODE();
  3813. DebugTrace( +1, Dbg, ("NtfsInitializeRecordAllocation\n") );
  3814. ASSERT( BytesPerRecord * ExtendGranularity >= DataScb->Vcb->BytesPerCluster );
  3815. ASSERT( BytesPerRecord * TruncateGranularity >= DataScb->Vcb->BytesPerCluster );
  3816. //
  3817. // First zero out the context record except for the data scb.
  3818. //
  3819. RtlZeroMemory( &RecordAllocationContext->BitmapScb,
  3820. sizeof(RECORD_ALLOCATION_CONTEXT) -
  3821. FIELD_OFFSET( RECORD_ALLOCATION_CONTEXT, BitmapScb ));
  3822. //
  3823. // And then set the fields in the context record that do not depend on
  3824. // whether the bitmap attribute is resident or not
  3825. //
  3826. RecordAllocationContext->DataScb = DataScb;
  3827. RecordAllocationContext->BytesPerRecord = BytesPerRecord;
  3828. RecordAllocationContext->ExtendGranularity = ExtendGranularity;
  3829. RecordAllocationContext->TruncateGranularity = TruncateGranularity;
  3830. //
  3831. // Set up our hint fields.
  3832. //
  3833. RecordAllocationContext->LowestDeallocatedIndex = MAXULONG;
  3834. if (DataScb == DataScb->Vcb->MftScb) {
  3835. RecordAllocationContext->StartingHint = FIRST_USER_FILE_NUMBER;
  3836. } else {
  3837. RecordAllocationContext->StartingHint = 0;
  3838. }
  3839. //
  3840. // Now get a reference to the bitmap record header and then take two
  3841. // different paths depending if the bitmap attribute is resident or not
  3842. //
  3843. AttributeRecordHeader = NtfsFoundAttribute(BitmapAttribute);
  3844. if (NtfsIsAttributeResident(AttributeRecordHeader)) {
  3845. ASSERTMSG("bitmap must be multiple quadwords", AttributeRecordHeader->Form.Resident.ValueLength % 8 == 0);
  3846. //
  3847. // For a resident bitmap attribute the bitmap scb field is null and we
  3848. // set the bitmap size from the value length. Also we will initialize
  3849. // our local bitmap variable and determine the number of free bits
  3850. // current available.
  3851. //
  3852. //
  3853. RecordAllocationContext->BitmapScb = NULL;
  3854. RecordAllocationContext->CurrentBitmapSize = 8 * AttributeRecordHeader->Form.Resident.ValueLength;
  3855. RtlInitializeBitMap( &Bitmap,
  3856. (PULONG)NtfsAttributeValue( AttributeRecordHeader ),
  3857. RecordAllocationContext->CurrentBitmapSize );
  3858. RecordAllocationContext->NumberOfFreeBits = RtlNumberOfClearBits( &Bitmap );
  3859. ClearLength = RtlFindLastBackwardRunClear( &Bitmap,
  3860. RecordAllocationContext->CurrentBitmapSize - 1,
  3861. &ClearIndex );
  3862. } else {
  3863. UNICODE_STRING BitmapName;
  3864. BOOLEAN ReturnedExistingScb;
  3865. PBCB BitmapBcb;
  3866. PVOID BitmapBuffer;
  3867. ASSERTMSG("bitmap must be multiple quadwords", ((ULONG)AttributeRecordHeader->Form.Nonresident.FileSize) % 8 == 0);
  3868. //
  3869. // For a non resident bitmap attribute we better have been given the
  3870. // record header for the first part and not somthing that has spilled
  3871. // into multiple segment records
  3872. //
  3873. ASSERT( AttributeRecordHeader->Form.Nonresident.LowestVcn == 0 );
  3874. BitmapBcb = NULL;
  3875. try {
  3876. ULONG StartingByte;
  3877. ULONG BitsThisPage;
  3878. ULONG BytesThisPage;
  3879. ULONG RemainingBytes;
  3880. ULONG ThisClearIndex;
  3881. ULONG ThisClearLength;
  3882. //
  3883. // Create the bitmap scb for the bitmap attribute
  3884. //
  3885. BitmapName.MaximumLength =
  3886. BitmapName.Length = AttributeRecordHeader->NameLength * sizeof( WCHAR );
  3887. BitmapName.Buffer = Add2Ptr(AttributeRecordHeader, AttributeRecordHeader->NameOffset);
  3888. RecordAllocationContext->BitmapScb = NtfsCreateScb( IrpContext,
  3889. DataScb->Fcb,
  3890. AttributeRecordHeader->TypeCode,
  3891. &BitmapName,
  3892. FALSE,
  3893. &ReturnedExistingScb );
  3894. //
  3895. // Now determine the bitmap size, for now we'll only take bitmap attributes that are
  3896. // no more than 16 pages large.
  3897. //
  3898. RecordAllocationContext->CurrentBitmapSize = 8 * ((ULONG)AttributeRecordHeader->Form.Nonresident.FileSize);
  3899. //
  3900. // Create the stream file if not present.
  3901. //
  3902. if (RecordAllocationContext->BitmapScb->FileObject == NULL) {
  3903. NtfsCreateInternalAttributeStream( IrpContext,
  3904. RecordAllocationContext->BitmapScb,
  3905. TRUE,
  3906. &NtfsInternalUseFile[INITIALIZERECORDALLOCATION_FILE_NUMBER] );
  3907. }
  3908. //
  3909. // Walk through each page of the bitmap and compute the number of set
  3910. // bits and the last set bit in the bitmap.
  3911. //
  3912. RecordAllocationContext->NumberOfFreeBits = 0;
  3913. RemainingBytes = (ULONG) AttributeRecordHeader->Form.Nonresident.FileSize;
  3914. StartingByte = 0;
  3915. ClearLength = 0;
  3916. while (TRUE) {
  3917. BytesThisPage = RemainingBytes;
  3918. if (RemainingBytes > PAGE_SIZE) {
  3919. BytesThisPage = PAGE_SIZE;
  3920. }
  3921. BitsThisPage = BytesThisPage * 8;
  3922. //
  3923. // Now map the bitmap data, initialize our local bitmap variable and
  3924. // calculate the number of free bits currently available
  3925. //
  3926. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  3927. NtfsMapStream( IrpContext,
  3928. RecordAllocationContext->BitmapScb,
  3929. (LONGLONG)StartingByte,
  3930. BytesThisPage,
  3931. &BitmapBcb,
  3932. &BitmapBuffer );
  3933. RtlInitializeBitMap( &Bitmap,
  3934. BitmapBuffer,
  3935. BitsThisPage );
  3936. RecordAllocationContext->NumberOfFreeBits += RtlNumberOfClearBits( &Bitmap );
  3937. //
  3938. // We are interested in remembering the last set bit in this bitmap.
  3939. // If the bitmap ends with a clear run then the last set bit is
  3940. // immediately prior to this clear run. We need to check each page
  3941. // as we go through the bitmap to see if a clear run ends at the end
  3942. // of the current page.
  3943. //
  3944. ThisClearLength = RtlFindLastBackwardRunClear( &Bitmap,
  3945. BitsThisPage - 1,
  3946. &ThisClearIndex );
  3947. //
  3948. // If there is a run and it ends at the end of the page then
  3949. // either combine with a previous run or remember that this is the
  3950. // start of the run.
  3951. //
  3952. if ((ThisClearLength != 0) &&
  3953. ((ThisClearLength + ThisClearIndex) == BitsThisPage)) {
  3954. //
  3955. // If this is the entire page and the previous page ended
  3956. // with a clear run then just extend that run.
  3957. //
  3958. if ((ThisClearIndex == 0) && (ClearLength != 0)) {
  3959. ClearLength += ThisClearLength;
  3960. //
  3961. // Otherwise this is a new clear run. Bias the starting index
  3962. // by the bit offset of this page.
  3963. //
  3964. } else {
  3965. ClearLength = ThisClearLength;
  3966. ClearIndex = ThisClearIndex + (StartingByte * 8);
  3967. }
  3968. //
  3969. // This page does not end with a clear run.
  3970. //
  3971. } else {
  3972. ClearLength = 0;
  3973. }
  3974. //
  3975. // If we are not at the end of the bitmap then update our
  3976. // counters.
  3977. //
  3978. if (RemainingBytes != BytesThisPage) {
  3979. StartingByte += PAGE_SIZE;
  3980. RemainingBytes -= PAGE_SIZE;
  3981. } else {
  3982. break;
  3983. }
  3984. }
  3985. } finally {
  3986. DebugUnwind( NtfsInitializeRecordAllocation );
  3987. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  3988. }
  3989. }
  3990. //
  3991. // With ClearLength and ClearIndex we can now deduce the last set bit in the
  3992. // bitmap
  3993. //
  3994. if ((ClearLength != 0) && ((ClearLength + ClearIndex) == RecordAllocationContext->CurrentBitmapSize)) {
  3995. RecordAllocationContext->IndexOfLastSetBit = ClearIndex - 1;
  3996. } else {
  3997. RecordAllocationContext->IndexOfLastSetBit = RecordAllocationContext->CurrentBitmapSize - 1;
  3998. }
  3999. DebugTrace( -1, Dbg, ("NtfsInitializeRecordAllocation -> VOID\n") );
  4000. return;
  4001. }
  4002. VOID
  4003. NtfsUninitializeRecordAllocation (
  4004. IN PIRP_CONTEXT IrpContext,
  4005. IN OUT PRECORD_ALLOCATION_CONTEXT RecordAllocationContext
  4006. )
  4007. /*++
  4008. Routine Description:
  4009. This routine is used to uninitialize the record allocation context.
  4010. Arguments:
  4011. RecordAllocationContext - Supplies the record allocation context being
  4012. decommissioned.
  4013. Return Value:
  4014. None.
  4015. --*/
  4016. {
  4017. ASSERT_IRP_CONTEXT( IrpContext );
  4018. PAGED_CODE();
  4019. DebugTrace( +1, Dbg, ("NtfsUninitializeRecordAllocation\n") );
  4020. //
  4021. // And then for safe measure zero out the entire record except for the
  4022. // the data Scb.
  4023. //
  4024. RtlZeroMemory( &RecordAllocationContext->BitmapScb,
  4025. sizeof(RECORD_ALLOCATION_CONTEXT) -
  4026. FIELD_OFFSET( RECORD_ALLOCATION_CONTEXT, BitmapScb ));
  4027. DebugTrace( -1, Dbg, ("NtfsUninitializeRecordAllocation -> VOID\n") );
  4028. return;
  4029. }
  4030. ULONG
  4031. NtfsAllocateRecord (
  4032. IN PIRP_CONTEXT IrpContext,
  4033. IN PRECORD_ALLOCATION_CONTEXT RecordAllocationContext,
  4034. IN PATTRIBUTE_ENUMERATION_CONTEXT BitmapAttribute
  4035. )
  4036. /*++
  4037. Routine Description:
  4038. This routine is used to allocate a new record for the specified record
  4039. allocation context.
  4040. It will return the index of a free record in the data scb as denoted by
  4041. the bitmap attribute. If necessary this routine will extend the bitmap
  4042. attribute size (including spilling over to the nonresident case), and
  4043. extend the data scb size.
  4044. On return the record is zeroed.
  4045. Arguments:
  4046. RecordAllocationContext - Supplies the record allocation context used
  4047. in this operation
  4048. BitmapAttribute - Supplies the enumeration context for the bitmap
  4049. attribute. This parameter is ignored if the bitmap attribute is
  4050. non resident, in which case we create an scb for the attribute and
  4051. store a pointer to it in the record allocation context.
  4052. Return Value:
  4053. ULONG - Returns the index of the record just allocated, zero based.
  4054. --*/
  4055. {
  4056. PSCB DataScb;
  4057. LONGLONG DataOffset;
  4058. LONGLONG ClusterCount;
  4059. ULONG BytesPerRecord;
  4060. ULONG ExtendGranularity;
  4061. ULONG TruncateGranularity;
  4062. PULONG CurrentBitmapSize;
  4063. PULONG NumberOfFreeBits;
  4064. PSCB BitmapScb;
  4065. PBCB BitmapBcb;
  4066. RTL_BITMAP Bitmap;
  4067. PUCHAR BitmapBuffer;
  4068. ULONG BitmapOffset;
  4069. ULONG BitmapIndex;
  4070. ULONG BitmapSizeInBytes;
  4071. ULONG BitmapCurrentOffset = 0;
  4072. ULONG BitmapSizeInPages;
  4073. BOOLEAN StuffAdded = FALSE;
  4074. BOOLEAN Rescan;
  4075. ULONG Hint;
  4076. PVCB Vcb;
  4077. ASSERT_IRP_CONTEXT( IrpContext );
  4078. PAGED_CODE();
  4079. DebugTrace( +1, Dbg, ("NtfsAllocateRecord\n") );
  4080. //
  4081. // Synchronize by acquiring the data scb exclusive, as an "end resource".
  4082. // Then use try-finally to insure we free it up.
  4083. //
  4084. DataScb = RecordAllocationContext->DataScb;
  4085. NtfsAcquireExclusiveScb( IrpContext, DataScb );
  4086. try {
  4087. //
  4088. // Remember some values for convenience.
  4089. //
  4090. BytesPerRecord = RecordAllocationContext->BytesPerRecord;
  4091. ExtendGranularity = RecordAllocationContext->ExtendGranularity;
  4092. TruncateGranularity = RecordAllocationContext->TruncateGranularity;
  4093. Vcb = DataScb->Vcb;
  4094. //
  4095. // See if someone made the bitmap nonresident, and we still think
  4096. // it is resident. If so, we must uninitialize and insure reinitialization
  4097. // below.
  4098. //
  4099. if ((RecordAllocationContext->BitmapScb == NULL) &&
  4100. !NtfsIsAttributeResident( NtfsFoundAttribute( BitmapAttribute ))) {
  4101. NtfsUninitializeRecordAllocation( IrpContext,
  4102. RecordAllocationContext );
  4103. RecordAllocationContext->CurrentBitmapSize = MAXULONG;
  4104. }
  4105. //
  4106. // Reinitialize the record context structure if necessary.
  4107. //
  4108. if (RecordAllocationContext->CurrentBitmapSize == MAXULONG) {
  4109. NtfsInitializeRecordAllocation( IrpContext,
  4110. DataScb,
  4111. BitmapAttribute,
  4112. BytesPerRecord,
  4113. ExtendGranularity,
  4114. TruncateGranularity,
  4115. RecordAllocationContext );
  4116. }
  4117. BitmapScb = RecordAllocationContext->BitmapScb;
  4118. CurrentBitmapSize = &RecordAllocationContext->CurrentBitmapSize;
  4119. NumberOfFreeBits = &RecordAllocationContext->NumberOfFreeBits;
  4120. BitmapSizeInBytes = *CurrentBitmapSize / 8;
  4121. Hint = RecordAllocationContext->StartingHint;
  4122. //
  4123. // We will do different operations based on whether the bitmap is resident or nonresident
  4124. // The first case we will handle is the resident bitmap.
  4125. //
  4126. if (BitmapScb == NULL) {
  4127. BOOLEAN SizeExtended = FALSE;
  4128. UCHAR NewByte;
  4129. //
  4130. // Now now initialize the local bitmap variable and hunt for that free bit
  4131. //
  4132. BitmapBuffer = (PUCHAR) NtfsAttributeValue( NtfsFoundAttribute( BitmapAttribute ));
  4133. RtlInitializeBitMap( &Bitmap,
  4134. (PULONG)BitmapBuffer,
  4135. *CurrentBitmapSize );
  4136. StuffAdded = NtfsAddDeallocatedRecords( Vcb, DataScb, 0, &Bitmap );
  4137. BitmapIndex = RtlFindClearBits( &Bitmap, 1, Hint );
  4138. //
  4139. // Check if we have found a free record that can be allocated, If not then extend
  4140. // the size of the bitmap by 64 bits, and set the index to the bit first bit
  4141. // of the extension we just added
  4142. //
  4143. if (BitmapIndex == 0xffffffff) {
  4144. union {
  4145. QUAD Quad;
  4146. UCHAR Uchar[ sizeof(QUAD) ];
  4147. } ZeroQuadWord;
  4148. *(PLARGE_INTEGER)&(ZeroQuadWord.Uchar)[0] = Li0;
  4149. NtfsChangeAttributeValue( IrpContext,
  4150. DataScb->Fcb,
  4151. BitmapSizeInBytes,
  4152. &(ZeroQuadWord.Uchar)[0],
  4153. sizeof( QUAD ),
  4154. TRUE,
  4155. TRUE,
  4156. FALSE,
  4157. TRUE,
  4158. BitmapAttribute );
  4159. BitmapIndex = *CurrentBitmapSize;
  4160. *CurrentBitmapSize += BITMAP_EXTEND_GRANULARITY;
  4161. *NumberOfFreeBits += BITMAP_EXTEND_GRANULARITY;
  4162. BitmapSizeInBytes += (BITMAP_EXTEND_GRANULARITY / 8);
  4163. SizeExtended = TRUE;
  4164. //
  4165. // We now know that the byte value we should start with is 0
  4166. // We cannot safely access the bitmap attribute any more because
  4167. // it may have moved.
  4168. //
  4169. NewByte = 0;
  4170. } else {
  4171. //
  4172. // Capture the current value of the byte for the index if we
  4173. // are not extending. Notice that we always take this from the
  4174. // unbiased original bitmap.
  4175. //
  4176. NewByte = BitmapBuffer[ BitmapIndex / 8 ];
  4177. }
  4178. //
  4179. // Check if we made the Bitmap go non-resident and if so then
  4180. // we will reinitialize the record allocation context and fall through
  4181. // to the non-resident case
  4182. //
  4183. if (SizeExtended && !NtfsIsAttributeResident( NtfsFoundAttribute( BitmapAttribute ))) {
  4184. NtfsUninitializeRecordAllocation( IrpContext,
  4185. RecordAllocationContext );
  4186. NtfsInitializeRecordAllocation( IrpContext,
  4187. DataScb,
  4188. BitmapAttribute,
  4189. BytesPerRecord,
  4190. ExtendGranularity,
  4191. TruncateGranularity,
  4192. RecordAllocationContext );
  4193. BitmapScb = RecordAllocationContext->BitmapScb;
  4194. ASSERT( BitmapScb != NULL );
  4195. //
  4196. // Snapshot the bitmap in case we modify it later on - we automatically
  4197. // snapped the data scb when we acquired it above
  4198. //
  4199. NtfsSnapshotScb( IrpContext, BitmapScb );
  4200. } else {
  4201. //
  4202. // Index is now the free bit so set the bit in the bitmap and also change
  4203. // the byte containing the bit in the attribute. Be careful to set the
  4204. // bit in the byte from the *original* bitmap, and not the one we merged
  4205. // the recently-deallocated bits with.
  4206. //
  4207. ASSERT( !FlagOn( NewByte, BitMask[BitmapIndex % 8]) );
  4208. SetFlag( NewByte, BitMask[BitmapIndex % 8] );
  4209. NtfsChangeAttributeValue( IrpContext,
  4210. DataScb->Fcb,
  4211. BitmapIndex / 8,
  4212. &NewByte,
  4213. 1,
  4214. FALSE,
  4215. FALSE,
  4216. FALSE,
  4217. FALSE,
  4218. BitmapAttribute );
  4219. }
  4220. } else {
  4221. //
  4222. // Snapshot the bitmap in case we modify it later on - we automatically
  4223. // snapped the data scb when we acquired it above
  4224. //
  4225. NtfsSnapshotScb( IrpContext, BitmapScb );
  4226. }
  4227. //
  4228. // Use a loop here to handle the extreme case where extending the allocation
  4229. // of the volume bitmap causes us to renter this routine recursively.
  4230. // In that case the top level guy will fail expecting the first bit to
  4231. // be available in the added clusters. Instead we will return to the
  4232. // top of this loop after extending the bitmap and just do our normal
  4233. // scan.
  4234. //
  4235. while (BitmapScb != NULL) {
  4236. ULONG SizeToPin;
  4237. ULONG HoleIndex;
  4238. BitmapBcb = NULL;
  4239. Rescan = FALSE;
  4240. HoleIndex = 0;
  4241. try {
  4242. if (!FlagOn( BitmapScb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  4243. NtfsUpdateScbFromAttribute( IrpContext, BitmapScb, NULL );
  4244. }
  4245. //
  4246. // Snapshot the Scb values in case we change any of them.
  4247. //
  4248. NtfsSnapshotScb( IrpContext, BitmapScb );
  4249. //
  4250. // Create the stream file if not present.
  4251. //
  4252. if (BitmapScb->FileObject == NULL) {
  4253. NtfsCreateInternalAttributeStream( IrpContext,
  4254. BitmapScb,
  4255. FALSE,
  4256. &NtfsInternalUseFile[DEALLOCATERECORD_FILE_NUMBER] );
  4257. }
  4258. //
  4259. // Remember the starting offset for the page containing the hint.
  4260. //
  4261. BitmapCurrentOffset = (Hint / 8) & ~(PAGE_SIZE - 1);
  4262. Hint &= (BITS_PER_PAGE - 1);
  4263. BitmapSizeInPages = (ULONG) ROUND_TO_PAGES( BitmapSizeInBytes );
  4264. //
  4265. // Loop for the size of the bitmap plus one page, so that we will
  4266. // retry the initial page once starting from a hint offset of 0.
  4267. //
  4268. for (BitmapOffset = 0;
  4269. BitmapOffset <= BitmapSizeInPages;
  4270. BitmapOffset += PAGE_SIZE, BitmapCurrentOffset += PAGE_SIZE) {
  4271. ULONG LocalHint;
  4272. //
  4273. // If our current position is past the end of the bitmap
  4274. // then go to the beginning of the bitmap.
  4275. //
  4276. if (BitmapCurrentOffset >= BitmapSizeInBytes) {
  4277. BitmapCurrentOffset = 0;
  4278. }
  4279. //
  4280. // If this is the Mft and there are more than the system
  4281. // files in the first cluster of the Mft then move past
  4282. // the first cluster.
  4283. //
  4284. if ((BitmapCurrentOffset == 0) &&
  4285. (DataScb == Vcb->MftScb) &&
  4286. (Vcb->FileRecordsPerCluster > FIRST_USER_FILE_NUMBER) &&
  4287. (Hint < Vcb->FileRecordsPerCluster)) {
  4288. Hint = Vcb->FileRecordsPerCluster;
  4289. }
  4290. //
  4291. // Calculate the size to read from this point to the end of
  4292. // bitmap, or a page, whichever is less.
  4293. //
  4294. SizeToPin = BitmapSizeInBytes - BitmapCurrentOffset;
  4295. if (SizeToPin > PAGE_SIZE) { SizeToPin = PAGE_SIZE; }
  4296. //
  4297. // Unpin any Bcb from a previous loop.
  4298. //
  4299. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE; }
  4300. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  4301. //
  4302. // Read the desired bitmap page.
  4303. //
  4304. NtfsPinStream( IrpContext,
  4305. BitmapScb,
  4306. (LONGLONG)BitmapCurrentOffset,
  4307. SizeToPin,
  4308. &BitmapBcb,
  4309. &BitmapBuffer );
  4310. //
  4311. // Initialize the bitmap and search for a free bit.
  4312. //
  4313. RtlInitializeBitMap( &Bitmap, (PULONG) BitmapBuffer, SizeToPin * 8 );
  4314. StuffAdded = NtfsAddDeallocatedRecords( Vcb,
  4315. DataScb,
  4316. BitmapCurrentOffset * 8,
  4317. &Bitmap );
  4318. //
  4319. // We make a loop here to test whether the index found is
  4320. // within an Mft hole. We will always use a hole last.
  4321. //
  4322. LocalHint = Hint;
  4323. while (TRUE) {
  4324. BitmapIndex = RtlFindClearBits( &Bitmap, 1, LocalHint );
  4325. //
  4326. // If this is the Mft Scb then check if this is a hole.
  4327. //
  4328. if ((BitmapIndex != 0xffffffff) &&
  4329. (DataScb == Vcb->MftScb)) {
  4330. ULONG ThisIndex;
  4331. ULONG HoleCount;
  4332. ThisIndex = BitmapIndex + (BitmapCurrentOffset * 8);
  4333. if (NtfsIsMftIndexInHole( IrpContext,
  4334. Vcb,
  4335. ThisIndex,
  4336. &HoleCount )) {
  4337. //
  4338. // There is a hole. Save this index if we haven't
  4339. // already saved one. If we can't find an index
  4340. // not part of a hole we will use this instead of
  4341. // extending the file.
  4342. //
  4343. if (HoleIndex == 0) {
  4344. HoleIndex = ThisIndex;
  4345. }
  4346. //
  4347. // Now update the hint and try this page again
  4348. // unless the reaches to the end of the page.
  4349. //
  4350. if (BitmapIndex + HoleCount < SizeToPin * 8) {
  4351. //
  4352. // Bias the bitmap with these Mft holes
  4353. // so the bitmap package doesn't see
  4354. // them if it rescans from the
  4355. // start of the page.
  4356. //
  4357. if (!StuffAdded) {
  4358. PVOID NewBuffer;
  4359. NewBuffer = NtfsAllocatePool(PagedPool, SizeToPin );
  4360. RtlCopyMemory( NewBuffer, Bitmap.Buffer, SizeToPin );
  4361. Bitmap.Buffer = NewBuffer;
  4362. StuffAdded = TRUE;
  4363. }
  4364. RtlSetBits( &Bitmap,
  4365. BitmapIndex,
  4366. HoleCount );
  4367. LocalHint = BitmapIndex + HoleCount;
  4368. continue;
  4369. }
  4370. //
  4371. // Store a -1 in Index to show we don't have
  4372. // anything yet.
  4373. //
  4374. BitmapIndex = 0xffffffff;
  4375. }
  4376. }
  4377. break;
  4378. }
  4379. //
  4380. // If we found something, then leave the loop.
  4381. //
  4382. if (BitmapIndex != 0xffffffff) {
  4383. break;
  4384. }
  4385. //
  4386. // If we get here, we could not find anything in the page of
  4387. // the hint, so clear out the page offset from the hint.
  4388. //
  4389. Hint = 0;
  4390. }
  4391. //
  4392. // Now check if we have located a record that can be allocated, If not then extend
  4393. // the size of the bitmap by 64 bits.
  4394. //
  4395. if (BitmapIndex == 0xffffffff) {
  4396. //
  4397. // Cleanup from previous loop.
  4398. //
  4399. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE; }
  4400. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  4401. //
  4402. // If we have a hole index it means that we found a free record but
  4403. // it exists in a hole. Let's go back to this page and set up
  4404. // to fill in the hole. We will do an unsafe test of the
  4405. // defrag permitted flag. This is OK here because once set it
  4406. // will only go to the non-set state in order to halt
  4407. // future defragging.
  4408. //
  4409. if ((HoleIndex != 0) && FlagOn( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED )) {
  4410. //
  4411. // Start by filling this hole.
  4412. //
  4413. NtfsCheckRecordStackUsage( IrpContext );
  4414. NtfsFillMftHole( IrpContext, Vcb, HoleIndex );
  4415. //
  4416. // Since filling the Mft hole may cause us to allocate
  4417. // a bit we will go back to the start of the routine
  4418. // and scan starting from the hole we just filled in.
  4419. //
  4420. Hint = HoleIndex;
  4421. Rescan = TRUE;
  4422. try_return( NOTHING );
  4423. } else {
  4424. //
  4425. // Allocate the first bit past the end of the bitmap.
  4426. //
  4427. BitmapIndex = *CurrentBitmapSize & (BITS_PER_PAGE - 1);
  4428. //
  4429. // Now advance the sizes and calculate the size in bytes to
  4430. // read.
  4431. //
  4432. *CurrentBitmapSize += BITMAP_EXTEND_GRANULARITY;
  4433. *NumberOfFreeBits += BITMAP_EXTEND_GRANULARITY;
  4434. //
  4435. // Calculate the size to read from this point to the end of
  4436. // bitmap.
  4437. //
  4438. BitmapSizeInBytes += BITMAP_EXTEND_GRANULARITY / 8;
  4439. BitmapCurrentOffset = BitmapScb->Header.FileSize.LowPart & ~(PAGE_SIZE - 1);
  4440. SizeToPin = BitmapSizeInBytes - BitmapCurrentOffset;
  4441. //
  4442. // Check for allocation first.
  4443. //
  4444. if (BitmapScb->Header.AllocationSize.LowPart < BitmapSizeInBytes) {
  4445. //
  4446. // Calculate number of clusters to next page boundary, and allocate
  4447. // that much.
  4448. //
  4449. ClusterCount = ((BitmapSizeInBytes + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
  4450. ClusterCount = LlClustersFromBytes( Vcb,
  4451. ((ULONG) ClusterCount - BitmapScb->Header.AllocationSize.LowPart) );
  4452. NtfsCheckRecordStackUsage( IrpContext );
  4453. NtfsAddAllocation( IrpContext,
  4454. BitmapScb->FileObject,
  4455. BitmapScb,
  4456. LlClustersFromBytes( Vcb,
  4457. BitmapScb->Header.AllocationSize.QuadPart ),
  4458. ClusterCount,
  4459. FALSE,
  4460. NULL );
  4461. }
  4462. //
  4463. // Tell the cache manager about the new file size.
  4464. //
  4465. BitmapScb->Header.FileSize.QuadPart = BitmapSizeInBytes;
  4466. CcSetFileSizes( BitmapScb->FileObject,
  4467. (PCC_FILE_SIZES)&BitmapScb->Header.AllocationSize );
  4468. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE; }
  4469. //
  4470. // Read the desired bitmap page.
  4471. //
  4472. NtfsPinStream( IrpContext,
  4473. BitmapScb,
  4474. (LONGLONG) BitmapCurrentOffset,
  4475. SizeToPin,
  4476. &BitmapBcb,
  4477. &BitmapBuffer );
  4478. //
  4479. // If we have just moved to the next page of the bitmap then
  4480. // set this page dirty so it doesn't leave memory while we
  4481. // twiddle valid data length. Otherwise it will be reread after
  4482. // we advance valid data and we will get garbage data from the
  4483. // disk.
  4484. //
  4485. if (FlagOn( BitmapSizeInBytes, PAGE_SIZE - 1 ) <= BITMAP_EXTEND_GRANULARITY / 8) {
  4486. *((volatile ULONG *) BitmapBuffer) = *((PULONG) BitmapBuffer);
  4487. CcSetDirtyPinnedData( BitmapBcb, NULL );
  4488. }
  4489. //
  4490. // Initialize the bitmap.
  4491. //
  4492. RtlInitializeBitMap( &Bitmap, (PULONG) BitmapBuffer, SizeToPin * 8 );
  4493. //
  4494. // Now look up a free bit in this page. We don't trust
  4495. // the index we already had since growing the MftBitmap
  4496. // allocation may have caused another bit in the bitmap
  4497. // to be set.
  4498. //
  4499. BitmapIndex = RtlFindClearBits( &Bitmap, 1, BitmapIndex );
  4500. //
  4501. // Update the ValidDataLength, now that we have read (and possibly
  4502. // zeroed) the page.
  4503. //
  4504. BitmapScb->Header.ValidDataLength.QuadPart = BitmapSizeInBytes;
  4505. NtfsWriteFileSizes( IrpContext,
  4506. BitmapScb,
  4507. &BitmapScb->Header.ValidDataLength.QuadPart,
  4508. TRUE,
  4509. TRUE,
  4510. TRUE );
  4511. }
  4512. }
  4513. //
  4514. // We can only make this check if it is not restart, because we have
  4515. // no idea whether the update is applied or not. Raise corrupt if
  4516. // the bits are not clear to prevent double allocation.
  4517. //
  4518. if (!RtlAreBitsClear( &Bitmap, BitmapIndex, 1 )) {
  4519. ASSERTMSG("Cannot set bits that are not clear ", FALSE );
  4520. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  4521. }
  4522. //
  4523. // Set the bit by calling the same routine used at restart.
  4524. // But first check if we should revert back to the orginal bitmap
  4525. // buffer.
  4526. //
  4527. if (StuffAdded) {
  4528. NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE;
  4529. Bitmap.Buffer = (PULONG) BitmapBuffer;
  4530. }
  4531. //
  4532. // Now log this change as well.
  4533. //
  4534. {
  4535. BITMAP_RANGE BitmapRange;
  4536. BitmapRange.BitMapOffset = BitmapIndex;
  4537. BitmapRange.NumberOfBits = 1;
  4538. (VOID) NtfsWriteLog( IrpContext,
  4539. BitmapScb,
  4540. BitmapBcb,
  4541. SetBitsInNonresidentBitMap,
  4542. &BitmapRange,
  4543. sizeof(BITMAP_RANGE),
  4544. ClearBitsInNonresidentBitMap,
  4545. &BitmapRange,
  4546. sizeof(BITMAP_RANGE),
  4547. BitmapCurrentOffset,
  4548. 0,
  4549. 0,
  4550. SizeToPin );
  4551. NtfsRestartSetBitsInBitMap( IrpContext,
  4552. &Bitmap,
  4553. BitmapIndex,
  4554. 1 );
  4555. }
  4556. try_exit: NOTHING;
  4557. } finally {
  4558. DebugUnwind( NtfsAllocateRecord );
  4559. //
  4560. // Reinitialize the context on any error.
  4561. //
  4562. if (AbnormalTermination()) {
  4563. RecordAllocationContext->CurrentBitmapSize = MAXULONG;
  4564. }
  4565. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE; }
  4566. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  4567. }
  4568. //
  4569. // If we added Mft allocation then go to the top of the loop.
  4570. //
  4571. if (Rescan) { continue; }
  4572. //
  4573. // The Index at this point is actually relative, so convert it to absolute
  4574. // before rejoining common code.
  4575. //
  4576. BitmapIndex += (BitmapCurrentOffset * 8);
  4577. //
  4578. // Always break out in the normal case.
  4579. //
  4580. break;
  4581. }
  4582. //
  4583. // Now that we've located an index we can subtract the number of free bits in the bitmap
  4584. //
  4585. *NumberOfFreeBits -= 1;
  4586. //
  4587. // Check if we need to extend the data stream.
  4588. //
  4589. DataOffset = UInt32x32To64( BitmapIndex + 1, BytesPerRecord );
  4590. //
  4591. // Now check if we are extending the file. We update the file size and
  4592. // valid data now.
  4593. //
  4594. if (DataOffset > DataScb->Header.FileSize.QuadPart) {
  4595. //
  4596. // Check for allocation first.
  4597. //
  4598. if (DataOffset > DataScb->Header.AllocationSize.QuadPart) {
  4599. //
  4600. // We want to allocate up to the next extend granularity
  4601. // boundary.
  4602. //
  4603. ClusterCount = UInt32x32To64( (BitmapIndex + ExtendGranularity) & ~(ExtendGranularity - 1),
  4604. BytesPerRecord );
  4605. ClusterCount -= DataScb->Header.AllocationSize.QuadPart;
  4606. ClusterCount = LlClustersFromBytesTruncate( Vcb, ClusterCount );
  4607. NtfsCheckRecordStackUsage( IrpContext );
  4608. NtfsAddAllocation( IrpContext,
  4609. DataScb->FileObject,
  4610. DataScb,
  4611. LlClustersFromBytes( Vcb,
  4612. DataScb->Header.AllocationSize.QuadPart ),
  4613. ClusterCount,
  4614. FALSE,
  4615. NULL );
  4616. }
  4617. DataScb->Header.FileSize.QuadPart = DataOffset;
  4618. DataScb->Header.ValidDataLength.QuadPart = DataOffset;
  4619. NtfsWriteFileSizes( IrpContext,
  4620. DataScb,
  4621. &DataScb->Header.ValidDataLength.QuadPart,
  4622. TRUE,
  4623. TRUE,
  4624. TRUE );
  4625. //
  4626. // Tell the cache manager about the new file size.
  4627. //
  4628. CcSetFileSizes( DataScb->FileObject,
  4629. (PCC_FILE_SIZES)&DataScb->Header.AllocationSize );
  4630. //
  4631. // If we didn't extend the file then we have used a free file record in the file.
  4632. // Update our bookeeping count for free file records.
  4633. //
  4634. } else if (DataScb == Vcb->MftScb) {
  4635. DataScb->ScbType.Mft.FreeRecordChange -= 1;
  4636. Vcb->MftFreeRecords -= 1;
  4637. }
  4638. //
  4639. // Now determine if we extended the index of the last set bit
  4640. //
  4641. if ((LONG)BitmapIndex > RecordAllocationContext->IndexOfLastSetBit) {
  4642. RecordAllocationContext->IndexOfLastSetBit = BitmapIndex;
  4643. }
  4644. } finally {
  4645. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); }
  4646. NtfsReleaseScb( IrpContext, DataScb );
  4647. }
  4648. //
  4649. // Update our hint with this value.
  4650. //
  4651. RecordAllocationContext->StartingHint = BitmapIndex;
  4652. //
  4653. // We shouldn't allocate within the same byte as the reserved index for
  4654. // the Mft.
  4655. //
  4656. ASSERT( (DataScb != DataScb->Vcb->MftScb) ||
  4657. ((BitmapIndex & ~7) != (DataScb->ScbType.Mft.ReservedIndex & ~7)) );
  4658. DebugTrace( -1, Dbg, ("NtfsAllocateRecord -> %08lx\n", BitmapIndex) );
  4659. return BitmapIndex;
  4660. }
  4661. VOID
  4662. NtfsDeallocateRecord (
  4663. IN PIRP_CONTEXT IrpContext,
  4664. IN PRECORD_ALLOCATION_CONTEXT RecordAllocationContext,
  4665. IN ULONG Index,
  4666. IN PATTRIBUTE_ENUMERATION_CONTEXT BitmapAttribute
  4667. )
  4668. /*++
  4669. Routine Description:
  4670. This routine is used to deallocate a record from the specified record
  4671. allocation context.
  4672. If necessary this routine will also shrink the bitmap attribute and
  4673. the data scb (according to the truncation granularity used to initialize
  4674. the allocation context).
  4675. Arguments:
  4676. RecordAllocationContext - Supplies the record allocation context used
  4677. in this operation
  4678. Index - Supplies the index of the record to deallocate, zero based.
  4679. BitmapAttribute - Supplies the enumeration context for the bitmap
  4680. attribute. This parameter is ignored if the bitmap attribute is
  4681. non resident, in which case we create an scb for the attribute and
  4682. store a pointer to it in the record allocation context.
  4683. Return Value:
  4684. None.
  4685. --*/
  4686. {
  4687. PSCB DataScb;
  4688. IO_STATUS_BLOCK Iosb;
  4689. PAGED_CODE();
  4690. ASSERT_IRP_CONTEXT( IrpContext );
  4691. DebugTrace( +1, Dbg, ("NtfsDeallocateRecord\n") );
  4692. //
  4693. // Synchronize by acquiring the data scb exclusive, as an "end resource".
  4694. // Then use try-finally to insure we free it up.
  4695. //
  4696. DataScb = RecordAllocationContext->DataScb;
  4697. NtfsAcquireExclusiveScb( IrpContext, DataScb );
  4698. try {
  4699. PVCB Vcb;
  4700. PSCB BitmapScb;
  4701. RTL_BITMAP Bitmap;
  4702. PLONG IndexOfLastSetBit;
  4703. ULONG BytesPerRecord;
  4704. ULONG TruncateGranularity;
  4705. ULONG ClearIndex;
  4706. ULONG BitmapOffset = 0;
  4707. Vcb = DataScb->Vcb;
  4708. {
  4709. ULONG ExtendGranularity;
  4710. //
  4711. // Remember the current values in the record context structure.
  4712. //
  4713. BytesPerRecord = RecordAllocationContext->BytesPerRecord;
  4714. TruncateGranularity = RecordAllocationContext->TruncateGranularity;
  4715. ExtendGranularity = RecordAllocationContext->ExtendGranularity;
  4716. //
  4717. // See if someone made the bitmap nonresident, and we still think
  4718. // it is resident. If so, we must uninitialize and insure reinitialization
  4719. // below.
  4720. //
  4721. if ((RecordAllocationContext->BitmapScb == NULL)
  4722. && !NtfsIsAttributeResident(NtfsFoundAttribute(BitmapAttribute))) {
  4723. NtfsUninitializeRecordAllocation( IrpContext,
  4724. RecordAllocationContext );
  4725. RecordAllocationContext->CurrentBitmapSize = MAXULONG;
  4726. }
  4727. //
  4728. // Reinitialize the record context structure if necessary.
  4729. //
  4730. if (RecordAllocationContext->CurrentBitmapSize == MAXULONG) {
  4731. NtfsInitializeRecordAllocation( IrpContext,
  4732. DataScb,
  4733. BitmapAttribute,
  4734. BytesPerRecord,
  4735. ExtendGranularity,
  4736. TruncateGranularity,
  4737. RecordAllocationContext );
  4738. }
  4739. }
  4740. BitmapScb = RecordAllocationContext->BitmapScb;
  4741. IndexOfLastSetBit = &RecordAllocationContext->IndexOfLastSetBit;
  4742. //
  4743. // We will do different operations based on whether the bitmap is resident or nonresident
  4744. // The first case will handle the resident bitmap
  4745. //
  4746. if (BitmapScb == NULL) {
  4747. UCHAR NewByte;
  4748. //
  4749. // Initialize the local bitmap
  4750. //
  4751. RtlInitializeBitMap( &Bitmap,
  4752. (PULONG)NtfsAttributeValue( NtfsFoundAttribute( BitmapAttribute )),
  4753. RecordAllocationContext->CurrentBitmapSize );
  4754. //
  4755. // And clear the indicated bit, and also change the byte containing the bit in the
  4756. // attribute
  4757. //
  4758. NewByte = ((PUCHAR)Bitmap.Buffer)[ Index / 8 ];
  4759. ASSERT( FlagOn( NewByte, BitMask[Index % 8]) );
  4760. ClearFlag( NewByte, BitMask[Index % 8] );
  4761. NtfsChangeAttributeValue( IrpContext,
  4762. DataScb->Fcb,
  4763. Index / 8,
  4764. &NewByte,
  4765. 1,
  4766. FALSE,
  4767. FALSE,
  4768. FALSE,
  4769. FALSE,
  4770. BitmapAttribute );
  4771. //
  4772. // Now if the bit set just cleared is the same as the index for the last set bit
  4773. // then we must compute a new last set bit
  4774. //
  4775. if (Index == (ULONG)*IndexOfLastSetBit) {
  4776. RtlFindLastBackwardRunClear( &Bitmap, Index, &ClearIndex );
  4777. }
  4778. } else {
  4779. PBCB BitmapBcb = NULL;
  4780. try {
  4781. ULONG RelativeIndex;
  4782. ULONG SizeToPin;
  4783. PVOID BitmapBuffer;
  4784. //
  4785. // Snapshot the Scb values in case we change any of them.
  4786. //
  4787. if (!FlagOn( BitmapScb->ScbState, SCB_STATE_HEADER_INITIALIZED )) {
  4788. NtfsUpdateScbFromAttribute( IrpContext, BitmapScb, NULL );
  4789. }
  4790. NtfsSnapshotScb( IrpContext, BitmapScb );
  4791. //
  4792. // Create the stream file if not present.
  4793. //
  4794. if (BitmapScb->FileObject == NULL) {
  4795. NtfsCreateInternalAttributeStream( IrpContext,
  4796. BitmapScb,
  4797. FALSE,
  4798. &NtfsInternalUseFile[DEALLOCATERECORD_FILE_NUMBER] );
  4799. }
  4800. //
  4801. // Calculate offset and relative index of the bit we will deallocate,
  4802. // from the nearest page boundary.
  4803. //
  4804. BitmapOffset = Index /8 & ~(PAGE_SIZE - 1);
  4805. RelativeIndex = Index & (BITS_PER_PAGE - 1);
  4806. //
  4807. // Calculate the size to read from this point to the end of
  4808. // bitmap.
  4809. //
  4810. SizeToPin = (RecordAllocationContext->CurrentBitmapSize / 8) - BitmapOffset;
  4811. if (SizeToPin > PAGE_SIZE) {
  4812. SizeToPin = PAGE_SIZE;
  4813. }
  4814. NtfsPinStream( IrpContext,
  4815. BitmapScb,
  4816. BitmapOffset,
  4817. SizeToPin,
  4818. &BitmapBcb,
  4819. &BitmapBuffer );
  4820. RtlInitializeBitMap( &Bitmap, BitmapBuffer, SizeToPin * 8 );
  4821. //
  4822. // We can only make this check if it is not restart, because we have
  4823. // no idea whether the update is applied or not. Raise corrupt if
  4824. // we are trying to clear bits which aren't set.
  4825. //
  4826. if (!RtlAreBitsSet( &Bitmap, RelativeIndex, 1 )) {
  4827. ASSERTMSG("Cannot clear bits that are not set ", FALSE );
  4828. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  4829. }
  4830. //
  4831. // Now log this change as well.
  4832. //
  4833. {
  4834. BITMAP_RANGE BitmapRange;
  4835. BitmapRange.BitMapOffset = RelativeIndex;
  4836. BitmapRange.NumberOfBits = 1;
  4837. (VOID) NtfsWriteLog( IrpContext,
  4838. BitmapScb,
  4839. BitmapBcb,
  4840. ClearBitsInNonresidentBitMap,
  4841. &BitmapRange,
  4842. sizeof(BITMAP_RANGE),
  4843. SetBitsInNonresidentBitMap,
  4844. &BitmapRange,
  4845. sizeof(BITMAP_RANGE),
  4846. BitmapOffset,
  4847. 0,
  4848. 0,
  4849. SizeToPin );
  4850. }
  4851. //
  4852. // Clear the bit by calling the same routine used at restart.
  4853. //
  4854. NtfsRestartClearBitsInBitMap( IrpContext,
  4855. &Bitmap,
  4856. RelativeIndex,
  4857. 1 );
  4858. //
  4859. // Now if the bit set just cleared is the same as the index for the last set bit
  4860. // then we must compute a new last set bit
  4861. //
  4862. if (Index == (ULONG)*IndexOfLastSetBit) {
  4863. ULONG ClearLength;
  4864. ClearLength = RtlFindLastBackwardRunClear( &Bitmap, RelativeIndex, &ClearIndex );
  4865. //
  4866. // If the last page of the bitmap is clear, then loop to
  4867. // find the first set bit in the previous page(s).
  4868. // When we reach the first page then we exit. The ClearBit
  4869. // value will be 0.
  4870. //
  4871. while ((ClearLength == (RelativeIndex + 1)) &&
  4872. (BitmapOffset != 0)) {
  4873. BitmapOffset -= PAGE_SIZE;
  4874. RelativeIndex = BITS_PER_PAGE - 1;
  4875. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  4876. NtfsMapStream( IrpContext,
  4877. BitmapScb,
  4878. BitmapOffset,
  4879. PAGE_SIZE,
  4880. &BitmapBcb,
  4881. &BitmapBuffer );
  4882. RtlInitializeBitMap( &Bitmap, BitmapBuffer, BITS_PER_PAGE );
  4883. ClearLength = RtlFindLastBackwardRunClear( &Bitmap, RelativeIndex, &ClearIndex );
  4884. }
  4885. }
  4886. } finally {
  4887. DebugUnwind( NtfsDeallocateRecord );
  4888. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  4889. }
  4890. }
  4891. RecordAllocationContext->NumberOfFreeBits += 1;
  4892. //
  4893. // Now decide if we need to truncate the allocation. First check if we need to
  4894. // set the last set bit index and then check if the new last set bit index is
  4895. // small enough that we should now truncate the allocation. We will truncate
  4896. // if the last set bit index plus the trucate granularity is smaller than
  4897. // the current number of records in the data scb.
  4898. //
  4899. // **** For now, we will not truncate the Mft, since we do not synchronize
  4900. // reads and writes, and a truncate can collide with the Lazy Writer.
  4901. //
  4902. if (Index == (ULONG)*IndexOfLastSetBit) {
  4903. *IndexOfLastSetBit = ClearIndex - 1 + (BitmapOffset * 8);
  4904. if ((DataScb != Vcb->MftScb) &&
  4905. (DataScb->Header.AllocationSize.QuadPart >
  4906. Int32x32To64( *IndexOfLastSetBit + 1 + TruncateGranularity, BytesPerRecord ))) {
  4907. VCN StartingVcn;
  4908. LONGLONG EndOfIndexOffset;
  4909. LONGLONG TruncatePoint;
  4910. //
  4911. // We can get into a situation where there is so much extra allocation that
  4912. // we can't delete it without overflowing the log file. We can't perform
  4913. // checkpoints in this path so we will forget about truncating in
  4914. // this path unless this is the first truncate of the data scb. We
  4915. // only deallocate a small piece of the allocation.
  4916. //
  4917. TruncatePoint =
  4918. EndOfIndexOffset = Int32x32To64( *IndexOfLastSetBit + 1, BytesPerRecord );
  4919. if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_EXCESS_LOG_FULL )) {
  4920. //
  4921. // Use a fudge factor of 8 to allow for the overused bits in
  4922. // the snapshot allocation field.
  4923. //
  4924. if (DataScb->Header.AllocationSize.QuadPart + 8 >= DataScb->ScbSnapshot->AllocationSize) {
  4925. TruncatePoint = DataScb->Header.AllocationSize.QuadPart - (MAXIMUM_RUNS_AT_ONCE * Vcb->BytesPerCluster);
  4926. if (TruncatePoint < EndOfIndexOffset) {
  4927. TruncatePoint = EndOfIndexOffset;
  4928. }
  4929. } else {
  4930. TruncatePoint = DataScb->Header.AllocationSize.QuadPart;
  4931. }
  4932. }
  4933. //
  4934. // Force deleted piece to flush first so dirty page dumps are
  4935. // accurate. This is only neccessary for indexes
  4936. //
  4937. if (DataScb->AttributeTypeCode == $INDEX_ALLOCATION ) {
  4938. ASSERT( DataScb->Header.PagingIoResource == NULL );
  4939. CcFlushCache( &DataScb->NonpagedScb->SegmentObject, (PLARGE_INTEGER)&TruncatePoint, (ULONG)(DataScb->Header.FileSize.QuadPart - TruncatePoint), &Iosb );
  4940. NtfsNormalizeAndCleanupTransaction( IrpContext, &Iosb.Status, TRUE, STATUS_UNEXPECTED_IO_ERROR );
  4941. }
  4942. StartingVcn = LlClustersFromBytes( Vcb, TruncatePoint );
  4943. NtfsDeleteAllocation( IrpContext,
  4944. DataScb->FileObject,
  4945. DataScb,
  4946. StartingVcn,
  4947. MAXLONGLONG,
  4948. TRUE,
  4949. FALSE );
  4950. //
  4951. // Now truncate the file sizes to the end of the last allocated record.
  4952. //
  4953. DataScb->Header.ValidDataLength.QuadPart =
  4954. DataScb->Header.FileSize.QuadPart = EndOfIndexOffset;
  4955. NtfsWriteFileSizes( IrpContext,
  4956. DataScb,
  4957. &DataScb->Header.ValidDataLength.QuadPart,
  4958. FALSE,
  4959. TRUE,
  4960. TRUE );
  4961. //
  4962. // Tell the cache manager about the new file size.
  4963. //
  4964. CcSetFileSizes( DataScb->FileObject,
  4965. (PCC_FILE_SIZES)&DataScb->Header.AllocationSize );
  4966. //
  4967. // We have truncated the index stream. Update the change count
  4968. // so that we won't trust any cached index entry information.
  4969. //
  4970. DataScb->ScbType.Index.ChangeCount += 1;
  4971. }
  4972. }
  4973. //
  4974. // As our final task we need to add this index to the recently deallocated
  4975. // queues for the Scb and the Irp Context. First scan through the IrpContext queue
  4976. // looking for a matching Scb. I do don't find one then we allocate a new one and insert
  4977. // it in the appropriate queues and lastly we add our index to the entry
  4978. //
  4979. {
  4980. PDEALLOCATED_RECORDS DeallocatedRecords;
  4981. PLIST_ENTRY Links;
  4982. //
  4983. // After the following loop either we've found an existing record in the irp context
  4984. // queue for the appropriate scb or deallocated records is null and we know we need
  4985. // to create a record
  4986. //
  4987. DeallocatedRecords = NULL;
  4988. for (Links = IrpContext->RecentlyDeallocatedQueue.Flink;
  4989. Links != &IrpContext->RecentlyDeallocatedQueue;
  4990. Links = Links->Flink) {
  4991. DeallocatedRecords = CONTAINING_RECORD( Links, DEALLOCATED_RECORDS, IrpContextLinks );
  4992. if (DeallocatedRecords->Scb == DataScb) {
  4993. break;
  4994. }
  4995. DeallocatedRecords = NULL;
  4996. }
  4997. //
  4998. // If we need to create a new record then allocate a record and insert it in both queues
  4999. // and initialize its other fields
  5000. //
  5001. if (DeallocatedRecords == NULL) {
  5002. DeallocatedRecords = (PDEALLOCATED_RECORDS)ExAllocateFromPagedLookasideList( &NtfsDeallocatedRecordsLookasideList );
  5003. InsertTailList( &DataScb->ScbType.Index.RecentlyDeallocatedQueue, &DeallocatedRecords->ScbLinks );
  5004. InsertTailList( &IrpContext->RecentlyDeallocatedQueue, &DeallocatedRecords->IrpContextLinks );
  5005. DeallocatedRecords->Scb = DataScb;
  5006. DeallocatedRecords->NumberOfEntries = DEALLOCATED_RECORD_ENTRIES;
  5007. DeallocatedRecords->NextFreeEntry = 0;
  5008. }
  5009. //
  5010. // At this point deallocated records points to a record that we are to fill in.
  5011. // We need to check whether there is space to add this entry. Otherwise we need
  5012. // to allocate a larger deallocated record structure from pool.
  5013. //
  5014. if (DeallocatedRecords->NextFreeEntry == DeallocatedRecords->NumberOfEntries) {
  5015. PDEALLOCATED_RECORDS NewDeallocatedRecords;
  5016. ULONG BytesInEntryArray;
  5017. //
  5018. // Double the number of entries in the current structure and
  5019. // allocate directly from pool.
  5020. //
  5021. BytesInEntryArray = 2 * DeallocatedRecords->NumberOfEntries * sizeof( ULONG );
  5022. NewDeallocatedRecords = NtfsAllocatePool( PagedPool,
  5023. DEALLOCATED_RECORDS_HEADER_SIZE + BytesInEntryArray );
  5024. RtlZeroMemory( NewDeallocatedRecords, DEALLOCATED_RECORDS_HEADER_SIZE + BytesInEntryArray );
  5025. //
  5026. // Initialize the structure by copying the existing structure. Then
  5027. // update the number of entries field.
  5028. //
  5029. RtlCopyMemory( NewDeallocatedRecords,
  5030. DeallocatedRecords,
  5031. DEALLOCATED_RECORDS_HEADER_SIZE + (BytesInEntryArray / 2) );
  5032. NewDeallocatedRecords->NumberOfEntries = DeallocatedRecords->NumberOfEntries * 2;
  5033. //
  5034. // Remove the previous structure from the list and insert the new structure.
  5035. //
  5036. RemoveEntryList( &DeallocatedRecords->ScbLinks );
  5037. RemoveEntryList( &DeallocatedRecords->IrpContextLinks );
  5038. InsertTailList( &DataScb->ScbType.Index.RecentlyDeallocatedQueue,
  5039. &NewDeallocatedRecords->ScbLinks );
  5040. InsertTailList( &IrpContext->RecentlyDeallocatedQueue,
  5041. &NewDeallocatedRecords->IrpContextLinks );
  5042. //
  5043. // Deallocate the previous structure and use the new structure in its place.
  5044. //
  5045. if (DeallocatedRecords->NumberOfEntries == DEALLOCATED_RECORD_ENTRIES) {
  5046. ExFreeToPagedLookasideList( &NtfsDeallocatedRecordsLookasideList, DeallocatedRecords );
  5047. } else {
  5048. NtfsFreePool( DeallocatedRecords );
  5049. }
  5050. DeallocatedRecords = NewDeallocatedRecords;
  5051. }
  5052. ASSERT( DeallocatedRecords->NextFreeEntry < DeallocatedRecords->NumberOfEntries );
  5053. DeallocatedRecords->Index[DeallocatedRecords->NextFreeEntry] = Index;
  5054. DeallocatedRecords->NextFreeEntry += 1;
  5055. }
  5056. } finally {
  5057. NtfsReleaseScb( IrpContext, DataScb );
  5058. }
  5059. //
  5060. // Check if this is the lowest index we've deallocated. It will be a future starting
  5061. // hint if so.
  5062. //
  5063. if (RecordAllocationContext->LowestDeallocatedIndex > Index) {
  5064. RecordAllocationContext->LowestDeallocatedIndex = Index;
  5065. }
  5066. DebugTrace( -1, Dbg, ("NtfsDeallocateRecord -> VOID\n") );
  5067. return;
  5068. }
  5069. VOID
  5070. NtfsReserveMftRecord (
  5071. IN PIRP_CONTEXT IrpContext,
  5072. IN OUT PVCB Vcb,
  5073. IN PATTRIBUTE_ENUMERATION_CONTEXT BitmapAttribute
  5074. )
  5075. /*++
  5076. Routine Description:
  5077. This routine reserves a record, without actually allocating it, so that the
  5078. record may be allocated later via NtfsAllocateReservedRecord. This support
  5079. is used, for example, to reserve a record for describing Mft extensions in
  5080. the current Mft mapping. Only one record may be reserved at a time.
  5081. Note that even though the reserved record number is returned, it may not
  5082. be used until it is allocated.
  5083. Arguments:
  5084. Vcb - This is the Vcb for the volume. We update flags in the Vcb on
  5085. completion of this operation.
  5086. BitmapAttribute - Supplies the enumeration context for the bitmap
  5087. attribute. This parameter is ignored if the bitmap attribute is
  5088. non resident, in which case we create an scb for the attribute and
  5089. store a pointer to it in the record allocation context.
  5090. Return Value:
  5091. None - We update the Vcb and MftScb during this operation.
  5092. --*/
  5093. {
  5094. PSCB DataScb;
  5095. RTL_BITMAP Bitmap;
  5096. BOOLEAN StuffAdded = FALSE;
  5097. PBCB BitmapBcb = NULL;
  5098. ASSERT_IRP_CONTEXT( IrpContext );
  5099. PAGED_CODE();
  5100. DebugTrace( +1, Dbg, ("NtfsReserveMftRecord\n") );
  5101. //
  5102. // Synchronize by acquiring the data scb exclusive, as an "end resource".
  5103. // Then use try-finally to insure we free it up.
  5104. //
  5105. DataScb = Vcb->MftScb;
  5106. NtfsAcquireExclusiveScb( IrpContext, DataScb );
  5107. try {
  5108. PSCB BitmapScb;
  5109. PULONG CurrentBitmapSize;
  5110. ULONG BitmapSizeInBytes;
  5111. LONGLONG EndOfIndexOffset;
  5112. LONGLONG ClusterCount;
  5113. ULONG Index;
  5114. ULONG BitOffset;
  5115. PVOID BitmapBuffer;
  5116. UCHAR BitmapByte = 0;
  5117. ULONG SizeToPin;
  5118. ULONG BitmapCurrentOffset;
  5119. //
  5120. // See if someone made the bitmap nonresident, and we still think
  5121. // it is resident. If so, we must uninitialize and insure reinitialization
  5122. // below.
  5123. //
  5124. {
  5125. ULONG BytesPerRecord = DataScb->ScbType.Index.RecordAllocationContext.BytesPerRecord;
  5126. ULONG ExtendGranularity = DataScb->ScbType.Index.RecordAllocationContext.ExtendGranularity;
  5127. if ((DataScb->ScbType.Index.RecordAllocationContext.BitmapScb == NULL) &&
  5128. !NtfsIsAttributeResident( NtfsFoundAttribute( BitmapAttribute ))) {
  5129. NtfsUninitializeRecordAllocation( IrpContext,
  5130. &DataScb->ScbType.Index.RecordAllocationContext );
  5131. DataScb->ScbType.Index.RecordAllocationContext.CurrentBitmapSize = MAXULONG;
  5132. }
  5133. //
  5134. // Reinitialize the record context structure if necessary.
  5135. //
  5136. if (DataScb->ScbType.Index.RecordAllocationContext.CurrentBitmapSize == MAXULONG) {
  5137. NtfsInitializeRecordAllocation( IrpContext,
  5138. DataScb,
  5139. BitmapAttribute,
  5140. BytesPerRecord,
  5141. ExtendGranularity,
  5142. ExtendGranularity,
  5143. &DataScb->ScbType.Index.RecordAllocationContext );
  5144. }
  5145. }
  5146. BitmapScb = DataScb->ScbType.Index.RecordAllocationContext.BitmapScb;
  5147. CurrentBitmapSize = &DataScb->ScbType.Index.RecordAllocationContext.CurrentBitmapSize;
  5148. BitmapSizeInBytes = *CurrentBitmapSize / 8;
  5149. //
  5150. // Snapshot the bitmap before possibly modifying it - we own it exclusive through
  5151. // the data scb since they share the same resource but have not snapped it before
  5152. //
  5153. NtfsSnapshotScb( IrpContext, BitmapScb );
  5154. //
  5155. // Loop through the entire bitmap. We always start from the first user
  5156. // file number as our starting point.
  5157. //
  5158. BitOffset = FIRST_USER_FILE_NUMBER;
  5159. for (BitmapCurrentOffset = 0;
  5160. BitmapCurrentOffset < BitmapSizeInBytes;
  5161. BitmapCurrentOffset += PAGE_SIZE) {
  5162. //
  5163. // Calculate the size to read from this point to the end of
  5164. // bitmap, or a page, whichever is less.
  5165. //
  5166. SizeToPin = BitmapSizeInBytes - BitmapCurrentOffset;
  5167. if (SizeToPin > PAGE_SIZE) { SizeToPin = PAGE_SIZE; }
  5168. //
  5169. // Unpin any Bcb from a previous loop.
  5170. //
  5171. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE; }
  5172. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  5173. //
  5174. // Read the desired bitmap page.
  5175. //
  5176. NtfsMapStream( IrpContext,
  5177. BitmapScb,
  5178. BitmapCurrentOffset,
  5179. SizeToPin,
  5180. &BitmapBcb,
  5181. &BitmapBuffer );
  5182. //
  5183. // Initialize the bitmap and search for a free bit.
  5184. //
  5185. RtlInitializeBitMap( &Bitmap, BitmapBuffer, SizeToPin * 8 );
  5186. StuffAdded = NtfsAddDeallocatedRecords( Vcb,
  5187. DataScb,
  5188. BitmapCurrentOffset * 8,
  5189. &Bitmap );
  5190. Index = RtlFindClearBits( &Bitmap, 1, BitOffset );
  5191. //
  5192. // If we found something, then leave the loop.
  5193. //
  5194. if (Index != 0xffffffff) {
  5195. //
  5196. // Remember the byte containing the reserved index.
  5197. //
  5198. BitmapByte = ((PCHAR) Bitmap.Buffer)[Index / 8];
  5199. break;
  5200. }
  5201. //
  5202. // For each subsequent page the page offset is zero.
  5203. //
  5204. BitOffset = 0;
  5205. }
  5206. //
  5207. // Now check if we have located a record that can be allocated, If not then extend
  5208. // the size of the bitmap by 64 bits.
  5209. //
  5210. if (Index == 0xffffffff) {
  5211. //
  5212. // Cleanup from previous loop.
  5213. //
  5214. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); StuffAdded = FALSE; }
  5215. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  5216. //
  5217. // Calculate the page offset for the next page to pin.
  5218. //
  5219. BitmapCurrentOffset = BitmapSizeInBytes & ~(PAGE_SIZE - 1);
  5220. //
  5221. // Calculate the index of next file record to allocate.
  5222. //
  5223. Index = *CurrentBitmapSize;
  5224. //
  5225. // Now advance the sizes and calculate the size in bytes to
  5226. // read.
  5227. //
  5228. *CurrentBitmapSize += BITMAP_EXTEND_GRANULARITY;
  5229. DataScb->ScbType.Index.RecordAllocationContext.NumberOfFreeBits += BITMAP_EXTEND_GRANULARITY;
  5230. //
  5231. // Calculate the new size of the bitmap in bits and check if we must grow
  5232. // the allocation.
  5233. //
  5234. BitmapSizeInBytes = *CurrentBitmapSize / 8;
  5235. //
  5236. // Check for allocation first.
  5237. //
  5238. if (BitmapScb->Header.AllocationSize.LowPart < BitmapSizeInBytes) {
  5239. //
  5240. // Calculate number of clusters to next page boundary, and allocate
  5241. // that much.
  5242. //
  5243. ClusterCount = ((BitmapSizeInBytes + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1));
  5244. ClusterCount = LlClustersFromBytes( Vcb,
  5245. ((ULONG) ClusterCount - BitmapScb->Header.AllocationSize.LowPart) );
  5246. NtfsAddAllocation( IrpContext,
  5247. BitmapScb->FileObject,
  5248. BitmapScb,
  5249. LlClustersFromBytes( Vcb,
  5250. BitmapScb->Header.AllocationSize.QuadPart ),
  5251. ClusterCount,
  5252. FALSE,
  5253. NULL );
  5254. }
  5255. //
  5256. // Tell the cache manager about the new file size.
  5257. //
  5258. BitmapScb->Header.FileSize.QuadPart = BitmapSizeInBytes;
  5259. CcSetFileSizes( BitmapScb->FileObject,
  5260. (PCC_FILE_SIZES)&BitmapScb->Header.AllocationSize );
  5261. //
  5262. // Now read the page in and mark it dirty so that any new range will
  5263. // be zeroed.
  5264. //
  5265. SizeToPin = BitmapSizeInBytes - BitmapCurrentOffset;
  5266. if (SizeToPin > PAGE_SIZE) { SizeToPin = PAGE_SIZE; }
  5267. NtfsPinStream( IrpContext,
  5268. BitmapScb,
  5269. BitmapCurrentOffset,
  5270. SizeToPin,
  5271. &BitmapBcb,
  5272. &BitmapBuffer );
  5273. CcSetDirtyPinnedData( BitmapBcb, NULL );
  5274. //
  5275. // Update the ValidDataLength, now that we have read (and possibly
  5276. // zeroed) the page.
  5277. //
  5278. BitmapScb->Header.ValidDataLength.LowPart = BitmapSizeInBytes;
  5279. NtfsWriteFileSizes( IrpContext,
  5280. BitmapScb,
  5281. &BitmapScb->Header.ValidDataLength.QuadPart,
  5282. TRUE,
  5283. TRUE,
  5284. TRUE );
  5285. } else {
  5286. //
  5287. // The Index at this point is actually relative, so convert it to absolute
  5288. // before rejoining common code.
  5289. //
  5290. Index += (BitmapCurrentOffset * 8);
  5291. }
  5292. //
  5293. // We now have an index. There are three possible states for the file
  5294. // record corresponding to this index within the Mft. They are:
  5295. //
  5296. // - File record could lie beyond the current end of the file.
  5297. // There is nothing to do in this case.
  5298. //
  5299. // - File record is part of a hole in the Mft. In that case
  5300. // we allocate space for it bring it into memory.
  5301. //
  5302. // - File record is already within allocated space. There is nothing
  5303. // to do in that case.
  5304. //
  5305. // We store the index as our reserved index and update the Vcb flags. If
  5306. // the hole filling operation fails then the RestoreScbSnapshots routine
  5307. // will clear these values.
  5308. //
  5309. DataScb->ScbType.Mft.ReservedIndex = Index;
  5310. NtfsAcquireCheckpoint( IrpContext, Vcb );
  5311. SetFlag( Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED );
  5312. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MFT_REC_RESERVED );
  5313. NtfsReleaseCheckpoint( IrpContext, Vcb );
  5314. if (NtfsIsMftIndexInHole( IrpContext, Vcb, Index, NULL )) {
  5315. //
  5316. // Make sure nothing is left pinned in the bitmap.
  5317. //
  5318. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  5319. //
  5320. // Try to fill the hole in the Mft. We will have this routine
  5321. // raise if unable to fill in the hole.
  5322. //
  5323. NtfsFillMftHole( IrpContext, Vcb, Index );
  5324. }
  5325. //
  5326. // At this point we have the index to reserve and the value of the
  5327. // byte in the bitmap which contains this bit. We make sure the
  5328. // Mft includes the allocation for this index and the other
  5329. // bits within the same byte. This is so we can uninitialize these
  5330. // file records so chkdsk won't look at stale data.
  5331. //
  5332. EndOfIndexOffset = LlBytesFromFileRecords( Vcb, (Index + 8) & ~(7));
  5333. //
  5334. // Now check if we are extending the file. We update the file size and
  5335. // valid data now.
  5336. //
  5337. if (EndOfIndexOffset > DataScb->Header.FileSize.QuadPart) {
  5338. ULONG AddedFileRecords;
  5339. ULONG CurrentIndex;
  5340. //
  5341. // Check for allocation first.
  5342. //
  5343. if (EndOfIndexOffset > DataScb->Header.AllocationSize.QuadPart) {
  5344. ClusterCount = ((Index + DataScb->ScbType.Index.RecordAllocationContext.ExtendGranularity) &
  5345. ~(DataScb->ScbType.Index.RecordAllocationContext.ExtendGranularity - 1));
  5346. ClusterCount = LlBytesFromFileRecords( Vcb, (ULONG) ClusterCount );
  5347. ClusterCount = LlClustersFromBytesTruncate( Vcb,
  5348. ClusterCount - DataScb->Header.AllocationSize.QuadPart );
  5349. NtfsAddAllocation( IrpContext,
  5350. DataScb->FileObject,
  5351. DataScb,
  5352. LlClustersFromBytes( Vcb,
  5353. DataScb->Header.AllocationSize.QuadPart ),
  5354. ClusterCount,
  5355. FALSE,
  5356. NULL );
  5357. }
  5358. //
  5359. // Now we have to figure out how many file records we will be
  5360. // adding and the index of the first record being added.
  5361. //
  5362. CurrentIndex = (ULONG) LlFileRecordsFromBytes( Vcb, DataScb->Header.FileSize.QuadPart );
  5363. AddedFileRecords = (ULONG) (EndOfIndexOffset - DataScb->Header.FileSize.QuadPart);
  5364. AddedFileRecords = FileRecordsFromBytes( Vcb, AddedFileRecords );
  5365. DataScb->Header.FileSize.QuadPart = EndOfIndexOffset;
  5366. DataScb->Header.ValidDataLength.QuadPart = EndOfIndexOffset;
  5367. NtfsWriteFileSizes( IrpContext,
  5368. DataScb,
  5369. &DataScb->Header.ValidDataLength.QuadPart,
  5370. TRUE,
  5371. TRUE,
  5372. TRUE );
  5373. //
  5374. // Tell the cache manager about the new file size.
  5375. //
  5376. CcSetFileSizes( DataScb->FileObject,
  5377. (PCC_FILE_SIZES)&DataScb->Header.AllocationSize );
  5378. //
  5379. // Update our bookeeping to reflect the number of file records
  5380. // added.
  5381. //
  5382. DataScb->ScbType.Mft.FreeRecordChange += AddedFileRecords;
  5383. Vcb->MftFreeRecords += AddedFileRecords;
  5384. //
  5385. // We now have to go through each of the file records added
  5386. // and mark it as not IN_USE. We don't want stale data in this range
  5387. // to ever confuse chkdsk or rescan. These records begin after the
  5388. // current end of file. We won't worry about anything currently
  5389. // in the file because it would already be marked as IN-USE or
  5390. // not correctly. We are only concerned with records which will
  5391. // become part of the valid portion of the file since we will
  5392. // skip them in the normal allocation path (we want to limit
  5393. // disk IO in a file record containing MFT mapping).
  5394. //
  5395. //
  5396. // Chop off the bits which are already part of the file.
  5397. //
  5398. BitmapByte >>= (8 - AddedFileRecords);
  5399. //
  5400. // Now perform the initialization routine for each file record beyond the
  5401. // previous end of the file.
  5402. //
  5403. while (AddedFileRecords) {
  5404. //
  5405. // If not allocated then uninitialize it now.
  5406. //
  5407. if (!FlagOn( BitmapByte, 0x1 )) {
  5408. NtfsInitializeMftHoleRecords( IrpContext,
  5409. Vcb,
  5410. CurrentIndex,
  5411. 1 );
  5412. }
  5413. BitmapByte >>= 1;
  5414. CurrentIndex += 1;
  5415. AddedFileRecords -= 1;
  5416. }
  5417. }
  5418. } finally {
  5419. DebugUnwind( NtfsReserveMftRecord );
  5420. if (StuffAdded) { NtfsFreePool( Bitmap.Buffer ); }
  5421. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  5422. NtfsReleaseScb( IrpContext, DataScb );
  5423. }
  5424. DebugTrace( -1, Dbg, ("NtfsReserveMftRecord -> Exit\n") );
  5425. return;
  5426. }
  5427. ULONG
  5428. NtfsAllocateMftReservedRecord (
  5429. IN PIRP_CONTEXT IrpContext,
  5430. IN PVCB Vcb,
  5431. IN PATTRIBUTE_ENUMERATION_CONTEXT BitmapAttribute
  5432. )
  5433. /*++
  5434. Routine Description:
  5435. This routine allocates a previously reserved record, and returns its
  5436. number.
  5437. Arguments:
  5438. Vcb - This is the Vcb for the volume.
  5439. BitmapAttribute - Supplies the enumeration context for the bitmap
  5440. attribute. This parameter is ignored if the bitmap attribute is
  5441. non resident, in which case we create an scb for the attribute and
  5442. store a pointer to it in the record allocation context.
  5443. Return Value:
  5444. ULONG - Returns the index of the record just reserved, zero based.
  5445. --*/
  5446. {
  5447. PSCB DataScb;
  5448. ULONG ReservedIndex;
  5449. PBCB BitmapBcb = NULL;
  5450. ASSERT_IRP_CONTEXT( IrpContext );
  5451. PAGED_CODE();
  5452. DebugTrace( +1, Dbg, ("NtfsAllocateMftReservedRecord\n") );
  5453. //
  5454. // Synchronize by acquiring the data scb exclusive, as an "end resource".
  5455. // Then use try-finally to insure we free it up.
  5456. //
  5457. DataScb = Vcb->MftScb;
  5458. NtfsAcquireExclusiveScb( IrpContext, DataScb );
  5459. try {
  5460. PSCB BitmapScb;
  5461. ULONG RelativeIndex;
  5462. ULONG SizeToPin;
  5463. RTL_BITMAP Bitmap;
  5464. PVOID BitmapBuffer;
  5465. BITMAP_RANGE BitmapRange;
  5466. ULONG BitmapCurrentOffset = 0;
  5467. //
  5468. // If we are going to allocate file record 15 then do so and set the
  5469. // flags in the IrpContext and Vcb.
  5470. //
  5471. if (!FlagOn( Vcb->MftReserveFlags, VCB_MFT_RECORD_15_USED )) {
  5472. SetFlag( Vcb->MftReserveFlags, VCB_MFT_RECORD_15_USED );
  5473. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MFT_REC_15_USED );
  5474. try_return( ReservedIndex = FIRST_USER_FILE_NUMBER - 1 );
  5475. }
  5476. //
  5477. // See if someone made the bitmap nonresident, and we still think
  5478. // it is resident. If so, we must uninitialize and insure reinitialization
  5479. // below.
  5480. //
  5481. {
  5482. ULONG BytesPerRecord = DataScb->ScbType.Index.RecordAllocationContext.BytesPerRecord;
  5483. ULONG ExtendGranularity = DataScb->ScbType.Index.RecordAllocationContext.ExtendGranularity;
  5484. if ((DataScb->ScbType.Index.RecordAllocationContext.BitmapScb == NULL) &&
  5485. !NtfsIsAttributeResident( NtfsFoundAttribute( BitmapAttribute ))) {
  5486. NtfsUninitializeRecordAllocation( IrpContext,
  5487. &DataScb->ScbType.Index.RecordAllocationContext );
  5488. DataScb->ScbType.Index.RecordAllocationContext.CurrentBitmapSize = MAXULONG;
  5489. }
  5490. //
  5491. // Reinitialize the record context structure if necessary.
  5492. //
  5493. if (DataScb->ScbType.Index.RecordAllocationContext.CurrentBitmapSize == MAXULONG) {
  5494. NtfsInitializeRecordAllocation( IrpContext,
  5495. DataScb,
  5496. BitmapAttribute,
  5497. BytesPerRecord,
  5498. ExtendGranularity,
  5499. ExtendGranularity,
  5500. &DataScb->ScbType.Index.RecordAllocationContext );
  5501. }
  5502. }
  5503. BitmapScb = DataScb->ScbType.Index.RecordAllocationContext.BitmapScb;
  5504. ReservedIndex = DataScb->ScbType.Mft.ReservedIndex;
  5505. //
  5506. // Find the start of the page containing the reserved index.
  5507. //
  5508. BitmapCurrentOffset = (ReservedIndex / 8) & ~(PAGE_SIZE - 1);
  5509. RelativeIndex = ReservedIndex & (BITS_PER_PAGE - 1);
  5510. //
  5511. // Calculate the size to read from this point to the end of
  5512. // bitmap, or a page, whichever is less.
  5513. //
  5514. SizeToPin = (DataScb->ScbType.Index.RecordAllocationContext.CurrentBitmapSize / 8)
  5515. - BitmapCurrentOffset;
  5516. if (SizeToPin > PAGE_SIZE) { SizeToPin = PAGE_SIZE; }
  5517. //
  5518. // Read the desired bitmap page.
  5519. //
  5520. NtfsPinStream( IrpContext,
  5521. BitmapScb,
  5522. BitmapCurrentOffset,
  5523. SizeToPin,
  5524. &BitmapBcb,
  5525. &BitmapBuffer );
  5526. //
  5527. // Initialize the bitmap.
  5528. //
  5529. RtlInitializeBitMap( &Bitmap, BitmapBuffer, SizeToPin * 8 );
  5530. //
  5531. // Now log this change as well.
  5532. //
  5533. BitmapRange.BitMapOffset = RelativeIndex;
  5534. BitmapRange.NumberOfBits = 1;
  5535. (VOID) NtfsWriteLog( IrpContext,
  5536. BitmapScb,
  5537. BitmapBcb,
  5538. SetBitsInNonresidentBitMap,
  5539. &BitmapRange,
  5540. sizeof(BITMAP_RANGE),
  5541. ClearBitsInNonresidentBitMap,
  5542. &BitmapRange,
  5543. sizeof(BITMAP_RANGE),
  5544. BitmapCurrentOffset,
  5545. 0,
  5546. 0,
  5547. Bitmap.SizeOfBitMap >> 3 );
  5548. NtfsRestartSetBitsInBitMap( IrpContext, &Bitmap, RelativeIndex, 1 );
  5549. //
  5550. // Now that we've located an index we can subtract the number of free bits in the bitmap
  5551. //
  5552. DataScb->ScbType.Index.RecordAllocationContext.NumberOfFreeBits -= 1;
  5553. //
  5554. // If we didn't extend the file then we have used a free file record in the file.
  5555. // Update our bookeeping count for free file records.
  5556. //
  5557. DataScb->ScbType.Mft.FreeRecordChange -= 1;
  5558. Vcb->MftFreeRecords -= 1;
  5559. //
  5560. // Now determine if we extended the index of the last set bit
  5561. //
  5562. if (ReservedIndex > (ULONG)DataScb->ScbType.Index.RecordAllocationContext.IndexOfLastSetBit) {
  5563. DataScb->ScbType.Index.RecordAllocationContext.IndexOfLastSetBit = ReservedIndex;
  5564. }
  5565. //
  5566. // Clear the fields that indicate we have a reserved index.
  5567. //
  5568. NtfsAcquireCheckpoint( IrpContext, Vcb );
  5569. ClearFlag( Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED );
  5570. NtfsReleaseCheckpoint( IrpContext, Vcb );
  5571. DataScb->ScbType.Mft.ReservedIndex = 0;
  5572. try_exit: NOTHING;
  5573. } finally {
  5574. DebugUnwind( NtfsAllocateMftReserveRecord );
  5575. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  5576. NtfsReleaseScb( IrpContext, DataScb );
  5577. }
  5578. DebugTrace( -1, Dbg, ("NtfsAllocateMftReserveRecord -> %08lx\n", ReservedIndex) );
  5579. return ReservedIndex;
  5580. }
  5581. VOID
  5582. NtfsDeallocateRecordsComplete (
  5583. IN PIRP_CONTEXT IrpContext
  5584. )
  5585. /*++
  5586. Routine Description:
  5587. This routine removes recently deallocated record information from
  5588. the Scb structures based on the input irp context.
  5589. Arguments:
  5590. IrpContext - Supplies the Queue of recently deallocate records
  5591. Return Value:
  5592. None.
  5593. --*/
  5594. {
  5595. PDEALLOCATED_RECORDS DeallocatedRecords;
  5596. PAGED_CODE();
  5597. DebugTrace( +1, Dbg, ("NtfsDeallocateRecordsComplete\n") );
  5598. //
  5599. // Now while the irp context's recently deallocated queue is not empty
  5600. // we will grap the first entry off the queue, remove it from both
  5601. // the scb and irp context queue, and free the record
  5602. //
  5603. while (!IsListEmpty( &IrpContext->RecentlyDeallocatedQueue )) {
  5604. DeallocatedRecords = CONTAINING_RECORD( IrpContext->RecentlyDeallocatedQueue.Flink,
  5605. DEALLOCATED_RECORDS,
  5606. IrpContextLinks );
  5607. RemoveEntryList( &DeallocatedRecords->ScbLinks );
  5608. //
  5609. // Reset our hint index if one of the deallocated indexes is suitable.
  5610. //
  5611. if (DeallocatedRecords->Scb->ScbType.Index.RecordAllocationContext.StartingHint >
  5612. DeallocatedRecords->Scb->ScbType.Index.RecordAllocationContext.LowestDeallocatedIndex) {
  5613. DeallocatedRecords->Scb->ScbType.Index.RecordAllocationContext.StartingHint =
  5614. DeallocatedRecords->Scb->ScbType.Index.RecordAllocationContext.LowestDeallocatedIndex;
  5615. }
  5616. //
  5617. // Make sure to reset the LowestDeallocated.
  5618. //
  5619. DeallocatedRecords->Scb->ScbType.Index.RecordAllocationContext.LowestDeallocatedIndex = MAXULONG;
  5620. //
  5621. // Now remove the record from the irp context queue and deallocate the
  5622. // record
  5623. //
  5624. RemoveEntryList( &DeallocatedRecords->IrpContextLinks );
  5625. //
  5626. // If this record is the default size then return it to our private list.
  5627. // Otherwise deallocate it to pool.
  5628. //
  5629. if (DeallocatedRecords->NumberOfEntries == DEALLOCATED_RECORD_ENTRIES) {
  5630. ExFreeToPagedLookasideList( &NtfsDeallocatedRecordsLookasideList, DeallocatedRecords );
  5631. } else {
  5632. NtfsFreePool( DeallocatedRecords );
  5633. }
  5634. }
  5635. DebugTrace( -1, Dbg, ("NtfsDeallocateRecordsComplete -> VOID\n") );
  5636. return;
  5637. }
  5638. BOOLEAN
  5639. NtfsIsRecordAllocated (
  5640. IN PIRP_CONTEXT IrpContext,
  5641. IN PRECORD_ALLOCATION_CONTEXT RecordAllocationContext,
  5642. IN ULONG Index,
  5643. IN PATTRIBUTE_ENUMERATION_CONTEXT BitmapAttribute
  5644. )
  5645. /*++
  5646. Routine Description:
  5647. This routine is used to query if a record is currently allocated for
  5648. the specified record allocation context.
  5649. Arguments:
  5650. RecordAllocationContext - Supplies the record allocation context used
  5651. in this operation
  5652. Index - Supplies the index of the record being queried, zero based.
  5653. BitmapAttribute - Supplies the enumeration context for the bitmap
  5654. attribute. This parameter is ignored if the bitmap attribute is
  5655. non resident, in which case we create an scb for the attribute and
  5656. store a pointer to it in the record allocation context.
  5657. Return Value:
  5658. BOOLEAN - TRUE if the record is currently allocated and FALSE otherwise.
  5659. --*/
  5660. {
  5661. BOOLEAN Results;
  5662. PSCB DataScb;
  5663. PSCB BitmapScb;
  5664. ULONG CurrentBitmapSize;
  5665. PVCB Vcb;
  5666. RTL_BITMAP Bitmap;
  5667. PBCB BitmapBcb = NULL;
  5668. PATTRIBUTE_RECORD_HEADER AttributeRecordHeader;
  5669. ASSERT_IRP_CONTEXT( IrpContext );
  5670. PAGED_CODE();
  5671. DebugTrace( +1, Dbg, ("NtfsIsRecordAllocated\n") );
  5672. //
  5673. // Synchronize by acquiring the data scb exclusive, as an "end resource".
  5674. // Then use try-finally to insure we free it up.
  5675. //
  5676. DataScb = RecordAllocationContext->DataScb;
  5677. NtfsAcquireExclusiveScb( IrpContext, DataScb );
  5678. try {
  5679. Vcb = DataScb->Fcb->Vcb;
  5680. //
  5681. // See if someone made the bitmap nonresident, and we still think
  5682. // it is resident. If so, we must uninitialize and insure reinitialization
  5683. // below.
  5684. //
  5685. BitmapScb = RecordAllocationContext->BitmapScb;
  5686. {
  5687. ULONG ExtendGranularity;
  5688. ULONG BytesPerRecord;
  5689. ULONG TruncateGranularity;
  5690. //
  5691. // Remember the current values in the record context structure.
  5692. //
  5693. BytesPerRecord = RecordAllocationContext->BytesPerRecord;
  5694. TruncateGranularity = RecordAllocationContext->TruncateGranularity;
  5695. ExtendGranularity = RecordAllocationContext->ExtendGranularity;
  5696. if ((BitmapScb == NULL) && !NtfsIsAttributeResident(NtfsFoundAttribute(BitmapAttribute))) {
  5697. NtfsUninitializeRecordAllocation( IrpContext,
  5698. RecordAllocationContext );
  5699. RecordAllocationContext->CurrentBitmapSize = MAXULONG;
  5700. }
  5701. //
  5702. // Reinitialize the record context structure if necessary.
  5703. //
  5704. if (RecordAllocationContext->CurrentBitmapSize == MAXULONG) {
  5705. NtfsInitializeRecordAllocation( IrpContext,
  5706. DataScb,
  5707. BitmapAttribute,
  5708. BytesPerRecord,
  5709. ExtendGranularity,
  5710. TruncateGranularity,
  5711. RecordAllocationContext );
  5712. }
  5713. }
  5714. BitmapScb = RecordAllocationContext->BitmapScb;
  5715. CurrentBitmapSize = RecordAllocationContext->CurrentBitmapSize;
  5716. //
  5717. // We will do different operations based on whether the bitmap is resident or nonresident
  5718. // The first case will handle the resident bitmap
  5719. //
  5720. if (BitmapScb == NULL) {
  5721. UCHAR NewByte;
  5722. //
  5723. // Initialize the local bitmap
  5724. //
  5725. AttributeRecordHeader = NtfsFoundAttribute( BitmapAttribute );
  5726. RtlInitializeBitMap( &Bitmap,
  5727. (PULONG)NtfsAttributeValue( AttributeRecordHeader ),
  5728. CurrentBitmapSize );
  5729. //
  5730. // And check if the indcated bit is Set. If it is set then the record is allocated.
  5731. //
  5732. NewByte = ((PUCHAR)Bitmap.Buffer)[ Index / 8 ];
  5733. Results = BooleanFlagOn( NewByte, BitMask[Index % 8] );
  5734. } else {
  5735. PVOID BitmapBuffer;
  5736. ULONG SizeToMap;
  5737. ULONG RelativeIndex;
  5738. ULONG BitmapCurrentOffset;
  5739. //
  5740. // Calculate Vcn and relative index of the bit we will deallocate,
  5741. // from the nearest page boundary.
  5742. //
  5743. BitmapCurrentOffset = (Index / 8) & ~(PAGE_SIZE - 1);
  5744. RelativeIndex = Index & (BITS_PER_PAGE - 1);
  5745. //
  5746. // Calculate the size to read from this point to the end of
  5747. // bitmap.
  5748. //
  5749. SizeToMap = CurrentBitmapSize / 8 - BitmapCurrentOffset;
  5750. if (SizeToMap > PAGE_SIZE) { SizeToMap = PAGE_SIZE; }
  5751. NtfsMapStream( IrpContext,
  5752. BitmapScb,
  5753. BitmapCurrentOffset,
  5754. SizeToMap,
  5755. &BitmapBcb,
  5756. &BitmapBuffer );
  5757. RtlInitializeBitMap( &Bitmap, BitmapBuffer, SizeToMap * 8 );
  5758. //
  5759. // Now check if the indicated bit is set. If it is set then the record is allocated.
  5760. // no idea whether the update is applied or not.
  5761. //
  5762. Results = RtlAreBitsSet(&Bitmap, RelativeIndex, 1);
  5763. }
  5764. } finally {
  5765. DebugUnwind( NtfsIsRecordDeallocated );
  5766. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  5767. NtfsReleaseScb( IrpContext, DataScb );
  5768. }
  5769. DebugTrace( -1, Dbg, ("NtfsIsRecordAllocated -> %08lx\n", Results) );
  5770. return Results;
  5771. }
  5772. VOID
  5773. NtfsScanMftBitmap (
  5774. IN PIRP_CONTEXT IrpContext,
  5775. IN OUT PVCB Vcb
  5776. )
  5777. /*++
  5778. Routine Description:
  5779. This routine is called during mount to initialize the values related to
  5780. the Mft in the Vcb. These include the number of free records and hole
  5781. records. Also whether we have already used file record 15. We also scan
  5782. the Mft to check whether there is any excess mapping.
  5783. Arguments:
  5784. Vcb - Supplies the Vcb for the volume.
  5785. Return Value:
  5786. None.
  5787. --*/
  5788. {
  5789. PBCB BitmapBcb = NULL;
  5790. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  5791. PAGED_CODE();
  5792. DebugTrace( +1, Dbg, ("NtfsScanMftBitmap...\n") );
  5793. NtfsInitializeAttributeContext( &AttrContext );
  5794. //
  5795. // Use a try-finally to facilitate cleanup.
  5796. //
  5797. try {
  5798. ULONG SizeToMap;
  5799. ULONG FileRecords;
  5800. ULONG RemainingRecords;
  5801. ULONG BitmapCurrentOffset;
  5802. ULONG BitmapBytesToRead;
  5803. PUCHAR BitmapBuffer;
  5804. UCHAR NextByte;
  5805. VCN Vcn;
  5806. LCN Lcn;
  5807. LONGLONG Clusters;
  5808. //
  5809. // Start by walking through the file records for the Mft
  5810. // checking for excess mapping.
  5811. //
  5812. NtfsLookupAttributeForScb( IrpContext, Vcb->MftScb, NULL, &AttrContext );
  5813. //
  5814. // We don't care about the first one. Let's find the rest of them.
  5815. //
  5816. while (NtfsLookupNextAttributeForScb( IrpContext,
  5817. Vcb->MftScb,
  5818. &AttrContext )) {
  5819. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  5820. SetFlag( Vcb->MftReserveFlags, VCB_MFT_RECORD_15_USED );
  5821. FileRecord = NtfsContainingFileRecord( &AttrContext );
  5822. //
  5823. // Now check for the free space.
  5824. //
  5825. if (FileRecord->BytesAvailable - FileRecord->FirstFreeByte < Vcb->MftReserved) {
  5826. NtfsAcquireCheckpoint( IrpContext, Vcb );
  5827. SetFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_EXCESS_MAP );
  5828. NtfsReleaseCheckpoint( IrpContext, Vcb );
  5829. break;
  5830. }
  5831. }
  5832. //
  5833. // We now want to find the number of free records within the Mft
  5834. // bitmap. We need to figure out how many file records are in
  5835. // the Mft and then map the necessary bytes in the bitmap and
  5836. // find the count of set bits. We will round the bitmap length
  5837. // down to a byte boundary and then look at the last byte
  5838. // separately.
  5839. //
  5840. FileRecords = (ULONG) LlFileRecordsFromBytes( Vcb, Vcb->MftScb->Header.FileSize.QuadPart );
  5841. //
  5842. // Remember how many file records are in the last byte of the bitmap.
  5843. //
  5844. RemainingRecords = FileRecords & 7;
  5845. FileRecords &= ~(7);
  5846. BitmapBytesToRead = FileRecords / 8;
  5847. for (BitmapCurrentOffset = 0;
  5848. BitmapCurrentOffset < BitmapBytesToRead;
  5849. BitmapCurrentOffset += PAGE_SIZE) {
  5850. RTL_BITMAP Bitmap;
  5851. ULONG MapAdjust;
  5852. //
  5853. // Calculate the size to read from this point to the end of
  5854. // bitmap, or a page, whichever is less.
  5855. //
  5856. SizeToMap = BitmapBytesToRead - BitmapCurrentOffset;
  5857. if (SizeToMap > PAGE_SIZE) { SizeToMap = PAGE_SIZE; }
  5858. //
  5859. // If we aren't pinning a full page and have some bits
  5860. // in the next byte then pin an extra byte.
  5861. //
  5862. if ((SizeToMap != PAGE_SIZE) && (RemainingRecords != 0)) {
  5863. MapAdjust = 1;
  5864. } else {
  5865. MapAdjust = 0;
  5866. }
  5867. //
  5868. // Unpin any Bcb from a previous loop.
  5869. //
  5870. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  5871. //
  5872. // Read the desired bitmap page.
  5873. //
  5874. NtfsMapStream( IrpContext,
  5875. Vcb->MftBitmapScb,
  5876. BitmapCurrentOffset,
  5877. SizeToMap + MapAdjust,
  5878. &BitmapBcb,
  5879. &BitmapBuffer );
  5880. //
  5881. // Initialize the bitmap and search for a free bit.
  5882. //
  5883. RtlInitializeBitMap( &Bitmap, (PULONG) BitmapBuffer, SizeToMap * 8 );
  5884. Vcb->MftFreeRecords += RtlNumberOfClearBits( &Bitmap );
  5885. }
  5886. //
  5887. // If there are some remaining bits in the next byte then process
  5888. // them now.
  5889. //
  5890. if (RemainingRecords) {
  5891. PVOID RangePtr;
  5892. ULONG Index;
  5893. //
  5894. // Hopefully this byte is on the same page. Otherwise we will
  5895. // free this page and go to the next. In this case the Vcn will
  5896. // have the correct value because we walked past the end of the
  5897. // current file records already.
  5898. //
  5899. if (SizeToMap == PAGE_SIZE) {
  5900. //
  5901. // Unpin any Bcb from a previous loop.
  5902. //
  5903. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  5904. //
  5905. // Read the desired bitmap page.
  5906. //
  5907. NtfsMapStream( IrpContext,
  5908. Vcb->MftScb->ScbType.Index.RecordAllocationContext.BitmapScb,
  5909. BitmapCurrentOffset,
  5910. 1,
  5911. &BitmapBcb,
  5912. &BitmapBuffer );
  5913. //
  5914. // Set this to the byte prior to the last byte. This will
  5915. // set this to the same state as if on the same page.
  5916. //
  5917. SizeToMap = 0;
  5918. }
  5919. //
  5920. // We look at the next byte in the page and figure out how
  5921. // many bits are set.
  5922. //
  5923. NextByte = *((PUCHAR) Add2Ptr( BitmapBuffer, SizeToMap + 1 ));
  5924. while (RemainingRecords--) {
  5925. if (!FlagOn( NextByte, 0x01 )) {
  5926. Vcb->MftFreeRecords += 1;
  5927. }
  5928. NextByte >>= 1;
  5929. }
  5930. //
  5931. // We are now ready to look for holes within the Mft. We will look
  5932. // through the Mcb for the Mft looking for holes. The holes must
  5933. // always be an integral number of file records.
  5934. //
  5935. RangePtr = NULL;
  5936. Index = 0;
  5937. while (NtfsGetSequentialMcbEntry( &Vcb->MftScb->Mcb,
  5938. &RangePtr,
  5939. Index,
  5940. &Vcn,
  5941. &Lcn,
  5942. &Clusters )) {
  5943. //
  5944. // Look for a hole and count the clusters.
  5945. //
  5946. if (Lcn == UNUSED_LCN) {
  5947. if (Vcb->FileRecordsPerCluster == 0) {
  5948. Vcb->MftHoleRecords += (((ULONG)Clusters) >> Vcb->MftToClusterShift);
  5949. } else {
  5950. Vcb->MftHoleRecords += (((ULONG)Clusters) << Vcb->MftToClusterShift);
  5951. }
  5952. }
  5953. Index += 1;
  5954. }
  5955. }
  5956. } finally {
  5957. DebugUnwind( NtfsScanMftBitmap );
  5958. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  5959. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  5960. DebugTrace( -1, Dbg, ("NtfsScanMftBitmap...\n") );
  5961. }
  5962. return;
  5963. }
  5964. //
  5965. // Local support routine
  5966. //
  5967. BOOLEAN
  5968. NtfsAddDeallocatedRecords (
  5969. IN PVCB Vcb,
  5970. IN PSCB Scb,
  5971. IN ULONG StartingIndexOfBitmap,
  5972. IN OUT PRTL_BITMAP Bitmap
  5973. )
  5974. /*++
  5975. Routine Description:
  5976. This routine will modify the input bitmap by removing from it
  5977. any records that are in the recently deallocated queue of the scb.
  5978. If we do add stuff then we will not modify the bitmap buffer itself but
  5979. will allocate a new copy for the bitmap.
  5980. Arguments:
  5981. Vcb - Supplies the Vcb for the volume
  5982. Scb - Supplies the Scb used in this operation
  5983. StartingIndexOfBitmap - Supplies the base index to use to bias the bitmap
  5984. Bitmap - Supplies the bitmap being modified
  5985. Return Value:
  5986. BOOLEAN - TRUE if the bitmap has been modified and FALSE
  5987. otherwise.
  5988. --*/
  5989. {
  5990. BOOLEAN Results;
  5991. ULONG EndingIndexOfBitmap;
  5992. PLIST_ENTRY Links;
  5993. PDEALLOCATED_RECORDS DeallocatedRecords;
  5994. ULONG i;
  5995. ULONG Index;
  5996. PVOID NewBuffer;
  5997. ULONG SizeOfBitmapInBytes;
  5998. PAGED_CODE();
  5999. DebugTrace( +1, Dbg, ("NtfsAddDeallocatedRecords...\n") );
  6000. //
  6001. // Until shown otherwise we will assume that we haven't updated anything
  6002. //
  6003. Results = FALSE;
  6004. //
  6005. // Calculate the last index in the bitmap
  6006. //
  6007. EndingIndexOfBitmap = StartingIndexOfBitmap + Bitmap->SizeOfBitMap - 1;
  6008. SizeOfBitmapInBytes = (Bitmap->SizeOfBitMap + 7) / 8;
  6009. //
  6010. // Check if we need to bias the bitmap with the reserved index
  6011. //
  6012. if ((Scb == Vcb->MftScb) &&
  6013. FlagOn( Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED ) &&
  6014. (StartingIndexOfBitmap <= Scb->ScbType.Mft.ReservedIndex) &&
  6015. (Scb->ScbType.Mft.ReservedIndex <= EndingIndexOfBitmap)) {
  6016. //
  6017. // The index is a hit so now bias the index with the start of the bitmap
  6018. // and allocate an extra buffer to hold the bitmap
  6019. //
  6020. Index = Scb->ScbType.Mft.ReservedIndex - StartingIndexOfBitmap;
  6021. NewBuffer = NtfsAllocatePool(PagedPool, SizeOfBitmapInBytes );
  6022. RtlCopyMemory( NewBuffer, Bitmap->Buffer, SizeOfBitmapInBytes );
  6023. Bitmap->Buffer = NewBuffer;
  6024. Results = TRUE;
  6025. //
  6026. // And now set the bits in the bitmap to indicate that the record
  6027. // cannot be reallocated yet. Also set the other bits within the
  6028. // same byte so we can put all of the file records for the Mft
  6029. // within the same pages of the Mft.
  6030. //
  6031. ((PUCHAR) Bitmap->Buffer)[ Index / 8 ] = 0xff;
  6032. }
  6033. //
  6034. // Scan through the recently deallocated queue looking for any indexes that
  6035. // we need to modify
  6036. //
  6037. for (Links = Scb->ScbType.Index.RecentlyDeallocatedQueue.Flink;
  6038. Links != &Scb->ScbType.Index.RecentlyDeallocatedQueue;
  6039. Links = Links->Flink) {
  6040. DeallocatedRecords = CONTAINING_RECORD( Links, DEALLOCATED_RECORDS, ScbLinks );
  6041. //
  6042. // For every index in the record check if the index is within the range
  6043. // of the bitmap we are working with
  6044. //
  6045. for (i = 0; i < DeallocatedRecords->NextFreeEntry; i += 1) {
  6046. if ((StartingIndexOfBitmap <= DeallocatedRecords->Index[i]) &&
  6047. (DeallocatedRecords->Index[i] <= EndingIndexOfBitmap)) {
  6048. //
  6049. // The index is a hit so now bias the index with the start of the bitmap
  6050. // and check if we need to allocate an extra buffer to hold the bitmap
  6051. //
  6052. Index = DeallocatedRecords->Index[i] - StartingIndexOfBitmap;
  6053. if (!Results) {
  6054. NewBuffer = NtfsAllocatePool(PagedPool, SizeOfBitmapInBytes );
  6055. RtlCopyMemory( NewBuffer, Bitmap->Buffer, SizeOfBitmapInBytes );
  6056. Bitmap->Buffer = NewBuffer;
  6057. Results = TRUE;
  6058. }
  6059. //
  6060. // And now set the bit in the bitmap to indicate that the record
  6061. // cannot be reallocated yet. It's possible that the bit is
  6062. // already set if we have aborted a transaction which then
  6063. // restores the bit.
  6064. //
  6065. SetFlag( ((PUCHAR)Bitmap->Buffer)[ Index / 8 ], BitMask[Index % 8] );
  6066. }
  6067. }
  6068. }
  6069. //
  6070. // And return to our caller
  6071. //
  6072. DebugTrace( -1, Dbg, ("NtfsAddDeallocatedRecords -> %08lx\n", Results) );
  6073. return Results;
  6074. }
  6075. //
  6076. // Local support routine
  6077. //
  6078. LCN
  6079. NtfsInitializeMftZone (
  6080. IN PIRP_CONTEXT IrpContext,
  6081. IN PVCB Vcb
  6082. )
  6083. /*++
  6084. Routine Description:
  6085. This routine is called to reserve a range of the volume bitmap for use by the
  6086. Mft zone. We first look for a range which is contiguous with the end of the Mft.
  6087. If unavailable we look for a suitable length range out of the cached runs array.
  6088. We expect our caller has loaded the cached runs array with free runs in the volume
  6089. bitmap and also that the Mcb for the Mft is fully loaded.
  6090. Arguments:
  6091. Vcb - This is the Vcb for the volume we are looking for the zone for.
  6092. Return Value:
  6093. LCN - Return the LCN for the first run in the free portion of the zone.
  6094. --*/
  6095. {
  6096. LONGLONG ClusterCount;
  6097. LCN Lcn;
  6098. VCN Vcn;
  6099. LCN ZoneStart;
  6100. LONGLONG MinZoneSize;
  6101. LONGLONG DefaultZoneSize;
  6102. LONGLONG MftClusters;
  6103. BOOLEAN FoundRun;
  6104. PAGED_CODE();
  6105. //
  6106. // We synchronize on the volume bitmap.
  6107. //
  6108. ASSERT( NtfsIsExclusiveScb( Vcb->BitmapScb ));
  6109. ASSERT( NtfsIsExclusiveScb( Vcb->MftScb ));
  6110. DebugTrace( +1, Dbg, ("NtfsInitializeMftZone\n") );
  6111. //
  6112. // Remember the default size of the new zone and the number of clusters in the Mft.
  6113. //
  6114. MinZoneSize = Vcb->TotalClusters >> (NTFS_MFT_ZONE_DEFAULT_SHIFT + 1);
  6115. DefaultZoneSize = (Vcb->TotalClusters >> NTFS_MFT_ZONE_DEFAULT_SHIFT) * NtfsMftZoneMultiplier;
  6116. MftClusters = LlClustersFromBytesTruncate( Vcb, Vcb->MftScb->Header.AllocationSize.QuadPart );
  6117. if (DefaultZoneSize > MftClusters + MinZoneSize) {
  6118. DefaultZoneSize -= MftClusters;
  6119. } else {
  6120. DefaultZoneSize = MinZoneSize;
  6121. }
  6122. //
  6123. // Get the last Lcn for the Mft and check if we can find a contiguous free run.
  6124. //
  6125. FoundRun = NtfsLookupLastNtfsMcbEntry( &Vcb->MftScb->Mcb,
  6126. &Vcn,
  6127. &Lcn );
  6128. ASSERT( FoundRun && (Vcn + 1 >= MftClusters) );
  6129. //
  6130. // Look first in the cached runs array. If not there then look to the disk.
  6131. //
  6132. Lcn += 1;
  6133. if (!NtfsLookupCachedLcn( &Vcb->CachedRuns,
  6134. Lcn,
  6135. &ZoneStart,
  6136. &ClusterCount,
  6137. NULL )) {
  6138. //
  6139. // If there are no free runs then set the zone to a default value.
  6140. //
  6141. if (Vcb->CachedRuns.Used == 0) {
  6142. ZoneStart = Lcn;
  6143. ClusterCount = DefaultZoneSize;
  6144. //
  6145. // There should be a run available in the bitmap.
  6146. //
  6147. } else {
  6148. NtfsFindFreeBitmapRun( IrpContext,
  6149. Vcb,
  6150. DefaultZoneSize,
  6151. Lcn,
  6152. TRUE,
  6153. TRUE,
  6154. &ZoneStart,
  6155. &ClusterCount );
  6156. //
  6157. // If there is no contiguous range then look for the best fit in the cached
  6158. // runs array. Start by asking for half the original zone request. Up it
  6159. // if the current Mft is rather small.
  6160. //
  6161. if (ZoneStart != Lcn) {
  6162. ClusterCount = DefaultZoneSize;
  6163. //
  6164. // Lookup in the cached runs array by length.
  6165. //
  6166. NtfsLookupCachedLcnByLength( &Vcb->CachedRuns,
  6167. ClusterCount,
  6168. TRUE,
  6169. Lcn,
  6170. &ZoneStart,
  6171. &ClusterCount,
  6172. NULL );
  6173. }
  6174. }
  6175. }
  6176. //
  6177. // We now have a value for the zone start and length. Make sure we aren't overreserving the
  6178. // volume. Consider the current size of the Mft and the length of the additional zone.
  6179. //
  6180. if (ClusterCount > DefaultZoneSize) {
  6181. ClusterCount = DefaultZoneSize;
  6182. }
  6183. //
  6184. // Align the zone on ULONG boundary. RtlFindNextForwardRunClear expects the pointers
  6185. // to be ulong aligned.
  6186. //
  6187. Vcb->MftZoneStart = ZoneStart & ~0x1f;
  6188. Vcb->MftZoneEnd = (ZoneStart + ClusterCount + 0x1f) & ~0x1f;
  6189. //
  6190. // Keep it close to total clusters.
  6191. //
  6192. if (Vcb->MftZoneEnd > Vcb->TotalClusters) {
  6193. Vcb->MftZoneEnd = (Vcb->TotalClusters + 0x1f) & ~0x1f;
  6194. }
  6195. ClearFlag( Vcb->VcbState, VCB_STATE_REDUCED_MFT );
  6196. //
  6197. // Remove the Mft zone from the cached runs. We always look to the
  6198. // bitmap directly when extending the Mft.
  6199. //
  6200. NtfsRemoveCachedLcn( &Vcb->CachedRuns,
  6201. Vcb->MftZoneStart,
  6202. Vcb->MftZoneEnd - Vcb->MftZoneStart );
  6203. DebugTrace( -1, Dbg, ("NtfsInitializeMftZone -> VOID\n") );
  6204. return ZoneStart;
  6205. }
  6206. //
  6207. // Local support routine
  6208. //
  6209. BOOLEAN
  6210. NtfsReduceMftZone (
  6211. IN PIRP_CONTEXT IrpContext,
  6212. IN PVCB Vcb
  6213. )
  6214. /*++
  6215. Routine Description:
  6216. This routine is called when it appears that there is no disk space left on the
  6217. disk except the Mft zone. We will try to reduce the zone to make space
  6218. available for user files.
  6219. Arguments:
  6220. Vcb - Supplies the Vcb for the volume
  6221. Return Value:
  6222. BOOLEAN - TRUE if the Mft zone was shrunk. FALSE otherwise.
  6223. --*/
  6224. {
  6225. BOOLEAN ReduceMft = FALSE;
  6226. LONGLONG FreeClusters;
  6227. LONGLONG TargetFreeClusters;
  6228. LONGLONG PrevFreeClusters;
  6229. ULONG CurrentOffset;
  6230. LCN Lcn;
  6231. LCN StartingLcn;
  6232. LCN SplitLcn;
  6233. LCN FinalLcn;
  6234. RTL_BITMAP Bitmap;
  6235. PBCB BitmapBcb = NULL;
  6236. PAGED_CODE();
  6237. //
  6238. // Nothing to do if disk is almost empty.
  6239. //
  6240. if (Vcb->FreeClusters < (4 * MFT_EXTEND_GRANULARITY)) {
  6241. return FALSE;
  6242. }
  6243. //
  6244. // Use a try-finally to facilitate cleanup.
  6245. //
  6246. try {
  6247. //
  6248. // Bound our search by the end of the volume.
  6249. //
  6250. FinalLcn = Vcb->MftZoneEnd;
  6251. if (Vcb->MftZoneEnd > Vcb->TotalClusters) {
  6252. FinalLcn = Vcb->TotalClusters;
  6253. }
  6254. //
  6255. // We want to find the number of free clusters in the Mft zone and
  6256. // return half of them to the pool of clusters for users files.
  6257. //
  6258. FreeClusters = 0;
  6259. for (Lcn = Vcb->MftZoneStart;
  6260. Lcn < FinalLcn;
  6261. Lcn = StartingLcn + Bitmap.SizeOfBitMap) {
  6262. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  6263. NtfsMapPageInBitmap( IrpContext, Vcb, Lcn, &StartingLcn, &Bitmap, &BitmapBcb );
  6264. if ((StartingLcn + Bitmap.SizeOfBitMap) > FinalLcn) {
  6265. Bitmap.SizeOfBitMap = (ULONG) (FinalLcn - StartingLcn);
  6266. }
  6267. if (StartingLcn != Lcn) {
  6268. Bitmap.SizeOfBitMap -= (ULONG) (Lcn - StartingLcn);
  6269. Bitmap.Buffer = Add2Ptr( Bitmap.Buffer,
  6270. (ULONG) (Lcn - StartingLcn) / 8 );
  6271. StartingLcn = Lcn;
  6272. }
  6273. FreeClusters += RtlNumberOfClearBits( &Bitmap );
  6274. }
  6275. //
  6276. // If we are below our threshold then don't do the split.
  6277. //
  6278. if (FreeClusters < (4 * MFT_EXTEND_GRANULARITY)) {
  6279. try_return( NOTHING );
  6280. }
  6281. //
  6282. // Now we want to calculate 1/2 of this number of clusters and set the
  6283. // zone end to this point.
  6284. //
  6285. TargetFreeClusters = Int64ShraMod32( FreeClusters, 1 );
  6286. //
  6287. // Now look for the page which contains the split point.
  6288. //
  6289. FreeClusters = 0;
  6290. for (Lcn = Vcb->MftZoneStart;
  6291. Lcn < FinalLcn;
  6292. Lcn = StartingLcn + Bitmap.SizeOfBitMap) {
  6293. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  6294. NtfsMapPageInBitmap( IrpContext, Vcb, Lcn, &StartingLcn, &Bitmap, &BitmapBcb );
  6295. if ((StartingLcn + Bitmap.SizeOfBitMap) > FinalLcn) {
  6296. Bitmap.SizeOfBitMap = (ULONG) (FinalLcn - StartingLcn);
  6297. }
  6298. if (StartingLcn != Lcn) {
  6299. Bitmap.SizeOfBitMap -= (ULONG) (Lcn - StartingLcn);
  6300. Bitmap.Buffer = Add2Ptr( Bitmap.Buffer,
  6301. (ULONG) (Lcn - StartingLcn) / 8 );
  6302. StartingLcn = Lcn;
  6303. }
  6304. PrevFreeClusters = FreeClusters;
  6305. FreeClusters += RtlNumberOfClearBits( &Bitmap );
  6306. //
  6307. // Check if we found the page containing the split point.
  6308. //
  6309. if (FreeClusters >= TargetFreeClusters) {
  6310. CurrentOffset = 0;
  6311. while (TRUE) {
  6312. if (!RtlCheckBit( &Bitmap, CurrentOffset )) {
  6313. PrevFreeClusters += 1;
  6314. if (PrevFreeClusters == TargetFreeClusters) {
  6315. break;
  6316. }
  6317. }
  6318. CurrentOffset += 1;
  6319. }
  6320. SplitLcn = Lcn + CurrentOffset;
  6321. ReduceMft = TRUE;
  6322. break;
  6323. }
  6324. }
  6325. //
  6326. // If we are to reduce the Mft zone then set the split point and exit.
  6327. // We always round the split point up to a ULONG bitmap boundary so
  6328. // that the bitmap for the zone is ULONG aligned. RtlFindNextForwardRunClear
  6329. // expects the pointers to be ulong aligned.
  6330. //
  6331. if (ReduceMft) {
  6332. Vcb->MftZoneEnd = (SplitLcn + 0x1f) & ~0x1f;
  6333. //
  6334. // Keep it close to total clusters.
  6335. //
  6336. if (Vcb->MftZoneEnd > Vcb->TotalClusters) {
  6337. Vcb->MftZoneEnd = (Vcb->TotalClusters + 0x1f) & ~0x1f;
  6338. }
  6339. ASSERT( Vcb->MftZoneEnd >= Vcb->MftZoneStart );
  6340. if (Int64ShraMod32( Vcb->TotalClusters, 4 ) > Vcb->FreeClusters) {
  6341. SetFlag( Vcb->VcbState, VCB_STATE_REDUCED_MFT );
  6342. }
  6343. }
  6344. try_exit: NOTHING;
  6345. } finally {
  6346. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  6347. }
  6348. return ReduceMft;
  6349. }
  6350. //
  6351. // Local support routine
  6352. //
  6353. VOID
  6354. NtfsCheckRecordStackUsage (
  6355. IN PIRP_CONTEXT IrpContext
  6356. )
  6357. /*++
  6358. Routine Description:
  6359. This routine is called in the record package prior to adding allocation
  6360. to either a data stream or bitmap stream. The purpose is to verify
  6361. that there is room on the stack to perform a log file full in the
  6362. AddAllocation operation. This routine will check the stack space and
  6363. the available log file space and raise LOG_FILE_FULL defensively if
  6364. both of these reach a critical threshold.
  6365. Arguments:
  6366. Return Value:
  6367. None - this routine will raise if necessary.
  6368. --*/
  6369. {
  6370. LOG_FILE_INFORMATION LogFileInfo;
  6371. ULONG InfoSize;
  6372. LONGLONG RemainingLogFile;
  6373. PAGED_CODE();
  6374. //
  6375. // Check the stack usage first.
  6376. //
  6377. if (IoGetRemainingStackSize() < OVERFLOW_RECORD_THRESHHOLD) {
  6378. //
  6379. // Now check the log file space.
  6380. //
  6381. InfoSize = sizeof( LOG_FILE_INFORMATION );
  6382. RtlZeroMemory( &LogFileInfo, InfoSize );
  6383. LfsReadLogFileInformation( IrpContext->Vcb->LogHandle,
  6384. &LogFileInfo,
  6385. &InfoSize );
  6386. //
  6387. // Check that 1/4 of the log file is available.
  6388. //
  6389. if (InfoSize != 0) {
  6390. RemainingLogFile = LogFileInfo.CurrentAvailable - LogFileInfo.TotalUndoCommitment;
  6391. if ((RemainingLogFile <= 0) ||
  6392. (RemainingLogFile < Int64ShraMod32(LogFileInfo.TotalAvailable, 2))) {
  6393. NtfsRaiseStatus( IrpContext, STATUS_LOG_FILE_FULL, NULL, NULL );
  6394. }
  6395. }
  6396. }
  6397. return;
  6398. }
  6399. //
  6400. // Local support routine
  6401. //
  6402. VOID
  6403. NtfsRunIsClear (
  6404. IN PIRP_CONTEXT IrpContext,
  6405. IN PVCB Vcb,
  6406. IN LCN StartingLcn,
  6407. IN LONGLONG RunLength
  6408. )
  6409. /*++
  6410. Routine Description:
  6411. This routine verifies that a group of clusters are unallocated.
  6412. Arguments:
  6413. Vcb - Supplies the Vcb used in this operation
  6414. StartingLcn - Supplies the start of the cluster run
  6415. RunLength - Supplies the length of the cluster run
  6416. Return Value:
  6417. None.
  6418. --*/
  6419. {
  6420. RTL_BITMAP Bitmap;
  6421. PBCB BitmapBcb = NULL;
  6422. BOOLEAN StuffAdded = FALSE;
  6423. LONGLONG BitOffset;
  6424. LONGLONG BitCount;
  6425. LCN BaseLcn;
  6426. LCN Lcn = StartingLcn;
  6427. LONGLONG ValidDataLength;
  6428. ASSERT_IRP_CONTEXT( IrpContext );
  6429. ASSERT_VCB( Vcb );
  6430. PAGED_CODE();
  6431. DebugTrace( +1, Dbg, ("NtfsRunIsClear\n") );
  6432. ValidDataLength = Vcb->BitmapScb->Header.ValidDataLength.QuadPart;
  6433. try {
  6434. //
  6435. // Ensure that StartingLcn is not past the length of the bitmap.
  6436. //
  6437. if (StartingLcn > ValidDataLength * 8) {
  6438. NtfsRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER, NULL, NULL );
  6439. }
  6440. while (RunLength > 0){
  6441. //
  6442. // Access the next page of bitmap and update it
  6443. //
  6444. NtfsMapPageInBitmap(IrpContext, Vcb, Lcn, &BaseLcn, &Bitmap, &BitmapBcb);
  6445. //
  6446. // Get offset into this page and bits to end of this page
  6447. //
  6448. BitOffset = Lcn - BaseLcn;
  6449. BitCount = Bitmap.SizeOfBitMap - BitOffset;
  6450. //
  6451. // Adjust for bits to end of page
  6452. //
  6453. if (BitCount > RunLength){
  6454. BitCount = RunLength;
  6455. }
  6456. //
  6457. // If any bit is set get out
  6458. //
  6459. if (!RtlAreBitsClear( &Bitmap, (ULONG)BitOffset, (ULONG)BitCount)) {
  6460. NtfsRaiseStatus( IrpContext, STATUS_ALREADY_COMMITTED, NULL, NULL );
  6461. }
  6462. StuffAdded = NtfsAddRecentlyDeallocated(Vcb, BaseLcn, &Bitmap);
  6463. //
  6464. // Now if anything was added, check if the desired clusters are still
  6465. // free, else just free the stuff added.
  6466. //
  6467. if (StuffAdded) {
  6468. //
  6469. // If any bit is set now, raise STATUS_DELETE_PENDING to indicate
  6470. // that the space will soon be free (or can be made free).
  6471. //
  6472. if (!RtlAreBitsClear( &Bitmap, (ULONG)BitOffset, (ULONG)BitCount)) {
  6473. NtfsRaiseStatus( IrpContext, STATUS_DELETE_PENDING, NULL, NULL );
  6474. }
  6475. //
  6476. // Free up resources
  6477. //
  6478. NtfsFreePool(Bitmap.Buffer);
  6479. StuffAdded = FALSE;
  6480. }
  6481. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  6482. //
  6483. // Decrease remaining bits by amount checked in this page and move Lcn to beginning
  6484. // of next page
  6485. //
  6486. RunLength = RunLength - BitCount;
  6487. Lcn = BaseLcn + Bitmap.SizeOfBitMap;
  6488. }
  6489. } finally {
  6490. DebugUnwind(NtfsRunIsClear);
  6491. //
  6492. // Free up resources
  6493. //
  6494. if(StuffAdded){ NtfsFreePool(Bitmap.Buffer); StuffAdded = FALSE; }
  6495. NtfsUnpinBcb( IrpContext, &BitmapBcb );
  6496. }
  6497. DebugTrace( -1, Dbg, ("NtfsRunIsClear -> VOID\n") );
  6498. return;
  6499. }
  6500. //
  6501. // Local support routine
  6502. //
  6503. VOID
  6504. NtfsInitializeCachedRuns (
  6505. IN PNTFS_CACHED_RUNS CachedRuns
  6506. )
  6507. /*++
  6508. Routine Description:
  6509. This routine will initialize the cached run information.
  6510. Arguments:
  6511. CachedRuns - Pointer to an uninitialized cached run structure.
  6512. Return Value:
  6513. None - this routine will raise if unable to initialize the structure.
  6514. --*/
  6515. {
  6516. USHORT Index;
  6517. PAGED_CODE();
  6518. DebugTrace( +1, Dbg, ("NtfsInitializeCachedRuns\n") );
  6519. //
  6520. // Initialize the operating parameters.
  6521. //
  6522. CachedRuns->MaximumSize = 9000;
  6523. CachedRuns->MinCount = 100;
  6524. //
  6525. // Allocate pool for the arrays.
  6526. //
  6527. CachedRuns->LcnArray = NtfsAllocatePool( PagedPool,
  6528. sizeof( NTFS_LCN_CLUSTER_RUN ) * NTFS_INITIAL_CACHED_RUNS );
  6529. CachedRuns->LengthArray = NtfsAllocatePool( PagedPool,
  6530. sizeof( USHORT ) * NTFS_INITIAL_CACHED_RUNS );
  6531. //
  6532. // Mark all entries so that they can be detected as deleted.
  6533. //
  6534. for (Index = 0; Index < NTFS_INITIAL_CACHED_RUNS; Index += 1) {
  6535. CachedRuns->LcnArray[Index].RunLength = 0;
  6536. CachedRuns->LcnArray[Index].LengthIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  6537. CachedRuns->LengthArray[Index] = NTFS_CACHED_RUNS_DEL_INDEX;
  6538. }
  6539. CachedRuns->Avail = NTFS_INITIAL_CACHED_RUNS;
  6540. //
  6541. // Allocate space for the histogram of small run lengths.
  6542. //
  6543. CachedRuns->Bins = NTFS_CACHED_RUNS_BIN_COUNT;
  6544. CachedRuns->BinArray = NtfsAllocatePool( PagedPool,
  6545. sizeof( USHORT ) * CachedRuns->Bins );
  6546. RtlZeroMemory( CachedRuns->BinArray,
  6547. sizeof( USHORT ) * CachedRuns->Bins );
  6548. //
  6549. // Allocate space for the windows of deleted entries in the sorted
  6550. // arrays.
  6551. //
  6552. CachedRuns->DelLcnCount = 0;
  6553. CachedRuns->DelLengthCount = 0;
  6554. CachedRuns->DeletedLcnWindows = NtfsAllocatePool( PagedPool,
  6555. sizeof( NTFS_DELETED_RUNS ) * NTFS_CACHED_RUNS_MAX_DEL_WINDOWS );
  6556. CachedRuns->DeletedLengthWindows = NtfsAllocatePool( PagedPool,
  6557. sizeof( NTFS_DELETED_RUNS ) * NTFS_CACHED_RUNS_MAX_DEL_WINDOWS );
  6558. //
  6559. // Create a window of deleted entries to cover the newly allocated
  6560. // entries.
  6561. //
  6562. NtfsAddDelWindow( CachedRuns, 0, CachedRuns->Avail - 1, TRUE );
  6563. NtfsAddDelWindow( CachedRuns, 0, CachedRuns->Avail - 1, FALSE );
  6564. //
  6565. // Clear the in use count.
  6566. //
  6567. CachedRuns->Used = 0;
  6568. //
  6569. // Reset the longest freed run.
  6570. //
  6571. CachedRuns->LongestFreedRun = MAXLONGLONG;
  6572. #ifdef NTFS_CHECK_CACHED_RUNS
  6573. if (NtfsDoVerifyCachedRuns) {
  6574. NtfsVerifyCachedRuns( CachedRuns, FALSE, FALSE );
  6575. }
  6576. #endif
  6577. DebugTrace( -1, Dbg, ("NtfsInitializeCachedRuns -> VOID\n") );
  6578. return;
  6579. }
  6580. //
  6581. // Local support routine
  6582. //
  6583. VOID
  6584. NtfsReinitializeCachedRuns (
  6585. IN PNTFS_CACHED_RUNS CachedRuns
  6586. )
  6587. /*++
  6588. Routine Description:
  6589. This routine is called to reinitialize the cached runs array.
  6590. Arguments:
  6591. CachedRuns - Pointer to a cached run structure.
  6592. Return Value:
  6593. None
  6594. --*/
  6595. {
  6596. USHORT Index;
  6597. PNTFS_LCN_CLUSTER_RUN NewLcnArray;
  6598. PUSHORT NewLengthArray;
  6599. PAGED_CODE();
  6600. DebugTrace( +1, Dbg, ("NtfsReinitializeCachedRuns\n") );
  6601. //
  6602. // Reallocate to get a smaller array. If we get an allocation failure then simply
  6603. // empty the larger arrays.
  6604. //
  6605. if (CachedRuns->Avail != NTFS_INITIAL_CACHED_RUNS) {
  6606. NewLcnArray = NtfsAllocatePoolNoRaise( PagedPool,
  6607. sizeof( NTFS_LCN_CLUSTER_RUN ) * NTFS_INITIAL_CACHED_RUNS );
  6608. if (NewLcnArray != NULL) {
  6609. //
  6610. // Allocate the length array.
  6611. //
  6612. NewLengthArray = NtfsAllocatePoolNoRaise( PagedPool,
  6613. sizeof( USHORT ) * NTFS_INITIAL_CACHED_RUNS );
  6614. //
  6615. // If we didn't get the Length array then simply use what we have already.
  6616. //
  6617. if (NewLengthArray == NULL) {
  6618. NtfsFreePool( NewLcnArray );
  6619. //
  6620. // Otherwise replace the Lcn and length arrays.
  6621. //
  6622. } else {
  6623. NtfsFreePool( CachedRuns->LcnArray );
  6624. CachedRuns->LcnArray = NewLcnArray;
  6625. NtfsFreePool( CachedRuns->LengthArray );
  6626. CachedRuns->LengthArray = NewLengthArray;
  6627. CachedRuns->Avail = NTFS_INITIAL_CACHED_RUNS;
  6628. }
  6629. }
  6630. }
  6631. //
  6632. // Mark all entries so that they can be detected as deleted.
  6633. //
  6634. for (Index = 0; Index < CachedRuns->Avail; Index += 1) {
  6635. CachedRuns->LcnArray[Index].RunLength = 0;
  6636. CachedRuns->LcnArray[Index].LengthIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  6637. CachedRuns->LengthArray[Index] = NTFS_CACHED_RUNS_DEL_INDEX;
  6638. }
  6639. //
  6640. // Clear the histogram of run lengths.
  6641. //
  6642. RtlZeroMemory( CachedRuns->BinArray, sizeof( USHORT ) * CachedRuns->Bins );
  6643. //
  6644. // Clear the list of windows of deleted entries.
  6645. //
  6646. CachedRuns->DelLcnCount = 0;
  6647. CachedRuns->DelLengthCount = 0;
  6648. //
  6649. // Create a window of deleted entries to cover all of the entries.
  6650. //
  6651. NtfsAddDelWindow( CachedRuns, 0, CachedRuns->Avail - 1, TRUE );
  6652. NtfsAddDelWindow( CachedRuns, 0, CachedRuns->Avail - 1, FALSE );
  6653. //
  6654. // Clear the in use count.
  6655. //
  6656. CachedRuns->Used = 0;
  6657. //
  6658. // Reset the longest freed run.
  6659. //
  6660. CachedRuns->LongestFreedRun = MAXLONGLONG;
  6661. #ifdef NTFS_CHECK_CACHED_RUNS
  6662. if (NtfsDoVerifyCachedRuns) {
  6663. NtfsVerifyCachedRuns( CachedRuns, FALSE, FALSE );
  6664. }
  6665. #endif
  6666. DebugTrace( -1, Dbg, ("NtfsReinitializeCachedRuns -> VOID\n") );
  6667. return;
  6668. }
  6669. //
  6670. // Local support routine
  6671. //
  6672. VOID
  6673. NtfsUninitializeCachedRuns (
  6674. IN PNTFS_CACHED_RUNS CachedRuns
  6675. )
  6676. /*++
  6677. Routine Description:
  6678. This routine is called to clean up the cached run information.
  6679. Arguments:
  6680. CachedRuns - Pointer to a cached run structure. Be defensive and check that
  6681. it is really initialized.
  6682. Return Value:
  6683. None
  6684. --*/
  6685. {
  6686. PAGED_CODE();
  6687. DebugTrace( +1, Dbg, ("NtfsUninitializeCachedRuns\n") );
  6688. if (CachedRuns->LcnArray != NULL) {
  6689. NtfsFreePool( CachedRuns->LcnArray );
  6690. CachedRuns->LcnArray = NULL;
  6691. }
  6692. if (CachedRuns->LengthArray != NULL) {
  6693. NtfsFreePool( CachedRuns->LengthArray );
  6694. CachedRuns->LengthArray = NULL;
  6695. }
  6696. if (CachedRuns->BinArray != NULL) {
  6697. NtfsFreePool( CachedRuns->BinArray );
  6698. CachedRuns->BinArray = NULL;
  6699. }
  6700. if (CachedRuns->DeletedLcnWindows != NULL) {
  6701. NtfsFreePool( CachedRuns->DeletedLcnWindows );
  6702. CachedRuns->DeletedLcnWindows = NULL;
  6703. }
  6704. if (CachedRuns->DeletedLengthWindows != NULL) {
  6705. NtfsFreePool( CachedRuns->DeletedLengthWindows );
  6706. CachedRuns->DeletedLengthWindows = NULL;
  6707. }
  6708. CachedRuns->Used = 0;
  6709. CachedRuns->Avail = 0;
  6710. CachedRuns->DelLcnCount = 0;
  6711. CachedRuns->DelLengthCount = 0;
  6712. CachedRuns->Bins = 0;
  6713. DebugTrace( -1, Dbg, ("NtfsUninitializeCachedRuns -> VOID\n") );
  6714. return;
  6715. }
  6716. //
  6717. // Local support routine
  6718. //
  6719. BOOLEAN
  6720. NtfsLookupCachedLcn (
  6721. IN PNTFS_CACHED_RUNS CachedRuns,
  6722. IN LCN Lcn,
  6723. OUT PLCN StartingLcn,
  6724. OUT PLONGLONG RunLength,
  6725. OUT PUSHORT Index OPTIONAL
  6726. )
  6727. /*++
  6728. Routine Description:
  6729. This routine is called to look up a specific Lcn in the cached runs structure.
  6730. If found it will return the entire run. It will also return the index in the
  6731. Lcn array to use as an optimization in a later call.
  6732. Arguments:
  6733. CachedRuns - Pointer to the cached runs structure.
  6734. Lcn - This is the desired Lcn.
  6735. StartingLcn - Address to store the Lcn which begins the run in the cached
  6736. structure. Typically this is the same as the Lcn above.
  6737. RunLength - Address to store the length of the found run.
  6738. Index - If specified we store the index for the run we found. This can be
  6739. used as an optimization if we later remove the run.
  6740. Return Value:
  6741. BOOLEAN - TRUE if the run was found. FALSE otherwise.
  6742. --*/
  6743. {
  6744. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  6745. USHORT FoundIndex;
  6746. BOOLEAN FoundLcn;
  6747. PAGED_CODE();
  6748. DebugTrace( +1, Dbg, ("NtfsLookupCachedLcn\n") );
  6749. //
  6750. // Lookup a run containing the specific Lcn.
  6751. //
  6752. FoundLcn = NtfsPositionCachedLcn( CachedRuns,
  6753. Lcn,
  6754. &FoundIndex );
  6755. //
  6756. // If we found the Lcn then return the full run.
  6757. //
  6758. if (FoundLcn) {
  6759. ThisEntry = CachedRuns->LcnArray + FoundIndex;
  6760. *StartingLcn = ThisEntry->Lcn;
  6761. *RunLength = ThisEntry->RunLength;
  6762. }
  6763. if (ARGUMENT_PRESENT( Index )) {
  6764. *Index = FoundIndex;
  6765. }
  6766. DebugTrace( -1, Dbg, ("NtfsLookupCachedLcn -> %01x\n", FoundLcn) );
  6767. return FoundLcn;
  6768. }
  6769. //
  6770. // Local support routine
  6771. //
  6772. BOOLEAN
  6773. NtfsGetNextCachedLcn (
  6774. IN PNTFS_CACHED_RUNS CachedRuns,
  6775. IN USHORT Index,
  6776. OUT PLCN StartingLcn,
  6777. OUT PLONGLONG RunLength
  6778. )
  6779. /*++
  6780. Routine Description:
  6781. This routine is called to find an entry in the Lcn array by position.
  6782. It is assumed that the entry is not deleted.
  6783. Arguments:
  6784. CachedRuns - Pointer to the cached runs structure.
  6785. Index - Index to look up. It might point beyond the array.
  6786. StartingLcn - Address to store the Lcn at this position.
  6787. RunLength - Address to store the RunLength at this position.
  6788. Return Value:
  6789. BOOLEAN - TRUE if an entry was found, FALSE otherwise.
  6790. --*/
  6791. {
  6792. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  6793. BOOLEAN FoundRun = FALSE;
  6794. PAGED_CODE();
  6795. DebugTrace( +1, Dbg, ("NtfsGetNextCachedLcn\n") );
  6796. //
  6797. // If the input index is within the array then return the run.
  6798. //
  6799. if (Index < CachedRuns->Used) {
  6800. ThisEntry = CachedRuns->LcnArray + Index;
  6801. ASSERT( ThisEntry->RunLength );
  6802. *StartingLcn = ThisEntry->Lcn;
  6803. *RunLength = ThisEntry->RunLength;
  6804. FoundRun = TRUE;
  6805. }
  6806. DebugTrace( -1, Dbg, ("NtfsGetNextCachedLcn -> %01x\n", FoundRun) );
  6807. return FoundRun;
  6808. }
  6809. //
  6810. // Local support routine
  6811. //
  6812. BOOLEAN
  6813. NtfsLookupCachedLcnByLength (
  6814. IN PNTFS_CACHED_RUNS CachedRuns,
  6815. IN LONGLONG Length,
  6816. IN BOOLEAN AllowShorter,
  6817. IN LCN Lcn,
  6818. OUT PLCN StartingLcn,
  6819. OUT PLONGLONG RunLength,
  6820. OUT PUSHORT Index OPTIONAL
  6821. )
  6822. /*++
  6823. Routine Description:
  6824. This routine is called to look up a cached run of a certain length. We
  6825. give caller any run of the given length or longer if possible. If there
  6826. is no such entry, we will use a shorter entry if allowed.
  6827. Arguments:
  6828. CachedRuns - Pointer to the cached run structure.
  6829. Length - Length of the run we are interested in.
  6830. AllowShorter - whether to accept a shorter length run if nothing else is available
  6831. Lcn - We try to find the run which is closest to this Lcn, but has the
  6832. requested Length.
  6833. StartingLcn - Address to store the starting Lcn of the run we found.
  6834. RunLength - Address to store the length of the run we found.
  6835. Index - If specified then this is the index in the RunLength array
  6836. of the entry we found. It can be used later to remove the entry.
  6837. Return Value:
  6838. BOOLEAN - TRUE if an entry was found, FALSE otherwise.
  6839. --*/
  6840. {
  6841. USHORT FoundIndex;
  6842. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  6843. PNTFS_DELETED_RUNS DelWindow;
  6844. BOOLEAN FoundRun;
  6845. PAGED_CODE();
  6846. DebugTrace( +1, Dbg, ("NtfsLookupCachedLcnByLength\n") );
  6847. //
  6848. // Position ourselves for a run of a particular length.
  6849. //
  6850. FoundRun = NtfsPositionCachedLcnByLength( CachedRuns,
  6851. Length,
  6852. &Lcn,
  6853. NULL,
  6854. TRUE,
  6855. &FoundIndex );
  6856. if (!FoundRun) {
  6857. //
  6858. // We didn't find a run with the desired length. However if
  6859. // we aren't pointing past the end of the array then there
  6860. // is an entry available we can use.
  6861. //
  6862. if (FoundIndex < CachedRuns->Used) {
  6863. FoundRun = TRUE;
  6864. } else if (AllowShorter && (CachedRuns->Used > 0)) {
  6865. //
  6866. // There are no larger entries, but there might be smaller
  6867. // ones and the caller has indicated we can use them. The
  6868. // entry at the end of the list should be the largest
  6869. // available.
  6870. //
  6871. FoundIndex = CachedRuns->Used - 1;
  6872. FoundRun = TRUE;
  6873. }
  6874. //
  6875. // Check and see if there is a suitable element at or near this index.
  6876. //
  6877. if (FoundRun) {
  6878. //
  6879. // The entry has been deleted. Get the window of deleted
  6880. // entries that covers it and see if there is a usable entry on either side.
  6881. //
  6882. if (CachedRuns->LengthArray[ FoundIndex ] == NTFS_CACHED_RUNS_DEL_INDEX) {
  6883. DelWindow = NtfsGetDelWindow( CachedRuns,
  6884. FoundIndex,
  6885. FoundIndex,
  6886. FALSE,
  6887. NULL);
  6888. ASSERT( DelWindow );
  6889. ASSERT( DelWindow->StartIndex <= FoundIndex );
  6890. ASSERT( DelWindow->EndIndex >= FoundIndex );
  6891. //
  6892. // Use the entry just before the start of this window
  6893. // of deleted entries if one exists.
  6894. //
  6895. if (DelWindow->StartIndex > 0) {
  6896. FoundIndex = DelWindow->StartIndex - 1;
  6897. //
  6898. // All of the entries are deleted.
  6899. //
  6900. } else {
  6901. FoundRun = FALSE;
  6902. }
  6903. //
  6904. // If we aren't considering a shorter element then this should be a longer one.
  6905. //
  6906. } else {
  6907. ASSERT( AllowShorter ||
  6908. (CachedRuns->LcnArray[ CachedRuns->LengthArray[ FoundIndex ]].RunLength >= Length) );
  6909. }
  6910. }
  6911. }
  6912. //
  6913. // If we have a run then return the run information.
  6914. //
  6915. if (FoundRun) {
  6916. ThisEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ FoundIndex ];
  6917. ASSERT( ThisEntry->RunLength != 0 );
  6918. *StartingLcn = ThisEntry->Lcn;
  6919. *RunLength = ThisEntry->RunLength;
  6920. if (ARGUMENT_PRESENT( Index )) {
  6921. *Index = FoundIndex;
  6922. }
  6923. }
  6924. #ifdef NTFS_CHECK_CACHED_RUNS
  6925. if (NtfsDoVerifyCachedRuns) {
  6926. NtfsVerifyCachedRuns( CachedRuns, FALSE, FALSE );
  6927. }
  6928. #endif
  6929. DebugTrace( -1, Dbg, ("NtfsLookupCachedLcnByLength -> %01x\n", FoundRun) );
  6930. return FoundRun;
  6931. }
  6932. //
  6933. // Local support routine
  6934. //
  6935. VOID
  6936. NtfsAddDelWindow (
  6937. IN PNTFS_CACHED_RUNS CachedRuns,
  6938. IN USHORT FirstIndex,
  6939. IN USHORT LastIndex,
  6940. IN BOOLEAN LcnList
  6941. )
  6942. /*++
  6943. Routine Description:
  6944. This routine is called to add the given range of indices to a window
  6945. of entries known to be deleted. If the entries are adjacent to an
  6946. existing window, that window is extended. Otherwise a new window is
  6947. allocated. If there is no space in the array to add a new window, the
  6948. list is compacted. Therefore, callers should be aware that indices may
  6949. change across this call. However we do guarantee that the indices in
  6950. [FirstIndex..LastIndex] will not move.
  6951. It is assumed that no window already includes the given index range.
  6952. Arguments:
  6953. CachedRuns - Pointer to the cached runs structure.
  6954. FirstIndex - Index that marks the start of the range of deleted entries.
  6955. LastIndex - The index of the last entry in the range of deleted entries.
  6956. LcnList - If TRUE, the indices are from the Lcn-sorted list.
  6957. If FALSE, the indices are from the length-sorted list.
  6958. Return Value:
  6959. None.
  6960. --*/
  6961. {
  6962. USHORT WindowIndex;
  6963. PUSHORT Count;
  6964. PNTFS_DELETED_RUNS FirstWindow;
  6965. PNTFS_DELETED_RUNS DelWindow;
  6966. PNTFS_DELETED_RUNS NextWindow;
  6967. PAGED_CODE();
  6968. DebugTrace( +1, Dbg, ("NtfsAddDelWindow\n") );
  6969. //
  6970. // Get pointers to the windows we will be updating.
  6971. //
  6972. if (LcnList) {
  6973. Count = &CachedRuns->DelLcnCount;
  6974. FirstWindow = CachedRuns->DeletedLcnWindows;
  6975. } else {
  6976. Count = &CachedRuns->DelLengthCount;
  6977. FirstWindow = CachedRuns->DeletedLengthWindows;
  6978. }
  6979. ASSERT( *Count <= NTFS_CACHED_RUNS_MAX_DEL_WINDOWS );
  6980. while (TRUE) {
  6981. DebugTrace( 0, Dbg, ("*Count=%04x, FirstIndex=%04x, LastIndex=%04x\n", *Count, FirstIndex, LastIndex) );
  6982. if (*Count != 0) {
  6983. //
  6984. // Get the window of deleted entries that is closest to the range
  6985. // of indices we are adding.
  6986. //
  6987. DelWindow = NtfsGetDelWindow(CachedRuns,
  6988. FirstIndex,
  6989. LastIndex,
  6990. LcnList,
  6991. &WindowIndex );
  6992. ASSERT( DelWindow != NULL );
  6993. ASSERT( (DelWindow->EndIndex < FirstIndex) || (DelWindow->StartIndex > LastIndex) );
  6994. DebugTrace( 0, Dbg, ("WindowIndex=%04x, StartIndex=%04x, EndIndex=%04x\n",
  6995. WindowIndex, DelWindow->StartIndex, DelWindow->EndIndex) );
  6996. //
  6997. // Check if our range extends this window.
  6998. //
  6999. if (DelWindow->EndIndex == (FirstIndex - 1)) {
  7000. //
  7001. // Extend this window upwards.
  7002. //
  7003. DebugTrace( 0, Dbg, ("Extend window up from %04x to %04x\n",
  7004. DelWindow->EndIndex, LastIndex) );
  7005. DelWindow->EndIndex = LastIndex;
  7006. //
  7007. // If not the last window then check if we ajoin the following window.
  7008. //
  7009. if (WindowIndex < (*Count - 1) ) {
  7010. NextWindow = DelWindow + 1;
  7011. ASSERT( NextWindow->StartIndex > LastIndex );
  7012. if (NextWindow->StartIndex == (LastIndex + 1) ) {
  7013. //
  7014. // Combine these two windows.
  7015. //
  7016. DebugTrace( 0, Dbg, ("Combine with next window up to %04x\n",
  7017. NextWindow->EndIndex) );
  7018. DelWindow->EndIndex = NextWindow->EndIndex;
  7019. //
  7020. // Delete the additional window.
  7021. //
  7022. NtfsDeleteDelWindow( CachedRuns,
  7023. LcnList,
  7024. WindowIndex + 1 );
  7025. }
  7026. }
  7027. break;
  7028. //
  7029. // Check if we extend this window downwards.
  7030. //
  7031. } else if (DelWindow->StartIndex == (LastIndex + 1)) {
  7032. DebugTrace( 0, Dbg, ("Extend window down from %04x to %04x\n",
  7033. DelWindow->EndIndex, FirstIndex) );
  7034. DelWindow->StartIndex = FirstIndex;
  7035. //
  7036. // Check if we join the previous window if present.
  7037. //
  7038. if (WindowIndex > 0) {
  7039. NextWindow = DelWindow - 1;
  7040. ASSERT( NextWindow->EndIndex < FirstIndex );
  7041. if (NextWindow->EndIndex == (FirstIndex - 1) ) {
  7042. //
  7043. // Combine these two windows.
  7044. //
  7045. DebugTrace( 0,
  7046. Dbg,
  7047. ("Combine with prev window up to %04x\n", NextWindow->StartIndex) );
  7048. NextWindow->EndIndex = DelWindow->EndIndex;
  7049. //
  7050. // Delete the unused window.
  7051. //
  7052. NtfsDeleteDelWindow( CachedRuns,
  7053. LcnList,
  7054. WindowIndex );
  7055. }
  7056. }
  7057. break;
  7058. //
  7059. // Add a new window after the window we found.
  7060. //
  7061. } else if (DelWindow->EndIndex < FirstIndex) {
  7062. //
  7063. // Insert the new window after WindowIndex.
  7064. //
  7065. DebugTrace( 0, Dbg, ("New window at %04x + 1\n", WindowIndex) );
  7066. WindowIndex += 1;
  7067. } else {
  7068. //
  7069. // Insert the new window at WindowIndex.
  7070. //
  7071. DebugTrace( 0, Dbg, ("New window at %04x\n", WindowIndex) );
  7072. }
  7073. } else {
  7074. //
  7075. // Just create a new window at index 0.
  7076. //
  7077. DebugTrace( 0, Dbg, ("First new window\n") );
  7078. WindowIndex = 0;
  7079. }
  7080. //
  7081. // If we reach this point then we need to make a new window. We have the position
  7082. // we want to put the window.
  7083. //
  7084. // If we don't have an available run then compact two of the existing runs.
  7085. //
  7086. if (*Count == NTFS_CACHED_RUNS_MAX_DEL_WINDOWS) {
  7087. DebugTrace( 0, Dbg, ("Compact\n") );
  7088. NtfsCompactCachedRuns( CachedRuns,
  7089. FirstIndex,
  7090. LastIndex,
  7091. LcnList );
  7092. ASSERT( *Count < NTFS_CACHED_RUNS_MAX_DEL_WINDOWS );
  7093. //
  7094. // Retry the loop to find the correct window position.
  7095. //
  7096. continue;
  7097. }
  7098. //
  7099. // Position ourselves at the insert point.
  7100. //
  7101. DelWindow = FirstWindow + WindowIndex;
  7102. //
  7103. // Right copy the windows to make a space if we aren't at the end.
  7104. //
  7105. if (WindowIndex < *Count) {
  7106. DebugTrace( 0, Dbg, ("Copy up window indices from %04x, %04x entries\n",
  7107. WindowIndex,
  7108. *Count - WindowIndex) );
  7109. RtlMoveMemory( DelWindow + 1,
  7110. DelWindow,
  7111. sizeof( NTFS_DELETED_RUNS ) * (*Count - WindowIndex) );
  7112. }
  7113. //
  7114. // Put the new information in DelWindow
  7115. //
  7116. DelWindow->StartIndex = FirstIndex;
  7117. DelWindow->EndIndex = LastIndex;
  7118. //
  7119. // Increment the windows count.
  7120. //
  7121. *Count += 1;
  7122. break;
  7123. }
  7124. ASSERT( (CachedRuns->DelLcnCount > 0) || !LcnList );
  7125. ASSERT( (CachedRuns->DelLengthCount > 0) || LcnList );
  7126. #ifdef NTFS_CHECK_CACHED_RUNS
  7127. if (NtfsDoVerifyCachedRuns) {
  7128. //
  7129. // Make certain that the windows are in order and don't overlap.
  7130. //
  7131. for (WindowIndex = 0, DelWindow = NextWindow = FirstWindow;
  7132. WindowIndex < *Count;
  7133. WindowIndex += 1, NextWindow += 1) {
  7134. ASSERT( NextWindow->StartIndex <= NextWindow->EndIndex );
  7135. if (NextWindow != DelWindow) {
  7136. ASSERT( NextWindow->StartIndex > (DelWindow->EndIndex + 1) );
  7137. DelWindow += 1;
  7138. }
  7139. }
  7140. }
  7141. #endif
  7142. DebugTrace( -1, Dbg, ("NtfsAddDelWindow -> VOID\n") );
  7143. return;
  7144. }
  7145. //
  7146. // Local support routine
  7147. //
  7148. VOID
  7149. NtfsShrinkDelWindow (
  7150. IN PNTFS_CACHED_RUNS CachedRuns,
  7151. IN BOOLEAN ShrinkFromStart,
  7152. IN BOOLEAN LcnWindow,
  7153. IN USHORT WindowIndex
  7154. )
  7155. /*++
  7156. Routine Description:
  7157. This routine is called to remove one entry from the given window
  7158. of entries known to be deleted.
  7159. Arguments:
  7160. CachedRuns - Pointer to the cached runs structure.
  7161. ShrinkFromStart - If TRUE, remove the first entry in the window.
  7162. If FALSE, remove the last entry in the window.
  7163. LcnWindow - If TRUE, the window is of Lcn indices. If FALSE, the window is
  7164. of length indices.
  7165. WindowIndex - The index of the window.
  7166. Return Value:
  7167. None.
  7168. --*/
  7169. {
  7170. PUSHORT Count;
  7171. PNTFS_DELETED_RUNS DelWindow;
  7172. PAGED_CODE();
  7173. DebugTrace( +1, Dbg, ("NtfsShrinkDelWindow\n") );
  7174. DebugTrace( 0, Dbg, ("WindowIndex %04x\n", WindowIndex) );
  7175. if (LcnWindow) {
  7176. Count = &CachedRuns->DelLcnCount;
  7177. DelWindow = (CachedRuns->DeletedLcnWindows + WindowIndex);
  7178. } else {
  7179. Count = &CachedRuns->DelLengthCount;
  7180. DelWindow = (CachedRuns->DeletedLengthWindows + WindowIndex);
  7181. }
  7182. //
  7183. // Caller better give us something in the correct range.
  7184. //
  7185. ASSERT( WindowIndex < *Count );
  7186. //
  7187. // If the window has a single entry then remove it.
  7188. //
  7189. if (DelWindow->StartIndex == DelWindow->EndIndex) {
  7190. NtfsDeleteDelWindow( CachedRuns,
  7191. LcnWindow,
  7192. WindowIndex );
  7193. //
  7194. // Remove the first entry if desired.
  7195. //
  7196. } else if (ShrinkFromStart) {
  7197. DelWindow->StartIndex += 1;
  7198. //
  7199. // Otherwise the last entry.
  7200. //
  7201. } else {
  7202. DelWindow->EndIndex -= 1;
  7203. }
  7204. #ifdef NTFS_CHECK_CACHED_RUNS
  7205. if (NtfsDoVerifyCachedRuns) {
  7206. PNTFS_DELETED_RUNS FirstWindow;
  7207. PNTFS_DELETED_RUNS NextWindow;
  7208. USHORT Index;
  7209. //
  7210. // Make certain that the windows are in order and don't overlap.
  7211. //
  7212. if (LcnWindow) {
  7213. FirstWindow = CachedRuns->DeletedLcnWindows;
  7214. } else {
  7215. FirstWindow = CachedRuns->DeletedLengthWindows;
  7216. }
  7217. for (Index = 0, DelWindow = NextWindow = FirstWindow;
  7218. Index < *Count;
  7219. Index += 1, NextWindow += 1) {
  7220. ASSERT( NextWindow->StartIndex <= NextWindow->EndIndex );
  7221. if (NextWindow != DelWindow) {
  7222. ASSERT( NextWindow->StartIndex > (DelWindow->EndIndex + 1) );
  7223. DelWindow += 1;
  7224. }
  7225. }
  7226. }
  7227. #endif
  7228. DebugTrace( -1, Dbg, ("NtfsShrinkDelWindow -> VOID\n") );
  7229. return;
  7230. }
  7231. //
  7232. // Local support routine
  7233. //
  7234. VOID
  7235. NtfsDeleteDelWindow (
  7236. IN PNTFS_CACHED_RUNS CachedRuns,
  7237. IN BOOLEAN LcnWindow,
  7238. IN USHORT WindowIndex
  7239. )
  7240. /*++
  7241. Routine Description:
  7242. This routine is called to remove the given window of entries known to
  7243. be deleted.
  7244. Arguments:
  7245. CachedRuns - Pointer to the cached runs structure.
  7246. LcnWindow - If TRUE, the window is of Lcn indices. If FALSE, the window is of length indices.
  7247. WindowIndex - The index of the window.
  7248. Return Value:
  7249. None.
  7250. --*/
  7251. {
  7252. PUSHORT Count;
  7253. PNTFS_DELETED_RUNS FirstWindow;
  7254. PAGED_CODE();
  7255. DebugTrace( +1, Dbg, ("NtfsDeleteDelWindow\n") );
  7256. DebugTrace( 0, Dbg, ("WindowIndex %04x\n", WindowIndex) );
  7257. //
  7258. // Use the correct deleted window array.
  7259. //
  7260. if (LcnWindow) {
  7261. Count = &CachedRuns->DelLcnCount;
  7262. FirstWindow = CachedRuns->DeletedLcnWindows;
  7263. } else {
  7264. Count = &CachedRuns->DelLengthCount;
  7265. FirstWindow = CachedRuns->DeletedLengthWindows;
  7266. }
  7267. //
  7268. // Delete this window by shifting any existing windows from the right.
  7269. //
  7270. if (WindowIndex < (*Count - 1)) {
  7271. //
  7272. // Remove the deleted window.
  7273. //
  7274. DebugTrace( 0,
  7275. Dbg,
  7276. ("Move from WindowIndex %04x, %04x entries\n", WindowIndex + 1, *Count - 1 - WindowIndex) );
  7277. RtlMoveMemory( FirstWindow + WindowIndex,
  7278. FirstWindow + WindowIndex + 1,
  7279. sizeof( NTFS_DELETED_RUNS ) * (*Count - 1 - WindowIndex) );
  7280. }
  7281. //
  7282. // Decrement the windows count.
  7283. //
  7284. *Count -= 1;
  7285. #ifdef NTFS_CHECK_CACHED_RUNS
  7286. if (NtfsDoVerifyCachedRuns) {
  7287. PNTFS_DELETED_RUNS DelWindow;
  7288. PNTFS_DELETED_RUNS NextWindow;
  7289. //
  7290. // Make certain that the windows are in order and don't overlap.
  7291. //
  7292. for (WindowIndex = 0, DelWindow = NextWindow = FirstWindow;
  7293. WindowIndex < *Count;
  7294. WindowIndex += 1, NextWindow += 1) {
  7295. ASSERT( NextWindow->StartIndex <= NextWindow->EndIndex );
  7296. //
  7297. // Check against previous window if not at the first element. We don't allow
  7298. // adjacent windows to touch because they should have merged.
  7299. //
  7300. if (NextWindow != DelWindow) {
  7301. ASSERT( NextWindow->StartIndex > (DelWindow->EndIndex + 1) );
  7302. DelWindow += 1;
  7303. }
  7304. }
  7305. }
  7306. #endif
  7307. DebugTrace( -1, Dbg, ("NtfsDeleteDelWindow -> VOID\n") );
  7308. return;
  7309. }
  7310. //
  7311. // Local support routine
  7312. //
  7313. PNTFS_DELETED_RUNS
  7314. NtfsGetDelWindow (
  7315. IN PNTFS_CACHED_RUNS CachedRuns,
  7316. IN USHORT FirstIndex,
  7317. IN USHORT LastIndex,
  7318. IN BOOLEAN LcnList,
  7319. OUT PUSHORT WindowIndex OPTIONAL
  7320. )
  7321. /*++
  7322. Routine Description:
  7323. This routine is called to find the window of entries known to be deleted
  7324. that is closest to the given range of indices.
  7325. Arguments:
  7326. CachedRuns - Pointer to the cached runs structure.
  7327. FirstIndex - Index that marks the start of the range.
  7328. LastIndex - The index of the last entry in the range.
  7329. LcnList - If TRUE, the indices are from the Lcn-sorted list.
  7330. If FALSE, the indices are from the length-sorted list.
  7331. WindowIndex - If specified, the index of the returned window is put here.
  7332. Return Value:
  7333. PNTFS_DELETED_RUNS - Returns the closest window of deleted entries, or
  7334. NULL is there are no windows.
  7335. --*/
  7336. {
  7337. USHORT Count;
  7338. USHORT Distance;
  7339. USHORT Max, Min, Current;
  7340. BOOLEAN Overlap = FALSE;
  7341. PNTFS_DELETED_RUNS FirstWindow, NextWindow;
  7342. PNTFS_DELETED_RUNS DelWindow = NULL;
  7343. PAGED_CODE();
  7344. DebugTrace( +1, Dbg, ("NtfsGetDelWindow\n") );
  7345. //
  7346. // Get pointers to the windows we will be searching.
  7347. //
  7348. if (LcnList) {
  7349. Count = CachedRuns->DelLcnCount;
  7350. FirstWindow = CachedRuns->DeletedLcnWindows;
  7351. } else {
  7352. Count = CachedRuns->DelLengthCount;
  7353. FirstWindow = CachedRuns->DeletedLengthWindows;
  7354. }
  7355. if (Count != 0) {
  7356. //
  7357. // Perform a binary search to find the next element to the right.
  7358. // We always do at least one comparison to determine if a single element
  7359. // is to the left or right.
  7360. //
  7361. Min = 0;
  7362. Max = Count - 1;
  7363. while (TRUE) {
  7364. Current = (USHORT) (((ULONG) Max + Min) / 2);
  7365. NextWindow = FirstWindow + Current;
  7366. if (LastIndex < NextWindow->StartIndex) {
  7367. //
  7368. // We are done if Max and Min match. We test before changing Max
  7369. // because if Min is still 0 then we've never looked at it.
  7370. //
  7371. if (Min == Max) {
  7372. break;
  7373. }
  7374. Max = Current;
  7375. } else if (LastIndex > NextWindow->EndIndex) {
  7376. //
  7377. // Advance Min past this point.
  7378. //
  7379. Min = Current + 1;
  7380. //
  7381. // Break if past Max. This should only occur if our range is
  7382. // past the last window.
  7383. //
  7384. if (Min > Max) {
  7385. ASSERT( Min == Count );
  7386. break;
  7387. }
  7388. } else {
  7389. //
  7390. // Simple case. This is an overlap.
  7391. //
  7392. Overlap = TRUE;
  7393. Min = Current;
  7394. break;
  7395. }
  7396. }
  7397. //
  7398. // Now find nearest. First check if we are beyond the end of the array.
  7399. //
  7400. if (Min == Count) {
  7401. Min = Count - 1;
  7402. //
  7403. // If we aren't at the first entry and didn't already detect an overlap then
  7404. // compare adjacent entries.
  7405. //
  7406. } else if ((Min != 0) && !Overlap) {
  7407. DelWindow = FirstWindow + Min - 1;
  7408. NextWindow = DelWindow + 1;
  7409. //
  7410. // Test that there is no overlap with the previous
  7411. // window. If no overlap then check for the distance to
  7412. // the adjacent ranges.
  7413. //
  7414. if (FirstIndex > DelWindow->EndIndex) {
  7415. ASSERT( NextWindow->StartIndex > LastIndex );
  7416. Distance = NextWindow->StartIndex - LastIndex;
  7417. if (Distance > FirstIndex - DelWindow->EndIndex) {
  7418. //
  7419. // Move to the previous window.
  7420. //
  7421. Min -= 1;
  7422. }
  7423. //
  7424. // The previous window has an overlap.
  7425. //
  7426. } else {
  7427. Min -= 1;
  7428. }
  7429. }
  7430. if (ARGUMENT_PRESENT( WindowIndex )) {
  7431. *WindowIndex = Min;
  7432. }
  7433. DelWindow = FirstWindow + Min;
  7434. DebugTrace( 0, Dbg, ("Index -> %04x\n", Min) );
  7435. }
  7436. DebugTrace( -1, Dbg, ("NtfsGetDelWindow -> 0x%x\n", DelWindow) );
  7437. return DelWindow;
  7438. }
  7439. //
  7440. // Local support routine
  7441. //
  7442. USHORT
  7443. NtfsGetCachedLengthInsertionPoint (
  7444. IN PNTFS_CACHED_RUNS CachedRuns,
  7445. IN LCN Lcn,
  7446. IN LONGLONG Length
  7447. )
  7448. /*++
  7449. Routine Description:
  7450. This routine is called to add a new entry in the Lcn-sorted and
  7451. length-sorted lists. It is assumed that the caller has made certain
  7452. that this new entry will not overlap any existing entries.
  7453. This routine may chose not to add the new entry to the lists. If adding
  7454. this entry would force an equally or more desirable run out of the cache,
  7455. we don't make the change.
  7456. This routine can compact the lists. Therefore, the caller should not
  7457. assume that entries will not move.
  7458. If this routine finds an insertion point and there is already an undeleted
  7459. at that position, the new run sorts before it. If the new run sorts
  7460. higher than the entry at index CachedRuns->Avail - 1, we will return an
  7461. index of CachedRuns->Avail. The caller must check for this case and
  7462. not access an entry beyond the list size.
  7463. Arguments:
  7464. CachedRuns - Pointer to the cached runs structure.
  7465. Lcn - Lcn to insert.
  7466. Length - Length of the run to insert.
  7467. Return Value:
  7468. USHORT - The index into the length-sorted table at which the given Length
  7469. should be inserted. If the entry should not be inserted,
  7470. NTFS_CACHED_RUNS_DEL_INDEX is returned.
  7471. --*/
  7472. {
  7473. BOOLEAN FoundRun;
  7474. USHORT Index;
  7475. LONGLONG RunLength;
  7476. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  7477. PAGED_CODE();
  7478. DebugTrace( +1, Dbg, ("NtfsGetCachedLengthInsertionPoint\n") );
  7479. if ((CachedRuns->Used == CachedRuns->Avail) &&
  7480. (CachedRuns->DelLengthCount == 0) ) {
  7481. //
  7482. // Grow the lists.
  7483. //
  7484. if (!NtfsGrowCachedRuns( CachedRuns )) {
  7485. //
  7486. // We couldn't grow the lists.
  7487. //
  7488. if (CachedRuns->Used == 0) {
  7489. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  7490. }
  7491. //
  7492. // Adding this entry will force another one to be deleted.
  7493. // Make sure the new entry is more desirable to add than
  7494. // all existing entries.
  7495. //
  7496. // The check is to make certain that we have more than enough
  7497. // entries of a size smaller than Length such that we would
  7498. // be willing to delete one.
  7499. //
  7500. RunLength = 0;
  7501. for (Index = 0;
  7502. (Index < CachedRuns->Bins) && (Index < (Length - 1) );
  7503. Index += 1) {
  7504. if (CachedRuns->BinArray[ Index ] > CachedRuns->MinCount) {
  7505. //
  7506. // We should delete an entry with RunLength = Index + 1
  7507. //
  7508. RunLength = Index + 1;
  7509. break;
  7510. }
  7511. }
  7512. if (RunLength != 0) {
  7513. //
  7514. // Find an entry of this length.
  7515. //
  7516. FoundRun = NtfsPositionCachedLcnByLength( CachedRuns,
  7517. RunLength,
  7518. NULL,
  7519. NULL,
  7520. TRUE,
  7521. &Index );
  7522. ASSERT( FoundRun );
  7523. ASSERT( (CachedRuns->LengthArray[Index] != NTFS_CACHED_RUNS_DEL_INDEX) &&
  7524. (CachedRuns->LcnArray[CachedRuns->LengthArray[Index]].RunLength != 0) );
  7525. //
  7526. // Delete the entry.
  7527. //
  7528. ThisEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ Index ];
  7529. NtfsDeleteCachedRun( CachedRuns,
  7530. CachedRuns->LengthArray[ Index ],
  7531. Index );
  7532. } else {
  7533. //
  7534. // Do not add the new entry.
  7535. //
  7536. DebugTrace( -1,
  7537. Dbg,
  7538. ("NtfsGetCachedLengthInsertionPoint -> 0x%x\n", NTFS_CACHED_RUNS_DEL_INDEX) );
  7539. return NTFS_CACHED_RUNS_DEL_INDEX;
  7540. }
  7541. }
  7542. }
  7543. //
  7544. // Get the insertion point for the new entry.
  7545. // If FoundRun is FALSE, the entry pointed to by Index is either deleted
  7546. // or sorts higher than the new one.
  7547. //
  7548. FoundRun = NtfsPositionCachedLcnByLength( CachedRuns,
  7549. Length,
  7550. &Lcn,
  7551. NULL,
  7552. TRUE,
  7553. &Index );
  7554. //
  7555. // Index points to the closest run by Lcn that has a RunLength equal
  7556. // to Length. We need to check to see if the new entry should be
  7557. // inserted before or after it.
  7558. //
  7559. if (FoundRun) {
  7560. ThisEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ Index ];
  7561. if (ThisEntry->Lcn < Lcn) {
  7562. //
  7563. // The new run should come after this one.
  7564. //
  7565. Index += 1;
  7566. }
  7567. }
  7568. DebugTrace( -1, Dbg, ("NtfsGetCachedLengthInsertionPoint -> 0x%x\n", Index) );
  7569. return Index;
  7570. }
  7571. //
  7572. // Local support routine
  7573. //
  7574. VOID
  7575. NtfsInsertCachedRun (
  7576. IN PNTFS_CACHED_RUNS CachedRuns,
  7577. IN LCN Lcn,
  7578. IN LONGLONG Length,
  7579. IN USHORT LcnIndex
  7580. )
  7581. /*++
  7582. Routine Description:
  7583. This routine is called to add a new entry in the Lcn-sorted and
  7584. length-sorted lists. It is assumed that the caller has made certain
  7585. that this new entry will not overlap any existing entries.
  7586. This routine may chose not to add the new entry to the lists. If adding
  7587. this entry would force an equally or more desirable run out of the cache,
  7588. we don't make the change.
  7589. This routine can compact the lists. Therefore, the caller should not
  7590. assume that entries will not move.
  7591. Arguments:
  7592. CachedRuns - Pointer to the cached runs structure.
  7593. Lcn - Lcn to insert.
  7594. Length - Length of the run to insert.
  7595. LcnIndex - Index into the Lcn-sorted list where this new entry should
  7596. be added. Any non-deleted entry already at this position sorts
  7597. after the new entry.
  7598. Return Value:
  7599. None.
  7600. --*/
  7601. {
  7602. USHORT Count;
  7603. USHORT RunIndex;
  7604. USHORT LenIndex;
  7605. USHORT WindowIndex;
  7606. PNTFS_DELETED_RUNS DelWindow;
  7607. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  7608. PAGED_CODE();
  7609. DebugTrace( +1, Dbg, ("NtfsInsertCachedRun\n") );
  7610. //
  7611. // Find the position in the length-sorted list to insert this new
  7612. // entry. This routine will grow the lists if necessary.
  7613. //
  7614. LenIndex = NtfsGetCachedLengthInsertionPoint( CachedRuns,
  7615. Lcn,
  7616. Length );
  7617. //
  7618. // This entry will not be added to the lists because it would degrade the
  7619. // distribution of the length entries.
  7620. //
  7621. if (LenIndex == NTFS_CACHED_RUNS_DEL_INDEX) {
  7622. return;
  7623. }
  7624. //
  7625. // Find the closest window of deleted entries to LcnIndex.
  7626. //
  7627. DelWindow = NtfsGetDelWindow( CachedRuns,
  7628. LcnIndex,
  7629. LcnIndex,
  7630. TRUE,
  7631. &WindowIndex );
  7632. ASSERT( DelWindow != NULL );
  7633. ASSERT( (DelWindow->EndIndex + 1 < LcnIndex) ||
  7634. (LcnIndex < CachedRuns->Avail) );
  7635. //
  7636. // Move the entries between LcnIndex and the start of the
  7637. // window up to make room for this new entry.
  7638. //
  7639. if (DelWindow->StartIndex > LcnIndex) {
  7640. RtlMoveMemory( CachedRuns->LcnArray + LcnIndex + 1,
  7641. CachedRuns->LcnArray + LcnIndex,
  7642. sizeof( NTFS_LCN_CLUSTER_RUN ) * (DelWindow->StartIndex - LcnIndex) );
  7643. //
  7644. // Update the indices in the Length-sorted list to reflect the
  7645. // move of the lcn-sorted entries.
  7646. //
  7647. for (Count = LcnIndex + 1;
  7648. Count < DelWindow->StartIndex + 1;
  7649. Count += 1) {
  7650. RunIndex = CachedRuns->LcnArray[ Count ].LengthIndex;
  7651. ASSERT( RunIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  7652. CachedRuns->LengthArray[ RunIndex ] += 1;
  7653. }
  7654. //
  7655. // Check if we are using the deleted window at the tail of the array. If
  7656. // so then we just increased the number of entries in use with this
  7657. // right shift.
  7658. //
  7659. if (DelWindow->StartIndex == CachedRuns->Used) {
  7660. CachedRuns->LengthArray[ CachedRuns->Used ] = NTFS_CACHED_RUNS_DEL_INDEX;
  7661. CachedRuns->Used += 1;
  7662. }
  7663. //
  7664. // Update the window.
  7665. //
  7666. NtfsShrinkDelWindow( CachedRuns,
  7667. TRUE,
  7668. TRUE,
  7669. WindowIndex);
  7670. //
  7671. // Check if we need to move entries down to the nearest deleted window.
  7672. //
  7673. } else if ((DelWindow->EndIndex + 1) < LcnIndex) {
  7674. //
  7675. // Update the insertion point to be LcnIndex - 1 and make a gap there
  7676. //
  7677. LcnIndex -= 1;
  7678. //
  7679. // Move the entries between the end of the window and
  7680. // LcnIndex down to make room for this new entry.
  7681. //
  7682. RtlMoveMemory( CachedRuns->LcnArray + DelWindow->EndIndex,
  7683. CachedRuns->LcnArray + DelWindow->EndIndex + 1,
  7684. sizeof( NTFS_LCN_CLUSTER_RUN ) * (LcnIndex - DelWindow->EndIndex) );
  7685. //
  7686. // Update the indices in the Length-sorted list to reflect the
  7687. // move of the lcn-sorted entries.
  7688. //
  7689. for (Count = DelWindow->EndIndex;
  7690. Count < LcnIndex;
  7691. Count += 1) {
  7692. RunIndex = CachedRuns->LcnArray[ Count ].LengthIndex;
  7693. ASSERT( RunIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  7694. CachedRuns->LengthArray[ RunIndex ] -= 1;
  7695. }
  7696. //
  7697. // Update the window.
  7698. //
  7699. NtfsShrinkDelWindow( CachedRuns,
  7700. FALSE,
  7701. TRUE,
  7702. WindowIndex);
  7703. //
  7704. // The window is adjacent to LcnIndex and the entry at LcnIndex
  7705. // sorts higher than the new run. No moves are necessary.
  7706. // Decrement LcnIndex.
  7707. //
  7708. } else if ((DelWindow->EndIndex + 1) == LcnIndex) {
  7709. LcnIndex -= 1;
  7710. //
  7711. // Update the window.
  7712. //
  7713. NtfsShrinkDelWindow( CachedRuns,
  7714. FALSE,
  7715. TRUE,
  7716. WindowIndex);
  7717. } else {
  7718. //
  7719. // The window covers LcnIndex. No moves are necessary.
  7720. //
  7721. if (DelWindow->StartIndex == LcnIndex) {
  7722. //
  7723. // Update the window.
  7724. //
  7725. NtfsShrinkDelWindow( CachedRuns,
  7726. TRUE,
  7727. TRUE,
  7728. WindowIndex);
  7729. } else if (DelWindow->EndIndex == LcnIndex) {
  7730. //
  7731. // Update the window.
  7732. //
  7733. NtfsShrinkDelWindow( CachedRuns,
  7734. FALSE,
  7735. TRUE,
  7736. WindowIndex);
  7737. } else {
  7738. //
  7739. // LcnIndex does not fall on the first or last entry in
  7740. // the window, we will update it to do so. Otherwise we
  7741. // would have to split the window, with no real gain.
  7742. //
  7743. LcnIndex = DelWindow->EndIndex;
  7744. //
  7745. // Update the window.
  7746. //
  7747. NtfsShrinkDelWindow( CachedRuns,
  7748. FALSE,
  7749. TRUE,
  7750. WindowIndex);
  7751. }
  7752. }
  7753. ASSERT( LcnIndex < CachedRuns->Avail );
  7754. ASSERT( LcnIndex <= CachedRuns->Used );
  7755. //
  7756. // Find the closest window of deleted entries to LenIndex.
  7757. //
  7758. DelWindow = NtfsGetDelWindow( CachedRuns,
  7759. LenIndex,
  7760. LenIndex,
  7761. FALSE,
  7762. &WindowIndex);
  7763. ASSERT( DelWindow != NULL );
  7764. ASSERT( (DelWindow->EndIndex < (LenIndex - 1)) ||
  7765. (LenIndex < CachedRuns->Avail) );
  7766. //
  7767. // The window is to the right. Go ahead and
  7768. // move the entries between LenIndex and the start of the
  7769. // window up to make room for this new entry.
  7770. //
  7771. if (DelWindow->StartIndex > LenIndex) {
  7772. RtlMoveMemory( CachedRuns->LengthArray + LenIndex + 1,
  7773. CachedRuns->LengthArray + LenIndex,
  7774. sizeof( USHORT ) * (DelWindow->StartIndex - LenIndex) );
  7775. //
  7776. // Update the indices in the Lcn-sorted list to reflect the
  7777. // move of the length-sorted entries.
  7778. //
  7779. for (Count = LenIndex + 1;
  7780. Count < DelWindow->StartIndex + 1;
  7781. Count += 1) {
  7782. RunIndex = CachedRuns->LengthArray[ Count ];
  7783. ASSERT( RunIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  7784. CachedRuns->LcnArray[ RunIndex ].LengthIndex += 1;
  7785. }
  7786. //
  7787. // We have just increased the number of entries in use with this
  7788. // right shift.
  7789. //
  7790. if (DelWindow->StartIndex == CachedRuns->Used) {
  7791. CachedRuns->LcnArray[ CachedRuns->Used ].RunLength = 0;
  7792. CachedRuns->LcnArray[ CachedRuns->Used ].LengthIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  7793. CachedRuns->Used += 1;
  7794. }
  7795. //
  7796. // Update the window.
  7797. //
  7798. NtfsShrinkDelWindow( CachedRuns,
  7799. TRUE,
  7800. FALSE,
  7801. WindowIndex);
  7802. //
  7803. // The deleted window is to the left. Slide everything to the left and
  7804. // Update the insertion point to be LenIndex - 1 and make a gap there.
  7805. //
  7806. } else if ((DelWindow->EndIndex + 1) < LenIndex) {
  7807. LenIndex -= 1;
  7808. //
  7809. // Move the entries between the end of the window and
  7810. // LenIndex down to make room for this new entry.
  7811. //
  7812. RtlMoveMemory( CachedRuns->LengthArray + DelWindow->EndIndex,
  7813. CachedRuns->LengthArray + DelWindow->EndIndex + 1,
  7814. sizeof( USHORT ) * (LenIndex - DelWindow->EndIndex) );
  7815. //
  7816. // Update the indices in the Lcn-sorted list to reflect the
  7817. // move of the length-sorted entries.
  7818. //
  7819. for (Count = DelWindow->EndIndex;
  7820. Count < LenIndex;
  7821. Count += 1) {
  7822. RunIndex = CachedRuns->LengthArray[ Count ];
  7823. ASSERT( RunIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  7824. CachedRuns->LcnArray[ RunIndex ].LengthIndex -= 1;
  7825. }
  7826. //
  7827. // Update the window.
  7828. //
  7829. NtfsShrinkDelWindow( CachedRuns,
  7830. FALSE,
  7831. FALSE,
  7832. WindowIndex);
  7833. //
  7834. // The window is adjacent to LenIndex and the entry at LenIndex
  7835. // sorts higher than the new run. No moves are necessary.
  7836. // Decrement LenIndex.
  7837. //
  7838. } else if ((DelWindow->EndIndex + 1) == LenIndex) {
  7839. LenIndex -= 1;
  7840. //
  7841. // Update the window.
  7842. //
  7843. NtfsShrinkDelWindow( CachedRuns,
  7844. FALSE,
  7845. FALSE,
  7846. WindowIndex);
  7847. //
  7848. // The window covers LenIndex. No moves are necessary.
  7849. //
  7850. } else {
  7851. if (DelWindow->StartIndex == LenIndex) {
  7852. //
  7853. // Update the window.
  7854. //
  7855. NtfsShrinkDelWindow( CachedRuns,
  7856. TRUE,
  7857. FALSE,
  7858. WindowIndex);
  7859. } else if (DelWindow->EndIndex == LenIndex) {
  7860. //
  7861. // Update the window.
  7862. //
  7863. NtfsShrinkDelWindow( CachedRuns,
  7864. FALSE,
  7865. FALSE,
  7866. WindowIndex);
  7867. } else {
  7868. //
  7869. // LenIndex does not fall on the first or last entry in
  7870. // the window, we will update it to do so. Otherwise we
  7871. // would have to split the window, with no real gain.
  7872. //
  7873. LenIndex = DelWindow->EndIndex;
  7874. //
  7875. // Update the window.
  7876. //
  7877. NtfsShrinkDelWindow( CachedRuns,
  7878. FALSE,
  7879. FALSE,
  7880. WindowIndex);
  7881. }
  7882. }
  7883. ASSERT( LenIndex < CachedRuns->Avail );
  7884. ASSERT( LenIndex <= CachedRuns->Used );
  7885. //
  7886. // Insert the new entry at LcnIndex, LenIndex
  7887. //
  7888. ThisEntry = CachedRuns->LcnArray + LcnIndex;
  7889. ThisEntry->Lcn = Lcn;
  7890. ThisEntry->RunLength = Length;
  7891. ThisEntry->LengthIndex = LenIndex;
  7892. CachedRuns->LengthArray[ LenIndex ] = LcnIndex;
  7893. //
  7894. // Update the count of entries of this size.
  7895. //
  7896. if (Length <= CachedRuns->Bins) {
  7897. CachedRuns->BinArray[ Length - 1 ] += 1;
  7898. }
  7899. //
  7900. // Check if we've grown the number of entries used.
  7901. //
  7902. if (LcnIndex == CachedRuns->Used) {
  7903. //
  7904. // Increase the count of the number of entries in use.
  7905. //
  7906. ASSERT( (CachedRuns->LengthArray[ CachedRuns->Used ] == NTFS_CACHED_RUNS_DEL_INDEX) ||
  7907. (LenIndex == CachedRuns->Used) );
  7908. CachedRuns->Used += 1;
  7909. }
  7910. if (LenIndex == CachedRuns->Used) {
  7911. //
  7912. // Increase the count of the number of entries in use.
  7913. //
  7914. ASSERT( (CachedRuns->LcnArray[ CachedRuns->Used ].RunLength == 0) &&
  7915. (CachedRuns->LcnArray[ CachedRuns->Used ].LengthIndex == NTFS_CACHED_RUNS_DEL_INDEX) );
  7916. CachedRuns->Used += 1;
  7917. }
  7918. #ifdef NTFS_CHECK_CACHED_RUNS
  7919. if (NtfsDoVerifyCachedRuns) {
  7920. NtfsVerifyCachedRuns( CachedRuns, FALSE, FALSE );
  7921. }
  7922. #endif
  7923. DebugTrace( -1, Dbg, ("NtfsInsertCachedRun -> VOID\n") );
  7924. return;
  7925. }
  7926. //
  7927. // Local support routine
  7928. //
  7929. VOID
  7930. NtfsDeleteCachedRun (
  7931. IN PNTFS_CACHED_RUNS CachedRuns,
  7932. IN USHORT LcnIndex,
  7933. IN USHORT LenIndex
  7934. )
  7935. /*++
  7936. Routine Description:
  7937. This routine is called to delete an Lcn run from the cached run arrays.
  7938. It is possible that the lists will be compacted. This will happen if
  7939. we use the last window of deleted entries that we are allowed to cache
  7940. for either the Lcn-sorted or Length-sorted lists. Therefore, callers
  7941. should be aware that indices may change across this call. However we do
  7942. guarantee that the indices LcnIndex and LenIndex will not move.
  7943. Arguments:
  7944. CachedRuns - Pointer to the cached runs structure.
  7945. LcnIndex - The index in the Lcn-sorted list of the entry to be deleted.
  7946. LenIndex - The index in the Length-sorted list of the entry to be deleted.
  7947. Return Value:
  7948. None.
  7949. --*/
  7950. {
  7951. PAGED_CODE();
  7952. DebugTrace( +1, Dbg, ("NtfsDeleteCachedRun\n") );
  7953. ASSERT( LcnIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  7954. ASSERT( LenIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  7955. //
  7956. // Update count of how many entries have this length.
  7957. //
  7958. if (CachedRuns->LcnArray[ LcnIndex ].RunLength <= CachedRuns->Bins) {
  7959. CachedRuns->BinArray[CachedRuns->LcnArray[LcnIndex].RunLength - 1] -= 1;
  7960. }
  7961. //
  7962. // Update the entries so they appear to be deleted.
  7963. //
  7964. CachedRuns->LcnArray[ LcnIndex ].RunLength = 0;
  7965. CachedRuns->LcnArray[ LcnIndex ].LengthIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  7966. CachedRuns->LengthArray[ LenIndex ] = NTFS_CACHED_RUNS_DEL_INDEX;
  7967. //
  7968. // Create windows of deleted entries to cover this newly deleted
  7969. // entry.
  7970. //
  7971. NtfsAddDelWindow( CachedRuns, LcnIndex, LcnIndex, TRUE );
  7972. NtfsAddDelWindow( CachedRuns, LenIndex, LenIndex, FALSE );
  7973. #ifdef NTFS_CHECK_CACHED_RUNS
  7974. //
  7975. // We will not check sort orders in NtfsVerifyCachedRuns because we
  7976. // could be making this call as part of deleting runs that have an
  7977. // overlap with a newly inserted run. This could give false corruption
  7978. // warnings.
  7979. //
  7980. if (NtfsDoVerifyCachedRuns) {
  7981. NtfsVerifyCachedRuns( CachedRuns, TRUE, TRUE );
  7982. }
  7983. #endif
  7984. DebugTrace( -1, Dbg, ("NtfsDeleteCachedRun -> VOID\n") );
  7985. return;
  7986. }
  7987. //
  7988. // Local support routine
  7989. //
  7990. VOID
  7991. NtfsInsertCachedLcn (
  7992. IN PNTFS_CACHED_RUNS CachedRuns,
  7993. IN LCN Lcn,
  7994. IN LONGLONG Length
  7995. )
  7996. /*++
  7997. Routine Description:
  7998. This routine is called to insert an Lcn run into the cached run arrays.
  7999. Arguments:
  8000. CachedRuns - Pointer to the cached runs structure.
  8001. Lcn - Lcn to insert.
  8002. Length - Length of the run to insert.
  8003. Return Value:
  8004. None
  8005. --*/
  8006. {
  8007. USHORT NextIndex;
  8008. USHORT ThisIndex;
  8009. LCN StartingLcn;
  8010. LCN SaveLcn;
  8011. LONGLONG RunLength;
  8012. LONGLONG OldLength = 0;
  8013. LCN EndOfNewRun;
  8014. LCN EndOfThisRun;
  8015. LCN EndOfNextRun;
  8016. BOOLEAN ExtendedEntry = FALSE;
  8017. BOOLEAN ScanForOverlap = FALSE;
  8018. PNTFS_DELETED_RUNS DelWindow;
  8019. PNTFS_LCN_CLUSTER_RUN ThisEntry, NextEntry;
  8020. PAGED_CODE();
  8021. DebugTrace( +1, Dbg, ("NtfsInsertCachedLcn\n") );
  8022. //
  8023. // Return immediately if length is zero.
  8024. //
  8025. if (Length == 0) {
  8026. DebugTrace( -1, Dbg, ("NtfsInsertCachedLcn -> VOID\n") );
  8027. return;
  8028. }
  8029. //
  8030. // Lookup the Lcn at the start of our run.
  8031. //
  8032. NtfsLookupCachedLcn( CachedRuns,
  8033. Lcn,
  8034. &StartingLcn,
  8035. &RunLength,
  8036. &NextIndex );
  8037. //
  8038. // We have a run to insert. We need to deal with the following cases.
  8039. // Our strategy is to position ThisEntry at the position we want to store
  8040. // the resulting run. Then remove any subsequent runs we overlap with, possibly
  8041. // extending the run we are working with.
  8042. //
  8043. //
  8044. // 1 - We can merge with the prior run. Save that position
  8045. // and remove any following slots we overlap with.
  8046. //
  8047. // 2 - We are beyond the array. Simply store our run in
  8048. // this slot.
  8049. //
  8050. // 3 - We don't overlap with the current run. Simply slide
  8051. // the runs up and insert a new entry.
  8052. //
  8053. // 4 - We are contained within the current run. There is nothing
  8054. // we need to do.
  8055. //
  8056. // 5 - We overlap with the current run. Use that slot
  8057. // and remove any following slots we overlap with.
  8058. //
  8059. NextEntry = CachedRuns->LcnArray + NextIndex;
  8060. //
  8061. // Find a previous entry if present.
  8062. //
  8063. ThisIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  8064. if (NextIndex > 0) {
  8065. ThisIndex = NextIndex - 1;
  8066. ThisEntry = CachedRuns->LcnArray + ThisIndex;
  8067. //
  8068. // If the entry has been deleted it must be ignored. Get the
  8069. // window of deleted entries that covers it.
  8070. //
  8071. if (ThisEntry->RunLength == 0) {
  8072. DelWindow = NtfsGetDelWindow( CachedRuns,
  8073. ThisIndex,
  8074. ThisIndex,
  8075. TRUE,
  8076. NULL);
  8077. ASSERT( DelWindow != NULL );
  8078. ASSERT( DelWindow->EndIndex >= ThisIndex );
  8079. ASSERT( DelWindow->StartIndex <= ThisIndex );
  8080. //
  8081. // Move to the entry just before the deleted window.
  8082. //
  8083. if (DelWindow->StartIndex > 0) {
  8084. ThisIndex = DelWindow->StartIndex - 1;
  8085. ThisEntry = CachedRuns->LcnArray + ThisIndex;
  8086. } else {
  8087. //
  8088. // All entries preceding NextEntry are deleted.
  8089. //
  8090. ThisIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  8091. }
  8092. }
  8093. //
  8094. // Capture the end of the run. It's invalid if we don't have
  8095. // a real index but all of the users of this will know that.
  8096. //
  8097. EndOfThisRun = ThisEntry->Lcn + ThisEntry->RunLength;
  8098. }
  8099. //
  8100. // Let's remember the end of the next run if present.
  8101. //
  8102. EndOfNewRun = Lcn + Length;
  8103. if ((NextIndex < CachedRuns->Used) &&
  8104. (NextEntry->RunLength != 0)) {
  8105. EndOfNextRun = NextEntry->Lcn + NextEntry->RunLength;
  8106. }
  8107. //
  8108. // Case 1 - Merge with previous run.
  8109. //
  8110. if ((ThisIndex != NTFS_CACHED_RUNS_DEL_INDEX) &&
  8111. (Lcn == EndOfThisRun)) {
  8112. //
  8113. // Extend the entry in the runs array and remember the
  8114. // new length. We will defer moving the run within the
  8115. // length-sorted array until we know the final length.
  8116. // It is possible that the combined entry overlaps with
  8117. // subsequent entries. If the overlap lands in the middle
  8118. // of the final entry, the length may need to be extended
  8119. // even more.
  8120. //
  8121. Lcn = ThisEntry->Lcn;
  8122. //
  8123. // Remember the original length of the entry.
  8124. //
  8125. OldLength = ThisEntry->RunLength;
  8126. Length += ThisEntry->RunLength;
  8127. ThisEntry->RunLength = Length;
  8128. ExtendedEntry = TRUE;
  8129. ScanForOverlap = TRUE;
  8130. //
  8131. // Case 2 - We are at the end of the array
  8132. // Case 3 - We have a non-overlapping interior entry
  8133. //
  8134. } else if ((NextIndex >= CachedRuns->Used) ||
  8135. (NextEntry->RunLength == 0) ||
  8136. (EndOfNewRun < NextEntry->Lcn)) {
  8137. //
  8138. // Insert the new run in both lists.
  8139. //
  8140. NtfsInsertCachedRun( CachedRuns,
  8141. Lcn,
  8142. Length,
  8143. NextIndex );
  8144. //
  8145. // Case 4 - We are contained within the current entry.
  8146. //
  8147. } else if ((Lcn >= NextEntry->Lcn) &&
  8148. (EndOfNewRun <= EndOfNextRun)) {
  8149. NOTHING;
  8150. //
  8151. // Case 5 - We overlap the next entry. Extend the run to the end of
  8152. // current run if we end early. Extend to the beginning of the
  8153. // run if we need to.
  8154. //
  8155. } else {
  8156. //
  8157. // Remember if we are extending the run backwards.
  8158. //
  8159. if (Lcn < NextEntry->Lcn) {
  8160. //
  8161. // Move the starting point back.
  8162. //
  8163. NextEntry->Lcn = Lcn;
  8164. ExtendedEntry = TRUE;
  8165. OldLength = NextEntry->RunLength;
  8166. }
  8167. //
  8168. // Remember if we go past the end of this run.
  8169. //
  8170. if (EndOfNewRun > EndOfNextRun) {
  8171. ExtendedEntry = TRUE;
  8172. ScanForOverlap = TRUE;
  8173. OldLength = NextEntry->RunLength;
  8174. Length = EndOfNewRun - NextEntry->Lcn;
  8175. //
  8176. // Remember the full new length of this run.
  8177. //
  8178. } else {
  8179. Length = EndOfNextRun - NextEntry->Lcn;
  8180. }
  8181. //
  8182. // Update the entry and position ThisEntry to point to it.
  8183. //
  8184. NextEntry->RunLength = Length;
  8185. ThisEntry = NextEntry;
  8186. ThisIndex = NextIndex;
  8187. }
  8188. //
  8189. // Walk forward to see if we need to join with other entires.
  8190. //
  8191. if (ScanForOverlap) {
  8192. NextIndex = ThisIndex + 1;
  8193. EndOfNewRun = ThisEntry->Lcn + ThisEntry->RunLength;
  8194. while (NextIndex < CachedRuns->Used) {
  8195. NextEntry = CachedRuns->LcnArray + NextIndex;
  8196. //
  8197. // The entry has been deleted and must be ignored. Get the
  8198. // window of deleted entries that covers it.
  8199. //
  8200. if (NextEntry->RunLength == 0) {
  8201. DelWindow = NtfsGetDelWindow( CachedRuns,
  8202. NextIndex,
  8203. NextIndex,
  8204. TRUE,
  8205. NULL );
  8206. ASSERT( DelWindow );
  8207. ASSERT( DelWindow->EndIndex >= NextIndex );
  8208. ASSERT( DelWindow->StartIndex <= NextIndex );
  8209. NextIndex = DelWindow->EndIndex + 1;
  8210. continue;
  8211. }
  8212. //
  8213. // Exit if there is no overlap.
  8214. //
  8215. if (EndOfNewRun < NextEntry->Lcn) {
  8216. break;
  8217. }
  8218. //
  8219. // The runs overlap.
  8220. //
  8221. EndOfNextRun = NextEntry->Lcn + NextEntry->RunLength;
  8222. if (EndOfNewRun < EndOfNextRun) {
  8223. //
  8224. // Extend the new run.
  8225. //
  8226. ThisEntry->RunLength = EndOfNextRun - ThisEntry->Lcn;
  8227. ExtendedEntry = TRUE;
  8228. EndOfNewRun = EndOfNextRun;
  8229. }
  8230. //
  8231. // Delete the run. This can cause compaction to be run and
  8232. // that will require us to have to recalculate ThisIndex.
  8233. //
  8234. SaveLcn = ThisEntry->Lcn;
  8235. NtfsDeleteCachedRun( CachedRuns,
  8236. NextIndex,
  8237. NextEntry->LengthIndex );
  8238. //
  8239. // Check if we should recompute ThisIndex because ThisEntry must have moved
  8240. // during compaction.
  8241. //
  8242. if ((ThisEntry->Lcn != SaveLcn) ||
  8243. (ThisEntry->RunLength == 0) ) {
  8244. NtfsLookupCachedLcn( CachedRuns,
  8245. Lcn,
  8246. &StartingLcn,
  8247. &RunLength,
  8248. &ThisIndex );
  8249. ThisEntry = CachedRuns->LcnArray + ThisIndex;
  8250. //
  8251. // Reset NextIndex to point to the end after ThisIndex. That
  8252. // value may have moved due to compaction.
  8253. //
  8254. NextIndex = ThisIndex + 1;
  8255. }
  8256. if (EndOfNewRun == EndOfNextRun) {
  8257. break;
  8258. }
  8259. }
  8260. }
  8261. //
  8262. // If we changed the existing entry then update the length bins.
  8263. //
  8264. if (ExtendedEntry) {
  8265. NtfsModifyCachedBinArray( CachedRuns,
  8266. OldLength,
  8267. ThisEntry->RunLength );
  8268. //
  8269. // Move the entry to the correct position in the length-sorted array
  8270. //
  8271. NtfsGrowLengthInCachedLcn( CachedRuns,
  8272. ThisEntry,
  8273. ThisIndex );
  8274. }
  8275. #ifdef NTFS_CHECK_CACHED_RUNS
  8276. if (NtfsDoVerifyCachedRuns) {
  8277. NtfsVerifyCachedRuns( CachedRuns, FALSE, FALSE );
  8278. }
  8279. #endif
  8280. DebugTrace( -1, Dbg, ("NtfsInsertCachedLcn -> VOID\n") );
  8281. return;
  8282. }
  8283. //
  8284. // Local support routine
  8285. //
  8286. VOID
  8287. NtfsGrowLengthInCachedLcn (
  8288. IN PNTFS_CACHED_RUNS CachedRuns,
  8289. IN PNTFS_LCN_CLUSTER_RUN ThisEntry,
  8290. IN USHORT LcnIndex
  8291. )
  8292. /*++
  8293. Routine Description:
  8294. This routine is called when a run's length has been increased. This
  8295. routine makes the necessary changes to the length-sorted list.
  8296. Arguments:
  8297. CachedRuns - Pointer to the cached runs structure.
  8298. ThisEntry - Entry whose size is being changed.
  8299. LcnIndex - The index in the Lcn-sorted array of this entry.
  8300. Return Value:
  8301. None
  8302. --*/
  8303. {
  8304. BOOLEAN FoundRun;
  8305. USHORT Index;
  8306. USHORT Count;
  8307. USHORT RunIndex;
  8308. USHORT WindowIndex;
  8309. USHORT FirstWindowIndex;
  8310. PNTFS_LCN_CLUSTER_RUN OldEntry;
  8311. PNTFS_DELETED_RUNS DelWindow;
  8312. PAGED_CODE();
  8313. DebugTrace( +1, Dbg, ("NtfsGrowLengthInCachedLcn\n") );
  8314. DebugTrace( 0, Dbg, ("ThisEntry = %08lx\n", ThisEntry) );
  8315. DebugTrace( 0, Dbg, ("LcnIndex = %04x\n", LcnIndex) );
  8316. DebugTrace( 0, Dbg, ("LengthIndex = %04x\n", ThisEntry->LengthIndex) );
  8317. //
  8318. // Find the new insertion point.
  8319. //
  8320. //
  8321. // Find the nearest non-deleted entry with
  8322. // index > ThisEntry->LengthIndex.
  8323. //
  8324. if (ThisEntry->LengthIndex < (CachedRuns->Used - 1) ) {
  8325. RunIndex = ThisEntry->LengthIndex + 1;
  8326. if (CachedRuns->LengthArray[ RunIndex ] == NTFS_CACHED_RUNS_DEL_INDEX) {
  8327. //
  8328. // The entry has been deleted and must be ignored. Get the
  8329. // window of deleted entries that covers it.
  8330. //
  8331. DelWindow = NtfsGetDelWindow( CachedRuns,
  8332. RunIndex,
  8333. RunIndex,
  8334. FALSE,
  8335. NULL);
  8336. ASSERT( DelWindow != NULL );
  8337. ASSERT( DelWindow->EndIndex >= RunIndex );
  8338. ASSERT( DelWindow->StartIndex <= RunIndex );
  8339. //
  8340. // Set RunIndex to the entry just after this deleted
  8341. // window.
  8342. //
  8343. if (DelWindow->EndIndex < (CachedRuns->Used - 1)) {
  8344. RunIndex = DelWindow->EndIndex + 1;
  8345. //
  8346. // Nothing to do. The entry is still the largest in the
  8347. // list.
  8348. //
  8349. } else {
  8350. RunIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  8351. }
  8352. }
  8353. //
  8354. // Nothing to do. The entry is still the largest in the list.
  8355. //
  8356. } else {
  8357. RunIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  8358. }
  8359. //
  8360. // If the run is possible out of position then compare our length with the following length.
  8361. //
  8362. if (RunIndex != NTFS_CACHED_RUNS_DEL_INDEX) {
  8363. OldEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ RunIndex ];
  8364. //
  8365. // The entry will move in the list. We need to search for
  8366. // the insertion position in the
  8367. // range [RunIndex..CachedRuns->Used].
  8368. //
  8369. if ((OldEntry->RunLength < ThisEntry->RunLength) ||
  8370. ((OldEntry->RunLength == ThisEntry->RunLength) &&
  8371. (OldEntry->Lcn < ThisEntry->Lcn)) ) {
  8372. //
  8373. // Get the insertion point for the new entry.
  8374. //
  8375. FoundRun = NtfsPositionCachedLcnByLength( CachedRuns,
  8376. ThisEntry->RunLength,
  8377. &ThisEntry->Lcn,
  8378. &RunIndex,
  8379. TRUE,
  8380. &Index );
  8381. //
  8382. // Index points to the closest run by Lcn that has a RunLength
  8383. // equal to Length. We need to check to see if the new entry
  8384. // should be inserted before or after it.
  8385. //
  8386. if (FoundRun) {
  8387. OldEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ Index ];
  8388. ASSERT( OldEntry->RunLength == ThisEntry->RunLength );
  8389. //
  8390. // The new run should come before this one.
  8391. //
  8392. if (OldEntry->Lcn > ThisEntry->Lcn) {
  8393. //
  8394. // We need to adjust Index downwards.
  8395. //
  8396. Index -= 1;
  8397. }
  8398. } else {
  8399. //
  8400. // The entry pointed to by Index is either deleted or sorts
  8401. // higher than the new one. Move the insertion point back one
  8402. // position.
  8403. //
  8404. Index -= 1;
  8405. }
  8406. //
  8407. // At this point, Index indicates the new position for the entry.
  8408. // Any entry currently at Index sorts lower.
  8409. //
  8410. ASSERT( Index > ThisEntry->LengthIndex );
  8411. if (CachedRuns->LengthArray[ Index ] == NTFS_CACHED_RUNS_DEL_INDEX) {
  8412. //
  8413. // Advance Index to before the start of this window of deleted
  8414. // entries.
  8415. //
  8416. DelWindow = NtfsGetDelWindow( CachedRuns,
  8417. Index,
  8418. Index,
  8419. FALSE,
  8420. NULL);
  8421. ASSERT( DelWindow );
  8422. ASSERT( DelWindow->StartIndex <= Index );
  8423. ASSERT( DelWindow->EndIndex >= Index );
  8424. Index = DelWindow->StartIndex - 1;
  8425. }
  8426. ASSERT( Index > ThisEntry->LengthIndex );
  8427. //
  8428. // Move the entries between ThisEntry->LengthIndex + 1 and Index
  8429. // to the left.
  8430. //
  8431. RtlMoveMemory( CachedRuns->LengthArray + ThisEntry->LengthIndex,
  8432. CachedRuns->LengthArray + ThisEntry->LengthIndex + 1,
  8433. sizeof( USHORT ) * (Index - ThisEntry->LengthIndex) );
  8434. //
  8435. // Update the indices in the Lcn-sorted list to reflect
  8436. // the move of the length-sorted entries.
  8437. //
  8438. for (Count = ThisEntry->LengthIndex, DelWindow = NULL;
  8439. Count < Index;
  8440. Count += 1) {
  8441. RunIndex = CachedRuns->LengthArray[ Count ];
  8442. //
  8443. // Update the Lcn array if the length entry isn't deleted.
  8444. //
  8445. if (RunIndex != NTFS_CACHED_RUNS_DEL_INDEX) {
  8446. CachedRuns->LcnArray[ RunIndex ].LengthIndex = Count;
  8447. } else {
  8448. //
  8449. // Update the window of deleted entries.
  8450. //
  8451. if (DelWindow != NULL) {
  8452. //
  8453. // The window we want must follow the last one we
  8454. // found.
  8455. //
  8456. DelWindow += 1;
  8457. WindowIndex += 1;
  8458. ASSERT( WindowIndex < CachedRuns->DelLengthCount );
  8459. } else {
  8460. //
  8461. // Lookup the window containing the entry. Remember
  8462. // to look for Count + 1 because the window we are
  8463. // seaching for has not yet been updated.
  8464. //
  8465. DelWindow = NtfsGetDelWindow( CachedRuns,
  8466. Count + 1,
  8467. Count + 1,
  8468. FALSE,
  8469. &WindowIndex);
  8470. ASSERT( DelWindow != NULL );
  8471. FirstWindowIndex = WindowIndex;
  8472. }
  8473. ASSERT( DelWindow->StartIndex == (Count + 1) );
  8474. ASSERT( DelWindow->EndIndex < Index );
  8475. //
  8476. // Update the window.
  8477. //
  8478. DelWindow->StartIndex -= 1;
  8479. DelWindow->EndIndex -= 1;
  8480. //
  8481. // Advance Count past window.
  8482. //
  8483. Count = DelWindow->EndIndex;
  8484. }
  8485. }
  8486. //
  8487. // We may have moved the first window to the left such that
  8488. // it should be merged with the preceding window.
  8489. //
  8490. if ((DelWindow != NULL) && (FirstWindowIndex > 0) ) {
  8491. PNTFS_DELETED_RUNS PrevWindow;
  8492. DelWindow = CachedRuns->DeletedLengthWindows + FirstWindowIndex;
  8493. PrevWindow = DelWindow - 1;
  8494. if (PrevWindow->EndIndex == (DelWindow->StartIndex - 1) ) {
  8495. //
  8496. // We need to merge these windows.
  8497. //
  8498. PrevWindow->EndIndex = DelWindow->EndIndex;
  8499. NtfsDeleteDelWindow( CachedRuns,
  8500. FALSE,
  8501. FirstWindowIndex);
  8502. }
  8503. }
  8504. //
  8505. // Update the entries corresponding to ThisEntry;
  8506. //
  8507. CachedRuns->LengthArray[ Index ] = LcnIndex;
  8508. ThisEntry->LengthIndex = Index;
  8509. }
  8510. }
  8511. DebugTrace( 0, Dbg, ("Final LengthIndex = %04x\n", ThisEntry->LengthIndex) );
  8512. #ifdef NTFS_CHECK_CACHED_RUNS
  8513. if (NtfsDoVerifyCachedRuns) {
  8514. NtfsVerifyCachedRuns( CachedRuns, TRUE, TRUE );
  8515. }
  8516. #endif
  8517. DebugTrace( -1, Dbg, ("NtfsGrowLengthInCachedLcn -> VOID\n") );
  8518. return;
  8519. }
  8520. //
  8521. // Local support routine
  8522. //
  8523. VOID
  8524. NtfsShrinkLengthInCachedLcn (
  8525. IN PNTFS_CACHED_RUNS CachedRuns,
  8526. IN PNTFS_LCN_CLUSTER_RUN ThisEntry,
  8527. IN USHORT LcnIndex
  8528. )
  8529. /*++
  8530. Routine Description:
  8531. This routine is called when a run's length has been reduced. This routine
  8532. makes the necessary changes to the length-sorted list.
  8533. Arguments:
  8534. CachedRuns - Pointer to the cached runs structure.
  8535. ThisEntry - Entry whose size is being changed.
  8536. LcnIndex - The index in the Lcn-sorted array of this entry.
  8537. Return Value:
  8538. None
  8539. --*/
  8540. {
  8541. BOOLEAN FoundRun;
  8542. USHORT Index;
  8543. USHORT WindowIndex;
  8544. USHORT Count;
  8545. USHORT RunIndex;
  8546. PNTFS_LCN_CLUSTER_RUN OldEntry;
  8547. PNTFS_DELETED_RUNS DelWindow;
  8548. PAGED_CODE();
  8549. DebugTrace( +1, Dbg, ("NtfsShrinkLengthInCachedLcn\n") );
  8550. DebugTrace( 0, Dbg, ("ThisEntry = %08lx\n", ThisEntry) );
  8551. DebugTrace( 0, Dbg, ("LcnIndex = %04x\n", LcnIndex) );
  8552. DebugTrace( 0, Dbg, ("LengthIndex = %04x\n", ThisEntry->LengthIndex) );
  8553. //
  8554. // Find the nearest non-deleted entry with
  8555. // index < ThisEntry->LengthIndex.
  8556. //
  8557. if (ThisEntry->LengthIndex > 0) {
  8558. RunIndex = ThisEntry->LengthIndex - 1;
  8559. if (CachedRuns->LengthArray[ RunIndex ] == NTFS_CACHED_RUNS_DEL_INDEX) {
  8560. //
  8561. // The entry has been deleted and must be ignored. Get the
  8562. // window of deleted entries that covers it.
  8563. //
  8564. DelWindow = NtfsGetDelWindow( CachedRuns,
  8565. RunIndex,
  8566. RunIndex,
  8567. FALSE,
  8568. NULL);
  8569. ASSERT( DelWindow );
  8570. ASSERT( DelWindow->EndIndex >= RunIndex );
  8571. ASSERT( DelWindow->StartIndex <= RunIndex );
  8572. //
  8573. // Move ahead of this window if possible.
  8574. //
  8575. if (DelWindow->StartIndex > 0) {
  8576. RunIndex = DelWindow->StartIndex - 1;
  8577. //
  8578. // Nothing to do. The entry is still the smallest in the
  8579. // list.
  8580. //
  8581. } else {
  8582. RunIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  8583. }
  8584. }
  8585. //
  8586. // Nothing to do. The entry is still the smallest in the list.
  8587. //
  8588. } else {
  8589. RunIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  8590. }
  8591. //
  8592. // If the run is possible out of position then compare our length with the prior length.
  8593. //
  8594. if (RunIndex != NTFS_CACHED_RUNS_DEL_INDEX) {
  8595. OldEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ RunIndex ];
  8596. //
  8597. // Check for a conflict with the previous run.
  8598. //
  8599. if ((OldEntry->RunLength > ThisEntry->RunLength) ||
  8600. ((OldEntry->RunLength == ThisEntry->RunLength) &&
  8601. (OldEntry->Lcn > ThisEntry->Lcn)) ) {
  8602. //
  8603. // Get the insertion point for the new entry.
  8604. //
  8605. FoundRun = NtfsPositionCachedLcnByLength( CachedRuns,
  8606. ThisEntry->RunLength,
  8607. &ThisEntry->Lcn,
  8608. &RunIndex,
  8609. FALSE,
  8610. &Index );
  8611. //
  8612. // If found Index points to the closest run by Lcn that has a RunLength
  8613. // equal to Length. We need to check to see if the new entry
  8614. // should be inserted before or after it.
  8615. //
  8616. if (FoundRun) {
  8617. OldEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ Index ];
  8618. ASSERT( OldEntry->RunLength == ThisEntry->RunLength );
  8619. if (OldEntry->Lcn < ThisEntry->Lcn) {
  8620. //
  8621. // The new run should come after this one.
  8622. // We need to adjust Index upwards.
  8623. //
  8624. Index += 1;
  8625. DebugTrace( 0, Dbg, ("Increment Index to %04x\n", Index) );
  8626. }
  8627. }
  8628. //
  8629. // At this point, Index indicates the new position for the entry.
  8630. // Any entry currently at Index sorts higher.
  8631. //
  8632. ASSERT( Index < ThisEntry->LengthIndex );
  8633. //
  8634. // Advance Index past the end of this window of deleted
  8635. // entries.
  8636. //
  8637. if (CachedRuns->LengthArray[ Index ] == NTFS_CACHED_RUNS_DEL_INDEX) {
  8638. DelWindow = NtfsGetDelWindow( CachedRuns,
  8639. Index,
  8640. Index,
  8641. FALSE,
  8642. NULL);
  8643. ASSERT( DelWindow );
  8644. ASSERT( DelWindow->StartIndex <= Index );
  8645. ASSERT( DelWindow->EndIndex >= Index );
  8646. Index = DelWindow->EndIndex + 1;
  8647. ASSERT( Index < ThisEntry->LengthIndex );
  8648. }
  8649. // Move the entries between Index and ThisEntry->LengthIndex - 1
  8650. // to the right.
  8651. //
  8652. RtlMoveMemory( CachedRuns->LengthArray + Index + 1,
  8653. CachedRuns->LengthArray + Index,
  8654. sizeof( USHORT ) * (ThisEntry->LengthIndex - Index) );
  8655. //
  8656. // Update the indices in the Lcn-sorted list to reflect
  8657. // the move of the length-sorted entries.
  8658. //
  8659. for (Count = Index + 1, DelWindow = NULL;
  8660. Count <= ThisEntry->LengthIndex;
  8661. Count += 1) {
  8662. RunIndex = CachedRuns->LengthArray[ Count ];
  8663. //
  8664. // Update the Lcn array if the length entry isn't deleted.
  8665. //
  8666. if (RunIndex != NTFS_CACHED_RUNS_DEL_INDEX) {
  8667. CachedRuns->LcnArray[ RunIndex ].LengthIndex = Count;
  8668. } else {
  8669. //
  8670. // Update the window of deleted entries.
  8671. //
  8672. if (DelWindow != NULL) {
  8673. //
  8674. // The window we want must follow the last one we
  8675. // found.
  8676. //
  8677. DelWindow += 1;
  8678. WindowIndex += 1;
  8679. ASSERT( WindowIndex < CachedRuns->DelLengthCount );
  8680. //
  8681. // Lookup the window containing the entry. Remeber
  8682. // to look for Count - 1 because the window we are
  8683. // seaching for has not yet been updated.
  8684. //
  8685. } else {
  8686. DelWindow = NtfsGetDelWindow( CachedRuns,
  8687. Count - 1,
  8688. Count - 1,
  8689. FALSE,
  8690. &WindowIndex);
  8691. ASSERT( DelWindow != NULL );
  8692. }
  8693. ASSERT( DelWindow->StartIndex == (Count - 1) );
  8694. ASSERT( DelWindow->EndIndex < ThisEntry->LengthIndex );
  8695. //
  8696. // Update the window.
  8697. //
  8698. DelWindow->StartIndex += 1;
  8699. DelWindow->EndIndex += 1;
  8700. //
  8701. // Advance Count past window.
  8702. //
  8703. Count = DelWindow->EndIndex;
  8704. }
  8705. }
  8706. //
  8707. // We may have moved the last window to the right such that
  8708. // it should be merged with the following window.
  8709. //
  8710. if ((DelWindow != NULL) &&
  8711. ((WindowIndex + 1) < CachedRuns->DelLengthCount)) {
  8712. PNTFS_DELETED_RUNS NextWindow = DelWindow + 1;
  8713. if (DelWindow->EndIndex == (NextWindow->StartIndex - 1) ) {
  8714. //
  8715. // We need to merge these windows.
  8716. //
  8717. DelWindow->EndIndex = NextWindow->EndIndex;
  8718. NtfsDeleteDelWindow( CachedRuns,
  8719. FALSE,
  8720. WindowIndex + 1);
  8721. }
  8722. }
  8723. //
  8724. // Update the entries corresponding to ThisEntry;
  8725. //
  8726. CachedRuns->LengthArray[ Index ] = LcnIndex;
  8727. ThisEntry->LengthIndex = Index;
  8728. }
  8729. }
  8730. DebugTrace( 0, Dbg, ("Final LengthIndex = %04x\n", ThisEntry->LengthIndex) );
  8731. #ifdef NTFS_CHECK_CACHED_RUNS
  8732. if (NtfsDoVerifyCachedRuns) {
  8733. NtfsVerifyCachedRuns( CachedRuns, FALSE, FALSE );
  8734. }
  8735. #endif
  8736. DebugTrace( -1, Dbg, ("NtfsShrinkLengthInCachedLcn -> VOID\n") );
  8737. return;
  8738. }
  8739. //
  8740. // Local support routine
  8741. //
  8742. VOID
  8743. NtfsRemoveCachedLcn (
  8744. IN PNTFS_CACHED_RUNS CachedRuns,
  8745. IN LCN Lcn,
  8746. IN LONGLONG Length
  8747. )
  8748. /*++
  8749. Routine Description:
  8750. This routine is called to remove an entry from the cached run array. The run is not
  8751. guaranteed to be present.
  8752. Arguments:
  8753. CachedRuns - Pointer to the cached runs structure.
  8754. Lcn - Start of run to remove.
  8755. Length - Length of run to remove.
  8756. Return Value:
  8757. None
  8758. --*/
  8759. {
  8760. USHORT Index;
  8761. LCN StartingLcn;
  8762. LCN EndOfExistingRun;
  8763. LCN EndOfInputRun = Lcn + Length;
  8764. LONGLONG RunLength;
  8765. PNTFS_DELETED_RUNS DelWindow;
  8766. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  8767. BOOLEAN FirstFragSmaller = FALSE;
  8768. BOOLEAN DontSplit = FALSE;
  8769. PAGED_CODE();
  8770. DebugTrace( +1, Dbg, ("NtfsRemoveCachedLcn\n") );
  8771. //
  8772. // Return immediately if length is zero.
  8773. //
  8774. if (Length == 0) {
  8775. DebugTrace( -1, Dbg, ("NtfsRemoveCachedLcn -> VOID\n") );
  8776. return;
  8777. }
  8778. //
  8779. // Lookup the run. If we don't find anything then point past the end
  8780. // of the array.
  8781. //
  8782. NtfsLookupCachedLcn( CachedRuns, Lcn, &StartingLcn, &RunLength, &Index );
  8783. //
  8784. // We have several cases to deal with.
  8785. //
  8786. // 1 - This run is past the end of array. Nothing to do.
  8787. // 2 - This run is not in the array. Nothing to do.
  8788. // 3 - This run starts past the beginning of a entry. Resize the entry.
  8789. // 4 - This run contains a complete array entry. Remove the entry.
  8790. // 5 - This run ends before the end of an entry. Resize the entry.
  8791. //
  8792. //
  8793. // Loop to process the case where we encounter several entries.
  8794. // Test for case 1 as the exit condition for the loop.
  8795. //
  8796. while (Index < CachedRuns->Used) {
  8797. ThisEntry = CachedRuns->LcnArray + Index;
  8798. //
  8799. // The entry has been deleted and must be ignored. Get the
  8800. // window of deleted entries that covers it.
  8801. //
  8802. if (ThisEntry->RunLength == 0) {
  8803. DelWindow = NtfsGetDelWindow( CachedRuns,
  8804. Index,
  8805. Index,
  8806. TRUE,
  8807. NULL);
  8808. ASSERT( DelWindow != NULL );
  8809. ASSERT( DelWindow->EndIndex >= Index );
  8810. ASSERT( DelWindow->StartIndex <= Index );
  8811. //
  8812. // Advance the index past the deleted entries.
  8813. //
  8814. Index = DelWindow->EndIndex + 1;
  8815. continue;
  8816. }
  8817. //
  8818. // Remember the range of this run.
  8819. //
  8820. EndOfExistingRun = ThisEntry->Lcn + ThisEntry->RunLength;
  8821. //
  8822. // Case 2 - No overlap.
  8823. //
  8824. if (EndOfInputRun <= ThisEntry->Lcn) {
  8825. break;
  8826. //
  8827. // Case 3 - The run starts beyond the beginning of this run.
  8828. //
  8829. } else if (Lcn > ThisEntry->Lcn) {
  8830. //
  8831. // Reduce the current entry so that is covers only the
  8832. // first fragment and move it to the correct position in
  8833. // the length-sorted array.
  8834. //
  8835. NtfsModifyCachedBinArray( CachedRuns,
  8836. ThisEntry->RunLength,
  8837. Lcn - ThisEntry->Lcn );
  8838. ThisEntry->RunLength = Lcn - ThisEntry->Lcn;
  8839. //
  8840. // Adjust this length in the run length array.
  8841. //
  8842. NtfsShrinkLengthInCachedLcn( CachedRuns,
  8843. ThisEntry,
  8844. Index );
  8845. //
  8846. // We need to split this entry in two. Now reinsert the portion
  8847. // split off.
  8848. //
  8849. if (EndOfInputRun < EndOfExistingRun) {
  8850. //
  8851. // Now create a new entry that covers the second
  8852. // fragment. It should directly follow ThisEntry in the
  8853. // Lcn-sorted list.
  8854. //
  8855. NtfsInsertCachedRun( CachedRuns,
  8856. EndOfInputRun,
  8857. EndOfExistingRun - EndOfInputRun,
  8858. Index + 1);
  8859. //
  8860. // Nothing else to do.
  8861. //
  8862. break;
  8863. //
  8864. // We will trim the tail of this entry.
  8865. //
  8866. } else if (EndOfInputRun > EndOfExistingRun) {
  8867. Lcn = EndOfExistingRun;
  8868. Index += 1;
  8869. } else {
  8870. break;
  8871. }
  8872. //
  8873. // Case 4 - Remove a complete entry.
  8874. //
  8875. } else if (EndOfInputRun >= EndOfExistingRun) {
  8876. ASSERT( Lcn <= ThisEntry->Lcn );
  8877. //
  8878. // Delete the run. This can cause compaction to be run but we
  8879. // are guaranteed that the entry at Index will not move.
  8880. //
  8881. NtfsDeleteCachedRun( CachedRuns,
  8882. Index,
  8883. ThisEntry->LengthIndex );
  8884. //
  8885. // Advance the Lcn if we go past this entry.
  8886. //
  8887. if (EndOfInputRun > EndOfExistingRun) {
  8888. Lcn = EndOfExistingRun;
  8889. } else {
  8890. break;
  8891. }
  8892. //
  8893. // Case 5 - This entry starts at or before the start of the run
  8894. // and ends before the end of the run.
  8895. //
  8896. } else {
  8897. ASSERT( Lcn <= ThisEntry->Lcn );
  8898. ASSERT( EndOfInputRun < EndOfExistingRun );
  8899. //
  8900. // Reduce the current entry so that is covers only the end of the
  8901. // run and move it to the correct position in the length-sorted
  8902. // array.
  8903. //
  8904. NtfsModifyCachedBinArray( CachedRuns,
  8905. ThisEntry->RunLength,
  8906. EndOfExistingRun - EndOfInputRun );
  8907. ThisEntry->RunLength = EndOfExistingRun - EndOfInputRun;
  8908. ThisEntry->Lcn = EndOfInputRun;
  8909. NtfsShrinkLengthInCachedLcn( CachedRuns,
  8910. ThisEntry,
  8911. Index );
  8912. break;
  8913. }
  8914. }
  8915. #ifdef NTFS_CHECK_CACHED_RUNS
  8916. if (NtfsDoVerifyCachedRuns) {
  8917. NtfsVerifyCachedRuns( CachedRuns, FALSE, FALSE );
  8918. }
  8919. #endif
  8920. DebugTrace( -1, Dbg, ("NtfsRemoveCachedLcn -> VOID\n") );
  8921. return;
  8922. }
  8923. //
  8924. // Local support routine
  8925. //
  8926. BOOLEAN
  8927. NtfsGrowCachedRuns (
  8928. IN PNTFS_CACHED_RUNS CachedRuns
  8929. )
  8930. /*++
  8931. Routine Description:
  8932. This routine is called to grow the size of the cached run arrays if
  8933. necessary. We will not exceed the CachedRuns->MaximumSize. It
  8934. is assumed that there are no deleted entries in the arrays. If we can
  8935. grow the arrays, we double the size unless we would grow it by more than
  8936. our max delta. Otherwise we grow it by that amount.
  8937. Arguments:
  8938. CachedRuns - Pointer to the cached runs structure to grow.
  8939. Return Value:
  8940. BOOLEAN - TRUE if we were able to grow the structure, FALSE otherwise.
  8941. --*/
  8942. {
  8943. USHORT NewSize;
  8944. USHORT OldSize = CachedRuns->Avail;
  8945. USHORT Index;
  8946. PNTFS_LCN_CLUSTER_RUN NewLcnArray;
  8947. PUSHORT NewLengthArray;
  8948. PAGED_CODE();
  8949. DebugTrace( +1, Dbg, ("NtfsGrowCachedRuns\n") );
  8950. //
  8951. // Calculate the new size.
  8952. //
  8953. if (CachedRuns->Avail > NTFS_MAX_CACHED_RUNS_DELTA) {
  8954. NewSize = CachedRuns->Avail + NTFS_MAX_CACHED_RUNS_DELTA;
  8955. } else {
  8956. NewSize = CachedRuns->Avail * 2;
  8957. }
  8958. if (NewSize > CachedRuns->MaximumSize) {
  8959. NewSize = CachedRuns->MaximumSize;
  8960. }
  8961. if (NewSize > CachedRuns->Avail) {
  8962. //
  8963. // Allocate the new buffers and copy the previous buffers over.
  8964. //
  8965. NewLcnArray = NtfsAllocatePoolNoRaise( PagedPool,
  8966. sizeof( NTFS_LCN_CLUSTER_RUN ) * NewSize );
  8967. if (NewLcnArray == NULL) {
  8968. DebugTrace( -1, Dbg, ("NtfsGrowCachedRuns -> FALSE\n") );
  8969. return FALSE;
  8970. }
  8971. NewLengthArray = NtfsAllocatePoolNoRaise( PagedPool,
  8972. sizeof( USHORT ) * NewSize );
  8973. if (NewLengthArray == NULL) {
  8974. NtfsFreePool( NewLcnArray );
  8975. DebugTrace( -1, Dbg, ("NtfsGrowCachedRuns -> FALSE\n") );
  8976. return FALSE;
  8977. }
  8978. RtlCopyMemory( NewLcnArray,
  8979. CachedRuns->LcnArray,
  8980. sizeof( NTFS_LCN_CLUSTER_RUN ) * CachedRuns->Used );
  8981. RtlCopyMemory( NewLengthArray,
  8982. CachedRuns->LengthArray,
  8983. sizeof( USHORT ) * CachedRuns->Used );
  8984. //
  8985. // Mark all entries so that they can be detected as deleted.
  8986. //
  8987. for (Index = CachedRuns->Used; Index < NewSize; Index += 1) {
  8988. NewLcnArray[ Index ].RunLength = 0;
  8989. NewLcnArray[ Index ].LengthIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  8990. NewLengthArray[ Index ] = NTFS_CACHED_RUNS_DEL_INDEX;
  8991. }
  8992. //
  8993. // Deallocate the existing buffers and set the cached runs structure
  8994. // to point to the new buffers.
  8995. //
  8996. NtfsFreePool( CachedRuns->LcnArray );
  8997. CachedRuns->LcnArray = NewLcnArray;
  8998. NtfsFreePool( CachedRuns->LengthArray );
  8999. CachedRuns->LengthArray = NewLengthArray;
  9000. //
  9001. // Update the count of available entries.
  9002. //
  9003. CachedRuns->Avail = NewSize;
  9004. //
  9005. // Create a window of deleted entries to cover the newly allocated
  9006. // entries.
  9007. //
  9008. NtfsAddDelWindow( CachedRuns, OldSize, NewSize - 1, TRUE );
  9009. NtfsAddDelWindow( CachedRuns, OldSize, NewSize - 1, FALSE );
  9010. } else {
  9011. DebugTrace( -1, Dbg, ("NtfsGrowCachedRuns -> FALSE\n") );
  9012. return FALSE;
  9013. }
  9014. #ifdef NTFS_CHECK_CACHED_RUNS
  9015. if (NtfsDoVerifyCachedRuns) {
  9016. NtfsVerifyCachedRuns( CachedRuns, FALSE, FALSE );
  9017. }
  9018. #endif
  9019. DebugTrace( -1, Dbg, ("NtfsGrowCachedRuns -> TRUE\n") );
  9020. return TRUE;
  9021. }
  9022. //
  9023. // Local support routine
  9024. //
  9025. VOID
  9026. NtfsCompactCachedRuns (
  9027. IN PNTFS_CACHED_RUNS CachedRuns,
  9028. IN USHORT FirstIndex,
  9029. IN USHORT LastIndex,
  9030. IN BOOLEAN LcnSortedList
  9031. )
  9032. /*++
  9033. Routine Description:
  9034. This routine is called to compact two of the windows of deleted entries
  9035. into a single window. Note that entries in the given range of indices
  9036. have been marked as deleted, but are not yet in a window of deleted
  9037. entries. This should not trigger a corruption warning. To avoid
  9038. confusion, we will be sure not to choose the windows to be compacted
  9039. such that the given range of indices gets moved.
  9040. Arguments:
  9041. CachedRuns - Pointer to the cached run structure.
  9042. FirstIndex - Index that marks the start of the newest range of deleted
  9043. entries.
  9044. LastIndex - The index of the last entry in the newest range of deleted
  9045. entries.
  9046. LcnSortedList - If TRUE, the Lcn-sorted list is compacted.
  9047. If FALSE, the length-sorted list is compacted.
  9048. Return Value:
  9049. None
  9050. --*/
  9051. {
  9052. USHORT Gap1;
  9053. USHORT Gap2;
  9054. USHORT RunIndex;
  9055. USHORT Count;
  9056. USHORT GapIndex;
  9057. PUSHORT WindowCount;
  9058. PNTFS_DELETED_RUNS DelWindow;
  9059. PNTFS_DELETED_RUNS PrevWindow;
  9060. PNTFS_DELETED_RUNS Windows;
  9061. PAGED_CODE();
  9062. DebugTrace( +1, Dbg, ("NtfsCompactCachedRuns\n") );
  9063. ASSERT( FirstIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  9064. ASSERT( LastIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  9065. if (LcnSortedList) {
  9066. WindowCount = &CachedRuns->DelLcnCount;
  9067. Windows = CachedRuns->DeletedLcnWindows;
  9068. } else {
  9069. WindowCount = &CachedRuns->DelLengthCount;
  9070. Windows = CachedRuns->DeletedLengthWindows;
  9071. }
  9072. ASSERT( *WindowCount > 1 );
  9073. //
  9074. // Loop through the windows looking for the smallest gap of non-deleted
  9075. // entries. We will not choose a gap the covers [FirstIndex..LastIndex]
  9076. //
  9077. Gap1 = NTFS_CACHED_RUNS_DEL_INDEX;
  9078. for (Count = 1, DelWindow = Windows + 1, PrevWindow = Windows;
  9079. (Count < *WindowCount) && (Gap1 > 1);
  9080. Count += 1, PrevWindow += 1, DelWindow += 1) {
  9081. //
  9082. // Compute this gap if the exempt range doesn't fall within it. We want to track the
  9083. // actual number of entries.
  9084. //
  9085. if ((PrevWindow->StartIndex > LastIndex) ||
  9086. (DelWindow->EndIndex < FirstIndex)) {
  9087. Gap2 = DelWindow->StartIndex - (PrevWindow->EndIndex + 1);
  9088. //
  9089. // Remember if this gap is our smallest so far.
  9090. //
  9091. if (Gap2 < Gap1) {
  9092. Gap1 = Gap2;
  9093. GapIndex = Count;
  9094. }
  9095. }
  9096. }
  9097. //
  9098. // Merge the window at GapIndex with the one that precedes it by moving
  9099. // the non-deleted entries in the gap between them to the start of the
  9100. // preceding window.
  9101. //
  9102. DelWindow = Windows + GapIndex;
  9103. PrevWindow = DelWindow - 1;
  9104. //
  9105. // Copy the block of entries that we will be keeping
  9106. // into the insertion point.
  9107. //
  9108. DebugTrace( 0,
  9109. Dbg,
  9110. ("copy %04x entries from=%04x to=%04x\n", Gap1, PrevWindow->EndIndex + 1, PrevWindow->StartIndex) );
  9111. if (LcnSortedList) {
  9112. RtlMoveMemory( CachedRuns->LcnArray + PrevWindow->StartIndex,
  9113. CachedRuns->LcnArray + PrevWindow->EndIndex + 1,
  9114. sizeof( NTFS_LCN_CLUSTER_RUN ) * Gap1 );
  9115. //
  9116. // Update the indices in the Length-sorted list to
  9117. // reflect the move of the lcn-sorted entries.
  9118. //
  9119. for (Count = 0; Count < Gap1; Count += 1) {
  9120. RunIndex = CachedRuns->LcnArray[ PrevWindow->StartIndex + Count ].LengthIndex;
  9121. ASSERT( RunIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  9122. CachedRuns->LengthArray[ RunIndex ] = PrevWindow->StartIndex + Count;
  9123. }
  9124. //
  9125. // Mark the entries from the gap that are going to be part of the
  9126. // merged windows as deleted.
  9127. //
  9128. // We only need to do this for entries past the end of the gap we are deleting.
  9129. //
  9130. Count = PrevWindow->StartIndex + Gap1;
  9131. if (Count < PrevWindow->EndIndex + 1) {
  9132. Count = PrevWindow->EndIndex + 1;
  9133. }
  9134. while (Count < DelWindow->StartIndex) {
  9135. CachedRuns->LcnArray[ Count ].LengthIndex = NTFS_CACHED_RUNS_DEL_INDEX;
  9136. CachedRuns->LcnArray[ Count ].RunLength = 0;
  9137. Count += 1;
  9138. }
  9139. } else {
  9140. RtlMoveMemory( CachedRuns->LengthArray + PrevWindow->StartIndex,
  9141. CachedRuns->LengthArray + PrevWindow->EndIndex + 1,
  9142. sizeof( USHORT ) * Gap1 );
  9143. //
  9144. // Update the indices in the Lcn-sorted list to reflect
  9145. // the move of the length-sorted entries.
  9146. //
  9147. for (Count = 0; Count < Gap1; Count += 1) {
  9148. RunIndex = CachedRuns->LengthArray[ PrevWindow->StartIndex + Count ];
  9149. ASSERT( RunIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  9150. CachedRuns->LcnArray[ RunIndex ].LengthIndex = PrevWindow->StartIndex + Count;
  9151. }
  9152. //
  9153. // Mark the entries from the gap that are going to be part of the
  9154. // merged windows as deleted.
  9155. //
  9156. // We only need to do this for entries past the end of the gap we are deleting.
  9157. //
  9158. Count = PrevWindow->StartIndex + Gap1;
  9159. if (Count < PrevWindow->EndIndex + 1) {
  9160. Count = PrevWindow->EndIndex + 1;
  9161. }
  9162. while (Count < DelWindow->StartIndex) {
  9163. CachedRuns->LengthArray[ Count ] = NTFS_CACHED_RUNS_DEL_INDEX;
  9164. Count += 1;
  9165. }
  9166. }
  9167. //
  9168. // Update the previous window to reflect the larger size.
  9169. //
  9170. ASSERT( (PrevWindow->EndIndex + Gap1 + 1) == DelWindow->StartIndex );
  9171. PrevWindow->StartIndex += Gap1;
  9172. PrevWindow->EndIndex = DelWindow->EndIndex;
  9173. //
  9174. // Delete DelWindow.
  9175. //
  9176. NtfsDeleteDelWindow( CachedRuns,
  9177. LcnSortedList,
  9178. GapIndex);
  9179. #ifdef NTFS_CHECK_CACHED_RUNS
  9180. //
  9181. // We will not check sort orders in NtfsVerifyCachedRuns because we
  9182. // could be making this call as part of deleting runs that have an
  9183. // overlap with a newly inserted run. This could give false corruption
  9184. // warnings.
  9185. //
  9186. if (LcnSortedList) {
  9187. NtfsVerifyCachedLcnRuns ( CachedRuns,
  9188. FirstIndex,
  9189. LastIndex,
  9190. TRUE,
  9191. TRUE );
  9192. } else {
  9193. NtfsVerifyCachedLenRuns ( CachedRuns,
  9194. FirstIndex,
  9195. LastIndex,
  9196. TRUE );
  9197. }
  9198. #endif
  9199. DebugTrace( -1, Dbg, ("NtfsCompactCachedRuns -> VOID\n") );
  9200. return;
  9201. }
  9202. //
  9203. // Local support routine
  9204. //
  9205. BOOLEAN
  9206. NtfsPositionCachedLcn (
  9207. IN PNTFS_CACHED_RUNS CachedRuns,
  9208. IN LCN Lcn,
  9209. OUT PUSHORT Index
  9210. )
  9211. /*++
  9212. Routine Description:
  9213. This routine is called to position ourselves with an Lcn lookup. On return
  9214. we will return the index where the current entry should go or where it
  9215. currently resides. The return value indicates whether the entry is
  9216. present. The Lcn does not have to be at the beginning of the found run.
  9217. Arguments:
  9218. CachedRuns - Pointer to the cached run structure.
  9219. Lcn - Lcn we are interested in.
  9220. Index - Address to store the index of the position in the Lcn array.
  9221. Return Value:
  9222. BOOLEAN - TRUE if the entry is found, FALSE otherwise.
  9223. --*/
  9224. {
  9225. USHORT Min, Max, Current;
  9226. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  9227. PNTFS_DELETED_RUNS DelWindow;
  9228. BOOLEAN FoundLcn = FALSE;
  9229. PAGED_CODE();
  9230. DebugTrace( +1, Dbg, ("NtfsPositionCachedLcn\n") );
  9231. //
  9232. // Perform a binary search to find the index. Note we start Max past
  9233. // the end so don't rely on it being valid.
  9234. //
  9235. Min = 0;
  9236. Max = CachedRuns->Avail;
  9237. while (Min != Max) {
  9238. Current = (USHORT) (((ULONG) Max + Min) / 2);
  9239. ThisEntry = CachedRuns->LcnArray + Current;
  9240. //
  9241. // The current entry has been deleted and must be ignored.
  9242. // Get the window of deleted entries that covers Current.
  9243. //
  9244. if (ThisEntry->RunLength == 0) {
  9245. DelWindow = NtfsGetDelWindow( CachedRuns,
  9246. Current,
  9247. Current,
  9248. TRUE,
  9249. NULL);
  9250. ASSERT( DelWindow != NULL );
  9251. ASSERT( DelWindow->EndIndex >= Current );
  9252. ASSERT( DelWindow->StartIndex <= Current );
  9253. //
  9254. // Go to the edges of this deleted entries window to determine
  9255. // which way we should go.
  9256. //
  9257. //
  9258. // If the deleted window spans the remaining used runs then move
  9259. // to the beginning of the window.
  9260. //
  9261. if ((DelWindow->EndIndex + 1) >= CachedRuns->Used ) {
  9262. Max = DelWindow->StartIndex;
  9263. ASSERT( Min <= Max );
  9264. //
  9265. // If the deleted window is not at index zero then look to the entry
  9266. // on the left.
  9267. //
  9268. } else if (DelWindow->StartIndex > 0) {
  9269. ThisEntry = CachedRuns->LcnArray + DelWindow->StartIndex - 1;
  9270. ASSERT( ThisEntry->RunLength != 0 );
  9271. if (Lcn < (ThisEntry->Lcn + ThisEntry->RunLength)) {
  9272. //
  9273. // The search should continue from the lower edge of the
  9274. // window.
  9275. //
  9276. Max = DelWindow->StartIndex;
  9277. ASSERT( Min <= Max );
  9278. } else {
  9279. //
  9280. // The search should continue from the upper edge of the
  9281. // window.
  9282. //
  9283. Min = DelWindow->EndIndex + 1;
  9284. ASSERT( Min <= Max );
  9285. }
  9286. //
  9287. // The search should continue from the upper edge of the
  9288. // deleted window.
  9289. //
  9290. } else {
  9291. Min = DelWindow->EndIndex + 1;
  9292. ASSERT( Min <= Max );
  9293. }
  9294. //
  9295. // Loop back now that Min or Max has been updated.
  9296. //
  9297. continue;
  9298. }
  9299. //
  9300. // If our Lcn is less than this then move the Max value down.
  9301. //
  9302. if (Lcn < ThisEntry->Lcn) {
  9303. Max = Current;
  9304. ASSERT( Min <= Max );
  9305. //
  9306. // If our Lcn is outside the range for this entry then move
  9307. // the Min value up. Make it one greater than the current
  9308. // index since we always round the index down.
  9309. //
  9310. } else if (Lcn >= (ThisEntry->Lcn + ThisEntry->RunLength)) {
  9311. Min = Current + 1;
  9312. ASSERT( Min <= Max );
  9313. //
  9314. // This must be a hit.
  9315. //
  9316. } else {
  9317. Min = Current;
  9318. FoundLcn = TRUE;
  9319. break;
  9320. }
  9321. }
  9322. *Index = Min;
  9323. //
  9324. // Check that we are positioned correctly.
  9325. //
  9326. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  9327. ThisEntry = CachedRuns->LcnArray + *Index - 1;
  9328. ASSERT( FoundLcn ||
  9329. (*Index == 0) ||
  9330. (ThisEntry->RunLength == 0) ||
  9331. (Lcn >= (ThisEntry->Lcn + ThisEntry->RunLength)) );
  9332. ThisEntry = CachedRuns->LcnArray + *Index;
  9333. ASSERT( FoundLcn ||
  9334. (*Index == CachedRuns->Used) ||
  9335. (ThisEntry->RunLength == 0) ||
  9336. (Lcn < ThisEntry->Lcn) );
  9337. #endif
  9338. DebugTrace( -1, Dbg, ("NtfsPositionCachedLcn -> %01x\n", FoundLcn) );
  9339. return FoundLcn;
  9340. }
  9341. //
  9342. // Local support routine
  9343. //
  9344. BOOLEAN
  9345. NtfsPositionCachedLcnByLength (
  9346. IN PNTFS_CACHED_RUNS CachedRuns,
  9347. IN LONGLONG RunLength,
  9348. IN PLCN Lcn OPTIONAL,
  9349. IN PUSHORT StartIndex OPTIONAL,
  9350. IN BOOLEAN SearchForward,
  9351. OUT PUSHORT RunIndex
  9352. )
  9353. /*++
  9354. Routine Description:
  9355. This routine is called to search for a run of a particular length. It
  9356. returns the position of the run being looked for. If the Lcn is specified
  9357. then the run matching the desired RunLength that is closest to Lcn is
  9358. chosen.
  9359. This routine can be used to determine the insertion position for a new
  9360. run. The returned Index will be at or adjacent to the new run's position
  9361. in the list. The caller will have to check which.
  9362. If this routine fails to find a run of the desired length, the returned
  9363. Index will either point to a deleted entry or an entry that is larger or
  9364. past the end of the array.
  9365. ENHANCEMENT - If there is no match for the desired RunLength we currently choose the
  9366. next higher size without checking for the one with the closest Lcn value.
  9367. We could change the routine to restart the loop looking explicitly for the
  9368. larger size so that the best choice in Lcn terms is returned.
  9369. Arguments:
  9370. CachedRuns - Pointer to cached run structure.
  9371. RunLength - Run length to look for.
  9372. Lcn - If specified then we try to find the run which is closest to
  9373. this Lcn, but has the requested Length. If Lcn is UNUSED_LCN, we
  9374. will end up choosing a match with the lowest Lcn as UNUSED_LCN
  9375. is < 0. This will result in maximum left-packing of the disk.
  9376. If not specified we will randomly allocate matches on the length
  9377. array.
  9378. StartIndex - Optional index where the search should begin.
  9379. SearchForward - If TRUE, the search should begin at StartIndex. If
  9380. FALSE, the search should end at StartIndex.
  9381. RunIndex - Address to store index where the desired run is or should be.
  9382. Return Value:
  9383. BOOLEAN - TRUE if we found a run with the desired RunLength,
  9384. FALSE otherwise.
  9385. --*/
  9386. {
  9387. USHORT Min, Max, Current, LcnIndex;
  9388. USHORT MinMatch, MaxMatch;
  9389. LONGLONG Distance;
  9390. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  9391. PNTFS_DELETED_RUNS DelWindow;
  9392. BOOLEAN FoundRun = FALSE;
  9393. PAGED_CODE();
  9394. DebugTrace( +1, Dbg, ("NtfsPositionCachedLcnByLength\n") );
  9395. ASSERT( UNUSED_LCN < 0 );
  9396. //
  9397. // Keep track of whether we are hitting matching length entries during the search.
  9398. //
  9399. MinMatch = MaxMatch = NTFS_CACHED_RUNS_DEL_INDEX;
  9400. //
  9401. // Binary search to find the first entry which is equal to
  9402. // or larger than the one we wanted. Bias the search with the
  9403. // user's end point if necessary.
  9404. //
  9405. Min = 0;
  9406. Max = CachedRuns->Avail;
  9407. if (ARGUMENT_PRESENT( StartIndex )) {
  9408. if (SearchForward) {
  9409. Min = *StartIndex;
  9410. } else {
  9411. Max = *StartIndex + 1;
  9412. //
  9413. // The only time this could happen is if we are trying to
  9414. // find an entry that is larger than the largest in use.
  9415. // Just use values that will terminate the search.
  9416. //
  9417. if (Max > CachedRuns->Used) {
  9418. Min = Max = CachedRuns->Used;
  9419. }
  9420. }
  9421. ASSERT( Min <= Max );
  9422. }
  9423. while (Min != Max) {
  9424. ASSERT( Min <= Max );
  9425. //
  9426. // Find the mid-index point along with the Lcn index out of
  9427. // the length array and the entry in the Lcn array.
  9428. //
  9429. Current = (USHORT) (((ULONG) Max + Min) / 2);
  9430. LcnIndex = CachedRuns->LengthArray[Current];
  9431. ThisEntry = CachedRuns->LcnArray + LcnIndex;
  9432. //
  9433. // The current entry has been deleted and must be
  9434. // ignored. Get the window of deleted entries that
  9435. // covers Current.
  9436. //
  9437. if (LcnIndex == NTFS_CACHED_RUNS_DEL_INDEX) {
  9438. DelWindow = NtfsGetDelWindow( CachedRuns,
  9439. Current,
  9440. Current,
  9441. FALSE,
  9442. NULL);
  9443. ASSERT( DelWindow );
  9444. ASSERT( DelWindow->EndIndex >= Current );
  9445. ASSERT( DelWindow->StartIndex <= Current );
  9446. //
  9447. // Go to the edges of this deleted entries window to determine
  9448. // which way we should go.
  9449. //
  9450. //
  9451. // If this window extends past the end of the used entries
  9452. // then move to the begining of it.
  9453. //
  9454. if ((DelWindow->EndIndex + 1) >= CachedRuns->Used ) {
  9455. Max = DelWindow->StartIndex;
  9456. ASSERT( Min <= Max );
  9457. //
  9458. // If this window doesn't start at index zero then determine which
  9459. // direction to go.
  9460. //
  9461. } else if (DelWindow->StartIndex > 0) {
  9462. //
  9463. // Point to the entry adjacent to the lower end of the window.
  9464. //
  9465. LcnIndex = CachedRuns->LengthArray[ DelWindow->StartIndex - 1 ];
  9466. ASSERT( LcnIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  9467. ThisEntry = CachedRuns->LcnArray + LcnIndex;
  9468. ASSERT( ThisEntry->RunLength != 0 );
  9469. //
  9470. // If this entry is longer than we asked for then the search
  9471. // should continue from the lower edge of the window.
  9472. //
  9473. if (RunLength < ThisEntry->RunLength) {
  9474. Max = DelWindow->StartIndex;
  9475. ASSERT( Min <= Max );
  9476. //
  9477. // The search should continue from the upper edge of the
  9478. // window if our run length is longer.
  9479. //
  9480. } else if (RunLength > ThisEntry->RunLength) {
  9481. Min = DelWindow->EndIndex + 1;
  9482. ASSERT( Min <= Max );
  9483. //
  9484. // We have found the desired run if our caller didn't specify
  9485. // an Lcn.
  9486. //
  9487. } else if (!ARGUMENT_PRESENT( Lcn )) {
  9488. Min = DelWindow->StartIndex - 1;
  9489. FoundRun = TRUE;
  9490. break;
  9491. //
  9492. // If our Lcn is less than the Lcn in the entry then the search
  9493. // should continue from the lower edge of the window.
  9494. //
  9495. } else if (*Lcn < ThisEntry->Lcn) {
  9496. Max = DelWindow->StartIndex;
  9497. ASSERT( Min <= Max );
  9498. //
  9499. // If the entry overlaps then we have a match. We already
  9500. // know our Lcn is >= to the start Lcn of the range from
  9501. // the test above.
  9502. //
  9503. } else if (*Lcn < (ThisEntry->Lcn + ThisEntry->RunLength)) {
  9504. Min = DelWindow->StartIndex - 1;
  9505. FoundRun = TRUE;
  9506. break;
  9507. //
  9508. // Move Min past the end of the window. We'll check later to see
  9509. // which end is closer.
  9510. //
  9511. } else {
  9512. Min = DelWindow->EndIndex + 1;
  9513. MinMatch = DelWindow->StartIndex - 1;
  9514. ASSERT( Min <= Max );
  9515. ASSERT( MinMatch != MaxMatch );
  9516. }
  9517. //
  9518. // The search should continue from the upper edge of the
  9519. // window.
  9520. //
  9521. } else {
  9522. Min = DelWindow->EndIndex + 1;
  9523. ASSERT( Min <= Max );
  9524. }
  9525. //
  9526. // Loop back now that Min or Max has been updated.
  9527. //
  9528. continue;
  9529. }
  9530. //
  9531. // If the run length of this entry is more than we want then
  9532. // move the Max value down.
  9533. //
  9534. if (RunLength < ThisEntry->RunLength) {
  9535. Max = Current;
  9536. ASSERT( Min <= Max );
  9537. //
  9538. // If the run length of this entry is less than we want then
  9539. // move the Min value up.
  9540. //
  9541. } else if (RunLength > ThisEntry->RunLength) {
  9542. Min = Current + 1;
  9543. ASSERT( Min <= Max );
  9544. //
  9545. // If our caller doesn't care about the Lcn then return this entry to
  9546. // him.
  9547. //
  9548. } else if (!ARGUMENT_PRESENT( Lcn )) {
  9549. //
  9550. // The caller doesn't care about the Lcn, or the Lcn falls in
  9551. // the current run.
  9552. //
  9553. Min = Current;
  9554. FoundRun = TRUE;
  9555. break;
  9556. //
  9557. // If the Lcn is less than the Lcn in the entry then move Max down.
  9558. //
  9559. } else if (*Lcn < ThisEntry->Lcn) {
  9560. Max = Current;
  9561. if (Current != MinMatch) {
  9562. MaxMatch = Current;
  9563. }
  9564. ASSERT( Min <= Max );
  9565. ASSERT( MinMatch != MaxMatch );
  9566. //
  9567. // If the entry overlaps then we have a match. We already
  9568. // know our Lcn is >= to the start Lcn of the range from
  9569. // the test above.
  9570. //
  9571. } else if (*Lcn < (ThisEntry->Lcn + ThisEntry->RunLength)) {
  9572. Min = Current;
  9573. FoundRun = TRUE;
  9574. break;
  9575. //
  9576. // Advance Min past the current point.
  9577. //
  9578. } else {
  9579. Min = Current + 1;
  9580. MinMatch = Current;
  9581. ASSERT( Min <= Max );
  9582. ASSERT( MinMatch != MaxMatch );
  9583. }
  9584. }
  9585. //
  9586. // If we don't have an exact match then we want to find the nearest point. We kept track
  9587. // of the nearest length matches as we went along.
  9588. //
  9589. if (!FoundRun) {
  9590. //
  9591. // We have a length match if either match entry was updated. Check for the nearest
  9592. // distance if they don't match.
  9593. //
  9594. ASSERT( (MinMatch == NTFS_CACHED_RUNS_DEL_INDEX) ||
  9595. (MinMatch != MaxMatch) );
  9596. if (MinMatch != MaxMatch) {
  9597. FoundRun = TRUE;
  9598. //
  9599. // Make sure our search found one of these.
  9600. //
  9601. ASSERT( (MinMatch == NTFS_CACHED_RUNS_DEL_INDEX) ||
  9602. (MinMatch <= Min) );
  9603. ASSERT( (MinMatch == NTFS_CACHED_RUNS_DEL_INDEX) ||
  9604. (MinMatch == Min) ||
  9605. (MinMatch == Min - 1) ||
  9606. (CachedRuns->LengthArray[ Min - 1 ] == NTFS_CACHED_RUNS_DEL_INDEX) );
  9607. ASSERT( (MaxMatch == NTFS_CACHED_RUNS_DEL_INDEX) ||
  9608. (MaxMatch >= Min) );
  9609. ASSERT( (MaxMatch == NTFS_CACHED_RUNS_DEL_INDEX) ||
  9610. (MaxMatch == Min) ||
  9611. (MaxMatch == Min + 1) ||
  9612. (CachedRuns->LengthArray[ Min + 1 ] == NTFS_CACHED_RUNS_DEL_INDEX) );
  9613. //
  9614. // If the user specified an Lcn then we need to check for the nearest entry.
  9615. //
  9616. if (ARGUMENT_PRESENT( Lcn )) {
  9617. Min = MinMatch;
  9618. if (MaxMatch != NTFS_CACHED_RUNS_DEL_INDEX) {
  9619. ThisEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ MaxMatch ];
  9620. Distance = ThisEntry->Lcn - *Lcn;
  9621. Min = MaxMatch;
  9622. if (MinMatch != NTFS_CACHED_RUNS_DEL_INDEX) {
  9623. ThisEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ MinMatch ];
  9624. if (*Lcn - (ThisEntry->Lcn + RunLength) < Distance) {
  9625. Min = MinMatch;
  9626. }
  9627. }
  9628. }
  9629. }
  9630. }
  9631. }
  9632. *RunIndex = Min;
  9633. #ifdef NTFS_CHECK_CACHED_RUNS
  9634. if (FoundRun) {
  9635. LcnIndex = CachedRuns->LengthArray[ Min ];
  9636. ASSERT( LcnIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  9637. ThisEntry = CachedRuns->LcnArray + LcnIndex;
  9638. ASSERT( RunLength == ThisEntry->RunLength );
  9639. }
  9640. #endif
  9641. DebugTrace( 0, Dbg, ("*RunIndex = %04x\n", *RunIndex) );
  9642. DebugTrace( -1, Dbg, ("NtfsPositionCachedLcnByLength -> %01x\n", FoundRun) );
  9643. return FoundRun;
  9644. }
  9645. #ifdef NTFS_CHECK_CACHED_RUNS
  9646. //
  9647. // Local support routine
  9648. //
  9649. VOID
  9650. NtfsVerifyCachedLcnRuns (
  9651. IN PNTFS_CACHED_RUNS CachedRuns,
  9652. IN USHORT FirstIndex,
  9653. IN USHORT LastIndex,
  9654. IN BOOLEAN SkipSortCheck,
  9655. IN BOOLEAN SkipBinCheck
  9656. )
  9657. /*++
  9658. Routine Description:
  9659. This routine is called to verify the state of the cached runs arrays.
  9660. Arguments:
  9661. CachedRuns - Pointer to the cached runs structure
  9662. FirstIndex - Index that marks the start of the newest range of deleted
  9663. entries. This new range will not be in a deleted window yet.
  9664. LastIndex - The index of the last entry in the newest range of deleted
  9665. entries. This new range will not be in a deleted window yet.
  9666. SkipSortCheck - If TRUE, the list may be out of order at this time and
  9667. we should skip the checks for overlapping ranges or length sorts.
  9668. SkipBinCheck - If TRUE, the BinArray may be out of sync and should not
  9669. be checked.
  9670. Return Value:
  9671. None
  9672. --*/
  9673. {
  9674. USHORT Index;
  9675. USHORT BinArray[ NTFS_CACHED_RUNS_BIN_COUNT ];
  9676. USHORT LcnWindowIndex = 0;
  9677. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  9678. PNTFS_LCN_CLUSTER_RUN LastEntry = NULL;
  9679. PNTFS_DELETED_RUNS LcnDelWindow = NULL;
  9680. PNTFS_DELETED_RUNS NextWindow;
  9681. PAGED_CODE();
  9682. DebugTrace( +1, Dbg, ("NtfsVerifyCachedLcnRuns\n") );
  9683. ASSERT( CachedRuns->Used <= CachedRuns->Avail );
  9684. //
  9685. // Initialize the tracking variables.
  9686. //
  9687. RtlZeroMemory( BinArray, NTFS_CACHED_RUNS_BIN_COUNT * sizeof( USHORT ));
  9688. if (CachedRuns->DelLcnCount != 0) {
  9689. LcnDelWindow = CachedRuns->DeletedLcnWindows;
  9690. }
  9691. ASSERT( CachedRuns->DelLcnCount <= NTFS_CACHED_RUNS_MAX_DEL_WINDOWS );
  9692. //
  9693. // Verify that every element in the Lcn-sorted list is correctly
  9694. // ordered. If it's RunLength is 0, make certain its index is
  9695. // recorded in a window of deleted entries. If its LengthIndex is
  9696. // not NTFS_CACHED_RUNS_DEL_INDEX, make sure it refers to an entry in
  9697. // the length-sorted list that refers back to it and is in a window of
  9698. // deleted entries if and only if RunLength is 0.
  9699. //
  9700. for (Index = 0, ThisEntry = CachedRuns->LcnArray;
  9701. Index < CachedRuns->Avail;
  9702. Index += 1, ThisEntry += 1) {
  9703. //
  9704. // This entry is not deleted.
  9705. //
  9706. if (ThisEntry->RunLength != 0) {
  9707. //
  9708. // Better be in the used region with valid indexes.
  9709. //
  9710. ASSERT( Index < CachedRuns->Used );
  9711. ASSERT( ThisEntry->LengthIndex != NTFS_CACHED_RUNS_DEL_INDEX );
  9712. ASSERT( ThisEntry->LengthIndex < CachedRuns->Used );
  9713. ASSERT( ThisEntry->Lcn != UNUSED_LCN );
  9714. //
  9715. // Verify that the entry is not in the current window of deleted
  9716. // entries.
  9717. //
  9718. ASSERT( (LcnDelWindow == NULL) ||
  9719. (LcnDelWindow->StartIndex > Index) );
  9720. //
  9721. // Verify the sort order.
  9722. //
  9723. ASSERT( (LastEntry == NULL) ||
  9724. SkipSortCheck ||
  9725. (ThisEntry->Lcn > (LastEntry->Lcn + LastEntry->RunLength)) );
  9726. LastEntry = ThisEntry;
  9727. //
  9728. // Make certain that the corresponding entry in the Length-sorted
  9729. // list points back to this entry.
  9730. //
  9731. ASSERT( CachedRuns->LengthArray[ ThisEntry->LengthIndex ] == Index );
  9732. //
  9733. // Keep track of how many entries have this length.
  9734. //
  9735. if (ThisEntry->RunLength <= CachedRuns->Bins) {
  9736. BinArray[ ThisEntry->RunLength - 1 ] += 1;
  9737. }
  9738. //
  9739. // This is a deleted entry. Make sure it is in the deleted window array.
  9740. //
  9741. } else {
  9742. ASSERT( ThisEntry->LengthIndex == NTFS_CACHED_RUNS_DEL_INDEX );
  9743. //
  9744. // Verify that the entry is in the current window of deleted
  9745. // entries unless we have excluded this entry.
  9746. //
  9747. if ((FirstIndex != NTFS_CACHED_RUNS_DEL_INDEX) &&
  9748. (LastIndex != NTFS_CACHED_RUNS_DEL_INDEX) &&
  9749. ((FirstIndex > Index) ||
  9750. (LastIndex < Index))) {
  9751. ASSERT( (LcnDelWindow != NULL) &&
  9752. (LcnDelWindow->StartIndex <= Index) &&
  9753. (LcnDelWindow->EndIndex >= Index) );
  9754. }
  9755. //
  9756. // Advance the window of deleted entries if we are at the end.
  9757. //
  9758. if ((LcnDelWindow != NULL) && (LcnDelWindow->EndIndex == Index)) {
  9759. LcnWindowIndex += 1;
  9760. if (LcnWindowIndex < CachedRuns->DelLcnCount) {
  9761. LcnDelWindow += 1;
  9762. } else {
  9763. LcnDelWindow = NULL;
  9764. }
  9765. }
  9766. }
  9767. }
  9768. //
  9769. // We should have walked past all of the deleted entries.
  9770. //
  9771. //
  9772. // Make certain that the windows are in order and don't overlap.
  9773. //
  9774. for (LcnWindowIndex = 0, LcnDelWindow = NextWindow = CachedRuns->DeletedLcnWindows;
  9775. LcnWindowIndex < CachedRuns->DelLcnCount;
  9776. LcnWindowIndex += 1, NextWindow += 1) {
  9777. ASSERT( NextWindow->StartIndex <= NextWindow->EndIndex );
  9778. if (NextWindow != LcnDelWindow) {
  9779. ASSERT( NextWindow->StartIndex > (LcnDelWindow->EndIndex + 1) );
  9780. LcnDelWindow += 1;
  9781. }
  9782. }
  9783. //
  9784. // Verify that the histogram of RunLengths is correct.
  9785. //
  9786. for (Index = 0;
  9787. Index < NTFS_CACHED_RUNS_BIN_COUNT;
  9788. Index += 1) {
  9789. ASSERT( SkipBinCheck || (BinArray[ Index ] == CachedRuns->BinArray[ Index ]) );
  9790. }
  9791. DebugTrace( -1, Dbg, ("NtfsVerifyCachedLcnRuns -> VOID\n") );
  9792. return;
  9793. }
  9794. //
  9795. // Local support routine
  9796. //
  9797. VOID
  9798. NtfsVerifyCachedLenRuns (
  9799. IN PNTFS_CACHED_RUNS CachedRuns,
  9800. IN USHORT FirstIndex,
  9801. IN USHORT LastIndex,
  9802. IN BOOLEAN SkipSortCheck
  9803. )
  9804. /*++
  9805. Routine Description:
  9806. This routine is called to verify the state of the cached runs arrays.
  9807. Arguments:
  9808. CachedRuns - Pointer to the cached runs structure
  9809. FirstIndex - Index that marks the start of the newest range of deleted
  9810. entries. This new range will not be in a deleted window yet.
  9811. LastIndex - The index of the last entry in the newest range of deleted
  9812. entries. This new range will not be in a deleted window yet.
  9813. SkipSortCheck - If TRUE, the list may be out of order at this time and
  9814. we should skip the checks for overlapping ranges or length sorts.
  9815. Return Value:
  9816. None
  9817. --*/
  9818. {
  9819. USHORT Index;
  9820. USHORT LenWindowIndex = 0;
  9821. PNTFS_LCN_CLUSTER_RUN ThisEntry;
  9822. PNTFS_LCN_CLUSTER_RUN LastEntry = NULL;
  9823. PNTFS_DELETED_RUNS LenDelWindow = NULL;
  9824. PNTFS_DELETED_RUNS NextWindow;
  9825. PAGED_CODE();
  9826. DebugTrace( +1, Dbg, ("NtfsVerifyCachedLenRuns\n") );
  9827. ASSERT( CachedRuns->Used <= CachedRuns->Avail );
  9828. //
  9829. // Initialize the tracking variables.
  9830. //
  9831. if (CachedRuns->DelLengthCount != 0) {
  9832. LenDelWindow = CachedRuns->DeletedLengthWindows;
  9833. }
  9834. ASSERT( CachedRuns->DelLengthCount <= NTFS_CACHED_RUNS_MAX_DEL_WINDOWS );
  9835. //
  9836. // Verify that every element in the Length-sorted list is correctly
  9837. // ordered. If it's index is NTFS_CACHED_RUNS_DEL_INDEX, make certain
  9838. // its index is recorded in a window of deleted entries. Otherwise,
  9839. // make certain that its Index refers to an entry in the lcn-sorted list
  9840. // that refers back to it.
  9841. //
  9842. for (Index = 0; Index < CachedRuns->Avail; Index += 1) {
  9843. //
  9844. // Verify any entry not in a deleted window.
  9845. //
  9846. if (CachedRuns->LengthArray[ Index ] != NTFS_CACHED_RUNS_DEL_INDEX) {
  9847. ASSERT( Index < CachedRuns->Used );
  9848. ASSERT( CachedRuns->LengthArray[ Index ] < CachedRuns->Used );
  9849. ThisEntry = CachedRuns->LcnArray + CachedRuns->LengthArray[ Index ];
  9850. //
  9851. // Verify that the corresponding Lcn-sorted entry is not deleted.
  9852. //
  9853. ASSERT( ThisEntry->RunLength != 0 );
  9854. //
  9855. // Verify that the entry is not in the current window of deleted
  9856. // entries.
  9857. //
  9858. ASSERT( (LenDelWindow == NULL) ||
  9859. (LenDelWindow->StartIndex > Index) );
  9860. //
  9861. // Verify the sort order if we have the previous entry.
  9862. //
  9863. ASSERT( (LastEntry == NULL) ||
  9864. SkipSortCheck ||
  9865. (LastEntry->RunLength < ThisEntry->RunLength) ||
  9866. ((LastEntry->RunLength == ThisEntry->RunLength) &&
  9867. (ThisEntry->Lcn > (LastEntry->Lcn + LastEntry->RunLength))) );
  9868. LastEntry = ThisEntry;
  9869. //
  9870. // Make certain that the corresponding entry in the Lcn-sorted
  9871. // list points back to this entry.
  9872. //
  9873. ASSERT( ThisEntry->LengthIndex == Index );
  9874. //
  9875. // The entry is deleted.
  9876. //
  9877. } else {
  9878. //
  9879. // Verify that the entry is in the current window of deleted
  9880. // entries unless we have excluded this entry.
  9881. //
  9882. if ((FirstIndex != NTFS_CACHED_RUNS_DEL_INDEX) &&
  9883. (LastIndex != NTFS_CACHED_RUNS_DEL_INDEX) &&
  9884. ((FirstIndex > Index) ||
  9885. (LastIndex < Index))) {
  9886. //
  9887. // Verify that the entry is in the current window of deleted
  9888. // entries.
  9889. //
  9890. ASSERT( (LenDelWindow != NULL) &&
  9891. (LenDelWindow->StartIndex <= Index) &&
  9892. (LenDelWindow->EndIndex >= Index) );
  9893. }
  9894. }
  9895. //
  9896. // Advance the window of deleted entries if we are at the end.
  9897. //
  9898. if ((LenDelWindow != NULL) && (LenDelWindow->EndIndex == Index)) {
  9899. LenWindowIndex += 1;
  9900. if (LenWindowIndex < CachedRuns->DelLengthCount) {
  9901. LenDelWindow += 1;
  9902. } else {
  9903. LenDelWindow = NULL;
  9904. }
  9905. }
  9906. }
  9907. //
  9908. // We should have walked past all of the deleted entries.
  9909. //
  9910. ASSERT( LenDelWindow == NULL );
  9911. //
  9912. // Make certain that the windows are in order and don't overlap.
  9913. //
  9914. for (LenWindowIndex = 0, LenDelWindow = NextWindow = CachedRuns->DeletedLengthWindows;
  9915. LenWindowIndex < CachedRuns->DelLengthCount;
  9916. LenWindowIndex += 1, NextWindow += 1) {
  9917. ASSERT( NextWindow->StartIndex <= NextWindow->EndIndex );
  9918. if (NextWindow != LenDelWindow) {
  9919. ASSERT( NextWindow->StartIndex > (LenDelWindow->EndIndex + 1) );
  9920. LenDelWindow += 1;
  9921. }
  9922. }
  9923. DebugTrace( -1, Dbg, ("NtfsVerifyCachedLenRuns -> VOID\n") );
  9924. return;
  9925. }
  9926. //
  9927. // Local support routine
  9928. //
  9929. VOID
  9930. NtfsVerifyCachedRuns (
  9931. IN PNTFS_CACHED_RUNS CachedRuns,
  9932. IN BOOLEAN SkipSortCheck,
  9933. IN BOOLEAN SkipBinCheck
  9934. )
  9935. /*++
  9936. Routine Description:
  9937. This routine is called to verify the state of the cached runs arrays.
  9938. Arguments:
  9939. CachedRuns - Pointer to the cached runs structure
  9940. SkipSortCheck - If TRUE, the list may be out of order at this time and
  9941. we should skip the checks for overlapping ranges or length sorts.
  9942. SkipBinCheck - If TRUE, the BinArray may be out of sync and should not
  9943. be checked.
  9944. Return Value:
  9945. None
  9946. --*/
  9947. {
  9948. PAGED_CODE();
  9949. DebugTrace( +1, Dbg, ("NtfsVerifyCachedRuns\n") );
  9950. NtfsVerifyCachedLcnRuns ( CachedRuns,
  9951. NTFS_CACHED_RUNS_DEL_INDEX,
  9952. NTFS_CACHED_RUNS_DEL_INDEX,
  9953. SkipSortCheck,
  9954. SkipBinCheck );
  9955. NtfsVerifyCachedLenRuns ( CachedRuns,
  9956. NTFS_CACHED_RUNS_DEL_INDEX,
  9957. NTFS_CACHED_RUNS_DEL_INDEX,
  9958. SkipSortCheck );
  9959. DebugTrace( -1, Dbg, ("NtfsVerifyCachedRuns -> VOID\n") );
  9960. return;
  9961. }
  9962. #endif