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

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