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.

2451 lines
67 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. MftSup.c
  5. Abstract:
  6. This module implements the master file table management routines for Ntfs
  7. Author:
  8. Your Name [Email] dd-Mon-Year
  9. Revision History:
  10. --*/
  11. #include "NtfsProc.h"
  12. //
  13. // The Bug check file id for this module
  14. //
  15. #define BugCheckFileId (NTFS_BUG_CHECK_STRUCSUP)
  16. //
  17. // Local debug trace level
  18. //
  19. #define Dbg (DEBUG_TRACE_MFTSUP)
  20. //
  21. // Boolean controlling whether to allow holes in the Mft.
  22. //
  23. BOOLEAN NtfsPerforateMft = FALSE;
  24. //
  25. // Local support routines
  26. //
  27. BOOLEAN
  28. NtfsTruncateMft (
  29. IN PIRP_CONTEXT IrpContext,
  30. IN PVCB Vcb
  31. );
  32. BOOLEAN
  33. NtfsDefragMftPriv (
  34. IN PIRP_CONTEXT IrpContext,
  35. IN PVCB Vcb
  36. );
  37. LONG
  38. NtfsReadMftExceptionFilter (
  39. IN PIRP_CONTEXT IrpContext,
  40. IN PEXCEPTION_POINTERS ExceptionPointer,
  41. IN PBCB Bcb,
  42. IN LONGLONG FileOffset
  43. );
  44. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  45. VOID
  46. NtfsVerifyFileReference (
  47. IN PIRP_CONTEXT IrpContext,
  48. IN PMFT_SEGMENT_REFERENCE MftSegment
  49. );
  50. #endif
  51. #ifdef ALLOC_PRAGMA
  52. #pragma alloc_text(PAGE, NtfsAllocateMftRecord)
  53. #pragma alloc_text(PAGE, NtfsCheckForDefrag)
  54. #pragma alloc_text(PAGE, NtfsDeallocateMftRecord)
  55. #pragma alloc_text(PAGE, NtfsDefragMftPriv)
  56. #pragma alloc_text(PAGE, NtfsFillMftHole)
  57. #pragma alloc_text(PAGE, NtfsInitializeMftHoleRecords)
  58. #pragma alloc_text(PAGE, NtfsInitializeMftRecord)
  59. #pragma alloc_text(PAGE, NtfsIsMftIndexInHole)
  60. #pragma alloc_text(PAGE, NtfsLogMftFileRecord)
  61. #pragma alloc_text(PAGE, NtfsPinMftRecord)
  62. #pragma alloc_text(PAGE, NtfsReadFileRecord)
  63. #pragma alloc_text(PAGE, NtfsReadMftRecord)
  64. #pragma alloc_text(PAGE, NtfsTruncateMft)
  65. #pragma alloc_text(PAGE, NtfsIterateMft)
  66. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  67. #pragma alloc_text(PAGE, NtfsVerifyFileReference)
  68. #endif
  69. #endif
  70. #if NTFSDBG
  71. ULONG FileRecordCacheHit = 0;
  72. ULONG FileRecordCacheMiss = 0;
  73. #endif // DBG
  74. VOID
  75. NtfsReadFileRecord (
  76. IN PIRP_CONTEXT IrpContext,
  77. IN PVCB Vcb,
  78. IN PFILE_REFERENCE FileReference,
  79. OUT PBCB *Bcb,
  80. OUT PFILE_RECORD_SEGMENT_HEADER *BaseFileRecord,
  81. OUT PATTRIBUTE_RECORD_HEADER *FirstAttribute,
  82. OUT PLONGLONG MftFileOffset OPTIONAL
  83. )
  84. /*++
  85. Routine Description:
  86. This routine reads the specified file record from the Mft or cache if its present
  87. If it comes from disk it is always verified.
  88. Arguments:
  89. Vcb - Vcb for volume on which Mft is to be read
  90. Fcb - If specified allows us to identify the file which owns the
  91. invalid file record.
  92. FileReference - File reference, including sequence number, of the file record
  93. to be read.
  94. Bcb - Returns the Bcb for the file record. This Bcb is mapped, not pinned.
  95. BaseFileRecord - Returns a pointer to the requested file record.
  96. FirstAttribute - Returns a pointer to the first attribute in the file record.
  97. MftFileOffset - If specified, returns the file offset of the file record.
  98. Return Value:
  99. None
  100. --*/
  101. {
  102. ASSERT_IRP_CONTEXT( IrpContext );
  103. ASSERT_VCB( Vcb );
  104. PAGED_CODE();
  105. DebugTrace( +1, Dbg, ("NtfsReadFileRecord\n") );
  106. //
  107. // Perform a quick look-aside to see if the file record being requested
  108. // is one that we have cached in the IrpContext. If so, we reuse that Bcb
  109. //
  110. if (NtfsFindCachedFileRecord( IrpContext,
  111. NtfsSegmentNumber( FileReference ),
  112. Bcb,
  113. BaseFileRecord )) {
  114. //
  115. // We found the Bcb and File record in the cache. Figure out the remainder
  116. // of the data
  117. //
  118. if (ARGUMENT_PRESENT( MftFileOffset )) {
  119. *MftFileOffset =
  120. LlBytesFromFileRecords( Vcb, NtfsSegmentNumber( FileReference ));
  121. DebugDoit( FileRecordCacheHit++ );
  122. }
  123. } else {
  124. USHORT SequenceNumber = FileReference->SequenceNumber;
  125. DebugDoit( FileRecordCacheMiss++ );
  126. NtfsReadMftRecord( IrpContext,
  127. Vcb,
  128. FileReference,
  129. TRUE,
  130. Bcb,
  131. BaseFileRecord,
  132. MftFileOffset );
  133. //
  134. // Make sure the file is in use - we validated everything else in NtfsReadMftRecord
  135. //
  136. if (!FlagOn( (*BaseFileRecord)->Flags, FILE_RECORD_SEGMENT_IN_USE )) {
  137. NtfsUnpinBcb( IrpContext, Bcb );
  138. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, FileReference, NULL );
  139. }
  140. }
  141. *FirstAttribute = (PATTRIBUTE_RECORD_HEADER)((PCHAR)*BaseFileRecord +
  142. (*BaseFileRecord)->FirstAttributeOffset);
  143. DebugTrace( -1, Dbg, ("NtfsReadFileRecord -> VOID\n") );
  144. return;
  145. }
  146. VOID
  147. NtfsReadMftRecord (
  148. IN PIRP_CONTEXT IrpContext,
  149. IN PVCB Vcb,
  150. IN PMFT_SEGMENT_REFERENCE SegmentReference,
  151. IN BOOLEAN CheckRecord,
  152. OUT PBCB *Bcb,
  153. OUT PFILE_RECORD_SEGMENT_HEADER *FileRecord,
  154. OUT PLONGLONG MftFileOffset OPTIONAL
  155. )
  156. /*++
  157. Routine Description:
  158. This routine reads the specified Mft record from the Mft, without checking
  159. sequence numbers. This routine may be used to read records in the Mft for
  160. a file other than its base file record, or it could conceivably be used for
  161. extraordinary maintenance functions.
  162. Arguments:
  163. Vcb - Vcb for volume on which Mft is to be read
  164. SegmentReference - File reference, including sequence number, of the file
  165. record to be read.
  166. Bcb - Returns the Bcb for the file record. This Bcb is mapped, not pinned.
  167. FileRecord - Returns a pointer to the requested file record.
  168. MftFileOffset - If specified, returns the file offset of the file record.
  169. CheckRecord - Do a check of records consistency - always set TRUE unless the
  170. record is unowned and could change beneath us
  171. Return Value:
  172. None
  173. --*/
  174. {
  175. PFILE_RECORD_SEGMENT_HEADER FileRecord2;
  176. LONGLONG FileOffset;
  177. PBCB Bcb2 = NULL;
  178. BOOLEAN ErrorPath = FALSE;
  179. LONGLONG LlTemp1;
  180. ULONG CorruptHint;
  181. ASSERT_IRP_CONTEXT( IrpContext );
  182. ASSERT_VCB( Vcb );
  183. PAGED_CODE();
  184. DebugTrace( +1, Dbg, ("NtfsReadMftRecord\n") );
  185. DebugTrace( 0, Dbg, ("Vcb = %08lx\n", Vcb) );
  186. DebugTrace( 0, Dbg, ("SegmentReference = %08lx\n", NtfsSegmentNumber( SegmentReference )) );
  187. *Bcb = NULL;
  188. try {
  189. //
  190. // Capture the Segment Reference and make sure the Sequence Number is 0.
  191. //
  192. FileOffset = NtfsFullSegmentNumber( SegmentReference );
  193. //
  194. // Calculate the file offset in the Mft to the file record segment.
  195. //
  196. FileOffset = LlBytesFromFileRecords( Vcb, FileOffset );
  197. //
  198. // Pass back the file offset within the Mft.
  199. //
  200. if (ARGUMENT_PRESENT( MftFileOffset )) {
  201. *MftFileOffset = FileOffset;
  202. }
  203. //
  204. // Try to read it from the normal Mft.
  205. //
  206. try {
  207. NtfsMapStream( IrpContext,
  208. Vcb->MftScb,
  209. FileOffset,
  210. Vcb->BytesPerFileRecordSegment,
  211. Bcb,
  212. (PVOID *)FileRecord );
  213. //
  214. // Raise here if we have a file record covered by the mirror,
  215. // and we do not see the file signature.
  216. //
  217. if ((FileOffset < Vcb->Mft2Scb->Header.FileSize.QuadPart) &&
  218. (*(PULONG)(*FileRecord)->MultiSectorHeader.Signature != *(PULONG)FileSignature)) {
  219. NtfsRaiseStatus( IrpContext, STATUS_DATA_ERROR, NULL, NULL );
  220. }
  221. //
  222. // If we get an exception that is not expected, then we will allow
  223. // the search to continue and let the crash occur in the "normal" place.
  224. // Otherwise, if the read is within the part of the Mft mirrored in Mft2,
  225. // then we will simply try to read the data from Mft2. If the expected
  226. // status came from a read not within Mft2, then we will also continue,
  227. // which cause one of our caller's try-except's to initiate an unwind.
  228. //
  229. } except (NtfsReadMftExceptionFilter( IrpContext, GetExceptionInformation(), *Bcb, FileOffset )) {
  230. NtfsMinimumExceptionProcessing( IrpContext );
  231. ErrorPath = TRUE;
  232. }
  233. if (ErrorPath) {
  234. //
  235. // Try to read from Mft2. If this fails with an expected status,
  236. // then we are just going to have to give up and let the unwind
  237. // occur from one of our caller's try-except.
  238. //
  239. NtfsMapStream( IrpContext,
  240. Vcb->Mft2Scb,
  241. FileOffset,
  242. Vcb->BytesPerFileRecordSegment,
  243. &Bcb2,
  244. (PVOID *)&FileRecord2 );
  245. //
  246. // Pin the original page because we are going to update it.
  247. //
  248. NtfsPinMappedData( IrpContext,
  249. Vcb->MftScb,
  250. FileOffset,
  251. Vcb->BytesPerFileRecordSegment,
  252. Bcb );
  253. //
  254. // Now copy the entire page.
  255. //
  256. RtlCopyMemory( *FileRecord,
  257. FileRecord2,
  258. Vcb->BytesPerFileRecordSegment );
  259. //
  260. // Set it dirty with the largest Lsn, so that whoever is doing Restart
  261. // will successfully establish the "oldest unapplied Lsn".
  262. //
  263. LlTemp1 = MAXLONGLONG;
  264. CcSetDirtyPinnedData( *Bcb,
  265. (PLARGE_INTEGER)&LlTemp1 );
  266. NtfsUnpinBcb( IrpContext, &Bcb2 );
  267. }
  268. //
  269. // Do a consistency check
  270. //
  271. if ( CheckRecord && FlagOn((*FileRecord)->Flags, FILE_RECORD_SEGMENT_IN_USE ) ) {
  272. if (!NtfsCheckFileRecord( Vcb, *FileRecord, SegmentReference, &CorruptHint )) {
  273. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, SegmentReference, NULL );
  274. }
  275. }
  276. } finally {
  277. if (AbnormalTermination()) {
  278. NtfsUnpinBcb( IrpContext, Bcb );
  279. NtfsUnpinBcb( IrpContext, &Bcb2 );
  280. }
  281. }
  282. //
  283. // Now that we've pinned a file record, cache it in the IrpContext so that
  284. // it can be safely retrieved later without the expense of mapping again.
  285. // Don't do any caching if there are no handles, we don't want to do this for
  286. // mount.
  287. //
  288. if (Vcb->CleanupCount != 0) {
  289. NtfsAddToFileRecordCache( IrpContext,
  290. NtfsSegmentNumber( SegmentReference ),
  291. *Bcb,
  292. *FileRecord );
  293. }
  294. DebugTrace( 0, Dbg, ("Bcb > %08lx\n", Bcb) );
  295. DebugTrace( 0, Dbg, ("FileRecord > %08lx\n", *FileRecord) );
  296. DebugTrace( -1, Dbg, ("NtfsReadMftRecord -> VOID\n") );
  297. return;
  298. }
  299. VOID
  300. NtfsPinMftRecord (
  301. IN PIRP_CONTEXT IrpContext,
  302. IN PVCB Vcb,
  303. IN PMFT_SEGMENT_REFERENCE SegmentReference,
  304. IN BOOLEAN PreparingToWrite,
  305. OUT PBCB *Bcb,
  306. OUT PFILE_RECORD_SEGMENT_HEADER *FileRecord,
  307. OUT PLONGLONG MftFileOffset OPTIONAL
  308. )
  309. /*++
  310. Routine Description:
  311. This routine pins the specified Mft record from the Mft, without checking
  312. sequence numbers. This routine may be used to pin records in the Mft for
  313. a file other than its base file record, or it could conceivably be used for
  314. extraordinary maintenance functions, such as during restart.
  315. Arguments:
  316. Vcb - Vcb for volume on which Mft is to be read
  317. SegmentReference - File reference, including sequence number, of the file
  318. record to be read.
  319. PreparingToWrite - TRUE if caller is preparing to write, and does not care
  320. about whether the record read correctly
  321. Bcb - Returns the Bcb for the file record. This Bcb is mapped, not pinned.
  322. FileRecord - Returns a pointer to the requested file record.
  323. MftFileOffset - If specified, returns the file offset of the file record.
  324. Return Value:
  325. None
  326. --*/
  327. {
  328. LONGLONG FileOffset;
  329. ASSERT_IRP_CONTEXT( IrpContext );
  330. ASSERT_VCB( Vcb );
  331. PAGED_CODE();
  332. DebugTrace( +1, Dbg, ("NtfsPinMftRecord\n") );
  333. DebugTrace( 0, Dbg, ("Vcb = %08lx\n", Vcb) );
  334. DebugTrace( 0, Dbg, ("SegmentReference = %08lx\n", NtfsSegmentNumber( SegmentReference )) );
  335. //
  336. // Capture the Segment Reference and make sure the Sequence Number is 0.
  337. //
  338. FileOffset = NtfsFullSegmentNumber( SegmentReference );
  339. //
  340. // Calculate the file offset in the Mft to the file record segment.
  341. //
  342. FileOffset = LlBytesFromFileRecords( Vcb, FileOffset );
  343. //
  344. // Pass back the file offset within the Mft.
  345. //
  346. if (ARGUMENT_PRESENT( MftFileOffset )) {
  347. *MftFileOffset = FileOffset;
  348. }
  349. //
  350. // Try to read it from the normal Mft.
  351. //
  352. try {
  353. NtfsPinStream( IrpContext,
  354. Vcb->MftScb,
  355. FileOffset,
  356. Vcb->BytesPerFileRecordSegment,
  357. Bcb,
  358. (PVOID *)FileRecord );
  359. //
  360. // If we get an exception that is not expected, then we will allow
  361. // the search to continue and let the crash occur in the "normal" place.
  362. // Otherwise, if the read is within the part of the Mft mirrored in Mft2,
  363. // then we will simply try to read the data from Mft2. If the expected
  364. // status came from a read not within Mft2, then we will also continue,
  365. // which cause one of our caller's try-except's to initiate an unwind.
  366. //
  367. } except(!FsRtlIsNtstatusExpected(GetExceptionCode()) ?
  368. EXCEPTION_CONTINUE_SEARCH :
  369. ( FileOffset < Vcb->Mft2Scb->Header.FileSize.QuadPart ) ?
  370. EXCEPTION_EXECUTE_HANDLER :
  371. EXCEPTION_CONTINUE_SEARCH ) {
  372. //
  373. // Try to read from Mft2. If this fails with an expected status,
  374. // then we are just going to have to give up and let the unwind
  375. // occur from one of our caller's try-except.
  376. //
  377. NtfsMinimumExceptionProcessing( IrpContext );
  378. NtfsPinStream( IrpContext,
  379. Vcb->Mft2Scb,
  380. FileOffset,
  381. Vcb->BytesPerFileRecordSegment,
  382. Bcb,
  383. (PVOID *)FileRecord );
  384. }
  385. if (!PreparingToWrite &&
  386. (*(PULONG)(*FileRecord)->MultiSectorHeader.Signature != *(PULONG)FileSignature)) {
  387. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, SegmentReference, NULL );
  388. }
  389. //
  390. // Now that we've pinned a file record, cache it in the IrpContext so that
  391. // it can be safely retrieved later without the expense of mapping again.
  392. // Don't do any caching if there are no handles, we don't want to do this for
  393. // mount.
  394. //
  395. if (Vcb->CleanupCount != 0) {
  396. NtfsAddToFileRecordCache( IrpContext,
  397. NtfsSegmentNumber( SegmentReference ),
  398. *Bcb,
  399. *FileRecord );
  400. }
  401. DebugTrace( 0, Dbg, ("Bcb > %08lx\n", Bcb) );
  402. DebugTrace( 0, Dbg, ("FileRecord > %08lx\n", *FileRecord) );
  403. DebugTrace( -1, Dbg, ("NtfsPinMftRecord -> VOID\n") );
  404. return;
  405. }
  406. MFT_SEGMENT_REFERENCE
  407. NtfsAllocateMftRecord (
  408. IN PIRP_CONTEXT IrpContext,
  409. IN PVCB Vcb,
  410. IN BOOLEAN MftData
  411. )
  412. /*++
  413. Routine Description:
  414. This routine is called to allocate a record in the Mft file. We need
  415. to find the bitmap attribute for the Mft file and call into the bitmap
  416. package to allocate us a record.
  417. Arguments:
  418. Vcb - Vcb for volume on which Mft is to be read
  419. MftData - TRUE if the file record is being allocated to describe the
  420. $DATA attribute for the Mft.
  421. Return Value:
  422. MFT_SEGMENT_REFERENCE - The is the segment reference for the allocated
  423. record. It contains the file reference number but without
  424. the previous sequence number.
  425. --*/
  426. {
  427. MFT_SEGMENT_REFERENCE NewMftRecord;
  428. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  429. BOOLEAN FoundAttribute;
  430. PAGED_CODE();
  431. DebugTrace( +1, Dbg, ("NtfsAllocateMftRecord: Entered\n") );
  432. //
  433. // Synchronize the lookup by acquiring the Mft.
  434. //
  435. NtfsAcquireExclusiveScb( IrpContext, Vcb->MftScb );
  436. //
  437. // Lookup the bitmap allocation for the Mft file. This is the
  438. // bitmap attribute for the Mft file.
  439. //
  440. NtfsInitializeAttributeContext( &AttrContext );
  441. //
  442. // Use a try finally to cleanup the attribute context.
  443. //
  444. try {
  445. //
  446. // Lookup the bitmap attribute for the Mft.
  447. //
  448. FoundAttribute = NtfsLookupAttributeByCode( IrpContext,
  449. Vcb->MftScb->Fcb,
  450. &Vcb->MftScb->Fcb->FileReference,
  451. $BITMAP,
  452. &AttrContext );
  453. //
  454. // Error if we don't find the bitmap
  455. //
  456. if (!FoundAttribute) {
  457. DebugTrace( 0, Dbg, ("Should find bitmap attribute\n") );
  458. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  459. }
  460. //
  461. // Reserve a new mft record if necc.
  462. //
  463. if (!FlagOn(Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED)) {
  464. (VOID)NtfsReserveMftRecord( IrpContext,
  465. Vcb,
  466. &AttrContext );
  467. }
  468. //
  469. // If we need this record for the Mft Data attribute, then we need to
  470. // use the one we have already reserved, and then remember there is'nt
  471. // one reserved anymore.
  472. //
  473. if (MftData) {
  474. ASSERT( FlagOn(Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED) );
  475. NtfsSetSegmentNumber( &NewMftRecord,
  476. 0,
  477. NtfsAllocateMftReservedRecord( IrpContext,
  478. Vcb,
  479. &AttrContext ) );
  480. //
  481. // Never let use get file record zero for this or we could lose a
  482. // disk.
  483. //
  484. ASSERT( NtfsUnsafeSegmentNumber( &NewMftRecord ) != 0 );
  485. if (NtfsUnsafeSegmentNumber( &NewMftRecord ) == 0) {
  486. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  487. }
  488. //
  489. // Allocate the record.
  490. //
  491. } else {
  492. NtfsSetSegmentNumber( &NewMftRecord,
  493. 0,
  494. NtfsAllocateRecord( IrpContext,
  495. &Vcb->MftScb->ScbType.Index.RecordAllocationContext,
  496. &AttrContext ) );
  497. }
  498. NtfsReleaseScb( IrpContext, Vcb->MftScb );
  499. } finally {
  500. DebugUnwind( NtfsAllocateMftRecord );
  501. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  502. DebugTrace( -1, Dbg, ("NtfsAllocateMftRecord: Exit\n") );
  503. }
  504. return NewMftRecord;
  505. }
  506. VOID
  507. NtfsInitializeMftRecord (
  508. IN PIRP_CONTEXT IrpContext,
  509. IN PVCB Vcb,
  510. IN OUT PMFT_SEGMENT_REFERENCE MftSegment,
  511. IN OUT PFILE_RECORD_SEGMENT_HEADER FileRecord,
  512. IN PBCB Bcb,
  513. IN BOOLEAN Directory
  514. )
  515. /*++
  516. Routine Description:
  517. This routine initializes a Mft record for use. We need to initialize the
  518. sequence number for this usage of the the record. We also initialize the
  519. update sequence array and the field which indicates the first usable
  520. attribute offset in the record.
  521. Arguments:
  522. Vcb - Vcb for volume for the Mft.
  523. MftSegment - This is a pointer to the file reference for this
  524. segment. We store the sequence number in it to make this
  525. a fully valid file reference.
  526. FileRecord - Pointer to the file record to initialize.
  527. Bcb - Bcb to use to set this page dirty via NtfsWriteLog.
  528. Directory - Boolean indicating if this file is a directory containing
  529. an index over the filename attribute.
  530. Return Value:
  531. None.
  532. --*/
  533. {
  534. LONGLONG FileRecordOffset;
  535. PUSHORT UsaSequenceNumber;
  536. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  537. PAGED_CODE();
  538. DebugTrace( +1, Dbg, ("NtfsInitializeMftRecord: Entered\n") );
  539. //
  540. // Write a log record to uninitialize the structure in case we abort.
  541. // We need to do this prior to setting the IN_USE bit.
  542. // We don't store the Lsn for this operation in the page because there
  543. // is no redo operation.
  544. //
  545. //
  546. // Capture the Segment Reference and make sure the Sequence Number is 0.
  547. //
  548. FileRecordOffset = NtfsFullSegmentNumber(MftSegment);
  549. FileRecordOffset = LlBytesFromFileRecords( Vcb, FileRecordOffset );
  550. //
  551. // We now log the new Mft record.
  552. //
  553. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  554. Vcb->MftScb,
  555. Bcb,
  556. Noop,
  557. NULL,
  558. 0,
  559. DeallocateFileRecordSegment,
  560. NULL,
  561. 0,
  562. FileRecordOffset,
  563. 0,
  564. 0,
  565. Vcb->BytesPerFileRecordSegment );
  566. RtlZeroMemory( &FileRecord->ReferenceCount,
  567. Vcb->BytesPerFileRecordSegment - FIELD_OFFSET( FILE_RECORD_SEGMENT_HEADER, ReferenceCount ));
  568. //
  569. // First we update the sequence count in the file record and our
  570. // Mft segment. We avoid using 0 as a sequence number.
  571. //
  572. if (FileRecord->SequenceNumber == 0) {
  573. FileRecord->SequenceNumber = 1;
  574. }
  575. //
  576. // Store the new sequence number in the Mft segment given us by the
  577. // caller.
  578. //
  579. MftSegment->SequenceNumber = FileRecord->SequenceNumber;
  580. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  581. //
  582. // Do a DBG-only sanity check to see if we're errorneously reusing this file reference.
  583. //
  584. NtfsVerifyFileReference( IrpContext, MftSegment );
  585. #endif
  586. //
  587. // Fill in the header for the Update sequence array.
  588. //
  589. *(PULONG)FileRecord->MultiSectorHeader.Signature = *(PULONG)FileSignature;
  590. FileRecord->MultiSectorHeader.UpdateSequenceArrayOffset = FIELD_OFFSET( FILE_RECORD_SEGMENT_HEADER, UpdateArrayForCreateOnly );
  591. FileRecord->MultiSectorHeader.UpdateSequenceArraySize = (USHORT)UpdateSequenceArraySize( Vcb->BytesPerFileRecordSegment );
  592. //
  593. // We initialize the update sequence array sequence number to one.
  594. //
  595. UsaSequenceNumber = Add2Ptr( FileRecord, FileRecord->MultiSectorHeader.UpdateSequenceArrayOffset );
  596. *UsaSequenceNumber = 1;
  597. //
  598. // The first attribute offset begins on a quad-align boundary
  599. // after the update sequence array.
  600. //
  601. FileRecord->FirstAttributeOffset = (USHORT)(FileRecord->MultiSectorHeader.UpdateSequenceArrayOffset
  602. + (FileRecord->MultiSectorHeader.UpdateSequenceArraySize
  603. * sizeof( UPDATE_SEQUENCE_NUMBER )));
  604. FileRecord->FirstAttributeOffset = (USHORT)QuadAlign( FileRecord->FirstAttributeOffset );
  605. //
  606. // This is also the first free byte in this file record.
  607. //
  608. FileRecord->FirstFreeByte = FileRecord->FirstAttributeOffset;
  609. //
  610. // We set the flags to show the segment is in use and look at
  611. // the directory parameter to indicate whether to show
  612. // the name index present.
  613. //
  614. FileRecord->Flags = (USHORT)(FILE_RECORD_SEGMENT_IN_USE |
  615. (Directory ? FILE_FILE_NAME_INDEX_PRESENT : 0));
  616. //
  617. // The size is given in the Vcb.
  618. //
  619. FileRecord->BytesAvailable = Vcb->BytesPerFileRecordSegment;
  620. //
  621. // The current FRS number.
  622. //
  623. FileRecord->SegmentNumberHighPart = MftSegment->SegmentNumberHighPart;
  624. FileRecord->SegmentNumberLowPart = MftSegment->SegmentNumberLowPart;
  625. //
  626. // Now we put an $END attribute in the File record.
  627. //
  628. AttributeHeader = (PATTRIBUTE_RECORD_HEADER) Add2Ptr( FileRecord,
  629. FileRecord->FirstFreeByte );
  630. FileRecord->FirstFreeByte += QuadAlign( sizeof(ATTRIBUTE_TYPE_CODE) );
  631. //
  632. // Fill in the fields in the attribute.
  633. //
  634. AttributeHeader->TypeCode = $END;
  635. //
  636. // Remember if this is the first time used.
  637. //
  638. AttributeHeader->RecordLength = 0x11477982;
  639. DebugTrace( -1, Dbg, ("NtfsInitializeMftRecord: Exit\n") );
  640. return;
  641. }
  642. VOID
  643. NtfsDeallocateMftRecord (
  644. IN PIRP_CONTEXT IrpContext,
  645. IN PVCB Vcb,
  646. IN ULONG FileNumber
  647. )
  648. /*++
  649. Routine Description:
  650. This routine will cause an Mft record to go into the NOT_USED state.
  651. We pin the record and modify the sequence count and IN USE bit.
  652. Arguments:
  653. Vcb - Vcb for volume.
  654. FileNumber - This is the low 32 bits for the file number.
  655. Return Value:
  656. None.
  657. --*/
  658. {
  659. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  660. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  661. LONGLONG FileOffset;
  662. MFT_SEGMENT_REFERENCE Reference;
  663. PBCB MftBcb = NULL;
  664. BOOLEAN FoundAttribute;
  665. BOOLEAN AcquiredMft = FALSE;
  666. PAGED_CODE();
  667. DebugTrace( +1, Dbg, ("NtfsDeallocateMftRecord: Entered\n") );
  668. NtfsSetSegmentNumber( &Reference, 0, FileNumber );
  669. Reference.SequenceNumber = 0;
  670. //
  671. // Lookup the bitmap allocation for the Mft file.
  672. //
  673. NtfsInitializeAttributeContext( &AttrContext );
  674. //
  675. // Use a try finally to cleanup the attribute context.
  676. //
  677. try {
  678. NtfsPinMftRecord( IrpContext,
  679. Vcb,
  680. &Reference,
  681. TRUE,
  682. &MftBcb,
  683. &FileRecord,
  684. &FileOffset );
  685. //
  686. // Log changes if the file is currently in use
  687. //
  688. if (FlagOn(FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE)) {
  689. FileRecord->Lsn = NtfsWriteLog( IrpContext,
  690. Vcb->MftScb,
  691. MftBcb,
  692. DeallocateFileRecordSegment,
  693. NULL,
  694. 0,
  695. InitializeFileRecordSegment,
  696. FileRecord,
  697. PtrOffset(FileRecord, &FileRecord->Flags) + 4,
  698. FileOffset,
  699. 0,
  700. 0,
  701. Vcb->BytesPerFileRecordSegment );
  702. //
  703. // We increment the sequence count in the file record and clear
  704. // the In-Use flag.
  705. //
  706. ClearFlag( FileRecord->Flags, FILE_RECORD_SEGMENT_IN_USE );
  707. FileRecord->SequenceNumber += 1;
  708. NtfsUnpinBcb( IrpContext, &MftBcb );
  709. }
  710. //
  711. // Synchronize the lookup by acquiring the Mft.
  712. //
  713. NtfsAcquireExclusiveScb( IrpContext, Vcb->MftScb );
  714. AcquiredMft = TRUE;
  715. //
  716. // Lookup the bitmap attribute for the Mft.
  717. //
  718. FoundAttribute = NtfsLookupAttributeByCode( IrpContext,
  719. Vcb->MftScb->Fcb,
  720. &Vcb->MftScb->Fcb->FileReference,
  721. $BITMAP,
  722. &AttrContext );
  723. //
  724. // Error if we don't find the bitmap
  725. //
  726. if (!FoundAttribute) {
  727. DebugTrace( 0, Dbg, ("Should find bitmap attribute\n") );
  728. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  729. }
  730. NtfsDeallocateRecord( IrpContext,
  731. &Vcb->MftScb->ScbType.Index.RecordAllocationContext,
  732. FileNumber,
  733. &AttrContext );
  734. //
  735. // If this file number is less than our reserved index then clear
  736. // the reserved index.
  737. //
  738. if (FlagOn( Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED ) &&
  739. (FileNumber < Vcb->MftScb->ScbType.Mft.ReservedIndex)) {
  740. ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MFT_REC_RESERVED );
  741. ClearFlag( Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED );
  742. Vcb->MftScb->ScbType.Mft.ReservedIndex = 0;
  743. }
  744. NtfsAcquireCheckpoint( IrpContext, Vcb );
  745. SetFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_ENABLED );
  746. NtfsReleaseCheckpoint( IrpContext, Vcb );
  747. Vcb->MftFreeRecords += 1;
  748. Vcb->MftScb->ScbType.Mft.FreeRecordChange += 1;
  749. if (AcquiredMft) {
  750. NtfsReleaseScb( IrpContext, Vcb->MftScb );
  751. }
  752. } finally {
  753. DebugUnwind( NtfsDeallocateMftRecord );
  754. NtfsUnpinBcb( IrpContext, &MftBcb );
  755. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  756. DebugTrace( -1, Dbg, ("NtfsDeallocateMftRecord: Exit\n") );
  757. }
  758. }
  759. BOOLEAN
  760. NtfsIsMftIndexInHole (
  761. IN PIRP_CONTEXT IrpContext,
  762. IN PVCB Vcb,
  763. IN ULONG Index,
  764. OUT PULONG HoleLength OPTIONAL
  765. )
  766. /*++
  767. Routine Description:
  768. This routine is called to check if an Mft index lies within a hole in
  769. the Mft.
  770. Arguments:
  771. Vcb - Vcb for volume.
  772. Index - This is the index to test. It is the lower 32 bits of an
  773. Mft segment.
  774. HoleLength - This is the length of the hole starting at this index.
  775. Return Value:
  776. BOOLEAN - TRUE if the index is within the Mft and there is no allocation
  777. for it.
  778. --*/
  779. {
  780. BOOLEAN InHole = FALSE;
  781. VCN Vcn;
  782. LCN Lcn;
  783. LONGLONG Clusters;
  784. PAGED_CODE();
  785. //
  786. // If the index is past the last file record then it is not considered
  787. // to be in a hole.
  788. //
  789. if (Index < (ULONG) LlFileRecordsFromBytes( Vcb, Vcb->MftScb->Header.FileSize.QuadPart )) {
  790. if (Vcb->FileRecordsPerCluster == 0) {
  791. Vcn = Index << Vcb->MftToClusterShift;
  792. } else {
  793. Vcn = Index >> Vcb->MftToClusterShift;
  794. }
  795. //
  796. // Now look this up the Mcb for the Mft. This Vcn had better be
  797. // in the Mcb or there is some problem.
  798. //
  799. if (!NtfsLookupNtfsMcbEntry( &Vcb->MftScb->Mcb,
  800. Vcn,
  801. &Lcn,
  802. &Clusters,
  803. NULL,
  804. NULL,
  805. NULL,
  806. NULL )) {
  807. ASSERT( FALSE );
  808. NtfsRaiseStatus( IrpContext,
  809. STATUS_FILE_CORRUPT_ERROR,
  810. NULL,
  811. Vcb->MftScb->Fcb );
  812. }
  813. if (Lcn == UNUSED_LCN) {
  814. InHole = TRUE;
  815. //
  816. // We know the number of clusters beginning from
  817. // this point in the Mcb. Convert to file records
  818. // and return to the user.
  819. //
  820. if (ARGUMENT_PRESENT( HoleLength )) {
  821. if (Vcb->FileRecordsPerCluster == 0) {
  822. *HoleLength = ((ULONG)Clusters) >> Vcb->MftToClusterShift;
  823. } else {
  824. *HoleLength = ((ULONG)Clusters) << Vcb->MftToClusterShift;
  825. }
  826. }
  827. }
  828. }
  829. return InHole;
  830. }
  831. VOID
  832. NtfsFillMftHole (
  833. IN PIRP_CONTEXT IrpContext,
  834. IN PVCB Vcb,
  835. IN ULONG Index
  836. )
  837. /*++
  838. Routine Description:
  839. This routine is called to fill in a hole within the Mft. We will find
  840. the beginning of the hole and then allocate the clusters to fill the
  841. hole. We will try to fill a hole with the HoleGranularity in the Vcb.
  842. If the hole containing this index is not that large we will truncate
  843. the size being added. We always guarantee to allocate the clusters on
  844. file record boundaries.
  845. Arguments:
  846. Vcb - Vcb for volume.
  847. Index - This is the index to test. It is the lower 32 bits of an
  848. Mft segment.
  849. Return Value:
  850. None.
  851. --*/
  852. {
  853. ULONG FileRecords;
  854. ULONG BaseIndex;
  855. VCN IndexVcn;
  856. VCN HoleStartVcn;
  857. VCN StartingVcn;
  858. LCN Lcn = UNUSED_LCN;
  859. LONGLONG ClusterCount;
  860. LONGLONG RunClusterCount;
  861. PAGED_CODE();
  862. //
  863. // Convert the Index to a Vcn in the file. Find the cluster that would
  864. // be the start of this hole if the hole is fully deallocated.
  865. //
  866. if (Vcb->FileRecordsPerCluster == 0) {
  867. IndexVcn = Index << Vcb->MftToClusterShift;
  868. HoleStartVcn = (Index & Vcb->MftHoleInverseMask) << Vcb->MftToClusterShift;
  869. } else {
  870. IndexVcn = Index >> Vcb->MftToClusterShift;
  871. HoleStartVcn = (Index & Vcb->MftHoleInverseMask) >> Vcb->MftToClusterShift;
  872. }
  873. //
  874. // Lookup the run containing this index.
  875. //
  876. NtfsLookupNtfsMcbEntry( &Vcb->MftScb->Mcb,
  877. IndexVcn,
  878. &Lcn,
  879. &ClusterCount,
  880. NULL,
  881. &RunClusterCount,
  882. NULL,
  883. NULL );
  884. //
  885. // This had better be a hole.
  886. //
  887. if (Lcn != UNUSED_LCN) {
  888. NtfsAcquireCheckpoint( IrpContext, Vcb );
  889. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
  890. NtfsReleaseCheckpoint( IrpContext, Vcb );
  891. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Vcb->MftScb->Fcb );
  892. }
  893. //
  894. // Take the start of the deallocated space and round up to a hole boundary.
  895. //
  896. StartingVcn = IndexVcn - (RunClusterCount - ClusterCount);
  897. if (StartingVcn <= HoleStartVcn) {
  898. StartingVcn = HoleStartVcn;
  899. RunClusterCount -= (HoleStartVcn - StartingVcn);
  900. StartingVcn = HoleStartVcn;
  901. //
  902. // We can go to the beginning of a hole. Just use the Vcn for the file
  903. // record we want to reallocate.
  904. //
  905. } else {
  906. RunClusterCount = ClusterCount;
  907. StartingVcn = IndexVcn;
  908. }
  909. //
  910. // Trim the cluster count back to a hole if necessary.
  911. //
  912. if ((ULONG) RunClusterCount >= Vcb->MftClustersPerHole) {
  913. RunClusterCount = Vcb->MftClustersPerHole;
  914. //
  915. // We don't have enough clusters for a full hole. Make sure
  916. // we end on a file record boundary however. We must end up
  917. // with enough clusters for the file record we are reallocating.
  918. //
  919. } else if (Vcb->FileRecordsPerCluster == 0) {
  920. ((PLARGE_INTEGER) &ClusterCount)->LowPart &= (Vcb->ClustersPerFileRecordSegment - 1);
  921. if (StartingVcn + ClusterCount < IndexVcn + Vcb->ClustersPerFileRecordSegment) {
  922. NtfsAcquireCheckpoint( IrpContext, Vcb );
  923. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
  924. NtfsReleaseCheckpoint( IrpContext, Vcb );
  925. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Vcb->MftScb->Fcb );
  926. }
  927. }
  928. //
  929. // Now attempt to allocate the space.
  930. //
  931. NtfsAddAllocation( IrpContext,
  932. Vcb->MftScb->FileObject,
  933. Vcb->MftScb,
  934. StartingVcn,
  935. ClusterCount,
  936. FALSE,
  937. NULL );
  938. //
  939. // Compute the number of file records reallocated and then
  940. // initialize and deallocate each file record.
  941. //
  942. if (Vcb->FileRecordsPerCluster == 0) {
  943. FileRecords = (ULONG) ClusterCount >> Vcb->MftToClusterShift;
  944. BaseIndex = (ULONG) StartingVcn >> Vcb->MftToClusterShift;
  945. } else {
  946. FileRecords = (ULONG) ClusterCount << Vcb->MftToClusterShift;
  947. BaseIndex = (ULONG) StartingVcn << Vcb->MftToClusterShift;
  948. }
  949. NtfsInitializeMftHoleRecords( IrpContext,
  950. Vcb,
  951. BaseIndex,
  952. FileRecords );
  953. Vcb->MftHoleRecords -= FileRecords;
  954. Vcb->MftScb->ScbType.Mft.HoleRecordChange -= FileRecords;
  955. return;
  956. }
  957. VOID
  958. NtfsLogMftFileRecord (
  959. IN PIRP_CONTEXT IrpContext,
  960. IN PVCB Vcb,
  961. IN PFILE_RECORD_SEGMENT_HEADER FileRecord,
  962. IN LONGLONG MftOffset,
  963. IN PBCB Bcb,
  964. IN BOOLEAN Redo
  965. )
  966. /*++
  967. Routine Description:
  968. This routine is called to log changes to the file record for the Mft
  969. file. We log the entire record instead of individual changes so
  970. that we can recover the data even if there is a USA error. The entire
  971. data will be sitting in the Log file.
  972. Arguments:
  973. Vcb - This is the Vcb for the volume being logged.
  974. FileRecord - This is the file record being logged.
  975. MftOffset - This is the offset of this file record in the Mft stream.
  976. Bcb - This is the Bcb for the pinned file record.
  977. RedoOperation - Boolean indicating if we are logging
  978. a redo or undo operation.
  979. Return Value:
  980. None.
  981. --*/
  982. {
  983. PVOID RedoBuffer;
  984. NTFS_LOG_OPERATION RedoOperation;
  985. ULONG RedoLength;
  986. PVOID UndoBuffer;
  987. NTFS_LOG_OPERATION UndoOperation;
  988. ULONG UndoLength;
  989. PAGED_CODE();
  990. //
  991. // Find the logging values based on whether this is an
  992. // undo or redo.
  993. //
  994. if (Redo) {
  995. RedoBuffer = FileRecord;
  996. RedoOperation = InitializeFileRecordSegment;
  997. RedoLength = FileRecord->FirstFreeByte;
  998. UndoBuffer = NULL;
  999. UndoOperation = Noop;
  1000. UndoLength = 0;
  1001. } else {
  1002. UndoBuffer = FileRecord;
  1003. UndoOperation = InitializeFileRecordSegment;
  1004. UndoLength = FileRecord->FirstFreeByte;
  1005. RedoBuffer = NULL;
  1006. RedoOperation = Noop;
  1007. RedoLength = 0;
  1008. }
  1009. //
  1010. // Now that we have calculated all the values, call the logging
  1011. // routine.
  1012. //
  1013. NtfsWriteLog( IrpContext,
  1014. Vcb->MftScb,
  1015. Bcb,
  1016. RedoOperation,
  1017. RedoBuffer,
  1018. RedoLength,
  1019. UndoOperation,
  1020. UndoBuffer,
  1021. UndoLength,
  1022. MftOffset,
  1023. 0,
  1024. 0,
  1025. Vcb->BytesPerFileRecordSegment );
  1026. return;
  1027. }
  1028. BOOLEAN
  1029. NtfsDefragMft (
  1030. IN PDEFRAG_MFT DefragMft
  1031. )
  1032. /*++
  1033. Routine Description:
  1034. This routine is called whenever we have detected that the Mft is in a state
  1035. where defragging is desired.
  1036. Arguments:
  1037. DefragMft - This is the defrag structure.
  1038. Return Value:
  1039. BOOLEAN - TRUE if we took some defrag step, FALSE otherwise.
  1040. --*/
  1041. {
  1042. TOP_LEVEL_CONTEXT TopLevelContext;
  1043. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  1044. PVCB Vcb;
  1045. PIRP_CONTEXT IrpContext = NULL;
  1046. BOOLEAN DefragStepTaken = FALSE;
  1047. DebugTrace( +1, Dbg, ("NtfsDefragMft: Entered\n") );
  1048. FsRtlEnterFileSystem();
  1049. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, FALSE );
  1050. ASSERT( ThreadTopLevelContext == &TopLevelContext );
  1051. Vcb = DefragMft->Vcb;
  1052. //
  1053. // Use a try-except to catch errors here.
  1054. //
  1055. try {
  1056. //
  1057. // Deallocate the defrag structure we were called with.
  1058. //
  1059. if (DefragMft->DeallocateWorkItem) {
  1060. NtfsFreePool( DefragMft );
  1061. }
  1062. //
  1063. // Create the Irp context. We will use all of the transaction support
  1064. // contained in a normal IrpContext.
  1065. //
  1066. NtfsInitializeIrpContext( NULL, TRUE, &IrpContext );
  1067. IrpContext->Vcb = Vcb;
  1068. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  1069. NtfsAcquireCheckpoint( IrpContext, Vcb );
  1070. if (FlagOn( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED )
  1071. && FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
  1072. NtfsReleaseCheckpoint( IrpContext, Vcb );
  1073. DefragStepTaken = NtfsDefragMftPriv( IrpContext,
  1074. Vcb );
  1075. } else {
  1076. NtfsReleaseCheckpoint( IrpContext, Vcb );
  1077. }
  1078. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  1079. } except( NtfsExceptionFilter( IrpContext, GetExceptionInformation())) {
  1080. NtfsProcessException( IrpContext, NULL, GetExceptionCode() );
  1081. //
  1082. // If the exception code was not LOG_FILE_FULL then
  1083. // disable defragging.
  1084. //
  1085. if (GetExceptionCode() != STATUS_LOG_FILE_FULL) {
  1086. NtfsAcquireCheckpoint( IrpContext, Vcb );
  1087. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_ENABLED );
  1088. NtfsReleaseCheckpoint( IrpContext, Vcb );
  1089. }
  1090. DefragStepTaken = FALSE;
  1091. }
  1092. NtfsAcquireCheckpoint( IrpContext, Vcb );
  1093. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_ACTIVE );
  1094. NtfsReleaseCheckpoint( IrpContext, Vcb );
  1095. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  1096. FsRtlExitFileSystem();
  1097. DebugTrace( -1, Dbg, ("NtfsDefragMft: Exit\n") );
  1098. return DefragStepTaken;
  1099. }
  1100. VOID
  1101. NtfsCheckForDefrag (
  1102. IN OUT PVCB Vcb
  1103. )
  1104. /*++
  1105. Routine Description:
  1106. This routine is called to check whether there is any defrag work to do
  1107. involving freeing file records and creating holes in the Mft. It
  1108. will modify the TRIGGERED flag in the Vcb if there is still work to
  1109. do.
  1110. Arguments:
  1111. Vcb - This is the Vcb for the volume to defrag.
  1112. Return Value:
  1113. None.
  1114. --*/
  1115. {
  1116. LONGLONG RecordsToClusters;
  1117. LONGLONG AdjClusters;
  1118. PAGED_CODE();
  1119. //
  1120. // Convert the available Mft records to clusters.
  1121. //
  1122. if (Vcb->FileRecordsPerCluster) {
  1123. RecordsToClusters = Int64ShllMod32(((LONGLONG)(Vcb->MftFreeRecords - Vcb->MftHoleRecords)),
  1124. Vcb->MftToClusterShift);
  1125. } else {
  1126. RecordsToClusters = Int64ShraMod32(((LONGLONG)(Vcb->MftFreeRecords - Vcb->MftHoleRecords)),
  1127. Vcb->MftToClusterShift);
  1128. }
  1129. //
  1130. // If we have already triggered the defrag then check if we are below
  1131. // the lower threshold.
  1132. //
  1133. if (FlagOn( Vcb->MftDefragState, VCB_MFT_DEFRAG_TRIGGERED )) {
  1134. AdjClusters = Vcb->FreeClusters >> MFT_DEFRAG_LOWER_THRESHOLD;
  1135. if (AdjClusters >= RecordsToClusters) {
  1136. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_TRIGGERED );
  1137. }
  1138. //
  1139. // Otherwise check if we have exceeded the upper threshold.
  1140. //
  1141. } else {
  1142. AdjClusters = Vcb->FreeClusters >> MFT_DEFRAG_UPPER_THRESHOLD;
  1143. if (AdjClusters < RecordsToClusters) {
  1144. SetFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_TRIGGERED );
  1145. }
  1146. }
  1147. return;
  1148. }
  1149. VOID
  1150. NtfsInitializeMftHoleRecords (
  1151. IN PIRP_CONTEXT IrpContext,
  1152. IN PVCB Vcb,
  1153. IN ULONG FirstIndex,
  1154. IN ULONG RecordCount
  1155. )
  1156. /*++
  1157. Routine Description:
  1158. This routine is called to initialize the file records created when filling
  1159. a hole in the Mft.
  1160. Arguments:
  1161. Vcb - Vcb for volume.
  1162. FirstIndex - Index for the start of the hole to fill.
  1163. RecordCount - Count of file records in the hole.
  1164. Return Value:
  1165. None.
  1166. --*/
  1167. {
  1168. PBCB Bcb = NULL;
  1169. PAGED_CODE();
  1170. //
  1171. // Use a try-finally to facilitate cleanup.
  1172. //
  1173. try {
  1174. //
  1175. // Loop to initialize each file record.
  1176. //
  1177. while (RecordCount--) {
  1178. PUSHORT UsaSequenceNumber;
  1179. PMULTI_SECTOR_HEADER UsaHeader;
  1180. MFT_SEGMENT_REFERENCE ThisMftSegment;
  1181. PFILE_RECORD_SEGMENT_HEADER FileRecord;
  1182. PATTRIBUTE_RECORD_HEADER AttributeHeader;
  1183. //
  1184. // Convert the index to a segment reference.
  1185. //
  1186. *((PLONGLONG)&ThisMftSegment) = FirstIndex;
  1187. //
  1188. // Pin the file record to initialize.
  1189. //
  1190. NtfsPinMftRecord( IrpContext,
  1191. Vcb,
  1192. &ThisMftSegment,
  1193. TRUE,
  1194. &Bcb,
  1195. &FileRecord,
  1196. NULL );
  1197. //
  1198. // Initialize the file record including clearing the in-use
  1199. // bit.
  1200. //
  1201. RtlZeroMemory( FileRecord, Vcb->BytesPerFileRecordSegment );
  1202. //
  1203. // Fill in the header for the Update sequence array.
  1204. //
  1205. UsaHeader = (PMULTI_SECTOR_HEADER) FileRecord;
  1206. *(PULONG)UsaHeader->Signature = *(PULONG)FileSignature;
  1207. UsaHeader->UpdateSequenceArrayOffset = FIELD_OFFSET( FILE_RECORD_SEGMENT_HEADER,
  1208. UpdateArrayForCreateOnly );
  1209. UsaHeader->UpdateSequenceArraySize = (USHORT)UpdateSequenceArraySize( Vcb->BytesPerFileRecordSegment );
  1210. //
  1211. // We initialize the update sequence array sequence number to one.
  1212. //
  1213. UsaSequenceNumber = Add2Ptr( FileRecord, UsaHeader->UpdateSequenceArrayOffset );
  1214. *UsaSequenceNumber = 1;
  1215. //
  1216. // The first attribute offset begins on a quad-align boundary
  1217. // after the update sequence array.
  1218. //
  1219. FileRecord->FirstAttributeOffset = (USHORT)(UsaHeader->UpdateSequenceArrayOffset
  1220. + (UsaHeader->UpdateSequenceArraySize
  1221. * sizeof( UPDATE_SEQUENCE_NUMBER )));
  1222. FileRecord->FirstAttributeOffset = (USHORT)QuadAlign( FileRecord->FirstAttributeOffset );
  1223. //
  1224. // The size is given in the Vcb.
  1225. //
  1226. FileRecord->BytesAvailable = Vcb->BytesPerFileRecordSegment;
  1227. //
  1228. // Now we put an $END attribute in the File record.
  1229. //
  1230. AttributeHeader = (PATTRIBUTE_RECORD_HEADER) Add2Ptr( FileRecord,
  1231. FileRecord->FirstAttributeOffset );
  1232. //
  1233. // The first free byte is after this location.
  1234. //
  1235. FileRecord->FirstFreeByte = QuadAlign( FileRecord->FirstAttributeOffset
  1236. + sizeof( ATTRIBUTE_TYPE_CODE ));
  1237. //
  1238. // Fill in the fields in the attribute.
  1239. //
  1240. AttributeHeader->TypeCode = $END;
  1241. //
  1242. // The current FRS number.
  1243. //
  1244. FileRecord->SegmentNumberHighPart = ThisMftSegment.SegmentNumberHighPart;
  1245. FileRecord->SegmentNumberLowPart = ThisMftSegment.SegmentNumberLowPart;
  1246. //
  1247. // Log the entire file record.
  1248. //
  1249. NtfsLogMftFileRecord( IrpContext,
  1250. Vcb,
  1251. FileRecord,
  1252. LlBytesFromFileRecords( Vcb, FirstIndex ),
  1253. Bcb,
  1254. TRUE );
  1255. NtfsUnpinBcb( IrpContext, &Bcb );
  1256. //
  1257. // Move to the next record.
  1258. //
  1259. FirstIndex += 1;
  1260. }
  1261. } finally {
  1262. DebugUnwind( NtfsInitializeMftHoleRecords );
  1263. NtfsUnpinBcb( IrpContext, &Bcb );
  1264. }
  1265. return;
  1266. }
  1267. //
  1268. // Local support routine
  1269. //
  1270. BOOLEAN
  1271. NtfsTruncateMft (
  1272. IN PIRP_CONTEXT IrpContext,
  1273. IN PVCB Vcb
  1274. )
  1275. /*++
  1276. Routine Description:
  1277. This routine is called to perform the work of truncating the Mft. If will
  1278. truncate the Mft and adjust the sizes of the Mft and Mft bitmap.
  1279. Arguments:
  1280. Vcb - This is the Vcb for the volume to defrag.
  1281. Return Value:
  1282. BOOLEAN - TRUE if we could deallocate any disk space, FALSE otherwise.
  1283. --*/
  1284. {
  1285. PVOID RangePtr;
  1286. ULONG Index;
  1287. VCN StartingVcn;
  1288. VCN NextVcn;
  1289. LCN NextLcn;
  1290. LONGLONG ClusterCount;
  1291. LONGLONG FileOffset;
  1292. ULONG FreeRecordChange;
  1293. IO_STATUS_BLOCK IoStatus;
  1294. PAGED_CODE();
  1295. //
  1296. // Try to find a range of file records at the end of the file which can
  1297. // be deallocated.
  1298. //
  1299. if (!NtfsFindMftFreeTail( IrpContext, Vcb, &FileOffset )) {
  1300. return FALSE;
  1301. }
  1302. FreeRecordChange = (ULONG) LlFileRecordsFromBytes( Vcb, Vcb->MftScb->Header.FileSize.QuadPart - FileOffset );
  1303. Vcb->MftFreeRecords -= FreeRecordChange;
  1304. Vcb->MftScb->ScbType.Mft.FreeRecordChange -= FreeRecordChange;
  1305. //
  1306. // Now we want to figure out how many holes we may be removing from the Mft.
  1307. // Walk through the Mcb and count the holes.
  1308. //
  1309. StartingVcn = LlClustersFromBytes( Vcb, FileOffset );
  1310. NtfsLookupNtfsMcbEntry( &Vcb->MftScb->Mcb,
  1311. StartingVcn,
  1312. &NextLcn,
  1313. &ClusterCount,
  1314. NULL,
  1315. NULL,
  1316. &RangePtr,
  1317. &Index );
  1318. do {
  1319. //
  1320. // If this is a hole then update the hole count in the Vcb and
  1321. // hole change count in the MftScb.
  1322. //
  1323. if (NextLcn == UNUSED_LCN) {
  1324. ULONG HoleChange;
  1325. if (Vcb->FileRecordsPerCluster == 0) {
  1326. HoleChange = ((ULONG)ClusterCount) >> Vcb->MftToClusterShift;
  1327. } else {
  1328. HoleChange = ((ULONG)ClusterCount) << Vcb->MftToClusterShift;
  1329. }
  1330. Vcb->MftHoleRecords -= HoleChange;
  1331. Vcb->MftScb->ScbType.Mft.HoleRecordChange -= HoleChange;
  1332. }
  1333. Index += 1;
  1334. } while (NtfsGetSequentialMcbEntry( &Vcb->MftScb->Mcb,
  1335. &RangePtr,
  1336. Index,
  1337. &NextVcn,
  1338. &NextLcn,
  1339. &ClusterCount ));
  1340. //
  1341. // We want to flush the data in the Mft out to disk in
  1342. // case a lazywrite comes in during a window where we have
  1343. // removed the allocation but before a possible abort.
  1344. //
  1345. CcFlushCache( &Vcb->MftScb->NonpagedScb->SegmentObject,
  1346. (PLARGE_INTEGER)&FileOffset,
  1347. BytesFromFileRecords( Vcb, FreeRecordChange ),
  1348. &IoStatus );
  1349. ASSERT( IoStatus.Status == STATUS_SUCCESS );
  1350. //
  1351. // Now do the truncation.
  1352. //
  1353. NtfsDeleteAllocation( IrpContext,
  1354. Vcb->MftScb->FileObject,
  1355. Vcb->MftScb,
  1356. StartingVcn,
  1357. MAXLONGLONG,
  1358. TRUE,
  1359. FALSE );
  1360. return TRUE;
  1361. }
  1362. NTSTATUS
  1363. NtfsIterateMft (
  1364. IN PIRP_CONTEXT IrpContext,
  1365. IN PVCB Vcb,
  1366. IN OUT PFILE_REFERENCE FileReference,
  1367. IN FILE_RECORD_WALK FileRecordFunction,
  1368. IN PVOID Context
  1369. )
  1370. /*++
  1371. Routine Description:
  1372. This routine interates over the MFT. It calls the FileRecordFunction
  1373. with an Fcb for each existing file on the volume. The Fcb is owned
  1374. exclusive and Vcb is owned shared. The starting FileReference number
  1375. is passed in so that iterate can be restarted where is left off.
  1376. Arguments:
  1377. Vcb - Pointer to the volume to control for the MFT
  1378. FileReference - Suplies a pointer to the starting file reference number
  1379. This value is updated as the interator progresses.
  1380. FileRecordFunction - Suplies a pointer to function to be called with
  1381. each file found in the MFT.
  1382. Context - Passed along to the FileRecordFunction.
  1383. Return Value:
  1384. Returns back status of the entire operation.
  1385. --*/
  1386. {
  1387. ULONG LogFileFullCount = 0;
  1388. NTSTATUS Status = STATUS_SUCCESS;
  1389. PFCB CurrentFcb = NULL;
  1390. BOOLEAN DecrementReferenceCount = FALSE;
  1391. KEVENT Event;
  1392. LARGE_INTEGER Timeout;
  1393. PAGED_CODE();
  1394. KeInitializeEvent( &Event, SynchronizationEvent, FALSE );
  1395. Timeout.QuadPart = 0;
  1396. while (TRUE) {
  1397. FsRtlExitFileSystem();
  1398. //
  1399. // Check for APC delivery indicating thread death or cancel
  1400. //
  1401. Status = KeWaitForSingleObject( &Event,
  1402. Executive,
  1403. UserMode,
  1404. FALSE,
  1405. &Timeout );
  1406. FsRtlEnterFileSystem();
  1407. if (STATUS_TIMEOUT == Status) {
  1408. Status = STATUS_SUCCESS;
  1409. } else {
  1410. break;
  1411. }
  1412. //
  1413. // If irp has been cancelled break out
  1414. //
  1415. if (IrpContext->OriginatingIrp && IrpContext->OriginatingIrp->Cancel) {
  1416. #ifdef BENL_DBG
  1417. KdPrint(( "Ntfs: cancelled mft iteration irp: 0x%x\n", IrpContext->OriginatingIrp ));
  1418. #endif
  1419. Status = STATUS_CANCELLED;
  1420. break;
  1421. }
  1422. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  1423. try {
  1424. //
  1425. // Acquire the VCB shared and check whether we should
  1426. // continue.
  1427. //
  1428. if (!NtfsIsVcbAvailable( Vcb )) {
  1429. //
  1430. // The volume is going away, bail out.
  1431. //
  1432. Status = STATUS_VOLUME_DISMOUNTED;
  1433. leave;
  1434. }
  1435. //
  1436. // Set the irp context flags to indicate that we are in the
  1437. // fsp and that the irp context should not be deleted when
  1438. // complete request or process exception are called. The in
  1439. // fsp flag keeps us from raising in a few places. These
  1440. // flags must be set inside the loop since they are cleared
  1441. // under certain conditions.
  1442. //
  1443. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP);
  1444. DecrementReferenceCount = TRUE;
  1445. Status = NtfsTryOpenFcb( IrpContext,
  1446. Vcb,
  1447. &CurrentFcb,
  1448. *FileReference );
  1449. if (!NT_SUCCESS( Status )) {
  1450. leave;
  1451. }
  1452. //
  1453. // Call the worker function.
  1454. //
  1455. Status = FileRecordFunction( IrpContext, CurrentFcb, Context );
  1456. if (!NT_SUCCESS( Status )) {
  1457. leave;
  1458. }
  1459. //
  1460. // Complete the request which commits the pending
  1461. // transaction if there is one and releases of the
  1462. // acquired resources. The IrpContext will not
  1463. // be deleted because the no delete flag is set.
  1464. //
  1465. NtfsCheckpointCurrentTransaction( IrpContext );
  1466. NtfsAcquireFcbTable( IrpContext, Vcb );
  1467. ASSERT(CurrentFcb->ReferenceCount > 0);
  1468. CurrentFcb->ReferenceCount--;
  1469. NtfsReleaseFcbTable( IrpContext, Vcb );
  1470. DecrementReferenceCount = FALSE;
  1471. NtfsTeardownStructures( IrpContext,
  1472. CurrentFcb,
  1473. NULL,
  1474. FALSE,
  1475. 0,
  1476. NULL );
  1477. } finally {
  1478. if (CurrentFcb != NULL) {
  1479. if (DecrementReferenceCount) {
  1480. NtfsAcquireFcbTable( IrpContext, Vcb );
  1481. ASSERT(CurrentFcb->ReferenceCount > 0);
  1482. CurrentFcb->ReferenceCount--;
  1483. NtfsReleaseFcbTable( IrpContext, Vcb );
  1484. DecrementReferenceCount = FALSE;
  1485. }
  1486. CurrentFcb = NULL;
  1487. }
  1488. //
  1489. // Make sure to release any maps in the cached file records in
  1490. // the Irp Context.
  1491. //
  1492. NtfsPurgeFileRecordCache( IrpContext );
  1493. NtfsReleaseVcb( IrpContext, Vcb );
  1494. }
  1495. //
  1496. // If a status of not found was return then just continue to
  1497. // the next file record.
  1498. //
  1499. if (Status == STATUS_NOT_FOUND) {
  1500. Status = STATUS_SUCCESS;
  1501. }
  1502. if (!NT_SUCCESS( Status )) {
  1503. break;
  1504. }
  1505. //
  1506. // Release resources
  1507. //
  1508. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DONT_DELETE | IRP_CONTEXT_FLAG_RETAIN_FLAGS );
  1509. NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
  1510. //
  1511. // Advance to the next file record.
  1512. //
  1513. (*((LONGLONG UNALIGNED *) FileReference))++;
  1514. }
  1515. return Status;
  1516. }
  1517. //
  1518. // Local support routine.
  1519. //
  1520. BOOLEAN
  1521. NtfsDefragMftPriv (
  1522. IN PIRP_CONTEXT IrpContext,
  1523. IN PVCB Vcb
  1524. )
  1525. /*++
  1526. Routine Description:
  1527. This is the main worker routine which performs the Mft defragging. This routine
  1528. will defrag according to the following priorities. First try to deallocate the
  1529. tail of the file. Second rewrite the mapping for the file if necessary. Finally
  1530. try to find a range of the Mft that we can turn into a hole. We will only do
  1531. the first and third if we are trying to reclaim disk space. The second we will
  1532. do to try and keep us from getting into trouble while modify Mft records which
  1533. describe the Mft itself.
  1534. Arguments:
  1535. Vcb - This is the Vcb for the volume being defragged.
  1536. Return Value:
  1537. BOOLEAN - TRUE if a defrag operation was successfully done, FALSE otherwise.
  1538. --*/
  1539. {
  1540. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  1541. BOOLEAN CleanupAttributeContext = FALSE;
  1542. BOOLEAN DefragStepTaken = FALSE;
  1543. PAGED_CODE();
  1544. //
  1545. // We will acquire the Scb for the Mft for this operation.
  1546. //
  1547. NtfsAcquireExclusiveScb( IrpContext, Vcb->MftScb );
  1548. //
  1549. // Use a try-finally to facilitate cleanup.
  1550. //
  1551. try {
  1552. //
  1553. // If we don't have a reserved record then reserve one now.
  1554. //
  1555. if (!FlagOn( Vcb->MftReserveFlags, VCB_MFT_RECORD_RESERVED )) {
  1556. NtfsInitializeAttributeContext( &AttrContext );
  1557. CleanupAttributeContext = TRUE;
  1558. //
  1559. // Lookup the bitmap. There is an error if we can't find
  1560. // it.
  1561. //
  1562. if (!NtfsLookupAttributeByCode( IrpContext,
  1563. Vcb->MftScb->Fcb,
  1564. &Vcb->MftScb->Fcb->FileReference,
  1565. $BITMAP,
  1566. &AttrContext )) {
  1567. NtfsRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR, NULL, NULL );
  1568. }
  1569. (VOID)NtfsReserveMftRecord( IrpContext,
  1570. Vcb,
  1571. &AttrContext );
  1572. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1573. CleanupAttributeContext = FALSE;
  1574. }
  1575. //
  1576. // We now want to test for the three defrag operation we
  1577. // do. Start by checking if we are still trying to
  1578. // recover Mft space for the disk. This is true if
  1579. // have begun defragging and are above the lower threshold
  1580. // or have not begun defragging and are above the upper
  1581. // threshold.
  1582. //
  1583. NtfsAcquireCheckpoint( IrpContext, Vcb );
  1584. NtfsCheckForDefrag( Vcb );
  1585. NtfsReleaseCheckpoint( IrpContext, Vcb );
  1586. //
  1587. // If we are actively defragging and can deallocate space
  1588. // from the tail of the file then do that. We won't synchronize
  1589. // testing the flag for the defrag state below since making
  1590. // the calls is benign in any case.
  1591. //
  1592. if (FlagOn( Vcb->MftDefragState, VCB_MFT_DEFRAG_TRIGGERED )) {
  1593. if (NtfsTruncateMft( IrpContext, Vcb )) {
  1594. try_return( DefragStepTaken = TRUE );
  1595. }
  1596. }
  1597. //
  1598. // Else if we need to rewrite the mapping for the file do
  1599. // so now.
  1600. //
  1601. if (FlagOn( Vcb->MftDefragState, VCB_MFT_DEFRAG_EXCESS_MAP )) {
  1602. if (NtfsRewriteMftMapping( IrpContext,
  1603. Vcb )) {
  1604. try_return( DefragStepTaken = TRUE );
  1605. }
  1606. }
  1607. //
  1608. // The last choice is to try to find a candidate for a hole in
  1609. // the file. We will walk backwards from the end of the file.
  1610. //
  1611. if (NtfsPerforateMft &&
  1612. FlagOn( Vcb->MftDefragState, VCB_MFT_DEFRAG_TRIGGERED )) {
  1613. if (NtfsCreateMftHole( IrpContext, Vcb )) {
  1614. try_return( DefragStepTaken = TRUE );
  1615. }
  1616. }
  1617. //
  1618. // We couldn't do any work to defrag. This means that we can't
  1619. // even try to defrag unless a file record is freed at some
  1620. // point.
  1621. //
  1622. NtfsAcquireCheckpoint( IrpContext, Vcb );
  1623. ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_ENABLED );
  1624. NtfsReleaseCheckpoint( IrpContext, Vcb );
  1625. try_exit: NOTHING;
  1626. } finally {
  1627. DebugUnwind( NtfsDefragMftPriv );
  1628. if (CleanupAttributeContext) {
  1629. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1630. }
  1631. NtfsReleaseScb( IrpContext, Vcb->MftScb );
  1632. }
  1633. return DefragStepTaken;
  1634. }
  1635. //
  1636. // Local support routine
  1637. //
  1638. LONG
  1639. NtfsReadMftExceptionFilter (
  1640. IN PIRP_CONTEXT IrpContext,
  1641. IN PEXCEPTION_POINTERS ExceptionPointer,
  1642. IN PBCB Bcb,
  1643. IN LONGLONG FileOffset
  1644. )
  1645. {
  1646. //
  1647. // Check if we support this error,
  1648. // if we didn't fail to totally page in the first time since we need the original
  1649. // to copy the mirror one into, or if the offset isn't within the mirror range
  1650. //
  1651. if (!FsRtlIsNtstatusExpected( ExceptionPointer->ExceptionRecord->ExceptionCode ) ||
  1652. (Bcb == NULL) ||
  1653. (FileOffset >= IrpContext->Vcb->Mft2Scb->Header.FileSize.QuadPart)) {
  1654. return EXCEPTION_CONTINUE_SEARCH;
  1655. }
  1656. //
  1657. // Clear the status field in the IrpContext. We're going to retry in the mirror
  1658. //
  1659. IrpContext->ExceptionStatus = STATUS_SUCCESS;
  1660. return EXCEPTION_EXECUTE_HANDLER;
  1661. }
  1662. #if (DBG || defined( NTFS_FREE_ASSERTS ))
  1663. //
  1664. // Look for a prior entry in the Fcb table for the same value.
  1665. //
  1666. VOID
  1667. NtfsVerifyFileReference (
  1668. IN PIRP_CONTEXT IrpContext,
  1669. IN PMFT_SEGMENT_REFERENCE MftSegment
  1670. )
  1671. {
  1672. MFT_SEGMENT_REFERENCE TestReference;
  1673. ULONG Index = 5;
  1674. FCB_TABLE_ELEMENT Key;
  1675. PFCB_TABLE_ELEMENT Entry;
  1676. TestReference = *MftSegment;
  1677. TestReference.SequenceNumber -= 1;
  1678. NtfsAcquireFcbTable( NULL, IrpContext->Vcb );
  1679. while((TestReference.SequenceNumber != 0) && (Index != 0)) {
  1680. Key.FileReference = TestReference;
  1681. if ((Entry = RtlLookupElementGenericTable( &IrpContext->Vcb->FcbTable, &Key )) != NULL) {
  1682. //
  1683. // Let's be optimistic and do an unsafe check. If we can't get the resource,
  1684. // we'll just assume that it's in the process of getting deleted.
  1685. //
  1686. if (!FlagOn( Entry->Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
  1687. if (NtfsAcquireResourceExclusive( IrpContext, Entry->Fcb, FALSE )) {
  1688. //
  1689. // Either the Fcb should be marked as deleted or there should be no
  1690. // Scbs lying around to flush.
  1691. //
  1692. if (!FlagOn( Entry->Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
  1693. PLIST_ENTRY Links;
  1694. PSCB NextScb;
  1695. Links = Entry->Fcb->ScbQueue.Flink;
  1696. //
  1697. // We don't care if there are Scb's as long as none of them
  1698. // represent real data.
  1699. //
  1700. while (Links != &Entry->Fcb->ScbQueue) {
  1701. NextScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
  1702. if (NextScb->AttributeTypeCode != $UNUSED) {
  1703. break;
  1704. }
  1705. Links = Links->Flink;
  1706. }
  1707. //
  1708. // Leave the test for deleted in the assert message so the debugger output
  1709. // is more descriptive.
  1710. //
  1711. ASSERT( FlagOn( Entry->Fcb->FcbState, FCB_STATE_FILE_DELETED ) ||
  1712. (Links == &Entry->Fcb->ScbQueue) );
  1713. }
  1714. NtfsReleaseResource( IrpContext, Entry->Fcb );
  1715. }
  1716. }
  1717. }
  1718. Index -= 1;
  1719. TestReference.SequenceNumber -= 1;
  1720. }
  1721. NtfsReleaseFcbTable( IrpContext, IrpContext->Vcb );
  1722. return;
  1723. }
  1724. #endif