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

3049 lines
113 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. Cleanup.c
  5. Abstract:
  6. This module implements the File Cleanup routine for Ntfs called by the
  7. dispatch driver.
  8. Author:
  9. Your Name [Email] dd-Mon-Year
  10. Revision History:
  11. --*/
  12. #include "NtfsProc.h"
  13. //
  14. // The Bug check file id for this module
  15. //
  16. #define BugCheckFileId (NTFS_BUG_CHECK_CLEANUP)
  17. //
  18. // The local debug trace level
  19. //
  20. #define Dbg (DEBUG_TRACE_CLEANUP)
  21. #ifdef BRIANDBG
  22. ULONG NtfsCleanupDiskFull = 0;
  23. ULONG NtfsCleanupNoPool = 0;
  24. #endif
  25. #ifdef BRIANDBG
  26. LONG
  27. NtfsFsdCleanupExceptionFilter (
  28. IN PIRP_CONTEXT IrpContext OPTIONAL,
  29. IN PEXCEPTION_POINTERS ExceptionPointer
  30. );
  31. #endif
  32. #ifdef ALLOC_PRAGMA
  33. #pragma alloc_text(PAGE, NtfsCommonCleanup)
  34. #pragma alloc_text(PAGE, NtfsFsdCleanup)
  35. #pragma alloc_text(PAGE, NtfsTrimNormalizedNames)
  36. #endif
  37. NTSTATUS
  38. NtfsFsdCleanup (
  39. IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
  40. IN PIRP Irp
  41. )
  42. /*++
  43. Routine Description:
  44. This routine implements the FSD part of Cleanup.
  45. Arguments:
  46. VolumeDeviceObject - Supplies the volume device object where the
  47. file exists
  48. Irp - Supplies the Irp being processed
  49. Return Value:
  50. NTSTATUS - The FSD status for the IRP
  51. --*/
  52. {
  53. TOP_LEVEL_CONTEXT TopLevelContext;
  54. PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
  55. NTSTATUS Status = STATUS_SUCCESS;
  56. PIRP_CONTEXT IrpContext = NULL;
  57. IRP_CONTEXT LocalIrpContext;
  58. ULONG LogFileFullCount = 0;
  59. ASSERT_IRP( Irp );
  60. PAGED_CODE();
  61. //
  62. // If we were called with our file system device object instead of a
  63. // volume device object, just complete this request with STATUS_SUCCESS
  64. //
  65. if (VolumeDeviceObject->DeviceObject.Size == (USHORT)sizeof(DEVICE_OBJECT)) {
  66. Irp->IoStatus.Status = STATUS_SUCCESS;
  67. Irp->IoStatus.Information = FILE_OPENED;
  68. IoCompleteRequest( Irp, IO_DISK_INCREMENT );
  69. return STATUS_SUCCESS;
  70. }
  71. DebugTrace( +1, Dbg, ("NtfsFsdCleanup\n") );
  72. //
  73. // Call the common Cleanup routine
  74. //
  75. FsRtlEnterFileSystem();
  76. ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
  77. //
  78. // Do the following in a loop to catch the log file full and cant wait
  79. // calls.
  80. //
  81. do {
  82. try {
  83. //
  84. // We are either initiating this request or retrying it.
  85. //
  86. if (IrpContext == NULL) {
  87. //
  88. // Allocate and initialize the Irp. Always use the local IrpContext
  89. // for cleanup. It is never posted.
  90. //
  91. IrpContext = &LocalIrpContext;
  92. NtfsInitializeIrpContext( Irp, TRUE, &IrpContext );
  93. //
  94. // Initialize the thread top level structure, if needed.
  95. //
  96. NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
  97. } else if (Status == STATUS_LOG_FILE_FULL) {
  98. NtfsCheckpointForLogFileFull( IrpContext );
  99. if (++LogFileFullCount >= 2) {
  100. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_EXCESS_LOG_FULL );
  101. }
  102. }
  103. Status = NtfsCommonCleanup( IrpContext, Irp );
  104. break;
  105. #ifdef BRIANDBG
  106. } except(NtfsFsdCleanupExceptionFilter( IrpContext, GetExceptionInformation() )) {
  107. #else
  108. } except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
  109. #endif
  110. //
  111. // We had some trouble trying to perform the requested
  112. // operation, so we'll abort the I/O request with
  113. // the error status that we get back from the
  114. // exception code
  115. //
  116. Status = NtfsProcessException( IrpContext, Irp, GetExceptionCode() );
  117. }
  118. } while (Status == STATUS_CANT_WAIT ||
  119. Status == STATUS_LOG_FILE_FULL);
  120. ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
  121. FsRtlExitFileSystem();
  122. //
  123. // And return to our caller
  124. //
  125. DebugTrace( -1, Dbg, ("NtfsFsdCleanup -> %08lx\n", Status) );
  126. return Status;
  127. }
  128. NTSTATUS
  129. NtfsCommonCleanup (
  130. IN PIRP_CONTEXT IrpContext,
  131. IN PIRP Irp
  132. )
  133. /*++
  134. Routine Description:
  135. This is the common routine for Cleanup called by both the fsd and fsp
  136. threads.
  137. Arguments:
  138. Irp - Supplies the Irp to process
  139. Return Value:
  140. NTSTATUS - The return status for the operation
  141. --*/
  142. {
  143. NTSTATUS Status;
  144. PIO_STACK_LOCATION IrpSp;
  145. PFILE_OBJECT FileObject;
  146. TYPE_OF_OPEN TypeOfOpen;
  147. PVCB Vcb;
  148. PFCB Fcb;
  149. PSCB Scb;
  150. PCCB Ccb;
  151. PLCB Lcb;
  152. PLCB LcbForUpdate;
  153. PLCB LcbForCounts;
  154. PSCB ParentScb = NULL;
  155. PFCB ParentFcb = NULL;
  156. PLCB ThisLcb;
  157. PSCB ThisScb;
  158. ATTRIBUTE_ENUMERATION_CONTEXT AttrContext;
  159. PLONGLONG TruncateSize = NULL;
  160. LONGLONG LocalTruncateSize;
  161. BOOLEAN DeleteFile = FALSE;
  162. BOOLEAN DeleteStream = FALSE;
  163. BOOLEAN OpenById;
  164. BOOLEAN RemoveLink;
  165. BOOLEAN AcquiredParentScb = FALSE;
  166. BOOLEAN VolumeMounted = TRUE;
  167. LOGICAL VolumeMountedReadOnly;
  168. BOOLEAN AcquiredObjectID = FALSE;
  169. BOOLEAN CleanupAttrContext = FALSE;
  170. BOOLEAN UpdateDuplicateInfo = FALSE;
  171. BOOLEAN AddToDelayQueue = TRUE;
  172. BOOLEAN UnlockedVolume = FALSE;
  173. BOOLEAN DeleteFromFcbTable = FALSE;
  174. USHORT TotalLinkAdj = 0;
  175. PLIST_ENTRY Links;
  176. NAME_PAIR NamePair;
  177. NTFS_TUNNELED_DATA TunneledData;
  178. ULONG FcbStateClearFlags = 0;
  179. BOOLEAN DecrementScb = FALSE;
  180. PSCB ImageScb;
  181. #ifdef BRIANDBG
  182. BOOLEAN DecrementedCleanupCount = FALSE;
  183. #endif
  184. PSCB CurrentParentScb;
  185. BOOLEAN AcquiredCheckpoint = FALSE;
  186. ASSERT_IRP_CONTEXT( IrpContext );
  187. ASSERT_IRP( Irp );
  188. ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
  189. PAGED_CODE();
  190. NtfsInitializeNamePair( &NamePair );
  191. //
  192. // Get the current Irp stack location
  193. //
  194. IrpSp = IoGetCurrentIrpStackLocation( Irp );
  195. DebugTrace( +1, Dbg, ("NtfsCommonCleanup\n") );
  196. DebugTrace( 0, Dbg, ("IrpContext = %08lx\n", IrpContext) );
  197. DebugTrace( 0, Dbg, ("Irp = %08lx\n", Irp) );
  198. //
  199. // Extract and decode the file object
  200. //
  201. FileObject = IrpSp->FileObject;
  202. TypeOfOpen = NtfsDecodeFileObject( IrpContext, FileObject, &Vcb, &Fcb, &Scb, &Ccb, FALSE );
  203. Status = STATUS_SUCCESS;
  204. //
  205. // Special case the unopened file object and stream files.
  206. //
  207. if ((TypeOfOpen == UnopenedFileObject) ||
  208. (TypeOfOpen == StreamFileOpen)) {
  209. //
  210. // Just set the FO_CLEANUP_COMPLETE flag, and get outsky...
  211. //
  212. SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
  213. //
  214. // Theoretically we should never hit this case. It means an app
  215. // tried to close a handle he didn't open (call NtClose with a handle
  216. // value that happens to be in the handle table). It is safe to
  217. // simply return SUCCESS in this case.
  218. //
  219. // Trigger an assert so we can find the bad app though.
  220. //
  221. ASSERT( TypeOfOpen != StreamFileOpen );
  222. DebugTrace( -1, Dbg, ("NtfsCommonCleanup -> %08lx\n", Status) );
  223. NtfsCompleteRequest( IrpContext, Irp, Status );
  224. return Status;
  225. }
  226. //
  227. // Remember if this is an open by file Id open.
  228. //
  229. OpenById = BooleanFlagOn( Ccb->Flags, CCB_FLAG_OPEN_BY_FILE_ID );
  230. //
  231. // Remember the source info flags in the Ccb.
  232. //
  233. IrpContext->SourceInfo = Ccb->UsnSourceInfo;
  234. //
  235. // Acquire exclusive access to the Vcb and enqueue the irp if we didn't
  236. // get access
  237. //
  238. if (TypeOfOpen == UserVolumeOpen) {
  239. if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_DASD_UNLOCK )) {
  240. //
  241. // Start by locking out all other checkpoint
  242. // operations.
  243. //
  244. NtfsAcquireCheckpointSynchronization( IrpContext, Vcb );
  245. AcquiredCheckpoint = TRUE;
  246. }
  247. ASSERTMSG( "Acquire could fail.\n", FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ) );
  248. NtfsAcquireExclusiveVcb( IrpContext, Vcb, FALSE );
  249. } else {
  250. //
  251. // We will never have the checkpoint here so we can raise if dismounted
  252. //
  253. ASSERT( !AcquiredCheckpoint );
  254. NtfsAcquireSharedVcb( IrpContext, Vcb, TRUE );
  255. }
  256. //
  257. // Remember if the volume has been dismounted. No point in makeing any disk
  258. // changes in that case.
  259. //
  260. if (!FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
  261. VolumeMounted = FALSE;
  262. }
  263. VolumeMountedReadOnly = (LOGICAL)NtfsIsVolumeReadOnly( Vcb );
  264. //
  265. // Use a try-finally to facilitate cleanup.
  266. //
  267. try {
  268. //
  269. // Acquire Paging I/O first, since we may be deleting or truncating.
  270. // Testing for the PagingIoResource is not really safe without
  271. // holding the main resource, so we correct for that below.
  272. //
  273. if (Fcb->PagingIoResource != NULL) {
  274. NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
  275. NtfsAcquireExclusiveScb( IrpContext, Scb );
  276. } else {
  277. NtfsAcquireExclusiveScb( IrpContext, Scb );
  278. //
  279. // If we now do not see a paging I/O resource we are golden,
  280. // othewise we can absolutely release and acquire the resources
  281. // safely in the right order, since a resource in the Fcb is
  282. // not going to go away.
  283. //
  284. if (Fcb->PagingIoResource != NULL) {
  285. NtfsReleaseScb( IrpContext, Scb );
  286. NtfsAcquireExclusivePagingIo( IrpContext, Fcb );
  287. NtfsAcquireExclusiveScb( IrpContext, Scb );
  288. }
  289. }
  290. LcbForUpdate = LcbForCounts = Lcb = Ccb->Lcb;
  291. if (Lcb != NULL) {
  292. ParentScb = Lcb->Scb;
  293. if (ParentScb != NULL) {
  294. ParentFcb = ParentScb->Fcb;
  295. }
  296. }
  297. if (VolumeMounted && !VolumeMountedReadOnly) {
  298. //
  299. // Update the Lcb/Scb to reflect the case where this opener had
  300. // specified delete on close.
  301. //
  302. if (FlagOn( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE )) {
  303. if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
  304. BOOLEAN LastLink;
  305. BOOLEAN NonEmptyIndex;
  306. //
  307. // It is ok to get rid of this guy. All we need to do is
  308. // mark this Lcb for delete and decrement the link count
  309. // in the Fcb. If this is a primary link, then we
  310. // indicate that the primary link has been deleted.
  311. //
  312. if (!LcbLinkIsDeleted( Lcb ) &&
  313. (!IsDirectory( &Fcb->Info ) ||
  314. NtfsIsLinkDeleteable( IrpContext, Fcb, &NonEmptyIndex, &LastLink))) {
  315. //
  316. // Walk through all of the Scb's for this stream and
  317. // make sure there are no active image sections.
  318. //
  319. ImageScb = NULL;
  320. while ((ImageScb = NtfsGetNextChildScb( Fcb, ImageScb )) != NULL) {
  321. InterlockedIncrement( &ImageScb->CloseCount );
  322. DecrementScb = TRUE;
  323. if (NtfsIsTypeCodeUserData( ImageScb->AttributeTypeCode ) &&
  324. !FlagOn( ImageScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED ) &&
  325. (ImageScb->NonpagedScb->SegmentObject.ImageSectionObject != NULL)) {
  326. if (!MmFlushImageSection( &ImageScb->NonpagedScb->SegmentObject,
  327. MmFlushForDelete )) {
  328. InterlockedDecrement( &ImageScb->CloseCount );
  329. DecrementScb = FALSE;
  330. break;
  331. }
  332. }
  333. InterlockedDecrement( &ImageScb->CloseCount );
  334. DecrementScb = FALSE;
  335. }
  336. if (ImageScb == NULL) {
  337. if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS )) {
  338. SetFlag( Fcb->FcbState, FCB_STATE_PRIMARY_LINK_DELETED );
  339. }
  340. Fcb->LinkCount -= 1;
  341. SetFlag( Lcb->LcbState, LCB_STATE_DELETE_ON_CLOSE );
  342. //
  343. // Call into the notify package to close any handles on
  344. // a directory being deleted.
  345. //
  346. if (IsDirectory( &Fcb->Info )) {
  347. FsRtlNotifyFilterChangeDirectory( Vcb->NotifySync,
  348. &Vcb->DirNotifyList,
  349. FileObject->FsContext,
  350. NULL,
  351. FALSE,
  352. FALSE,
  353. 0,
  354. NULL,
  355. NULL,
  356. NULL,
  357. NULL );
  358. }
  359. }
  360. }
  361. //
  362. // Otherwise we are simply removing the attribute.
  363. //
  364. } else {
  365. ImageScb = Scb;
  366. InterlockedIncrement( &ImageScb->CloseCount );
  367. DecrementScb = TRUE;
  368. if ((ImageScb->NonpagedScb->SegmentObject.ImageSectionObject == NULL) ||
  369. MmFlushImageSection( &ImageScb->NonpagedScb->SegmentObject,
  370. MmFlushForDelete )) {
  371. SetFlag( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE );
  372. }
  373. InterlockedDecrement( &ImageScb->CloseCount );
  374. DecrementScb = FALSE;
  375. }
  376. //
  377. // Clear the flag so we will ignore it in the log file full case.
  378. //
  379. ClearFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
  380. }
  381. //
  382. // If we are going to try and delete something, anything, knock the file
  383. // size and valid data down to zero. Then update the snapshot
  384. // so that the sizes will be zero even if the operation fails.
  385. //
  386. // If we're deleting the file, go through all of the Scb's.
  387. //
  388. if ((Fcb->LinkCount == 0) &&
  389. (Fcb->CleanupCount == 1)) {
  390. DeleteFile = TRUE;
  391. NtfsFreeSnapshotsForFcb( IrpContext, Scb->Fcb );
  392. for (Links = Fcb->ScbQueue.Flink;
  393. Links != &Fcb->ScbQueue;
  394. Links = Links->Flink) {
  395. ThisScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
  396. //
  397. // Set the Scb sizes to zero except for the attribute list.
  398. //
  399. if ((ThisScb->AttributeTypeCode != $ATTRIBUTE_LIST) &&
  400. (ThisScb->AttributeTypeCode != $REPARSE_POINT)) {
  401. //
  402. // If the file is non-resident we will need a file object
  403. // when we delete the allocation. Create it now
  404. // so that CC will know the stream is shrinking to zero
  405. // and will purge the data.
  406. //
  407. if ((ThisScb->FileObject == NULL) &&
  408. (ThisScb->AttributeTypeCode == $DATA) &&
  409. (ThisScb->NonpagedScb->SegmentObject.DataSectionObject != NULL) &&
  410. !FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  411. //
  412. // Use a try-finally here in case we get an
  413. // INSUFFICIENT_RESOURCES. We still need to
  414. // proceed with the delete.
  415. //
  416. try {
  417. NtfsCreateInternalAttributeStream( IrpContext,
  418. ThisScb,
  419. TRUE,
  420. &NtfsInternalUseFile[COMMONCLEANUP_FILE_NUMBER] );
  421. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  422. if (Status == STATUS_INSUFFICIENT_RESOURCES) {
  423. Status = STATUS_SUCCESS;
  424. }
  425. }
  426. }
  427. ThisScb->Header.FileSize =
  428. ThisScb->Header.ValidDataLength = Li0;
  429. }
  430. if (FlagOn( ThisScb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
  431. NtfsSnapshotScb( IrpContext, ThisScb );
  432. }
  433. }
  434. //
  435. // Otherwise we may only be deleting this stream.
  436. //
  437. } else if (FlagOn( Scb->ScbState, SCB_STATE_DELETE_ON_CLOSE ) &&
  438. (Scb->CleanupCount == 1)) {
  439. try {
  440. //
  441. // We may have expanded the quota here and currently own some
  442. // quota resource. We want to release them now so we don't
  443. // deadlock with resources acquired later.
  444. //
  445. NtfsCheckpointCurrentTransaction( IrpContext );
  446. if (IrpContext->SharedScb != NULL) {
  447. NtfsReleaseSharedResources( IrpContext );
  448. }
  449. DeleteStream = TRUE;
  450. Scb->Header.FileSize =
  451. Scb->Header.ValidDataLength = Li0;
  452. NtfsFreeSnapshotsForFcb( IrpContext, Scb->Fcb );
  453. if (FlagOn( Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED )) {
  454. NtfsSnapshotScb( IrpContext, Scb );
  455. }
  456. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  457. NOTHING;
  458. }
  459. }
  460. }
  461. //
  462. // Let's do a sanity check.
  463. //
  464. ASSERT( Fcb->CleanupCount != 0 );
  465. ASSERT( Scb->CleanupCount != 0 );
  466. //
  467. // Case on the type of open that we are trying to cleanup.
  468. //
  469. switch (TypeOfOpen) {
  470. case UserVolumeOpen :
  471. DebugTrace( 0, Dbg, ("Cleanup on user volume\n") );
  472. //
  473. // First set the FO_CLEANUP_COMPLETE flag.
  474. //
  475. SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
  476. //
  477. // For a volume open, we check if this open locked the volume.
  478. // All the other work is done in common code below.
  479. //
  480. if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED ) &&
  481. (((ULONG_PTR)Vcb->FileObjectWithVcbLocked & ~1) == (ULONG_PTR)FileObject)) {
  482. //
  483. // Note its unlock attempt and retry so we can serialize with checkpoints
  484. //
  485. if (!FlagOn( IrpContext->State, IRP_CONTEXT_STATE_DASD_UNLOCK )) {
  486. SetFlag( IrpContext->State, IRP_CONTEXT_STATE_DASD_UNLOCK );
  487. NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
  488. }
  489. if ((ULONG_PTR)Vcb->FileObjectWithVcbLocked == ((ULONG_PTR)FileObject)+1) {
  490. NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE, NULL );
  491. //
  492. // Purge the volume for the autocheck case.
  493. //
  494. } else if (FlagOn( FileObject->Flags, FO_FILE_MODIFIED )) {
  495. if (VolumeMounted && !VolumeMountedReadOnly) {
  496. //
  497. // Drop the Scb for the volume Dasd around this call.
  498. //
  499. NtfsReleaseScb( IrpContext, Scb );
  500. try {
  501. NtfsFlushVolume( IrpContext, Vcb, FALSE, TRUE, TRUE, FALSE );
  502. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  503. NOTHING;
  504. }
  505. NtfsAcquireExclusiveScb( IrpContext, Scb );
  506. }
  507. //
  508. // If this is not the boot partition then dismount the Vcb.
  509. //
  510. #ifdef SYSCACHE_DEBUG
  511. if ((((Vcb->SyscacheScb) && (Vcb->CleanupCount == 2)) || (Vcb->CleanupCount == 1)) &&
  512. #else
  513. if ((Vcb->CleanupCount == 1) &&
  514. #endif
  515. ((Vcb->CloseCount - Vcb->SystemFileCloseCount) == 1)) {
  516. try {
  517. NtfsPerformDismountOnVcb( IrpContext, Vcb, TRUE, NULL );
  518. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  519. NOTHING;
  520. }
  521. }
  522. }
  523. ClearFlag( Vcb->VcbState, VCB_STATE_LOCKED | VCB_STATE_EXPLICIT_LOCK );
  524. Vcb->FileObjectWithVcbLocked = NULL;
  525. UnlockedVolume = TRUE;
  526. //
  527. // If the quota tracking has been requested and the quotas
  528. // need to be repaired then try to repair them now. Also restart
  529. // the Usn journal deletion if it was underway.
  530. //
  531. if (VolumeMounted && !VolumeMountedReadOnly) {
  532. if ((Status == STATUS_SUCCESS) &&
  533. (Vcb->DeleteUsnData.FinalStatus == STATUS_VOLUME_DISMOUNTED)) {
  534. Vcb->DeleteUsnData.FinalStatus = STATUS_SUCCESS;
  535. try {
  536. NtfsPostSpecial( IrpContext, Vcb, NtfsDeleteUsnSpecial, &Vcb->DeleteUsnData );
  537. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  538. NOTHING;
  539. }
  540. }
  541. if ((Status == STATUS_SUCCESS) &&
  542. FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_TRACKING_REQUESTED) &&
  543. FlagOn( Vcb->QuotaFlags, QUOTA_FLAG_OUT_OF_DATE |
  544. QUOTA_FLAG_CORRUPT |
  545. QUOTA_FLAG_PENDING_DELETES)) {
  546. try {
  547. NtfsPostRepairQuotaIndex( IrpContext, Vcb );
  548. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  549. NOTHING;
  550. }
  551. }
  552. }
  553. }
  554. break;
  555. case UserViewIndexOpen :
  556. case UserDirectoryOpen :
  557. DebugTrace( 0, Dbg, ("Cleanup on user directory/file\n") );
  558. NtfsSnapshotScb( IrpContext, Scb );
  559. //
  560. // Capture any changes to the time stamps for this file.
  561. //
  562. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  563. //
  564. // Now set the FO_CLEANUP_COMPLETE flag.
  565. //
  566. SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
  567. //
  568. // To perform cleanup on a directory, we first complete any
  569. // Irps watching from this directory. If we are deleting the
  570. // file then we remove all prefix entries for all the Lcb's going
  571. // into this directory and delete the file. We then report to
  572. // dir notify that this file is going away.
  573. //
  574. //
  575. // Complete any Notify Irps on this file handle.
  576. //
  577. if (FlagOn( Ccb->Flags, CCB_FLAG_DIR_NOTIFY )) {
  578. //
  579. // Both the notify count and notify list are separate for view
  580. // indices versus file name indices (directories).
  581. //
  582. if (TypeOfOpen == UserViewIndexOpen) {
  583. FsRtlNotifyCleanup( Vcb->NotifySync, &Vcb->ViewIndexNotifyList, Ccb );
  584. ClearFlag( Ccb->Flags, CCB_FLAG_DIR_NOTIFY );
  585. InterlockedDecrement( &Vcb->ViewIndexNotifyCount );
  586. } else {
  587. FsRtlNotifyCleanup( Vcb->NotifySync, &Vcb->DirNotifyList, Ccb );
  588. ClearFlag( Ccb->Flags, CCB_FLAG_DIR_NOTIFY );
  589. InterlockedDecrement( &Vcb->NotifyCount );
  590. }
  591. }
  592. //
  593. // Try to remove normalized name if its long and if no handles active (only this 1 left)
  594. // and no lcbs are active - all notifies farther down in function
  595. // use parent Scb's normalized name. If we don't remove it here
  596. // this always goes away during a close
  597. //
  598. if ((Scb->ScbType.Index.NormalizedName.MaximumLength > LONGNAME_THRESHOLD) &&
  599. (Fcb->CleanupCount == 1)) {
  600. //
  601. // Cleanup the current scb node and then trim its parents
  602. //
  603. NtfsDeleteNormalizedName( Scb );
  604. if (Lcb != NULL) {
  605. CurrentParentScb = Lcb->Scb;
  606. } else {
  607. CurrentParentScb = NULL;
  608. }
  609. if ((CurrentParentScb != NULL) &&
  610. (CurrentParentScb->ScbType.Index.NormalizedName.MaximumLength > LONGNAME_THRESHOLD)) {
  611. NtfsTrimNormalizedNames( IrpContext, Fcb, CurrentParentScb );
  612. }
  613. }
  614. //
  615. // When cleaning up a user directory, we always remove the
  616. // share access and modify the file counts. If the Fcb
  617. // has been marked as delete on close and this is the last
  618. // open file handle, we remove the file from the Mft and
  619. // remove it from it's parent index entry.
  620. //
  621. if (VolumeMounted &&
  622. (!VolumeMountedReadOnly) &&
  623. ((SafeNodeType( Scb ) == NTFS_NTC_SCB_INDEX))) {
  624. if (DeleteFile) {
  625. BOOLEAN AcquiredFcbTable = FALSE;
  626. ASSERT( (Lcb == NULL) ||
  627. (LcbLinkIsDeleted( Lcb ) && Lcb->CleanupCount == 1 ));
  628. //
  629. // If we don't have an Lcb and there is one on the Fcb then
  630. // let's use it.
  631. //
  632. if ((Lcb == NULL) && !IsListEmpty( &Fcb->LcbQueue )) {
  633. Lcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
  634. LCB,
  635. FcbLinks );
  636. ParentScb = Lcb->Scb;
  637. if (ParentScb != NULL) {
  638. ParentFcb = ParentScb->Fcb;
  639. }
  640. }
  641. try {
  642. //
  643. // In a very rare case the handle on a file may be an
  644. // OpenByFileID handle. In that case we need to find
  645. // the parent for the remaining link on the file.
  646. //
  647. if (ParentScb == NULL) {
  648. PFILE_NAME FileNameAttr;
  649. UNICODE_STRING FileName;
  650. NtfsInitializeAttributeContext( &AttrContext );
  651. CleanupAttrContext = TRUE;
  652. if (!NtfsLookupAttributeByCode( IrpContext,
  653. Fcb,
  654. &Fcb->FileReference,
  655. $FILE_NAME,
  656. &AttrContext )) {
  657. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  658. }
  659. //
  660. // Now we need an Fcb and then an Scb for the directory.
  661. //
  662. FileNameAttr = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  663. NtfsAcquireFcbTable( IrpContext, Vcb );
  664. AcquiredFcbTable = TRUE;
  665. ParentFcb = NtfsCreateFcb( IrpContext,
  666. Vcb,
  667. FileNameAttr->ParentDirectory,
  668. FALSE,
  669. TRUE,
  670. NULL );
  671. ParentFcb->ReferenceCount += 1;
  672. if (!NtfsAcquireExclusiveFcb( IrpContext, ParentFcb, NULL, ACQUIRE_NO_DELETE_CHECK | ACQUIRE_DONT_WAIT )) {
  673. NtfsReleaseFcbTable( IrpContext, Vcb );
  674. NtfsAcquireExclusiveFcb( IrpContext, ParentFcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  675. NtfsAcquireFcbTable( IrpContext, Vcb );
  676. }
  677. ParentFcb->ReferenceCount -= 1;
  678. NtfsReleaseFcbTable( IrpContext, Vcb );
  679. AcquiredFcbTable = FALSE;
  680. ParentScb = NtfsCreateScb( IrpContext,
  681. ParentFcb,
  682. $INDEX_ALLOCATION,
  683. &NtfsFileNameIndex,
  684. FALSE,
  685. NULL );
  686. if (FlagOn(Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED)) {
  687. NtfsSnapshotScb( IrpContext, Scb );
  688. }
  689. //
  690. // Make sure this new Scb is connected to our Fcb so teardown
  691. // will happen.
  692. //
  693. FileName.Buffer = &FileNameAttr->FileName[0];
  694. FileName.MaximumLength =
  695. FileName.Length = FileNameAttr->FileNameLength * sizeof( WCHAR );
  696. NtfsCreateLcb( IrpContext,
  697. ParentScb,
  698. Fcb,
  699. FileName,
  700. FileNameAttr->Flags,
  701. NULL );
  702. AcquiredParentScb = TRUE;
  703. }
  704. NtfsDeleteFile( IrpContext, Fcb, ParentScb, &AcquiredParentScb, NULL, NULL );
  705. //
  706. // Commit the delete - this has the nice effect of writing out all usn journal records for the
  707. // delete after this we can safely remove the in memory structures (esp. the fcbtable)
  708. // and not worry about retrying the request due to a logfilefull
  709. //
  710. NtfsCheckpointCurrentTransaction( IrpContext );
  711. TotalLinkAdj += 1;
  712. //
  713. // Remove all tunneling entries for this directory
  714. //
  715. FsRtlDeleteKeyFromTunnelCache( &Vcb->Tunnel,
  716. *(PULONGLONG) &Fcb->FileReference );
  717. if (ParentFcb != NULL) {
  718. NtfsUpdateFcb( ParentFcb,
  719. (FCB_INFO_CHANGED_LAST_CHANGE |
  720. FCB_INFO_CHANGED_LAST_MOD |
  721. FCB_INFO_UPDATE_LAST_ACCESS) );
  722. }
  723. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  724. NOTHING;
  725. }
  726. if (AcquiredFcbTable) {
  727. NtfsReleaseFcbTable( IrpContext, Vcb );
  728. }
  729. if (STATUS_SUCCESS == Status) {
  730. if (!OpenById && (Vcb->NotifyCount != 0)) {
  731. NtfsReportDirNotify( IrpContext,
  732. Vcb,
  733. &Ccb->FullFileName,
  734. Ccb->LastFileNameOffset,
  735. NULL,
  736. ((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
  737. (Ccb->Lcb != NULL) &&
  738. (Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Length != 0)) ?
  739. &Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
  740. NULL),
  741. FILE_NOTIFY_CHANGE_DIR_NAME,
  742. FILE_ACTION_REMOVED,
  743. ParentFcb );
  744. }
  745. SetFlag( Fcb->FcbState, FCB_STATE_FILE_DELETED );
  746. SetFlag( FcbStateClearFlags, FCB_STATE_FILE_DELETED );
  747. DeleteFromFcbTable = TRUE;
  748. //
  749. // We need to mark all of the links on the file as gone.
  750. // If there is a parent Scb then it will be the parent
  751. // for all of the links.
  752. //
  753. for (Links = Fcb->LcbQueue.Flink;
  754. Links != &Fcb->LcbQueue;
  755. Links = Links->Flink) {
  756. ThisLcb = CONTAINING_RECORD( Links, LCB, FcbLinks );
  757. //
  758. // Remove all remaining prefixes on this link.
  759. // Make sure the resource is acquired.
  760. //
  761. if (!AcquiredParentScb) {
  762. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  763. AcquiredParentScb = TRUE;
  764. }
  765. NtfsRemovePrefix( ThisLcb );
  766. //
  767. // Remove any hash table entries for this Lcb.
  768. //
  769. NtfsRemoveHashEntriesForLcb( ThisLcb );
  770. SetFlag( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE );
  771. //
  772. // We don't need to report any changes on this link.
  773. //
  774. ThisLcb->InfoFlags = 0;
  775. }
  776. //
  777. // We need to mark all of the Scbs as gone.
  778. //
  779. for (Links = Fcb->ScbQueue.Flink;
  780. Links != &Fcb->ScbQueue;
  781. Links = Links->Flink) {
  782. ThisScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
  783. ClearFlag( ThisScb->ScbState,
  784. SCB_STATE_NOTIFY_ADD_STREAM |
  785. SCB_STATE_NOTIFY_REMOVE_STREAM |
  786. SCB_STATE_NOTIFY_RESIZE_STREAM |
  787. SCB_STATE_NOTIFY_MODIFY_STREAM );
  788. if (!FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  789. NtfsSnapshotScb( IrpContext, ThisScb );
  790. ThisScb->ValidDataToDisk =
  791. ThisScb->Header.AllocationSize.QuadPart =
  792. ThisScb->Header.FileSize.QuadPart =
  793. ThisScb->Header.ValidDataLength.QuadPart = 0;
  794. SetFlag( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  795. }
  796. }
  797. //
  798. // We certainly don't need to any on disk update for this
  799. // file now.
  800. //
  801. Fcb->InfoFlags = 0;
  802. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  803. ClearFlag( Ccb->Flags,
  804. CCB_FLAG_USER_SET_LAST_MOD_TIME |
  805. CCB_FLAG_USER_SET_LAST_CHANGE_TIME |
  806. CCB_FLAG_USER_SET_LAST_ACCESS_TIME );
  807. }
  808. } else {
  809. //
  810. // Determine if we should put this on the delayed close list.
  811. // The following must be true.
  812. //
  813. // - This is not the root directory
  814. // - This directory is not about to be deleted
  815. // - This is the last handle and last file object for this
  816. // directory.
  817. // - There are no other file objects on this file.
  818. // - We are not currently reducing the delayed close queue.
  819. //
  820. if ((Fcb->CloseCount == 1) &&
  821. (NtfsData.DelayedCloseCount <= NtfsMaxDelayedCloseCount)) {
  822. NtfsAcquireFsrtlHeader( Scb );
  823. SetFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
  824. NtfsReleaseFsrtlHeader( Scb );
  825. }
  826. }
  827. }
  828. break;
  829. case UserFileOpen :
  830. DebugTrace( 0, Dbg, ("Cleanup on user file\n") );
  831. //
  832. // If the Scb is uninitialized, we read it from the disk.
  833. //
  834. if (!FlagOn( Scb->ScbState, SCB_STATE_HEADER_INITIALIZED ) &&
  835. VolumeMounted &&
  836. !VolumeMountedReadOnly) {
  837. try {
  838. NtfsUpdateScbFromAttribute( IrpContext, Scb, NULL );
  839. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  840. NOTHING;
  841. }
  842. }
  843. NtfsSnapshotScb( IrpContext, Scb );
  844. //
  845. // Coordinate the cleanup operation with the oplock state.
  846. // Cleanup operations can always cleanup immediately.
  847. //
  848. if (SafeNodeType( Scb ) == NTFS_NTC_SCB_DATA) {
  849. FsRtlCheckOplock( &Scb->ScbType.Data.Oplock,
  850. Irp,
  851. IrpContext,
  852. NULL,
  853. NULL );
  854. }
  855. //
  856. // In this case, we have to unlock all the outstanding file
  857. // locks, update the time stamps for the file and sizes for
  858. // this attribute, and set the archive bit if necessary.
  859. //
  860. if (SafeNodeType( Scb ) == NTFS_NTC_SCB_DATA &&
  861. Scb->ScbType.Data.FileLock != NULL) {
  862. (VOID) FsRtlFastUnlockAll( Scb->ScbType.Data.FileLock,
  863. FileObject,
  864. IoGetRequestorProcess( Irp ),
  865. NULL );
  866. }
  867. //
  868. // Update the FastIoField.
  869. //
  870. NtfsAcquireFsrtlHeader( Scb );
  871. Scb->Header.IsFastIoPossible = NtfsIsFastIoPossible( Scb );
  872. NtfsReleaseFsrtlHeader( Scb );
  873. //
  874. // Trim normalized names of parent dirs if they are over the threshold
  875. //
  876. ASSERT( IrpContext->TransactionId == 0 );
  877. if (Fcb->CleanupCount == 1) {
  878. if (Lcb != NULL) {
  879. CurrentParentScb = Lcb->Scb;
  880. } else {
  881. CurrentParentScb = NULL;
  882. }
  883. if ((CurrentParentScb != NULL) &&
  884. (CurrentParentScb->ScbType.Index.NormalizedName.MaximumLength > LONGNAME_THRESHOLD)) {
  885. NtfsTrimNormalizedNames( IrpContext, Fcb, CurrentParentScb );
  886. }
  887. } // endif cleanupcnt == 1
  888. //
  889. // If the Fcb is in valid shape, we check on the cases where we delete
  890. // the file or attribute.
  891. //
  892. if (VolumeMounted && !VolumeMountedReadOnly) {
  893. //
  894. // Capture any changes to the time stamps for this file.
  895. //
  896. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  897. //
  898. // Now set the FO_CLEANUP_COMPLETE flag.
  899. //
  900. SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
  901. if (Status == STATUS_SUCCESS) {
  902. //
  903. // We are checking here for special actions we take when
  904. // we have the last user handle on a link and the link has
  905. // been marked for delete. We could either be removing the
  906. // file or removing a link.
  907. //
  908. if ((Lcb == NULL) || (LcbLinkIsDeleted( Lcb ) && (Lcb->CleanupCount == 1))) {
  909. if (DeleteFile) {
  910. BOOLEAN AcquiredFcbTable = FALSE;
  911. //
  912. // If we don't have an Lcb and the Fcb has some entries then
  913. // grab one of these to do the update.
  914. //
  915. if (Lcb == NULL) {
  916. for (Links = Fcb->LcbQueue.Flink;
  917. Links != &Fcb->LcbQueue;
  918. Links = Links->Flink) {
  919. ThisLcb = CONTAINING_RECORD( Fcb->LcbQueue.Flink,
  920. LCB,
  921. FcbLinks );
  922. if (!FlagOn( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE )) {
  923. Lcb = ThisLcb;
  924. ParentScb = Lcb->Scb;
  925. if (ParentScb != NULL) {
  926. ParentFcb = ParentScb->Fcb;
  927. }
  928. break;
  929. }
  930. }
  931. }
  932. try {
  933. //
  934. // In a very rare case the handle on a file may be an
  935. // OpenByFileID handle. In that case we need to find
  936. // the parent for the remaining link on the file.
  937. //
  938. if (ParentScb == NULL) {
  939. PFILE_NAME FileNameAttr;
  940. UNICODE_STRING FileName;
  941. NtfsInitializeAttributeContext( &AttrContext );
  942. CleanupAttrContext = TRUE;
  943. if (!NtfsLookupAttributeByCode( IrpContext,
  944. Fcb,
  945. &Fcb->FileReference,
  946. $FILE_NAME,
  947. &AttrContext )) {
  948. NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Fcb );
  949. }
  950. //
  951. // Now we need an Fcb and then an Scb for the directory.
  952. //
  953. FileNameAttr = (PFILE_NAME) NtfsAttributeValue( NtfsFoundAttribute( &AttrContext ));
  954. NtfsAcquireFcbTable( IrpContext, Vcb );
  955. AcquiredFcbTable = TRUE;
  956. ParentFcb = NtfsCreateFcb( IrpContext,
  957. Vcb,
  958. FileNameAttr->ParentDirectory,
  959. FALSE,
  960. TRUE,
  961. NULL );
  962. ParentFcb->ReferenceCount += 1;
  963. if (!NtfsAcquireExclusiveFcb( IrpContext, ParentFcb, NULL, ACQUIRE_NO_DELETE_CHECK | ACQUIRE_DONT_WAIT )) {
  964. NtfsReleaseFcbTable( IrpContext, Vcb );
  965. NtfsAcquireExclusiveFcb( IrpContext, ParentFcb, NULL, ACQUIRE_NO_DELETE_CHECK );
  966. NtfsAcquireFcbTable( IrpContext, Vcb );
  967. }
  968. ParentFcb->ReferenceCount -= 1;
  969. NtfsReleaseFcbTable( IrpContext, Vcb );
  970. AcquiredFcbTable = FALSE;
  971. ParentScb = NtfsCreateScb( IrpContext,
  972. ParentFcb,
  973. $INDEX_ALLOCATION,
  974. &NtfsFileNameIndex,
  975. FALSE,
  976. NULL );
  977. if (FlagOn(Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED)) {
  978. NtfsSnapshotScb( IrpContext, Scb );
  979. }
  980. //
  981. // Make sure this new Scb is connected to our Fcb so teardown
  982. // will happen.
  983. //
  984. FileName.Buffer = &FileNameAttr->FileName[0];
  985. FileName.MaximumLength =
  986. FileName.Length = FileNameAttr->FileNameLength * sizeof( WCHAR );
  987. NtfsCreateLcb( IrpContext,
  988. ParentScb,
  989. Fcb,
  990. FileName,
  991. FileNameAttr->Flags,
  992. NULL );
  993. AcquiredParentScb = TRUE;
  994. }
  995. AddToDelayQueue = FALSE;
  996. TunneledData.HasObjectId = FALSE;
  997. NtfsDeleteFile( IrpContext, Fcb, ParentScb, &AcquiredParentScb, &NamePair, &TunneledData );
  998. //
  999. // Commit the delete - this has the nice effect of writing out all usn journal records for the
  1000. // delete after this we can safely remove the in memory structures (esp. the fcbtable)
  1001. // and not worry about retrying the request due to a logfilefull
  1002. //
  1003. NtfsCheckpointCurrentTransaction( IrpContext );
  1004. TotalLinkAdj += 1;
  1005. //
  1006. // Stash property information in the tunnel if the object was
  1007. // opened by name, has a parent directory caller was treating it
  1008. // as a non-POSIX object and we had an good, active link
  1009. //
  1010. if (!OpenById &&
  1011. ParentScb &&
  1012. Ccb->Lcb &&
  1013. !FlagOn(FileObject->Flags, FO_OPENED_CASE_SENSITIVE)) {
  1014. NtfsGetTunneledData( IrpContext,
  1015. Fcb,
  1016. &TunneledData );
  1017. FsRtlAddToTunnelCache( &Vcb->Tunnel,
  1018. *(PULONGLONG)&ParentScb->Fcb->FileReference,
  1019. &NamePair.Short,
  1020. &NamePair.Long,
  1021. BooleanFlagOn(Ccb->Lcb->FileNameAttr->Flags, FILE_NAME_DOS),
  1022. sizeof(NTFS_TUNNELED_DATA),
  1023. &TunneledData);
  1024. }
  1025. if (ParentFcb != NULL) {
  1026. NtfsUpdateFcb( ParentFcb,
  1027. (FCB_INFO_CHANGED_LAST_CHANGE |
  1028. FCB_INFO_CHANGED_LAST_MOD |
  1029. FCB_INFO_UPDATE_LAST_ACCESS) );
  1030. }
  1031. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1032. NOTHING;
  1033. }
  1034. if (AcquiredFcbTable) {
  1035. NtfsReleaseFcbTable( IrpContext, Vcb );
  1036. }
  1037. if (Status == STATUS_SUCCESS) {
  1038. if ((Vcb->NotifyCount != 0) &&
  1039. !OpenById) {
  1040. NtfsReportDirNotify( IrpContext,
  1041. Vcb,
  1042. &Ccb->FullFileName,
  1043. Ccb->LastFileNameOffset,
  1044. NULL,
  1045. ((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
  1046. (Ccb->Lcb != NULL) &&
  1047. (Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Length != 0)) ?
  1048. &Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
  1049. NULL),
  1050. FILE_NOTIFY_CHANGE_FILE_NAME,
  1051. FILE_ACTION_REMOVED,
  1052. ParentFcb );
  1053. }
  1054. SetFlag( Fcb->FcbState, FCB_STATE_FILE_DELETED );
  1055. SetFlag( FcbStateClearFlags, FCB_STATE_FILE_DELETED );
  1056. DeleteFromFcbTable = TRUE;
  1057. //
  1058. // We need to mark all of the links on the file as gone.
  1059. //
  1060. for (Links = Fcb->LcbQueue.Flink;
  1061. Links != &Fcb->LcbQueue;
  1062. Links = Links->Flink) {
  1063. ThisLcb = CONTAINING_RECORD( Links, LCB, FcbLinks );
  1064. if (ThisLcb->Scb == ParentScb) {
  1065. //
  1066. // Remove all remaining prefixes on this link.
  1067. // Make sure the resource is acquired.
  1068. //
  1069. if (!AcquiredParentScb) {
  1070. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  1071. AcquiredParentScb = TRUE;
  1072. }
  1073. NtfsRemovePrefix( ThisLcb );
  1074. //
  1075. // Remove any hash table entries for this Lcb.
  1076. //
  1077. NtfsRemoveHashEntriesForLcb( ThisLcb );
  1078. SetFlag( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE );
  1079. //
  1080. // We don't need to report any changes on this link.
  1081. //
  1082. ThisLcb->InfoFlags = 0;
  1083. }
  1084. }
  1085. //
  1086. // We need to mark all of the Scbs as gone.
  1087. //
  1088. for (Links = Fcb->ScbQueue.Flink;
  1089. Links != &Fcb->ScbQueue;
  1090. Links = Links->Flink) {
  1091. ThisScb = CONTAINING_RECORD( Links, SCB, FcbLinks );
  1092. ClearFlag( ThisScb->ScbState,
  1093. SCB_STATE_NOTIFY_ADD_STREAM |
  1094. SCB_STATE_NOTIFY_REMOVE_STREAM |
  1095. SCB_STATE_NOTIFY_RESIZE_STREAM |
  1096. SCB_STATE_NOTIFY_MODIFY_STREAM );
  1097. if (!FlagOn( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )) {
  1098. NtfsSnapshotScb( IrpContext, ThisScb );
  1099. ThisScb->ValidDataToDisk =
  1100. ThisScb->Header.AllocationSize.QuadPart =
  1101. ThisScb->Header.FileSize.QuadPart =
  1102. ThisScb->Header.ValidDataLength.QuadPart = 0;
  1103. SetFlag( ThisScb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  1104. }
  1105. }
  1106. //
  1107. // We certainly don't need to any on disk update for this
  1108. // file now.
  1109. //
  1110. Fcb->InfoFlags = 0;
  1111. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  1112. ClearFlag( Ccb->Flags,
  1113. CCB_FLAG_USER_SET_LAST_MOD_TIME |
  1114. CCB_FLAG_USER_SET_LAST_CHANGE_TIME |
  1115. CCB_FLAG_USER_SET_LAST_ACCESS_TIME );
  1116. //
  1117. // We will truncate the attribute to size 0.
  1118. //
  1119. TruncateSize = (PLONGLONG)&Li0;
  1120. }
  1121. //
  1122. // Now we want to check for the last user's handle on a
  1123. // link (or the last handle on a Ntfs/8.3 pair). In this
  1124. // case we want to remove the links from the disk.
  1125. //
  1126. } else if (Lcb != NULL) {
  1127. ThisLcb = NULL;
  1128. RemoveLink = TRUE;
  1129. if (FlagOn( Lcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS ) &&
  1130. (Lcb->FileNameAttr->Flags != (FILE_NAME_NTFS | FILE_NAME_DOS))) {
  1131. //
  1132. // Walk through all the links looking for a link
  1133. // with a flag set which is not the same as the
  1134. // link we already have.
  1135. //
  1136. for (Links = Fcb->LcbQueue.Flink;
  1137. Links != &Fcb->LcbQueue;
  1138. Links = Links->Flink) {
  1139. ThisLcb = CONTAINING_RECORD( Links, LCB, FcbLinks );
  1140. //
  1141. // If this has a flag set and is not the Lcb
  1142. // for this cleanup, then we check if there
  1143. // are no Ccb's left for this.
  1144. //
  1145. if (FlagOn( ThisLcb->FileNameAttr->Flags, FILE_NAME_DOS | FILE_NAME_NTFS )
  1146. &&
  1147. (ThisLcb != Lcb)) {
  1148. if (ThisLcb->CleanupCount != 0) {
  1149. RemoveLink = FALSE;
  1150. }
  1151. break;
  1152. }
  1153. ThisLcb = NULL;
  1154. }
  1155. }
  1156. //
  1157. // If we are to remove the link, we do so now. This removes
  1158. // the filename attributes and the entries in the parent
  1159. // indexes for this link. In addition, we mark the links
  1160. // as having been removed and decrement the number of links
  1161. // left on the file. We don't remove the link if this the
  1162. // last link on the file. This means that someone has the
  1163. // file open by FileID. We don't want to remove the last
  1164. // link until all the handles are closed.
  1165. //
  1166. if (RemoveLink && (Fcb->TotalLinks > 1)) {
  1167. NtfsAcquireExclusiveScb( IrpContext, ParentScb );
  1168. AcquiredParentScb = TRUE;
  1169. //
  1170. // We might end up with deallocating or even allocating an MFT
  1171. // record in the process of deleting this entry. So, it's wise to preacquire
  1172. // QuotaControl resource, lest we deadlock with the MftScb resource.
  1173. // Unfortunately, it's not easy to figure out whether any of those will
  1174. // actually happen at this point.
  1175. //
  1176. // We must have acquired all the other resources except $ObjId
  1177. // at this point. QuotaControl must get acquired after ObjId Scb.
  1178. //
  1179. if (NtfsPerformQuotaOperation( Fcb )) {
  1180. NtfsAcquireSharedScb( IrpContext, Vcb->ObjectIdTableScb );
  1181. AcquiredObjectID = TRUE;
  1182. NtfsAcquireQuotaControl( IrpContext, Fcb->QuotaControl );
  1183. }
  1184. try {
  1185. AddToDelayQueue = FALSE;
  1186. TunneledData.HasObjectId = FALSE;
  1187. //
  1188. // Post the link change.
  1189. //
  1190. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_HARD_LINK_CHANGE );
  1191. NtfsRemoveLink( IrpContext,
  1192. Fcb,
  1193. ParentScb,
  1194. Lcb->ExactCaseLink.LinkName,
  1195. &NamePair,
  1196. &TunneledData );
  1197. //
  1198. // It's possible that this is the link that was used for the
  1199. // Usn name. Make sure we look up the Usn name on the
  1200. // next operation.
  1201. //
  1202. ClearFlag( Fcb->FcbState, FCB_STATE_VALID_USN_NAME );
  1203. //
  1204. // Stash property information in the tunnel if caller opened the
  1205. // object by name and was treating it as a non-POSIX object
  1206. //
  1207. if (!OpenById && !FlagOn(FileObject->Flags, FO_OPENED_CASE_SENSITIVE)) {
  1208. NtfsGetTunneledData( IrpContext,
  1209. Fcb,
  1210. &TunneledData );
  1211. FsRtlAddToTunnelCache( &Vcb->Tunnel,
  1212. *(PULONGLONG)&ParentScb->Fcb->FileReference,
  1213. &NamePair.Short,
  1214. &NamePair.Long,
  1215. BooleanFlagOn(Lcb->FileNameAttr->Flags, FILE_NAME_DOS),
  1216. sizeof(NTFS_TUNNELED_DATA),
  1217. &TunneledData);
  1218. }
  1219. TotalLinkAdj += 1;
  1220. NtfsUpdateFcb( ParentFcb,
  1221. (FCB_INFO_CHANGED_LAST_CHANGE |
  1222. FCB_INFO_CHANGED_LAST_MOD |
  1223. FCB_INFO_UPDATE_LAST_ACCESS) );
  1224. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1225. NOTHING;
  1226. }
  1227. if ((Vcb->NotifyCount != 0) &&
  1228. !OpenById &&
  1229. (Status == STATUS_SUCCESS)) {
  1230. NtfsReportDirNotify( IrpContext,
  1231. Vcb,
  1232. &Ccb->FullFileName,
  1233. Ccb->LastFileNameOffset,
  1234. NULL,
  1235. ((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
  1236. (Ccb->Lcb != NULL) &&
  1237. (Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Length != 0)) ?
  1238. &Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
  1239. NULL),
  1240. FILE_NOTIFY_CHANGE_FILE_NAME,
  1241. FILE_ACTION_REMOVED,
  1242. ParentFcb );
  1243. }
  1244. //
  1245. // Remove all remaining prefixes on this link.
  1246. //
  1247. ASSERT( NtfsIsExclusiveScb( Lcb->Scb ) );
  1248. NtfsRemovePrefix( Lcb );
  1249. //
  1250. // Remove any hash table entries for this Lcb.
  1251. //
  1252. NtfsRemoveHashEntriesForLcb( Lcb );
  1253. //
  1254. // Mark the links as being removed.
  1255. //
  1256. SetFlag( Lcb->LcbState, LCB_STATE_LINK_IS_GONE );
  1257. if (ThisLcb != NULL) {
  1258. //
  1259. // Remove all remaining prefixes on this link.
  1260. //
  1261. ASSERT( NtfsIsExclusiveScb( ThisLcb->Scb ) );
  1262. NtfsRemovePrefix( ThisLcb );
  1263. //
  1264. // Remove any hash table entries for this Lcb.
  1265. //
  1266. NtfsRemoveHashEntriesForLcb( ThisLcb );
  1267. SetFlag( ThisLcb->LcbState, LCB_STATE_LINK_IS_GONE );
  1268. ThisLcb->InfoFlags = 0;
  1269. }
  1270. //
  1271. // Since the link is gone we don't want to update the
  1272. // duplicate information for this link.
  1273. //
  1274. Lcb->InfoFlags = 0;
  1275. LcbForUpdate = NULL;
  1276. //
  1277. // Update the time stamps for removing the link. Clear the
  1278. // FO_CLEANUP_COMPLETE flag around this call so the time
  1279. // stamp change is not nooped.
  1280. //
  1281. SetFlag( Ccb->Flags, CCB_FLAG_UPDATE_LAST_CHANGE );
  1282. ClearFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
  1283. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  1284. SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
  1285. }
  1286. }
  1287. }
  1288. //
  1289. // If the file/attribute is not going away, we update the
  1290. // attribute size now rather than waiting for the Lazy
  1291. // Writer to catch up. If the cleanup count isn't 1 then
  1292. // defer the following actions.
  1293. //
  1294. if ((Scb->CleanupCount == 1) &&
  1295. (Fcb->LinkCount != 0) &&
  1296. (Status == STATUS_SUCCESS)) {
  1297. //
  1298. // We may also have to delete this attribute only.
  1299. //
  1300. if (DeleteStream) {
  1301. //
  1302. // If this stream is subject to quota then we need to acquire the
  1303. // parent now. Other we will acquire quota control during the
  1304. // delete operation and then try to acquire the parent to
  1305. // perform the update duplicate info.
  1306. //
  1307. if (NtfsPerformQuotaOperation( Fcb ) && (LcbForUpdate != NULL) && (!FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE ))) {
  1308. NtfsPrepareForUpdateDuplicate( IrpContext,
  1309. Fcb,
  1310. &LcbForUpdate,
  1311. &ParentScb,
  1312. TRUE );
  1313. }
  1314. //
  1315. // We might be retrying the delete stream operation due to a log file
  1316. // full. If the attribute type code is $UNUSED then the stream has
  1317. // already been deleted.
  1318. //
  1319. if (Scb->AttributeTypeCode != $UNUSED) {
  1320. try {
  1321. //
  1322. // Delete the attribute only.
  1323. //
  1324. if (CleanupAttrContext) {
  1325. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1326. }
  1327. NtfsInitializeAttributeContext( &AttrContext );
  1328. CleanupAttrContext = TRUE;
  1329. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  1330. do {
  1331. NtfsDeleteAttributeRecord( IrpContext,
  1332. Fcb,
  1333. (DELETE_LOG_OPERATION |
  1334. DELETE_RELEASE_FILE_RECORD |
  1335. DELETE_RELEASE_ALLOCATION),
  1336. &AttrContext );
  1337. } while (NtfsLookupNextAttributeForScb( IrpContext, Scb, &AttrContext ));
  1338. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1339. NOTHING;
  1340. }
  1341. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_STREAM_CHANGE );
  1342. }
  1343. //
  1344. // Now that we're done deleting the attribute, we need to checkpoint
  1345. // so that the Mft resource can be released. First we need to set
  1346. // the appropriate IrpContext flag to indicate whether we really need
  1347. // to release the Mft.
  1348. //
  1349. if ((Vcb->MftScb != NULL) &&
  1350. (Vcb->MftScb->Fcb->ExclusiveFcbLinks.Flink != NULL) &&
  1351. NtfsIsExclusiveScb( Vcb->MftScb )) {
  1352. SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_MFT );
  1353. }
  1354. try {
  1355. NtfsCheckpointCurrentTransaction( IrpContext );
  1356. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1357. NOTHING;
  1358. }
  1359. //
  1360. // Set the Scb flag to indicate that the attribute is
  1361. // gone.
  1362. //
  1363. Scb->ValidDataToDisk =
  1364. Scb->Header.AllocationSize.QuadPart =
  1365. Scb->Header.FileSize.QuadPart =
  1366. Scb->Header.ValidDataLength.QuadPart = 0;
  1367. Scb->AttributeTypeCode = $UNUSED;
  1368. SetFlag( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED );
  1369. SetFlag( Scb->ScbState, SCB_STATE_NOTIFY_REMOVE_STREAM );
  1370. ClearFlag( Scb->ScbState,
  1371. SCB_STATE_NOTIFY_RESIZE_STREAM |
  1372. SCB_STATE_NOTIFY_MODIFY_STREAM |
  1373. SCB_STATE_NOTIFY_ADD_STREAM );
  1374. //
  1375. // Update the time stamps for removing the link. Clear the
  1376. // FO_CLEANUP_COMPLETE flag around this call so the time
  1377. // stamp change is not nooped.
  1378. //
  1379. SetFlag( Ccb->Flags,
  1380. CCB_FLAG_UPDATE_LAST_CHANGE | CCB_FLAG_SET_ARCHIVE );
  1381. ClearFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
  1382. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  1383. SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
  1384. TruncateSize = (PLONGLONG)&Li0;
  1385. //
  1386. // Check if we're to modify the allocation size or file size.
  1387. //
  1388. } else {
  1389. if (FlagOn( Scb->ScbState, SCB_STATE_CHECK_ATTRIBUTE_SIZE )) {
  1390. //
  1391. // Acquire the parent now so we enforce our locking
  1392. // rules that the Mft Scb must be acquired after
  1393. // the normal file resources.
  1394. //
  1395. if (!FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  1396. NtfsPrepareForUpdateDuplicate( IrpContext,
  1397. Fcb,
  1398. &LcbForUpdate,
  1399. &ParentScb,
  1400. TRUE );
  1401. }
  1402. //
  1403. // For the non-resident streams we will write the file
  1404. // size to disk.
  1405. //
  1406. if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  1407. //
  1408. // Setting AdvanceOnly to FALSE guarantees we will not
  1409. // incorrectly advance the valid data size.
  1410. //
  1411. try {
  1412. NtfsWriteFileSizes( IrpContext,
  1413. Scb,
  1414. &Scb->Header.ValidDataLength.QuadPart,
  1415. FALSE,
  1416. TRUE,
  1417. TRUE );
  1418. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1419. NOTHING;
  1420. }
  1421. //
  1422. // For resident streams we will write the correct size to
  1423. // the resident attribute.
  1424. //
  1425. } else {
  1426. //
  1427. // We need to lookup the attribute and change
  1428. // the attribute value. We can point to
  1429. // the attribute itself as the changing
  1430. // value.
  1431. //
  1432. NtfsInitializeAttributeContext( &AttrContext );
  1433. CleanupAttrContext = TRUE;
  1434. try {
  1435. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  1436. NtfsChangeAttributeValue( IrpContext,
  1437. Fcb,
  1438. Scb->Header.FileSize.LowPart,
  1439. NULL,
  1440. 0,
  1441. TRUE,
  1442. TRUE,
  1443. FALSE,
  1444. FALSE,
  1445. &AttrContext );
  1446. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1447. NOTHING;
  1448. }
  1449. //
  1450. // Verify the allocation size is now correct.
  1451. //
  1452. if (QuadAlign( Scb->Header.FileSize.LowPart ) != Scb->Header.AllocationSize.LowPart) {
  1453. Scb->Header.AllocationSize.LowPart = QuadAlign(Scb->Header.FileSize.LowPart);
  1454. }
  1455. }
  1456. //
  1457. // Update the size change to the Fcb.
  1458. //
  1459. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  1460. }
  1461. if (Status == STATUS_SUCCESS) {
  1462. if (FlagOn( Scb->ScbState, SCB_STATE_TRUNCATE_ON_CLOSE ) &&
  1463. (Status == STATUS_SUCCESS)) {
  1464. //
  1465. // Acquire the parent now so we enforce our locking
  1466. // rules that the Mft Scb must be acquired after
  1467. // the normal file resources.
  1468. //
  1469. if (!FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE)) {
  1470. NtfsPrepareForUpdateDuplicate( IrpContext,
  1471. Fcb,
  1472. &LcbForUpdate,
  1473. &ParentScb,
  1474. TRUE );
  1475. }
  1476. //
  1477. // We have two cases:
  1478. //
  1479. // Resident: We are looking for the case where the
  1480. // valid data length is less than the file size.
  1481. // In this case we shrink the attribute.
  1482. //
  1483. // NonResident: We are looking for unused clusters
  1484. // past the end of the file.
  1485. //
  1486. // We skip the following if we had any previous errors.
  1487. //
  1488. if (!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT )) {
  1489. //
  1490. // We don't need to truncate if the file size is 0.
  1491. //
  1492. if (Scb->Header.AllocationSize.QuadPart != 0) {
  1493. VCN StartingCluster;
  1494. VCN EndingCluster;
  1495. LONGLONG AllocationSize;
  1496. //
  1497. // **** Do we need to give up the Vcb for this
  1498. // call.
  1499. //
  1500. StartingCluster = LlClustersFromBytes( Vcb, Scb->Header.FileSize.QuadPart );
  1501. EndingCluster = LlClustersFromBytes( Vcb, Scb->Header.AllocationSize.QuadPart );
  1502. AllocationSize = Scb->Header.AllocationSize.QuadPart;
  1503. //
  1504. // If there are clusters to delete, we do so now.
  1505. //
  1506. if (EndingCluster != StartingCluster) {
  1507. try {
  1508. NtfsDeleteAllocation( IrpContext,
  1509. FileObject,
  1510. Scb,
  1511. StartingCluster,
  1512. MAXLONGLONG,
  1513. TRUE,
  1514. TRUE );
  1515. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1516. NOTHING;
  1517. }
  1518. }
  1519. LocalTruncateSize = Scb->Header.FileSize.QuadPart;
  1520. TruncateSize = &LocalTruncateSize;
  1521. }
  1522. //
  1523. // This is the resident case.
  1524. //
  1525. } else {
  1526. //
  1527. // Check if the file size length is less than
  1528. // the allocated size.
  1529. //
  1530. if (QuadAlign( Scb->Header.FileSize.LowPart ) < Scb->Header.AllocationSize.LowPart) {
  1531. //
  1532. // We need to lookup the attribute and change
  1533. // the attribute value. We can point to
  1534. // the attribute itself as the changing
  1535. // value.
  1536. //
  1537. if (CleanupAttrContext) {
  1538. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1539. }
  1540. NtfsInitializeAttributeContext( &AttrContext );
  1541. CleanupAttrContext = TRUE;
  1542. try {
  1543. NtfsLookupAttributeForScb( IrpContext, Scb, NULL, &AttrContext );
  1544. NtfsChangeAttributeValue( IrpContext,
  1545. Fcb,
  1546. Scb->Header.FileSize.LowPart,
  1547. NULL,
  1548. 0,
  1549. TRUE,
  1550. TRUE,
  1551. FALSE,
  1552. FALSE,
  1553. &AttrContext );
  1554. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1555. NOTHING;
  1556. }
  1557. //
  1558. // Remember the smaller allocation size
  1559. //
  1560. Scb->Header.AllocationSize.LowPart = QuadAlign( Scb->Header.FileSize.LowPart );
  1561. Scb->TotalAllocated = Scb->Header.AllocationSize.QuadPart;
  1562. }
  1563. }
  1564. NtfsUpdateScbFromFileObject( IrpContext, FileObject, Scb, TRUE );
  1565. }
  1566. }
  1567. }
  1568. }
  1569. //
  1570. // If this was the last cached open, and there are open
  1571. // non-cached handles, attempt a flush and purge operation
  1572. // to avoid cache coherency overhead from these non-cached
  1573. // handles later. We ignore any I/O errors from the flush
  1574. // except for CANT_WAIT and LOG_FILE_FULL.
  1575. //
  1576. if ((Scb->NonCachedCleanupCount != 0) &&
  1577. (Scb->CleanupCount == (Scb->NonCachedCleanupCount + 1)) &&
  1578. (Scb->CompressionUnit == 0) &&
  1579. (Scb->NonpagedScb->SegmentObject.ImageSectionObject == NULL) &&
  1580. !FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) &&
  1581. (Status == STATUS_SUCCESS) &&
  1582. MmCanFileBeTruncated( &Scb->NonpagedScb->SegmentObject, NULL ) &&
  1583. !FlagOn( Scb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
  1584. //
  1585. // FlushAndPurge may release the parent. Go ahead and explicitly
  1586. // release it. Otherwise we may overrelease it later before uninitializing
  1587. // the cache map. At the same time release the quota control if necc. via
  1588. // release shared resources
  1589. //
  1590. try {
  1591. NtfsCheckpointCurrentTransaction( IrpContext );
  1592. FcbStateClearFlags = 0;
  1593. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1594. NOTHING;
  1595. }
  1596. if (AcquiredParentScb) {
  1597. NtfsReleaseScb( IrpContext, ParentScb );
  1598. AcquiredParentScb = FALSE;
  1599. }
  1600. if (IrpContext->SharedScbSize > 0) {
  1601. NtfsReleaseSharedResources( IrpContext );
  1602. }
  1603. //
  1604. // Flush and purge the stream.
  1605. //
  1606. try {
  1607. NtfsFlushAndPurgeScb( IrpContext,
  1608. Scb,
  1609. NULL );
  1610. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1611. NOTHING;
  1612. }
  1613. //
  1614. // Ignore any errors in this path.
  1615. //
  1616. IrpContext->ExceptionStatus = STATUS_SUCCESS;
  1617. }
  1618. if ((Fcb->CloseCount == 1) &&
  1619. AddToDelayQueue &&
  1620. (NtfsData.DelayedCloseCount <= NtfsMaxDelayedCloseCount) &&
  1621. (Status == STATUS_SUCCESS) &&
  1622. !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
  1623. NtfsAcquireFsrtlHeader( Scb );
  1624. SetFlag( Scb->ScbState, SCB_STATE_DELAY_CLOSE );
  1625. NtfsReleaseFsrtlHeader( Scb );
  1626. }
  1627. }
  1628. //
  1629. // If the Fcb is bad, we will truncate the cache to size zero.
  1630. //
  1631. } else {
  1632. //
  1633. // Now set the FO_CLEANUP_COMPLETE flag.
  1634. //
  1635. SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
  1636. TruncateSize = (PLONGLONG)&Li0;
  1637. }
  1638. break;
  1639. default :
  1640. NtfsBugCheck( TypeOfOpen, 0, 0 );
  1641. }
  1642. //
  1643. // If any of the Fcb Info flags are set we call the routine
  1644. // to update the duplicated information in the parent directories.
  1645. // We need to check here in case none of the flags are set but
  1646. // we want to update last access time.
  1647. //
  1648. if (Fcb->Info.LastAccessTime != Fcb->CurrentLastAccess) {
  1649. ASSERT( TypeOfOpen != UserVolumeOpen );
  1650. if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO )) {
  1651. ASSERT( !FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ));
  1652. Fcb->Info.LastAccessTime = Fcb->CurrentLastAccess;
  1653. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
  1654. } else if (!FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
  1655. if (NtfsCheckLastAccess( IrpContext, Fcb )) {
  1656. SetFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  1657. SetFlag( Fcb->InfoFlags, FCB_INFO_CHANGED_LAST_ACCESS );
  1658. }
  1659. }
  1660. }
  1661. if (VolumeMounted && !VolumeMountedReadOnly) {
  1662. //
  1663. // We check if we have the standard information attribute.
  1664. // We can only update attributes on mounted volumes.
  1665. //
  1666. if (FlagOn( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO ) &&
  1667. (Status == STATUS_SUCCESS)) {
  1668. ASSERT( !FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ));
  1669. ASSERT( TypeOfOpen != UserVolumeOpen );
  1670. try {
  1671. NtfsUpdateStandardInformation( IrpContext, Fcb );
  1672. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1673. NOTHING;
  1674. }
  1675. }
  1676. //
  1677. // Now update the duplicate information as well for volumes that are still mounted.
  1678. //
  1679. if ((FlagOn( Fcb->InfoFlags, FCB_INFO_DUPLICATE_FLAGS ) ||
  1680. ((LcbForUpdate != NULL) &&
  1681. FlagOn( LcbForUpdate->InfoFlags, FCB_INFO_DUPLICATE_FLAGS ))) &&
  1682. (Status == STATUS_SUCCESS) &&
  1683. (!FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE))) {
  1684. ASSERT( TypeOfOpen != UserVolumeOpen );
  1685. ASSERT( !FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ));
  1686. NtfsPrepareForUpdateDuplicate( IrpContext, Fcb, &LcbForUpdate, &ParentScb, TRUE );
  1687. //
  1688. // Now update the duplicate info.
  1689. //
  1690. try {
  1691. NtfsUpdateDuplicateInfo( IrpContext, Fcb, LcbForUpdate, ParentScb );
  1692. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1693. NOTHING;
  1694. }
  1695. UpdateDuplicateInfo = TRUE;
  1696. }
  1697. //
  1698. // If we have modified the Info structure or security, we report this
  1699. // to the dir-notify package (except for OpenById cases).
  1700. //
  1701. if (!OpenById && (Status == STATUS_SUCCESS)) {
  1702. ULONG FilterMatch;
  1703. //
  1704. // Check whether we need to report on file changes.
  1705. //
  1706. if ((Vcb->NotifyCount != 0) &&
  1707. (UpdateDuplicateInfo || FlagOn( Fcb->InfoFlags, FCB_INFO_MODIFIED_SECURITY ))) {
  1708. ASSERT( TypeOfOpen != UserVolumeOpen );
  1709. //
  1710. // We map the Fcb info flags into the dir notify flags.
  1711. //
  1712. FilterMatch = NtfsBuildDirNotifyFilter( IrpContext,
  1713. (Fcb->InfoFlags |
  1714. (LcbForUpdate ? LcbForUpdate->InfoFlags : 0) ));
  1715. //
  1716. // If the filter match is non-zero, that means we also need to do a
  1717. // dir notify call.
  1718. //
  1719. if (FilterMatch != 0) {
  1720. ASSERT( TypeOfOpen != UserVolumeOpen );
  1721. ASSERT( !FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ));
  1722. NtfsReportDirNotify( IrpContext,
  1723. Vcb,
  1724. &Ccb->FullFileName,
  1725. Ccb->LastFileNameOffset,
  1726. NULL,
  1727. ((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
  1728. (Ccb->Lcb != NULL) &&
  1729. (Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Length != 0)) ?
  1730. &Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
  1731. NULL),
  1732. FilterMatch,
  1733. FILE_ACTION_MODIFIED,
  1734. ParentFcb );
  1735. }
  1736. }
  1737. ClearFlag( Fcb->InfoFlags, FCB_INFO_MODIFIED_SECURITY );
  1738. //
  1739. // If this is a named stream with changes then report them as well.
  1740. //
  1741. if ((Scb->AttributeName.Length != 0) &&
  1742. NtfsIsTypeCodeUserData( Scb->AttributeTypeCode )) {
  1743. if ((Vcb->NotifyCount != 0) &&
  1744. FlagOn( Scb->ScbState,
  1745. SCB_STATE_NOTIFY_REMOVE_STREAM |
  1746. SCB_STATE_NOTIFY_RESIZE_STREAM |
  1747. SCB_STATE_NOTIFY_MODIFY_STREAM )) {
  1748. ULONG Action;
  1749. FilterMatch = 0;
  1750. //
  1751. // Start by checking for a delete.
  1752. //
  1753. if (FlagOn( Scb->ScbState, SCB_STATE_NOTIFY_REMOVE_STREAM )) {
  1754. FilterMatch = FILE_NOTIFY_CHANGE_STREAM_NAME;
  1755. Action = FILE_ACTION_REMOVED_STREAM;
  1756. } else {
  1757. //
  1758. // Check if the file size changed.
  1759. //
  1760. if (FlagOn( Scb->ScbState, SCB_STATE_NOTIFY_RESIZE_STREAM )) {
  1761. FilterMatch = FILE_NOTIFY_CHANGE_STREAM_SIZE;
  1762. }
  1763. //
  1764. // Now check if the stream data was modified.
  1765. //
  1766. if (FlagOn( Scb->ScbState, SCB_STATE_NOTIFY_MODIFY_STREAM )) {
  1767. SetFlag( FilterMatch, FILE_NOTIFY_CHANGE_STREAM_WRITE );
  1768. }
  1769. Action = FILE_ACTION_MODIFIED_STREAM;
  1770. }
  1771. NtfsReportDirNotify( IrpContext,
  1772. Vcb,
  1773. &Ccb->FullFileName,
  1774. Ccb->LastFileNameOffset,
  1775. &Scb->AttributeName,
  1776. ((FlagOn( Ccb->Flags, CCB_FLAG_PARENT_HAS_DOS_COMPONENT ) &&
  1777. (Ccb->Lcb != NULL) &&
  1778. (Ccb->Lcb->Scb->ScbType.Index.NormalizedName.Length != 0)) ?
  1779. &Ccb->Lcb->Scb->ScbType.Index.NormalizedName :
  1780. NULL),
  1781. FilterMatch,
  1782. Action,
  1783. ParentFcb );
  1784. }
  1785. ClearFlag( Scb->ScbState,
  1786. SCB_STATE_NOTIFY_ADD_STREAM |
  1787. SCB_STATE_NOTIFY_REMOVE_STREAM |
  1788. SCB_STATE_NOTIFY_RESIZE_STREAM |
  1789. SCB_STATE_NOTIFY_MODIFY_STREAM );
  1790. }
  1791. }
  1792. if (UpdateDuplicateInfo) {
  1793. NtfsUpdateLcbDuplicateInfo( Fcb, LcbForUpdate );
  1794. Fcb->InfoFlags = 0;
  1795. }
  1796. }
  1797. //
  1798. // Always clear the update standard information flag.
  1799. //
  1800. ClearFlag( Fcb->FcbState, FCB_STATE_UPDATE_STD_INFO );
  1801. //
  1802. // Let's give up the parent Fcb if we have acquired it. This will
  1803. // prevent deadlocks in any uninitialize code below.
  1804. //
  1805. if (AcquiredParentScb) {
  1806. NtfsReleaseScb( IrpContext, ParentScb );
  1807. AcquiredParentScb = FALSE;
  1808. }
  1809. //
  1810. // Uninitialize the cache map if this file has been cached or we are
  1811. // trying to delete.
  1812. //
  1813. if ((FileObject->PrivateCacheMap != NULL) || (TruncateSize != NULL)) {
  1814. CcUninitializeCacheMap( FileObject, (PLARGE_INTEGER)TruncateSize, NULL );
  1815. }
  1816. //
  1817. // Check that the non-cached handle count is consistent.
  1818. //
  1819. ASSERT( !FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) ||
  1820. (Scb->NonCachedCleanupCount != 0 ));
  1821. if (CleanupAttrContext) {
  1822. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1823. CleanupAttrContext = FALSE;
  1824. }
  1825. //
  1826. // On final cleanup, post the close to the Usn Journal, if other changes have been
  1827. // posted.
  1828. //
  1829. if ((Fcb->CleanupCount == 1) &&
  1830. (Fcb->FcbUsnRecord != NULL) &&
  1831. ((Fcb->FcbUsnRecord->UsnRecord.Reason != 0) ||
  1832. ((IrpContext->Usn.CurrentUsnFcb != NULL) && (IrpContext->Usn.NewReasons != 0)))) {
  1833. PSCB TempScb;
  1834. //
  1835. // Leave if there are any streams with user-mapped files.
  1836. //
  1837. TempScb = (PSCB)CONTAINING_RECORD( Fcb->ScbQueue.Flink,
  1838. SCB,
  1839. FcbLinks );
  1840. while (&TempScb->FcbLinks != &Fcb->ScbQueue) {
  1841. if (FlagOn(TempScb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE)) {
  1842. goto NoPost;
  1843. }
  1844. TempScb = (PSCB)CONTAINING_RECORD( TempScb->FcbLinks.Flink,
  1845. SCB,
  1846. FcbLinks );
  1847. }
  1848. //
  1849. // Now try to actually post the change.
  1850. //
  1851. NtfsPostUsnChange( IrpContext, Fcb, USN_REASON_CLOSE );
  1852. //
  1853. // Escape here if we are not posting the close due to a user-mapped file.
  1854. //
  1855. NoPost: NOTHING;
  1856. }
  1857. //
  1858. // Now, if anything at all is posted to the Usn Journal, we must write it now
  1859. // so that we do not get a log file full later.
  1860. //
  1861. ASSERT( IrpContext->Usn.NextUsnFcb == NULL );
  1862. if ((IrpContext->Usn.CurrentUsnFcb != NULL) &&
  1863. (Status == STATUS_SUCCESS)) {
  1864. //
  1865. // Now write the journal, checkpoint the transaction, and free the UsnJournal to
  1866. // reduce contention.
  1867. //
  1868. try {
  1869. NtfsWriteUsnJournalChanges( IrpContext );
  1870. NtfsCheckpointCurrentTransaction( IrpContext );
  1871. FcbStateClearFlags = 0;
  1872. } except( NtfsCleanupExceptionFilter( IrpContext, GetExceptionInformation(), &Status )) {
  1873. NOTHING;
  1874. }
  1875. }
  1876. //
  1877. // Make sure the TRUNCATE and ATTRIBUTE_SIZE flags in the Scb are cleared.
  1878. //
  1879. if (FlagOn( Scb->ScbState,
  1880. SCB_STATE_CHECK_ATTRIBUTE_SIZE | SCB_STATE_TRUNCATE_ON_CLOSE ) &&
  1881. (Scb->CleanupCount == 1)) {
  1882. NtfsAcquireFsrtlHeader( Scb );
  1883. ClearFlag( Scb->ScbState,
  1884. SCB_STATE_CHECK_ATTRIBUTE_SIZE | SCB_STATE_TRUNCATE_ON_CLOSE );
  1885. NtfsReleaseFsrtlHeader( Scb );
  1886. }
  1887. //
  1888. // Now decrement the cleanup counts.
  1889. //
  1890. // NOTE - DO NOT ADD CODE AFTER THIS POINT THAT CAN CAUSE A RETRY.
  1891. //
  1892. NtfsDecrementCleanupCounts( Scb,
  1893. LcbForCounts,
  1894. BooleanFlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ));
  1895. #ifdef BRIANDBG
  1896. DecrementedCleanupCount = TRUE;
  1897. #endif
  1898. //
  1899. // We remove the share access from the Scb.
  1900. //
  1901. IoRemoveShareAccess( FileObject, &Scb->ShareAccess );
  1902. //
  1903. // Modify the delete counts in the Fcb.
  1904. //
  1905. if (FlagOn( Ccb->Flags, CCB_FLAG_DELETE_FILE )) {
  1906. Fcb->FcbDeleteFile -= 1;
  1907. ClearFlag( Ccb->Flags, CCB_FLAG_DELETE_FILE );
  1908. }
  1909. if (FlagOn( Ccb->Flags, CCB_FLAG_DENY_DELETE )) {
  1910. Fcb->FcbDenyDelete -= 1;
  1911. ClearFlag( Ccb->Flags, CCB_FLAG_DENY_DELETE );
  1912. }
  1913. if (FlagOn( Ccb->Flags, CCB_FLAG_DENY_DEFRAG)) {
  1914. ClearFlag( Ccb->Flags, CCB_FLAG_DENY_DEFRAG );
  1915. ClearFlag( Scb->ScbPersist, SCB_PERSIST_DENY_DEFRAG );
  1916. }
  1917. //
  1918. // Since this request has completed we can adjust the total link count
  1919. // in the Fcb.
  1920. //
  1921. Fcb->TotalLinks -= TotalLinkAdj;
  1922. //
  1923. // Release the quota control block. This does not have to be done
  1924. // here however, it allows us to free up the quota control block
  1925. // before the fcb is removed from the table. This keeps the assert
  1926. // about quota table empty from triggering in
  1927. // NtfsClearAndVerifyQuotaIndex.
  1928. //
  1929. if (NtfsPerformQuotaOperation(Fcb) &&
  1930. FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )) {
  1931. NtfsDereferenceQuotaControlBlock( Vcb,
  1932. &Fcb->QuotaControl );
  1933. }
  1934. //
  1935. // If we hit some failure in modifying the disk then make sure to roll back
  1936. // all of the changes.
  1937. //
  1938. if (Status != STATUS_SUCCESS) {
  1939. NtfsRaiseStatus( IrpContext, STATUS_SUCCESS, NULL, NULL );
  1940. }
  1941. FcbStateClearFlags = 0;
  1942. } finally {
  1943. DebugUnwind( NtfsCommonCleanup );
  1944. //
  1945. // Clear any FcbState flags we want to unwind.
  1946. //
  1947. ClearFlag( Fcb->FcbState, FcbStateClearFlags );
  1948. //
  1949. // Remove this fcb from the Fcb table if neccessary. We delay this for
  1950. // synchronization with usn delete worker. By finishing the delete now we're
  1951. // guarranteed our usn work occured safely
  1952. //
  1953. if (DeleteFromFcbTable && FlagOn( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE )) {
  1954. NtfsAcquireFcbTable( IrpContext, Vcb );
  1955. NtfsDeleteFcbTableEntry( Fcb->Vcb, Fcb->FileReference );
  1956. NtfsReleaseFcbTable( IrpContext, Vcb );
  1957. ClearFlag( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE );
  1958. }
  1959. //
  1960. // We clear the file object pointer in the Ccb.
  1961. // This prevents us from trying to access this in a
  1962. // rename operation.
  1963. //
  1964. SetFlag( Ccb->Flags, CCB_FLAG_CLEANUP );
  1965. //
  1966. // Release any resources held.
  1967. //
  1968. if (AcquiredObjectID) {
  1969. NtfsReleaseScb( IrpContext, Vcb->ObjectIdTableScb );
  1970. }
  1971. NtfsReleaseVcb( IrpContext, Vcb );
  1972. if (AcquiredCheckpoint) {
  1973. NtfsReleaseCheckpointSynchronization( IrpContext, Vcb );
  1974. AcquiredCheckpoint = FALSE;
  1975. }
  1976. if (CleanupAttrContext) {
  1977. NtfsCleanupAttributeContext( IrpContext, &AttrContext );
  1978. }
  1979. if (NamePair.Long.Buffer != NamePair.LongBuffer) {
  1980. NtfsFreePool(NamePair.Long.Buffer);
  1981. }
  1982. if (DecrementScb) {
  1983. InterlockedDecrement( &ImageScb->CloseCount );
  1984. }
  1985. //
  1986. // If we just cleaned up a volume handle and in so doing unlocked the
  1987. // volume, notify anyone who might be interested. We can't do this
  1988. // until we've released all our resources, since there's no telling
  1989. // what services might do when they get notified, and we don't want
  1990. // to introduce potential deadlocks.
  1991. //
  1992. if (UnlockedVolume) {
  1993. FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_UNLOCK );
  1994. }
  1995. ASSERT( !AbnormalTermination() ||
  1996. !DeleteFile ||
  1997. FlagOn( Fcb->FcbState, FCB_STATE_IN_FCB_TABLE ) ||
  1998. (IrpContext->TransactionId == 0) );
  1999. //
  2000. // After a file is deleted and committed no more usn reasons should be left
  2001. // whether or not the journal is active
  2002. //
  2003. ASSERT( AbnormalTermination() ||
  2004. !FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ) ||
  2005. (Fcb->FcbUsnRecord == NULL) ||
  2006. (Fcb->FcbUsnRecord->UsnRecord.Reason == 0) );
  2007. //
  2008. // And return to our caller
  2009. //
  2010. DebugTrace( -1, Dbg, ("NtfsCommonCleanup -> %08lx\n", Status) );
  2011. }
  2012. #ifdef BRIANDBG
  2013. ASSERT( DecrementedCleanupCount );
  2014. #endif
  2015. NtfsCompleteRequest( IrpContext, Irp, Status );
  2016. return Status;
  2017. }
  2018. VOID
  2019. NtfsTrimNormalizedNames (
  2020. IN PIRP_CONTEXT IrpContext,
  2021. IN PFCB Fcb,
  2022. IN PSCB ParentScb
  2023. )
  2024. /*++
  2025. Routine Description:
  2026. Walk up the FCB/SCB chain removing all normalized names longer than
  2027. the threshold size in inactive directories
  2028. Try to remove parent dir normalized name if its long and if no handles active (only this 1 left)
  2029. and no lcbs are active - all notifies farther down in function (NtfsCommonCleanup)
  2030. use parent Scb's normalized name. If we don't remove it here
  2031. this always goes away during a close
  2032. Arguments:
  2033. IrpContext --
  2034. Fcb -- The fcb of the starting node should already be acquired
  2035. ParentScb - The scb of the parent of the current node
  2036. Return Value:
  2037. none
  2038. --*/
  2039. {
  2040. BOOLEAN DirectoryActive = FALSE;
  2041. PFCB CurrentFcb;
  2042. PSCB CurrentParentScb;
  2043. PLCB ChildLcb;
  2044. PLCB ParentLcb;
  2045. PLIST_ENTRY Links;
  2046. PAGED_CODE()
  2047. //
  2048. // We may be occuring during a transaction so be careful to not acquire resources
  2049. // for the life of the transaction while traversing the tree
  2050. //
  2051. CurrentFcb = Fcb;
  2052. CurrentParentScb = ParentScb;
  2053. NtfsAcquireResourceExclusive( IrpContext, CurrentParentScb, TRUE );
  2054. while ((CurrentParentScb->ScbType.Index.NormalizedName.MaximumLength > LONGNAME_THRESHOLD) &&
  2055. (CurrentParentScb->CleanupCount == 0)) {
  2056. ASSERT( (CurrentParentScb->Header.NodeTypeCode == NTFS_NTC_SCB_INDEX) ||
  2057. (CurrentParentScb->Header.NodeTypeCode == NTFS_NTC_SCB_ROOT_INDEX) );
  2058. //
  2059. // Determine that directory has no active links or handles
  2060. //
  2061. for (Links = CurrentParentScb->ScbType.Index.LcbQueue.Flink;
  2062. Links != &(CurrentParentScb->ScbType.Index.LcbQueue);
  2063. Links = Links->Flink) {
  2064. ChildLcb = CONTAINING_RECORD( Links, LCB, ScbLinks );
  2065. //
  2066. // We know the Fcb we were called with has a single cleanup count left.
  2067. //
  2068. if ((ChildLcb->CleanupCount != 0) &&
  2069. (ChildLcb->Fcb != CurrentFcb)) {
  2070. DirectoryActive = TRUE;
  2071. break;
  2072. }
  2073. }
  2074. if (!DirectoryActive) {
  2075. //
  2076. // Now acquire and free name in SCB
  2077. //
  2078. NtfsDeleteNormalizedName( CurrentParentScb );
  2079. //
  2080. // Move up to next level
  2081. //
  2082. if (CurrentFcb != Fcb) {
  2083. NtfsReleaseResource( IrpContext, CurrentFcb );
  2084. }
  2085. CurrentFcb = CurrentParentScb->Fcb;
  2086. if (CurrentFcb->CleanupCount != 0) {
  2087. //
  2088. // Setting equal to FCB just means don't release it when we exit below
  2089. //
  2090. CurrentFcb = Fcb;
  2091. break;
  2092. }
  2093. if (!(IsListEmpty( &(CurrentFcb->LcbQueue) ))) {
  2094. ParentLcb = CONTAINING_RECORD( CurrentFcb->LcbQueue.Flink,
  2095. LCB,
  2096. FcbLinks );
  2097. CurrentParentScb = ParentLcb->Scb;
  2098. if (CurrentParentScb != NULL) {
  2099. NtfsAcquireResourceExclusive( IrpContext, CurrentParentScb, TRUE );
  2100. } else {
  2101. break;
  2102. }
  2103. } else {
  2104. CurrentParentScb = NULL;
  2105. break;
  2106. }
  2107. } else {
  2108. break;
  2109. } // endif directory active
  2110. } // endwhile longname in currentparentscb
  2111. //
  2112. // Release last node if it isn't the starting one
  2113. //
  2114. if (CurrentFcb != Fcb) {
  2115. ASSERT( CurrentFcb != NULL );
  2116. NtfsReleaseResource( IrpContext, CurrentFcb );
  2117. }
  2118. if (CurrentParentScb != NULL) {
  2119. NtfsReleaseResource( IrpContext, CurrentParentScb );
  2120. }
  2121. return;
  2122. } // NtfsTrimNormalizedNames
  2123. //
  2124. // Local support routine
  2125. //
  2126. LONG
  2127. NtfsCleanupExceptionFilter (
  2128. IN PIRP_CONTEXT IrpContext,
  2129. IN PEXCEPTION_POINTERS ExceptionPointer,
  2130. OUT PNTSTATUS Status
  2131. )
  2132. /*++
  2133. Routine Description:
  2134. Exception filter for errors during cleanup. We want to raise if this is
  2135. a retryable condition or fatal error, plow on as best we can if not.
  2136. Arguments:
  2137. IrpContext - IrpContext
  2138. ExceptionPointer - Pointer to the exception context.
  2139. Status - Address to store the error status.
  2140. Return Value:
  2141. Exception status - EXCEPTION_CONTINUE_SEARCH if we want to raise to another handler,
  2142. EXCEPTION_EXECUTE_HANDLER if we plan to proceed on.
  2143. --*/
  2144. {
  2145. *Status = ExceptionPointer->ExceptionRecord->ExceptionCode;
  2146. //
  2147. // For now break if we catch corruption errors on both free and checked
  2148. // TODO: Remove this before we ship
  2149. //
  2150. if (NtfsBreakOnCorrupt &&
  2151. ((*Status == STATUS_FILE_CORRUPT_ERROR) ||
  2152. (*Status == STATUS_DISK_CORRUPT_ERROR))) {
  2153. if (*KdDebuggerEnabled) {
  2154. DbgPrint("*******************************************\n");
  2155. DbgPrint("NTFS detected corruption on your volume\n");
  2156. DbgPrint("IrpContext=0x%08x, VCB=0x%08x\n",IrpContext,IrpContext->Vcb);
  2157. DbgPrint("Send email to NTFSDEV\n");
  2158. DbgPrint("*******************************************\n");
  2159. DbgBreakPoint();
  2160. }
  2161. }
  2162. // ASSERT( *Status != STATUS_FILE_CORRUPT_ERROR );
  2163. if ((*Status == STATUS_LOG_FILE_FULL) ||
  2164. (*Status == STATUS_CANT_WAIT) ||
  2165. !FsRtlIsNtstatusExpected( *Status )) {
  2166. return EXCEPTION_CONTINUE_SEARCH;
  2167. }
  2168. NtfsMinimumExceptionProcessing( IrpContext );
  2169. #ifdef BRIANDBG
  2170. #ifndef LFS_CLUSTER_CHECK
  2171. //
  2172. // Some errors are acceptable in this path.
  2173. //
  2174. if (*Status == STATUS_DISK_FULL) {
  2175. NtfsCleanupDiskFull += 1;
  2176. } else if (*Status == STATUS_INSUFFICIENT_RESOURCES) {
  2177. NtfsCleanupNoPool += 1;
  2178. } else {
  2179. //
  2180. // Cluster systems can hit inpage errors here because of DEVICE_OFFLINE
  2181. // for log I/O.
  2182. //
  2183. ASSERT( FALSE );
  2184. }
  2185. #endif
  2186. #endif
  2187. return EXCEPTION_EXECUTE_HANDLER;
  2188. }
  2189. #ifdef BRIANDBG
  2190. LONG
  2191. NtfsFsdCleanupExceptionFilter (
  2192. IN PIRP_CONTEXT IrpContext OPTIONAL,
  2193. IN PEXCEPTION_POINTERS ExceptionPointer
  2194. )
  2195. {
  2196. NTSTATUS ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
  2197. ASSERT( NT_SUCCESS( ExceptionCode ) ||
  2198. (ExceptionCode == STATUS_CANT_WAIT) ||
  2199. (ExceptionCode == STATUS_LOG_FILE_FULL) );
  2200. return NtfsExceptionFilter( IrpContext, ExceptionPointer );
  2201. }
  2202. #endif