3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the Fat in-memory data structure manipulation
20 // The Bug check file id for this module
23 #define BugCheckFileId (FAT_BUG_CHECK_STRUCSUP)
26 // The debug trace level
29 #define Dbg (DEBUG_TRACE_STRUCSUP)
31 #define FillMemory(BUF,SIZ,MASK) { \
33 for (i = 0; i < (((SIZ)/4) - 1); i += 2) { \
34 ((PULONG)(BUF))[i] = (MASK); \
35 ((PULONG)(BUF))[i+1] = (ULONG)PsGetCurrentThread(); \
39 #define IRP_CONTEXT_HEADER (sizeof( IRP_CONTEXT ) * 0x10000 + FAT_NTC_IRP_CONTEXT)
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.
49 // Fortunately, the hit rates on the Fcb/Ccb lists weren't stunning.
52 #define FAT_FILL_FREE 0
59 return (PCCB
) FsRtlAllocatePoolWithTag( PagedPool
, sizeof(CCB
), TAG_CCB
);
69 RtlFillMemoryUlong(Ccb
, sizeof(CCB
), FAT_FILL_FREE
);
80 return (PFCB
) FsRtlAllocatePoolWithTag( PagedPool
, sizeof(FCB
), TAG_FCB
);
90 RtlFillMemoryUlong(Fcb
, sizeof(FCB
), FAT_FILL_FREE
);
98 FatAllocateNonPagedFcb (
101 return (PNON_PAGED_FCB
) ExAllocateFromNPagedLookasideList( &FatNonPagedFcbLookasideList
);
107 PNON_PAGED_FCB NonPagedFcb
111 RtlFillMemoryUlong(NonPagedFcb
, sizeof(NON_PAGED_FCB
), FAT_FILL_FREE
);
114 ExFreeToNPagedLookasideList( &FatNonPagedFcbLookasideList
, (PVOID
) NonPagedFcb
);
119 FatAllocateResource (
124 Resource
= (PERESOURCE
) ExAllocateFromNPagedLookasideList( &FatEResourceLookasideList
);
126 ExInitializeResourceLite( Resource
);
134 IN PERESOURCE Resource
137 ExDeleteResourceLite( Resource
);
140 RtlFillMemoryUlong(Resource
, sizeof(ERESOURCE
), FAT_FILL_FREE
);
143 ExFreeToNPagedLookasideList( &FatEResourceLookasideList
, (PVOID
) Resource
);
148 FatAllocateIrpContext (
151 return (PIRP_CONTEXT
) ExAllocateFromNPagedLookasideList( &FatIrpContextLookasideList
);
157 IN PIRP_CONTEXT IrpContext
161 RtlFillMemoryUlong(IrpContext
, sizeof(IRP_CONTEXT
), FAT_FILL_FREE
);
164 ExFreeToNPagedLookasideList( &FatIrpContextLookasideList
, (PVOID
) IrpContext
);
168 #pragma alloc_text(PAGE, FatInitializeVcb)
169 #pragma alloc_text(PAGE, FatTearDownVcb)
170 #pragma alloc_text(PAGE, FatDeleteVcb)
171 #pragma alloc_text(PAGE, FatCreateRootDcb)
172 #pragma alloc_text(PAGE, FatCreateFcb)
173 #pragma alloc_text(PAGE, FatCreateDcb)
174 #pragma alloc_text(PAGE, FatDeleteFcb)
175 #pragma alloc_text(PAGE, FatCreateCcb)
176 #pragma alloc_text(PAGE, FatDeallocateCcbStrings)
177 #pragma alloc_text(PAGE, FatDeleteCcb)
178 #pragma alloc_text(PAGE, FatGetNextFcbTopDown)
179 #pragma alloc_text(PAGE, FatGetNextFcbBottomUp)
180 #pragma alloc_text(PAGE, FatConstructNamesInFcb)
181 #pragma alloc_text(PAGE, FatCheckFreeDirentBitmap)
182 #pragma alloc_text(PAGE, FatCreateIrpContext)
183 #pragma alloc_text(PAGE, FatDeleteIrpContext_Real)
184 #pragma alloc_text(PAGE, FatIsHandleCountZero)
185 #pragma alloc_text(PAGE, FatAllocateCloseContext)
186 #pragma alloc_text(PAGE, FatPreallocateCloseContext)
187 #pragma alloc_text(PAGE, FatEnsureStringBufferEnough)
188 #pragma alloc_text(PAGE, FatFreeStringBuffer)
189 #pragma alloc_text(PAGE, FatScanForDataTrack)
193 _Requires_lock_held_(_Global_critical_region_
)
196 IN PIRP_CONTEXT IrpContext
,
198 IN PDEVICE_OBJECT TargetDeviceObject
,
200 IN PDEVICE_OBJECT FsDeviceObject
207 This routine initializes and inserts a new Vcb record into the in-memory
208 data structure. The Vcb record "hangs" off the end of the Volume device
209 object and must be allocated by our caller.
213 Vcb - Supplies the address of the Vcb record being initialized.
215 TargetDeviceObject - Supplies the address of the target device object to
216 associate with the Vcb record.
218 Vpb - Supplies the address of the Vpb to associate with the Vcb record.
220 FsDeviceObject - The filesystem device object that the mount was directed
230 CC_FILE_SIZES FileSizes
;
231 PDEVICE_OBJECT RealDevice
;
234 STORAGE_HOTPLUG_INFO HotplugInfo
;
235 STORAGE_DEVICE_NUMBER StorDeviceNumber
;
239 // The following variables are used for abnormal unwind
242 PLIST_ENTRY UnwindEntryList
= NULL
;
243 PERESOURCE UnwindResource
= NULL
;
244 PERESOURCE UnwindResource2
= NULL
;
245 PFILE_OBJECT UnwindFileObject
= NULL
;
246 PFILE_OBJECT UnwindCacheMap
= NULL
;
247 BOOLEAN UnwindWeAllocatedMcb
= FALSE
;
248 PFILE_SYSTEM_STATISTICS UnwindStatistics
= NULL
;
249 BOOLEAN UnwindWeAllocatedBadBlockMap
= FALSE
;
250 BOOLEAN CloseContextAllocated
= FALSE
;
253 UNREFERENCED_PARAMETER( FsDeviceObject
);
255 DebugTrace(+1, Dbg
, "FatInitializeVcb, Vcb = %p\n", Vcb
);
260 // We start by first zeroing out all of the VCB, this will guarantee
261 // that any stale data is wiped clean
264 RtlZeroMemory( Vcb
, sizeof(VCB
) );
267 // Set the proper node type code and node byte size
270 Vcb
->VolumeFileHeader
.NodeTypeCode
= FAT_NTC_VCB
;
271 Vcb
->VolumeFileHeader
.NodeByteSize
= sizeof(VCB
);
274 // Initialize the tunneling cache
277 FsRtlInitializeTunnelCache(&Vcb
->Tunnel
);
280 // Insert this Vcb record on the FatData.VcbQueue
283 NT_ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
287 #pragma prefast( push )
288 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
289 #pragma prefast( disable: 28193, "this will always wait" )
292 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
295 #pragma prefast( pop )
298 InsertTailList( &FatData
.VcbQueue
, &Vcb
->VcbLinks
);
299 FatReleaseGlobal( IrpContext
);
300 UnwindEntryList
= &Vcb
->VcbLinks
;
303 // Set the Target Device Object, Vpb, and Vcb State fields
307 ObReferenceObject( TargetDeviceObject
);
308 Vcb
->TargetDeviceObject
= TargetDeviceObject
;
311 Vcb
->CurrentDevice
= Vpb
->RealDevice
;
314 // Set the removable media and defflush flags based on the storage
315 // inquiry and the old characteristic bits.
318 Status
= FatPerformDevIoCtrl( IrpContext
,
319 IOCTL_STORAGE_GET_HOTPLUG_INFO
,
329 if (NT_SUCCESS( Status
)) {
331 if (HotplugInfo
.MediaRemovable
) {
333 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
);
337 // If the media or device is hot-pluggable, then set this flag.
340 if (HotplugInfo
.MediaHotplug
|| HotplugInfo
.DeviceHotplug
) {
342 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_HOTPLUGGABLE
);
345 if (!HotplugInfo
.WriteCacheEnableOverride
) {
348 // If the device or media is hotplug and the override is not
349 // set, force defflush behavior for the device.
352 if (HotplugInfo
.MediaHotplug
|| HotplugInfo
.DeviceHotplug
) {
354 NT_ASSERT( FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_HOTPLUGGABLE
));
356 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
);
359 // Now, for removables that claim to be lockable, lob a lock
360 // request and see if it works. There can unfortunately be
361 // transient, media dependent reasons that it can fail. If
362 // it does not, we must force defflush on.
365 } else if (HotplugInfo
.MediaRemovable
&&
366 !HotplugInfo
.MediaHotplug
) {
368 Status
= FatToggleMediaEjectDisable( IrpContext
, Vcb
, TRUE
);
370 if (!NT_SUCCESS( Status
)) {
372 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
);
376 (VOID
)FatToggleMediaEjectDisable( IrpContext
, Vcb
, FALSE
);
381 if (FlagOn(Vpb
->RealDevice
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
383 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
);
387 // Make sure we turn on deferred flushing for floppies like we always
391 if (FlagOn(Vpb
->RealDevice
->Characteristics
, FILE_FLOPPY_DISKETTE
)) {
393 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
);
397 // Query the storage device number.
400 Status
= FatPerformDevIoCtrl( IrpContext
,
401 IOCTL_STORAGE_GET_DEVICE_NUMBER
,
406 sizeof(StorDeviceNumber
),
411 if (NT_SUCCESS( Status
)) {
413 Vcb
->DeviceNumber
= StorDeviceNumber
.DeviceNumber
;
417 Vcb
->DeviceNumber
= (ULONG
)(-1);
420 FatSetVcbCondition( Vcb
, VcbGood
);
423 // Initialize the resource variable for the Vcb
426 ExInitializeResourceLite( &Vcb
->Resource
);
427 UnwindResource
= &Vcb
->Resource
;
429 ExInitializeResourceLite( &Vcb
->ChangeBitMapResource
);
430 UnwindResource2
= &Vcb
->ChangeBitMapResource
;
433 // Initialize the free cluster bitmap mutex.
436 ExInitializeFastMutex( &Vcb
->FreeClusterBitMapMutex
);
439 // Create the special file object for the virtual volume file with a close
440 // context, its pointers back to the Vcb and the section object pointer.
442 // We don't have to unwind the close context. That will happen in the close
443 // path automatically.
446 RealDevice
= Vcb
->CurrentDevice
;
448 FatPreallocateCloseContext(Vcb
);
449 CloseContextAllocated
= TRUE
;
451 Vcb
->VirtualVolumeFile
= UnwindFileObject
= IoCreateStreamFileObject( NULL
, RealDevice
);
453 FatSetFileObject( Vcb
->VirtualVolumeFile
,
459 // Remember this internal, residual open.
462 InterlockedIncrement( (LONG
*)&(Vcb
->InternalOpenCount
) );
463 InterlockedIncrement( (LONG
*)&(Vcb
->ResidualOpenCount
) );
465 Vcb
->VirtualVolumeFile
->SectionObjectPointer
= &Vcb
->SectionObjectPointers
;
467 Vcb
->VirtualVolumeFile
->ReadAccess
= TRUE
;
468 Vcb
->VirtualVolumeFile
->WriteAccess
= TRUE
;
469 Vcb
->VirtualVolumeFile
->DeleteAccess
= TRUE
;
472 // Initialize the notify structures.
475 InitializeListHead( &Vcb
->DirNotifyList
);
477 FsRtlNotifyInitializeSync( &Vcb
->NotifySync
);
480 // Initialize the Cache Map for the volume file. The size is
481 // initially set to that of our first read. It will be extended
482 // when we know how big the Fat is.
485 FileSizes
.AllocationSize
.QuadPart
=
486 FileSizes
.FileSize
.QuadPart
= sizeof(PACKED_BOOT_SECTOR
);
487 FileSizes
.ValidDataLength
= FatMaxLarge
;
489 FatInitializeCacheMap( Vcb
->VirtualVolumeFile
,
492 &FatData
.CacheManagerNoOpCallbacks
,
495 UnwindCacheMap
= Vcb
->VirtualVolumeFile
;
498 // Initialize the structure that will keep track of dirty fat sectors.
499 // The largest possible Mcb structures are less than 1K, so we use
503 FsRtlInitializeLargeMcb( &Vcb
->DirtyFatMcb
, PagedPool
);
505 UnwindWeAllocatedMcb
= TRUE
;
508 // Initialize the structure that will keep track of bad clusters on the volume.
510 // It will be empty until it is populated by FSCTL_GET_RETRIEVAL_POINTERS with a volume handle.
513 FsRtlInitializeLargeMcb( &Vcb
->BadBlockMcb
, PagedPool
);
514 UnwindWeAllocatedBadBlockMap
= TRUE
;
517 // Set the cluster index hint to the first valid cluster of a fat: 2
520 Vcb
->ClusterHint
= 2;
523 // Initialize the directory stream file object creation event.
524 // This event is also "borrowed" for async non-cached writes.
527 ExInitializeFastMutex( &Vcb
->DirectoryFileCreationMutex
);
530 // Initialize the clean volume callback Timer and DPC.
533 KeInitializeTimer( &Vcb
->CleanVolumeTimer
);
535 KeInitializeDpc( &Vcb
->CleanVolumeDpc
, FatCleanVolumeDpc
, Vcb
);
538 // Initialize the performance counters.
542 Vcb
->Statistics
= FsRtlAllocatePoolWithTag( NonPagedPoolNx
,
544 Vcb
->Statistics
= FsRtlAllocatePoolWithTag( NonPagedPool
,
546 sizeof(FILE_SYSTEM_STATISTICS
) * FatData
.NumberProcessors
,
548 UnwindStatistics
= Vcb
->Statistics
;
550 RtlZeroMemory( Vcb
->Statistics
, sizeof(FILE_SYSTEM_STATISTICS
) * FatData
.NumberProcessors
);
552 for (i
= 0; i
< FatData
.NumberProcessors
; i
+= 1) {
553 Vcb
->Statistics
[i
].Common
.FileSystemType
= FILESYSTEM_STATISTICS_TYPE_FAT
;
554 Vcb
->Statistics
[i
].Common
.Version
= 1;
555 Vcb
->Statistics
[i
].Common
.SizeOfCompleteStructure
=
556 sizeof(FILE_SYSTEM_STATISTICS
);
560 // Pick up a VPB right now so we know we can pull this filesystem stack off
561 // of the storage stack on demand.
565 Vcb
->SwapVpb
= FsRtlAllocatePoolWithTag( NonPagedPoolNx
,
567 Vcb
->SwapVpb
= FsRtlAllocatePoolWithTag( NonPagedPool
,
572 RtlZeroMemory( Vcb
->SwapVpb
, sizeof( VPB
) );
575 // Initialize the close queue listheads.
578 InitializeListHead( &Vcb
->AsyncCloseList
);
579 InitializeListHead( &Vcb
->DelayedCloseList
);
582 // Initialize the Advanced FCB Header
585 ExInitializeFastMutex( &Vcb
->AdvancedFcbHeaderMutex
);
586 FsRtlSetupAdvancedHeader( &Vcb
->VolumeFileHeader
,
587 &Vcb
->AdvancedFcbHeaderMutex
);
591 // With the Vcb now set up, set the IrpContext Vcb field.
594 IrpContext
->Vcb
= Vcb
;
598 DebugUnwind( FatInitializeVcb
);
601 // If this is an abnormal termination then undo our work
604 if (_SEH2_AbnormalTermination()) {
606 if (UnwindCacheMap
!= NULL
) { FatSyncUninitializeCacheMap( IrpContext
, UnwindCacheMap
); }
607 if (UnwindFileObject
!= NULL
) { ObDereferenceObject( UnwindFileObject
); }
608 if (UnwindResource
!= NULL
) { FatDeleteResource( UnwindResource
); }
609 if (UnwindResource2
!= NULL
) { FatDeleteResource( UnwindResource2
); }
610 if (UnwindWeAllocatedMcb
) { FsRtlUninitializeLargeMcb( &Vcb
->DirtyFatMcb
); }
611 if (UnwindWeAllocatedBadBlockMap
) { FsRtlUninitializeLargeMcb(&Vcb
->BadBlockMcb
); }
612 if (UnwindEntryList
!= NULL
) {
614 #pragma prefast( suppress: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
616 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
617 RemoveEntryList( UnwindEntryList
);
618 FatReleaseGlobal( IrpContext
);
620 if (UnwindStatistics
!= NULL
) { ExFreePool( UnwindStatistics
); }
623 // Cleanup the close context we preallocated above.
626 if (CloseContextAllocated
&& (Vcb
->VirtualVolumeFile
== NULL
)) {
629 // FatAllocateCloseContext does not allocate memory, it
630 // pulls a close context off the preallocated slist queue.
632 // Doing this here is necessary to balance out the one we
633 // preallocated for the Vcb earlier in this function, but
634 // only if we failed to create the virtual volume file.
636 // If VirtualVolumeFile is not NULL, then this CloseContext
637 // will get cleaned up when the close comes in for it during
641 PCLOSE_CONTEXT CloseContext
= FatAllocateCloseContext(Vcb
);
643 ExFreePool( CloseContext
);
644 CloseContextAllocated
= FALSE
;
648 DebugTrace(-1, Dbg
, "FatInitializeVcb -> VOID\n", 0);
652 // and return to our caller
655 UNREFERENCED_PARAMETER( IrpContext
);
663 IN PIRP_CONTEXT IrpContext
,
671 This routine tries to remove all internal opens from the volume.
675 IrpContext - Supplies the context for the overall request.
677 Vcb - Supplies the Vcb to be torn down.
686 PFILE_OBJECT DirectoryFileObject
;
692 // Get rid of the virtual volume file, if we need to.
695 if (Vcb
->VirtualVolumeFile
!= NULL
) {
698 // Uninitialize the cache
701 CcUninitializeCacheMap( Vcb
->VirtualVolumeFile
,
705 FsRtlTeardownPerStreamContexts( &Vcb
->VolumeFileHeader
);
707 ObDereferenceObject( Vcb
->VirtualVolumeFile
);
709 Vcb
->VirtualVolumeFile
= NULL
;
713 // Close down the EA file.
716 FatCloseEaFile( IrpContext
, Vcb
, FALSE
);
719 // Close down the root directory stream..
722 if (Vcb
->RootDcb
!= NULL
) {
724 DirectoryFileObject
= Vcb
->RootDcb
->Specific
.Dcb
.DirectoryFile
;
726 if (DirectoryFileObject
!= NULL
) {
729 // Tear down this directory file.
732 CcUninitializeCacheMap( DirectoryFileObject
,
736 Vcb
->RootDcb
->Specific
.Dcb
.DirectoryFile
= NULL
;
737 ObDereferenceObject( DirectoryFileObject
);
742 // The VCB can no longer be used.
745 FatSetVcbCondition( Vcb
, VcbBad
);
751 IN PIRP_CONTEXT IrpContext
,
759 This routine removes the Vcb record from Fat's in-memory data
760 structures. It also will remove all associated underlings
765 Vcb - Supplies the Vcb to be removed
778 DebugTrace(+1, Dbg
, "FatDeleteVcb, Vcb = %p\n", Vcb
);
781 // If the IrpContext points to the VCB being deleted NULL out the stail
785 if (IrpContext
->Vcb
== Vcb
) {
787 IrpContext
->Vcb
= NULL
;
792 // Chuck the backpocket Vpb we kept just in case.
797 ExFreePool( Vcb
->SwapVpb
);
802 // Free the VPB, if we need to.
805 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
)) {
808 // We swapped the VPB, so we need to free the main one.
811 ExFreePool( Vcb
->Vpb
);
814 if (Vcb
->VolumeGuidPath
.Buffer
) {
815 ExFreePool( Vcb
->VolumeGuidPath
.Buffer
);
816 Vcb
->VolumeGuidPath
.Buffer
= NULL
;
820 // Remove this record from the global list of all Vcb records.
821 // Note that the global lock must already be held when calling
825 RemoveEntryList( &(Vcb
->VcbLinks
) );
828 // Make sure the direct access open count is zero, and the open file count
832 if ((Vcb
->DirectAccessOpenCount
!= 0) || (Vcb
->OpenFileCount
!= 0)) {
835 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
837 FatBugCheck( 0, 0, 0 );
841 // Remove the EaFcb and dereference the Fcb for the Ea file if it
845 if (Vcb
->EaFcb
!= NULL
) {
847 Vcb
->EaFcb
->OpenCount
= 0;
848 FatDeleteFcb( IrpContext
, &Vcb
->EaFcb
);
852 // Remove the Root Dcb
855 if (Vcb
->RootDcb
!= NULL
) {
858 // Rundown stale child Fcbs that may be hanging around. Yes, this
859 // can happen. No, the create path isn't perfectly defensive about
860 // tearing down branches built up on creates that don't wind up
861 // succeeding. Normal system operation usually winds up having
862 // cleaned them out through re-visiting, but ...
864 // Just pick off Fcbs from the bottom of the tree until we run out.
865 // Then we delete the root Dcb.
868 while( (Fcb
= FatGetNextFcbBottomUp( IrpContext
, NULL
, Vcb
->RootDcb
)) != Vcb
->RootDcb
) {
870 FatDeleteFcb( IrpContext
, &Fcb
);
873 FatDeleteFcb( IrpContext
, &Vcb
->RootDcb
);
877 // Uninitialize the notify sychronization object.
880 FsRtlNotifyUninitializeSync( &Vcb
->NotifySync
);
883 // Uninitialize the resource variable for the Vcb
886 FatDeleteResource( &Vcb
->Resource
);
887 FatDeleteResource( &Vcb
->ChangeBitMapResource
);
890 // If allocation support has been setup, free it.
893 if (Vcb
->FreeClusterBitMap
.Buffer
!= NULL
) {
895 FatTearDownAllocationSupport( IrpContext
, Vcb
);
899 // UnInitialize the Mcb structure that kept track of dirty fat sectors.
902 FsRtlUninitializeLargeMcb( &Vcb
->DirtyFatMcb
);
905 // Uninitialize the Mcb structure that kept track of bad sectors.
908 FsRtlUninitializeLargeMcb( &Vcb
->BadBlockMcb
);
911 // Free the pool for the stached copy of the boot sector
914 if ( Vcb
->First0x24BytesOfBootSector
) {
916 ExFreePool( Vcb
->First0x24BytesOfBootSector
);
917 Vcb
->First0x24BytesOfBootSector
= NULL
;
921 // Cancel the CleanVolume Timer and Dpc
924 (VOID
)KeCancelTimer( &Vcb
->CleanVolumeTimer
);
926 (VOID
)KeRemoveQueueDpc( &Vcb
->CleanVolumeDpc
);
929 // Free the performance counters memory
932 ExFreePool( Vcb
->Statistics
);
935 // Clean out the tunneling cache
938 FsRtlDeleteTunnelCache(&Vcb
->Tunnel
);
941 // Dereference the target device object.
944 ObDereferenceObject( Vcb
->TargetDeviceObject
);
947 // We better have used all the close contexts we allocated. There could be
948 // one remaining if we're doing teardown due to a final close coming in on
949 // a directory file stream object. It will be freed on the way back up.
952 NT_ASSERT( Vcb
->CloseContextCount
<= 1);
955 // And zero out the Vcb, this will help ensure that any stale data is
959 RtlZeroMemory( Vcb
, sizeof(VCB
) );
962 // return and tell the caller
965 DebugTrace(-1, Dbg
, "FatDeleteVcb -> VOID\n", 0);
971 _Requires_lock_held_(_Global_critical_region_
)
974 IN PIRP_CONTEXT IrpContext
,
982 This routine allocates, initializes, and inserts a new root DCB record
983 into the in memory data structure.
987 Vcb - Supplies the Vcb to associate the new DCB under
991 None. The Vcb is modified in-place.
999 // The following variables are used for abnormal unwind
1002 PVOID UnwindStorage
[2] = { NULL
, NULL
};
1003 PERESOURCE UnwindResource
= NULL
;
1004 PERESOURCE UnwindResource2
= NULL
;
1005 PLARGE_MCB UnwindMcb
= NULL
;
1006 PFILE_OBJECT UnwindFileObject
= NULL
;
1010 DebugTrace(+1, Dbg
, "FatCreateRootDcb, Vcb = %p\n", Vcb
);
1015 // Make sure we don't already have a root dcb for this vcb
1018 if (Vcb
->RootDcb
!= NULL
) {
1020 DebugDump("Error trying to create multiple root dcbs\n", 0, Vcb
);
1022 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1024 FatBugCheck( 0, 0, 0 );
1028 // Allocate a new DCB and zero it out, we use Dcb locally so we don't
1029 // have to continually reference through the Vcb
1033 UnwindStorage
[0] = Dcb
= Vcb
->RootDcb
= FsRtlAllocatePoolWithTag( NonPagedPoolNx
,
1035 UnwindStorage
[0] = Dcb
= Vcb
->RootDcb
= FsRtlAllocatePoolWithTag( NonPagedPool
,
1040 RtlZeroMemory( Dcb
, sizeof(DCB
));
1043 Dcb
->NonPaged
= FatAllocateNonPagedFcb();
1045 RtlZeroMemory( Dcb
->NonPaged
, sizeof( NON_PAGED_FCB
) );
1048 // Set the proper node type code, node byte size, and call backs
1051 Dcb
->Header
.NodeTypeCode
= FAT_NTC_ROOT_DCB
;
1052 Dcb
->Header
.NodeByteSize
= sizeof(DCB
);
1054 Dcb
->FcbCondition
= FcbGood
;
1057 // The parent Dcb, initial state, open count, dirent location
1058 // information, and directory change count fields are already zero so
1059 // we can skip setting them
1063 // Initialize the resource variable
1067 Dcb
->Header
.Resource
= FatAllocateResource();
1070 // Initialize the PagingIo Resource. We no longer use the FsRtl common
1071 // shared pool because this led to a) deadlocks due to cases where files
1072 // and their parent directories shared a resource and b) there is no way
1073 // to anticipate inter-driver induced deadlock via recursive operation.
1077 Dcb
->Header
.PagingIoResource
= FatAllocateResource();
1080 // The root Dcb has an empty parent dcb links field
1083 InitializeListHead( &Dcb
->ParentDcbLinks
);
1092 // initialize the parent dcb queue.
1095 InitializeListHead( &Dcb
->Specific
.Dcb
.ParentDcbQueue
);
1098 // Set the full file name up.
1101 Dcb
->FullFileName
.Buffer
= L
"\\";
1102 Dcb
->FullFileName
.Length
= (USHORT
)2;
1103 Dcb
->FullFileName
.MaximumLength
= (USHORT
)4;
1105 Dcb
->ShortName
.Name
.Oem
.Buffer
= "\\";
1106 Dcb
->ShortName
.Name
.Oem
.Length
= (USHORT
)1;
1107 Dcb
->ShortName
.Name
.Oem
.MaximumLength
= (USHORT
)2;
1110 // Construct a lie about file properties since we don't
1111 // have a proper "." entry to look at.
1114 Dcb
->DirentFatFlags
= FILE_ATTRIBUTE_DIRECTORY
;
1117 // Initialize Advanced FCB Header fields
1120 ExInitializeFastMutex( &Dcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1121 FsRtlSetupAdvancedHeader( &Dcb
->Header
,
1122 &Dcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1125 // Initialize the Mcb, and setup its mapping. Note that the root
1126 // directory is a fixed size so we can set it everything up now.
1130 FsRtlInitializeLargeMcb( &Dcb
->Mcb
, NonPagedPoolNx
);
1132 FsRtlInitializeLargeMcb( &Dcb
->Mcb
, NonPagedPool
);
1134 UnwindMcb
= &Dcb
->Mcb
;
1136 if (FatIsFat32(Vcb
)) {
1139 // The first cluster of fat32 roots comes from the BPB
1142 Dcb
->FirstClusterOfFile
= Vcb
->Bpb
.RootDirFirstCluster
;
1146 FatAddMcbEntry( Vcb
, &Dcb
->Mcb
,
1148 FatRootDirectoryLbo( &Vcb
->Bpb
),
1149 FatRootDirectorySize( &Vcb
->Bpb
));
1152 if (FatIsFat32(Vcb
)) {
1155 // Find the size of the fat32 root. As a side-effect, this will create
1156 // MCBs for the entire root. In the process of doing this, we may
1157 // discover that the FAT chain is bogus and raise corruption.
1160 Dcb
->Header
.AllocationSize
.LowPart
= 0xFFFFFFFF;
1161 FatLookupFileAllocationSize( IrpContext
, Dcb
);
1163 Dcb
->Header
.FileSize
.QuadPart
=
1164 Dcb
->Header
.AllocationSize
.QuadPart
;
1168 // set the allocation size to real size of the root directory
1171 Dcb
->Header
.FileSize
.QuadPart
=
1172 Dcb
->Header
.AllocationSize
.QuadPart
= FatRootDirectorySize( &Vcb
->Bpb
);
1177 // Set our two create dirent aids to represent that we have yet to
1178 // enumerate the directory for never used or deleted dirents.
1181 Dcb
->Specific
.Dcb
.UnusedDirentVbo
= 0xffffffff;
1182 Dcb
->Specific
.Dcb
.DeletedDirentHint
= 0xffffffff;
1185 // Setup the free dirent bitmap buffer.
1188 RtlInitializeBitMap( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
1192 FatCheckFreeDirentBitmap( IrpContext
, Dcb
);
1194 #if (NTDDI_VERSION >= NTDDI_WIN8)
1196 // Initialize the oplock structure.
1199 FsRtlInitializeOplock( FatGetFcbOplock(Dcb
) );
1204 DebugUnwind( FatCreateRootDcb
);
1207 // If this is an abnormal termination then undo our work
1210 if (_SEH2_AbnormalTermination()) {
1214 if (UnwindFileObject
!= NULL
) { ObDereferenceObject( UnwindFileObject
); }
1215 if (UnwindMcb
!= NULL
) { FsRtlUninitializeLargeMcb( UnwindMcb
); }
1216 if (UnwindResource
!= NULL
) { FatFreeResource( UnwindResource
); }
1217 if (UnwindResource2
!= NULL
) { FatFreeResource( UnwindResource2
); }
1219 for (i
= 0; i
< sizeof(UnwindStorage
)/sizeof(PVOID
); i
+= 1) {
1220 if (UnwindStorage
[i
] != NULL
) { ExFreePool( UnwindStorage
[i
] ); }
1224 // Re-zero the entry in the Vcb.
1227 Vcb
->RootDcb
= NULL
;
1230 DebugTrace(-1, Dbg
, "FatCreateRootDcb -> %p\n", Dcb
);
1239 IN PIRP_CONTEXT IrpContext
,
1242 IN ULONG LfnOffsetWithinDirectory
,
1243 IN ULONG DirentOffsetWithinDirectory
,
1245 IN PUNICODE_STRING Lfn OPTIONAL
,
1246 IN PUNICODE_STRING OrigLfn OPTIONAL
,
1247 IN BOOLEAN IsPagingFile
,
1248 IN BOOLEAN SingleResource
1253 Routine Description:
1255 This routine allocates, initializes, and inserts a new Fcb record into
1256 the in-memory data structures.
1260 Vcb - Supplies the Vcb to associate the new FCB under.
1262 ParentDcb - Supplies the parent dcb that the new FCB is under.
1264 LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is
1265 no LFN associated with this file then this value is same as
1266 DirentOffsetWithinDirectory.
1268 DirentOffsetWithinDirectory - Supplies the offset, in bytes from the
1269 start of the directory file where the dirent for the fcb is located
1271 Dirent - Supplies the dirent for the fcb being created
1273 Lfn - Supplies a long UNICODE name associated with this file.
1275 IsPagingFile - Indicates if we are creating an FCB for a paging file
1276 or some other type of file.
1278 SingleResource - Indicates if this Fcb should share a single resource
1279 as both main and paging.
1283 PFCB - Returns a pointer to the newly allocated FCB
1292 // The following variables are used for abnormal unwind
1295 PVOID UnwindStorage
[2] = { NULL
, NULL
};
1296 PERESOURCE UnwindResource
= NULL
;
1297 PERESOURCE UnwindResource2
= NULL
;
1298 PLIST_ENTRY UnwindEntryList
= NULL
;
1299 PLARGE_MCB UnwindMcb
= NULL
;
1300 PFILE_LOCK UnwindFileLock
= NULL
;
1301 POPLOCK UnwindOplock
= NULL
;
1305 UNREFERENCED_PARAMETER( OrigLfn
);
1307 DebugTrace(+1, Dbg
, "FatCreateFcb\n", 0);
1312 // Determine the pool type we should be using for the fcb and the
1319 PoolType
= NonPagedPoolNx
;
1320 Fcb
= UnwindStorage
[0] = FsRtlAllocatePoolWithTag( NonPagedPoolNx
,
1322 PoolType
= NonPagedPool
;
1323 Fcb
= UnwindStorage
[0] = FsRtlAllocatePoolWithTag( NonPagedPool
,
1329 PoolType
= PagedPool
;
1330 Fcb
= UnwindStorage
[0] = FatAllocateFcb();
1335 // ... and zero it out
1338 RtlZeroMemory( Fcb
, sizeof(FCB
) );
1341 Fcb
->NonPaged
= FatAllocateNonPagedFcb();
1343 RtlZeroMemory( Fcb
->NonPaged
, sizeof( NON_PAGED_FCB
) );
1346 // Set the proper node type code, node byte size, and call backs
1349 Fcb
->Header
.NodeTypeCode
= FAT_NTC_FCB
;
1350 Fcb
->Header
.NodeByteSize
= sizeof(FCB
);
1352 Fcb
->FcbCondition
= FcbGood
;
1355 // Check to see if we need to set the Fcb state to indicate that this
1356 // is a paging/system file. This will prevent it from being opened
1362 SetFlag( Fcb
->FcbState
, FCB_STATE_PAGING_FILE
| FCB_STATE_SYSTEM_FILE
);
1366 // The initial state, open count, and segment objects fields are already
1367 // zero so we can skip setting them
1371 // Initialize the resource variable
1376 Fcb
->Header
.Resource
= FatAllocateResource();
1379 // Initialize the PagingIo Resource. We no longer use the FsRtl common
1380 // shared pool because this led to a) deadlocks due to cases where files
1381 // and their parent directories shared a resource and b) there is no way
1382 // to anticipate inter-driver induced deadlock via recursive operation.
1385 if (SingleResource
) {
1387 Fcb
->Header
.PagingIoResource
= Fcb
->Header
.Resource
;
1392 Fcb
->Header
.PagingIoResource
= FatAllocateResource();
1396 // Insert this fcb into our parent dcb's queue.
1398 // There is a deep reason why this goes on the tail, to allow us
1399 // to easily enumerate all child directories before child files.
1400 // This is important to let us maintain whole-volume lockorder
1401 // via BottomUp enumeration.
1404 InsertTailList( &ParentDcb
->Specific
.Dcb
.ParentDcbQueue
,
1405 &Fcb
->ParentDcbLinks
);
1406 UnwindEntryList
= &Fcb
->ParentDcbLinks
;
1409 // Point back to our parent dcb
1412 Fcb
->ParentDcb
= ParentDcb
;
1421 // Set the dirent offset within the directory
1424 Fcb
->LfnOffsetWithinDirectory
= LfnOffsetWithinDirectory
;
1425 Fcb
->DirentOffsetWithinDirectory
= DirentOffsetWithinDirectory
;
1428 // Set the DirentFatFlags and LastWriteTime
1431 Fcb
->DirentFatFlags
= Dirent
->Attributes
;
1433 Fcb
->LastWriteTime
= FatFatTimeToNtTime( IrpContext
,
1434 Dirent
->LastWriteTime
,
1438 // These fields are only non-zero when in Chicago mode.
1441 if (FatData
.ChicagoMode
) {
1444 LARGE_INTEGER FatSystemJanOne1980
= {0};
1446 LARGE_INTEGER FatSystemJanOne1980
= {{0}};
1450 // If either date is possibly zero, get the system
1451 // version of 1/1/80.
1454 if ((((PUSHORT
)Dirent
)[9] & ((PUSHORT
)Dirent
)[8]) == 0) {
1456 ExLocalTimeToSystemTime( &FatJanOne1980
,
1457 &FatSystemJanOne1980
);
1461 // Only do the really hard work if this field is non-zero.
1464 if (((PUSHORT
)Dirent
)[9] != 0) {
1466 Fcb
->LastAccessTime
=
1467 FatFatDateToNtTime( IrpContext
,
1468 Dirent
->LastAccessDate
);
1472 Fcb
->LastAccessTime
= FatSystemJanOne1980
;
1476 // Only do the really hard work if this field is non-zero.
1479 if (((PUSHORT
)Dirent
)[8] != 0) {
1482 FatFatTimeToNtTime( IrpContext
,
1483 Dirent
->CreationTime
,
1484 Dirent
->CreationMSec
);
1488 Fcb
->CreationTime
= FatSystemJanOne1980
;
1493 // Initialize Advanced FCB Header fields
1496 ExInitializeFastMutex( &Fcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1497 FsRtlSetupAdvancedHeader( &Fcb
->Header
,
1498 &Fcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1501 // To make FAT match the present functionality of NTFS, disable
1502 // stream contexts on paging files
1507 SetFlag( Fcb
->Header
.Flags2
, FSRTL_FLAG2_IS_PAGING_FILE
);
1508 ClearFlag( Fcb
->Header
.Flags2
, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS
);
1512 // Initialize the Mcb
1515 FsRtlInitializeLargeMcb( &Fcb
->Mcb
, PoolType
);
1516 UnwindMcb
= &Fcb
->Mcb
;
1519 // Set the file size, valid data length, first cluster of file,
1520 // and allocation size based on the information stored in the dirent
1523 Fcb
->Header
.FileSize
.LowPart
= Dirent
->FileSize
;
1525 Fcb
->Header
.ValidDataLength
.LowPart
= Dirent
->FileSize
;
1527 Fcb
->ValidDataToDisk
= Dirent
->FileSize
;
1529 Fcb
->FirstClusterOfFile
= (ULONG
)Dirent
->FirstClusterOfFile
;
1531 if ( FatIsFat32(Vcb
) ) {
1533 Fcb
->FirstClusterOfFile
+= Dirent
->FirstClusterOfFileHi
<< 16;
1536 if ( Fcb
->FirstClusterOfFile
== 0 ) {
1538 Fcb
->Header
.AllocationSize
.QuadPart
= 0;
1542 Fcb
->Header
.AllocationSize
.QuadPart
= FCB_LOOKUP_ALLOCATIONSIZE_HINT
;
1547 // Initialize the Fcb's file lock record
1550 FsRtlInitializeFileLock( &Fcb
->Specific
.Fcb
.FileLock
, NULL
, NULL
);
1551 UnwindFileLock
= &Fcb
->Specific
.Fcb
.FileLock
;
1554 // Initialize the oplock structure.
1557 FsRtlInitializeOplock( FatGetFcbOplock(Fcb
) );
1558 UnwindOplock
= FatGetFcbOplock(Fcb
);
1561 // Indicate that Fast I/O is possible
1564 Fcb
->Header
.IsFastIoPossible
= TRUE
;
1567 // Set the file names. This must be the last thing we do.
1570 FatConstructNamesInFcb( IrpContext
,
1576 // Drop the shortname hint so prefix searches can figure out
1580 Fcb
->ShortName
.FileNameDos
= TRUE
;
1584 DebugUnwind( FatCreateFcb
);
1587 // If this is an abnormal termination then undo our work
1590 if (_SEH2_AbnormalTermination()) {
1594 if (UnwindOplock
!= NULL
) { FsRtlUninitializeOplock( UnwindOplock
); }
1595 if (UnwindFileLock
!= NULL
) { FsRtlUninitializeFileLock( UnwindFileLock
); }
1596 if (UnwindMcb
!= NULL
) { FsRtlUninitializeLargeMcb( UnwindMcb
); }
1597 if (UnwindEntryList
!= NULL
) { RemoveEntryList( UnwindEntryList
); }
1598 if (UnwindResource
!= NULL
) { FatFreeResource( UnwindResource
); }
1599 if (UnwindResource2
!= NULL
) { FatFreeResource( UnwindResource2
); }
1601 for (i
= 0; i
< sizeof(UnwindStorage
)/sizeof(PVOID
); i
+= 1) {
1602 if (UnwindStorage
[i
] != NULL
) { ExFreePool( UnwindStorage
[i
] ); }
1606 DebugTrace(-1, Dbg
, "FatCreateFcb -> %p\n", Fcb
);
1610 // return and tell the caller
1619 IN PIRP_CONTEXT IrpContext
,
1622 IN ULONG LfnOffsetWithinDirectory
,
1623 IN ULONG DirentOffsetWithinDirectory
,
1625 IN PUNICODE_STRING Lfn OPTIONAL
1630 Routine Description:
1632 This routine allocates, initializes, and inserts a new Dcb record into
1633 the in memory data structures.
1637 Vcb - Supplies the Vcb to associate the new DCB under.
1639 ParentDcb - Supplies the parent dcb that the new DCB is under.
1641 LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is
1642 no LFN associated with this file then this value is same as
1643 DirentOffsetWithinDirectory.
1645 DirentOffsetWithinDirectory - Supplies the offset, in bytes from the
1646 start of the directory file where the dirent for the fcb is located
1648 Dirent - Supplies the dirent for the dcb being created
1650 FileName - Supplies the file name of the file relative to the directory
1651 it's in (e.g., the file \config.sys is called "CONFIG.SYS" without
1652 the preceding backslash).
1654 Lfn - Supplies a long UNICODE name associated with this directory.
1658 PDCB - Returns a pointer to the newly allocated DCB
1666 // The following variables are used for abnormal unwind
1669 PVOID UnwindStorage
[2] = { NULL
, NULL
};
1670 PERESOURCE UnwindResource
= NULL
;
1671 PERESOURCE UnwindResource2
= NULL
;
1672 PLIST_ENTRY UnwindEntryList
= NULL
;
1673 PLARGE_MCB UnwindMcb
= NULL
;
1674 POPLOCK UnwindOplock
= NULL
;
1678 DebugTrace(+1, Dbg
, "FatCreateDcb\n", 0);
1683 // assert that the only time we are called is if wait is true
1686 NT_ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
1689 // Allocate a new DCB, and zero it out
1692 UnwindStorage
[0] = Dcb
= FatAllocateFcb();
1694 RtlZeroMemory( Dcb
, sizeof(DCB
) );
1697 Dcb
->NonPaged
= FatAllocateNonPagedFcb();
1699 RtlZeroMemory( Dcb
->NonPaged
, sizeof( NON_PAGED_FCB
) );
1702 // Set the proper node type code, node byte size and call backs
1705 Dcb
->Header
.NodeTypeCode
= FAT_NTC_DCB
;
1706 Dcb
->Header
.NodeByteSize
= sizeof(DCB
);
1708 Dcb
->FcbCondition
= FcbGood
;
1711 // The initial state, open count, and directory change count fields are
1712 // already zero so we can skip setting them
1716 // Initialize the resource variable
1721 Dcb
->Header
.Resource
= FatAllocateResource();
1724 // Initialize the PagingIo Resource. We no longer use the FsRtl common
1725 // shared pool because this led to a) deadlocks due to cases where files
1726 // and their parent directories shared a resource and b) there is no way
1727 // to anticipate inter-driver induced deadlock via recursive operation.
1731 Dcb
->Header
.PagingIoResource
= FatAllocateResource();
1734 // Insert this Dcb into our parent dcb's queue
1736 // There is a deep reason why this goes on the head, to allow us
1737 // to easily enumerate all child directories before child files.
1738 // This is important to let us maintain whole-volume lockorder
1739 // via BottomUp enumeration.
1742 InsertHeadList( &ParentDcb
->Specific
.Dcb
.ParentDcbQueue
,
1743 &Dcb
->ParentDcbLinks
);
1744 UnwindEntryList
= &Dcb
->ParentDcbLinks
;
1747 // Point back to our parent dcb
1750 Dcb
->ParentDcb
= ParentDcb
;
1759 // Set the dirent offset within the directory
1762 Dcb
->LfnOffsetWithinDirectory
= LfnOffsetWithinDirectory
;
1763 Dcb
->DirentOffsetWithinDirectory
= DirentOffsetWithinDirectory
;
1766 // Set the DirentFatFlags and LastWriteTime
1769 Dcb
->DirentFatFlags
= Dirent
->Attributes
;
1771 Dcb
->LastWriteTime
= FatFatTimeToNtTime( IrpContext
,
1772 Dirent
->LastWriteTime
,
1776 // These fields are only non-zero when in Chicago mode.
1779 if (FatData
.ChicagoMode
) {
1782 LARGE_INTEGER FatSystemJanOne1980
= {0};
1784 LARGE_INTEGER FatSystemJanOne1980
= {{0}};
1788 // If either date is possibly zero, get the system
1789 // version of 1/1/80.
1792 if ((((PUSHORT
)Dirent
)[9] & ((PUSHORT
)Dirent
)[8]) == 0) {
1794 ExLocalTimeToSystemTime( &FatJanOne1980
,
1795 &FatSystemJanOne1980
);
1799 // Only do the really hard work if this field is non-zero.
1802 if (((PUSHORT
)Dirent
)[9] != 0) {
1804 Dcb
->LastAccessTime
=
1805 FatFatDateToNtTime( IrpContext
,
1806 Dirent
->LastAccessDate
);
1810 Dcb
->LastAccessTime
= FatSystemJanOne1980
;
1814 // Only do the really hard work if this field is non-zero.
1817 if (((PUSHORT
)Dirent
)[8] != 0) {
1820 FatFatTimeToNtTime( IrpContext
,
1821 Dirent
->CreationTime
,
1822 Dirent
->CreationMSec
);
1826 Dcb
->CreationTime
= FatSystemJanOne1980
;
1831 // Initialize Advanced FCB Header fields
1834 ExInitializeFastMutex( &Dcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1835 FsRtlSetupAdvancedHeader( &Dcb
->Header
,
1836 &Dcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1839 // Initialize the Mcb
1842 FsRtlInitializeLargeMcb( &Dcb
->Mcb
, PagedPool
);
1843 UnwindMcb
= &Dcb
->Mcb
;
1846 // Set the file size, first cluster of file, and allocation size
1847 // based on the information stored in the dirent
1850 Dcb
->FirstClusterOfFile
= (ULONG
)Dirent
->FirstClusterOfFile
;
1852 if ( FatIsFat32(Dcb
->Vcb
) ) {
1854 Dcb
->FirstClusterOfFile
+= Dirent
->FirstClusterOfFileHi
<< 16;
1857 if ( Dcb
->FirstClusterOfFile
== 0 ) {
1859 Dcb
->Header
.AllocationSize
.QuadPart
= 0;
1863 Dcb
->Header
.AllocationSize
.QuadPart
= FCB_LOOKUP_ALLOCATIONSIZE_HINT
;
1867 // initialize the notify queues, and the parent dcb queue.
1870 InitializeListHead( &Dcb
->Specific
.Dcb
.ParentDcbQueue
);
1873 // Setup the free dirent bitmap buffer. Since we don't know the
1874 // size of the directory, leave it zero for now.
1877 RtlInitializeBitMap( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
1882 // Set our two create dirent aids to represent that we have yet to
1883 // enumerate the directory for never used or deleted dirents.
1886 Dcb
->Specific
.Dcb
.UnusedDirentVbo
= 0xffffffff;
1887 Dcb
->Specific
.Dcb
.DeletedDirentHint
= 0xffffffff;
1889 #if (NTDDI_VERSION >= NTDDI_WIN8)
1891 // Initialize the oplock structure.
1894 FsRtlInitializeOplock( FatGetFcbOplock(Dcb
) );
1895 UnwindOplock
= FatGetFcbOplock(Dcb
);
1899 // Postpone initializing the cache map until we need to do a read/write
1900 // of the directory file.
1904 // set the file names. This must be the last thing we do.
1907 FatConstructNamesInFcb( IrpContext
,
1912 Dcb
->ShortName
.FileNameDos
= TRUE
;
1916 DebugUnwind( FatCreateDcb
);
1919 // If this is an abnormal termination then undo our work
1922 if (_SEH2_AbnormalTermination()) {
1926 if (UnwindOplock
!= NULL
) { FsRtlUninitializeOplock( UnwindOplock
); }
1927 if (UnwindMcb
!= NULL
) { FsRtlUninitializeLargeMcb( UnwindMcb
); }
1928 if (UnwindEntryList
!= NULL
) { RemoveEntryList( UnwindEntryList
); }
1929 if (UnwindResource
!= NULL
) { FatFreeResource( UnwindResource
); }
1930 if (UnwindResource2
!= NULL
) { FatFreeResource( UnwindResource2
); }
1932 for (i
= 0; i
< sizeof(UnwindStorage
)/sizeof(PVOID
); i
+= 1) {
1933 if (UnwindStorage
[i
] != NULL
) { ExFreePool( UnwindStorage
[i
] ); }
1937 DebugTrace(-1, Dbg
, "FatCreateDcb -> %p\n", Dcb
);
1941 // return and tell the caller
1944 DebugTrace(-1, Dbg
, "FatCreateDcb -> %p\n", Dcb
);
1952 IN PIRP_CONTEXT IrpContext
,
1958 Routine Description:
1960 This routine deallocates and removes an FCB, DCB, or ROOT DCB record
1961 from Fat's in-memory data structures. It also will remove all
1962 associated underlings (i.e., Notify irps, and child FCB/DCB records).
1966 Fcb - Supplies the FCB/DCB/ROOT DCB to be removed
1979 DebugTrace(+1, Dbg
, "FatDeleteFcb, Fcb = %p\n", Fcb
);
1982 // We can only delete this record if the open count is zero.
1985 if (Fcb
->OpenCount
!= 0) {
1987 DebugDump("Error deleting Fcb, Still Open\n", 0, Fcb
);
1989 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1991 FatBugCheck( 0, 0, 0 );
1995 // Better be an FCB/DCB.
1998 if ((Fcb
->Header
.NodeTypeCode
!= FAT_NTC_DCB
) &&
1999 (Fcb
->Header
.NodeTypeCode
!= FAT_NTC_ROOT_DCB
) &&
2000 (Fcb
->Header
.NodeTypeCode
!= FAT_NTC_FCB
)) {
2003 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
2005 FatBugCheck( 0, 0, 0 );
2009 // If this is a DCB then remove every Notify record from the two
2013 if ((Fcb
->Header
.NodeTypeCode
== FAT_NTC_DCB
) ||
2014 (Fcb
->Header
.NodeTypeCode
== FAT_NTC_ROOT_DCB
)) {
2017 // If we allocated a free dirent bitmap buffer, free it.
2020 if ((Fcb
->Specific
.Dcb
.FreeDirentBitmap
.Buffer
!= NULL
) &&
2021 (Fcb
->Specific
.Dcb
.FreeDirentBitmap
.Buffer
!=
2022 &Fcb
->Specific
.Dcb
.FreeDirentBitmapBuffer
[0])) {
2024 ExFreePool(Fcb
->Specific
.Dcb
.FreeDirentBitmap
.Buffer
);
2027 #if (NTDDI_VERSION >= NTDDI_WIN8)
2029 // Uninitialize the oplock.
2032 FsRtlUninitializeOplock( FatGetFcbOplock(Fcb
) );
2035 NT_ASSERT( Fcb
->Specific
.Dcb
.DirectoryFileOpenCount
== 0 );
2036 NT_ASSERT( IsListEmpty(&Fcb
->Specific
.Dcb
.ParentDcbQueue
) );
2037 NT_ASSERT( NULL
== Fcb
->Specific
.Dcb
.DirectoryFile
);
2042 // Uninitialize the byte range file locks and opportunistic locks
2045 FsRtlUninitializeFileLock( &Fcb
->Specific
.Fcb
.FileLock
);
2046 FsRtlUninitializeOplock( FatGetFcbOplock(Fcb
) );
2051 // Release any Filter Context structures associated with this FCB
2054 FsRtlTeardownPerStreamContexts( &Fcb
->Header
);
2057 // Uninitialize the Mcb
2060 FsRtlUninitializeLargeMcb( &Fcb
->Mcb
);
2063 // If this is not the root dcb then we need to remove ourselves from
2064 // our parents Dcb queue
2067 if (Fcb
->Header
.NodeTypeCode
!= FAT_NTC_ROOT_DCB
) {
2069 RemoveEntryList( &(Fcb
->ParentDcbLinks
) );
2073 // Remove the entry from the splay table if there is still is one.
2076 if (FlagOn( Fcb
->FcbState
, FCB_STATE_NAMES_IN_SPLAY_TREE
)) {
2078 FatRemoveNames( IrpContext
, Fcb
);
2082 // Free the file name pool if allocated.
2085 if (Fcb
->Header
.NodeTypeCode
!= FAT_NTC_ROOT_DCB
) {
2088 // If we blew up at inconvenient times, the shortname
2089 // could be null even though you will *never* see this
2090 // normally. Rename is a good example of this case.
2093 if (Fcb
->ShortName
.Name
.Oem
.Buffer
) {
2095 ExFreePool( Fcb
->ShortName
.Name
.Oem
.Buffer
);
2098 if (Fcb
->FullFileName
.Buffer
) {
2100 ExFreePool( Fcb
->FullFileName
.Buffer
);
2104 if (Fcb
->ExactCaseLongName
.Buffer
) {
2106 ExFreePool(Fcb
->ExactCaseLongName
.Buffer
);
2109 #ifdef SYSCACHE_COMPILE
2111 if (Fcb
->WriteMask
) {
2113 ExFreePool( Fcb
->WriteMask
);
2119 // Finally deallocate the Fcb and non-paged fcb records
2122 FatFreeResource( Fcb
->Header
.Resource
);
2124 if (Fcb
->Header
.PagingIoResource
!= Fcb
->Header
.Resource
) {
2126 FatFreeResource( Fcb
->Header
.PagingIoResource
);
2130 // If an Event was allocated, get rid of it.
2133 if (Fcb
->NonPaged
->OutstandingAsyncEvent
) {
2135 ExFreePool( Fcb
->NonPaged
->OutstandingAsyncEvent
);
2138 FatFreeNonPagedFcb( Fcb
->NonPaged
);
2140 Fcb
->Header
.NodeTypeCode
= NTC_UNDEFINED
;
2146 // and return to our caller
2149 DebugTrace(-1, Dbg
, "FatDeleteFcb -> VOID\n", 0);
2155 IN PIRP_CONTEXT IrpContext
2160 Routine Description:
2162 This routine creates a new CCB record
2168 CCB - returns a pointer to the newly allocate CCB
2177 DebugTrace(+1, Dbg
, "FatCreateCcb\n", 0);
2180 // Allocate a new CCB Record
2183 Ccb
= FatAllocateCcb();
2185 RtlZeroMemory( Ccb
, sizeof(CCB
) );
2188 // Set the proper node type code and node byte size
2191 Ccb
->NodeTypeCode
= FAT_NTC_CCB
;
2192 Ccb
->NodeByteSize
= sizeof(CCB
);
2195 // return and tell the caller
2198 DebugTrace(-1, Dbg
, "FatCreateCcb -> %p\n", Ccb
);
2200 UNREFERENCED_PARAMETER( IrpContext
);
2208 FatDeallocateCcbStrings(
2213 Routine Description:
2215 This routine deallocates CCB query templates
2219 Ccb - Supplies the CCB
2230 // If we allocated query template buffers, deallocate them now.
2233 if (FlagOn(Ccb
->Flags
, CCB_FLAG_FREE_UNICODE
)) {
2235 NT_ASSERT( Ccb
->UnicodeQueryTemplate
.Buffer
);
2236 NT_ASSERT( !FlagOn( Ccb
->Flags
, CCB_FLAG_CLOSE_CONTEXT
));
2237 RtlFreeUnicodeString( &Ccb
->UnicodeQueryTemplate
);
2240 if (FlagOn(Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
)) {
2242 NT_ASSERT( Ccb
->OemQueryTemplate
.Wild
.Buffer
);
2243 NT_ASSERT( !FlagOn( Ccb
->Flags
, CCB_FLAG_CLOSE_CONTEXT
));
2244 RtlFreeOemString( &Ccb
->OemQueryTemplate
.Wild
);
2247 ClearFlag( Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
| CCB_FLAG_FREE_UNICODE
);
2254 IN PIRP_CONTEXT IrpContext
,
2260 Routine Description:
2262 This routine deallocates and removes the specified CCB record
2263 from the Fat in memory data structures
2267 Ccb - Supplies the CCB to remove
2278 DebugTrace(+1, Dbg
, "FatDeleteCcb, Ccb = %p\n", *Ccb
);
2280 FatDeallocateCcbStrings( *Ccb
);
2283 // Deallocate the Ccb record
2290 // return and tell the caller
2293 DebugTrace(-1, Dbg
, "FatDeleteCcb -> VOID\n", 0);
2295 UNREFERENCED_PARAMETER( IrpContext
);
2300 FatCreateIrpContext (
2307 Routine Description:
2309 This routine creates a new IRP_CONTEXT record
2313 Irp - Supplies the originating Irp.
2315 Wait - Supplies the wait value to store in the context
2319 PIRP_CONTEXT - returns a pointer to the newly allocate IRP_CONTEXT Record
2324 PIRP_CONTEXT IrpContext
;
2325 PIO_STACK_LOCATION IrpSp
;
2329 DebugTrace(+1, Dbg
, "FatCreateIrpContext\n", 0);
2331 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2334 // The only operations a filesystem device object should ever receive
2335 // are create/teardown of fsdo handles and operations which do not
2336 // occur in the context of fileobjects (i.e., mount).
2339 if (FatDeviceIsFatFsdo( IrpSp
->DeviceObject
)) {
2341 if (IrpSp
->FileObject
!= NULL
&&
2342 IrpSp
->MajorFunction
!= IRP_MJ_CREATE
&&
2343 IrpSp
->MajorFunction
!= IRP_MJ_CLEANUP
&&
2344 IrpSp
->MajorFunction
!= IRP_MJ_CLOSE
) {
2346 ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST
);
2349 NT_ASSERT( IrpSp
->FileObject
!= NULL
||
2351 (IrpSp
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
&&
2352 IrpSp
->MinorFunction
== IRP_MN_USER_FS_REQUEST
&&
2353 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
== FSCTL_INVALIDATE_VOLUMES
) ||
2355 (IrpSp
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
&&
2356 IrpSp
->MinorFunction
== IRP_MN_MOUNT_VOLUME
) ||
2358 IrpSp
->MajorFunction
== IRP_MJ_SHUTDOWN
);
2362 // Attemtp to allocate from the region first and failing that allocate
2366 DebugDoit( FatFsdEntryCount
+= 1);
2368 IrpContext
= FatAllocateIrpContext();
2371 // Zero out the irp context.
2374 RtlZeroMemory( IrpContext
, sizeof(IRP_CONTEXT
) );
2377 // Set the proper node type code and node byte size
2380 IrpContext
->NodeTypeCode
= FAT_NTC_IRP_CONTEXT
;
2381 IrpContext
->NodeByteSize
= sizeof(IRP_CONTEXT
);
2384 // Set the originating Irp field
2387 IrpContext
->OriginatingIrp
= Irp
;
2390 // Major/Minor Function codes
2393 IrpContext
->MajorFunction
= IrpSp
->MajorFunction
;
2394 IrpContext
->MinorFunction
= IrpSp
->MinorFunction
;
2397 // Copy RealDevice for workque algorithms, and also set Write Through
2398 // and Removable Media if there is a file object. Only file system
2399 // control Irps won't have a file object, and they should all have
2400 // a Vpb as the first IrpSp location.
2403 if (IrpSp
->FileObject
!= NULL
) {
2405 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2407 IrpContext
->RealDevice
= FileObject
->DeviceObject
;
2408 IrpContext
->Vcb
= &((PVOLUME_DEVICE_OBJECT
)(IrpSp
->DeviceObject
))->Vcb
;
2411 // See if the request is Write Through. Look for both FileObjects opened
2412 // as write through, and non-cached requests with the SL_WRITE_THROUGH flag set.
2414 // The latter can only originate from kernel components. (Note - NtWriteFile()
2415 // does redundantly set the SL_W_T flag for all requests it issues on write
2416 // through file objects)
2419 if (IsFileWriteThrough( FileObject
, IrpContext
->Vcb
) ||
2420 ( (IrpSp
->MajorFunction
== IRP_MJ_WRITE
) &&
2421 BooleanFlagOn( Irp
->Flags
, IRP_NOCACHE
) &&
2422 BooleanFlagOn( IrpSp
->Flags
, SL_WRITE_THROUGH
))) {
2424 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WRITE_THROUGH
);
2426 } else if (IrpContext
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
) {
2428 IrpContext
->RealDevice
= IrpSp
->Parameters
.MountVolume
.Vpb
->RealDevice
;
2432 // Set the wait parameter
2435 if (Wait
) { SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
); }
2438 // Set the recursive file system call parameter. We set it true if
2439 // the TopLevelIrp field in the thread local storage is not the current
2440 // irp, otherwise we leave it as FALSE.
2443 if ( IoGetTopLevelIrp() != Irp
) {
2445 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_RECURSIVE_CALL
);
2449 // return and tell the caller
2452 DebugTrace(-1, Dbg
, "FatCreateIrpContext -> %p\n", IrpContext
);
2460 FatDeleteIrpContext_Real (
2461 IN PIRP_CONTEXT IrpContext
2466 Routine Description:
2468 This routine deallocates and removes the specified IRP_CONTEXT record
2469 from the Fat in memory data structures. It should only be called
2470 by FatCompleteRequest.
2474 IrpContext - Supplies the IRP_CONTEXT to remove
2485 DebugTrace(+1, Dbg
, "FatDeleteIrpContext, IrpContext = %p\n", IrpContext
);
2487 NT_ASSERT( IrpContext
->NodeTypeCode
== FAT_NTC_IRP_CONTEXT
);
2488 NT_ASSERT( IrpContext
->PinCount
== 0 );
2492 // If there is a FatIoContext that was allocated, free it.
2495 if (IrpContext
->FatIoContext
!= NULL
) {
2497 if (!FlagOn(IrpContext
->Flags
, IRP_CONTEXT_STACK_IO_CONTEXT
)) {
2499 if (IrpContext
->FatIoContext
->ZeroMdl
) {
2500 IoFreeMdl( IrpContext
->FatIoContext
->ZeroMdl
);
2503 ExFreePool( IrpContext
->FatIoContext
);
2508 // Drop the IrpContext.
2511 FatFreeIrpContext( IrpContext
);
2514 // return and tell the caller
2517 DebugTrace(-1, Dbg
, "FatDeleteIrpContext -> VOID\n", 0);
2524 FatGetNextFcbBottomUp (
2525 IN PIRP_CONTEXT IrpContext
,
2526 IN PFCB Fcb OPTIONAL
,
2527 IN PFCB TerminationFcb
2532 Routine Description:
2534 This routine is used to iterate through Fcbs in a tree. In order to match
2535 the lockorder for getting multiple Fcbs (so this can be used for acquiring
2536 all Fcbs), this version does a bottom-up enumeration.
2538 This is different than the old one, now called TopDown. The problem with
2539 lockorder was very well hidden.
2541 The transition rule is still pretty simple:
2543 A) If you have an adjacent sibling, go to it
2544 1) Descend to its leftmost child
2545 B) Else go to your parent
2547 If this routine is called with in invalid TerminationFcb it will fail,
2550 The TerminationFcb is the last Fcb returned in the enumeration.
2552 This method is incompatible with the possibility that ancestors may vanish
2553 based on operations done on the last returned node. For instance,
2554 FatPurgeReferencedFileObjects cannot use BottomUp enumeration.
2558 Fcb - Supplies the current Fcb. This is NULL if enumeration is starting.
2560 TerminationFcb - The root Fcb of the tree in which the enumeration starts
2561 and at which it inclusively stops.
2565 The next Fcb in the enumeration, or NULL if Fcb was the final one.
2573 UNREFERENCED_PARAMETER( IrpContext
);
2575 NT_ASSERT( FatVcbAcquiredExclusive( IrpContext
, TerminationFcb
->Vcb
) ||
2576 FlagOn( TerminationFcb
->Vcb
->VcbState
, VCB_STATE_FLAG_LOCKED
) );
2579 // Do we need to begin the enumeration?
2588 if (Fcb
== TerminationFcb
) {
2594 // Do we have a sibling to return?
2597 NextFcb
= FatGetNextSibling( Fcb
);
2600 // If not, return our parent. We are done with this branch.
2603 if (NextFcb
== NULL
) {
2605 return Fcb
->ParentDcb
;
2610 NextFcb
= TerminationFcb
;
2614 // Decend to its furthest child (if it exists) and return it.
2618 NodeType( NextFcb
) != FAT_NTC_FCB
&& FatGetFirstChild( NextFcb
) != NULL
;
2619 NextFcb
= FatGetFirstChild( NextFcb
)) {
2626 FatGetNextFcbTopDown (
2627 IN PIRP_CONTEXT IrpContext
,
2629 IN PFCB TerminationFcb
2634 Routine Description:
2636 This routine is used to iterate through Fcbs in a tree, from the top down.
2638 The rule is very simple:
2640 A) If you have a child, go to it, else
2641 B) If you have an older sibling, go to it, else
2642 C) Go to your parent's older sibling.
2644 If this routine is called with in invalid TerminationFcb it will fail,
2647 The Termination Fcb is never returned. If it is the root of the tree you
2648 are traversing, visit it first.
2650 This routine never returns direct ancestors of Fcb, and thus is useful when
2651 making Fcb's go away (which may tear up the tree).
2655 Fcb - Supplies the current Fcb
2657 TerminationFcb - The Fcb at which the enumeration should (non-inclusivly)
2658 stop. Assumed to be a directory.
2662 The next Fcb in the enumeration, or NULL if Fcb was the final one.
2670 UNREFERENCED_PARAMETER( IrpContext
);
2672 NT_ASSERT( FatVcbAcquiredExclusive( IrpContext
, Fcb
->Vcb
) ||
2673 FlagOn( Fcb
->Vcb
->VcbState
, VCB_STATE_FLAG_LOCKED
) );
2676 // If this was a directory (ie. not a file), get the child. If
2677 // there aren't any children and this is our termination Fcb,
2681 if ( ((NodeType(Fcb
) == FAT_NTC_DCB
) ||
2682 (NodeType(Fcb
) == FAT_NTC_ROOT_DCB
)) &&
2683 !IsListEmpty(&Fcb
->Specific
.Dcb
.ParentDcbQueue
) ) {
2685 return FatGetFirstChild( Fcb
);
2689 // Were we only meant to do one iteration?
2692 if ( Fcb
== TerminationFcb
) {
2697 Sibling
= FatGetNextSibling(Fcb
);
2702 // Do we still have an "older" sibling in this directory who is
2703 // not the termination Fcb?
2706 if ( Sibling
!= NULL
) {
2708 return (Sibling
!= TerminationFcb
) ? Sibling
: NULL
;
2712 // OK, let's move on to out parent and see if he is the termination
2713 // node or has any older siblings.
2716 if ( Fcb
->ParentDcb
== TerminationFcb
) {
2721 Fcb
= Fcb
->ParentDcb
;
2723 Sibling
= FatGetNextSibling(Fcb
);
2730 IN PIRP_CONTEXT IrpContext
,
2736 Routine Description:
2738 This routine swaps the VPB for this VCB if it has not been done already.
2739 This means the device object will get our spare VPB and we will cleanup
2740 the one used while the volume was mounted.
2744 IrpContext - Supplies the context for the overall request.
2746 Vcb - Supplies the VCB to swap the VPB on.
2750 TRUE - If the VPB was actually swapped.
2752 FALSE - If the VPB was already swapped.
2757 BOOLEAN Result
= FALSE
;
2759 PIO_STACK_LOCATION IrpSp
;
2763 // Make sure we have not already swapped it.
2769 #pragma prefast( push )
2770 #pragma prefast( disable: 28175, "touching Vpb is ok for a filesystem" )
2773 if (!FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
) && OldVpb
->RealDevice
->Vpb
== OldVpb
) {
2776 // If not the final reference and we are forcing the disconnect,
2777 // then swap out the Vpb. We must preserve the REMOVE_PENDING flag
2778 // so that the device is not remounted in the middle of a PnP remove
2782 NT_ASSERT( Vcb
->SwapVpb
!= NULL
);
2784 Vcb
->SwapVpb
->Type
= IO_TYPE_VPB
;
2785 Vcb
->SwapVpb
->Size
= sizeof( VPB
);
2786 Vcb
->SwapVpb
->RealDevice
= OldVpb
->RealDevice
;
2788 Vcb
->SwapVpb
->RealDevice
->Vpb
= Vcb
->SwapVpb
;
2790 Vcb
->SwapVpb
->Flags
= FlagOn( OldVpb
->Flags
, VPB_REMOVE_PENDING
);
2793 // If we are working on a mount request, we need to make sure we update
2794 // the VPB in the IRP, since the one it points to may no longer be valid.
2797 if (IrpContext
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
&& IrpContext
->MinorFunction
== IRP_MN_MOUNT_VOLUME
) {
2800 // Get the IRP stack.
2803 IrpSp
= IoGetCurrentIrpStackLocation( IrpContext
->OriginatingIrp
);
2805 NT_ASSERT( IrpSp
->FileObject
== NULL
);
2808 // Check the VPB in the IRP to see if it is the one we are swapping.
2811 if (IrpSp
->Parameters
.MountVolume
.Vpb
== OldVpb
) {
2814 // Change the IRP to point to the swap VPB.
2817 IrpSp
->Parameters
.MountVolume
.Vpb
= Vcb
->SwapVpb
;
2822 #pragma prefast( pop )
2826 // We place the volume in the Bad state (as opposed to NotMounted) so
2827 // that it is not eligible for a remount. Also indicate we used up
2831 Vcb
->SwapVpb
= NULL
;
2832 FatSetVcbCondition( Vcb
, VcbBad
);
2833 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
);
2842 _Requires_lock_held_(_Global_critical_region_
)
2844 FatCheckForDismount (
2845 IN PIRP_CONTEXT IrpContext
,
2852 Routine Description:
2854 This routine determines if a volume is ready for deletion. It
2855 correctly synchronizes with creates en-route to the file system.
2859 Vcb - Supplies the volume to examine
2861 Force - Specifies whether we want this Vcb forcibly disconnected
2862 from the driver stack if it will not be deleted (a new vpb will
2863 be installed if neccesary). Caller is responsible for making
2864 sure that the volume has been marked in such a way that attempts
2865 to operate through the realdevice are blocked (i.e., move the vcb
2866 out of the mounted state).
2870 BOOLEAN - TRUE if the volume was deleted, FALSE otherwise.
2876 BOOLEAN VcbDeleted
= FALSE
;
2880 // If the VCB condition is good and we are not forcing, just return.
2883 if (Vcb
->VcbCondition
== VcbGood
&& !Force
) {
2889 // Now check for a zero Vpb count on an unmounted volume. These
2890 // volumes will be deleted as they now have no file objects and
2891 // there are no creates en route to this volume.
2894 IoAcquireVpbSpinLock( &SavedIrql
);
2896 if (Vcb
->Vpb
->ReferenceCount
== Vcb
->ResidualOpenCount
&& Vcb
->OpenFileCount
== 0) {
2898 PVPB Vpb
= Vcb
->Vpb
;
2901 UNICODE_STRING VolumeLabel
;
2904 // Setup the VolumeLabel string
2907 VolumeLabel
.Length
= Vcb
->Vpb
->VolumeLabelLength
;
2908 VolumeLabel
.MaximumLength
= MAXIMUM_VOLUME_LABEL_LENGTH
;
2909 VolumeLabel
.Buffer
= &Vcb
->Vpb
->VolumeLabel
[0];
2911 KdPrintEx((DPFLTR_FASTFAT_ID
,
2913 "FASTFAT: Dismounting Volume %Z\n",
2918 // Swap this VCB's VPB.
2921 FatSwapVpb( IrpContext
,
2925 // Clear the VPB_MOUNTED bit. New opens should not come in due
2926 // to the swapped VPB, but having the flag cleared helps debugging.
2927 // Note that we must leave the Vpb->DeviceObject field set until
2928 // after the FatTearDownVcb call as closes will have to make their
2932 ClearFlag( Vpb
->Flags
, VPB_MOUNTED
);
2935 // If this Vpb was locked, clear this flag now.
2938 ClearFlag( Vpb
->Flags
, (VPB_LOCKED
| VPB_DIRECT_WRITES_ALLOWED
) );
2940 IoReleaseVpbSpinLock( SavedIrql
);
2943 // We are going to attempt the dismount, so mark the VCB as having
2944 // a dismount in progress.
2947 NT_ASSERT( !FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
) );
2948 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
);
2951 // Close down our internal opens.
2954 FatTearDownVcb( IrpContext
,
2958 // Process any delayed closes.
2964 // Grab the VPB lock again so that we can recheck our counts.
2967 IoAcquireVpbSpinLock( &SavedIrql
);
2970 // See if we can delete this VCB.
2973 if (Vcb
->Vpb
->ReferenceCount
== 0 && Vcb
->InternalOpenCount
== 0) {
2975 Vpb
->DeviceObject
= NULL
;
2977 IoReleaseVpbSpinLock( SavedIrql
);
2979 FatDeleteVcb( IrpContext
, Vcb
);
2981 IoDeleteDevice( (PDEVICE_OBJECT
)
2982 CONTAINING_RECORD( Vcb
,
2983 VOLUME_DEVICE_OBJECT
,
2990 IoReleaseVpbSpinLock( SavedIrql
);
2992 NT_ASSERT( FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
) );
2993 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
);
2999 // The requester is forcing the issue. We need to swap the VPB with our spare.
3002 FatSwapVpb( IrpContext
,
3005 IoReleaseVpbSpinLock( SavedIrql
);
3010 // Just drop the Vpb spinlock.
3013 IoReleaseVpbSpinLock( SavedIrql
);
3021 FatConstructNamesInFcb (
3022 IN PIRP_CONTEXT IrpContext
,
3025 PUNICODE_STRING Lfn OPTIONAL
3030 Routine Description:
3032 This routine places the short name in the dirent in the first set of
3033 STRINGs in the Fcb. If a long file name (Lfn) was specified, then
3034 we must decide whether we will store its Oem equivolent in the same
3035 prefix table as the short name, or rather just save the upcased
3036 version of the UNICODE string in the FCB.
3038 For looking up Fcbs, the first approach will be faster, so we want to
3039 do this as much as possible. Here are the rules that I have thought
3040 through extensively to determine when it is safe to store only Oem
3041 version of the UNICODE name.
3043 - If the UNICODE name contains no extended characters (>0x80), use Oem.
3045 - Let U be the upcased version of the UNICODE name.
3046 Let Up(x) be the function that upcases a UNICODE string.
3047 Let Down(x) be the function that upcases a UNICODE string.
3048 Let OemToUni(x) be the function that converts an Oem string to Unicode.
3049 Let UniToOem(x) be the function that converts a Unicode string to Oem.
3050 Let BestOemFit(x) be the function that creates the Best uppercase Oem
3051 fit for the UNICODE string x.
3053 BestOemFit(x) = UniToOem(Up(OemToUni(UniToOem(x)))) <1>
3055 if (BestOemFit(U) == BestOemFit(Down(U)) <2>
3057 then I know that there exists no UNICODE string Y such that:
3063 BestOemFit(U) != BestOemFit(Y) <4>
3065 Consider string U as a collection of one character strings. The
3066 conjecture is clearly true for each sub-string, thus it is true
3067 for the entire string.
3069 Equation <1> is what we use to convert an incoming unicode name in
3070 FatCommonCreate() to Oem. The double conversion is done to provide
3071 better visual best fitting for characters in the Ansi code page but
3072 not in the Oem code page. A single Nls routine is provided to do
3073 this conversion efficiently.
3075 The idea is that with U, I only have to worry about a case varient Y
3076 matching it in a unicode compare, and I have shown that any case varient
3077 of U (the set Y defined in equation <3>), when filtered through <1>
3078 (as in create), will match the Oem string defined in <1>.
3080 Thus I do not have to worry about another UNICODE string missing in
3081 the prefix lookup, but matching when comparing LFNs in the directory.
3085 Fcb - The Fcb we are supposed to fill in. Note that ParentDcb must
3086 already be filled in.
3088 Dirent - The gives up the short name.
3090 Lfn - If provided, this gives us the long name.
3108 UNICODE_STRING Unicode
;
3109 POEM_STRING ShortName
;
3110 POEM_STRING LongOemName
;
3111 PUNICODE_STRING LongUniName
;
3115 ShortName
= &Fcb
->ShortName
.Name
.Oem
;
3117 NT_ASSERT( ShortName
->Buffer
== NULL
);
3122 // First do the short name.
3126 // Copy over the case flags for the short name of the file
3129 if (FlagOn(Dirent
->NtByte
, FAT_DIRENT_NT_BYTE_8_LOWER_CASE
)) {
3131 SetFlag(Fcb
->FcbState
, FCB_STATE_8_LOWER_CASE
);
3135 ClearFlag(Fcb
->FcbState
, FCB_STATE_8_LOWER_CASE
);
3138 if (FlagOn(Dirent
->NtByte
, FAT_DIRENT_NT_BYTE_3_LOWER_CASE
)) {
3140 SetFlag(Fcb
->FcbState
, FCB_STATE_3_LOWER_CASE
);
3144 ClearFlag(Fcb
->FcbState
, FCB_STATE_3_LOWER_CASE
);
3147 ShortName
->MaximumLength
= 16;
3148 ShortName
->Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3150 TAG_FILENAME_BUFFER
);
3152 Fat8dot3ToString( IrpContext
, Dirent
, FALSE
, ShortName
);
3154 Fcb
->ShortName
.FileNameDos
= TRUE
;
3157 // If no Lfn was specified, we are done. In either case, set the
3158 // final name length.
3161 NT_ASSERT( Fcb
->ExactCaseLongName
.Buffer
== NULL
);
3163 if (!ARGUMENT_PRESENT(Lfn
) || (Lfn
->Length
== 0)) {
3165 Fcb
->FinalNameLength
= (USHORT
) RtlOemStringToCountedUnicodeSize( ShortName
);
3166 Fcb
->ExactCaseLongName
.Length
= Fcb
->ExactCaseLongName
.MaximumLength
= 0;
3168 try_return( NOTHING
);
3172 // If we already set up the full filename, we could be in trouble. If the fast
3173 // path for doing it already fired, FatSetFullFileNameInFcb, it will have missed
3174 // this and could have built the full filename out of the shortname of the file.
3176 // At that point, disaster could be inevitable since the final name length will not
3177 // match. We use this to tell the notify package what to do - FatNotifyReportChange.
3180 NT_ASSERT( Fcb
->FullFileName
.Buffer
== NULL
);
3183 // We know now we have an Lfn, save away a copy.
3186 Fcb
->FinalNameLength
= Lfn
->Length
;
3188 Fcb
->ExactCaseLongName
.Length
= Fcb
->ExactCaseLongName
.MaximumLength
= Lfn
->Length
;
3189 Fcb
->ExactCaseLongName
.Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3191 TAG_FILENAME_BUFFER
);
3192 RtlCopyMemory(Fcb
->ExactCaseLongName
.Buffer
, Lfn
->Buffer
, Lfn
->Length
);
3195 // First check for no extended characters.
3198 for (i
=0; i
< Lfn
->Length
/sizeof(WCHAR
); i
++) {
3200 if (Lfn
->Buffer
[i
] >= 0x80) {
3206 if (i
== Lfn
->Length
/sizeof(WCHAR
)) {
3209 // Cool, I can go with the Oem, upcase it fast by hand.
3212 LongOemName
= &Fcb
->LongName
.Oem
.Name
.Oem
;
3215 LongOemName
->Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3216 Lfn
->Length
/sizeof(WCHAR
),
3217 TAG_FILENAME_BUFFER
);
3218 LongOemName
->Length
=
3219 LongOemName
->MaximumLength
= Lfn
->Length
/sizeof(WCHAR
);
3221 for (i
=0; i
< Lfn
->Length
/sizeof(WCHAR
); i
++) {
3228 #pragma warning( push )
3229 #pragma warning( disable:4244 )
3231 LongOemName
->Buffer
[i
] = c
< 'a' ?
3234 c
- (UCHAR
)('a'-'A') :
3237 #pragma warning( pop )
3242 // If this name happens to be exactly the same as the short
3243 // name, don't add it to the splay table.
3246 if (FatAreNamesEqual(IrpContext
, *ShortName
, *LongOemName
) ||
3247 (FatFindFcb( IrpContext
,
3248 &Fcb
->ParentDcb
->Specific
.Dcb
.RootOemNode
,
3252 ExFreePool( LongOemName
->Buffer
);
3254 LongOemName
->Buffer
= NULL
;
3255 LongOemName
->Length
=
3256 LongOemName
->MaximumLength
= 0;
3260 SetFlag( Fcb
->FcbState
, FCB_STATE_HAS_OEM_LONG_NAME
);
3263 try_return( NOTHING
);
3267 // Now we have the fun part. Make a copy of the Lfn.
3274 Unicode
.Buffer
= NULL
;
3277 Unicode
.MaximumLength
= Lfn
->Length
;
3278 Unicode
.Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3280 TAG_FILENAME_BUFFER
);
3282 RtlCopyMemory( Unicode
.Buffer
, Lfn
->Buffer
, Lfn
->Length
);
3285 Status
= STATUS_SUCCESS
;
3290 // Unfortunately, this next block of code breaks down when you have
3291 // two long Unicode filenames that both map to the same Oem (and are,
3292 // well, long, i.e. are not the short names). In this case, with one
3293 // in the prefix table first, the other will hit the common Oem
3294 // representation. This leads to several forms of user astonishment.
3296 // It isn't worth it, or probably even possible, to try to figure out
3297 // when this is really safe to go through. Simply omit the attempt.
3299 // Ex: ANSI 0x82 and 0x84 in the 1252 ANSI->UNI and 437 UNI->OEM codepages.
3301 // 0x82 => 0x201a => 0x2c
3302 // 0x84 => 0x201e => 0x2c
3304 // 0x2c is comma, so is FAT Oem illegal and forces shortname generation.
3305 // Since it is otherwise well-formed by the rules articulated previously,
3306 // we would have put 0x2c in the Oem prefix tree. In terms of the
3307 // argument given above, even though there exist no Y and U s.t.
3309 // Up(Y) == Up(U) && BestOemFit(U) != BestOemFit(Y)
3311 // there most certainly exist Y and U s.t.
3313 // Up(Y) != Up(U) && BestOemFit(U) == BestOemFit(Y)
3315 // and that is enough to keep us from doing this. Note that the < 0x80
3316 // case is OK since we know that the mapping in the OEM codepages are
3317 // the identity in that range.
3319 // We still need to monocase it, though. Do this through a full down/up
3323 (VOID
)RtlDowncaseUnicodeString( &Unicode
, &Unicode
, FALSE
);
3324 (VOID
)RtlUpcaseUnicodeString( &Unicode
, &Unicode
, FALSE
);
3327 // Downcase and convert to upcased Oem. Only continue if we can
3328 // convert without error. Any error other than UNMAPPABLE_CHAR
3329 // is a fatal error and we raise.
3331 // Note that even if the conversion fails, we must leave Unicode
3332 // in an upcased state.
3334 // NB: The Rtl doesn't NULL .Buffer on error.
3337 (VOID
)RtlDowncaseUnicodeString( &Unicode
, &Unicode
, FALSE
);
3338 Status
= RtlUpcaseUnicodeStringToCountedOemString( &OemA
, &Unicode
, TRUE
);
3339 (VOID
)RtlUpcaseUnicodeString( &Unicode
, &Unicode
, FALSE
);
3341 if (!NT_SUCCESS(Status
)) {
3343 if (Status
!= STATUS_UNMAPPABLE_CHARACTER
) {
3345 NT_ASSERT( Status
== STATUS_NO_MEMORY
);
3346 ExFreePool(Unicode
.Buffer
);
3347 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
3353 // The same as above except upcase.
3356 Status
= RtlUpcaseUnicodeStringToCountedOemString( &OemB
, &Unicode
, TRUE
);
3358 if (!NT_SUCCESS(Status
)) {
3360 RtlFreeOemString( &OemA
);
3362 if (Status
!= STATUS_UNMAPPABLE_CHARACTER
) {
3364 NT_ASSERT( Status
== STATUS_NO_MEMORY
);
3365 ExFreePool(Unicode
.Buffer
);
3366 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
3372 // If the final OemNames are equal, I can use save only the Oem
3373 // name. If the name did not map, then I have to go with the UNICODE
3374 // name because I could get a case varient that didn't convert
3375 // in create, but did match the LFN.
3378 if (NT_SUCCESS(Status
) && FatAreNamesEqual( IrpContext
, OemA
, OemB
)) {
3381 // Cool, I can go with the Oem. If we didn't convert correctly,
3382 // get a fresh convert from the original LFN.
3385 ExFreePool(Unicode
.Buffer
);
3387 RtlFreeOemString( &OemB
);
3389 Fcb
->LongName
.Oem
.Name
.Oem
= OemA
;
3392 // If this name happens to be exactly the same as the short
3393 // name, or a similar short name already exists don't add it
3394 // to the splay table (note the final condition implies a
3398 if (FatAreNamesEqual(IrpContext
, *ShortName
, OemA
) ||
3399 (FatFindFcb( IrpContext
,
3400 &Fcb
->ParentDcb
->Specific
.Dcb
.RootOemNode
,
3404 RtlFreeOemString( &OemA
);
3408 SetFlag( Fcb
->FcbState
, FCB_STATE_HAS_OEM_LONG_NAME
);
3411 try_return( NOTHING
);
3415 // The long name must be left in UNICODE. Free the two Oem strings
3416 // if we got here just because they weren't equal.
3419 if (NT_SUCCESS(Status
)) {
3421 RtlFreeOemString( &OemA
);
3422 RtlFreeOemString( &OemB
);
3426 LongUniName
= &Fcb
->LongName
.Unicode
.Name
.Unicode
;
3428 LongUniName
->Length
=
3429 LongUniName
->MaximumLength
= Unicode
.Length
;
3430 LongUniName
->Buffer
= Unicode
.Buffer
;
3432 SetFlag(Fcb
->FcbState
, FCB_STATE_HAS_UNICODE_LONG_NAME
);
3437 if (_SEH2_AbnormalTermination()) {
3439 if (ShortName
->Buffer
!= NULL
) {
3441 ExFreePool( ShortName
->Buffer
);
3442 ShortName
->Buffer
= NULL
;
3448 // Creating all the names worked, so add all the names
3449 // to the splay tree.
3452 FatInsertName( IrpContext
,
3453 &Fcb
->ParentDcb
->Specific
.Dcb
.RootOemNode
,
3456 Fcb
->ShortName
.Fcb
= Fcb
;
3458 if (FlagOn(Fcb
->FcbState
, FCB_STATE_HAS_OEM_LONG_NAME
)) {
3460 FatInsertName( IrpContext
,
3461 &Fcb
->ParentDcb
->Specific
.Dcb
.RootOemNode
,
3462 &Fcb
->LongName
.Oem
);
3464 Fcb
->LongName
.Oem
.Fcb
= Fcb
;
3467 if (FlagOn(Fcb
->FcbState
, FCB_STATE_HAS_UNICODE_LONG_NAME
)) {
3469 FatInsertName( IrpContext
,
3470 &Fcb
->ParentDcb
->Specific
.Dcb
.RootUnicodeNode
,
3471 &Fcb
->LongName
.Unicode
);
3473 Fcb
->LongName
.Unicode
.Fcb
= Fcb
;
3476 SetFlag(Fcb
->FcbState
, FCB_STATE_NAMES_IN_SPLAY_TREE
);
3484 _Requires_lock_held_(_Global_critical_region_
)
3486 FatCheckFreeDirentBitmap (
3487 IN PIRP_CONTEXT IrpContext
,
3493 Routine Description:
3495 This routine checks if the size of the free dirent bitmap is
3496 sufficient to for the current directory size. It is called
3497 whenever we grow a directory.
3501 Dcb - Supplies the directory in question.
3510 ULONG OldNumberOfDirents
;
3511 ULONG NewNumberOfDirents
;
3514 UNREFERENCED_PARAMETER( IrpContext
);
3517 // Setup the Bitmap buffer if it is not big enough already
3520 NT_ASSERT( Dcb
->Header
.AllocationSize
.QuadPart
!= FCB_LOOKUP_ALLOCATIONSIZE_HINT
);
3522 OldNumberOfDirents
= Dcb
->Specific
.Dcb
.FreeDirentBitmap
.SizeOfBitMap
;
3523 NewNumberOfDirents
= Dcb
->Header
.AllocationSize
.LowPart
/ sizeof(DIRENT
);
3526 // Do the usual unsync/sync check.
3529 if (NewNumberOfDirents
> OldNumberOfDirents
) {
3531 FatAcquireDirectoryFileMutex( Dcb
->Vcb
);
3535 PULONG OldBitmapBuffer
;
3536 PULONG BitmapBuffer
;
3538 ULONG BytesInBitmapBuffer
;
3539 ULONG BytesInOldBitmapBuffer
;
3541 OldNumberOfDirents
= Dcb
->Specific
.Dcb
.FreeDirentBitmap
.SizeOfBitMap
;
3542 NewNumberOfDirents
= Dcb
->Header
.AllocationSize
.LowPart
/ sizeof(DIRENT
);
3544 if (NewNumberOfDirents
> OldNumberOfDirents
) {
3547 // Remember the old bitmap
3550 OldBitmapBuffer
= Dcb
->Specific
.Dcb
.FreeDirentBitmap
.Buffer
;
3553 // Now make a new bitmap bufffer
3556 BytesInBitmapBuffer
= NewNumberOfDirents
/ 8;
3558 BytesInOldBitmapBuffer
= OldNumberOfDirents
/ 8;
3560 if (DCB_UNION_SLACK_SPACE
>= BytesInBitmapBuffer
) {
3562 BitmapBuffer
= &Dcb
->Specific
.Dcb
.FreeDirentBitmapBuffer
[0];
3566 BitmapBuffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3567 BytesInBitmapBuffer
,
3568 TAG_DIRENT_BITMAP
);
3572 // Copy the old buffer to the new buffer, free the old one, and zero
3573 // the rest of the new one. Only do the first two steps though if
3574 // we moved out of the initial buffer.
3577 if ((OldNumberOfDirents
!= 0) &&
3578 (BitmapBuffer
!= &Dcb
->Specific
.Dcb
.FreeDirentBitmapBuffer
[0])) {
3580 RtlCopyMemory( BitmapBuffer
,
3582 BytesInOldBitmapBuffer
);
3584 if (OldBitmapBuffer
!= &Dcb
->Specific
.Dcb
.FreeDirentBitmapBuffer
[0]) {
3586 ExFreePool( OldBitmapBuffer
);
3590 NT_ASSERT( BytesInBitmapBuffer
> BytesInOldBitmapBuffer
);
3592 RtlZeroMemory( (PUCHAR
)BitmapBuffer
+ BytesInOldBitmapBuffer
,
3593 BytesInBitmapBuffer
- BytesInOldBitmapBuffer
);
3596 // Now initialize the new bitmap.
3599 RtlInitializeBitMap( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
3601 NewNumberOfDirents
);
3606 FatReleaseDirectoryFileMutex( Dcb
->Vcb
);
3613 FatIsHandleCountZero (
3614 IN PIRP_CONTEXT IrpContext
,
3620 Routine Description:
3622 This routine decides if the handle count on the volume is zero.
3626 Vcb - The volume in question
3630 BOOLEAN - TRUE if there are no open handles on the volume, FALSE
3642 while (Fcb
!= NULL
) {
3644 if (Fcb
->UncleanCount
!= 0) {
3649 Fcb
= FatGetNextFcbTopDown(IrpContext
, Fcb
, Vcb
->RootDcb
);
3658 FatAllocateCloseContext(
3663 Routine Description:
3665 This routine preallocates a close context, presumeably on behalf
3666 of a fileobject which does not have a structure we can embed one
3680 UNREFERENCED_PARAMETER( Vcb
);
3683 if (ARGUMENT_PRESENT(Vcb
)) {
3685 NT_ASSERT( 0 != Vcb
->CloseContextCount
);
3686 InterlockedDecrement( (LONG
*)&Vcb
->CloseContextCount
);
3689 return (PCLOSE_CONTEXT
)ExInterlockedPopEntrySList( &FatCloseContextSList
,
3690 &FatData
.GeneralSpinLock
);
3695 FatPreallocateCloseContext (
3701 Routine Description:
3703 This routine preallocates a close context, presumeably on behalf
3704 of a fileobject which does not have a structure we can embed one
3718 PCLOSE_CONTEXT CloseContext
;
3722 UNREFERENCED_PARAMETER( Vcb
);
3724 CloseContext
= FsRtlAllocatePoolWithTag( PagedPool
,
3725 sizeof(CLOSE_CONTEXT
),
3726 TAG_FAT_CLOSE_CONTEXT
);
3728 ExInterlockedPushEntrySList( &FatCloseContextSList
,
3729 (PSLIST_ENTRY
) CloseContext
,
3730 &FatData
.GeneralSpinLock
);
3732 DbgDoit( InterlockedIncrement( (LONG
*)&Vcb
->CloseContextCount
));
3738 FatEnsureStringBufferEnough (
3739 _Inout_ PVOID String
,
3740 _In_ USHORT DesiredBufferSize
3745 Routine Description:
3747 Ensures that a string string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING)
3748 has a buffer >= DesiredBufferSize, allocating from pool if neccessary. Any
3749 existing pool buffer will be freed if a new one is allocated.
3751 NOTE: No copy of old buffer contents is performed on reallocation.
3753 Will raise on allocation failure.
3757 String - pointer to string structure
3759 DesiredBufferSize - (bytes) minimum required buffer size
3764 PSTRING LocalString
= String
;
3768 if (LocalString
->MaximumLength
< DesiredBufferSize
) {
3770 FatFreeStringBuffer( LocalString
);
3772 LocalString
->Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3774 TAG_DYNAMIC_NAME_BUFFER
);
3775 NT_ASSERT( LocalString
->Buffer
);
3777 LocalString
->MaximumLength
= DesiredBufferSize
;
3783 FatFreeStringBuffer (
3784 _Inout_ PVOID String
3789 Routine Description:
3791 Frees the buffer of an string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING)
3792 structure if it is not within the current thread's stack limits.
3794 Regardless of action performed, on exit String->Buffer will be set to NULL and
3795 String->MaximumLength to zero.
3799 String - pointer to string structure
3804 ULONG_PTR High
, Low
;
3805 PSTRING LocalString
= String
;
3809 if (NULL
!= LocalString
->Buffer
) {
3811 IoGetStackLimits( &Low
, &High
);
3813 if (((ULONG_PTR
)(LocalString
->Buffer
) < Low
) ||
3814 ((ULONG_PTR
)(LocalString
->Buffer
) > High
)) {
3816 ExFreePool( LocalString
->Buffer
);
3819 LocalString
->Buffer
= NULL
;
3822 LocalString
->MaximumLength
= LocalString
->Length
= 0;
3827 FatScanForDataTrack(
3828 IN PIRP_CONTEXT IrpContext
,
3829 IN PDEVICE_OBJECT TargetDeviceObject
3834 Routine Description:
3836 This routine is called to verify and process the TOC for this disk.
3838 FAT queries for the TOC to avoid trying to mount on CD-DA/CD-E media, Doing data reads on
3839 audio/leadin of that media sends a lot of drives into what could charitably be called
3840 "conniptions" which take a couple seconds to clear and would also convince FAT that the
3841 device was busted, and fail the mount (not letting CDFS get its crack).
3843 There is special handling of PD media. These things fail the TOC read, but return
3844 a special error code so FAT knows to continue to try the mount anyway.
3848 TargetDeviceObject - Device object to send TOC request to.
3852 BOOLEAN - TRUE if we found a TOC with a single data track.
3858 IO_STATUS_BLOCK Iosb
;
3860 ULONG LocalTrackCount
;
3861 ULONG LocalTocLength
;
3863 PCDROM_TOC CdromToc
;
3864 BOOLEAN Result
= FALSE
;
3868 CdromToc
= FsRtlAllocatePoolWithTag( PagedPool
,
3869 sizeof( CDROM_TOC
),
3872 RtlZeroMemory( CdromToc
, sizeof( CDROM_TOC
));
3877 // Go ahead and read the table of contents
3880 Status
= FatPerformDevIoCtrl( IrpContext
,
3881 IOCTL_CDROM_READ_TOC
,
3886 sizeof( CDROM_TOC
),
3892 // Nothing to process if this request fails.
3895 if (Status
!= STATUS_SUCCESS
) {
3898 // If we get the special error indicating a failed TOC read on PD media just
3899 // plow ahead with the mount (see comments above).
3902 if ((Status
== STATUS_IO_DEVICE_ERROR
) || (Status
== STATUS_INVALID_DEVICE_REQUEST
)) {
3908 try_leave( NOTHING
);
3912 // Get the number of tracks and stated size of this structure.
3915 LocalTrackCount
= CdromToc
->LastTrack
- CdromToc
->FirstTrack
+ 1;
3916 LocalTocLength
= PtrOffset( CdromToc
, &CdromToc
->TrackData
[LocalTrackCount
+ 1] );
3919 // Get out if there is an immediate problem with the TOC, or more than
3923 if ((LocalTocLength
> Iosb
.Information
) ||
3924 (CdromToc
->FirstTrack
> CdromToc
->LastTrack
) ||
3925 (LocalTrackCount
!= 1)) {
3927 try_leave( NOTHING
);
3931 // Is it a data track? DVD-RAM reports single, data, track.
3934 Result
= BooleanFlagOn( CdromToc
->TrackData
[ 0].Control
, 0x04 );
3938 ExFreePool( CdromToc
);