1 /*++
2 
3 
4 Copyright (c) 1989-2000 Microsoft Corporation
5 
6 Module Name:
7 
8     FsCtrl.c
9 
10 Abstract:
11 
12     This module implements the File System Control routines for Fat called
13     by the dispatch driver.
14 
15 
16 --*/
17 
18 #include "fatprocs.h"
19 
20 //
21 //  The Bug check file id for this module
22 //
23 
24 #define BugCheckFileId                   (FAT_BUG_CHECK_FSCTRL)
25 
26 //
27 //  The local debug trace level
28 //
29 
30 #define Dbg                              (DEBUG_TRACE_FSCTRL)
31 
32 //
33 //  Local procedure prototypes
34 //
35 
36 _Requires_lock_held_(_Global_critical_region_)
37 NTSTATUS
38 FatMountVolume (
39     IN PIRP_CONTEXT IrpContext,
40     IN PDEVICE_OBJECT TargetDeviceObject,
41     IN PVPB Vpb,
42     IN PDEVICE_OBJECT FsDeviceObject
43     );
44 
45 _Requires_lock_held_(_Global_critical_region_)
46 NTSTATUS
47 FatVerifyVolume (
48     IN PIRP_CONTEXT IrpContext,
49     IN PIRP Irp
50     );
51 
52 BOOLEAN
53 FatIsMediaWriteProtected (
54     IN PIRP_CONTEXT IrpContext,
55     IN PDEVICE_OBJECT TargetDeviceObject
56     );
57 
58 _Requires_lock_held_(_Global_critical_region_)
59 NTSTATUS
60 FatUserFsCtrl (
61     IN PIRP_CONTEXT IrpContext,
62     IN PIRP Irp
63     );
64 
65 _Requires_lock_held_(_Global_critical_region_)
66 NTSTATUS
67 FatOplockRequest (
68     _In_ PIRP_CONTEXT IrpContext,
69     _In_ PIRP Irp
70     );
71 
72 _Requires_lock_held_(_Global_critical_region_)
73 NTSTATUS
74 FatLockVolume (
75     IN PIRP_CONTEXT IrpContext,
76     IN PIRP Irp
77     );
78 
79 NTSTATUS
80 FatUnlockVolume (
81     IN PIRP_CONTEXT IrpContext,
82     IN PIRP Irp
83     );
84 
85 _Requires_lock_held_(_Global_critical_region_)
86 NTSTATUS
87 FatDismountVolume (
88     IN PIRP_CONTEXT IrpContext,
89     IN PIRP Irp
90     );
91 
92 _Requires_lock_held_(_Global_critical_region_)
93 NTSTATUS
94 FatDirtyVolume (
95     IN PIRP_CONTEXT IrpContext,
96     IN PIRP Irp
97     );
98 
99 NTSTATUS
100 FatIsVolumeDirty (
101     IN PIRP_CONTEXT IrpContext,
102     IN PIRP Irp
103     );
104 
105 NTSTATUS
106 FatIsVolumeMounted (
107     IN PIRP_CONTEXT IrpContext,
108     IN PIRP Irp
109     );
110 
111 NTSTATUS
112 FatIsPathnameValid (
113     IN PIRP_CONTEXT IrpContext,
114     IN PIRP Irp
115     );
116 
117 _Requires_lock_held_(_Global_critical_region_)
118 NTSTATUS
119 FatInvalidateVolumes (
120     IN PIRP Irp
121     );
122 
123 _Requires_lock_held_(_Global_critical_region_)
124 VOID
125 FatScanForDismountedVcb (
126     IN PIRP_CONTEXT IrpContext
127     );
128 
129 BOOLEAN
130 FatPerformVerifyDiskRead (
131     IN PIRP_CONTEXT IrpContext,
132     IN PVCB Vcb,
133     IN PVOID Buffer,
134     IN LBO Lbo,
135     IN ULONG NumberOfBytesToRead,
136     IN BOOLEAN ReturnOnError
137     );
138 
139 _Requires_lock_held_(_Global_critical_region_)
140 NTSTATUS
141 FatQueryRetrievalPointers (
142     IN PIRP_CONTEXT IrpContext,
143     IN PIRP Irp
144     );
145 
146 NTSTATUS
147 FatQueryBpb (
148     IN PIRP_CONTEXT IrpContext,
149     IN PIRP Irp
150     );
151 
152 NTSTATUS
153 FatGetStatistics (
154     IN PIRP_CONTEXT IrpContext,
155     IN PIRP Irp
156     );
157 
158 NTSTATUS
159 FatAllowExtendedDasdIo (
160     IN PIRP_CONTEXT IrpContext,
161     IN PIRP Irp
162     );
163 
164 _Requires_lock_held_(_Global_critical_region_)
165 NTSTATUS
166 FatGetBootAreaInfo (
167     _In_ PIRP_CONTEXT IrpContext,
168     _In_ PIRP Irp
169     );
170 
171 _Requires_lock_held_(_Global_critical_region_)
172 NTSTATUS
173 FatGetRetrievalPointerBase (
174     _In_ PIRP_CONTEXT IrpContext,
175     _In_ PIRP Irp
176     );
177 
178 _Requires_lock_held_(_Global_critical_region_)
179 NTSTATUS
180 FatMarkHandle(
181     _In_ PIRP_CONTEXT IrpContext,
182     _In_ PIRP Irp
183     );
184 
185 NTSTATUS
186 FatSetZeroOnDeallocate (
187     __in PIRP_CONTEXT IrpContext,
188     __in PIRP Irp
189     );
190 
191 _Requires_lock_held_(_Global_critical_region_)
192 NTSTATUS
193 FatSetPurgeFailureMode (
194     _In_ PIRP_CONTEXT IrpContext,
195     _In_ PIRP Irp
196     );
197 
198 //
199 //  Local support routine prototypes
200 //
201 
202 _Requires_lock_held_(_Global_critical_region_)
203 NTSTATUS
204 FatGetVolumeBitmap (
205     IN PIRP_CONTEXT IrpContext,
206     IN PIRP Irp
207     );
208 
209 _Requires_lock_held_(_Global_critical_region_)
210 NTSTATUS
211 FatGetRetrievalPointers (
212     IN PIRP_CONTEXT IrpContext,
213     IN PIRP Irp
214     );
215 
216 _Requires_lock_held_(_Global_critical_region_)
217 VOID
218 FatMoveFileNeedsWriteThrough (
219     _In_ PIRP_CONTEXT IrpContext,
220     _In_ PFCB FcbOrDcb,
221     _In_ ULONG OldWriteThroughFlags
222     );
223 
224 _Requires_lock_held_(_Global_critical_region_)
225 NTSTATUS
226 FatMoveFile (
227     IN PIRP_CONTEXT IrpContext,
228     IN PIRP Irp
229     );
230 
231 VOID
232 FatComputeMoveFileSplicePoints (
233     PIRP_CONTEXT IrpContext,
234     PFCB FcbOrDcb,
235     ULONG FileOffset,
236     ULONG TargetCluster,
237     ULONG BytesToReallocate,
238     PULONG FirstSpliceSourceCluster,
239     PULONG FirstSpliceTargetCluster,
240     PULONG SecondSpliceSourceCluster,
241     PULONG SecondSpliceTargetCluster,
242     PLARGE_MCB SourceMcb
243 );
244 
245 _Requires_lock_held_(_Global_critical_region_)
246 VOID
247 FatComputeMoveFileParameter (
248     IN PIRP_CONTEXT IrpContext,
249     IN PFCB FcbOrDcb,
250     IN ULONG BufferSize,
251     IN ULONG FileOffset,
252     IN OUT PULONG ByteCount,
253     OUT PULONG BytesToReallocate,
254     OUT PULONG BytesToWrite,
255     OUT PLARGE_INTEGER SourceLbo
256 );
257 
258 NTSTATUS
259 FatSearchBufferForLabel(
260     IN  PIRP_CONTEXT IrpContext,
261     IN  PVPB  Vpb,
262     IN  PVOID Buffer,
263     IN  ULONG Size,
264     OUT PBOOLEAN LabelFound
265 );
266 
267 VOID
268 FatVerifyLookupFatEntry (
269     IN  PIRP_CONTEXT IrpContext,
270     IN  PVCB Vcb,
271     IN  ULONG FatIndex,
272     IN OUT PULONG FatEntry
273     );
274 
275 #ifdef ALLOC_PRAGMA
276 #pragma alloc_text(PAGE, FatAddMcbEntry)
277 #pragma alloc_text(PAGE, FatAllowExtendedDasdIo)
278 #pragma alloc_text(PAGE, FatCommonFileSystemControl)
279 #pragma alloc_text(PAGE, FatComputeMoveFileParameter)
280 #pragma alloc_text(PAGE, FatComputeMoveFileSplicePoints)
281 #pragma alloc_text(PAGE, FatDirtyVolume)
282 #pragma alloc_text(PAGE, FatFsdFileSystemControl)
283 #pragma alloc_text(PAGE, FatGetRetrievalPointerBase)
284 #pragma alloc_text(PAGE, FatGetBootAreaInfo)
285 #pragma alloc_text(PAGE, FatMarkHandle)
286 #pragma alloc_text(PAGE, FatGetRetrievalPointers)
287 #pragma alloc_text(PAGE, FatGetStatistics)
288 #pragma alloc_text(PAGE, FatGetVolumeBitmap)
289 #pragma alloc_text(PAGE, FatIsMediaWriteProtected)
290 #pragma alloc_text(PAGE, FatIsPathnameValid)
291 #pragma alloc_text(PAGE, FatIsVolumeDirty)
292 #pragma alloc_text(PAGE, FatIsVolumeMounted)
293 #pragma alloc_text(PAGE, FatLockVolume)
294 #pragma alloc_text(PAGE, FatLookupLastMcbEntry)
295 #pragma alloc_text(PAGE, FatGetNextMcbEntry)
296 #pragma alloc_text(PAGE, FatMountVolume)
297 #pragma alloc_text(PAGE, FatMoveFileNeedsWriteThrough)
298 #pragma alloc_text(PAGE, FatMoveFile)
299 #pragma alloc_text(PAGE, FatOplockRequest)
300 #pragma alloc_text(PAGE, FatPerformVerifyDiskRead)
301 #pragma alloc_text(PAGE, FatQueryBpb)
302 #pragma alloc_text(PAGE, FatQueryRetrievalPointers)
303 #pragma alloc_text(PAGE, FatRemoveMcbEntry)
304 #pragma alloc_text(PAGE, FatScanForDismountedVcb)
305 #pragma alloc_text(PAGE, FatFlushAndCleanVolume)
306 #pragma alloc_text(PAGE, FatSearchBufferForLabel)
307 #pragma alloc_text(PAGE, FatSetPurgeFailureMode)
308 #pragma alloc_text(PAGE, FatUnlockVolume)
309 #pragma alloc_text(PAGE, FatUserFsCtrl)
310 #pragma alloc_text(PAGE, FatVerifyLookupFatEntry)
311 #pragma alloc_text(PAGE, FatVerifyVolume)
312 #endif
313 
314 #if DBG
315 
316 BOOLEAN FatMoveFileDebug = 0;
317 
318 #endif
319 
320 //
321 //  These wrappers go around the MCB package; we scale the LBO's passed
322 //  in (which can be bigger than 32 bits on fat32) by the volume's sector
323 //  size.
324 //
325 //  Note we now use the real large mcb package.  This means these shims
326 //  now also convert the -1 unused LBN number to the 0 of the original
327 //  mcb package.
328 //
329 
330 #define     MCB_SCALE_LOG2      (Vcb->AllocationSupport.LogOfBytesPerSector)
331 #define     MCB_SCALE           (1 << MCB_SCALE_LOG2)
332 #define     MCB_SCALE_MODULO    (MCB_SCALE - 1)
333 
334 BOOLEAN
FatNonSparseMcb(_In_ PVCB Vcb,_In_ PLARGE_MCB Mcb,_Out_ PVBO Vbo,_Out_ PLONGLONG ByteCount)335 FatNonSparseMcb(
336     _In_ PVCB Vcb,
337     _In_ PLARGE_MCB Mcb,
338     _Out_ PVBO Vbo,
339     _Out_ PLONGLONG ByteCount
340     )
341 {
342     LBO Lbo;
343     ULONG Index = 0;
344     LONGLONG llVbo = 0;
345 
346     UNREFERENCED_PARAMETER(Vcb);
347 
348     while (FsRtlGetNextLargeMcbEntry(Mcb, Index, &llVbo, &Lbo, ByteCount)) {
349         *Vbo = (VBO)llVbo;
350         if (((ULONG)Lbo) == -1) {
351             return FALSE;
352         }
353 
354         Index++;
355     }
356 
357     *Vbo = (VBO)llVbo;
358 
359     return TRUE;
360 }
361 
362 
363 BOOLEAN
FatAddMcbEntry(IN PVCB Vcb,IN PLARGE_MCB Mcb,IN VBO Vbo,IN LBO Lbo,IN ULONG SectorCount)364 FatAddMcbEntry (
365     IN PVCB Vcb,
366     IN PLARGE_MCB Mcb,
367     IN VBO Vbo,
368     IN LBO Lbo,
369     IN ULONG SectorCount
370     )
371 
372 {
373     BOOLEAN Result;
374 #if DBG
375     VBO SparseVbo;
376     LONGLONG SparseByteCount;
377 #endif
378 
379     PAGED_CODE();
380 
381     if (SectorCount) {
382 
383         //
384         //  Round up sectors, but be careful as SectorCount approaches 4Gb.
385         //  Note that for x>0, (x+m-1)/m = ((x-1)/m)+(m/m) = ((x-1)/m)+1
386         //
387 
388         SectorCount--;
389         SectorCount >>= MCB_SCALE_LOG2;
390         SectorCount++;
391     }
392 
393     Vbo >>= MCB_SCALE_LOG2;
394     Lbo >>= MCB_SCALE_LOG2;
395 
396     NT_ASSERT( SectorCount != 0 );
397 
398     if (Mcb != &Vcb->DirtyFatMcb) {
399         NT_ASSERT( FatNonSparseMcb( Vcb, Mcb, &SparseVbo, &SparseByteCount ) ||
400                 ((SparseVbo == Vbo) && (SparseByteCount == SectorCount )) );
401     }
402 
403     Result = FsRtlAddLargeMcbEntry( Mcb,
404                                   ((LONGLONG) Vbo),
405                                   ((LONGLONG) Lbo),
406                                   ((LONGLONG) SectorCount) );
407 
408     if (Mcb != &Vcb->DirtyFatMcb) {
409         NT_ASSERT( FatNonSparseMcb( Vcb, Mcb, &SparseVbo, &SparseByteCount ) ||
410                 ((SparseVbo == Vbo) && (SparseByteCount == SectorCount )) );
411     }
412 
413     return Result;
414 }
415 
416 
417 BOOLEAN
FatLookupMcbEntry(IN PVCB Vcb,IN PLARGE_MCB Mcb,IN VBO Vbo,OUT PLBO Lbo,OUT PULONG ByteCount OPTIONAL,OUT PULONG Index OPTIONAL)418 FatLookupMcbEntry (
419     IN PVCB Vcb,
420     IN PLARGE_MCB Mcb,
421     IN VBO Vbo,
422     OUT PLBO Lbo,
423     OUT PULONG ByteCount OPTIONAL,
424     OUT PULONG Index OPTIONAL
425     )
426 {
427     BOOLEAN Results;
428     LONGLONG LiLbo;
429     LONGLONG LiSectorCount;
430     ULONG Remainder;
431 
432     LiLbo = 0;
433     LiSectorCount = 0;
434 
435     Remainder = Vbo & MCB_SCALE_MODULO;
436 
437     Results = FsRtlLookupLargeMcbEntry( Mcb,
438                                         (Vbo >> MCB_SCALE_LOG2),
439                                         &LiLbo,
440                                         ARGUMENT_PRESENT(ByteCount) ? &LiSectorCount : NULL,
441                                         NULL,
442                                         NULL,
443                                         Index );
444 
445     if ((ULONG) LiLbo != -1) {
446 
447         *Lbo = (((LBO) LiLbo) << MCB_SCALE_LOG2);
448 
449         if (Results) {
450 
451             *Lbo += Remainder;
452         }
453 
454     } else {
455 
456         *Lbo = 0;
457     }
458 
459     if (ARGUMENT_PRESENT(ByteCount)) {
460 
461         *ByteCount = (ULONG) LiSectorCount;
462 
463         if (*ByteCount) {
464 
465             *ByteCount <<= MCB_SCALE_LOG2;
466 
467             //
468             //  If ByteCount overflows, then this is likely the case of
469             //  a file of max-supported size (4GiB - 1), allocated in a
470             //  single continuous run.
471             //
472 
473             if (*ByteCount == 0) {
474 
475                 *ByteCount = 0xFFFFFFFF;
476             }
477 
478             if (Results) {
479 
480                 *ByteCount -= Remainder;
481             }
482         }
483 
484     }
485 
486     return Results;
487 }
488 
489 //
490 //  NOTE: Vbo/Lbn undefined if MCB is empty & return code false.
491 //
492 
493 BOOLEAN
FatLookupLastMcbEntry(IN PVCB Vcb,IN PLARGE_MCB Mcb,OUT PVBO Vbo,OUT PLBO Lbo,OUT PULONG Index)494 FatLookupLastMcbEntry (
495     IN PVCB Vcb,
496     IN PLARGE_MCB Mcb,
497     OUT PVBO Vbo,
498     OUT PLBO Lbo,
499     OUT PULONG Index
500     )
501 
502 {
503     BOOLEAN Results;
504     LONGLONG LiVbo;
505     LONGLONG LiLbo;
506     ULONG LocalIndex;
507 
508     PAGED_CODE();
509 
510     LiVbo = LiLbo = 0;
511     LocalIndex = 0;
512 
513     Results = FsRtlLookupLastLargeMcbEntryAndIndex( Mcb,
514                                                     &LiVbo,
515                                                     &LiLbo,
516                                                     &LocalIndex );
517 
518     *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2;
519 
520     if (((ULONG) LiLbo) != -1) {
521 
522         *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2;
523 
524         *Lbo += (MCB_SCALE - 1);
525         *Vbo += (MCB_SCALE - 1);
526 
527     } else {
528 
529         *Lbo = 0;
530     }
531 
532     if (Index) {
533         *Index = LocalIndex;
534     }
535 
536     return Results;
537 }
538 
539 
540 BOOLEAN
FatGetNextMcbEntry(IN PVCB Vcb,IN PLARGE_MCB Mcb,IN ULONG RunIndex,OUT PVBO Vbo,OUT PLBO Lbo,OUT PULONG ByteCount)541 FatGetNextMcbEntry (
542     IN PVCB Vcb,
543     IN PLARGE_MCB Mcb,
544     IN ULONG RunIndex,
545     OUT PVBO Vbo,
546     OUT PLBO Lbo,
547     OUT PULONG ByteCount
548     )
549 
550 {
551     BOOLEAN Results;
552     LONGLONG LiVbo;
553     LONGLONG LiLbo;
554     LONGLONG LiSectorCount;
555 
556     PAGED_CODE();
557 
558     LiVbo = LiLbo = 0;
559 
560     Results = FsRtlGetNextLargeMcbEntry( Mcb,
561                                          RunIndex,
562                                          &LiVbo,
563                                          &LiLbo,
564                                          &LiSectorCount );
565 
566     if (Results) {
567 
568         *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2;
569 
570         if (((ULONG) LiLbo) != -1) {
571 
572             *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2;
573 
574         } else {
575 
576             *Lbo = 0;
577         }
578 
579         *ByteCount = ((ULONG) LiSectorCount) << MCB_SCALE_LOG2;
580 
581         if ((*ByteCount == 0) && (LiSectorCount != 0)) {
582 
583             //
584             //  If 'ByteCount' overflows, then this is likely a file of
585             //  max supported size (2^32 - 1) in one contiguous run.
586             //
587 
588             NT_ASSERT( RunIndex == 0 );
589 
590             *ByteCount = 0xFFFFFFFF;
591         }
592     }
593 
594     return Results;
595 }
596 
597 
598 VOID
FatRemoveMcbEntry(IN PVCB Vcb,IN PLARGE_MCB Mcb,IN VBO Vbo,IN ULONG SectorCount)599 FatRemoveMcbEntry (
600     IN PVCB Vcb,
601     IN PLARGE_MCB Mcb,
602     IN VBO Vbo,
603     IN ULONG SectorCount
604     )
605 {
606     PAGED_CODE();
607 
608     if ((SectorCount) && (SectorCount != 0xFFFFFFFF)) {
609 
610         SectorCount--;
611         SectorCount >>= MCB_SCALE_LOG2;
612         SectorCount++;
613     }
614 
615     Vbo >>= MCB_SCALE_LOG2;
616 
617 #if DBG
618     _SEH2_TRY {
619 #endif
620 
621         FsRtlRemoveLargeMcbEntry( Mcb,
622                                   (LONGLONG) Vbo,
623                                   (LONGLONG) SectorCount);
624 
625 #if DBG
626     } _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) {
627 
628           NOTHING;
629     } _SEH2_END;
630 #endif
631 
632 }
633 
634 
635 _Function_class_(IRP_MJ_FILE_SYSTEM_CONTROL)
_Function_class_(DRIVER_DISPATCH)636 _Function_class_(DRIVER_DISPATCH)
637 NTSTATUS
638 NTAPI
639 FatFsdFileSystemControl (
640     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
641     _Inout_ PIRP Irp
642     )
643 
644 /*++
645 
646 Routine Description:
647 
648     This routine implements the FSD part of FileSystem control operations
649 
650 Arguments:
651 
652     VolumeDeviceObject - Supplies the volume device object where the
653         file exists
654 
655     Irp - Supplies the Irp being processed
656 
657 Return Value:
658 
659     NTSTATUS - The FSD status for the IRP
660 
661 --*/
662 
663 {
664     BOOLEAN Wait;
665     NTSTATUS Status;
666     PIRP_CONTEXT IrpContext = NULL;
667 
668     BOOLEAN TopLevel;
669 
670     PAGED_CODE();
671     UNREFERENCED_PARAMETER( VolumeDeviceObject );
672 
673     DebugTrace(+1, Dbg,"FatFsdFileSystemControl\n", 0);
674 
675     //
676     //  Call the common FileSystem Control routine, with blocking allowed if
677     //  synchronous.  This opeation needs to special case the mount
678     //  and verify suboperations because we know they are allowed to block.
679     //  We identify these suboperations by looking at the file object field
680     //  and seeing if its null.
681     //
682 
683     if (IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL) {
684 
685         Wait = TRUE;
686 
687     } else {
688 
689         Wait = CanFsdWait( Irp );
690     }
691 
692     FsRtlEnterFileSystem();
693 
694     TopLevel = FatIsIrpTopLevel( Irp );
695 
696     _SEH2_TRY {
697 
698         PIO_STACK_LOCATION IrpSp;
699 
700         IrpSp = IoGetCurrentIrpStackLocation( Irp );
701 
702         //
703         //  We need to made a special check here for the InvalidateVolumes
704         //  FSCTL as that comes in with a FileSystem device object instead
705         //  of a volume device object.
706         //
707 
708         if (FatDeviceIsFatFsdo( IrpSp->DeviceObject) &&
709             (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
710             (IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST) &&
711             (IrpSp->Parameters.FileSystemControl.FsControlCode ==
712              FSCTL_INVALIDATE_VOLUMES)) {
713 
714             Status = FatInvalidateVolumes( Irp );
715 
716         } else {
717 
718             IrpContext = FatCreateIrpContext( Irp, Wait );
719 
720             Status = FatCommonFileSystemControl( IrpContext, Irp );
721         }
722 
723     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
724 
725         //
726         //  We had some trouble trying to perform the requested
727         //  operation, so we'll abort the I/O request with
728         //  the error status that we get back from the
729         //  execption code
730         //
731 
732         Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
733     } _SEH2_END;
734 
735     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
736 
737     FsRtlExitFileSystem();
738 
739     //
740     //  And return to our caller
741     //
742 
743     DebugTrace(-1, Dbg, "FatFsdFileSystemControl -> %08lx\n", Status);
744 
745     return Status;
746 }
747 
748 
_Requires_lock_held_(_Global_critical_region_)749 _Requires_lock_held_(_Global_critical_region_)
750 NTSTATUS
751 FatCommonFileSystemControl (
752     IN PIRP_CONTEXT IrpContext,
753     IN PIRP Irp
754     )
755 
756 /*++
757 
758 Routine Description:
759 
760     This is the common routine for doing FileSystem control operations called
761     by both the fsd and fsp threads
762 
763 Arguments:
764 
765     Irp - Supplies the Irp to process
766 
767 Return Value:
768 
769     NTSTATUS - The return status for the operation
770 
771 --*/
772 
773 {
774     NTSTATUS Status;
775     PIO_STACK_LOCATION IrpSp;
776 
777     PAGED_CODE();
778 
779     //
780     //  Get a pointer to the current Irp stack location
781     //
782 
783     IrpSp = IoGetCurrentIrpStackLocation( Irp );
784 
785     DebugTrace(+1, Dbg,"FatCommonFileSystemControl\n", 0);
786     DebugTrace( 0, Dbg,"Irp           = %p\n", Irp);
787     DebugTrace( 0, Dbg,"MinorFunction = %08lx\n", IrpSp->MinorFunction);
788 
789     //
790     //  We know this is a file system control so we'll case on the
791     //  minor function, and call a internal worker routine to complete
792     //  the irp.
793     //
794 
795     switch (IrpSp->MinorFunction) {
796 
797     case IRP_MN_USER_FS_REQUEST:
798 
799         Status = FatUserFsCtrl( IrpContext, Irp );
800         break;
801 
802     case IRP_MN_MOUNT_VOLUME:
803 
804         Status = FatMountVolume( IrpContext,
805                                  IrpSp->Parameters.MountVolume.DeviceObject,
806                                  IrpSp->Parameters.MountVolume.Vpb,
807                                  IrpSp->DeviceObject );
808 
809         //
810         //  Complete the request.
811         //
812         //  We do this here because FatMountVolume can be called recursively,
813         //  but the Irp is only to be completed once.
814         //
815         //  NOTE: I don't think this is true anymore (danlo 3/15/1999).  Probably
816         //  an artifact of the old doublespace attempt.
817         //
818 
819         FatCompleteRequest( IrpContext, Irp, Status );
820         break;
821 
822     case IRP_MN_VERIFY_VOLUME:
823 
824         Status = FatVerifyVolume( IrpContext, Irp );
825         break;
826 
827     default:
828 
829         DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction);
830 
831         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
832         Status = STATUS_INVALID_DEVICE_REQUEST;
833         break;
834     }
835 
836     DebugTrace(-1, Dbg, "FatCommonFileSystemControl -> %08lx\n", Status);
837 
838     return Status;
839 }
840 
841 
842 //
843 //  Local Support Routine
844 //
845 
_Requires_lock_held_(_Global_critical_region_)846 _Requires_lock_held_(_Global_critical_region_)
847 NTSTATUS
848 FatMountVolume (
849     IN PIRP_CONTEXT IrpContext,
850     IN PDEVICE_OBJECT TargetDeviceObject,
851     IN PVPB Vpb,
852     IN PDEVICE_OBJECT FsDeviceObject
853     )
854 
855 /*++
856 
857 Routine Description:
858 
859     This routine performs the mount volume operation.  It is responsible for
860     either completing of enqueuing the input Irp.
861 
862     Its job is to verify that the volume denoted in the IRP is a Fat volume,
863     and create the VCB and root DCB structures.  The algorithm it uses is
864     essentially as follows:
865 
866     1. Create a new Vcb Structure, and initialize it enough to do cached
867        volume file I/O.
868 
869     2. Read the disk and check if it is a Fat volume.
870 
871     3. If it is not a Fat volume then free the cached volume file, delete
872        the VCB, and complete the IRP with STATUS_UNRECOGNIZED_VOLUME
873 
874     4. Check if the volume was previously mounted and if it was then do a
875        remount operation.  This involves reinitializing the cached volume
876        file, checking the dirty bit, resetting up the allocation support,
877        deleting the VCB, hooking in the old VCB, and completing the IRP.
878 
879     5. Otherwise create a root DCB, create Fsp threads as necessary, and
880        complete the IRP.
881 
882 Arguments:
883 
884     TargetDeviceObject - This is where we send all of our requests.
885 
886     Vpb - This gives us additional information needed to complete the mount.
887 
888 Return Value:
889 
890     NTSTATUS - The return status for the operation
891 
892 --*/
893 
894 {
895     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
896     NTSTATUS Status = STATUS_INVALID_PARAMETER;
897 
898     PBCB BootBcb;
899     PPACKED_BOOT_SECTOR BootSector = NULL;
900 
901     PBCB DirentBcb;
902     PDIRENT Dirent;
903     ULONG ByteOffset;
904 
905     BOOLEAN MountNewVolume = FALSE;
906     BOOLEAN WeClearedVerifyRequiredBit = FALSE;
907     BOOLEAN DoARemount = FALSE;
908 
909     PVCB OldVcb = NULL;
910     PVPB OldVpb = NULL;
911 
912     PDEVICE_OBJECT RealDevice = NULL;
913     PVOLUME_DEVICE_OBJECT VolDo = NULL;
914     PVCB Vcb = NULL;
915     PFILE_OBJECT RootDirectoryFile = NULL;
916 
917     PLIST_ENTRY Links;
918 
919     IO_STATUS_BLOCK Iosb = {0};
920     ULONG ChangeCount = 0;
921 
922     DISK_GEOMETRY Geometry;
923 
924     PARTITION_INFORMATION_EX PartitionInformation;
925     NTSTATUS StatusPartInfo;
926 
927 #if (NTDDI_VERSION > NTDDI_WIN8)
928     GUID VolumeGuid = {0};
929 #endif
930 
931 
932     PAGED_CODE();
933 
934     DebugTrace(+1, Dbg, "FatMountVolume\n", 0);
935     DebugTrace( 0, Dbg, "TargetDeviceObject = %p\n", TargetDeviceObject);
936     DebugTrace( 0, Dbg, "Vpb                = %p\n", Vpb);
937 
938     NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
939     NT_ASSERT( FatDeviceIsFatFsdo( FsDeviceObject));
940 
941     //
942     // Only send down IOCTL_DISK_CHECK_VERIFY if it is removable media.
943     //
944 
945     if (FlagOn(TargetDeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
946 
947         //
948         //  Verify that there is a disk here and pick up the change count.
949         //
950 
951         Status = FatPerformDevIoCtrl( IrpContext,
952                                       IOCTL_DISK_CHECK_VERIFY,
953                                       TargetDeviceObject,
954                                       NULL,
955                                       0,
956                                       &ChangeCount,
957                                       sizeof(ULONG),
958                                       FALSE,
959                                       TRUE,
960                                       &Iosb );
961 
962         if (!NT_SUCCESS( Status )) {
963 
964             //
965             //  If we will allow a raw mount then avoid sending the popup.
966             //
967             //  Only send this on "true" disk devices to handle the accidental
968             //  legacy of FAT. No other FS will throw a harderror on empty
969             //  drives.
970             //
971             //  Cmd should really handle this per 9x.
972             //
973 
974             if (!FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT ) &&
975                 Vpb->RealDevice->DeviceType == FILE_DEVICE_DISK) {
976 
977                 FatNormalizeAndRaiseStatus( IrpContext, Status );
978             }
979 
980             return Status;
981         }
982 
983     }
984 
985     if (Iosb.Information != sizeof(ULONG)) {
986 
987         //
988         //  Be safe about the count in case the driver didn't fill it in
989         //
990 
991         ChangeCount = 0;
992     }
993 
994     //
995     //  If this is a CD class device,  then check to see if there is a
996     //  'data track' or not.  This is to avoid issuing paging reads which will
997     //  fail later in the mount process (e.g. CD-DA or blank CD media)
998     //
999 
1000     if ((TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) &&
1001         !FatScanForDataTrack( IrpContext, TargetDeviceObject))  {
1002 
1003         return STATUS_UNRECOGNIZED_VOLUME;
1004     }
1005 
1006     //
1007     //  Ping the volume with a partition query and pick up the partition
1008     //  type.  We'll check this later to avoid some scurrilous volumes.
1009     //
1010 
1011     StatusPartInfo = FatPerformDevIoCtrl( IrpContext,
1012                                           IOCTL_DISK_GET_PARTITION_INFO_EX,
1013                                           TargetDeviceObject,
1014                                           NULL,
1015                                           0,
1016                                           &PartitionInformation,
1017                                           sizeof(PARTITION_INFORMATION_EX),
1018                                           FALSE,
1019                                           TRUE,
1020                                           &Iosb );
1021 
1022     //
1023     //  Make sure we can wait.
1024     //
1025 
1026     SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
1027 
1028     //
1029     //  Do a quick check to see if there any Vcb's which can be removed.
1030     //
1031 
1032     FatScanForDismountedVcb( IrpContext );
1033 
1034     //
1035     //  Initialize the Bcbs and our final state so that the termination
1036     //  handlers will know what to free or unpin
1037     //
1038 
1039     BootBcb = NULL;
1040     DirentBcb = NULL;
1041 
1042     Vcb = NULL;
1043     VolDo = NULL;
1044     MountNewVolume = FALSE;
1045 
1046     _SEH2_TRY {
1047 
1048         //
1049         //  Synchronize with FatCheckForDismount(), which modifies the vpb.
1050         //
1051 
1052 #ifdef _MSC_VER
1053 #pragma prefast( push )
1054 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
1055 #pragma prefast( disable: 28193, "this will always wait" )
1056 #endif
1057 
1058         (VOID)FatAcquireExclusiveGlobal( IrpContext );
1059 
1060 #ifdef _MSC_VER
1061 #pragma prefast( pop )
1062 #endif
1063 
1064         //
1065         //  Create a new volume device object.  This will have the Vcb
1066         //  hanging off of its end, and set its alignment requirement
1067         //  from the device we talk to.
1068         //
1069 
1070         if (!NT_SUCCESS(Status = IoCreateDevice( FatData.DriverObject,
1071                                                  sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT),
1072                                                  NULL,
1073                                                  FILE_DEVICE_DISK_FILE_SYSTEM,
1074                                                  0,
1075                                                  FALSE,
1076                                                  (PDEVICE_OBJECT *)&VolDo))) {
1077 
1078             try_return( Status );
1079         }
1080 
1081         //
1082         //  Our alignment requirement is the larger of the processor alignment requirement
1083         //  already in the volume device object and that in the TargetDeviceObject
1084         //
1085 
1086         if (TargetDeviceObject->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) {
1087 
1088             VolDo->DeviceObject.AlignmentRequirement = TargetDeviceObject->AlignmentRequirement;
1089         }
1090 
1091         //
1092         //  Initialize the overflow queue for the volume
1093         //
1094 
1095         VolDo->OverflowQueueCount = 0;
1096         InitializeListHead( &VolDo->OverflowQueue );
1097 
1098         VolDo->PostedRequestCount = 0;
1099         KeInitializeSpinLock( &VolDo->OverflowQueueSpinLock );
1100 
1101         //
1102         //  We must initialize the stack size in our device object before
1103         //  the following reads, because the I/O system has not done it yet.
1104         //  This must be done before we clear the device initializing flag
1105         //  otherwise a filter could attach and copy the wrong stack size into
1106         //  it's device object.
1107         //
1108 
1109         VolDo->DeviceObject.StackSize = (CCHAR)(TargetDeviceObject->StackSize + 1);
1110 
1111         //
1112         //  We must also set the sector size correctly in our device object
1113         //  before clearing the device initializing flag.
1114         //
1115 
1116         Status = FatPerformDevIoCtrl( IrpContext,
1117                                       IOCTL_DISK_GET_DRIVE_GEOMETRY,
1118                                       TargetDeviceObject,
1119                                       NULL,
1120                                       0,
1121                                       &Geometry,
1122                                       sizeof( DISK_GEOMETRY ),
1123                                       FALSE,
1124                                       TRUE,
1125                                       NULL );
1126 
1127         if (!NT_SUCCESS( Status )) {
1128 
1129             try_return( Status );
1130         }
1131 
1132 #ifdef _MSC_VER
1133 #pragma prefast( suppress: 28175, "this is a filesystem driver, touching SectorSize is fine" )
1134 #endif
1135         VolDo->DeviceObject.SectorSize = (USHORT)Geometry.BytesPerSector;
1136 
1137         //
1138         //  Indicate that this device object is now completely initialized
1139         //
1140 
1141         ClearFlag(VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING);
1142 
1143         //
1144         //  Now Before we can initialize the Vcb we need to set up the device
1145         //  object field in the Vpb to point to our new volume device object.
1146         //  This is needed when we create the virtual volume file's file object
1147         //  in initialize vcb.
1148         //
1149 
1150         Vpb->DeviceObject = (PDEVICE_OBJECT)VolDo;
1151 
1152         //
1153         //  If the real device needs verification, temporarily clear the
1154         //  field.
1155         //
1156 
1157         RealDevice = Vpb->RealDevice;
1158 
1159         if ( FlagOn(RealDevice->Flags, DO_VERIFY_VOLUME) ) {
1160 
1161             ClearFlag(RealDevice->Flags, DO_VERIFY_VOLUME);
1162 
1163             WeClearedVerifyRequiredBit = TRUE;
1164         }
1165 
1166         //
1167         //  Initialize the new vcb
1168         //
1169 
1170         FatInitializeVcb( IrpContext,
1171                           &VolDo->Vcb,
1172                           TargetDeviceObject,
1173                           Vpb,
1174                           FsDeviceObject);
1175         //
1176         //  Get a reference to the Vcb hanging off the end of the device object
1177         //
1178 
1179         Vcb = &VolDo->Vcb;
1180 
1181         //
1182         //  Read in the boot sector, and have the read be the minumum size
1183         //  needed.  We know we can wait.
1184         //
1185 
1186         //
1187         //  We need to commute errors on CD so that CDFS will get its crack.  Audio
1188         //  and even data media may not be universally readable on sector zero.
1189         //
1190 
1191         _SEH2_TRY {
1192 
1193             FatReadVolumeFile( IrpContext,
1194                                Vcb,
1195                                0,                          // Starting Byte
1196                                sizeof(PACKED_BOOT_SECTOR),
1197                                &BootBcb,
1198                                (PVOID *)&BootSector );
1199 
1200         } _SEH2_EXCEPT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ?
1201                   EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
1202 
1203               NOTHING;
1204         } _SEH2_END;
1205 
1206         //
1207         //  Call a routine to check the boot sector to see if it is fat
1208         //
1209 
1210         if (BootBcb == NULL || !FatIsBootSectorFat( BootSector)) {
1211 
1212             DebugTrace(0, Dbg, "Not a Fat Volume\n", 0);
1213 
1214             //
1215             //  Complete the request and return to our caller
1216             //
1217 
1218             try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
1219         }
1220 
1221 #if (NTDDI_VERSION > NTDDI_WIN8)
1222         //
1223         //  Initialize the volume guid.
1224         //
1225 
1226         if (NT_SUCCESS( IoVolumeDeviceToGuid( Vcb->TargetDeviceObject, &VolumeGuid ))) {
1227 
1228 
1229             //
1230             // Stash a copy away in the VCB.
1231             //
1232 
1233             RtlCopyMemory( &Vcb->VolumeGuid, &VolumeGuid, sizeof(GUID));
1234 
1235         }
1236 
1237 
1238         //
1239         // Stash away a copy of the volume GUID path in our VCB.
1240         //
1241 
1242         if (Vcb->VolumeGuidPath.Buffer) {
1243             ExFreePool( Vcb->VolumeGuidPath.Buffer );
1244             Vcb->VolumeGuidPath.Buffer = NULL;
1245             Vcb->VolumeGuidPath.Length = Vcb->VolumeGuidPath.MaximumLength = 0;
1246         }
1247 
1248         IoVolumeDeviceToGuidPath( Vcb->TargetDeviceObject, &Vcb->VolumeGuidPath );
1249 #endif
1250 
1251         //
1252         //  Unpack the BPB.  We used to do some sanity checking of the FATs at
1253         //  this point, but authoring errors on third-party devices prevent
1254         //  us from continuing to safeguard ourselves.  We can only hope the
1255         //  boot sector check is good enough.
1256         //
1257         //  (read: digital cameras)
1258         //
1259         //  Win9x does the same.
1260         //
1261 
1262         FatUnpackBios( &Vcb->Bpb, &BootSector->PackedBpb );
1263 
1264         //
1265         //  Check if we have an OS/2 Boot Manager partition and treat it as an
1266         //  unknown file system.  We'll check the partition type in from the
1267         //  partition table and we ensure that it has less than 0x80 sectors,
1268         //  which is just a heuristic that will capture all real OS/2 BM partitions
1269         //  and avoid the chance we'll discover partitions which erroneously
1270         //  (but to this point, harmlessly) put down the OS/2 BM type.
1271         //
1272         //  Note that this is only conceivable on good old MBR media.
1273         //
1274         //  The OS/2 Boot Manager boot format mimics a FAT16 partition in sector
1275         //  zero but does is not a real FAT16 file system.  For example, the boot
1276         //  sector indicates it has 2 FATs but only really has one, with the boot
1277         //  manager code overlaying the second FAT.  If we then set clean bits in
1278         //  FAT[0] we'll corrupt that code.
1279         //
1280 
1281         if (NT_SUCCESS( StatusPartInfo ) &&
1282             (PartitionInformation.PartitionStyle == PARTITION_STYLE_MBR &&
1283              PartitionInformation.Mbr.PartitionType == PARTITION_OS2BOOTMGR) &&
1284             (Vcb->Bpb.Sectors != 0 &&
1285              Vcb->Bpb.Sectors < 0x80)) {
1286 
1287             DebugTrace( 0, Dbg, "OS/2 Boot Manager volume detected, volume not mounted. \n", 0 );
1288 
1289             //
1290             //  Complete the request and return to our caller
1291             //
1292 
1293             try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
1294         }
1295 
1296         //
1297         //  Verify that the sector size recorded in the Bpb matches what the
1298         //  device currently reports it's sector size to be.
1299         //
1300 
1301         if ( !NT_SUCCESS( Status) ||
1302              (Geometry.BytesPerSector != Vcb->Bpb.BytesPerSector))  {
1303 
1304             try_return( Status = STATUS_UNRECOGNIZED_VOLUME );
1305         }
1306 
1307         //
1308         //  This is a fat volume, so extract the bpb, serial number.  The
1309         //  label we'll get later after we've created the root dcb.
1310         //
1311         //  Note that the way data caching is done, we set neither the
1312         //  direct I/O or Buffered I/O bit in the device object flags.
1313         //
1314 
1315         if (Vcb->Bpb.Sectors != 0) { Vcb->Bpb.LargeSectors = 0; }
1316 
1317         if (IsBpbFat32(&BootSector->PackedBpb)) {
1318 
1319             CopyUchar4( &Vpb->SerialNumber, ((PPACKED_BOOT_SECTOR_EX)BootSector)->Id );
1320 
1321         } else  {
1322 
1323             CopyUchar4( &Vpb->SerialNumber, BootSector->Id );
1324 
1325             //
1326             //  Allocate space for the stashed boot sector chunk.  This only has meaning on
1327             //  FAT12/16 volumes since this only is kept for the FSCTL_QUERY_FAT_BPB and it and
1328             //  its users are a bit wierd, thinking that a BPB exists wholly in the first 0x24
1329             //  bytes.
1330             //
1331 
1332             Vcb->First0x24BytesOfBootSector =
1333                 FsRtlAllocatePoolWithTag( PagedPool,
1334                                           0x24,
1335                                           TAG_STASHED_BPB );
1336 
1337             //
1338             //  Stash a copy of the first 0x24 bytes
1339             //
1340 
1341             RtlCopyMemory( Vcb->First0x24BytesOfBootSector,
1342                            BootSector,
1343                            0x24 );
1344         }
1345 
1346         //
1347         //  Now unpin the boot sector, so when we set up allocation eveything
1348         //  works.
1349         //
1350 
1351         FatUnpinBcb( IrpContext, BootBcb );
1352 
1353         //
1354         //  Compute a number of fields for Vcb.AllocationSupport
1355         //
1356 
1357         FatSetupAllocationSupport( IrpContext, Vcb );
1358 
1359         //
1360         //  Sanity check the FsInfo information for FAT32 volumes.  Silently deal
1361         //  with messed up information by effectively disabling FsInfo updates.
1362         //
1363 
1364         if (FatIsFat32( Vcb )) {
1365 
1366             if (Vcb->Bpb.FsInfoSector >= Vcb->Bpb.ReservedSectors) {
1367 
1368                 Vcb->Bpb.FsInfoSector = 0;
1369             }
1370         }
1371 
1372 
1373         //
1374         //  Create a root Dcb so we can read in the volume label.  If this is FAT32, we can
1375         //  discover corruption in the FAT chain.
1376         //
1377         //  NOTE: this exception handler presumes that this is the only spot where we can
1378         //  discover corruption in the mount process.  If this ever changes, this handler
1379         //  MUST be expanded.  The reason we have this guy here is because we have to rip
1380         //  the structures down now (in the finally below) and can't wait for the outer
1381         //  exception handling to do it for us, at which point everything will have vanished.
1382         //
1383 
1384         _SEH2_TRY {
1385 
1386             FatCreateRootDcb( IrpContext, Vcb );
1387 
1388         } _SEH2_EXCEPT (_SEH2_GetExceptionCode() == STATUS_FILE_CORRUPT_ERROR ? EXCEPTION_EXECUTE_HANDLER :
1389                                                                     EXCEPTION_CONTINUE_SEARCH) {
1390 
1391             //
1392             //  The volume needs to be dirtied, do it now.  Note that at this point we have built
1393             //  enough of the Vcb to pull this off.
1394             //
1395 
1396 	    FatCheckDirtyBit( IrpContext,
1397 		              Vcb );
1398 
1399             //
1400             // Set the dirty bit if it is not set already
1401             //
1402 
1403             if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
1404 
1405                 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNT_IN_PROGRESS );
1406                 FatMarkVolume( IrpContext, Vcb, VolumeDirty );
1407                 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNT_IN_PROGRESS );
1408             }
1409 
1410             //
1411             //  Now keep bailing out ...
1412             //
1413 
1414             FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1415         } _SEH2_END;
1416 
1417         FatLocateVolumeLabel( IrpContext,
1418                               Vcb,
1419                               &Dirent,
1420                               &DirentBcb,
1421                               (PVBO)&ByteOffset );
1422 
1423         if (Dirent != NULL) {
1424 
1425             UCHAR OemBuffer[11];
1426             OEM_STRING OemString;
1427             UNICODE_STRING UnicodeString;
1428 
1429             OemString.Buffer = (PCHAR)&OemBuffer[0];
1430             OemString.MaximumLength = 11;
1431 
1432             RtlCopyMemory( OemString.Buffer, Dirent->FileName, 11 );
1433 
1434             //
1435             //  Translate the first character from 0x5 to 0xe5.
1436             //
1437 
1438             if (OemString.Buffer[0] == FAT_DIRENT_REALLY_0E5) {
1439 
1440                 OemString.Buffer[0] = 0xe5;
1441             }
1442 
1443             //
1444             //  Compute the length of the volume name
1445             //
1446 
1447             for ( OemString.Length = 11;
1448                   OemString.Length > 0;
1449                   OemString.Length -= 1) {
1450 
1451                 if ( (OemString.Buffer[OemString.Length-1] != 0x00) &&
1452                      (OemString.Buffer[OemString.Length-1] != 0x20) ) { break; }
1453             }
1454 
1455             UnicodeString.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
1456             UnicodeString.Buffer = &Vcb->Vpb->VolumeLabel[0];
1457 
1458             Status = RtlOemStringToCountedUnicodeString( &UnicodeString,
1459                                                          &OemString,
1460                                                          FALSE );
1461 
1462             if ( !NT_SUCCESS( Status ) ) {
1463 
1464                 try_return( Status );
1465             }
1466 
1467             Vpb->VolumeLabelLength = UnicodeString.Length;
1468 
1469         } else {
1470 
1471             Vpb->VolumeLabelLength = 0;
1472         }
1473 
1474         //
1475         //  Use the change count we noted initially *before* doing any work.
1476         //  If something came along in the midst of this operation, we'll
1477         //  verify and discover the problem.
1478         //
1479 
1480         Vcb->ChangeCount = ChangeCount;
1481 
1482         //
1483         //  Now scan the list of previously mounted volumes and compare
1484         //  serial numbers and volume labels off not currently mounted
1485         //  volumes to see if we have a match.
1486         //
1487 
1488         for (Links = FatData.VcbQueue.Flink;
1489              Links != &FatData.VcbQueue;
1490              Links = Links->Flink) {
1491 
1492             OldVcb = CONTAINING_RECORD( Links, VCB, VcbLinks );
1493             OldVpb = OldVcb->Vpb;
1494 
1495             //
1496             //  Skip over ourselves since we're already in the VcbQueue
1497             //
1498 
1499             if (OldVpb == Vpb) { continue; }
1500 
1501             //
1502             //  Check for a match:
1503             //
1504             //  Serial Number, VolumeLabel and Bpb must all be the same.
1505             //  Also the volume must have failed a verify before (ie.
1506             //  VolumeNotMounted), and it must be in the same physical
1507             //  drive than it was mounted in before.
1508             //
1509 
1510             if ( (OldVpb->SerialNumber == Vpb->SerialNumber) &&
1511                  (OldVcb->VcbCondition == VcbNotMounted) &&
1512                  (OldVpb->RealDevice == RealDevice) &&
1513                  (OldVpb->VolumeLabelLength == Vpb->VolumeLabelLength) &&
1514                  (RtlEqualMemory(&OldVpb->VolumeLabel[0],
1515                                  &Vpb->VolumeLabel[0],
1516                                  Vpb->VolumeLabelLength)) &&
1517                  (RtlEqualMemory(&OldVcb->Bpb,
1518                                  &Vcb->Bpb,
1519                                  IsBpbFat32(&Vcb->Bpb) ?
1520                                      sizeof(BIOS_PARAMETER_BLOCK) :
1521                                      FIELD_OFFSET(BIOS_PARAMETER_BLOCK,
1522                                                   LargeSectorsPerFat) ))) {
1523 
1524                 DoARemount = TRUE;
1525 
1526                 break;
1527             }
1528         }
1529 
1530         if ( DoARemount ) {
1531 
1532             PVPB *IrpVpb;
1533 
1534             DebugTrace(0, Dbg, "Doing a remount\n", 0);
1535             DebugTrace(0, Dbg, "Vcb = %p\n", Vcb);
1536             DebugTrace(0, Dbg, "Vpb = %p\n", Vpb);
1537             DebugTrace(0, Dbg, "OldVcb = %p\n", OldVcb);
1538             DebugTrace(0, Dbg, "OldVpb = %p\n", OldVpb);
1539 
1540             //
1541             //  Swap target device objects between the VCBs. That way
1542             //  the old VCB will start using the new target device object,
1543             //  and the new VCB will be torn down and deference the old
1544             //  target device object.
1545             //
1546 
1547             Vcb->TargetDeviceObject = OldVcb->TargetDeviceObject;
1548             OldVcb->TargetDeviceObject = TargetDeviceObject;
1549 
1550             //
1551             //  This is a remount, so link the old vpb in place
1552             //  of the new vpb.
1553             //
1554 
1555             NT_ASSERT( !FlagOn( OldVcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ) );
1556 
1557             FatSetVcbCondition( OldVcb, VcbGood);
1558             OldVpb->RealDevice = Vpb->RealDevice;
1559             ClearFlag( OldVcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE);
1560 
1561 #ifdef _MSC_VER
1562 #pragma prefast( suppress: 28175, "touching Vpb is ok for a filesystem" )
1563 #endif
1564             OldVpb->RealDevice->Vpb = OldVpb;
1565 
1566             //
1567             //  Use the new changecount.
1568             //
1569 
1570             OldVcb->ChangeCount = Vcb->ChangeCount;
1571 
1572             //
1573             //  If the new VPB is the VPB referenced in the original Irp, set
1574             //  that reference back to the old VPB.
1575             //
1576 
1577             IrpVpb = &IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.MountVolume.Vpb;
1578 
1579             if (*IrpVpb == Vpb) {
1580 
1581                 *IrpVpb = OldVpb;
1582             }
1583 
1584             //
1585             //  We do not want to touch this VPB again.  It will get cleaned up when
1586             //  the new VCB is cleaned up.
1587             //
1588 
1589             NT_ASSERT( Vcb->Vpb == Vpb );
1590 
1591             Vpb = NULL;
1592             SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED );
1593             FatSetVcbCondition( Vcb, VcbBad );
1594 
1595             //
1596             //  Reinitialize the volume file cache and allocation support.
1597             //
1598 
1599             {
1600                 CC_FILE_SIZES FileSizes;
1601 
1602                 FileSizes.AllocationSize.QuadPart =
1603                 FileSizes.FileSize.QuadPart = ( 0x40000 + 0x1000 );
1604                 FileSizes.ValidDataLength = FatMaxLarge;
1605 
1606                 DebugTrace(0, Dbg, "Truncate and reinitialize the volume file\n", 0);
1607 
1608                 FatInitializeCacheMap( OldVcb->VirtualVolumeFile,
1609                                        &FileSizes,
1610                                        TRUE,
1611                                        &FatData.CacheManagerNoOpCallbacks,
1612                                        Vcb );
1613 
1614                 //
1615                 //  Redo the allocation support
1616                 //
1617 
1618                 FatSetupAllocationSupport( IrpContext, OldVcb );
1619 
1620                 //
1621                 //  Get the state of the dirty bit.
1622                 //
1623 
1624                 FatCheckDirtyBit( IrpContext, OldVcb );
1625 
1626                 //
1627                 //  Check for write protected media.
1628                 //
1629 
1630                 if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) {
1631 
1632                     SetFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
1633 
1634                 } else {
1635 
1636                     ClearFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
1637                 }
1638             }
1639 
1640             //
1641             //  Complete the request and return to our caller
1642             //
1643 
1644             try_return( Status = STATUS_SUCCESS );
1645         }
1646 
1647         DebugTrace(0, Dbg, "Mount a new volume\n", 0);
1648 
1649         //
1650         //  This is a new mount
1651         //
1652         //  Create a blank ea data file fcb, just not for Fat32.
1653         //
1654 
1655         if (!FatIsFat32(Vcb)) {
1656 
1657             DIRENT TempDirent;
1658             PFCB EaFcb;
1659 
1660             RtlZeroMemory( &TempDirent, sizeof(DIRENT) );
1661             RtlCopyMemory( &TempDirent.FileName[0], "EA DATA  SF", 11 );
1662 
1663             EaFcb = FatCreateFcb( IrpContext,
1664                                   Vcb,
1665                                   Vcb->RootDcb,
1666                                   0,
1667                                   0,
1668                                   &TempDirent,
1669                                   NULL,
1670                                   NULL,
1671                                   FALSE,
1672                                   TRUE );
1673 
1674             //
1675             //  Deny anybody who trys to open the file.
1676             //
1677 
1678             SetFlag( EaFcb->FcbState, FCB_STATE_SYSTEM_FILE );
1679 
1680             Vcb->EaFcb = EaFcb;
1681         }
1682 
1683         //
1684         //  Get the state of the dirty bit.
1685         //
1686 
1687         FatCheckDirtyBit( IrpContext, Vcb );
1688 
1689 
1690         //
1691         //  Check for write protected media.
1692         //
1693 
1694         if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) {
1695 
1696             SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
1697 
1698         } else {
1699 
1700             ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
1701         }
1702 
1703 
1704         //
1705         //  Lock volume in drive if we just mounted the boot drive.
1706         //
1707 
1708         if (FlagOn(RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION)) {
1709 
1710             SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE);
1711 
1712             if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
1713 
1714                 FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE );
1715             }
1716         }
1717 
1718 
1719         //
1720         //  Indicate to our termination handler that we have mounted
1721         //  a new volume.
1722         //
1723 
1724         MountNewVolume = TRUE;
1725 
1726         //
1727         //  Complete the request
1728         //
1729 
1730         Status = STATUS_SUCCESS;
1731 
1732         //
1733         //  Ref the root dir stream object so we can send mount notification.
1734         //
1735 
1736         RootDirectoryFile = Vcb->RootDcb->Specific.Dcb.DirectoryFile;
1737         ObReferenceObject( RootDirectoryFile );
1738 
1739         //
1740         //  Remove the extra reference to this target DO made on behalf of us
1741         //  by the IO system.  In the remount case, we permit regular Vcb
1742         //  deletion to do this work.
1743         //
1744 
1745         ObDereferenceObject( TargetDeviceObject );
1746 
1747 
1748     try_exit: NOTHING;
1749 
1750     } _SEH2_FINALLY {
1751 
1752         DebugUnwind( FatMountVolume );
1753 
1754         FatUnpinBcb( IrpContext, BootBcb );
1755         FatUnpinBcb( IrpContext, DirentBcb );
1756 
1757         //
1758         //  Check if a volume was mounted.  If not then we need to
1759         //  mark the Vpb not mounted again.
1760         //
1761 
1762         if ( !MountNewVolume ) {
1763 
1764             if ( Vcb != NULL ) {
1765 
1766                 //
1767                 //  A VCB was created and initialized.  We need to try to tear it down.
1768                 //
1769 
1770                 FatCheckForDismount( IrpContext,
1771                                      Vcb,
1772                                      TRUE );
1773 
1774                 IrpContext->Vcb = NULL;
1775 
1776             } else if (VolDo != NULL) {
1777 
1778                 //
1779                 //  The VCB was never initialized, so we need to delete the
1780                 //  device right here.
1781                 //
1782 
1783                 IoDeleteDevice( &VolDo->DeviceObject );
1784             }
1785 
1786             //
1787             //  See if a remount failed.
1788             //
1789 
1790             if (DoARemount && _SEH2_AbnormalTermination()) {
1791 
1792                 //
1793                 //  The remount failed. Try to tear down the old VCB as well.
1794                 //
1795 
1796                 FatCheckForDismount( IrpContext,
1797                                      OldVcb,
1798                                      TRUE );
1799             }
1800         }
1801 
1802         if ( WeClearedVerifyRequiredBit == TRUE ) {
1803 
1804             SetFlag(RealDevice->Flags, DO_VERIFY_VOLUME);
1805         }
1806 
1807         FatReleaseGlobal( IrpContext );
1808 
1809         DebugTrace(-1, Dbg, "FatMountVolume -> %08lx\n", Status);
1810     } _SEH2_END;
1811 
1812     //
1813     //  Now send mount notification. Note that since this is outside of any
1814     //  synchronization since the synchronous delivery of this may go to
1815     //  folks that provoke re-entrance to the FS.
1816     //
1817 
1818     if (RootDirectoryFile != NULL) {
1819 
1820 #if (NTDDI_VERSION >= NTDDI_WIN8)
1821         if (FatDiskAccountingEnabled) {
1822 
1823             CcSetAdditionalCacheAttributesEx( RootDirectoryFile, CC_ENABLE_DISK_IO_ACCOUNTING );
1824         }
1825 #endif
1826 
1827         FsRtlNotifyVolumeEvent( RootDirectoryFile, FSRTL_VOLUME_MOUNT );
1828         ObDereferenceObject( RootDirectoryFile );
1829     }
1830 
1831     return Status;
1832 }
1833 
1834 
1835 //
1836 //  Local Support Routine
1837 //
1838 
_Requires_lock_held_(_Global_critical_region_)1839 _Requires_lock_held_(_Global_critical_region_)
1840 NTSTATUS
1841 FatVerifyVolume (
1842     IN PIRP_CONTEXT IrpContext,
1843     IN PIRP Irp
1844     )
1845 
1846 /*++
1847 
1848 Routine Description:
1849 
1850     This routine performs the verify volume operation by checking the volume
1851     label and serial number physically on the media with the the Vcb
1852     currently claiming to have the volume mounted. It is responsible for
1853     either completing or enqueuing the input Irp.
1854 
1855     Regardless of whether the verify operation succeeds, the following
1856     operations are performed:
1857 
1858         - Set Vcb->VirtualEaFile back to its initial state.
1859         - Purge all cached data (flushing first if verify succeeds)
1860         - Mark all Fcbs as needing verification
1861 
1862     If the volumes verifies correctly we also must:
1863 
1864         - Check the volume dirty bit.
1865         - Reinitialize the allocation support
1866         - Flush any dirty data
1867 
1868     If the volume verify fails, it may never be mounted again.  If it is
1869     mounted again, it will happen as a remount operation.  In preparation
1870     for that, and to leave the volume in a state that can be "lazy deleted"
1871     the following operations are performed:
1872 
1873         - Set the Vcb condition to VcbNotMounted
1874         - Uninitialize the volume file cachemap
1875         - Tear down the allocation support
1876 
1877     In the case of an abnormal termination we haven't determined the state
1878     of the volume, so we set the Device Object as needing verification again.
1879 
1880 Arguments:
1881 
1882     Irp - Supplies the Irp to process
1883 
1884 Return Value:
1885 
1886     NTSTATUS - If the verify operation completes, it will return either
1887         STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly.  If an IO or
1888         other error is encountered, that status will be returned.
1889 
1890 --*/
1891 
1892 {
1893     NTSTATUS Status = STATUS_SUCCESS;
1894 
1895     PIO_STACK_LOCATION IrpSp;
1896 
1897     PDIRENT RootDirectory = NULL;
1898     PPACKED_BOOT_SECTOR BootSector = NULL;
1899 
1900     BIOS_PARAMETER_BLOCK Bpb;
1901 
1902     PVOLUME_DEVICE_OBJECT VolDo;
1903     PVCB Vcb;
1904     PVPB Vpb;
1905 
1906     ULONG SectorSize;
1907     BOOLEAN ClearVerify = FALSE;
1908     BOOLEAN ReleaseEntireVolume = FALSE;
1909     BOOLEAN VerifyAlreadyDone = FALSE;
1910 
1911     DISK_GEOMETRY DiskGeometry;
1912 
1913     LBO RootDirectoryLbo;
1914     ULONG RootDirectorySize;
1915     BOOLEAN LabelFound;
1916 
1917     ULONG ChangeCount = 0;
1918     IO_STATUS_BLOCK Iosb = {0};
1919 
1920     PAGED_CODE();
1921 
1922     //
1923     //  Get the current Irp stack location
1924     //
1925 
1926     IrpSp = IoGetCurrentIrpStackLocation( Irp );
1927 
1928     DebugTrace(+1, Dbg, "FatVerifyVolume\n", 0);
1929     DebugTrace( 0, Dbg, "DeviceObject = %p\n", IrpSp->Parameters.VerifyVolume.DeviceObject);
1930     DebugTrace( 0, Dbg, "Vpb          = %p\n", IrpSp->Parameters.VerifyVolume.Vpb);
1931 
1932     //
1933     //  Save some references to make our life a little easier.  Note the Vcb for the purposes
1934     //  of exception handling.
1935     //
1936 
1937     VolDo = (PVOLUME_DEVICE_OBJECT)IrpSp->Parameters.VerifyVolume.DeviceObject;
1938 
1939     Vpb                   = IrpSp->Parameters.VerifyVolume.Vpb;
1940     IrpContext->Vcb = Vcb = &VolDo->Vcb;
1941 
1942     //
1943     //  If we cannot wait then enqueue the irp to the fsp and
1944     //  return the status to our caller.
1945     //
1946 
1947     if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
1948 
1949         DebugTrace(0, Dbg, "Cannot wait for verify.\n", 0);
1950 
1951         Status = FatFsdPostRequest( IrpContext, Irp );
1952 
1953         DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status );
1954         return Status;
1955     }
1956 
1957     //
1958     //  We are serialized at this point allowing only one thread to
1959     //  actually perform the verify operation.  Any others will just
1960     //  wait and then no-op when checking if the volume still needs
1961     //  verification.
1962     //
1963 
1964 #ifdef _MSC_VER
1965 #pragma prefast( push )
1966 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
1967 #pragma prefast( disable: 28193, "this will always wait" )
1968 #endif
1969 
1970     (VOID)FatAcquireExclusiveGlobal( IrpContext );
1971 
1972 #ifdef _MSC_VER
1973 #pragma prefast( pop )
1974 #endif
1975 
1976     (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
1977 
1978     _SEH2_TRY {
1979 
1980         BOOLEAN AllowRawMount = BooleanFlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT );
1981 
1982         //
1983         //  Mark ourselves as verifying this volume so that recursive I/Os
1984         //  will be able to complete.
1985         //
1986 
1987         NT_ASSERT( Vcb->VerifyThread == NULL );
1988         Vcb->VerifyThread = KeGetCurrentThread();
1989 
1990         //
1991         //  Check if the real device still needs to be verified.  If it doesn't
1992         //  then obviously someone beat us here and already did the work
1993         //  so complete the verify irp with success.  Otherwise reenable
1994         //  the real device and get to work.
1995         //
1996 
1997         if (!FlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) {
1998 
1999             DebugTrace(0, Dbg, "RealDevice has already been verified\n", 0);
2000 
2001             VerifyAlreadyDone = TRUE;
2002             try_return( Status = STATUS_SUCCESS );
2003         }
2004 
2005         //
2006         //  Ping the volume with a partition query to make Jeff happy.
2007         //
2008 
2009         {
2010             PARTITION_INFORMATION_EX PartitionInformation;
2011 
2012             (VOID) FatPerformDevIoCtrl( IrpContext,
2013                                         IOCTL_DISK_GET_PARTITION_INFO_EX,
2014                                         Vcb->TargetDeviceObject,
2015                                         NULL,
2016                                         0,
2017                                         &PartitionInformation,
2018                                         sizeof(PARTITION_INFORMATION_EX),
2019                                         FALSE,
2020                                         TRUE,
2021                                         &Iosb );
2022         }
2023 
2024         //
2025         // Only send down IOCTL_DISK_CHECK_VERIFY if it is removable media.
2026         //
2027 
2028         if (FlagOn(Vcb->TargetDeviceObject->Characteristics, FILE_REMOVABLE_MEDIA)) {
2029 
2030             //
2031             //  Verify that there is a disk here and pick up the change count.
2032             //
2033 
2034             Status = FatPerformDevIoCtrl( IrpContext,
2035                                           IOCTL_DISK_CHECK_VERIFY,
2036                                           Vcb->TargetDeviceObject,
2037                                           NULL,
2038                                           0,
2039                                           &ChangeCount,
2040                                           sizeof(ULONG),
2041                                           FALSE,
2042                                           TRUE,
2043                                           &Iosb );
2044 
2045             if (!NT_SUCCESS( Status )) {
2046 
2047                 //
2048                 //  If we will allow a raw mount then return WRONG_VOLUME to
2049                 //  allow the volume to be mounted by raw.
2050                 //
2051 
2052                 if (AllowRawMount) {
2053 
2054                     try_return( Status = STATUS_WRONG_VOLUME );
2055                 }
2056 
2057                 FatNormalizeAndRaiseStatus( IrpContext, Status );
2058             }
2059 
2060         }
2061 
2062         if (Iosb.Information != sizeof(ULONG)) {
2063 
2064             //
2065             //  Be safe about the count in case the driver didn't fill it in
2066             //
2067 
2068             ChangeCount = 0;
2069         }
2070 
2071         //
2072         //  Whatever happens we will have verified this volume at this change
2073         //  count, so record that fact.
2074         //
2075 
2076         Vcb->ChangeCount = ChangeCount;
2077 
2078         //
2079         //  If this is a CD class device,  then check to see if there is a
2080         //  'data track' or not.  This is to avoid issuing paging reads which will
2081         //  fail later in the mount process (e.g. CD-DA or blank CD media)
2082         //
2083 
2084         if ((Vcb->TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) &&
2085             !FatScanForDataTrack( IrpContext, Vcb->TargetDeviceObject))  {
2086 
2087             try_return( Status = STATUS_WRONG_VOLUME);
2088         }
2089 
2090         //
2091         //  Some devices can change sector sizes on the fly.  Obviously, it
2092         //  isn't the same volume if that happens.
2093         //
2094 
2095         Status = FatPerformDevIoCtrl( IrpContext,
2096                                       IOCTL_DISK_GET_DRIVE_GEOMETRY,
2097                                       Vcb->TargetDeviceObject,
2098                                       NULL,
2099                                       0,
2100                                       &DiskGeometry,
2101                                       sizeof( DISK_GEOMETRY ),
2102                                       FALSE,
2103                                       TRUE,
2104                                       NULL );
2105 
2106         if (!NT_SUCCESS( Status )) {
2107 
2108             //
2109             //  If we will allow a raw mount then return WRONG_VOLUME to
2110             //  allow the volume to be mounted by raw.
2111             //
2112 
2113             if (AllowRawMount) {
2114 
2115                 try_return( Status = STATUS_WRONG_VOLUME );
2116             }
2117 
2118             FatNormalizeAndRaiseStatus( IrpContext, Status );
2119         }
2120 
2121         //
2122         //  Read in the boot sector
2123         //
2124 
2125         SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
2126 
2127         if (SectorSize != DiskGeometry.BytesPerSector) {
2128 
2129             try_return( Status = STATUS_WRONG_VOLUME );
2130         }
2131 
2132         BootSector = FsRtlAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
2133                                               (ULONG) ROUND_TO_PAGES( SectorSize ),
2134                                               TAG_VERIFY_BOOTSECTOR);
2135 
2136         //
2137         //  If this verify is on behalf of a DASD open, allow a RAW mount.
2138         //
2139 
2140         if (!FatPerformVerifyDiskRead( IrpContext,
2141                                        Vcb,
2142                                        BootSector,
2143                                        0,
2144                                        SectorSize,
2145                                        AllowRawMount )) {
2146 
2147             try_return( Status = STATUS_WRONG_VOLUME );
2148         }
2149 
2150         //
2151         //  Call a routine to check the boot sector to see if it is fat.
2152         //  If it is not fat then mark the vcb as not mounted tell our
2153         //  caller its the wrong volume
2154         //
2155 
2156         if (!FatIsBootSectorFat( BootSector )) {
2157 
2158             DebugTrace(0, Dbg, "Not a Fat Volume\n", 0);
2159 
2160             try_return( Status = STATUS_WRONG_VOLUME );
2161         }
2162 
2163         //
2164         //  This is a fat volume, so extract serial number and see if it is
2165         //  ours.
2166         //
2167 
2168         {
2169             ULONG SerialNumber;
2170 
2171             if (IsBpbFat32(&BootSector->PackedBpb)) {
2172                 CopyUchar4( &SerialNumber, ((PPACKED_BOOT_SECTOR_EX)BootSector)->Id );
2173             } else {
2174                 CopyUchar4( &SerialNumber, BootSector->Id );
2175             }
2176 
2177             if (SerialNumber != Vpb->SerialNumber) {
2178 
2179                 DebugTrace(0, Dbg, "Not our serial number\n", 0);
2180 
2181                 try_return( Status = STATUS_WRONG_VOLUME );
2182             }
2183         }
2184 
2185         //
2186         //  Make sure the Bpbs are not different.  We have to zero out our
2187         //  stack version of the Bpb since unpacking leaves holes.
2188         //
2189 
2190         RtlZeroMemory( &Bpb, sizeof(BIOS_PARAMETER_BLOCK) );
2191 
2192         FatUnpackBios( &Bpb, &BootSector->PackedBpb );
2193         if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; }
2194 
2195         if ( !RtlEqualMemory( &Bpb,
2196                               &Vcb->Bpb,
2197                               IsBpbFat32(&Bpb) ?
2198                                     sizeof(BIOS_PARAMETER_BLOCK) :
2199                                     FIELD_OFFSET(BIOS_PARAMETER_BLOCK,
2200                                                  LargeSectorsPerFat) )) {
2201 
2202             DebugTrace(0, Dbg, "Bpb is different\n", 0);
2203 
2204             try_return( Status = STATUS_WRONG_VOLUME );
2205         }
2206 
2207         //
2208         //  Check the volume label.  We do this by trying to locate the
2209         //  volume label, making two strings one for the saved volume label
2210         //  and the other for the new volume label and then we compare the
2211         //  two labels.
2212         //
2213 
2214         if (FatRootDirectorySize(&Bpb) > 0) {
2215 
2216             RootDirectorySize = FatRootDirectorySize(&Bpb);
2217 
2218         } else {
2219 
2220             RootDirectorySize = FatBytesPerCluster(&Bpb);
2221         }
2222 
2223         RootDirectory = FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned,
2224                                                   (ULONG) ROUND_TO_PAGES( RootDirectorySize ),
2225                                                   TAG_VERIFY_ROOTDIR);
2226 
2227         if (!IsBpbFat32(&BootSector->PackedBpb)) {
2228 
2229             //
2230             //  The Fat12/16 case is simple -- read the root directory in and
2231             //  search it.
2232             //
2233 
2234             RootDirectoryLbo = FatRootDirectoryLbo(&Bpb);
2235 
2236             if (!FatPerformVerifyDiskRead( IrpContext,
2237                                            Vcb,
2238                                            RootDirectory,
2239                                            RootDirectoryLbo,
2240                                            RootDirectorySize,
2241                                            AllowRawMount )) {
2242 
2243                 try_return( Status = STATUS_WRONG_VOLUME );
2244             }
2245 
2246             Status = FatSearchBufferForLabel(IrpContext, Vpb,
2247                                              RootDirectory, RootDirectorySize,
2248                                              &LabelFound);
2249 
2250             if (!NT_SUCCESS(Status)) {
2251 
2252                 try_return( Status );
2253             }
2254 
2255             if (!LabelFound && Vpb->VolumeLabelLength > 0) {
2256 
2257                 try_return( Status = STATUS_WRONG_VOLUME );
2258             }
2259 
2260         } else {
2261 
2262             ULONG RootDirectoryCluster;
2263 
2264             RootDirectoryCluster = Bpb.RootDirFirstCluster;
2265 
2266             while (RootDirectoryCluster != FAT_CLUSTER_LAST) {
2267 
2268                 RootDirectoryLbo = FatGetLboFromIndex(Vcb, RootDirectoryCluster);
2269 
2270                 if (!FatPerformVerifyDiskRead( IrpContext,
2271                                                Vcb,
2272                                                RootDirectory,
2273                                                RootDirectoryLbo,
2274                                                RootDirectorySize,
2275                                                AllowRawMount )) {
2276 
2277                     try_return( Status = STATUS_WRONG_VOLUME );
2278                 }
2279 
2280                 Status = FatSearchBufferForLabel(IrpContext, Vpb,
2281                                                  RootDirectory, RootDirectorySize,
2282                                                  &LabelFound);
2283 
2284                 if (!NT_SUCCESS(Status)) {
2285 
2286                     try_return( Status );
2287                 }
2288 
2289                 if (LabelFound) {
2290 
2291                     //
2292                     //  Found a matching label.
2293                     //
2294 
2295                     break;
2296                 }
2297 
2298                 //
2299                 //  Set ourselves up for the next loop iteration.
2300                 //
2301 
2302                 FatVerifyLookupFatEntry( IrpContext, Vcb,
2303                                          RootDirectoryCluster,
2304                                          &RootDirectoryCluster );
2305 
2306                 switch (FatInterpretClusterType(Vcb, RootDirectoryCluster)) {
2307 
2308                 case FatClusterAvailable:
2309                 case FatClusterReserved:
2310                 case FatClusterBad:
2311 
2312                     //
2313                     //  Bail all the way out if we have a bad root.
2314                     //
2315 
2316                     FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
2317                     break;
2318 
2319                 default:
2320 
2321                     break;
2322                 }
2323 
2324             }
2325 
2326             if (RootDirectoryCluster == FAT_CLUSTER_LAST &&
2327                 Vpb->VolumeLabelLength > 0) {
2328 
2329                 //
2330                 //  Should have found a label, didn't find any.
2331                 //
2332 
2333                 try_return( Status = STATUS_WRONG_VOLUME );
2334             }
2335         }
2336 
2337 
2338     try_exit: NOTHING;
2339 
2340         //
2341         //  Note that we have previously acquired the Vcb to serialize
2342         //  the EA file stuff the marking all the Fcbs as NeedToBeVerified.
2343         //
2344         //  Put the Ea file back in a initial state.
2345         //
2346 
2347         FatCloseEaFile( IrpContext, Vcb, (BOOLEAN)(Status == STATUS_SUCCESS) );
2348 
2349         //
2350         //  Mark all Fcbs as needing verification, but only if we really have
2351         //  to do it.
2352         //
2353 
2354         if (!VerifyAlreadyDone) {
2355 
2356             FatAcquireExclusiveVolume( IrpContext, Vcb );
2357             ReleaseEntireVolume = TRUE;
2358 
2359             FatMarkFcbCondition( IrpContext, Vcb->RootDcb, FcbNeedsToBeVerified, TRUE );
2360         }
2361 
2362         //
2363         //  If the verify didn't succeed, get the volume ready for a
2364         //  remount or eventual deletion.
2365         //
2366 
2367         if (Vcb->VcbCondition == VcbNotMounted) {
2368 
2369             //
2370             //  If the volume was already in an unmounted state, just bail
2371             //  and make sure we return STATUS_WRONG_VOLUME.
2372             //
2373 
2374             Status = STATUS_WRONG_VOLUME;
2375 
2376         } else if ( Status == STATUS_WRONG_VOLUME ) {
2377 
2378             //
2379             //  Grab everything so we can safely transition the volume state without
2380             //  having a thread stumble into the torn-down allocation engine.
2381             //
2382 
2383             if (!ReleaseEntireVolume) {
2384                 FatAcquireExclusiveVolume( IrpContext, Vcb );
2385                 ReleaseEntireVolume = TRUE;
2386             }
2387 
2388             //
2389             //  Get rid of any cached data, without flushing
2390             //
2391 
2392             FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, NoFlush );
2393 
2394             //
2395             //  Uninitialize the volume file cache map.  Note that we cannot
2396             //  do a "FatSyncUninit" because of deadlock problems.  However,
2397             //  since this FileObject is referenced by us, and thus included
2398             //  in the Vpb residual count, it is OK to do a normal CcUninit.
2399             //
2400 
2401             CcUninitializeCacheMap( Vcb->VirtualVolumeFile,
2402                                     &FatLargeZero,
2403                                     NULL );
2404 
2405             FatTearDownAllocationSupport( IrpContext, Vcb );
2406 
2407             FatSetVcbCondition( Vcb, VcbNotMounted);
2408 
2409             ClearVerify = TRUE;
2410 
2411         } else if (!VerifyAlreadyDone) {
2412 
2413             //
2414             //  Grab everything so we can safely transition the volume state without
2415             //  having a thread stumble into the torn-down allocation engine.
2416             //
2417 
2418             if (!ReleaseEntireVolume) {
2419                 FatAcquireExclusiveVolume( IrpContext, Vcb );
2420                 ReleaseEntireVolume = TRUE;
2421             }
2422 
2423             //
2424             //  Get rid of any cached data, flushing first.
2425             //
2426             //  Future work (and for bonus points, around the other flush points)
2427             //  could address the possibility that the dirent filesize hasn't been
2428             //  updated yet, causing us to fail the re-verification of a file in
2429             //  DetermineAndMark. This is pretty subtle and very very uncommon.
2430             //
2431 
2432             FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
2433 
2434             //
2435             //  Flush and Purge the volume file.
2436             //
2437 
2438             (VOID)FatFlushFat( IrpContext, Vcb );
2439             CcPurgeCacheSection( &Vcb->SectionObjectPointers, NULL, 0, FALSE );
2440 
2441             //
2442             //  Redo the allocation support with newly paged stuff.
2443             //
2444 
2445             FatTearDownAllocationSupport( IrpContext, Vcb );
2446             FatSetupAllocationSupport( IrpContext, Vcb );
2447 
2448             FatCheckDirtyBit( IrpContext, Vcb );
2449 
2450             //
2451             //  Check for write protected media.
2452             //
2453 
2454             if (FatIsMediaWriteProtected(IrpContext, Vcb->TargetDeviceObject)) {
2455 
2456                 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
2457 
2458             } else {
2459 
2460                 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED );
2461             }
2462 
2463             ClearVerify = TRUE;
2464         }
2465 
2466         if (ClearVerify) {
2467 
2468             //
2469             //  Mark the device as no longer needing verification.
2470             //
2471 
2472             ClearFlag( Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );
2473         }
2474 
2475     } _SEH2_FINALLY {
2476 
2477         DebugUnwind( FatVerifyVolume );
2478 
2479         //
2480         //  Free any buffer we may have allocated
2481         //
2482 
2483         if ( BootSector != NULL ) { ExFreePool( BootSector ); }
2484         if ( RootDirectory != NULL ) { ExFreePool( RootDirectory ); }
2485 
2486         //
2487         //  Show that we are done with this volume.
2488         //
2489 
2490         NT_ASSERT( Vcb->VerifyThread == KeGetCurrentThread() );
2491         Vcb->VerifyThread = NULL;
2492 
2493         if (ReleaseEntireVolume) {
2494 
2495             FatReleaseVolume( IrpContext, Vcb );
2496         }
2497 
2498         FatReleaseVcb( IrpContext, Vcb );
2499         FatReleaseGlobal( IrpContext );
2500 
2501         //
2502         //  If this was not an abnormal termination, complete the irp.
2503         //
2504 
2505         if (!_SEH2_AbnormalTermination()) {
2506 
2507             FatCompleteRequest( IrpContext, Irp, Status );
2508         }
2509 
2510         DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status);
2511     } _SEH2_END;
2512 
2513     return Status;
2514 }
2515 
2516 
2517 //
2518 //  Local Support Routine
2519 //
2520 
2521 BOOLEAN
FatIsBootSectorFat(IN PPACKED_BOOT_SECTOR BootSector)2522 FatIsBootSectorFat (
2523     IN PPACKED_BOOT_SECTOR BootSector
2524     )
2525 
2526 /*++
2527 
2528 Routine Description:
2529 
2530     This routine checks if the boot sector is for a fat file volume.
2531 
2532 Arguments:
2533 
2534     BootSector - Supplies the packed boot sector to check
2535 
2536 Return Value:
2537 
2538     BOOLEAN - TRUE if the volume is Fat and FALSE otherwise.
2539 
2540 --*/
2541 
2542 {
2543     BOOLEAN Result;
2544     BIOS_PARAMETER_BLOCK Bpb = {0};
2545 
2546     DebugTrace(+1, Dbg, "FatIsBootSectorFat, BootSector = %p\n", BootSector);
2547 
2548     //
2549     //  The result is true unless we decide that it should be false
2550     //
2551 
2552     Result = TRUE;
2553 
2554     //
2555     //  Unpack the bios and then test everything
2556     //
2557 
2558     FatUnpackBios( &Bpb, &BootSector->PackedBpb );
2559     if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; }
2560 
2561     if ((BootSector->Jump[0] != 0xe9) &&
2562         (BootSector->Jump[0] != 0xeb) &&
2563         (BootSector->Jump[0] != 0x49)) {
2564 
2565         Result = FALSE;
2566 
2567     //
2568     //  Enforce some sanity on the sector size (easy check)
2569     //
2570 
2571     } else if ((Bpb.BytesPerSector !=  128) &&
2572                (Bpb.BytesPerSector !=  256) &&
2573                (Bpb.BytesPerSector !=  512) &&
2574                (Bpb.BytesPerSector != 1024) &&
2575                (Bpb.BytesPerSector != 2048) &&
2576                (Bpb.BytesPerSector != 4096)) {
2577 
2578         Result = FALSE;
2579 
2580     //
2581     //  Likewise on the clustering.
2582     //
2583 
2584     } else if ((Bpb.SectorsPerCluster !=  1) &&
2585                (Bpb.SectorsPerCluster !=  2) &&
2586                (Bpb.SectorsPerCluster !=  4) &&
2587                (Bpb.SectorsPerCluster !=  8) &&
2588                (Bpb.SectorsPerCluster != 16) &&
2589                (Bpb.SectorsPerCluster != 32) &&
2590                (Bpb.SectorsPerCluster != 64) &&
2591                (Bpb.SectorsPerCluster != 128)) {
2592 
2593         Result = FALSE;
2594 
2595     //
2596     //  Likewise on the reserved sectors (must reflect at least the boot sector!)
2597     //
2598 
2599     } else if (Bpb.ReservedSectors == 0) {
2600 
2601         Result = FALSE;
2602 
2603     //
2604     //  No FATs? Wrong ...
2605     //
2606 
2607     } else if (Bpb.Fats == 0) {
2608 
2609         Result = FALSE;
2610 
2611     //
2612     // Prior to DOS 3.2 might contains value in both of Sectors and
2613     // Sectors Large.
2614     //
2615 
2616     } else if ((Bpb.Sectors == 0) && (Bpb.LargeSectors == 0)) {
2617 
2618         Result = FALSE;
2619 
2620     //
2621     //  Check that FAT32 (SectorsPerFat == 0) claims some FAT space and
2622     //  is of a version we recognize, currently Version 0.0.
2623     //
2624 
2625     } else if (Bpb.SectorsPerFat == 0 && ( Bpb.LargeSectorsPerFat == 0 ||
2626                                            Bpb.FsVersion != 0 )) {
2627 
2628         Result = FALSE;
2629 
2630     } else if ((Bpb.Media != 0xf0) &&
2631                (Bpb.Media != 0xf8) &&
2632                (Bpb.Media != 0xf9) &&
2633                (Bpb.Media != 0xfb) &&
2634                (Bpb.Media != 0xfc) &&
2635                (Bpb.Media != 0xfd) &&
2636                (Bpb.Media != 0xfe) &&
2637                (Bpb.Media != 0xff) &&
2638                (!FatData.FujitsuFMR || ((Bpb.Media != 0x00) &&
2639                                         (Bpb.Media != 0x01) &&
2640                                         (Bpb.Media != 0xfa)))) {
2641 
2642         Result = FALSE;
2643 
2644     //
2645     //  If this isn't FAT32, then there better be a claimed root directory
2646     //  size here ...
2647     //
2648 
2649     } else if (Bpb.SectorsPerFat != 0 && Bpb.RootEntries == 0) {
2650 
2651         Result = FALSE;
2652 
2653     //
2654     //  If this is FAT32 (i.e., extended BPB), look for and refuse to mount
2655     //  mirror-disabled volumes. If we did, we would need to only write to
2656     //  the FAT# indicated in the ActiveFat field. The only user of this is
2657     //  the FAT->FAT32 converter after the first pass of protected mode work
2658     //  (booting into realmode) and NT should absolutely not be attempting
2659     //  to mount such an in-transition volume.
2660     //
2661 
2662     } else if (Bpb.SectorsPerFat == 0 && Bpb.MirrorDisabled) {
2663 
2664         Result = FALSE;
2665     }
2666 
2667     DebugTrace(-1, Dbg, "FatIsBootSectorFat -> %08lx\n", Result);
2668 
2669     return Result;
2670 }
2671 
2672 
2673 //
2674 //  Local Support Routine
2675 //
2676 
2677 BOOLEAN
FatIsMediaWriteProtected(IN PIRP_CONTEXT IrpContext,IN PDEVICE_OBJECT TargetDeviceObject)2678 FatIsMediaWriteProtected (
2679     IN PIRP_CONTEXT IrpContext,
2680     IN PDEVICE_OBJECT TargetDeviceObject
2681     )
2682 
2683 /*++
2684 
2685 Routine Description:
2686 
2687     This routine determines if the target media is write protected.
2688 
2689 Arguments:
2690 
2691     TargetDeviceObject - The target of the query
2692 
2693 Return Value:
2694 
2695     NTSTATUS - The return status for the operation
2696 
2697 --*/
2698 
2699 {
2700     PIRP Irp;
2701     KEVENT Event;
2702     NTSTATUS Status;
2703     IO_STATUS_BLOCK Iosb;
2704 
2705     PAGED_CODE();
2706     UNREFERENCED_PARAMETER( IrpContext );
2707 
2708     //
2709     //  Query the partition table
2710     //
2711 
2712     KeInitializeEvent( &Event, NotificationEvent, FALSE );
2713 
2714     //
2715     //  See if the media is write protected.  On success or any kind
2716     //  of error (possibly illegal device function), assume it is
2717     //  writeable, and only complain if he tells us he is write protected.
2718     //
2719 
2720     Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE,
2721                                          TargetDeviceObject,
2722                                          NULL,
2723                                          0,
2724                                          NULL,
2725                                          0,
2726                                          FALSE,
2727                                          &Event,
2728                                          &Iosb );
2729 
2730     //
2731     //  Just return FALSE in the unlikely event we couldn't allocate an Irp.
2732     //
2733 
2734     if ( Irp == NULL ) {
2735 
2736         return FALSE;
2737     }
2738 
2739     SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
2740 
2741     Status = IoCallDriver( TargetDeviceObject, Irp );
2742 
2743     if ( Status == STATUS_PENDING ) {
2744 
2745         (VOID) KeWaitForSingleObject( &Event,
2746                                       Executive,
2747                                       KernelMode,
2748                                       FALSE,
2749                                       (PLARGE_INTEGER)NULL );
2750 
2751         Status = Iosb.Status;
2752     }
2753 
2754     return (BOOLEAN)(Status == STATUS_MEDIA_WRITE_PROTECTED);
2755 }
2756 
2757 
2758 //
2759 //  Local Support Routine
2760 //
2761 
_Requires_lock_held_(_Global_critical_region_)2762 _Requires_lock_held_(_Global_critical_region_)
2763 NTSTATUS
2764 FatUserFsCtrl (
2765     IN PIRP_CONTEXT IrpContext,
2766     IN PIRP Irp
2767     )
2768 
2769 /*++
2770 
2771 Routine Description:
2772 
2773     This is the common routine for implementing the user's requests made
2774     through NtFsControlFile.
2775 
2776 Arguments:
2777 
2778     Irp - Supplies the Irp being processed
2779 
2780 Return Value:
2781 
2782     NTSTATUS - The return status for the operation
2783 
2784 --*/
2785 
2786 {
2787     NTSTATUS Status;
2788     ULONG FsControlCode;
2789 
2790     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
2791 
2792     PAGED_CODE();
2793 
2794     //
2795     //  Save some references to make our life a little easier
2796     //
2797 
2798     FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
2799 
2800     DebugTrace(+1, Dbg,"FatUserFsCtrl...\n", 0);
2801     DebugTrace( 0, Dbg,"FsControlCode = %08lx\n", FsControlCode);
2802 
2803     //
2804     //  Some of these Fs Controls use METHOD_NEITHER buffering.  If the previous mode
2805     //  of the caller was userspace and this is a METHOD_NEITHER, we have the choice
2806     //  of realy buffering the request through so we can possibly post, or making the
2807     //  request synchronous.  Since the former was not done by design, do the latter.
2808     //
2809 
2810     if (Irp->RequestorMode != KernelMode && (FsControlCode & 3) == METHOD_NEITHER) {
2811 
2812         SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
2813     }
2814 
2815     //
2816     //  Case on the control code.
2817     //
2818 
2819     switch ( FsControlCode ) {
2820 
2821     case FSCTL_REQUEST_OPLOCK_LEVEL_1:
2822     case FSCTL_REQUEST_OPLOCK_LEVEL_2:
2823     case FSCTL_REQUEST_BATCH_OPLOCK:
2824     case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
2825     case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
2826     case FSCTL_OPLOCK_BREAK_NOTIFY:
2827     case FSCTL_OPLOCK_BREAK_ACK_NO_2:
2828     case FSCTL_REQUEST_FILTER_OPLOCK:
2829 #if (NTDDI_VERSION >= NTDDI_WIN7)
2830     case FSCTL_REQUEST_OPLOCK:
2831 #endif
2832         Status = FatOplockRequest( IrpContext, Irp );
2833         break;
2834 
2835     case FSCTL_LOCK_VOLUME:
2836 
2837         Status = FatLockVolume( IrpContext, Irp );
2838         break;
2839 
2840     case FSCTL_UNLOCK_VOLUME:
2841 
2842         Status = FatUnlockVolume( IrpContext, Irp );
2843         break;
2844 
2845     case FSCTL_DISMOUNT_VOLUME:
2846 
2847         Status = FatDismountVolume( IrpContext, Irp );
2848         break;
2849 
2850     case FSCTL_MARK_VOLUME_DIRTY:
2851 
2852         Status = FatDirtyVolume( IrpContext, Irp );
2853         break;
2854 
2855     case FSCTL_IS_VOLUME_DIRTY:
2856 
2857         Status = FatIsVolumeDirty( IrpContext, Irp );
2858         break;
2859 
2860     case FSCTL_IS_VOLUME_MOUNTED:
2861 
2862         Status = FatIsVolumeMounted( IrpContext, Irp );
2863         break;
2864 
2865     case FSCTL_IS_PATHNAME_VALID:
2866         Status = FatIsPathnameValid( IrpContext, Irp );
2867         break;
2868 
2869     case FSCTL_QUERY_RETRIEVAL_POINTERS:
2870         Status = FatQueryRetrievalPointers( IrpContext, Irp );
2871         break;
2872 
2873     case FSCTL_QUERY_FAT_BPB:
2874         Status = FatQueryBpb( IrpContext, Irp );
2875         break;
2876 
2877     case FSCTL_FILESYSTEM_GET_STATISTICS:
2878         Status = FatGetStatistics( IrpContext, Irp );
2879         break;
2880 
2881 #if (NTDDI_VERSION >= NTDDI_WIN7)
2882     case FSCTL_GET_RETRIEVAL_POINTER_BASE:
2883         Status = FatGetRetrievalPointerBase( IrpContext, Irp );
2884         break;
2885 
2886     case FSCTL_GET_BOOT_AREA_INFO:
2887         Status = FatGetBootAreaInfo( IrpContext, Irp );
2888         break;
2889 #endif
2890 
2891     case FSCTL_GET_VOLUME_BITMAP:
2892         Status = FatGetVolumeBitmap( IrpContext, Irp );
2893         break;
2894 
2895     case FSCTL_GET_RETRIEVAL_POINTERS:
2896         Status = FatGetRetrievalPointers( IrpContext, Irp );
2897         break;
2898 
2899     case FSCTL_MOVE_FILE:
2900         Status = FatMoveFile( IrpContext, Irp );
2901         break;
2902 
2903     case FSCTL_ALLOW_EXTENDED_DASD_IO:
2904         Status = FatAllowExtendedDasdIo( IrpContext, Irp );
2905         break;
2906 
2907     case FSCTL_MARK_HANDLE:
2908         Status = FatMarkHandle( IrpContext, Irp );
2909         break;
2910 
2911 #if (NTDDI_VERSION >= NTDDI_WIN8)
2912 
2913     case FSCTL_SET_PURGE_FAILURE_MODE:
2914         Status = FatSetPurgeFailureMode( IrpContext, Irp );
2915         break;
2916 
2917 #endif
2918 
2919 
2920 #if (NTDDI_VERSION >= NTDDI_WIN7)
2921     case FSCTL_SET_ZERO_ON_DEALLOCATION:
2922         Status = FatSetZeroOnDeallocate( IrpContext, Irp );
2923         break;
2924 #endif
2925 
2926     default :
2927 
2928         DebugTrace(0, Dbg, "Invalid control code -> %08lx\n", FsControlCode );
2929 
2930         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
2931         Status = STATUS_INVALID_DEVICE_REQUEST;
2932         break;
2933     }
2934 
2935     DebugTrace(-1, Dbg, "FatUserFsCtrl -> %08lx\n", Status );
2936     return Status;
2937 }
2938 
2939 
2940 
2941 //
2942 //  Local support routine
2943 //
2944 
_Requires_lock_held_(_Global_critical_region_)2945 _Requires_lock_held_(_Global_critical_region_)
2946 NTSTATUS
2947 FatOplockRequest (
2948     _In_ PIRP_CONTEXT IrpContext,
2949     _In_ PIRP Irp
2950     )
2951 
2952 /*++
2953 
2954 Routine Description:
2955 
2956     This is the common routine to handle oplock requests made via the
2957     NtFsControlFile call.
2958 
2959 Arguments:
2960 
2961     Irp - Supplies the Irp being processed
2962 
2963 Return Value:
2964 
2965     NTSTATUS - The return status for the operation
2966 
2967 --*/
2968 
2969 {
2970     NTSTATUS Status = STATUS_SUCCESS;
2971     ULONG FsControlCode;
2972     PFCB Fcb;
2973     PVCB Vcb;
2974     PCCB Ccb;
2975 
2976     ULONG OplockCount = 0;
2977 
2978     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
2979 
2980     BOOLEAN AcquiredVcb = FALSE;
2981     BOOLEAN AcquiredFcb = FALSE;
2982 
2983 #if (NTDDI_VERSION >= NTDDI_WIN7)
2984     PREQUEST_OPLOCK_INPUT_BUFFER InputBuffer = NULL;
2985     ULONG InputBufferLength;
2986     ULONG OutputBufferLength;
2987 #endif
2988 
2989     TYPE_OF_OPEN TypeOfOpen;
2990 
2991     PAGED_CODE();
2992 
2993     //
2994     //  Save some references to make our life a little easier
2995     //
2996 
2997     FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
2998 
2999     TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
3000 
3001     DebugTrace(+1, Dbg, "FatOplockRequest...\n", 0);
3002     DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", FsControlCode);
3003 
3004     //
3005     //  We permit oplock requests on files and directories.
3006     //
3007 
3008     if ((TypeOfOpen != UserFileOpen)
3009 #if (NTDDI_VERSION >= NTDDI_WIN8)
3010         &&
3011         (TypeOfOpen != UserDirectoryOpen)
3012 #endif
3013         ) {
3014 
3015         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3016         DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
3017         return STATUS_INVALID_PARAMETER;
3018     }
3019 
3020 #if (NTDDI_VERSION >= NTDDI_WIN7)
3021 
3022     //
3023     //  Get the input & output buffer lengths and pointers.
3024     //
3025 
3026     if (FsControlCode == FSCTL_REQUEST_OPLOCK) {
3027 
3028         InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
3029         InputBuffer = (PREQUEST_OPLOCK_INPUT_BUFFER) Irp->AssociatedIrp.SystemBuffer;
3030 
3031         OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
3032 
3033         //
3034         //  Check for a minimum length on the input and ouput buffers.
3035         //
3036 
3037         if ((InputBufferLength < sizeof( REQUEST_OPLOCK_INPUT_BUFFER )) ||
3038             (OutputBufferLength < sizeof( REQUEST_OPLOCK_OUTPUT_BUFFER ))) {
3039 
3040             FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
3041             DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_BUFFER_TOO_SMALL\n", 0);
3042             return STATUS_BUFFER_TOO_SMALL;
3043         }
3044     }
3045 
3046     //
3047     //  If the oplock request is on a directory it must be for a Read or Read-Handle
3048     //  oplock only.
3049     //
3050 
3051     if ((TypeOfOpen == UserDirectoryOpen) &&
3052         ((FsControlCode != FSCTL_REQUEST_OPLOCK) ||
3053          !FsRtlOplockIsSharedRequest( Irp ))) {
3054 
3055         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3056         DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0);
3057         return STATUS_INVALID_PARAMETER;
3058     }
3059 
3060 #endif
3061 
3062     //
3063     //  Make this a waitable Irpcontext so we don't fail to acquire
3064     //  the resources.
3065     //
3066 
3067     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
3068 
3069     //
3070     //  Use a try finally to free the Fcb/Vcb
3071     //
3072 
3073     _SEH2_TRY {
3074 
3075         //
3076         //  We grab the Fcb exclusively for oplock requests, shared for oplock
3077         //  break acknowledgement.
3078         //
3079 
3080         if ((FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_1) ||
3081             (FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK) ||
3082             (FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) ||
3083             (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2)
3084 #if (NTDDI_VERSION >= NTDDI_WIN7)
3085             ||
3086             ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn( InputBuffer->Flags, REQUEST_OPLOCK_INPUT_FLAG_REQUEST ))
3087 #endif
3088             ) {
3089 
3090             FatAcquireSharedVcb( IrpContext, Fcb->Vcb );
3091             AcquiredVcb = TRUE;
3092             FatAcquireExclusiveFcb( IrpContext, Fcb );
3093             AcquiredFcb = TRUE;
3094 
3095 #if (NTDDI_VERSION >= NTDDI_WIN7)
3096             if (FsRtlOplockIsSharedRequest( Irp )) {
3097 #else
3098             if (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) {
3099 #endif
3100 
3101                 //
3102                 //  Byte-range locks are only valid on files.
3103                 //
3104 
3105                 if (TypeOfOpen == UserFileOpen) {
3106 
3107                     //
3108                     //  Set OplockCount to nonzero if FsRtl denies access
3109                     //  based on current byte-range lock state.
3110                     //
3111 
3112 #if (NTDDI_VERSION >= NTDDI_WIN8)
3113                     OplockCount = (ULONG) !FsRtlCheckLockForOplockRequest( &Fcb->Specific.Fcb.FileLock, &Fcb->Header.AllocationSize );
3114 #elif (NTDDI_VERSION >= NTDDI_WIN7)
3115                     OplockCount = (ULONG) FsRtlAreThereCurrentOrInProgressFileLocks( &Fcb->Specific.Fcb.FileLock );
3116 #else
3117                     OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( &Fcb->Specific.Fcb.FileLock );
3118 #endif
3119 
3120                 }
3121 
3122             } else {
3123 
3124                 OplockCount = Fcb->UncleanCount;
3125             }
3126 
3127         } else if ((FsControlCode == FSCTL_OPLOCK_BREAK_ACKNOWLEDGE) ||
3128                    (FsControlCode == FSCTL_OPBATCH_ACK_CLOSE_PENDING) ||
3129                    (FsControlCode == FSCTL_OPLOCK_BREAK_NOTIFY) ||
3130                    (FsControlCode == FSCTL_OPLOCK_BREAK_ACK_NO_2)
3131 #if (NTDDI_VERSION >= NTDDI_WIN7)
3132                    ||
3133                    ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn( InputBuffer->Flags, REQUEST_OPLOCK_INPUT_FLAG_ACK ))
3134 #endif
3135                     ) {
3136 
3137             FatAcquireSharedFcb( IrpContext, Fcb );
3138             AcquiredFcb = TRUE;
3139 #if (NTDDI_VERSION >= NTDDI_WIN7)
3140         } else if (FsControlCode == FSCTL_REQUEST_OPLOCK) {
3141 
3142             //
3143             //  The caller didn't provide either REQUEST_OPLOCK_INPUT_FLAG_REQUEST or
3144             //  REQUEST_OPLOCK_INPUT_FLAG_ACK on the input buffer.
3145             //
3146 
3147             try_leave( Status = STATUS_INVALID_PARAMETER );
3148 
3149         } else {
3150 #else
3151         } else {
3152 #endif
3153 
3154 #ifdef _MSC_VER
3155 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
3156 #endif
3157             FatBugCheck( FsControlCode, 0, 0 );
3158         }
3159 
3160         //
3161         //  Fail batch, filter, and handle oplock requests if the file is marked
3162         //  for delete.
3163         //
3164 
3165         if (((FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) ||
3166              (FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK)
3167 #if (NTDDI_VERSION >= NTDDI_WIN7)
3168              ||
3169              ((FsControlCode == FSCTL_REQUEST_OPLOCK) && FlagOn( InputBuffer->RequestedOplockLevel, OPLOCK_LEVEL_CACHE_HANDLE ))
3170 #endif
3171             ) &&
3172             FlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) {
3173 
3174             try_leave( Status = STATUS_DELETE_PENDING );
3175         }
3176 
3177         //
3178         //  Call the FsRtl routine to grant/acknowledge oplock.
3179         //
3180 
3181         Status = FsRtlOplockFsctrl( FatGetFcbOplock(Fcb),
3182                                     Irp,
3183                                     OplockCount );
3184 
3185         //
3186         //  Once we call FsRtlOplockFsctrl, we no longer own the IRP and we should not complete it.
3187         //
3188 
3189         Irp = NULL;
3190 
3191         //
3192         //  Set the flag indicating if Fast I/O is possible
3193         //
3194 
3195         Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
3196 
3197     } _SEH2_FINALLY {
3198 
3199         DebugUnwind( FatOplockRequest );
3200 
3201         //
3202         //  Release all of our resources
3203         //
3204 
3205         if (AcquiredVcb) {
3206 
3207             FatReleaseVcb( IrpContext, Fcb->Vcb );
3208         }
3209 
3210         if (AcquiredFcb)  {
3211 
3212             FatReleaseFcb( IrpContext, Fcb );
3213         }
3214 
3215         DebugTrace(-1, Dbg, "FatOplockRequest -> %08lx\n", Status );
3216     } _SEH2_END;
3217 
3218     FatCompleteRequest( IrpContext, Irp, Status );
3219 
3220     return Status;
3221 }
3222 
3223 
3224 //
3225 //  Local Support Routine
3226 //
3227 
3228 _Requires_lock_held_(_Global_critical_region_)
3229 NTSTATUS
3230 FatLockVolume (
3231     IN PIRP_CONTEXT IrpContext,
3232     IN PIRP Irp
3233     )
3234 
3235 /*++
3236 
3237 Routine Description:
3238 
3239     This routine performs the lock volume operation.  It is responsible for
3240     either completing of enqueuing the input Irp.
3241 
3242 Arguments:
3243 
3244     Irp - Supplies the Irp to process
3245 
3246 Return Value:
3247 
3248     NTSTATUS - The return status for the operation
3249 
3250 --*/
3251 
3252 {
3253     NTSTATUS Status = STATUS_SUCCESS;
3254 
3255     PIO_STACK_LOCATION IrpSp;
3256 
3257     PVCB Vcb;
3258     PFCB Fcb;
3259     PCCB Ccb;
3260 
3261     PAGED_CODE();
3262 
3263     IrpSp = IoGetCurrentIrpStackLocation( Irp );
3264 
3265     DebugTrace(+1, Dbg, "FatLockVolume...\n", 0);
3266 
3267     //
3268     //  Decode the file object, the only type of opens we accept are
3269     //  user volume opens.
3270     //
3271 
3272     if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
3273 
3274         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3275 
3276         DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3277         return STATUS_INVALID_PARAMETER;
3278     }
3279 
3280     if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
3281 
3282         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3283 
3284         DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3285         return STATUS_INVALID_PARAMETER;
3286     }
3287 
3288     //
3289     //  Send our notification so that folks that like to hold handles on
3290     //  volumes can get out of the way.
3291     //
3292 
3293     FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK );
3294 
3295     //
3296     //  Acquire exclusive access to the Vcb and enqueue the Irp if we
3297     //  didn't get access.
3298     //
3299 
3300     if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
3301 
3302         DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0);
3303 
3304         Status = FatFsdPostRequest( IrpContext, Irp );
3305 
3306         DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status);
3307         return Status;
3308     }
3309 
3310     _SEH2_TRY {
3311 
3312         Status = FatLockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject );
3313 
3314     } _SEH2_FINALLY {
3315 
3316         //
3317         //  Since we drop and release the vcb while trying to punch the volume
3318         //  down, it may be the case that we decide the operation should not
3319         //  continue if the user raced a CloeseHandle() with us (and it finished
3320         //  the cleanup) while we were waiting for our closes to finish.
3321         //
3322         //  In this case, we will have been raised out of the acquire logic with
3323         //  STATUS_FILE_CLOSED, and the volume will not be held.
3324         //
3325 
3326         if (!_SEH2_AbnormalTermination() || ExIsResourceAcquiredExclusiveLite( &Vcb->Resource )) {
3327 
3328             FatReleaseVcb( IrpContext, Vcb );
3329         }
3330 
3331         if (!NT_SUCCESS( Status ) || _SEH2_AbnormalTermination()) {
3332 
3333             //
3334             //  The volume lock will be failing.
3335             //
3336 
3337             FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED );
3338         }
3339     } _SEH2_END;
3340 
3341     FatCompleteRequest( IrpContext, Irp, Status );
3342 
3343     DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", Status);
3344 
3345     return Status;
3346 }
3347 
3348 
3349 //
3350 //  Local Support Routine
3351 //
3352 
3353 NTSTATUS
3354 FatUnlockVolume (
3355     IN PIRP_CONTEXT IrpContext,
3356     IN PIRP Irp
3357     )
3358 
3359 /*++
3360 
3361 Routine Description:
3362 
3363     This routine performs the unlock volume operation.  It is responsible for
3364     either completing of enqueuing the input Irp.
3365 
3366 Arguments:
3367 
3368     Irp - Supplies the Irp to process
3369 
3370 Return Value:
3371 
3372     NTSTATUS - The return status for the operation
3373 
3374 --*/
3375 
3376 {
3377     NTSTATUS Status;
3378 
3379     PIO_STACK_LOCATION IrpSp;
3380 
3381     PVCB Vcb;
3382     PFCB Fcb;
3383     PCCB Ccb;
3384 
3385     PAGED_CODE();
3386 
3387     IrpSp = IoGetCurrentIrpStackLocation( Irp );
3388 
3389     DebugTrace(+1, Dbg, "FatUnlockVolume...\n", 0);
3390 
3391     //
3392     //  Decode the file object, the only type of opens we accept are
3393     //  user volume opens.
3394     //
3395 
3396     if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
3397 
3398         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3399 
3400         DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3401         return STATUS_INVALID_PARAMETER;
3402     }
3403 
3404     if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
3405 
3406         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3407 
3408         DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3409         return STATUS_INVALID_PARAMETER;
3410     }
3411 
3412     Status = FatUnlockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject );
3413 
3414     //
3415     //  Send notification that the volume is avaliable.
3416     //
3417 
3418     if (NT_SUCCESS( Status )) {
3419 
3420         FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_UNLOCK );
3421     }
3422 
3423     FatCompleteRequest( IrpContext, Irp, Status );
3424 
3425     DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status);
3426 
3427     return Status;
3428 }
3429 
3430 
3431 _Requires_lock_held_(_Global_critical_region_)
3432 NTSTATUS
3433 FatLockVolumeInternal (
3434     IN PIRP_CONTEXT IrpContext,
3435     IN PVCB Vcb,
3436     IN PFILE_OBJECT FileObject OPTIONAL
3437     )
3438 
3439 /*++
3440 
3441 Routine Description:
3442 
3443     This routine performs the actual lock volume operation.  It will be called
3444     by anyone wishing to try to protect the volume for a long duration.  PNP
3445     operations are such a user.
3446 
3447     The volume must be held exclusive by the caller.
3448 
3449 Arguments:
3450 
3451     Vcb - The volume being locked.
3452 
3453     FileObject - File corresponding to the handle locking the volume.  If this
3454         is not specified, a system lock is assumed.
3455 
3456 Return Value:
3457 
3458     NTSTATUS - The return status for the operation
3459 
3460 --*/
3461 
3462 {
3463     NTSTATUS Status = STATUS_SUCCESS;
3464     KIRQL SavedIrql;
3465     ULONG RemainingUserReferences = (FileObject? 1: 0);
3466 
3467     NT_ASSERT( ExIsResourceAcquiredExclusiveLite( &Vcb->Resource ) &&
3468             !ExIsResourceAcquiredExclusiveLite( &FatData.Resource ));
3469     //
3470     //  Go synchronous for the rest of the lock operation.  It may be
3471     //  reasonable to try to revisit this in the future, but for now
3472     //  the purge below expects to be able to wait.
3473     //
3474     //  We know it is OK to leave the flag up given how we're used at
3475     //  the moment.
3476     //
3477 
3478     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
3479 
3480     //
3481     //  If there are any open handles, this will fail.
3482     //
3483 
3484     if (!FatIsHandleCountZero( IrpContext, Vcb )) {
3485 
3486         return STATUS_ACCESS_DENIED;
3487     }
3488 
3489     //
3490     //  Force Mm to get rid of its referenced file objects.
3491     //
3492 
3493     FatFlushFat( IrpContext, Vcb );
3494 
3495     FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
3496 
3497     FatCloseEaFile( IrpContext, Vcb, TRUE );
3498 
3499     //
3500     //  Now back out of our synchronization and wait for the lazy writer
3501     //  to finish off any lazy closes that could have been outstanding.
3502     //
3503     //  Since we flushed, we know that the lazy writer will issue all
3504     //  possible lazy closes in the next tick - if we hadn't, an otherwise
3505     //  unopened file with a large amount of dirty data could have hung
3506     //  around for a while as the data trickled out to the disk.
3507     //
3508     //  This is even more important now since we send notification to
3509     //  alert other folks that this style of check is about to happen so
3510     //  that they can close their handles.  We don't want to enter a fast
3511     //  race with the lazy writer tearing down his references to the file.
3512     //
3513 
3514     FatReleaseVcb( IrpContext, Vcb );
3515 
3516     Status = CcWaitForCurrentLazyWriterActivity();
3517 
3518     FatAcquireExclusiveVcb( IrpContext, Vcb );
3519 
3520     if (!NT_SUCCESS( Status )) {
3521 
3522         return Status;
3523     }
3524 
3525     //
3526     //  The act of closing and purging may have touched pages in various
3527     //  parent DCBs. We handle this by purging a second time.
3528     //
3529 
3530     FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush );
3531 
3532     FatReleaseVcb( IrpContext, Vcb );
3533 
3534     Status = CcWaitForCurrentLazyWriterActivity();
3535 
3536     FatAcquireExclusiveVcb( IrpContext, Vcb );
3537 
3538     if (!NT_SUCCESS( Status )) {
3539 
3540         return Status;
3541     }
3542 
3543     //
3544     //  Now rundown the delayed closes one last time.  We appear to be able
3545     //  to have additional collisions.
3546     //
3547 
3548     FatFspClose( Vcb );
3549 
3550     //
3551     //  Check if the Vcb is already locked, or if the open file count
3552     //  is greater than 1 (which implies that someone else also is
3553     //  currently using the volume, or a file on the volume), and that the
3554     //  VPB reference count only includes our residual and the handle (as
3555     //  appropriate).
3556     //
3557     //  We used to only check for the vpb refcount.  This is unreliable since
3558     //  the vpb refcount is dropped immediately before final close, meaning
3559     //  that even though we had a good refcount, the close was inflight and
3560     //  subsequent operations could get confused.  Especially if the PNP path
3561     //  was the lock caller, we delete the VCB with an outstanding opencount!
3562     //
3563 
3564     IoAcquireVpbSpinLock( &SavedIrql );
3565 
3566     if (!FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) &&
3567         (Vcb->Vpb->ReferenceCount <= 2 + RemainingUserReferences) &&
3568         (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 ))) {
3569 
3570         SetFlag(Vcb->Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED));
3571         SetFlag(Vcb->VcbState, VCB_STATE_FLAG_LOCKED);
3572         Vcb->FileObjectWithVcbLocked = FileObject;
3573 
3574     } else {
3575 
3576         Status = STATUS_ACCESS_DENIED;
3577     }
3578 
3579     IoReleaseVpbSpinLock( SavedIrql );
3580 
3581     //
3582     //  If we successully locked the volume, see if it is clean now.
3583     //
3584 
3585     if (NT_SUCCESS( Status ) &&
3586         FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) &&
3587         !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) &&
3588         !CcIsThereDirtyData(Vcb->Vpb)) {
3589 
3590         FatMarkVolume( IrpContext, Vcb, VolumeClean );
3591         ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
3592     }
3593 
3594     NT_ASSERT( !NT_SUCCESS(Status) || (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 )));
3595 
3596     return Status;
3597 }
3598 
3599 
3600 NTSTATUS
3601 FatUnlockVolumeInternal (
3602     IN PIRP_CONTEXT IrpContext,
3603     IN PVCB Vcb,
3604     IN PFILE_OBJECT FileObject OPTIONAL
3605     )
3606 
3607 /*++
3608 
3609 Routine Description:
3610 
3611     This routine performs the actual unlock volume operation.
3612 
3613     The volume must be held exclusive by the caller.
3614 
3615 Arguments:
3616 
3617     Vcb - The volume being locked.
3618 
3619     FileObject - File corresponding to the handle locking the volume.  If this
3620         is not specified, a system lock is assumed.
3621 
3622 Return Value:
3623 
3624     NTSTATUS - The return status for the operation
3625 
3626     Attempting to remove a system lock that did not exist is OK.
3627 
3628 --*/
3629 
3630 {
3631     KIRQL SavedIrql;
3632     NTSTATUS Status = STATUS_NOT_LOCKED;
3633 
3634     UNREFERENCED_PARAMETER( IrpContext );
3635 
3636     IoAcquireVpbSpinLock( &SavedIrql );
3637 
3638     if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) && FileObject == Vcb->FileObjectWithVcbLocked) {
3639 
3640         //
3641         //  This one locked it, unlock the volume
3642         //
3643 
3644         ClearFlag( Vcb->Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED) );
3645         ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_LOCKED );
3646         Vcb->FileObjectWithVcbLocked = NULL;
3647 
3648         Status = STATUS_SUCCESS;
3649     }
3650 
3651     IoReleaseVpbSpinLock( SavedIrql );
3652 
3653     return Status;
3654 }
3655 
3656 
3657 //
3658 //  Local Support Routine
3659 //
3660 
3661 _Requires_lock_held_(_Global_critical_region_)
3662 NTSTATUS
3663 FatDismountVolume (
3664     IN PIRP_CONTEXT IrpContext,
3665     IN PIRP Irp
3666     )
3667 
3668 /*++
3669 
3670 Routine Description:
3671 
3672     This routine performs the dismount volume operation.  It is responsible for
3673     either completing of enqueuing the input Irp.
3674 
3675 Arguments:
3676 
3677     Irp - Supplies the Irp to process
3678 
3679 Return Value:
3680 
3681     NTSTATUS - The return status for the operation
3682 
3683 --*/
3684 
3685 {
3686     PIO_STACK_LOCATION IrpSp;
3687     NTSTATUS Status = STATUS_SUCCESS;
3688     BOOLEAN VcbHeld = FALSE;
3689     KIRQL SavedIrql;
3690 
3691     PVCB Vcb;
3692     PFCB Fcb;
3693     PCCB Ccb;
3694 
3695     IrpSp = IoGetCurrentIrpStackLocation( Irp );
3696 
3697     DebugTrace(+1, Dbg, "FatDismountVolume...\n", 0);
3698 
3699     //
3700     //  Decode the file object, the only type of opens we accept are
3701     //  user volume opens on media that is not boot/paging and is not
3702     //  already dismounted ... (but we need to check that stuff while
3703     //  synchronized)
3704     //
3705 
3706     if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
3707 
3708         Status = STATUS_INVALID_PARAMETER;
3709         goto fn_return;
3710     }
3711 
3712     if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
3713 
3714         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3715 
3716         DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3717         return STATUS_INVALID_PARAMETER;
3718     }
3719 
3720     //
3721     //  Make some unsynchronized checks to see if this operation is possible.
3722     //  We will repeat the appropriate ones inside synchronization, but it is
3723     //  good to avoid bogus notifications.
3724     //
3725 
3726     if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE )) {
3727 
3728         Status = STATUS_ACCESS_DENIED;
3729         goto fn_return;
3730     }
3731 
3732     if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
3733 
3734         Status = STATUS_VOLUME_DISMOUNTED;
3735         goto fn_return;
3736     }
3737 
3738     //
3739     //  A bit of historical comment is in order.
3740     //
3741     //  In all versions prior to NT5, we only permitted dismount if the volume had
3742     //  previously been locked.  Now we must permit a forced dismount, meaning that
3743     //  we grab ahold of the whole kit-n-kaboodle - regardless of activity, open
3744     //  handles, etc. - to flush and invalidate the volume.
3745     //
3746     //  Previously, dismount assumed that lock had come along earlier and done some
3747     //  of the work that we are now going to do - i.e., flush, tear down the eas. All
3748     //  we had to do here is flush the device out and kill off as many of the orphan
3749     //  fcbs as possible. This now changes.
3750     //
3751     //  In fact, everything is a forced dismount now. This changes one interesting
3752     //  aspect, which is that it used to be the case that the handle used to dismount
3753     //  could come back, read, and induce a verify/remount. This is just not possible
3754     //  now.  The point of forced dismount is that very shortly someone will come along
3755     //  and be destructive to the possibility of using the media further - format, eject,
3756     //  etc.  By using this path, callers are expected to tolerate the consequences.
3757     //
3758     //  Note that the volume can still be successfully unlocked by this handle.
3759     //
3760 
3761     //
3762     //  Send notification.
3763     //
3764 
3765     FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT );
3766 
3767     //
3768     //  Force ourselves to wait and grab everything.
3769     //
3770 
3771     SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
3772 
3773 #ifdef _MSC_VER
3774 #pragma prefast( push )
3775 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
3776 #pragma prefast( disable: 28193, "this will always wait" )
3777 #endif
3778 
3779     (VOID)FatAcquireExclusiveGlobal( IrpContext );
3780 
3781 #ifdef _MSC_VER
3782 #pragma prefast( pop )
3783 #endif
3784 
3785     _SEH2_TRY {
3786 
3787         //
3788         //  Guess what? This can raise if a cleanup on the fileobject we
3789         //  got races in ahead of us.
3790         //
3791 
3792         FatAcquireExclusiveVolume( IrpContext, Vcb );
3793         VcbHeld = TRUE;
3794 
3795         if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) {
3796 
3797             try_return( Status = STATUS_VOLUME_DISMOUNTED );
3798         }
3799 
3800         FatFlushAndCleanVolume( IrpContext, Irp, Vcb, FlushAndInvalidate );
3801 
3802         //
3803         //  We defer the physical dismount until this handle is closed, per symmetric
3804         //  implemntation in the other FS. This permits a dismounter to issue IOCTL
3805         //  through this handle and perform device manipulation without racing with
3806         //  creates attempting to mount the volume again.
3807         //
3808         //  Raise a flag to tell the cleanup path to complete the dismount.
3809         //
3810 
3811         SetFlag( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT );
3812 
3813         //
3814         //  Indicate that the volume was dismounted so that we may return the
3815         //  correct error code when operations are attempted via open handles.
3816         //
3817 
3818         FatSetVcbCondition( Vcb, VcbBad);
3819 
3820         SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED );
3821 
3822         //
3823         //  Set a flag in the VPB to let others know that direct volume access is allowed.
3824         //
3825 
3826         IoAcquireVpbSpinLock( &SavedIrql );
3827         SetFlag( Vcb->Vpb->Flags, VPB_DIRECT_WRITES_ALLOWED );
3828         IoReleaseVpbSpinLock( SavedIrql );
3829 
3830         Status = STATUS_SUCCESS;
3831 
3832     try_exit: NOTHING;
3833 
3834     } _SEH2_FINALLY {
3835 
3836 #if (NTDDI_VERSION >= NTDDI_WIN8)
3837 
3838         FsRtlDismountComplete( Vcb->TargetDeviceObject, Status );
3839 
3840 #endif
3841 
3842         if (VcbHeld) {
3843 
3844             FatReleaseVolume( IrpContext, Vcb );
3845         }
3846 
3847         FatReleaseGlobal( IrpContext );
3848 
3849         //
3850         //  I do not believe it is possible to raise, but for completeness
3851         //  notice and send notification of failure.  We absolutely
3852         //  cannot have raised in CheckForDismount.
3853         //
3854         //  We decline to call an attempt to dismount a dismounted volume
3855         //  a failure to do so.
3856         //
3857 
3858         if ((!NT_SUCCESS( Status ) && Status != STATUS_VOLUME_DISMOUNTED)
3859             || _SEH2_AbnormalTermination()) {
3860 
3861             FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT_FAILED );
3862         }
3863     } _SEH2_END;
3864 
3865     fn_return:
3866 
3867     FatCompleteRequest( IrpContext, Irp, Status );
3868     DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", Status);
3869     return Status;
3870 }
3871 
3872 
3873 //
3874 //  Local Support Routine
3875 //
3876 
3877 _Requires_lock_held_(_Global_critical_region_)
3878 NTSTATUS
3879 FatDirtyVolume (
3880     IN PIRP_CONTEXT IrpContext,
3881     IN PIRP Irp
3882     )
3883 
3884 /*++
3885 
3886 Routine Description:
3887 
3888     This routine marks the volume as dirty.
3889 
3890 Arguments:
3891 
3892     Irp - Supplies the Irp to process
3893 
3894 Return Value:
3895 
3896     NTSTATUS - The return status for the operation
3897 
3898 --*/
3899 
3900 {
3901     PIO_STACK_LOCATION IrpSp;
3902 
3903     PVCB Vcb;
3904     PFCB Fcb;
3905     PCCB Ccb;
3906 
3907     PAGED_CODE();
3908 
3909     IrpSp = IoGetCurrentIrpStackLocation( Irp );
3910 
3911     DebugTrace(+1, Dbg, "FatDirtyVolume...\n", 0);
3912 
3913     //
3914     //  Decode the file object, the only type of opens we accept are
3915     //  user volume opens.
3916     //
3917 
3918     if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
3919 
3920         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3921 
3922         DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3923         return STATUS_INVALID_PARAMETER;
3924     }
3925 
3926     if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
3927 
3928         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
3929 
3930         DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER);
3931         return STATUS_INVALID_PARAMETER;
3932     }
3933 
3934 
3935     //
3936     //  Disable popups, we will just return any error.
3937     //
3938 
3939     SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
3940 
3941     //
3942     //  Verify the Vcb.  We want to make sure we don't dirty some
3943     //  random chunk of media that happens to be in the drive now.
3944     //
3945 
3946     FatVerifyVcb( IrpContext, Vcb );
3947 
3948     SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY );
3949 
3950     FatMarkVolume( IrpContext, Vcb, VolumeDirty );
3951 
3952     FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
3953 
3954     DebugTrace(-1, Dbg, "FatDirtyVolume -> STATUS_SUCCESS\n", 0);
3955 
3956     return STATUS_SUCCESS;
3957 }
3958 
3959 
3960 //
3961 //  Local Support Routine
3962 //
3963 
3964 NTSTATUS
3965 FatIsVolumeDirty (
3966     IN PIRP_CONTEXT IrpContext,
3967     IN PIRP Irp
3968     )
3969 
3970 /*++
3971 
3972 Routine Description:
3973 
3974     This routine determines if a volume is currently dirty.
3975 
3976 Arguments:
3977 
3978     Irp - Supplies the Irp to process
3979 
3980 Return Value:
3981 
3982     NTSTATUS - The return status for the operation
3983 
3984 --*/
3985 
3986 {
3987     PIO_STACK_LOCATION IrpSp;
3988 
3989     TYPE_OF_OPEN TypeOfOpen;
3990     PVCB Vcb;
3991     PFCB Fcb;
3992     PCCB Ccb;
3993 
3994     PULONG VolumeState;
3995 
3996     PAGED_CODE();
3997 
3998     //
3999     //  Get the current stack location and extract the output
4000     //  buffer information.
4001     //
4002 
4003     IrpSp = IoGetCurrentIrpStackLocation( Irp );
4004 
4005     //
4006     //  Get a pointer to the output buffer.  Look at the system buffer field in the
4007     //  irp first.  Then the Irp Mdl.
4008     //
4009 
4010     if (Irp->AssociatedIrp.SystemBuffer != NULL) {
4011 
4012         VolumeState = Irp->AssociatedIrp.SystemBuffer;
4013 
4014     } else if (Irp->MdlAddress != NULL) {
4015 
4016         VolumeState = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, LowPagePriority | MdlMappingNoExecute );
4017 
4018         if (VolumeState == NULL) {
4019 
4020             FatCompleteRequest( IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES );
4021             return STATUS_INSUFFICIENT_RESOURCES;
4022         }
4023 
4024     } else {
4025 
4026         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER );
4027         return STATUS_INVALID_USER_BUFFER;
4028     }
4029 
4030     //
4031     //  Make sure the output buffer is large enough and then initialize
4032     //  the answer to be that the volume isn't dirty.
4033     //
4034 
4035     if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) {
4036 
4037         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
4038         return STATUS_INVALID_PARAMETER;
4039     }
4040 
4041     *VolumeState = 0;
4042 
4043     //
4044     //  Decode the file object
4045     //
4046 
4047     TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
4048 
4049     if (TypeOfOpen != UserVolumeOpen) {
4050 
4051         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
4052         return STATUS_INVALID_PARAMETER;
4053     }
4054 
4055     if (Vcb->VcbCondition != VcbGood) {
4056 
4057         FatCompleteRequest( IrpContext, Irp, STATUS_VOLUME_DISMOUNTED );
4058         return STATUS_VOLUME_DISMOUNTED;
4059     }
4060 
4061     //
4062     //  Disable PopUps, we want to return any error.
4063     //
4064 
4065     SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
4066 
4067     //
4068     //  Verify the Vcb.  We want to make double sure that this volume
4069     //  is around so that we know our information is good.
4070     //
4071 
4072     FatVerifyVcb( IrpContext, Vcb );
4073 
4074     //
4075     //  Now set the returned information.  We can avoid probing the disk since
4076     //  we know our internal state is in sync.
4077     //
4078 
4079     if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY) ) {
4080 
4081         SetFlag( *VolumeState, VOLUME_IS_DIRTY );
4082     }
4083 
4084     Irp->IoStatus.Information = sizeof( ULONG );
4085 
4086     FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
4087     return STATUS_SUCCESS;
4088 }
4089 
4090 
4091 //
4092 //  Local Support Routine
4093 //
4094 
4095 NTSTATUS
4096 FatIsVolumeMounted (
4097     IN PIRP_CONTEXT IrpContext,
4098     IN PIRP Irp
4099     )
4100 
4101 /*++
4102 
4103 Routine Description:
4104 
4105     This routine determines if a volume is currently mounted.
4106 
4107 Arguments:
4108 
4109     Irp - Supplies the Irp to process
4110 
4111 Return Value:
4112 
4113     NTSTATUS - The return status for the operation
4114 
4115 --*/
4116 
4117 {
4118     NTSTATUS Status;
4119 
4120     PIO_STACK_LOCATION IrpSp;
4121 
4122     PVCB Vcb = NULL;
4123     PFCB Fcb;
4124     PCCB Ccb;
4125 
4126     PAGED_CODE();
4127 
4128     IrpSp = IoGetCurrentIrpStackLocation( Irp );
4129 
4130     Status = STATUS_SUCCESS;
4131 
4132     DebugTrace(+1, Dbg, "FatIsVolumeMounted...\n", 0);
4133 
4134     //
4135     //  Decode the file object.
4136     //
4137 
4138     (VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
4139 
4140     NT_ASSERT( Vcb != NULL );
4141     _Analysis_assume_( Vcb != NULL );
4142 
4143     //
4144     //  Disable PopUps, we want to return any error.
4145     //
4146 
4147     SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS);
4148 
4149     //
4150     //  Verify the Vcb.
4151     //
4152 
4153     FatVerifyVcb( IrpContext, Vcb );
4154 
4155     FatCompleteRequest( IrpContext, Irp, Status );
4156 
4157     DebugTrace(-1, Dbg, "FatIsVolumeMounted -> %08lx\n", Status);
4158 
4159     return Status;
4160 }
4161 
4162 
4163 //
4164 //  Local Support Routine
4165 //
4166 
4167 NTSTATUS
4168 FatIsPathnameValid (
4169     IN PIRP_CONTEXT IrpContext,
4170     IN PIRP Irp
4171     )
4172 
4173 /*++
4174 
4175 Routine Description:
4176 
4177     This routine determines if a pathname is a-priori illegal by inspecting
4178     the the characters used.  It is required to be correct on a FALSE return.
4179 
4180     N.B.: current implementation is intentioanlly a no-op.  This may change
4181     in the future.  A careful reader of the previous implementation of this
4182     FSCTL in FAT would discover that it violated the requirement stated above
4183     and could return FALSE for a valid (createable) pathname.
4184 
4185 Arguments:
4186 
4187     Irp - Supplies the Irp to process
4188 
4189 Return Value:
4190 
4191     NTSTATUS - The return status for the operation
4192 
4193 --*/
4194 
4195 {
4196     PAGED_CODE();
4197 
4198     DebugTrace(+1, Dbg, "FatIsPathnameValid...\n", 0);
4199 
4200     FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
4201 
4202     DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", STATUS_SUCCESS);
4203 
4204     return STATUS_SUCCESS;
4205 }
4206 
4207 
4208 //
4209 //  Local Support Routine
4210 //
4211 
4212 NTSTATUS
4213 FatQueryBpb (
4214     IN PIRP_CONTEXT IrpContext,
4215     IN PIRP Irp
4216     )
4217 
4218 /*++
4219 
4220 Routine Description:
4221 
4222     This routine simply returns the first 0x24 bytes of sector 0.
4223 
4224 Arguments:
4225 
4226     Irp - Supplies the Irp to process
4227 
4228 Return Value:
4229 
4230     NTSTATUS - The return status for the operation
4231 
4232 --*/
4233 
4234 {
4235     PIO_STACK_LOCATION IrpSp;
4236 
4237     PVCB Vcb;
4238 
4239     PFSCTL_QUERY_FAT_BPB_BUFFER BpbBuffer;
4240 
4241     PAGED_CODE();
4242 
4243     IrpSp = IoGetCurrentIrpStackLocation( Irp );
4244 
4245     DebugTrace(+1, Dbg, "FatQueryBpb...\n", 0);
4246 
4247     //
4248     //  Get the Vcb.  If we didn't keep the information needed for this call,
4249     //  we had a reason ...
4250     //
4251 
4252     Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
4253 
4254     if (Vcb->First0x24BytesOfBootSector == NULL) {
4255 
4256         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
4257         DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST );
4258         return STATUS_INVALID_DEVICE_REQUEST;
4259     }
4260 
4261     //
4262     // Extract the buffer
4263     //
4264 
4265     BpbBuffer = (PFSCTL_QUERY_FAT_BPB_BUFFER)Irp->AssociatedIrp.SystemBuffer;
4266 
4267     //
4268     //  Make sure the buffer is big enough.
4269     //
4270 
4271     if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < 0x24) {
4272 
4273         FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
4274         DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
4275         return STATUS_BUFFER_TOO_SMALL;
4276     }
4277 
4278     //
4279     //  Fill in the output buffer
4280     //
4281 
4282     RtlCopyMemory( BpbBuffer->First0x24BytesOfBootSector,
4283                    Vcb->First0x24BytesOfBootSector,
4284                    0x24 );
4285 
4286     Irp->IoStatus.Information = 0x24;
4287 
4288     FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
4289     DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_SUCCESS);
4290     return STATUS_SUCCESS;
4291 }
4292 
4293 
4294 //
4295 //  Local Support Routine
4296 //
4297 
4298 _Requires_lock_held_(_Global_critical_region_)
4299 NTSTATUS
4300 FatInvalidateVolumes (
4301     IN PIRP Irp
4302     )
4303 
4304 /*++
4305 
4306 Routine Description:
4307 
4308     This routine searches for all the volumes mounted on the same real device
4309     of the current DASD handle, and marks them all bad.  The only operation
4310     that can be done on such handles is cleanup and close.
4311 
4312 Arguments:
4313 
4314     Irp - Supplies the Irp to process
4315 
4316 Return Value:
4317 
4318     NTSTATUS - The return status for the operation
4319 
4320 --*/
4321 
4322 {
4323     NTSTATUS Status;
4324     IRP_CONTEXT IrpContext;
4325     PIO_STACK_LOCATION IrpSp;
4326 
4327     LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0};
4328 
4329     HANDLE Handle;
4330 
4331     PLIST_ENTRY Links;
4332 
4333     PFILE_OBJECT FileToMarkBad;
4334     PDEVICE_OBJECT DeviceToMarkBad;
4335 
4336     IrpSp = IoGetCurrentIrpStackLocation( Irp );
4337 
4338     DebugTrace(+1, Dbg, "FatInvalidateVolumes...\n", 0);
4339 
4340     //
4341     //  Check for the correct security access.
4342     //  The caller must have the SeTcbPrivilege.
4343     //
4344 
4345     if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode)) {
4346 
4347         FatCompleteRequest( FatNull, Irp, STATUS_PRIVILEGE_NOT_HELD );
4348 
4349         DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_PRIVILEGE_NOT_HELD);
4350         return STATUS_PRIVILEGE_NOT_HELD;
4351     }
4352 
4353     //
4354     //  Try to get a pointer to the device object from the handle passed in.
4355     //
4356 
4357 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
4358     if (IoIs32bitProcess( Irp )) {
4359 
4360         if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32)) {
4361 
4362             FatCompleteRequest( FatNull, Irp, STATUS_INVALID_PARAMETER );
4363 
4364             DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER);
4365             return STATUS_INVALID_PARAMETER;
4366         }
4367 
4368         Handle = (HANDLE) LongToHandle( (*(PUINT32)Irp->AssociatedIrp.SystemBuffer) );
4369     } else {
4370 #endif
4371         if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) {
4372 
4373             FatCompleteRequest( FatNull, Irp, STATUS_INVALID_PARAMETER );
4374 
4375             DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER);
4376             return STATUS_INVALID_PARAMETER;
4377         }
4378 
4379         Handle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer;
4380 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
4381     }
4382 #endif
4383 
4384 
4385     Status = ObReferenceObjectByHandle( Handle,
4386                                         0,
4387                                         *IoFileObjectType,
4388                                         KernelMode,
4389 #ifndef __REACTOS__
4390                                         &FileToMarkBad,
4391 #else
4392                                         (PVOID *)&FileToMarkBad,
4393 #endif
4394                                         NULL );
4395 
4396     if (!NT_SUCCESS(Status)) {
4397 
4398         FatCompleteRequest( FatNull, Irp, Status );
4399 
4400         DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", Status);
4401         return Status;
4402 
4403     } else {
4404 
4405         //
4406         //  We only needed the pointer, not a reference.
4407         //
4408 
4409         ObDereferenceObject( FileToMarkBad );
4410 
4411         //
4412         //  Grab the DeviceObject from the FileObject.
4413         //
4414 
4415         DeviceToMarkBad = FileToMarkBad->DeviceObject;
4416     }
4417 
4418     RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) );
4419 
4420     SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT );
4421     IrpContext.MajorFunction = IrpSp->MajorFunction;
4422     IrpContext.MinorFunction = IrpSp->MinorFunction;
4423 
4424 #ifdef _MSC_VER
4425 #pragma prefast( push )
4426 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
4427 #pragma prefast( disable: 28193, "this will always wait" )
4428 #endif
4429 
4430     FatAcquireExclusiveGlobal( &IrpContext );
4431 
4432 #ifdef _MSC_VER
4433 #pragma prefast( pop )
4434 #endif
4435 
4436     //
4437     //  First acquire the FatData resource shared, then walk through all the
4438     //  mounted VCBs looking for candidates to mark BAD.
4439     //
4440     //  On volumes we mark bad, check for dismount possibility (which is
4441     //  why we have to get the next link early).
4442     //
4443 
4444     Links = FatData.VcbQueue.Flink;
4445 
4446     while (Links != &FatData.VcbQueue) {
4447 
4448         PVCB ExistingVcb;
4449 
4450         ExistingVcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
4451 
4452         Links = Links->Flink;
4453 
4454         //
4455         //  If we get a match, mark the volume Bad, and also check to
4456         //  see if the volume should go away.
4457         //
4458 
4459         if (ExistingVcb->Vpb->RealDevice == DeviceToMarkBad) {
4460 
4461             BOOLEAN VcbDeleted = FALSE;
4462 
4463             //
4464             //  Here we acquire the Vcb exclusive and try to purge
4465             //  all the open files.  The idea is to have as little as
4466             //  possible stale data visible and to hasten the volume
4467             //  going away.
4468             //
4469 
4470             (VOID)FatAcquireExclusiveVcb( &IrpContext, ExistingVcb );
4471 
4472 #ifdef _MSC_VER
4473 #pragma prefast( push )
4474 #pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" )
4475 #endif
4476 
4477             if (ExistingVcb->Vpb == DeviceToMarkBad->Vpb) {
4478 
4479                 KIRQL OldIrql;
4480 
4481                 IoAcquireVpbSpinLock( &OldIrql );
4482 
4483                 if (FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_MOUNTED )) {
4484 
4485                     PVPB NewVpb;
4486 
4487                     NewVpb = ExistingVcb->SwapVpb;
4488                     ExistingVcb->SwapVpb = NULL;
4489                     SetFlag( ExistingVcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED );
4490 
4491                     RtlZeroMemory( NewVpb, sizeof( VPB ) );
4492                     NewVpb->Type = IO_TYPE_VPB;
4493                     NewVpb->Size = sizeof( VPB );
4494                     NewVpb->RealDevice = DeviceToMarkBad;
4495                     NewVpb->Flags = FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_REMOVE_PENDING );
4496 
4497                     DeviceToMarkBad->Vpb = NewVpb;
4498                 }
4499 
4500                 NT_ASSERT( DeviceToMarkBad->Vpb->DeviceObject == NULL );
4501 
4502 #ifdef _MSC_VER
4503 #pragma prefast( pop )
4504 #endif
4505 
4506                 IoReleaseVpbSpinLock( OldIrql );
4507             }
4508 
4509             FatSetVcbCondition( ExistingVcb, VcbBad );
4510 
4511             //
4512             //  Process the root directory, if it is present.
4513             //
4514 
4515             if (ExistingVcb->RootDcb != NULL) {
4516 
4517                 //
4518                 //  In order to safely mark all FCBs bad, we must acquire everything.
4519                 //
4520 
4521                 FatAcquireExclusiveVolume(&IrpContext, ExistingVcb);
4522                 FatMarkFcbCondition( &IrpContext, ExistingVcb->RootDcb, FcbBad, TRUE );
4523                 FatReleaseVolume(&IrpContext, ExistingVcb);
4524 
4525                 //
4526                 //  Purging the file objects on this volume could result in the memory manager
4527                 //  dereferencing it's file pointer which could be the last reference and
4528                 //  trigger object deletion and VCB deletion. Protect against that here by
4529                 //  temporarily biasing the file count, and later checking for dismount.
4530                 //
4531 
4532                 ExistingVcb->OpenFileCount += 1;
4533 
4534                 FatPurgeReferencedFileObjects( &IrpContext,
4535                                                ExistingVcb->RootDcb,
4536                                                NoFlush );
4537 
4538                 ExistingVcb->OpenFileCount -= 1;
4539 
4540                 VcbDeleted = FatCheckForDismount( &IrpContext, ExistingVcb, FALSE );
4541             }
4542 
4543             //
4544             //  Only release the VCB if it did not go away.
4545             //
4546 
4547             if (!VcbDeleted) {
4548 
4549                 FatReleaseVcb( &IrpContext, ExistingVcb );
4550             }
4551         }
4552     }
4553 
4554     FatReleaseGlobal( &IrpContext );
4555 
4556     FatCompleteRequest( FatNull, Irp, STATUS_SUCCESS );
4557 
4558     DebugTrace(-1, Dbg, "FatInvalidateVolumes -> STATUS_SUCCESS\n", 0);
4559 
4560     return STATUS_SUCCESS;
4561 }
4562 
4563 
4564 //
4565 //  Local Support routine
4566 //
4567 
4568 BOOLEAN
4569 FatPerformVerifyDiskRead (
4570     IN PIRP_CONTEXT IrpContext,
4571     IN PVCB Vcb,
4572     IN PVOID Buffer,
4573     IN LBO Lbo,
4574     IN ULONG NumberOfBytesToRead,
4575     IN BOOLEAN ReturnOnError
4576     )
4577 
4578 /*++
4579 
4580 Routine Description:
4581 
4582     This routine is used to read in a range of bytes from the disk.  It
4583     bypasses all of the caching and regular I/O logic, and builds and issues
4584     the requests itself.  It does this operation overriding the verify
4585     volume flag in the device object.
4586 
4587 Arguments:
4588 
4589     Vcb - Supplies the target device object for this operation.
4590 
4591     Buffer - Supplies the buffer that will recieve the results of this operation
4592 
4593     Lbo - Supplies the byte offset of where to start reading
4594 
4595     NumberOfBytesToRead - Supplies the number of bytes to read, this must
4596         be in multiple of bytes units acceptable to the disk driver.
4597 
4598     ReturnOnError - Indicates that we should return on an error, instead
4599         of raising.
4600 
4601 Return Value:
4602 
4603     BOOLEAN - TRUE if the operation succeded, FALSE otherwise.
4604 
4605 --*/
4606 
4607 {
4608     KEVENT Event;
4609     PIRP Irp;
4610     LARGE_INTEGER ByteOffset;
4611     NTSTATUS Status;
4612     IO_STATUS_BLOCK Iosb;
4613 
4614     PAGED_CODE();
4615 
4616     DebugTrace(0, Dbg, "FatPerformVerifyDiskRead, Lbo = %08lx\n", Lbo );
4617 
4618     //
4619     //  Initialize the event we're going to use
4620     //
4621 
4622     KeInitializeEvent( &Event, NotificationEvent, FALSE );
4623 
4624     //
4625     //  Build the irp for the operation and also set the overrride flag
4626     //
4627 
4628     ByteOffset.QuadPart = Lbo;
4629 
4630     Irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
4631                                         Vcb->TargetDeviceObject,
4632                                         Buffer,
4633                                         NumberOfBytesToRead,
4634                                         &ByteOffset,
4635                                         &Event,
4636                                         &Iosb );
4637 
4638     if ( Irp == NULL ) {
4639 
4640         FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
4641     }
4642 
4643     SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
4644 
4645     //
4646     //  Call the device to do the read and wait for it to finish.
4647     //
4648 
4649     Status = IoCallDriver( Vcb->TargetDeviceObject, Irp );
4650 
4651     if (Status == STATUS_PENDING) {
4652 
4653         (VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
4654 
4655         Status = Iosb.Status;
4656     }
4657 
4658     NT_ASSERT( Status != STATUS_VERIFY_REQUIRED );
4659 
4660     //
4661     //  Special case this error code because this probably means we used
4662     //  the wrong sector size and we want to reject STATUS_WRONG_VOLUME.
4663     //
4664 
4665     if (Status == STATUS_INVALID_PARAMETER) {
4666 
4667         return FALSE;
4668     }
4669 
4670     //
4671     //  If it doesn't succeed then either return or raise the error.
4672     //
4673 
4674     if (!NT_SUCCESS(Status)) {
4675 
4676         if (ReturnOnError) {
4677 
4678             return FALSE;
4679 
4680         } else {
4681 
4682             FatNormalizeAndRaiseStatus( IrpContext, Status );
4683         }
4684     }
4685 
4686     //
4687     //  And return to our caller
4688     //
4689 
4690     return TRUE;
4691 }
4692 
4693 
4694 //
4695 //  Local Support Routine
4696 //
4697 
4698 _Requires_lock_held_(_Global_critical_region_)
4699 NTSTATUS
4700 FatQueryRetrievalPointers (
4701     IN PIRP_CONTEXT IrpContext,
4702     IN PIRP Irp
4703     )
4704 
4705 /*++
4706 
4707 Routine Description:
4708 
4709     This routine performs the query retrieval pointers operation.
4710     It returns the retrieval pointers for the specified input
4711     file from the start of the file to the request map size specified
4712     in the input buffer.
4713 
4714 Arguments:
4715 
4716     Irp - Supplies the Irp to process
4717 
4718 Return Value:
4719 
4720     NTSTATUS - The return status for the operation
4721 
4722 --*/
4723 
4724 {
4725     NTSTATUS Status = STATUS_SUCCESS;
4726 
4727     PIO_STACK_LOCATION IrpSp;
4728 
4729     PVCB Vcb;
4730     PFCB Fcb;
4731     PCCB Ccb;
4732 
4733     PLARGE_INTEGER RequestedMapSize;
4734     PLARGE_INTEGER *MappingPairs;
4735 
4736     ULONG Index;
4737     ULONG i;
4738     ULONG SectorCount;
4739     LBO Lbo;
4740     ULONG Vbo;
4741     ULONG MapSize;
4742     BOOLEAN Result;
4743 
4744     PAGED_CODE();
4745 
4746     //
4747     //  Get the current stack location
4748     //
4749 
4750     IrpSp = IoGetCurrentIrpStackLocation( Irp );
4751 
4752     //
4753     //  Make this a synchronous IRP because we need access to the input buffer and
4754     //  this Irp is marked METHOD_NEITHER.
4755     //
4756 
4757     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
4758 
4759     //
4760     //  Decode the file object and ensure that it is the paging file
4761     //
4762     //  Only Kernel mode clients may query retrieval pointer information about
4763     //  a file.  Ensure that this is the case for this caller.
4764     //
4765 
4766     (VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
4767 
4768     if (Irp->RequestorMode != KernelMode ||
4769         Fcb == NULL ||
4770         !FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) ) {
4771 
4772         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
4773         return STATUS_INVALID_PARAMETER;
4774     }
4775 
4776     //
4777     //  Extract the input and output buffer information.  The input contains
4778     //  the requested size of the mappings in terms of VBO.  The output
4779     //  parameter will receive a pointer to nonpaged pool where the mapping
4780     //  pairs are stored.
4781     //
4782 
4783     NT_ASSERT( IrpSp->Parameters.FileSystemControl.InputBufferLength == sizeof(LARGE_INTEGER) );
4784     NT_ASSERT( IrpSp->Parameters.FileSystemControl.OutputBufferLength == sizeof(PVOID) );
4785 
4786     RequestedMapSize = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
4787     MappingPairs = Irp->UserBuffer;
4788 
4789     //
4790     //  Acquire exclusive access to the Fcb
4791     //
4792 
4793     FatAcquireExclusiveFcb( IrpContext, Fcb );
4794 
4795     _SEH2_TRY {
4796 
4797         //
4798         //  Verify the Fcb is still OK
4799         //
4800 
4801         FatVerifyFcb( IrpContext, Fcb );
4802 
4803         //
4804         //  Check if the mapping the caller requested is too large
4805         //
4806 
4807         if ((*RequestedMapSize).QuadPart > Fcb->Header.FileSize.QuadPart) {
4808 
4809             try_leave( Status = STATUS_INVALID_PARAMETER );
4810         }
4811 
4812         //
4813         //  Now get the index for the mcb entry that will contain the
4814         //  callers request and allocate enough pool to hold the
4815         //  output mapping pairs. Mapping should always be present, but handle
4816         //  the case where it isn't.
4817         //
4818 
4819         Result = FatLookupMcbEntry( Fcb->Vcb,
4820                                     &Fcb->Mcb,
4821                                     RequestedMapSize->LowPart - 1,
4822                                     &Lbo,
4823                                     NULL,
4824                                     &Index );
4825 
4826         if (!Result) {
4827 
4828             NT_ASSERT(FALSE);
4829             try_leave( Status = STATUS_FILE_CORRUPT_ERROR);
4830         }
4831 
4832         *MappingPairs = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
4833                                                   (Index + 2) * (2 * sizeof(LARGE_INTEGER)),
4834                                                   TAG_OUTPUT_MAPPINGPAIRS );
4835 
4836         //
4837         //  Now copy over the mapping pairs from the mcb
4838         //  to the output buffer.  We store in [sector count, lbo]
4839         //  mapping pairs and end with a zero sector count.
4840         //
4841 
4842         MapSize = RequestedMapSize->LowPart;
4843 
4844         for (i = 0; i <= Index; i += 1) {
4845 
4846             (VOID)FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb, i, (PVBO)&Vbo, &Lbo, &SectorCount );
4847 
4848             if (SectorCount > MapSize) {
4849                 SectorCount = MapSize;
4850             }
4851 
4852             (*MappingPairs)[ i*2 + 0 ].QuadPart = SectorCount;
4853             (*MappingPairs)[ i*2 + 1 ].QuadPart = Lbo;
4854 
4855             MapSize -= SectorCount;
4856         }
4857 
4858         (*MappingPairs)[ i*2 + 0 ].QuadPart = 0;
4859 
4860         Status = STATUS_SUCCESS;
4861     }
4862     _SEH2_FINALLY {
4863 
4864         DebugUnwind( FatQueryRetrievalPointers );
4865 
4866         //
4867         //  Release all of our resources
4868         //
4869 
4870         FatReleaseFcb( IrpContext, Fcb );
4871 
4872         //
4873         //  If this is an abnormal termination then undo our work, otherwise
4874         //  complete the irp
4875         //
4876 
4877         if (!_SEH2_AbnormalTermination()) {
4878 
4879             FatCompleteRequest( IrpContext, Irp, Status );
4880         }
4881     } _SEH2_END;
4882 
4883     return Status;
4884 }
4885 
4886 
4887 //
4888 //  Local Support Routine
4889 //
4890 
4891 NTSTATUS
4892 FatGetStatistics (
4893     IN PIRP_CONTEXT IrpContext,
4894     IN PIRP Irp
4895     )
4896 
4897 /*++
4898 
4899 Routine Description:
4900 
4901     This routine returns the filesystem performance counters from the
4902     appropriate VCB.
4903 
4904 Arguments:
4905 
4906     Irp - Supplies the Irp to process
4907 
4908 Return Value:
4909 
4910     NTSTATUS - The return status for the operation
4911 
4912 --*/
4913 
4914 {
4915     PIO_STACK_LOCATION IrpSp;
4916     NTSTATUS Status;
4917     PVCB Vcb;
4918 
4919     PFILE_SYSTEM_STATISTICS Buffer;
4920     ULONG BufferLength;
4921     ULONG StatsSize;
4922     ULONG BytesToCopy;
4923 
4924     PAGED_CODE();
4925 
4926     IrpSp = IoGetCurrentIrpStackLocation( Irp );
4927 
4928     DebugTrace(+1, Dbg, "FatGetStatistics...\n", 0);
4929 
4930     //
4931     // Extract the buffer
4932     //
4933 
4934     BufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
4935 
4936     //
4937     //  Get a pointer to the output buffer.
4938     //
4939 
4940     Buffer = Irp->AssociatedIrp.SystemBuffer;
4941 
4942     //
4943     //  Make sure the buffer is big enough for at least the common part.
4944     //
4945 
4946     if (BufferLength < sizeof(FILESYSTEM_STATISTICS)) {
4947 
4948         FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
4949 
4950         DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
4951 
4952         return STATUS_BUFFER_TOO_SMALL;
4953     }
4954 
4955     //
4956     //  Now see how many bytes we can copy.
4957     //
4958 
4959     StatsSize = sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors;
4960 
4961     if (BufferLength < StatsSize) {
4962 
4963         BytesToCopy = BufferLength;
4964         Status = STATUS_BUFFER_OVERFLOW;
4965 
4966     } else {
4967 
4968         BytesToCopy = StatsSize;
4969         Status =  STATUS_SUCCESS;
4970     }
4971 
4972     //
4973     //  Get the Vcb.
4974     //
4975 
4976     Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
4977 
4978     //
4979     //  Fill in the output buffer
4980     //
4981 
4982     RtlCopyMemory( Buffer, Vcb->Statistics, BytesToCopy );
4983 
4984     Irp->IoStatus.Information = BytesToCopy;
4985 
4986     FatCompleteRequest( IrpContext, Irp, Status );
4987 
4988     DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", Status);
4989 
4990     return Status;
4991 }
4992 
4993 //
4994 //  Local Support Routine
4995 //
4996 
4997 _Requires_lock_held_(_Global_critical_region_)
4998 NTSTATUS
4999 FatGetVolumeBitmap(
5000     IN PIRP_CONTEXT IrpContext,
5001     IN PIRP Irp
5002     )
5003 
5004 /*++
5005 
5006 Routine Description:
5007 
5008     This routine returns the volume allocation bitmap.
5009 
5010         Input = the STARTING_LCN_INPUT_BUFFER data structure is passed in
5011             through the input buffer.
5012         Output = the VOLUME_BITMAP_BUFFER data structure is returned through
5013             the output buffer.
5014 
5015     We return as much as the user buffer allows starting the specified input
5016     LCN (trucated to a byte).  If there is no input buffer, we start at zero.
5017 
5018 Arguments:
5019 
5020     Irp - Supplies the Irp being processed.
5021 
5022 Return Value:
5023 
5024     NTSTATUS - The return status for the operation.
5025 
5026 --*/
5027 {
5028     NTSTATUS Status;
5029     PIO_STACK_LOCATION IrpSp;
5030 
5031     PVCB Vcb;
5032     PFCB Fcb;
5033     PCCB Ccb;
5034 
5035     ULONG BytesToCopy;
5036     ULONG TotalClusters;
5037     ULONG DesiredClusters;
5038     ULONG StartingCluster;
5039     ULONG EndingCluster;
5040     ULONG InputBufferLength;
5041     ULONG OutputBufferLength;
5042     LARGE_INTEGER StartingLcn;
5043     PVOLUME_BITMAP_BUFFER OutputBuffer;
5044 
5045     PAGED_CODE();
5046 
5047     //
5048     //  Get the current Irp stack location and save some references.
5049     //
5050 
5051     IrpSp = IoGetCurrentIrpStackLocation( Irp );
5052 
5053     DebugTrace(+1, Dbg, "FatGetVolumeBitmap, FsControlCode = %08lx\n",
5054                IrpSp->Parameters.FileSystemControl.FsControlCode);
5055 
5056     //
5057     //  Make this a synchronous IRP because we need access to the input buffer and
5058     //  this Irp is marked METHOD_NEITHER.
5059     //
5060 
5061     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
5062 
5063     //
5064     //  Extract and decode the file object and check for type of open.
5065     //
5066 
5067     if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
5068 
5069         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5070         return STATUS_INVALID_PARAMETER;
5071     }
5072 
5073     if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
5074 
5075         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5076 
5077         DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> %08lx\n", STATUS_INVALID_PARAMETER);
5078         return STATUS_INVALID_PARAMETER;
5079     }
5080 
5081     InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
5082     OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
5083 
5084     OutputBuffer = (PVOLUME_BITMAP_BUFFER)FatMapUserBuffer( IrpContext, Irp );
5085 
5086     //
5087     //  Check for a minimum length on the input and output buffers.
5088     //
5089 
5090     if ((InputBufferLength < sizeof(STARTING_LCN_INPUT_BUFFER)) ||
5091         (OutputBufferLength < sizeof(VOLUME_BITMAP_BUFFER))) {
5092 
5093         FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
5094         return STATUS_BUFFER_TOO_SMALL;
5095     }
5096 
5097     //
5098     //  Check if a starting cluster was specified.
5099     //
5100 
5101     TotalClusters = Vcb->AllocationSupport.NumberOfClusters;
5102 
5103     //
5104     //  Check for valid buffers
5105     //
5106 
5107     _SEH2_TRY {
5108 
5109         if (Irp->RequestorMode != KernelMode) {
5110 
5111             ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
5112                           InputBufferLength,
5113                           sizeof(UCHAR) );
5114 
5115             ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) );
5116         }
5117 
5118         StartingLcn = ((PSTARTING_LCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingLcn;
5119 
5120     } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
5121 
5122           Status = _SEH2_GetExceptionCode();
5123 
5124           FatRaiseStatus( IrpContext,
5125                           FsRtlIsNtstatusExpected(Status) ?
5126                           Status : STATUS_INVALID_USER_BUFFER );
5127     } _SEH2_END;
5128 
5129     if (StartingLcn.HighPart || StartingLcn.LowPart >= TotalClusters) {
5130 
5131         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5132         return STATUS_INVALID_PARAMETER;
5133 
5134     } else {
5135 
5136         StartingCluster = StartingLcn.LowPart & ~7;
5137     }
5138 
5139     (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb );
5140 
5141     //
5142     //  Only return what will fit in the user buffer.
5143     //
5144 
5145     OutputBufferLength -= FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer);
5146     DesiredClusters = TotalClusters - StartingCluster;
5147 
5148     if (OutputBufferLength < (DesiredClusters + 7) / 8) {
5149 
5150         BytesToCopy = OutputBufferLength;
5151         Status = STATUS_BUFFER_OVERFLOW;
5152 
5153     } else {
5154 
5155         BytesToCopy = (DesiredClusters + 7) / 8;
5156         Status = STATUS_SUCCESS;
5157     }
5158 
5159     //
5160     //  Use try/finally for cleanup.
5161     //
5162 
5163     _SEH2_TRY {
5164 
5165         _SEH2_TRY {
5166 
5167             //
5168             //  Verify the Vcb is still OK
5169             //
5170 
5171             FatQuickVerifyVcb( IrpContext, Vcb );
5172 
5173             //
5174             //  Fill in the fixed part of the output buffer
5175             //
5176 
5177             OutputBuffer->StartingLcn.QuadPart = StartingCluster;
5178             OutputBuffer->BitmapSize.QuadPart = DesiredClusters;
5179 
5180             if (Vcb->NumberOfWindows == 1) {
5181 
5182                 //
5183                 //  Just copy the volume bitmap into the user buffer.
5184                 //
5185 
5186                 NT_ASSERT( Vcb->FreeClusterBitMap.Buffer != NULL );
5187 
5188                 RtlCopyMemory( &OutputBuffer->Buffer[0],
5189                                (PUCHAR)Vcb->FreeClusterBitMap.Buffer + StartingCluster/8,
5190                                BytesToCopy );
5191             } else {
5192 
5193                 //
5194                 //  Call out to analyze the FAT.  We must bias by two to account for
5195                 //  the zero base of this API and FAT's physical reality of starting
5196                 //  the file heap at cluster 2.
5197                 //
5198                 //  Note that the end index is inclusive - we need to subtract one to
5199                 //  calculcate it.
5200                 //
5201                 //  I.e.: StartingCluster 0 for one byte of bitmap means a start cluster
5202                 //  of 2 and end cluster of 9, a run of eight clusters.
5203                 //
5204 
5205                 EndingCluster = StartingCluster + (BytesToCopy * 8);
5206 
5207                 //
5208                 //  Make sure we do not read past the end of the entries.
5209                 //
5210 
5211                 if (EndingCluster > TotalClusters) {
5212 
5213                     EndingCluster = TotalClusters;
5214                 }
5215 
5216                 FatExamineFatEntries( IrpContext,
5217                                       Vcb,
5218                                       StartingCluster + 2,
5219                                       EndingCluster + 2 - 1,
5220                                       FALSE,
5221                                       NULL,
5222                                       (PULONG)&OutputBuffer->Buffer[0] );
5223             }
5224 
5225         } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
5226 
5227             Status = _SEH2_GetExceptionCode();
5228 
5229             FatRaiseStatus( IrpContext,
5230                             FsRtlIsNtstatusExpected(Status) ?
5231                             Status : STATUS_INVALID_USER_BUFFER );
5232         } _SEH2_END;
5233 
5234     } _SEH2_FINALLY {
5235 
5236         FatReleaseVcb( IrpContext, Vcb );
5237     } _SEH2_END;
5238 
5239     Irp->IoStatus.Information = FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer) +
5240                                 BytesToCopy;
5241 
5242     FatCompleteRequest( IrpContext, Irp, Status );
5243 
5244     DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> VOID\n", 0);
5245 
5246     return Status;
5247 }
5248 
5249 
5250 //
5251 //  Local Support Routine
5252 //
5253 
5254 _Requires_lock_held_(_Global_critical_region_)
5255 NTSTATUS
5256 FatGetRetrievalPointers (
5257     IN PIRP_CONTEXT IrpContext,
5258     IN PIRP Irp
5259     )
5260 
5261 /*++
5262 
5263 Routine Description:
5264 
5265     This routine scans the MCB and builds an extent list.  The first run in
5266     the output extent list will start at the begining of the contiguous
5267     run specified by the input parameter.
5268 
5269         Input = STARTING_VCN_INPUT_BUFFER;
5270         Output = RETRIEVAL_POINTERS_BUFFER.
5271 
5272 Arguments:
5273 
5274     Irp - Supplies the Irp being processed.
5275 
5276 Return Value:
5277 
5278     NTSTATUS - The return status for the operation.
5279 
5280 --*/
5281 {
5282     NTSTATUS Status = STATUS_SUCCESS;
5283     PIO_STACK_LOCATION IrpSp;
5284 
5285     PVCB Vcb;
5286     PFCB FcbOrDcb;
5287     PCCB Ccb;
5288     PLARGE_MCB McbToUse = NULL;
5289     TYPE_OF_OPEN TypeOfOpen;
5290 
5291     ULONG Index;
5292     ULONG ClusterShift = 0;
5293     ULONG ClusterSize;
5294     LONGLONG AllocationSize = 0;
5295 
5296     ULONG Run;
5297     ULONG RunCount;
5298     ULONG StartingRun;
5299     LARGE_INTEGER StartingVcn;
5300 
5301     ULONG InputBufferLength;
5302     ULONG OutputBufferLength;
5303 
5304     VBO   LastVbo;
5305     LBO   LastLbo;
5306     ULONG LastIndex;
5307 
5308     PRETRIEVAL_POINTERS_BUFFER OutputBuffer;
5309 
5310     PAGED_CODE();
5311 
5312     //
5313     //  Get the current Irp stack location and save some references.
5314     //
5315 
5316     IrpSp = IoGetCurrentIrpStackLocation( Irp );
5317 
5318     DebugTrace(+1, Dbg, "FatGetRetrievalPointers, FsControlCode = %08lx\n",
5319                IrpSp->Parameters.FileSystemControl.FsControlCode);
5320 
5321     //
5322     //  Make this a synchronous IRP because we need access to the input buffer and
5323     //  this Irp is marked METHOD_NEITHER.
5324     //
5325 
5326     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
5327 
5328     //
5329     //  Extract and decode the file object and check for type of open.
5330     //
5331 
5332     TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb );
5333 
5334     if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen) && (TypeOfOpen != UserVolumeOpen) ) {
5335 
5336         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5337         return STATUS_INVALID_PARAMETER;
5338     }
5339 
5340     //
5341     //  Get the input and output buffer lengths and pointers.
5342     //  Initialize some variables.
5343     //
5344 
5345     InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
5346     OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
5347 
5348     OutputBuffer = (PRETRIEVAL_POINTERS_BUFFER)FatMapUserBuffer( IrpContext, Irp );
5349 
5350     //
5351     //  Check for a minimum length on the input and ouput buffers.
5352     //
5353 
5354     if ((InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER)) ||
5355         (OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))) {
5356 
5357         FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
5358         return STATUS_BUFFER_TOO_SMALL;
5359     }
5360 
5361     //
5362     //  Acquire the Fcb and enqueue the Irp if we didn't get access.  Go for
5363     //  shared on read-only media so we can allow prototype XIP to get
5364     //  recursive, as well as recognizing this is safe anyway.
5365     //
5366     if( (TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen) ) {
5367 
5368         if (FlagOn( FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED )) {
5369 
5370             (VOID)FatAcquireSharedFcb( IrpContext, FcbOrDcb );
5371 
5372         } else {
5373 
5374             (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb );
5375         }
5376     } else if ((TypeOfOpen == UserVolumeOpen )) {
5377 
5378         if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
5379 
5380             FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
5381 
5382             DebugTrace(-1, Dbg, "FatMoveFile -> 0x%x\n", STATUS_ACCESS_DENIED);
5383             return STATUS_ACCESS_DENIED;
5384         }
5385 
5386         (VOID)FatAcquireExclusiveVcb(IrpContext, Vcb);
5387     }
5388 
5389     _SEH2_TRY {
5390 
5391         //
5392         //  Verify the Fcb is still OK, or if it is a volume handle, the VCB.
5393         //
5394 
5395         if( (TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen) ) {
5396 
5397             FatVerifyFcb( IrpContext, FcbOrDcb );
5398 
5399             //
5400             //  If we haven't yet set the correct AllocationSize, do so.
5401             //
5402 
5403             if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
5404 
5405                 FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
5406 
5407                 //
5408                 //  If this is a non-root directory, we have a bit more to
5409                 //  do since it has not gone through FatOpenDirectoryFile().
5410                 //
5411 
5412                 if (NodeType(FcbOrDcb) == FAT_NTC_DCB ||
5413                     (NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && FatIsFat32(Vcb))) {
5414 
5415                     FcbOrDcb->Header.FileSize.LowPart =
5416                         FcbOrDcb->Header.AllocationSize.LowPart;
5417                 }
5418             }
5419 
5420 
5421             ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster;
5422 
5423 #ifdef _MSC_VER
5424 #pragma prefast( suppress:28931, "calculate it anyway, in case someone adds code that uses this in the future" )
5425 #endif
5426             ClusterSize = 1 << ClusterShift;
5427 
5428             AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart;
5429             McbToUse = &FcbOrDcb->Mcb;
5430 
5431         } else if ((TypeOfOpen == UserVolumeOpen )) {
5432 
5433             FatQuickVerifyVcb( IrpContext, Vcb );
5434 
5435             if (!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BAD_BLOCKS_POPULATED)) {
5436 
5437                 //
5438                 //  If the bad cluster mcb isn't populated, something is wrong. (It should have been
5439                 //  populated during mount when we scanned the FAT.
5440                 //
5441 
5442                 FatRaiseStatus(IrpContext, STATUS_FILE_CORRUPT_ERROR );
5443             }
5444 
5445             ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster;
5446             ClusterSize = 1 << ClusterShift;
5447 
5448             if (!FatLookupLastMcbEntry(Vcb, &Vcb->BadBlockMcb, &LastVbo, &LastLbo, &LastIndex)) {
5449                 AllocationSize = 0;
5450             } else {
5451 
5452                 //
5453                 //  Round the allocation size to a multiple of of the cluster size.
5454                 //
5455 
5456                 AllocationSize = (LastVbo + ((LONGLONG)ClusterSize-1)) & ~((LONGLONG)ClusterSize-1);
5457             }
5458 
5459             McbToUse = &Vcb->BadBlockMcb;
5460 
5461         }
5462 
5463         //
5464         //  Check if a starting cluster was specified.
5465         //
5466 
5467         _SEH2_TRY {
5468 
5469             if (Irp->RequestorMode != KernelMode) {
5470 
5471                 ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer,
5472                               InputBufferLength,
5473                               sizeof(UCHAR) );
5474 
5475                 ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) );
5476             }
5477 
5478             StartingVcn = ((PSTARTING_VCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingVcn;
5479 
5480         } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
5481 
5482               Status = _SEH2_GetExceptionCode();
5483 
5484               FatRaiseStatus( IrpContext,
5485                               FsRtlIsNtstatusExpected(Status) ?
5486                               Status : STATUS_INVALID_USER_BUFFER );
5487         } _SEH2_END;
5488 
5489         if (StartingVcn.HighPart ||
5490             StartingVcn.LowPart >= (AllocationSize >> ClusterShift)) {
5491 
5492             try_return( Status = STATUS_END_OF_FILE );
5493 
5494         } else {
5495 
5496             //
5497             //  If we don't find the run, something is very wrong.
5498             //
5499 
5500             LBO Lbo;
5501 
5502             if (!FatLookupMcbEntry( Vcb, McbToUse,
5503                                     StartingVcn.LowPart << ClusterShift,
5504                                     &Lbo,
5505                                     NULL,
5506                                     &StartingRun)) {
5507 
5508 #ifdef _MSC_VER
5509 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
5510 #endif
5511                 FatBugCheck( (ULONG_PTR)FcbOrDcb, (ULONG_PTR)McbToUse, StartingVcn.LowPart );
5512             }
5513         }
5514 
5515         //
5516         //  Now go fill in the ouput buffer with run information
5517         //
5518 
5519         RunCount = FsRtlNumberOfRunsInLargeMcb( McbToUse );
5520 
5521         for (Index = 0, Run = StartingRun; Run < RunCount; Index++, Run++) {
5522 
5523             ULONG Vcn;
5524             LBO Lbo;
5525             ULONG ByteLength;
5526 
5527             //
5528             //  Check for an exhausted output buffer.
5529             //
5530 
5531             if ((ULONG)FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index+1]) > OutputBufferLength) {
5532 
5533 
5534                 //
5535                 //  We've run out of space, so we won't be storing as many runs to the
5536                 //  user's buffer as we had originally planned.  We need to return the
5537                 //  number of runs that we did have room for.
5538                 //
5539 
5540                 _SEH2_TRY {
5541 
5542                     OutputBuffer->ExtentCount = Index;
5543 
5544                 } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
5545 
5546                     Status = _SEH2_GetExceptionCode();
5547 
5548                     FatRaiseStatus( IrpContext,
5549                                     FsRtlIsNtstatusExpected(Status) ?
5550                                     Status : STATUS_INVALID_USER_BUFFER );
5551                 } _SEH2_END;
5552 
5553                 Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]);
5554                 try_return( Status = STATUS_BUFFER_OVERFLOW );
5555             }
5556 
5557             //
5558             //  Get the extent.  If it's not there or malformed, something is very wrong.
5559             //
5560 
5561             if (!FatGetNextMcbEntry(Vcb, McbToUse, Run, (PVBO)&Vcn, &Lbo, &ByteLength)) {
5562 
5563 #ifdef _MSC_VER
5564 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
5565 #endif
5566                 FatBugCheck( (ULONG_PTR)FcbOrDcb, (ULONG_PTR)McbToUse, Run );
5567             }
5568 
5569             //
5570             //  Fill in the next array element.
5571             //
5572 
5573             _SEH2_TRY {
5574 
5575                 OutputBuffer->Extents[Index].NextVcn.QuadPart = ((LONGLONG)Vcn + ByteLength) >> ClusterShift;
5576                 OutputBuffer->Extents[Index].Lcn.QuadPart = FatGetIndexFromLbo( Vcb, Lbo ) - 2;
5577 
5578                 //
5579                 //  If this is the first run, fill in the starting Vcn
5580                 //
5581 
5582                 if (Index == 0) {
5583                     OutputBuffer->ExtentCount = RunCount - StartingRun;
5584                     OutputBuffer->StartingVcn.QuadPart = Vcn >> ClusterShift;
5585                 }
5586 
5587             } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) {
5588 
5589                 Status = _SEH2_GetExceptionCode();
5590 
5591                 FatRaiseStatus( IrpContext,
5592                                 FsRtlIsNtstatusExpected(Status) ?
5593                                 Status : STATUS_INVALID_USER_BUFFER );
5594             } _SEH2_END;
5595         }
5596 
5597         //
5598         //  We successfully retrieved extent info to the end of the allocation.
5599         //
5600 
5601         Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]);
5602         Status = STATUS_SUCCESS;
5603 
5604     try_exit: NOTHING;
5605 
5606     } _SEH2_FINALLY {
5607 
5608         DebugUnwind( FatGetRetrievalPointers );
5609 
5610         //
5611         //  Release resources
5612         //
5613 
5614         if( (TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen) ) {
5615 
5616             FatReleaseFcb( IrpContext, FcbOrDcb );
5617         } else if ((TypeOfOpen == UserVolumeOpen )) {
5618 
5619             FatReleaseVcb(IrpContext, Vcb);
5620         }
5621 
5622         //
5623         //  If nothing raised then complete the irp.
5624         //
5625 
5626         if (!_SEH2_AbnormalTermination()) {
5627 
5628             FatCompleteRequest( IrpContext, Irp, Status );
5629         }
5630 
5631         DebugTrace(-1, Dbg, "FatGetRetrievalPointers -> VOID\n", 0);
5632     } _SEH2_END;
5633 
5634     return Status;
5635 }
5636 
5637 
5638 //
5639 //  Local Support Routine
5640 //
5641 
5642 _Requires_lock_held_(_Global_critical_region_)
5643 VOID
5644 FatMoveFileNeedsWriteThrough (
5645     _In_ PIRP_CONTEXT IrpContext,
5646     _In_ PFCB FcbOrDcb,
5647     _In_ ULONG OldWriteThroughFlags
5648     )
5649 {
5650     PAGED_CODE();
5651 
5652     if (NodeType(FcbOrDcb) == FAT_NTC_FCB) {
5653 
5654 
5655         if (FcbOrDcb->Header.ValidDataLength.QuadPart == 0) {
5656 
5657 
5658             ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH );
5659             SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH );
5660 
5661         } else {
5662 
5663             IrpContext->Flags &= ~(IRP_CONTEXT_FLAG_WRITE_THROUGH|IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH);
5664             IrpContext->Flags |= OldWriteThroughFlags;
5665 
5666         }
5667     }
5668 }
5669 
5670 _Requires_lock_held_(_Global_critical_region_)
5671 NTSTATUS
5672 FatMoveFile (
5673     IN PIRP_CONTEXT IrpContext,
5674     IN PIRP Irp
5675     )
5676 
5677 /*++
5678 
5679 Routine Description:
5680 
5681     Routine moves a file to the requested Starting Lcn from Starting Vcn for the length
5682     of cluster count. These values are passed in through the the input buffer as a
5683     MOVE_DATA structure.
5684 
5685     The call must be made with a DASD handle.  The file to move is passed in as a
5686     parameter.
5687 
5688 Arguments:
5689 
5690     Irp - Supplies the Irp being processed.
5691 
5692 Return Value:
5693 
5694     NTSTATUS - The return status for the operation.
5695 
5696 --*/
5697 
5698 {
5699     NTSTATUS Status;
5700     PIO_STACK_LOCATION IrpSp;
5701 
5702     PFILE_OBJECT FileObject;
5703     TYPE_OF_OPEN TypeOfOpen;
5704     PVCB Vcb;
5705     PFCB FcbOrDcb;
5706     PCCB Ccb;
5707 
5708     ULONG InputBufferLength;
5709     PMOVE_FILE_DATA InputBuffer;
5710 
5711     ULONG ClusterShift;
5712     ULONG MaxClusters;
5713 
5714     ULONG FileOffset;
5715 
5716     LBO TargetLbo;
5717     ULONG TargetCluster;
5718     LARGE_INTEGER LargeSourceLbo;
5719     LARGE_INTEGER LargeTargetLbo;
5720 
5721     ULONG ByteCount;
5722     ULONG BytesToWrite;
5723     ULONG BytesToReallocate;
5724 
5725     ULONG FirstSpliceSourceCluster;
5726     ULONG FirstSpliceTargetCluster;
5727     ULONG SecondSpliceSourceCluster;
5728     ULONG SecondSpliceTargetCluster;
5729 
5730     LARGE_MCB SourceMcb;
5731     LARGE_MCB TargetMcb;
5732 
5733     KEVENT StackEvent;
5734 
5735     PVOID Buffer = NULL;
5736     ULONG BufferSize;
5737 
5738     BOOLEAN SourceMcbInitialized = FALSE;
5739     BOOLEAN TargetMcbInitialized = FALSE;
5740 
5741     BOOLEAN FcbAcquired = FALSE;
5742     BOOLEAN EventArmed = FALSE;
5743     BOOLEAN DiskSpaceAllocated = FALSE;
5744 
5745     PDIRENT Dirent;
5746     PBCB DirentBcb = NULL;
5747 
5748     ULONG OldWriteThroughFlags = (IrpContext->Flags & (IRP_CONTEXT_FLAG_WRITE_THROUGH|IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH));
5749 
5750 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
5751     MOVE_FILE_DATA LocalMoveFileData;
5752     PMOVE_FILE_DATA32 MoveFileData32;
5753 #endif
5754 
5755     ULONG LocalAbnormalTermination = 0;
5756 
5757     PAGED_CODE();
5758 
5759     //
5760     //  Get the current Irp stack location and save some references.
5761     //
5762 
5763     IrpSp = IoGetCurrentIrpStackLocation( Irp );
5764 
5765     DebugTrace(+1, Dbg, "FatMoveFile, FsControlCode = %08lx\n",
5766                IrpSp->Parameters.FileSystemControl.FsControlCode);
5767 
5768     //
5769     //  Force WAIT to true.  We have a handle in the input buffer which can only
5770     //  be referenced within the originating process.
5771     //
5772 
5773     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
5774 
5775     //
5776     //  Extract and decode the file object and check for type of open.
5777     //
5778 
5779     if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ) != UserVolumeOpen) {
5780 
5781         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5782 
5783         DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5784         return STATUS_INVALID_PARAMETER;
5785     }
5786 
5787     if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
5788 
5789         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5790 
5791         DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5792         return STATUS_INVALID_PARAMETER;
5793     }
5794 
5795     InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
5796     InputBuffer = (PMOVE_FILE_DATA)Irp->AssociatedIrp.SystemBuffer;
5797 
5798     //
5799     //  Do a quick check on the input buffer.
5800     //
5801 
5802 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
5803     if (IoIs32bitProcess( Irp )) {
5804 
5805         if (InputBuffer == NULL || InputBufferLength < sizeof(MOVE_FILE_DATA32)) {
5806 
5807             FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
5808             return STATUS_BUFFER_TOO_SMALL;
5809         }
5810 
5811         MoveFileData32 = (PMOVE_FILE_DATA32) InputBuffer;
5812 
5813         LocalMoveFileData.FileHandle = (HANDLE) LongToHandle( MoveFileData32->FileHandle );
5814         LocalMoveFileData.StartingVcn = MoveFileData32->StartingVcn;
5815         LocalMoveFileData.StartingLcn = MoveFileData32->StartingLcn;
5816         LocalMoveFileData.ClusterCount = MoveFileData32->ClusterCount;
5817 
5818         InputBuffer = &LocalMoveFileData;
5819 
5820     } else {
5821 #endif
5822         if (InputBuffer == NULL || InputBufferLength < sizeof(MOVE_FILE_DATA)) {
5823 
5824             FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
5825             return STATUS_BUFFER_TOO_SMALL;
5826         }
5827 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
5828     }
5829 #endif
5830 
5831     MaxClusters = Vcb->AllocationSupport.NumberOfClusters;
5832     TargetCluster = InputBuffer->StartingLcn.LowPart + 2;
5833 
5834     if (InputBuffer->StartingVcn.HighPart ||
5835         InputBuffer->StartingLcn.HighPart ||
5836         (TargetCluster < 2) ||
5837         (TargetCluster + InputBuffer->ClusterCount < TargetCluster) ||
5838         (TargetCluster + InputBuffer->ClusterCount > MaxClusters + 2) ||
5839         (InputBuffer->StartingVcn.LowPart >= MaxClusters) ||
5840         InputBuffer->ClusterCount == 0
5841         ) {
5842 
5843         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5844 
5845         DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5846         return STATUS_INVALID_PARAMETER;
5847     }
5848 
5849     //
5850     //  Try to get a pointer to the file object from the handle passed in.
5851     //
5852 
5853     Status = ObReferenceObjectByHandle( InputBuffer->FileHandle,
5854                                         0,
5855                                         *IoFileObjectType,
5856                                         Irp->RequestorMode,
5857 #ifndef __REACTOS__
5858                                         &FileObject,
5859 #else
5860                                         (PVOID *)&FileObject,
5861 #endif
5862                                         NULL );
5863 
5864     if (!NT_SUCCESS(Status)) {
5865 
5866         FatCompleteRequest( IrpContext, Irp, Status );
5867 
5868         DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", Status);
5869         return Status;
5870     }
5871 
5872     //
5873     //  There are three basic ways this could be an invalid attempt, so
5874     //  we need to
5875     //
5876     //    - check that this file object is opened on the same volume as the
5877     //      DASD handle used to call this routine.
5878     //
5879     //    - extract and decode the file object and check for type of open.
5880     //
5881     //    - if this is a directory, verify that it's not the root and that
5882     //      we are not trying to move the first cluster.  We cannot move the
5883     //      first cluster because sub-directories have this cluster number
5884     //      in them and there is no safe way to simultaneously update them
5885     //      all.
5886     //
5887     //  We'll allow movefile on the root dir if its fat32, since the root dir
5888     //  is a real chained file there.
5889     //
5890 
5891     if (FileObject->Vpb != Vcb->Vpb) {
5892 
5893         ObDereferenceObject( FileObject );
5894         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5895 
5896         DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5897         return STATUS_INVALID_PARAMETER;
5898     }
5899 
5900     TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &FcbOrDcb, &Ccb );
5901 
5902     if ((TypeOfOpen != UserFileOpen &&
5903          TypeOfOpen != UserDirectoryOpen) ||
5904 
5905         ((TypeOfOpen == UserDirectoryOpen) &&
5906          ((NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && !FatIsFat32(Vcb)) ||
5907           (InputBuffer->StartingVcn.QuadPart == 0)))) {
5908 
5909         ObDereferenceObject( FileObject );
5910         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
5911 
5912         DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
5913         return STATUS_INVALID_PARAMETER;
5914     }
5915 
5916     //
5917     //  If the VDL of the file is zero, it has no valid data in it anyway.
5918     //  So it should be safe to avoid flushing the FAT entries and let them be
5919     //  lazily written out.
5920     //
5921     //  This is  done so that bitlocker's cover file doesn't cause
5922     //  unnecessary FAT table I/O when it's moved around.
5923     //  (See Win8 bug 106505)
5924     //
5925 
5926     //
5927     //  If this is a file, and the VDL is zero, clear write through.
5928     //
5929 
5930     FatMoveFileNeedsWriteThrough(IrpContext, FcbOrDcb, OldWriteThroughFlags);
5931 
5932 
5933     //
5934     //  Indicate we're getting to parents of this fcb by their child, and that
5935     //  this is a sufficient assertion of our ability to by synchronized
5936     //  with respect to the parent directory going away.
5937     //
5938     //  The defrag path is an example of one which arrives at an Fcb by
5939     //  a means which would be unreasonable to duplicate in the assertion
5940     //  code. See FatOpenDirectoryFile.
5941     //
5942 
5943     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD );
5944 
5945     ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster;
5946 
5947     _SEH2_TRY {
5948 
5949         //
5950         //  Initialize our state variables and the event.
5951         //
5952 
5953         FileOffset = InputBuffer->StartingVcn.LowPart << ClusterShift;
5954 
5955         ByteCount = InputBuffer->ClusterCount << ClusterShift;
5956 
5957         TargetLbo = FatGetLboFromIndex( Vcb, TargetCluster );
5958         LargeTargetLbo.QuadPart = TargetLbo;
5959 
5960         Buffer = NULL;
5961 
5962         //
5963         //  Do a quick check on parameters here
5964         //
5965 
5966         if (FileOffset + ByteCount < FileOffset) {
5967 
5968             try_return( Status = STATUS_INVALID_PARAMETER );
5969         }
5970 
5971         KeInitializeEvent( &StackEvent, NotificationEvent, FALSE );
5972 
5973         //
5974         //  Initialize two MCBs we will be using
5975         //
5976 
5977         FsRtlInitializeLargeMcb( &SourceMcb, PagedPool );
5978         SourceMcbInitialized = TRUE;
5979 
5980         FsRtlInitializeLargeMcb( &TargetMcb, PagedPool );
5981         TargetMcbInitialized = TRUE;
5982 
5983         //
5984         //  Ok, now if this is a directory open we need to switch to the internal
5985         //  stream fileobject since it is set up for caching.  The top-level
5986         //  fileobject has no section object pointers in order prevent folks from
5987         //  mapping it.
5988         //
5989 
5990         if (TypeOfOpen == UserDirectoryOpen) {
5991 
5992             PFILE_OBJECT DirStreamFileObject;
5993 
5994             //
5995             //  Open the stream fileobject if neccesary.  We must acquire the Fcb
5996             //  now to synchronize with other operations (such as dismount ripping
5997             //  apart the allocator).
5998             //
5999 
6000             (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb );
6001             FcbAcquired = TRUE;
6002 
6003             FatVerifyFcb( IrpContext, FcbOrDcb );
6004 
6005             FatOpenDirectoryFile( IrpContext, FcbOrDcb );
6006             DirStreamFileObject = FcbOrDcb->Specific.Dcb.DirectoryFile;
6007 
6008             //
6009             //  Transfer our reference to the internal stream and proceed.  Note that
6010             //  if we dereferenced first, the user could sneak a teardown through since
6011             //  we'd have no references.
6012             //
6013 
6014             ObReferenceObject( DirStreamFileObject );
6015             ObDereferenceObject( FileObject );
6016             FileObject = DirStreamFileObject;
6017 
6018             //
6019             //  We've referenced the DirStreamFileObject, so it should be ok to drop
6020             //  the Dcb now.
6021             //
6022 
6023             FatReleaseFcb( IrpContext, FcbOrDcb );
6024             FcbAcquired = FALSE;
6025         }
6026 
6027         //
6028         //  Determine the size of the buffer we will use to move data.
6029         //
6030 
6031         BufferSize = FAT_DEFAULT_DEFRAG_CHUNK_IN_BYTES;
6032 
6033         if (BufferSize < (ULONG)(1 << ClusterShift)) {
6034 
6035             BufferSize = (1 << ClusterShift);
6036         }
6037 
6038         while (ByteCount) {
6039 
6040             VBO TempVbo;
6041             LBO TempLbo;
6042             ULONG TempByteCount;
6043 
6044             //
6045             //  We must throttle our writes.
6046             //
6047 
6048             CcCanIWrite( FileObject,
6049                          BufferSize,
6050                          TRUE,
6051                          FALSE );
6052 
6053             //
6054             //  Aqcuire file resource exclusive to freeze FileSize and block
6055             //  user non-cached I/O.  Verify the integrity of the fcb - the
6056             //  media may have changed (or been dismounted) on us.
6057             //
6058 
6059             if (FcbAcquired == FALSE) {
6060 
6061                 (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb );
6062                 FcbAcquired = TRUE;
6063 
6064                 FatVerifyFcb( IrpContext, FcbOrDcb );
6065             }
6066 
6067             //
6068             //  Check if the handle indicates we're allowed to move the file.
6069             //
6070             //  FCB_STATE_DENY_DEFRAG indicates that someone blocked move file on this FCB.
6071             //  CCB_FLAG_DENY_DEFRAG indicates that this handle was the one that blocked move file, and hence
6072             //  it still gets to move the file around.
6073             //
6074 
6075             if ((FcbOrDcb->FcbState & FCB_STATE_DENY_DEFRAG) && !(Ccb->Flags & CCB_FLAG_DENY_DEFRAG)) {
6076                 DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_ACCESS_DENIED);
6077                 try_return( Status = STATUS_ACCESS_DENIED );
6078             }
6079 
6080             //
6081             //  Allocate our buffer, if we need to.
6082             //
6083 
6084             if (Buffer == NULL) {
6085 
6086                 Buffer = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
6087                                                    BufferSize,
6088                                                    TAG_DEFRAG_BUFFER );
6089             }
6090 
6091             //
6092             //  Analyzes the range of file allocation we are moving
6093             //  and determines the actual amount of allocation to be
6094             //  moved and how much needs to be written.  In addition
6095             //  it guarantees that the Mcb in the file is large enough
6096             //  so that later MCB operations cannot fail.
6097             //
6098 
6099             FatComputeMoveFileParameter( IrpContext,
6100                                          FcbOrDcb,
6101                                          BufferSize,
6102                                          FileOffset,
6103                                          &ByteCount,
6104                                          &BytesToReallocate,
6105                                          &BytesToWrite,
6106                                          &LargeSourceLbo );
6107 
6108             //
6109             //  If ByteCount comes back zero, break here.
6110             //
6111 
6112             if (ByteCount == 0) {
6113                 break;
6114             }
6115 
6116             //
6117             //  At this point (before actually doing anything with the disk
6118             //  meta data), calculate the FAT splice clusters and build an
6119             //  MCB describing the space to be deallocated.
6120             //
6121 
6122             FatComputeMoveFileSplicePoints( IrpContext,
6123                                             FcbOrDcb,
6124                                             FileOffset,
6125                                             TargetCluster,
6126                                             BytesToReallocate,
6127                                             &FirstSpliceSourceCluster,
6128                                             &FirstSpliceTargetCluster,
6129                                             &SecondSpliceSourceCluster,
6130                                             &SecondSpliceTargetCluster,
6131                                             &SourceMcb );
6132 
6133             //
6134             //  Now attempt to allocate the new disk storage using the
6135             //  Target Lcn as a hint.
6136             //
6137 
6138             TempByteCount = BytesToReallocate;
6139             FatAllocateDiskSpace( IrpContext,
6140                                   Vcb,
6141                                   TargetCluster,
6142                                   &TempByteCount,
6143                                   TRUE,
6144                                   &TargetMcb );
6145 
6146             DiskSpaceAllocated = TRUE;
6147 
6148             //
6149             //  If we didn't get EXACTLY what we wanted, return immediately.
6150             //
6151 
6152             if ((FsRtlNumberOfRunsInLargeMcb( &TargetMcb ) != 1) ||
6153                 !FatGetNextMcbEntry( Vcb, &TargetMcb, 0, &TempVbo, &TempLbo, &TempByteCount ) ||
6154                 (FatGetIndexFromLbo( Vcb, TempLbo) != TargetCluster ) ||
6155                 (TempByteCount != BytesToReallocate)) {
6156 
6157                 //
6158                 //  It would be nice if we could be more specific, but such is life.
6159                 //
6160                 try_return( Status = STATUS_INVALID_PARAMETER );
6161             }
6162 
6163 #if DBG
6164             //
6165             //  We are going to attempt a move, note it.
6166             //
6167 
6168             if (FatMoveFileDebug) {
6169                 DbgPrint("0x%p: Vcn 0x%lx, Lcn 0x%lx, Count 0x%lx.\n",
6170                          PsGetCurrentThread(),
6171                          FileOffset >> ClusterShift,
6172                          TargetCluster,
6173                          BytesToReallocate >> ClusterShift );
6174             }
6175 #endif
6176 
6177             //
6178             //  Now attempt to commit the new allocation to disk.  If this
6179             //  raises, the allocation will be deallocated.
6180             //
6181             //  If the VDL of the file is zero, it has no valid data in it anyway.
6182             //  So it should be safe to avoid flushing the FAT entries and let them be
6183             //  lazily written out.
6184             //
6185             //  This is  done so that bitlocker's cover file doesn't cause
6186             //  unnecessary FAT table I/O when it's moved around.
6187             //  (See Win8 bug 106505)
6188             //
6189 
6190             if ((FcbOrDcb->Header.ValidDataLength.QuadPart != 0) || (NodeType(FcbOrDcb) != FAT_NTC_FCB)) {
6191 
6192                 FatFlushFatEntries( IrpContext,
6193                                     Vcb,
6194                                     TargetCluster,
6195                                     BytesToReallocate >> ClusterShift );
6196             }
6197 
6198             //
6199             //  Aqcuire both resources exclusive now, guaranteeing that NOBODY
6200             //  is in either the read or write paths.
6201             //
6202 
6203             ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE );
6204 
6205             //
6206             //  This is the first part of some tricky synchronization.
6207             //
6208             //  Set the Event pointer in the FCB.  Any paging I/O will block on
6209             //  this event (if set in FCB) after acquiring the PagingIo resource.
6210             //
6211             //  This is how I keep ALL I/O out of this path without holding the
6212             //  PagingIo resource exclusive for an extended time.
6213             //
6214 
6215             FcbOrDcb->MoveFileEvent = &StackEvent;
6216             EventArmed = TRUE;
6217 
6218             ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
6219 
6220             //
6221             //  Now write out the data, but only if we have to.  We don't have
6222             //  to copy any file data if the range being reallocated is wholly
6223             //  beyond valid data length.
6224             //
6225 
6226             if (BytesToWrite) {
6227 
6228                 PIRP IoIrp;
6229                 KEVENT IoEvent;
6230                 IO_STATUS_BLOCK Iosb;
6231 
6232                 KeInitializeEvent( &IoEvent,
6233                                    NotificationEvent,
6234                                    FALSE );
6235 
6236                 NT_ASSERT( LargeTargetLbo.QuadPart >= Vcb->AllocationSupport.FileAreaLbo );
6237 
6238                 //
6239                 //  Read in the data that is being moved.
6240                 //
6241 
6242                 IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
6243                                                       Vcb->TargetDeviceObject,
6244                                                       Buffer,
6245                                                       BytesToWrite,
6246                                                       &LargeSourceLbo,
6247                                                       &IoEvent,
6248                                                       &Iosb );
6249 
6250                 if (IoIrp == NULL) {
6251 
6252                     FatRaiseStatus( IrpContext,
6253                                     STATUS_INSUFFICIENT_RESOURCES );
6254                 }
6255 
6256                 Status = IoCallDriver( Vcb->TargetDeviceObject, IoIrp );
6257 
6258                 if (Status == STATUS_PENDING) {
6259 
6260                     (VOID)KeWaitForSingleObject( &IoEvent,
6261                                                  Executive,
6262                                                  KernelMode,
6263                                                  FALSE,
6264                                                  (PLARGE_INTEGER)NULL );
6265 
6266                     Status = Iosb.Status;
6267                 }
6268 
6269                 if (!NT_SUCCESS( Status )) {
6270 
6271                     FatNormalizeAndRaiseStatus( IrpContext,
6272                                                 Status );
6273                 }
6274 
6275                 //
6276                 //  Write the data to its new location.
6277                 //
6278 
6279                 KeClearEvent( &IoEvent );
6280 
6281                 IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE,
6282                                                       Vcb->TargetDeviceObject,
6283                                                       Buffer,
6284                                                       BytesToWrite,
6285                                                       &LargeTargetLbo,
6286                                                       &IoEvent,
6287                                                       &Iosb );
6288 
6289                 if (IoIrp == NULL) {
6290 
6291                     FatRaiseStatus( IrpContext,
6292                                     STATUS_INSUFFICIENT_RESOURCES );
6293                 }
6294 
6295                 //
6296                 //  Set a flag indicating that we want to write through any
6297                 //  cache on the controller.  This eliminates the need for
6298                 //  an explicit flush-device after the write.
6299                 //
6300 
6301                 SetFlag( IoGetNextIrpStackLocation(IoIrp)->Flags, SL_WRITE_THROUGH );
6302 
6303                 Status = IoCallDriver( Vcb->TargetDeviceObject, IoIrp );
6304 
6305                 if (Status == STATUS_PENDING) {
6306 
6307                     (VOID)KeWaitForSingleObject( &IoEvent,
6308                                                  Executive,
6309                                                  KernelMode,
6310                                                  FALSE,
6311                                                  (PLARGE_INTEGER)NULL );
6312 
6313                     Status = Iosb.Status;
6314                 }
6315 
6316                 if (!NT_SUCCESS( Status )) {
6317 
6318                     FatNormalizeAndRaiseStatus( IrpContext,
6319                                                 Status );
6320                 }
6321             }
6322 
6323             //
6324             //  Now that the file data has been moved successfully, we'll go
6325             //  to fix up the links in the FAT table and perhaps change the
6326             //  entry in the parent directory.
6327             //
6328             //  First we'll do the second splice and commit it.  At that point,
6329             //  while the volume is in an inconsistent state, the file is
6330             //  still OK.
6331             //
6332 
6333             FatSetFatEntry( IrpContext,
6334                             Vcb,
6335                             SecondSpliceSourceCluster,
6336                             (FAT_ENTRY)SecondSpliceTargetCluster );
6337 
6338             if ((FcbOrDcb->Header.ValidDataLength.QuadPart != 0) || (NodeType(FcbOrDcb) != FAT_NTC_FCB)) {
6339 
6340                 FatFlushFatEntries( IrpContext, Vcb, SecondSpliceSourceCluster, 1 );
6341             }
6342 
6343             //
6344             //  Now do the first splice OR update the dirent in the parent
6345             //  and flush the respective object.  After this flush the file
6346             //  now points to the new allocation.
6347             //
6348 
6349             if (FirstSpliceSourceCluster == 0) {
6350 
6351                 NT_ASSERT( NodeType(FcbOrDcb) == FAT_NTC_FCB );
6352 
6353                 //
6354                 //  We are moving the first cluster of the file, so we need
6355                 //  to update our parent directory.
6356                 //
6357 
6358                 FatGetDirentFromFcbOrDcb( IrpContext,
6359                                           FcbOrDcb,
6360                                           FALSE,
6361                                           &Dirent,
6362                                           &DirentBcb );
6363 
6364                 Dirent->FirstClusterOfFile = (USHORT)FirstSpliceTargetCluster;
6365 
6366                 if (FatIsFat32(Vcb)) {
6367 
6368                     Dirent->FirstClusterOfFileHi =
6369                         (USHORT)(FirstSpliceTargetCluster >> 16);
6370 
6371                 }
6372 
6373                 FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE );
6374 
6375                 FatUnpinBcb( IrpContext, DirentBcb );
6376                 DirentBcb = NULL;
6377 
6378                 FatFlushDirentForFile( IrpContext, FcbOrDcb );
6379 
6380                 FcbOrDcb->FirstClusterOfFile = FirstSpliceTargetCluster;
6381 
6382             } else {
6383 
6384                 FatSetFatEntry( IrpContext,
6385                                 Vcb,
6386                                 FirstSpliceSourceCluster,
6387                                 (FAT_ENTRY)FirstSpliceTargetCluster );
6388 
6389                 if ((FcbOrDcb->Header.ValidDataLength.QuadPart != 0) || (NodeType(FcbOrDcb) != FAT_NTC_FCB)) {
6390 
6391                     FatFlushFatEntries( IrpContext, Vcb, FirstSpliceSourceCluster, 1 );
6392                 }
6393             }
6394 
6395             //
6396             //  This was successfully committed.  We no longer want to free
6397             //  this allocation on error.
6398             //
6399 
6400             DiskSpaceAllocated = FALSE;
6401 
6402             //
6403             //  Check if we need to turn off write through for this file.
6404             //
6405 
6406             FatMoveFileNeedsWriteThrough(IrpContext, FcbOrDcb, OldWriteThroughFlags);
6407 
6408             //
6409             //  Now we just have to free the orphaned space.  We don't have
6410             //  to commit this right now as the integrity of the file doesn't
6411             //  depend on it.
6412             //
6413 
6414             FatDeallocateDiskSpace( IrpContext, Vcb, &SourceMcb, FALSE );
6415 
6416             FatUnpinRepinnedBcbs( IrpContext );
6417 
6418             Status = FatHijackIrpAndFlushDevice( IrpContext,
6419                                                  Irp,
6420                                                  Vcb->TargetDeviceObject );
6421 
6422             if (!NT_SUCCESS(Status)) {
6423                 FatNormalizeAndRaiseStatus( IrpContext, Status );
6424             }
6425 
6426             //
6427             //  Finally we must replace the old MCB extent information with
6428             //  the new.  If this fails from pool allocation, we fix it in
6429             //  the finally clause by resetting the file's Mcb.
6430             //
6431 
6432             FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb,
6433                                FileOffset,
6434                                BytesToReallocate );
6435 
6436             FatAddMcbEntry( Vcb, &FcbOrDcb->Mcb,
6437                             FileOffset,
6438                             TargetLbo,
6439                             BytesToReallocate );
6440 
6441             //
6442             //  Now this is the second part of the tricky synchronization.
6443             //
6444             //  We drop the paging I/O here and signal the notification
6445             //  event which allows all waiters (present or future) to proceed.
6446             //  Then we block again on the PagingIo exclusive.  When
6447             //  we have it, we again know that there can be nobody in the
6448             //  read/write path and thus nobody touching the event, so we
6449             //  NULL the pointer to it and then drop the PagingIo resource.
6450             //
6451             //  This combined with our synchronization before the write above
6452             //  guarantees that while we were moving the allocation, there
6453             //  was no other I/O to this file and because we do not hold
6454             //  the paging resource across a flush, we are not exposed to
6455             //  a deadlock.
6456             //
6457 
6458             KeSetEvent( &StackEvent, 0, FALSE );
6459 
6460             ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE );
6461 
6462             FcbOrDcb->MoveFileEvent = NULL;
6463             EventArmed = FALSE;
6464 
6465             ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
6466 
6467             //
6468             //  Release the resources and let anyone else access the file before
6469             //  looping back.
6470             //
6471 
6472             FatReleaseFcb( IrpContext, FcbOrDcb );
6473             FcbAcquired = FALSE;
6474 
6475             //
6476             //  Advance the state variables.
6477             //
6478 
6479             TargetCluster += BytesToReallocate >> ClusterShift;
6480 
6481             FileOffset += BytesToReallocate;
6482             TargetLbo += BytesToReallocate;
6483             ByteCount -= BytesToReallocate;
6484 
6485             LargeTargetLbo.QuadPart += BytesToReallocate;
6486 
6487             //
6488             //  Clear the two Mcbs
6489             //
6490 
6491             FatRemoveMcbEntry( Vcb, &SourceMcb, 0, 0xFFFFFFFF );
6492             FatRemoveMcbEntry( Vcb, &TargetMcb, 0, 0xFFFFFFFF );
6493 
6494             //
6495             //  Make the event blockable again.
6496             //
6497 
6498             KeClearEvent( &StackEvent );
6499         }
6500 
6501         Status = STATUS_SUCCESS;
6502 
6503     try_exit: NOTHING;
6504 
6505     } _SEH2_FINALLY {
6506 
6507         DebugUnwind( FatMoveFile );
6508 
6509         LocalAbnormalTermination |= _SEH2_AbnormalTermination();
6510 
6511         ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD );
6512 
6513         //
6514         //  Free the data buffer, if it was allocated.
6515         //
6516 
6517         if (Buffer != NULL) {
6518 
6519             ExFreePool( Buffer );
6520         }
6521 
6522         //
6523         //  Use a nested try-finally for cleanup if our unpinrepinned
6524         //  encounters write-through errors.  This may even be a re-raise.
6525         //
6526 
6527         _SEH2_TRY {
6528 
6529             //
6530             //  If we have some new allocation hanging around, remove it.  The
6531             //  pages needed to do this are guaranteed to be resident because
6532             //  we have already repinned them.
6533             //
6534 
6535             if (DiskSpaceAllocated) {
6536                 FatDeallocateDiskSpace( IrpContext, Vcb, &TargetMcb, FALSE );
6537                 FatUnpinRepinnedBcbs( IrpContext );
6538             }
6539 
6540         } _SEH2_FINALLY {
6541 
6542             LocalAbnormalTermination |= _SEH2_AbnormalTermination();
6543 
6544             //
6545             //  Check on the directory Bcb
6546             //
6547 
6548             if (DirentBcb != NULL) {
6549                 FatUnpinBcb( IrpContext, DirentBcb );
6550             }
6551 
6552             //
6553             //  Uninitialize our MCBs
6554             //
6555 
6556             if (SourceMcbInitialized) {
6557                 FsRtlUninitializeLargeMcb( &SourceMcb );
6558             }
6559 
6560             if (TargetMcbInitialized) {
6561                 FsRtlUninitializeLargeMcb( &TargetMcb );
6562             }
6563 
6564             //
6565             //  If this is an abnormal termination then presumably something
6566             //  bad happened.  Set the Allocation size to unknown and clear
6567             //  the Mcb, but only if we still own the Fcb.
6568             //
6569             //  It is important to make sure we use a 64bit form of -1.  This is
6570             //  what will convince the fastIO path that it cannot extend the file
6571             //  in the cache until we have picked up the mapping pairs again.
6572             //
6573             //  Also, we have to do this while owning PagingIo or we can tear the
6574             //  Mcb down in the midst of the noncached IO path looking up extents
6575             //  (after we drop it and let them all in).
6576             //
6577 
6578             if (LocalAbnormalTermination && FcbAcquired) {
6579 
6580                 if (FcbOrDcb->FirstClusterOfFile == 0) {
6581 
6582                     FcbOrDcb->Header.AllocationSize.QuadPart = 0;
6583 
6584                 } else {
6585 
6586                     FcbOrDcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
6587                 }
6588 
6589                 FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb, 0, 0xFFFFFFFF );
6590             }
6591 
6592             //
6593             //  If we broke out of the loop with the Event armed, defuse it
6594             //  in the same way we do it after a write.
6595             //
6596 
6597             if (EventArmed) {
6598                 KeSetEvent( &StackEvent, 0, FALSE );
6599                 ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE );
6600                 FcbOrDcb->MoveFileEvent = NULL;
6601                 ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
6602             }
6603 
6604             //
6605             //  Finally release the main file resource.
6606             //
6607 
6608             if (FcbAcquired) {
6609 
6610                 FatReleaseFcb( IrpContext, FcbOrDcb );
6611             }
6612 
6613             //
6614             //  Now dereference the fileobject.  If the user was a wacko they could have
6615             //  tried to nail us by closing the handle right after they threw this move
6616             //  down, so we had to keep the fileobject referenced across the entire
6617             //  operation.
6618             //
6619 
6620             ObDereferenceObject( FileObject );
6621 
6622         } _SEH2_END;
6623     } _SEH2_END;
6624 
6625     //
6626     //  Complete the irp if we terminated normally.
6627     //
6628 
6629     FatCompleteRequest( IrpContext, Irp, Status );
6630 
6631     return Status;
6632 }
6633 
6634 
6635 //
6636 //  Local Support Routine
6637 //
6638 
6639 _Requires_lock_held_(_Global_critical_region_)
6640 VOID
6641 FatComputeMoveFileParameter (
6642     IN PIRP_CONTEXT IrpContext,
6643     IN PFCB FcbOrDcb,
6644     IN ULONG BufferSize,
6645     IN ULONG FileOffset,
6646     IN OUT PULONG ByteCount,
6647     OUT PULONG BytesToReallocate,
6648     OUT PULONG BytesToWrite,
6649     OUT PLARGE_INTEGER SourceLbo
6650 )
6651 
6652 /*++
6653 
6654 Routine Description:
6655 
6656     This is a helper routine for FatMoveFile that analyses the range of
6657     file allocation we are moving and determines the actual amount
6658     of allocation to be moved and how much needs to be written.
6659 
6660 Arguments:
6661 
6662     FcbOrDcb - Supplies the file and its various sizes.
6663 
6664     BufferSize - Supplies the size of the buffer we are using to store the data
6665                  being moved.
6666 
6667     FileOffset - Supplies the beginning Vbo of the reallocation zone.
6668 
6669     ByteCount - Supplies the request length to reallocate.  This will
6670         be bounded by allocation size on return.
6671 
6672     BytesToReallocate - Receives ByteCount bounded by the file allocation size
6673         and buffer size.
6674 
6675     BytesToWrite - Receives BytesToReallocate bounded by ValidDataLength.
6676 
6677     SourceLbo - Receives the logical byte offset of the source data on the volume.
6678 
6679 Return Value:
6680 
6681     VOID
6682 
6683 --*/
6684 
6685 {
6686     ULONG ClusterSize;
6687 
6688     ULONG AllocationSize;
6689     ULONG ValidDataLength;
6690     ULONG ClusterAlignedVDL;
6691     LBO RunLbo;
6692     ULONG RunByteCount;
6693     ULONG RunIndex;
6694     BOOLEAN RunAllocated;
6695     BOOLEAN RunEndOnMax;
6696 
6697     PAGED_CODE();
6698 
6699     //
6700     //  If we haven't yet set the correct AllocationSize, do so.
6701     //
6702 
6703     if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
6704 
6705         FatLookupFileAllocationSize( IrpContext, FcbOrDcb );
6706 
6707         //
6708         //  If this is a non-root directory, we have a bit more to
6709         //  do since it has not gone through FatOpenDirectoryFile().
6710         //
6711 
6712         if (NodeType(FcbOrDcb) == FAT_NTC_DCB ||
6713             (NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && FatIsFat32(FcbOrDcb->Vcb))) {
6714 
6715             FcbOrDcb->Header.FileSize.LowPart =
6716                 FcbOrDcb->Header.AllocationSize.LowPart;
6717         }
6718     }
6719 
6720     //
6721     //  Get the number of bytes left to write and ensure that it does
6722     //  not extend beyond allocation size.  We return here if FileOffset
6723     //  is beyond AllocationSize which can happn on a truncation.
6724     //
6725 
6726     AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart;
6727     ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart;
6728 
6729     if (FileOffset + *ByteCount > AllocationSize) {
6730 
6731         if (FileOffset >= AllocationSize) {
6732             *ByteCount = 0;
6733             *BytesToReallocate = 0;
6734             *BytesToWrite = 0;
6735 
6736             return;
6737         }
6738 
6739         *ByteCount = AllocationSize - FileOffset;
6740     }
6741 
6742     //
6743     //  If there is more than our max, then reduce the byte count for this
6744     //  pass to our maximum. We must also align the file offset to a
6745     //  buffer size byte boundary.
6746     //
6747 
6748     if ((FileOffset & (BufferSize - 1)) + *ByteCount > BufferSize) {
6749 
6750         *BytesToReallocate = BufferSize - (FileOffset & (BufferSize - 1));
6751 
6752     } else {
6753 
6754         *BytesToReallocate = *ByteCount;
6755     }
6756 
6757     //
6758     //  Find where this data exists on the volume.
6759     //
6760 
6761     FatLookupFileAllocation( IrpContext,
6762                              FcbOrDcb,
6763                              FileOffset,
6764                              &RunLbo,
6765                              &RunByteCount,
6766                              &RunAllocated,
6767                              &RunEndOnMax,
6768                              &RunIndex );
6769 
6770     NT_ASSERT( RunAllocated );
6771 
6772     //
6773     //  Limit this run to the contiguous length.
6774     //
6775 
6776     if (RunByteCount < *BytesToReallocate) {
6777 
6778         *BytesToReallocate = RunByteCount;
6779     }
6780 
6781     //
6782     //  Set the starting offset of the source.
6783     //
6784 
6785     SourceLbo->QuadPart = RunLbo;
6786 
6787     //
6788     //  We may be able to skip some (or all) of the write
6789     //  if allocation size is significantly greater than valid data length.
6790     //
6791 
6792     ClusterSize = 1 << FcbOrDcb->Vcb->AllocationSupport.LogOfBytesPerCluster;
6793 
6794     NT_ASSERT( ClusterSize <= BufferSize );
6795 
6796     ClusterAlignedVDL = (ValidDataLength + (ClusterSize - 1)) & ~(ClusterSize - 1);
6797 
6798     if ((NodeType(FcbOrDcb) == FAT_NTC_FCB) &&
6799         (FileOffset + *BytesToReallocate > ClusterAlignedVDL)) {
6800 
6801         if (FileOffset > ClusterAlignedVDL) {
6802 
6803             *BytesToWrite = 0;
6804 
6805         } else {
6806 
6807             *BytesToWrite = ClusterAlignedVDL - FileOffset;
6808         }
6809 
6810     } else {
6811 
6812         *BytesToWrite = *BytesToReallocate;
6813     }
6814 }
6815 
6816 
6817 //
6818 //  Local Support Routine
6819 //
6820 
6821 VOID
6822 FatComputeMoveFileSplicePoints (
6823     IN PIRP_CONTEXT IrpContext,
6824     IN PFCB FcbOrDcb,
6825     IN ULONG FileOffset,
6826     IN ULONG TargetCluster,
6827     IN ULONG BytesToReallocate,
6828     OUT PULONG FirstSpliceSourceCluster,
6829     OUT PULONG FirstSpliceTargetCluster,
6830     OUT PULONG SecondSpliceSourceCluster,
6831     OUT PULONG SecondSpliceTargetCluster,
6832     IN OUT PLARGE_MCB SourceMcb
6833 )
6834 
6835 /*++
6836 
6837 Routine Description:
6838 
6839     This is a helper routine for FatMoveFile that analyzes the range of
6840     file allocation we are moving and generates the splice points in the
6841     FAT table.
6842 
6843 Arguments:
6844 
6845     FcbOrDcb - Supplies the file and thus Mcb.
6846 
6847     FileOffset - Supplies the beginning Vbo of the reallocation zone.
6848 
6849     TargetCluster - Supplies the beginning cluster of the reallocation target.
6850 
6851     BytesToReallocate - Suppies the length of the reallocation zone.
6852 
6853     FirstSpliceSourceCluster - Receives the last cluster in previous allocation
6854         or zero if we are reallocating from VBO 0.
6855 
6856     FirstSpliceTargetCluster - Receives the target cluster (i.e. new allocation)
6857 
6858     SecondSpliceSourceCluster - Receives the final target cluster.
6859 
6860     SecondSpliceTargetCluster - Receives the first cluster of the remaining
6861         source allocation or FAT_CLUSTER_LAST if the reallocation zone
6862         extends to the end of the file.
6863 
6864     SourceMcb - This supplies an MCB that will be filled in with run
6865         information describing the file allocation being replaced.  The Mcb
6866         must be initialized by the caller.
6867 
6868 Return Value:
6869 
6870     VOID
6871 
6872 --*/
6873 
6874 {
6875     VBO SourceVbo;
6876     LBO SourceLbo;
6877     ULONG SourceIndex;
6878     ULONG SourceBytesInRun;
6879     ULONG SourceBytesRemaining;
6880 
6881     ULONG SourceMcbVbo = 0;
6882     ULONG SourceMcbBytesInRun = 0;
6883 
6884     PVCB Vcb;
6885     BOOLEAN Result;
6886 
6887     PAGED_CODE();
6888 
6889     Vcb = FcbOrDcb->Vcb;
6890 
6891     //
6892     //  Get information on the final cluster in the previous allocation and
6893     //  prepare to enumerate it in the follow loop.
6894     //
6895 
6896     if (FileOffset == 0) {
6897 
6898         SourceIndex = 0;
6899         *FirstSpliceSourceCluster = 0;
6900         Result = FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb,
6901                                      0,
6902                                      &SourceVbo,
6903                                      &SourceLbo,
6904                                      &SourceBytesInRun );
6905 
6906     } else {
6907 
6908         Result = FatLookupMcbEntry( Vcb, &FcbOrDcb->Mcb,
6909                                     FileOffset-1,
6910                                     &SourceLbo,
6911                                     &SourceBytesInRun,
6912                                     &SourceIndex);
6913 
6914         *FirstSpliceSourceCluster = FatGetIndexFromLbo( Vcb, SourceLbo );
6915 
6916         if ((Result) && (SourceBytesInRun == 1)) {
6917 
6918             SourceIndex += 1;
6919             Result = FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb,
6920                                          SourceIndex,
6921                                          &SourceVbo,
6922                                          &SourceLbo,
6923                                          &SourceBytesInRun);
6924 
6925         } else {
6926 
6927             SourceVbo = FileOffset;
6928             SourceLbo += 1;
6929             SourceBytesInRun -= 1;
6930         }
6931     }
6932 
6933     //
6934     //  Run should always be present, but don't bugcheck in the case where it's not.
6935     //
6936 
6937     if (!Result) {
6938 
6939         NT_ASSERT( FALSE);
6940         FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR);
6941     }
6942 
6943     //
6944     //  At this point the variables:
6945     //
6946     //  - SourceIndex - SourceLbo - SourceBytesInRun -
6947     //
6948     //  all correctly decribe the allocation to be removed.  In the loop
6949     //  below we will start here and continue enumerating the Mcb runs
6950     //  until we are finished with the allocation to be relocated.
6951     //
6952 
6953     *FirstSpliceTargetCluster = TargetCluster;
6954 
6955     *SecondSpliceSourceCluster =
6956          *FirstSpliceTargetCluster +
6957          (BytesToReallocate >> Vcb->AllocationSupport.LogOfBytesPerCluster) - 1;
6958 
6959     for (SourceBytesRemaining = BytesToReallocate, SourceMcbVbo = 0;
6960 
6961          SourceBytesRemaining > 0;
6962 
6963          SourceIndex += 1,
6964          SourceBytesRemaining -= SourceMcbBytesInRun,
6965          SourceMcbVbo += SourceMcbBytesInRun) {
6966 
6967         if (SourceMcbVbo != 0) {
6968 #ifdef _MSC_VER
6969 #pragma prefast( suppress:28931, "needed for debug build" )
6970 #endif
6971             Result = FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb,
6972                                          SourceIndex,
6973                                          &SourceVbo,
6974                                          &SourceLbo,
6975                                          &SourceBytesInRun );
6976             NT_ASSERT( Result);
6977         }
6978 
6979         NT_ASSERT( SourceVbo == SourceMcbVbo + FileOffset );
6980 
6981         SourceMcbBytesInRun =
6982             SourceBytesInRun < SourceBytesRemaining ?
6983             SourceBytesInRun : SourceBytesRemaining;
6984 
6985         FatAddMcbEntry( Vcb, SourceMcb,
6986                         SourceMcbVbo,
6987                         SourceLbo,
6988                         SourceMcbBytesInRun );
6989     }
6990 
6991     //
6992     //  Now compute the cluster of the target of the second
6993     //  splice.  If the final run in the above loop was
6994     //  more than we needed, then we can just do arithmetic,
6995     //  otherwise we have to look up the next run.
6996     //
6997 
6998     if (SourceMcbBytesInRun < SourceBytesInRun) {
6999 
7000         *SecondSpliceTargetCluster =
7001             FatGetIndexFromLbo( Vcb, SourceLbo + SourceMcbBytesInRun );
7002 
7003     } else {
7004 
7005         if (FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb,
7006                                 SourceIndex,
7007                                 &SourceVbo,
7008                                 &SourceLbo,
7009                                 &SourceBytesInRun )) {
7010 
7011             *SecondSpliceTargetCluster = FatGetIndexFromLbo( Vcb, SourceLbo );
7012 
7013         } else {
7014 
7015             *SecondSpliceTargetCluster = FAT_CLUSTER_LAST;
7016         }
7017     }
7018 }
7019 
7020 
7021 NTSTATUS
7022 FatAllowExtendedDasdIo(
7023     IN PIRP_CONTEXT IrpContext,
7024     IN PIRP Irp
7025     )
7026 /*++
7027 
7028 Routine Description:
7029 
7030     This routine marks the CCB to indicate that the handle
7031     may be used to read past the end of the volume file.  The
7032     handle must be a dasd handle.
7033 
7034 Arguments:
7035 
7036     Irp - Supplies the Irp being processed.
7037 
7038 Return Value:
7039 
7040     NTSTATUS - The return status for the operation.
7041 
7042 --*/
7043 {
7044     PIO_STACK_LOCATION IrpSp;
7045     PVCB Vcb;
7046     PFCB Fcb;
7047     PCCB Ccb;
7048 
7049     PAGED_CODE();
7050 
7051     //
7052     //  Get the current Irp stack location and save some references.
7053     //
7054 
7055     IrpSp = IoGetCurrentIrpStackLocation( Irp );
7056 
7057     //
7058     //  Extract and decode the file object and check for type of open.
7059     //
7060 
7061     if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
7062 
7063         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7064         return STATUS_INVALID_PARAMETER;
7065     }
7066 
7067     if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
7068 
7069         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7070 
7071         DebugTrace(-1, Dbg, "FatAllowExtendedDasdIo -> %08lx\n", STATUS_INVALID_PARAMETER);
7072         return STATUS_INVALID_PARAMETER;
7073     }
7074 
7075     SetFlag( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO );
7076 
7077     FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
7078     return STATUS_SUCCESS;
7079 }
7080 
7081 #if (NTDDI_VERSION >= NTDDI_WIN7)
7082 
7083 _Requires_lock_held_(_Global_critical_region_)
7084 NTSTATUS
7085 FatGetRetrievalPointerBase (
7086     _In_ PIRP_CONTEXT IrpContext,
7087     _In_ PIRP Irp
7088     )
7089 /*++
7090 
7091 Routine Description:
7092 
7093     This routine retrieves the sector offset to the first allocation unit.
7094 
7095 Arguments:
7096 
7097     IrpContext - Supplies the Irp Context.
7098     Irp - Supplies the Irp being processed.
7099 
7100 Return Value:
7101 
7102     NTSTATUS - The return status for the operation.
7103 
7104 --*/
7105 {
7106     PIO_STACK_LOCATION              IrpSp = NULL;
7107     PVCB                            Vcb = NULL;
7108     PFCB                            Fcb = NULL;
7109     PCCB                            Ccb = NULL;
7110     ULONG                           BufferLength = 0;
7111     PRETRIEVAL_POINTER_BASE         RetrievalPointerBase = NULL;
7112 
7113     PAGED_CODE();
7114 
7115     IrpSp = IoGetCurrentIrpStackLocation( Irp );
7116 
7117     //
7118     //  Force WAIT to true.
7119     //
7120 
7121     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
7122 
7123     //
7124     //  Extract and decode the file object and check for type of open.
7125     //
7126 
7127     if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
7128 
7129         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7130         return STATUS_INVALID_PARAMETER;
7131     }
7132 
7133     //
7134     // Extract the buffer
7135     //
7136 
7137     RetrievalPointerBase = Irp->AssociatedIrp.SystemBuffer;
7138     BufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
7139 
7140     //
7141     // Verify the handle has manage volume access.
7142     //
7143 
7144     if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
7145 
7146         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7147         return STATUS_INVALID_PARAMETER;
7148     }
7149 
7150     //
7151     //  Validate the output buffer is the right size.
7152     //
7153     //  Note that the default size of BOOT_AREA_INFO has enough room for 2 boot sectors, so we're fine.
7154     //
7155 
7156     if (BufferLength < sizeof(RETRIEVAL_POINTER_BASE)) {
7157 
7158         FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
7159         return STATUS_BUFFER_TOO_SMALL;
7160     }
7161 
7162     //
7163     // Fill out the offset to the file area.
7164     //
7165 
7166     RtlZeroMemory( RetrievalPointerBase, BufferLength );
7167 
7168     try {
7169 
7170         FatAcquireSharedVcb(IrpContext, Vcb);
7171         FatQuickVerifyVcb(IrpContext, Vcb);
7172 
7173         RetrievalPointerBase->FileAreaOffset.QuadPart = Vcb->AllocationSupport.FileAreaLbo >> Vcb->AllocationSupport.LogOfBytesPerSector;
7174         Irp->IoStatus.Information = sizeof( RETRIEVAL_POINTER_BASE );
7175 
7176     } finally {
7177 
7178         FatReleaseVcb(IrpContext, Vcb);
7179 
7180     }
7181 
7182     FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
7183 
7184     return STATUS_SUCCESS;
7185 
7186 }
7187 
7188 
7189 _Requires_lock_held_(_Global_critical_region_)
7190 NTSTATUS
7191 FatGetBootAreaInfo (
7192     _In_ PIRP_CONTEXT IrpContext,
7193     _In_ PIRP Irp
7194     )
7195 /*++
7196 
7197 Routine Description:
7198 
7199     This routine retrieves information about the boot areas of the filesystem.
7200 
7201 Arguments:
7202 
7203     IrpContext - Supplies the Irp Context.
7204     Irp - Supplies the Irp being processed.
7205 
7206 Return Value:
7207 
7208     NTSTATUS - The return status for the operation.
7209 
7210 --*/
7211 {
7212     PIO_STACK_LOCATION              IrpSp = NULL;
7213     PVCB                            Vcb = NULL;
7214     PFCB                            Fcb = NULL;
7215     PCCB                            Ccb = NULL;
7216     ULONG                           BufferLength = 0;
7217     PBOOT_AREA_INFO                 BootAreaInfo = NULL;
7218 
7219     PAGED_CODE();
7220 
7221     IrpSp = IoGetCurrentIrpStackLocation( Irp );
7222 
7223     //
7224     //  Force WAIT to true.
7225     //
7226 
7227     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
7228 
7229     //
7230     //  Extract and decode the file object and check for type of open.
7231     //
7232 
7233     if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) {
7234 
7235         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7236         return STATUS_INVALID_PARAMETER;
7237     }
7238 
7239     //
7240     // Extract the buffer
7241     //
7242 
7243     BootAreaInfo = Irp->AssociatedIrp.SystemBuffer;
7244     BufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
7245 
7246     //
7247     // Verify the handle has manage volume access.
7248     //
7249 
7250     if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
7251 
7252         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7253         return STATUS_INVALID_PARAMETER;
7254     }
7255 
7256     //
7257     // Validate the output buffer is the right size.
7258     //
7259     // Note that the default size of BOOT_AREA_INFO has enough room for 2 boot sectors, so we're fine.
7260     //
7261 
7262     if (BufferLength < sizeof(BOOT_AREA_INFO)) {
7263 
7264         FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
7265         return STATUS_BUFFER_TOO_SMALL;
7266     }
7267 
7268     //
7269     // Fill out our boot areas.
7270     //
7271 
7272     RtlZeroMemory( BootAreaInfo, BufferLength );
7273 
7274     try {
7275 
7276         FatAcquireSharedVcb(IrpContext, Vcb);
7277         FatQuickVerifyVcb(IrpContext, Vcb);
7278 
7279         if (FatIsFat32( Vcb )) {
7280 
7281             BootAreaInfo->BootSectorCount = 2;
7282             BootAreaInfo->BootSectors[0].Offset.QuadPart = 0;
7283             BootAreaInfo->BootSectors[1].Offset.QuadPart = 6;
7284         } else {
7285 
7286             BootAreaInfo->BootSectorCount = 1;
7287             BootAreaInfo->BootSectors[0].Offset.QuadPart = 0;
7288         }
7289 
7290         Irp->IoStatus.Information = sizeof( BOOT_AREA_INFO );
7291 
7292     } finally {
7293 
7294         FatReleaseVcb(IrpContext, Vcb);
7295     }
7296 
7297     FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
7298     return STATUS_SUCCESS;
7299 }
7300 
7301 #endif
7302 
7303 
7304 _Requires_lock_held_(_Global_critical_region_)
7305 NTSTATUS
7306 FatMarkHandle (
7307     _In_ PIRP_CONTEXT IrpContext,
7308     _In_ PIRP Irp
7309     )
7310 /*++
7311 
7312 Routine Description:
7313 
7314     This routine is used to attach special properties to a user handle.
7315 
7316 Arguments:
7317 
7318     IrpContext - Supplies the Irp Context.
7319     Irp - Supplies the Irp being processed.
7320 
7321 Return Value:
7322 
7323     NTSTATUS - The return status for the operation.
7324 
7325 --*/
7326 {
7327     NTSTATUS                        Status = STATUS_SUCCESS;
7328     PIO_STACK_LOCATION              IrpSp = NULL;
7329     PVCB                            Vcb = NULL;
7330     PFCB                            Fcb = NULL;
7331     PCCB                            Ccb = NULL;
7332     PFCB                            DasdFcb = NULL;
7333     PCCB                            DasdCcb = NULL;
7334     TYPE_OF_OPEN                    TypeOfOpen;
7335     PMARK_HANDLE_INFO               HandleInfo = NULL;
7336     PFILE_OBJECT                    DasdFileObject = NULL;
7337     BOOLEAN                         ReleaseFcb = FALSE;
7338 
7339 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
7340     MARK_HANDLE_INFO                LocalMarkHandleInfo = {0};
7341 #endif
7342 
7343     PAGED_CODE();
7344 
7345     IrpSp = IoGetCurrentIrpStackLocation( Irp );
7346 
7347     //
7348     //  Always make this a synchronous IRP.
7349     //
7350 
7351     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
7352 
7353     //
7354     //  Extract and decode the file object and check for type of open.
7355     //
7356 
7357     TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) ;
7358 
7359     //
7360     //  We currently support this call for files and directories only.
7361     //
7362 
7363     if ((TypeOfOpen != UserFileOpen) &&
7364         (TypeOfOpen != UserDirectoryOpen)) {
7365 
7366         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7367         return STATUS_INVALID_PARAMETER;
7368     }
7369 
7370 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
7371 
7372     //
7373     //  Win32/64 thunking code
7374     //
7375 
7376     if (IoIs32bitProcess( Irp )) {
7377 
7378         PMARK_HANDLE_INFO32 MarkHandle32;
7379 
7380         if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof( MARK_HANDLE_INFO32 )) {
7381 
7382             FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
7383             return STATUS_BUFFER_TOO_SMALL;
7384         }
7385 
7386         MarkHandle32 = (PMARK_HANDLE_INFO32) Irp->AssociatedIrp.SystemBuffer;
7387         LocalMarkHandleInfo.HandleInfo = MarkHandle32->HandleInfo;
7388         LocalMarkHandleInfo.UsnSourceInfo = MarkHandle32->UsnSourceInfo;
7389         LocalMarkHandleInfo.VolumeHandle = (HANDLE)(ULONG_PTR)(LONG) MarkHandle32->VolumeHandle;
7390 
7391         HandleInfo = &LocalMarkHandleInfo;
7392 
7393     } else {
7394 
7395 #endif
7396 
7397     //
7398     //  Get the input buffer pointer and check its length.
7399     //
7400 
7401     if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof( MARK_HANDLE_INFO )) {
7402 
7403         FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
7404         return STATUS_BUFFER_TOO_SMALL;
7405     }
7406 
7407     HandleInfo = (PMARK_HANDLE_INFO) Irp->AssociatedIrp.SystemBuffer;
7408 
7409 #if defined(_WIN64) && defined(BUILD_WOW64_ENABLED)
7410     }
7411 #endif
7412 
7413     //
7414     //  Check that only legal bits are being set.
7415     //  We currently only support two bits: protect clusters and unprotect clusters.
7416     //
7417     //  Note that we don't actually support the USN journal, but we must ignore the flags in order
7418     //  to preserve compatibility.
7419     //
7420 
7421     if (FlagOn( HandleInfo->HandleInfo,
7422                 ~(MARK_HANDLE_PROTECT_CLUSTERS)) ||
7423         (FlagOn( HandleInfo->HandleInfo,
7424                  0 ) &&
7425          (IrpSp->MinorFunction != IRP_MN_KERNEL_CALL)) ||
7426         FlagOn(HandleInfo->UsnSourceInfo,
7427                 ~(USN_SOURCE_DATA_MANAGEMENT |
7428                   USN_SOURCE_AUXILIARY_DATA |
7429                   USN_SOURCE_REPLICATION_MANAGEMENT) ) ) {
7430 
7431         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7432         return STATUS_INVALID_PARAMETER;
7433     }
7434 
7435     //
7436     //  Check that the user has a valid volume handle or the manage volume
7437     //  privilege or is a kernel mode caller
7438     //
7439     //  NOTE: the kernel mode check is only valid because the rdr doesn't support this
7440     //  FSCTL.
7441     //
7442 
7443     if ((Irp->RequestorMode != KernelMode) &&
7444         (IrpSp->MinorFunction != IRP_MN_KERNEL_CALL) &&
7445         !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS ) &&
7446         ( FlagOn( HandleInfo->HandleInfo, MARK_HANDLE_PROTECT_CLUSTERS ) || (HandleInfo->UsnSourceInfo != 0) )) {
7447 
7448         if (HandleInfo->VolumeHandle == 0) {
7449             FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
7450             return STATUS_ACCESS_DENIED;
7451         }
7452 
7453         Status = ObReferenceObjectByHandle( HandleInfo->VolumeHandle,
7454                                             0,
7455                                             *IoFileObjectType,
7456                                             UserMode,
7457 #ifndef __REACTOS__
7458                                             &DasdFileObject,
7459 #else
7460                                             (PVOID *)&DasdFileObject,
7461 #endif
7462                                             NULL );
7463 
7464         if (!NT_SUCCESS(Status)) {
7465 
7466             FatCompleteRequest( IrpContext, Irp, Status );
7467             return Status;
7468         }
7469 
7470         //
7471         //  Check that this file object is opened on the same volume as the
7472         //  handle used to call this routine.
7473         //
7474 
7475         if (DasdFileObject->Vpb != Vcb->Vpb) {
7476 
7477             ObDereferenceObject( DasdFileObject );
7478 
7479             FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7480             return STATUS_INVALID_PARAMETER;
7481         }
7482 
7483         //
7484         //  Now decode this FileObject and verify it is a volume handle.
7485         //  We don't care to raise on dismounts here because
7486         //  we check for that further down anyway. So send FALSE.
7487         //
7488 
7489 #ifdef _MSC_VER
7490 #pragma prefast( suppress:28931, "convenient for debugging" )
7491 #endif
7492         TypeOfOpen = FatDecodeFileObject( DasdFileObject, &Vcb, &DasdFcb, &DasdCcb ) ;
7493 
7494         ObDereferenceObject( DasdFileObject );
7495 
7496         if ((DasdCcb == NULL) || !FlagOn( DasdCcb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
7497 
7498             FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
7499             return STATUS_ACCESS_DENIED;
7500         }
7501 
7502     }
7503 
7504     _SEH2_TRY {
7505 
7506         FatAcquireExclusiveFcb(IrpContext, Fcb);
7507         ReleaseFcb = TRUE;
7508 
7509         FatVerifyFcb( IrpContext, Fcb );
7510 
7511         if (HandleInfo->HandleInfo & MARK_HANDLE_PROTECT_CLUSTERS) {
7512 
7513             if (Fcb->FcbState & FCB_STATE_DENY_DEFRAG) {
7514 
7515                 //
7516                 //  It's already set, bail out.
7517                 //
7518 
7519                 try_return( Status = STATUS_ACCESS_DENIED );
7520             }
7521 
7522             Ccb->Flags |= CCB_FLAG_DENY_DEFRAG;
7523             Fcb->FcbState|= FCB_STATE_DENY_DEFRAG;
7524 
7525         }
7526 
7527         try_exit: NOTHING;
7528 
7529     } _SEH2_FINALLY {
7530 
7531         if (ReleaseFcb) {
7532 
7533             FatReleaseFcb(IrpContext, Fcb);
7534         }
7535 
7536     } _SEH2_END;
7537 
7538     FatCompleteRequest( IrpContext, Irp, Status );
7539     return Status;
7540 }
7541 
7542 
7543 _Requires_lock_held_(_Global_critical_region_)
7544 VOID
7545 FatFlushAndCleanVolume(
7546     IN PIRP_CONTEXT IrpContext,
7547     IN PIRP Irp,
7548     IN PVCB Vcb,
7549     IN FAT_FLUSH_TYPE FlushType
7550     )
7551 /*++
7552 
7553 Routine Description:
7554 
7555     This routine flushes and otherwise preparse a volume to be eligible
7556     for deletion.  The dismount and PNP paths share the need for this
7557     common work.
7558 
7559     The Vcb will always be valid on return from this function. It is the
7560     caller's responsibility to attempt the dismount/deletion, and to setup
7561     allocation support again if the volume will be brought back from the
7562     brink.
7563 
7564 Arguments:
7565 
7566     Irp - Irp for the overlying request
7567 
7568     Vcb - the volume being operated on
7569 
7570     FlushType - specifies the kind of flushing desired
7571 
7572 Return Value:
7573 
7574     NTSTATUS - The return status for the operation.
7575 
7576 --*/
7577 {
7578     PAGED_CODE();
7579 
7580     //
7581     //  The volume must be held exclusive.
7582     //
7583 
7584     NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, Vcb ));
7585 
7586     //
7587     //  There is no fail, flush everything. If invalidating, it is important
7588     //  that we invalidate as we flush (eventually, w/ paging io held) so that we
7589     //  error out the maximum number of late writes.
7590     //
7591 
7592     if (FlushType != NoFlush) {
7593 
7594         (VOID) FatFlushVolume( IrpContext, Vcb, FlushType );
7595     }
7596 
7597     FatCloseEaFile( IrpContext, Vcb, FALSE );
7598 
7599     //
7600     //  Now, tell the device to flush its buffers.
7601     //
7602 
7603     if (FlushType != NoFlush) {
7604 
7605         (VOID)FatHijackIrpAndFlushDevice( IrpContext, Irp, Vcb->TargetDeviceObject );
7606     }
7607 
7608     //
7609     //  Now purge everything in sight.  We're trying to provoke as many closes as
7610     //  soon as possible, this volume may be on its way out.
7611     //
7612 
7613     if (FlushType != FlushWithoutPurge) {
7614 
7615         CcPurgeCacheSection( &Vcb->SectionObjectPointers,
7616                              NULL,
7617                              0,
7618                              FALSE );
7619 
7620         (VOID) FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, NoFlush );
7621     }
7622 
7623     //
7624     //  If the volume was dirty and we were allowed to flush, do the processing that
7625     //  the delayed callback would have done.
7626     //
7627 
7628     if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) {
7629 
7630         //
7631         //  Cancel any pending clean volumes.
7632         //
7633 
7634         (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer );
7635         (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc );
7636 
7637 
7638         if (FlushType != NoFlush) {
7639 
7640             //
7641             //  The volume is now clean, note it.
7642             //
7643 
7644             if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
7645 
7646                 FatMarkVolume( IrpContext, Vcb, VolumeClean );
7647                 ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
7648             }
7649 
7650             //
7651             //  Unlock the volume if it is removable.
7652             //
7653 
7654             if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
7655                 !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {
7656 
7657                 FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
7658             }
7659         }
7660     }
7661 
7662 }
7663 
7664 #if (NTDDI_VERSION >= NTDDI_WIN8)
7665 
7666 
7667 _Requires_lock_held_(_Global_critical_region_)
7668 NTSTATUS
7669 FatSetPurgeFailureMode (
7670     _In_ PIRP_CONTEXT IrpContext,
7671     _In_ PIRP Irp
7672     )
7673 /*++
7674 
7675     This routine is used to enable or disable the purge failure mode
7676     on a file. When in this mode the file system will propagate purge
7677     failures encountered during coherency purges. Normally these are
7678     ignored for application compatibilty purposes. Since the normal
7679     behavior can lead to cache incoherency there needs to be a way to
7680     force error propagation, particulary when a filter has mapped a
7681     section for the purposes of scanning the file in the background.
7682 
7683     The purge failure mode is a reference count because it is set
7684     per mapped section and there may be multiple sections backed by
7685     the file.
7686 
7687 Arguments:
7688 
7689     IrpContext - Supplies the Irp Context.
7690     Irp - Supplies the Irp being processed.
7691 
7692 Return Value:
7693 
7694     NTSTATUS - The return status for the operation.
7695 
7696 --*/
7697 {
7698     NTSTATUS Status = STATUS_SUCCESS;
7699     PIO_STACK_LOCATION IrpSp;
7700     TYPE_OF_OPEN TypeOfOpen;
7701     PVCB Vcb;
7702     PFCB Fcb;
7703     PCCB Ccb;
7704     PSET_PURGE_FAILURE_MODE_INPUT SetPurgeInput;
7705     BOOLEAN FcbAcquired = FALSE;
7706 
7707     PAGED_CODE();
7708 
7709     IrpSp = IoGetCurrentIrpStackLocation( Irp );
7710 
7711     //
7712     //  Force WAIT to true.
7713     //
7714 
7715     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
7716 
7717     //
7718     //  This has to be a kernel only call. Can't let a user request
7719     //  change the purge failure mode count
7720     //
7721 
7722     if (Irp->RequestorMode != KernelMode) {
7723 
7724         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7725         return STATUS_INVALID_PARAMETER;
7726     }
7727 
7728     //
7729     //  Extract and decode the file object and check for type of open.
7730     //
7731 
7732     TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb );
7733 
7734     if (TypeOfOpen == UserDirectoryOpen) {
7735 
7736         FatCompleteRequest( IrpContext, Irp, STATUS_FILE_IS_A_DIRECTORY );
7737         return STATUS_FILE_IS_A_DIRECTORY;
7738     }
7739 
7740     if (TypeOfOpen != UserFileOpen) {
7741 
7742         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7743         return STATUS_INVALID_PARAMETER;
7744     }
7745 
7746     //
7747     //  Get the input buffer pointer and check its length.
7748     //
7749 
7750     if (IrpSp->Parameters.FileSystemControl.InputBufferLength < sizeof( SET_PURGE_FAILURE_MODE_INPUT )) {
7751 
7752         FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL );
7753         return STATUS_BUFFER_TOO_SMALL;
7754     }
7755 
7756     SetPurgeInput = (PSET_PURGE_FAILURE_MODE_INPUT) Irp->AssociatedIrp.SystemBuffer;
7757 
7758     if (!FlagOn( SetPurgeInput->Flags, SET_PURGE_FAILURE_MODE_ENABLED | SET_PURGE_FAILURE_MODE_DISABLED )) {
7759 
7760         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
7761         return STATUS_INVALID_PARAMETER;
7762     }
7763 
7764     try {
7765 
7766         //
7767         //  Acquire the FCB exclusively to synchronize with coherency flush
7768         //  and purge.
7769         //
7770 
7771         FatAcquireExclusiveFcb( IrpContext, Fcb );
7772         FcbAcquired = TRUE;
7773 
7774         FatVerifyFcb( IrpContext, Fcb );
7775 
7776         if (FlagOn( SetPurgeInput->Flags, SET_PURGE_FAILURE_MODE_ENABLED )) {
7777 
7778             if (Fcb->PurgeFailureModeEnableCount == MAXULONG) {
7779 
7780                 try_return( Status = STATUS_INVALID_PARAMETER );
7781             }
7782 
7783             Fcb->PurgeFailureModeEnableCount += 1;
7784 
7785         } else {
7786 
7787             ASSERT( FlagOn( SetPurgeInput->Flags, SET_PURGE_FAILURE_MODE_DISABLED ));
7788 
7789             if (Fcb->PurgeFailureModeEnableCount == 0) {
7790 
7791                 try_return( Status = STATUS_INVALID_PARAMETER );
7792             }
7793 
7794             Fcb->PurgeFailureModeEnableCount -= 1;
7795         }
7796 
7797         try_exit: NOTHING;
7798 
7799     } finally {
7800 
7801         if (FcbAcquired) {
7802             FatReleaseFcb( IrpContext, Fcb );
7803         }
7804     }
7805 
7806     //
7807     //  Complete the irp if we terminated normally.
7808     //
7809 
7810     FatCompleteRequest( IrpContext, Irp, Status );
7811 
7812     return Status;
7813 }
7814 
7815 #endif
7816 
7817 
7818 NTSTATUS
7819 FatSearchBufferForLabel(
7820     IN  PIRP_CONTEXT IrpContext,
7821     IN  PVPB  Vpb,
7822     IN  PVOID Buffer,
7823     IN  ULONG Size,
7824     OUT PBOOLEAN LabelFound
7825     )
7826 /*++
7827 
7828 Routine Description:
7829 
7830     Search a buffer (taken from the root directory) for a volume label
7831     matching the label in the
7832 
7833 Arguments:
7834 
7835     IrpContext - Supplies our irp context
7836     Vpb        - Vpb supplying the volume label
7837     Buffer     - Supplies the buffer we'll search
7838     Size       - The size of the buffer in bytes.
7839     LabelFound - Returns whether a label was found.
7840 
7841 Return Value:
7842 
7843     There are four interesting cases:
7844 
7845     1) Some random error occurred - that error returned as status, LabelFound
7846                                     is indeterminate.
7847 
7848     2) No label was found         - STATUS_SUCCESS returned, LabelFound is FALSE.
7849 
7850     3) A matching label was found - STATUS_SUCCESS returned, LabelFound is TRUE.
7851 
7852     4) A non-matching label found - STATUS_WRONG_VOLUME returned, LabelFound
7853                                     is indeterminate.
7854 
7855 --*/
7856 
7857 {
7858     NTSTATUS Status;
7859     WCHAR UnicodeBuffer[11];
7860 
7861     PDIRENT Dirent;
7862     PDIRENT TerminationDirent;
7863     ULONG VolumeLabelLength;
7864     UCHAR OemBuffer[11];
7865     OEM_STRING OemString;
7866     UNICODE_STRING UnicodeString;
7867 
7868     PAGED_CODE();
7869 
7870     UNREFERENCED_PARAMETER( IrpContext );
7871 
7872     Dirent = Buffer;
7873 
7874     TerminationDirent = Dirent + Size / sizeof(DIRENT);
7875 
7876     while ( Dirent < TerminationDirent ) {
7877 
7878         if ( Dirent->FileName[0] == FAT_DIRENT_NEVER_USED ) {
7879 
7880             Dirent = TerminationDirent;
7881             break;
7882         }
7883 
7884         //
7885         //  If the entry is the non-deleted volume label break from the loop.
7886         //
7887         //  Note that all out parameters are already correctly set.
7888         //
7889 
7890         if (((Dirent->Attributes & ~FAT_DIRENT_ATTR_ARCHIVE) ==
7891              FAT_DIRENT_ATTR_VOLUME_ID) &&
7892             (Dirent->FileName[0] != FAT_DIRENT_DELETED)) {
7893 
7894             break;
7895         }
7896 
7897         Dirent += 1;
7898     }
7899 
7900     if (Dirent >= TerminationDirent) {
7901 
7902         //
7903         //  We've run out of buffer.
7904         //
7905 
7906         *LabelFound = FALSE;
7907         return STATUS_SUCCESS;
7908     }
7909 
7910 
7911     OemString.Buffer = (PCHAR)&OemBuffer[0];
7912     OemString.MaximumLength = 11;
7913 
7914     RtlCopyMemory( OemString.Buffer, Dirent->FileName, 11 );
7915 
7916     //
7917     //  Translate the first character from 0x5 to 0xe5.
7918     //
7919 
7920     if (OemString.Buffer[0] == FAT_DIRENT_REALLY_0E5) {
7921 
7922         OemString.Buffer[0] = 0xe5;
7923     }
7924 
7925     //
7926     //  Compute the length of the volume name
7927     //
7928 
7929     for ( OemString.Length = 11;
7930           OemString.Length > 0;
7931           OemString.Length -= 1) {
7932 
7933         if ( (OemString.Buffer[OemString.Length-1] != 0x00) &&
7934              (OemString.Buffer[OemString.Length-1] != 0x20) ) { break; }
7935     }
7936 
7937     UnicodeString.MaximumLength = sizeof( UnicodeBuffer );
7938     UnicodeString.Buffer = &UnicodeBuffer[0];
7939 
7940     Status = RtlOemStringToCountedUnicodeString( &UnicodeString,
7941                                                  &OemString,
7942                                                  FALSE );
7943 
7944     if ( !NT_SUCCESS( Status ) ) {
7945 
7946         return Status;
7947     }
7948 
7949     VolumeLabelLength = UnicodeString.Length;
7950 
7951     if ( (VolumeLabelLength != (ULONG)Vpb->VolumeLabelLength) ||
7952          (!RtlEqualMemory(&UnicodeBuffer[0],
7953                           &Vpb->VolumeLabel[0],
7954                           VolumeLabelLength)) ) {
7955 
7956         return STATUS_WRONG_VOLUME;
7957     }
7958 
7959     //
7960     //  We found a matching label.
7961     //
7962 
7963     *LabelFound = TRUE;
7964     return STATUS_SUCCESS;
7965 }
7966 
7967 
7968 VOID
7969 FatVerifyLookupFatEntry (
7970     IN PIRP_CONTEXT IrpContext,
7971     IN PVCB Vcb,
7972     IN ULONG FatIndex,
7973     IN OUT PULONG FatEntry
7974     )
7975 {
7976     ULONG PageEntryOffset;
7977     ULONG OffsetIntoVolumeFile;
7978     PVOID Buffer;
7979 
7980     PAGED_CODE();
7981 
7982     NT_ASSERT(Vcb->AllocationSupport.FatIndexBitSize == 32);
7983 
7984     FatVerifyIndexIsValid( IrpContext, Vcb, FatIndex);
7985 
7986     Buffer = FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned,
7987                                        PAGE_SIZE,
7988                                        TAG_ENTRY_LOOKUP_BUFFER );
7989 
7990     OffsetIntoVolumeFile =  FatReservedBytes(&Vcb->Bpb) + FatIndex * sizeof(ULONG);
7991     PageEntryOffset = (OffsetIntoVolumeFile % PAGE_SIZE) / sizeof(ULONG);
7992 
7993     _SEH2_TRY {
7994 
7995         FatPerformVerifyDiskRead( IrpContext,
7996                                   Vcb,
7997                                   Buffer,
7998                                   OffsetIntoVolumeFile & ~(PAGE_SIZE - 1),
7999                                   PAGE_SIZE,
8000                                   TRUE );
8001 
8002         *FatEntry = ((PULONG)(Buffer))[PageEntryOffset];
8003 
8004     } _SEH2_FINALLY {
8005 
8006         ExFreePool( Buffer );
8007     } _SEH2_END;
8008 }
8009 
8010 //
8011 //  Local support routine
8012 //
8013 
8014 _Requires_lock_held_(_Global_critical_region_)
8015 VOID
8016 FatScanForDismountedVcb (
8017     IN PIRP_CONTEXT IrpContext
8018     )
8019 
8020 /*++
8021 
8022 Routine Description:
8023 
8024     This routine walks through the list of Vcb's looking for any which may
8025     now be deleted.  They may have been left on the list because there were
8026     outstanding references.
8027 
8028 Arguments:
8029 
8030 Return Value:
8031 
8032     None
8033 
8034 --*/
8035 
8036 {
8037     PVCB Vcb;
8038     PLIST_ENTRY Links;
8039     BOOLEAN VcbDeleted;
8040 
8041 
8042     PAGED_CODE();
8043 
8044     //
8045     //  Walk through all of the Vcb's attached to the global data.
8046     //
8047 
8048     NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
8049 
8050 #ifdef _MSC_VER
8051 #pragma prefast( push )
8052 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
8053 #pragma prefast( disable: 28193, "this will always wait" )
8054 #endif
8055 
8056     FatAcquireExclusiveGlobal( IrpContext );
8057 
8058 #ifdef _MSC_VER
8059 #pragma prefast( pop )
8060 #endif
8061 
8062     Links = FatData.VcbQueue.Flink;
8063 
8064     while (Links != &FatData.VcbQueue) {
8065 
8066         Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks );
8067 
8068         //
8069         //  Move to the next link now since the current Vcb may be deleted.
8070         //
8071 
8072         Links = Links->Flink;
8073 
8074         //
8075         //  Try to acquire the VCB for exclusive access.  If we cannot, just skip
8076         //  it for now.
8077         //
8078 
8079 #ifdef _MSC_VER
8080 #pragma prefast( push )
8081 #pragma prefast( disable:28103,"prefast cannot work out that Vcb->Resource will be released below." )
8082 #pragma prefast( disable:28109,"prefast cannot work out the Vcb is not already held" );
8083 #endif
8084 
8085         if (!ExAcquireResourceExclusiveLite( &(Vcb->Resource), FALSE )) {
8086 
8087             continue;
8088         }
8089 
8090 #ifdef _MSC_VER
8091 #pragma prefast( pop )
8092 #endif
8093         //
8094         //  Check if this Vcb can go away.
8095         //
8096 
8097         VcbDeleted = FatCheckForDismount( IrpContext,
8098                                           Vcb,
8099                                           FALSE );
8100 
8101         //
8102         //  If the VCB was not deleted, release it.
8103         //
8104 
8105         if (!VcbDeleted) {
8106 
8107             ExReleaseResourceLite( &(Vcb->Resource) );
8108         }
8109     }
8110 
8111     FatReleaseGlobal( IrpContext);
8112 
8113     return;
8114 }
8115 
8116 #if (NTDDI_VERSION >= NTDDI_WIN7)
8117 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
8118 //
8119 //  FatSetZeroOnDeallocate is used when we need to stomp over the contents with zeros when a file is deleted.
8120 //
8121 
8122 NTSTATUS
8123 FatSetZeroOnDeallocate (
8124     __in PIRP_CONTEXT IrpContext,
8125     __in PIRP Irp
8126     )
8127 {
8128     NTSTATUS Status = STATUS_SUCCESS;
8129 
8130     PVCB Vcb;
8131     PFCB FcbOrDcb;
8132     PCCB Ccb;
8133 
8134     TYPE_OF_OPEN TypeOfOpen;
8135 
8136     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
8137 
8138     BOOLEAN ReleaseFcb = FALSE;
8139 
8140     PAGED_CODE();
8141 
8142     //
8143     //  This call should always be synchronous.
8144     //
8145 
8146     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
8147 
8148     TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb );
8149 
8150     if ((TypeOfOpen != UserFileOpen) ||
8151         (!IrpSp->FileObject->WriteAccess) ) {
8152 
8153         FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
8154         return STATUS_ACCESS_DENIED;
8155     }
8156 
8157     //
8158     //  Readonly mount should be just that: read only.
8159     //
8160 
8161     if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
8162 
8163         FatCompleteRequest( IrpContext, Irp, STATUS_MEDIA_WRITE_PROTECTED );
8164         return STATUS_MEDIA_WRITE_PROTECTED;
8165     }
8166 
8167     //
8168     // Acquire main then paging to exclude everyone from this FCB.
8169     //
8170 
8171     FatAcquireExclusiveFcb(IrpContext, FcbOrDcb);
8172     ReleaseFcb = TRUE;
8173 
8174     _SEH2_TRY {
8175 
8176         SetFlag( FcbOrDcb->FcbState, FCB_STATE_ZERO_ON_DEALLOCATION );
8177 
8178     } _SEH2_FINALLY {
8179 
8180         if (ReleaseFcb) {
8181             FatReleaseFcb(IrpContext, FcbOrDcb);
8182         }
8183 
8184     } _SEH2_END;
8185 
8186     FatCompleteRequest( IrpContext, Irp, Status );
8187     return Status;
8188 }
8189 #endif
8190 
8191