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.

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