1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     StrucSup.c
8 
9 Abstract:
10 
11     This module implements the Fat in-memory data structure manipulation
12     routines
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_STRUCSUP)
24 
25 //
26 //  The debug trace level
27 //
28 
29 #define Dbg                              (DEBUG_TRACE_STRUCSUP)
30 
31 #define FillMemory(BUF,SIZ,MASK) {                          \
32     ULONG i;                                                \
33     for (i = 0; i < (((SIZ)/4) - 1); i += 2) {              \
34         ((PULONG)(BUF))[i] = (MASK);                        \
35         ((PULONG)(BUF))[i+1] = (ULONG)PsGetCurrentThread(); \
36     }                                                       \
37 }
38 
39 #define IRP_CONTEXT_HEADER (sizeof( IRP_CONTEXT ) * 0x10000 + FAT_NTC_IRP_CONTEXT)
40 
41 //
42 //  Local macros.
43 //
44 //  Define our lookaside list allocators.  For the time being, and perhaps
45 //  permanently, the paged structures don't come off of lookasides.  This
46 //  is due to complications with clean unload as FAT can be in the paging
47 //  path, making it really hard to find the right time to empty them.
48 //
49 //  Fortunately, the hit rates on the Fcb/Ccb lists weren't stunning.
50 //
51 
52 #define FAT_FILL_FREE 0
53 
54 #ifdef __REACTOS__
55 static
56 #endif
57 INLINE
58 PCCB
FatAllocateCcb()59 FatAllocateCcb (
60     )
61 {
62     return (PCCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(CCB), TAG_CCB );
63 }
64 
65 #ifdef __REACTOS__
66 static
67 #endif
68 INLINE
69 VOID
FatFreeCcb(IN PCCB Ccb)70 FatFreeCcb (
71     IN PCCB Ccb
72     )
73 {
74 #if FAT_FILL_FREE
75     RtlFillMemoryUlong(Ccb, sizeof(CCB), FAT_FILL_FREE);
76 #endif
77 
78     ExFreePool( Ccb );
79 }
80 
81 #ifdef __REACTOS__
82 static
83 #endif
84 INLINE
85 PFCB
FatAllocateFcb()86 FatAllocateFcb (
87     )
88 {
89     return (PFCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(FCB), TAG_FCB );
90 }
91 
92 #ifdef __REACTOS__
93 static
94 #endif
95 INLINE
96 VOID
FatFreeFcb(IN PFCB Fcb)97 FatFreeFcb (
98     IN PFCB Fcb
99     )
100 {
101 #if FAT_FILL_FREE
102     RtlFillMemoryUlong(Fcb, sizeof(FCB), FAT_FILL_FREE);
103 #endif
104 
105     ExFreePool( Fcb );
106 }
107 
108 #ifdef __REACTOS__
109 static
110 #endif
111 INLINE
112 PNON_PAGED_FCB
FatAllocateNonPagedFcb()113 FatAllocateNonPagedFcb (
114     )
115 {
116     return (PNON_PAGED_FCB) ExAllocateFromNPagedLookasideList( &FatNonPagedFcbLookasideList );
117 }
118 
119 #ifdef __REACTOS__
120 static
121 #endif
122 INLINE
123 VOID
FatFreeNonPagedFcb(PNON_PAGED_FCB NonPagedFcb)124 FatFreeNonPagedFcb (
125     PNON_PAGED_FCB NonPagedFcb
126     )
127 {
128 #if FAT_FILL_FREE
129     RtlFillMemoryUlong(NonPagedFcb, sizeof(NON_PAGED_FCB), FAT_FILL_FREE);
130 #endif
131 
132     ExFreeToNPagedLookasideList( &FatNonPagedFcbLookasideList, (PVOID) NonPagedFcb );
133 }
134 
135 #ifdef __REACTOS__
136 static
137 #endif
138 INLINE
139 PERESOURCE
FatAllocateResource()140 FatAllocateResource (
141     )
142 {
143     PERESOURCE Resource;
144 
145     Resource = (PERESOURCE) ExAllocateFromNPagedLookasideList( &FatEResourceLookasideList );
146 
147     ExInitializeResourceLite( Resource );
148 
149     return Resource;
150 }
151 
152 #ifdef __REACTOS__
153 static
154 #endif
155 INLINE
156 VOID
FatFreeResource(IN PERESOURCE Resource)157 FatFreeResource (
158     IN PERESOURCE Resource
159     )
160 {
161     ExDeleteResourceLite( Resource );
162 
163 #if FAT_FILL_FREE
164     RtlFillMemoryUlong(Resource, sizeof(ERESOURCE), FAT_FILL_FREE);
165 #endif
166 
167     ExFreeToNPagedLookasideList( &FatEResourceLookasideList, (PVOID) Resource );
168 }
169 
170 #ifdef __REACTOS__
171 static
172 #endif
173 INLINE
174 PIRP_CONTEXT
FatAllocateIrpContext()175 FatAllocateIrpContext (
176     )
177 {
178     return (PIRP_CONTEXT) ExAllocateFromNPagedLookasideList( &FatIrpContextLookasideList );
179 }
180 
181 #ifdef __REACTOS__
182 static
183 #endif
184 INLINE
185 VOID
FatFreeIrpContext(IN PIRP_CONTEXT IrpContext)186 FatFreeIrpContext (
187     IN PIRP_CONTEXT IrpContext
188     )
189 {
190 #if FAT_FILL_FREE
191     RtlFillMemoryUlong(IrpContext, sizeof(IRP_CONTEXT), FAT_FILL_FREE);
192 #endif
193 
194     ExFreeToNPagedLookasideList( &FatIrpContextLookasideList, (PVOID) IrpContext );
195 }
196 
197 #ifdef ALLOC_PRAGMA
198 #pragma alloc_text(PAGE, FatInitializeVcb)
199 #pragma alloc_text(PAGE, FatTearDownVcb)
200 #pragma alloc_text(PAGE, FatDeleteVcb)
201 #pragma alloc_text(PAGE, FatCreateRootDcb)
202 #pragma alloc_text(PAGE, FatCreateFcb)
203 #pragma alloc_text(PAGE, FatCreateDcb)
204 #pragma alloc_text(PAGE, FatDeleteFcb)
205 #pragma alloc_text(PAGE, FatCreateCcb)
206 #pragma alloc_text(PAGE, FatDeallocateCcbStrings)
207 #pragma alloc_text(PAGE, FatDeleteCcb)
208 #pragma alloc_text(PAGE, FatGetNextFcbTopDown)
209 #pragma alloc_text(PAGE, FatGetNextFcbBottomUp)
210 #pragma alloc_text(PAGE, FatConstructNamesInFcb)
211 #pragma alloc_text(PAGE, FatCheckFreeDirentBitmap)
212 #pragma alloc_text(PAGE, FatCreateIrpContext)
213 #pragma alloc_text(PAGE, FatDeleteIrpContext_Real)
214 #pragma alloc_text(PAGE, FatIsHandleCountZero)
215 #pragma alloc_text(PAGE, FatAllocateCloseContext)
216 #pragma alloc_text(PAGE, FatPreallocateCloseContext)
217 #pragma alloc_text(PAGE, FatEnsureStringBufferEnough)
218 #pragma alloc_text(PAGE, FatFreeStringBuffer)
219 #pragma alloc_text(PAGE, FatScanForDataTrack)
220 #endif
221 
222 
_Requires_lock_held_(_Global_critical_region_)223 _Requires_lock_held_(_Global_critical_region_)
224 VOID
225 FatInitializeVcb (
226     IN PIRP_CONTEXT IrpContext,
227     IN OUT PVCB Vcb,
228     IN PDEVICE_OBJECT TargetDeviceObject,
229     IN PVPB Vpb,
230     IN PDEVICE_OBJECT FsDeviceObject
231     )
232 
233 /*++
234 
235 Routine Description:
236 
237     This routine initializes and inserts a new Vcb record into the in-memory
238     data structure.  The Vcb record "hangs" off the end of the Volume device
239     object and must be allocated by our caller.
240 
241 Arguments:
242 
243     Vcb - Supplies the address of the Vcb record being initialized.
244 
245     TargetDeviceObject - Supplies the address of the target device object to
246         associate with the Vcb record.
247 
248     Vpb - Supplies the address of the Vpb to associate with the Vcb record.
249 
250     FsDeviceObject - The filesystem device object that the mount was directed
251                      too.
252 
253 Return Value:
254 
255     None.
256 
257 --*/
258 
259 {
260     CC_FILE_SIZES FileSizes;
261     PDEVICE_OBJECT RealDevice;
262     ULONG i;
263 
264     STORAGE_HOTPLUG_INFO HotplugInfo;
265     STORAGE_DEVICE_NUMBER StorDeviceNumber;
266     NTSTATUS Status;
267 
268     //
269     //  The following variables are used for abnormal unwind
270     //
271 
272     PLIST_ENTRY UnwindEntryList = NULL;
273     PERESOURCE UnwindResource = NULL;
274     PERESOURCE UnwindResource2 = NULL;
275     PFILE_OBJECT UnwindFileObject = NULL;
276     PFILE_OBJECT UnwindCacheMap = NULL;
277     BOOLEAN UnwindWeAllocatedMcb = FALSE;
278     PFILE_SYSTEM_STATISTICS UnwindStatistics = NULL;
279     BOOLEAN UnwindWeAllocatedBadBlockMap = FALSE;
280     BOOLEAN CloseContextAllocated = FALSE;
281 
282     PAGED_CODE();
283     UNREFERENCED_PARAMETER( FsDeviceObject );
284 
285     DebugTrace(+1, Dbg, "FatInitializeVcb, Vcb = %p\n", Vcb);
286 
287     _SEH2_TRY {
288 
289         //
290         //  We start by first zeroing out all of the VCB, this will guarantee
291         //  that any stale data is wiped clean
292         //
293 
294         RtlZeroMemory( Vcb, sizeof(VCB) );
295 
296         //
297         //  Set the proper node type code and node byte size
298         //
299 
300         Vcb->VolumeFileHeader.NodeTypeCode = FAT_NTC_VCB;
301         Vcb->VolumeFileHeader.NodeByteSize = sizeof(VCB);
302 
303         //
304         //  Initialize the tunneling cache
305         //
306 
307         FsRtlInitializeTunnelCache(&Vcb->Tunnel);
308 
309         //
310         //  Insert this Vcb record on the FatData.VcbQueue
311         //
312 
313         NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
314 
315 
316 #ifdef _MSC_VER
317 #pragma prefast( push )
318 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
319 #pragma prefast( disable: 28193, "this will always wait" )
320 #endif
321 
322         (VOID)FatAcquireExclusiveGlobal( IrpContext );
323 
324 #ifdef _MSC_VER
325 #pragma prefast( pop )
326 #endif
327 
328         InsertTailList( &FatData.VcbQueue, &Vcb->VcbLinks );
329         FatReleaseGlobal( IrpContext );
330         UnwindEntryList = &Vcb->VcbLinks;
331 
332         //
333         //  Set the Target Device Object, Vpb, and Vcb State fields
334         //
335 
336 
337         ObReferenceObject( TargetDeviceObject );
338         Vcb->TargetDeviceObject = TargetDeviceObject;
339         Vcb->Vpb = Vpb;
340 
341         Vcb->CurrentDevice = Vpb->RealDevice;
342 
343         //
344         //  Set the removable media and defflush flags based on the storage
345         //  inquiry and the old characteristic bits.
346         //
347 
348         Status = FatPerformDevIoCtrl( IrpContext,
349                                       IOCTL_STORAGE_GET_HOTPLUG_INFO,
350                                       TargetDeviceObject,
351                                       NULL,
352                                       0,
353                                       &HotplugInfo,
354                                       sizeof(HotplugInfo),
355                                       FALSE,
356                                       TRUE,
357                                       NULL );
358 
359         if (NT_SUCCESS( Status )) {
360 
361             if (HotplugInfo.MediaRemovable) {
362 
363                 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA );
364             }
365 
366             //
367             //  If the media or device is hot-pluggable, then set this flag.
368             //
369 
370             if (HotplugInfo.MediaHotplug || HotplugInfo.DeviceHotplug) {
371 
372                 SetFlag( Vcb->VcbState, VCB_STATE_FLAG_HOTPLUGGABLE );
373             }
374 
375             if (!HotplugInfo.WriteCacheEnableOverride) {
376 
377                 //
378                 //  If the device or media is hotplug and the override is not
379                 //  set, force defflush behavior for the device.
380                 //
381 
382                 if (HotplugInfo.MediaHotplug || HotplugInfo.DeviceHotplug) {
383 
384                     NT_ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_HOTPLUGGABLE ));
385 
386                     SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH );
387 
388                 //
389                 //  Now, for removables that claim to be lockable, lob a lock
390                 //  request and see if it works.  There can unfortunately be
391                 //  transient, media dependent reasons that it can fail.  If
392                 //  it does not, we must force defflush on.
393                 //
394 
395                 } else if (HotplugInfo.MediaRemovable &&
396                            !HotplugInfo.MediaHotplug) {
397 
398                     Status = FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE );
399 
400                     if (!NT_SUCCESS( Status )) {
401 
402                         SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH );
403 
404                     }
405 
406                     (VOID)FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
407                 }
408             }
409         }
410 
411         if (FlagOn(Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA)) {
412 
413             SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA );
414         }
415 
416         //
417         //  Make sure we turn on deferred flushing for floppies like we always
418         //  have.
419         //
420 
421         if (FlagOn(Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) {
422 
423             SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH );
424         }
425 
426         //
427         //  Query the storage device number.
428         //
429 
430         Status = FatPerformDevIoCtrl( IrpContext,
431                                       IOCTL_STORAGE_GET_DEVICE_NUMBER,
432                                       TargetDeviceObject,
433                                       NULL,
434                                       0,
435                                       &StorDeviceNumber,
436                                       sizeof(StorDeviceNumber),
437                                       FALSE,
438                                       TRUE,
439                                       NULL );
440 
441         if (NT_SUCCESS( Status )) {
442 
443             Vcb->DeviceNumber = StorDeviceNumber.DeviceNumber;
444 
445         } else {
446 
447             Vcb->DeviceNumber = (ULONG)(-1);
448         }
449 
450         FatSetVcbCondition( Vcb, VcbGood);
451 
452         //
453         //  Initialize the resource variable for the Vcb
454         //
455 
456         ExInitializeResourceLite( &Vcb->Resource );
457         UnwindResource = &Vcb->Resource;
458 
459         ExInitializeResourceLite( &Vcb->ChangeBitMapResource );
460         UnwindResource2 = &Vcb->ChangeBitMapResource;
461 
462         //
463         //  Initialize the free cluster bitmap mutex.
464         //
465 
466         ExInitializeFastMutex( &Vcb->FreeClusterBitMapMutex );
467 
468         //
469         //  Create the special file object for the virtual volume file with a close
470         //  context, its pointers back to the Vcb and the section object pointer.
471         //
472         //  We don't have to unwind the close context.  That will happen in the close
473         //  path automatically.
474         //
475 
476         RealDevice = Vcb->CurrentDevice;
477 
478         FatPreallocateCloseContext(Vcb);
479         CloseContextAllocated = TRUE;
480 
481         Vcb->VirtualVolumeFile = UnwindFileObject = IoCreateStreamFileObject( NULL, RealDevice );
482 
483         FatSetFileObject( Vcb->VirtualVolumeFile,
484                           VirtualVolumeFile,
485                           Vcb,
486                           NULL );
487 
488         //
489         //  Remember this internal, residual open.
490         //
491 
492         InterlockedIncrement( (LONG*)&(Vcb->InternalOpenCount) );
493         InterlockedIncrement( (LONG*)&(Vcb->ResidualOpenCount) );
494 
495         Vcb->VirtualVolumeFile->SectionObjectPointer = &Vcb->SectionObjectPointers;
496 
497         Vcb->VirtualVolumeFile->ReadAccess = TRUE;
498         Vcb->VirtualVolumeFile->WriteAccess = TRUE;
499         Vcb->VirtualVolumeFile->DeleteAccess = TRUE;
500 
501         //
502         //  Initialize the notify structures.
503         //
504 
505         InitializeListHead( &Vcb->DirNotifyList );
506 
507         FsRtlNotifyInitializeSync( &Vcb->NotifySync );
508 
509         //
510         //  Initialize the Cache Map for the volume file.  The size is
511         //  initially set to that of our first read.  It will be extended
512         //  when we know how big the Fat is.
513         //
514 
515         FileSizes.AllocationSize.QuadPart =
516         FileSizes.FileSize.QuadPart = sizeof(PACKED_BOOT_SECTOR);
517         FileSizes.ValidDataLength = FatMaxLarge;
518 
519         FatInitializeCacheMap( Vcb->VirtualVolumeFile,
520                                &FileSizes,
521                                TRUE,
522                                &FatData.CacheManagerNoOpCallbacks,
523                                Vcb );
524 
525         UnwindCacheMap = Vcb->VirtualVolumeFile;
526 
527         //
528         //  Initialize the structure that will keep track of dirty fat sectors.
529         //  The largest possible Mcb structures are less than 1K, so we use
530         //  non paged pool.
531         //
532 
533         FsRtlInitializeLargeMcb( &Vcb->DirtyFatMcb, PagedPool );
534 
535         UnwindWeAllocatedMcb = TRUE;
536 
537         //
538         //  Initialize the structure that will keep track of bad clusters on the volume.
539         //
540         //  It will be empty until it is populated by FSCTL_GET_RETRIEVAL_POINTERS with a volume handle.
541         //
542 
543         FsRtlInitializeLargeMcb( &Vcb->BadBlockMcb, PagedPool );
544         UnwindWeAllocatedBadBlockMap = TRUE;
545 
546         //
547         //  Set the cluster index hint to the first valid cluster of a fat: 2
548         //
549 
550         Vcb->ClusterHint = 2;
551 
552         //
553         //  Initialize the directory stream file object creation event.
554         //  This event is also "borrowed" for async non-cached writes.
555         //
556 
557         ExInitializeFastMutex( &Vcb->DirectoryFileCreationMutex );
558 
559         //
560         //  Initialize the clean volume callback Timer and DPC.
561         //
562 
563         KeInitializeTimer( &Vcb->CleanVolumeTimer );
564 
565         KeInitializeDpc( &Vcb->CleanVolumeDpc, FatCleanVolumeDpc, Vcb );
566 
567         //
568         //  Initialize the performance counters.
569         //
570 
571         Vcb->Statistics = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
572                                                     sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors,
573                                                     TAG_VCB_STATS );
574         UnwindStatistics = Vcb->Statistics;
575 
576         RtlZeroMemory( Vcb->Statistics, sizeof(FILE_SYSTEM_STATISTICS) * FatData.NumberProcessors );
577 
578         for (i = 0; i < FatData.NumberProcessors; i += 1) {
579             Vcb->Statistics[i].Common.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT;
580             Vcb->Statistics[i].Common.Version = 1;
581             Vcb->Statistics[i].Common.SizeOfCompleteStructure =
582                 sizeof(FILE_SYSTEM_STATISTICS);
583         }
584 
585         //
586         //  Pick up a VPB right now so we know we can pull this filesystem stack off
587         //  of the storage stack on demand.
588         //
589 
590         Vcb->SwapVpb = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
591                                                  sizeof( VPB ),
592                                                  TAG_VPB );
593 
594         RtlZeroMemory( Vcb->SwapVpb, sizeof( VPB ) );
595 
596         //
597         //  Initialize the close queue listheads.
598         //
599 
600         InitializeListHead( &Vcb->AsyncCloseList );
601         InitializeListHead( &Vcb->DelayedCloseList );
602 
603         //
604         //  Initialize the Advanced FCB Header
605         //
606 
607         ExInitializeFastMutex( &Vcb->AdvancedFcbHeaderMutex );
608         FsRtlSetupAdvancedHeader( &Vcb->VolumeFileHeader,
609                                   &Vcb->AdvancedFcbHeaderMutex );
610 
611 
612         //
613         //  With the Vcb now set up, set the IrpContext Vcb field.
614         //
615 
616         IrpContext->Vcb = Vcb;
617 
618     } _SEH2_FINALLY {
619 
620         DebugUnwind( FatInitializeVcb );
621 
622         //
623         //  If this is an abnormal termination then undo our work
624         //
625 
626         if (_SEH2_AbnormalTermination()) {
627 
628             if (UnwindCacheMap != NULL) { FatSyncUninitializeCacheMap( IrpContext, UnwindCacheMap ); }
629             if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); }
630             if (UnwindResource != NULL) { FatDeleteResource( UnwindResource ); }
631             if (UnwindResource2 != NULL) { FatDeleteResource( UnwindResource2 ); }
632             if (UnwindWeAllocatedMcb) { FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb ); }
633             if (UnwindWeAllocatedBadBlockMap) { FsRtlUninitializeLargeMcb(&Vcb->BadBlockMcb ); }
634             if (UnwindEntryList != NULL) {
635 #ifdef _MSC_VER
636 #pragma prefast( suppress: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
637 #endif
638                 (VOID)FatAcquireExclusiveGlobal( IrpContext );
639                 RemoveEntryList( UnwindEntryList );
640                 FatReleaseGlobal( IrpContext );
641             }
642             if (UnwindStatistics != NULL) { ExFreePool( UnwindStatistics ); }
643 
644             //
645             // Cleanup the close context we preallocated above.
646             //
647 
648             if (CloseContextAllocated && (Vcb->VirtualVolumeFile == NULL)) {
649 
650                 //
651                 // FatAllocateCloseContext does not allocate memory, it
652                 // pulls a close context off the preallocated slist queue.
653                 //
654                 // Doing this here is necessary to balance out the one we
655                 // preallocated for the Vcb earlier in this function, but
656                 // only if we failed to create the virtual volume file.
657                 //
658                 // If VirtualVolumeFile is not NULL, then this CloseContext
659                 // will get cleaned up when the close comes in for it during
660                 // Vcb teardown.
661                 //
662 
663                 PCLOSE_CONTEXT CloseContext = FatAllocateCloseContext(Vcb);
664 
665                 ExFreePool( CloseContext );
666                 CloseContextAllocated = FALSE;
667             }
668         }
669 
670         DebugTrace(-1, Dbg, "FatInitializeVcb -> VOID\n", 0);
671     } _SEH2_END;
672 
673     //
674     //  and return to our caller
675     //
676 
677     UNREFERENCED_PARAMETER( IrpContext );
678 
679     return;
680 }
681 
682 
683 VOID
FatTearDownVcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb)684 FatTearDownVcb (
685     IN PIRP_CONTEXT IrpContext,
686     IN PVCB Vcb
687     )
688 
689 /*++
690 
691 Routine Description:
692 
693     This routine tries to remove all internal opens from the volume.
694 
695 Arguments:
696 
697     IrpContext - Supplies the context for the overall request.
698 
699     Vcb - Supplies the Vcb to be torn down.
700 
701 Return Value:
702 
703     None
704 
705 --*/
706 
707 {
708     PFILE_OBJECT DirectoryFileObject;
709 
710 
711     PAGED_CODE();
712 
713     //
714     //  Get rid of the virtual volume file, if we need to.
715     //
716 
717     if (Vcb->VirtualVolumeFile != NULL) {
718 
719         //
720         //  Uninitialize the cache
721         //
722 
723         CcUninitializeCacheMap( Vcb->VirtualVolumeFile,
724                                 &FatLargeZero,
725                                 NULL );
726 
727         FsRtlTeardownPerStreamContexts( &Vcb->VolumeFileHeader );
728 
729         ObDereferenceObject( Vcb->VirtualVolumeFile );
730 
731         Vcb->VirtualVolumeFile = NULL;
732     }
733 
734     //
735     //  Close down the EA file.
736     //
737 
738     FatCloseEaFile( IrpContext, Vcb, FALSE );
739 
740     //
741     //  Close down the root directory stream..
742     //
743 
744     if (Vcb->RootDcb != NULL) {
745 
746         DirectoryFileObject = Vcb->RootDcb->Specific.Dcb.DirectoryFile;
747 
748         if (DirectoryFileObject != NULL) {
749 
750             //
751             //  Tear down this directory file.
752             //
753 
754             CcUninitializeCacheMap( DirectoryFileObject,
755                                     &FatLargeZero,
756                                     NULL );
757 
758             Vcb->RootDcb->Specific.Dcb.DirectoryFile = NULL;
759             ObDereferenceObject( DirectoryFileObject );
760         }
761     }
762 
763     //
764     //  The VCB can no longer be used.
765     //
766 
767     FatSetVcbCondition( Vcb, VcbBad );
768 }
769 
770 
771 VOID
FatDeleteVcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb)772 FatDeleteVcb (
773     IN PIRP_CONTEXT IrpContext,
774     IN PVCB Vcb
775     )
776 
777 /*++
778 
779 Routine Description:
780 
781     This routine removes the Vcb record from Fat's in-memory data
782     structures.  It also will remove all associated underlings
783     (i.e., FCB records).
784 
785 Arguments:
786 
787     Vcb - Supplies the Vcb to be removed
788 
789 Return Value:
790 
791     None
792 
793 --*/
794 
795 {
796     PFCB Fcb;
797 
798     PAGED_CODE();
799 
800     DebugTrace(+1, Dbg, "FatDeleteVcb, Vcb = %p\n", Vcb);
801 
802     //
803     //  If the IrpContext points to the VCB being deleted NULL out the stail
804     //  pointer.
805     //
806 
807     if (IrpContext->Vcb == Vcb) {
808 
809         IrpContext->Vcb = NULL;
810 
811     }
812 
813     //
814     //  Chuck the backpocket Vpb we kept just in case.
815     //
816 
817     if (Vcb->SwapVpb) {
818 
819         ExFreePool( Vcb->SwapVpb );
820 
821     }
822 
823     //
824     //  Free the VPB, if we need to.
825     //
826 
827     if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED )) {
828 
829         //
830         //  We swapped the VPB, so we need to free the main one.
831         //
832 
833         ExFreePool( Vcb->Vpb );
834     }
835 
836     if (Vcb->VolumeGuidPath.Buffer) {
837         ExFreePool( Vcb->VolumeGuidPath.Buffer );
838         Vcb->VolumeGuidPath.Buffer = NULL;
839     }
840 
841     //
842     //  Remove this record from the global list of all Vcb records.
843     //  Note that the global lock must already be held when calling
844     //  this function.
845     //
846 
847     RemoveEntryList( &(Vcb->VcbLinks) );
848 
849     //
850     //  Make sure the direct access open count is zero, and the open file count
851     //  is also zero.
852     //
853 
854     if ((Vcb->DirectAccessOpenCount != 0) || (Vcb->OpenFileCount != 0)) {
855 
856 #ifdef _MSC_VER
857 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
858 #endif
859         FatBugCheck( 0, 0, 0 );
860     }
861 
862     //
863     //  Remove the EaFcb and dereference the Fcb for the Ea file if it
864     //  exists.
865     //
866 
867     if (Vcb->EaFcb != NULL) {
868 
869         Vcb->EaFcb->OpenCount = 0;
870         FatDeleteFcb( IrpContext, &Vcb->EaFcb );
871     }
872 
873     //
874     //  Remove the Root Dcb
875     //
876 
877     if (Vcb->RootDcb != NULL) {
878 
879         //
880         //  Rundown stale child Fcbs that may be hanging around.  Yes, this
881         //  can happen.  No, the create path isn't perfectly defensive about
882         //  tearing down branches built up on creates that don't wind up
883         //  succeeding.  Normal system operation usually winds up having
884         //  cleaned them out through re-visiting, but ...
885         //
886         //  Just pick off Fcbs from the bottom of the tree until we run out.
887         //  Then we delete the root Dcb.
888         //
889 
890         while( (Fcb = FatGetNextFcbBottomUp( IrpContext, NULL, Vcb->RootDcb )) != Vcb->RootDcb ) {
891 
892             FatDeleteFcb( IrpContext, &Fcb );
893         }
894 
895         FatDeleteFcb( IrpContext, &Vcb->RootDcb );
896     }
897 
898     //
899     //  Uninitialize the notify sychronization object.
900     //
901 
902     FsRtlNotifyUninitializeSync( &Vcb->NotifySync );
903 
904     //
905     //  Uninitialize the resource variable for the Vcb
906     //
907 
908     FatDeleteResource( &Vcb->Resource );
909     FatDeleteResource( &Vcb->ChangeBitMapResource );
910 
911     //
912     //  If allocation support has been setup, free it.
913     //
914 
915     if (Vcb->FreeClusterBitMap.Buffer != NULL) {
916 
917         FatTearDownAllocationSupport( IrpContext, Vcb );
918     }
919 
920     //
921     //  UnInitialize the Mcb structure that kept track of dirty fat sectors.
922     //
923 
924     FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb );
925 
926     //
927     //  Uninitialize the Mcb structure that kept track of bad sectors.
928     //
929 
930     FsRtlUninitializeLargeMcb( &Vcb->BadBlockMcb );
931 
932     //
933     //  Free the pool for the stached copy of the boot sector
934     //
935 
936     if ( Vcb->First0x24BytesOfBootSector ) {
937 
938         ExFreePool( Vcb->First0x24BytesOfBootSector );
939         Vcb->First0x24BytesOfBootSector = NULL;
940     }
941 
942     //
943     //  Cancel the CleanVolume Timer and Dpc
944     //
945 
946     (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer );
947 
948     (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc );
949 
950     //
951     //  Free the performance counters memory
952     //
953 
954     ExFreePool( Vcb->Statistics );
955 
956     //
957     //  Clean out the tunneling cache
958     //
959 
960     FsRtlDeleteTunnelCache(&Vcb->Tunnel);
961 
962     //
963     // Dereference the target device object.
964     //
965 
966     ObDereferenceObject( Vcb->TargetDeviceObject );
967 
968     //
969     //  We better have used all the close contexts we allocated. There could be
970     //  one remaining if we're doing teardown due to a final close coming in on
971     //  a directory file stream object.  It will be freed on the way back up.
972     //
973 
974     NT_ASSERT( Vcb->CloseContextCount <= 1);
975 
976     //
977     //  And zero out the Vcb, this will help ensure that any stale data is
978     //  wiped clean
979     //
980 
981     RtlZeroMemory( Vcb, sizeof(VCB) );
982 
983     //
984     //  return and tell the caller
985     //
986 
987     DebugTrace(-1, Dbg, "FatDeleteVcb -> VOID\n", 0);
988 
989     return;
990 }
991 
992 
_Requires_lock_held_(_Global_critical_region_)993 _Requires_lock_held_(_Global_critical_region_)
994 VOID
995 FatCreateRootDcb (
996     IN PIRP_CONTEXT IrpContext,
997     IN PVCB Vcb
998     )
999 
1000 /*++
1001 
1002 Routine Description:
1003 
1004     This routine allocates, initializes, and inserts a new root DCB record
1005     into the in memory data structure.
1006 
1007 Arguments:
1008 
1009     Vcb - Supplies the Vcb to associate the new DCB under
1010 
1011 Return Value:
1012 
1013     None. The Vcb is modified in-place.
1014 
1015 --*/
1016 
1017 {
1018     PDCB Dcb = NULL;
1019 
1020     //
1021     //  The following variables are used for abnormal unwind
1022     //
1023 
1024     PVOID UnwindStorage[2] = { NULL, NULL };
1025     PERESOURCE UnwindResource = NULL;
1026     PERESOURCE UnwindResource2 = NULL;
1027     PLARGE_MCB UnwindMcb = NULL;
1028     PFILE_OBJECT UnwindFileObject = NULL;
1029 
1030     PAGED_CODE();
1031 
1032     DebugTrace(+1, Dbg, "FatCreateRootDcb, Vcb = %p\n", Vcb);
1033 
1034     _SEH2_TRY {
1035 
1036         //
1037         //  Make sure we don't already have a root dcb for this vcb
1038         //
1039 
1040         if (Vcb->RootDcb != NULL) {
1041 
1042             DebugDump("Error trying to create multiple root dcbs\n", 0, Vcb);
1043 #ifdef _MSC_VER
1044 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1045 #endif
1046             FatBugCheck( 0, 0, 0 );
1047         }
1048 
1049         //
1050         //  Allocate a new DCB and zero it out, we use Dcb locally so we don't
1051         //  have to continually reference through the Vcb
1052         //
1053 
1054         UnwindStorage[0] = Dcb = Vcb->RootDcb = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
1055                                                                           sizeof(DCB),
1056                                                                           TAG_FCB );
1057 
1058         RtlZeroMemory( Dcb, sizeof(DCB));
1059 
1060         UnwindStorage[1] =
1061         Dcb->NonPaged = FatAllocateNonPagedFcb();
1062 
1063         RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) );
1064 
1065         //
1066         //  Set the proper node type code, node byte size, and call backs
1067         //
1068 
1069         Dcb->Header.NodeTypeCode = FAT_NTC_ROOT_DCB;
1070         Dcb->Header.NodeByteSize = sizeof(DCB);
1071 
1072         Dcb->FcbCondition = FcbGood;
1073 
1074         //
1075         //  The parent Dcb, initial state, open count, dirent location
1076         //  information, and directory change count fields are already zero so
1077         //  we can skip setting them
1078         //
1079 
1080         //
1081         //  Initialize the resource variable
1082         //
1083 
1084         UnwindResource =
1085         Dcb->Header.Resource = FatAllocateResource();
1086 
1087         //
1088         //  Initialize the PagingIo Resource.  We no longer use the FsRtl common
1089         //  shared pool because this led to a) deadlocks due to cases where files
1090         //  and their parent directories shared a resource and b) there is no way
1091         //  to anticipate inter-driver induced deadlock via recursive operation.
1092         //
1093 
1094         UnwindResource2 =
1095         Dcb->Header.PagingIoResource = FatAllocateResource();
1096 
1097         //
1098         //  The root Dcb has an empty parent dcb links field
1099         //
1100 
1101         InitializeListHead( &Dcb->ParentDcbLinks );
1102 
1103         //
1104         //  Set the Vcb
1105         //
1106 
1107         Dcb->Vcb = Vcb;
1108 
1109         //
1110         //  initialize the parent dcb queue.
1111         //
1112 
1113         InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue );
1114 
1115         //
1116         //  Set the full file name up.
1117         //
1118 
1119         Dcb->FullFileName.Buffer = L"\\";
1120         Dcb->FullFileName.Length = (USHORT)2;
1121         Dcb->FullFileName.MaximumLength = (USHORT)4;
1122 
1123         Dcb->ShortName.Name.Oem.Buffer = "\\";
1124         Dcb->ShortName.Name.Oem.Length = (USHORT)1;
1125         Dcb->ShortName.Name.Oem.MaximumLength = (USHORT)2;
1126 
1127         //
1128         //  Construct a lie about file properties since we don't
1129         //  have a proper "." entry to look at.
1130         //
1131 
1132         Dcb->DirentFatFlags = FILE_ATTRIBUTE_DIRECTORY;
1133 
1134         //
1135         //  Initialize Advanced FCB Header fields
1136         //
1137 
1138         ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex );
1139         FsRtlSetupAdvancedHeader( &Dcb->Header,
1140                                   &Dcb->NonPaged->AdvancedFcbHeaderMutex );
1141 
1142         //
1143         //  Initialize the Mcb, and setup its mapping.  Note that the root
1144         //  directory is a fixed size so we can set it everything up now.
1145         //
1146 
1147         FsRtlInitializeLargeMcb( &Dcb->Mcb, NonPagedPoolNx );
1148         UnwindMcb = &Dcb->Mcb;
1149 
1150         if (FatIsFat32(Vcb)) {
1151 
1152             //
1153             //  The first cluster of fat32 roots comes from the BPB
1154             //
1155 
1156             Dcb->FirstClusterOfFile = Vcb->Bpb.RootDirFirstCluster;
1157 
1158         } else {
1159 
1160             FatAddMcbEntry( Vcb, &Dcb->Mcb,
1161                             0,
1162                             FatRootDirectoryLbo( &Vcb->Bpb ),
1163                             FatRootDirectorySize( &Vcb->Bpb ));
1164         }
1165 
1166         if (FatIsFat32(Vcb)) {
1167 
1168             //
1169             //  Find the size of the fat32 root. As a side-effect, this will create
1170             //  MCBs for the entire root. In the process of doing this, we may
1171             //  discover that the FAT chain is bogus and raise corruption.
1172             //
1173 
1174             Dcb->Header.AllocationSize.LowPart = 0xFFFFFFFF;
1175             FatLookupFileAllocationSize( IrpContext, Dcb);
1176 
1177             Dcb->Header.FileSize.QuadPart =
1178                     Dcb->Header.AllocationSize.QuadPart;
1179         } else {
1180 
1181             //
1182             //  set the allocation size to real size of the root directory
1183             //
1184 
1185             Dcb->Header.FileSize.QuadPart =
1186             Dcb->Header.AllocationSize.QuadPart = FatRootDirectorySize( &Vcb->Bpb );
1187 
1188         }
1189 
1190         //
1191         //  Set our two create dirent aids to represent that we have yet to
1192         //  enumerate the directory for never used or deleted dirents.
1193         //
1194 
1195         Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff;
1196         Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff;
1197 
1198         //
1199         //  Setup the free dirent bitmap buffer.
1200         //
1201 
1202         RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap,
1203                              NULL,
1204                              0 );
1205 
1206         FatCheckFreeDirentBitmap( IrpContext, Dcb );
1207 
1208 #if (NTDDI_VERSION >= NTDDI_WIN8)
1209         //
1210         //  Initialize the oplock structure.
1211         //
1212 
1213         FsRtlInitializeOplock( FatGetFcbOplock(Dcb) );
1214 #endif
1215 
1216     } _SEH2_FINALLY {
1217 
1218         DebugUnwind( FatCreateRootDcb );
1219 
1220         //
1221         //  If this is an abnormal termination then undo our work
1222         //
1223 
1224         if (_SEH2_AbnormalTermination()) {
1225 
1226             ULONG i;
1227 
1228             if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); }
1229             if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); }
1230             if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); }
1231             if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); }
1232 
1233             for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) {
1234                 if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); }
1235             }
1236 
1237             //
1238             //  Re-zero the entry in the Vcb.
1239             //
1240 
1241             Vcb->RootDcb = NULL;
1242         }
1243 
1244         DebugTrace(-1, Dbg, "FatCreateRootDcb -> %p\n", Dcb);
1245     } _SEH2_END;
1246 
1247     return;
1248 }
1249 
1250 
1251 PFCB
FatCreateFcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb,IN PDCB ParentDcb,IN ULONG LfnOffsetWithinDirectory,IN ULONG DirentOffsetWithinDirectory,IN PDIRENT Dirent,IN PUNICODE_STRING Lfn OPTIONAL,IN PUNICODE_STRING OrigLfn OPTIONAL,IN BOOLEAN IsPagingFile,IN BOOLEAN SingleResource)1252 FatCreateFcb (
1253     IN PIRP_CONTEXT IrpContext,
1254     IN PVCB Vcb,
1255     IN PDCB ParentDcb,
1256     IN ULONG LfnOffsetWithinDirectory,
1257     IN ULONG DirentOffsetWithinDirectory,
1258     IN PDIRENT Dirent,
1259     IN PUNICODE_STRING Lfn OPTIONAL,
1260     IN PUNICODE_STRING OrigLfn OPTIONAL,
1261     IN BOOLEAN IsPagingFile,
1262     IN BOOLEAN SingleResource
1263     )
1264 
1265 /*++
1266 
1267 Routine Description:
1268 
1269     This routine allocates, initializes, and inserts a new Fcb record into
1270     the in-memory data structures.
1271 
1272 Arguments:
1273 
1274     Vcb - Supplies the Vcb to associate the new FCB under.
1275 
1276     ParentDcb - Supplies the parent dcb that the new FCB is under.
1277 
1278     LfnOffsetWithinDirectory - Supplies the offset of the LFN.  If there is
1279         no LFN associated with this file then this value is same as
1280         DirentOffsetWithinDirectory.
1281 
1282     DirentOffsetWithinDirectory - Supplies the offset, in bytes from the
1283         start of the directory file where the dirent for the fcb is located
1284 
1285     Dirent - Supplies the dirent for the fcb being created
1286 
1287     Lfn - Supplies a long UNICODE name associated with this file.
1288 
1289     IsPagingFile - Indicates if we are creating an FCB for a paging file
1290         or some other type of file.
1291 
1292     SingleResource - Indicates if this Fcb should share a single resource
1293         as both main and paging.
1294 
1295 Return Value:
1296 
1297     PFCB - Returns a pointer to the newly allocated FCB
1298 
1299 --*/
1300 
1301 {
1302     PFCB Fcb = NULL;
1303     POOL_TYPE PoolType;
1304 
1305     //
1306     //  The following variables are used for abnormal unwind
1307     //
1308 
1309     PVOID UnwindStorage[2] = { NULL, NULL };
1310     PERESOURCE UnwindResource = NULL;
1311     PERESOURCE UnwindResource2 = NULL;
1312     PLIST_ENTRY UnwindEntryList = NULL;
1313     PLARGE_MCB UnwindMcb = NULL;
1314     PFILE_LOCK UnwindFileLock = NULL;
1315     POPLOCK UnwindOplock = NULL;
1316 
1317     PAGED_CODE();
1318 
1319     UNREFERENCED_PARAMETER( OrigLfn );
1320 
1321     DebugTrace(+1, Dbg, "FatCreateFcb\n", 0);
1322 
1323     _SEH2_TRY {
1324 
1325         //
1326         //  Determine the pool type we should be using for the fcb and the
1327         //  mcb structure
1328         //
1329 
1330         if (IsPagingFile) {
1331 
1332             PoolType = NonPagedPoolNx;
1333             Fcb = UnwindStorage[0] = FsRtlAllocatePoolWithTag( NonPagedPoolNx,
1334                                                                sizeof(FCB),
1335                                                                TAG_FCB );
1336         } else {
1337 
1338             PoolType = PagedPool;
1339             Fcb = UnwindStorage[0] = FatAllocateFcb();
1340 
1341         }
1342 
1343         //
1344         //  ... and zero it out
1345         //
1346 
1347         RtlZeroMemory( Fcb, sizeof(FCB) );
1348 
1349         UnwindStorage[1] =
1350         Fcb->NonPaged = FatAllocateNonPagedFcb();
1351 
1352         RtlZeroMemory( Fcb->NonPaged, sizeof( NON_PAGED_FCB ) );
1353 
1354         //
1355         //  Set the proper node type code, node byte size, and call backs
1356         //
1357 
1358         Fcb->Header.NodeTypeCode = FAT_NTC_FCB;
1359         Fcb->Header.NodeByteSize = sizeof(FCB);
1360 
1361         Fcb->FcbCondition = FcbGood;
1362 
1363         //
1364         //  Check to see if we need to set the Fcb state to indicate that this
1365         //  is a paging/system file.  This will prevent it from being opened
1366         //  again.
1367         //
1368 
1369         if (IsPagingFile) {
1370 
1371             SetFlag( Fcb->FcbState, FCB_STATE_PAGING_FILE | FCB_STATE_SYSTEM_FILE );
1372         }
1373 
1374         //
1375         //  The initial state, open count, and segment objects fields are already
1376         //  zero so we can skip setting them
1377         //
1378 
1379         //
1380         //  Initialize the resource variable
1381         //
1382 
1383 
1384         UnwindResource =
1385         Fcb->Header.Resource = FatAllocateResource();
1386 
1387         //
1388         //  Initialize the PagingIo Resource.  We no longer use the FsRtl common
1389         //  shared pool because this led to a) deadlocks due to cases where files
1390         //  and their parent directories shared a resource and b) there is no way
1391         //  to anticipate inter-driver induced deadlock via recursive operation.
1392         //
1393 
1394         if (SingleResource) {
1395 
1396             Fcb->Header.PagingIoResource = Fcb->Header.Resource;
1397 
1398         } else {
1399 
1400             UnwindResource2 =
1401             Fcb->Header.PagingIoResource = FatAllocateResource();
1402         }
1403 
1404         //
1405         //  Insert this fcb into our parent dcb's queue.
1406         //
1407         //  There is a deep reason why this goes on the tail, to allow us
1408         //  to easily enumerate all child directories before child files.
1409         //  This is important to let us maintain whole-volume lockorder
1410         //  via BottomUp enumeration.
1411         //
1412 
1413         InsertTailList( &ParentDcb->Specific.Dcb.ParentDcbQueue,
1414                         &Fcb->ParentDcbLinks );
1415         UnwindEntryList = &Fcb->ParentDcbLinks;
1416 
1417         //
1418         //  Point back to our parent dcb
1419         //
1420 
1421         Fcb->ParentDcb = ParentDcb;
1422 
1423         //
1424         //  Set the Vcb
1425         //
1426 
1427         Fcb->Vcb = Vcb;
1428 
1429         //
1430         //  Set the dirent offset within the directory
1431         //
1432 
1433         Fcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory;
1434         Fcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory;
1435 
1436         //
1437         //  Set the DirentFatFlags and LastWriteTime
1438         //
1439 
1440         Fcb->DirentFatFlags = Dirent->Attributes;
1441 
1442         Fcb->LastWriteTime = FatFatTimeToNtTime( IrpContext,
1443                                                  Dirent->LastWriteTime,
1444                                                  0 );
1445 
1446         //
1447         //  These fields are only non-zero when in Chicago mode.
1448         //
1449 
1450         if (FatData.ChicagoMode) {
1451 
1452             LARGE_INTEGER FatSystemJanOne1980 = {0};
1453 
1454             //
1455             //  If either date is possibly zero, get the system
1456             //  version of 1/1/80.
1457             //
1458 
1459             if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) {
1460 
1461                 ExLocalTimeToSystemTime( &FatJanOne1980,
1462                                          &FatSystemJanOne1980 );
1463             }
1464 
1465             //
1466             //  Only do the really hard work if this field is non-zero.
1467             //
1468 
1469             if (((PUSHORT)Dirent)[9] != 0) {
1470 
1471                 Fcb->LastAccessTime =
1472                     FatFatDateToNtTime( IrpContext,
1473                                         Dirent->LastAccessDate );
1474 
1475             } else {
1476 
1477                 Fcb->LastAccessTime = FatSystemJanOne1980;
1478             }
1479 
1480             //
1481             //  Only do the really hard work if this field is non-zero.
1482             //
1483 
1484             if (((PUSHORT)Dirent)[8] != 0) {
1485 
1486                 Fcb->CreationTime =
1487                     FatFatTimeToNtTime( IrpContext,
1488                                         Dirent->CreationTime,
1489                                         Dirent->CreationMSec );
1490 
1491             } else {
1492 
1493                 Fcb->CreationTime = FatSystemJanOne1980;
1494             }
1495         }
1496 
1497         //
1498         //  Initialize Advanced FCB Header fields
1499         //
1500 
1501         ExInitializeFastMutex( &Fcb->NonPaged->AdvancedFcbHeaderMutex );
1502         FsRtlSetupAdvancedHeader( &Fcb->Header,
1503                                   &Fcb->NonPaged->AdvancedFcbHeaderMutex );
1504 
1505         //
1506         //  To make FAT match the present functionality of NTFS, disable
1507         //  stream contexts on paging files
1508         //
1509 
1510         if (IsPagingFile) {
1511 
1512             SetFlag( Fcb->Header.Flags2, FSRTL_FLAG2_IS_PAGING_FILE );
1513             ClearFlag( Fcb->Header.Flags2, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS );
1514         }
1515 
1516         //
1517         //  Initialize the Mcb
1518         //
1519 
1520         FsRtlInitializeLargeMcb( &Fcb->Mcb, PoolType );
1521         UnwindMcb = &Fcb->Mcb;
1522 
1523         //
1524         //  Set the file size, valid data length, first cluster of file,
1525         //  and allocation size based on the information stored in the dirent
1526         //
1527 
1528         Fcb->Header.FileSize.LowPart = Dirent->FileSize;
1529 
1530         Fcb->Header.ValidDataLength.LowPart = Dirent->FileSize;
1531 
1532         Fcb->ValidDataToDisk = Dirent->FileSize;
1533 
1534         Fcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile;
1535 
1536         if ( FatIsFat32(Vcb) ) {
1537 
1538             Fcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16;
1539         }
1540 
1541         if ( Fcb->FirstClusterOfFile == 0 ) {
1542 
1543             Fcb->Header.AllocationSize.QuadPart = 0;
1544 
1545         } else {
1546 
1547             Fcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
1548         }
1549 
1550 
1551         //
1552         //  Initialize the Fcb's file lock record
1553         //
1554 
1555         FsRtlInitializeFileLock( &Fcb->Specific.Fcb.FileLock, NULL, NULL );
1556         UnwindFileLock = &Fcb->Specific.Fcb.FileLock;
1557 
1558         //
1559         //  Initialize the oplock structure.
1560         //
1561 
1562         FsRtlInitializeOplock( FatGetFcbOplock(Fcb) );
1563         UnwindOplock = FatGetFcbOplock(Fcb);
1564 
1565         //
1566         //  Indicate that Fast I/O is possible
1567         //
1568 
1569         Fcb->Header.IsFastIoPossible = TRUE;
1570 
1571         //
1572         //  Set the file names.  This must be the last thing we do.
1573         //
1574 
1575         FatConstructNamesInFcb( IrpContext,
1576                                 Fcb,
1577                                 Dirent,
1578                                 Lfn );
1579 
1580         //
1581         //  Drop the shortname hint so prefix searches can figure out
1582         //  what they found
1583         //
1584 
1585         Fcb->ShortName.FileNameDos = TRUE;
1586 
1587     } _SEH2_FINALLY {
1588 
1589         DebugUnwind( FatCreateFcb );
1590 
1591         //
1592         //  If this is an abnormal termination then undo our work
1593         //
1594 
1595         if (_SEH2_AbnormalTermination()) {
1596 
1597             ULONG i;
1598 
1599             if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); }
1600             if (UnwindFileLock != NULL) { FsRtlUninitializeFileLock( UnwindFileLock ); }
1601             if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); }
1602             if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); }
1603             if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); }
1604             if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); }
1605 
1606             for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) {
1607                 if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); }
1608             }
1609         }
1610 
1611         DebugTrace(-1, Dbg, "FatCreateFcb -> %p\n", Fcb);
1612     } _SEH2_END;
1613 
1614     //
1615     //  return and tell the caller
1616     //
1617 
1618     return Fcb;
1619 }
1620 
1621 
1622 PDCB
FatCreateDcb(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb,IN PDCB ParentDcb,IN ULONG LfnOffsetWithinDirectory,IN ULONG DirentOffsetWithinDirectory,IN PDIRENT Dirent,IN PUNICODE_STRING Lfn OPTIONAL)1623 FatCreateDcb (
1624     IN PIRP_CONTEXT IrpContext,
1625     IN PVCB Vcb,
1626     IN PDCB ParentDcb,
1627     IN ULONG LfnOffsetWithinDirectory,
1628     IN ULONG DirentOffsetWithinDirectory,
1629     IN PDIRENT Dirent,
1630     IN PUNICODE_STRING Lfn OPTIONAL
1631     )
1632 
1633 /*++
1634 
1635 Routine Description:
1636 
1637     This routine allocates, initializes, and inserts a new Dcb record into
1638     the in memory data structures.
1639 
1640 Arguments:
1641 
1642     Vcb - Supplies the Vcb to associate the new DCB under.
1643 
1644     ParentDcb - Supplies the parent dcb that the new DCB is under.
1645 
1646     LfnOffsetWithinDirectory - Supplies the offset of the LFN.  If there is
1647         no LFN associated with this file then this value is same as
1648         DirentOffsetWithinDirectory.
1649 
1650     DirentOffsetWithinDirectory - Supplies the offset, in bytes from the
1651         start of the directory file where the dirent for the fcb is located
1652 
1653     Dirent - Supplies the dirent for the dcb being created
1654 
1655     FileName - Supplies the file name of the file relative to the directory
1656         it's in (e.g., the file \config.sys is called "CONFIG.SYS" without
1657         the preceding backslash).
1658 
1659     Lfn - Supplies a long UNICODE name associated with this directory.
1660 
1661 Return Value:
1662 
1663     PDCB - Returns a pointer to the newly allocated DCB
1664 
1665 --*/
1666 
1667 {
1668     PDCB Dcb = NULL;
1669 
1670     //
1671     //  The following variables are used for abnormal unwind
1672     //
1673 
1674     PVOID UnwindStorage[2] = { NULL, NULL  };
1675     PERESOURCE UnwindResource = NULL;
1676     PERESOURCE UnwindResource2 = NULL;
1677     PLIST_ENTRY UnwindEntryList = NULL;
1678     PLARGE_MCB UnwindMcb = NULL;
1679     POPLOCK UnwindOplock = NULL;
1680 
1681     PAGED_CODE();
1682 
1683     DebugTrace(+1, Dbg, "FatCreateDcb\n", 0);
1684 
1685     _SEH2_TRY {
1686 
1687         //
1688         //  assert that the only time we are called is if wait is true
1689         //
1690 
1691         NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
1692 
1693         //
1694         //  Allocate a new DCB, and zero it out
1695         //
1696 
1697         UnwindStorage[0] = Dcb = FatAllocateFcb();
1698 
1699         RtlZeroMemory( Dcb, sizeof(DCB) );
1700 
1701         UnwindStorage[1] =
1702         Dcb->NonPaged = FatAllocateNonPagedFcb();
1703 
1704         RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) );
1705 
1706         //
1707         //  Set the proper node type code, node byte size and call backs
1708         //
1709 
1710         Dcb->Header.NodeTypeCode = FAT_NTC_DCB;
1711         Dcb->Header.NodeByteSize = sizeof(DCB);
1712 
1713         Dcb->FcbCondition = FcbGood;
1714 
1715         //
1716         //  The initial state, open count, and directory change count fields are
1717         //  already zero so we can skip setting them
1718         //
1719 
1720         //
1721         //  Initialize the resource variable
1722         //
1723 
1724 
1725         UnwindResource =
1726         Dcb->Header.Resource = FatAllocateResource();
1727 
1728         //
1729         //  Initialize the PagingIo Resource.  We no longer use the FsRtl common
1730         //  shared pool because this led to a) deadlocks due to cases where files
1731         //  and their parent directories shared a resource and b) there is no way
1732         //  to anticipate inter-driver induced deadlock via recursive operation.
1733         //
1734 
1735         UnwindResource2 =
1736         Dcb->Header.PagingIoResource = FatAllocateResource();
1737 
1738         //
1739         //  Insert this Dcb into our parent dcb's queue
1740         //
1741         //  There is a deep reason why this goes on the head, to allow us
1742         //  to easily enumerate all child directories before child files.
1743         //  This is important to let us maintain whole-volume lockorder
1744         //  via BottomUp enumeration.
1745         //
1746 
1747         InsertHeadList( &ParentDcb->Specific.Dcb.ParentDcbQueue,
1748                         &Dcb->ParentDcbLinks );
1749         UnwindEntryList = &Dcb->ParentDcbLinks;
1750 
1751         //
1752         //  Point back to our parent dcb
1753         //
1754 
1755         Dcb->ParentDcb = ParentDcb;
1756 
1757         //
1758         //  Set the Vcb
1759         //
1760 
1761         Dcb->Vcb = Vcb;
1762 
1763         //
1764         //  Set the dirent offset within the directory
1765         //
1766 
1767         Dcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory;
1768         Dcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory;
1769 
1770         //
1771         //  Set the DirentFatFlags and LastWriteTime
1772         //
1773 
1774         Dcb->DirentFatFlags = Dirent->Attributes;
1775 
1776         Dcb->LastWriteTime = FatFatTimeToNtTime( IrpContext,
1777                                                  Dirent->LastWriteTime,
1778                                                  0 );
1779 
1780         //
1781         //  These fields are only non-zero when in Chicago mode.
1782         //
1783 
1784         if (FatData.ChicagoMode) {
1785 
1786             LARGE_INTEGER FatSystemJanOne1980 = {0};
1787 
1788             //
1789             //  If either date is possibly zero, get the system
1790             //  version of 1/1/80.
1791             //
1792 
1793             if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) {
1794 
1795                 ExLocalTimeToSystemTime( &FatJanOne1980,
1796                                          &FatSystemJanOne1980 );
1797             }
1798 
1799             //
1800             //  Only do the really hard work if this field is non-zero.
1801             //
1802 
1803             if (((PUSHORT)Dirent)[9] != 0) {
1804 
1805                 Dcb->LastAccessTime =
1806                     FatFatDateToNtTime( IrpContext,
1807                                         Dirent->LastAccessDate );
1808 
1809             } else {
1810 
1811                 Dcb->LastAccessTime = FatSystemJanOne1980;
1812             }
1813 
1814             //
1815             //  Only do the really hard work if this field is non-zero.
1816             //
1817 
1818             if (((PUSHORT)Dirent)[8] != 0) {
1819 
1820                 Dcb->CreationTime =
1821                     FatFatTimeToNtTime( IrpContext,
1822                                         Dirent->CreationTime,
1823                                         Dirent->CreationMSec );
1824 
1825             } else {
1826 
1827                 Dcb->CreationTime = FatSystemJanOne1980;
1828             }
1829         }
1830 
1831         //
1832         //  Initialize Advanced FCB Header fields
1833         //
1834 
1835         ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex );
1836         FsRtlSetupAdvancedHeader( &Dcb->Header,
1837                                   &Dcb->NonPaged->AdvancedFcbHeaderMutex );
1838 
1839         //
1840         //  Initialize the Mcb
1841         //
1842 
1843         FsRtlInitializeLargeMcb( &Dcb->Mcb, PagedPool );
1844         UnwindMcb = &Dcb->Mcb;
1845 
1846         //
1847         //  Set the file size, first cluster of file, and allocation size
1848         //  based on the information stored in the dirent
1849         //
1850 
1851         Dcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile;
1852 
1853         if ( FatIsFat32(Dcb->Vcb) ) {
1854 
1855             Dcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16;
1856         }
1857 
1858         if ( Dcb->FirstClusterOfFile == 0 ) {
1859 
1860             Dcb->Header.AllocationSize.QuadPart = 0;
1861 
1862         } else {
1863 
1864             Dcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT;
1865         }
1866 
1867 
1868         //  initialize the notify queues, and the parent dcb queue.
1869         //
1870 
1871         InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue );
1872 
1873         //
1874         //  Setup the free dirent bitmap buffer.  Since we don't know the
1875         //  size of the directory, leave it zero for now.
1876         //
1877 
1878         RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap,
1879                              NULL,
1880                              0 );
1881 
1882         //
1883         //  Set our two create dirent aids to represent that we have yet to
1884         //  enumerate the directory for never used or deleted dirents.
1885         //
1886 
1887         Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff;
1888         Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff;
1889 
1890 #if (NTDDI_VERSION >= NTDDI_WIN8)
1891         //
1892         //  Initialize the oplock structure.
1893         //
1894 
1895         FsRtlInitializeOplock( FatGetFcbOplock(Dcb) );
1896         UnwindOplock = FatGetFcbOplock(Dcb);
1897 #endif
1898 
1899         //
1900         //  Postpone initializing the cache map until we need to do a read/write
1901         //  of the directory file.
1902 
1903 
1904         //
1905         //  set the file names.  This must be the last thing we do.
1906         //
1907 
1908         FatConstructNamesInFcb( IrpContext,
1909                                 Dcb,
1910                                 Dirent,
1911                                 Lfn );
1912 
1913         Dcb->ShortName.FileNameDos = TRUE;
1914 
1915     } _SEH2_FINALLY {
1916 
1917         DebugUnwind( FatCreateDcb );
1918 
1919         //
1920         //  If this is an abnormal termination then undo our work
1921         //
1922 
1923         if (_SEH2_AbnormalTermination()) {
1924 
1925             ULONG i;
1926 
1927             if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); }
1928             if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); }
1929             if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); }
1930             if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); }
1931             if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); }
1932 
1933             for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) {
1934                 if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); }
1935             }
1936         }
1937 
1938         DebugTrace(-1, Dbg, "FatCreateDcb -> %p\n", Dcb);
1939     } _SEH2_END;
1940 
1941     //
1942     //  return and tell the caller
1943     //
1944 
1945     DebugTrace(-1, Dbg, "FatCreateDcb -> %p\n", Dcb);
1946 
1947     return Dcb;
1948 }
1949 
1950 
1951 VOID
FatDeleteFcb(IN PIRP_CONTEXT IrpContext,IN PFCB * FcbPtr)1952 FatDeleteFcb (
1953     IN PIRP_CONTEXT IrpContext,
1954     IN PFCB *FcbPtr
1955     )
1956 
1957 /*++
1958 
1959 Routine Description:
1960 
1961     This routine deallocates and removes an FCB, DCB, or ROOT DCB record
1962     from Fat's in-memory data structures.  It also will remove all
1963     associated underlings (i.e., Notify irps, and child FCB/DCB records).
1964 
1965 Arguments:
1966 
1967     Fcb - Supplies the FCB/DCB/ROOT DCB to be removed
1968 
1969 Return Value:
1970 
1971     None
1972 
1973 --*/
1974 
1975 {
1976     PFCB Fcb = *FcbPtr;
1977 
1978     PAGED_CODE();
1979 
1980     DebugTrace(+1, Dbg, "FatDeleteFcb, Fcb = %p\n", Fcb);
1981 
1982     //
1983     //  We can only delete this record if the open count is zero.
1984     //
1985 
1986     if (Fcb->OpenCount != 0) {
1987 
1988         DebugDump("Error deleting Fcb, Still Open\n", 0, Fcb);
1989 #ifdef _MSC_VER
1990 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1991 #endif
1992         FatBugCheck( 0, 0, 0 );
1993     }
1994 
1995     //
1996     //  Better be an FCB/DCB.
1997     //
1998 
1999     if ((Fcb->Header.NodeTypeCode != FAT_NTC_DCB) &&
2000         (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) &&
2001         (Fcb->Header.NodeTypeCode != FAT_NTC_FCB)) {
2002 
2003 #ifdef _MSC_VER
2004 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
2005 #endif
2006         FatBugCheck( 0, 0, 0 );
2007     }
2008 
2009     //
2010     //  If this is a DCB then remove every Notify record from the two
2011     //  notify queues
2012     //
2013 
2014     if ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) ||
2015         (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) {
2016 
2017         //
2018         //  If we allocated a free dirent bitmap buffer, free it.
2019         //
2020 
2021         if ((Fcb->Specific.Dcb.FreeDirentBitmap.Buffer != NULL) &&
2022             (Fcb->Specific.Dcb.FreeDirentBitmap.Buffer !=
2023              &Fcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) {
2024 
2025             ExFreePool(Fcb->Specific.Dcb.FreeDirentBitmap.Buffer);
2026         }
2027 
2028 #if (NTDDI_VERSION >= NTDDI_WIN8)
2029         //
2030         //  Uninitialize the oplock.
2031         //
2032 
2033         FsRtlUninitializeOplock( FatGetFcbOplock(Fcb) );
2034 #endif
2035 
2036         NT_ASSERT( Fcb->Specific.Dcb.DirectoryFileOpenCount == 0 );
2037         NT_ASSERT( IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) );
2038         NT_ASSERT( NULL == Fcb->Specific.Dcb.DirectoryFile);
2039 
2040     } else {
2041 
2042         //
2043         //  Uninitialize the byte range file locks and opportunistic locks
2044         //
2045 
2046         FsRtlUninitializeFileLock( &Fcb->Specific.Fcb.FileLock );
2047         FsRtlUninitializeOplock( FatGetFcbOplock(Fcb) );
2048     }
2049 
2050 
2051     //
2052     //  Release any Filter Context structures associated with this FCB
2053     //
2054 
2055     FsRtlTeardownPerStreamContexts( &Fcb->Header );
2056 
2057     //
2058     //  Uninitialize the Mcb
2059     //
2060 
2061     FsRtlUninitializeLargeMcb( &Fcb->Mcb );
2062 
2063     //
2064     //  If this is not the root dcb then we need to remove ourselves from
2065     //  our parents Dcb queue
2066     //
2067 
2068     if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) {
2069 
2070         RemoveEntryList( &(Fcb->ParentDcbLinks) );
2071     }
2072 
2073     //
2074     //  Remove the entry from the splay table if there is still is one.
2075     //
2076 
2077     if (FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )) {
2078 
2079         FatRemoveNames( IrpContext, Fcb );
2080     }
2081 
2082     //
2083     //  Free the file name pool if allocated.
2084     //
2085 
2086     if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) {
2087 
2088         //
2089         //  If we blew up at inconvenient times, the shortname
2090         //  could be null even though you will *never* see this
2091         //  normally.  Rename is a good example of this case.
2092         //
2093 
2094         if (Fcb->ShortName.Name.Oem.Buffer) {
2095 
2096             ExFreePool( Fcb->ShortName.Name.Oem.Buffer );
2097         }
2098 
2099         if (Fcb->FullFileName.Buffer) {
2100 
2101             ExFreePool( Fcb->FullFileName.Buffer );
2102         }
2103     }
2104 
2105     if (Fcb->ExactCaseLongName.Buffer) {
2106 
2107         ExFreePool(Fcb->ExactCaseLongName.Buffer);
2108     }
2109 
2110 #ifdef SYSCACHE_COMPILE
2111 
2112     if (Fcb->WriteMask) {
2113 
2114         ExFreePool( Fcb->WriteMask );
2115     }
2116 
2117 #endif
2118 
2119     //
2120     //  Finally deallocate the Fcb and non-paged fcb records
2121     //
2122 
2123     FatFreeResource( Fcb->Header.Resource );
2124 
2125     if (Fcb->Header.PagingIoResource != Fcb->Header.Resource) {
2126 
2127         FatFreeResource( Fcb->Header.PagingIoResource );
2128     }
2129 
2130     //
2131     //  If an Event was allocated, get rid of it.
2132     //
2133 
2134     if (Fcb->NonPaged->OutstandingAsyncEvent) {
2135 
2136         ExFreePool( Fcb->NonPaged->OutstandingAsyncEvent );
2137     }
2138 
2139     FatFreeNonPagedFcb( Fcb->NonPaged );
2140 
2141     Fcb->Header.NodeTypeCode = NTC_UNDEFINED;
2142 
2143     FatFreeFcb( Fcb );
2144     *FcbPtr = NULL;
2145 
2146     //
2147     //  and return to our caller
2148     //
2149 
2150     DebugTrace(-1, Dbg, "FatDeleteFcb -> VOID\n", 0);
2151 }
2152 
2153 
2154 PCCB
FatCreateCcb(IN PIRP_CONTEXT IrpContext)2155 FatCreateCcb (
2156     IN PIRP_CONTEXT IrpContext
2157     )
2158 
2159 /*++
2160 
2161 Routine Description:
2162 
2163     This routine creates a new CCB record
2164 
2165 Arguments:
2166 
2167 Return Value:
2168 
2169     CCB - returns a pointer to the newly allocate CCB
2170 
2171 --*/
2172 
2173 {
2174     PCCB Ccb;
2175 
2176     PAGED_CODE();
2177 
2178     DebugTrace(+1, Dbg, "FatCreateCcb\n", 0);
2179 
2180     //
2181     //  Allocate a new CCB Record
2182     //
2183 
2184     Ccb = FatAllocateCcb();
2185 
2186     RtlZeroMemory( Ccb, sizeof(CCB) );
2187 
2188     //
2189     //  Set the proper node type code and node byte size
2190     //
2191 
2192     Ccb->NodeTypeCode = FAT_NTC_CCB;
2193     Ccb->NodeByteSize = sizeof(CCB);
2194 
2195     //
2196     //  return and tell the caller
2197     //
2198 
2199     DebugTrace(-1, Dbg, "FatCreateCcb -> %p\n", Ccb);
2200 
2201     UNREFERENCED_PARAMETER( IrpContext );
2202 
2203     return Ccb;
2204 }
2205 
2206 
2207 
2208 VOID
FatDeallocateCcbStrings(IN PCCB Ccb)2209 FatDeallocateCcbStrings(
2210     IN PCCB Ccb
2211     )
2212 /*++
2213 
2214 Routine Description:
2215 
2216     This routine deallocates CCB query templates
2217 
2218 Arguments:
2219 
2220     Ccb - Supplies the CCB
2221 
2222 Return Value:
2223 
2224     None
2225 
2226 --*/
2227 {
2228     PAGED_CODE();
2229 
2230     //
2231     //  If we allocated query template buffers, deallocate them now.
2232     //
2233 
2234     if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_UNICODE)) {
2235 
2236         NT_ASSERT( Ccb->UnicodeQueryTemplate.Buffer);
2237         NT_ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT));
2238         RtlFreeUnicodeString( &Ccb->UnicodeQueryTemplate );
2239     }
2240 
2241     if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) {
2242 
2243         NT_ASSERT( Ccb->OemQueryTemplate.Wild.Buffer );
2244         NT_ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT));
2245         RtlFreeOemString( &Ccb->OemQueryTemplate.Wild );
2246     }
2247 
2248     ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT | CCB_FLAG_FREE_UNICODE);
2249 }
2250 
2251 
2252 
2253 VOID
FatDeleteCcb(IN PIRP_CONTEXT IrpContext,IN PCCB * Ccb)2254 FatDeleteCcb (
2255     IN PIRP_CONTEXT IrpContext,
2256     IN PCCB *Ccb
2257     )
2258 
2259 /*++
2260 
2261 Routine Description:
2262 
2263     This routine deallocates and removes the specified CCB record
2264     from the Fat in memory data structures
2265 
2266 Arguments:
2267 
2268     Ccb - Supplies the CCB to remove
2269 
2270 Return Value:
2271 
2272     None
2273 
2274 --*/
2275 
2276 {
2277     PAGED_CODE();
2278 
2279     DebugTrace(+1, Dbg, "FatDeleteCcb, Ccb = %p\n", *Ccb);
2280 
2281     FatDeallocateCcbStrings( *Ccb);
2282 
2283     //
2284     //  Deallocate the Ccb record
2285     //
2286 
2287     FatFreeCcb( *Ccb );
2288     *Ccb = NULL;
2289 
2290     //
2291     //  return and tell the caller
2292     //
2293 
2294     DebugTrace(-1, Dbg, "FatDeleteCcb -> VOID\n", 0);
2295 
2296     UNREFERENCED_PARAMETER( IrpContext );
2297 }
2298 
2299 
2300 PIRP_CONTEXT
FatCreateIrpContext(IN PIRP Irp,IN BOOLEAN Wait)2301 FatCreateIrpContext (
2302     IN PIRP Irp,
2303     IN BOOLEAN Wait
2304     )
2305 
2306 /*++
2307 
2308 Routine Description:
2309 
2310     This routine creates a new IRP_CONTEXT record
2311 
2312 Arguments:
2313 
2314     Irp - Supplies the originating Irp.
2315 
2316     Wait - Supplies the wait value to store in the context
2317 
2318 Return Value:
2319 
2320     PIRP_CONTEXT - returns a pointer to the newly allocate IRP_CONTEXT Record
2321 
2322 --*/
2323 
2324 {
2325     PIRP_CONTEXT IrpContext;
2326     PIO_STACK_LOCATION IrpSp;
2327 
2328     PAGED_CODE();
2329 
2330     DebugTrace(+1, Dbg, "FatCreateIrpContext\n", 0);
2331 
2332     IrpSp = IoGetCurrentIrpStackLocation( Irp );
2333 
2334     //
2335     //  The only operations a filesystem device object should ever receive
2336     //  are create/teardown of fsdo handles and operations which do not
2337     //  occur in the context of fileobjects (i.e., mount).
2338     //
2339 
2340     if (FatDeviceIsFatFsdo( IrpSp->DeviceObject))  {
2341 
2342         if (IrpSp->FileObject != NULL &&
2343             IrpSp->MajorFunction != IRP_MJ_CREATE &&
2344             IrpSp->MajorFunction != IRP_MJ_CLEANUP &&
2345             IrpSp->MajorFunction != IRP_MJ_CLOSE) {
2346 
2347             ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST );
2348         }
2349 
2350         NT_ASSERT( IrpSp->FileObject != NULL ||
2351 
2352                 (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
2353                  IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST &&
2354                  IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_INVALIDATE_VOLUMES) ||
2355 
2356                 (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL &&
2357                  IrpSp->MinorFunction == IRP_MN_MOUNT_VOLUME ) ||
2358 
2359                 IrpSp->MajorFunction == IRP_MJ_SHUTDOWN );
2360     }
2361 
2362     //
2363     //  Attemtp to allocate from the region first and failing that allocate
2364     //  from pool.
2365     //
2366 
2367     DebugDoit( FatFsdEntryCount += 1);
2368 
2369     IrpContext = FatAllocateIrpContext();
2370 
2371     //
2372     //  Zero out the irp context.
2373     //
2374 
2375     RtlZeroMemory( IrpContext, sizeof(IRP_CONTEXT) );
2376 
2377     //
2378     //  Set the proper node type code and node byte size
2379     //
2380 
2381     IrpContext->NodeTypeCode = FAT_NTC_IRP_CONTEXT;
2382     IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
2383 
2384     //
2385     //  Set the originating Irp field
2386     //
2387 
2388     IrpContext->OriginatingIrp = Irp;
2389 
2390     //
2391     //  Major/Minor Function codes
2392     //
2393 
2394     IrpContext->MajorFunction = IrpSp->MajorFunction;
2395     IrpContext->MinorFunction = IrpSp->MinorFunction;
2396 
2397     //
2398     //  Copy RealDevice for workque algorithms, and also set Write Through
2399     //  and Removable Media if there is a file object.  Only file system
2400     //  control Irps won't have a file object, and they should all have
2401     //  a Vpb as the first IrpSp location.
2402     //
2403 
2404     if (IrpSp->FileObject != NULL) {
2405 
2406         PFILE_OBJECT FileObject = IrpSp->FileObject;
2407 
2408         IrpContext->RealDevice = FileObject->DeviceObject;
2409         IrpContext->Vcb = &((PVOLUME_DEVICE_OBJECT)(IrpSp->DeviceObject))->Vcb;
2410 
2411         //
2412         //  See if the request is Write Through. Look for both FileObjects opened
2413         //  as write through, and non-cached requests with the SL_WRITE_THROUGH flag set.
2414         //
2415         //  The latter can only originate from kernel components. (Note - NtWriteFile()
2416         //  does redundantly set the SL_W_T flag for all requests it issues on write
2417         //  through file objects)
2418         //
2419 
2420         if (IsFileWriteThrough( FileObject, IrpContext->Vcb ) ||
2421             ( (IrpSp->MajorFunction == IRP_MJ_WRITE) &&
2422               BooleanFlagOn( Irp->Flags, IRP_NOCACHE) &&
2423               BooleanFlagOn( IrpSp->Flags, SL_WRITE_THROUGH))) {
2424 
2425             SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
2426         }
2427     } else if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) {
2428 
2429         IrpContext->RealDevice = IrpSp->Parameters.MountVolume.Vpb->RealDevice;
2430     }
2431 
2432     //
2433     //  Set the wait parameter
2434     //
2435 
2436     if (Wait) { SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); }
2437 
2438     //
2439     //  Set the recursive file system call parameter.  We set it true if
2440     //  the TopLevelIrp field in the thread local storage is not the current
2441     //  irp, otherwise we leave it as FALSE.
2442     //
2443 
2444     if ( IoGetTopLevelIrp() != Irp) {
2445 
2446         SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL);
2447     }
2448 
2449     //
2450     //  return and tell the caller
2451     //
2452 
2453     DebugTrace(-1, Dbg, "FatCreateIrpContext -> %p\n", IrpContext);
2454 
2455     return IrpContext;
2456 }
2457 
2458 
2459 
2460 VOID
FatDeleteIrpContext_Real(IN PIRP_CONTEXT IrpContext)2461 FatDeleteIrpContext_Real (
2462     IN PIRP_CONTEXT IrpContext
2463     )
2464 
2465 /*++
2466 
2467 Routine Description:
2468 
2469     This routine deallocates and removes the specified IRP_CONTEXT record
2470     from the Fat in memory data structures.  It should only be called
2471     by FatCompleteRequest.
2472 
2473 Arguments:
2474 
2475     IrpContext - Supplies the IRP_CONTEXT to remove
2476 
2477 Return Value:
2478 
2479     None
2480 
2481 --*/
2482 
2483 {
2484     PAGED_CODE();
2485 
2486     DebugTrace(+1, Dbg, "FatDeleteIrpContext, IrpContext = %p\n", IrpContext);
2487 
2488     NT_ASSERT( IrpContext->NodeTypeCode == FAT_NTC_IRP_CONTEXT );
2489     NT_ASSERT( IrpContext->PinCount == 0 );
2490 
2491 
2492     //
2493     //  If there is a FatIoContext that was allocated, free it.
2494     //
2495 
2496     if (IrpContext->FatIoContext != NULL) {
2497 
2498         if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) {
2499 
2500             if (IrpContext->FatIoContext->ZeroMdl) {
2501                 IoFreeMdl( IrpContext->FatIoContext->ZeroMdl );
2502             }
2503 
2504             ExFreePool( IrpContext->FatIoContext );
2505         }
2506     }
2507 
2508     //
2509     //  Drop the IrpContext.
2510     //
2511 
2512     FatFreeIrpContext( IrpContext );
2513 
2514     //
2515     //  return and tell the caller
2516     //
2517 
2518     DebugTrace(-1, Dbg, "FatDeleteIrpContext -> VOID\n", 0);
2519 
2520     return;
2521 }
2522 
2523 
2524 PFCB
FatGetNextFcbBottomUp(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb OPTIONAL,IN PFCB TerminationFcb)2525 FatGetNextFcbBottomUp (
2526     IN PIRP_CONTEXT IrpContext,
2527     IN PFCB Fcb OPTIONAL,
2528     IN PFCB TerminationFcb
2529     )
2530 
2531 /*++
2532 
2533 Routine Description:
2534 
2535     This routine is used to iterate through Fcbs in a tree.  In order to match
2536     the lockorder for getting multiple Fcbs (so this can be used for acquiring
2537     all Fcbs), this version does a bottom-up enumeration.
2538 
2539     This is different than the old one, now called TopDown. The problem with
2540     lockorder was very well hidden.
2541 
2542     The transition rule is still pretty simple:
2543 
2544         A) If you have an adjacent sibling, go to it
2545             1) Descend to its leftmost child
2546         B) Else go to your parent
2547 
2548     If this routine is called with in invalid TerminationFcb it will fail,
2549     badly.
2550 
2551     The TerminationFcb is the last Fcb returned in the enumeration.
2552 
2553     This method is incompatible with the possibility that ancestors may vanish
2554     based on operations done on the last returned node.  For instance,
2555     FatPurgeReferencedFileObjects cannot use BottomUp enumeration.
2556 
2557 Arguments:
2558 
2559     Fcb - Supplies the current Fcb.  This is NULL if enumeration is starting.
2560 
2561     TerminationFcb - The root Fcb of the tree in which the enumeration starts
2562         and at which it inclusively stops.
2563 
2564 Return Value:
2565 
2566     The next Fcb in the enumeration, or NULL if Fcb was the final one.
2567 
2568 --*/
2569 
2570 {
2571     PFCB NextFcb;
2572 
2573     PAGED_CODE();
2574     UNREFERENCED_PARAMETER( IrpContext );
2575 
2576     NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, TerminationFcb->Vcb ) ||
2577             FlagOn( TerminationFcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) );
2578 
2579     //
2580     //  Do we need to begin the enumeration?
2581     //
2582 
2583     if (Fcb != NULL) {
2584 
2585         //
2586         //  Did we complete?
2587         //
2588 
2589         if (Fcb == TerminationFcb) {
2590 
2591             return NULL;
2592         }
2593 
2594         //
2595         //  Do we have a sibling to return?
2596         //
2597 
2598         NextFcb = FatGetNextSibling( Fcb );
2599 
2600         //
2601         //  If not, return our parent.  We are done with this branch.
2602         //
2603 
2604         if (NextFcb == NULL) {
2605 
2606             return Fcb->ParentDcb;
2607         }
2608 
2609     } else {
2610 
2611         NextFcb = TerminationFcb;
2612     }
2613 
2614     //
2615     //  Decend to its furthest child (if it exists) and return it.
2616     //
2617 
2618     for (;
2619          NodeType( NextFcb ) != FAT_NTC_FCB && FatGetFirstChild( NextFcb ) != NULL;
2620          NextFcb = FatGetFirstChild( NextFcb )) {
2621     }
2622 
2623     return NextFcb;
2624 }
2625 
2626 PFCB
FatGetNextFcbTopDown(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb,IN PFCB TerminationFcb)2627 FatGetNextFcbTopDown (
2628     IN PIRP_CONTEXT IrpContext,
2629     IN PFCB Fcb,
2630     IN PFCB TerminationFcb
2631     )
2632 
2633 /*++
2634 
2635 Routine Description:
2636 
2637     This routine is used to iterate through Fcbs in a tree, from the top down.
2638 
2639     The rule is very simple:
2640 
2641         A) If you have a child, go to it, else
2642         B) If you have an older sibling, go to it, else
2643         C) Go to your parent's older sibling.
2644 
2645     If this routine is called with in invalid TerminationFcb it will fail,
2646     badly.
2647 
2648     The Termination Fcb is never returned.  If it is the root of the tree you
2649     are traversing, visit it first.
2650 
2651     This routine never returns direct ancestors of Fcb, and thus is useful when
2652     making Fcb's go away (which may tear up the tree).
2653 
2654 Arguments:
2655 
2656     Fcb - Supplies the current Fcb
2657 
2658     TerminationFcb - The Fcb at which the enumeration should (non-inclusivly)
2659         stop.  Assumed to be a directory.
2660 
2661 Return Value:
2662 
2663     The next Fcb in the enumeration, or NULL if Fcb was the final one.
2664 
2665 --*/
2666 
2667 {
2668     PFCB Sibling;
2669 
2670     PAGED_CODE();
2671     UNREFERENCED_PARAMETER( IrpContext );
2672 
2673     NT_ASSERT( FatVcbAcquiredExclusive( IrpContext, Fcb->Vcb ) ||
2674             FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) );
2675 
2676     //
2677     //  If this was a directory (ie. not a file), get the child.  If
2678     //  there aren't any children and this is our termination Fcb,
2679     //  return NULL.
2680     //
2681 
2682     if ( ((NodeType(Fcb) == FAT_NTC_DCB) ||
2683           (NodeType(Fcb) == FAT_NTC_ROOT_DCB)) &&
2684          !IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) ) {
2685 
2686         return FatGetFirstChild( Fcb );
2687     }
2688 
2689     //
2690     //  Were we only meant to do one iteration?
2691     //
2692 
2693     if ( Fcb == TerminationFcb ) {
2694 
2695         return NULL;
2696     }
2697 
2698     Sibling = FatGetNextSibling(Fcb);
2699 
2700     while (TRUE) {
2701 
2702         //
2703         //  Do we still have an "older" sibling in this directory who is
2704         //  not the termination Fcb?
2705         //
2706 
2707         if ( Sibling != NULL ) {
2708 
2709             return (Sibling != TerminationFcb) ? Sibling : NULL;
2710         }
2711 
2712         //
2713         //  OK, let's move on to out parent and see if he is the termination
2714         //  node or has any older siblings.
2715         //
2716 
2717         if ( Fcb->ParentDcb == TerminationFcb ) {
2718 
2719             return NULL;
2720         }
2721 
2722         Fcb = Fcb->ParentDcb;
2723 
2724         Sibling = FatGetNextSibling(Fcb);
2725     }
2726 }
2727 
2728 
2729 BOOLEAN
FatSwapVpb(IN PIRP_CONTEXT IrpContext,PVCB Vcb)2730 FatSwapVpb (
2731     IN PIRP_CONTEXT IrpContext,
2732     PVCB Vcb
2733     )
2734 
2735 /*++
2736 
2737 Routine Description:
2738 
2739     This routine swaps the VPB for this VCB if it has not been done already.
2740     This means the device object will get our spare VPB and we will cleanup
2741     the one used while the volume was mounted.
2742 
2743 Arguments:
2744 
2745     IrpContext - Supplies the context for the overall request.
2746 
2747     Vcb - Supplies the VCB to swap the VPB on.
2748 
2749 Return Value:
2750 
2751     TRUE - If the VPB was actually swapped.
2752 
2753     FALSE - If the VPB was already swapped.
2754 
2755 --*/
2756 
2757 {
2758     BOOLEAN Result = FALSE;
2759     PVPB OldVpb;
2760     PIO_STACK_LOCATION IrpSp;
2761 
2762 
2763     //
2764     //  Make sure we have not already swapped it.
2765     //
2766 
2767     OldVpb = Vcb->Vpb;
2768 
2769 #ifdef _MSC_VER
2770 #pragma prefast( push )
2771 #pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" )
2772 #endif
2773 
2774     if (!FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ) && OldVpb->RealDevice->Vpb == OldVpb) {
2775 
2776         //
2777         //  If not the final reference and we are forcing the disconnect,
2778         //  then swap out the Vpb.  We must preserve the REMOVE_PENDING flag
2779         //  so that the device is not remounted in the middle of a PnP remove
2780         //  operation.
2781         //
2782 
2783         NT_ASSERT( Vcb->SwapVpb != NULL );
2784 
2785         Vcb->SwapVpb->Type = IO_TYPE_VPB;
2786         Vcb->SwapVpb->Size = sizeof( VPB );
2787         Vcb->SwapVpb->RealDevice = OldVpb->RealDevice;
2788 
2789         Vcb->SwapVpb->RealDevice->Vpb = Vcb->SwapVpb;
2790 
2791         Vcb->SwapVpb->Flags = FlagOn( OldVpb->Flags, VPB_REMOVE_PENDING );
2792 
2793         //
2794         //  If we are working on a mount request, we need to make sure we update
2795         //  the VPB in the IRP, since the one it points to may no longer be valid.
2796         //
2797 
2798         if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) {
2799 
2800             //
2801             //  Get the IRP stack.
2802             //
2803 
2804             IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp );
2805 
2806             NT_ASSERT( IrpSp->FileObject == NULL );
2807 
2808             //
2809             //  Check the VPB in the IRP to see if it is the one we are swapping.
2810             //
2811 
2812             if (IrpSp->Parameters.MountVolume.Vpb == OldVpb) {
2813 
2814                 //
2815                 //  Change the IRP to point to the swap VPB.
2816                 //
2817 
2818                 IrpSp->Parameters.MountVolume.Vpb = Vcb->SwapVpb;
2819             }
2820         }
2821 
2822 #ifdef _MSC_VER
2823 #pragma prefast( pop )
2824 #endif
2825 
2826         //
2827         //  We place the volume in the Bad state (as opposed to NotMounted) so
2828         //  that it is not eligible for a remount.  Also indicate we used up
2829         //  the swap.
2830         //
2831 
2832         Vcb->SwapVpb = NULL;
2833         FatSetVcbCondition( Vcb, VcbBad);
2834         SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED );
2835 
2836         Result = TRUE;
2837     }
2838 
2839     return Result;
2840 }
2841 
2842 
_Requires_lock_held_(_Global_critical_region_)2843 _Requires_lock_held_(_Global_critical_region_)
2844 BOOLEAN
2845 FatCheckForDismount (
2846     IN PIRP_CONTEXT IrpContext,
2847     PVCB Vcb,
2848     IN BOOLEAN Force
2849     )
2850 
2851 /*++
2852 
2853 Routine Description:
2854 
2855     This routine determines if a volume is ready for deletion.  It
2856     correctly synchronizes with creates en-route to the file system.
2857 
2858 Arguments:
2859 
2860     Vcb - Supplies the volume to examine
2861 
2862     Force - Specifies whether we want this Vcb forcibly disconnected
2863         from the driver stack if it will not be deleted (a new vpb will
2864         be installed if neccesary).  Caller is responsible for making
2865         sure that the volume has been marked in such a way that attempts
2866         to operate through the realdevice are blocked (i.e., move the vcb
2867         out of the mounted state).
2868 
2869 Return Value:
2870 
2871     BOOLEAN - TRUE if the volume was deleted, FALSE otherwise.
2872 
2873 --*/
2874 
2875 {
2876     KIRQL SavedIrql;
2877     BOOLEAN VcbDeleted = FALSE;
2878 
2879 
2880     //
2881     //  If the VCB condition is good and we are not forcing, just return.
2882     //
2883 
2884     if (Vcb->VcbCondition == VcbGood && !Force) {
2885 
2886         return FALSE;
2887     }
2888 
2889     //
2890     //  Now check for a zero Vpb count on an unmounted volume.  These
2891     //  volumes will be deleted as they now have no file objects and
2892     //  there are no creates en route to this volume.
2893     //
2894 
2895     IoAcquireVpbSpinLock( &SavedIrql );
2896 
2897     if (Vcb->Vpb->ReferenceCount == Vcb->ResidualOpenCount && Vcb->OpenFileCount == 0) {
2898 
2899         PVPB Vpb = Vcb->Vpb;
2900 
2901 #if DBG
2902         UNICODE_STRING VolumeLabel;
2903 
2904         //
2905         //  Setup the VolumeLabel string
2906         //
2907 
2908         VolumeLabel.Length = Vcb->Vpb->VolumeLabelLength;
2909         VolumeLabel.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH;
2910         VolumeLabel.Buffer = &Vcb->Vpb->VolumeLabel[0];
2911 
2912         KdPrintEx((DPFLTR_FASTFAT_ID,
2913                    DPFLTR_INFO_LEVEL,
2914                    "FASTFAT: Dismounting Volume %Z\n",
2915                    &VolumeLabel));
2916 #endif // DBG
2917 
2918         //
2919         //  Swap this VCB's VPB.
2920         //
2921 
2922         FatSwapVpb( IrpContext,
2923                     Vcb );
2924 
2925         //
2926         //  Clear the VPB_MOUNTED bit.  New opens should not come in due
2927         //  to the swapped VPB, but having the flag cleared helps debugging.
2928         //  Note that we must leave the Vpb->DeviceObject field set until
2929         //  after the FatTearDownVcb call as closes will have to make their
2930         //  way back to us.
2931         //
2932 
2933         ClearFlag( Vpb->Flags, VPB_MOUNTED );
2934 
2935         //
2936         //  If this Vpb was locked, clear this flag now.
2937         //
2938 
2939         ClearFlag( Vpb->Flags, (VPB_LOCKED | VPB_DIRECT_WRITES_ALLOWED) );
2940 
2941         IoReleaseVpbSpinLock( SavedIrql );
2942 
2943         //
2944         //  We are going to attempt the dismount, so mark the VCB as having
2945         //  a dismount in progress.
2946         //
2947 
2948         NT_ASSERT( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) );
2949         SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS );
2950 
2951         //
2952         //  Close down our internal opens.
2953         //
2954 
2955         FatTearDownVcb( IrpContext,
2956                         Vcb );
2957 
2958         //
2959         //  Process any delayed closes.
2960         //
2961 
2962         FatFspClose( Vcb );
2963 
2964         //
2965         //  Grab the VPB lock again so that we can recheck our counts.
2966         //
2967 
2968         IoAcquireVpbSpinLock( &SavedIrql );
2969 
2970         //
2971         //  See if we can delete this VCB.
2972         //
2973 
2974         if (Vcb->Vpb->ReferenceCount == 0 && Vcb->InternalOpenCount == 0) {
2975 
2976             Vpb->DeviceObject = NULL;
2977 
2978             IoReleaseVpbSpinLock( SavedIrql );
2979 
2980             FatDeleteVcb( IrpContext, Vcb );
2981 
2982             IoDeleteDevice( (PDEVICE_OBJECT)
2983                             CONTAINING_RECORD( Vcb,
2984                                                VOLUME_DEVICE_OBJECT,
2985                                                Vcb ) );
2986 
2987             VcbDeleted = TRUE;
2988 
2989         } else {
2990 
2991             IoReleaseVpbSpinLock( SavedIrql );
2992 
2993             NT_ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) );
2994             ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS );
2995         }
2996 
2997     } else if (Force) {
2998 
2999         //
3000         //  The requester is forcing the issue.  We need to swap the VPB with our spare.
3001         //
3002 
3003         FatSwapVpb( IrpContext,
3004                     Vcb );
3005 
3006         IoReleaseVpbSpinLock( SavedIrql );
3007 
3008     } else {
3009 
3010         //
3011         //  Just drop the Vpb spinlock.
3012         //
3013 
3014         IoReleaseVpbSpinLock( SavedIrql );
3015     }
3016 
3017     return VcbDeleted;
3018 }
3019 
3020 
3021 VOID
FatConstructNamesInFcb(IN PIRP_CONTEXT IrpContext,PFCB Fcb,PDIRENT Dirent,PUNICODE_STRING Lfn OPTIONAL)3022 FatConstructNamesInFcb (
3023     IN PIRP_CONTEXT IrpContext,
3024     PFCB Fcb,
3025     PDIRENT Dirent,
3026     PUNICODE_STRING Lfn OPTIONAL
3027     )
3028 
3029 /*++
3030 
3031 Routine Description:
3032 
3033     This routine places the short name in the dirent in the first set of
3034     STRINGs in the Fcb.  If a long file name (Lfn) was specified, then
3035     we must decide whether we will store its Oem equivolent in the same
3036     prefix table as the short name, or rather just save the upcased
3037     version of the UNICODE string in the FCB.
3038 
3039     For looking up Fcbs, the first approach will be faster, so we want to
3040     do this as much as possible.  Here are the rules that I have thought
3041     through extensively to determine when it is safe to store only Oem
3042     version of the UNICODE name.
3043 
3044     - If the UNICODE name contains no extended characters (>0x80), use Oem.
3045 
3046     - Let U be the upcased version of the UNICODE name.
3047       Let Up(x) be the function that upcases a UNICODE string.
3048       Let Down(x) be the function that upcases a UNICODE string.
3049       Let OemToUni(x) be the function that converts an Oem string to Unicode.
3050       Let UniToOem(x) be the function that converts a Unicode string to Oem.
3051       Let BestOemFit(x) be the function that creates the Best uppercase Oem
3052         fit for the UNICODE string x.
3053 
3054       BestOemFit(x) = UniToOem(Up(OemToUni(UniToOem(x))))   <1>
3055 
3056       if (BestOemFit(U) == BestOemFit(Down(U))              <2>
3057 
3058           then I know that there exists no UNICODE string Y such that:
3059 
3060               Up(Y) == Up(U)                                <3>
3061 
3062               AND
3063 
3064               BestOemFit(U) != BestOemFit(Y)                <4>
3065 
3066       Consider string U as a collection of one character strings.  The
3067       conjecture is clearly true for each sub-string, thus it is true
3068       for the entire string.
3069 
3070       Equation <1> is what we use to convert an incoming unicode name in
3071       FatCommonCreate() to Oem.  The double conversion is done to provide
3072       better visual best fitting for characters in the Ansi code page but
3073       not in the Oem code page.  A single Nls routine is provided to do
3074       this conversion efficiently.
3075 
3076       The idea is that with U, I only have to worry about a case varient Y
3077       matching it in a unicode compare, and I have shown that any case varient
3078       of U (the set Y defined in equation <3>), when filtered through <1>
3079       (as in create), will match the Oem string defined in <1>.
3080 
3081       Thus I do not have to worry about another UNICODE string missing in
3082       the prefix lookup, but matching when comparing LFNs in the directory.
3083 
3084 Arguments:
3085 
3086     Fcb - The Fcb we are supposed to fill in.  Note that ParentDcb must
3087         already be filled in.
3088 
3089     Dirent - The gives up the short name.
3090 
3091     Lfn - If provided, this gives us the long name.
3092 
3093 Return Value:
3094 
3095     None
3096 
3097 --*/
3098 
3099 {
3100 #ifndef __REACTOS__
3101     NTSTATUS Status;
3102 #endif
3103     ULONG i;
3104 
3105 #ifndef __REACTOS__
3106     OEM_STRING OemA;
3107     OEM_STRING OemB;
3108 #endif
3109     UNICODE_STRING Unicode;
3110     POEM_STRING ShortName;
3111     POEM_STRING LongOemName;
3112     PUNICODE_STRING LongUniName;
3113 
3114     PAGED_CODE();
3115 
3116     ShortName = &Fcb->ShortName.Name.Oem;
3117 
3118     NT_ASSERT( ShortName->Buffer == NULL );
3119 
3120     _SEH2_TRY {
3121 
3122         //
3123         //  First do the short name.
3124         //
3125 
3126         //
3127         //  Copy over the case flags for the short name of the file
3128         //
3129 
3130         if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) {
3131 
3132             SetFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE);
3133 
3134         } else {
3135 
3136             ClearFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE);
3137         }
3138 
3139         if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) {
3140 
3141             SetFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE);
3142 
3143         } else {
3144 
3145             ClearFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE);
3146         }
3147 
3148         ShortName->MaximumLength = 16;
3149         ShortName->Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3150                                                       16,
3151                                                       TAG_FILENAME_BUFFER );
3152 
3153         Fat8dot3ToString( IrpContext, Dirent, FALSE, ShortName );
3154 
3155         Fcb->ShortName.FileNameDos = TRUE;
3156 
3157         //
3158         //  If no Lfn was specified, we are done.  In either case, set the
3159         //  final name length.
3160         //
3161 
3162         NT_ASSERT( Fcb->ExactCaseLongName.Buffer == NULL );
3163 
3164         if (!ARGUMENT_PRESENT(Lfn) || (Lfn->Length == 0)) {
3165 
3166             Fcb->FinalNameLength = (USHORT) RtlOemStringToCountedUnicodeSize( ShortName );
3167             Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = 0;
3168 
3169             try_return( NOTHING );
3170         }
3171 
3172         //
3173         //  If we already set up the full filename, we could be in trouble.  If the fast
3174         //  path for doing it already fired, FatSetFullFileNameInFcb, it will have missed
3175         //  this and could have built the full filename out of the shortname of the file.
3176         //
3177         //  At that point, disaster could be inevitable since the final name length will not
3178         //  match.  We use this to tell the notify package what to do - FatNotifyReportChange.
3179         //
3180 
3181         NT_ASSERT( Fcb->FullFileName.Buffer == NULL );
3182 
3183         //
3184         //  We know now we have an Lfn, save away a copy.
3185         //
3186 
3187         Fcb->FinalNameLength = Lfn->Length;
3188 
3189         Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = Lfn->Length;
3190         Fcb->ExactCaseLongName.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3191                                                                   Lfn->Length,
3192                                                                   TAG_FILENAME_BUFFER );
3193         RtlCopyMemory(Fcb->ExactCaseLongName.Buffer, Lfn->Buffer, Lfn->Length);
3194 
3195         //
3196         //  First check for no extended characters.
3197         //
3198 
3199         for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) {
3200 
3201             if (Lfn->Buffer[i] >= 0x80) {
3202 
3203                 break;
3204             }
3205         }
3206 
3207         if (i == Lfn->Length/sizeof(WCHAR)) {
3208 
3209             //
3210             //  Cool, I can go with the Oem, upcase it fast by hand.
3211             //
3212 
3213             LongOemName = &Fcb->LongName.Oem.Name.Oem;
3214 
3215 
3216             LongOemName->Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3217                                                             Lfn->Length/sizeof(WCHAR),
3218                                                             TAG_FILENAME_BUFFER );
3219             LongOemName->Length =
3220             LongOemName->MaximumLength = Lfn->Length/sizeof(WCHAR);
3221 
3222             for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) {
3223 
3224                 WCHAR c;
3225 
3226                 c = Lfn->Buffer[i];
3227 
3228 #ifdef _MSC_VER
3229 #pragma warning( push )
3230 #pragma warning( disable:4244 )
3231 #endif
3232                 LongOemName->Buffer[i] = c < 'a' ?
3233                                          (UCHAR)c :
3234                                          c <= 'z' ?
3235                                          c - (UCHAR)('a'-'A') :
3236                                          (UCHAR)c;
3237 #ifdef _MSC_VER
3238 #pragma warning( pop )
3239 #endif
3240             }
3241 
3242             //
3243             //  If this name happens to be exactly the same as the short
3244             //  name, don't add it to the splay table.
3245             //
3246 
3247             if (FatAreNamesEqual(IrpContext, *ShortName, *LongOemName) ||
3248                 (FatFindFcb( IrpContext,
3249                              &Fcb->ParentDcb->Specific.Dcb.RootOemNode,
3250                              LongOemName,
3251                              NULL) != NULL)) {
3252 
3253                 ExFreePool( LongOemName->Buffer );
3254 
3255                 LongOemName->Buffer = NULL;
3256                 LongOemName->Length =
3257                 LongOemName->MaximumLength = 0;
3258 
3259             } else {
3260 
3261                 SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME );
3262             }
3263 
3264             try_return( NOTHING );
3265         }
3266 
3267         //
3268         //  Now we have the fun part.  Make a copy of the Lfn.
3269         //
3270 
3271 #ifndef __REACTOS__
3272         OemA.Buffer = NULL;
3273         OemB.Buffer = NULL;
3274 #endif
3275         Unicode.Buffer = NULL;
3276 
3277         Unicode.Length =
3278         Unicode.MaximumLength = Lfn->Length;
3279         Unicode.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3280                                                    Lfn->Length,
3281                                                    TAG_FILENAME_BUFFER );
3282 
3283         RtlCopyMemory( Unicode.Buffer, Lfn->Buffer, Lfn->Length );
3284 
3285 #ifndef __REACTOS__
3286         Status = STATUS_SUCCESS;
3287 #endif
3288 
3289 #if TRUE
3290         //
3291         //  Unfortunately, this next block of code breaks down when you have
3292         //  two long Unicode filenames that both map to the same Oem (and are,
3293         //  well, long, i.e. are not the short names). In this case, with one
3294         //  in the prefix table first, the other will hit the common Oem
3295         //  representation.  This leads to several forms of user astonishment.
3296         //
3297         //  It isn't worth it, or probably even possible, to try to figure out
3298         //  when this is really safe to go through.  Simply omit the attempt.
3299         //
3300         //  Ex: ANSI 0x82 and 0x84 in the 1252 ANSI->UNI and 437 UNI->OEM codepages.
3301         //
3302         //      0x82 => 0x201a => 0x2c
3303         //      0x84 => 0x201e => 0x2c
3304         //
3305         //  0x2c is comma, so is FAT Oem illegal and forces shortname generation.
3306         //  Since it is otherwise well-formed by the rules articulated previously,
3307         //  we would have put 0x2c in the Oem prefix tree.  In terms of the
3308         //  argument given above, even though there exist no Y and U s.t.
3309         //
3310         //  Up(Y) == Up(U) && BestOemFit(U) != BestOemFit(Y)
3311         //
3312         //  there most certainly exist Y and U s.t.
3313         //
3314         //  Up(Y) != Up(U) && BestOemFit(U) == BestOemFit(Y)
3315         //
3316         //  and that is enough to keep us from doing this.  Note that the < 0x80
3317         //  case is OK since we know that the mapping in the OEM codepages are
3318         //  the identity in that range.
3319         //
3320         //  We still need to monocase it, though.  Do this through a full down/up
3321         //  transition.
3322         //
3323 
3324         (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE );
3325         (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE );
3326 #else
3327         //
3328         //  Downcase and convert to upcased Oem.  Only continue if we can
3329         //  convert without error.  Any error other than UNMAPPABLE_CHAR
3330         //  is a fatal error and we raise.
3331         //
3332         //  Note that even if the conversion fails, we must leave Unicode
3333         //  in an upcased state.
3334         //
3335         //  NB: The Rtl doesn't NULL .Buffer on error.
3336         //
3337 
3338         (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE );
3339         Status = RtlUpcaseUnicodeStringToCountedOemString( &OemA, &Unicode, TRUE );
3340         (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE );
3341 
3342         if (!NT_SUCCESS(Status)) {
3343 
3344             if (Status != STATUS_UNMAPPABLE_CHARACTER) {
3345 
3346                 NT_ASSERT( Status == STATUS_NO_MEMORY );
3347                 ExFreePool(Unicode.Buffer);
3348                 FatNormalizeAndRaiseStatus( IrpContext, Status );
3349             }
3350 
3351         } else {
3352 
3353             //
3354             //  The same as above except upcase.
3355             //
3356 
3357             Status = RtlUpcaseUnicodeStringToCountedOemString( &OemB, &Unicode, TRUE );
3358 
3359             if (!NT_SUCCESS(Status)) {
3360 
3361                 RtlFreeOemString( &OemA );
3362 
3363                 if (Status != STATUS_UNMAPPABLE_CHARACTER) {
3364 
3365                     NT_ASSERT( Status == STATUS_NO_MEMORY );
3366                     ExFreePool(Unicode.Buffer);
3367                     FatNormalizeAndRaiseStatus( IrpContext, Status );
3368                 }
3369             }
3370         }
3371 
3372         //
3373         //  If the final OemNames are equal, I can use save only the Oem
3374         //  name.  If the name did not map, then I have to go with the UNICODE
3375         //  name because I could get a case varient that didn't convert
3376         //  in create, but did match the LFN.
3377         //
3378 
3379         if (NT_SUCCESS(Status) && FatAreNamesEqual( IrpContext, OemA, OemB )) {
3380 
3381             //
3382             //  Cool, I can go with the Oem.  If we didn't convert correctly,
3383             //  get a fresh convert from the original LFN.
3384             //
3385 
3386             ExFreePool(Unicode.Buffer);
3387 
3388             RtlFreeOemString( &OemB );
3389 
3390             Fcb->LongName.Oem.Name.Oem = OemA;
3391 
3392             //
3393             //  If this name happens to be exactly the same as the short
3394             //  name, or a similar short name already exists don't add it
3395             //  to the splay table (note the final condition implies a
3396             //  corrupt disk.
3397             //
3398 
3399             if (FatAreNamesEqual(IrpContext, *ShortName, OemA) ||
3400                 (FatFindFcb( IrpContext,
3401                              &Fcb->ParentDcb->Specific.Dcb.RootOemNode,
3402                              &OemA,
3403                              NULL) != NULL)) {
3404 
3405                 RtlFreeOemString( &OemA );
3406 
3407             } else {
3408 
3409                 SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME );
3410             }
3411 
3412             try_return( NOTHING );
3413         }
3414 
3415         //
3416         //  The long name must be left in UNICODE.  Free the two Oem strings
3417         //  if we got here just because they weren't equal.
3418         //
3419 
3420         if (NT_SUCCESS(Status)) {
3421 
3422             RtlFreeOemString( &OemA );
3423             RtlFreeOemString( &OemB );
3424         }
3425 #endif
3426 
3427         LongUniName = &Fcb->LongName.Unicode.Name.Unicode;
3428 
3429         LongUniName->Length =
3430         LongUniName->MaximumLength = Unicode.Length;
3431         LongUniName->Buffer = Unicode.Buffer;
3432 
3433         SetFlag(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME);
3434 
3435     try_exit: NOTHING;
3436     } _SEH2_FINALLY {
3437 
3438         if (_SEH2_AbnormalTermination()) {
3439 
3440             if (ShortName->Buffer != NULL) {
3441 
3442                 ExFreePool( ShortName->Buffer );
3443                 ShortName->Buffer = NULL;
3444             }
3445 
3446         } else {
3447 
3448             //
3449             //  Creating all the names worked, so add all the names
3450             //  to the splay tree.
3451             //
3452 
3453             FatInsertName( IrpContext,
3454                            &Fcb->ParentDcb->Specific.Dcb.RootOemNode,
3455                            &Fcb->ShortName );
3456 
3457             Fcb->ShortName.Fcb = Fcb;
3458 
3459             if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME)) {
3460 
3461                 FatInsertName( IrpContext,
3462                                &Fcb->ParentDcb->Specific.Dcb.RootOemNode,
3463                                &Fcb->LongName.Oem );
3464 
3465                 Fcb->LongName.Oem.Fcb = Fcb;
3466             }
3467 
3468             if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME)) {
3469 
3470                 FatInsertName( IrpContext,
3471                                &Fcb->ParentDcb->Specific.Dcb.RootUnicodeNode,
3472                                &Fcb->LongName.Unicode );
3473 
3474                 Fcb->LongName.Unicode.Fcb = Fcb;
3475             }
3476 
3477             SetFlag(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE);
3478         }
3479     } _SEH2_END;
3480 
3481     return;
3482 }
3483 
3484 
_Requires_lock_held_(_Global_critical_region_)3485 _Requires_lock_held_(_Global_critical_region_)
3486 VOID
3487 FatCheckFreeDirentBitmap (
3488     IN PIRP_CONTEXT IrpContext,
3489     IN PDCB Dcb
3490     )
3491 
3492 /*++
3493 
3494 Routine Description:
3495 
3496     This routine checks if the size of the free dirent bitmap is
3497     sufficient to for the current directory size.  It is called
3498     whenever we grow a directory.
3499 
3500 Arguments:
3501 
3502     Dcb -  Supplies the directory in question.
3503 
3504 Return Value:
3505 
3506     None
3507 
3508 --*/
3509 
3510 {
3511     ULONG OldNumberOfDirents;
3512     ULONG NewNumberOfDirents;
3513 
3514     PAGED_CODE();
3515     UNREFERENCED_PARAMETER( IrpContext );
3516 
3517     //
3518     //  Setup the Bitmap buffer if it is not big enough already
3519     //
3520 
3521     NT_ASSERT( Dcb->Header.AllocationSize.QuadPart != FCB_LOOKUP_ALLOCATIONSIZE_HINT );
3522 
3523     OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap;
3524     NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT);
3525 
3526     //
3527     //  Do the usual unsync/sync check.
3528     //
3529 
3530     if (NewNumberOfDirents > OldNumberOfDirents) {
3531 
3532         FatAcquireDirectoryFileMutex( Dcb->Vcb );
3533 
3534         _SEH2_TRY {
3535 
3536             PULONG OldBitmapBuffer;
3537             PULONG BitmapBuffer;
3538 
3539             ULONG BytesInBitmapBuffer;
3540             ULONG BytesInOldBitmapBuffer;
3541 
3542             OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap;
3543             NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT);
3544 
3545             if (NewNumberOfDirents > OldNumberOfDirents) {
3546 
3547                 //
3548                 //  Remember the old bitmap
3549                 //
3550 
3551                 OldBitmapBuffer = Dcb->Specific.Dcb.FreeDirentBitmap.Buffer;
3552 
3553                 //
3554                 //  Now make a new bitmap bufffer
3555                 //
3556 
3557                 BytesInBitmapBuffer = NewNumberOfDirents / 8;
3558 
3559                 BytesInOldBitmapBuffer = OldNumberOfDirents / 8;
3560 
3561                 if (DCB_UNION_SLACK_SPACE >= BytesInBitmapBuffer) {
3562 
3563                     BitmapBuffer = &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0];
3564 
3565                 } else {
3566 
3567                     BitmapBuffer = FsRtlAllocatePoolWithTag( PagedPool,
3568                                                              BytesInBitmapBuffer,
3569                                                              TAG_DIRENT_BITMAP );
3570                 }
3571 
3572                 //
3573                 //  Copy the old buffer to the new buffer, free the old one, and zero
3574                 //  the rest of the new one.  Only do the first two steps though if
3575                 //  we moved out of the initial buffer.
3576                 //
3577 
3578                 if ((OldNumberOfDirents != 0) &&
3579                     (BitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) {
3580 
3581                     RtlCopyMemory( BitmapBuffer,
3582                                    OldBitmapBuffer,
3583                                    BytesInOldBitmapBuffer );
3584 
3585                     if (OldBitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0]) {
3586 
3587                         ExFreePool( OldBitmapBuffer );
3588                     }
3589                 }
3590 
3591                 NT_ASSERT( BytesInBitmapBuffer > BytesInOldBitmapBuffer );
3592 
3593                 RtlZeroMemory( (PUCHAR)BitmapBuffer + BytesInOldBitmapBuffer,
3594                                BytesInBitmapBuffer - BytesInOldBitmapBuffer );
3595 
3596                 //
3597                 //  Now initialize the new bitmap.
3598                 //
3599 
3600                 RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap,
3601                                      BitmapBuffer,
3602                                      NewNumberOfDirents );
3603             }
3604 
3605         } _SEH2_FINALLY {
3606 
3607             FatReleaseDirectoryFileMutex( Dcb->Vcb );
3608         } _SEH2_END;
3609     }
3610 }
3611 
3612 
3613 BOOLEAN
FatIsHandleCountZero(IN PIRP_CONTEXT IrpContext,IN PVCB Vcb)3614 FatIsHandleCountZero (
3615     IN PIRP_CONTEXT IrpContext,
3616     IN PVCB Vcb
3617     )
3618 
3619 /*++
3620 
3621 Routine Description:
3622 
3623     This routine decides if the handle count on the volume is zero.
3624 
3625 Arguments:
3626 
3627     Vcb - The volume in question
3628 
3629 Return Value:
3630 
3631     BOOLEAN - TRUE if there are no open handles on the volume, FALSE
3632               otherwise.
3633 
3634 --*/
3635 
3636 {
3637     PFCB Fcb;
3638 
3639     PAGED_CODE();
3640 
3641     Fcb = Vcb->RootDcb;
3642 
3643     while (Fcb != NULL) {
3644 
3645         if (Fcb->UncleanCount != 0) {
3646 
3647             return FALSE;
3648         }
3649 
3650         Fcb = FatGetNextFcbTopDown(IrpContext, Fcb, Vcb->RootDcb);
3651     }
3652 
3653     return TRUE;
3654 }
3655 
3656 
3657 PCLOSE_CONTEXT
3658 
FatAllocateCloseContext(OPTIONAL PVCB Vcb)3659 FatAllocateCloseContext(
3660     OPTIONAL PVCB Vcb
3661     )
3662 /*++
3663 
3664 Routine Description:
3665 
3666     This routine preallocates a close context, presumeably on behalf
3667     of a fileobject which does not have a structure we can embed one
3668     in.
3669 
3670 Arguments:
3671 
3672     None.
3673 
3674 Return Value:
3675 
3676     None.
3677 
3678 --*/
3679 {
3680     PAGED_CODE();
3681     UNREFERENCED_PARAMETER( Vcb );
3682 
3683 #if DBG
3684     if (ARGUMENT_PRESENT(Vcb)) {
3685 
3686         NT_ASSERT( 0 != Vcb->CloseContextCount);
3687         InterlockedDecrement( (LONG*)&Vcb->CloseContextCount);
3688     }
3689 #endif
3690     return (PCLOSE_CONTEXT)ExInterlockedPopEntrySList( &FatCloseContextSList,
3691                                                        &FatData.GeneralSpinLock );
3692 }
3693 
3694 
3695 VOID
FatPreallocateCloseContext(PVCB Vcb)3696 FatPreallocateCloseContext (
3697     PVCB Vcb
3698     )
3699 
3700 /*++
3701 
3702 Routine Description:
3703 
3704     This routine preallocates a close context, presumeably on behalf
3705     of a fileobject which does not have a structure we can embed one
3706     in.
3707 
3708 Arguments:
3709 
3710     None.
3711 
3712 Return Value:
3713 
3714     None.
3715 
3716 --*/
3717 
3718 {
3719     PCLOSE_CONTEXT CloseContext;
3720 
3721     PAGED_CODE();
3722 
3723     UNREFERENCED_PARAMETER( Vcb );
3724 
3725     CloseContext = FsRtlAllocatePoolWithTag( PagedPool,
3726                                              sizeof(CLOSE_CONTEXT),
3727                                              TAG_FAT_CLOSE_CONTEXT );
3728 
3729     ExInterlockedPushEntrySList( &FatCloseContextSList,
3730                                  (PSLIST_ENTRY) CloseContext,
3731                                  &FatData.GeneralSpinLock );
3732 
3733     DbgDoit( InterlockedIncrement( (LONG*)&Vcb->CloseContextCount));
3734 }
3735 
3736 
3737 
3738 VOID
FatEnsureStringBufferEnough(_Inout_ PVOID String,_In_ USHORT DesiredBufferSize)3739 FatEnsureStringBufferEnough (
3740     _Inout_ PVOID String,
3741     _In_ USHORT DesiredBufferSize
3742     )
3743 
3744 /*++
3745 
3746 Routine Description:
3747 
3748     Ensures that a string string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING)
3749     has a buffer >= DesiredBufferSize,  allocating from pool if neccessary.  Any
3750     existing pool buffer will be freed if a new one is allocated.
3751 
3752     NOTE: No copy of old buffer contents is performed on reallocation.
3753 
3754     Will raise on allocation failure.
3755 
3756 Arguments:
3757 
3758     String - pointer to string structure
3759 
3760     DesiredBufferSize - (bytes) minimum required buffer size
3761 
3762 --*/
3763 
3764 {
3765     PSTRING LocalString = String;
3766 
3767     PAGED_CODE();
3768 
3769     if (LocalString->MaximumLength < DesiredBufferSize)  {
3770 
3771         FatFreeStringBuffer( LocalString);
3772 
3773         LocalString->Buffer = FsRtlAllocatePoolWithTag( PagedPool,
3774                                                         DesiredBufferSize,
3775                                                         TAG_DYNAMIC_NAME_BUFFER);
3776         NT_ASSERT( LocalString->Buffer);
3777 
3778         LocalString->MaximumLength = DesiredBufferSize;
3779     }
3780 }
3781 
3782 
3783 VOID
FatFreeStringBuffer(_Inout_ PVOID String)3784 FatFreeStringBuffer (
3785     _Inout_ PVOID String
3786     )
3787 
3788 /*++
3789 
3790 Routine Description:
3791 
3792     Frees the buffer of an string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING)
3793     structure if it is not within the current thread's stack limits.
3794 
3795     Regardless of action performed,  on exit String->Buffer will be set to NULL and
3796     String->MaximumLength to zero.
3797 
3798 Arguments:
3799 
3800     String - pointer to string structure
3801 
3802 --*/
3803 
3804 {
3805     ULONG_PTR High, Low;
3806     PSTRING LocalString = String;
3807 
3808     PAGED_CODE();
3809 
3810     if (NULL != LocalString->Buffer)  {
3811 
3812         IoGetStackLimits( &Low, &High );
3813 
3814         if (((ULONG_PTR)(LocalString->Buffer) < Low) ||
3815             ((ULONG_PTR)(LocalString->Buffer) > High))  {
3816 
3817             ExFreePool( LocalString->Buffer);
3818         }
3819 
3820         LocalString->Buffer = NULL;
3821     }
3822 
3823     LocalString->MaximumLength = LocalString->Length = 0;
3824 }
3825 
3826 
3827 BOOLEAN
FatScanForDataTrack(IN PIRP_CONTEXT IrpContext,IN PDEVICE_OBJECT TargetDeviceObject)3828 FatScanForDataTrack(
3829     IN PIRP_CONTEXT IrpContext,
3830     IN PDEVICE_OBJECT TargetDeviceObject
3831     )
3832 
3833 /*++
3834 
3835 Routine Description:
3836 
3837     This routine is called to verify and process the TOC for this disk.
3838 
3839     FAT queries for the TOC to avoid trying to mount on CD-DA/CD-E media,  Doing data reads on
3840     audio/leadin of that media sends a lot of drives into what could charitably be called
3841     "conniptions" which take a couple seconds to clear and would also convince FAT that the
3842     device was busted, and fail the mount (not letting CDFS get its crack).
3843 
3844     There is special handling of PD media.  These things fail the TOC read, but return
3845     a special error code so FAT knows to continue to try the mount anyway.
3846 
3847 Arguments:
3848 
3849     TargetDeviceObject - Device object to send TOC request to.
3850 
3851 Return Value:
3852 
3853     BOOLEAN - TRUE if we found a TOC with a single data track.
3854 
3855 --*/
3856 
3857 {
3858     NTSTATUS Status;
3859     IO_STATUS_BLOCK Iosb;
3860 
3861     ULONG LocalTrackCount;
3862     ULONG LocalTocLength;
3863 
3864     PCDROM_TOC CdromToc;
3865     BOOLEAN Result = FALSE;
3866 
3867     PAGED_CODE();
3868 
3869     CdromToc = FsRtlAllocatePoolWithTag( PagedPool,
3870                                          sizeof( CDROM_TOC ),
3871                                          TAG_IO_BUFFER );
3872 
3873     RtlZeroMemory( CdromToc, sizeof( CDROM_TOC ));
3874 
3875     _SEH2_TRY {
3876 
3877         //
3878         //  Go ahead and read the table of contents
3879         //
3880 
3881         Status = FatPerformDevIoCtrl( IrpContext,
3882                                      IOCTL_CDROM_READ_TOC,
3883                                      TargetDeviceObject,
3884                                      NULL,
3885                                      0,
3886                                      CdromToc,
3887                                      sizeof( CDROM_TOC ),
3888                                      FALSE,
3889                                      TRUE,
3890                                      &Iosb );
3891 
3892         //
3893         //  Nothing to process if this request fails.
3894         //
3895 
3896         if (Status != STATUS_SUCCESS) {
3897 
3898             //
3899             //  If we get the special error indicating a failed TOC read on PD media just
3900             //  plow ahead with the mount (see comments above).
3901             //
3902 
3903             if ((Status == STATUS_IO_DEVICE_ERROR) || (Status == STATUS_INVALID_DEVICE_REQUEST)) {
3904 
3905                 Result = TRUE;
3906 
3907             }
3908 
3909             try_leave( NOTHING );
3910         }
3911 
3912         //
3913         //  Get the number of tracks and stated size of this structure.
3914         //
3915 
3916         LocalTrackCount = CdromToc->LastTrack - CdromToc->FirstTrack + 1;
3917         LocalTocLength = PtrOffset( CdromToc, &CdromToc->TrackData[LocalTrackCount + 1] );
3918 
3919         //
3920         //  Get out if there is an immediate problem with the TOC,  or more than
3921         //  one track.
3922         //
3923 
3924         if ((LocalTocLength > Iosb.Information) ||
3925             (CdromToc->FirstTrack > CdromToc->LastTrack) ||
3926             (LocalTrackCount != 1)) {
3927 
3928             try_leave( NOTHING);
3929         }
3930 
3931         //
3932         //  Is it a data track?  DVD-RAM reports single,  data,  track.
3933         //
3934 
3935         Result = BooleanFlagOn( CdromToc->TrackData[ 0].Control, 0x04 );
3936     }
3937     _SEH2_FINALLY {
3938 
3939         ExFreePool( CdromToc);
3940     } _SEH2_END;
3941 
3942     return Result;
3943 }
3944 
3945 
3946