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.

2848 lines
72 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. McbSup.c
  5. Abstract:
  6. This module implements the Ntfs Mcb package.
  7. Author:
  8. Gary Kimura [GaryKi] 10-Sep-1994
  9. Tom Miller [TomM]
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. #define FIRST_RANGE ((PVOID)1)
  14. #ifndef NTFS_VERIFY_MCB
  15. #define NtfsVerifyNtfsMcb(M) NOTHING;
  16. #define NtfsVerifyUncompressedNtfsMcb(M,S,E) NOTHING;
  17. #endif
  18. //
  19. // Define a tag for general pool allocations from this module
  20. //
  21. #undef MODULE_POOL_TAG
  22. #define MODULE_POOL_TAG ('MFtN')
  23. //
  24. // Local procedure prototypes
  25. //
  26. ULONG
  27. NtfsMcbLookupArrayIndex (
  28. IN PNTFS_MCB Mcb,
  29. IN VCN Vcn
  30. );
  31. VOID
  32. NtfsInsertNewRange (
  33. IN PNTFS_MCB Mcb,
  34. IN LONGLONG StartingVcn,
  35. IN ULONG ArrayIndex,
  36. IN BOOLEAN MakeNewRangeEmpty
  37. );
  38. VOID
  39. NtfsCollapseRanges (
  40. IN PNTFS_MCB Mcb,
  41. IN ULONG StartingArrayIndex,
  42. IN ULONG EndingArrayIndex
  43. );
  44. VOID
  45. NtfsMcbCleanupLruQueue (
  46. IN PVOID Parameter
  47. );
  48. #ifdef NTFS_VERIFY_MCB
  49. VOID
  50. NtfsVerifyNtfsMcb (
  51. IN PNTFS_MCB Mcb
  52. );
  53. VOID
  54. NtfsVerifyUncompressedNtfsMcb (
  55. IN PNTFS_MCB Mcb,
  56. IN LONGLONG StartingVcn,
  57. IN LONGLONG EndingVcn
  58. );
  59. #endif
  60. BOOLEAN
  61. NtfsLockNtfsMcb (
  62. IN PNTFS_MCB Mcb
  63. );
  64. VOID
  65. NtfsUnlockNtfsMcb (
  66. IN PNTFS_MCB Mcb
  67. );
  68. NtfsGrowMcbArray(
  69. IN PNTFS_MCB Mcb
  70. );
  71. //
  72. // Local macros to ASSERT that caller's resource is exclusive or restart is
  73. // underway.
  74. //
  75. #define ASSERT_STREAM_EXCLUSIVE(M) { \
  76. ASSERT( FlagOn( ((PSCB) (M)->FcbHeader)->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS ) || \
  77. ExIsResourceAcquiredExclusiveLite((M)->FcbHeader->Resource )); \
  78. }
  79. //
  80. // Local macros to enqueue and dequeue elements from the lru queue
  81. //
  82. #define NtfsMcbEnqueueLruEntry(M,E) { \
  83. InsertTailList( &NtfsMcbLruQueue, &(E)->LruLinks ); \
  84. NtfsMcbCurrentLevel += 1; \
  85. }
  86. #define NtfsMcbDequeueLruEntry(M,E) { \
  87. if ((E)->LruLinks.Flink != NULL) { \
  88. RemoveEntryList( &(E)->LruLinks ); \
  89. NtfsMcbCurrentLevel -= 1; \
  90. } \
  91. }
  92. //
  93. // Local macro to unload a single array entry
  94. //
  95. #define UnloadEntry(M,I) { \
  96. PNTFS_MCB_ENTRY _Entry; \
  97. _Entry = (M)->NtfsMcbArray[(I)].NtfsMcbEntry; \
  98. (M)->NtfsMcbArray[(I)].NtfsMcbEntry = NULL; \
  99. if (_Entry != NULL) { \
  100. ExAcquireFastMutex( &NtfsMcbFastMutex ); \
  101. NtfsMcbDequeueLruEntry( Mcb, _Entry ); \
  102. ExReleaseFastMutex( &NtfsMcbFastMutex ); \
  103. FsRtlUninitializeLargeMcb( &_Entry->LargeMcb ); \
  104. if ((M)->NtfsMcbArraySize != MCB_ARRAY_PHASE1_SIZE) { \
  105. NtfsFreePool( _Entry ); \
  106. } \
  107. } \
  108. }
  109. VOID
  110. NtfsInitializeNtfsMcb (
  111. IN PNTFS_MCB Mcb,
  112. IN PNTFS_ADVANCED_FCB_HEADER FcbHeader,
  113. IN PNTFS_MCB_INITIAL_STRUCTS McbStructs,
  114. IN POOL_TYPE PoolType
  115. )
  116. /*++
  117. Routine Description:
  118. This routine initializes a new Ntfs Mcb structure.
  119. Arguments:
  120. Mcb - Supplies the Mcb being initialized
  121. FcbHeader - Supplies a pointer to the Fcb header containing
  122. the resource to grab when accessing the Mcb
  123. McbStructs - Initial allocation typically coresident in another
  124. structure to handle initial structures for small and
  125. medium files. This structure should be initially zeroed.
  126. PoolType - Supplies the type of pool to use when
  127. allocating mapping information storage
  128. Return Value:
  129. None.
  130. --*/
  131. {
  132. PNTFS_MCB_ARRAY Array;
  133. RtlZeroMemory( McbStructs, sizeof(NTFS_MCB_INITIAL_STRUCTS) );
  134. //
  135. // Initialize the fcb header field of the mcb
  136. //
  137. Mcb->FcbHeader = FcbHeader;
  138. //
  139. // Initialize the pool type
  140. //
  141. Mcb->PoolType = PoolType;
  142. //
  143. // Now initialize the initial array element
  144. //
  145. Mcb->NtfsMcbArray = Array = &McbStructs->Phase1.SingleMcbArrayEntry;
  146. Mcb->NtfsMcbArraySize = MCB_ARRAY_PHASE1_SIZE;
  147. Mcb->NtfsMcbArraySizeInUse = 1;
  148. Mcb->FastMutex = FcbHeader->FastMutex;
  149. //
  150. // Initialize the first array entry.
  151. //
  152. Array[0].StartingVcn = 0;
  153. Array[0].EndingVcn = -1;
  154. //
  155. // And return to our caller
  156. //
  157. NtfsVerifyNtfsMcb(Mcb);
  158. return;
  159. }
  160. VOID
  161. NtfsUninitializeNtfsMcb (
  162. IN PNTFS_MCB Mcb
  163. )
  164. /*++
  165. Routine Description:
  166. This routine uninitializes an Ntfs Mcb structure.
  167. Arguments:
  168. Mcb - Supplies the Mcb being decommissioned
  169. Return Value:
  170. None.
  171. --*/
  172. {
  173. ULONG i;
  174. PNTFS_MCB_ENTRY Entry;
  175. NtfsVerifyNtfsMcb(Mcb);
  176. //
  177. // Take out the global mutex
  178. //
  179. ExAcquireFastMutex( &NtfsMcbFastMutex );
  180. //
  181. // Deallocate the mcb array if it exists. For every entry in the array
  182. // if the mcb entry is not null then remove the entry from the lru
  183. // queue, uninitialize the large mcb, and free the pool.
  184. //
  185. if (Mcb->NtfsMcbArray != NULL) {
  186. for (i = 0; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
  187. if ((Entry = Mcb->NtfsMcbArray[i].NtfsMcbEntry) != NULL) {
  188. //
  189. // Remove the entry from the lru queue
  190. //
  191. NtfsMcbDequeueLruEntry( Mcb, Entry );
  192. //
  193. // Now release the entry
  194. //
  195. FsRtlUninitializeLargeMcb( &Entry->LargeMcb );
  196. //
  197. // We can tell from the array count whether this is
  198. // the initial entry and does not need to be deallocated.
  199. //
  200. if (Mcb->NtfsMcbArraySize > MCB_ARRAY_PHASE1_SIZE) {
  201. NtfsFreePool( Entry );
  202. }
  203. }
  204. }
  205. //
  206. // We can tell from the array count whether this is
  207. // the initial array entry(s) and do not need to be deallocated.
  208. //
  209. if (Mcb->NtfsMcbArraySize > MCB_ARRAY_PHASE2_SIZE) {
  210. NtfsFreePool( Mcb->NtfsMcbArray );
  211. }
  212. Mcb->NtfsMcbArray = NULL;
  213. //
  214. // Clear the fast mutex field.
  215. //
  216. Mcb->FastMutex = NULL;
  217. }
  218. ExReleaseFastMutex( &NtfsMcbFastMutex );
  219. //
  220. // And return to our caller
  221. //
  222. return;
  223. }
  224. ULONG
  225. NtfsNumberOfRangesInNtfsMcb (
  226. IN PNTFS_MCB Mcb
  227. )
  228. /*++
  229. Routine Description:
  230. This routine returns the total number of ranges stored in
  231. the mcb
  232. Arguments:
  233. Mcb - Supplies the Mcb being queried
  234. Return Value:
  235. ULONG - The number of ranges mapped by the input mcb
  236. --*/
  237. {
  238. ASSERT_STREAM_EXCLUSIVE(Mcb);
  239. //
  240. // Our answer is the number of ranges in use in the mcb
  241. //
  242. NtfsVerifyNtfsMcb(Mcb);
  243. return Mcb->NtfsMcbArraySizeInUse;
  244. }
  245. BOOLEAN
  246. NtfsNumberOfRunsInRange (
  247. IN PNTFS_MCB Mcb,
  248. IN PVOID RangePtr,
  249. OUT PULONG NumberOfRuns
  250. )
  251. /*++
  252. Routine Description:
  253. This routine returns the total number of runs stored withing a range
  254. Arguments:
  255. Mcb - Supplies the Mcb being queried
  256. RangePtr - Supplies the range to being queried
  257. NumberOrRuns - Returns the number of run in the specified range
  258. but only if the range is loaded
  259. Return Value:
  260. BOOLEAN - TRUE if the range is loaded and then output variable
  261. is valid and FALSE if the range is not loaded.
  262. --*/
  263. {
  264. VCN TempVcn;
  265. LCN TempLcn;
  266. PNTFS_MCB_ENTRY Entry = (PNTFS_MCB_ENTRY)RangePtr;
  267. //
  268. // Null RangePtr means first range
  269. //
  270. if (Entry == FIRST_RANGE) {
  271. Entry = Mcb->NtfsMcbArray[0].NtfsMcbEntry;
  272. //
  273. // If not loaded, return FALSE
  274. //
  275. if (Entry == NULL) {
  276. return FALSE;
  277. }
  278. }
  279. ASSERT_STREAM_EXCLUSIVE(Mcb);
  280. NtfsVerifyNtfsMcb(Mcb);
  281. ASSERT( Mcb == Entry->NtfsMcb );
  282. *NumberOfRuns = FsRtlNumberOfRunsInLargeMcb( &Entry->LargeMcb );
  283. //
  284. // Check if the current entry ends with a hole and increment the run count
  285. // to reflect this. Detect the case where the range has length 0 for a
  286. // file with no allocation. EndingVcn will be less than the starting Vcn
  287. // in this case.
  288. //
  289. if (!FsRtlLookupLastLargeMcbEntry( &Entry->LargeMcb, &TempVcn, &TempLcn )) {
  290. //
  291. // If this is a non-zero length range then add one for the implied hole.
  292. //
  293. if (Entry->NtfsMcbArray->EndingVcn >= Entry->NtfsMcbArray->StartingVcn) {
  294. *NumberOfRuns += 1;
  295. }
  296. //
  297. // There is an entry then check if it reaches the end boundary of the range.
  298. //
  299. } else if (TempVcn != (Entry->NtfsMcbArray->EndingVcn - Entry->NtfsMcbArray->StartingVcn)) {
  300. *NumberOfRuns += 1;
  301. }
  302. return TRUE;
  303. }
  304. BOOLEAN
  305. NtfsLookupLastNtfsMcbEntry (
  306. IN PNTFS_MCB Mcb,
  307. OUT PLONGLONG Vcn,
  308. OUT PLONGLONG Lcn
  309. )
  310. /*++
  311. Routine Description:
  312. This routine returns the last mapping stored in the mcb
  313. Arguments:
  314. Mcb - Supplies the Mcb being queried
  315. Vcn - Receives the Vcn of the last mapping
  316. Lcn - Receives the Lcn corresponding to the Vcn
  317. Return Value:
  318. BOOLEAN - TRUE if the mapping exist and FALSE if no mapping has been
  319. defined or it is unloaded
  320. --*/
  321. {
  322. PNTFS_MCB_ENTRY Entry;
  323. LONGLONG StartingVcn;
  324. ASSERT_STREAM_EXCLUSIVE(Mcb);
  325. NtfsVerifyNtfsMcb(Mcb);
  326. //
  327. // Get the last entry and compute its starting vcn, and make sure
  328. // the entry is valid
  329. //
  330. if ((Entry = Mcb->NtfsMcbArray[Mcb->NtfsMcbArraySizeInUse - 1].NtfsMcbEntry) == NULL) {
  331. return FALSE;
  332. }
  333. StartingVcn = Mcb->NtfsMcbArray[Mcb->NtfsMcbArraySizeInUse - 1].StartingVcn;
  334. //
  335. // Otherwise lookup the last entry and compute the real vcn
  336. //
  337. if (FsRtlLookupLastLargeMcbEntry( &Entry->LargeMcb, Vcn, Lcn )) {
  338. *Vcn += StartingVcn;
  339. } else {
  340. *Vcn = Mcb->NtfsMcbArray[Mcb->NtfsMcbArraySizeInUse - 1].EndingVcn;
  341. *Lcn = UNUSED_LCN;
  342. }
  343. return TRUE;
  344. }
  345. BOOLEAN
  346. NtfsLookupNtfsMcbEntry (
  347. IN PNTFS_MCB Mcb,
  348. IN LONGLONG Vcn,
  349. OUT PLONGLONG Lcn OPTIONAL,
  350. OUT PLONGLONG CountFromLcn OPTIONAL,
  351. OUT PLONGLONG StartingLcn OPTIONAL,
  352. OUT PLONGLONG CountFromStartingLcn OPTIONAL,
  353. OUT PVOID *RangePtr OPTIONAL,
  354. OUT PULONG RunIndex OPTIONAL
  355. )
  356. /*++
  357. Routine Description:
  358. This routine is used to query mapping information
  359. Arguments:
  360. Mcb - Supplies the Mcb being queried
  361. Vcn - Supplies the Vcn being queried
  362. Lcn - Optionally receives the lcn corresponding to the input vcn
  363. CountFromLcn - Optionally receives the number of clusters following
  364. the lcn in the run
  365. StartingLcn - Optionally receives the start of the run containing the
  366. input vcn
  367. CountFromStartingLcn - Optionally receives the number of clusters in
  368. the entire run
  369. RangePtr - Optionally receives the index for the range that we're returning
  370. RunIndex - Optionally receives the index for the run within the range that
  371. we're returning
  372. Return Value:
  373. BOOLEAN - TRUE if the mapping exists and FALSE if it doesn't exist
  374. or if it is unloaded.
  375. --*/
  376. {
  377. ULONG LocalRangeIndex;
  378. PNTFS_MCB_ENTRY Entry;
  379. NtfsAcquireNtfsMcbMutex( Mcb );
  380. NtfsVerifyNtfsMcb(Mcb);
  381. //
  382. // Do a basic bounds check
  383. //
  384. ASSERT( Mcb->NtfsMcbArraySizeInUse > 0 );
  385. //
  386. // Locate the array entry that has the hit for the input vcn, and
  387. // make sure it is valid. Also set the output range index if present
  388. //
  389. LocalRangeIndex = NtfsMcbLookupArrayIndex(Mcb, Vcn);
  390. //
  391. // Now lookup the large mcb entry. The Vcn we pass in is
  392. // biased by the starting vcn. If we miss then we'll just return false
  393. //
  394. if (((Entry = Mcb->NtfsMcbArray[LocalRangeIndex].NtfsMcbEntry) == NULL) ||
  395. (Vcn > Entry->NtfsMcbArray->EndingVcn) ||
  396. (Vcn < Entry->NtfsMcbArray->StartingVcn)) {
  397. ASSERT( (Entry == NULL) || (Vcn > Entry->NtfsMcbArray->EndingVcn) || (Vcn < 0) );
  398. if (ARGUMENT_PRESENT(RangePtr)) {
  399. *RangePtr = (PVOID)Entry;
  400. //
  401. // If this is the first range, always normalize back to the reserved pointer,
  402. // since this is the only range which can move if we split out of our
  403. // initial static allocation!
  404. //
  405. if (LocalRangeIndex == 0) {
  406. *RangePtr = FIRST_RANGE;
  407. }
  408. }
  409. NtfsReleaseNtfsMcbMutex( Mcb );
  410. return FALSE;
  411. }
  412. if (!FsRtlLookupLargeMcbEntry( &Entry->LargeMcb,
  413. Vcn - Mcb->NtfsMcbArray[LocalRangeIndex].StartingVcn,
  414. Lcn,
  415. CountFromLcn,
  416. StartingLcn,
  417. CountFromStartingLcn,
  418. RunIndex )) {
  419. //
  420. // If we go off the end of the Mcb, but are in the range, then we
  421. // return a hole to the end of the range.
  422. //
  423. if (ARGUMENT_PRESENT(Lcn)) {
  424. *Lcn = UNUSED_LCN;
  425. }
  426. if (ARGUMENT_PRESENT(CountFromLcn)) {
  427. *CountFromLcn = Mcb->NtfsMcbArray[LocalRangeIndex].EndingVcn - Vcn + 1;
  428. }
  429. if (ARGUMENT_PRESENT(StartingLcn)) {
  430. *StartingLcn = UNUSED_LCN;
  431. }
  432. if (ARGUMENT_PRESENT(RunIndex)) {
  433. *RunIndex = FsRtlNumberOfRunsInLargeMcb( &Entry->LargeMcb );
  434. }
  435. if (ARGUMENT_PRESENT( CountFromStartingLcn )) {
  436. //
  437. // If there are no runs in the Mcb then specify
  438. // a hole for the full range.
  439. //
  440. *CountFromStartingLcn = Mcb->NtfsMcbArray[LocalRangeIndex].EndingVcn -
  441. Mcb->NtfsMcbArray[LocalRangeIndex].StartingVcn + 1;
  442. if (*RunIndex != 0) {
  443. VCN LastVcn;
  444. LCN LastLcn;
  445. FsRtlLookupLastLargeMcbEntry( &Entry->LargeMcb,
  446. &LastVcn,
  447. &LastLcn );
  448. ASSERT( LastVcn <= *CountFromStartingLcn );
  449. *CountFromStartingLcn -= (LastVcn + 1);
  450. }
  451. }
  452. }
  453. if (ARGUMENT_PRESENT(RangePtr)) {
  454. *RangePtr = (PVOID)Entry;
  455. //
  456. // If this is the first range, always normalize back to the reserved pointer,
  457. // since this is the only range which can move if we split out of our
  458. // initial static allocation!
  459. //
  460. if (LocalRangeIndex == 0) {
  461. *RangePtr = FIRST_RANGE;
  462. }
  463. }
  464. //
  465. // Now move this entry to the tail of the lru queue.
  466. // We need to take out the global mutex to do this.
  467. // Only do this if he is already in the queue - we can
  468. // deadlock if we take a fault in the paging file path.
  469. //
  470. if (Entry->LruLinks.Flink != NULL) {
  471. if (ExTryToAcquireFastMutex( &NtfsMcbFastMutex )) {
  472. NtfsMcbDequeueLruEntry( Mcb, Entry );
  473. NtfsMcbEnqueueLruEntry( Mcb, Entry );
  474. ExReleaseFastMutex( &NtfsMcbFastMutex );
  475. }
  476. }
  477. NtfsReleaseNtfsMcbMutex( Mcb );
  478. return TRUE;
  479. }
  480. BOOLEAN
  481. NtfsGetNextNtfsMcbEntry (
  482. IN PNTFS_MCB Mcb,
  483. IN PVOID *RangePtr,
  484. IN ULONG RunIndex,
  485. OUT PLONGLONG Vcn,
  486. OUT PLONGLONG Lcn,
  487. OUT PLONGLONG Count
  488. )
  489. /*++
  490. Routine Description:
  491. This routine returns the range denoted by the type index values
  492. Arguments:
  493. Mcb - Supplies the Mcb being queried
  494. RangePtr - Supplies the pointer to the range being queried, or NULL for the first one,
  495. returns next range
  496. RunIndex - Supplies the index within then being queried, or MAXULONG for first in next
  497. Vcn - Receives the starting Vcn of the run being returned
  498. Lcn - Receives the starting Lcn of the run being returned or unused
  499. lbn value of -1
  500. Count - Receives the number of clusters within this run
  501. Return Value:
  502. BOOLEAN - TRUE if the two input indices are valid and FALSE if the
  503. the index are not valid or if the range is not loaded
  504. --*/
  505. {
  506. PNTFS_MCB_ENTRY Entry = (PNTFS_MCB_ENTRY)*RangePtr;
  507. BOOLEAN Result = FALSE;
  508. NtfsAcquireNtfsMcbMutex( Mcb );
  509. NtfsVerifyNtfsMcb(Mcb);
  510. try {
  511. //
  512. // Null RangePtr means first range
  513. //
  514. if (Entry == FIRST_RANGE) {
  515. Entry = Mcb->NtfsMcbArray[0].NtfsMcbEntry;
  516. }
  517. //
  518. // If there is no entry 0, get out.
  519. //
  520. if (Entry == NULL) {
  521. try_return(Result = FALSE);
  522. }
  523. //
  524. // RunIndex of MAXULONG means first of next
  525. //
  526. if (RunIndex == MAXULONG) {
  527. //
  528. // If we are already in the last range, get out.
  529. //
  530. if (Entry->NtfsMcbArray == (Mcb->NtfsMcbArray + Mcb->NtfsMcbArraySizeInUse - 1)) {
  531. try_return(Result = FALSE);
  532. }
  533. *RangePtr = Entry = (Entry->NtfsMcbArray + 1)->NtfsMcbEntry;
  534. RunIndex = 0;
  535. }
  536. //
  537. // If there is no next entry, get out.
  538. //
  539. if (Entry == NULL) {
  540. try_return(Result = FALSE);
  541. }
  542. ASSERT( Mcb == Entry->NtfsMcb );
  543. //
  544. // Lookup the large mcb entry. If we get a miss then the we're
  545. // beyond the end of the ntfs mcb and should return false
  546. //
  547. if (!FsRtlGetNextLargeMcbEntry( &Entry->LargeMcb, RunIndex, Vcn, Lcn, Count )) {
  548. //
  549. // Our caller should only be off by one or two (if there is
  550. // a hole) runs.
  551. //
  552. ASSERT(RunIndex <= (FsRtlNumberOfRunsInLargeMcb(&Entry->LargeMcb) + 1));
  553. //
  554. // Get the first Vcn past the last Vcn in a run. It is -1 if there
  555. // are no runs.
  556. //
  557. if (!FsRtlLookupLastLargeMcbEntry( &Entry->LargeMcb, Vcn, Lcn )) {
  558. *Vcn = -1;
  559. }
  560. *Vcn += Entry->NtfsMcbArray->StartingVcn + 1;
  561. //
  562. // If that one is beyond the ending Vcn, then get out.
  563. // Otherwise there is a hole at the end of the range, and we
  564. // must return that when he is reading one index beyond the
  565. // last run. If we have a run index beyond that, then it is
  566. // time to return FALSE as well.
  567. //
  568. if ((*Vcn > Entry->NtfsMcbArray->EndingVcn) ||
  569. (RunIndex > FsRtlNumberOfRunsInLargeMcb(&Entry->LargeMcb))) {
  570. try_return(Result = FALSE);
  571. }
  572. //
  573. // If we go off the end of the Mcb, but are in the range, then we
  574. // return a hole to the end of the range.
  575. //
  576. *Lcn = UNUSED_LCN;
  577. *Count = Entry->NtfsMcbArray->EndingVcn - *Vcn + 1;
  578. } else {
  579. //
  580. // Otherwise we have a hit on the large mcb and need to bias the returned
  581. // vcn by the starting vcn value for this range.
  582. //
  583. *Vcn = *Vcn + Entry->NtfsMcbArray->StartingVcn;
  584. }
  585. //
  586. // Make certain we aren't returning a VCN that maps over to
  587. // the next range.
  588. //
  589. ASSERT(*Vcn - 1 != Entry->NtfsMcbArray->EndingVcn);
  590. Result = TRUE;
  591. try_exit: NOTHING;
  592. } finally {
  593. NtfsReleaseNtfsMcbMutex( Mcb );
  594. }
  595. return Result;
  596. }
  597. BOOLEAN
  598. NtfsSplitNtfsMcb (
  599. IN PNTFS_MCB Mcb,
  600. IN LONGLONG Vcn,
  601. IN LONGLONG Amount
  602. )
  603. /*++
  604. Routine Description:
  605. This routine splits an mcb
  606. Arguments:
  607. Mcb - Supplies the Mcb being maniuplated
  608. Vcn - Supplies the Vcn to be shifted
  609. Amount - Supplies the amount to shift by
  610. Return Value:
  611. BOOLEAN - TRUE if worked okay and FALSE otherwise
  612. --*/
  613. {
  614. ULONG RangeIndex;
  615. PNTFS_MCB_ENTRY Entry;
  616. ULONG i;
  617. ASSERT_STREAM_EXCLUSIVE(Mcb);
  618. NtfsVerifyNtfsMcb(Mcb);
  619. //
  620. // Locate the array entry that has the hit for the input vcn
  621. //
  622. RangeIndex = NtfsMcbLookupArrayIndex(Mcb, Vcn);
  623. Entry = Mcb->NtfsMcbArray[RangeIndex].NtfsMcbEntry;
  624. //
  625. // Now if the entry is not null then we have to call the large
  626. // mcb package to split the mcb. Bias the vcn by the starting vcn
  627. //
  628. if (Entry != NULL) {
  629. if (!FsRtlSplitLargeMcb( &Entry->LargeMcb,
  630. Vcn - Mcb->NtfsMcbArray[RangeIndex].StartingVcn,
  631. Amount )) {
  632. NtfsVerifyNtfsMcb(Mcb);
  633. return FALSE;
  634. }
  635. }
  636. //
  637. // Even if the entry is null we will march through the rest of our ranges
  638. // updating the ending vcn and starting vcn as we go. We will update the
  639. // ending vcn for the range we split and only update the starting vcn
  640. // for the last entry, because its ending vcn is already max long long
  641. //
  642. for (i = RangeIndex + 1; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
  643. Mcb->NtfsMcbArray[i - 1].EndingVcn += Amount;
  644. Mcb->NtfsMcbArray[i].StartingVcn += Amount;
  645. }
  646. //
  647. // And grow the last range unless it would wrap.
  648. //
  649. if ((Mcb->NtfsMcbArray[i - 1].EndingVcn + Amount) > Mcb->NtfsMcbArray[i - 1].EndingVcn) {
  650. Mcb->NtfsMcbArray[i - 1].EndingVcn += Amount;
  651. }
  652. //
  653. // Then return to our caller
  654. //
  655. NtfsVerifyNtfsMcb(Mcb);
  656. return TRUE;
  657. }
  658. VOID
  659. NtfsRemoveNtfsMcbEntry (
  660. IN PNTFS_MCB Mcb,
  661. IN LONGLONG StartingVcn,
  662. IN LONGLONG Count
  663. )
  664. /*++
  665. Routine Description:
  666. This routine removes an range of mappings from the Mcb. After
  667. the call the mapping for the range will be a hole. It is an
  668. error to call this routine with the mapping range being removed
  669. also being unloaded.
  670. Arguments:
  671. Mcb - Supplies the Mcb being maniuplated
  672. StartingVcn - Supplies the starting Vcn to remove
  673. Count - Supplies the number of mappings to remove
  674. Return Value:
  675. None.
  676. --*/
  677. {
  678. LONGLONG Vcn;
  679. LONGLONG RunLength;
  680. LONGLONG RemainingCount;
  681. ULONG RangeIndex;
  682. PNTFS_MCB_ENTRY Entry;
  683. VCN EntryStartingVcn;
  684. VCN EntryEndingVcn;
  685. ASSERT_STREAM_EXCLUSIVE(Mcb);
  686. NtfsVerifyNtfsMcb(Mcb);
  687. //
  688. // Loop through the range of vcn's that we need to remove
  689. //
  690. for (Vcn = StartingVcn, RemainingCount = Count;
  691. Vcn < StartingVcn + Count;
  692. Vcn += RunLength, RemainingCount -= RunLength) {
  693. //
  694. // Locate the array entry that has the hit for the vcn
  695. //
  696. RangeIndex = NtfsMcbLookupArrayIndex(Mcb, Vcn);
  697. Entry = Mcb->NtfsMcbArray[RangeIndex].NtfsMcbEntry;
  698. EntryStartingVcn = Mcb->NtfsMcbArray[RangeIndex].StartingVcn;
  699. EntryEndingVcn = Mcb->NtfsMcbArray[RangeIndex].EndingVcn;
  700. //
  701. // Compute how much to delete from the entry. We will delete to
  702. // to end of the entry or as much as count is remaining
  703. //
  704. RunLength = EntryEndingVcn - Vcn + 1;
  705. //
  706. // If the Mcb is set up correctly, the only way we can get
  707. // RunLength == 0 is if the Mcb is completely empty. Assume
  708. // that this is error recovery, and that it is ok.
  709. //
  710. if ((Entry == NULL) || (RunLength == 0)) {
  711. break;
  712. }
  713. //
  714. // If that is too much, then just delete what we need.
  715. //
  716. if ((ULONGLONG)RunLength > (ULONGLONG)RemainingCount) { RunLength = RemainingCount; }
  717. //
  718. // Now remove the mapping from the large mcb, bias the vcn
  719. // by the start of the range
  720. //
  721. FsRtlRemoveLargeMcbEntry( &Entry->LargeMcb, Vcn - EntryStartingVcn, RunLength );
  722. }
  723. NtfsVerifyNtfsMcb(Mcb);
  724. return;
  725. }
  726. BOOLEAN
  727. NtfsAddNtfsMcbEntry (
  728. IN PNTFS_MCB Mcb,
  729. IN LONGLONG Vcn,
  730. IN LONGLONG Lcn,
  731. IN LONGLONG RunCount,
  732. IN BOOLEAN AlreadySynchronized
  733. )
  734. /*++
  735. Routine Description:
  736. This routine add a new entry to a Mcb
  737. Arguments:
  738. Mcb - Supplies the Mcb being modified
  739. Vcn - Supplies the Vcn that we are providing a mapping for
  740. Lcn - Supplies the Lcn corresponding to the input Vcn if run count is non zero
  741. RunCount - Supplies the size of the run following the hole
  742. AlreadySynchronized - Indicates if the caller has already acquired the mcb mutex
  743. Return Value:
  744. BOOLEAN - TRUE if the mapping was added successfully and FALSE otherwise
  745. --*/
  746. {
  747. LONGLONG LocalVcn;
  748. LONGLONG LocalLcn;
  749. LONGLONG RunLength;
  750. LONGLONG RemainingCount;
  751. ULONG RangeIndex;
  752. PNTFS_MCB_ENTRY Entry;
  753. PNTFS_MCB_ENTRY NewEntry = NULL;
  754. LONGLONG EntryStartingVcn;
  755. LONGLONG EntryEndingVcn;
  756. LONGLONG PrevEndingVcn;
  757. BOOLEAN Result = FALSE;
  758. if (!AlreadySynchronized) { NtfsAcquireNtfsMcbMutex( Mcb ); }
  759. NtfsVerifyNtfsMcb(Mcb);
  760. try {
  761. //
  762. // Loop through the range of vcn's that we need to add
  763. //
  764. for (LocalVcn = Vcn, LocalLcn = Lcn, RemainingCount = RunCount;
  765. LocalVcn < Vcn + RunCount;
  766. LocalVcn += RunLength, LocalLcn += RunLength, RemainingCount -= RunLength) {
  767. //
  768. // Locate the array entry that has the hit for the vcn
  769. //
  770. RangeIndex = NtfsMcbLookupArrayIndex(Mcb, LocalVcn);
  771. Entry = Mcb->NtfsMcbArray[RangeIndex].NtfsMcbEntry;
  772. EntryStartingVcn = Mcb->NtfsMcbArray[RangeIndex].StartingVcn;
  773. //
  774. // Now if the entry doesn't exist then we'll need to create one
  775. //
  776. if (Entry == NULL) {
  777. //
  778. // See if we need to get the first entry in the initial structs.
  779. //
  780. if (Mcb->NtfsMcbArraySize == MCB_ARRAY_PHASE1_SIZE) {
  781. Entry = &CONTAINING_RECORD(&Mcb->NtfsMcbArray[0],
  782. NTFS_MCB_INITIAL_STRUCTS,
  783. Phase1.SingleMcbArrayEntry)->Phase1.McbEntry;
  784. //
  785. // Allocate pool and initialize the fields in of the entry
  786. //
  787. } else {
  788. NewEntry =
  789. Entry = NtfsAllocatePoolWithTag( Mcb->PoolType, sizeof(NTFS_MCB_ENTRY), 'MftN' );
  790. }
  791. //
  792. // Initialize the entry but don't put into the Mcb array until
  793. // initialization is complete.
  794. //
  795. Entry->NtfsMcb = Mcb;
  796. Entry->NtfsMcbArray = &Mcb->NtfsMcbArray[RangeIndex];
  797. FsRtlInitializeLargeMcb( &Entry->LargeMcb, Mcb->PoolType );
  798. //
  799. // Now put the entry into the lru queue under the protection of
  800. // the global mutex
  801. //
  802. ExAcquireFastMutex( &NtfsMcbFastMutex );
  803. //
  804. // Only put paged Mcb entries in the queue.
  805. //
  806. if (Mcb->PoolType == PagedPool) {
  807. NtfsMcbEnqueueLruEntry( Mcb, Entry );
  808. }
  809. //
  810. // Now that the initialization is complete we can store
  811. // this entry in the Mcb array. This will now be cleaned
  812. // up with the Scb if there is a future error.
  813. //
  814. Mcb->NtfsMcbArray[RangeIndex].NtfsMcbEntry = Entry;
  815. NewEntry = NULL;
  816. //
  817. // Check if we should fire off the cleanup lru queue work item
  818. //
  819. if ((NtfsMcbCurrentLevel > NtfsMcbHighWaterMark) && !NtfsMcbCleanupInProgress) {
  820. NtfsMcbCleanupInProgress = TRUE;
  821. ExInitializeWorkItem( &NtfsMcbWorkItem, NtfsMcbCleanupLruQueue, NULL );
  822. ExQueueWorkItem( &NtfsMcbWorkItem, CriticalWorkQueue );
  823. }
  824. ExReleaseFastMutex( &NtfsMcbFastMutex );
  825. }
  826. //
  827. // Get out if he is trying to add a hole. At least we created the LargeMcb
  828. //
  829. if (Lcn == UNUSED_LCN) {
  830. try_return( Result = TRUE );
  831. }
  832. //
  833. // If this request goes beyond the end of the range,
  834. // and it is the last range, and we will simply
  835. // grow it.
  836. //
  837. EntryEndingVcn = LocalVcn + RemainingCount - 1;
  838. if ((EntryEndingVcn > Mcb->NtfsMcbArray[RangeIndex].EndingVcn) &&
  839. ((RangeIndex + 1) == Mcb->NtfsMcbArraySizeInUse)) {
  840. PrevEndingVcn = Mcb->NtfsMcbArray[RangeIndex].EndingVcn;
  841. Mcb->NtfsMcbArray[RangeIndex].EndingVcn = EntryEndingVcn;
  842. //
  843. // Otherwise, just insert enough of this run to go to the end
  844. // of the range.
  845. //
  846. } else {
  847. EntryEndingVcn = Mcb->NtfsMcbArray[RangeIndex].EndingVcn;
  848. }
  849. //
  850. // At this point the entry exists so now compute how much to add
  851. // We will add to end of the entry or as much as count allows us
  852. //
  853. RunLength = EntryEndingVcn - LocalVcn + 1;
  854. if (((ULONGLONG)RunLength) > ((ULONGLONG)RemainingCount)) { RunLength = RemainingCount; }
  855. //
  856. // We need to deal with the case where a range is larger than (2^32 - 1) clusters.
  857. // If there are no runs in this range then the state is legal. Otherwise we
  858. // need to split up the entry.
  859. //
  860. if (EntryEndingVcn - EntryStartingVcn >= MAX_CLUSTERS_PER_RANGE) {
  861. //
  862. // We should only be adding this entry as part of a transaction and the
  863. // snapshot limits should force this range to be unloaded on error.
  864. //
  865. ASSERT( ExIsResourceAcquiredExclusiveLite( ((PSCB) (Mcb->FcbHeader))->Header.Resource ));
  866. ASSERT( ((PSCB) (Mcb->FcbHeader))->ScbSnapshot != NULL);
  867. if (Mcb->NtfsMcbArray[RangeIndex].StartingVcn < ((PSCB) (Mcb->FcbHeader))->ScbSnapshot->LowestModifiedVcn) {
  868. ((PSCB) (Mcb->FcbHeader))->ScbSnapshot->LowestModifiedVcn = Mcb->NtfsMcbArray[RangeIndex].StartingVcn;
  869. }
  870. if (Mcb->NtfsMcbArray[RangeIndex].EndingVcn > ((PSCB) (Mcb->FcbHeader))->ScbSnapshot->HighestModifiedVcn) {
  871. ((PSCB) (Mcb->FcbHeader))->ScbSnapshot->HighestModifiedVcn = Mcb->NtfsMcbArray[RangeIndex].EndingVcn;
  872. }
  873. //
  874. // If the count in the this Mcb is non-zero then we must be growing the
  875. // range. We can simply split at the previoius end of the Mcb. It must
  876. // be legal.
  877. //
  878. if (FsRtlNumberOfRunsInLargeMcb( &Entry->LargeMcb ) != 0) {
  879. ASSERT( PrevEndingVcn < EntryEndingVcn );
  880. NtfsInsertNewRange( Mcb, PrevEndingVcn + 1, RangeIndex, FALSE );
  881. //
  882. // There are no runs currently in this range. If we are at the
  883. // start of the range then split at our maximum range value.
  884. // Otherwise split at the Vcn being inserted. We don't need
  885. // to be too smart here. The mapping pair package will decide where
  886. // the final range values are.
  887. //
  888. } else if (LocalVcn == EntryStartingVcn) {
  889. NtfsInsertNewRange( Mcb,
  890. EntryStartingVcn + MAX_CLUSTERS_PER_RANGE,
  891. RangeIndex,
  892. FALSE );
  893. //
  894. // Go ahead and split at the CurrentVcn. On our next pass we will
  895. // trim the length of this new range if necessary.
  896. //
  897. } else {
  898. NtfsInsertNewRange( Mcb,
  899. LocalVcn,
  900. RangeIndex,
  901. FALSE );
  902. }
  903. //
  904. // Set the run length to 0 and go back to the start of the loop.
  905. // We will encounter the inserted range on the next pass.
  906. //
  907. RunLength = 0;
  908. continue;
  909. }
  910. //
  911. // Now add the mapping from the large mcb, bias the vcn
  912. // by the start of the range
  913. //
  914. ASSERT( (LocalVcn - EntryStartingVcn) >= 0 );
  915. if (!FsRtlAddLargeMcbEntry( &Entry->LargeMcb,
  916. LocalVcn - EntryStartingVcn,
  917. LocalLcn,
  918. RunLength )) {
  919. try_return( Result = FALSE );
  920. }
  921. }
  922. Result = TRUE;
  923. try_exit: NOTHING;
  924. } finally {
  925. NtfsVerifyNtfsMcb(Mcb);
  926. if (!AlreadySynchronized) { NtfsReleaseNtfsMcbMutex( Mcb ); }
  927. if (NewEntry != NULL) { NtfsFreePool( NewEntry ); }
  928. }
  929. return Result;
  930. }
  931. VOID
  932. NtfsUnloadNtfsMcbRange (
  933. IN PNTFS_MCB Mcb,
  934. IN LONGLONG StartingVcn,
  935. IN LONGLONG EndingVcn,
  936. IN BOOLEAN TruncateOnly,
  937. IN BOOLEAN AlreadySynchronized
  938. )
  939. /*++
  940. Routine Description:
  941. This routine unloads the mapping stored in the Mcb. After
  942. the call everything from startingVcn and endingvcn is now unmapped and unknown.
  943. Arguments:
  944. Mcb - Supplies the Mcb being manipulated
  945. StartingVcn - Supplies the first Vcn which is no longer being mapped
  946. EndingVcn - Supplies the last vcn to be unloaded
  947. TruncateOnly - Supplies TRUE if last affected range should only be
  948. truncated, or FALSE if it should be unloaded (as during
  949. error recovery)
  950. AlreadySynchronized - Supplies TRUE if our caller already owns the Mcb mutex.
  951. Return Value:
  952. None.
  953. --*/
  954. {
  955. ULONG StartingRangeIndex;
  956. ULONG EndingRangeIndex;
  957. ULONG i;
  958. if (!AlreadySynchronized) { NtfsAcquireNtfsMcbMutex( Mcb ); }
  959. //
  960. // Verify that we've been called to unload a valid range. If we haven't,
  961. // then there's nothing we can unload, so we just return here. Still,
  962. // we'll assert so we can see why we were called with an invalid range.
  963. //
  964. if ((StartingVcn < 0) || (EndingVcn < StartingVcn)) {
  965. //
  966. // The only legal case is if the range is empty.
  967. //
  968. ASSERT( StartingVcn == EndingVcn + 1 );
  969. if (!AlreadySynchronized) { NtfsReleaseNtfsMcbMutex( Mcb ); }
  970. return;
  971. }
  972. NtfsVerifyNtfsMcb(Mcb);
  973. NtfsVerifyUncompressedNtfsMcb(Mcb,StartingVcn,EndingVcn);
  974. //
  975. // Get the starting and ending range indices for this call
  976. //
  977. StartingRangeIndex = NtfsMcbLookupArrayIndex( Mcb, StartingVcn );
  978. EndingRangeIndex = NtfsMcbLookupArrayIndex( Mcb, EndingVcn );
  979. //
  980. // Use try finally to enforce common termination processing.
  981. //
  982. try {
  983. //
  984. // For all paged Mcbs, just unload all ranges touched by the
  985. // unload range, and collapse with any unloaded neighbors.
  986. //
  987. if (Mcb->PoolType == PagedPool) {
  988. //
  989. // Handle truncate case. The first test insures that we only truncate
  990. // the Mcb were were initialized with (we cannot deallocate it).
  991. //
  992. // Also only truncate if ending is MAXLONGLONG and we are not eliminating
  993. // the entire range, because that is the common truncate case, and we
  994. // do not want to unload the last range every time we truncate on close.
  995. //
  996. if (((StartingRangeIndex == 0) && (Mcb->NtfsMcbArraySizeInUse == 1))
  997. ||
  998. (TruncateOnly && (StartingVcn != Mcb->NtfsMcbArray[StartingRangeIndex].StartingVcn))) {
  999. //
  1000. // If this is not a truncate call, make sure to eliminate the
  1001. // entire range.
  1002. //
  1003. if (!TruncateOnly) {
  1004. StartingVcn = 0;
  1005. }
  1006. if (Mcb->NtfsMcbArray[StartingRangeIndex].NtfsMcbEntry != NULL) {
  1007. FsRtlTruncateLargeMcb( &Mcb->NtfsMcbArray[StartingRangeIndex].NtfsMcbEntry->LargeMcb,
  1008. StartingVcn - Mcb->NtfsMcbArray[StartingRangeIndex].StartingVcn );
  1009. }
  1010. Mcb->NtfsMcbArray[StartingRangeIndex].EndingVcn = StartingVcn - 1;
  1011. StartingRangeIndex += 1;
  1012. }
  1013. //
  1014. // Unload entries that are beyond the starting range index
  1015. //
  1016. for (i = StartingRangeIndex; i <= EndingRangeIndex; i += 1) {
  1017. UnloadEntry( Mcb, i );
  1018. }
  1019. //
  1020. // If there is a preceding unloaded range, we must collapse him too.
  1021. //
  1022. if ((StartingRangeIndex != 0) &&
  1023. (Mcb->NtfsMcbArray[StartingRangeIndex - 1].NtfsMcbEntry == NULL)) {
  1024. StartingRangeIndex -= 1;
  1025. }
  1026. //
  1027. // If there is a subsequent unloaded range, we must collapse him too.
  1028. //
  1029. if ((EndingRangeIndex != (Mcb->NtfsMcbArraySizeInUse - 1)) &&
  1030. (Mcb->NtfsMcbArray[EndingRangeIndex + 1].NtfsMcbEntry == NULL)) {
  1031. EndingRangeIndex += 1;
  1032. }
  1033. //
  1034. // Now collapse empty ranges.
  1035. //
  1036. if (StartingRangeIndex < EndingRangeIndex) {
  1037. NtfsCollapseRanges( Mcb, StartingRangeIndex, EndingRangeIndex );
  1038. }
  1039. try_return(NOTHING);
  1040. }
  1041. //
  1042. // For nonpaged Mcbs, there is only one range and we truncate it.
  1043. //
  1044. ASSERT((StartingRangeIndex | EndingRangeIndex) == 0);
  1045. if (Mcb->NtfsMcbArray[0].NtfsMcbEntry != NULL) {
  1046. FsRtlTruncateLargeMcb( &Mcb->NtfsMcbArray[0].NtfsMcbEntry->LargeMcb, StartingVcn );
  1047. }
  1048. Mcb->NtfsMcbArray[0].EndingVcn = StartingVcn - 1;
  1049. try_exit: NOTHING;
  1050. } finally {
  1051. //
  1052. // Truncate all unused entries from the end by dropping ArraySizeInUse
  1053. // to be the index of the last loaded entry + 1.
  1054. //
  1055. for (i = Mcb->NtfsMcbArraySizeInUse - 1;
  1056. (Mcb->NtfsMcbArray[i].NtfsMcbEntry == NULL);
  1057. i--) {
  1058. //
  1059. // If the first range is unloaded, set it to its initial state
  1060. // (empty) and break out.
  1061. //
  1062. if (i==0) {
  1063. Mcb->NtfsMcbArray[0].EndingVcn = -1;
  1064. break;
  1065. }
  1066. }
  1067. Mcb->NtfsMcbArraySizeInUse = i + 1;
  1068. //
  1069. // See if we broke anything.
  1070. //
  1071. NtfsVerifyNtfsMcb(Mcb);
  1072. NtfsVerifyUncompressedNtfsMcb(Mcb,StartingVcn,EndingVcn);
  1073. if (!AlreadySynchronized) { NtfsReleaseNtfsMcbMutex( Mcb ); }
  1074. }
  1075. return;
  1076. }
  1077. VOID
  1078. NtfsDefineNtfsMcbRange (
  1079. IN PNTFS_MCB Mcb,
  1080. IN LONGLONG StartingVcn,
  1081. IN LONGLONG EndingVcn,
  1082. IN BOOLEAN AlreadySynchronized
  1083. )
  1084. /*++
  1085. Routine Description:
  1086. This routine splits an existing range within the Mcb into two ranges
  1087. Arguments:
  1088. Mcb - Supplies the Mcb being modified
  1089. StartingVcn - Supplies the beginning of the new range being split
  1090. EndingVcn - Supplies the ending vcn to include in this new range
  1091. AlreadySynchronized - Indicates if the caller has already acquired the mcb mutex
  1092. Return Value:
  1093. None.
  1094. --*/
  1095. {
  1096. ULONG StartingRangeIndex, EndingRangeIndex;
  1097. if (!AlreadySynchronized) { NtfsAcquireNtfsMcbMutex( Mcb ); }
  1098. NtfsVerifyNtfsMcb(Mcb);
  1099. //
  1100. // Make sure we're of the right pool type
  1101. //
  1102. // If the ending vcn is less than or equal to the starting vcn then we will no op
  1103. // this call
  1104. //
  1105. if ((Mcb->PoolType != PagedPool) || (EndingVcn < StartingVcn)) {
  1106. if (!AlreadySynchronized) { NtfsReleaseNtfsMcbMutex( Mcb ); }
  1107. return;
  1108. }
  1109. try {
  1110. PNTFS_MCB_ARRAY StartingArray;
  1111. PNTFS_MCB_ARRAY EndingArray;
  1112. PNTFS_MCB_ENTRY StartingEntry;
  1113. PNTFS_MCB_ENTRY EndingEntry;
  1114. ULONG i;
  1115. //
  1116. // Locate the Starting Mcb
  1117. //
  1118. StartingRangeIndex = NtfsMcbLookupArrayIndex( Mcb, StartingVcn );
  1119. //
  1120. // Locate the ending Mcb
  1121. //
  1122. EndingRangeIndex = NtfsMcbLookupArrayIndex( Mcb, EndingVcn );
  1123. EndingArray = &Mcb->NtfsMcbArray[EndingRangeIndex];
  1124. EndingEntry = EndingArray->NtfsMcbEntry;
  1125. //
  1126. // Special case: extending last range where StartingVcn matches
  1127. //
  1128. if (((EndingRangeIndex + 1) == Mcb->NtfsMcbArraySizeInUse) &&
  1129. (StartingVcn == EndingArray->StartingVcn) &&
  1130. (EndingArray->EndingVcn <= EndingVcn)) {
  1131. //
  1132. // Since this range already starts with the desired Vcn
  1133. // we adjust the end to match the caller's request
  1134. //
  1135. EndingArray->EndingVcn = EndingVcn;
  1136. ASSERT( ((EndingVcn - StartingVcn) < MAX_CLUSTERS_PER_RANGE) ||
  1137. (EndingEntry == NULL) ||
  1138. (FsRtlNumberOfRunsInLargeMcb( &EndingEntry->LargeMcb ) == 0) );
  1139. leave;
  1140. }
  1141. //
  1142. // Special case: handling defining a range after the end of the file
  1143. //
  1144. if (StartingVcn > EndingArray->EndingVcn) {
  1145. LONGLONG OldEndingVcn = EndingArray->EndingVcn;
  1146. //
  1147. // Has to be the last range.
  1148. //
  1149. ASSERT( StartingRangeIndex == EndingRangeIndex );
  1150. ASSERT( (EndingRangeIndex + 1) == Mcb->NtfsMcbArraySizeInUse );
  1151. //
  1152. // First extend the last range to include our new range.
  1153. //
  1154. EndingArray->EndingVcn = EndingVcn;
  1155. //
  1156. // We will be adding a new range and inserting or growing the
  1157. // previous range up to the new range. If the previous range is
  1158. // *empty* but has an NtfsMcbEntry then we want to unload the entry.
  1159. // Otherwise we will grow that range to the correct value but
  1160. // the Mcb won't contain the clusters for the range. We want
  1161. // to unload that range and update the OldEndingVcn value so
  1162. // as not to create two empty ranges prior to this.
  1163. //
  1164. if ((OldEndingVcn == -1) &&
  1165. (EndingArray->NtfsMcbEntry != NULL)) {
  1166. ASSERT( EndingRangeIndex == 0 );
  1167. UnloadEntry( Mcb, EndingRangeIndex );
  1168. }
  1169. //
  1170. // Create the range the caller specified.
  1171. //
  1172. NtfsInsertNewRange( Mcb, StartingVcn, EndingRangeIndex, TRUE );
  1173. DebugDoit( StartingArray = EndingArray = NULL );
  1174. DebugDoit( StartingEntry = EndingEntry = NULL );
  1175. //
  1176. // If this range does not abut the previous last range, *and*
  1177. // the previous range was not *empty*, then we have to define a
  1178. // range to contain the unloaded space in the middle.
  1179. //
  1180. if (((OldEndingVcn + 1) < StartingVcn) &&
  1181. ((OldEndingVcn + 1) != 0)) {
  1182. NtfsInsertNewRange( Mcb, OldEndingVcn + 1, StartingRangeIndex, TRUE );
  1183. DebugDoit( StartingArray = EndingArray = NULL );
  1184. DebugDoit( StartingEntry = EndingEntry = NULL );
  1185. }
  1186. ASSERT( ((EndingVcn - StartingVcn) < MAX_CLUSTERS_PER_RANGE) ||
  1187. (Mcb->NtfsMcbArray[NtfsMcbLookupArrayIndex( Mcb, EndingVcn )].NtfsMcbEntry == NULL) ||
  1188. (FsRtlNumberOfRunsInLargeMcb( &Mcb->NtfsMcbArray[NtfsMcbLookupArrayIndex( Mcb, EndingVcn )].NtfsMcbEntry->LargeMcb ) == 0) );
  1189. leave;
  1190. }
  1191. //
  1192. // Check if we really need to insert a new range at the ending vcn
  1193. // we only need to do the work if there is not already one at that vcn
  1194. // and this is not the last range
  1195. //
  1196. if (EndingVcn < EndingArray->EndingVcn) {
  1197. NtfsInsertNewRange( Mcb, EndingVcn + 1, EndingRangeIndex, FALSE );
  1198. DebugDoit( StartingArray = EndingArray = NULL );
  1199. DebugDoit( StartingEntry = EndingEntry = NULL );
  1200. //
  1201. // Recache pointers since NtfsMcbArray may have moved
  1202. //
  1203. EndingArray = &Mcb->NtfsMcbArray[EndingRangeIndex];
  1204. EndingEntry = EndingArray->NtfsMcbEntry;
  1205. ASSERT( EndingArray->EndingVcn == EndingVcn );
  1206. }
  1207. //
  1208. // Determine location for insertion
  1209. //
  1210. StartingArray = &Mcb->NtfsMcbArray[StartingRangeIndex];
  1211. StartingEntry = StartingArray->NtfsMcbEntry;
  1212. //
  1213. // Check if we really need to insert a new range at the starting vcn
  1214. // we only need to do the work if this Mcb doesn't start at the
  1215. // requested Vcn
  1216. //
  1217. if (StartingArray->StartingVcn < StartingVcn) {
  1218. NtfsInsertNewRange( Mcb, StartingVcn, StartingRangeIndex, FALSE );
  1219. DebugDoit( StartingArray = EndingArray = NULL );
  1220. DebugDoit( StartingEntry = EndingEntry = NULL );
  1221. StartingRangeIndex++;
  1222. StartingArray = &Mcb->NtfsMcbArray[StartingRangeIndex];
  1223. StartingEntry = StartingArray->NtfsMcbEntry;
  1224. ASSERT( StartingArray->StartingVcn == StartingVcn );
  1225. EndingRangeIndex++;
  1226. // EndingArray = &Mcb->NtfsMcbArray[EndingRangeIndex];
  1227. // EndingEntry = EndingArray->NtfsMcbEntry;
  1228. // ASSERT( EndingArray->EndingVcn == EndingVcn );
  1229. }
  1230. ASSERT( StartingArray->StartingVcn == StartingVcn );
  1231. // ASSERT( EndingArray->EndingVcn == EndingVcn );
  1232. //
  1233. // At this point, we have a Vcn range beginning at StartingVcn stored in
  1234. // NtfsMcbArray[StartingRangeIndex] AND ending at EndingVcb which is the
  1235. // end of NtfsMcbArray[StartingRangeIndex]. This is a collection (>= 1)
  1236. // of NtfsMcbEntry's. Our caller expects to have these reduced to
  1237. // a single run. Note that our caller should never break the restriction
  1238. // on maximum number clusters per range.
  1239. //
  1240. while (StartingRangeIndex != EndingRangeIndex) {
  1241. VCN Vcn;
  1242. BOOLEAN MoreEntries;
  1243. LCN Lcn;
  1244. LONGLONG Count;
  1245. ULONG Index;
  1246. PNTFS_MCB_ARRAY NextArray;
  1247. PNTFS_MCB_ENTRY NextEntry;
  1248. //
  1249. // We merge the contents of NtfsMcbArray[StartingRangeIndex + 1] into
  1250. // NtfsMcbArray[StartingRangeIndex]
  1251. //
  1252. //
  1253. // Look up the first Vcn to move in the second Mcb. If this
  1254. // Mcb consists of one large hole then there is nothing to
  1255. // move.
  1256. //
  1257. NextArray = &Mcb->NtfsMcbArray[StartingRangeIndex + 1];
  1258. NextEntry = NextArray->NtfsMcbEntry;
  1259. //
  1260. // We should never exceed our limit on the maximum number of clusters.
  1261. //
  1262. ASSERT( ((NextArray->EndingVcn - StartingArray->StartingVcn + 1) <= MAX_CLUSTERS_PER_RANGE) ||
  1263. ((FsRtlNumberOfRunsInLargeMcb( &StartingEntry->LargeMcb ) == 0) &&
  1264. (FsRtlNumberOfRunsInLargeMcb( &NextEntry->LargeMcb ) == 0)) );
  1265. Vcn = 0;
  1266. MoreEntries = FsRtlLookupLargeMcbEntry( &NextEntry->LargeMcb,
  1267. Vcn,
  1268. &Lcn,
  1269. &Count,
  1270. NULL,
  1271. NULL,
  1272. &Index );
  1273. //
  1274. // Loop to move entries over.
  1275. //
  1276. //
  1277. // this is the case described by bug #9054.
  1278. // the mcb has somehow? been incorrectly split
  1279. // so this will force everything to be unloaded
  1280. // instead of half loaded and half unloaded
  1281. //
  1282. // the assert is here simply for debug purposes.
  1283. // if this assert fires then we simply want to step
  1284. // thru the code and examine the mcb state to
  1285. // be certain that our assumtions about this bug
  1286. // are correct. the actual bug scenario could not
  1287. // be reproed so this code path is un-tested.
  1288. //
  1289. ASSERT( StartingEntry != NULL );
  1290. if (StartingEntry != NULL) {
  1291. while (MoreEntries) {
  1292. //
  1293. // If this entry is not a hole, move it.
  1294. //
  1295. if (Lcn != UNUSED_LCN) {
  1296. FsRtlAddLargeMcbEntry( &StartingEntry->LargeMcb,
  1297. (Vcn + NextArray->StartingVcn) - StartingArray->StartingVcn,
  1298. Lcn,
  1299. Count );
  1300. }
  1301. Index += 1;
  1302. MoreEntries = FsRtlGetNextLargeMcbEntry( &NextEntry->LargeMcb,
  1303. Index,
  1304. &Vcn,
  1305. &Lcn,
  1306. &Count );
  1307. }
  1308. ASSERT( StartingArray->EndingVcn < NextArray->EndingVcn );
  1309. StartingArray->EndingVcn = NextArray->EndingVcn;
  1310. }
  1311. //
  1312. // We've completely emptied the next Mcb. Unload it.
  1313. //
  1314. UnloadEntry( Mcb, StartingRangeIndex + 1 );
  1315. Mcb->NtfsMcbArraySizeInUse -= 1;
  1316. //
  1317. // Compact the array
  1318. //
  1319. RtlMoveMemory( StartingArray + 1,
  1320. StartingArray + 2,
  1321. sizeof( NTFS_MCB_ARRAY ) * (Mcb->NtfsMcbArraySizeInUse - (StartingRangeIndex + 1))
  1322. );
  1323. //
  1324. // Adjust the backpointers
  1325. //
  1326. for (i = StartingRangeIndex + 1;
  1327. i < Mcb->NtfsMcbArraySizeInUse;
  1328. i += 1) {
  1329. if (Mcb->NtfsMcbArray[i].NtfsMcbEntry != NULL) {
  1330. Mcb->NtfsMcbArray[i].NtfsMcbEntry->NtfsMcbArray = &Mcb->NtfsMcbArray[i];
  1331. }
  1332. }
  1333. EndingRangeIndex--;
  1334. }
  1335. } finally {
  1336. NtfsVerifyNtfsMcb(Mcb);
  1337. if (!AlreadySynchronized) { NtfsReleaseNtfsMcbMutex( Mcb ); }
  1338. }
  1339. return;
  1340. }
  1341. //
  1342. // Local support routines
  1343. //
  1344. ULONG
  1345. NtfsMcbLookupArrayIndex (
  1346. IN PNTFS_MCB Mcb,
  1347. IN VCN Vcn
  1348. )
  1349. /*++
  1350. Routine Description:
  1351. This routines searches the mcb array for an entry that contains
  1352. the input vcn value
  1353. Arguments:
  1354. Mcb - Supplies the Mcb being queried
  1355. Vcn - Supplies the Vcn to lookup
  1356. Return Value:
  1357. ULONG - The index of the entry containing the input Vcn value
  1358. --*/
  1359. {
  1360. ULONG Index;
  1361. ULONG MinIndex;
  1362. ULONG MaxIndex;
  1363. NtfsVerifyNtfsMcb(Mcb);
  1364. //
  1365. // Do a quick binary search for the entry containing the vcn
  1366. //
  1367. MinIndex = 0;
  1368. MaxIndex = Mcb->NtfsMcbArraySizeInUse - 1;
  1369. while (TRUE) {
  1370. Index = (MaxIndex + MinIndex) / 2;
  1371. if ((Mcb->NtfsMcbArray[Index].StartingVcn > Vcn) &&
  1372. (Index != 0)) {
  1373. MaxIndex = Index - 1;
  1374. } else if ((Mcb->NtfsMcbArray[Index].EndingVcn < Vcn) &&
  1375. (Index != Mcb->NtfsMcbArraySizeInUse - 1)) {
  1376. MinIndex = Index + 1;
  1377. } else {
  1378. return Index;
  1379. }
  1380. }
  1381. }
  1382. //
  1383. // Local support routines
  1384. //
  1385. VOID
  1386. NtfsInsertNewRange (
  1387. IN PNTFS_MCB Mcb,
  1388. IN LONGLONG StartingVcn,
  1389. IN ULONG ArrayIndex,
  1390. IN BOOLEAN MakeNewRangeEmpty
  1391. )
  1392. /*++
  1393. This routine is used to add a new range at the specified vcn and index location.
  1394. Since this routine will resize the NtfsMcbArray, the caller must be sure to
  1395. invalidate all cached pointers to NtfsMcbArray entries.
  1396. Arguments:
  1397. Mcb - Supplies the Mcb being modified
  1398. StartingVcn - Supplies the vcn for the new range
  1399. ArrayIndex - Supplies the index currently containing the starting vcn
  1400. MakeNewRangeEmpty - TRUE if the caller wants the new range unloaded regardless
  1401. of the state of the current range
  1402. Return Value:
  1403. None.
  1404. --*/
  1405. {
  1406. ULONG i;
  1407. PNTFS_MCB_ENTRY Entry;
  1408. PNTFS_MCB_ENTRY NewEntry;
  1409. NtfsVerifyNtfsMcb(Mcb);
  1410. //
  1411. // Check if we need to grow the array
  1412. //
  1413. if (Mcb->NtfsMcbArraySizeInUse >= Mcb->NtfsMcbArraySize) {
  1414. NtfsGrowMcbArray( Mcb );
  1415. }
  1416. //
  1417. // Now move entries that are beyond the array index over by one to make
  1418. // room for the new entry
  1419. //
  1420. if (ArrayIndex + 2 <= Mcb->NtfsMcbArraySizeInUse) {
  1421. RtlMoveMemory( &Mcb->NtfsMcbArray[ArrayIndex + 2],
  1422. &Mcb->NtfsMcbArray[ArrayIndex + 1],
  1423. sizeof(NTFS_MCB_ARRAY) * (Mcb->NtfsMcbArraySizeInUse - ArrayIndex - 1));
  1424. for (i = ArrayIndex + 2; i < Mcb->NtfsMcbArraySizeInUse + 1; i += 1) {
  1425. if (Mcb->NtfsMcbArray[i].NtfsMcbEntry != NULL) {
  1426. Mcb->NtfsMcbArray[i].NtfsMcbEntry->NtfsMcbArray = &Mcb->NtfsMcbArray[i];
  1427. }
  1428. }
  1429. }
  1430. //
  1431. // Increment our in use count by one
  1432. //
  1433. Mcb->NtfsMcbArraySizeInUse += 1;
  1434. //
  1435. // Now fix the starting and ending Vcn values for the old entry and the
  1436. // new entry
  1437. //
  1438. Mcb->NtfsMcbArray[ArrayIndex + 1].StartingVcn = StartingVcn;
  1439. Mcb->NtfsMcbArray[ArrayIndex + 1].EndingVcn = Mcb->NtfsMcbArray[ArrayIndex].EndingVcn;
  1440. Mcb->NtfsMcbArray[ArrayIndex + 1].NtfsMcbEntry = NULL;
  1441. Mcb->NtfsMcbArray[ArrayIndex].EndingVcn = StartingVcn - 1;
  1442. //
  1443. // Now if the entry is old entry is not null then we have a bunch of work to do
  1444. //
  1445. if (!MakeNewRangeEmpty && (Entry = Mcb->NtfsMcbArray[ArrayIndex].NtfsMcbEntry) != NULL) {
  1446. LONGLONG Vcn;
  1447. LONGLONG Lcn;
  1448. LONGLONG RunLength;
  1449. ULONG Index;
  1450. BOOLEAN FreeNewEntry = FALSE;
  1451. //
  1452. // Use a try-finally in case the Mcb initialization fails.
  1453. //
  1454. try {
  1455. //
  1456. // Allocate the new entry slot
  1457. //
  1458. NewEntry = NtfsAllocatePoolWithTag( Mcb->PoolType, sizeof(NTFS_MCB_ENTRY), 'MftN' );
  1459. FreeNewEntry = TRUE;
  1460. NewEntry->NtfsMcb = Mcb;
  1461. NewEntry->NtfsMcbArray = &Mcb->NtfsMcbArray[ArrayIndex + 1];
  1462. FsRtlInitializeLargeMcb( &NewEntry->LargeMcb, Mcb->PoolType );
  1463. ExAcquireFastMutex( &NtfsMcbFastMutex );
  1464. NtfsMcbEnqueueLruEntry( Mcb, NewEntry );
  1465. ExReleaseFastMutex( &NtfsMcbFastMutex );
  1466. //
  1467. // Now that the initialization is complete we can store
  1468. // this entry in the Mcb array. This will now be cleaned
  1469. // up with the Scb if there is a future error.
  1470. //
  1471. Mcb->NtfsMcbArray[ArrayIndex + 1].NtfsMcbEntry = NewEntry;
  1472. FreeNewEntry = FALSE;
  1473. //
  1474. // Lookup the entry containing the starting vcn in the old entry and put it
  1475. // in the new entry. But only if the entry exists otherwise we know that
  1476. // the large mcb doesn't extend into the new range
  1477. //
  1478. if (FsRtlLookupLargeMcbEntry( &Entry->LargeMcb,
  1479. StartingVcn - Mcb->NtfsMcbArray[ArrayIndex].StartingVcn,
  1480. &Lcn,
  1481. &RunLength,
  1482. NULL,
  1483. NULL,
  1484. &Index )) {
  1485. if (Lcn != UNUSED_LCN) {
  1486. FsRtlAddLargeMcbEntry( &NewEntry->LargeMcb,
  1487. 0,
  1488. Lcn,
  1489. RunLength );
  1490. }
  1491. //
  1492. // Now for every run in the old entry that is beyond the starting vcn we will
  1493. // copy it into the new entry. This will also copy over the dummy run at the end
  1494. // of the mcb if it exists
  1495. //
  1496. for (i = Index + 1; FsRtlGetNextLargeMcbEntry( &Entry->LargeMcb, i, &Vcn, &Lcn, &RunLength ); i += 1) {
  1497. if (Lcn != UNUSED_LCN) {
  1498. ASSERT( (Vcn - (StartingVcn - Mcb->NtfsMcbArray[ArrayIndex].StartingVcn)) >= 0 );
  1499. FsRtlAddLargeMcbEntry( &NewEntry->LargeMcb,
  1500. Vcn - (StartingVcn - Mcb->NtfsMcbArray[ArrayIndex].StartingVcn),
  1501. Lcn,
  1502. RunLength );
  1503. }
  1504. }
  1505. //
  1506. // Now modify the old mcb to be smaller and put in the dummy run
  1507. //
  1508. FsRtlTruncateLargeMcb( &Entry->LargeMcb,
  1509. StartingVcn - Mcb->NtfsMcbArray[ArrayIndex].StartingVcn );
  1510. }
  1511. } finally {
  1512. if (FreeNewEntry) { NtfsFreePool( NewEntry ); }
  1513. }
  1514. }
  1515. NtfsVerifyNtfsMcb(Mcb);
  1516. return;
  1517. }
  1518. //
  1519. // Local support routines
  1520. //
  1521. VOID
  1522. NtfsCollapseRanges (
  1523. IN PNTFS_MCB Mcb,
  1524. IN ULONG StartingArrayIndex,
  1525. IN ULONG EndingArrayIndex
  1526. )
  1527. /*++
  1528. Routine Description:
  1529. This routine will remove the indicated array entries
  1530. Arguments:
  1531. Mcb - Supplies the Mcb being modified
  1532. StartingArrayIndex - Supplies the first index to remove
  1533. EndingArrayIndex - Supplies the last index to remove
  1534. Return Value:
  1535. None.
  1536. --*/
  1537. {
  1538. ULONG i;
  1539. NtfsVerifyNtfsMcb(Mcb);
  1540. //
  1541. // Make sure all the ranges are unloaded.
  1542. //
  1543. DebugDoit(
  1544. for (i = StartingArrayIndex; i <= EndingArrayIndex; i++) {
  1545. ASSERT(Mcb->NtfsMcbArray[i].NtfsMcbEntry == NULL);
  1546. }
  1547. );
  1548. //
  1549. // We keep the first entry by we need to copy over
  1550. // the ending vcn of the last entry
  1551. //
  1552. Mcb->NtfsMcbArray[StartingArrayIndex].EndingVcn = Mcb->NtfsMcbArray[EndingArrayIndex].EndingVcn;
  1553. //
  1554. // Check if we need to move the ending entries up the array
  1555. // if so then move them forward, and adjust the back pointers.
  1556. //
  1557. if (EndingArrayIndex < Mcb->NtfsMcbArraySizeInUse - 1) {
  1558. RtlMoveMemory( &Mcb->NtfsMcbArray[StartingArrayIndex + 1],
  1559. &Mcb->NtfsMcbArray[EndingArrayIndex + 1],
  1560. sizeof(NTFS_MCB_ARRAY) * (Mcb->NtfsMcbArraySizeInUse - EndingArrayIndex - 1));
  1561. for (i = StartingArrayIndex + 1;
  1562. i <= (StartingArrayIndex + Mcb->NtfsMcbArraySizeInUse - EndingArrayIndex - 1);
  1563. i += 1) {
  1564. if (Mcb->NtfsMcbArray[i].NtfsMcbEntry != NULL) {
  1565. Mcb->NtfsMcbArray[i].NtfsMcbEntry->NtfsMcbArray = &Mcb->NtfsMcbArray[i];
  1566. }
  1567. }
  1568. }
  1569. //
  1570. // Decrement the in use count and return to our caller
  1571. //
  1572. Mcb->NtfsMcbArraySizeInUse -= (EndingArrayIndex - StartingArrayIndex);
  1573. NtfsVerifyNtfsMcb(Mcb);
  1574. return;
  1575. }
  1576. //
  1577. // Local support routine
  1578. //
  1579. VOID
  1580. NtfsMcbCleanupLruQueue (
  1581. IN PVOID Parameter
  1582. )
  1583. /*++
  1584. Routine Description:
  1585. This routine is called as an ex work queue item and its job is
  1586. to free up the lru queue until we reach the low water mark
  1587. Arguments:
  1588. Parameter - ignored
  1589. Return Value:
  1590. None.
  1591. --*/
  1592. {
  1593. PLIST_ENTRY Links;
  1594. PNTFS_MCB Mcb;
  1595. PNTFS_MCB_ARRAY Array;
  1596. PNTFS_MCB_ENTRY Entry;
  1597. UNREFERENCED_PARAMETER( Parameter );
  1598. //
  1599. // Grab the global lock
  1600. //
  1601. ExAcquireFastMutex( &NtfsMcbFastMutex );
  1602. try {
  1603. //
  1604. // Scan through the lru queue until we either exhaust the queue
  1605. // or we've trimmed enough
  1606. //
  1607. for (Links = NtfsMcbLruQueue.Flink;
  1608. (Links != &NtfsMcbLruQueue) && (NtfsMcbCurrentLevel > NtfsMcbLowWaterMark);
  1609. Links = Links->Flink ) {
  1610. //
  1611. // Get the entry and the mcb it points to
  1612. //
  1613. Entry = CONTAINING_RECORD( Links, NTFS_MCB_ENTRY, LruLinks );
  1614. Mcb = Entry->NtfsMcb;
  1615. //
  1616. // Skip this entry if it is in the open attribute table.
  1617. //
  1618. if (((PSCB)(Mcb->FcbHeader))->NonpagedScb->OpenAttributeTableIndex != 0) {
  1619. continue;
  1620. }
  1621. //
  1622. // Try and lock the mcb
  1623. //
  1624. if (NtfsLockNtfsMcb( Mcb )) {
  1625. NtfsVerifyNtfsMcb(Mcb);
  1626. //
  1627. // The previous test was an unsafe test. Check again in case
  1628. // this entry has been added.
  1629. //
  1630. if (((PSCB)(Mcb->FcbHeader))->NonpagedScb->OpenAttributeTableIndex == 0) {
  1631. //
  1632. // We locked the mcb so we can remove this entry, but
  1633. // first backup our links pointer so we can continue with the loop
  1634. //
  1635. Links = Links->Blink;
  1636. //
  1637. // Get a point to the array entry and then remove this entry and return
  1638. // it to pool
  1639. //
  1640. Array = Entry->NtfsMcbArray;
  1641. Array->NtfsMcbEntry = NULL;
  1642. NtfsMcbDequeueLruEntry( Mcb, Entry );
  1643. FsRtlUninitializeLargeMcb( &Entry->LargeMcb );
  1644. if (Mcb->NtfsMcbArraySize != 1) {
  1645. NtfsFreePool( Entry );
  1646. }
  1647. }
  1648. NtfsUnlockNtfsMcb( Mcb );
  1649. }
  1650. }
  1651. } finally {
  1652. //
  1653. // Say we're done with the cleanup so that another one can be fired off when
  1654. // necessary
  1655. //
  1656. NtfsMcbCleanupInProgress = FALSE;
  1657. ExReleaseFastMutex( &NtfsMcbFastMutex );
  1658. }
  1659. //
  1660. // Return to our caller
  1661. //
  1662. return;
  1663. }
  1664. VOID
  1665. NtfsSwapMcbs (
  1666. IN PNTFS_MCB McbTarget,
  1667. IN PNTFS_MCB McbSource
  1668. )
  1669. /*++
  1670. Routine Description:
  1671. This routine swaps the mapping pairs between two mcbs atomically
  1672. Arguments:
  1673. McbTarget -
  1674. McbSource -
  1675. Return Value:
  1676. None.
  1677. --*/
  1678. {
  1679. ULONG TempNtfsMcbArraySizeInUse;
  1680. ULONG TempNtfsMcbArraySize;
  1681. PNTFS_MCB_ARRAY TempNtfsMcbArray;
  1682. ULONG Index;
  1683. ASSERT( McbTarget->PoolType == McbSource->PoolType );
  1684. //
  1685. // Grab the mutex in the original and new mcb to block everyone out
  1686. //
  1687. NtfsAcquireNtfsMcbMutex( McbTarget );
  1688. NtfsAcquireNtfsMcbMutex( McbSource );
  1689. try {
  1690. //
  1691. // Check if we need to grow either array so they are in the general form
  1692. // In the general form we can swap the two by switching the array of mcb entries
  1693. //
  1694. if (McbSource->NtfsMcbArraySize == MCB_ARRAY_PHASE1_SIZE) {
  1695. NtfsGrowMcbArray( McbSource );
  1696. }
  1697. if (McbSource->NtfsMcbArraySize == MCB_ARRAY_PHASE2_SIZE) {
  1698. NtfsGrowMcbArray( McbSource );
  1699. }
  1700. if (McbTarget->NtfsMcbArraySize == MCB_ARRAY_PHASE1_SIZE) {
  1701. NtfsGrowMcbArray( McbTarget);
  1702. }
  1703. if (McbTarget->NtfsMcbArraySize == MCB_ARRAY_PHASE2_SIZE) {
  1704. NtfsGrowMcbArray( McbTarget );
  1705. }
  1706. //
  1707. // Swap the arrays in the two mcb's
  1708. //
  1709. TempNtfsMcbArraySizeInUse = McbTarget->NtfsMcbArraySizeInUse;
  1710. TempNtfsMcbArraySize = McbTarget->NtfsMcbArraySize;
  1711. TempNtfsMcbArray = McbTarget->NtfsMcbArray;
  1712. McbTarget->NtfsMcbArray = McbSource->NtfsMcbArray;
  1713. McbTarget->NtfsMcbArraySize = McbSource->NtfsMcbArraySize;
  1714. McbTarget->NtfsMcbArraySizeInUse = McbSource->NtfsMcbArraySizeInUse;
  1715. McbSource->NtfsMcbArray = TempNtfsMcbArray;
  1716. McbSource->NtfsMcbArraySize = TempNtfsMcbArraySize;
  1717. McbSource->NtfsMcbArraySizeInUse = TempNtfsMcbArraySizeInUse;
  1718. //
  1719. // Fixup the backptr in the array entries to point the the correct mcb
  1720. //
  1721. for (Index=0; Index < McbSource->NtfsMcbArraySize; Index++) {
  1722. if (McbSource->NtfsMcbArray[Index].NtfsMcbEntry != NULL) {
  1723. McbSource->NtfsMcbArray[Index].NtfsMcbEntry->NtfsMcb = McbSource;
  1724. }
  1725. }
  1726. for (Index=0; Index < McbTarget->NtfsMcbArraySize; Index++) {
  1727. if (McbTarget->NtfsMcbArray[Index].NtfsMcbEntry != NULL) {
  1728. McbTarget->NtfsMcbArray[Index].NtfsMcbEntry->NtfsMcb = McbTarget;
  1729. }
  1730. }
  1731. } finally {
  1732. NtfsReleaseNtfsMcbMutex( McbSource );
  1733. NtfsReleaseNtfsMcbMutex( McbTarget );
  1734. }
  1735. }
  1736. //
  1737. // Local support routine
  1738. //
  1739. BOOLEAN
  1740. NtfsLockNtfsMcb (
  1741. IN PNTFS_MCB Mcb
  1742. )
  1743. /*++
  1744. Routine Description:
  1745. This routine attempts to get the Fcb resource(s) exclusive so that
  1746. ranges may be unloaded.
  1747. Arguments:
  1748. Mcb - Supplies the mcb being queried
  1749. Return Value:
  1750. --*/
  1751. {
  1752. //
  1753. // Try to acquire paging resource exclusive.
  1754. //
  1755. if ((Mcb->FcbHeader->PagingIoResource == NULL) ||
  1756. ExAcquireResourceExclusiveLite(Mcb->FcbHeader->PagingIoResource, FALSE)) {
  1757. //
  1758. // Now we can try to acquire the main resource exclusively as well.
  1759. //
  1760. if (ExAcquireResourceExclusiveLite(Mcb->FcbHeader->Resource, FALSE)) {
  1761. return TRUE;
  1762. }
  1763. //
  1764. // We failed to acquire the paging I/O resource, so free the main one
  1765. // on the way out.
  1766. //
  1767. if (Mcb->FcbHeader->PagingIoResource != NULL) {
  1768. ExReleaseResourceLite( Mcb->FcbHeader->PagingIoResource );
  1769. }
  1770. }
  1771. //
  1772. // Could not get this file exclusive.
  1773. //
  1774. return FALSE;
  1775. }
  1776. //
  1777. // Local support routine
  1778. //
  1779. VOID
  1780. NtfsUnlockNtfsMcb (
  1781. IN PNTFS_MCB Mcb
  1782. )
  1783. /*++
  1784. Routine Description:
  1785. This routine verifies that an mcb is properly formed
  1786. Arguments:
  1787. Mcb - Supplies the mcb being queried
  1788. Return Value:
  1789. None.
  1790. --*/
  1791. {
  1792. //
  1793. // If there is a paging I/O resource, release it first.
  1794. //
  1795. if (Mcb->FcbHeader->PagingIoResource != NULL) {
  1796. ExReleaseResourceLite(Mcb->FcbHeader->PagingIoResource);
  1797. }
  1798. //
  1799. // Now release the main resource.
  1800. //
  1801. ExReleaseResourceLite(Mcb->FcbHeader->Resource);
  1802. }
  1803. //
  1804. // Local support routine
  1805. //
  1806. NtfsGrowMcbArray(
  1807. IN PNTFS_MCB Mcb
  1808. )
  1809. /*++
  1810. Routine Description:
  1811. This routine grows the mcb array. If its phase1 - then it will be promoted to phase2
  1812. If its phase2 it will become the general form. If its the general form 8 new entries will be added.
  1813. Arguments:
  1814. Mcb - Supplies the mcb being grown
  1815. Return Value:
  1816. None.
  1817. --*/
  1818. {
  1819. PNTFS_MCB_ARRAY NewArray;
  1820. ULONG OldArraySize = Mcb->NtfsMcbArraySize;
  1821. PNTFS_MCB_ENTRY Entry;
  1822. //
  1823. // Test for initial case where we only have one array entry.
  1824. //
  1825. if (Mcb->NtfsMcbArraySize == MCB_ARRAY_PHASE1_SIZE) {
  1826. //
  1827. // Convince ourselves that we do not have to move the array entry.
  1828. //
  1829. ASSERT(FIELD_OFFSET(NTFS_MCB_INITIAL_STRUCTS, Phase1.SingleMcbArrayEntry) ==
  1830. FIELD_OFFSET(NTFS_MCB_INITIAL_STRUCTS, Phase2.ThreeMcbArrayEntries));
  1831. if (Mcb->NtfsMcbArray[0].NtfsMcbEntry != NULL) {
  1832. //
  1833. // Allocate a new Mcb Entry, copy the current one over and change the pointer.
  1834. //
  1835. Entry = NtfsAllocatePoolWithTag( Mcb->PoolType, sizeof(NTFS_MCB_ENTRY), 'MftN' );
  1836. //
  1837. // Once space is allocated, dequeue the old entry.
  1838. //
  1839. ExAcquireFastMutex( &NtfsMcbFastMutex );
  1840. NtfsMcbDequeueLruEntry( Mcb, Mcb->NtfsMcbArray[0].NtfsMcbEntry );
  1841. RtlCopyMemory( Entry, Mcb->NtfsMcbArray[0].NtfsMcbEntry, sizeof(NTFS_MCB_ENTRY) );
  1842. Mcb->NtfsMcbArray[0].NtfsMcbEntry = Entry;
  1843. NtfsMcbEnqueueLruEntry( Mcb, Entry );
  1844. ExReleaseFastMutex( &NtfsMcbFastMutex );
  1845. }
  1846. //
  1847. // Now change to using the three array elements
  1848. //
  1849. Mcb->NtfsMcbArraySize = MCB_ARRAY_PHASE2_SIZE;
  1850. } else {
  1851. ULONG i;
  1852. //
  1853. // If we do then allocate an array that can contain 8 more entires
  1854. //
  1855. NewArray = NtfsAllocatePoolWithTag( Mcb->PoolType, sizeof(NTFS_MCB_ARRAY) * (Mcb->NtfsMcbArraySize + 8), 'mftN' );
  1856. Mcb->NtfsMcbArraySize += 8;
  1857. //
  1858. // Copy over the memory from the old array to the new array and then
  1859. // for every loaded entry we need to adjust its back pointer to the
  1860. // array
  1861. //
  1862. RtlCopyMemory( NewArray, Mcb->NtfsMcbArray, sizeof(NTFS_MCB_ARRAY) * OldArraySize );
  1863. for (i = 0; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
  1864. if (NewArray[i].NtfsMcbEntry != NULL) {
  1865. NewArray[i].NtfsMcbEntry->NtfsMcbArray = &NewArray[i];
  1866. }
  1867. }
  1868. //
  1869. // Free the old array if it was not the original array.
  1870. //
  1871. if (OldArraySize > MCB_ARRAY_PHASE2_SIZE) {
  1872. NtfsFreePool( Mcb->NtfsMcbArray );
  1873. }
  1874. Mcb->NtfsMcbArray = NewArray;
  1875. }
  1876. //
  1877. // Zero the new part of the array.
  1878. //
  1879. ASSERT(sizeof(NTFS_MCB_ARRAY) == ((PCHAR)&NewArray[1] - (PCHAR)&NewArray[0]));
  1880. RtlZeroMemory( &Mcb->NtfsMcbArray[OldArraySize],
  1881. (Mcb->NtfsMcbArraySize - OldArraySize) * sizeof(NTFS_MCB_ARRAY) );
  1882. }
  1883. #ifdef NTFS_VERIFY_MCB
  1884. //
  1885. // Local support routine
  1886. //
  1887. VOID
  1888. NtfsVerifyNtfsMcb (
  1889. IN PNTFS_MCB Mcb
  1890. )
  1891. /*++
  1892. Routine Description:
  1893. This routine verifies that an mcb is properly formed
  1894. Arguments:
  1895. Mcb - Supplies the mcb being queried
  1896. Return Value:
  1897. --*/
  1898. {
  1899. ULONG i;
  1900. PNTFS_MCB_ARRAY Array;
  1901. PNTFS_MCB_ENTRY Entry;
  1902. LONGLONG Vbn;
  1903. LONGLONG Lbn;
  1904. ASSERT(Mcb->FcbHeader != NULL);
  1905. ASSERT(Mcb->FcbHeader->NodeTypeCode != 0);
  1906. ASSERT((Mcb->PoolType == PagedPool) || (Mcb->PoolType == NonPagedPool));
  1907. ASSERT(Mcb->NtfsMcbArraySizeInUse <= Mcb->NtfsMcbArraySize);
  1908. for (i = 0; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
  1909. Array = &Mcb->NtfsMcbArray[i];
  1910. ASSERT(((i == 0) && (Array->StartingVcn == 0)) ||
  1911. ((i != 0) && (Array->StartingVcn != 0)));
  1912. ASSERT(Array->StartingVcn <= (Array->EndingVcn + 1));
  1913. if ((Entry = Array->NtfsMcbEntry) != NULL) {
  1914. ASSERT(Entry->NtfsMcb == Mcb);
  1915. ASSERT(Entry->NtfsMcbArray == Array);
  1916. if (FsRtlLookupLastLargeMcbEntry( &Entry->LargeMcb, &Vbn, &Lbn )) {
  1917. ASSERT( Vbn <= (Array->EndingVcn - Array->StartingVcn) );
  1918. }
  1919. }
  1920. }
  1921. }
  1922. //
  1923. // Local support routine
  1924. //
  1925. VOID
  1926. NtfsVerifyUncompressedNtfsMcb (
  1927. IN PNTFS_MCB Mcb,
  1928. IN LONGLONG StartingVcn,
  1929. IN LONGLONG EndingVcn
  1930. )
  1931. /*++
  1932. Routine Description:
  1933. This routines checks if an mcb is for an uncompressed scb and then
  1934. checks that there are no holes in the mcb. Holes within the range being
  1935. removed are legal provided EndingVcn is max long long.
  1936. Arguments:
  1937. Mcb - Supplies the Mcb being examined
  1938. StartingVcn - The starting Vcn being unloaded
  1939. EndingVcn - The ending Vcn being unloaded
  1940. Return Value:
  1941. None
  1942. --*/
  1943. {
  1944. ULONG i;
  1945. ULONG j;
  1946. PNTFS_MCB_ARRAY Array;
  1947. PNTFS_MCB_ENTRY Entry;
  1948. LONGLONG Vbn;
  1949. LONGLONG Lbn;
  1950. LONGLONG Count;
  1951. //
  1952. // Check if the scb is compressed
  1953. //
  1954. if (((PSCB)Mcb->FcbHeader)->CompressionUnit != 0) { return; }
  1955. //
  1956. // For each large mcb in the ntfs mcb we will make sure it doesn't
  1957. // have any holes.
  1958. //
  1959. for (i = 0; i < Mcb->NtfsMcbArraySizeInUse; i += 1) {
  1960. Array = &Mcb->NtfsMcbArray[i];
  1961. if ((Entry = Array->NtfsMcbEntry) != NULL) {
  1962. for (j = 0; FsRtlGetNextLargeMcbEntry(&Entry->LargeMcb,j,&Vbn,&Lbn,&Count); j += 1) {
  1963. ASSERT((Lbn != -1) ||
  1964. ((Vbn + Array->StartingVcn >= StartingVcn) && (EndingVcn == MAXLONGLONG)) ||
  1965. FlagOn(((PSCB)Mcb->FcbHeader)->Vcb->VcbState, VCB_STATE_RESTART_IN_PROGRESS));
  1966. }
  1967. }
  1968. }
  1969. return;
  1970. }
  1971. #endif