xref: /reactos/drivers/filesystems/fastfat/flush.c (revision d6eebaa4)
1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     Flush.c
8 
9 Abstract:
10 
11     This module implements the File Flush buffers 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_FLUSH)
24 
25 //
26 //  The local debug trace level
27 //
28 
29 #define Dbg                              (DEBUG_TRACE_FLUSH)
30 
31 #ifdef ALLOC_PRAGMA
32 #pragma alloc_text(PAGE, FatCommonFlushBuffers)
33 #pragma alloc_text(PAGE, FatFlushDirectory)
34 #pragma alloc_text(PAGE, FatFlushFat)
35 #pragma alloc_text(PAGE, FatFlushFile)
36 #pragma alloc_text(PAGE, FatFlushVolume)
37 #pragma alloc_text(PAGE, FatFsdFlushBuffers)
38 #pragma alloc_text(PAGE, FatFlushDirentForFile)
39 #pragma alloc_text(PAGE, FatFlushFatEntries)
40 #pragma alloc_text(PAGE, FatHijackIrpAndFlushDevice)
41 #endif
42 
43 //
44 //  Local procedure prototypes
45 //
46 
47 IO_COMPLETION_ROUTINE FatFlushCompletionRoutine;
48 
49 NTSTATUS
50 NTAPI
51 FatFlushCompletionRoutine (
52     _In_ PDEVICE_OBJECT DeviceObject,
53     _In_ PIRP Irp,
54     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
55     );
56 
57 IO_COMPLETION_ROUTINE FatHijackCompletionRoutine;
58 
59 NTSTATUS
60 NTAPI
61 FatHijackCompletionRoutine (
62     _In_ PDEVICE_OBJECT DeviceObject,
63     _In_ PIRP Irp,
64     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
65     );
66 
67 
68 _Function_class_(IRP_MJ_FLUSH_BUFFERS)
69 _Function_class_(DRIVER_DISPATCH)
70 NTSTATUS
71 NTAPI
72 FatFsdFlushBuffers (
73     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
74     _Inout_ PIRP Irp
75     )
76 
77 /*++
78 
79 Routine Description:
80 
81     This routine implements the FSD part of Flush buffers.
82 
83 Arguments:
84 
85     VolumeDeviceObject - Supplies the volume device object where the
86         file being flushed exists
87 
88     Irp - Supplies the Irp being processed
89 
90 Return Value:
91 
92     NTSTATUS - The FSD status for the IRP
93 
94 --*/
95 
96 {
97     NTSTATUS Status;
98     PIRP_CONTEXT IrpContext = NULL;
99 
100     BOOLEAN TopLevel;
101 
102     PAGED_CODE();
103 
104     DebugTrace(+1, Dbg, "FatFsdFlushBuffers\n", 0);
105 
106     //
107     //  Call the common Cleanup routine, with blocking allowed if synchronous
108     //
109 
110     FsRtlEnterFileSystem();
111 
112     TopLevel = FatIsIrpTopLevel( Irp );
113 
114     _SEH2_TRY {
115 
116         IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
117 
118         Status = FatCommonFlushBuffers( IrpContext, Irp );
119 
120     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
121 
122         //
123         //  We had some trouble trying to perform the requested
124         //  operation, so we'll abort the I/O request with
125         //  the error status that we get back from the
126         //  execption code
127         //
128 
129         Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
130     } _SEH2_END;
131 
132     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
133 
134     FsRtlExitFileSystem();
135 
136     //
137     //  And return to our caller
138     //
139 
140     DebugTrace(-1, Dbg, "FatFsdFlushBuffers -> %08lx\n", Status);
141 
142     UNREFERENCED_PARAMETER( VolumeDeviceObject );
143 
144     return Status;
145 }
146 
147 
148 _Requires_lock_held_(_Global_critical_region_)
149 NTSTATUS
150 FatCommonFlushBuffers (
151     IN PIRP_CONTEXT IrpContext,
152     IN PIRP Irp
153     )
154 
155 /*++
156 
157 Routine Description:
158 
159     This is the common routine for flushing a buffer.
160 
161 Arguments:
162 
163     Irp - Supplies the Irp to process
164 
165 Return Value:
166 
167     NTSTATUS - The return status for the operation
168 
169 --*/
170 
171 {
172     NTSTATUS Status;
173 
174     PIO_STACK_LOCATION IrpSp;
175 
176     PFILE_OBJECT FileObject;
177 
178     TYPE_OF_OPEN TypeOfOpen;
179     PVCB Vcb;
180     PFCB Fcb;
181     PFCB NextFcb;
182     PCCB Ccb;
183 
184     BOOLEAN VcbAcquired = FALSE;
185     BOOLEAN FcbAcquired = FALSE;
186     BOOLEAN FatFlushRequired = FALSE;
187 
188     PAGED_CODE();
189 
190     IrpSp = IoGetCurrentIrpStackLocation( Irp );
191 
192     DebugTrace(+1, Dbg, "FatCommonFlushBuffers\n", 0);
193     DebugTrace( 0, Dbg, "Irp           = %p\n", Irp);
194     DebugTrace( 0, Dbg, "->FileObject  = %p\n", IrpSp->FileObject);
195 
196     //
197     //  Extract and decode the file object
198     //
199 
200     FileObject = IrpSp->FileObject;
201     TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
202 
203     //
204     //  CcFlushCache is always synchronous, so if we can't wait enqueue
205     //  the irp to the Fsp.
206     //
207 
208     if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) {
209 
210         Status = FatFsdPostRequest( IrpContext, Irp );
211 
212         DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status );
213         return Status;
214     }
215 
216     Status = STATUS_SUCCESS;
217 
218     _SEH2_TRY {
219 
220 #if (NTDDI_VERSION >= NTDDI_WIN8)
221 
222         if (FatDiskAccountingEnabled) {
223 
224             PETHREAD OriginatingThread = NULL;
225 
226             //
227             //  Charge the flush to the originating thread.
228             //  Try the Thread in Irp's tail first, if that is NULL, then charge
229             //  the flush to current thread.
230             //
231 
232             if ((Irp->Tail.Overlay.Thread != NULL) &&
233                 !IoIsSystemThread( Irp->Tail.Overlay.Thread )) {
234 
235                 OriginatingThread = Irp->Tail.Overlay.Thread;
236 
237             } else {
238 
239                 OriginatingThread = PsGetCurrentThread();
240             }
241 
242             NT_ASSERT( OriginatingThread != NULL );
243 
244             PsUpdateDiskCounters( PsGetThreadProcess( OriginatingThread ),
245                                   0,
246                                   0,
247                                   0,
248                                   0,
249                                   1 );
250         }
251 
252 #endif
253 
254         //
255         //  Case on the type of open that we are trying to flush
256         //
257 
258         switch (TypeOfOpen) {
259 
260         case VirtualVolumeFile:
261         case EaFile:
262         case DirectoryFile:
263             DebugTrace(0, Dbg, "Flush that does nothing\n", 0);
264             break;
265 
266         case UserFileOpen:
267 
268             DebugTrace(0, Dbg, "Flush User File Open\n", 0);
269 
270             (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
271 
272             FcbAcquired = TRUE;
273 
274             FatVerifyFcb( IrpContext, Fcb );
275 
276             //
277             //  If the file is cached then flush its cache
278             //
279 
280             Status = FatFlushFile( IrpContext, Fcb, Flush );
281 
282             //
283             //  Also flush the file's dirent in the parent directory if the file
284             //  flush worked.
285             //
286 
287             if (NT_SUCCESS( Status )) {
288 
289                 //
290                 //  Insure that we get the filesize to disk correctly.  This is
291                 //  benign if it was already good.
292                 //
293 
294                 SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED);
295 
296 
297                 FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb );
298 
299                 if (FlagOn(Fcb->FcbState, FCB_STATE_FLUSH_FAT)) {
300 
301                     FatFlushRequired = TRUE;
302                 }
303 
304                 //
305                 //  Flush the parent Dcb's to get any dirent updates to disk.
306                 //
307 
308                 NextFcb = Fcb->ParentDcb;
309 
310                 while (NextFcb != NULL) {
311 
312                     //
313                     //  Make sure the Fcb is OK.
314                     //
315 
316                     _SEH2_TRY {
317 
318                         FatVerifyFcb( IrpContext, NextFcb );
319 
320                     } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
321                               EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
322 
323                         FatResetExceptionState( IrpContext );
324                     } _SEH2_END;
325 
326                     if (NextFcb->FcbCondition == FcbGood) {
327 
328                         NTSTATUS LocalStatus;
329 
330                         LocalStatus = FatFlushFile( IrpContext, NextFcb, Flush );
331 
332                         if (!NT_SUCCESS(LocalStatus)) {
333 
334                             Status = LocalStatus;
335                         }
336 
337                         if (FlagOn(NextFcb->FcbState, FCB_STATE_FLUSH_FAT)) {
338 
339                             FatFlushRequired = TRUE;
340                         }
341                     }
342 
343                     NextFcb = NextFcb->ParentDcb;
344                 }
345 
346                 //
347                 //  Flush the volume file to get any allocation information
348                 //  updates to disk.
349                 //
350 
351                 if (FatFlushRequired) {
352 
353                     Status = FatFlushFat( IrpContext, Vcb );
354 
355                     ClearFlag(Fcb->FcbState, FCB_STATE_FLUSH_FAT);
356                 }
357 
358                 //
359                 //  Set the write through bit so that these modifications
360                 //  will be completed with the request.
361                 //
362 
363                 SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
364             }
365 
366             break;
367 
368         case UserDirectoryOpen:
369 
370             //
371             //  If the user had opened the root directory then we'll
372             //  oblige by flushing the volume.
373             //
374 
375             if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) {
376 
377                 DebugTrace(0, Dbg, "Flush a directory does nothing\n", 0);
378                 break;
379             }
380 
381         case UserVolumeOpen:
382 
383             DebugTrace(0, Dbg, "Flush User Volume Open, or root dcb\n", 0);
384 
385             //
386             //  Acquire exclusive access to the Vcb.
387             //
388 
389             {
390                 BOOLEAN Finished;
391 #ifdef _MSC_VER
392 #pragma prefast( suppress:28931, "needed for debug build" )
393 #endif
394                 Finished = FatAcquireExclusiveVcb( IrpContext, Vcb );
395                 NT_ASSERT( Finished );
396             }
397 
398             VcbAcquired = TRUE;
399 
400             //
401             //  Mark the volume clean and then flush the volume file,
402             //  and then all directories
403             //
404 
405             Status = FatFlushVolume( IrpContext, Vcb, Flush );
406 
407             //
408             //  If the volume was dirty, do the processing that the delayed
409             //  callback would have done.
410             //
411 
412             if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) {
413 
414                 //
415                 //  Cancel any pending clean volumes.
416                 //
417 
418                 (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer );
419                 (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc );
420 
421                 //
422                 //  The volume is now clean, note it.
423                 //
424 
425                 if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
426 
427                     FatMarkVolume( IrpContext, Vcb, VolumeClean );
428                     ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
429                 }
430 
431                 //
432                 //  Unlock the volume if it is removable.
433                 //
434 
435                 if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
436                     !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {
437 
438                     FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
439                 }
440             }
441 
442             break;
443 
444         default:
445 
446 #ifdef _MSC_VER
447 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
448 #endif
449             FatBugCheck( TypeOfOpen, 0, 0 );
450         }
451 
452         FatUnpinRepinnedBcbs( IrpContext );
453 
454     } _SEH2_FINALLY {
455 
456         DebugUnwind( FatCommonFlushBuffers );
457 
458         if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
459 
460         if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
461 
462         //
463         //  If this is a normal termination then pass the request on
464         //  to the target device object.
465         //
466 
467         if (!_SEH2_AbnormalTermination()) {
468 
469 #if (NTDDI_VERSION >= NTDDI_WIN8)
470             if ((IrpSp->MinorFunction != IRP_MN_FLUSH_DATA_ONLY) &&
471                 (IrpSp->MinorFunction != IRP_MN_FLUSH_NO_SYNC)) {
472 #endif
473 
474                 NTSTATUS DriverStatus;
475 
476                 //
477                 //  Get the next stack location, and copy over the stack location
478                 //
479 
480                 IoCopyCurrentIrpStackLocationToNext(Irp);
481 
482                 //
483                 //  Set up the completion routine
484                 //
485 
486                 IoSetCompletionRoutine( Irp,
487                                         FatFlushCompletionRoutine,
488                                         ULongToPtr( Status ),
489                                         TRUE,
490                                         TRUE,
491                                         TRUE );
492 
493                 //
494                 //  Send the request.
495                 //
496 
497                 DriverStatus = IoCallDriver(Vcb->TargetDeviceObject, Irp);
498 
499                 if ((DriverStatus == STATUS_PENDING) ||
500                     (!NT_SUCCESS(DriverStatus) &&
501                      (DriverStatus != STATUS_INVALID_DEVICE_REQUEST))) {
502 
503                     Status = DriverStatus;
504                 }
505 
506                 Irp = NULL;
507 
508 #if (NTDDI_VERSION >= NTDDI_WIN8)
509             }
510 #endif
511 
512             //
513             //  Complete the Irp if necessary and return to the caller.
514             //
515 
516             FatCompleteRequest( IrpContext, Irp, Status );
517 
518         }
519 
520         DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status);
521     } _SEH2_END;
522 
523     return Status;
524 }
525 
526 
527 _Requires_lock_held_(_Global_critical_region_)
528 NTSTATUS
529 FatFlushDirectory (
530     IN PIRP_CONTEXT IrpContext,
531     IN PDCB Dcb,
532     IN FAT_FLUSH_TYPE FlushType
533     )
534 
535 /*++
536 
537 Routine Description:
538 
539     This routine non-recursively flushes a dcb tree.
540 
541 Arguments:
542 
543     Dcb - Supplies the Dcb being flushed
544 
545     FlushType - Specifies the kind of flushing to perform
546 
547 Return Value:
548 
549     VOID
550 
551 --*/
552 
553 {
554     PFCB Fcb;
555     PVCB Vcb;
556     PFCB NextFcb;
557 
558     PDIRENT Dirent;
559     PBCB DirentBcb = NULL;
560 
561     NTSTATUS Status;
562     NTSTATUS ReturnStatus = STATUS_SUCCESS;
563 
564     BOOLEAN ClearWriteThroughOnExit = FALSE;
565     BOOLEAN ClearWaitOnExit = FALSE;
566 
567     ULONG CorrectedFileSize = 0;
568 
569     PAGED_CODE();
570 
571     NT_ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) );
572 
573     DebugTrace(+1, Dbg, "FatFlushDirectory, Dcb = %p\n", Dcb);
574 
575     //
576     //  First flush all the files, then the directories, to make sure all the
577     //  file sizes and times get sets correctly on disk.
578     //
579     //  We also have to check here if the "Ea Data. Sf" fcb really
580     //  corressponds to an existing file.
581     //
582 
583     if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) {
584 
585         ClearWriteThroughOnExit = TRUE;
586         SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
587     }
588 
589     if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
590 
591         ClearWaitOnExit = TRUE;
592         SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
593     }
594 
595     Vcb = Dcb->Vcb;
596     Fcb = Dcb;
597 
598     while (Fcb != NULL) {
599 
600         NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb);
601 
602         if ( (NodeType( Fcb ) == FAT_NTC_FCB) &&
603              (Vcb->EaFcb != Fcb) &&
604              !IsFileDeleted(IrpContext, Fcb)) {
605 
606             (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
607 
608             ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );
609 
610             //
611             //  Exception handler to catch and commute errors encountered
612             //  doing the flush dance.  We may encounter corruption, and
613             //  should continue flushing the volume as much as possible.
614             //
615 
616             _SEH2_TRY {
617 
618                 //
619                 //  Standard handler to release resources, etc.
620                 //
621 
622                 _SEH2_TRY {
623 
624                     //
625                     //  Make sure the Fcb is OK.
626                     //
627 
628                     _SEH2_TRY {
629 
630                         FatVerifyFcb( IrpContext, Fcb );
631 
632                     } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
633                               EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
634 
635                         FatResetExceptionState( IrpContext );
636                     } _SEH2_END;
637 
638                     //
639                     //  If this Fcb is not good skip it.  Note that a 'continue'
640                     //  here would be very expensive as we inside a try{} body.
641                     //
642 
643                     if (Fcb->FcbCondition != FcbGood) {
644 
645                         try_leave( NOTHING);
646                     }
647 
648                     //
649                     //  In case a handle was never closed and the FS and AS are more
650                     //  than a cluster different, do this truncate.
651                     //
652 
653                     if ( FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE) ) {
654 
655 
656                         FatTruncateFileAllocation( IrpContext,
657                                                    Fcb,
658                                                    Fcb->Header.FileSize.LowPart );
659 
660 
661                     }
662 
663                     //
664                     //  Also compare the file's dirent in the parent directory
665                     //  with the size information in the Fcb and update
666                     //  it if neccessary.  Note that we don't mark the Bcb dirty
667                     //  because we will be flushing the file object presently, and
668                     //  Mm knows what's really dirty.
669                     //
670 
671                     FatGetDirentFromFcbOrDcb( IrpContext,
672                                               Fcb,
673                                               FALSE,
674                                               &Dirent,
675                                               &DirentBcb );
676 
677 
678                     CorrectedFileSize = Fcb->Header.FileSize.LowPart;
679 
680 
681                     if (Dirent->FileSize != CorrectedFileSize) {
682 
683                         Dirent->FileSize = CorrectedFileSize;
684 
685 
686                     }
687 
688                     //
689                     //  We must unpin the Bcb before the flush since we recursively tear up
690                     //  the tree if Mm decides that the data section is no longer referenced
691                     //  and the final close comes in for this file. If this parent has no
692                     //  more children as a result, we will try to initiate teardown on it
693                     //  and Cc will deadlock against the active count of this Bcb.
694                     //
695 
696                     FatUnpinBcb( IrpContext, DirentBcb );
697 
698                     //
699                     //  Now flush the file.  Note that this may make the Fcb
700                     //  go away if Mm dereferences its file object.
701                     //
702 
703                     Status = FatFlushFile( IrpContext, Fcb, FlushType );
704 
705                     if (!NT_SUCCESS(Status)) {
706 
707                         ReturnStatus = Status;
708                     }
709 
710                 } _SEH2_FINALLY {
711 
712                     FatUnpinBcb( IrpContext, DirentBcb );
713 
714                     //
715                     //  Since we have the Vcb exclusive we know that if any closes
716                     //  come in it is because the CcPurgeCacheSection caused the
717                     //  Fcb to go away.
718                     //
719 
720                     if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB) ) {
721 
722                         FatReleaseFcb( (IRPCONTEXT), Fcb );
723                     }
724                 } _SEH2_END;
725             } _SEH2_EXCEPT( (FsRtlIsNtstatusExpected( ReturnStatus = _SEH2_GetExceptionCode() ) != 0 ) ?
726                        EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
727                    FatResetExceptionState( IrpContext );
728              } _SEH2_END;
729 
730         }
731 
732         Fcb = NextFcb;
733     }
734 
735     //
736     //  OK, now flush the directories.
737     //
738 
739     Fcb = Dcb;
740 
741     while (Fcb != NULL) {
742 
743         NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb);
744 
745         if ( (NodeType( Fcb ) != FAT_NTC_FCB) &&
746              !IsFileDeleted(IrpContext, Fcb) ) {
747 
748             //
749             //  Make sure the Fcb is OK.
750             //
751 
752             _SEH2_TRY {
753 
754                 FatVerifyFcb( IrpContext, Fcb );
755 
756             } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
757                       EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
758 
759                 FatResetExceptionState( IrpContext );
760             } _SEH2_END;
761 
762             if (Fcb->FcbCondition == FcbGood) {
763 
764                 Status = FatFlushFile( IrpContext, Fcb, FlushType );
765 
766                 if (!NT_SUCCESS(Status)) {
767 
768                     ReturnStatus = Status;
769                 }
770             }
771         }
772 
773         Fcb = NextFcb;
774     }
775 
776     _SEH2_TRY {
777 
778         FatUnpinRepinnedBcbs( IrpContext );
779 
780     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
781 
782         ReturnStatus = IrpContext->ExceptionStatus;
783     } _SEH2_END;
784 
785     if (ClearWriteThroughOnExit) {
786 
787         ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
788     }
789     if (ClearWaitOnExit) {
790 
791         ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
792     }
793 
794     DebugTrace(-1, Dbg, "FatFlushDirectory -> 0x%08lx\n", ReturnStatus);
795 
796     return ReturnStatus;
797 }
798 
799 
800 NTSTATUS
801 FatFlushFat (
802     IN PIRP_CONTEXT IrpContext,
803     IN PVCB Vcb
804     )
805 
806 /*++
807 
808 Routine Description:
809 
810     The function carefully flushes the entire FAT for a volume.  It is
811     nessecary to dance around a bit because of complicated synchronization
812     reasons.
813 
814 Arguments:
815 
816     Vcb - Supplies the Vcb whose FAT is being flushed
817 
818 Return Value:
819 
820     VOID
821 
822 --*/
823 
824 {
825     PBCB Bcb;
826     PVOID DontCare;
827     IO_STATUS_BLOCK Iosb;
828     LARGE_INTEGER Offset;
829 
830     NTSTATUS ReturnStatus = STATUS_SUCCESS;
831 
832     PAGED_CODE();
833 
834     //
835     //  If this volume is write protected, no need to flush.
836     //
837 
838     if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
839 
840         return STATUS_SUCCESS;
841     }
842 
843     //
844     //  Make sure the Vcb is OK.
845     //
846 
847     _SEH2_TRY {
848 
849         FatVerifyVcb( IrpContext, Vcb );
850 
851     } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
852               EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
853 
854         FatResetExceptionState( IrpContext );
855     } _SEH2_END;
856 
857     if (Vcb->VcbCondition != VcbGood) {
858 
859         return STATUS_FILE_INVALID;
860     }
861 
862     //
863     //  The only way we have to correctly synchronize things is to
864     //  repin stuff, and then unpin repin it.
865     //
866     //  With NT 5.0, we can use some new cache manager support to make
867     //  this a lot more efficient (important for FAT32).  Since we're
868     //  only worried about ranges that are dirty - and since we're a
869     //  modified-no-write stream - we can assume that if there is no
870     //  BCB, there is no work to do in the range. I.e., the lazy writer
871     //  beat us to it.
872     //
873     //  This is much better than reading the entire FAT in and trying
874     //  to punch it out (see the test in the write path to blow
875     //  off writes that don't correspond to dirty ranges of the FAT).
876     //  For FAT32, this would be a *lot* of reading.
877     //
878 
879     if (Vcb->AllocationSupport.FatIndexBitSize != 12) {
880 
881         //
882         //  Walk through the Fat, one page at a time.
883         //
884 
885         ULONG NumberOfPages;
886         ULONG Page;
887 
888         NumberOfPages = ( FatReservedBytes(&Vcb->Bpb) +
889                           FatBytesPerFat(&Vcb->Bpb) +
890                           (PAGE_SIZE - 1) ) / PAGE_SIZE;
891 
892 
893         for ( Page = 0, Offset.QuadPart = 0;
894               Page < NumberOfPages;
895               Page++, Offset.LowPart += PAGE_SIZE ) {
896 
897             _SEH2_TRY {
898 
899                 if (CcPinRead( Vcb->VirtualVolumeFile,
900                                &Offset,
901                                PAGE_SIZE,
902                                PIN_WAIT | PIN_IF_BCB,
903                                &Bcb,
904                                &DontCare )) {
905 
906                     CcSetDirtyPinnedData( Bcb, NULL );
907                     CcRepinBcb( Bcb );
908                     CcUnpinData( Bcb );
909                     CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb );
910 
911                     if (!NT_SUCCESS(Iosb.Status)) {
912 
913                         ReturnStatus = Iosb.Status;
914                     }
915                 }
916 
917             } _SEH2_EXCEPT(FatExceptionFilter(IrpContext, _SEH2_GetExceptionInformation())) {
918 
919                 ReturnStatus = IrpContext->ExceptionStatus;
920                 continue;
921             } _SEH2_END;
922         }
923 
924     } else {
925 
926         //
927         //  We read in the entire fat in the 12 bit case.
928         //
929 
930         Offset.QuadPart = FatReservedBytes( &Vcb->Bpb );
931 
932         _SEH2_TRY {
933 
934             if (CcPinRead( Vcb->VirtualVolumeFile,
935                            &Offset,
936                            FatBytesPerFat( &Vcb->Bpb ),
937                            PIN_WAIT | PIN_IF_BCB,
938                            &Bcb,
939                            &DontCare )) {
940 
941                 CcSetDirtyPinnedData( Bcb, NULL );
942                 CcRepinBcb( Bcb );
943                 CcUnpinData( Bcb );
944                 CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb );
945 
946                 if (!NT_SUCCESS(Iosb.Status)) {
947 
948                     ReturnStatus = Iosb.Status;
949                 }
950             }
951 
952         } _SEH2_EXCEPT(FatExceptionFilter(IrpContext, _SEH2_GetExceptionInformation())) {
953 
954             ReturnStatus = IrpContext->ExceptionStatus;
955         } _SEH2_END;
956     }
957 
958     return ReturnStatus;
959 }
960 
961 
962 _Requires_lock_held_(_Global_critical_region_)
963 NTSTATUS
964 FatFlushVolume (
965     IN PIRP_CONTEXT IrpContext,
966     IN PVCB Vcb,
967     IN FAT_FLUSH_TYPE FlushType
968     )
969 
970 /*++
971 
972 Routine Description:
973 
974     The following routine is used to flush a volume to disk, including the
975     volume file, and ea file.
976 
977 Arguments:
978 
979     Vcb - Supplies the volume being flushed
980 
981     FlushType - Specifies the kind of flushing to perform
982 
983 Return Value:
984 
985     NTSTATUS - The Status from the flush.
986 
987 --*/
988 
989 {
990     NTSTATUS Status;
991     NTSTATUS ReturnStatus = STATUS_SUCCESS;
992 
993     PAGED_CODE();
994 
995     //
996     //  If this volume is write protected, no need to flush.
997     //
998 
999     if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
1000 
1001         return STATUS_SUCCESS;
1002     }
1003 
1004     //
1005     //  Flush all the files and directories.
1006     //
1007 
1008     Status = FatFlushDirectory( IrpContext, Vcb->RootDcb, FlushType );
1009 
1010     if (!NT_SUCCESS(Status)) {
1011 
1012         ReturnStatus = Status;
1013     }
1014 
1015     //
1016     //  Now Flush the FAT
1017     //
1018 
1019     Status = FatFlushFat( IrpContext, Vcb );
1020 
1021     if (!NT_SUCCESS(Status)) {
1022 
1023         ReturnStatus = Status;
1024     }
1025 
1026     //
1027     //  Unlock the volume if it is removable.
1028     //
1029 
1030     if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
1031         !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {
1032 
1033         FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
1034     }
1035 
1036     return ReturnStatus;
1037 }
1038 
1039 
1040 _Requires_lock_held_(_Global_critical_region_)
1041 NTSTATUS
1042 FatFlushFile (
1043     IN PIRP_CONTEXT IrpContext,
1044     IN PFCB Fcb,
1045     IN FAT_FLUSH_TYPE FlushType
1046     )
1047 
1048 /*++
1049 
1050 Routine Description:
1051 
1052     This routine simply flushes the data section on a file.
1053 
1054 Arguments:
1055 
1056     Fcb - Supplies the file being flushed
1057 
1058     FlushType - Specifies the kind of flushing to perform
1059 
1060 Return Value:
1061 
1062     NTSTATUS - The Status from the flush.
1063 
1064 --*/
1065 
1066 {
1067     IO_STATUS_BLOCK Iosb;
1068     PVCB Vcb = Fcb->Vcb;
1069 
1070     PAGED_CODE();
1071 
1072     CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, &Iosb );
1073 
1074 
1075     if ( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB )) {
1076 
1077         //
1078         //  Grab and release PagingIo to serialize ourselves with the lazy writer.
1079         //  This will work to ensure that all IO has completed on the cached
1080         //  data.
1081         //
1082         //  If we are to invalidate the file, now is the right time to do it.  Do
1083         //  it non-recursively so we don't thump children before their time.
1084         //
1085 
1086         ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE);
1087 
1088         if (FlushType == FlushAndInvalidate) {
1089 
1090             FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE );
1091         }
1092 
1093         ExReleaseResourceLite( Fcb->Header.PagingIoResource );
1094     }
1095 
1096     return Iosb.Status;
1097 }
1098 
1099 
1100 NTSTATUS
1101 FatHijackIrpAndFlushDevice (
1102     IN PIRP_CONTEXT IrpContext,
1103     IN PIRP Irp,
1104     IN PDEVICE_OBJECT TargetDeviceObject
1105     )
1106 
1107 /*++
1108 
1109 Routine Description:
1110 
1111     This routine is called when we need to send a flush to a device but
1112     we don't have a flush Irp.  What this routine does is make a copy
1113     of its current Irp stack location, but changes the Irp Major code
1114     to a IRP_MJ_FLUSH_BUFFERS amd then send it down, but cut it off at
1115     the knees in the completion routine, fix it up and return to the
1116     user as if nothing had happened.
1117 
1118 Arguments:
1119 
1120     Irp - The Irp to hijack
1121 
1122     TargetDeviceObject - The device to send the request to.
1123 
1124 Return Value:
1125 
1126     NTSTATUS - The Status from the flush in case anybody cares.
1127 
1128 --*/
1129 
1130 {
1131     KEVENT Event;
1132     NTSTATUS Status;
1133     PIO_STACK_LOCATION NextIrpSp;
1134 
1135     PAGED_CODE();
1136 
1137     UNREFERENCED_PARAMETER( IrpContext );
1138 
1139     //
1140     //  Get the next stack location, and copy over the stack location
1141     //
1142 
1143     IoCopyCurrentIrpStackLocationToNext(Irp);
1144 
1145     NextIrpSp = IoGetNextIrpStackLocation( Irp );
1146     NextIrpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS;
1147     NextIrpSp->MinorFunction = 0;
1148 
1149     //
1150     //  Set up the completion routine
1151     //
1152 
1153     KeInitializeEvent( &Event, NotificationEvent, FALSE );
1154 
1155     IoSetCompletionRoutine( Irp,
1156                             FatHijackCompletionRoutine,
1157                             &Event,
1158                             TRUE,
1159                             TRUE,
1160                             TRUE );
1161 
1162     //
1163     //  Send the request.
1164     //
1165 
1166     Status = IoCallDriver( TargetDeviceObject, Irp );
1167 
1168     if (Status == STATUS_PENDING) {
1169 
1170         KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
1171 
1172         Status = Irp->IoStatus.Status;
1173     }
1174 
1175     //
1176     //  If the driver doesn't support flushes, return SUCCESS.
1177     //
1178 
1179     if (Status == STATUS_INVALID_DEVICE_REQUEST) {
1180         Status = STATUS_SUCCESS;
1181     }
1182 
1183     Irp->IoStatus.Status = 0;
1184     Irp->IoStatus.Information = 0;
1185 
1186     return Status;
1187 }
1188 
1189 
1190 VOID
1191 FatFlushFatEntries (
1192     IN PIRP_CONTEXT IrpContext,
1193     IN PVCB Vcb,
1194     IN ULONG Cluster,
1195     IN ULONG Count
1196 )
1197 
1198 /*++
1199 
1200 Routine Description:
1201 
1202     This macro flushes the FAT page(s) containing the passed in run.
1203 
1204 Arguments:
1205 
1206     Vcb - Supplies the volume being flushed
1207 
1208     Cluster - The starting cluster
1209 
1210     Count -  The number of FAT entries in the run
1211 
1212 Return Value:
1213 
1214     VOID
1215 
1216 --*/
1217 
1218 {
1219     ULONG ByteCount;
1220     LARGE_INTEGER FileOffset;
1221 
1222     IO_STATUS_BLOCK Iosb;
1223 
1224     PAGED_CODE();
1225 
1226     FileOffset.HighPart = 0;
1227     FileOffset.LowPart = FatReservedBytes( &Vcb->Bpb );
1228 
1229     if (Vcb->AllocationSupport.FatIndexBitSize == 12) {
1230 
1231         FileOffset.LowPart += Cluster * 3 / 2;
1232         ByteCount = (Count * 3 / 2) + 1;
1233 
1234     } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) {
1235 
1236         FileOffset.LowPart += Cluster * sizeof(ULONG);
1237         ByteCount = Count * sizeof(ULONG);
1238 
1239     } else {
1240 
1241         FileOffset.LowPart += Cluster * sizeof( USHORT );
1242         ByteCount = Count * sizeof( USHORT );
1243 
1244     }
1245 
1246     CcFlushCache( &Vcb->SectionObjectPointers,
1247                   &FileOffset,
1248                   ByteCount,
1249                   &Iosb );
1250 
1251     if (NT_SUCCESS(Iosb.Status)) {
1252         Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext,
1253                                                   IrpContext->OriginatingIrp,
1254                                                   Vcb->TargetDeviceObject );
1255     }
1256 
1257     if (!NT_SUCCESS(Iosb.Status)) {
1258         FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status);
1259     }
1260 }
1261 
1262 
1263 VOID
1264 FatFlushDirentForFile (
1265     IN PIRP_CONTEXT IrpContext,
1266     IN PFCB Fcb
1267 )
1268 
1269 /*++
1270 
1271 Routine Description:
1272 
1273     This macro flushes the page containing a file's DIRENT in its parent.
1274 
1275 Arguments:
1276 
1277     Fcb - Supplies the file whose DIRENT is being flushed
1278 
1279 Return Value:
1280 
1281     VOID
1282 
1283 --*/
1284 
1285 {
1286     LARGE_INTEGER FileOffset;
1287     IO_STATUS_BLOCK Iosb;
1288 
1289     PAGED_CODE();
1290 
1291     FileOffset.QuadPart = Fcb->DirentOffsetWithinDirectory;
1292 
1293     CcFlushCache( &Fcb->ParentDcb->NonPaged->SectionObjectPointers,
1294                   &FileOffset,
1295                   sizeof( DIRENT ),
1296                   &Iosb );
1297 
1298     if (NT_SUCCESS(Iosb.Status)) {
1299         Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext,
1300                                                   IrpContext->OriginatingIrp,
1301                                                   Fcb->Vcb->TargetDeviceObject );
1302     }
1303 
1304     if (!NT_SUCCESS(Iosb.Status)) {
1305         FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status);
1306     }
1307 }
1308 
1309 
1310 //
1311 //  Local support routine
1312 //
1313 
1314 NTSTATUS
1315 NTAPI
1316 FatFlushCompletionRoutine (
1317     IN PDEVICE_OBJECT DeviceObject,
1318     IN PIRP Irp,
1319     IN PVOID Contxt
1320     )
1321 
1322 {
1323     NTSTATUS Status = (NTSTATUS) (ULONG_PTR) Contxt;
1324 
1325     if ( Irp->PendingReturned ) {
1326 
1327         IoMarkIrpPending( Irp );
1328     }
1329 
1330     //
1331     //  If the Irp got STATUS_INVALID_DEVICE_REQUEST, normalize it
1332     //  to STATUS_SUCCESS.
1333     //
1334 
1335     if (NT_SUCCESS(Irp->IoStatus.Status) ||
1336         (Irp->IoStatus.Status == STATUS_INVALID_DEVICE_REQUEST)) {
1337 
1338         Irp->IoStatus.Status = Status;
1339     }
1340 
1341     UNREFERENCED_PARAMETER( DeviceObject );
1342     UNREFERENCED_PARAMETER( Contxt );
1343 
1344     return STATUS_SUCCESS;
1345 }
1346 
1347 //
1348 //  Local support routine
1349 //
1350 
1351 NTSTATUS
1352 NTAPI
1353 FatHijackCompletionRoutine (
1354     _In_ PDEVICE_OBJECT DeviceObject,
1355     _In_ PIRP Irp,
1356     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
1357     )
1358 
1359 {
1360     //
1361     //  Set the event so that our call will wake up.
1362     //
1363 
1364     KeSetEvent( (PKEVENT)Contxt, 0, FALSE );
1365 
1366     UNREFERENCED_PARAMETER( DeviceObject );
1367     UNREFERENCED_PARAMETER( Irp );
1368 
1369     return STATUS_MORE_PROCESSING_REQUIRED;
1370 }
1371 
1372