Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5249 lines
156 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. AllocSup.c
  5. Abstract:
  6. This module implements the general file stream allocation & truncation
  7. routines for Ntfs
  8. Author:
  9. Tom Miller [TomM] 15-Jul-1991
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // Local debug trace level
  15. //
  16. #define Dbg (DEBUG_TRACE_ALLOCSUP)
  17. //
  18. // Define a tag for general pool allocations from this module
  19. //
  20. #undef MODULE_POOL_TAG
  21. #define MODULE_POOL_TAG ('aFtN')
  22. ULONG NtfsExtendFactor = 4;
  23. //
  24. // Internal support routines
  25. //
  26. VOID
  27. NtfsDeleteAllocationInternal (
  28. IN PIRP_CONTEXT IrpContext,
  29. IN OUT PSCB Scb,
  30. IN VCN StartingVcn,
  31. IN VCN EndingVcn,
  32. IN BOOLEAN LogIt
  33. );
  34. #ifdef ALLOC_PRAGMA
  35. #pragma alloc_text(PAGE, NtfsPreloadAllocation)
  36. #pragma alloc_text(PAGE, NtfsAddAllocation)
  37. #pragma alloc_text(PAGE, NtfsAddSparseAllocation)
  38. #pragma alloc_text(PAGE, NtfsAllocateAttribute)
  39. #pragma alloc_text(PAGE, NtfsBuildMappingPairs)
  40. #pragma alloc_text(PAGE, NtfsCheckForReservedClusters)
  41. #pragma alloc_text(PAGE, NtfsDeleteAllocation)
  42. #pragma alloc_text(PAGE, NtfsDeleteAllocationInternal)
  43. #pragma alloc_text(PAGE, NtfsDeleteReservedBitmap)
  44. #pragma alloc_text(PAGE, NtfsGetHighestVcn)
  45. #pragma alloc_text(PAGE, NtfsGetSizeForMappingPairs)
  46. #pragma alloc_text(PAGE, NtfsIsRangeAllocated)
  47. #pragma alloc_text(PAGE, NtfsReallocateRange)
  48. #endif
  49. ULONG
  50. NtfsPreloadAllocation (
  51. IN PIRP_CONTEXT IrpContext,
  52. IN OUT PSCB Scb,
  53. IN VCN StartingVcn,
  54. IN VCN EndingVcn
  55. )
  56. /*++
  57. Routine Description:
  58. This routine assures that all ranges of the Mcb are loaded in the specified
  59. Vcn range
  60. Arguments:
  61. Scb - Specifies which Scb is to be preloaded
  62. StartingVcn - Specifies the first Vcn to be loaded
  63. EndingVcn - Specifies the last Vcn to be loaded
  64. Return Value:
  65. Number of ranges spanned by the load request.
  66. --*/
  67. {
  68. VCN CurrentVcn, LastCurrentVcn;
  69. LCN Lcn;
  70. LONGLONG Count;
  71. PVOID RangePtr;
  72. ULONG RunIndex;
  73. ULONG RangesLoaded = 0;
  74. PAGED_CODE();
  75. //
  76. // Start with starting Vcn
  77. //
  78. CurrentVcn = StartingVcn;
  79. //
  80. // Always load the nonpaged guys from the front, so we don't
  81. // produce an Mcb with a "known hole".
  82. //
  83. if (FlagOn(Scb->Fcb->FcbState, FCB_STATE_NONPAGED)) {
  84. CurrentVcn = 0;
  85. }
  86. //
  87. // Loop until it's all loaded.
  88. //
  89. while (CurrentVcn <= EndingVcn) {
  90. //
  91. // Remember this CurrentVcn as a way to know when we have hit the end
  92. // (stopped making progress).
  93. //
  94. LastCurrentVcn = CurrentVcn;
  95. //
  96. // Load range with CurrentVcn, and if it is not there, get out.
  97. //
  98. (VOID)NtfsLookupAllocation( IrpContext, Scb, CurrentVcn, &Lcn, &Count, &RangePtr, &RunIndex );
  99. //
  100. // If preloading the mft flush and purge it afterwards. This is to
  101. // remove any partial pages we generated above if any mft record for
  102. // the mft described others records in the same page after it
  103. //
  104. if (FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_PRELOAD_MFT )) {
  105. IO_STATUS_BLOCK IoStatus;
  106. CcFlushCache( &Scb->NonpagedScb->SegmentObject,
  107. NULL,
  108. 0,
  109. &IoStatus );
  110. if (!NT_SUCCESS( IoStatus.Status )) {
  111. NtfsNormalizeAndRaiseStatus( IrpContext,
  112. IoStatus.Status,
  113. STATUS_UNEXPECTED_IO_ERROR );
  114. }
  115. CcPurgeCacheSection( &Scb->NonpagedScb->SegmentObject,
  116. (PLARGE_INTEGER)NULL,
  117. 0,
  118. FALSE );
  119. }
  120. //
  121. // Find out how many runs there are in this range
  122. //
  123. if (!NtfsNumberOfRunsInRange(&Scb->Mcb, RangePtr, &RunIndex) || (RunIndex == 0)) {
  124. break;
  125. }
  126. //
  127. // Get the highest run in this range and calculate the next Vcn beyond this range.
  128. //
  129. NtfsGetNextNtfsMcbEntry( &Scb->Mcb, &RangePtr, RunIndex - 1, &CurrentVcn, &Lcn, &Count );
  130. CurrentVcn += Count;
  131. //
  132. // If we are making no progress, we must have hit the end of the allocation,
  133. // and we are done.
  134. //
  135. if (CurrentVcn == LastCurrentVcn) {
  136. break;
  137. }
  138. RangesLoaded += 1;
  139. }
  140. return RangesLoaded;
  141. }
  142. BOOLEAN
  143. NtfsLookupAllocation (
  144. IN PIRP_CONTEXT IrpContext,
  145. IN OUT PSCB Scb,
  146. IN VCN Vcn,
  147. OUT PLCN Lcn,
  148. OUT PLONGLONG ClusterCount,
  149. OUT PVOID *RangePtr OPTIONAL,
  150. OUT PULONG RunIndex OPTIONAL
  151. )
  152. /*++
  153. Routine Description:
  154. This routine looks up the given Vcn for an Scb, and returns whether it
  155. is allocated and how many contiguously allocated (or deallocated) Lcns
  156. exist at that point.
  157. Arguments:
  158. Scb - Specifies which attribute the lookup is to occur on.
  159. Vcn - Specifies the Vcn to be looked up.
  160. Lcn - If returning TRUE, returns the Lcn that the specified Vcn is mapped
  161. to. If returning FALSE, the return value is undefined.
  162. ClusterCount - If returning TRUE, returns the number of contiguously allocated
  163. Lcns exist beginning at the Lcn returned. If returning FALSE,
  164. specifies the number of unallocated Vcns exist beginning with
  165. the specified Vcn.
  166. RangePtr - If specified, we return the range index for the start of the mapping.
  167. RunIndex - If specified, we return the run index within the range for the start of the mapping.
  168. Return Value:
  169. BOOLEAN - TRUE if the input Vcn has a corresponding Lcn and
  170. FALSE otherwise.
  171. --*/
  172. {
  173. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  174. PATTRIBUTE_RECORD_HEADER Attribute;
  175. VCN HighestCandidate;
  176. BOOLEAN Found;
  177. BOOLEAN EntryAdded;
  178. VCN CapturedLowestVcn;
  179. VCN CapturedHighestVcn;
  180. PVCB Vcb = Scb->Fcb->Vcb;
  181. BOOLEAN McbMutexAcquired = FALSE;
  182. LONGLONG AllocationClusters;
  183. BOOLEAN MountInProgress;
  184. ASSERT_IRP_CONTEXT( IrpContext );
  185. ASSERT_SCB( Scb );
  186. DebugTrace( +1, Dbg, ("NtfsLookupAllocation\n") );
  187. DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) );
  188. DebugTrace( 0, Dbg, ("Vcn = %I64x\n", Vcn) );
  189. MountInProgress = ((IrpContext->TopLevelIrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
  190. (IrpContext->TopLevelIrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME));
  191. //
  192. // First try to look up the allocation in the mcb, and return the run
  193. // from there if we can. Also, if we are doing restart, just return
  194. // the answer straight from the Mcb, because we cannot read the disk.
  195. // We also do this for the Mft if the volume has been mounted as the
  196. // Mcb for the Mft should always represent the entire file.
  197. //
  198. HighestCandidate = MAXLONGLONG;
  199. if ((Found = NtfsLookupNtfsMcbEntry( &Scb->Mcb, Vcn, Lcn, ClusterCount, NULL, NULL, RangePtr, RunIndex ))
  200. ||
  201. (Scb == Vcb->MftScb
  202. &&
  203. ((!MountInProgress) ||
  204. //
  205. // we will not try to load the mft hole during mount while preloading in any
  206. // recursive faults
  207. //
  208. (FlagOn( Vcb->VcbState, VCB_STATE_PRELOAD_MFT) &&
  209. (!NtfsIsTopLevelNtfs( IrpContext )))))
  210. ||
  211. FlagOn( Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS )) {
  212. //
  213. // If not found (beyond the end of the Mcb), we will return the
  214. // count to the largest representable Lcn.
  215. //
  216. if ( !Found ) {
  217. *ClusterCount = MAXLONGLONG - Vcn;
  218. //
  219. // Test if we found a hole in the allocation. In this case
  220. // Found will be TRUE and the Lcn will be the UNUSED_LCN.
  221. // We only expect this case at restart.
  222. //
  223. } else if (*Lcn == UNUSED_LCN) {
  224. //
  225. // If the Mcb package returned UNUSED_LCN, because of a hole, then
  226. // we turn this into FALSE.
  227. //
  228. Found = FALSE;
  229. }
  230. ASSERT( !Found ||
  231. (*Lcn != 0) ||
  232. (NtfsEqualMftRef( &Scb->Fcb->FileReference, &BootFileReference )) ||
  233. (NtfsEqualMftRef( &Scb->Fcb->FileReference, &VolumeFileReference )));
  234. DebugTrace( -1, Dbg, ("NtfsLookupAllocation -> %02lx\n", Found) );
  235. return Found;
  236. }
  237. PAGED_CODE();
  238. //
  239. // Prepare for looking up attribute records to get the retrieval
  240. // information.
  241. //
  242. CapturedLowestVcn = MAXLONGLONG;
  243. NtfsInitializeAttributeContext( &Context );
  244. //
  245. // Make sure we have the main resource acquired shared so that the
  246. // attributes in the file record are not moving around. We blindly
  247. // use Wait = TRUE. Most of the time when we go to the disk for I/O
  248. // (and thus need mapping) we are synchronous, and otherwise, the Mcb
  249. // is virtually always loaded anyway and we do not get here.
  250. //
  251. NtfsAcquireResourceShared( IrpContext, Scb, TRUE );
  252. try {
  253. //
  254. // Lookup the attribute record for this Scb.
  255. //
  256. NtfsLookupAttributeForScb( IrpContext, Scb, &Vcn, &Context );
  257. Attribute = NtfsFoundAttribute( &Context );
  258. ASSERT( !NtfsIsAttributeResident(Attribute) );
  259. if (FlagOn( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
  260. AllocationClusters = LlClustersFromBytesTruncate( Vcb, Scb->Header.AllocationSize.QuadPart );
  261. } else {
  262. ASSERT( Attribute->Form.Nonresident.LowestVcn == 0);
  263. AllocationClusters = LlClustersFromBytesTruncate( Vcb, Attribute->Form.Nonresident.AllocatedLength );
  264. }
  265. //
  266. // The desired Vcn is not currently in the Mcb. We will loop to lookup all
  267. // the allocation, and we need to make sure we cleanup on the way out.
  268. //
  269. // It is important to note that if we ever optimize this lookup to do random
  270. // access to the mapping pairs, rather than sequentially loading up the Mcb
  271. // until we get the Vcn he asked for, then NtfsDeleteAllocation will have to
  272. // be changed.
  273. //
  274. //
  275. // Acquire exclusive access to the mcb to keep others from looking at
  276. // it while it is not fully loaded. Otherwise they might see a hole
  277. // while we're still filling up the mcb
  278. //
  279. if (!FlagOn(Scb->Fcb->FcbState, FCB_STATE_NONPAGED)) {
  280. NtfsAcquireNtfsMcbMutex( &Scb->Mcb );
  281. McbMutexAcquired = TRUE;
  282. }
  283. //
  284. // Store run information in the Mcb until we hit the last Vcn we are
  285. // interested in, or until we cannot find any more attribute records.
  286. //
  287. while(TRUE) {
  288. VCN CurrentVcn;
  289. LCN CurrentLcn;
  290. LONGLONG Change;
  291. PCHAR ch;
  292. ULONG VcnBytes;
  293. ULONG LcnBytes;
  294. //
  295. // If we raise here either there is some discrepancy between memory
  296. // structures and on disk values or the on-disk value is completely corrupted
  297. //
  298. // We Check:
  299. // 1) Verify the highest and lowest Vcn values on disk are valid.
  300. // 2) our starting Vcn sits within this range.
  301. // 3) the on-disk allocation matches the in memory value in the Scb
  302. //
  303. if ((Attribute->Form.Nonresident.LowestVcn < 0) ||
  304. (Attribute->Form.Nonresident.LowestVcn - 1 > Attribute->Form.Nonresident.HighestVcn) ||
  305. (Vcn < Attribute->Form.Nonresident.LowestVcn) ||
  306. (Attribute->Form.Nonresident.HighestVcn >= AllocationClusters)) {
  307. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  308. }
  309. //
  310. // Define the new range.
  311. //
  312. NtfsDefineNtfsMcbRange( &Scb->Mcb,
  313. CapturedLowestVcn = Attribute->Form.Nonresident.LowestVcn,
  314. CapturedHighestVcn = Attribute->Form.Nonresident.HighestVcn,
  315. McbMutexAcquired );
  316. //
  317. // Implement the decompression algorithm, as defined in ntfs.h.
  318. //
  319. HighestCandidate = Attribute->Form.Nonresident.LowestVcn;
  320. CurrentLcn = 0;
  321. ch = (PCHAR)Attribute + Attribute->Form.Nonresident.MappingPairsOffset;
  322. //
  323. // Loop to process mapping pairs.
  324. //
  325. EntryAdded = FALSE;
  326. while (!IsCharZero(*ch)) {
  327. //
  328. // Set Current Vcn from initial value or last pass through loop.
  329. //
  330. CurrentVcn = HighestCandidate;
  331. //
  332. // VCNs should never be negative.
  333. //
  334. if (CurrentVcn < 0) {
  335. ASSERT( FALSE );
  336. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  337. }
  338. //
  339. // Extract the counts from the two nibbles of this byte.
  340. //
  341. VcnBytes = *ch & 0xF;
  342. LcnBytes = *ch++ >> 4;
  343. //
  344. // Extract the Vcn change (use of RtlCopyMemory works for little-Endian)
  345. // and update HighestCandidate.
  346. //
  347. Change = 0;
  348. //
  349. // The file is corrupt if there are 0 or more than 8 Vcn change bytes,
  350. // more than 8 Lcn change bytes, or if we would walk off the end of
  351. // the record, or a Vcn change is negative.
  352. //
  353. if (((ULONG)(VcnBytes - 1) > 7) || (LcnBytes > 8) ||
  354. ((ch + VcnBytes + LcnBytes + 1) > (PCHAR)Add2Ptr(Attribute, Attribute->RecordLength)) ||
  355. IsCharLtrZero(*(ch + VcnBytes - 1))) {
  356. ASSERT( FALSE );
  357. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb );
  358. }
  359. RtlCopyMemory( &Change, ch, VcnBytes );
  360. ch += VcnBytes;
  361. HighestCandidate = HighestCandidate + Change;
  362. //
  363. // Extract the Lcn change and update CurrentLcn.
  364. //
  365. if (LcnBytes != 0) {
  366. Change = 0;
  367. if (IsCharLtrZero(*(ch + LcnBytes - 1))) {
  368. Change = Change - 1;
  369. }
  370. RtlCopyMemory( &Change, ch, LcnBytes );
  371. ch += LcnBytes;
  372. CurrentLcn = CurrentLcn + Change;
  373. //
  374. // Now add it in to the mcb.
  375. //
  376. if ((CurrentLcn >= 0) && (LcnBytes != 0)) {
  377. LONGLONG ClustersToAdd;
  378. ClustersToAdd = HighestCandidate - CurrentVcn;
  379. //
  380. // Now try to add the current run. We never expect this
  381. // call to return false.
  382. //
  383. ASSERT( ((ULONG)CurrentLcn) != 0xffffffff );
  384. #ifdef NTFS_CHECK_BITMAP
  385. //
  386. // Make sure these bits are allocated in our copy of the bitmap.
  387. //
  388. if ((Vcb->BitmapCopy != NULL) &&
  389. !NtfsCheckBitmap( Vcb,
  390. (ULONG) CurrentLcn,
  391. (ULONG) ClustersToAdd,
  392. TRUE )) {
  393. NtfsBadBitmapCopy( IrpContext, (ULONG) CurrentLcn, (ULONG) ClustersToAdd );
  394. }
  395. #endif
  396. if (!NtfsAddNtfsMcbEntry( &Scb->Mcb,
  397. CurrentVcn,
  398. CurrentLcn,
  399. ClustersToAdd,
  400. McbMutexAcquired )) {
  401. ASSERTMSG( "Unable to add entry to Mcb\n", FALSE );
  402. NtfsRaiseStatus( IrpContext,
  403. STATUS_FILE_CORRUPT_ERROR,
  404. NULL,
  405. Scb->Fcb );
  406. }
  407. EntryAdded = TRUE;
  408. }
  409. }
  410. }
  411. //
  412. // Make sure that at least the Mcb gets loaded.
  413. //
  414. if (!EntryAdded) {
  415. NtfsAddNtfsMcbEntry( &Scb->Mcb,
  416. CapturedLowestVcn,
  417. UNUSED_LCN,
  418. 1,
  419. McbMutexAcquired );
  420. }
  421. if ((Vcn < HighestCandidate) ||
  422. (!NtfsLookupNextAttributeForScb( IrpContext, Scb, &Context ))) {
  423. break;
  424. } else {
  425. Attribute = NtfsFoundAttribute( &Context );
  426. ASSERT( !NtfsIsAttributeResident(Attribute) );
  427. }
  428. }
  429. //
  430. // Now free the mutex and lookup in the Mcb while we still own
  431. // the resource.
  432. //
  433. if (McbMutexAcquired) {
  434. NtfsReleaseNtfsMcbMutex( &Scb->Mcb );
  435. McbMutexAcquired = FALSE;
  436. }
  437. if (NtfsLookupNtfsMcbEntry( &Scb->Mcb, Vcn, Lcn, ClusterCount, NULL, NULL, RangePtr, RunIndex )) {
  438. Found = (BOOLEAN)(*Lcn != UNUSED_LCN);
  439. if (Found) { ASSERT_LCN_RANGE_CHECKING( Vcb, (*Lcn + *ClusterCount) ); }
  440. } else {
  441. Found = FALSE;
  442. //
  443. // At the end of file, we pretend there is one large hole!
  444. //
  445. if (HighestCandidate >=
  446. LlClustersFromBytes(Vcb, Scb->Header.AllocationSize.QuadPart)) {
  447. HighestCandidate = MAXLONGLONG;
  448. }
  449. //
  450. // If we're asked to lookup a vcn but the highest vcn we found on disk
  451. // is less than it - than the file record is corrupt - even if there is
  452. // a hole at the end it must be written in the mapping pairs on disk
  453. //
  454. if (HighestCandidate <= Vcn) {
  455. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, &Scb->Fcb->FileReference, Scb->Fcb );
  456. }
  457. *ClusterCount = HighestCandidate - Vcn;
  458. }
  459. } finally {
  460. DebugUnwind( NtfsLookupAllocation );
  461. //
  462. // If this is an error case then we better unload what we've just
  463. // loaded
  464. //
  465. if (AbnormalTermination() &&
  466. (CapturedLowestVcn != MAXLONGLONG) ) {
  467. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  468. CapturedLowestVcn,
  469. CapturedHighestVcn,
  470. FALSE,
  471. McbMutexAcquired );
  472. }
  473. //
  474. // In all cases we free up the mcb that we locked before entering
  475. // the try statement
  476. //
  477. if (McbMutexAcquired) {
  478. NtfsReleaseNtfsMcbMutex( &Scb->Mcb );
  479. }
  480. NtfsReleaseResource( IrpContext, Scb );
  481. //
  482. // Cleanup the attribute context on the way out.
  483. //
  484. NtfsCleanupAttributeContext( IrpContext, &Context );
  485. }
  486. ASSERT( !Found ||
  487. (*Lcn != 0) ||
  488. (NtfsEqualMftRef( &Scb->Fcb->FileReference, &BootFileReference )) ||
  489. (NtfsEqualMftRef( &Scb->Fcb->FileReference, &VolumeFileReference )));
  490. DebugTrace( 0, Dbg, ("Lcn < %0I64x\n", *Lcn) );
  491. DebugTrace( 0, Dbg, ("ClusterCount < %0I64x\n", *ClusterCount) );
  492. DebugTrace( -1, Dbg, ("NtfsLookupAllocation -> %02lx\n", Found) );
  493. return Found;
  494. }
  495. BOOLEAN
  496. NtfsIsRangeAllocated (
  497. IN PSCB Scb,
  498. IN VCN StartVcn,
  499. IN VCN FinalCluster,
  500. IN BOOLEAN RoundToSparseUnit,
  501. OUT PLONGLONG ClusterCount
  502. )
  503. /*++
  504. Routine Description:
  505. This routine is called on a sparse file to test the status of a range of the
  506. file. Ntfs will return whether the range is allocated and also a known value
  507. for the length of the allocation. It is possible that the range extends
  508. beyond this point but another call needs to be made to check it.
  509. Our caller needs to verify that the Mcb is loaded in this range i.e precall NtfsPreLoadAllocation
  510. Arguments:
  511. Scb - Scb for the file to check. This should be a sparse file.
  512. StartVcn - Vcn within the range to check first.
  513. FinalCluster - Trim the clusters found so we don't go beyond this point.
  514. RoundToSparseUnit - If TRUE the range is rounded up to VCB->SparseFileUnit == 0x10000
  515. so you may get a range returned as allocated which contain
  516. a partial sparse area depending on the compression unit.
  517. ClusterCount - Address to store the count of clusters of a known state.
  518. Return Value:
  519. BOOLEAN - TRUE if the range is allocated, FALSE otherwise.
  520. --*/
  521. {
  522. BOOLEAN AllocatedRange;
  523. VCN ThisVcn;
  524. VCN ThisLcn;
  525. VCN ThisClusterCount;
  526. PVOID RangePtr;
  527. ULONG RunIndex;
  528. ULONG VcnClusterOffset = 0;
  529. VCN FoundClusterCount = 0;
  530. PAGED_CODE();
  531. //
  532. // Assert that the file is sparse, non-resident and we are within file size.
  533. //
  534. ASSERT( FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE ));
  535. ASSERT( !FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT ));
  536. //
  537. // Move the starting point back to a sparse file boundary.
  538. //
  539. ThisVcn = StartVcn;
  540. if (RoundToSparseUnit) {
  541. VcnClusterOffset = ((PLARGE_INTEGER) &ThisVcn)->LowPart & (Scb->Vcb->SparseFileClusters - 1);
  542. ((PLARGE_INTEGER) &ThisVcn)->LowPart &= ~(Scb->Vcb->SparseFileClusters - 1);
  543. }
  544. //
  545. // Lookup the allocation at that position.
  546. //
  547. AllocatedRange = NtfsLookupNtfsMcbEntry( &Scb->Mcb,
  548. ThisVcn,
  549. &ThisLcn,
  550. &ThisClusterCount,
  551. NULL,
  552. NULL,
  553. &RangePtr,
  554. &RunIndex );
  555. //
  556. // If the range has no mapping then it is entirely sparse.
  557. //
  558. if (!AllocatedRange) {
  559. ThisClusterCount = MAXLONGLONG;
  560. //
  561. // If the block is not allocated and the length of the run is not enough
  562. // clusters for a sparse file unit then look to make sure the block
  563. // is fully deallocated.
  564. //
  565. } else if (ThisLcn == UNUSED_LCN) {
  566. AllocatedRange = FALSE;
  567. while (TRUE) {
  568. FoundClusterCount += ThisClusterCount;
  569. ThisVcn += ThisClusterCount;
  570. ThisClusterCount = 0;
  571. //
  572. // Check successive runs to extend the hole.
  573. //
  574. if (ThisVcn >= FinalCluster) {
  575. break;
  576. }
  577. RunIndex += 1;
  578. if (!NtfsGetSequentialMcbEntry( &Scb->Mcb,
  579. &RangePtr,
  580. RunIndex,
  581. &ThisVcn,
  582. &ThisLcn,
  583. &ThisClusterCount )) {
  584. //
  585. // The file is deallocated from here to the end of the Mcb.
  586. // Treat this as a large hole.
  587. //
  588. ThisClusterCount = MAXLONGLONG - FoundClusterCount;
  589. break;
  590. }
  591. //
  592. // If the range is allocated and we haven't found a full sparse unit
  593. // then mark the block as allocated. If we have at lease one sparse
  594. // file unit then trim the hole back to the nearest sparse file
  595. // unit boundary.
  596. //
  597. if (ThisLcn != UNUSED_LCN) {
  598. if (RoundToSparseUnit) {
  599. if (FoundClusterCount < Scb->Fcb->Vcb->SparseFileClusters) {
  600. //
  601. // Set our variables to indicate we are at the start of a fully
  602. // allocated sparse block.
  603. //
  604. ThisVcn -= FoundClusterCount;
  605. ThisClusterCount += FoundClusterCount;
  606. FoundClusterCount = 0;
  607. AllocatedRange = TRUE;
  608. } else {
  609. ThisClusterCount = 0;
  610. ((PLARGE_INTEGER) &FoundClusterCount)->LowPart &= ~(Scb->Vcb->SparseFileClusters - 1);
  611. }
  612. }
  613. break;
  614. }
  615. }
  616. }
  617. //
  618. // If we have an allocated block then find all of the contiguous allocated
  619. // blocks we can.
  620. //
  621. if (AllocatedRange) {
  622. while (TRUE) {
  623. if (RoundToSparseUnit) {
  624. //
  625. // Round the clusters found to a sparse file unit and update
  626. // the next vcn and count of clusters found.
  627. //
  628. ThisClusterCount = BlockAlign( ThisClusterCount, (LONG)Scb->Fcb->Vcb->SparseFileClusters );
  629. }
  630. ThisVcn += ThisClusterCount;
  631. FoundClusterCount += ThisClusterCount;
  632. //
  633. // Break out if we are past our final target or the beginning of the
  634. // next range is not allocated.
  635. //
  636. if ((ThisVcn >= FinalCluster) ||
  637. !NtfsLookupNtfsMcbEntry( &Scb->Mcb,
  638. ThisVcn,
  639. &ThisLcn,
  640. &ThisClusterCount,
  641. NULL,
  642. NULL,
  643. &RangePtr,
  644. &RunIndex ) ||
  645. (ThisLcn == UNUSED_LCN)) {
  646. ThisClusterCount = 0;
  647. break;
  648. }
  649. }
  650. }
  651. //
  652. // Trim the clusters found to either a sparse file unit or the input final
  653. // cluster value.
  654. //
  655. *ClusterCount = ThisClusterCount + FoundClusterCount - (LONGLONG) VcnClusterOffset;
  656. if ((FinalCluster - StartVcn) < *ClusterCount) {
  657. *ClusterCount = FinalCluster - StartVcn;
  658. }
  659. return AllocatedRange;
  660. }
  661. BOOLEAN
  662. NtfsAllocateAttribute (
  663. IN PIRP_CONTEXT IrpContext,
  664. IN PSCB Scb,
  665. IN ATTRIBUTE_TYPE_CODE AttributeTypeCode,
  666. IN PUNICODE_STRING AttributeName OPTIONAL,
  667. IN USHORT AttributeFlags,
  668. IN BOOLEAN AllocateAll,
  669. IN BOOLEAN LogIt,
  670. IN LONGLONG Size,
  671. IN PATTRIBUTE_ENUMERATION_CONTEXT NewLocation OPTIONAL
  672. )
  673. /*++
  674. Routine Description:
  675. This routine creates a new attribute and allocates space for it, either in a
  676. file record, or as a nonresident attribute.
  677. Arguments:
  678. Scb - Scb for the attribute.
  679. AttributeTypeCode - Attribute type code to be created.
  680. AttributeName - Optional name for the attribute.
  681. AttributeFlags - Flags to be stored in the attribute record for this attribute.
  682. AllocateAll - Specified as TRUE if all allocation should be allocated,
  683. even if we have to break up the transaction.
  684. LogIt - Most callers should specify TRUE, to have the change logged. However,
  685. we can specify FALSE if we are creating a new file record, and
  686. will be logging the entire new file record.
  687. Size - Size in bytes to allocate for the attribute.
  688. NewLocation - If specified, this is the location to store the attribute.
  689. Return Value:
  690. FALSE - if the attribute was created, but not all of the space was allocated
  691. (this can only happen if Scb was not specified)
  692. TRUE - if the space was allocated.
  693. --*/
  694. {
  695. BOOLEAN UninitializeOnClose = FALSE;
  696. BOOLEAN NewLocationSpecified;
  697. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  698. LONGLONG ClusterCount, SavedClusterCount;
  699. BOOLEAN FullAllocation;
  700. PFCB Fcb = Scb->Fcb;
  701. LONGLONG Delta = NtfsResidentStreamQuota( Fcb->Vcb );
  702. PAGED_CODE();
  703. //
  704. // Either there is no compression taking place or the attribute
  705. // type code allows compression to be specified in the header.
  706. // $INDEX_ROOT is a special hack to store the inherited-compression
  707. // flag.
  708. //
  709. ASSERT( (AttributeFlags == 0) ||
  710. (AttributeTypeCode == $INDEX_ROOT) ||
  711. NtfsIsTypeCodeCompressible( AttributeTypeCode ));
  712. //
  713. // If the file is being created compressed, then we need to round its
  714. // size to a compression unit boundary.
  715. //
  716. if ((Scb->CompressionUnit != 0) &&
  717. (Scb->Header.NodeTypeCode == NTFS_NTC_SCB_DATA)) {
  718. Size = BlockAlign( Size, (LONG)Scb->CompressionUnit );
  719. }
  720. //
  721. // Prepare for looking up attribute records to get the retrieval
  722. // information.
  723. //
  724. if (ARGUMENT_PRESENT( NewLocation )) {
  725. NewLocationSpecified = TRUE;
  726. } else {
  727. NtfsInitializeAttributeContext( &Context );
  728. NewLocationSpecified = FALSE;
  729. NewLocation = &Context;
  730. }
  731. try {
  732. //
  733. // If the FILE_SIZE_LOADED flag is not set, then this Scb is for
  734. // an attribute that does not yet exist on disk. We will put zero
  735. // into all of the sizes fields and set the flags indicating that
  736. // Scb is valid. NOTE - This routine expects both FILE_SIZE_LOADED
  737. // and HEADER_INITIALIZED to be both set or both clear.
  738. //
  739. ASSERT( BooleanFlagOn( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED )
  740. == BooleanFlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED ));
  741. if (!FlagOn( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
  742. Scb->ValidDataToDisk =
  743. Scb->Header.AllocationSize.QuadPart =
  744. Scb->Header.FileSize.QuadPart =
  745. Scb->Header.ValidDataLength.QuadPart = 0;
  746. SetFlag( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED |
  747. SCB_STATE_HEADER_INITIALIZED |
  748. SCB_STATE_UNINITIALIZE_ON_RESTORE );
  749. UninitializeOnClose = TRUE;
  750. }
  751. //
  752. // Now snapshot this Scb. We use a try-finally so we can uninitialize
  753. // the scb if neccessary.
  754. //
  755. NtfsSnapshotScb( IrpContext, Scb );
  756. UninitializeOnClose = FALSE;
  757. //
  758. // First allocate the space he wants.
  759. //
  760. SavedClusterCount =
  761. ClusterCount = LlClustersFromBytes(Fcb->Vcb, Size);
  762. Scb->TotalAllocated = 0;
  763. if (Size != 0) {
  764. ASSERT( NtfsIsExclusiveScb( Scb ));
  765. Scb->ScbSnapshot->LowestModifiedVcn = 0;
  766. Scb->ScbSnapshot->HighestModifiedVcn = MAXLONGLONG;
  767. NtfsAllocateClusters( IrpContext,
  768. Fcb->Vcb,
  769. Scb,
  770. (LONGLONG)0,
  771. (BOOLEAN)!NtfsIsTypeCodeUserData( AttributeTypeCode ),
  772. ClusterCount,
  773. NULL,
  774. &ClusterCount );
  775. //
  776. // Account for any new clusters in the allocation.
  777. //
  778. Delta += LlBytesFromClusters( Fcb->Vcb, ClusterCount );
  779. }
  780. //
  781. // Make sure the owner is allowed to have this space.
  782. //
  783. if (FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA )) {
  784. ASSERT( NtfsIsTypeCodeSubjectToQuota( Scb->AttributeTypeCode ));
  785. NtfsConditionallyUpdateQuota( IrpContext,
  786. Fcb,
  787. &Delta,
  788. LogIt,
  789. TRUE );
  790. }
  791. //
  792. // Now create the attribute. Remember if this routine
  793. // cut the allocation because of logging problems.
  794. //
  795. FullAllocation = NtfsCreateAttributeWithAllocation( IrpContext,
  796. Scb,
  797. AttributeTypeCode,
  798. AttributeName,
  799. AttributeFlags,
  800. LogIt,
  801. NewLocationSpecified,
  802. NewLocation );
  803. if (AllocateAll &&
  804. (!FullAllocation ||
  805. (ClusterCount < SavedClusterCount))) {
  806. //
  807. // If we are creating the attribute, then we only need to pass a
  808. // file object below if we already cached it ourselves, such as
  809. // in the case of ConvertToNonresident.
  810. //
  811. NtfsAddAllocation( IrpContext,
  812. Scb->FileObject,
  813. Scb,
  814. ClusterCount,
  815. (SavedClusterCount - ClusterCount),
  816. FALSE,
  817. NULL );
  818. //
  819. // Show that we allocated all of the space.
  820. //
  821. ClusterCount = SavedClusterCount;
  822. FullAllocation = TRUE;
  823. }
  824. } finally {
  825. DebugUnwind( NtfsAllocateAttribute );
  826. //
  827. // Cleanup the attribute context on the way out.
  828. //
  829. if (!NewLocationSpecified) {
  830. NtfsCleanupAttributeContext( IrpContext, &Context );
  831. }
  832. //
  833. // Clear out the Scb if it was uninitialized to begin with.
  834. //
  835. if (UninitializeOnClose) {
  836. ClearFlag( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED |
  837. SCB_STATE_HEADER_INITIALIZED |
  838. SCB_STATE_UNINITIALIZE_ON_RESTORE );
  839. }
  840. }
  841. return (FullAllocation && (SavedClusterCount <= ClusterCount));
  842. }
  843. VOID
  844. NtfsAddAllocation (
  845. IN PIRP_CONTEXT IrpContext,
  846. IN PFILE_OBJECT FileObject OPTIONAL,
  847. IN OUT PSCB Scb,
  848. IN VCN StartingVcn,
  849. IN LONGLONG ClusterCount,
  850. IN LOGICAL AskForMore,
  851. IN OUT PCCB CcbForWriteExtend OPTIONAL
  852. )
  853. /*++
  854. Routine Description:
  855. This routine adds allocation to an existing nonresident attribute. None of
  856. the allocation is allowed to already exist, as this would make error recovery
  857. too difficult. The caller must insure that he only asks for space not already
  858. allocated.
  859. Arguments:
  860. FileObject - FileObject for the Scb
  861. Scb - Scb for the attribute needing allocation
  862. StartingVcn - First Vcn to be allocated.
  863. ClusterCount - Number of clusters to allocate.
  864. AskForMore - Indicates if we want to ask for extra allocation.
  865. CcbForWriteExtend - Use the WriteExtendCount in this Ccb to determine the number of times
  866. this file has been write extended. Use this in combination with AskForMore to
  867. determine how much more to ask for.
  868. Return Value:
  869. None.
  870. --*/
  871. {
  872. LONGLONG DesiredClusterCount;
  873. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  874. BOOLEAN Extending;
  875. BOOLEAN AllocateAll;
  876. PVCB Vcb = IrpContext->Vcb;
  877. LONGLONG LlTemp1;
  878. LONGLONG LlTemp2;
  879. PAGED_CODE();
  880. ASSERT_IRP_CONTEXT( IrpContext );
  881. ASSERT_SCB( Scb );
  882. ASSERT_EXCLUSIVE_SCB( Scb );
  883. DebugTrace( +1, Dbg, ("NtfsAddAllocation\n") );
  884. //
  885. // Determine if we must allocate in one shot or if partial results are allowed.
  886. //
  887. if (NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ) &&
  888. NtfsSegmentNumber( &Scb->Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER) {
  889. AllocateAll = FALSE;
  890. } else {
  891. AllocateAll = TRUE;
  892. }
  893. //
  894. // We cannot add space in this high level routine during restart.
  895. // Everything we can use is in the Mcb.
  896. //
  897. if (FlagOn(Scb->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS)) {
  898. DebugTrace( -1, Dbg, ("NtfsAddAllocation (Nooped for Restart) -> VOID\n") );
  899. return;
  900. }
  901. //
  902. // We limit the user to 32 bits for the clusters unless the file is
  903. // sparse. For sparse files we limit ourselves to 63 bits for the file size.
  904. //
  905. LlTemp1 = ClusterCount + StartingVcn;
  906. if (!FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE )) {
  907. if ((((PLARGE_INTEGER)&ClusterCount)->HighPart != 0)
  908. || (((PLARGE_INTEGER)&StartingVcn)->HighPart != 0)
  909. || (((PLARGE_INTEGER)&LlTemp1)->HighPart != 0)) {
  910. NtfsRaiseStatus( IrpContext, STATUS_DISK_FULL, NULL, NULL );
  911. }
  912. }
  913. //
  914. // First make sure the Mcb is loaded.
  915. //
  916. NtfsPreloadAllocation( IrpContext, Scb, StartingVcn, StartingVcn + ClusterCount - 1 );
  917. //
  918. // Now make the call to add the new allocation, and get out if we do
  919. // not actually have to allocate anything. Before we do the allocation
  920. // call check if we need to compute a new desired cluster count for
  921. // extending a data attribute. We never allocate more than the requested
  922. // clusters for the Mft.
  923. //
  924. Extending = (BOOLEAN)((LONGLONG)LlBytesFromClusters(Vcb, (StartingVcn + ClusterCount)) >
  925. Scb->Header.AllocationSize.QuadPart);
  926. //
  927. // Check if we need to modified the base Vcn value stored in the snapshot for
  928. // the abort case.
  929. //
  930. ASSERT( NtfsIsExclusiveScb( Scb ));
  931. NtfsSnapshotScb( IrpContext, Scb );
  932. if (Scb->ScbSnapshot != NULL) {
  933. if (StartingVcn < Scb->ScbSnapshot->LowestModifiedVcn) {
  934. Scb->ScbSnapshot->LowestModifiedVcn = StartingVcn;
  935. }
  936. LlTemp1 -= 1;
  937. if (LlTemp1 > Scb->ScbSnapshot->HighestModifiedVcn) {
  938. if (Extending) {
  939. Scb->ScbSnapshot->HighestModifiedVcn = MAXLONGLONG;
  940. } else {
  941. Scb->ScbSnapshot->HighestModifiedVcn = LlTemp1;
  942. }
  943. }
  944. }
  945. ASSERT( (Scb->ScbSnapshot != NULL) ||
  946. !NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ) ||
  947. (Scb == Vcb->BitmapScb) );
  948. if (AskForMore) {
  949. LONGLONG MaxFreeClusters;
  950. //
  951. // Assume these are the same.
  952. //
  953. DesiredClusterCount = ClusterCount;
  954. //
  955. // If there is a Ccb with a WriteExtend count less than 4 then use it.
  956. //
  957. if (ARGUMENT_PRESENT( CcbForWriteExtend )) {
  958. //
  959. // We want to be slightly smart about the rounding factor. The key thing is to keep
  960. // the user's data contiguous within likely IO boundaries (MM flush regions, etc).
  961. // We will progressively round to higher even cluster values based on the number of times the
  962. // user has extended the file.
  963. //
  964. if (CcbForWriteExtend->WriteExtendCount != 0) {
  965. //
  966. // Initialize the rounding mask to 2 clusters and higher multiples of 2.
  967. //
  968. ULONG RoundingMask = (1 << CcbForWriteExtend->WriteExtendCount);
  969. //
  970. // Next perform the basic shift based on the size of this allocation.
  971. //
  972. DesiredClusterCount = Int64ShllMod32( ClusterCount, CcbForWriteExtend->WriteExtendCount );
  973. //
  974. // Now bias this by the StartingVcn and round this to the selected boundary.
  975. //
  976. DesiredClusterCount = BlockAlign( DesiredClusterCount + StartingVcn, (LONG)RoundingMask );
  977. //
  978. // Remove the StartingVcn bias and see if there is anything left.
  979. // Note: the 2nd test is for a longlong rollover
  980. //
  981. if ((DesiredClusterCount - StartingVcn < ClusterCount) ||
  982. (DesiredClusterCount < StartingVcn)) {
  983. DesiredClusterCount = ClusterCount;
  984. } else {
  985. DesiredClusterCount -= StartingVcn;
  986. }
  987. //
  988. // Don't use more than 2^32 clusters.
  989. //
  990. if (StartingVcn + DesiredClusterCount > MAX_CLUSTERS_PER_RANGE) {
  991. DesiredClusterCount = ClusterCount;
  992. }
  993. }
  994. //
  995. // Increment the extend count.
  996. //
  997. if (CcbForWriteExtend->WriteExtendCount < NtfsExtendFactor) {
  998. CcbForWriteExtend->WriteExtendCount += 1;
  999. }
  1000. }
  1001. //
  1002. // Make sure we don't exceed our maximum file size.
  1003. // Also don't swallow up too much of the remaining disk space.
  1004. //
  1005. MaxFreeClusters = Int64ShraMod32( Vcb->FreeClusters, 10 ) + ClusterCount;
  1006. if (Vcb->MaxClusterCount - StartingVcn < MaxFreeClusters) {
  1007. MaxFreeClusters = Vcb->MaxClusterCount - StartingVcn;
  1008. ASSERT( MaxFreeClusters >= ClusterCount );
  1009. }
  1010. if (DesiredClusterCount > MaxFreeClusters) {
  1011. DesiredClusterCount = MaxFreeClusters;
  1012. }
  1013. if (NtfsPerformQuotaOperation(Scb->Fcb)) {
  1014. NtfsGetRemainingQuota( IrpContext,
  1015. Scb->Fcb->OwnerId,
  1016. &LlTemp1,
  1017. &LlTemp2,
  1018. &Scb->Fcb->QuotaControl->QuickIndexHint );
  1019. //
  1020. // Do not use LlClustersFromBytesTruncate it is signed and this must be
  1021. // an unsigned operation.
  1022. //
  1023. LlTemp1 = Int64ShrlMod32( LlTemp1, Vcb->ClusterShift );
  1024. if (DesiredClusterCount > LlTemp1) {
  1025. //
  1026. // The owner is near their quota limit. Do not grow the
  1027. // file past the requested amount. Note we do not bother
  1028. // calculating a desired amount based on the remaining quota.
  1029. // This keeps us from using up a bunch of quota that we may
  1030. // not need when the user is near the limit.
  1031. //
  1032. DesiredClusterCount = ClusterCount;
  1033. }
  1034. }
  1035. } else {
  1036. DesiredClusterCount = ClusterCount;
  1037. }
  1038. //
  1039. // All allocation adds for compressed / sparse files should start on a compression unit boundary
  1040. //
  1041. ASSERT( (Scb->CompressionUnit == 0) ||
  1042. !FlagOn( StartingVcn, ClustersFromBytes( Scb->Vcb, Scb->CompressionUnit ) - 1) );
  1043. //
  1044. // Prepare for looking up attribute records to get the retrieval
  1045. // information.
  1046. //
  1047. NtfsInitializeAttributeContext( &Context );
  1048. if (Extending &&
  1049. FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA ) &&
  1050. NtfsPerformQuotaOperation( Scb->Fcb )) {
  1051. ASSERT( NtfsIsTypeCodeSubjectToQuota( Scb->AttributeTypeCode ));
  1052. //
  1053. // The quota index must be acquired before the mft scb is acquired.
  1054. //
  1055. ASSERT( !NtfsIsExclusiveScb( Vcb->MftScb ) || NtfsIsSharedFcb( Vcb->QuotaTableScb->Fcb ) );
  1056. NtfsAcquireQuotaControl( IrpContext, Scb->Fcb->QuotaControl );
  1057. }
  1058. try {
  1059. while (TRUE) {
  1060. // Toplevel action is currently incompatible with our error recovery.
  1061. // It also costs in performance.
  1062. //
  1063. // //
  1064. // // Start the top-level action by remembering the current UndoNextLsn.
  1065. // //
  1066. //
  1067. // if (IrpContext->TransactionId != 0) {
  1068. //
  1069. // PTRANSACTION_ENTRY TransactionEntry;
  1070. //
  1071. // NtfsAcquireSharedRestartTable( &Vcb->TransactionTable, TRUE );
  1072. //
  1073. // TransactionEntry = (PTRANSACTION_ENTRY)GetRestartEntryFromIndex(
  1074. // &Vcb->TransactionTable, IrpContext->TransactionId );
  1075. //
  1076. // StartLsn = TransactionEntry->UndoNextLsn;
  1077. // SavedUndoRecords = TransactionEntry->UndoRecords;
  1078. // SavedUndoBytes = TransactionEntry->UndoBytes;
  1079. // NtfsReleaseRestartTable( &Vcb->TransactionTable );
  1080. //
  1081. // } else {
  1082. //
  1083. // StartLsn = *(PLSN)&Li0;
  1084. // SavedUndoRecords = 0;
  1085. // SavedUndoBytes = 0;
  1086. // }
  1087. //
  1088. //
  1089. // Remember that the clusters are only in the Scb now.
  1090. //
  1091. if (NtfsAllocateClusters( IrpContext,
  1092. Scb->Vcb,
  1093. Scb,
  1094. StartingVcn,
  1095. AllocateAll,
  1096. ClusterCount,
  1097. NULL,
  1098. &DesiredClusterCount )) {
  1099. //
  1100. // We defer looking up the attribute to make the "already-allocated"
  1101. // case faster.
  1102. //
  1103. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
  1104. //
  1105. // Now add the space to the file record, if any was allocated.
  1106. //
  1107. if (Extending) {
  1108. LlTemp1 = Scb->Header.AllocationSize.QuadPart;
  1109. NtfsAddAttributeAllocation( IrpContext,
  1110. Scb,
  1111. &Context,
  1112. NULL,
  1113. NULL );
  1114. //
  1115. // Make sure the owner is allowed to have these
  1116. // clusters.
  1117. //
  1118. if (FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA )) {
  1119. //
  1120. // Note the allocated clusters cannot be used
  1121. // here because StartingVcn may be greater
  1122. // then allocation size.
  1123. //
  1124. LlTemp1 = Scb->Header.AllocationSize.QuadPart - LlTemp1;
  1125. NtfsConditionallyUpdateQuota( IrpContext,
  1126. Scb->Fcb,
  1127. &LlTemp1,
  1128. TRUE,
  1129. TRUE );
  1130. }
  1131. } else {
  1132. NtfsAddAttributeAllocation( IrpContext,
  1133. Scb,
  1134. &Context,
  1135. &StartingVcn,
  1136. &ClusterCount );
  1137. }
  1138. //
  1139. // If he did not allocate anything, make sure we get out below.
  1140. //
  1141. } else {
  1142. DesiredClusterCount = ClusterCount;
  1143. }
  1144. //
  1145. // Call the Cache Manager to extend the section, now that we have
  1146. // succeeded.
  1147. //
  1148. if (ARGUMENT_PRESENT( FileObject) && Extending) {
  1149. NtfsSetBothCacheSizes( FileObject,
  1150. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  1151. Scb );
  1152. }
  1153. //
  1154. // Set up to truncate on close.
  1155. //
  1156. SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
  1157. //
  1158. // See if we need to loop back.
  1159. //
  1160. if (DesiredClusterCount < ClusterCount) {
  1161. NtfsCleanupAttributeContext( IrpContext, &Context );
  1162. //
  1163. // Commit the current transaction if we have one.
  1164. //
  1165. NtfsCheckpointCurrentTransaction( IrpContext );
  1166. //
  1167. // Adjust our parameters and reinitialize the context
  1168. // for the loop back.
  1169. //
  1170. StartingVcn = StartingVcn + DesiredClusterCount;
  1171. ClusterCount = ClusterCount - DesiredClusterCount;
  1172. DesiredClusterCount = ClusterCount;
  1173. NtfsInitializeAttributeContext( &Context );
  1174. //
  1175. // Else we are done.
  1176. //
  1177. } else {
  1178. break;
  1179. }
  1180. }
  1181. } finally {
  1182. DebugUnwind( NtfsAddAllocation );
  1183. //
  1184. // Cleanup the attribute context on the way out.
  1185. //
  1186. NtfsCleanupAttributeContext( IrpContext, &Context );
  1187. }
  1188. DebugTrace( -1, Dbg, ("NtfsAddAllocation -> VOID\n") );
  1189. return;
  1190. }
  1191. VOID
  1192. NtfsAddSparseAllocation (
  1193. IN PIRP_CONTEXT IrpContext,
  1194. IN PFILE_OBJECT FileObject OPTIONAL,
  1195. IN OUT PSCB Scb,
  1196. IN LONGLONG StartingOffset,
  1197. IN LONGLONG ByteCount
  1198. )
  1199. /*++
  1200. Routine Description:
  1201. This routine is called to add a hole to the end of a sparse file. We need to
  1202. force NtfsAddAttributeAllocation to extend a file via a hole. We do this be
  1203. adding a new range to the end of the Mcb and force it to have a LargeMcb.
  1204. NtfsAddAttributeAllocation recognizes this and will write the file record.
  1205. Otherwise that routine will truncate the hole at the end of a file.
  1206. Arguments:
  1207. FileObject - FileObject for the Scb
  1208. Scb - Scb for the attribute needing allocation
  1209. StartingOffset - File offset which contains the first compression unit to add.
  1210. ByteCount - Number of bytes to allocate from the StartingOffset.
  1211. Return Value:
  1212. None.
  1213. --*/
  1214. {
  1215. LONGLONG Range;
  1216. VCN StartingVcn = LlClustersFromBytesTruncate( Scb->Vcb,
  1217. Scb->Header.AllocationSize.LowPart );
  1218. BOOLEAN UnloadMcb = TRUE;
  1219. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  1220. PAGED_CODE();
  1221. ASSERT_IRP_CONTEXT( IrpContext );
  1222. ASSERT_SCB( Scb );
  1223. ASSERT_EXCLUSIVE_SCB( Scb );
  1224. DebugTrace( +1, Dbg, ("NtfsAddSparseAllocation\n") );
  1225. //
  1226. // Do a sanity check on the following.
  1227. //
  1228. // - This is not restart.
  1229. // - This is a sparse file.
  1230. // - The StartingOffset is beyond the end of the file.
  1231. //
  1232. ASSERT( !FlagOn( Scb->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) &&
  1233. FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_SPARSE ) &&
  1234. (StartingOffset >= Scb->Header.AllocationSize.QuadPart) );
  1235. //
  1236. // Check if we need to modified the base Vcn value stored in the snapshot for
  1237. // the abort case.
  1238. //
  1239. NtfsSnapshotScb( IrpContext, Scb );
  1240. if (Scb->ScbSnapshot != NULL) {
  1241. if (StartingVcn < Scb->ScbSnapshot->LowestModifiedVcn) {
  1242. Scb->ScbSnapshot->LowestModifiedVcn = StartingVcn;
  1243. }
  1244. Scb->ScbSnapshot->HighestModifiedVcn = MAXLONGLONG;
  1245. }
  1246. ASSERT( Scb->ScbSnapshot != NULL );
  1247. //
  1248. // Round the end of the allocation upto a compression unit boundary.
  1249. //
  1250. Range = BlockAlign( StartingOffset + ByteCount, (LONG)Scb->CompressionUnit );
  1251. ASSERT( Range <= MAXFILESIZE );
  1252. //
  1253. // Convert from bytes to clusters.
  1254. //
  1255. StartingVcn = LlClustersFromBytesTruncate( Scb->Vcb, Scb->Header.AllocationSize.QuadPart );
  1256. Range = LlClustersFromBytesTruncate( Scb->Vcb, Range );
  1257. //
  1258. // Initialize the lookup context.
  1259. //
  1260. NtfsInitializeAttributeContext( &Context );
  1261. try {
  1262. //
  1263. // Load the allocation for the range ahead of us.
  1264. //
  1265. if (StartingOffset != 0) {
  1266. NtfsPreloadAllocation( IrpContext,
  1267. Scb,
  1268. StartingVcn - 1,
  1269. StartingVcn - 1 );
  1270. }
  1271. //
  1272. // Define a range past the current end of the file.
  1273. //
  1274. NtfsDefineNtfsMcbRange( &Scb->Mcb,
  1275. StartingVcn,
  1276. Range - 1,
  1277. FALSE );
  1278. //
  1279. // Now add a single hole so that there is an Mcb entry.
  1280. //
  1281. NtfsAddNtfsMcbEntry( &Scb->Mcb,
  1282. StartingVcn,
  1283. UNUSED_LCN,
  1284. Range - StartingVcn,
  1285. FALSE );
  1286. //
  1287. // Lookup the first file record for this Scb.
  1288. //
  1289. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
  1290. if (FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA ) &&
  1291. NtfsPerformQuotaOperation( Scb->Fcb )) {
  1292. ASSERT( NtfsIsTypeCodeSubjectToQuota( Scb->AttributeTypeCode ));
  1293. //
  1294. // The quota index must be acquired before the mft scb is acquired.
  1295. //
  1296. ASSERT( !NtfsIsExclusiveScb( Scb->Vcb->MftScb ) ||
  1297. NtfsIsSharedScb( Scb->Fcb->Vcb->QuotaTableScb ) );
  1298. NtfsAcquireQuotaControl( IrpContext, Scb->Fcb->QuotaControl );
  1299. }
  1300. //
  1301. // Now add the space to the file record, if any was allocated.
  1302. //
  1303. Range = Scb->Header.AllocationSize.QuadPart;
  1304. NtfsAddAttributeAllocation( IrpContext,
  1305. Scb,
  1306. &Context,
  1307. NULL,
  1308. NULL );
  1309. //
  1310. // Make sure the owner is allowed to have these
  1311. // clusters.
  1312. //
  1313. if (FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA )) {
  1314. //
  1315. // Note the allocated clusters cannot be used
  1316. // here because StartingVcn may be greater
  1317. // then allocation size.
  1318. //
  1319. Range = Scb->Header.AllocationSize.QuadPart - Range;
  1320. NtfsConditionallyUpdateQuota( IrpContext,
  1321. Scb->Fcb,
  1322. &Range,
  1323. TRUE,
  1324. TRUE );
  1325. }
  1326. //
  1327. // Call the Cache Manager to extend the section, now that we have
  1328. // succeeded.
  1329. //
  1330. if (ARGUMENT_PRESENT( FileObject)) {
  1331. NtfsSetBothCacheSizes( FileObject,
  1332. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  1333. Scb );
  1334. }
  1335. //
  1336. // Set up to truncate on close.
  1337. //
  1338. SetFlag( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE );
  1339. UnloadMcb = FALSE;
  1340. } finally {
  1341. DebugUnwind( NtfsAddSparseAllocation );
  1342. //
  1343. // Manually unload the Mcb in the event of an error. There may not be a
  1344. // transaction underway.
  1345. //
  1346. if (UnloadMcb) {
  1347. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  1348. StartingVcn,
  1349. MAXLONGLONG,
  1350. FALSE,
  1351. FALSE );
  1352. }
  1353. //
  1354. // Cleanup the attribute context on the way out.
  1355. //
  1356. NtfsCleanupAttributeContext( IrpContext, &Context );
  1357. }
  1358. DebugTrace( -1, Dbg, ("NtfsAddSparseAllocation -> VOID\n") );
  1359. return;
  1360. }
  1361. VOID
  1362. NtfsDeleteAllocation (
  1363. IN PIRP_CONTEXT IrpContext,
  1364. IN PFILE_OBJECT FileObject OPTIONAL,
  1365. IN OUT PSCB Scb,
  1366. IN VCN StartingVcn,
  1367. IN VCN EndingVcn,
  1368. IN BOOLEAN LogIt,
  1369. IN BOOLEAN BreakupAllowed
  1370. )
  1371. /*++
  1372. Routine Description:
  1373. This routine deletes allocation from an existing nonresident attribute. If all
  1374. or part of the allocation does not exist, the effect is benign, and only the
  1375. remaining allocation is deleted.
  1376. Arguments:
  1377. FileObject - FileObject for the Scb. This should always be specified if
  1378. possible, and must be specified if it is possible that MM has a
  1379. section created.
  1380. Scb - Scb for the attribute needing allocation
  1381. StartingVcn - First Vcn to be deallocated.
  1382. EndingVcn - Last Vcn to be deallocated, or xxMax to truncate at StartingVcn.
  1383. If EndingVcn is *not* xxMax, a sparse deallocation is performed,
  1384. and none of the stream sizes are changed.
  1385. LogIt - Most callers should specify TRUE, to have the change logged. However,
  1386. we can specify FALSE if we are deleting the file record, and
  1387. will be logging this delete.
  1388. BreakupAllowed - TRUE if the caller can tolerate breaking up the deletion of
  1389. allocation into multiple transactions, if there are a large
  1390. number of runs.
  1391. Return Value:
  1392. None.
  1393. --*/
  1394. {
  1395. VCN MyStartingVcn, MyEndingVcn;
  1396. VCN BlockStartingVcn = 0;
  1397. PVOID FirstRangePtr;
  1398. ULONG FirstRunIndex;
  1399. PVOID LastRangePtr;
  1400. ULONG LastRunIndex;
  1401. BOOLEAN BreakingUp = FALSE;
  1402. PVCB Vcb = Scb->Vcb;
  1403. LCN TempLcn;
  1404. LONGLONG TempCount;
  1405. ULONG CompressionUnitInClusters = 1;
  1406. PAGED_CODE();
  1407. if (Scb->CompressionUnit != 0) {
  1408. CompressionUnitInClusters = ClustersFromBytes( Vcb, Scb->CompressionUnit );
  1409. }
  1410. //
  1411. // If the file is compressed, make sure we round the allocation
  1412. // size to a compression unit boundary, so we correctly interpret
  1413. // the compression state of the data at the point we are
  1414. // truncating to. I.e., the danger is that we throw away one
  1415. // or more clusters at the end of compressed data! Note that this
  1416. // adjustment could cause us to noop the call.
  1417. //
  1418. if (Scb->CompressionUnit != 0) {
  1419. //
  1420. // Now check if we are truncating at the end of the file.
  1421. //
  1422. if (EndingVcn == MAXLONGLONG) {
  1423. StartingVcn = BlockAlign( StartingVcn, (LONG)CompressionUnitInClusters );
  1424. }
  1425. }
  1426. //
  1427. // Make sure we have a snapshot and update it with the range of this deallocation.
  1428. //
  1429. ASSERT( NtfsIsExclusiveScb( Scb ));
  1430. NtfsSnapshotScb( IrpContext, Scb );
  1431. //
  1432. // Make sure update the VCN range in the snapshot. We need to
  1433. // do it each pass through the loop
  1434. //
  1435. if (Scb->ScbSnapshot != NULL) {
  1436. if (StartingVcn < Scb->ScbSnapshot->LowestModifiedVcn) {
  1437. Scb->ScbSnapshot->LowestModifiedVcn = StartingVcn;
  1438. }
  1439. if (EndingVcn > Scb->ScbSnapshot->HighestModifiedVcn) {
  1440. Scb->ScbSnapshot->HighestModifiedVcn = EndingVcn;
  1441. }
  1442. }
  1443. ASSERT( (Scb->ScbSnapshot != NULL) ||
  1444. !NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ));
  1445. //
  1446. // We may not be able to preload the entire allocation for an
  1447. // extremely large fragmented file. The number of Mcb's may exhaust
  1448. // available pool. We will break the range to deallocate into smaller
  1449. // ranges when preloading the allocation.
  1450. //
  1451. do {
  1452. //
  1453. // If this is a large file and breakup is allowed then see if we
  1454. // want to break up the range of the deallocation.
  1455. //
  1456. if ((Scb->Header.AllocationSize.HighPart != 0) && BreakupAllowed) {
  1457. //
  1458. // If this is the first pass through then determine the starting point
  1459. // for this range.
  1460. //
  1461. if (BlockStartingVcn == 0) {
  1462. MyEndingVcn = EndingVcn;
  1463. if (EndingVcn == MAXLONGLONG) {
  1464. MyEndingVcn = LlClustersFromBytesTruncate( Vcb,
  1465. Scb->Header.AllocationSize.QuadPart ) - 1;
  1466. }
  1467. BlockStartingVcn = MyEndingVcn - Vcb->ClustersPer4Gig;
  1468. //
  1469. // Remember we are breaking up now, and that as a result
  1470. // we have to log everything.
  1471. //
  1472. BreakingUp = TRUE;
  1473. LogIt = TRUE;
  1474. } else {
  1475. //
  1476. // If we are truncating from the end of the file then raise CANT_WAIT. This will
  1477. // cause us to release our resources periodically when deleting a large file.
  1478. //
  1479. if (BreakingUp && (EndingVcn == MAXLONGLONG)) {
  1480. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  1481. }
  1482. BlockStartingVcn -= Vcb->ClustersPer4Gig;
  1483. }
  1484. if (BlockStartingVcn < StartingVcn) {
  1485. BlockStartingVcn = StartingVcn;
  1486. } else if (Scb->CompressionUnit != 0) {
  1487. //
  1488. // Now check if we are truncating at the end of the file.
  1489. // Always truncate to a compression unit boundary.
  1490. //
  1491. if (EndingVcn == MAXLONGLONG) {
  1492. BlockStartingVcn = BlockAlign( BlockStartingVcn, (LONG)CompressionUnitInClusters );
  1493. }
  1494. }
  1495. } else {
  1496. BlockStartingVcn = StartingVcn;
  1497. }
  1498. //
  1499. // First make sure the Mcb is loaded. Note it is possible that
  1500. // we could need the previous range loaded if the delete starts
  1501. // at the beginning of a file record boundary, thus the -1.
  1502. //
  1503. NtfsPreloadAllocation( IrpContext, Scb, ((BlockStartingVcn != 0) ? (BlockStartingVcn - 1) : 0), EndingVcn );
  1504. //
  1505. // Loop to do one or more deallocate calls.
  1506. //
  1507. MyEndingVcn = EndingVcn;
  1508. do {
  1509. //
  1510. // Now lookup and get the indices for the first Vcn being deleted.
  1511. // If we are off the end, get out. We do this in the loop, because
  1512. // conceivably deleting space could change the range pointer and
  1513. // index of the first entry.
  1514. //
  1515. if (!NtfsLookupNtfsMcbEntry( &Scb->Mcb,
  1516. BlockStartingVcn,
  1517. NULL,
  1518. NULL,
  1519. NULL,
  1520. NULL,
  1521. &FirstRangePtr,
  1522. &FirstRunIndex )) {
  1523. break;
  1524. }
  1525. //
  1526. // Now see if we can deallocate everything at once.
  1527. //
  1528. MyStartingVcn = BlockStartingVcn;
  1529. LastRunIndex = MAXULONG;
  1530. if (BreakupAllowed) {
  1531. //
  1532. // Now lookup and get the indices for the last Vcn being deleted.
  1533. // If we are off the end, get the last index.
  1534. //
  1535. if (!NtfsLookupNtfsMcbEntry( &Scb->Mcb,
  1536. MyEndingVcn,
  1537. NULL,
  1538. NULL,
  1539. NULL,
  1540. NULL,
  1541. &LastRangePtr,
  1542. &LastRunIndex )) {
  1543. NtfsNumberOfRunsInRange(&Scb->Mcb, LastRangePtr, &LastRunIndex);
  1544. }
  1545. //
  1546. // If the Vcns to delete span multiple ranges, or there
  1547. // are too many in the last range to delete, then we
  1548. // will calculate the index of a run to start with for
  1549. // this pass through the loop.
  1550. //
  1551. if ((FirstRangePtr != LastRangePtr) ||
  1552. ((LastRunIndex - FirstRunIndex) > MAXIMUM_RUNS_AT_ONCE)) {
  1553. //
  1554. // Figure out where we can afford to truncate to.
  1555. //
  1556. if (LastRunIndex >= MAXIMUM_RUNS_AT_ONCE) {
  1557. LastRunIndex -= MAXIMUM_RUNS_AT_ONCE;
  1558. } else {
  1559. LastRunIndex = 0;
  1560. }
  1561. //
  1562. // Now lookup the first Vcn in this run.
  1563. //
  1564. NtfsGetNextNtfsMcbEntry( &Scb->Mcb,
  1565. &LastRangePtr,
  1566. LastRunIndex,
  1567. &MyStartingVcn,
  1568. &TempLcn,
  1569. &TempCount );
  1570. ASSERT(MyStartingVcn > BlockStartingVcn);
  1571. //
  1572. // If compressed, round down to a compression unit boundary.
  1573. //
  1574. MyStartingVcn = BlockAlignTruncate( MyStartingVcn, (LONG)CompressionUnitInClusters );
  1575. //
  1576. // Remember we are breaking up now, and that as a result
  1577. // we have to log everything.
  1578. //
  1579. BreakingUp = TRUE;
  1580. LogIt = TRUE;
  1581. }
  1582. }
  1583. //
  1584. // CAIROBUG Consider optimizing this code when the cairo ifdef's
  1585. // are removed.
  1586. //
  1587. //
  1588. // If this is a user data stream and we are truncating to end the
  1589. // return the quota to the owner.
  1590. //
  1591. if (FlagOn( Scb->ScbState, SCB_STATE_SUBJECT_TO_QUOTA ) &&
  1592. (EndingVcn == MAXLONGLONG)) {
  1593. //
  1594. // Calculate the amount that allocation size is being reduced.
  1595. //
  1596. TempCount = LlBytesFromClusters( Vcb, MyStartingVcn ) -
  1597. Scb->Header.AllocationSize.QuadPart;
  1598. NtfsConditionallyUpdateQuota( IrpContext,
  1599. Scb->Fcb,
  1600. &TempCount,
  1601. TRUE,
  1602. FALSE );
  1603. }
  1604. //
  1605. // Now deallocate a range of clusters
  1606. //
  1607. NtfsDeleteAllocationInternal( IrpContext,
  1608. Scb,
  1609. MyStartingVcn,
  1610. EndingVcn,
  1611. LogIt );
  1612. //
  1613. // Now, if we are breaking up this deallocation, then do some
  1614. // transaction cleanup.
  1615. //
  1616. if (BreakingUp) {
  1617. //
  1618. // Free the Mft Scb if we currently own it provided we are not
  1619. // truncating a stream in the Mft.
  1620. //
  1621. if ((NtfsSegmentNumber( &Scb->Fcb->FileReference ) != MASTER_FILE_TABLE_NUMBER) &&
  1622. (EndingVcn == MAXLONGLONG) &&
  1623. (Vcb->MftScb != NULL) &&
  1624. (Vcb->MftScb->Fcb->ExclusiveFcbLinks.Flink != NULL) &&
  1625. NtfsIsExclusiveScb( Vcb->MftScb )) {
  1626. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_MFT );
  1627. }
  1628. NtfsCheckpointCurrentTransaction( IrpContext );
  1629. //
  1630. // Move the ending Vcn backwards in the file. This will
  1631. // let us move down to the next earlier file record if
  1632. // this case spans multiple file records.
  1633. //
  1634. MyEndingVcn = MyStartingVcn - 1;
  1635. }
  1636. //
  1637. // Call the Cache Manager to change allocation size for either
  1638. // truncate or SplitMcb case (where EndingVcn was set to xxMax!).
  1639. //
  1640. if ((EndingVcn == MAXLONGLONG) && ARGUMENT_PRESENT( FileObject )) {
  1641. NtfsSetBothCacheSizes( FileObject,
  1642. (PCC_FILE_SIZES)&Scb->Header.AllocationSize,
  1643. Scb );
  1644. }
  1645. } while (MyStartingVcn != BlockStartingVcn);
  1646. } while (BlockStartingVcn != StartingVcn);
  1647. }
  1648. VOID
  1649. NtfsReallocateRange (
  1650. IN PIRP_CONTEXT IrpContext,
  1651. IN OUT PSCB Scb,
  1652. IN VCN DeleteVcn,
  1653. IN LONGLONG DeleteCount,
  1654. IN VCN AllocateVcn,
  1655. IN LONGLONG AllocateCount,
  1656. IN PLCN TargetLcn OPTIONAL
  1657. )
  1658. /*++
  1659. Routine Description:
  1660. This routine is called to reallocate a individual range within the existing
  1661. allocation of the file. Typically this might be used to reallocate a
  1662. compression unit or perform MoveFile. We can modify the Mcb and then
  1663. write a single log record to write the mapping information. This routine
  1664. doesn't make any attempt to split the Mcb. Also our caller must know
  1665. that the change of allocation occurs entirely within the existing virtual
  1666. allocation of the file.
  1667. We might expand this routine in the future to optimize the case where we
  1668. are reallocating a compression unit only because we believe it is fragmented
  1669. and there is a good chance to reduce fragmentation. We could check to see
  1670. if a single run is available and only reallocate if such a run exists.
  1671. Arguments:
  1672. Scb - Scb for the attribute needing a change of allocation.
  1673. DeleteVcn - Starting Vcn for the range to delete.
  1674. DeleteClusters - Count of clusters to delete. May be zero.
  1675. AllocateVcn - Starting Vcn for the range to allocate.
  1676. AllocateClusters - Count of clusters to allocate. May be zero.
  1677. TargetLcn - If specified reallocate to this particular LCN
  1678. Return Value:
  1679. None
  1680. --*/
  1681. {
  1682. VCN StartingVcn;
  1683. VCN EndingVcn;
  1684. ATTRIBUTE_ENUMERATION_CONTEXT Context;
  1685. ULONG CleanupContext = FALSE;
  1686. BOOLEAN ChangedAllocation = FALSE;
  1687. PAGED_CODE();
  1688. DebugTrace( +1, Dbg, ("NtfsReallocateRange: Entered\n") );
  1689. //
  1690. // Let's make sure we are within the full allocation of the stream.
  1691. //
  1692. ASSERT( (DeleteCount == 0) ||
  1693. ((DeleteVcn <= LlClustersFromBytesTruncate( IrpContext->Vcb, Scb->Header.AllocationSize.QuadPart )) &&
  1694. ((DeleteVcn + DeleteCount) <= LlClustersFromBytesTruncate( IrpContext->Vcb, Scb->Header.AllocationSize.QuadPart ))));
  1695. ASSERT( (AllocateCount == 0) ||
  1696. ((AllocateVcn <= LlClustersFromBytesTruncate( IrpContext->Vcb, Scb->Header.AllocationSize.QuadPart )) &&
  1697. ((AllocateVcn + AllocateCount) <= LlClustersFromBytesTruncate( IrpContext->Vcb, Scb->Header.AllocationSize.QuadPart ))));
  1698. //
  1699. // Either one or both or our input counts may be zero. Make sure the zero-length
  1700. // ranges don't make us do extra work.
  1701. //
  1702. if (DeleteCount == 0) {
  1703. if (AllocateCount == 0) {
  1704. DebugTrace( -1, Dbg, ("NtfsReallocateRange: Exit\n") );
  1705. return;
  1706. }
  1707. DeleteVcn = AllocateVcn;
  1708. //
  1709. // The range is set by the allocation clusters.
  1710. //
  1711. StartingVcn = AllocateVcn;
  1712. EndingVcn = AllocateVcn + AllocateCount;
  1713. } else if (AllocateCount == 0) {
  1714. AllocateVcn = DeleteVcn;
  1715. //
  1716. // The range is set by the deallocation clusters.
  1717. //
  1718. StartingVcn = DeleteVcn;
  1719. EndingVcn = DeleteVcn + DeleteCount;
  1720. } else {
  1721. //
  1722. // Find the lowest starting point.
  1723. //
  1724. StartingVcn = DeleteVcn;
  1725. if (DeleteVcn > AllocateVcn) {
  1726. StartingVcn = AllocateVcn;
  1727. }
  1728. //
  1729. // Find the highest ending point.
  1730. //
  1731. EndingVcn = DeleteVcn + DeleteCount;
  1732. if (AllocateVcn + AllocateCount > EndingVcn) {
  1733. EndingVcn = AllocateVcn + AllocateCount;
  1734. }
  1735. }
  1736. //
  1737. // Make sure we have a snapshot and update it with the range of this deallocation.
  1738. //
  1739. ASSERT( NtfsIsExclusiveScb( Scb ));
  1740. NtfsSnapshotScb( IrpContext, Scb );
  1741. //
  1742. // Make sure update the VCN range in the snapshot. We need to do this for both ranges.
  1743. //
  1744. if (Scb->ScbSnapshot != NULL) {
  1745. if (StartingVcn < Scb->ScbSnapshot->LowestModifiedVcn) {
  1746. Scb->ScbSnapshot->LowestModifiedVcn = StartingVcn;
  1747. }
  1748. if (EndingVcn > Scb->ScbSnapshot->HighestModifiedVcn) {
  1749. Scb->ScbSnapshot->HighestModifiedVcn = EndingVcn;
  1750. }
  1751. }
  1752. ASSERT( (Scb->ScbSnapshot != NULL) ||
  1753. !NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ));
  1754. //
  1755. // First make sure the Mcb is loaded. Note it is possible that
  1756. // we could need the previous range loaded if the delete starts
  1757. // at the beginning of a file record boundary, thus the -1.
  1758. //
  1759. NtfsPreloadAllocation( IrpContext,
  1760. Scb,
  1761. ((StartingVcn != 0) ? (StartingVcn - 1) : 0),
  1762. EndingVcn - 1 );
  1763. //
  1764. // Use a try-finally in case we need to unload the Mcb.
  1765. //
  1766. try {
  1767. //
  1768. // Do the deallocate first.
  1769. //
  1770. if (DeleteCount != 0) {
  1771. ChangedAllocation = NtfsDeallocateClusters( IrpContext,
  1772. Scb->Vcb,
  1773. Scb,
  1774. DeleteVcn,
  1775. DeleteVcn + DeleteCount - 1,
  1776. &Scb->TotalAllocated );
  1777. }
  1778. //
  1779. // Now do the allocation.
  1780. //
  1781. if (AllocateCount != 0) {
  1782. //
  1783. // The allocate path is simpler. We don't worry about ranges.
  1784. // Remember if any bits are allocated though.
  1785. //
  1786. if (NtfsAllocateClusters( IrpContext,
  1787. Scb->Vcb,
  1788. Scb,
  1789. AllocateVcn,
  1790. TRUE,
  1791. AllocateCount,
  1792. TargetLcn,
  1793. &AllocateCount )) {
  1794. ChangedAllocation = TRUE;
  1795. }
  1796. }
  1797. if (ChangedAllocation) {
  1798. //
  1799. // Now rewrite the mapping for this range.
  1800. //
  1801. AllocateCount = EndingVcn - StartingVcn;
  1802. NtfsInitializeAttributeContext( &Context );
  1803. CleanupContext = TRUE;
  1804. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
  1805. NtfsAddAttributeAllocation( IrpContext,
  1806. Scb,
  1807. &Context,
  1808. &StartingVcn,
  1809. &AllocateCount );
  1810. }
  1811. } finally {
  1812. if (AbnormalTermination()) {
  1813. //
  1814. // Unload the Mcb if we don't have a transaction. We need to do this
  1815. // in case we have already removed part of a range.
  1816. //
  1817. if (IrpContext->TransactionId == 0) {
  1818. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  1819. StartingVcn,
  1820. MAXLONGLONG,
  1821. FALSE,
  1822. FALSE );
  1823. }
  1824. }
  1825. //
  1826. // Cleanup the context if needed.
  1827. //
  1828. if (CleanupContext) {
  1829. NtfsCleanupAttributeContext( IrpContext, &Context );
  1830. }
  1831. DebugTrace( -1, Dbg, ("NtfsReallocateRange: Exit\n") );
  1832. }
  1833. return;
  1834. }
  1835. //
  1836. // Internal support routine
  1837. //
  1838. VOID
  1839. NtfsDeleteAllocationInternal (
  1840. IN PIRP_CONTEXT IrpContext,
  1841. IN OUT PSCB Scb,
  1842. IN VCN StartingVcn,
  1843. IN VCN EndingVcn,
  1844. IN BOOLEAN LogIt
  1845. )
  1846. /*++
  1847. Routine Description:
  1848. This routine deletes allocation from an existing nonresident attribute. If all
  1849. or part of the allocation does not exist, the effect is benign, and only the
  1850. remaining allocation is deleted.
  1851. Arguments:
  1852. Scb - Scb for the attribute needing allocation
  1853. StartingVcn - First Vcn to be deallocated.
  1854. EndingVcn - Last Vcn to be deallocated, or xxMax to truncate at StartingVcn.
  1855. If EndingVcn is *not* xxMax, a sparse deallocation is performed,
  1856. and none of the stream sizes are changed.
  1857. LogIt - Most callers should specify TRUE, to have the change logged. However,
  1858. we can specify FALSE if we are deleting the file record, and
  1859. will be logging this delete.
  1860. Return Value:
  1861. None.
  1862. --*/
  1863. {
  1864. ATTRIBUTE_ENUMERATION_CONTEXT Context, TempContext;
  1865. PATTRIBUTE_RECORD_HEADER Attribute;
  1866. LONGLONG SizeInBytes, SizeInClusters;
  1867. VCN Vcn1;
  1868. PVCB Vcb = Scb->Vcb;
  1869. BOOLEAN AddSpaceBack = FALSE;
  1870. BOOLEAN SplitMcb = FALSE;
  1871. BOOLEAN UpdatedAllocationSize = FALSE;
  1872. ASSERT_IRP_CONTEXT( IrpContext );
  1873. ASSERT_SCB( Scb );
  1874. ASSERT_EXCLUSIVE_SCB( Scb );
  1875. PAGED_CODE();
  1876. DebugTrace( +1, Dbg, ("NtfsDeleteAllocation\n") );
  1877. //
  1878. // Calculate new allocation size, assuming truncate.
  1879. //
  1880. SizeInBytes = LlBytesFromClusters( Vcb, StartingVcn );
  1881. ASSERT( (Scb->ScbSnapshot == NULL) ||
  1882. (Scb->ScbSnapshot->LowestModifiedVcn <= StartingVcn) );
  1883. //
  1884. // If this is a sparse deallocation, then we will have to call
  1885. // NtfsAddAttributeAllocation at the end to complete the fixup.
  1886. //
  1887. if (EndingVcn != MAXLONGLONG) {
  1888. AddSpaceBack = TRUE;
  1889. //
  1890. // If we have not written anything beyond the last Vcn to be
  1891. // deleted, then we can actually call FsRtlSplitLargeMcb to
  1892. // slide the allocated space up and keep the file contiguous!
  1893. //
  1894. // Ignore this if this is the Mft and we are creating a hole or
  1895. // if we are in the process of changing the compression state.
  1896. //
  1897. // If we were called from either SetEOF or SetAllocation for a
  1898. // compressed file then we can be doing a flush for the last
  1899. // page of the file as a result of a call to CcSetFileSizes.
  1900. // In this case we don't want to split the Mcb because we could
  1901. // reenter CcSetFileSizes and throw away the last page.
  1902. //
  1903. if (FlagOn( Scb->ScbState, SCB_STATE_WRITE_COMPRESSED ) &&
  1904. (EndingVcn >= LlClustersFromBytesTruncate( Vcb,
  1905. ((Scb->ValidDataToDisk + Scb->CompressionUnit - 1) &
  1906. ~((LONGLONG) (Scb->CompressionUnit - 1))))) &&
  1907. (Scb != Vcb->MftScb) &&
  1908. !FlagOn( Scb->ScbState, SCB_STATE_REALLOCATE_ON_WRITE ) &&
  1909. ((IrpContext == IrpContext->TopLevelIrpContext) ||
  1910. (IrpContext->TopLevelIrpContext->MajorFunction != IRP_MJ_SET_INFORMATION))) {
  1911. ASSERT( Scb->CompressionUnit != 0 );
  1912. //
  1913. // If we are going to split the Mcb, then make sure it is fully loaded.
  1914. // Do not bother to split if there are multiple ranges involved, so we
  1915. // do not end up rewriting lots of file records.
  1916. //
  1917. if (NtfsPreloadAllocation(IrpContext, Scb, StartingVcn, MAXLONGLONG) <= 1) {
  1918. SizeInClusters = (EndingVcn - StartingVcn) + 1;
  1919. ASSERT( NtfsIsTypeCodeUserData( Scb->AttributeTypeCode ));
  1920. SplitMcb = NtfsSplitNtfsMcb( &Scb->Mcb, StartingVcn, SizeInClusters );
  1921. //
  1922. // If the delete is off the end, we can get out.
  1923. //
  1924. if (!SplitMcb) {
  1925. return;
  1926. }
  1927. //
  1928. // We must already have a snapshot to make sure the mcb is unloaded if
  1929. // something goes wrong
  1930. //
  1931. ASSERT( Scb->ScbSnapshot != NULL );
  1932. //
  1933. // We must protect the call below with a try-finally in
  1934. // order to unload the Split Mcb. If there is no transaction
  1935. // underway then a release of the Scb would cause the
  1936. // snapshot to go away.
  1937. //
  1938. try {
  1939. //
  1940. // We are not properly synchronized to change AllocationSize,
  1941. // so we will delete any clusters that may have slid off the
  1942. // end. Since we are going to smash EndingVcn soon anyway,
  1943. // use it as a scratch to hold AllocationSize in Vcns...
  1944. //
  1945. EndingVcn = LlClustersFromBytes(Vcb, Scb->Header.AllocationSize.QuadPart);
  1946. NtfsDeallocateClusters( IrpContext,
  1947. Vcb,
  1948. Scb,
  1949. EndingVcn,
  1950. MAXLONGLONG,
  1951. &Scb->TotalAllocated );
  1952. } finally {
  1953. if (AbnormalTermination() && (IrpContext->TransactionId == 0)) {
  1954. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  1955. StartingVcn,
  1956. MAXLONGLONG,
  1957. FALSE,
  1958. FALSE );
  1959. }
  1960. }
  1961. NtfsUnloadNtfsMcbRange( &Scb->Mcb,
  1962. EndingVcn,
  1963. MAXLONGLONG,
  1964. TRUE,
  1965. FALSE );
  1966. //
  1967. // Since we did a split, jam highest modified all the way up.
  1968. //
  1969. Scb->ScbSnapshot->HighestModifiedVcn = MAXLONGLONG;
  1970. //
  1971. // We will have to redo all of the allocation to the end now.
  1972. //
  1973. EndingVcn = MAXLONGLONG;
  1974. }
  1975. }
  1976. }
  1977. //
  1978. // Now make the call to delete the allocation (if we did not just split
  1979. // the Mcb), and get out if we didn't have to do anything, because a
  1980. // hole is being created where there is already a hole.
  1981. //
  1982. if (!SplitMcb &&
  1983. !NtfsDeallocateClusters( IrpContext,
  1984. Vcb,
  1985. Scb,
  1986. StartingVcn,
  1987. EndingVcn,
  1988. &Scb->TotalAllocated ) &&
  1989. EndingVcn != MAXLONGLONG) {
  1990. return;
  1991. }
  1992. //
  1993. // On successful truncates, we nuke the entire range here.
  1994. //
  1995. if (!SplitMcb && (EndingVcn == MAXLONGLONG)) {
  1996. NtfsUnloadNtfsMcbRange( &Scb->Mcb, StartingVcn, MAXLONGLONG, TRUE, FALSE );
  1997. }
  1998. //
  1999. // Prepare for looking up attribute records to get the retrieval
  2000. // information.
  2001. //
  2002. NtfsInitializeAttributeContext( &Context );
  2003. NtfsInitializeAttributeContext( &TempContext );
  2004. try {
  2005. //
  2006. // Lookup the attribute record so we can ultimately delete space to it.
  2007. //
  2008. NtfsLookupAttributeForScb( IrpContext, Scb, &StartingVcn, &Context );
  2009. //
  2010. // Now loop to delete the space to the file record. Do not do this if LogIt
  2011. // is FALSE, as this is someone trying to delete the entire file
  2012. // record, so we do not have to clean up the attribute record.
  2013. //
  2014. if (LogIt) {
  2015. do {
  2016. Attribute = NtfsFoundAttribute(&Context);
  2017. //
  2018. // If there is no overlap, then continue.
  2019. //
  2020. if ((Attribute->Form.Nonresident.HighestVcn < StartingVcn) ||
  2021. (Attribute->Form.Nonresident.LowestVcn > EndingVcn)) {
  2022. continue;
  2023. //
  2024. // If all of the allocation is going away, then delete the entire
  2025. // record. We have to show that the allocation is already deleted
  2026. // to avoid being called back via NtfsDeleteAttributeRecord! We
  2027. // avoid this for the first instance of this attribute.
  2028. //
  2029. } else if ((Attribute->Form.Nonresident.LowestVcn >= StartingVcn) &&
  2030. (EndingVcn == MAXLONGLONG) &&
  2031. (Attribute->Form.Nonresident.LowestVcn != 0)) {
  2032. NtfsDeleteAttributeRecord( IrpContext,
  2033. Scb->Fcb,
  2034. (LogIt ? DELETE_LOG_OPERATION : 0) |
  2035. DELETE_RELEASE_FILE_RECORD,
  2036. &Context );
  2037. //
  2038. // If just part of the allocation is going away, then make the
  2039. // call here to reconstruct the mapping pairs array.
  2040. //
  2041. } else {
  2042. //
  2043. // If this is the end of a sparse deallocation, then break out
  2044. // because we will rewrite this file record below anyway.
  2045. //
  2046. if (EndingVcn <= Attribute->Form.Nonresident.HighestVcn) {
  2047. break;
  2048. //
  2049. // If we split the Mcb, then make sure we only regenerate the
  2050. // mapping pairs once at the split point (but continue to
  2051. // scan for any entire records to delete).
  2052. //
  2053. } else if (SplitMcb) {
  2054. continue;
  2055. }
  2056. //
  2057. // If this is a sparse deallocation, then we have to call to
  2058. // add the allocation, since it is possible that the file record
  2059. // must split.
  2060. //
  2061. if (EndingVcn != MAXLONGLONG) {
  2062. //
  2063. // Compute the last Vcn in the file, Then remember if it is smaller,
  2064. // because that is the last one we will delete to, in that case.
  2065. //
  2066. Vcn1 = Attribute->Form.Nonresident.HighestVcn;
  2067. SizeInClusters = (Vcn1 - Attribute->Form.Nonresident.LowestVcn) + 1;
  2068. Vcn1 = Attribute->Form.Nonresident.LowestVcn;
  2069. NtfsCleanupAttributeContext( IrpContext, &TempContext );
  2070. NtfsInitializeAttributeContext( &TempContext );
  2071. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &TempContext );
  2072. NtfsAddAttributeAllocation( IrpContext,
  2073. Scb,
  2074. &TempContext,
  2075. &Vcn1,
  2076. &SizeInClusters );
  2077. //
  2078. // Since we used a temporary context we will need to
  2079. // restart the scan from the first file record. We update
  2080. // the range to deallocate by the last operation. In most
  2081. // cases we will only need to modify one file record and
  2082. // we can exit this loop.
  2083. //
  2084. StartingVcn = Vcn1 + SizeInClusters;
  2085. if (StartingVcn > EndingVcn) {
  2086. break;
  2087. }
  2088. NtfsCleanupAttributeContext( IrpContext, &Context );
  2089. NtfsInitializeAttributeContext( &Context );
  2090. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
  2091. continue;
  2092. //
  2093. // Otherwise, we can simply delete the allocation, because
  2094. // we know the file record cannot grow.
  2095. //
  2096. } else {
  2097. Vcn1 = StartingVcn - 1;
  2098. NtfsDeleteAttributeAllocation( IrpContext,
  2099. Scb,
  2100. LogIt,
  2101. &Vcn1,
  2102. &Context,
  2103. TRUE );
  2104. //
  2105. // The call above will update the allocation size and
  2106. // set the new file sizes on disk.
  2107. //
  2108. UpdatedAllocationSize = TRUE;
  2109. }
  2110. }
  2111. } while (NtfsLookupNextAttributeForScb(IrpContext, Scb, &Context));
  2112. //
  2113. // If this deletion makes the file sparse, then we have to call
  2114. // NtfsAddAttributeAllocation to regenerate the mapping pairs.
  2115. // Note that potentially they may no longer fit, and we could actually
  2116. // have to add a file record.
  2117. //
  2118. if (AddSpaceBack) {
  2119. //
  2120. // If we did not just split the Mcb, we have to calculate the
  2121. // SizeInClusters parameter for NtfsAddAttributeAllocation.
  2122. //
  2123. if (!SplitMcb) {
  2124. //
  2125. // Compute the last Vcn in the file, Then remember if it is smaller,
  2126. // because that is the last one we will delete to, in that case.
  2127. //
  2128. Vcn1 = Attribute->Form.Nonresident.HighestVcn;
  2129. //
  2130. // Get out if there is nothing to delete.
  2131. //
  2132. if (Vcn1 < StartingVcn) {
  2133. try_return(NOTHING);
  2134. }
  2135. SizeInClusters = (Vcn1 - Attribute->Form.Nonresident.LowestVcn) + 1;
  2136. Vcn1 = Attribute->Form.Nonresident.LowestVcn;
  2137. NtfsCleanupAttributeContext( IrpContext, &Context );
  2138. NtfsInitializeAttributeContext( &Context );
  2139. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
  2140. NtfsAddAttributeAllocation( IrpContext,
  2141. Scb,
  2142. &Context,
  2143. &Vcn1,
  2144. &SizeInClusters );
  2145. } else {
  2146. NtfsCleanupAttributeContext( IrpContext, &Context );
  2147. NtfsInitializeAttributeContext( &Context );
  2148. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &Context );
  2149. NtfsAddAttributeAllocation( IrpContext,
  2150. Scb,
  2151. &Context,
  2152. NULL,
  2153. NULL );
  2154. }
  2155. //
  2156. // If we truncated the file by removing a file record but didn't update
  2157. // the new allocation size then do so now. We don't have to worry about
  2158. // this for the sparse deallocation path.
  2159. //
  2160. } else if (!UpdatedAllocationSize) {
  2161. Scb->Header.AllocationSize.QuadPart = SizeInBytes;
  2162. if (Scb->Header.ValidDataLength.QuadPart > SizeInBytes) {
  2163. Scb->Header.ValidDataLength.QuadPart = SizeInBytes;
  2164. }
  2165. if (Scb->Header.FileSize.QuadPart > SizeInBytes) {
  2166. Scb->Header.FileSize.QuadPart = SizeInBytes;
  2167. }
  2168. //
  2169. // Possibly update ValidDataToDisk
  2170. //
  2171. if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK ) &&
  2172. (SizeInBytes < Scb->ValidDataToDisk)) {
  2173. Scb->ValidDataToDisk = SizeInBytes;
  2174. }
  2175. }
  2176. }
  2177. //
  2178. // If this was a sparse deallocation, it is time to get out once we
  2179. // have fixed up the allocation information.
  2180. //
  2181. if (SplitMcb || (EndingVcn != MAXLONGLONG)) {
  2182. try_return(NOTHING);
  2183. }
  2184. //
  2185. // We update the allocation size in the attribute, only for normal
  2186. // truncates (AddAttributeAllocation does this for SplitMcb case).
  2187. //
  2188. if (LogIt) {
  2189. #ifdef BENL_DBG
  2190. BOOLEAN WroteIt;
  2191. WroteIt =
  2192. #endif
  2193. NtfsWriteFileSizes( IrpContext,
  2194. Scb,
  2195. &Scb->Header.ValidDataLength.QuadPart,
  2196. FALSE,
  2197. TRUE,
  2198. TRUE );
  2199. #ifdef BENL_DBG
  2200. ASSERT( WroteIt );
  2201. #endif
  2202. }
  2203. //
  2204. // Free any reserved clusters in the space freed.
  2205. //
  2206. if ((EndingVcn == MAXLONGLONG) && (Scb->CompressionUnit != 0)) {
  2207. NtfsFreeReservedClusters( Scb,
  2208. LlBytesFromClusters(Vcb, StartingVcn),
  2209. 0 );
  2210. }
  2211. try_exit: NOTHING;
  2212. } finally {
  2213. //
  2214. // If we raised and have split the mcb and have not started a transaction
  2215. // throw out the modified range of the mcb. We do this because in this case the
  2216. // snapshot can then be discarded by a caller releasing the fcb involved see NtfsZeroData
  2217. // so that the normal truncation that occurs in ProcessException will be skipped
  2218. //
  2219. if (AbnormalTermination() && SplitMcb && (IrpContext->TransactionId == 0)) {
  2220. ASSERT( Scb->ScbSnapshot );
  2221. //
  2222. // Unload any modified ranges in the Mcb.
  2223. //
  2224. NtfsUnloadNtfsMcbRange( &Scb->Mcb, Scb->ScbSnapshot->LowestModifiedVcn, MAXLONGLONG, FALSE, FALSE );
  2225. }
  2226. DebugUnwind( NtfsDeleteAllocationInternal );
  2227. //
  2228. // Cleanup the attribute context on the way out.
  2229. //
  2230. NtfsCleanupAttributeContext( IrpContext, &Context );
  2231. NtfsCleanupAttributeContext( IrpContext, &TempContext );
  2232. }
  2233. DebugTrace( -1, Dbg, ("NtfsDeleteAllocationInternal -> VOID\n") );
  2234. return;
  2235. }
  2236. ULONG
  2237. NtfsGetSizeForMappingPairs (
  2238. IN PNTFS_MCB Mcb,
  2239. IN ULONG BytesAvailable,
  2240. IN VCN LowestVcn,
  2241. IN PVCN StopOnVcn OPTIONAL,
  2242. OUT PVCN StoppedOnVcn
  2243. )
  2244. /*++
  2245. Routine Description:
  2246. This routine calculates the size required to describe the given Mcb in
  2247. a mapping pairs array. The caller may specify how many bytes are available
  2248. for mapping pairs storage, for the event that the entire Mcb cannot be
  2249. be represented. In any case, StoppedOnVcn returns the Vcn to supply to
  2250. NtfsBuildMappingPairs in order to generate the specified number of bytes.
  2251. In the event that the entire Mcb could not be described in the bytes available,
  2252. StoppedOnVcn is also the correct value to specify to resume the building
  2253. of mapping pairs in a subsequent record.
  2254. Arguments:
  2255. Mcb - The Mcb describing new allocation.
  2256. BytesAvailable - Bytes available for storing mapping pairs. This routine
  2257. is guaranteed to stop before returning a count greater
  2258. than this.
  2259. LowestVcn - Lowest Vcn field applying to the mapping pairs array
  2260. StopOnVcn - If specified, calculating size at the first run starting with a Vcn
  2261. beyond the specified Vcn
  2262. StoppedOnVcn - Returns the Vcn on which a stop was necessary, or xxMax if
  2263. the entire Mcb could be stored. This Vcn should be
  2264. subsequently supplied to NtfsBuildMappingPairs to generate
  2265. the calculated number of bytes.
  2266. Return Value:
  2267. Size required required for entire new array in bytes.
  2268. --*/
  2269. {
  2270. VCN NextVcn, CurrentVcn, LimitVcn;
  2271. LCN CurrentLcn;
  2272. VCN RunVcn;
  2273. LCN RunLcn;
  2274. BOOLEAN Found;
  2275. LONGLONG RunCount;
  2276. VCN HighestVcn;
  2277. PVOID RangePtr;
  2278. ULONG RunIndex;
  2279. ULONG MSize = 0;
  2280. ULONG LastSize = 0;
  2281. BOOLEAN FoundRun = FALSE;
  2282. PAGED_CODE();
  2283. HighestVcn = MAXLONGLONG;
  2284. //
  2285. // Initialize CurrentLcn as it will be initialized for decode.
  2286. //
  2287. CurrentLcn = 0;
  2288. NextVcn = RunVcn = LowestVcn;
  2289. //
  2290. // Limit ourselves to less than 32 bits for each mapping pair range.
  2291. // We use -2 here because we point to the Vcn to stop on, the length
  2292. // is one greater.
  2293. //
  2294. LimitVcn = MAXLONGLONG - 1;
  2295. //
  2296. // Use the input stop point if smaller.
  2297. //
  2298. if (ARGUMENT_PRESENT( StopOnVcn )) {
  2299. LimitVcn = *StopOnVcn;
  2300. }
  2301. Found = NtfsLookupNtfsMcbEntry( Mcb, RunVcn, &RunLcn, &RunCount, NULL, NULL, &RangePtr, &RunIndex );
  2302. //
  2303. // Loop through the Mcb to calculate the size of the mapping array.
  2304. //
  2305. while (TRUE) {
  2306. LONGLONG Change;
  2307. PCHAR cp;
  2308. //
  2309. // See if there is another entry in the Mcb.
  2310. //
  2311. if (!Found) {
  2312. //
  2313. // If the caller did not specify StopOnVcn, then break out.
  2314. //
  2315. if (!ARGUMENT_PRESENT(StopOnVcn)) {
  2316. break;
  2317. }
  2318. //
  2319. // Otherwise, describe the "hole" up to and including the
  2320. // Vcn we are stopping on.
  2321. //
  2322. RunVcn = NextVcn;
  2323. RunLcn = UNUSED_LCN;
  2324. RunCount = (LimitVcn - RunVcn) + 1;
  2325. RunIndex = MAXULONG - 1;
  2326. //
  2327. // If this is the first non-hole then we need to enforce a cluster
  2328. // per range limit.
  2329. //
  2330. } else if (!FoundRun &&
  2331. (RunLcn != UNUSED_LCN)) {
  2332. if ((LowestVcn + MAX_CLUSTERS_PER_RANGE) <= LimitVcn) {
  2333. //
  2334. // If we are already beyond the limit then set
  2335. // the limit back to just before the current run.
  2336. // We allow a hole which is larger than our limit.
  2337. //
  2338. if (RunVcn >= MAX_CLUSTERS_PER_RANGE) {
  2339. LimitVcn = RunVcn - 1;
  2340. } else {
  2341. LimitVcn = LowestVcn + MAX_CLUSTERS_PER_RANGE - 1;
  2342. }
  2343. }
  2344. //
  2345. // Other checks in the system should prevent rollover.
  2346. //
  2347. ASSERT( (LimitVcn + 1) >= LowestVcn );
  2348. FoundRun = TRUE;
  2349. }
  2350. //
  2351. // If we were asked to stop after a certain Vcn, or we have
  2352. // exceeded our limit then stop now.
  2353. //
  2354. if (RunVcn > LimitVcn) {
  2355. if (HighestVcn == MAXLONGLONG) {
  2356. HighestVcn = LimitVcn + 1;
  2357. }
  2358. break;
  2359. //
  2360. // If this run extends beyond the current end of this attribute
  2361. // record, then we still need to stop where we are supposed to
  2362. // after outputting this run.
  2363. //
  2364. } else if ((RunVcn + RunCount) > LimitVcn) {
  2365. HighestVcn = LimitVcn + 1;
  2366. }
  2367. //
  2368. // Advance the RunIndex for the next call.
  2369. //
  2370. RunIndex += 1;
  2371. //
  2372. // Add in one for the count byte.
  2373. //
  2374. MSize += 1;
  2375. //
  2376. // NextVcn becomes current Vcn and we calculate the new NextVcn.
  2377. //
  2378. CurrentVcn = RunVcn;
  2379. NextVcn = RunVcn + RunCount;
  2380. //
  2381. // Calculate the Vcn change to store.
  2382. //
  2383. Change = NextVcn - CurrentVcn;
  2384. //
  2385. // Now calculate the first byte to actually output
  2386. //
  2387. if (Change < 0) {
  2388. GetNegativeByte( (PLARGE_INTEGER)&Change, &cp );
  2389. } else {
  2390. GetPositiveByte( (PLARGE_INTEGER)&Change, &cp );
  2391. }
  2392. //
  2393. // Now add in the number of Vcn change bytes.
  2394. //
  2395. MSize += (ULONG)(cp - (PCHAR)&Change + 1);
  2396. //
  2397. // Do not output any Lcn bytes if it is the unused Lcn.
  2398. //
  2399. if (RunLcn != UNUSED_LCN) {
  2400. //
  2401. // Calculate the Lcn change to store.
  2402. //
  2403. Change = RunLcn - CurrentLcn;
  2404. //
  2405. // Now calculate the first byte to actually output
  2406. //
  2407. if (Change < 0) {
  2408. GetNegativeByte( (PLARGE_INTEGER)&Change, &cp );
  2409. } else {
  2410. GetPositiveByte( (PLARGE_INTEGER)&Change, &cp );
  2411. }
  2412. //
  2413. // Now add in the number of Lcn change bytes.
  2414. //
  2415. MSize += (ULONG)(cp - (PCHAR)&Change + 1);
  2416. CurrentLcn = RunLcn;
  2417. //
  2418. // If this is the first run then enforce the 32 bit limit.
  2419. //
  2420. if (!FoundRun) {
  2421. if ((LowestVcn + MAX_CLUSTERS_PER_RANGE - 1) < LimitVcn) {
  2422. LimitVcn = LowestVcn + MAX_CLUSTERS_PER_RANGE - 1;
  2423. }
  2424. FoundRun = TRUE;
  2425. }
  2426. }
  2427. //
  2428. // Now see if we can still store the required number of bytes,
  2429. // and get out if not.
  2430. //
  2431. if ((MSize + 1) > BytesAvailable) {
  2432. HighestVcn = RunVcn;
  2433. MSize = LastSize;
  2434. break;
  2435. }
  2436. //
  2437. // Now advance some locals before looping back.
  2438. //
  2439. LastSize = MSize;
  2440. Found = NtfsGetSequentialMcbEntry( Mcb, &RangePtr, RunIndex, &RunVcn, &RunLcn, &RunCount );
  2441. }
  2442. //
  2443. // The caller had sufficient bytes available to store at least one
  2444. // run, or that we were able to process the entire (empty) Mcb.
  2445. //
  2446. ASSERT( (MSize != 0) || (HighestVcn == LimitVcn + 1) );
  2447. //
  2448. // Return the Vcn we stopped on (or xxMax) and the size caculated,
  2449. // adding one for the terminating 0.
  2450. //
  2451. *StoppedOnVcn = HighestVcn;
  2452. return MSize + 1;
  2453. }
  2454. BOOLEAN
  2455. NtfsBuildMappingPairs (
  2456. IN PNTFS_MCB Mcb,
  2457. IN VCN LowestVcn,
  2458. IN OUT PVCN HighestVcn,
  2459. OUT PCHAR MappingPairs
  2460. )
  2461. /*++
  2462. Routine Description:
  2463. This routine builds a new mapping pairs array or adds to an old one.
  2464. At this time, this routine only supports adding to the end of the
  2465. Mapping Pairs Array.
  2466. Arguments:
  2467. Mcb - The Mcb describing new allocation.
  2468. LowestVcn - Lowest Vcn field applying to the mapping pairs array
  2469. HighestVcn - On input supplies the highest Vcn, after which we are to stop.
  2470. On output, returns the actual Highest Vcn represented in the
  2471. MappingPairs array, or LlNeg1 if the array is empty.
  2472. MappingPairs - Points to the current mapping pairs array to be extended.
  2473. To build a new array, the byte pointed to must contain 0.
  2474. Return Value:
  2475. BOOLEAN - TRUE if this mapping pair only describes a hole, FALSE otherwise.
  2476. --*/
  2477. {
  2478. VCN NextVcn, CurrentVcn;
  2479. LCN CurrentLcn;
  2480. VCN RunVcn;
  2481. LCN RunLcn;
  2482. BOOLEAN Found;
  2483. LONGLONG RunCount;
  2484. PVOID RangePtr;
  2485. ULONG RunIndex;
  2486. BOOLEAN SingleHole = TRUE;
  2487. PAGED_CODE();
  2488. //
  2489. // Initialize NextVcn and CurrentLcn as they will be initialized for decode.
  2490. //
  2491. CurrentLcn = 0;
  2492. NextVcn = RunVcn = LowestVcn;
  2493. Found = NtfsLookupNtfsMcbEntry( Mcb, RunVcn, &RunLcn, &RunCount, NULL, NULL, &RangePtr, &RunIndex );
  2494. //
  2495. // Loop through the Mcb to calculate the size of the mapping array.
  2496. //
  2497. while (TRUE) {
  2498. LONGLONG ChangeV, ChangeL;
  2499. PCHAR cp;
  2500. ULONG SizeV;
  2501. ULONG SizeL;
  2502. //
  2503. // See if there is another entry in the Mcb.
  2504. //
  2505. if (!Found) {
  2506. //
  2507. // Break out in the normal case
  2508. //
  2509. if (*HighestVcn == MAXLONGLONG) {
  2510. break;
  2511. }
  2512. //
  2513. // Otherwise, describe the "hole" up to and including the
  2514. // Vcn we are stopping on.
  2515. //
  2516. RunVcn = NextVcn;
  2517. RunLcn = UNUSED_LCN;
  2518. RunCount = *HighestVcn - NextVcn;
  2519. RunIndex = MAXULONG - 1;
  2520. }
  2521. //
  2522. // Advance the RunIndex for the next call.
  2523. //
  2524. RunIndex += 1;
  2525. //
  2526. // Exit loop if we hit the HighestVcn we are looking for.
  2527. //
  2528. if (RunVcn >= *HighestVcn) {
  2529. break;
  2530. }
  2531. //
  2532. // This run may go beyond the highest we are looking for, if so
  2533. // we need to shrink the count.
  2534. //
  2535. if ((RunVcn + RunCount) > *HighestVcn) {
  2536. RunCount = *HighestVcn - RunVcn;
  2537. }
  2538. //
  2539. // NextVcn becomes current Vcn and we calculate the new NextVcn.
  2540. //
  2541. CurrentVcn = RunVcn;
  2542. NextVcn = RunVcn + RunCount;
  2543. //
  2544. // Calculate the Vcn change to store.
  2545. //
  2546. ChangeV = NextVcn - CurrentVcn;
  2547. //
  2548. // Now calculate the first byte to actually output
  2549. //
  2550. if (ChangeV < 0) {
  2551. GetNegativeByte( (PLARGE_INTEGER)&ChangeV, &cp );
  2552. } else {
  2553. GetPositiveByte( (PLARGE_INTEGER)&ChangeV, &cp );
  2554. }
  2555. //
  2556. // Now add in the number of Vcn change bytes.
  2557. //
  2558. SizeV = (ULONG)(cp - (PCHAR)&ChangeV + 1);
  2559. //
  2560. // Do not output any Lcn bytes if it is the unused Lcn.
  2561. //
  2562. SizeL = 0;
  2563. if (RunLcn != UNUSED_LCN) {
  2564. //
  2565. // Calculate the Lcn change to store.
  2566. //
  2567. ChangeL = RunLcn - CurrentLcn;
  2568. //
  2569. // Now calculate the first byte to actually output
  2570. //
  2571. if (ChangeL < 0) {
  2572. GetNegativeByte( (PLARGE_INTEGER)&ChangeL, &cp );
  2573. } else {
  2574. GetPositiveByte( (PLARGE_INTEGER)&ChangeL, &cp );
  2575. }
  2576. //
  2577. // Now add in the number of Lcn change bytes.
  2578. //
  2579. SizeL = (ULONG)(cp - (PCHAR)&ChangeL) + 1;
  2580. //
  2581. // Now advance CurrentLcn before looping back.
  2582. //
  2583. CurrentLcn = RunLcn;
  2584. SingleHole = FALSE;
  2585. }
  2586. //
  2587. // Now we can produce our outputs to the MappingPairs array.
  2588. //
  2589. *MappingPairs++ = (CHAR)(SizeV + (SizeL * 16));
  2590. while (SizeV != 0) {
  2591. *MappingPairs++ = (CHAR)(((ULONG)ChangeV) & 0xFF);
  2592. ChangeV = ChangeV >> 8;
  2593. SizeV -= 1;
  2594. }
  2595. while (SizeL != 0) {
  2596. *MappingPairs++ = (CHAR)(((ULONG)ChangeL) & 0xFF);
  2597. ChangeL = ChangeL >> 8;
  2598. SizeL -= 1;
  2599. }
  2600. Found = NtfsGetSequentialMcbEntry( Mcb, &RangePtr, RunIndex, &RunVcn, &RunLcn, &RunCount );
  2601. }
  2602. //
  2603. // Terminate the size with a 0 byte.
  2604. //
  2605. *MappingPairs = 0;
  2606. //
  2607. // Also return the actual highest Vcn.
  2608. //
  2609. *HighestVcn = NextVcn - 1;
  2610. return SingleHole;
  2611. }
  2612. VCN
  2613. NtfsGetHighestVcn (
  2614. IN PIRP_CONTEXT IrpContext,
  2615. IN VCN LowestVcn,
  2616. IN PCHAR EndOfMappingPairs,
  2617. IN PCHAR MappingPairs
  2618. )
  2619. /*++
  2620. Routine Description:
  2621. This routine returns the highest Vcn from a mapping pairs array. This
  2622. routine is intended for restart, in order to update the HighestVcn field
  2623. and possibly AllocatedLength in an attribute record after updating the
  2624. MappingPairs array.
  2625. Arguments:
  2626. LowestVcn - Lowest Vcn field applying to the mapping pairs array
  2627. EndOfMappingPairs - Points to the byte RIGHT AFTER the mapping pairs array
  2628. MappingPairs - Points to the mapping pairs array from which the highest
  2629. Vcn is to be extracted.
  2630. Return Value:
  2631. The Highest Vcn represented by the MappingPairs array.
  2632. --*/
  2633. {
  2634. VCN CurrentVcn, NextVcn;
  2635. ULONG VcnBytes, LcnBytes;
  2636. LONGLONG Change;
  2637. PCHAR ch = MappingPairs;
  2638. PCHAR VcnStart;
  2639. PAGED_CODE();
  2640. //
  2641. // Implement the decompression algorithm, as defined in ntfs.h.
  2642. //
  2643. NextVcn = LowestVcn;
  2644. ch = MappingPairs;
  2645. //
  2646. // Loop to process mapping pairs.
  2647. //
  2648. while ((ch < EndOfMappingPairs) && !IsCharZero(*ch)) {
  2649. //
  2650. // Set Current Vcn from initial value or last pass through loop.
  2651. //
  2652. CurrentVcn = NextVcn;
  2653. //
  2654. // Extract the counts from the two nibbles of this byte.
  2655. //
  2656. VcnBytes = *ch & 0xF;
  2657. LcnBytes = (*ch++ >> 4) & 0xF; // avoid sign extended case
  2658. VcnStart = ch;
  2659. //
  2660. // Calculate next mapping pair location first and check for buffer overrun
  2661. //
  2662. ch += VcnBytes + LcnBytes;
  2663. //
  2664. // Extract the Vcn change (use of RtlCopyMemory works for little-Endian)
  2665. // and update NextVcn.
  2666. //
  2667. Change = 0;
  2668. if ((ch > EndOfMappingPairs) ||
  2669. VcnBytes > 8 ||
  2670. LcnBytes > 8 ||
  2671. IsCharLtrZero(*(VcnStart + VcnBytes - 1))) {
  2672. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, NULL );
  2673. }
  2674. RtlCopyMemory( &Change, VcnStart, VcnBytes );
  2675. NextVcn = NextVcn + Change;
  2676. }
  2677. Change = NextVcn - 1;
  2678. return *(PVCN)&Change;
  2679. }
  2680. BOOLEAN
  2681. NtfsReserveClusters (
  2682. IN PIRP_CONTEXT IrpContext OPTIONAL,
  2683. IN PSCB Scb,
  2684. IN LONGLONG FileOffset,
  2685. IN ULONG ByteCount
  2686. )
  2687. /*++
  2688. Routine Description:
  2689. This routine reserves all clusters that would be required to write
  2690. the full range of compression units covered by the described range
  2691. of Vcns. All clusters in the range are reserved, without regard to
  2692. how many clusters are already reserved in that range. Not paying
  2693. attention to how many clusters are already allocated in that range
  2694. is not only a simplification, but it is also necessary, since we
  2695. sometimes deallocate all existing clusters anyway, and make them
  2696. ineligible for reallocation in the same transaction. Thus in the
  2697. worst case you do always need an additional 16 clusters when a
  2698. compression unit is first modified. Note that although we could
  2699. specifically reserve (double-reserve, in fact) the entire allocation
  2700. size of the stream, when reserving from the volume, we never reserve
  2701. more than AllocationSize + MM_MAXIMUM_DISK_IO_SIZE - size actually
  2702. allocated, since the worst we could ever need to doubly allocate is
  2703. limited by the maximum flush size.
  2704. For user-mapped streams, we have no way of keeping track of dirty
  2705. pages, so we effectivel always reserve AllocationSize +
  2706. MM_MAXIMUM_DISK_IO_SIZE.
  2707. This routine is called from FastIo, and therefore has no IrpContext.
  2708. Arguments:
  2709. IrpContext - If IrpContext is not specified, then not all data is
  2710. available to determine if the clusters can be reserved,
  2711. and FALSE may be returned unnecessarily. This case
  2712. is intended for the fast I/O path, which will just
  2713. force us to take the long path to write.
  2714. Scb - Address of a compressed stream for which we are reserving space
  2715. FileOffset - Starting byte being modified by caller
  2716. ByteCount - Number of bytes being modified by caller
  2717. Return Value:
  2718. FALSE if not all clusters could be reserved
  2719. TRUE if all clusters were reserved
  2720. --*/
  2721. {
  2722. ULONG FirstBit, LastBit, CurrentLastBit;
  2723. ULONG FirstRange, LastRange;
  2724. PRESERVED_BITMAP_RANGE FreeBitmap, NextBitmap, CurrentBitmap;
  2725. ULONG CompressionShift;
  2726. PVCB Vcb = Scb->Vcb;
  2727. ULONG SizeTemp;
  2728. LONGLONG TempL;
  2729. PVOID NewBitmapBuffer;
  2730. BOOLEAN ReturnValue = FALSE;
  2731. ULONG MappedFile;
  2732. BOOLEAN FlippedBit = FALSE;
  2733. ASSERT( Scb->Header.NodeTypeCode == NTFS_NTC_SCB_DATA );
  2734. //
  2735. // Nothing to do if byte count is zero.
  2736. //
  2737. if (ByteCount == 0) { return TRUE; }
  2738. //
  2739. // Calculate first and last bits to reserve.
  2740. //
  2741. CompressionShift = Vcb->ClusterShift + (ULONG)Scb->CompressionUnitShift;
  2742. FirstBit = ((ULONG) Int64ShraMod32( FileOffset, (CompressionShift) )) & NTFS_BITMAP_RANGE_MASK;
  2743. FirstRange = (ULONG) Int64ShraMod32( FileOffset, CompressionShift + NTFS_BITMAP_RANGE_SHIFT );
  2744. LastBit = ((ULONG) Int64ShraMod32( FileOffset + ByteCount - 1, CompressionShift )) & NTFS_BITMAP_RANGE_MASK;
  2745. LastRange = (ULONG) Int64ShraMod32( FileOffset + ByteCount - 1,
  2746. CompressionShift + NTFS_BITMAP_RANGE_SHIFT );
  2747. MappedFile = FlagOn( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE );
  2748. //
  2749. // Make sure we started with numbers in range.
  2750. //
  2751. ASSERT( (((LONGLONG) FirstRange << (CompressionShift + NTFS_BITMAP_RANGE_SHIFT)) +
  2752. ((LONGLONG)(FirstBit + 1) << CompressionShift)) > FileOffset );
  2753. ASSERT( (FirstRange < LastRange) || (LastBit >= FirstBit) );
  2754. ASSERT( FileOffset + ByteCount <= Scb->Header.AllocationSize.QuadPart );
  2755. //
  2756. // Purge the cache since getting the bitmap may be blocked behind the mft
  2757. // which needs to wait for the cache to purge
  2758. //
  2759. if (IrpContext) {
  2760. NtfsPurgeFileRecordCache( IrpContext );
  2761. }
  2762. NtfsAcquireResourceExclusive( IrpContext, Vcb->BitmapScb, TRUE );
  2763. NtfsAcquireReservedClusters( Vcb );
  2764. //
  2765. // Loop through all of the bitmap ranges for this request.
  2766. //
  2767. while (TRUE) {
  2768. CurrentBitmap = NULL;
  2769. //
  2770. // If we are at the last range then set the current last bit to
  2771. // our final last bit.
  2772. //
  2773. CurrentLastBit = LastBit;
  2774. if (FirstRange != LastRange) {
  2775. CurrentLastBit = NTFS_BITMAP_RANGE_MASK;
  2776. }
  2777. //
  2778. // If there is no bitmap then create the first entry in the list.
  2779. //
  2780. if (Scb->ScbType.Data.ReservedBitMap == NULL) {
  2781. //
  2782. // If we are at range zero and the bitcount is not
  2783. // too high then use the basic model.
  2784. //
  2785. if ((LastRange == 0) && (CurrentLastBit < NTFS_BITMAP_MAX_BASIC_SIZE)) {
  2786. SizeTemp = NtfsBasicBitmapSize( CurrentLastBit + 1 );
  2787. //
  2788. // Allocate a buffer for the basic bitmap.
  2789. //
  2790. CurrentBitmap = NtfsAllocatePoolNoRaise( PagedPool, SizeTemp );
  2791. //
  2792. // Initialize the data if there is no error.
  2793. //
  2794. if (CurrentBitmap == NULL) { goto AllocationFailure; }
  2795. //
  2796. // Initialize the new structure.
  2797. //
  2798. RtlZeroMemory( CurrentBitmap, SizeTemp );
  2799. RtlInitializeBitMap( &CurrentBitmap->Bitmap,
  2800. &CurrentBitmap->RangeOffset,
  2801. (SizeTemp - FIELD_OFFSET( RESERVED_BITMAP_RANGE, RangeOffset )) * 8);
  2802. //
  2803. // Allocate a link entry and create the bitmap. We will defer
  2804. // allocating the buffer for the bitmap until later.
  2805. //
  2806. } else {
  2807. CurrentBitmap = NtfsAllocatePoolNoRaise( PagedPool, sizeof( RESERVED_BITMAP_RANGE ));
  2808. if (CurrentBitmap == NULL) { goto AllocationFailure; }
  2809. RtlZeroMemory( CurrentBitmap, sizeof( RESERVED_BITMAP_RANGE ));
  2810. InitializeListHead( &CurrentBitmap->Links );
  2811. CurrentBitmap->RangeOffset = FirstRange;
  2812. }
  2813. //
  2814. // Update our pointer to the reserved bitmap.
  2815. //
  2816. Scb->ScbType.Data.ReservedBitMap = CurrentBitmap;
  2817. //
  2818. // Look through the existing ranges to find the range we are interested in.
  2819. // If we currently have the basic single bitmap structure
  2820. // then we can either use it or must convert it.
  2821. //
  2822. } else if (Scb->ScbType.Data.ReservedBitMap->Links.Flink == NULL) {
  2823. //
  2824. // If we are accessing range zero then grow the bitmap if necessary.
  2825. //
  2826. if ((FirstRange == 0) && (CurrentLastBit < NTFS_BITMAP_MAX_BASIC_SIZE)) {
  2827. //
  2828. // Remember this bitmap.
  2829. //
  2830. NextBitmap = Scb->ScbType.Data.ReservedBitMap;
  2831. if (CurrentLastBit >= NextBitmap->Bitmap.SizeOfBitMap) {
  2832. SizeTemp = NtfsBasicBitmapSize( CurrentLastBit + 1 );
  2833. CurrentBitmap = NtfsAllocatePoolNoRaise( PagedPool, SizeTemp );
  2834. if (CurrentBitmap == NULL) { goto AllocationFailure; }
  2835. RtlZeroMemory( CurrentBitmap, SizeTemp );
  2836. RtlInitializeBitMap( &CurrentBitmap->Bitmap,
  2837. &CurrentBitmap->RangeOffset,
  2838. (SizeTemp - FIELD_OFFSET( RESERVED_BITMAP_RANGE, RangeOffset )) * 8);
  2839. CurrentBitmap->BasicDirtyBits = NextBitmap->BasicDirtyBits;
  2840. RtlCopyMemory( CurrentBitmap->Bitmap.Buffer,
  2841. NextBitmap->Bitmap.Buffer,
  2842. NextBitmap->Bitmap.SizeOfBitMap / 8 );
  2843. //
  2844. // Now store this into the Scb.
  2845. //
  2846. Scb->ScbType.Data.ReservedBitMap = CurrentBitmap;
  2847. NtfsFreePool( NextBitmap );
  2848. } else {
  2849. CurrentBitmap = NextBitmap;
  2850. }
  2851. //
  2852. // Otherwise we want to convert to the linked list of bitmap ranges.
  2853. //
  2854. } else {
  2855. NextBitmap = NtfsAllocatePoolNoRaise( PagedPool, sizeof( RESERVED_BITMAP_RANGE ));
  2856. if (NextBitmap == NULL) { goto AllocationFailure; }
  2857. //
  2858. // Update the new structure.
  2859. //
  2860. RtlZeroMemory( NextBitmap, sizeof( RESERVED_BITMAP_RANGE ));
  2861. InitializeListHead( &NextBitmap->Links );
  2862. NextBitmap->DirtyBits = Scb->ScbType.Data.ReservedBitMap->BasicDirtyBits;
  2863. SizeTemp = Scb->ScbType.Data.ReservedBitMap->Bitmap.SizeOfBitMap / 8;
  2864. //
  2865. // We will use the existing bitmap as the buffer for the new bitmap.
  2866. // Move the bits to the start of the buffer and then zero
  2867. // the remaining bytes.
  2868. //
  2869. RtlMoveMemory( Scb->ScbType.Data.ReservedBitMap,
  2870. Scb->ScbType.Data.ReservedBitMap->Bitmap.Buffer,
  2871. SizeTemp );
  2872. RtlZeroMemory( Add2Ptr( Scb->ScbType.Data.ReservedBitMap, SizeTemp ),
  2873. sizeof( LIST_ENTRY ) + sizeof( RTL_BITMAP ));
  2874. //
  2875. // Limit ourselves to the maximum range size.
  2876. //
  2877. SizeTemp = (SizeTemp + sizeof( LIST_ENTRY ) + sizeof( RTL_BITMAP )) * 8;
  2878. if (SizeTemp > NTFS_BITMAP_RANGE_SIZE) {
  2879. SizeTemp = NTFS_BITMAP_RANGE_SIZE;
  2880. }
  2881. RtlInitializeBitMap( &NextBitmap->Bitmap,
  2882. (PULONG) Scb->ScbType.Data.ReservedBitMap,
  2883. SizeTemp );
  2884. //
  2885. // Now point to this new bitmap.
  2886. //
  2887. Scb->ScbType.Data.ReservedBitMap = NextBitmap;
  2888. }
  2889. }
  2890. //
  2891. // If we didn't find the correct bitmap above then scan the list looking
  2892. // for the entry.
  2893. //
  2894. if (CurrentBitmap == NULL) {
  2895. //
  2896. // Walk the list looking for a matching entry.
  2897. //
  2898. NextBitmap = Scb->ScbType.Data.ReservedBitMap;
  2899. FreeBitmap = NULL;
  2900. while (TRUE) {
  2901. //
  2902. // Exit if we found the correct range.
  2903. //
  2904. if (NextBitmap->RangeOffset == FirstRange) {
  2905. CurrentBitmap = NextBitmap;
  2906. break;
  2907. }
  2908. //
  2909. // Remember if this is a free range.
  2910. //
  2911. if (NextBitmap->DirtyBits == 0) {
  2912. FreeBitmap = NextBitmap;
  2913. }
  2914. //
  2915. // Exit if we are past our target and have a empty range then break out.
  2916. //
  2917. if ((NextBitmap->RangeOffset > FirstRange) &&
  2918. (FreeBitmap != NULL)) {
  2919. break;
  2920. }
  2921. //
  2922. // Move to the next entry.
  2923. //
  2924. NextBitmap = CONTAINING_RECORD( NextBitmap->Links.Flink,
  2925. RESERVED_BITMAP_RANGE,
  2926. Links );
  2927. //
  2928. // Break out if we are back at the beginning of the list.
  2929. //
  2930. if (NextBitmap == Scb->ScbType.Data.ReservedBitMap) {
  2931. break;
  2932. }
  2933. }
  2934. //
  2935. // If we still don't have the bitmap then we can look to see if
  2936. // we found any available free bitmaps.
  2937. //
  2938. if (CurrentBitmap == NULL) {
  2939. //
  2940. // We lucked out and found a free bitmap. Let's use it for
  2941. // this new range.
  2942. //
  2943. if (FreeBitmap != NULL) {
  2944. CurrentBitmap = FreeBitmap;
  2945. //
  2946. // Go ahead and remove it from the list. Deal with the cases where
  2947. // we are the first entry and possibly the only entry.
  2948. //
  2949. if (Scb->ScbType.Data.ReservedBitMap == FreeBitmap) {
  2950. if (IsListEmpty( &FreeBitmap->Links )) {
  2951. Scb->ScbType.Data.ReservedBitMap = NULL;
  2952. } else {
  2953. Scb->ScbType.Data.ReservedBitMap = CONTAINING_RECORD( FreeBitmap->Links.Flink,
  2954. RESERVED_BITMAP_RANGE,
  2955. Links );
  2956. }
  2957. }
  2958. //
  2959. // Remove this entry from the list.
  2960. //
  2961. RemoveEntryList( &FreeBitmap->Links );
  2962. //
  2963. // We need to allocate a new range and insert it
  2964. // in the correct location.
  2965. //
  2966. } else {
  2967. //
  2968. // Allocate a new bitmap and remember we need to insert it into the list.
  2969. //
  2970. CurrentBitmap = NtfsAllocatePoolNoRaise( PagedPool, sizeof( RESERVED_BITMAP_RANGE ));
  2971. if (CurrentBitmap == NULL) { goto AllocationFailure; }
  2972. RtlZeroMemory( CurrentBitmap, sizeof( RESERVED_BITMAP_RANGE ));
  2973. }
  2974. //
  2975. // Set the correct range value in the new bitmap.
  2976. //
  2977. CurrentBitmap->RangeOffset = FirstRange;
  2978. //
  2979. // Now walk through and insert the new range into the list. Start by checking if
  2980. // we are the only entry in the list.
  2981. //
  2982. if (Scb->ScbType.Data.ReservedBitMap == NULL) {
  2983. InitializeListHead( &CurrentBitmap->Links );
  2984. Scb->ScbType.Data.ReservedBitMap = CurrentBitmap;
  2985. } else {
  2986. NextBitmap = Scb->ScbType.Data.ReservedBitMap;
  2987. //
  2988. // Walk through the list if we are not the new first element.
  2989. //
  2990. if (CurrentBitmap->RangeOffset > NextBitmap->RangeOffset) {
  2991. do {
  2992. //
  2993. // Move to the next entry.
  2994. //
  2995. NextBitmap = CONTAINING_RECORD( NextBitmap->Links.Flink,
  2996. RESERVED_BITMAP_RANGE,
  2997. Links );
  2998. ASSERT( NextBitmap->RangeOffset != CurrentBitmap->RangeOffset );
  2999. //
  3000. // Exit if we are at the last entry.
  3001. //
  3002. if (NextBitmap == Scb->ScbType.Data.ReservedBitMap ) {
  3003. break;
  3004. }
  3005. //
  3006. // Continue until we find an entry larger than us.
  3007. //
  3008. } while (CurrentBitmap->RangeOffset > NextBitmap->RangeOffset);
  3009. //
  3010. // We are the new first element.
  3011. //
  3012. } else {
  3013. Scb->ScbType.Data.ReservedBitMap = CurrentBitmap;
  3014. }
  3015. //
  3016. // Insert the new entry ahead of the next entry we found.
  3017. //
  3018. InsertTailList( &NextBitmap->Links, &CurrentBitmap->Links );
  3019. }
  3020. }
  3021. }
  3022. //
  3023. // We have a current bitmap. Make sure it is large enough for the current
  3024. // bit.
  3025. //
  3026. if (CurrentBitmap->Bitmap.SizeOfBitMap <= CurrentLastBit) {
  3027. //
  3028. // We should already have adjusted the sizes for the basic bitmap.
  3029. //
  3030. ASSERT( CurrentBitmap->Links.Flink != NULL );
  3031. SizeTemp = NtfsBitmapSize( CurrentLastBit + 1 );
  3032. //
  3033. // Allocate the new buffer and copy the previous bits over.
  3034. //
  3035. NewBitmapBuffer = NtfsAllocatePoolNoRaise( PagedPool, SizeTemp );
  3036. if (NewBitmapBuffer == NULL) { goto AllocationFailure; }
  3037. if (CurrentBitmap->Bitmap.SizeOfBitMap != 0) {
  3038. RtlCopyMemory( NewBitmapBuffer,
  3039. CurrentBitmap->Bitmap.Buffer,
  3040. CurrentBitmap->Bitmap.SizeOfBitMap / 8 );
  3041. NtfsFreePool( CurrentBitmap->Bitmap.Buffer );
  3042. }
  3043. RtlZeroMemory( Add2Ptr( NewBitmapBuffer, CurrentBitmap->Bitmap.SizeOfBitMap / 8 ),
  3044. SizeTemp - (CurrentBitmap->Bitmap.SizeOfBitMap / 8) );
  3045. //
  3046. // Limit the bitmap size by the max range size.
  3047. //
  3048. SizeTemp *= 8;
  3049. if (SizeTemp > NTFS_BITMAP_RANGE_SIZE) {
  3050. SizeTemp = NTFS_BITMAP_RANGE_SIZE;
  3051. }
  3052. RtlInitializeBitMap( &CurrentBitmap->Bitmap,
  3053. NewBitmapBuffer,
  3054. SizeTemp );
  3055. }
  3056. //
  3057. // Figure out the worst case reservation required for this Scb, in bytes.
  3058. //
  3059. TempL = NtfsCalculateNeededReservedSpace( Scb );
  3060. //
  3061. // Now loop to reserve the space, a compression unit at a time.
  3062. // We use the security fast mutex as a convenient end resource.
  3063. //
  3064. do {
  3065. //
  3066. // If this compression unit is not already reserved do it now.
  3067. //
  3068. FlippedBit = FALSE;
  3069. if (!RtlCheckBit( &CurrentBitmap->Bitmap, FirstBit )) {
  3070. //
  3071. // If there is not sufficient space on the volume, then
  3072. // we must see if this Scb is totally reserved anyway.
  3073. //
  3074. if (((Vcb->TotalReserved + (Int64ShraMod32( Vcb->TotalReserved, 8 )) +
  3075. (1 << Scb->CompressionUnitShift)) >= Vcb->FreeClusters) &&
  3076. (Scb->ScbType.Data.TotalReserved < TempL) &&
  3077. #ifdef BRIANDBG
  3078. !NtfsIgnoreReserved &&
  3079. #endif
  3080. (FlagOn(Scb->ScbState, SCB_STATE_WRITE_ACCESS_SEEN))) {
  3081. NtfsReleaseReservedClusters( Vcb );
  3082. NtfsReleaseResource( IrpContext, Vcb->BitmapScb );
  3083. return FALSE;
  3084. }
  3085. //
  3086. // Reserve this compression unit and increase the number of dirty
  3087. // bits for this range.
  3088. //
  3089. SetFlag( CurrentBitmap->Bitmap.Buffer[FirstBit / 32], 1 << (FirstBit % 32) );
  3090. if (CurrentBitmap->Links.Flink != NULL) {
  3091. CurrentBitmap->DirtyBits += 1;
  3092. } else {
  3093. CurrentBitmap->BasicDirtyBits += 1;
  3094. }
  3095. FlippedBit = TRUE;
  3096. }
  3097. if (FlippedBit || (MappedFile && (Scb->ScbType.Data.TotalReserved <= TempL))) {
  3098. //
  3099. // Increased TotalReserved bytes in the Scb.
  3100. //
  3101. Scb->ScbType.Data.TotalReserved += Scb->CompressionUnit;
  3102. ASSERT( Scb->CompressionUnit != 0 );
  3103. ASSERT( (Scb->CompressionUnitShift != 0) ||
  3104. (Vcb->BytesPerCluster == 0x10000) );
  3105. //
  3106. // Increase total reserved clusters in the Vcb, if the user has
  3107. // write access. (Otherwise this must be a call from a read
  3108. // to a usermapped section.)
  3109. //
  3110. if (FlagOn(Scb->ScbState, SCB_STATE_WRITE_ACCESS_SEEN)) {
  3111. Vcb->TotalReserved += 1 << Scb->CompressionUnitShift;
  3112. }
  3113. TempL -= Scb->CompressionUnit;
  3114. TempL += Int64ShraMod32( Scb->CompressionUnit, 8 );
  3115. }
  3116. FirstBit += 1;
  3117. } while (FirstBit <= CurrentLastBit);
  3118. //
  3119. // Exit if we have reached the last range.
  3120. //
  3121. if (FirstRange == LastRange) { break; }
  3122. FirstRange += 1;
  3123. FirstBit = 0;
  3124. }
  3125. ReturnValue = TRUE;
  3126. AllocationFailure:
  3127. NtfsReleaseReservedClusters( Vcb );
  3128. NtfsReleaseResource( IrpContext, Vcb->BitmapScb );
  3129. //
  3130. // If we have an Irp Context then we can raise insufficient resources. Otherwise
  3131. // return FALSE.
  3132. //
  3133. if (!ReturnValue && ARGUMENT_PRESENT( IrpContext )) {
  3134. NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
  3135. }
  3136. return ReturnValue;
  3137. }
  3138. VOID
  3139. NtfsFreeReservedClusters (
  3140. IN PSCB Scb,
  3141. IN LONGLONG FileOffset,
  3142. IN ULONG ByteCount
  3143. )
  3144. /*++
  3145. Routine Description:
  3146. This routine frees any previously reserved clusters in the specified range.
  3147. Arguments:
  3148. Scb - Address of a compressed stream for which we are freeing reserved space
  3149. FileOffset - Starting byte being freed
  3150. ByteCount - Number of bytes being freed by caller, or 0 if to end of file
  3151. Return Value:
  3152. None (all errors simply raise)
  3153. --*/
  3154. {
  3155. ULONG FirstBit, LastBit, CurrentLastBit;
  3156. ULONG FirstRange, LastRange;
  3157. ULONG CompressionShift;
  3158. PRESERVED_BITMAP_RANGE CurrentBitmap = NULL;
  3159. PUSHORT DirtyBits;
  3160. PRESERVED_BITMAP_RANGE NextBitmap;
  3161. PVCB Vcb = Scb->Vcb;
  3162. LONGLONG TempL;
  3163. ULONG MappedFile;
  3164. NtfsAcquireReservedClusters( Vcb );
  3165. MappedFile = FlagOn( Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE );
  3166. //
  3167. // If there is no bitmap for non mapped files or the reserved count is zero we
  3168. // can get out immediately.
  3169. //
  3170. if ((Scb->Header.NodeTypeCode != NTFS_NTC_SCB_DATA) ||
  3171. (NULL == Scb->ScbType.Data.ReservedBitMap) ||
  3172. (Scb->ScbType.Data.TotalReserved == 0)) {
  3173. NtfsReleaseReservedClusters( Vcb );
  3174. return;
  3175. }
  3176. TempL = NtfsCalculateNeededReservedSpace( Scb );
  3177. if (MappedFile) {
  3178. //
  3179. // Mapped files can only shrink reserved down to upper limit
  3180. //
  3181. if (Scb->ScbType.Data.TotalReserved <= TempL + Scb->CompressionUnit) {
  3182. NtfsReleaseReservedClusters( Vcb );
  3183. return;
  3184. }
  3185. }
  3186. //
  3187. // Calculate first bit to free, and initialize LastBit
  3188. //
  3189. CompressionShift = Vcb->ClusterShift + (ULONG)Scb->CompressionUnitShift;
  3190. FirstBit = ((ULONG) Int64ShraMod32( FileOffset, CompressionShift )) & NTFS_BITMAP_RANGE_MASK;
  3191. FirstRange = (ULONG) Int64ShraMod32( FileOffset, CompressionShift + NTFS_BITMAP_RANGE_SHIFT );
  3192. LastRange = MAXULONG;
  3193. LastBit = MAXULONG;
  3194. //
  3195. // If ByteCount was specified, then calculate LastBit.
  3196. //
  3197. if (ByteCount != 0) {
  3198. LastBit = ((ULONG) Int64ShraMod32( FileOffset + ByteCount - 1, CompressionShift )) & NTFS_BITMAP_RANGE_MASK;
  3199. LastRange = (ULONG) Int64ShraMod32( FileOffset + ByteCount - 1,
  3200. CompressionShift + NTFS_BITMAP_RANGE_SHIFT );
  3201. }
  3202. //
  3203. // Make sure we started with numbers in range.
  3204. //
  3205. ASSERT( (((LONGLONG) FirstRange << (CompressionShift + NTFS_BITMAP_RANGE_SHIFT)) +
  3206. ((LONGLONG)(FirstBit + 1) << CompressionShift)) > FileOffset );
  3207. ASSERT( (FirstRange < LastRange) || (LastBit >= FirstBit) );
  3208. //
  3209. // Look for the first range which lies within our input range.
  3210. //
  3211. NextBitmap = Scb->ScbType.Data.ReservedBitMap;
  3212. //
  3213. // If this is a basic bitmap range then our input should be range zero.
  3214. //
  3215. if (NextBitmap->Links.Flink == NULL) {
  3216. if (FirstRange == 0) {
  3217. CurrentBitmap = NextBitmap;
  3218. DirtyBits = &CurrentBitmap->BasicDirtyBits;
  3219. }
  3220. //
  3221. // Otherwise loop through the links.
  3222. //
  3223. } else {
  3224. do {
  3225. //
  3226. // Check if this bitmap is within the range being checked.
  3227. //
  3228. if (NextBitmap->RangeOffset >= FirstRange) {
  3229. if (NextBitmap->RangeOffset <= LastRange) {
  3230. CurrentBitmap = NextBitmap;
  3231. DirtyBits = &CurrentBitmap->DirtyBits;
  3232. if (NextBitmap->RangeOffset != FirstRange) {
  3233. FirstBit = 0;
  3234. FirstRange = NextBitmap->RangeOffset;
  3235. }
  3236. }
  3237. break;
  3238. }
  3239. NextBitmap = CONTAINING_RECORD( NextBitmap->Links.Flink,
  3240. RESERVED_BITMAP_RANGE,
  3241. Links );
  3242. } while (NextBitmap != Scb->ScbType.Data.ReservedBitMap);
  3243. }
  3244. //
  3245. // If we didn't find a match we can exit.
  3246. //
  3247. if (CurrentBitmap == NULL) {
  3248. NtfsReleaseReservedClusters( Vcb );
  3249. return;
  3250. }
  3251. //
  3252. // Loop for each bitmap in the input range.
  3253. //
  3254. while (TRUE) {
  3255. //
  3256. // If we are at the last range then use the input last bit.
  3257. //
  3258. CurrentLastBit = LastBit;
  3259. if (FirstRange != LastRange) {
  3260. CurrentLastBit = NTFS_BITMAP_RANGE_MASK;
  3261. }
  3262. //
  3263. // Under no circumstances should we go off the end!
  3264. //
  3265. if (CurrentLastBit >= CurrentBitmap->Bitmap.SizeOfBitMap) {
  3266. CurrentLastBit = CurrentBitmap->Bitmap.SizeOfBitMap - 1;
  3267. }
  3268. //
  3269. // Now loop to free the space, a compression unit at a time.
  3270. // We use the security fast mutex as a convenient end resource.
  3271. //
  3272. if (MappedFile || (*DirtyBits != 0)) {
  3273. while (FirstBit <= CurrentLastBit) {
  3274. //
  3275. // If this compression unit is reserved, then free it.
  3276. //
  3277. if (MappedFile || RtlCheckBit( &CurrentBitmap->Bitmap, FirstBit )) {
  3278. //
  3279. // Free this compression unit and decrement the dirty bits
  3280. // for this bitmap if required.
  3281. //
  3282. if (!MappedFile) {
  3283. ClearFlag( CurrentBitmap->Bitmap.Buffer[FirstBit / 32], 1 << (FirstBit % 32) );
  3284. }
  3285. //
  3286. // Decrease TotalReserved bytes in the Scb.
  3287. //
  3288. ASSERT( Scb->ScbType.Data.TotalReserved >= Scb->CompressionUnit );
  3289. Scb->ScbType.Data.TotalReserved -= Scb->CompressionUnit;
  3290. ASSERT( Scb->CompressionUnit != 0 );
  3291. //
  3292. // Decrease total reserved clusters in the Vcb, if we are counting
  3293. // against the Vcb.
  3294. //
  3295. if (FlagOn(Scb->ScbState, SCB_STATE_WRITE_ACCESS_SEEN)) {
  3296. ASSERT(Vcb->TotalReserved >= (1 << Scb->CompressionUnitShift));
  3297. Vcb->TotalReserved -= 1 << Scb->CompressionUnitShift;
  3298. }
  3299. if (MappedFile) {
  3300. TempL += Scb->CompressionUnit;
  3301. TempL -= Int64ShraMod32( Scb->CompressionUnit, 8 );
  3302. if (Scb->ScbType.Data.TotalReserved <= TempL) {
  3303. break;
  3304. }
  3305. }
  3306. //
  3307. // Go ahead and break out if the count of dirty bits goes to zero.
  3308. //
  3309. ASSERT( MappedFile || *DirtyBits != 0 );
  3310. if (!MappedFile) {
  3311. *DirtyBits -= 1;
  3312. if (*DirtyBits == 0) { break; }
  3313. }
  3314. }
  3315. FirstBit += 1;
  3316. }
  3317. }
  3318. //
  3319. // Break out if we are last the last range or there is no next range
  3320. // or we're mapped and not at the limit
  3321. //
  3322. if ((NULL == CurrentBitmap->Links.Flink) ||
  3323. (FirstRange == LastRange) ||
  3324. (MappedFile &&
  3325. (Scb->ScbType.Data.TotalReserved <= TempL))) {
  3326. break;
  3327. }
  3328. //
  3329. // Move to the next range.
  3330. //
  3331. CurrentBitmap = CONTAINING_RECORD( CurrentBitmap->Links.Flink,
  3332. RESERVED_BITMAP_RANGE,
  3333. Links );
  3334. //
  3335. // Exit if we did not find a new range within the user specified range.
  3336. //
  3337. if ((CurrentBitmap->RangeOffset > LastRange) ||
  3338. (CurrentBitmap->RangeOffset <= FirstRange)) {
  3339. break;
  3340. }
  3341. FirstRange = CurrentBitmap->RangeOffset;
  3342. DirtyBits = &CurrentBitmap->DirtyBits;
  3343. FirstBit = 0;
  3344. }
  3345. NtfsReleaseReservedClusters( Vcb );
  3346. }
  3347. BOOLEAN
  3348. NtfsCheckForReservedClusters (
  3349. IN PSCB Scb,
  3350. IN LONGLONG StartingVcn,
  3351. IN OUT PLONGLONG ClusterCount
  3352. )
  3353. /*++
  3354. Routine Description:
  3355. This routine is called to determine if a range of a stream has reserved
  3356. clusters. It is used when the user queries for the allocated ranges. We
  3357. want to tell the user that a range which has reserved clusters is allocated.
  3358. Otherwise he may skip over this range when reading from the file for a
  3359. backup or copy operation.
  3360. Arguments:
  3361. Scb - Address of the Scb for a sparsestream for which we are checking for
  3362. reservation. Our caller should only call us for this type of stream.
  3363. StartingVcn - Starting offset of a potential zeroed range. This is guaranteed
  3364. to begin on a sparse range boundary.
  3365. ClusterCount - On input this is the length of the range to check. On output it
  3366. is the length of the deallocated range beginning at this offset. The length
  3367. will be zero if the first compression unit is reserved.
  3368. Return Value:
  3369. BOOLEAN - TRUE if a reserved unit is found in the range, FALSE otherwise.
  3370. --*/
  3371. {
  3372. ULONG CompressionShift;
  3373. ULONG FirstBit, LastBit, CurrentLastBit, CurrentBits;
  3374. ULONG FirstRange, LastRange;
  3375. ULONG RemainingBits;
  3376. ULONG FoundBit;
  3377. PRESERVED_BITMAP_RANGE CurrentBitmap = NULL;
  3378. PRESERVED_BITMAP_RANGE NextBitmap;
  3379. PUSHORT DirtyBits;
  3380. PVCB Vcb = Scb->Vcb;
  3381. LONGLONG FoundBits = 0;
  3382. BOOLEAN FoundReserved = FALSE;
  3383. RTL_BITMAP LocalBitmap;
  3384. PAGED_CODE();
  3385. //
  3386. // Check that the stream is really sparse and that the file offset is on a sparse
  3387. // boundary.
  3388. //
  3389. ASSERT( FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK | ATTRIBUTE_FLAG_SPARSE ));
  3390. ASSERT( (((ULONG) LlBytesFromClusters( Vcb, StartingVcn )) & (Scb->CompressionUnit - 1)) == 0 );
  3391. //
  3392. // If there is no bitmap, we can get out.
  3393. //
  3394. if ((Scb->ScbType.Data.ReservedBitMap == NULL) ||
  3395. (Scb->ScbType.Data.TotalReserved == 0)) {
  3396. return FoundReserved;
  3397. }
  3398. //
  3399. // Compute the range of bits that need to be checked. Trim this by the range of
  3400. // the bitmap.
  3401. //
  3402. CompressionShift = (ULONG) Scb->CompressionUnitShift;
  3403. FirstBit = ((ULONG) Int64ShraMod32( StartingVcn, CompressionShift )) & NTFS_BITMAP_RANGE_MASK;
  3404. FirstRange = (ULONG) Int64ShraMod32( StartingVcn, CompressionShift + NTFS_BITMAP_RANGE_SHIFT );
  3405. LastBit = ((ULONG) Int64ShraMod32( StartingVcn + *ClusterCount - 1, CompressionShift )) & NTFS_BITMAP_RANGE_MASK;
  3406. LastRange = (ULONG) Int64ShraMod32( StartingVcn + *ClusterCount - 1,
  3407. CompressionShift + NTFS_BITMAP_RANGE_SHIFT );
  3408. NtfsAcquireReservedClusters( Vcb );
  3409. //
  3410. // Look for the first range which lies within our input range.
  3411. //
  3412. NextBitmap = Scb->ScbType.Data.ReservedBitMap;
  3413. //
  3414. // If this is a basic bitmap range then our input should be range zero.
  3415. //
  3416. if (NextBitmap->Links.Flink == NULL) {
  3417. if (FirstRange == 0) {
  3418. CurrentBitmap = NextBitmap;
  3419. DirtyBits = &CurrentBitmap->BasicDirtyBits;
  3420. }
  3421. //
  3422. // Otherwise loop through the links.
  3423. //
  3424. } else {
  3425. do {
  3426. //
  3427. // Check if this bitmap is within the range being checked.
  3428. //
  3429. if (NextBitmap->RangeOffset >= FirstRange) {
  3430. if (NextBitmap->RangeOffset <= LastRange) {
  3431. CurrentBitmap = NextBitmap;
  3432. DirtyBits = &CurrentBitmap->DirtyBits;
  3433. //
  3434. // If we are skipping any ranges then remember how
  3435. // many bits are implicitly clear.
  3436. //
  3437. if (NextBitmap->RangeOffset != FirstRange) {
  3438. FoundBits = (NextBitmap->RangeOffset - FirstRange) * NTFS_BITMAP_RANGE_SIZE;
  3439. FoundBits -= FirstBit;
  3440. FirstBit = 0;
  3441. FirstRange = NextBitmap->RangeOffset;
  3442. }
  3443. }
  3444. break;
  3445. }
  3446. NextBitmap = CONTAINING_RECORD( NextBitmap->Links.Flink,
  3447. RESERVED_BITMAP_RANGE,
  3448. Links );
  3449. } while (NextBitmap != Scb->ScbType.Data.ReservedBitMap);
  3450. }
  3451. //
  3452. // If we didn't find a match we can exit.
  3453. //
  3454. if (CurrentBitmap == NULL) {
  3455. NtfsReleaseReservedClusters( Vcb );
  3456. return FoundReserved;
  3457. }
  3458. //
  3459. // Loop for each bitmap in the input range.
  3460. //
  3461. while (TRUE) {
  3462. //
  3463. // If we are at the last range then use the input last bit.
  3464. //
  3465. CurrentLastBit = LastBit;
  3466. if (FirstRange != LastRange) {
  3467. CurrentLastBit = NTFS_BITMAP_RANGE_MASK;
  3468. }
  3469. CurrentBits = CurrentLastBit - FirstBit + 1;
  3470. //
  3471. // Skip this range if there are no dirty bits.
  3472. //
  3473. if (*DirtyBits != 0) {
  3474. //
  3475. // Under no circumstances should we go off the end!
  3476. //
  3477. if (CurrentLastBit >= CurrentBitmap->Bitmap.SizeOfBitMap) {
  3478. CurrentLastBit = CurrentBitmap->Bitmap.SizeOfBitMap - 1;
  3479. }
  3480. //
  3481. // Check on the number of bits remaining in this bitmap.
  3482. //
  3483. if (FirstBit <= CurrentLastBit) {
  3484. RemainingBits = CurrentLastBit - FirstBit + 1;
  3485. ASSERT( RemainingBits != 0 );
  3486. //
  3487. // If the starting bit is set then there is nothing else to do.
  3488. // Otherwise find the length of the clear run.
  3489. //
  3490. if (RtlCheckBit( &CurrentBitmap->Bitmap, FirstBit )) {
  3491. FoundBit = FirstBit;
  3492. } else {
  3493. RtlInitializeBitMap( &LocalBitmap,
  3494. CurrentBitmap->Bitmap.Buffer,
  3495. CurrentLastBit + 1 );
  3496. FoundBit = RtlFindNextForwardRunClear( &LocalBitmap,
  3497. FirstBit,
  3498. &FirstBit );
  3499. if (FoundBit == RemainingBits) {
  3500. FoundBit = 0xffffffff;
  3501. } else {
  3502. FoundBit += FirstBit;
  3503. }
  3504. }
  3505. //
  3506. // If a bit was found then we need to compute where it lies in the
  3507. // requested range.
  3508. //
  3509. if (FoundBit != 0xffffffff) {
  3510. //
  3511. // Include any clear bits from this range in our total.
  3512. //
  3513. FoundBits += (FoundBit - FirstBit);
  3514. //
  3515. // Convert from compression units to clusters and trim to a compression
  3516. // unit boundary.
  3517. //
  3518. *ClusterCount = BlockAlignTruncate( Int64ShllMod32( FoundBits, CompressionShift ), (LONG)Vcb->SparseFileClusters );
  3519. //
  3520. // Now adjust the output cluster range value.
  3521. //
  3522. ASSERT( LlBytesFromClusters( Vcb, StartingVcn + *ClusterCount ) <= (ULONGLONG) Scb->Header.FileSize.QuadPart );
  3523. FoundReserved = TRUE;
  3524. break;
  3525. }
  3526. }
  3527. }
  3528. //
  3529. // Break out if we are last the last range or there is no next range.
  3530. //
  3531. if ((CurrentBitmap->Links.Flink == NULL) ||
  3532. (FirstRange == LastRange)) {
  3533. break;
  3534. }
  3535. //
  3536. // Move to the next range.
  3537. //
  3538. CurrentBitmap = CONTAINING_RECORD( CurrentBitmap->Links.Flink,
  3539. RESERVED_BITMAP_RANGE,
  3540. Links );
  3541. //
  3542. // Exit if we did not find a new range within the user specified range.
  3543. //
  3544. if ((CurrentBitmap->RangeOffset <= FirstRange) ||
  3545. (CurrentBitmap->RangeOffset > LastRange)) {
  3546. break;
  3547. }
  3548. //
  3549. // Add in the bits for any ranges we skipped.
  3550. //
  3551. FoundBits += (CurrentBitmap->RangeOffset - FirstRange - 1) * NTFS_BITMAP_RANGE_SIZE;
  3552. FirstRange = CurrentBitmap->RangeOffset;
  3553. FirstBit = 0;
  3554. //
  3555. // Include the bits from the most recent range in our count of found bits.
  3556. //
  3557. FoundBits += CurrentBits;
  3558. //
  3559. // Remember where the dirty bits field is.
  3560. //
  3561. DirtyBits = &CurrentBitmap->DirtyBits;
  3562. }
  3563. NtfsReleaseReservedClusters( Vcb );
  3564. return FoundReserved;
  3565. }
  3566. VOID
  3567. NtfsDeleteReservedBitmap (
  3568. IN PSCB Scb
  3569. )
  3570. /*++
  3571. Routine Description:
  3572. This routine is called to free all of the components of the reserved bitmap. We
  3573. free any remaining reserved clusters and deallocate all of the pool associated with
  3574. the bitmap.
  3575. Arguments:
  3576. Scb - Scb for the stream.
  3577. Return Value:
  3578. None.
  3579. --*/
  3580. {
  3581. PRESERVED_BITMAP_RANGE FirstRange;
  3582. PRESERVED_BITMAP_RANGE CurrentRange;
  3583. PAGED_CODE();
  3584. FirstRange = Scb->ScbType.Data.ReservedBitMap;
  3585. ASSERT( FirstRange != NULL );
  3586. //
  3587. // Free any reserved clusters still present.
  3588. //
  3589. if ((Scb->ScbType.Data.TotalReserved != 0) && FlagOn( Scb->ScbState, SCB_STATE_WRITE_ACCESS_SEEN )) {
  3590. LONGLONG ClusterCount;
  3591. ClusterCount = LlClustersFromBytesTruncate( Scb->Vcb, Scb->ScbType.Data.TotalReserved );
  3592. //
  3593. // Use the security fast mutex as a convenient end resource.
  3594. //
  3595. NtfsAcquireReservedClusters( Scb->Vcb );
  3596. ASSERT(Scb->Vcb->TotalReserved >= ClusterCount);
  3597. Scb->Vcb->TotalReserved -= ClusterCount;
  3598. NtfsReleaseReservedClusters( Scb->Vcb );
  3599. }
  3600. Scb->ScbType.Data.TotalReserved = 0;
  3601. //
  3602. // The typical case is where the first range is the only range
  3603. // for a small file.
  3604. //
  3605. if (FirstRange->Links.Flink == NULL) {
  3606. NtfsFreePool( FirstRange );
  3607. //
  3608. // Otherwise we need to walk through the list of ranges.
  3609. //
  3610. } else {
  3611. //
  3612. // Loop through the reserved bitmaps until we hit the first.
  3613. //
  3614. do {
  3615. CurrentRange = CONTAINING_RECORD( FirstRange->Links.Flink,
  3616. RESERVED_BITMAP_RANGE,
  3617. Links );
  3618. RemoveEntryList( &CurrentRange->Links );
  3619. if (CurrentRange->Bitmap.Buffer != NULL) {
  3620. NtfsFreePool( CurrentRange->Bitmap.Buffer );
  3621. }
  3622. NtfsFreePool( CurrentRange );
  3623. } while (CurrentRange != FirstRange);
  3624. }
  3625. //
  3626. // Show that the bitmap is gone.
  3627. //
  3628. Scb->ScbType.Data.ReservedBitMap = NULL;
  3629. return;
  3630. }
  3631. #if (defined(NTFS_RWCMP_TRACE) || defined(SYSCACHE) || defined(NTFS_RWC_DEBUG) || defined(SYSCACHE_DEBUG))
  3632. BOOLEAN
  3633. FsRtlIsSyscacheFile (
  3634. IN PFILE_OBJECT FileObject
  3635. )
  3636. /*++
  3637. Routine Description:
  3638. This routine returns to the caller whether or not the specified
  3639. file object is a file to be logged. Originally this was only used for
  3640. the syscache stress test (thus the name). The function understands minimal
  3641. wildcard patterns. To change which filename is logged against change the
  3642. variable MakName.
  3643. Arguments:
  3644. FileObject - supplies the FileObject to be tested (it must not be
  3645. cleaned up yet).
  3646. Return Value:
  3647. FALSE - if the file is not a Syscache file.
  3648. TRUE - if the file is a Syscache file.
  3649. --*/
  3650. {
  3651. ULONG iM = 0;
  3652. ULONG iF;
  3653. PWSTR MakName = L"cac*.tmp";
  3654. ULONG LenMakName = wcslen(MakName);
  3655. if (FileObject && NtfsSyscacheTrackingActive) {
  3656. iF = FileObject->FileName.Length / 2;
  3657. while ((iF != 0) && (FileObject->FileName.Buffer[iF - 1] != '\\')) {
  3658. iF--;
  3659. }
  3660. while (TRUE) {
  3661. //
  3662. // If we are past the end of the file object then we are done in any case.
  3663. //
  3664. if ((LONG)iF == FileObject->FileName.Length / 2) {
  3665. //
  3666. // Both strings exausted then we are done.
  3667. //
  3668. if (iM == LenMakName) {
  3669. return TRUE;
  3670. }
  3671. break;
  3672. //
  3673. // Break if more input but the match string is exhausted.
  3674. //
  3675. } else if (iM == LenMakName) {
  3676. break;
  3677. //
  3678. // If we are at the '*' then match everything but skip to next character
  3679. // on a '.'
  3680. //
  3681. } else if (MakName[iM] == '*') {
  3682. //
  3683. // if we're at the last character move past wildchar in template
  3684. //
  3685. if ((FileObject->FileName.Buffer[iF] == L'.') && (LenMakName != iM + 1)) {
  3686. //
  3687. // Move past * and . in NakName
  3688. //
  3689. ASSERT(MakName[iM + 1] == L'.');
  3690. iM++; iM++;
  3691. } else if (((LONG)iF + 1 == FileObject->FileName.Length / 2)) {
  3692. iM++;
  3693. }
  3694. iF++;
  3695. } else if (MakName[iM] == (WCHAR)(FileObject->FileName.Buffer[iF] )) {
  3696. iM++; iF++;
  3697. } else {
  3698. break;
  3699. }
  3700. }
  3701. }
  3702. return FALSE;
  3703. }
  3704. VOID
  3705. FsRtlVerifySyscacheData (
  3706. IN PFILE_OBJECT FileObject,
  3707. IN PVOID Buffer,
  3708. IN ULONG Length,
  3709. IN ULONG Offset
  3710. )
  3711. /*
  3712. Routine Description:
  3713. This routine scans a buffer to see if it is valid data for a syscache
  3714. file, and stops if it sees bad data.
  3715. HINT TO CALLERS: Make sure (Offset + Length) <= FileSize!
  3716. Arguments:
  3717. Buffer - Pointer to the buffer to be checked
  3718. Length - Length of the buffer to be checked in bytes
  3719. Offset - File offset at which this data starts (syscache files are currently
  3720. limited to 24 bits of file offset).
  3721. Return Value:
  3722. None (stops on error)
  3723. --*/
  3724. {
  3725. PULONG BufferEnd;
  3726. BufferEnd = (PULONG)((PCHAR)Buffer + (Length & ~3));
  3727. while ((PULONG)Buffer < BufferEnd) {
  3728. if ((*(PULONG)Buffer != 0) && (((*(PULONG)Buffer & 0xFFFFFF) ^ Offset) != 0xFFFFFF) &&
  3729. ((Offset & 0x1FF) != 0)) {
  3730. DbgPrint("Bad Data, FileObject = %08lx, Offset = %08lx, Buffer = %08lx\n",
  3731. FileObject, Offset, (PULONG)Buffer );
  3732. DbgBreakPoint();
  3733. }
  3734. Offset += 4;
  3735. Buffer = (PVOID)((PULONG)Buffer + 1);
  3736. }
  3737. }
  3738. #endif