1 /*++
2 
3 Copyright (c) 1990-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     cache.c
8 
9 Abstract:
10 
11     This module implements the cache management routines for the Fat
12     FSD and FSP, by calling the Common Cache Manager.
13 
14 
15 --*/
16 
17 #include "fatprocs.h"
18 
19 //
20 //  The Bug check file id for this module
21 //
22 
23 #define BugCheckFileId                   (FAT_BUG_CHECK_CACHESUP)
24 
25 //
26 //  Local debug trace level
27 //
28 
29 #define Dbg                              (DEBUG_TRACE_CACHESUP)
30 
31 #if DBG
32 
33 BOOLEAN
34 FatIsCurrentOperationSynchedForDcbTeardown (
35     IN PIRP_CONTEXT IrpContext,
36     IN PDCB Dcb
37     );
38 
39 #endif
40 
41 #ifdef ALLOC_PRAGMA
42 #pragma alloc_text(PAGE, FatCloseEaFile)
43 #pragma alloc_text(PAGE, FatCompleteMdl)
44 #pragma alloc_text(PAGE, FatOpenDirectoryFile)
45 #pragma alloc_text(PAGE, FatOpenEaFile)
46 #pragma alloc_text(PAGE, FatPinMappedData)
47 #pragma alloc_text(PAGE, FatPrepareWriteDirectoryFile)
48 #pragma alloc_text(PAGE, FatPrepareWriteVolumeFile)
49 #pragma alloc_text(PAGE, FatReadDirectoryFile)
50 #pragma alloc_text(PAGE, FatReadVolumeFile)
51 #pragma alloc_text(PAGE, FatRepinBcb)
52 #pragma alloc_text(PAGE, FatSyncUninitializeCacheMap)
53 #pragma alloc_text(PAGE, FatUnpinRepinnedBcbs)
54 #pragma alloc_text(PAGE, FatZeroData)
55 #pragma alloc_text(PAGE, FatPrefetchPages)
56 #if DBG
57 #pragma alloc_text(PAGE, FatIsCurrentOperationSynchedForDcbTeardown)
58 #endif
59 #endif
60 
61 VOID
62 FatInitializeCacheMap (
63     _In_ PFILE_OBJECT FileObject,
64     _In_ PCC_FILE_SIZES FileSizes,
65     _In_ BOOLEAN PinAccess,
66     _In_ PCACHE_MANAGER_CALLBACKS Callbacks,
67     _In_ PVOID LazyWriteContext
68     )
69 /*++
70 
71 Routine Description:
72 
73     Wrapper over CcInitializeCacheMap and CcSetAdditionalCacheAttributesEx to initialize
74     caching and enable IO accounting on a file.
75 
76 --*/
77 
78 {
79     //
80     //  Initialize caching
81     //
82 
83     CcInitializeCacheMap( FileObject,
84                           FileSizes,
85                           PinAccess,
86                           Callbacks,
87                           LazyWriteContext );
88 
89 #if (NTDDI_VERSION >= NTDDI_WIN8)
90     //
91     //  Enable Disk IO Accounting for this file
92     //
93 
94     if (FatDiskAccountingEnabled) {
95 
96         CcSetAdditionalCacheAttributesEx( FileObject, CC_ENABLE_DISK_IO_ACCOUNTING );
97     }
98 #endif
99 }
100 
101 VOID
102 FatReadVolumeFile (
103     IN PIRP_CONTEXT IrpContext,
104     IN PVCB Vcb,
105     IN VBO StartingVbo,
106     IN ULONG ByteCount,
107     OUT PBCB *Bcb,
108     OUT PVOID *Buffer
109     )
110 
111 /*++
112 
113 Routine Description:
114 
115     This routine is called when the specified range of sectors is to be
116     read into the cache.  In fat, the volume file only contains the boot
117     sector, reserved sectors, and the "fat(s)."  Thus the volume file is
118     of fixed size and only extends up to (but not not including) the root
119     directory entry, and will never move or change size.
120 
121     The fat volume file is also peculiar in that, since it starts at the
122     logical beginning of the disk, Vbo == Lbo.
123 
124 Arguments:
125 
126     Vcb - Pointer to the VCB for the volume
127 
128     StartingVbo - The virtual offset of the first desired byte
129 
130     ByteCount - Number of bytes desired
131 
132     Bcb - Returns a pointer to the BCB which is valid until unpinned
133 
134     Buffer - Returns a pointer to the sectors, which is valid until unpinned
135 
136 --*/
137 
138 {
139     LARGE_INTEGER Vbo;
140 
141     PAGED_CODE();
142 
143     //
144     //  Check to see that all references are within the Bios Parameter Block
145     //  or the fat(s).  A special case is made when StartingVbo == 0 at
146     //  mounting time since we do not know how big the fat is.
147     //
148 
149     NT_ASSERT( ((StartingVbo == 0) || ((StartingVbo + ByteCount) <= (ULONG)
150             (FatRootDirectoryLbo( &Vcb->Bpb ) + PAGE_SIZE))));
151 
152     DebugTrace(+1, Dbg, "FatReadVolumeFile\n", 0);
153     DebugTrace( 0, Dbg, "Vcb         = %p\n", Vcb);
154     DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo);
155     DebugTrace( 0, Dbg, "ByteCount   = %08lx\n", ByteCount);
156 
157     //
158     //  Call the Cache manager to attempt the transfer.
159     //
160 
161     Vbo.QuadPart = StartingVbo;
162 
163     if (!CcMapData( Vcb->VirtualVolumeFile,
164                     &Vbo,
165                     ByteCount,
166                     BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
167                     Bcb,
168                     Buffer )) {
169 
170         NT_ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
171 
172         //
173         // Could not read the data without waiting (cache miss).
174         //
175 
176         FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
177     }
178 
179     DbgDoit( IrpContext->PinCount += 1 )
180 
181     DebugTrace(-1, Dbg, "FatReadVolumeFile -> VOID, *BCB = %p\n", *Bcb);
182 
183     return;
184 }
185 
186 
187 _Requires_lock_held_(_Global_critical_region_)
188 VOID
189 FatPrepareWriteVolumeFile (
190     IN PIRP_CONTEXT IrpContext,
191     IN PVCB Vcb,
192     IN VBO StartingVbo,
193     IN ULONG ByteCount,
194     OUT PBCB *Bcb,
195     OUT PVOID *Buffer,
196     IN BOOLEAN Reversible,
197     IN BOOLEAN Zero
198     )
199 
200 /*++
201 
202 Routine Description:
203 
204     This routine first looks to see if the specified range of sectors,
205     is already in the cache.  If so, it increments the BCB PinCount,
206     sets the BCB dirty, and returns with the location of the sectors.
207 
208     If the sectors are not in the cache and Wait is TRUE, it finds a
209     free BCB (potentially causing a flush), and clears out the entire
210     buffer.  Once this is done, it increments the BCB PinCount, sets the
211     BCB dirty, and returns with the location of the sectors.
212 
213     If the sectors are not in the cache and Wait is FALSE, this routine
214     raises STATUS_CANT_WAIT.
215 
216 Arguments:
217 
218     Vcb - Pointer to the VCB for the volume
219 
220     StartingVbo - The virtual offset of the first byte to be written
221 
222     ByteCount - Number of bytes to be written
223 
224     Bcb - Returns a pointer to the BCB which is valid until unpinned
225 
226     Buffer - Returns a pointer to the sectors, which is valid until unpinned
227 
228     Reversible - Supplies TRUE if the specified range of modification should
229         be repinned so that the operation can be reversed in a controlled
230         fashion if errors are encountered.
231 
232     Zero - Supplies TRUE if the specified range of bytes should be zeroed
233 
234 --*/
235 
236 {
237     LARGE_INTEGER Vbo;
238 
239     PAGED_CODE();
240 
241     //
242     //  Check to see that all references are within the Bios Parameter Block
243     //  or the fat(s).
244     //
245 
246     NT_ASSERT( ((StartingVbo + ByteCount) <= (ULONG)
247             (FatRootDirectoryLbo( &Vcb->Bpb ))));
248 
249     DebugTrace(+1, Dbg, "FatPrepareWriteVolumeFile\n", 0);
250     DebugTrace( 0, Dbg, "Vcb         = %p\n", Vcb);
251     DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", (ULONG)StartingVbo);
252     DebugTrace( 0, Dbg, "ByteCount   = %08lx\n", ByteCount);
253     DebugTrace( 0, Dbg, "Zero        = %08lx\n", Zero);
254 
255     //
256     //  Call the Cache manager to attempt the transfer.
257     //
258 
259     Vbo.QuadPart = StartingVbo;
260 
261     if (!CcPinRead( Vcb->VirtualVolumeFile,
262                     &Vbo,
263                     ByteCount,
264                     BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
265                     Bcb,
266                     Buffer )) {
267 
268         NT_ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
269 
270         //
271         // Could not read the data without waiting (cache miss).
272         //
273 
274         FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
275     }
276 
277     //
278     //  This keeps the data pinned until we complete the request
279     //  and writes the dirty bit through to the disk.
280     //
281 
282     DbgDoit( IrpContext->PinCount += 1 )
283 
284     _SEH2_TRY {
285 
286         if (Zero) {
287 
288             RtlZeroMemory( *Buffer, ByteCount );
289         }
290 
291         FatSetDirtyBcb( IrpContext, *Bcb, Vcb, Reversible );
292 
293     } _SEH2_FINALLY {
294 
295         if (_SEH2_AbnormalTermination()) {
296 
297             FatUnpinBcb(IrpContext, *Bcb);
298         }
299     } _SEH2_END;
300 
301     DebugTrace(-1, Dbg, "FatPrepareWriteVolumeFile -> VOID, *Bcb = %p\n", *Bcb);
302 
303     return;
304 }
305 
306 
307 _Requires_lock_held_(_Global_critical_region_)
308 VOID
309 FatReadDirectoryFile (
310     IN PIRP_CONTEXT IrpContext,
311     IN PDCB Dcb,
312     IN VBO StartingVbo,
313     IN ULONG ByteCount,
314     IN BOOLEAN Pin,
315     OUT PBCB *Bcb,
316     OUT PVOID *Buffer,
317     OUT PNTSTATUS Status
318     )
319 
320 /*++
321 
322 Routine Description:
323 
324     This routine is called when the specified range of sectors is to be
325     read into the cache.  If the desired range falls beyond the current
326     cache mapping, the fat will be searched, and if the desired range can
327     be satisfied, the cache mapping will be extended and the MCB updated
328     accordingly.
329 
330 Arguments:
331 
332     Dcb - Pointer to the DCB for the directory
333 
334     StartingVbo - The virtual offset of the first desired byte
335 
336     ByteCount - Number of bytes desired
337 
338     Pin - Tells us if we should pin instead of just mapping.
339 
340     Bcb - Returns a pointer to the BCB which is valid until unpinned
341 
342     Buffer - Returns a pointer to the sectors, which is valid until unpinned
343 
344     Status - Returns the status of the operation.
345 
346 --*/
347 
348 {
349     LARGE_INTEGER Vbo;
350 
351     PAGED_CODE();
352 
353     DebugTrace(+1, Dbg, "FatReadDirectoryFile\n", 0);
354     DebugTrace( 0, Dbg, "Dcb         = %p\n", Dcb);
355     DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo);
356     DebugTrace( 0, Dbg, "ByteCount   = %08lx\n", ByteCount);
357 
358     //
359     //  Check for the zero case
360     //
361 
362     if (ByteCount == 0) {
363 
364         DebugTrace(0, Dbg, "Nothing to read\n", 0);
365 
366         *Bcb = NULL;
367         *Buffer = NULL;
368         *Status = STATUS_SUCCESS;
369 
370         DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID\n", 0);
371         return;
372     }
373 
374     //
375     //  If we need to create a directory file and initialize the
376     //  cachemap, do so.
377     //
378 
379     FatOpenDirectoryFile( IrpContext, Dcb );
380 
381     //
382     //  Now if the transfer is beyond the allocation size return EOF.
383     //
384 
385     if (StartingVbo >= Dcb->Header.AllocationSize.LowPart) {
386 
387         DebugTrace(0, Dbg, "End of file read for directory\n", 0);
388 
389         *Bcb = NULL;
390         *Buffer = NULL;
391         *Status = STATUS_END_OF_FILE;
392 
393         DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID\n", 0);
394         return;
395     }
396 
397     //
398     // If the caller is trying to read past the EOF, truncate the
399     // read.
400     //
401 
402     ByteCount = (Dcb->Header.AllocationSize.LowPart - StartingVbo < ByteCount) ?
403                  Dcb->Header.AllocationSize.LowPart - StartingVbo : ByteCount;
404 
405     NT_ASSERT( ByteCount != 0 );
406 
407     //
408     //  Call the Cache manager to attempt the transfer.
409     //
410 
411     Vbo.QuadPart = StartingVbo;
412 
413     if (Pin ?
414 
415         !CcPinRead( Dcb->Specific.Dcb.DirectoryFile,
416                     &Vbo,
417                     ByteCount,
418                     BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
419                     Bcb,
420                     Buffer )
421         :
422 
423         !CcMapData( Dcb->Specific.Dcb.DirectoryFile,
424                     &Vbo,
425                     ByteCount,
426                     BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
427                     Bcb,
428                     Buffer ) ) {
429 
430         //
431         // Could not read the data without waiting (cache miss).
432         //
433 
434         *Bcb = NULL;
435         *Buffer = NULL;
436         FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
437     }
438 
439     DbgDoit( IrpContext->PinCount += 1 )
440 
441     *Status = STATUS_SUCCESS;
442 
443     DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID, *BCB = %p\n", *Bcb);
444 
445     return;
446 }
447 
448 
449 _Requires_lock_held_(_Global_critical_region_)
450 VOID
451 FatPrepareWriteDirectoryFile (
452     IN PIRP_CONTEXT IrpContext,
453     IN PDCB Dcb,
454     IN VBO StartingVbo,
455     IN ULONG ByteCount,
456     OUT PBCB *Bcb,
457     OUT PVOID *Buffer,
458     IN BOOLEAN Zero,
459     IN BOOLEAN Reversible,
460     OUT PNTSTATUS Status
461     )
462 
463 /*++
464 
465 Routine Description:
466 
467     This routine first looks to see if the specified range of sectors
468     is already in the cache.  If so, it increments the BCB PinCount,
469     sets the BCB dirty, and returns TRUE with the location of the sectors.
470 
471     The IrpContext->Flags .. Wait == TRUE/FALSE actions of this routine are identical to
472     FatPrepareWriteVolumeFile() above.
473 
474 Arguments:
475 
476     Dcb - Pointer to the DCB for the directory
477 
478     StartingVbo - The virtual offset of the first byte to be written
479 
480     ByteCount - Number of bytes to be written
481 
482     Bcb - Returns a pointer to the BCB which is valid until unpinned
483 
484     Buffer - Returns a pointer to the sectors, which is valid until unpinned
485 
486     Zero - Supplies TRUE if the specified range of bytes should be zeroed
487 
488     Reversible - Supplies TRUE if the specified range of modification should
489         be repinned so that the operation can be reversed in a controlled
490         fashion if errors are encountered.
491 
492     Status - Returns the status of the operation.
493 
494 --*/
495 
496 {
497     LARGE_INTEGER Vbo;
498     ULONG InitialAllocation = 0;
499     BOOLEAN UnwindWeAllocatedDiskSpace = FALSE;
500     PBCB    LocalBcb = NULL;
501     PVOID   LocalBuffer = NULL;
502     ULONG   InitialRequest = ByteCount;
503     ULONG   MappingGranularity = PAGE_SIZE;
504 
505     PAGED_CODE();
506 
507     DebugTrace(+1, Dbg, "FatPrepareWriteDirectoryFile\n", 0);
508     DebugTrace( 0, Dbg, "Dcb         = %p\n", Dcb);
509     DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", (ULONG)StartingVbo);
510     DebugTrace( 0, Dbg, "ByteCount   = %08lx\n", ByteCount);
511     DebugTrace( 0, Dbg, "Zero        = %08lx\n", Zero);
512 
513     *Bcb = NULL;
514     *Buffer = NULL;
515 
516     //
517     //  If we need to create a directory file and initialize the
518     //  cachemap, do so.
519     //
520 
521     FatOpenDirectoryFile( IrpContext, Dcb );
522 
523     //
524     //  If the transfer is beyond the allocation size we need to
525     //  extend the directory's allocation.  The call to
526     //  AddFileAllocation will raise a condition if
527     //  it runs out of disk space.  Note that the root directory
528     //  cannot be extended.
529     //
530 
531     Vbo.QuadPart = StartingVbo;
532 
533     _SEH2_TRY {
534 
535         if (StartingVbo + ByteCount > Dcb->Header.AllocationSize.LowPart) {
536 
537             if (NodeType(Dcb) == FAT_NTC_ROOT_DCB &&
538                 !FatIsFat32(Dcb->Vcb)) {
539 
540                 FatRaiseStatus( IrpContext, STATUS_DISK_FULL );
541             }
542 
543             DebugTrace(0, Dbg, "Try extending normal directory\n", 0);
544 
545             InitialAllocation = Dcb->Header.AllocationSize.LowPart;
546 
547             FatAddFileAllocation( IrpContext,
548                                   Dcb,
549                                   Dcb->Specific.Dcb.DirectoryFile,
550                                   StartingVbo + ByteCount );
551 
552             UnwindWeAllocatedDiskSpace = TRUE;
553 
554             //
555             //  Inform the cache manager of the new allocation
556             //
557 
558             Dcb->Header.FileSize.LowPart =
559                 Dcb->Header.AllocationSize.LowPart;
560 
561             CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile,
562                             (PCC_FILE_SIZES)&Dcb->Header.AllocationSize );
563 
564             //
565             //  Set up the Bitmap buffer if it is not big enough already
566             //
567 
568             FatCheckFreeDirentBitmap( IrpContext, Dcb );
569 
570             //
571             //  The newly allocated clusters should be zeroed starting at
572             //  the previous allocation size
573             //
574 
575             Zero = TRUE;
576             Vbo.QuadPart = InitialAllocation;
577             ByteCount = Dcb->Header.AllocationSize.LowPart - InitialAllocation;
578         }
579 
580         while (ByteCount > 0) {
581 
582             ULONG BytesToPin;
583 
584             LocalBcb = NULL;
585 
586             //
587             //  We must pin in terms of pages below the boundary of the initial request.
588             //  Once we pass the end of the request, we are free to expand the pin size to
589             //  VACB_MAPPING_GRANULARITY. This will prevent Cc from returning OBCBs
590             //  and hence will prevent bugchecks when we then attempt to repin one, yet
591             //  allow us to be more efficient by pinning in 256KB chunks instead of 4KB pages.
592             //
593 
594             if (Vbo.QuadPart > StartingVbo + InitialRequest) {
595 
596                 MappingGranularity = VACB_MAPPING_GRANULARITY;
597             }
598 
599             //
600             //  If the first and final byte are both described by the same page, pin
601             //  the entire range. Note we pin in pages to prevent cache manager from
602             //  returning OBCBs, which would result in a bugcheck on CcRepinBcb.
603             //
604 
605             if ((Vbo.QuadPart / MappingGranularity) ==
606                 ((Vbo.QuadPart + ByteCount - 1) / MappingGranularity)) {
607 
608                 BytesToPin = ByteCount;
609 
610             } else {
611 
612                 BytesToPin = MappingGranularity -
613                              ((ULONG)Vbo.QuadPart & (MappingGranularity - 1));
614             }
615 
616             if (!CcPinRead( Dcb->Specific.Dcb.DirectoryFile,
617                             &Vbo,
618                             BytesToPin,
619                             BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
620                             &LocalBcb,
621                             &LocalBuffer )) {
622 
623                 //
624                 // Could not read the data without waiting (cache miss).
625                 //
626 
627                 FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
628             }
629 
630             //
631             //  Update our caller with the beginning of their request.
632             //
633 
634             if (*Buffer == NULL) {
635 
636                 *Buffer = LocalBuffer;
637                 *Bcb = LocalBcb;
638             }
639 
640             DbgDoit( IrpContext->PinCount += 1 )
641 
642             if (Zero) {
643 
644                 //
645                 //  We set this guy dirty right now so that we can raise CANT_WAIT when
646                 //  it needs to be done.  It'd be beautiful if we could noop the read IO
647                 //  since we know we don't care about it.
648                 //
649 
650                 RtlZeroMemory( LocalBuffer, BytesToPin );
651                 CcSetDirtyPinnedData( LocalBcb, NULL );
652             }
653 
654             ByteCount -= BytesToPin;
655             Vbo.QuadPart += BytesToPin;
656 
657             if (*Bcb != LocalBcb) {
658 
659                 FatRepinBcb( IrpContext, LocalBcb );
660                 FatUnpinBcb( IrpContext, LocalBcb );
661             }
662         }
663 
664         //
665         //  This lets us get the data pinned until we complete the request
666         //  and writes the dirty bit through to the disk.
667         //
668 
669         FatSetDirtyBcb( IrpContext, *Bcb, Dcb->Vcb, Reversible );
670 
671         *Status = STATUS_SUCCESS;
672 
673     } _SEH2_FINALLY {
674 
675         DebugUnwind( FatPrepareWriteDirectoryFile );
676 
677         if (_SEH2_AbnormalTermination()) {
678 
679             //
680             //  Make sure we unpin the buffers.
681             //
682 
683             if (*Bcb != LocalBcb) {
684 
685                 FatUnpinBcb( IrpContext, LocalBcb );
686             }
687 
688             FatUnpinBcb(IrpContext, *Bcb);
689 
690             //
691             //  These steps are carefully arranged - FatTruncateFileAllocation can raise.
692             //  Make sure we unpin the buffer.  If FTFA raises, the effect should be benign.
693             //
694 
695             if (UnwindWeAllocatedDiskSpace == TRUE) {
696 
697                 //
698                 //  Inform the cache manager of the change.
699                 //
700 
701                 FatTruncateFileAllocation( IrpContext, Dcb, InitialAllocation );
702 
703                 Dcb->Header.FileSize.LowPart =
704                     Dcb->Header.AllocationSize.LowPart;
705 
706                 CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile,
707                                 (PCC_FILE_SIZES)&Dcb->Header.AllocationSize );
708             }
709         }
710 
711         DebugTrace(-1, Dbg, "FatPrepareWriteDirectoryFile -> (VOID), *Bcb = %p\n", *Bcb);
712     } _SEH2_END;
713 
714     return;
715 }
716 
717 
718 #if DBG
719 BOOLEAN FatDisableParentCheck = 0;
720 
721 BOOLEAN
722 FatIsCurrentOperationSynchedForDcbTeardown (
723     IN PIRP_CONTEXT IrpContext,
724     IN PDCB Dcb
725     )
726 {
727     PIRP Irp = IrpContext->OriginatingIrp;
728     PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation( Irp ) ;
729     PVCB Vcb;
730     PFCB Fcb;
731     PCCB Ccb;
732 
733     PFILE_OBJECT ToCheck[3];
734     ULONG Index = 0;
735 
736     PAGED_CODE();
737 
738     //
739     //  While mounting, we're OK without having to own anything.
740     //
741 
742     if (Stack->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
743         Stack->MinorFunction == IRP_MN_MOUNT_VOLUME) {
744 
745         return TRUE;
746     }
747 
748     //
749     //  With the Vcb held, the close path is blocked out.
750     //
751 
752     if (ExIsResourceAcquiredSharedLite( &Dcb->Vcb->Resource ) ||
753         ExIsResourceAcquiredExclusiveLite( &Dcb->Vcb->Resource )) {
754 
755         return TRUE;
756     }
757 
758     //
759     //  Accept this assertion at face value.  It comes from GetDirentForFcbOrDcb,
760     //  and is reliable.
761     //
762 
763     if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD )) {
764 
765         return TRUE;
766     }
767 
768     //
769     //  Determine which fileobjects are around on this operation.
770     //
771 
772     if (Stack->MajorFunction == IRP_MJ_SET_INFORMATION &&
773         Stack->Parameters.SetFile.FileObject) {
774 
775         ToCheck[Index++] = Stack->Parameters.SetFile.FileObject;
776     }
777 
778     if (Stack->FileObject) {
779 
780         ToCheck[Index++] = Stack->FileObject;
781     }
782 
783     ToCheck[Index] = NULL;
784 
785     //
786     //  If the fileobjects we have are for this dcb or a child of it, we are
787     //  also guaranteed that this dcb isn't going anywhere (even without
788     //  the Vcb).
789     //
790 
791     for (Index = 0; ToCheck[Index] != NULL; Index++) {
792 
793         (VOID) FatDecodeFileObject( ToCheck[Index], &Vcb, &Fcb, &Ccb );
794 
795         while ( Fcb ) {
796 
797             if (Fcb == Dcb) {
798 
799                 return TRUE;
800             }
801 
802             Fcb = Fcb->ParentDcb;
803         }
804     }
805 
806     return FatDisableParentCheck;
807 }
808 #endif // DBG
809 
810 _Requires_lock_held_(_Global_critical_region_)
811 VOID
812 FatOpenDirectoryFile (
813     IN PIRP_CONTEXT IrpContext,
814     IN PDCB Dcb
815     )
816 
817 /*++
818 
819 Routine Description:
820 
821     This routine opens a new directory file if one is not already open.
822 
823 Arguments:
824 
825     Dcb - Pointer to the DCB for the directory
826 
827 Return Value:
828 
829     None.
830 
831 --*/
832 
833 {
834     PAGED_CODE();
835 
836     DebugTrace(+1, Dbg, "FatOpenDirectoryFile\n", 0);
837     DebugTrace( 0, Dbg, "Dcb = %p\n", Dcb);
838 
839     //
840     //  If we don't have some hold on this Dcb (there are several ways), there is nothing
841     //  to prevent child files from closing and tearing this branch of the tree down in the
842     //  midst of our slapping this reference onto it.
843     //
844     //  I really wish we had a proper Fcb synchronization model (like CDFS/UDFS/NTFS).
845     //
846 
847     NT_ASSERT( FatIsCurrentOperationSynchedForDcbTeardown( IrpContext, Dcb ));
848 
849     //
850     //  If we haven't yet set the correct AllocationSize, do so.
851     //
852 
853     if (Dcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
854 
855         FatLookupFileAllocationSize( IrpContext, Dcb );
856 
857         Dcb->Header.FileSize.LowPart =
858         Dcb->Header.AllocationSize.LowPart;
859     }
860 
861     //
862     //  Setup the Bitmap buffer if it is not big enough already
863     //
864 
865     FatCheckFreeDirentBitmap( IrpContext, Dcb );
866 
867     //
868     //  Check if we need to create a directory file.
869     //
870     //  We first do a spot check and then synchronize and check again.
871     //
872 
873     if (Dcb->Specific.Dcb.DirectoryFile == NULL) {
874 
875         PFILE_OBJECT DirectoryFileObject = NULL;
876 
877         FatAcquireDirectoryFileMutex( Dcb->Vcb );
878 
879         _SEH2_TRY {
880 
881             if (Dcb->Specific.Dcb.DirectoryFile == NULL) {
882 
883                 PDEVICE_OBJECT RealDevice;
884 
885                 //
886                 //  Create the special file object for the directory file, and set
887                 //  up its pointers back to the Dcb and the section object pointer.
888                 //  Note that setting the DirectoryFile pointer in the Dcb has
889                 //  to be the last thing done.
890                 //
891                 //  Preallocate a close context since we have no Ccb for this object.
892                 //
893 
894                 RealDevice = Dcb->Vcb->CurrentDevice;
895 
896                 DirectoryFileObject = IoCreateStreamFileObject( NULL, RealDevice );
897                 FatPreallocateCloseContext( Dcb->Vcb);
898 
899                 FatSetFileObject( DirectoryFileObject,
900                                   DirectoryFile,
901                                   Dcb,
902                                   NULL );
903 
904                 //
905                 //  Remember this internal open.
906                 //
907 
908                 InterlockedIncrement( (LONG*)&(Dcb->Vcb->InternalOpenCount) );
909 
910                 //
911                 //  If this is the root directory, it is also a residual open.
912                 //
913 
914                 if (NodeType( Dcb ) == FAT_NTC_ROOT_DCB) {
915 
916                     InterlockedIncrement( (LONG*)&(Dcb->Vcb->ResidualOpenCount) );
917                 }
918 
919                 DirectoryFileObject->SectionObjectPointer = &Dcb->NonPaged->SectionObjectPointers;
920 
921                 DirectoryFileObject->ReadAccess = TRUE;
922                 DirectoryFileObject->WriteAccess = TRUE;
923                 DirectoryFileObject->DeleteAccess = TRUE;
924 
925                 InterlockedIncrement( (LONG*)&Dcb->Specific.Dcb.DirectoryFileOpenCount );
926 
927                 Dcb->Specific.Dcb.DirectoryFile = DirectoryFileObject;
928 
929                 //
930                 //  Indicate we're happy with the fileobject now.
931                 //
932 
933                 DirectoryFileObject = NULL;
934             }
935 
936         } _SEH2_FINALLY {
937 
938             FatReleaseDirectoryFileMutex( Dcb->Vcb );
939 
940             //
941             //  Rip the object up if we couldn't get the close context.
942             //
943 
944             if (DirectoryFileObject) {
945 
946                 ObDereferenceObject( DirectoryFileObject );
947             }
948         } _SEH2_END;
949     }
950 
951     //
952     //  Finally check if we need to initialize the Cache Map for the
953     //  directory file.  The size of the section we are going to map
954     //  the current allocation size for the directory.  Note that the
955     //  cache manager will provide syncronization for us.
956     //
957 
958     if ( Dcb->Specific.Dcb.DirectoryFile->PrivateCacheMap == NULL ) {
959 
960         Dcb->Header.ValidDataLength = FatMaxLarge;
961         Dcb->ValidDataToDisk = MAXULONG;
962 
963         FatInitializeCacheMap( Dcb->Specific.Dcb.DirectoryFile,
964                                (PCC_FILE_SIZES)&Dcb->Header.AllocationSize,
965                                TRUE,
966                                &FatData.CacheManagerNoOpCallbacks,
967                                Dcb );
968     }
969 
970     DebugTrace(-1, Dbg, "FatOpenDirectoryFile -> VOID\n", 0);
971 
972     return;
973 }
974 
975 
976 
977 
978 PFILE_OBJECT
979 FatOpenEaFile (
980     IN PIRP_CONTEXT IrpContext,
981     IN PFCB EaFcb
982     )
983 
984 /*++
985 
986 Routine Description:
987 
988     This routine opens the Ea file.
989 
990 Arguments:
991 
992     EaFcb - Pointer to the Fcb for the Ea file.
993 
994 Return Value:
995 
996     Pointer to the new file object.
997 
998 --*/
999 
1000 {
1001     PFILE_OBJECT EaFileObject = NULL;
1002     PDEVICE_OBJECT RealDevice;
1003 
1004     PAGED_CODE();
1005 
1006     DebugTrace(+1, Dbg, "FatOpenEaFile\n", 0);
1007     DebugTrace( 0, Dbg, "EaFcb = %p\n", EaFcb);
1008 
1009     //
1010     //  Create the special file object for the ea file, and set
1011     //  up its pointers back to the Fcb and the section object pointer
1012     //
1013 
1014     RealDevice = EaFcb->Vcb->CurrentDevice;
1015 
1016     EaFileObject = IoCreateStreamFileObject( NULL, RealDevice );
1017 
1018     _SEH2_TRY {
1019 
1020         FatPreallocateCloseContext( IrpContext->Vcb);
1021 
1022         FatSetFileObject( EaFileObject,
1023                           EaFile,
1024                           EaFcb,
1025                           NULL );
1026 
1027         //
1028         //  Remember this internal, residual open.
1029         //
1030 
1031         InterlockedIncrement( (LONG*)&(EaFcb->Vcb->InternalOpenCount) );
1032         InterlockedIncrement( (LONG*)&(EaFcb->Vcb->ResidualOpenCount) );
1033 
1034         EaFileObject->SectionObjectPointer = &EaFcb->NonPaged->SectionObjectPointers;
1035 
1036         EaFileObject->ReadAccess = TRUE;
1037         EaFileObject->WriteAccess = TRUE;
1038 
1039         //
1040         //  Finally check if we need to initialize the Cache Map for the
1041         //  ea file.  The size of the section we are going to map
1042         //  the current allocation size for the Fcb.
1043         //
1044 
1045         EaFcb->Header.ValidDataLength = FatMaxLarge;
1046 
1047         FatInitializeCacheMap( EaFileObject,
1048                                (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize,
1049                                TRUE,
1050                                &FatData.CacheManagerCallbacks,
1051                                EaFcb );
1052 
1053         CcSetAdditionalCacheAttributes( EaFileObject, TRUE, TRUE );
1054 
1055     } _SEH2_FINALLY {
1056 
1057         //
1058         //  Drop the fileobject if we're raising.  Two cases: couldn't get
1059         //  the close context, and it is still an UnopenedFileObject, or
1060         //  we lost trying to build the cache map - in which case we're
1061         //  OK for the close context if we have to.
1062         //
1063 
1064         if (_SEH2_AbnormalTermination()) {
1065 
1066             ObDereferenceObject( EaFileObject );
1067         }
1068     } _SEH2_END;
1069 
1070     DebugTrace(-1, Dbg, "FatOpenEaFile -> %p\n", EaFileObject);
1071 
1072     UNREFERENCED_PARAMETER( IrpContext );
1073 
1074     return EaFileObject;
1075 }
1076 
1077 
1078 VOID
1079 FatCloseEaFile (
1080     IN PIRP_CONTEXT IrpContext,
1081     IN PVCB Vcb,
1082     IN BOOLEAN FlushFirst
1083     )
1084 
1085 /*++
1086 
1087 Routine Description:
1088 
1089     This routine shuts down the ea file.  Usually this is required when the volume
1090     begins to leave the system: after verify, dismount, deletion, pnp.
1091 
1092 Arguments:
1093 
1094     Vcb - the volume to close the ea file on
1095 
1096     FlushFirst - whether the file should be flushed
1097 
1098 Return Value:
1099 
1100     None. As a side effect, the EA fileobject in the Vcb is cleared.
1101 
1102     Caller must have the Vcb exclusive.
1103 
1104 --*/
1105 
1106 {
1107     PFILE_OBJECT EaFileObject = Vcb->VirtualEaFile;
1108 
1109     PAGED_CODE();
1110 
1111     DebugTrace(+1, Dbg, "FatCloseEaFile\n", 0);
1112     DebugTrace( 0, Dbg, "Vcb = %p\n", Vcb);
1113 
1114     NT_ASSERT( FatVcbAcquiredExclusive(IrpContext, Vcb) );
1115 
1116     if (EaFileObject != NULL) {
1117 
1118         EaFileObject = Vcb->VirtualEaFile;
1119 
1120         if (FlushFirst) {
1121 
1122             CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
1123         }
1124 
1125         Vcb->VirtualEaFile = NULL;
1126 
1127         //
1128         //  Empty the Mcb for the Ea file.
1129         //
1130 
1131         FatRemoveMcbEntry( Vcb, &Vcb->EaFcb->Mcb, 0, 0xFFFFFFFF );
1132 
1133         //
1134         //  Uninitialize the cache for this file object and dereference it.
1135         //
1136 
1137         FatSyncUninitializeCacheMap( IrpContext, EaFileObject );
1138 
1139         ObDereferenceObject( EaFileObject );
1140     }
1141 
1142     DebugTrace(-1, Dbg, "FatCloseEaFile -> %p\n", EaFileObject);
1143 }
1144 
1145 
1146 _Requires_lock_held_(_Global_critical_region_)
1147 VOID
1148 FatSetDirtyBcb (
1149     IN PIRP_CONTEXT IrpContext,
1150     IN PBCB Bcb,
1151     IN PVCB Vcb OPTIONAL,
1152     IN BOOLEAN Reversible
1153     )
1154 
1155 /*++
1156 
1157 Routine Description:
1158 
1159     This routine saves a reference to the bcb in the irp context and
1160     sets the bcb dirty.  This will have the affect of keeping the page in
1161     memory until we complete the request
1162 
1163     In addition, a DPC is set to fire in 5 seconds (or if one is pending,
1164     pushed back 5 seconds) to mark the volume clean.
1165 
1166 Arguments:
1167 
1168     Bcb - Supplies the Bcb being set dirty
1169 
1170     Vcb - Supplies the volume being marked dirty
1171 
1172     Reversible - Supplies TRUE if the specified range of bcb should be repinned
1173         so that the changes can be reversed in a controlled fashion if errors
1174         are encountered.
1175 
1176 Return Value:
1177 
1178     None.
1179 
1180 --*/
1181 
1182 {
1183     DebugTrace(+1, Dbg, "FatSetDirtyBcb\n", 0 );
1184     DebugTrace( 0, Dbg, "IrpContext = %p\n", IrpContext );
1185     DebugTrace( 0, Dbg, "Bcb        = %p\n", Bcb );
1186     DebugTrace( 0, Dbg, "Vcb        = %p\n", Vcb );
1187 
1188     //
1189     //  Repin the bcb as required
1190     //
1191 
1192     if (Reversible) {
1193 
1194         FatRepinBcb( IrpContext, Bcb );
1195     }
1196 
1197     //
1198     //  Set the bcb dirty
1199     //
1200 
1201     CcSetDirtyPinnedData( Bcb, NULL );
1202 
1203     //
1204     //  If volume dirtying isn't disabled for this operation (for
1205     //  instance, when we're changing the dirty state), set the
1206     //  volume dirty if we were given a Vcb that we want to perform
1207     //  clean volume processing on, and return.
1208     //
1209     //  As a historical note, we used to key off of the old floppy
1210     //  (now deferred flush) bit to disable dirtying behavior.  Since
1211     //  hotpluggable media can still be yanked while operations are
1212     //  in flight, recognize that its really the case that FAT12
1213     //  doesn't have the dirty bit.
1214     //
1215 
1216     if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_DIRTY) &&
1217          ARGUMENT_PRESENT(Vcb) &&
1218          !FatIsFat12(Vcb)) {
1219 
1220         KIRQL SavedIrql;
1221 
1222         BOOLEAN SetTimer;
1223 
1224         LARGE_INTEGER TimeSincePreviousCall;
1225         LARGE_INTEGER CurrentTime;
1226 
1227         //
1228         //  "Borrow" the irp context spinlock.
1229         //
1230 
1231         KeQuerySystemTime( &CurrentTime );
1232 
1233         KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql );
1234 
1235         TimeSincePreviousCall.QuadPart =
1236                 CurrentTime.QuadPart - Vcb->LastFatMarkVolumeDirtyCall.QuadPart;
1237 
1238         //
1239         //  If more than one second has elapsed since the prior call
1240         //  to here, bump the timer up again and see if we need to
1241         //  physically mark the volume dirty.
1242         //
1243 
1244         if ( (TimeSincePreviousCall.HighPart != 0) ||
1245              (TimeSincePreviousCall.LowPart > (1000 * 1000 * 10)) ) {
1246 
1247             SetTimer = TRUE;
1248 
1249         } else {
1250 
1251             SetTimer = FALSE;
1252         }
1253 
1254         KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql );
1255 
1256         if ( SetTimer ) {
1257 
1258             LARGE_INTEGER CleanVolumeTimer;
1259 
1260             //
1261             //  We use a shorter volume clean timer for hot plug volumes.
1262             //
1263 
1264             CleanVolumeTimer.QuadPart = FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH)
1265                                            ? (LONG)-1500*1000*10
1266                                            : (LONG)-8*1000*1000*10;
1267 
1268             (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer );
1269             (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc );
1270 
1271             //
1272             //  We have now synchronized with anybody clearing the dirty
1273             //  flag, so we can now see if we really have to actually write
1274             //  out the physical bit.
1275             //
1276 
1277             if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY) ) {
1278 
1279                 //
1280                 //  We want to really mark the volume dirty now.
1281                 //
1282 
1283                 if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
1284 
1285                     FatMarkVolume( IrpContext, Vcb, VolumeDirty );
1286                 }
1287 
1288                 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
1289 
1290                 //
1291                 //  Lock the volume if it is removable.
1292                 //
1293 
1294                 if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
1295 
1296                     FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE );
1297                 }
1298             }
1299 
1300             KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql );
1301 
1302             KeQuerySystemTime( &Vcb->LastFatMarkVolumeDirtyCall );
1303 
1304             KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql );
1305 
1306             KeSetTimer( &Vcb->CleanVolumeTimer,
1307                         CleanVolumeTimer,
1308                         &Vcb->CleanVolumeDpc );
1309         }
1310     }
1311 
1312     DebugTrace(-1, Dbg, "FatSetDirtyBcb -> VOID\n", 0 );
1313 }
1314 
1315 
1316 VOID
1317 FatRepinBcb (
1318     IN PIRP_CONTEXT IrpContext,
1319     IN PBCB Bcb
1320     )
1321 
1322 /*++
1323 
1324 Routine Description:
1325 
1326     This routine saves a reference to the bcb in the irp context. This will
1327     have the affect of keeping the page in memory until we complete the
1328     request
1329 
1330 Arguments:
1331 
1332     Bcb - Supplies the Bcb being referenced
1333 
1334 Return Value:
1335 
1336     None.
1337 
1338 --*/
1339 
1340 {
1341     PREPINNED_BCBS Repinned;
1342     ULONG i;
1343 
1344     PAGED_CODE();
1345 
1346     DebugTrace(+1, Dbg, "FatRepinBcb\n", 0 );
1347     DebugTrace( 0, Dbg, "IrpContext = %p\n", IrpContext );
1348     DebugTrace( 0, Dbg, "Bcb        = %p\n", Bcb );
1349 
1350     //
1351     //  The algorithm is to search the list of repinned records until
1352     //  we either find a match for the bcb or we find a null slot.
1353     //
1354 
1355     Repinned = &IrpContext->Repinned;
1356 
1357     while (TRUE) {
1358 
1359         //
1360         //  For every entry in the repinned record check if the bcb's
1361         //  match or if the entry is null.  If the bcb's match then
1362         //  we've done because we've already repinned this bcb, if
1363         //  the entry is null then we know, because it's densely packed,
1364         //  that the bcb is not in the list so add it to the repinned
1365         //  record and repin it.
1366         //
1367 
1368         for (i = 0; i < REPINNED_BCBS_ARRAY_SIZE; i += 1) {
1369 
1370             if (Repinned->Bcb[i] == Bcb) {
1371 
1372                 DebugTrace(-1, Dbg, "FatRepinBcb -> VOID\n", 0 );
1373                 return;
1374             }
1375 
1376             if (Repinned->Bcb[i] == NULL) {
1377 
1378                 Repinned->Bcb[i] = Bcb;
1379                 CcRepinBcb( Bcb );
1380 
1381                 DebugTrace(-1, Dbg, "FatRepinBcb -> VOID\n", 0 );
1382                 return;
1383             }
1384         }
1385 
1386         //
1387         //  We finished checking one repinned record so now locate the next
1388         //  repinned record,  If there isn't one then allocate and zero out
1389         //  a new one.
1390         //
1391 
1392         if (Repinned->Next == NULL) {
1393 
1394             Repinned->Next = FsRtlAllocatePoolWithTag( PagedPool,
1395                                                        sizeof(REPINNED_BCBS),
1396                                                        TAG_REPINNED_BCB );
1397 
1398             RtlZeroMemory( Repinned->Next, sizeof(REPINNED_BCBS) );
1399         }
1400 
1401         Repinned = Repinned->Next;
1402     }
1403 }
1404 
1405 
1406 VOID
1407 FatUnpinRepinnedBcbs (
1408     IN PIRP_CONTEXT IrpContext
1409     )
1410 
1411 /*++
1412 
1413 Routine Description:
1414 
1415     This routine frees all of the repinned bcbs, stored in an IRP context.
1416 
1417 Arguments:
1418 
1419 Return Value:
1420 
1421     None.
1422 
1423 --*/
1424 
1425 {
1426     IO_STATUS_BLOCK RaiseIosb;
1427     PREPINNED_BCBS Repinned;
1428     BOOLEAN WriteThroughToDisk;
1429     PFILE_OBJECT FileObject = NULL;
1430     BOOLEAN ForceVerify = FALSE;
1431     ULONG i;
1432     PFCB FcbOrDcb = NULL;
1433 
1434     PAGED_CODE();
1435 
1436     DebugTrace(+1, Dbg, "FatUnpinRepinnedBcbs\n", 0 );
1437     DebugTrace( 0, Dbg, "IrpContext = %p\n", IrpContext );
1438 
1439     //
1440     //  The algorithm for this procedure is to scan the entire list of
1441     //  repinned records unpinning any repinned bcbs.  We start off
1442     //  with the first record in the irp context, and while there is a
1443     //  record to scan we do the following loop.
1444     //
1445 
1446     Repinned = &IrpContext->Repinned;
1447     RaiseIosb.Status = STATUS_SUCCESS;
1448 
1449     //
1450     //  WinSE bug #307418 "Occasional data corruption when
1451     //  standby/resume while copying files to removable FAT
1452     //  formatted media".
1453     //  Extract main FCB pointer from the irp context - we
1454     //  will need it later to detect new file creation operation.
1455     //
1456 
1457     if (IrpContext->MajorFunction == IRP_MJ_CREATE &&
1458         IrpContext->OriginatingIrp != NULL) {
1459         PIO_STACK_LOCATION IrpSp;
1460 
1461         IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
1462 
1463         if (IrpSp != NULL &&
1464             IrpSp->FileObject != NULL &&
1465             IrpSp->FileObject->FsContext != NULL) {
1466 
1467             FcbOrDcb = IrpSp->FileObject->FsContext;
1468         }
1469     }
1470 
1471     //
1472     //  If the request is write through or the media is deferred flush,
1473     //  unpin the bcb's write through.
1474     //
1475 
1476     WriteThroughToDisk = (BOOLEAN) (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH) &&
1477                                     IrpContext->Vcb != NULL &&
1478                                     (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH) ||
1479                                      FlagOn(IrpContext->Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH)));
1480 
1481     while (Repinned != NULL) {
1482 
1483         //
1484         //  For every non-null entry in the repinned record unpin the
1485         //  repinned entry.
1486         //
1487         //  If the this is removable media (therefore all requests write-
1488         //  through) and the write fails, purge the cache so that we throw
1489         //  away the modifications as we will be returning an error to the
1490         //  user.
1491         //
1492 
1493         for (i = 0; i < REPINNED_BCBS_ARRAY_SIZE; i += 1) {
1494 
1495             if (Repinned->Bcb[i] != NULL) {
1496 
1497                 IO_STATUS_BLOCK Iosb;
1498 
1499                 if (WriteThroughToDisk &&
1500                     FlagOn(IrpContext->Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH)) {
1501 
1502                     FileObject = CcGetFileObjectFromBcb( Repinned->Bcb[i] );
1503                 }
1504 
1505                 CcUnpinRepinnedBcb( Repinned->Bcb[i],
1506                                     WriteThroughToDisk,
1507                                     &Iosb );
1508 
1509                 if (!NT_SUCCESS(Iosb.Status)) {
1510 
1511                     if (RaiseIosb.Status == STATUS_SUCCESS) {
1512 
1513                         RaiseIosb = Iosb;
1514                     }
1515 
1516                     //
1517                     //  If this was a writethrough device, purge the cache,
1518                     //  except for Irp major codes that either don't handle
1519                     //  the error paths correctly or are simple victims like
1520                     //  cleanup.c.
1521                     //
1522 
1523                     if (FileObject &&
1524                         (IrpContext->MajorFunction != IRP_MJ_CLEANUP) &&
1525                         (IrpContext->MajorFunction != IRP_MJ_FLUSH_BUFFERS) &&
1526                         (IrpContext->MajorFunction != IRP_MJ_SET_INFORMATION)
1527 
1528                                         &&
1529 
1530                             //
1531                             //  WinSE bug #307418 "Occasional data corruption when
1532                             //  standby/resume while copying files to removable FAT
1533                             //  formatted media".
1534                             //  Buffer unpinning for new file creation operation can
1535                             //  be interrupted by system syspend. As a result some BCBs
1536                             //  will be successfully written to the disk while others will
1537                             //  be kicked back with STATUS_VERIFY_REQUIRED. Since there is
1538                             //  is still a chance for the failed BCBs to reach the disk
1539                             //  after the volume verification we'll not purge them.
1540                             //  Instead FatCommonCreate() will unroll the file creation
1541                             //  changes for these pages.
1542                             //
1543 
1544                         !(IrpContext->MajorFunction == IRP_MJ_CREATE &&
1545                           Iosb.Status == STATUS_VERIFY_REQUIRED &&
1546                           FcbOrDcb != NULL &&
1547                           NodeType( FcbOrDcb ) == FAT_NTC_FCB)) {
1548 
1549                         //
1550                         //  The call to CcPurgeCacheSection() below will
1551                         //  purge the entire file from memory.  It will also
1552                         //  block until all the file's BCB's are pinned.
1553                         //
1554                         //  We end up in a deadlock situation of there
1555                         //  are any other pinned BCB's in this IRP context
1556                         //  so the first thing we do is search the list
1557                         //  for BCB's pinned in the same file and unpin
1558                         //  them.
1559                         //
1560                         //  We are probably not going to lose data because
1561                         //  it's safe to assume that all flushes will
1562                         //  fail after the first one fails.
1563                         //
1564 
1565                         ULONG j;
1566                         ULONG k = i + 1;
1567                         PREPINNED_BCBS RepinnedToPurge = Repinned;
1568 
1569                         while( RepinnedToPurge != NULL ) {
1570 
1571                             for (j = k; j < REPINNED_BCBS_ARRAY_SIZE; j++) {
1572 
1573                                 if (RepinnedToPurge->Bcb[j] != NULL) {
1574 
1575                                     if (CcGetFileObjectFromBcb( RepinnedToPurge->Bcb[j] ) == FileObject) {
1576 
1577                                         CcUnpinRepinnedBcb( RepinnedToPurge->Bcb[j],
1578                                                             FALSE,
1579                                                             &Iosb );
1580 
1581                                         RepinnedToPurge->Bcb[j] = NULL;
1582                                     }
1583                                 }
1584                             }
1585 
1586                             RepinnedToPurge = RepinnedToPurge->Next;
1587                             k = 0;
1588                         }
1589 
1590                         CcPurgeCacheSection( FileObject->SectionObjectPointer,
1591                                              NULL,
1592                                              0,
1593                                              FALSE );
1594 
1595                         //
1596                         //  Force a verify operation here since who knows
1597                         //  what state things are in.
1598                         //
1599 
1600                         ForceVerify = TRUE;
1601                     }
1602                 }
1603 
1604                 Repinned->Bcb[i] = NULL;
1605 
1606             }
1607         }
1608 
1609         //
1610         //  Now find the next repinned record in the list, and possibly
1611         //  delete the one we've just processed.
1612         //
1613 
1614         if (Repinned != &IrpContext->Repinned) {
1615 
1616             PREPINNED_BCBS Saved;
1617 
1618             Saved = Repinned->Next;
1619             ExFreePool( Repinned );
1620             Repinned = Saved;
1621 
1622         } else {
1623 
1624             Repinned = Repinned->Next;
1625             IrpContext->Repinned.Next = NULL;
1626         }
1627     }
1628 
1629     //
1630     //  Now if we weren't completely successful in the our unpin
1631     //  then raise the iosb we got
1632     //
1633 
1634     if (!NT_SUCCESS(RaiseIosb.Status)) {
1635 
1636         if (ForceVerify && FileObject) {
1637 
1638             SetFlag(FileObject->DeviceObject->Flags, DO_VERIFY_VOLUME);
1639 
1640             IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp,
1641                                           FileObject->DeviceObject );
1642         }
1643 
1644         if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE )) {
1645             if (IrpContext->OriginatingIrp) {
1646                 IrpContext->OriginatingIrp->IoStatus = RaiseIosb;
1647             }
1648             FatNormalizeAndRaiseStatus( IrpContext, RaiseIosb.Status );
1649         }
1650     }
1651 
1652     DebugTrace(-1, Dbg, "FatUnpinRepinnedBcbs -> VOID\n", 0 );
1653 
1654     return;
1655 }
1656 
1657 
1658 FINISHED
1659 FatZeroData (
1660     IN PIRP_CONTEXT IrpContext,
1661     IN PVCB Vcb,
1662     IN PFILE_OBJECT FileObject,
1663     IN ULONG StartingZero,
1664     IN ULONG ByteCount
1665     )
1666 
1667 /*++
1668 
1669     **** Temporary function - Remove when CcZeroData is capable of handling
1670     non sector aligned requests.
1671 
1672 --*/
1673 {
1674 #ifndef __REACTOS__
1675     LARGE_INTEGER ZeroStart = {0,0};
1676     LARGE_INTEGER BeyondZeroEnd = {0,0};
1677 #else
1678     LARGE_INTEGER ZeroStart = {{0,0}};
1679     LARGE_INTEGER BeyondZeroEnd = {{0,0}};
1680 #endif
1681 
1682     ULONG SectorSize;
1683 
1684     BOOLEAN Finished;
1685 
1686     PAGED_CODE();
1687 
1688     SectorSize = (ULONG)Vcb->Bpb.BytesPerSector;
1689 
1690     ZeroStart.LowPart = (StartingZero + (SectorSize - 1)) & ~(SectorSize - 1);
1691 
1692     //
1693     //  Detect overflow if we were asked to zero in the last sector of the file,
1694     //  which must be "zeroed" already (or we're in trouble).
1695     //
1696 
1697     if (StartingZero != 0 && ZeroStart.LowPart == 0) {
1698 
1699         return TRUE;
1700     }
1701 
1702     //
1703     //  Note that BeyondZeroEnd can take the value 4gb.
1704     //
1705 
1706     BeyondZeroEnd.QuadPart = ((ULONGLONG) StartingZero + ByteCount + (SectorSize - 1))
1707                              & (~((LONGLONG) SectorSize - 1));
1708 
1709     //
1710     //  If we were called to just zero part of a sector we are in trouble.
1711     //
1712 
1713     if ( ZeroStart.QuadPart == BeyondZeroEnd.QuadPart ) {
1714 
1715         return TRUE;
1716     }
1717 
1718     Finished = CcZeroData( FileObject,
1719                            &ZeroStart,
1720                            &BeyondZeroEnd,
1721                            BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
1722 
1723     return Finished;
1724 }
1725 
1726 
1727 NTSTATUS
1728 FatCompleteMdl (
1729     IN PIRP_CONTEXT IrpContext,
1730     IN PIRP Irp
1731     )
1732 
1733 /*++
1734 
1735 Routine Description:
1736 
1737     This routine performs the function of completing Mdl read and write
1738     requests.  It should be called only from FatFsdRead and FatFsdWrite.
1739 
1740 Arguments:
1741 
1742     Irp - Supplies the originating Irp.
1743 
1744 Return Value:
1745 
1746     NTSTATUS - Will always be STATUS_PENDING or STATUS_SUCCESS.
1747 
1748 --*/
1749 
1750 {
1751     PFILE_OBJECT FileObject;
1752     PIO_STACK_LOCATION IrpSp;
1753 
1754     PAGED_CODE();
1755 
1756     DebugTrace(+1, Dbg, "FatCompleteMdl\n", 0 );
1757     DebugTrace( 0, Dbg, "IrpContext = %p\n", IrpContext );
1758     DebugTrace( 0, Dbg, "Irp        = %p\n", Irp );
1759 
1760     //
1761     // Do completion processing.
1762     //
1763 
1764     FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject;
1765 
1766     switch( IrpContext->MajorFunction ) {
1767 
1768     case IRP_MJ_READ:
1769 
1770         CcMdlReadComplete( FileObject, Irp->MdlAddress );
1771         break;
1772 
1773     case IRP_MJ_WRITE:
1774 
1775         IrpSp = IoGetCurrentIrpStackLocation( Irp );
1776 
1777         NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ));
1778 
1779         CcMdlWriteComplete( FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress );
1780 
1781         Irp->IoStatus.Status = STATUS_SUCCESS;
1782 
1783         break;
1784 
1785     default:
1786 
1787         DebugTrace( DEBUG_TRACE_ERROR, 0, "Illegal Mdl Complete.\n", 0);
1788 #ifdef _MSC_VER
1789 #pragma prefast( suppress: 28159, "we're very broken if we get here" )
1790 #endif
1791         FatBugCheck( IrpContext->MajorFunction, 0, 0 );
1792     }
1793 
1794     //
1795     // Mdl is now deallocated.
1796     //
1797 
1798     Irp->MdlAddress = NULL;
1799 
1800     //
1801     // Complete the request and exit right away.
1802     //
1803 
1804     FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
1805 
1806     DebugTrace(-1, Dbg, "FatCompleteMdl -> STATUS_SUCCESS\n", 0 );
1807 
1808     return STATUS_SUCCESS;
1809 }
1810 
1811 VOID
1812 FatSyncUninitializeCacheMap (
1813     IN PIRP_CONTEXT IrpContext,
1814     IN PFILE_OBJECT FileObject
1815     )
1816 
1817 /*++
1818 
1819 Routine Description:
1820 
1821     The routine performs a CcUnitializeCacheMap to LargeZero synchronously.  That
1822     is it waits on the Cc event.  This call is useful when we want to be certain
1823     when a close will actually some in.
1824 
1825 Return Value:
1826 
1827     None.
1828 
1829 --*/
1830 
1831 {
1832     CACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent;
1833     NTSTATUS WaitStatus;
1834 
1835     UNREFERENCED_PARAMETER( IrpContext );
1836 
1837     PAGED_CODE();
1838 
1839     KeInitializeEvent( &UninitializeCompleteEvent.Event,
1840                        SynchronizationEvent,
1841                        FALSE);
1842 
1843     CcUninitializeCacheMap( FileObject,
1844                             &FatLargeZero,
1845                             &UninitializeCompleteEvent );
1846 
1847     //
1848     //  Now wait for the cache manager to finish purging the file.
1849     //  This will garentee that Mm gets the purge before we
1850     //  delete the Vcb.
1851     //
1852 
1853 #ifdef _MSC_VER
1854 #pragma prefast( suppress: 28931, "we use WaitStatus in the debug assert, in fre builds prefast complains it's unused" )
1855 #endif
1856     WaitStatus = KeWaitForSingleObject( &UninitializeCompleteEvent.Event,
1857                                         Executive,
1858                                         KernelMode,
1859                                         FALSE,
1860                                         NULL);
1861 
1862     NT_ASSERT(WaitStatus == STATUS_SUCCESS);
1863 }
1864 
1865 VOID
1866 FatPinMappedData (
1867     IN PIRP_CONTEXT IrpContext,
1868     IN PDCB Dcb,
1869     IN VBO StartingVbo,
1870     IN ULONG ByteCount,
1871     OUT PBCB *Bcb
1872     )
1873 
1874 /*++
1875 
1876 Routine Description:
1877 
1878     This routine pins data that was previously mapped before setting it dirty.
1879 
1880 Arguments:
1881 
1882     Dcb - Pointer to the DCB for the directory
1883 
1884     StartingVbo - The virtual offset of the first desired byte
1885 
1886     ByteCount - Number of bytes desired
1887 
1888     Bcb - Returns a pointer to the BCB which is valid until unpinned
1889 
1890 --*/
1891 
1892 {
1893     LARGE_INTEGER Vbo;
1894 
1895     PAGED_CODE();
1896 
1897     DebugTrace(+1, Dbg, "FatPinMappedData\n", 0);
1898     DebugTrace( 0, Dbg, "Dcb         = %p\n", Dcb);
1899     DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo);
1900     DebugTrace( 0, Dbg, "ByteCount   = %08lx\n", ByteCount);
1901 
1902     //
1903     //  Call the Cache manager to perform the operation.
1904     //
1905 
1906     Vbo.QuadPart = StartingVbo;
1907 
1908     if (!CcPinMappedData( Dcb->Specific.Dcb.DirectoryFile,
1909                           &Vbo,
1910                           ByteCount,
1911                           BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
1912                           Bcb )) {
1913 
1914         //
1915         // Could not pin the data without waiting (cache miss).
1916         //
1917 
1918         FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
1919     }
1920 
1921     DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID, *BCB = %p\n", *Bcb);
1922 
1923     return;
1924 }
1925 
1926 #if (NTDDI_VERSION >= NTDDI_WIN8)
1927 
1928 NTSTATUS
1929 FatPrefetchPages (
1930     IN PIRP_CONTEXT IrpContext,
1931     IN PFILE_OBJECT FileObject,
1932     IN ULONG StartingPage,
1933     IN ULONG PageCount
1934     )
1935 {
1936     IO_PRIORITY_INFO PriorityInformation = {0};
1937     MM_PREFETCH_FLAGS PrefetchFlags;
1938     ULONG PageNo;
1939     NTSTATUS Status;
1940 
1941     PREAD_LIST ReadList = NULL;
1942 
1943     UNREFERENCED_PARAMETER( IrpContext );
1944 
1945     PAGED_CODE();
1946 
1947     //
1948     //  Succeed zero page prefetch requests.
1949     //
1950 
1951     if (PageCount == 0) {
1952 
1953         return STATUS_SUCCESS;
1954     }
1955 
1956     //
1957     //  Mm's prefetch API's "only" support fetching a ULONG worth of pages.
1958     //  Make sure we don't overflow.
1959     //
1960 
1961     ASSERT( PageCount < (PFN_NUMBER)MAXULONG );
1962 
1963     IoInitializePriorityInfo( &PriorityInformation );
1964 
1965     Status = IoRetrievePriorityInfo( IrpContext->OriginatingIrp,
1966                                      FileObject,
1967                                      IrpContext->OriginatingIrp->Tail.Overlay.Thread,
1968                                      &PriorityInformation );
1969 
1970     if (!NT_SUCCESS( Status)) {
1971 
1972         goto Cleanup;
1973     }
1974 
1975     ReadList = ExAllocatePoolWithTag( PagedPool,
1976                                       FIELD_OFFSET( READ_LIST, List ) + PageCount * sizeof( FILE_SEGMENT_ELEMENT ),
1977                                       ' taF' );
1978 
1979     if (ReadList == NULL) {
1980 
1981         Status = STATUS_INSUFFICIENT_RESOURCES;
1982         goto Cleanup;
1983     }
1984 
1985     //
1986     //  Call Mm to prefetch data.
1987     //
1988 
1989     ReadList->FileObject = FileObject;
1990     ReadList->IsImage = FALSE;
1991     ReadList->NumberOfEntries = PageCount;
1992 
1993     PrefetchFlags.AllFlags = 0;
1994     PrefetchFlags.Flags.Priority = PriorityInformation.PagePriority;
1995     PrefetchFlags.Flags.RepurposePriority = SYSTEM_PAGE_PRIORITY_LEVELS - 1;
1996     PrefetchFlags.Flags.PriorityProtection = 1;
1997     ReadList->List[0].Alignment = StartingPage * PAGE_SIZE;
1998     ReadList->List[0].Alignment |= PrefetchFlags.AllFlags;
1999 
2000     for (PageNo = 1; PageNo < PageCount; PageNo++) {
2001 
2002         ReadList->List[PageNo].Alignment = ReadList->List[PageNo-1].Alignment + PAGE_SIZE;
2003     }
2004 
2005     Status = MmPrefetchPages( 1, &ReadList );
2006 
2007 Cleanup:
2008 
2009     if (ReadList != NULL) {
2010 
2011         ExFreePoolWithTag( ReadList, ' taF' );
2012     }
2013 
2014     return Status;
2015 }
2016 #endif
2017 
2018