[FASTFAT] Fix size checking in VfatGetFileNameInformation()
[reactos.git] / drivers / filesystems / fastfat_new / flush.c
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)\f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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