1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     Cleanup.c
8 
9 Abstract:
10 
11     This module implements the File Cleanup routine for Fat called by the
12     dispatch driver.
13 
14 
15 --*/
16 
17 #include "fatprocs.h"
18 
19 //
20 //  The Bug check file id for this module
21 //
22 
23 #define BugCheckFileId                   (FAT_BUG_CHECK_CLEANUP)
24 
25 //
26 //  The local debug trace level
27 //
28 
29 #define Dbg                              (DEBUG_TRACE_CLEANUP)
30 
31 //
32 //  The following little routine exists solely because it need a spin lock.
33 //
34 
35 VOID
36 FatAutoUnlock (
37     IN PIRP_CONTEXT IrpContext,
38     IN PVCB Vcb
39     );
40 
41 #ifdef ALLOC_PRAGMA
42 #pragma alloc_text(PAGE, FatCommonCleanup)
43 #pragma alloc_text(PAGE, FatFsdCleanup)
44 #endif
45 
46 
47 _Function_class_(IRP_MJ_CLEANUP)
48 _Function_class_(DRIVER_DISPATCH)
49 NTSTATUS
50 NTAPI
51 FatFsdCleanup (
52     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
53     _Inout_ PIRP Irp
54     )
55 
56 /*++
57 
58 Routine Description:
59 
60     This routine implements the FSD part of closing down a handle to a
61     file object.
62 
63 Arguments:
64 
65     VolumeDeviceObject - Supplies the volume device object where the
66         file being Cleanup exists
67 
68     Irp - Supplies the Irp being processed
69 
70 Return Value:
71 
72     NTSTATUS - The FSD status for the IRP
73 
74 --*/
75 
76 {
77     NTSTATUS Status;
78     PIRP_CONTEXT IrpContext = NULL;
79 
80     BOOLEAN TopLevel;
81 
82     PAGED_CODE();
83 
84     //
85     //  If we were called with our file system device object instead of a
86     //  volume device object, just complete this request with STATUS_SUCCESS
87     //
88 
89     if ( FatDeviceIsFatFsdo( VolumeDeviceObject))  {
90 
91         Irp->IoStatus.Status = STATUS_SUCCESS;
92         Irp->IoStatus.Information = FILE_OPENED;
93 
94         IoCompleteRequest( Irp, IO_DISK_INCREMENT );
95 
96         return STATUS_SUCCESS;
97     }
98 
99     DebugTrace(+1, Dbg, "FatFsdCleanup\n", 0);
100 
101     //
102     //  Call the common Cleanup routine, with blocking allowed.
103     //
104 
105     FsRtlEnterFileSystem();
106 
107     TopLevel = FatIsIrpTopLevel( Irp );
108 
109     _SEH2_TRY {
110 
111         IrpContext = FatCreateIrpContext( Irp, TRUE );
112 
113         Status = FatCommonCleanup( IrpContext, Irp );
114 
115     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
116 
117         //
118         //  We had some trouble trying to perform the requested
119         //  operation, so we'll abort the I/O request with
120         //  the error status that we get back from the
121         //  execption code
122         //
123 
124         Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
125     } _SEH2_END;
126 
127     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
128 
129     FsRtlExitFileSystem();
130 
131     //
132     //  And return to our caller
133     //
134 
135     DebugTrace(-1, Dbg, "FatFsdCleanup -> %08lx\n", Status);
136 
137     UNREFERENCED_PARAMETER( VolumeDeviceObject );
138 
139     return Status;
140 }
141 
142 
143 _Requires_lock_held_(_Global_critical_region_)
144 NTSTATUS
145 FatCommonCleanup (
146     IN PIRP_CONTEXT IrpContext,
147     IN PIRP Irp
148     )
149 
150 /*++
151 
152 Routine Description:
153 
154     This is the common routine for cleanup of a file/directory called by both
155     the fsd and fsp threads.
156 
157     Cleanup is invoked whenever the last handle to a file object is closed.
158     This is different than the Close operation which is invoked when the last
159     reference to a file object is deleted.
160 
161     The function of cleanup is to essentially "cleanup" the file/directory
162     after a user is done with it.  The Fcb/Dcb remains around (because MM
163     still has the file object referenced) but is now available for another
164     user to open (i.e., as far as the user is concerned the is now closed).
165 
166     See close for a more complete description of what close does.
167 
168 Arguments:
169 
170     Irp - Supplies the Irp to process
171 
172 Return Value:
173 
174     NTSTATUS - The return status for the operation
175 
176 --*/
177 
178 {
179     NTSTATUS Status = STATUS_SUCCESS;
180 
181     PIO_STACK_LOCATION IrpSp;
182 
183     PFILE_OBJECT FileObject;
184 
185     TYPE_OF_OPEN TypeOfOpen;
186     PVCB Vcb;
187     PFCB Fcb;
188     PCCB Ccb;
189 
190     BOOLEAN SendUnlockNotification = FALSE;
191 
192     PSHARE_ACCESS ShareAccess;
193 
194     PLARGE_INTEGER TruncateSize = NULL;
195     LARGE_INTEGER LocalTruncateSize;
196 
197     BOOLEAN AcquiredVcb = FALSE;
198     BOOLEAN AcquiredFcb = FALSE;
199 
200 #if (NTDDI_VERSION >= NTDDI_WIN8)
201     BOOLEAN ProcessingDeleteOnClose = FALSE;
202 #endif
203 
204     PAGED_CODE();
205 
206     IrpSp = IoGetCurrentIrpStackLocation( Irp );
207 
208     DebugTrace(+1, Dbg, "FatCommonCleanup\n", 0);
209     DebugTrace( 0, Dbg, "Irp           = %p\n", Irp);
210     DebugTrace( 0, Dbg, "->FileObject  = %p\n", IrpSp->FileObject);
211 
212     //
213     //  Extract and decode the file object
214     //
215 
216     FileObject = IrpSp->FileObject;
217     TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
218 
219     //
220     //  Special case the unopened file object.  This will occur only when
221     //  we are initializing Vcb and IoCreateStreamFileObject is being
222     //  called.
223     //
224 
225     if (TypeOfOpen == UnopenedFileObject) {
226 
227         DebugTrace(0, Dbg, "Unopened File Object\n", 0);
228 
229         FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
230 
231         DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0);
232         return STATUS_SUCCESS;
233     }
234 
235     //
236     //  If this is not our first time through (for whatever reason)
237     //  only see if we have to flush the file.
238     //
239 
240     if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) {
241 
242         if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) &&
243             FlagOn(FileObject->Flags, FO_FILE_MODIFIED) &&
244             !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) &&
245             (TypeOfOpen == UserFileOpen)) {
246 
247             //
248             //  Flush the file.
249             //
250 
251             Status = FatFlushFile( IrpContext, Fcb, Flush );
252 
253             if (!NT_SUCCESS(Status)) {
254 
255                 FatNormalizeAndRaiseStatus( IrpContext, Status );
256             }
257         }
258 
259         FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
260 
261         DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0);
262         return STATUS_SUCCESS;
263     }
264 
265     //
266     //  If we call change the allocation or call CcUninitialize,
267     //  we have to take the Fcb exclusive
268     //
269 
270     if ((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) {
271 
272         NT_ASSERT( Fcb != NULL );
273 
274         (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
275 
276         AcquiredFcb = TRUE;
277 
278         //
279         //  Do a check here if this was a DELETE_ON_CLOSE FileObject, and
280         //  set the Fcb flag appropriately.
281         //
282 
283         if (FlagOn(Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE)) {
284 
285             NT_ASSERT( NodeType(Fcb) != FAT_NTC_ROOT_DCB );
286 
287             //
288             //  Transfer the delete-on-close state to the FCB.  We do this rather
289             //  than leave the CCB_FLAG_DELETE_ON_CLOSE flag set so that if we
290             //  end up breaking an oplock and come in again we won't try to break
291             //  the oplock again (and again, and again...).
292             //
293 
294             SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
295             ClearFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE );
296 
297 #if (NTDDI_VERSION >= NTDDI_WIN8)
298             ProcessingDeleteOnClose = TRUE;
299 #endif
300 
301             //
302             //  Report this to the dir notify package for a directory.
303             //
304 
305             if (TypeOfOpen == UserDirectoryOpen) {
306 
307 #ifdef _MSC_VER
308 #pragma prefast( suppress:6309, "FullDirectoryName may be NULL if NotifyIrp is also NULL. this indicates the object is being deleted." )
309 #endif
310                 FsRtlNotifyFullChangeDirectory( Vcb->NotifySync,
311                                                 &Vcb->DirNotifyList,
312                                                 FileObject->FsContext,
313                                                 NULL,
314                                                 FALSE,
315                                                 FALSE,
316                                                 0,
317                                                 NULL,
318                                                 NULL,
319                                                 NULL );
320             }
321         }
322 
323         //
324         //  Now if we may delete the file, drop the Fcb and acquire the Vcb
325         //  first.  Note that while we own the Fcb exclusive, a file cannot
326         //  become DELETE_ON_CLOSE and cannot be opened via CommonCreate.
327         //
328 
329         if ((Fcb->UncleanCount == 1) &&
330             FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) &&
331             (Fcb->FcbCondition != FcbBad) &&
332             !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
333 
334             FatReleaseFcb( IrpContext, Fcb );
335             AcquiredFcb = FALSE;
336 
337             (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
338             AcquiredVcb = TRUE;
339 
340             (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
341             AcquiredFcb = TRUE;
342         }
343     }
344 
345     //
346     //  For user DASD cleanups, grab the Vcb exclusive.
347     //
348 
349     if (TypeOfOpen == UserVolumeOpen) {
350 
351         (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
352         AcquiredVcb = TRUE;
353     }
354 
355     //
356     //  Complete any Notify Irps on this file handle.
357     //
358 
359     if (TypeOfOpen == UserDirectoryOpen) {
360 
361         FsRtlNotifyCleanup( Vcb->NotifySync,
362                             &Vcb->DirNotifyList,
363                             Ccb );
364     }
365 
366     //
367     //  Determine the Fcb state, Good or Bad, for better or for worse.
368     //
369     //  We can only read the volume file if VcbCondition is good.
370     //
371 
372     if ( Fcb != NULL) {
373 
374         //
375         //  Stop any raises from FatVerifyFcb, unless it is REAL bad.
376         //
377 
378         _SEH2_TRY {
379 
380             _SEH2_TRY {
381 
382                 FatVerifyFcb( IrpContext, Fcb );
383 
384             } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
385                       EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
386 
387                   FatResetExceptionState( IrpContext );
388             } _SEH2_END;
389 
390         } _SEH2_FINALLY {
391 
392             if ( _SEH2_AbnormalTermination() ) {
393 
394                 //
395                 //  We will be raising out of here.
396                 //
397 
398                 if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); }
399                 if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); }
400             }
401         } _SEH2_END;
402     }
403 
404     _SEH2_TRY {
405 
406 #if (NTDDI_VERSION >= NTDDI_WIN8)
407 
408         //
409         //  See if this is a delete-on-close handle on a file or empty directory.
410         //  If so we may need to break an oplock.  We do this in the try block
411         //  so that resources will be properly released.
412         //
413 
414         if (ProcessingDeleteOnClose &&
415             FatIsFileOplockable( Fcb ) &&
416             ((NodeType( Fcb ) != FAT_NTC_DCB) ||
417              FatIsDirectoryEmpty( IrpContext, Fcb ))) {
418 
419             Status = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb),
420                                          Irp,
421                                          OPLOCK_FLAG_CLOSING_DELETE_ON_CLOSE,
422                                          IrpContext,
423                                          FatOplockComplete,
424                                          FatPrePostIrp );
425 
426             if (Status != STATUS_SUCCESS) {
427 
428                 if (Status == STATUS_PENDING) {
429 
430                     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_CLEANUP_BREAKING_OPLOCK );
431                     try_return( Status );
432 
433                 } else {
434 
435                     FatNormalizeAndRaiseStatus( IrpContext, Status );
436                 }
437             }
438         }
439 #endif
440 
441         //
442         //  Case on the type of open that we are trying to cleanup.
443         //  For all cases we need to set the share access to point to the
444         //  share access variable (if there is one). After the switch
445         //  we then remove the share access and complete the Irp.
446         //  In the case of UserFileOpen we actually have a lot more work
447         //  to do and we have the FsdLockControl complete the Irp for us.
448         //
449 
450         switch (TypeOfOpen) {
451 
452         case DirectoryFile:
453         case VirtualVolumeFile:
454 
455             DebugTrace(0, Dbg, "Cleanup VirtualVolumeFile/DirectoryFile\n", 0);
456 
457             ShareAccess = NULL;
458 
459             break;
460 
461 
462         case UserVolumeOpen:
463 
464             DebugTrace(0, Dbg, "Cleanup UserVolumeOpen\n", 0);
465 
466             if (FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) {
467 
468                 FatCheckForDismount( IrpContext, Vcb, TRUE );
469 
470             //
471             //  If this handle had write access, and actually wrote something,
472             //  flush the device buffers, and then set the verify bit now
473             //  just to be safe (in case there is no dismount).
474             //
475 
476             } else if (FileObject->WriteAccess &&
477                        FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) {
478 
479                 (VOID)FatHijackIrpAndFlushDevice( IrpContext,
480                                                   Irp,
481                                                   Vcb->TargetDeviceObject );
482 
483                 SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
484             }
485 
486             //
487             //  If the volume is locked by this file object then release
488             //  the volume and send notification.
489             //
490 
491             if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED) &&
492                 (Vcb->FileObjectWithVcbLocked == FileObject)) {
493 
494                 FatAutoUnlock( IrpContext, Vcb );
495                 SendUnlockNotification = TRUE;
496             }
497 
498             ShareAccess = &Vcb->ShareAccess;
499 
500             break;
501 
502         case EaFile:
503 
504             DebugTrace(0, Dbg, "Cleanup EaFileObject\n", 0);
505 
506             ShareAccess = NULL;
507 
508             break;
509 
510         case UserDirectoryOpen:
511 
512             DebugTrace(0, Dbg, "Cleanup UserDirectoryOpen\n", 0);
513 
514             ShareAccess = &Fcb->ShareAccess;
515 
516             //
517             //  Determine here if we should try do delayed close.
518             //
519 
520             if ((Fcb->UncleanCount == 1) &&
521                 (Fcb->OpenCount == 1) &&
522                 (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0) &&
523                 !FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) &&
524                 Fcb->FcbCondition == FcbGood) {
525 
526                 //
527                 //  Delay our close.
528                 //
529 
530                 SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE );
531             }
532 
533             //
534             //  Clear the deny defrag bit, if the handle we're cleaning up was the one that set it.
535             //
536 
537             if( FlagOn(Fcb->FcbState, FCB_STATE_DENY_DEFRAG) && FlagOn(Ccb->Flags, CCB_FLAG_DENY_DEFRAG) ) {
538 
539                 ClearFlag(Ccb->Flags, CCB_FLAG_DENY_DEFRAG);
540                 ClearFlag(Fcb->FcbState, FCB_STATE_DENY_DEFRAG );
541             }
542 
543             if ((VcbGood == Vcb->VcbCondition) &&
544                 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN)) {
545 
546                 FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb );
547 
548                 //
549                 //  If the directory has a unclean count of 1 then we know
550                 //  that this is the last handle for the file object.  If
551                 //  we are supposed to delete it, do so.
552                 //
553 
554                 if ((Fcb->UncleanCount == 1) &&
555                     (NodeType(Fcb) == FAT_NTC_DCB) &&
556                     (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) &&
557                     (Fcb->FcbCondition == FcbGood) &&
558                     !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
559 
560                     if (!FatIsDirectoryEmpty(IrpContext, Fcb)) {
561 
562                         //
563                         //  If there are files in the directory at this point,
564                         //  forget that we were trying to delete it.
565                         //
566 
567                         ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
568 
569                     } else {
570 
571 #if (NTDDI_VERSION >= NTDDI_WIN8)
572                         NTSTATUS BreakStatus;
573 #endif
574 
575                         //
576                         //  Even if something goes wrong, we cannot turn back!
577                         //
578 
579                         _SEH2_TRY {
580 
581                             DELETE_CONTEXT DeleteContext;
582 
583 
584                             //
585                             //  Before truncating file allocation remember this
586                             //  info for FatDeleteDirent.
587                             //
588 
589                             DeleteContext.FileSize = Fcb->Header.FileSize.LowPart;
590                             DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile;
591 
592                             //
593                             //  Synchronize here with paging IO
594                             //
595 
596                             (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource,
597                                                               TRUE );
598 
599                             Fcb->Header.FileSize.LowPart = 0;
600 
601                             ExReleaseResourceLite( Fcb->Header.PagingIoResource );
602 
603                             //
604                             //  Truncate the file allocation down to zero
605                             //
606 
607                             DebugTrace(0, Dbg, "Delete File allocation\n", 0);
608 
609                             FatTruncateFileAllocation( IrpContext, Fcb, 0 );
610 
611                             if (Fcb->Header.AllocationSize.LowPart == 0) {
612 
613                                 //
614                                 //  Tunnel and remove the dirent for the directory
615                                 //
616 
617                                 DebugTrace(0, Dbg, "Delete the directory dirent\n", 0);
618 
619                                 FatTunnelFcbOrDcb( Fcb, NULL );
620 
621                                 FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE );
622 
623                                 //
624                                 //  Report that we have removed an entry.
625                                 //
626 
627                                 FatNotifyReportChange( IrpContext,
628                                                        Vcb,
629                                                        Fcb,
630                                                        FILE_NOTIFY_CHANGE_DIR_NAME,
631                                                        FILE_ACTION_REMOVED );
632                             }
633 
634                         } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
635                                   EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
636 
637                               FatResetExceptionState( IrpContext );
638                         } _SEH2_END;
639 
640                         //
641                         //  Remove the entry from the name table.
642                         //  This will ensure that
643                         //  we will not collide with the Dcb if the user wants
644                         //  to recreate the same file over again before we
645                         //  get a close irp.
646                         //
647 
648                         FatRemoveNames( IrpContext, Fcb );
649 
650 #if (NTDDI_VERSION >= NTDDI_WIN8)
651                         //
652                         //  We've removed the names so break any parent directory oplock.
653                         //  Directory oplock breaks are always advisory, so we will never
654                         //  block/get STATUS_PENDING here.
655                         //
656 
657                         BreakStatus = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb),
658                                                           Irp,
659                                                           (OPLOCK_FLAG_PARENT_OBJECT |
660                                                            OPLOCK_FLAG_REMOVING_FILE_OR_LINK),
661                                                           NULL,
662                                                           NULL,
663                                                           NULL );
664 
665                         ASSERT( BreakStatus != STATUS_PENDING );
666 #endif
667                     }
668                 }
669             }
670 
671             //
672             //  Decrement the unclean count.
673             //
674 
675             NT_ASSERT( Fcb->UncleanCount != 0 );
676             Fcb->UncleanCount -= 1;
677 
678             break;
679 
680         case UserFileOpen:
681 
682             DebugTrace(0, Dbg, "Cleanup UserFileOpen\n", 0);
683 
684             ShareAccess = &Fcb->ShareAccess;
685 
686             //
687             //  Determine here if we should do a delayed close.
688             //
689 
690             if ((FileObject->SectionObjectPointer->DataSectionObject == NULL) &&
691                 (FileObject->SectionObjectPointer->ImageSectionObject == NULL) &&
692                 (Fcb->UncleanCount == 1) &&
693                 (Fcb->OpenCount == 1) &&
694                 !FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) &&
695                 !FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) &&
696                 Fcb->FcbCondition == FcbGood) {
697 
698                 //
699                 //  Delay our close.
700                 //
701 
702                 SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE );
703             }
704 
705             //
706             //  Clear the deny defrag bit, if the handle we're cleaning up was the one that set it.
707             //
708 
709             if( FlagOn(Fcb->FcbState, FCB_STATE_DENY_DEFRAG) && FlagOn(Ccb->Flags, CCB_FLAG_DENY_DEFRAG) ) {
710 
711                 ClearFlag(Ccb->Flags, CCB_FLAG_DENY_DEFRAG);
712                 ClearFlag(Fcb->FcbState, FCB_STATE_DENY_DEFRAG );
713             }
714 
715             //
716             //  Unlock all outstanding file locks.
717             //
718 
719             (VOID) FsRtlFastUnlockAll( &Fcb->Specific.Fcb.FileLock,
720                                        FileObject,
721                                        IoGetRequestorProcess( Irp ),
722                                        NULL );
723 
724 
725 
726             //
727             //  We can proceed with on-disk updates only if the volume is mounted
728             //  and we can still write to it if it hasn't been shutdown. Remember that
729             //  we toss all sections in the failed-verify and dismount cases.
730             //
731 
732             if ((Vcb->VcbCondition == VcbGood) &&
733                 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN)) {
734 
735                 if (Fcb->FcbCondition == FcbGood) {
736 
737 
738                     FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb );
739 
740                 }
741 
742                 //
743                 //  If the file has a unclean count of 1 then we know
744                 //  that this is the last handle for the file object.
745                 //
746 
747                 if ( (Fcb->UncleanCount == 1) && (Fcb->FcbCondition == FcbGood) ) {
748 
749                     DELETE_CONTEXT DeleteContext;
750 
751                     //
752                     //  Check if we should be deleting the file.  The
753                     //  delete operation really deletes the file but
754                     //  keeps the Fcb around for close to do away with.
755                     //
756 
757                     if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) &&
758                         !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
759 
760                         //
761                         //  Before truncating file allocation remember this
762                         //  info for FatDeleteDirent.
763                         //
764 
765                         DeleteContext.FileSize = Fcb->Header.FileSize.LowPart;
766                         DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile;
767 
768                         DebugTrace(0, Dbg, "Delete File allocation\n", 0);
769 
770                         //
771                         //  Synchronize here with paging IO
772                         //
773 
774                         (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource,
775                                                           TRUE );
776 
777                         Fcb->Header.FileSize.LowPart = 0;
778                         Fcb->Header.ValidDataLength.LowPart = 0;
779                         Fcb->ValidDataToDisk = 0;
780 
781                         ExReleaseResourceLite( Fcb->Header.PagingIoResource );
782 
783                         _SEH2_TRY {
784 
785                             FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
786 
787                         } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
788                                   EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
789 
790                               FatResetExceptionState( IrpContext );
791                         } _SEH2_END;
792 
793                         Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;
794 
795                     } else {
796 
797                         //
798                         //  We must zero between ValidDataLength and FileSize
799                         //
800 
801                         if (!FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) &&
802                             (Fcb->Header.ValidDataLength.LowPart < Fcb->Header.FileSize.LowPart)) {
803 
804                             ULONG ValidDataLength;
805 
806                             ValidDataLength = Fcb->Header.ValidDataLength.LowPart;
807 
808                             if (ValidDataLength < Fcb->ValidDataToDisk) {
809                                 ValidDataLength = Fcb->ValidDataToDisk;
810                             }
811 
812                             //
813                             //  Recheck, VDD can be >= FS
814                             //
815 
816                             if (ValidDataLength < Fcb->Header.FileSize.LowPart) {
817 
818                                 _SEH2_TRY {
819 
820                                     (VOID)FatZeroData( IrpContext,
821                                                        Vcb,
822                                                        FileObject,
823                                                        ValidDataLength,
824                                                        Fcb->Header.FileSize.LowPart -
825                                                        ValidDataLength );
826 
827                                     //
828                                     //  Since we just zeroed this, we can now bump
829                                     //  up VDL in the Fcb.
830                                     //
831 
832                                     Fcb->ValidDataToDisk =
833                                     Fcb->Header.ValidDataLength.LowPart =
834                                     Fcb->Header.FileSize.LowPart;
835 
836                                     //
837                                     //  We inform Cc of the motion so that the cache map is updated.
838                                     //  This prevents optimized zero-page faults in case the cache
839                                     //  structures are re-used for another handle before they are torn
840                                     //  down by our soon-to-occur uninitialize. If they were, a noncached
841                                     //  producer could write into the region we just zeroed and Cc would
842                                     //  be none the wiser, then our async cached reader comes in and takes
843                                     //  the optimized path, and we get bad (zero) data.
844                                     //
845                                     //  If this was memory mapped, we don't have to (can't) tell Cc, it'll
846                                     //  figure it out when a cached handle is opened.
847                                     //
848 
849                                     if (CcIsFileCached( FileObject )) {
850                                         CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
851                                     }
852 
853                                 } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
854                                           EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
855 
856                                       FatResetExceptionState( IrpContext );
857                                 } _SEH2_END;
858                             }
859                         }
860                     }
861 
862                     //
863                     //  See if we are supposed to truncate the file on the last
864                     //  close.  If we cannot wait we'll ship this off to the fsp
865                     //
866 
867                     _SEH2_TRY {
868 
869                         if (FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE)) {
870 
871                             DebugTrace(0, Dbg, "truncate file allocation\n", 0);
872 
873                             if (Vcb->VcbCondition == VcbGood) {
874 
875 
876                                 FatTruncateFileAllocation( IrpContext,
877                                                            Fcb,
878                                                            Fcb->Header.FileSize.LowPart );
879 
880 
881                             }
882 
883                             //
884                             //  We also have to get rid of the Cache Map because
885                             //  this is the only way we have of trashing the
886                             //  truncated pages.
887                             //
888 
889                             LocalTruncateSize = Fcb->Header.FileSize;
890                             TruncateSize = &LocalTruncateSize;
891 
892                             //
893                             //  Mark the Fcb as having now been truncated, just incase
894                             //  we have to reship this off to the fsp.
895                             //
896 
897                             Fcb->FcbState &= ~FCB_STATE_TRUNCATE_ON_CLOSE;
898                         }
899 
900                         //
901                         //  Now check again if we are to delete the file and if
902                         //  so then we remove the file from the disk.
903                         //
904 
905                         if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) &&
906                             Fcb->Header.AllocationSize.LowPart == 0) {
907 
908                             DebugTrace(0, Dbg, "Delete File\n", 0);
909 
910                             //
911                             //  Now tunnel and delete the dirent
912                             //
913 
914                             FatTunnelFcbOrDcb( Fcb, Ccb );
915 
916                             FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE );
917 
918                             //
919                             //  Report that we have removed an entry.
920                             //
921 
922                             FatNotifyReportChange( IrpContext,
923                                                    Vcb,
924                                                    Fcb,
925                                                    FILE_NOTIFY_CHANGE_FILE_NAME,
926                                                    FILE_ACTION_REMOVED );
927                         }
928 
929                     } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
930                               EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
931 
932                           FatResetExceptionState( IrpContext );
933                     } _SEH2_END;
934 
935                     if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) {
936 
937 #if (NTDDI_VERSION >= NTDDI_WIN8)
938                         NTSTATUS BreakStatus;
939 #endif
940                         //
941                         //  Remove the entry from the splay table. This will
942                         //  ensure that we will not collide with the Fcb if the
943                         //  user wants to recreate the same file over again
944                         //  before we get a close irp.
945                         //
946                         //  Note that we remove the name even if we couldn't
947                         //  truncate the allocation and remove the dirent above.
948                         //
949 
950                         FatRemoveNames( IrpContext, Fcb );
951 
952 #if (NTDDI_VERSION >= NTDDI_WIN8)
953                         //
954                         //  We've removed the names so break any parent directory oplock.
955                         //  Directory oplock breaks are always advisory, so we will never
956                         //  block/get STATUS_PENDING here.
957                         //
958 
959                         BreakStatus = FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb),
960                                                           Irp,
961                                                           (OPLOCK_FLAG_PARENT_OBJECT |
962                                                            OPLOCK_FLAG_REMOVING_FILE_OR_LINK),
963                                                           NULL,
964                                                           NULL,
965                                                           NULL );
966 
967                         ASSERT( BreakStatus != STATUS_PENDING );
968 #endif
969                     }
970                 }
971             }
972 
973             //
974             //  We've just finished everything associated with an unclean
975             //  fcb so now decrement the unclean count before releasing
976             //  the resource.
977             //
978 
979             NT_ASSERT( Fcb->UncleanCount != 0 );
980             Fcb->UncleanCount -= 1;
981             if (!FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED )) {
982                 NT_ASSERT( Fcb->NonCachedUncleanCount != 0 );
983                 Fcb->NonCachedUncleanCount -= 1;
984             }
985 
986             //
987             //  If this was the last cached open, and there are open
988             //  non-cached handles, attempt a flush and purge operation
989             //  to avoid cache coherency overhead from these non-cached
990             //  handles later.  We ignore any I/O errors from the flush.
991             //
992 
993             if (FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED ) &&
994                 (Fcb->NonCachedUncleanCount != 0) &&
995                 (Fcb->NonCachedUncleanCount == Fcb->UncleanCount) &&
996                 (Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL)) {
997 
998                 CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, NULL );
999 
1000                 //
1001                 //  Grab and release PagingIo to serialize ourselves with the lazy writer.
1002                 //  This will work to ensure that all IO has completed on the cached
1003                 //  data and we will succesfully tear away the cache section.
1004                 //
1005 
1006                 ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE);
1007                 ExReleaseResourceLite( Fcb->Header.PagingIoResource );
1008 
1009                 CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers,
1010                                      NULL,
1011                                      0,
1012                                      FALSE );
1013             }
1014 
1015             //
1016             //  If the file is invalid, hint to the cache that we should throw everything out.
1017             //
1018 
1019             if ( Fcb->FcbCondition == FcbBad ) {
1020 
1021                 TruncateSize = &FatLargeZero;
1022             }
1023 
1024             //
1025             //  Cleanup the cache map
1026             //
1027 
1028             CcUninitializeCacheMap( FileObject, TruncateSize, NULL );
1029 
1030             break;
1031 
1032         default:
1033 
1034 #ifdef _MSC_VER
1035 #pragma prefast( suppress: 28159, "if the type of open is unknown then things are very bad." )
1036 #endif
1037             FatBugCheck( TypeOfOpen, 0, 0 );
1038         }
1039 
1040         //
1041         //  We must clean up the share access at this time, since we may not
1042         //  get a Close call for awhile if the file was mapped through this
1043         //  File Object.
1044         //
1045 
1046         if (ShareAccess != NULL) {
1047 
1048             DebugTrace(0, Dbg, "Cleanup the Share access\n", 0);
1049             IoRemoveShareAccess( FileObject, ShareAccess );
1050         }
1051 
1052         if ((TypeOfOpen == UserFileOpen)
1053 #if (NTDDI_VERSION >= NTDDI_WIN8)
1054             ||
1055             (TypeOfOpen == UserDirectoryOpen)
1056 #endif
1057             ) {
1058 
1059             //
1060             //  Coordinate the cleanup operation with the oplock state.
1061             //  Cleanup operations can always cleanup immediately.
1062             //
1063 
1064             FsRtlCheckOplock( FatGetFcbOplock(Fcb),
1065                               Irp,
1066                               IrpContext,
1067                               NULL,
1068                               NULL );
1069 
1070             Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
1071         }
1072 
1073         //
1074         //  First set the FO_CLEANUP_COMPLETE flag.
1075         //
1076 
1077         SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE );
1078 
1079         Status = STATUS_SUCCESS;
1080 
1081         //
1082         //  Now unpin any repinned Bcbs.
1083         //
1084 
1085         FatUnpinRepinnedBcbs( IrpContext );
1086 
1087         //
1088         //  If this was deferred flush media, flush the volume.
1089         //  We used to do this in lieu of write through for all removable
1090         //  media.
1091         //
1092 
1093         if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) &&
1094             !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED))  {
1095 
1096             //
1097             //  Flush the file.
1098             //
1099 
1100             if ((TypeOfOpen == UserFileOpen) &&
1101                 FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) {
1102 
1103                 Status = FatFlushFile( IrpContext, Fcb, Flush );
1104             }
1105 
1106             //
1107             //  If that worked ok,  then see if we should flush the FAT as well.
1108             //
1109 
1110             if (NT_SUCCESS(Status) && Fcb && !FatIsFat12( Vcb) &&
1111                 FlagOn( Fcb->FcbState, FCB_STATE_FLUSH_FAT)) {
1112 
1113                 Status = FatFlushFat( IrpContext, Vcb);
1114 
1115                 //
1116                 // Also flush the parent directory.
1117                 //
1118 
1119                 if (NT_SUCCESS(Status) && (Fcb->ParentDcb != NULL)) {
1120 
1121                     Status = FatFlushFile( IrpContext, Fcb->ParentDcb, Flush );
1122                 }
1123             }
1124 
1125             if (!NT_SUCCESS(Status)) {
1126 
1127                 FatNormalizeAndRaiseStatus( IrpContext, Status );
1128             }
1129         }
1130 
1131 #if (NTDDI_VERSION >= NTDDI_WIN8)
1132 
1133     try_exit: NOTHING;
1134 
1135 #endif
1136 
1137     } _SEH2_FINALLY {
1138 
1139         DebugUnwind( FatCommonCleanup );
1140 
1141         if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); }
1142         if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); }
1143 
1144         if (SendUnlockNotification) {
1145 
1146             FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_UNLOCK );
1147         }
1148 
1149         //
1150         //  If this is a normal termination then complete the request
1151         //
1152 
1153         if (!_SEH2_AbnormalTermination() &&
1154             (Status != STATUS_PENDING)) {
1155 
1156             FatCompleteRequest( IrpContext, Irp, Status );
1157         }
1158 
1159         DebugTrace(-1, Dbg, "FatCommonCleanup -> %08lx\n", Status);
1160     } _SEH2_END;
1161 
1162     return Status;
1163 }
1164 
1165 VOID
1166 FatAutoUnlock (
1167     IN PIRP_CONTEXT IrpContext,
1168     IN PVCB Vcb
1169     )
1170 {
1171     KIRQL SavedIrql;
1172 
1173     //
1174     //  Unlock the volume.
1175     //
1176 
1177     UNREFERENCED_PARAMETER( IrpContext );
1178 
1179     IoAcquireVpbSpinLock( &SavedIrql );
1180 
1181     ClearFlag( Vcb->Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED) );
1182 
1183     Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED;
1184     Vcb->FileObjectWithVcbLocked = NULL;
1185 
1186     IoReleaseVpbSpinLock( SavedIrql );
1187 }
1188 
1189 
1190