1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     DevIoSup.c
8 
9 Abstract:
10 
11     This module implements the low lever disk read/write support for Fat.
12 
13 
14 --*/
15 
16 #include "fatprocs.h"
17 
18 //
19 //  The Bug check file id for this module
20 //
21 
22 #define BugCheckFileId                   (FAT_BUG_CHECK_DEVIOSUP)
23 
24 //
25 //  Local debug trace level
26 //
27 
28 #define Dbg                              (DEBUG_TRACE_DEVIOSUP)
29 
30 #define CollectDiskIoStats(VCB,FUNCTION,IS_USER_IO,COUNT) {                                    \
31     PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors].Common;   \
32     if (IS_USER_IO) {                                                                          \
33         if ((FUNCTION) == IRP_MJ_WRITE) {                                                      \
34             Stats->UserDiskWrites += (COUNT);                                                  \
35         } else {                                                                               \
36             Stats->UserDiskReads += (COUNT);                                                   \
37         }                                                                                      \
38     } else {                                                                                   \
39         if ((FUNCTION) == IRP_MJ_WRITE) {                                                      \
40             Stats->MetaDataDiskWrites += (COUNT);                                              \
41         } else {                                                                               \
42             Stats->MetaDataDiskReads += (COUNT);                                               \
43         }                                                                                      \
44     }                                                                                          \
45 }
46 
47 typedef struct _FAT_SYNC_CONTEXT {
48 
49     //
50     //  Io status block for the request
51     //
52 
53     IO_STATUS_BLOCK Iosb;
54 
55     //
56     //  Event to be signaled when the request completes
57     //
58 
59     KEVENT Event;
60 
61 } FAT_SYNC_CONTEXT, *PFAT_SYNC_CONTEXT;
62 
63 
64 //
65 // Completion Routine declarations
66 //
67 
68 IO_COMPLETION_ROUTINE FatMultiSyncCompletionRoutine;
69 
70 NTSTATUS
71 NTAPI
72 FatMultiSyncCompletionRoutine (
73     _In_ PDEVICE_OBJECT DeviceObject,
74     _In_ PIRP Irp,
75     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
76     );
77 
78 IO_COMPLETION_ROUTINE FatMultiAsyncCompletionRoutine;
79 
80 NTSTATUS
81 NTAPI
82 FatMultiAsyncCompletionRoutine (
83     _In_ PDEVICE_OBJECT DeviceObject,
84     _In_ PIRP Irp,
85     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
86     );
87 
88 IO_COMPLETION_ROUTINE FatSpecialSyncCompletionRoutine;
89 
90 NTSTATUS
91 NTAPI
92 FatSpecialSyncCompletionRoutine (
93     _In_ PDEVICE_OBJECT DeviceObject,
94     _In_ PIRP Irp,
95     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
96     );
97 
98 IO_COMPLETION_ROUTINE FatSingleSyncCompletionRoutine;
99 
100 NTSTATUS
101 NTAPI
102 FatSingleSyncCompletionRoutine (
103     _In_ PDEVICE_OBJECT DeviceObject,
104     _In_ PIRP Irp,
105     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
106     );
107 
108 IO_COMPLETION_ROUTINE FatSingleAsyncCompletionRoutine;
109 
110 NTSTATUS
111 NTAPI
112 FatSingleAsyncCompletionRoutine (
113     _In_ PDEVICE_OBJECT DeviceObject,
114     _In_ PIRP Irp,
115     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
116     );
117 
118 IO_COMPLETION_ROUTINE FatPagingFileCompletionRoutine;
119 
120 NTSTATUS
121 NTAPI
122 FatPagingFileCompletionRoutine (
123     _In_ PDEVICE_OBJECT DeviceObject,
124     _In_ PIRP Irp,
125     _In_reads_opt_(_Inexpressible_("varies")) PVOID MasterIrp
126     );
127 
128 IO_COMPLETION_ROUTINE FatPagingFileCompletionRoutineCatch;
129 
130 NTSTATUS
131 NTAPI
132 FatPagingFileCompletionRoutineCatch (
133     _In_ PDEVICE_OBJECT DeviceObject,
134     _In_ PIRP Irp,
135     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
136     );
137 
138 VOID
139 FatSingleNonAlignedSync (
140     IN PIRP_CONTEXT IrpContext,
141     IN PVCB Vcb,
142     IN PUCHAR Buffer,
143     IN LBO Lbo,
144     IN ULONG ByteCount,
145     IN PIRP Irp
146     );
147 
148 //
149 //  The following macro decides whether to send a request directly to
150 //  the device driver, or to other routines.  It was meant to
151 //  replace IoCallDriver as transparently as possible.  It must only be
152 //  called with a read or write Irp.
153 //
154 //  NTSTATUS
155 //  FatLowLevelReadWrite (
156 //      PIRP_CONTEXT IrpContext,
157 //      PDEVICE_OBJECT DeviceObject,
158 //      PIRP Irp,
159 //      PVCB Vcb
160 //      );
161 //
162 
163 #define FatLowLevelReadWrite(IRPCONTEXT,DO,IRP,VCB) ( \
164     IoCallDriver((DO),(IRP))                          \
165 )
166 
167 //
168 //  The following macro handles completion-time zeroing of buffers.
169 //
170 
171 #define FatDoCompletionZero( I, C )                                     \
172     if ((C)->ZeroMdl) {                                                 \
173         NT_ASSERT( (C)->ZeroMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |  \
174                                           MDL_SOURCE_IS_NONPAGED_POOL));\
175         if (NT_SUCCESS((I)->IoStatus.Status)) {                         \
176             RtlZeroMemory( (C)->ZeroMdl->MappedSystemVa,                \
177                            (C)->ZeroMdl->ByteCount );                   \
178         }                                                               \
179         IoFreeMdl((C)->ZeroMdl);                                        \
180         (C)->ZeroMdl = NULL;                                            \
181     }
182 
183 #if (NTDDI_VERSION >= NTDDI_WIN8)
184 #define FatUpdateIOCountersPCW(IsAWrite,Count)                          \
185     FsRtlUpdateDiskCounters( ((IsAWrite) ? 0       : (Count) ),         \
186                              ((IsAWrite) ? (Count) : 0) )
187 #else
188 #define FatUpdateIOCountersPCW(IsAWrite,Count)
189 #endif
190 
191 #ifdef ALLOC_PRAGMA
192 #pragma alloc_text(PAGE, FatMultipleAsync)
193 #pragma alloc_text(PAGE, FatSingleAsync)
194 #pragma alloc_text(PAGE, FatSingleNonAlignedSync)
195 #pragma alloc_text(PAGE, FatWaitSync)
196 #pragma alloc_text(PAGE, FatLockUserBuffer)
197 #pragma alloc_text(PAGE, FatBufferUserBuffer)
198 #pragma alloc_text(PAGE, FatMapUserBuffer)
199 #pragma alloc_text(PAGE, FatNonCachedIo)
200 #pragma alloc_text(PAGE, FatNonCachedNonAlignedRead)
201 #pragma alloc_text(PAGE, FatPerformDevIoCtrl)
202 #endif
203 
204 typedef struct FAT_PAGING_FILE_CONTEXT {
205     KEVENT Event;
206     PMDL RestoreMdl;
207 } FAT_PAGING_FILE_CONTEXT, *PFAT_PAGING_FILE_CONTEXT;
208 
209 
210 VOID
211 FatPagingFileIo (
212     IN PIRP Irp,
213     IN PFCB Fcb
214     )
215 
216 /*++
217 
218 Routine Description:
219 
220     This routine performs the non-cached disk io described in its parameters.
221     This routine nevers blocks, and should only be used with the paging
222     file since no completion processing is performed.
223 
224 Arguments:
225 
226     Irp - Supplies the requesting Irp.
227 
228     Fcb - Supplies the file to act on.
229 
230 Return Value:
231 
232     None.
233 
234 --*/
235 
236 {
237     //
238     // Declare some local variables for enumeration through the
239     // runs of the file.
240     //
241 
242     VBO Vbo;
243     ULONG ByteCount;
244 
245     PMDL Mdl;
246     LBO NextLbo;
247     VBO NextVbo = 0;
248     ULONG NextByteCount;
249     ULONG RemainingByteCount;
250     BOOLEAN MustSucceed;
251 
252     ULONG FirstIndex;
253     ULONG CurrentIndex;
254     ULONG LastIndex;
255 
256     LBO LastLbo;
257     ULONG LastByteCount;
258 
259     BOOLEAN MdlIsReserve = FALSE;
260     BOOLEAN IrpIsMaster = FALSE;
261     FAT_PAGING_FILE_CONTEXT Context;
262     LONG IrpCount;
263 
264     PIRP AssocIrp;
265     PIO_STACK_LOCATION IrpSp;
266     PIO_STACK_LOCATION NextIrpSp;
267     ULONG BufferOffset;
268     PDEVICE_OBJECT DeviceObject;
269 
270 #ifndef __REACTOS__
271     BOOLEAN IsAWrite = FALSE;
272 #endif
273 
274     DebugTrace(+1, Dbg, "FatPagingFileIo\n", 0);
275     DebugTrace( 0, Dbg, "Irp = %p\n", Irp );
276     DebugTrace( 0, Dbg, "Fcb = %p\n", Fcb );
277 
278     NT_ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ));
279 
280     //
281     //  Initialize some locals.
282     //
283 
284     BufferOffset = 0;
285     DeviceObject = Fcb->Vcb->TargetDeviceObject;
286     IrpSp = IoGetCurrentIrpStackLocation( Irp );
287 
288     Vbo = IrpSp->Parameters.Read.ByteOffset.LowPart;
289     ByteCount = IrpSp->Parameters.Read.Length;
290 #ifndef __REACTOS__
291     IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE);
292 #endif
293 
294     MustSucceed = FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb,
295                                      Vbo,
296                                      &NextLbo,
297                                      &NextByteCount,
298                                      &FirstIndex);
299 
300     //
301     //  If this run isn't present, something is very wrong.
302     //
303 
304     if (!MustSucceed) {
305 
306 #ifdef _MSC_VER
307 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
308 #endif
309         FatBugCheck( Vbo, ByteCount, 0 );
310     }
311 
312 #if (NTDDI_VERSION >= NTDDI_WIN8)
313 
314     //
315     //  Charge the IO to paging file to current thread
316     //
317 
318     if (FatDiskAccountingEnabled) {
319 
320         PETHREAD ThreadIssuingIo = PsGetCurrentThread();
321         BOOLEAN IsWriteOperation = FALSE;
322 
323         if (IrpSp->MajorFunction == IRP_MJ_WRITE) {
324             IsWriteOperation = TRUE;
325         }
326 
327         PsUpdateDiskCounters( PsGetThreadProcess( ThreadIssuingIo ),
328                               (IsWriteOperation ? 0 : ByteCount ),       // bytes to read
329                               (IsWriteOperation ? ByteCount : 0),       // bytes to write
330                               (IsWriteOperation ? 0 : 1),               // # of reads
331                               (IsWriteOperation ? 1 : 0),               // # of writes
332                               0 );
333     }
334 #endif
335 
336     // See if the write covers a single valid run, and if so pass
337     // it on.
338     //
339 
340     if ( NextByteCount >= ByteCount ) {
341 
342         DebugTrace( 0, Dbg, "Passing Irp on to Disk Driver\n", 0 );
343 
344         //
345         //  Setup the next IRP stack location for the disk driver beneath us.
346         //
347 
348         NextIrpSp = IoGetNextIrpStackLocation( Irp );
349 
350         NextIrpSp->MajorFunction = IrpSp->MajorFunction;
351         NextIrpSp->Parameters.Read.Length = ByteCount;
352         NextIrpSp->Parameters.Read.ByteOffset.QuadPart = NextLbo;
353 
354         //
355         //  Since this is Paging file IO, we'll just ignore the verify bit.
356         //
357 
358         SetFlag( NextIrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
359 
360         //
361         //  Set up the completion routine address in our stack frame.
362         //  This is only invoked on error or cancel, and just copies
363         //  the error Status into master irp's iosb.
364         //
365         //  If the error implies a media problem, it also enqueues a
366         //  worker item to write out the dirty bit so that the next
367         //  time we run we will do a autochk /r
368         //
369 
370         IoSetCompletionRoutine( Irp,
371                                 &FatPagingFileCompletionRoutine,
372                                 Irp,
373                                 FALSE,
374                                 TRUE,
375                                 TRUE );
376 
377         //
378         //  Issue the read/write request
379         //
380         //  If IoCallDriver returns an error, it has completed the Irp
381         //  and the error will be dealt with as a normal IO error.
382         //
383 
384         (VOID)IoCallDriver( DeviceObject, Irp );
385 
386         //
387         //  We just issued an IO to the storage stack, update the counters indicating so.
388         //
389 
390         if (FatDiskAccountingEnabled) {
391 
392             FatUpdateIOCountersPCW( IsAWrite, ByteCount );
393         }
394 
395         DebugTrace(-1, Dbg, "FatPagingFileIo -> VOID\n", 0);
396         return;
397     }
398 
399     //
400     //  Find out how may runs there are.
401     //
402 
403     MustSucceed = FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb,
404                                      Vbo + ByteCount - 1,
405                                      &LastLbo,
406                                      &LastByteCount,
407                                      &LastIndex);
408 
409     //
410     //  If this run isn't present, something is very wrong.
411     //
412 
413     if (!MustSucceed) {
414 
415 #ifdef _MSC_VER
416 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
417 #endif
418         FatBugCheck( Vbo + ByteCount - 1, 1, 0 );
419     }
420 
421     CurrentIndex = FirstIndex;
422 
423     //
424     //  Now set up the Irp->IoStatus.  It will be modified by the
425     //  multi-completion routine in case of error or verify required.
426     //
427 
428     Irp->IoStatus.Status = STATUS_SUCCESS;
429     Irp->IoStatus.Information = ByteCount;
430 
431     //
432     //  Loop while there are still byte writes to satisfy.  The way we'll work this
433     //  is to hope for the best - one associated IRP per run, which will let us be
434     //  completely async after launching all the IO.
435     //
436     //  IrpCount will indicate the remaining number of associated Irps to launch.
437     //
438     //  All we have to do is make sure IrpCount doesn't hit zero before we're building
439     //  the very last Irp.  If it is positive when we're done, it means we have to
440     //  wait for the rest of the associated Irps to come back before we complete the
441     //  master by hand.
442     //
443     //  This will keep the master from completing early.
444     //
445 
446     Irp->AssociatedIrp.IrpCount = IrpCount = LastIndex - FirstIndex + 1;
447 
448     while (CurrentIndex <= LastIndex) {
449 
450         //
451         //  Reset this for unwinding purposes
452         //
453 
454         AssocIrp = NULL;
455 
456         //
457         //  If next run is larger than we need, "ya get what ya need".
458         //
459 
460         if (NextByteCount > ByteCount) {
461             NextByteCount = ByteCount;
462         }
463 
464         RemainingByteCount = 0;
465 
466         //
467         // Allocate and build a partial Mdl for the request.
468         //
469 
470         Mdl = IoAllocateMdl( (PCHAR)Irp->UserBuffer + BufferOffset,
471                              NextByteCount,
472                              FALSE,
473                              FALSE,
474                              AssocIrp );
475 
476         if (Mdl == NULL) {
477 
478             //
479             //  Pick up the reserve MDL
480             //
481 
482             KeWaitForSingleObject( &FatReserveEvent, Executive, KernelMode, FALSE, NULL );
483 
484             Mdl = FatReserveMdl;
485             MdlIsReserve = TRUE;
486 
487             //
488             //  Trim to fit the size of the reserve MDL.
489             //
490 
491             if (NextByteCount > FAT_RESERVE_MDL_SIZE * PAGE_SIZE) {
492 
493                 RemainingByteCount = NextByteCount - FAT_RESERVE_MDL_SIZE * PAGE_SIZE;
494                 NextByteCount = FAT_RESERVE_MDL_SIZE * PAGE_SIZE;
495             }
496         }
497 
498         IoBuildPartialMdl( Irp->MdlAddress,
499                            Mdl,
500                            (PCHAR)Irp->UserBuffer + BufferOffset,
501                            NextByteCount );
502 
503         //
504         //  Now that we have properly bounded this piece of the transfer, it is
505         //  time to read/write it.  We can simplify life slightly by always
506         //  re-using the master IRP for cases where we use the reserve MDL,
507         //  since we'll always be synchronous for those and can use a single
508         //  completion context on our local stack.
509         //
510         //  We also must prevent ourselves from issuing an associated IRP that would
511         //  complete the master UNLESS this is the very last IRP we'll issue.
512         //
513         //  This logic looks a bit complicated, but is hopefully understandable.
514         //
515 
516         if (!MdlIsReserve &&
517             (IrpCount != 1 ||
518              (CurrentIndex == LastIndex &&
519               RemainingByteCount == 0))) {
520 
521             AssocIrp = IoMakeAssociatedIrp( Irp, (CCHAR)(DeviceObject->StackSize + 1) );
522         }
523 
524         if (AssocIrp == NULL) {
525 
526             AssocIrp = Irp;
527             IrpIsMaster = TRUE;
528 
529             //
530             //  We need to drain the associated Irps so we can reliably figure out if
531             //  the master Irp is showing a failed status, in which case we bail out
532             //  immediately - as opposed to putting the value in the status field in
533             //  jeopardy due to our re-use of the master Irp.
534             //
535 
536             while (Irp->AssociatedIrp.IrpCount != IrpCount) {
537 
538                 KeDelayExecutionThread (KernelMode, FALSE, &Fat30Milliseconds);
539             }
540 
541             //
542             //  Note that since we failed to launch this associated Irp, that the completion
543             //  code at the bottom will take care of completing the master Irp.
544             //
545 
546             if (!NT_SUCCESS(Irp->IoStatus.Status)) {
547 
548                 NT_ASSERT( IrpCount );
549                 break;
550             }
551 
552         } else {
553 
554             //
555             //  Indicate we used an associated Irp.
556             //
557 
558             IrpCount -= 1;
559         }
560 
561         //
562         //  With an associated IRP, we must take over the first stack location so
563         //  we can have one to put the completion routine on.  When re-using the
564         //  master IRP, its already there.
565         //
566 
567         if (!IrpIsMaster) {
568 
569             //
570             //  Get the first IRP stack location in the associated Irp
571             //
572 
573             IoSetNextIrpStackLocation( AssocIrp );
574             NextIrpSp = IoGetCurrentIrpStackLocation( AssocIrp );
575 
576             //
577             //  Setup the Stack location to describe our read.
578             //
579 
580             NextIrpSp->MajorFunction = IrpSp->MajorFunction;
581             NextIrpSp->Parameters.Read.Length = NextByteCount;
582             NextIrpSp->Parameters.Read.ByteOffset.QuadPart = Vbo;
583 
584             //
585             //  We also need the VolumeDeviceObject in the Irp stack in case
586             //  we take the failure path.
587             //
588 
589             NextIrpSp->DeviceObject = IrpSp->DeviceObject;
590 
591         } else {
592 
593             //
594             //  Save the MDL in the IRP and prepare the stack
595             //  context for the completion routine.
596             //
597 
598             KeInitializeEvent( &Context.Event, SynchronizationEvent, FALSE );
599             Context.RestoreMdl = Irp->MdlAddress;
600         }
601 
602         //
603         //  And drop our Mdl into the Irp.
604         //
605 
606         AssocIrp->MdlAddress = Mdl;
607 
608         //
609         //  Set up the completion routine address in our stack frame.
610         //  For true associated IRPs, this is only invoked on error or
611         //  cancel, and just copies the error Status into master irp's
612         //  iosb.
613         //
614         //  If the error implies a media problem, it also enqueues a
615         //  worker item to write out the dirty bit so that the next
616         //  time we run we will do a autochk /r
617         //
618 
619         if (IrpIsMaster) {
620 
621             IoSetCompletionRoutine( AssocIrp,
622                                     FatPagingFileCompletionRoutineCatch,
623                                     &Context,
624                                     TRUE,
625                                     TRUE,
626                                     TRUE );
627 
628         } else {
629 
630             IoSetCompletionRoutine( AssocIrp,
631                                     FatPagingFileCompletionRoutine,
632                                     Irp,
633                                     FALSE,
634                                     TRUE,
635                                     TRUE );
636         }
637 
638         //
639         //  Setup the next IRP stack location for the disk driver beneath us.
640         //
641 
642         NextIrpSp = IoGetNextIrpStackLocation( AssocIrp );
643 
644         //
645         //  Since this is paging file IO, we'll just ignore the verify bit.
646         //
647 
648         SetFlag( NextIrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
649 
650         //
651         //  Setup the Stack location to do a read from the disk driver.
652         //
653 
654         NextIrpSp->MajorFunction = IrpSp->MajorFunction;
655         NextIrpSp->Parameters.Read.Length = NextByteCount;
656         NextIrpSp->Parameters.Read.ByteOffset.QuadPart = NextLbo;
657 
658         (VOID)IoCallDriver( DeviceObject, AssocIrp );
659 
660         //
661         //  We just issued an IO to the storage stack, update the counters indicating so.
662         //
663 
664         if (FatDiskAccountingEnabled) {
665 
666             FatUpdateIOCountersPCW( IsAWrite, (ULONG64)NextByteCount );
667         }
668 
669         //
670         //  Wait for the Irp in the catch case and drop the flags.
671         //
672 
673         if (IrpIsMaster) {
674 
675             KeWaitForSingleObject( &Context.Event, Executive, KernelMode, FALSE, NULL );
676             IrpIsMaster = MdlIsReserve = FALSE;
677 
678             //
679             //  If the Irp is showing a failed status, there is no point in continuing.
680             //  In doing so, we get to avoid squirreling away the failed status in case
681             //  we were to re-use the master irp again.
682             //
683             //  Note that since we re-used the master, we must not have issued the "last"
684             //  associated Irp, and thus the completion code at the bottom will take care
685             //  of that for us.
686             //
687 
688             if (!NT_SUCCESS(Irp->IoStatus.Status)) {
689 
690                 NT_ASSERT( IrpCount );
691                 break;
692             }
693         }
694 
695         //
696         //  Now adjust everything for the next pass through the loop.
697         //
698 
699         Vbo += NextByteCount;
700         BufferOffset += NextByteCount;
701         ByteCount -= NextByteCount;
702 
703         //
704         //  Try to lookup the next run, if we are not done and we got
705         //  all the way through the current run.
706         //
707 
708         if (RemainingByteCount) {
709 
710             //
711             //  Advance the Lbo/Vbo if we have more to do in the current run.
712             //
713 
714             NextLbo += NextByteCount;
715             NextVbo += NextByteCount;
716 
717             NextByteCount = RemainingByteCount;
718 
719         } else {
720 
721             CurrentIndex += 1;
722 
723             if ( CurrentIndex <= LastIndex ) {
724 
725                 NT_ASSERT( ByteCount != 0 );
726 
727                 FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb,
728                                     CurrentIndex,
729                                     &NextVbo,
730                                     &NextLbo,
731                                     &NextByteCount );
732 
733                 NT_ASSERT( NextVbo == Vbo );
734             }
735         }
736     } // while ( CurrentIndex <= LastIndex )
737 
738     //
739     //  If we didn't get enough associated Irps going to make this asynchronous, we
740     //  twiddle our thumbs and wait for those we did launch to complete.
741     //
742 
743     if (IrpCount) {
744 
745         while (Irp->AssociatedIrp.IrpCount != IrpCount) {
746 
747             KeDelayExecutionThread (KernelMode, FALSE, &Fat30Milliseconds);
748         }
749 
750         IoCompleteRequest( Irp, IO_DISK_INCREMENT );
751     }
752 
753     DebugTrace(-1, Dbg, "FatPagingFileIo -> VOID\n", 0);
754     return;
755 }
756 
757 #if (NTDDI_VERSION >= NTDDI_WIN8)
758 
759 VOID
760 FatUpdateDiskStats (
761     IN PIRP_CONTEXT IrpContext,
762     IN PIRP Irp,
763     IN ULONG ByteCount
764     )
765 /*++
766 
767 Routine Description:
768 
769     Charge appropriate process for the IO this IRP will cause.
770 
771 Arguments:
772 
773     IrpContext- The Irp Context
774 
775     Irp - Supplies the requesting Irp.
776 
777     ByteCount - The lengh of the operation.
778 
779 Return Value:
780 
781     None.
782 
783 --*/
784 
785 {
786     PETHREAD OriginatingThread = NULL;
787     ULONG NumReads = 0;
788     ULONG NumWrites = 0;
789     ULONGLONG BytesToRead = 0;
790     ULONGLONG BytesToWrite = 0;
791 
792     //
793     //  Here we attempt to charge the IO back to the originating process.
794     //   - These checks are intended to cover following cases:
795     //      o Buffered sync reads
796     //      o Unbuffered sync read
797     //      o Inline metadata reads
798     //      o memory mapped reads (in-line faulting of data)
799     //
800 
801     if (IrpContext->MajorFunction == IRP_MJ_READ) {
802 
803         NumReads++;
804         BytesToRead = ByteCount;
805 
806         if ((Irp->Tail.Overlay.Thread != NULL) &&
807             !IoIsSystemThread( Irp->Tail.Overlay.Thread )) {
808 
809             OriginatingThread = Irp->Tail.Overlay.Thread;
810 
811         } else if (!IoIsSystemThread( PsGetCurrentThread() )) {
812 
813             OriginatingThread = PsGetCurrentThread();
814 
815         //
816         //  We couldn't find a non-system entity, so this should be charged to system.
817         //  Do so only if we are top level.
818         //  If we are not top-level then the read was initiated by someone like Cc (read ahead)
819         //  who should have already accounted for this IO.
820         //
821 
822         } else if (IoIsSystemThread( PsGetCurrentThread() ) &&
823                    (IoGetTopLevelIrp() == Irp)) {
824 
825             OriginatingThread = PsGetCurrentThread();
826         }
827 
828     //
829     //  Charge the write to Originating process.
830     //  Intended to cover the following writes:
831     //      - Unbuffered sync write
832     //      - unbuffered async write
833     //
834     //  If we re not top-level, then it should already have been accounted for
835     //  somewhere else (Cc).
836     //
837 
838     } else if (IrpContext->MajorFunction == IRP_MJ_WRITE) {
839 
840         NumWrites++;
841         BytesToWrite = ByteCount;
842 
843         if (IoGetTopLevelIrp() == Irp) {
844 
845             if ((Irp->Tail.Overlay.Thread != NULL) &&
846                 !IoIsSystemThread( Irp->Tail.Overlay.Thread )) {
847 
848                 OriginatingThread = Irp->Tail.Overlay.Thread;
849 
850             } else {
851 
852                 OriginatingThread = PsGetCurrentThread();
853             }
854 
855         //
856         //  For mapped page writes
857         //
858 
859         } else if (IoGetTopLevelIrp() == (PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP) {
860 
861             OriginatingThread = PsGetCurrentThread();
862         }
863     }
864 
865     if (OriginatingThread != NULL) {
866 
867         PsUpdateDiskCounters( PsGetThreadProcess( OriginatingThread ),
868                               BytesToRead,
869                               BytesToWrite,
870                               NumReads,
871                               NumWrites,
872                               0 );
873     }
874 }
875 
876 #endif
877 
878 
879 
880 _Requires_lock_held_(_Global_critical_region_)
881 NTSTATUS
882 FatNonCachedIo (
883     IN PIRP_CONTEXT IrpContext,
884     IN PIRP Irp,
885     IN PFCB FcbOrDcb,
886     IN ULONG StartingVbo,
887     IN ULONG ByteCount,
888     IN ULONG UserByteCount,
889     IN ULONG StreamFlags
890     )
891 /*++
892 
893 Routine Description:
894 
895     This routine performs the non-cached disk io described in its parameters.
896     The choice of a single run is made if possible, otherwise multiple runs
897     are executed.
898 
899 Arguments:
900 
901     IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
902 
903     Irp - Supplies the requesting Irp.
904 
905     FcbOrDcb - Supplies the file to act on.
906 
907     StartingVbo - The starting point for the operation.
908 
909     ByteCount - The lengh of the operation.
910 
911     UserByteCount - The last byte the user can see, rest to be zeroed.
912 
913     StreamFlags - flag to indicate special attributes for a NonCachedIo.
914 
915 Return Value:
916 
917     None.
918 
919 --*/
920 
921 {
922 
923     //
924     // Declare some local variables for enumeration through the
925     // runs of the file, and an array to store parameters for
926     // parallel I/Os
927     //
928 
929     BOOLEAN Wait;
930 
931     LBO NextLbo;
932     VBO NextVbo;
933     ULONG NextByteCount;
934     BOOLEAN NextIsAllocated;
935 
936     LBO LastLbo;
937     ULONG LastByteCount;
938     BOOLEAN LastIsAllocated;
939 
940     BOOLEAN EndOnMax;
941 
942     ULONG FirstIndex;
943     ULONG CurrentIndex;
944     ULONG LastIndex;
945 
946     ULONG NextRun;
947     ULONG BufferOffset;
948     ULONG OriginalByteCount;
949 
950 
951 
952     IO_RUN StackIoRuns[FAT_MAX_IO_RUNS_ON_STACK];
953     PIO_RUN IoRuns;
954 
955 
956     PAGED_CODE();
957 
958     UNREFERENCED_PARAMETER( StreamFlags );
959 
960     DebugTrace(+1, Dbg, "FatNonCachedIo\n", 0);
961     DebugTrace( 0, Dbg, "Irp           = %p\n", Irp );
962     DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction );
963     DebugTrace( 0, Dbg, "FcbOrDcb      = %p\n", FcbOrDcb );
964     DebugTrace( 0, Dbg, "StartingVbo   = %08lx\n", StartingVbo );
965     DebugTrace( 0, Dbg, "ByteCount     = %08lx\n", ByteCount );
966 
967     if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) {
968 
969         PFILE_SYSTEM_STATISTICS Stats =
970             &FcbOrDcb->Vcb->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors];
971 
972         if (IrpContext->MajorFunction == IRP_MJ_READ) {
973             Stats->Fat.NonCachedReads += 1;
974             Stats->Fat.NonCachedReadBytes += ByteCount;
975         } else {
976             Stats->Fat.NonCachedWrites += 1;
977             Stats->Fat.NonCachedWriteBytes += ByteCount;
978         }
979     }
980 
981     //
982     //  Initialize some locals.
983     //
984 
985     NextRun = 0;
986     BufferOffset = 0;
987     OriginalByteCount = ByteCount;
988 
989     Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
990 
991 #if (NTDDI_VERSION >= NTDDI_WIN8)
992 
993     //
994     //  Disk IO accounting
995     //
996 
997     if (FatDiskAccountingEnabled) {
998 
999         FatUpdateDiskStats( IrpContext,
1000                             Irp,
1001                             ByteCount );
1002     }
1003 #endif
1004 
1005     //
1006     // For nonbuffered I/O, we need the buffer locked in all
1007     // cases.
1008     //
1009     // This call may raise.  If this call succeeds and a subsequent
1010     // condition is raised, the buffers are unlocked automatically
1011     // by the I/O system when the request is completed, via the
1012     // Irp->MdlAddress field.
1013     //
1014 
1015     FatLockUserBuffer( IrpContext,
1016                        Irp,
1017                        (IrpContext->MajorFunction == IRP_MJ_READ) ?
1018                        IoWriteAccess : IoReadAccess,
1019                        ByteCount );
1020 
1021 
1022 
1023     //
1024     //  No zeroing for trailing sectors if requested.
1025     //  Otherwise setup the required zeroing for read requests.
1026     //
1027 
1028 
1029     if (UserByteCount != ByteCount) {
1030 
1031 
1032         PMDL Mdl;
1033 
1034         NT_ASSERT( ByteCount > UserByteCount );
1035         _Analysis_assume_(ByteCount > UserByteCount);
1036 
1037         Mdl = IoAllocateMdl( (PUCHAR) Irp->UserBuffer + UserByteCount,
1038                              ByteCount - UserByteCount,
1039                              FALSE,
1040                              FALSE,
1041                              NULL );
1042 
1043         if (Mdl == NULL) {
1044 
1045             FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
1046         }
1047 
1048         IoBuildPartialMdl( Irp->MdlAddress,
1049                            Mdl,
1050                            (PUCHAR) Irp->UserBuffer + UserByteCount,
1051                            ByteCount - UserByteCount );
1052 
1053         IrpContext->FatIoContext->ZeroMdl = Mdl;
1054 
1055         //
1056         //  Map the MDL now so we can't fail at IO completion time.  Note
1057         //  that this will be only a single page.
1058         //
1059 
1060         if (MmGetSystemAddressForMdlSafe( Mdl, NormalPagePriority | MdlMappingNoExecute ) == NULL) {
1061 
1062             FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
1063         }
1064     }
1065 
1066 
1067     //
1068     //  Try to lookup the first run.  If there is just a single run,
1069     //  we may just be able to pass it on.
1070     //
1071 
1072     FatLookupFileAllocation( IrpContext,
1073                              FcbOrDcb,
1074                              StartingVbo,
1075                              &NextLbo,
1076                              &NextByteCount,
1077                              &NextIsAllocated,
1078                              &EndOnMax,
1079                              &FirstIndex );
1080 
1081     //
1082     //  We just added the allocation, thus there must be at least
1083     //  one entry in the mcb corresponding to our write, ie.
1084     //  NextIsAllocated must be true.  If not, the pre-existing file
1085     //  must have an allocation error.
1086     //
1087 
1088     if ( !NextIsAllocated ) {
1089 
1090         FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
1091 
1092         FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1093     }
1094 
1095     NT_ASSERT( NextByteCount != 0 );
1096 
1097     //
1098     //  If the request was not aligned correctly, read in the first
1099     //  part first.
1100     //
1101 
1102 
1103     //
1104     //  See if the write covers a single valid run, and if so pass
1105     //  it on.  We must bias this by the byte that is lost at the
1106     //  end of the maximal file.
1107     //
1108 
1109     if ( NextByteCount >= ByteCount - (EndOnMax ? 1 : 0)) {
1110 
1111         if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
1112             CollectDiskIoStats(FcbOrDcb->Vcb, IrpContext->MajorFunction,
1113                                FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO), 1);
1114         } else {
1115 
1116             PFILE_SYSTEM_STATISTICS Stats =
1117                 &FcbOrDcb->Vcb->Statistics[KeGetCurrentProcessorNumber() % FatData.NumberProcessors];
1118 
1119             if (IrpContext->MajorFunction == IRP_MJ_READ) {
1120                 Stats->Fat.NonCachedDiskReads += 1;
1121             } else {
1122                 Stats->Fat.NonCachedDiskWrites += 1;
1123             }
1124         }
1125 
1126         DebugTrace( 0, Dbg, "Passing 1 Irp on to Disk Driver\n", 0 );
1127 
1128         FatSingleAsync( IrpContext,
1129                         FcbOrDcb->Vcb,
1130                         NextLbo,
1131                         ByteCount,
1132                         Irp );
1133 
1134     } else {
1135 
1136         //
1137         //  If there we can't wait, and there are more runs than we can handle,
1138         //  we will have to post this request.
1139         //
1140 
1141         FatLookupFileAllocation( IrpContext,
1142                                  FcbOrDcb,
1143                                  StartingVbo + ByteCount - 1,
1144                                  &LastLbo,
1145                                  &LastByteCount,
1146                                  &LastIsAllocated,
1147                                  &EndOnMax,
1148                                  &LastIndex );
1149 
1150         //
1151         // Since we already added the allocation for the whole
1152         // write, assert that we find runs until ByteCount == 0
1153         // Otherwise this file is corrupt.
1154         //
1155 
1156         if ( !LastIsAllocated ) {
1157 
1158             FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
1159 
1160             FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1161         }
1162 
1163         if (LastIndex - FirstIndex + 1 > FAT_MAX_IO_RUNS_ON_STACK) {
1164 
1165             IoRuns = FsRtlAllocatePoolWithTag( PagedPool,
1166                                                (LastIndex - FirstIndex + 1) * sizeof(IO_RUN),
1167                                                TAG_IO_RUNS );
1168 
1169         } else {
1170 
1171             IoRuns = StackIoRuns;
1172         }
1173 
1174         NT_ASSERT( LastIndex != FirstIndex );
1175 
1176         CurrentIndex = FirstIndex;
1177 
1178         //
1179         // Loop while there are still byte writes to satisfy.
1180         //
1181 
1182         while (CurrentIndex <= LastIndex) {
1183 
1184 
1185             NT_ASSERT( NextByteCount != 0);
1186             NT_ASSERT( ByteCount != 0);
1187 
1188             //
1189             // If next run is larger than we need, "ya get what you need".
1190             //
1191 
1192             if (NextByteCount > ByteCount) {
1193                 NextByteCount = ByteCount;
1194             }
1195 
1196             //
1197             // Now that we have properly bounded this piece of the
1198             // transfer, it is time to write it.
1199             //
1200             // We remember each piece of a parallel run by saving the
1201             // essential information in the IoRuns array.  The tranfers
1202             // are started up in parallel below.
1203             //
1204 
1205             IoRuns[NextRun].Vbo = StartingVbo;
1206             IoRuns[NextRun].Lbo = NextLbo;
1207             IoRuns[NextRun].Offset = BufferOffset;
1208             IoRuns[NextRun].ByteCount = NextByteCount;
1209             NextRun += 1;
1210 
1211             //
1212             // Now adjust everything for the next pass through the loop.
1213             //
1214 
1215             StartingVbo += NextByteCount;
1216             BufferOffset += NextByteCount;
1217             ByteCount -= NextByteCount;
1218 
1219             //
1220             // Try to lookup the next run (if we are not done).
1221             //
1222 
1223             CurrentIndex += 1;
1224 
1225             if ( CurrentIndex <= LastIndex ) {
1226 
1227                 NT_ASSERT( ByteCount != 0 );
1228 
1229                 FatGetNextMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb,
1230                                     CurrentIndex,
1231                                     &NextVbo,
1232                                     &NextLbo,
1233                                     &NextByteCount );
1234 
1235 
1236                 NT_ASSERT(NextVbo == StartingVbo);
1237 
1238 
1239             }
1240 
1241         } // while ( CurrentIndex <= LastIndex )
1242 
1243         //
1244         //  Now set up the Irp->IoStatus.  It will be modified by the
1245         //  multi-completion routine in case of error or verify required.
1246         //
1247 
1248         Irp->IoStatus.Status = STATUS_SUCCESS;
1249         Irp->IoStatus.Information = OriginalByteCount;
1250 
1251         if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
1252             CollectDiskIoStats(FcbOrDcb->Vcb, IrpContext->MajorFunction,
1253                                FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO), NextRun);
1254         }
1255 
1256         //
1257         //  OK, now do the I/O.
1258         //
1259 
1260         _SEH2_TRY {
1261 
1262             DebugTrace( 0, Dbg, "Passing Multiple Irps on to Disk Driver\n", 0 );
1263 
1264             FatMultipleAsync( IrpContext,
1265                               FcbOrDcb->Vcb,
1266                               Irp,
1267                               NextRun,
1268                               IoRuns );
1269 
1270         } _SEH2_FINALLY {
1271 
1272             if (IoRuns != StackIoRuns) {
1273 
1274                 ExFreePool( IoRuns );
1275             }
1276         } _SEH2_END;
1277     }
1278 
1279     if (!Wait) {
1280 
1281         DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0);
1282         return STATUS_PENDING;
1283     }
1284 
1285     FatWaitSync( IrpContext );
1286 
1287 
1288     DebugTrace(-1, Dbg, "FatNonCachedIo -> 0x%08lx\n", Irp->IoStatus.Status);
1289     return Irp->IoStatus.Status;
1290 }
1291 
1292 
1293 _Requires_lock_held_(_Global_critical_region_)
1294 VOID
1295 FatNonCachedNonAlignedRead (
1296     IN PIRP_CONTEXT IrpContext,
1297     IN PIRP Irp,
1298     IN PFCB FcbOrDcb,
1299     IN ULONG StartingVbo,
1300     IN ULONG ByteCount
1301     )
1302 
1303 /*++
1304 
1305 Routine Description:
1306 
1307     This routine performs the non-cached disk io described in its parameters.
1308     This routine differs from the above in that the range does not have to be
1309     sector aligned.  This accomplished with the use of intermediate buffers.
1310 
1311 Arguments:
1312 
1313     IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
1314 
1315     Irp - Supplies the requesting Irp.
1316 
1317     FcbOrDcb - Supplies the file to act on.
1318 
1319     StartingVbo - The starting point for the operation.
1320 
1321     ByteCount - The lengh of the operation.
1322 
1323 Return Value:
1324 
1325     None.
1326 
1327 --*/
1328 
1329 {
1330     //
1331     // Declare some local variables for enumeration through the
1332     // runs of the file, and an array to store parameters for
1333     // parallel I/Os
1334     //
1335 
1336     LBO NextLbo;
1337     ULONG NextByteCount;
1338     BOOLEAN NextIsAllocated;
1339 
1340     ULONG SectorSize;
1341     ULONG BytesToCopy;
1342     ULONG OriginalByteCount;
1343     ULONG OriginalStartingVbo;
1344 
1345     BOOLEAN EndOnMax;
1346 
1347     PUCHAR UserBuffer;
1348     PUCHAR DiskBuffer = NULL;
1349 
1350     PMDL Mdl;
1351     PMDL SavedMdl;
1352     PVOID SavedUserBuffer;
1353 
1354     PAGED_CODE();
1355 
1356     DebugTrace(+1, Dbg, "FatNonCachedNonAlignedRead\n", 0);
1357     DebugTrace( 0, Dbg, "Irp           = %p\n", Irp );
1358     DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction );
1359     DebugTrace( 0, Dbg, "FcbOrDcb      = %p\n", FcbOrDcb );
1360     DebugTrace( 0, Dbg, "StartingVbo   = %08lx\n", StartingVbo );
1361     DebugTrace( 0, Dbg, "ByteCount     = %08lx\n", ByteCount );
1362 
1363     //
1364     //  Initialize some locals.
1365     //
1366 
1367     OriginalByteCount = ByteCount;
1368     OriginalStartingVbo = StartingVbo;
1369     SectorSize = FcbOrDcb->Vcb->Bpb.BytesPerSector;
1370 
1371     NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
1372 
1373     //
1374     // For nonbuffered I/O, we need the buffer locked in all
1375     // cases.
1376     //
1377     // This call may raise.  If this call succeeds and a subsequent
1378     // condition is raised, the buffers are unlocked automatically
1379     // by the I/O system when the request is completed, via the
1380     // Irp->MdlAddress field.
1381     //
1382 
1383     FatLockUserBuffer( IrpContext,
1384                        Irp,
1385                        IoWriteAccess,
1386                        ByteCount );
1387 
1388     UserBuffer = FatMapUserBuffer( IrpContext, Irp );
1389 
1390     //
1391     //  Allocate the local buffer
1392     //
1393 
1394     DiskBuffer = FsRtlAllocatePoolWithTag( NonPagedPoolNxCacheAligned,
1395                                            (ULONG) ROUND_TO_PAGES( SectorSize ),
1396                                            TAG_IO_BUFFER );
1397 
1398     //
1399     //  We use a try block here to ensure the buffer is freed, and to
1400     //  fill in the correct byte count in the Iosb.Information field.
1401     //
1402 
1403     _SEH2_TRY {
1404 
1405         //
1406         //  If the beginning of the request was not aligned correctly, read in
1407         //  the first part first.
1408         //
1409 
1410         if ( StartingVbo & (SectorSize - 1) ) {
1411 
1412             VBO Hole;
1413 
1414             //
1415             // Try to lookup the first run.
1416             //
1417 
1418             FatLookupFileAllocation( IrpContext,
1419                                      FcbOrDcb,
1420                                      StartingVbo,
1421                                      &NextLbo,
1422                                      &NextByteCount,
1423                                      &NextIsAllocated,
1424                                      &EndOnMax,
1425                                      NULL );
1426 
1427             //
1428             // We just added the allocation, thus there must be at least
1429             // one entry in the mcb corresponding to our write, ie.
1430             // NextIsAllocated must be true.  If not, the pre-existing file
1431             // must have an allocation error.
1432             //
1433 
1434             if ( !NextIsAllocated ) {
1435 
1436                 FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
1437 
1438                 FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1439             }
1440 
1441             FatSingleNonAlignedSync( IrpContext,
1442                                      FcbOrDcb->Vcb,
1443                                      DiskBuffer,
1444                                      NextLbo & ~((LONG)SectorSize - 1),
1445                                      SectorSize,
1446                                      Irp );
1447 
1448             if (!NT_SUCCESS( Irp->IoStatus.Status )) {
1449 
1450                 try_return( NOTHING );
1451             }
1452 
1453             //
1454             //  Now copy the part of the first sector that we want to the user
1455             //  buffer.
1456             //
1457 
1458             Hole = StartingVbo & (SectorSize - 1);
1459 
1460             BytesToCopy = ByteCount >= SectorSize - Hole ?
1461                                        SectorSize - Hole : ByteCount;
1462 
1463             RtlCopyMemory( UserBuffer, DiskBuffer + Hole, BytesToCopy );
1464 
1465             StartingVbo += BytesToCopy;
1466             ByteCount -= BytesToCopy;
1467 
1468             if ( ByteCount == 0 ) {
1469 
1470                 try_return( NOTHING );
1471             }
1472         }
1473 
1474         NT_ASSERT( (StartingVbo & (SectorSize - 1)) == 0 );
1475 
1476         //
1477         //  If there is a tail part that is not sector aligned, read it.
1478         //
1479 
1480         if ( ByteCount & (SectorSize - 1) ) {
1481 
1482             VBO LastSectorVbo;
1483 
1484             LastSectorVbo = StartingVbo + (ByteCount & ~(SectorSize - 1));
1485 
1486             //
1487             // Try to lookup the last part of the requested range.
1488             //
1489 
1490             FatLookupFileAllocation( IrpContext,
1491                                      FcbOrDcb,
1492                                      LastSectorVbo,
1493                                      &NextLbo,
1494                                      &NextByteCount,
1495                                      &NextIsAllocated,
1496                                      &EndOnMax,
1497                                      NULL );
1498 
1499             //
1500             // We just added the allocation, thus there must be at least
1501             // one entry in the mcb corresponding to our write, ie.
1502             // NextIsAllocated must be true.  If not, the pre-existing file
1503             // must have an allocation error.
1504             //
1505 
1506             if ( !NextIsAllocated ) {
1507 
1508                 FatPopUpFileCorrupt( IrpContext, FcbOrDcb );
1509 
1510                 FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1511             }
1512 
1513             FatSingleNonAlignedSync( IrpContext,
1514                                      FcbOrDcb->Vcb,
1515                                      DiskBuffer,
1516                                      NextLbo,
1517                                      SectorSize,
1518                                      Irp );
1519 
1520             if (!NT_SUCCESS( Irp->IoStatus.Status )) {
1521 
1522                 try_return( NOTHING );
1523             }
1524 
1525             //
1526             //  Now copy over the part of this last sector that we need.
1527             //
1528 
1529             BytesToCopy = ByteCount & (SectorSize - 1);
1530 
1531             UserBuffer += LastSectorVbo - OriginalStartingVbo;
1532 
1533             RtlCopyMemory( UserBuffer, DiskBuffer, BytesToCopy );
1534 
1535             ByteCount -= BytesToCopy;
1536 
1537             if ( ByteCount == 0 ) {
1538 
1539                 try_return( NOTHING );
1540             }
1541         }
1542 
1543         NT_ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 );
1544 
1545         //
1546         //  Now build a Mdl describing the sector aligned balance of the transfer,
1547         //  and put it in the Irp, and read that part.
1548         //
1549 
1550         SavedMdl = Irp->MdlAddress;
1551         Irp->MdlAddress = NULL;
1552 
1553         SavedUserBuffer = Irp->UserBuffer;
1554 
1555         Irp->UserBuffer = (PUCHAR)MmGetMdlVirtualAddress( SavedMdl ) +
1556                           (StartingVbo - OriginalStartingVbo);
1557 
1558         Mdl = IoAllocateMdl( Irp->UserBuffer,
1559                              ByteCount,
1560                              FALSE,
1561                              FALSE,
1562                              Irp );
1563 
1564         if (Mdl == NULL) {
1565 
1566             Irp->MdlAddress = SavedMdl;
1567             Irp->UserBuffer = SavedUserBuffer;
1568             FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
1569         }
1570 
1571         IoBuildPartialMdl( SavedMdl,
1572                            Mdl,
1573                            Irp->UserBuffer,
1574                            ByteCount );
1575 
1576         //
1577         //  Try to read in the pages.
1578         //
1579 
1580         _SEH2_TRY {
1581 
1582             FatNonCachedIo( IrpContext,
1583                             Irp,
1584                             FcbOrDcb,
1585                             StartingVbo,
1586                             ByteCount,
1587                             ByteCount,
1588                             0 );
1589 
1590         } _SEH2_FINALLY {
1591 
1592             IoFreeMdl( Irp->MdlAddress );
1593 
1594             Irp->MdlAddress = SavedMdl;
1595             Irp->UserBuffer = SavedUserBuffer;
1596         } _SEH2_END;
1597 
1598     try_exit: NOTHING;
1599 
1600     } _SEH2_FINALLY {
1601 
1602         ExFreePool( DiskBuffer );
1603 
1604         if ( !_SEH2_AbnormalTermination() && NT_SUCCESS(Irp->IoStatus.Status) ) {
1605 
1606             Irp->IoStatus.Information = OriginalByteCount;
1607 
1608             //
1609             //  We now flush the user's buffer to memory.
1610             //
1611 
1612             KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE );
1613         }
1614     } _SEH2_END;
1615 
1616     DebugTrace(-1, Dbg, "FatNonCachedNonAlignedRead -> VOID\n", 0);
1617     return;
1618 }
1619 
1620 
1621 VOID
1622 FatMultipleAsync (
1623     IN PIRP_CONTEXT IrpContext,
1624     IN PVCB Vcb,
1625     IN PIRP MasterIrp,
1626     IN ULONG MultipleIrpCount,
1627     IN PIO_RUN IoRuns
1628     )
1629 
1630 /*++
1631 
1632 Routine Description:
1633 
1634     This routine first does the initial setup required of a Master IRP that is
1635     going to be completed using associated IRPs.  This routine should not
1636     be used if only one async request is needed, instead the single read/write
1637     async routines should be called.
1638 
1639     A context parameter is initialized, to serve as a communications area
1640     between here and the common completion routine.  This initialization
1641     includes allocation of a spinlock.  The spinlock is deallocated in the
1642     FatWaitSync routine, so it is essential that the caller insure that
1643     this routine is always called under all circumstances following a call
1644     to this routine.
1645 
1646     Next this routine reads or writes one or more contiguous sectors from
1647     a device asynchronously, and is used if there are multiple reads for a
1648     master IRP.  A completion routine is used to synchronize with the
1649     completion of all of the I/O requests started by calls to this routine.
1650 
1651     Also, prior to calling this routine the caller must initialize the
1652     IoStatus field in the Context, with the correct success status and byte
1653     count which are expected if all of the parallel transfers complete
1654     successfully.  After return this status will be unchanged if all requests
1655     were, in fact, successful.  However, if one or more errors occur, the
1656     IoStatus will be modified to reflect the error status and byte count
1657     from the first run (by Vbo) which encountered an error.  I/O status
1658     from all subsequent runs will not be indicated.
1659 
1660 Arguments:
1661 
1662     IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
1663 
1664     Vcb - Supplies the device to be read
1665 
1666     MasterIrp - Supplies the master Irp.
1667 
1668     MulitpleIrpCount - Supplies the number of multiple async requests
1669         that will be issued against the master irp.
1670 
1671     IoRuns - Supplies an array containing the Vbo, Lbo, BufferOffset, and
1672         ByteCount for all the runs to executed in parallel.
1673 
1674 Return Value:
1675 
1676     None.
1677 
1678 --*/
1679 
1680 {
1681     PIRP Irp;
1682     PIO_STACK_LOCATION IrpSp;
1683     PMDL Mdl;
1684     BOOLEAN Wait;
1685     PFAT_IO_CONTEXT Context;
1686 #ifndef __REACTOS__
1687     BOOLEAN IsAWrite = FALSE;
1688     ULONG Length = 0;
1689 #endif
1690 
1691     ULONG UnwindRunCount = 0;
1692 
1693     BOOLEAN ExceptionExpected = TRUE;
1694 
1695     PAGED_CODE();
1696 
1697     DebugTrace(+1, Dbg, "FatMultipleAsync\n", 0);
1698     DebugTrace( 0, Dbg, "MajorFunction    = %08lx\n", IrpContext->MajorFunction );
1699     DebugTrace( 0, Dbg, "Vcb              = %p\n", Vcb );
1700     DebugTrace( 0, Dbg, "MasterIrp        = %p\n", MasterIrp );
1701     DebugTrace( 0, Dbg, "MultipleIrpCount = %08lx\n", MultipleIrpCount );
1702     DebugTrace( 0, Dbg, "IoRuns           = %08lx\n", IoRuns );
1703 
1704     //
1705     //  If this I/O originating during FatVerifyVolume, bypass the
1706     //  verify logic.
1707     //
1708 
1709     if (Vcb->VerifyThread == KeGetCurrentThread()) {
1710 
1711         SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY );
1712     }
1713 
1714     //
1715     //  Set up things according to whether this is truely async.
1716     //
1717 
1718     Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
1719 
1720     Context = IrpContext->FatIoContext;
1721 
1722     //
1723     //  Finish initializing Context, for use in Read/Write Multiple Asynch.
1724     //
1725 
1726     Context->MasterIrp = MasterIrp;
1727 
1728     IrpSp = IoGetCurrentIrpStackLocation( MasterIrp );
1729 #ifndef __REACTOS__
1730     IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE);
1731     Length = IrpSp->Parameters.Read.Length;
1732 #endif
1733 
1734     _SEH2_TRY {
1735 
1736         //
1737         //  Itterate through the runs, doing everything that can fail
1738         //
1739 
1740         for ( UnwindRunCount = 0;
1741               UnwindRunCount < MultipleIrpCount;
1742               UnwindRunCount++ ) {
1743 
1744             //
1745             //  Create an associated IRP, making sure there is one stack entry for
1746             //  us, as well.
1747             //
1748 
1749             IoRuns[UnwindRunCount].SavedIrp = 0;
1750 
1751             Irp = IoMakeAssociatedIrp( MasterIrp,
1752                                        (CCHAR)(Vcb->TargetDeviceObject->StackSize + 1) );
1753 
1754             if (Irp == NULL) {
1755 
1756                 FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
1757             }
1758 
1759             IoRuns[UnwindRunCount].SavedIrp = Irp;
1760 
1761             //
1762             // Allocate and build a partial Mdl for the request.
1763             //
1764 
1765             Mdl = IoAllocateMdl( (PCHAR)MasterIrp->UserBuffer +
1766                                  IoRuns[UnwindRunCount].Offset,
1767                                  IoRuns[UnwindRunCount].ByteCount,
1768                                  FALSE,
1769                                  FALSE,
1770                                  Irp );
1771 
1772             if (Mdl == NULL) {
1773 
1774                 FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
1775             }
1776 
1777             //
1778             //  Sanity Check
1779             //
1780 
1781             NT_ASSERT( Mdl == Irp->MdlAddress );
1782 
1783             IoBuildPartialMdl( MasterIrp->MdlAddress,
1784                                Mdl,
1785                                (PCHAR)MasterIrp->UserBuffer +
1786                                IoRuns[UnwindRunCount].Offset,
1787                                IoRuns[UnwindRunCount].ByteCount );
1788 
1789             //
1790             //  Get the first IRP stack location in the associated Irp
1791             //
1792 
1793             IoSetNextIrpStackLocation( Irp );
1794             IrpSp = IoGetCurrentIrpStackLocation( Irp );
1795 
1796             //
1797             //  Setup the Stack location to describe our read.
1798             //
1799 
1800             IrpSp->MajorFunction = IrpContext->MajorFunction;
1801             IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].ByteCount;
1802             IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].Vbo;
1803 
1804             //
1805             // Set up the completion routine address in our stack frame.
1806             //
1807 
1808             IoSetCompletionRoutine( Irp,
1809                                     Wait ?
1810                                     &FatMultiSyncCompletionRoutine :
1811                                     &FatMultiAsyncCompletionRoutine,
1812                                     Context,
1813                                     TRUE,
1814                                     TRUE,
1815                                     TRUE );
1816 
1817             //
1818             //  Setup the next IRP stack location in the associated Irp for the disk
1819             //  driver beneath us.
1820             //
1821 
1822             IrpSp = IoGetNextIrpStackLocation( Irp );
1823 
1824             //
1825             //  Setup the Stack location to do a read from the disk driver.
1826             //
1827 
1828             IrpSp->MajorFunction = IrpContext->MajorFunction;
1829             IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].ByteCount;
1830             IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].Lbo;
1831 
1832             //
1833             //  If this Irp is the result of a WriteThough operation,
1834             //  tell the device to write it through.
1835             //
1836 
1837             if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH )) {
1838 
1839                 SetFlag( IrpSp->Flags, SL_WRITE_THROUGH );
1840             }
1841 
1842             //
1843             //  If this I/O requires override verify, bypass the verify logic.
1844             //
1845 
1846             if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY )) {
1847 
1848                 SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
1849             }
1850         }
1851 
1852         //
1853         //  Now we no longer expect an exception.  If the driver raises, we
1854         //  must bugcheck, because we do not know how to recover from that
1855         //  case.
1856         //
1857 
1858         ExceptionExpected = FALSE;
1859 
1860         //
1861         //  We only need to set the associated IRP count in the master irp to
1862         //  make it a master IRP.  But we set the count to one more than our
1863         //  caller requested, because we do not want the I/O system to complete
1864         //  the I/O.  We also set our own count.
1865         //
1866 
1867         Context->IrpCount = MultipleIrpCount;
1868         MasterIrp->AssociatedIrp.IrpCount = MultipleIrpCount;
1869 
1870         if (Wait) {
1871 
1872             MasterIrp->AssociatedIrp.IrpCount += 1;
1873         }
1874         else if (FlagOn( Context->Wait.Async.ResourceThreadId, 3 )) {
1875 
1876             //
1877             //  For async requests if we acquired locks, transition the lock owners to an
1878             //  object, since when we return this thread could go away before request
1879             //  completion, and the resource package may try to boost priority.
1880             //
1881 
1882             if (Context->Wait.Async.Resource != NULL) {
1883 
1884                 ExSetResourceOwnerPointer( Context->Wait.Async.Resource,
1885                                            (PVOID)Context->Wait.Async.ResourceThreadId );
1886             }
1887 
1888             if (Context->Wait.Async.Resource2 != NULL) {
1889 
1890                 ExSetResourceOwnerPointer( Context->Wait.Async.Resource2,
1891                                            (PVOID)Context->Wait.Async.ResourceThreadId );
1892             }
1893         }
1894 
1895         //
1896         //  Back up a copy of the IrpContext flags for later use in async completion.
1897         //
1898 
1899         Context->IrpContextFlags = IrpContext->Flags;
1900 
1901         //
1902         //  Now that all the dangerous work is done, issue the read requests
1903         //
1904 
1905         for (UnwindRunCount = 0;
1906              UnwindRunCount < MultipleIrpCount;
1907              UnwindRunCount++) {
1908 
1909             Irp = IoRuns[UnwindRunCount].SavedIrp;
1910 
1911             DebugDoit( FatIoCallDriverCount += 1);
1912 
1913             //
1914             //  If IoCallDriver returns an error, it has completed the Irp
1915             //  and the error will be caught by our completion routines
1916             //  and dealt with as a normal IO error.
1917             //
1918 
1919             (VOID)FatLowLevelReadWrite( IrpContext,
1920                                         Vcb->TargetDeviceObject,
1921                                         Irp,
1922                                         Vcb );
1923         }
1924 
1925         //
1926         //  We just issued an IO to the storage stack, update the counters indicating so.
1927         //
1928 
1929         if (FatDiskAccountingEnabled) {
1930 
1931             FatUpdateIOCountersPCW( IsAWrite, Length );
1932         }
1933 
1934     } _SEH2_FINALLY {
1935 
1936         ULONG i;
1937 
1938         DebugUnwind( FatMultipleAsync );
1939 
1940         //
1941         //  Only allocating the spinlock, making the associated Irps
1942         //  and allocating the Mdls can fail.
1943         //
1944 
1945         if ( _SEH2_AbnormalTermination() ) {
1946 
1947             //
1948             //  If the driver raised, we are hosed.  He is not supposed to raise,
1949             //  and it is impossible for us to figure out how to clean up.
1950             //
1951 
1952             if (!ExceptionExpected) {
1953                 NT_ASSERT( ExceptionExpected );
1954 #ifdef _MSC_VER
1955 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1956 #endif
1957                 FatBugCheck( 0, 0, 0 );
1958             }
1959 
1960             //
1961             //  Unwind
1962             //
1963 
1964             for (i = 0; i <= UnwindRunCount; i++) {
1965 
1966                 if ( (Irp = IoRuns[i].SavedIrp) != NULL ) {
1967 
1968                     if ( Irp->MdlAddress != NULL ) {
1969 
1970                         IoFreeMdl( Irp->MdlAddress );
1971                     }
1972 
1973                     IoFreeIrp( Irp );
1974                 }
1975             }
1976         }
1977 
1978         //
1979         //  And return to our caller
1980         //
1981 
1982         DebugTrace(-1, Dbg, "FatMultipleAsync -> VOID\n", 0);
1983     } _SEH2_END;
1984 
1985     return;
1986 }
1987 
1988 
1989 VOID
1990 FatSingleAsync (
1991     IN PIRP_CONTEXT IrpContext,
1992     IN PVCB Vcb,
1993     IN LBO Lbo,
1994     IN ULONG ByteCount,
1995     IN PIRP Irp
1996     )
1997 
1998 /*++
1999 
2000 Routine Description:
2001 
2002     This routine reads or writes one or more contiguous sectors from a device
2003     asynchronously, and is used if there is only one read necessary to
2004     complete the IRP.  It implements the read by simply filling
2005     in the next stack frame in the Irp, and passing it on.  The transfer
2006     occurs to the single buffer originally specified in the user request.
2007 
2008 Arguments:
2009 
2010     IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
2011 
2012     Vcb - Supplies the device to read
2013 
2014     Lbo - Supplies the starting Logical Byte Offset to begin reading from
2015 
2016     ByteCount - Supplies the number of bytes to read from the device
2017 
2018     Irp - Supplies the master Irp to associated with the async
2019           request.
2020 
2021 Return Value:
2022 
2023     None.
2024 
2025 --*/
2026 
2027 {
2028     PIO_STACK_LOCATION IrpSp;
2029     PFAT_IO_CONTEXT Context;
2030 #ifndef __REACTOS__
2031     BOOLEAN IsAWrite = FALSE;
2032 #endif
2033 
2034     PAGED_CODE();
2035 
2036     DebugTrace(+1, Dbg, "FatSingleAsync\n", 0);
2037     DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction );
2038     DebugTrace( 0, Dbg, "Vcb           = %p\n", Vcb );
2039     DebugTrace( 0, Dbg, "Lbo           = %08lx\n", Lbo);
2040     DebugTrace( 0, Dbg, "ByteCount     = %08lx\n", ByteCount);
2041     DebugTrace( 0, Dbg, "Irp           = %p\n", Irp );
2042 
2043     //
2044     //  If this I/O originating during FatVerifyVolume, bypass the
2045     //  verify logic.
2046     //
2047 
2048     if (Vcb->VerifyThread == KeGetCurrentThread()) {
2049 
2050         SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY );
2051     }
2052 
2053     //
2054     //  Set up the completion routine address in our stack frame.
2055     //
2056 
2057     IoSetCompletionRoutine( Irp,
2058                             FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ?
2059                             &FatSingleSyncCompletionRoutine :
2060                             &FatSingleAsyncCompletionRoutine,
2061                             IrpContext->FatIoContext,
2062                             TRUE,
2063                             TRUE,
2064                             TRUE );
2065 
2066     //
2067     //  Setup the next IRP stack location in the associated Irp for the disk
2068     //  driver beneath us.
2069     //
2070 
2071     IrpSp = IoGetNextIrpStackLocation( Irp );
2072 
2073     //
2074     //  Setup the Stack location to do a read from the disk driver.
2075     //
2076 
2077     IrpSp->MajorFunction = IrpContext->MajorFunction;
2078     IrpSp->Parameters.Read.Length = ByteCount;
2079     IrpSp->Parameters.Read.ByteOffset.QuadPart = Lbo;
2080 
2081 #ifndef __REACTOS__
2082     IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE);
2083 #endif
2084 
2085     //
2086     //  If this Irp is the result of a WriteThough operation,
2087     //  tell the device to write it through.
2088     //
2089 
2090     if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH )) {
2091 
2092         SetFlag( IrpSp->Flags, SL_WRITE_THROUGH );
2093     }
2094 
2095     //
2096     //  If this I/O requires override verify, bypass the verify logic.
2097     //
2098 
2099     if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY )) {
2100 
2101         SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
2102     }
2103 
2104     //
2105     //  For async requests if we acquired locks, transition the lock owners to an
2106     //  object, since when we return this thread could go away before request
2107     //  completion, and the resource package may try to boost priority.
2108     //
2109 
2110     if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ) &&
2111         FlagOn( IrpContext->FatIoContext->Wait.Async.ResourceThreadId, 3 )) {
2112 
2113         Context = IrpContext->FatIoContext;
2114 
2115         if (Context->Wait.Async.Resource != NULL) {
2116 
2117             ExSetResourceOwnerPointer( Context->Wait.Async.Resource,
2118                                        (PVOID)Context->Wait.Async.ResourceThreadId );
2119         }
2120 
2121         if (Context->Wait.Async.Resource2 != NULL) {
2122 
2123             ExSetResourceOwnerPointer( Context->Wait.Async.Resource2,
2124                                        (PVOID)Context->Wait.Async.ResourceThreadId );
2125         }
2126     }
2127 
2128     //
2129     //  Back up a copy of the IrpContext flags for later use in async completion.
2130     //
2131 
2132     IrpContext->FatIoContext->IrpContextFlags = IrpContext->Flags;
2133 
2134     //
2135     //  Issue the read request
2136     //
2137 
2138     DebugDoit( FatIoCallDriverCount += 1);
2139 
2140     //
2141     //  If IoCallDriver returns an error, it has completed the Irp
2142     //  and the error will be caught by our completion routines
2143     //  and dealt with as a normal IO error.
2144     //
2145 
2146     (VOID)FatLowLevelReadWrite( IrpContext,
2147                                 Vcb->TargetDeviceObject,
2148                                 Irp,
2149                                 Vcb );
2150 
2151     //
2152     //  We just issued an IO to the storage stack, update the counters indicating so.
2153     //
2154 
2155     if (FatDiskAccountingEnabled) {
2156 
2157         FatUpdateIOCountersPCW( IsAWrite, ByteCount );
2158     }
2159 
2160     //
2161     //  And return to our caller
2162     //
2163 
2164     DebugTrace(-1, Dbg, "FatSingleAsync -> VOID\n", 0);
2165 
2166     return;
2167 }
2168 
2169 
2170 VOID
2171 FatSingleNonAlignedSync (
2172     IN PIRP_CONTEXT IrpContext,
2173     IN PVCB Vcb,
2174     IN PUCHAR Buffer,
2175     IN LBO Lbo,
2176     IN ULONG ByteCount,
2177     IN PIRP Irp
2178     )
2179 
2180 /*++
2181 
2182 Routine Description:
2183 
2184     This routine reads or writes one or more contiguous sectors from a device
2185     Synchronously, and does so to a buffer that must come from non paged
2186     pool.  It saves a pointer to the Irp's original Mdl, and creates a new
2187     one describing the given buffer.  It implements the read by simply filling
2188     in the next stack frame in the Irp, and passing it on.  The transfer
2189     occurs to the single buffer originally specified in the user request.
2190 
2191 Arguments:
2192 
2193     IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE.
2194 
2195     Vcb - Supplies the device to read
2196 
2197     Buffer - Supplies a buffer from non-paged pool.
2198 
2199     Lbo - Supplies the starting Logical Byte Offset to begin reading from
2200 
2201     ByteCount - Supplies the number of bytes to read from the device
2202 
2203     Irp - Supplies the master Irp to associated with the async
2204           request.
2205 
2206 Return Value:
2207 
2208     None.
2209 
2210 --*/
2211 
2212 {
2213     PIO_STACK_LOCATION IrpSp;
2214 
2215     PMDL Mdl;
2216     PMDL SavedMdl;
2217 #ifndef __REACTOS__
2218     BOOLEAN IsAWrite = FALSE;
2219 #endif
2220 
2221     PAGED_CODE();
2222 
2223     DebugTrace(+1, Dbg, "FatSingleNonAlignedAsync\n", 0);
2224     DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction );
2225     DebugTrace( 0, Dbg, "Vcb           = %p\n", Vcb );
2226     DebugTrace( 0, Dbg, "Buffer        = %p\n", Buffer );
2227     DebugTrace( 0, Dbg, "Lbo           = %08lx\n", Lbo);
2228     DebugTrace( 0, Dbg, "ByteCount     = %08lx\n", ByteCount);
2229     DebugTrace( 0, Dbg, "Irp           = %p\n", Irp );
2230 
2231     //
2232     //  Create a new Mdl describing the buffer, saving the current one in the
2233     //  Irp
2234     //
2235 
2236     SavedMdl = Irp->MdlAddress;
2237 
2238     Irp->MdlAddress = 0;
2239 
2240     Mdl = IoAllocateMdl( Buffer,
2241                          ByteCount,
2242                          FALSE,
2243                          FALSE,
2244                          Irp );
2245 
2246     if (Mdl == NULL) {
2247 
2248         Irp->MdlAddress = SavedMdl;
2249 
2250         FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
2251     }
2252 
2253     //
2254     //  Lock the new Mdl in memory.
2255     //
2256 
2257     _SEH2_TRY {
2258 
2259         MmProbeAndLockPages( Mdl, KernelMode, IoWriteAccess );
2260 
2261     } _SEH2_FINALLY {
2262 
2263         if ( _SEH2_AbnormalTermination() ) {
2264 
2265             IoFreeMdl( Mdl );
2266             Irp->MdlAddress = SavedMdl;
2267         }
2268     } _SEH2_END;
2269 
2270     //
2271     // Set up the completion routine address in our stack frame.
2272     //
2273 
2274     IoSetCompletionRoutine( Irp,
2275                             &FatSingleSyncCompletionRoutine,
2276                             IrpContext->FatIoContext,
2277                             TRUE,
2278                             TRUE,
2279                             TRUE );
2280 
2281     //
2282     //  Setup the next IRP stack location in the associated Irp for the disk
2283     //  driver beneath us.
2284     //
2285 
2286     IrpSp = IoGetNextIrpStackLocation( Irp );
2287 
2288     //
2289     //  Setup the Stack location to do a read from the disk driver.
2290     //
2291 
2292     IrpSp->MajorFunction = IrpContext->MajorFunction;
2293     IrpSp->Parameters.Read.Length = ByteCount;
2294     IrpSp->Parameters.Read.ByteOffset.QuadPart = Lbo;
2295 
2296 #ifndef __REACTOS__
2297     IsAWrite = (IrpSp->MajorFunction == IRP_MJ_WRITE);
2298 #endif
2299 
2300     //
2301     //  If this I/O originating during FatVerifyVolume, bypass the
2302     //  verify logic.
2303     //
2304 
2305     if (Vcb->VerifyThread == KeGetCurrentThread()) {
2306 
2307         SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY );
2308     }
2309 
2310     //
2311     //  If this I/O requires override verify, bypass the verify logic.
2312     //
2313 
2314     if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_OVERRIDE_VERIFY )) {
2315 
2316         SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
2317     }
2318 
2319     //
2320     //  Issue the read request
2321     //
2322 
2323     DebugDoit( FatIoCallDriverCount += 1);
2324 
2325     //
2326     //  If IoCallDriver returns an error, it has completed the Irp
2327     //  and the error will be caught by our completion routines
2328     //  and dealt with as a normal IO error.
2329     //
2330 
2331     _SEH2_TRY {
2332 
2333         (VOID)FatLowLevelReadWrite( IrpContext,
2334                                     Vcb->TargetDeviceObject,
2335                                     Irp,
2336                                     Vcb );
2337 
2338         FatWaitSync( IrpContext );
2339 
2340     } _SEH2_FINALLY {
2341 
2342         MmUnlockPages( Mdl );
2343         IoFreeMdl( Mdl );
2344         Irp->MdlAddress = SavedMdl;
2345     } _SEH2_END;
2346 
2347     //
2348     //  We just issued an IO to the storage stack, update the counters indicating so.
2349     //
2350 
2351     if (FatDiskAccountingEnabled) {
2352 
2353         FatUpdateIOCountersPCW( IsAWrite, ByteCount );
2354     }
2355 
2356     //
2357     //  And return to our caller
2358     //
2359 
2360     DebugTrace(-1, Dbg, "FatSingleNonAlignedSync -> VOID\n", 0);
2361 
2362     return;
2363 }
2364 
2365 
2366 VOID
2367 FatWaitSync (
2368     IN PIRP_CONTEXT IrpContext
2369     )
2370 
2371 /*++
2372 
2373 Routine Description:
2374 
2375     This routine waits for one or more previously started I/O requests
2376     from the above routines, by simply waiting on the event.
2377 
2378 Arguments:
2379 
2380 Return Value:
2381 
2382     None
2383 
2384 --*/
2385 
2386 {
2387     PAGED_CODE();
2388 
2389     DebugTrace(+1, Dbg, "FatWaitSync, Context = %p\n", IrpContext->FatIoContext );
2390 
2391     KeWaitForSingleObject( &IrpContext->FatIoContext->Wait.SyncEvent,
2392                            Executive, KernelMode, FALSE, NULL );
2393 
2394     KeClearEvent( &IrpContext->FatIoContext->Wait.SyncEvent );
2395 
2396     DebugTrace(-1, Dbg, "FatWaitSync -> VOID\n", 0 );
2397 }
2398 
2399 
2400 //
2401 // Internal Support Routine
2402 //
2403 
2404 NTSTATUS
2405 NTAPI
2406 FatMultiSyncCompletionRoutine (
2407     _In_ PDEVICE_OBJECT DeviceObject,
2408     _In_ PIRP Irp,
2409     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
2410     )
2411 
2412 /*++
2413 
2414 Routine Description:
2415 
2416     This is the completion routine for all reads and writes started via
2417     FatRead/WriteMultipleAsynch.  It must synchronize its operation for
2418     multiprocessor environments with itself on all other processors, via
2419     a spin lock found via the Context parameter.
2420 
2421     The completion routine has the following responsibilities:
2422 
2423         If the individual request was completed with an error, then
2424         this completion routine must see if this is the first error
2425         (essentially by Vbo), and if so it must correctly reduce the
2426         byte count and remember the error status in the Context.
2427 
2428         If the IrpCount goes to 1, then it sets the event in the Context
2429         parameter to signal the caller that all of the asynch requests
2430         are done.
2431 
2432 Arguments:
2433 
2434     DeviceObject - Pointer to the file system device object.
2435 
2436     Irp - Pointer to the associated Irp which is being completed.  (This
2437           Irp will no longer be accessible after this routine returns.)
2438 
2439     Contxt - The context parameter which was specified for all of
2440              the multiple asynch I/O requests for this MasterIrp.
2441 
2442 Return Value:
2443 
2444     The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can
2445     immediately complete the Master Irp without being in a race condition
2446     with the IoCompleteRequest thread trying to decrement the IrpCount in
2447     the Master Irp.
2448 
2449 --*/
2450 
2451 {
2452 
2453     PFAT_IO_CONTEXT Context = Contxt;
2454     PIRP MasterIrp = Context->MasterIrp;
2455 
2456     DebugTrace(+1, Dbg, "FatMultiSyncCompletionRoutine, Context = %p\n", Context );
2457 
2458     //
2459     //  If we got an error (or verify required), remember it in the Irp
2460     //
2461 
2462     if (!NT_SUCCESS( Irp->IoStatus.Status )) {
2463 
2464 #if DBG
2465         if(!( NT_SUCCESS( FatBreakOnInterestingIoCompletion ) || Irp->IoStatus.Status != FatBreakOnInterestingIoCompletion )) {
2466             DbgBreakPoint();
2467         }
2468 #endif
2469 
2470 #ifdef SYSCACHE_COMPILE
2471         DbgPrint( "FAT SYSCACHE: MultiSync (IRP %08x for Master %08x) -> %08x\n", Irp, MasterIrp, Irp->IoStatus );
2472 #endif
2473 
2474         MasterIrp->IoStatus = Irp->IoStatus;
2475     }
2476 
2477     NT_ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 ));
2478 
2479     //
2480     //  We must do this here since IoCompleteRequest won't get a chance
2481     //  on this associated Irp.
2482     //
2483 
2484     IoFreeMdl( Irp->MdlAddress );
2485     IoFreeIrp( Irp );
2486 
2487     if (InterlockedDecrement(&Context->IrpCount) == 0) {
2488 
2489         FatDoCompletionZero( MasterIrp, Context );
2490         KeSetEvent( &Context->Wait.SyncEvent, 0, FALSE );
2491     }
2492 
2493     DebugTrace(-1, Dbg, "FatMultiSyncCompletionRoutine -> SUCCESS\n", 0 );
2494 
2495     UNREFERENCED_PARAMETER( DeviceObject );
2496 
2497     return STATUS_MORE_PROCESSING_REQUIRED;
2498 }
2499 
2500 
2501 //
2502 // Internal Support Routine
2503 //
2504 
2505 NTSTATUS
2506 NTAPI
2507 FatMultiAsyncCompletionRoutine (
2508     _In_ PDEVICE_OBJECT DeviceObject,
2509     _In_ PIRP Irp,
2510     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
2511     )
2512 
2513 /*++
2514 
2515 Routine Description:
2516 
2517     This is the completion routine for all reads and writes started via
2518     FatRead/WriteMultipleAsynch.  It must synchronize its operation for
2519     multiprocessor environments with itself on all other processors, via
2520     a spin lock found via the Context parameter.
2521 
2522     The completion routine has has the following responsibilities:
2523 
2524         If the individual request was completed with an error, then
2525         this completion routine must see if this is the first error
2526         (essentially by Vbo), and if so it must correctly reduce the
2527         byte count and remember the error status in the Context.
2528 
2529         If the IrpCount goes to 1, then it sets the event in the Context
2530         parameter to signal the caller that all of the asynch requests
2531         are done.
2532 
2533 Arguments:
2534 
2535     DeviceObject - Pointer to the file system device object.
2536 
2537     Irp - Pointer to the associated Irp which is being completed.  (This
2538           Irp will no longer be accessible after this routine returns.)
2539 
2540     Contxt - The context parameter which was specified for all of
2541              the multiple asynch I/O requests for this MasterIrp.
2542 
2543 Return Value:
2544 
2545     The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can
2546     immediately complete the Master Irp without being in a race condition
2547     with the IoCompleteRequest thread trying to decrement the IrpCount in
2548     the Master Irp.
2549 
2550 --*/
2551 
2552 {
2553     NTSTATUS Status = STATUS_SUCCESS;
2554     PFAT_IO_CONTEXT Context = Contxt;
2555     PIRP MasterIrp = Context->MasterIrp;
2556     BOOLEAN PostRequest = FALSE;
2557 
2558     DebugTrace(+1, Dbg, "FatMultiAsyncCompletionRoutine, Context = %p\n", Context );
2559 
2560     //
2561     //  If we got an error (or verify required), remember it in the Irp
2562     //
2563 
2564     if (!NT_SUCCESS( Irp->IoStatus.Status )) {
2565 
2566 #if DBG
2567         if (!( NT_SUCCESS( FatBreakOnInterestingIoCompletion ) || Irp->IoStatus.Status != FatBreakOnInterestingIoCompletion )) {
2568             DbgBreakPoint();
2569         }
2570 #endif
2571 
2572 #ifdef SYSCACHE_COMPILE
2573         DbgPrint( "FAT SYSCACHE: MultiAsync (IRP %08x for Master %08x) -> %08x\n", Irp, MasterIrp, Irp->IoStatus );
2574 #endif
2575 
2576         MasterIrp->IoStatus = Irp->IoStatus;
2577 
2578     }
2579 
2580     NT_ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 ));
2581 
2582     if (InterlockedDecrement(&Context->IrpCount) == 0) {
2583 
2584         FatDoCompletionZero( MasterIrp, Context );
2585 
2586         if (NT_SUCCESS(MasterIrp->IoStatus.Status)) {
2587 
2588             MasterIrp->IoStatus.Information =
2589                 Context->Wait.Async.RequestedByteCount;
2590 
2591             NT_ASSERT(MasterIrp->IoStatus.Information != 0);
2592 
2593             //
2594             //  Now if this wasn't PagingIo, set either the read or write bit.
2595             //
2596 
2597             if (!FlagOn(MasterIrp->Flags, IRP_PAGING_IO)) {
2598 
2599                 SetFlag( Context->Wait.Async.FileObject->Flags,
2600                          IoGetCurrentIrpStackLocation(MasterIrp)->MajorFunction == IRP_MJ_READ ?
2601                          FO_FILE_FAST_IO_READ : FO_FILE_MODIFIED );
2602             }
2603 
2604         } else {
2605 
2606             //
2607             // Post STATUS_VERIFY_REQUIRED failures. Only post top level IRPs, because recursive I/Os
2608             // cannot process volume verification.
2609             //
2610 
2611             if (!FlagOn(Context->IrpContextFlags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) &&
2612                 (MasterIrp->IoStatus.Status == STATUS_VERIFY_REQUIRED)) {
2613                 PostRequest = TRUE;
2614             }
2615 
2616         }
2617 
2618         //
2619         //  If this was a special async write, decrement the count.  Set the
2620         //  event if this was the final outstanding I/O for the file.  We will
2621         //  also want to queue an APC to deal with any error conditionions.
2622         //
2623     	_Analysis_assume_(!(Context->Wait.Async.NonPagedFcb) &&
2624         (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites,
2625                                 0xffffffff,
2626                                 &FatData.GeneralSpinLock ) != 1));
2627         if ((Context->Wait.Async.NonPagedFcb) &&
2628             (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites,
2629                                     0xffffffff,
2630                                     &FatData.GeneralSpinLock ) == 1)) {
2631 
2632             KeSetEvent( Context->Wait.Async.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE );
2633         }
2634 
2635         //
2636         //  Now release the resources.
2637         //
2638 
2639         if (Context->Wait.Async.Resource != NULL) {
2640 
2641             ExReleaseResourceForThreadLite( Context->Wait.Async.Resource,
2642                                             Context->Wait.Async.ResourceThreadId );
2643         }
2644 
2645         if (Context->Wait.Async.Resource2 != NULL) {
2646 
2647             ExReleaseResourceForThreadLite( Context->Wait.Async.Resource2,
2648                                             Context->Wait.Async.ResourceThreadId );
2649         }
2650 
2651         //
2652         //  Mark the master Irp pending
2653         //
2654 
2655         IoMarkIrpPending( MasterIrp );
2656 
2657         //
2658         //  and finally, free the context record.
2659         //
2660 
2661         ExFreePool( Context );
2662 
2663         if (PostRequest) {
2664 
2665             PIRP_CONTEXT IrpContext = NULL;
2666 
2667             _SEH2_TRY {
2668 
2669                 IrpContext = FatCreateIrpContext(Irp, TRUE );
2670                 ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL);
2671                 FatFsdPostRequest( IrpContext, Irp );
2672                 Status = STATUS_MORE_PROCESSING_REQUIRED;
2673 
2674             } _SEH2_EXCEPT( FatExceptionFilter(NULL, _SEH2_GetExceptionInformation()) ) {
2675 
2676                 //
2677                 // If we failed to post the IRP, we just have to return the failure
2678                 // to the user. :(
2679                 //
2680 
2681                 NOTHING;
2682             } _SEH2_END;
2683         }
2684     }
2685 
2686     DebugTrace(-1, Dbg, "FatMultiAsyncCompletionRoutine -> SUCCESS\n", 0 );
2687 
2688     UNREFERENCED_PARAMETER( DeviceObject );
2689 
2690     return Status;
2691 }
2692 
2693 
2694 NTSTATUS
2695 FatPagingFileErrorHandler (
2696     IN PIRP Irp,
2697     IN PKEVENT Event OPTIONAL
2698     )
2699 
2700 /*++
2701 
2702 Routine Description:
2703 
2704     This routine attempts to guarantee that the media is marked dirty
2705     with the surface test bit if a paging file IO fails.
2706 
2707     The work done here has several basic problems
2708 
2709         1) when paging file writes start failing, this is a good sign
2710            that the rest of the system is about to fall down around us
2711 
2712         2) it has no forward progress guarantee
2713 
2714     With Whistler, it is actually quite intentional that we're rejiggering
2715     the paging file write path to make forward progress at all times.  This
2716     means that the cases where it *does* fail, we're truly seeing media errors
2717     and this is probably going to mean the paging file is going to stop working
2718     very soon.
2719 
2720     It'd be nice to make this guarantee progress.  It would need
2721 
2722         1) a guaranteed worker thread which can only be used by items which
2723            will make forward progress (i.e., not block out this one)
2724 
2725         2) the virtual volume file's pages containing the boot sector and
2726            1st FAT entry would have to be pinned resident and have a guaranteed
2727            mapping address
2728 
2729         3) mark volume would have to have a stashed irp/mdl and roll the write
2730            irp, or use a generalized mechanism to guarantee issue of the irp
2731 
2732         4) the lower stack would have to guarantee progress
2733 
2734     Of these, 1 and 4 may actually exist shortly.
2735 
2736 Arguments:
2737 
2738     Irp - Pointer to the associated Irp which is being failed.
2739 
2740     Event - Pointer to optional event to be signalled instead of completing
2741         the IRP
2742 
2743 Return Value:
2744 
2745     Returns STATUS_MORE_PROCESSING_REQUIRED if we managed to queue off the workitem,
2746     STATUS_SUCCESS otherwise.
2747 
2748 --*/
2749 
2750 {
2751     NTSTATUS Status;
2752 
2753     //
2754     //  If this was a media error, we want to chkdsk /r the next time we boot.
2755     //
2756 
2757     if (FsRtlIsTotalDeviceFailure(Irp->IoStatus.Status)) {
2758 
2759         Status = STATUS_SUCCESS;
2760 
2761     } else {
2762 
2763         PCLEAN_AND_DIRTY_VOLUME_PACKET Packet;
2764 
2765         //
2766         //  We are going to try to mark the volume needing recover.
2767         //  If we can't get pool, oh well....
2768         //
2769 
2770         Packet = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(CLEAN_AND_DIRTY_VOLUME_PACKET), ' taF');
2771 
2772         if ( Packet ) {
2773 
2774             Packet->Vcb = &((PVOLUME_DEVICE_OBJECT)IoGetCurrentIrpStackLocation(Irp)->DeviceObject)->Vcb;
2775             Packet->Irp = Irp;
2776             Packet->Event = Event;
2777 
2778             ExInitializeWorkItem( &Packet->Item,
2779                                   &FatFspMarkVolumeDirtyWithRecover,
2780                                   Packet );
2781 
2782 #ifdef _MSC_VER
2783 #pragma prefast( suppress:28159, "prefast indicates this is obsolete, but it is ok for fastfat to use it" )
2784 #endif
2785             ExQueueWorkItem( &Packet->Item, CriticalWorkQueue );
2786 
2787             Status = STATUS_MORE_PROCESSING_REQUIRED;
2788 
2789         } else {
2790 
2791             Status = STATUS_SUCCESS;
2792         }
2793     }
2794 
2795     return Status;
2796 }
2797 
2798 
2799 //
2800 // Internal Support Routine
2801 //
2802 
2803 NTSTATUS
2804 NTAPI
2805 FatPagingFileCompletionRoutineCatch (
2806     _In_ PDEVICE_OBJECT DeviceObject,
2807     _In_ PIRP Irp,
2808     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
2809     )
2810 
2811 /*++
2812 
2813 Routine Description:
2814 
2815     This is the completion routine for all reads and writes started via
2816     FatPagingFileIo that reuse the master irp (that we have to catch
2817     on the way back).  It is always invoked.
2818 
2819     The completion routine has has the following responsibility:
2820 
2821         If the error implies a media problem, it enqueues a
2822         worker item to write out the dirty bit so that the next
2823         time we run we will do a autochk /r.  This is not forward
2824         progress guaranteed at the moment.
2825 
2826         Clean up the Mdl used for this partial request.
2827 
2828     Note that if the Irp is failing, the error code is already where
2829     we want it.
2830 
2831 Arguments:
2832 
2833     DeviceObject - Pointer to the file system device object.
2834 
2835     Irp - Pointer to the associated Irp which is being completed.  (This
2836           Irp will no longer be accessible after this routine returns.)
2837 
2838     MasterIrp - Pointer to the master Irp.
2839 
2840 Return Value:
2841 
2842     Always returns STATUS_MORE_PROCESSING_REQUIRED.
2843 
2844 --*/
2845 
2846 {
2847     PFAT_PAGING_FILE_CONTEXT Context = (PFAT_PAGING_FILE_CONTEXT) Contxt;
2848 
2849     UNREFERENCED_PARAMETER( DeviceObject );
2850 
2851     DebugTrace(+1, Dbg, "FatPagingFileCompletionRoutineCatch, Context = %p\n", Context );
2852 
2853     //
2854     //  Cleanup the existing Mdl, perhaps by returning the reserve.
2855     //
2856 
2857     if (Irp->MdlAddress == FatReserveMdl) {
2858 
2859         MmPrepareMdlForReuse( Irp->MdlAddress );
2860         KeSetEvent( &FatReserveEvent, 0, FALSE );
2861 
2862     } else {
2863 
2864         IoFreeMdl( Irp->MdlAddress );
2865     }
2866 
2867     //
2868     //  Restore the original Mdl.
2869     //
2870 
2871     Irp->MdlAddress = Context->RestoreMdl;
2872 
2873     DebugTrace(-1, Dbg, "FatPagingFileCompletionRoutine => (done)\n", 0 );
2874 
2875     //
2876     //  If the IRP is succeeding or the failure handler did not post off the
2877     //  completion, we're done and should set the event to let the master
2878     //  know the IRP is his again.
2879     //
2880 
2881     if (NT_SUCCESS( Irp->IoStatus.Status ) ||
2882         FatPagingFileErrorHandler( Irp, &Context->Event ) == STATUS_SUCCESS) {
2883 
2884         KeSetEvent( &Context->Event, 0, FALSE );
2885     }
2886 
2887     return STATUS_MORE_PROCESSING_REQUIRED;
2888 
2889 }
2890 
2891 
2892 //
2893 // Internal Support Routine
2894 //
2895 
2896 NTSTATUS
2897 NTAPI
2898 FatPagingFileCompletionRoutine (
2899     _In_ PDEVICE_OBJECT DeviceObject,
2900     _In_ PIRP Irp,
2901     _In_reads_opt_(_Inexpressible_("varies")) PVOID MasterIrp
2902     )
2903 
2904 /*++
2905 
2906 Routine Description:
2907 
2908     This is the completion routine for all reads and writes started via
2909     FatPagingFileIo.  It should only be invoked on error or cancel.
2910 
2911     The completion routine has has the following responsibility:
2912 
2913         Since the individual request was completed with an error,
2914         this completion routine must stuff it into the master irp.
2915 
2916         If the error implies a media problem, it also enqueues a
2917         worker item to write out the dirty bit so that the next
2918         time we run we will do a autochk /r
2919 
2920 Arguments:
2921 
2922     DeviceObject - Pointer to the file system device object.
2923 
2924     Irp - Pointer to the associated Irp which is being completed.  (This
2925           Irp will no longer be accessible after this routine returns.)
2926 
2927     MasterIrp - Pointer to the master Irp.
2928 
2929 Return Value:
2930 
2931     Always returns STATUS_SUCCESS.
2932 
2933 --*/
2934 
2935 {
2936     DebugTrace(+1, Dbg, "FatPagingFileCompletionRoutine, MasterIrp = %p\n", MasterIrp );
2937 
2938     //
2939     //  If we got an error (or verify required), remember it in the Irp
2940     //
2941 
2942     NT_ASSERT( !NT_SUCCESS( Irp->IoStatus.Status ));
2943 
2944     //
2945     //  If we were invoked with an assoicated Irp, copy the error over.
2946     //
2947 
2948     if (Irp != MasterIrp) {
2949 
2950         ((PIRP)MasterIrp)->IoStatus = Irp->IoStatus;
2951     }
2952 
2953     DebugTrace(-1, Dbg, "FatPagingFileCompletionRoutine => (done)\n", 0 );
2954 
2955     UNREFERENCED_PARAMETER( DeviceObject );
2956 
2957     return FatPagingFileErrorHandler( Irp, NULL );
2958 }
2959 
2960 
2961 //
2962 // Internal Support Routine
2963 //
2964 
2965 NTSTATUS
2966 NTAPI
2967 FatSpecialSyncCompletionRoutine (
2968     _In_ PDEVICE_OBJECT DeviceObject,
2969     _In_ PIRP Irp,
2970     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
2971     )
2972 
2973 /*++
2974 
2975 Routine Description:
2976 
2977     This is the completion routine for a special set of sub irps
2978     that have to work at APC level.
2979 
2980     The completion routine has has the following responsibilities:
2981 
2982         It sets the event passed as the context to signal that the
2983         request is done.
2984 
2985     By doing this, the caller will be released before final APC
2986     completion with knowledge that the IRP is finished.  Final
2987     completion will occur at an indeterminate time after this
2988     occurs, and by using this completion routine the caller expects
2989     to not have any output or status returned.  A junk user Iosb
2990     should be used to capture the status without forcing Io to take
2991     an exception on NULL.
2992 
2993 Arguments:
2994 
2995     DeviceObject - Pointer to the file system device object.
2996 
2997     Irp - Pointer to the Irp for this request.  (This Irp will no longer
2998     be accessible after this routine returns.)
2999 
3000     Contxt - The context parameter which was specified in the call to
3001              FatRead/WriteSingleAsynch.
3002 
3003 Return Value:
3004 
3005     Currently always returns STATUS_SUCCESS.
3006 
3007 --*/
3008 
3009 {
3010     PFAT_SYNC_CONTEXT SyncContext = (PFAT_SYNC_CONTEXT)Contxt;
3011 
3012     UNREFERENCED_PARAMETER( Irp );
3013 
3014     DebugTrace(+1, Dbg, "FatSpecialSyncCompletionRoutine, Context = %p\n", Contxt );
3015 
3016     SyncContext->Iosb = Irp->IoStatus;
3017 
3018     KeSetEvent( &SyncContext->Event, 0, FALSE );
3019 
3020     DebugTrace(-1, Dbg, "FatSpecialSyncCompletionRoutine -> STATUS_SUCCESS\n", 0 );
3021 
3022     UNREFERENCED_PARAMETER( DeviceObject );
3023 
3024     return STATUS_SUCCESS;
3025 }
3026 
3027 
3028 //
3029 // Internal Support Routine
3030 //
3031 
3032 NTSTATUS
3033 NTAPI
3034 FatSingleSyncCompletionRoutine (
3035     _In_ PDEVICE_OBJECT DeviceObject,
3036     _In_ PIRP Irp,
3037     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
3038     )
3039 
3040 /*++
3041 
3042 Routine Description:
3043 
3044     This is the completion routine for all reads and writes started via
3045     FatRead/WriteSingleAsynch.
3046 
3047     The completion routine has has the following responsibilities:
3048 
3049         Copy the I/O status from the Irp to the Context, since the Irp
3050         will no longer be accessible.
3051 
3052         It sets the event in the Context parameter to signal the caller
3053         that all of the asynch requests are done.
3054 
3055 Arguments:
3056 
3057     DeviceObject - Pointer to the file system device object.
3058 
3059     Irp - Pointer to the Irp for this request.  (This Irp will no longer
3060     be accessible after this routine returns.)
3061 
3062     Contxt - The context parameter which was specified in the call to
3063              FatRead/WriteSingleAsynch.
3064 
3065 Return Value:
3066 
3067     Currently always returns STATUS_SUCCESS.
3068 
3069 --*/
3070 
3071 {
3072     PFAT_IO_CONTEXT Context = Contxt;
3073 
3074     DebugTrace(+1, Dbg, "FatSingleSyncCompletionRoutine, Context = %p\n", Context );
3075 
3076     FatDoCompletionZero( Irp, Context );
3077 
3078     if (!NT_SUCCESS( Irp->IoStatus.Status )) {
3079 
3080 #if DBG
3081         if(!( NT_SUCCESS( FatBreakOnInterestingIoCompletion ) || Irp->IoStatus.Status != FatBreakOnInterestingIoCompletion )) {
3082             DbgBreakPoint();
3083         }
3084 #endif
3085 
3086     }
3087 
3088     NT_ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 ));
3089 
3090     KeSetEvent( &Context->Wait.SyncEvent, 0, FALSE );
3091 
3092     DebugTrace(-1, Dbg, "FatSingleSyncCompletionRoutine -> STATUS_MORE_PROCESSING_REQUIRED\n", 0 );
3093 
3094     UNREFERENCED_PARAMETER( DeviceObject );
3095 
3096     return STATUS_MORE_PROCESSING_REQUIRED;
3097 }
3098 
3099 
3100 //
3101 // Internal Support Routine
3102 //
3103 
3104 NTSTATUS
3105 NTAPI
3106 FatSingleAsyncCompletionRoutine (
3107     _In_ PDEVICE_OBJECT DeviceObject,
3108     _In_ PIRP Irp,
3109     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
3110     )
3111 
3112 /*++
3113 
3114 Routine Description:
3115 
3116     This is the completion routine for all reads and writes started via
3117     FatRead/WriteSingleAsynch.
3118 
3119     The completion routine has has the following responsibilities:
3120 
3121         Copy the I/O status from the Irp to the Context, since the Irp
3122         will no longer be accessible.
3123 
3124         It sets the event in the Context parameter to signal the caller
3125         that all of the asynch requests are done.
3126 
3127 Arguments:
3128 
3129     DeviceObject - Pointer to the file system device object.
3130 
3131     Irp - Pointer to the Irp for this request.  (This Irp will no longer
3132     be accessible after this routine returns.)
3133 
3134     Contxt - The context parameter which was specified in the call to
3135              FatRead/WriteSingleAsynch.
3136 
3137 Return Value:
3138 
3139     Currently always returns STATUS_SUCCESS.
3140 
3141 --*/
3142 
3143 {
3144     NTSTATUS Status = STATUS_SUCCESS;
3145 
3146     PFAT_IO_CONTEXT Context = Contxt;
3147     BOOLEAN PostRequest = FALSE;
3148 
3149     DebugTrace(+1, Dbg, "FatSingleAsyncCompletionRoutine, Context = %p\n", Context );
3150 
3151     //
3152     //  Fill in the information field correctedly if this worked.
3153     //
3154 
3155     FatDoCompletionZero( Irp, Context );
3156 
3157     if (NT_SUCCESS(Irp->IoStatus.Status)) {
3158 
3159         NT_ASSERT( Irp->IoStatus.Information != 0 );
3160         Irp->IoStatus.Information = Context->Wait.Async.RequestedByteCount;
3161         NT_ASSERT( Irp->IoStatus.Information != 0 );
3162 
3163         //
3164         //  Now if this wasn't PagingIo, set either the read or write bit.
3165         //
3166 
3167         if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) {
3168 
3169             SetFlag( Context->Wait.Async.FileObject->Flags,
3170                      IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_READ ?
3171                      FO_FILE_FAST_IO_READ : FO_FILE_MODIFIED );
3172         }
3173 
3174     } else {
3175 
3176 #if DBG
3177         if(!( NT_SUCCESS( FatBreakOnInterestingIoCompletion ) || Irp->IoStatus.Status != FatBreakOnInterestingIoCompletion )) {
3178             DbgBreakPoint();
3179         }
3180 #endif
3181 
3182 #ifdef SYSCACHE_COMPILE
3183         DbgPrint( "FAT SYSCACHE: SingleAsync (IRP %08x) -> %08x\n", Irp, Irp->IoStatus );
3184 #endif
3185 
3186         //
3187         // Post STATUS_VERIFY_REQUIRED failures. Only post top level IRPs, because recursive I/Os
3188         // cannot process volume verification.
3189         //
3190 
3191         if (!FlagOn(Context->IrpContextFlags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) &&
3192             (Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)) {
3193             PostRequest = TRUE;
3194         }
3195 
3196     }
3197 
3198     //
3199     //  If this was a special async write, decrement the count.  Set the
3200     //  event if this was the final outstanding I/O for the file.  We will
3201     //  also want to queue an APC to deal with any error conditionions.
3202     //
3203     _Analysis_assume_(!(Context->Wait.Async.NonPagedFcb) &&
3204         (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites,
3205                                 0xffffffff,
3206                                 &FatData.GeneralSpinLock ) != 1));
3207 
3208     if ((Context->Wait.Async.NonPagedFcb) &&
3209         (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites,
3210                                 0xffffffff,
3211                                 &FatData.GeneralSpinLock ) == 1)) {
3212 
3213         KeSetEvent( Context->Wait.Async.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE );
3214     }
3215 
3216     //
3217     //  Now release the resources
3218     //
3219 
3220     if (Context->Wait.Async.Resource != NULL) {
3221 
3222         ExReleaseResourceForThreadLite( Context->Wait.Async.Resource,
3223                                     Context->Wait.Async.ResourceThreadId );
3224     }
3225 
3226     if (Context->Wait.Async.Resource2 != NULL) {
3227 
3228         ExReleaseResourceForThreadLite( Context->Wait.Async.Resource2,
3229                                     Context->Wait.Async.ResourceThreadId );
3230     }
3231 
3232     //
3233     //  Mark the Irp pending
3234     //
3235 
3236     IoMarkIrpPending( Irp );
3237 
3238     //
3239     //  and finally, free the context record.
3240     //
3241 
3242     ExFreePool( Context );
3243 
3244     if (PostRequest) {
3245 
3246         PIRP_CONTEXT IrpContext = NULL;
3247 
3248         _SEH2_TRY {
3249 
3250             IrpContext = FatCreateIrpContext(Irp, TRUE );
3251             ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL);
3252             FatFsdPostRequest( IrpContext, Irp );
3253             Status = STATUS_MORE_PROCESSING_REQUIRED;
3254 
3255         } _SEH2_EXCEPT( FatExceptionFilter(NULL, _SEH2_GetExceptionInformation()) ) {
3256 
3257             //
3258             // If we failed to post the IRP, we just have to return the failure
3259             // to the user. :(
3260             //
3261 
3262             NOTHING;
3263         } _SEH2_END;
3264     }
3265 
3266 
3267     DebugTrace(-1, Dbg, "FatSingleAsyncCompletionRoutine -> STATUS_MORE_PROCESSING_REQUIRED\n", 0 );
3268 
3269     UNREFERENCED_PARAMETER( DeviceObject );
3270 
3271     return Status;
3272 }
3273 
3274 
3275 VOID
3276 FatLockUserBuffer (
3277     IN PIRP_CONTEXT IrpContext,
3278     IN OUT PIRP Irp,
3279     IN LOCK_OPERATION Operation,
3280     IN ULONG BufferLength
3281     )
3282 
3283 /*++
3284 
3285 Routine Description:
3286 
3287     This routine locks the specified buffer for the specified type of
3288     access.  The file system requires this routine since it does not
3289     ask the I/O system to lock its buffers for direct I/O.  This routine
3290     may only be called from the Fsd while still in the user context.
3291 
3292     Note that this is the *input/output* buffer.
3293 
3294 Arguments:
3295 
3296     Irp - Pointer to the Irp for which the buffer is to be locked.
3297 
3298     Operation - IoWriteAccess for read operations, or IoReadAccess for
3299                 write operations.
3300 
3301     BufferLength - Length of user buffer.
3302 
3303 Return Value:
3304 
3305     None
3306 
3307 --*/
3308 
3309 {
3310     PMDL Mdl = NULL;
3311 
3312     PAGED_CODE();
3313 
3314     if (Irp->MdlAddress == NULL) {
3315 
3316         //
3317         // Allocate the Mdl, and Raise if we fail.
3318         //
3319 
3320         Mdl = IoAllocateMdl( Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp );
3321 
3322         if (Mdl == NULL) {
3323 
3324             FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
3325         }
3326 
3327         //
3328         // Now probe the buffer described by the Irp.  If we get an exception,
3329         // deallocate the Mdl and return the appropriate "expected" status.
3330         //
3331 
3332         _SEH2_TRY {
3333 
3334             MmProbeAndLockPages( Mdl,
3335                                  Irp->RequestorMode,
3336                                  Operation );
3337 
3338         } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
3339 
3340             NTSTATUS Status;
3341 
3342             Status = _SEH2_GetExceptionCode();
3343 
3344             IoFreeMdl( Mdl );
3345             Irp->MdlAddress = NULL;
3346 
3347             FatRaiseStatus( IrpContext,
3348                             FsRtlIsNtstatusExpected(Status) ? Status : STATUS_INVALID_USER_BUFFER );
3349         } _SEH2_END;
3350     }
3351 
3352     UNREFERENCED_PARAMETER( IrpContext );
3353 }
3354 
3355 
3356 PVOID
3357 FatMapUserBuffer (
3358     IN PIRP_CONTEXT IrpContext,
3359     IN OUT PIRP Irp
3360     )
3361 
3362 /*++
3363 
3364 Routine Description:
3365 
3366     This routine conditionally maps the user buffer for the current I/O
3367     request in the specified mode.  If the buffer is already mapped, it
3368     just returns its address.
3369 
3370     Note that this is the *input/output* buffer.
3371 
3372 Arguments:
3373 
3374     Irp - Pointer to the Irp for the request.
3375 
3376 Return Value:
3377 
3378     Mapped address
3379 
3380 --*/
3381 
3382 {
3383     UNREFERENCED_PARAMETER( IrpContext );
3384 
3385     PAGED_CODE();
3386 
3387     //
3388     // If there is no Mdl, then we must be in the Fsd, and we can simply
3389     // return the UserBuffer field from the Irp.
3390     //
3391 
3392     if (Irp->MdlAddress == NULL) {
3393 
3394         return Irp->UserBuffer;
3395 
3396     } else {
3397 
3398         PVOID Address = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority | MdlMappingNoExecute );
3399 
3400         if (Address == NULL) {
3401 
3402             ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
3403         }
3404 
3405         return Address;
3406     }
3407 }
3408 
3409 
3410 PVOID
3411 FatBufferUserBuffer (
3412     IN PIRP_CONTEXT IrpContext,
3413     IN OUT PIRP Irp,
3414     IN ULONG BufferLength
3415     )
3416 
3417 /*++
3418 
3419 Routine Description:
3420 
3421     This routine conditionally buffers the user buffer for the current I/O
3422     request.  If the buffer is already buffered, it just returns its address.
3423 
3424     Note that this is the *input* buffer.
3425 
3426 Arguments:
3427 
3428     Irp - Pointer to the Irp for the request.
3429 
3430     BufferLength - Length of user buffer.
3431 
3432 Return Value:
3433 
3434     Buffered address.
3435 
3436 --*/
3437 
3438 {
3439     PUCHAR UserBuffer;
3440 
3441     UNREFERENCED_PARAMETER( IrpContext );
3442 
3443     PAGED_CODE();
3444 
3445     //
3446     //  Handle the no buffer case.
3447     //
3448 
3449     if (BufferLength == 0) {
3450 
3451         return NULL;
3452     }
3453 
3454     //
3455     //  If there is no system buffer we must have been supplied an Mdl
3456     //  describing the users input buffer, which we will now snapshot.
3457     //
3458 
3459     if (Irp->AssociatedIrp.SystemBuffer == NULL) {
3460 
3461         UserBuffer = FatMapUserBuffer( IrpContext, Irp );
3462 
3463         Irp->AssociatedIrp.SystemBuffer = FsRtlAllocatePoolWithQuotaTag( NonPagedPoolNx,
3464                                                                          BufferLength,
3465                                                                          TAG_IO_USER_BUFFER );
3466 
3467         //
3468         // Set the flags so that the completion code knows to deallocate the
3469         // buffer.
3470         //
3471 
3472         Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
3473 
3474         _SEH2_TRY {
3475 
3476             RtlCopyMemory( Irp->AssociatedIrp.SystemBuffer,
3477                            UserBuffer,
3478                            BufferLength );
3479 
3480         } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
3481 
3482               NTSTATUS Status;
3483 
3484               Status = _SEH2_GetExceptionCode();
3485               FatRaiseStatus( IrpContext,
3486                               FsRtlIsNtstatusExpected(Status) ? Status : STATUS_INVALID_USER_BUFFER );
3487         } _SEH2_END;
3488     }
3489 
3490     return Irp->AssociatedIrp.SystemBuffer;
3491 }
3492 
3493 
3494 NTSTATUS
3495 FatToggleMediaEjectDisable (
3496     IN PIRP_CONTEXT IrpContext,
3497     IN PVCB Vcb,
3498     IN BOOLEAN PreventRemoval
3499     )
3500 
3501 /*++
3502 
3503 Routine Description:
3504 
3505     The routine either enables or disables the eject button on removable
3506     media.
3507 
3508 Arguments:
3509 
3510     Vcb - Descibes the volume to operate on
3511 
3512     PreventRemoval - TRUE if we should disable the media eject button.  FALSE
3513         if we want to enable it.
3514 
3515 Return Value:
3516 
3517     Status of the operation.
3518 
3519 --*/
3520 
3521 {
3522     PIRP Irp;
3523     KIRQL SavedIrql;
3524     NTSTATUS Status;
3525     FAT_SYNC_CONTEXT SyncContext;
3526     PREVENT_MEDIA_REMOVAL Prevent;
3527 
3528     UNREFERENCED_PARAMETER( IrpContext );
3529 
3530     //
3531     //  If PreventRemoval is the same as VCB_STATE_FLAG_REMOVAL_PREVENTED,
3532     //  no-op this call, otherwise toggle the state of the flag.
3533     //
3534 
3535     KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql );
3536 
3537     if ((PreventRemoval ^
3538          BooleanFlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVAL_PREVENTED)) == 0) {
3539 
3540         KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql );
3541 
3542         return STATUS_SUCCESS;
3543 
3544     } else {
3545 
3546         Vcb->VcbState ^= VCB_STATE_FLAG_REMOVAL_PREVENTED;
3547 
3548         KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql );
3549     }
3550 
3551     Prevent.PreventMediaRemoval = PreventRemoval;
3552 
3553     KeInitializeEvent( &SyncContext.Event, NotificationEvent, FALSE );
3554 
3555     //
3556     //  We build this IRP using a junk Iosb that will receive the final
3557     //  completion status since we won't be around for it.
3558     //
3559     // We fill in the UserIosb manually below,
3560     // So passing NULL for the final parameter is ok in this special case.
3561     //
3562 #ifdef _MSC_VER
3563 #pragma warning(suppress: 6387)
3564 #endif
3565     Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_MEDIA_REMOVAL,
3566                                          Vcb->TargetDeviceObject,
3567                                          &Prevent,
3568                                          sizeof(PREVENT_MEDIA_REMOVAL),
3569                                          NULL,
3570                                          0,
3571                                          FALSE,
3572                                          NULL,
3573                                          NULL );
3574 
3575     if ( Irp != NULL ) {
3576 
3577         //
3578         //  Use our special completion routine which will remove the requirement that
3579         //  the caller must be below APC level.  All it tells us is that the Irp got
3580         //  back, but will not tell us if it was succesful or not.  We don't care,
3581         //  and there is of course no fallback if the attempt to prevent removal
3582         //  doesn't work for some mysterious reason.
3583         //
3584         //  Normally, all IO is done at passive level. However, MM needs to be able
3585         //  to issue IO with fast mutexes locked down, which raises us to APC.  The
3586         //  overlying IRP is set up to complete in yet another magical fashion even
3587         //  though APCs are disabled, and any IRPage we do in these cases has to do
3588         //  the same.  Marking media dirty (and toggling eject state) is one.
3589         //
3590 
3591         Irp->UserIosb = &Irp->IoStatus;
3592 
3593         IoSetCompletionRoutine( Irp,
3594                                 FatSpecialSyncCompletionRoutine,
3595                                 &SyncContext,
3596                                 TRUE,
3597                                 TRUE,
3598                                 TRUE );
3599 
3600         Status = IoCallDriver( Vcb->TargetDeviceObject, Irp );
3601 
3602         if (Status == STATUS_PENDING) {
3603 
3604             (VOID) KeWaitForSingleObject( &SyncContext.Event,
3605                                           Executive,
3606                                           KernelMode,
3607                                           FALSE,
3608                                           NULL );
3609 
3610             Status = SyncContext.Iosb.Status;
3611         }
3612 
3613         return Status;
3614     }
3615 
3616     return STATUS_INSUFFICIENT_RESOURCES;
3617 }
3618 
3619 
3620 NTSTATUS
3621 FatPerformDevIoCtrl (
3622     IN PIRP_CONTEXT IrpContext,
3623     IN ULONG IoControlCode,
3624     IN PDEVICE_OBJECT Device,
3625     IN PVOID InputBuffer OPTIONAL,
3626     IN ULONG InputBufferLength,
3627     OUT PVOID OutputBuffer OPTIONAL,
3628     IN ULONG OutputBufferLength,
3629     IN BOOLEAN InternalDeviceIoControl,
3630     IN BOOLEAN OverrideVerify,
3631     OUT PIO_STATUS_BLOCK Iosb OPTIONAL
3632     )
3633 
3634 /*++
3635 
3636 Routine Description:
3637 
3638     This routine is called to perform DevIoCtrl functions internally within
3639     the filesystem.  We take the status from the driver and return it to our
3640     caller.
3641 
3642 Arguments:
3643 
3644     IoControlCode - Code to send to driver.
3645 
3646     Device - This is the device to send the request to.
3647 
3648     OutPutBuffer - Pointer to output buffer.
3649 
3650     OutputBufferLength - Length of output buffer above.
3651 
3652     InternalDeviceIoControl - Indicates if this is an internal or external
3653         Io control code.
3654 
3655     OverrideVerify - Indicates if we should tell the driver not to return
3656         STATUS_VERIFY_REQUIRED for mount and verify.
3657 
3658     Iosb - If specified, we return the results of the operation here.
3659 
3660 Return Value:
3661 
3662     NTSTATUS - Status returned by next lower driver.
3663 
3664 --*/
3665 
3666 {
3667     NTSTATUS Status;
3668     PIRP Irp;
3669     KEVENT Event;
3670     IO_STATUS_BLOCK LocalIosb;
3671     PIO_STATUS_BLOCK IosbToUse = &LocalIosb;
3672 
3673     PAGED_CODE();
3674 
3675     UNREFERENCED_PARAMETER( IrpContext );
3676 
3677     //
3678     //  Check if the user gave us an Iosb.
3679     //
3680 
3681     if (ARGUMENT_PRESENT( Iosb )) {
3682 
3683         IosbToUse = Iosb;
3684     }
3685 
3686     IosbToUse->Status = 0;
3687     IosbToUse->Information = 0;
3688 
3689     KeInitializeEvent( &Event, NotificationEvent, FALSE );
3690 
3691     Irp = IoBuildDeviceIoControlRequest( IoControlCode,
3692                                          Device,
3693                                          InputBuffer,
3694                                          InputBufferLength,
3695                                          OutputBuffer,
3696                                          OutputBufferLength,
3697                                          InternalDeviceIoControl,
3698                                          &Event,
3699                                          IosbToUse );
3700 
3701     if (Irp == NULL) {
3702 
3703         return STATUS_INSUFFICIENT_RESOURCES;
3704     }
3705 
3706     if (OverrideVerify) {
3707 
3708         SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
3709     }
3710 
3711     Status = IoCallDriver( Device, Irp );
3712 
3713     //
3714     //  We check for device not ready by first checking Status
3715     //  and then if status pending was returned, the Iosb status
3716     //  value.
3717     //
3718 
3719     if (Status == STATUS_PENDING) {
3720 
3721         (VOID) KeWaitForSingleObject( &Event,
3722                                       Executive,
3723                                       KernelMode,
3724                                       FALSE,
3725                                       (PLARGE_INTEGER)NULL );
3726 
3727         Status = IosbToUse->Status;
3728     }
3729 
3730     return Status;
3731 }
3732 
3733 PMDL
3734 FatBuildZeroMdl (
3735     __in PIRP_CONTEXT IrpContext,
3736     __in ULONG Length
3737     )
3738 /*++
3739 
3740 Routine Description:
3741 
3742     Create an efficient mdl that describe a given length of zeros. We'll only
3743     use a one page buffer and make a mdl that maps all the pages back to the single
3744     physical page. We'll default to a smaller size buffer down to 1 PAGE if memory
3745     is tight. The caller should check the Mdl->ByteCount to see the true size
3746 
3747 Arguments:
3748 
3749     Length - The desired length of the zero buffer. We may return less than this
3750 
3751 Return Value:
3752 
3753     a MDL if successful / NULL if not
3754 
3755 --*/
3756 
3757 {
3758     PMDL ZeroMdl;
3759     ULONG SavedByteCount;
3760     PPFN_NUMBER Page;
3761     ULONG i;
3762 
3763     UNREFERENCED_PARAMETER( IrpContext );
3764 
3765     //
3766     //  Spin down trying to get an MDL which can describe our operation.
3767     //
3768 
3769     while (TRUE) {
3770 
3771         ZeroMdl = IoAllocateMdl( FatData.ZeroPage, Length, FALSE, FALSE, NULL );
3772 
3773         //
3774         //  Throttle ourselves to what we've physically allocated.  Note that
3775         //  we could have started with an odd multiple of this number.  If we
3776         //  tried for exactly that size and failed, we're toast.
3777         //
3778 
3779         if (ZeroMdl || (Length <= PAGE_SIZE)) {
3780 
3781             break;
3782         }
3783 
3784         //
3785         //  Fallback by half and round down to a page multiple.
3786         //
3787 
3788         ASSERT( IrpContext->Vcb->Bpb.BytesPerSector <= PAGE_SIZE );
3789         Length = BlockAlignTruncate( Length / 2, PAGE_SIZE );
3790         if (Length < PAGE_SIZE) {
3791             Length = PAGE_SIZE;
3792         }
3793     }
3794 
3795     if (ZeroMdl == NULL) {
3796         return NULL;
3797     }
3798 
3799     //
3800     //  If we have throttled all the way down, stop and just build a
3801     //  simple MDL describing our previous allocation.
3802     //
3803 
3804     if (Length == PAGE_SIZE) {
3805 
3806         MmBuildMdlForNonPagedPool( ZeroMdl );
3807         return ZeroMdl;
3808     }
3809 
3810     //
3811     //  Now we will temporarily lock the allocated pages
3812     //  only, and then replicate the page frame numbers through
3813     //  the entire Mdl to keep writing the same pages of zeros.
3814     //
3815     //  It would be nice if Mm exported a way for us to not have
3816     //  to pull the Mdl apart and rebuild it ourselves, but this
3817     //  is so bizarre a purpose as to be tolerable.
3818     //
3819 
3820     SavedByteCount = ZeroMdl->ByteCount;
3821     ZeroMdl->ByteCount = PAGE_SIZE;
3822     MmBuildMdlForNonPagedPool( ZeroMdl );
3823 
3824     ZeroMdl->MdlFlags &= ~MDL_SOURCE_IS_NONPAGED_POOL;
3825     ZeroMdl->MdlFlags |= MDL_PAGES_LOCKED;
3826     ZeroMdl->MappedSystemVa = NULL;
3827     ZeroMdl->StartVa = NULL;
3828     ZeroMdl->ByteCount = SavedByteCount;
3829     Page = MmGetMdlPfnArray( ZeroMdl );
3830     for (i = 1; i < (ADDRESS_AND_SIZE_TO_SPAN_PAGES( 0, SavedByteCount )); i++) {
3831         *(Page + i) = *(Page);
3832     }
3833 
3834 
3835     return ZeroMdl;
3836 }
3837 
3838 
3839