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

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