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_Real)
175 #pragma alloc_text(PAGE, FatCreateCcb)
176 #pragma alloc_text(PAGE, FatDeallocateCcbStrings)
177 #pragma alloc_text(PAGE, FatDeleteCcb_Real)
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, FatPreallocateCloseContext)
191 IN PIRP_CONTEXT IrpContext
,
193 IN PDEVICE_OBJECT TargetDeviceObject
,
195 IN PDEVICE_OBJECT FsDeviceObject
202 This routine initializes and inserts a new Vcb record into the in-memory
203 data structure. The Vcb record "hangs" off the end of the Volume device
204 object and must be allocated by our caller.
208 Vcb - Supplies the address of the Vcb record being initialized.
210 TargetDeviceObject - Supplies the address of the target device object to
211 associate with the Vcb record.
213 Vpb - Supplies the address of the Vpb to associate with the Vcb record.
215 FsDeviceObject - The filesystem device object that the mount was directed
225 CC_FILE_SIZES FileSizes
;
226 PDEVICE_OBJECT RealDevice
;
229 STORAGE_HOTPLUG_INFO HotplugInfo
;
233 // The following variables are used for abnormal unwind
236 PLIST_ENTRY UnwindEntryList
= NULL
;
237 PERESOURCE UnwindResource
= NULL
;
238 PERESOURCE UnwindResource2
= NULL
;
239 PFILE_OBJECT UnwindFileObject
= NULL
;
240 PFILE_OBJECT UnwindCacheMap
= NULL
;
241 BOOLEAN UnwindWeAllocatedMcb
= FALSE
;
242 PFILE_SYSTEM_STATISTICS UnwindStatistics
= NULL
;
244 DebugTrace(+1, Dbg
, "FatInitializeVcb, Vcb = %08lx\n", Vcb
);
249 // We start by first zeroing out all of the VCB, this will guarantee
250 // that any stale data is wiped clean
253 RtlZeroMemory( Vcb
, sizeof(VCB
) );
256 // Set the proper node type code and node byte size
259 Vcb
->VolumeFileHeader
.NodeTypeCode
= FAT_NTC_VCB
;
260 Vcb
->VolumeFileHeader
.NodeByteSize
= sizeof(VCB
);
263 // Initialize the tunneling cache
266 FsRtlInitializeTunnelCache(&Vcb
->Tunnel
);
269 // Insert this Vcb record on the FatData.VcbQueue
272 ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
274 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
275 InsertTailList( &FatData
.VcbQueue
, &Vcb
->VcbLinks
);
276 FatReleaseGlobal( IrpContext
);
277 UnwindEntryList
= &Vcb
->VcbLinks
;
280 // Set the Target Device Object, Vpb, and Vcb State fields
284 ObReferenceObject( TargetDeviceObject
);
285 Vcb
->TargetDeviceObject
= TargetDeviceObject
;
288 Vcb
->CurrentDevice
= Vpb
->RealDevice
;
291 // Set the removable media and defflush flags based on the storage
292 // inquiry and the old characteristic bits.
295 Status
= FatPerformDevIoCtrl( IrpContext
,
296 IOCTL_STORAGE_GET_HOTPLUG_INFO
,
304 if (NT_SUCCESS( Status
)) {
306 if (HotplugInfo
.MediaRemovable
) {
308 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
);
311 if (!HotplugInfo
.WriteCacheEnableOverride
) {
314 // If the device or media is hotplug and the override is not
315 // set, force defflush behavior for the device.
318 if (HotplugInfo
.MediaHotplug
|| HotplugInfo
.DeviceHotplug
) {
320 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
);
323 // Now, for removables that claim to be lockable, lob a lock
324 // request and see if it works. There can unfortunately be
325 // transient, media dependent reasons that it can fail. If
326 // it does not, we must force defflush on.
329 } else if (HotplugInfo
.MediaRemovable
&&
330 !HotplugInfo
.MediaHotplug
) {
332 Status
= FatToggleMediaEjectDisable( IrpContext
, Vcb
, TRUE
);
334 if (!NT_SUCCESS( Status
)) {
336 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
);
340 Status
= FatToggleMediaEjectDisable( IrpContext
, Vcb
, FALSE
);
345 if (FlagOn(Vpb
->RealDevice
->Characteristics
, FILE_REMOVABLE_MEDIA
)) {
347 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_REMOVABLE_MEDIA
);
351 // Make sure we turn on deferred flushing for floppies like we always
355 if (FlagOn(Vpb
->RealDevice
->Characteristics
, FILE_FLOPPY_DISKETTE
)) {
357 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
);
360 Vcb
->VcbCondition
= VcbGood
;
363 // Initialize the resource variable for the Vcb
366 ExInitializeResourceLite( &Vcb
->Resource
);
367 UnwindResource
= &Vcb
->Resource
;
369 ExInitializeResourceLite( &Vcb
->ChangeBitMapResource
);
370 UnwindResource2
= &Vcb
->ChangeBitMapResource
;
373 // Initialize the free cluster bitmap mutex.
376 ExInitializeFastMutex( &Vcb
->FreeClusterBitMapMutex
);
379 // Create the special file object for the virtual volume file with a close
380 // context, its pointers back to the Vcb and the section object pointer.
382 // We don't have to unwind the close context. That will happen in the close
383 // path automatically.
386 RealDevice
= Vcb
->CurrentDevice
;
388 Vcb
->VirtualVolumeFile
= UnwindFileObject
= IoCreateStreamFileObject( NULL
, RealDevice
);
389 FatPreallocateCloseContext();
391 FatSetFileObject( Vcb
->VirtualVolumeFile
,
397 // Remember this internal, residual open.
401 InterlockedIncrement( &(Vcb
->InternalOpenCount
) );
402 InterlockedIncrement( &(Vcb
->ResidualOpenCount
) );
405 InterlockedIncrement( (LONG
*)&(Vcb
->InternalOpenCount
) );
406 InterlockedIncrement( (LONG
*)&(Vcb
->ResidualOpenCount
) );
409 Vcb
->VirtualVolumeFile
->SectionObjectPointer
= &Vcb
->SectionObjectPointers
;
411 Vcb
->VirtualVolumeFile
->ReadAccess
= TRUE
;
412 Vcb
->VirtualVolumeFile
->WriteAccess
= TRUE
;
413 Vcb
->VirtualVolumeFile
->DeleteAccess
= TRUE
;
416 // Initialize the notify structures.
419 InitializeListHead( &Vcb
->DirNotifyList
);
421 FsRtlNotifyInitializeSync( &Vcb
->NotifySync
);
424 // Initialize the Cache Map for the volume file. The size is
425 // initially set to that of our first read. It will be extended
426 // when we know how big the Fat is.
429 FileSizes
.AllocationSize
.QuadPart
=
430 FileSizes
.FileSize
.QuadPart
= sizeof(PACKED_BOOT_SECTOR
);
431 FileSizes
.ValidDataLength
= FatMaxLarge
;
433 CcInitializeCacheMap( Vcb
->VirtualVolumeFile
,
436 &FatData
.CacheManagerNoOpCallbacks
,
438 UnwindCacheMap
= Vcb
->VirtualVolumeFile
;
441 // Initialize the structure that will keep track of dirty fat sectors.
442 // The largest possible Mcb structures are less than 1K, so we use
446 FsRtlInitializeLargeMcb( &Vcb
->DirtyFatMcb
, PagedPool
);
448 UnwindWeAllocatedMcb
= TRUE
;
451 // Set the cluster index hint to the first valid cluster of a fat: 2
454 Vcb
->ClusterHint
= 2;
457 // Initialize the directory stream file object creation event.
458 // This event is also "borrowed" for async non-cached writes.
461 ExInitializeFastMutex( &Vcb
->DirectoryFileCreationMutex
);
464 // Initialize the clean volume callback Timer and DPC.
467 KeInitializeTimer( &Vcb
->CleanVolumeTimer
);
469 KeInitializeDpc( &Vcb
->CleanVolumeDpc
, FatCleanVolumeDpc
, Vcb
);
472 // Initialize the performance counters.
475 Vcb
->Statistics
= FsRtlAllocatePoolWithTag( NonPagedPool
,
476 sizeof(FILE_SYSTEM_STATISTICS
) * KeNumberProcessors
,
478 UnwindStatistics
= Vcb
->Statistics
;
480 RtlZeroMemory( Vcb
->Statistics
, sizeof(FILE_SYSTEM_STATISTICS
) * KeNumberProcessors
);
482 for (i
= 0; i
< KeNumberProcessors
; i
+= 1) {
483 Vcb
->Statistics
[i
].Common
.FileSystemType
= FILESYSTEM_STATISTICS_TYPE_FAT
;
484 Vcb
->Statistics
[i
].Common
.Version
= 1;
485 Vcb
->Statistics
[i
].Common
.SizeOfCompleteStructure
=
486 sizeof(FILE_SYSTEM_STATISTICS
);
490 // Pick up a VPB right now so we know we can pull this filesystem stack off
491 // of the storage stack on demand.
494 Vcb
->SwapVpb
= FsRtlAllocatePoolWithTag( NonPagedPool
,
498 RtlZeroMemory( Vcb
->SwapVpb
, sizeof( VPB
) );
501 // Initialize the close queue listheads.
504 InitializeListHead( &Vcb
->AsyncCloseList
);
505 InitializeListHead( &Vcb
->DelayedCloseList
);
508 // Initialize the Advanced FCB Header
511 ExInitializeFastMutex( &Vcb
->AdvancedFcbHeaderMutex
);
512 FsRtlSetupAdvancedHeader( &Vcb
->VolumeFileHeader
,
513 &Vcb
->AdvancedFcbHeaderMutex
);
516 // With the Vcb now set up, set the IrpContext Vcb field.
519 IrpContext
->Vcb
= Vcb
;
523 DebugUnwind( FatInitializeVcb
);
526 // If this is an abnormal termination then undo our work
529 if (_SEH2_AbnormalTermination()) {
531 if (UnwindCacheMap
!= NULL
) { FatSyncUninitializeCacheMap( IrpContext
, UnwindCacheMap
); }
532 if (UnwindFileObject
!= NULL
) { ObDereferenceObject( UnwindFileObject
); }
533 if (UnwindResource
!= NULL
) { FatDeleteResource( UnwindResource
); }
534 if (UnwindResource2
!= NULL
) { FatDeleteResource( UnwindResource2
); }
535 if (UnwindWeAllocatedMcb
) { FsRtlUninitializeLargeMcb( &Vcb
->DirtyFatMcb
); }
536 if (UnwindEntryList
!= NULL
) {
537 (VOID
)FatAcquireExclusiveGlobal( IrpContext
);
538 RemoveEntryList( UnwindEntryList
);
539 FatReleaseGlobal( IrpContext
);
541 if (UnwindStatistics
!= NULL
) { ExFreePool( UnwindStatistics
); }
544 DebugTrace(-1, Dbg
, "FatInitializeVcb -> VOID\n", 0);
548 // and return to our caller
551 UNREFERENCED_PARAMETER( IrpContext
);
558 IN PIRP_CONTEXT IrpContext
,
566 This routine tries to remove all internal opens from the volume.
570 IrpContext - Supplies the context for the overall request.
572 Vcb - Supplies the Vcb to be torn down.
581 PFILE_OBJECT DirectoryFileObject
;
587 // Get rid of the virtual volume file, if we need to.
590 if (Vcb
->VirtualVolumeFile
!= NULL
) {
593 // Uninitialize the cache
596 FatSyncUninitializeCacheMap( IrpContext
, Vcb
->VirtualVolumeFile
);
599 // Dereference the virtual volume file. This will cause a close
600 // Irp to be processed, so we need to do this before we destory
604 FsRtlTeardownPerStreamContexts( &Vcb
->VolumeFileHeader
);
606 ObDereferenceObject( Vcb
->VirtualVolumeFile
);
608 Vcb
->VirtualVolumeFile
= NULL
;
612 // Close down the EA file.
615 FatCloseEaFile( IrpContext
, Vcb
, FALSE
);
618 // Close down the root directory stream..
621 if (Vcb
->RootDcb
!= NULL
) {
623 DirectoryFileObject
= Vcb
->RootDcb
->Specific
.Dcb
.DirectoryFile
;
625 if (DirectoryFileObject
!= NULL
) {
628 // Tear down this directory file.
631 FatSyncUninitializeCacheMap( IrpContext
,
632 DirectoryFileObject
);
634 Vcb
->RootDcb
->Specific
.Dcb
.DirectoryFile
= NULL
;
635 ObDereferenceObject( DirectoryFileObject
);
640 // The VCB can no longer be used.
643 FatSetVcbCondition( Vcb
, VcbBad
);
649 IN PIRP_CONTEXT IrpContext
,
657 This routine removes the Vcb record from Fat's in-memory data
658 structures. It also will remove all associated underlings
663 Vcb - Supplies the Vcb to be removed
674 DebugTrace(+1, Dbg
, "FatDeleteVcb, Vcb = %08lx\n", Vcb
);
677 // If the IrpContext points to the VCB being deleted NULL out the stall
681 if (IrpContext
->Vcb
== Vcb
) {
683 IrpContext
->Vcb
= NULL
;
689 // Check the backpocket Vpb we kept just in case.
694 ExFreePool( Vcb
->SwapVpb
);
698 // Free the VPB, if we need to.
701 if (FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
)) {
704 // We swapped the VPB, so we need to free the main one.
707 ExFreePool( Vcb
->Vpb
);
711 // Remove this record from the global list of all Vcb records.
712 // Note that the global lock must already be held when calling
716 RemoveEntryList( &(Vcb
->VcbLinks
) );
719 // Make sure the direct access open count is zero, and the open file count
723 if ((Vcb
->DirectAccessOpenCount
!= 0) || (Vcb
->OpenFileCount
!= 0)) {
725 FatBugCheck( 0, 0, 0 );
729 // Remove the EaFcb and dereference the Fcb for the Ea file if it
733 if (Vcb
->EaFcb
!= NULL
) {
735 Vcb
->EaFcb
->OpenCount
= 0;
736 FatDeleteFcb( IrpContext
, Vcb
->EaFcb
);
742 // Remove the Root Dcb
745 if (Vcb
->RootDcb
!= NULL
) {
748 // Rundown stale child Fcbs that may be hanging around. Yes, this
749 // can happen. No, the create path isn't perfectly defensive about
750 // tearing down branches built up on creates that don't wind up
751 // succeeding. Normal system operation usually winds up having
752 // cleaned them out through re-visiting, but ...
754 // Just pick off Fcbs from the bottom of the tree until we run out.
755 // Then we delete the root Dcb.
758 while( (Fcb
= FatGetNextFcbBottomUp( IrpContext
, NULL
, Vcb
->RootDcb
)) != Vcb
->RootDcb
) {
760 FatDeleteFcb( IrpContext
, Fcb
);
763 FatDeleteFcb( IrpContext
, Vcb
->RootDcb
);
767 // Uninitialize the notify sychronization object.
770 FsRtlNotifyUninitializeSync( &Vcb
->NotifySync
);
773 // Uninitialize the resource variable for the Vcb
776 FatDeleteResource( &Vcb
->Resource
);
777 FatDeleteResource( &Vcb
->ChangeBitMapResource
);
780 // If allocation support has been setup, free it.
783 if (Vcb
->FreeClusterBitMap
.Buffer
!= NULL
) {
785 FatTearDownAllocationSupport( IrpContext
, Vcb
);
789 // UnInitialize the Mcb structure that kept track of dirty fat sectors.
792 FsRtlUninitializeLargeMcb( &Vcb
->DirtyFatMcb
);
795 // Free the pool for the stached copy of the boot sector
798 if ( Vcb
->First0x24BytesOfBootSector
) {
800 ExFreePool( Vcb
->First0x24BytesOfBootSector
);
804 // Cancel the CleanVolume Timer and Dpc
807 (VOID
)KeCancelTimer( &Vcb
->CleanVolumeTimer
);
809 (VOID
)KeRemoveQueueDpc( &Vcb
->CleanVolumeDpc
);
812 // Free the performance counters memory
815 ExFreePool( Vcb
->Statistics
);
818 // Clean out the tunneling cache
821 FsRtlDeleteTunnelCache(&Vcb
->Tunnel
);
824 // Dereference the target device object.
827 ObDereferenceObject( Vcb
->TargetDeviceObject
);
830 // And zero out the Vcb, this will help ensure that any stale data is
834 RtlZeroMemory( Vcb
, sizeof(VCB
) );
837 // return and tell the caller
840 DebugTrace(-1, Dbg
, "FatDeleteVcb -> VOID\n", 0);
848 IN PIRP_CONTEXT IrpContext
,
856 This routine allocates, initializes, and inserts a new root DCB record
857 into the in memory data structure.
861 Vcb - Supplies the Vcb to associate the new DCB under
865 None. The Vcb is modified in-place.
873 // The following variables are used for abnormal unwind
876 PVOID UnwindStorage
[2] = { NULL
, NULL
};
877 PERESOURCE UnwindResource
= NULL
;
878 PERESOURCE UnwindResource2
= NULL
;
879 PLARGE_MCB UnwindMcb
= NULL
;
880 PFILE_OBJECT UnwindFileObject
= NULL
;
882 DebugTrace(+1, Dbg
, "FatCreateRootDcb, Vcb = %08lx\n", Vcb
);
887 // Make sure we don't already have a root dcb for this vcb
890 if (Vcb
->RootDcb
!= NULL
) {
892 DebugDump("Error trying to create multiple root dcbs\n", 0, Vcb
);
893 FatBugCheck( 0, 0, 0 );
897 // Allocate a new DCB and zero it out, we use Dcb locally so we don't
898 // have to continually reference through the Vcb
901 UnwindStorage
[0] = Dcb
= Vcb
->RootDcb
= FsRtlAllocatePoolWithTag( NonPagedPool
,
905 RtlZeroMemory( Dcb
, sizeof(DCB
));
908 Dcb
->NonPaged
= FatAllocateNonPagedFcb();
910 RtlZeroMemory( Dcb
->NonPaged
, sizeof( NON_PAGED_FCB
) );
913 // Set the proper node type code, node byte size, and call backs
916 Dcb
->Header
.NodeTypeCode
= FAT_NTC_ROOT_DCB
;
917 Dcb
->Header
.NodeByteSize
= sizeof(DCB
);
919 Dcb
->FcbCondition
= FcbGood
;
922 // The parent Dcb, initial state, open count, dirent location
923 // information, and directory change count fields are already zero so
924 // we can skip setting them
928 // Initialize the resource variable
932 Dcb
->Header
.Resource
= FatAllocateResource();
935 // Initialize the PagingIo Resource. We no longer use the FsRtl common
936 // shared pool because this led to a) deadlocks due to cases where files
937 // and their parent directories shared a resource and b) there is no way
938 // to anticipate inter-driver induced deadlock via recursive operation.
942 Dcb
->Header
.PagingIoResource
= FatAllocateResource();
945 // The root Dcb has an empty parent dcb links field
948 InitializeListHead( &Dcb
->ParentDcbLinks
);
957 // initialize the parent dcb queue.
960 InitializeListHead( &Dcb
->Specific
.Dcb
.ParentDcbQueue
);
963 // Set the full file name up.
966 Dcb
->FullFileName
.Buffer
= L
"\\";
967 Dcb
->FullFileName
.Length
= (USHORT
)2;
968 Dcb
->FullFileName
.MaximumLength
= (USHORT
)4;
970 Dcb
->ShortName
.Name
.Oem
.Buffer
= "\\";
971 Dcb
->ShortName
.Name
.Oem
.Length
= (USHORT
)1;
972 Dcb
->ShortName
.Name
.Oem
.MaximumLength
= (USHORT
)2;
975 // Construct a lie about file properties since we don't
976 // have a proper "." entry to look at.
979 Dcb
->DirentFatFlags
= FILE_ATTRIBUTE_DIRECTORY
;
982 // Initialize Advanced FCB Header fields
985 ExInitializeFastMutex( &Dcb
->NonPaged
->AdvancedFcbHeaderMutex
);
986 FsRtlSetupAdvancedHeader( &Dcb
->Header
,
987 &Dcb
->NonPaged
->AdvancedFcbHeaderMutex
);
990 // Initialize the Mcb, and setup its mapping. Note that the root
991 // directory is a fixed size so we can set it everything up now.
994 FsRtlInitializeLargeMcb( &Dcb
->Mcb
, NonPagedPool
);
995 UnwindMcb
= &Dcb
->Mcb
;
997 if (FatIsFat32(Vcb
)) {
1000 // The first cluster of fat32 roots comes from the BPB
1003 Dcb
->FirstClusterOfFile
= Vcb
->Bpb
.RootDirFirstCluster
;
1007 FatAddMcbEntry( Vcb
, &Dcb
->Mcb
,
1009 FatRootDirectoryLbo( &Vcb
->Bpb
),
1010 FatRootDirectorySize( &Vcb
->Bpb
));
1013 if (FatIsFat32(Vcb
)) {
1016 // Find the size of the fat32 root. As a side-effect, this will create
1017 // MCBs for the entire root. In the process of doing this, we may
1018 // discover that the FAT chain is bogus and raise corruption.
1021 Dcb
->Header
.AllocationSize
.LowPart
= 0xFFFFFFFF;
1022 FatLookupFileAllocationSize( IrpContext
, Dcb
);
1024 Dcb
->Header
.FileSize
.QuadPart
=
1025 Dcb
->Header
.AllocationSize
.QuadPart
;
1029 // set the allocation size to real size of the root directory
1032 Dcb
->Header
.FileSize
.QuadPart
=
1033 Dcb
->Header
.AllocationSize
.QuadPart
= FatRootDirectorySize( &Vcb
->Bpb
);
1038 // Set our two create dirent aids to represent that we have yet to
1039 // enumerate the directory for never used or deleted dirents.
1042 Dcb
->Specific
.Dcb
.UnusedDirentVbo
= 0xffffffff;
1043 Dcb
->Specific
.Dcb
.DeletedDirentHint
= 0xffffffff;
1046 // Setup the free dirent bitmap buffer.
1049 RtlInitializeBitMap( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
1053 FatCheckFreeDirentBitmap( IrpContext
, Dcb
);
1057 DebugUnwind( FatCreateRootDcb
);
1060 // If this is an abnormal termination then undo our work
1063 if (_SEH2_AbnormalTermination()) {
1067 if (UnwindFileObject
!= NULL
) { ObDereferenceObject( UnwindFileObject
); }
1068 if (UnwindMcb
!= NULL
) { FsRtlUninitializeLargeMcb( UnwindMcb
); }
1069 if (UnwindResource
!= NULL
) { FatFreeResource( UnwindResource
); }
1070 if (UnwindResource2
!= NULL
) { FatFreeResource( UnwindResource2
); }
1072 for (i
= 0; i
< sizeof(UnwindStorage
)/sizeof(PVOID
); i
+= 1) {
1073 if (UnwindStorage
[i
] != NULL
) { ExFreePool( UnwindStorage
[i
] ); }
1077 // Re-zero the entry in the Vcb.
1080 Vcb
->RootDcb
= NULL
;
1083 DebugTrace(-1, Dbg
, "FatCreateRootDcb -> %8lx\n", Dcb
);
1092 IN PIRP_CONTEXT IrpContext
,
1095 IN ULONG LfnOffsetWithinDirectory
,
1096 IN ULONG DirentOffsetWithinDirectory
,
1098 IN PUNICODE_STRING Lfn OPTIONAL
,
1099 IN BOOLEAN IsPagingFile
,
1100 IN BOOLEAN SingleResource
1105 Routine Description:
1107 This routine allocates, initializes, and inserts a new Fcb record into
1108 the in-memory data structures.
1112 Vcb - Supplies the Vcb to associate the new FCB under.
1114 ParentDcb - Supplies the parent dcb that the new FCB is under.
1116 LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is
1117 no LFN associated with this file then this value is same as
1118 DirentOffsetWithinDirectory.
1120 DirentOffsetWithinDirectory - Supplies the offset, in bytes from the
1121 start of the directory file where the dirent for the fcb is located
1123 Dirent - Supplies the dirent for the fcb being created
1125 Lfn - Supplies a long UNICODE name associated with this file.
1127 IsPagingFile - Indicates if we are creating an FCB for a paging file
1128 or some other type of file.
1130 SingleResource - Indicates if this Fcb should share a single resource
1131 as both main and paging.
1135 PFCB - Returns a pointer to the newly allocated FCB
1144 // The following variables are used for abnormal unwind
1147 PVOID UnwindStorage
[2] = { NULL
, NULL
};
1148 PERESOURCE UnwindResource
= NULL
;
1149 PERESOURCE UnwindResource2
= NULL
;
1150 PLIST_ENTRY UnwindEntryList
= NULL
;
1151 PLARGE_MCB UnwindMcb
= NULL
;
1152 PFILE_LOCK UnwindFileLock
= NULL
;
1153 POPLOCK UnwindOplock
= NULL
;
1155 DebugTrace(+1, Dbg
, "FatCreateFcb\n", 0);
1160 // Determine the pool type we should be using for the fcb and the
1166 PoolType
= NonPagedPool
;
1167 Fcb
= UnwindStorage
[0] = FsRtlAllocatePoolWithTag( NonPagedPool
,
1172 PoolType
= PagedPool
;
1173 Fcb
= UnwindStorage
[0] = FatAllocateFcb();
1178 // ... and zero it out
1181 RtlZeroMemory( Fcb
, sizeof(FCB
) );
1184 Fcb
->NonPaged
= FatAllocateNonPagedFcb();
1186 RtlZeroMemory( Fcb
->NonPaged
, sizeof( NON_PAGED_FCB
) );
1189 // Set the proper node type code, node byte size, and call backs
1192 Fcb
->Header
.NodeTypeCode
= FAT_NTC_FCB
;
1193 Fcb
->Header
.NodeByteSize
= sizeof(FCB
);
1195 Fcb
->FcbCondition
= FcbGood
;
1198 // Check to see if we need to set the Fcb state to indicate that this
1199 // is a paging/system file. This will prevent it from being opened
1205 SetFlag( Fcb
->FcbState
, FCB_STATE_PAGING_FILE
| FCB_STATE_SYSTEM_FILE
);
1209 // The initial state, open count, and segment objects fields are already
1210 // zero so we can skip setting them
1214 // Initialize the resource variable
1219 Fcb
->Header
.Resource
= FatAllocateResource();
1222 // Initialize the PagingIo Resource. We no longer use the FsRtl common
1223 // shared pool because this led to a) deadlocks due to cases where files
1224 // and their parent directories shared a resource and b) there is no way
1225 // to anticipate inter-driver induced deadlock via recursive operation.
1228 if (SingleResource
) {
1230 Fcb
->Header
.PagingIoResource
= Fcb
->Header
.Resource
;
1235 Fcb
->Header
.PagingIoResource
= FatAllocateResource();
1239 // Insert this fcb into our parent dcb's queue.
1241 // There is a deep reason why this goes on the tail, to allow us
1242 // to easily enumerate all child directories before child files.
1243 // This is important to let us maintain whole-volume lockorder
1244 // via BottomUp enumeration.
1247 InsertTailList( &ParentDcb
->Specific
.Dcb
.ParentDcbQueue
,
1248 &Fcb
->ParentDcbLinks
);
1249 UnwindEntryList
= &Fcb
->ParentDcbLinks
;
1252 // Point back to our parent dcb
1255 Fcb
->ParentDcb
= ParentDcb
;
1264 // Set the dirent offset within the directory
1267 Fcb
->LfnOffsetWithinDirectory
= LfnOffsetWithinDirectory
;
1268 Fcb
->DirentOffsetWithinDirectory
= DirentOffsetWithinDirectory
;
1271 // Set the DirentFatFlags and LastWriteTime
1274 Fcb
->DirentFatFlags
= Dirent
->Attributes
;
1276 Fcb
->LastWriteTime
= FatFatTimeToNtTime( IrpContext
,
1277 Dirent
->LastWriteTime
,
1281 // These fields are only non-zero when in Chicago mode.
1284 if (FatData
.ChicagoMode
) {
1286 LARGE_INTEGER FatSystemJanOne1980
;
1289 // If either date is possibly zero, get the system
1290 // version of 1/1/80.
1293 if ((((PUSHORT
)Dirent
)[9] & ((PUSHORT
)Dirent
)[8]) == 0) {
1295 ExLocalTimeToSystemTime( &FatJanOne1980
,
1296 &FatSystemJanOne1980
);
1300 // Only do the really hard work if this field is non-zero.
1303 if (((PUSHORT
)Dirent
)[9] != 0) {
1305 Fcb
->LastAccessTime
=
1306 FatFatDateToNtTime( IrpContext
,
1307 Dirent
->LastAccessDate
);
1311 Fcb
->LastAccessTime
= FatSystemJanOne1980
;
1315 // Only do the really hard work if this field is non-zero.
1318 if (((PUSHORT
)Dirent
)[8] != 0) {
1321 FatFatTimeToNtTime( IrpContext
,
1322 Dirent
->CreationTime
,
1323 Dirent
->CreationMSec
);
1327 Fcb
->CreationTime
= FatSystemJanOne1980
;
1332 // Initialize Advanced FCB Header fields
1335 ExInitializeFastMutex( &Fcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1336 FsRtlSetupAdvancedHeader( &Fcb
->Header
,
1337 &Fcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1340 // To make FAT match the present functionality of NTFS, disable
1341 // stream contexts on paging files (nealch 7/2/01)
1346 ClearFlag( Fcb
->Header
.Flags2
, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS
);
1350 // Initialize the Mcb
1353 FsRtlInitializeLargeMcb( &Fcb
->Mcb
, PoolType
);
1354 UnwindMcb
= &Fcb
->Mcb
;
1357 // Set the file size, valid data length, first cluster of file,
1358 // and allocation size based on the information stored in the dirent
1361 Fcb
->Header
.FileSize
.LowPart
= Dirent
->FileSize
;
1363 Fcb
->Header
.ValidDataLength
.LowPart
= Dirent
->FileSize
;
1365 Fcb
->ValidDataToDisk
= Dirent
->FileSize
;
1367 Fcb
->FirstClusterOfFile
= (ULONG
)Dirent
->FirstClusterOfFile
;
1369 if ( FatIsFat32(Vcb
) ) {
1371 Fcb
->FirstClusterOfFile
+= Dirent
->FirstClusterOfFileHi
<< 16;
1374 if ( Fcb
->FirstClusterOfFile
== 0 ) {
1376 Fcb
->Header
.AllocationSize
.QuadPart
= 0;
1380 Fcb
->Header
.AllocationSize
.QuadPart
= FCB_LOOKUP_ALLOCATIONSIZE_HINT
;
1384 // Initialize the Fcb's file lock record
1387 FsRtlInitializeFileLock( &Fcb
->Specific
.Fcb
.FileLock
, NULL
, NULL
);
1388 UnwindFileLock
= &Fcb
->Specific
.Fcb
.FileLock
;
1391 // Initialize the oplock structure.
1394 FsRtlInitializeOplock( &Fcb
->Specific
.Fcb
.Oplock
);
1395 UnwindOplock
= &Fcb
->Specific
.Fcb
.Oplock
;
1398 // Indicate that Fast I/O is possible
1401 Fcb
->Header
.IsFastIoPossible
= TRUE
;
1404 // Set the file names. This must be the last thing we do.
1407 FatConstructNamesInFcb( IrpContext
,
1413 // Drop the shortname hint so prefix searches can figure out
1417 Fcb
->ShortName
.FileNameDos
= TRUE
;
1421 DebugUnwind( FatCreateFcb
);
1424 // If this is an abnormal termination then undo our work
1427 if (_SEH2_AbnormalTermination()) {
1431 if (UnwindOplock
!= NULL
) { FsRtlUninitializeOplock( UnwindOplock
); }
1432 if (UnwindFileLock
!= NULL
) { FsRtlUninitializeFileLock( UnwindFileLock
); }
1433 if (UnwindMcb
!= NULL
) { FsRtlUninitializeLargeMcb( UnwindMcb
); }
1434 if (UnwindEntryList
!= NULL
) { RemoveEntryList( UnwindEntryList
); }
1435 if (UnwindResource
!= NULL
) { FatFreeResource( UnwindResource
); }
1436 if (UnwindResource2
!= NULL
) { FatFreeResource( UnwindResource2
); }
1438 for (i
= 0; i
< sizeof(UnwindStorage
)/sizeof(PVOID
); i
+= 1) {
1439 if (UnwindStorage
[i
] != NULL
) { ExFreePool( UnwindStorage
[i
] ); }
1443 DebugTrace(-1, Dbg
, "FatCreateFcb -> %08lx\n", Fcb
);
1447 // return and tell the caller
1456 IN PIRP_CONTEXT IrpContext
,
1459 IN ULONG LfnOffsetWithinDirectory
,
1460 IN ULONG DirentOffsetWithinDirectory
,
1462 IN PUNICODE_STRING Lfn OPTIONAL
1467 Routine Description:
1469 This routine allocates, initializes, and inserts a new Dcb record into
1470 the in memory data structures.
1474 Vcb - Supplies the Vcb to associate the new DCB under.
1476 ParentDcb - Supplies the parent dcb that the new DCB is under.
1478 LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is
1479 no LFN associated with this file then this value is same as
1480 DirentOffsetWithinDirectory.
1482 DirentOffsetWithinDirectory - Supplies the offset, in bytes from the
1483 start of the directory file where the dirent for the fcb is located
1485 Dirent - Supplies the dirent for the dcb being created
1487 FileName - Supplies the file name of the file relative to the directory
1488 it's in (e.g., the file \config.sys is called "CONFIG.SYS" without
1489 the preceding backslash).
1491 Lfn - Supplies a long UNICODE name associated with this directory.
1495 PDCB - Returns a pointer to the newly allocated DCB
1503 // The following variables are used for abnormal unwind
1506 PVOID UnwindStorage
[2] = { NULL
, NULL
};
1507 PERESOURCE UnwindResource
= NULL
;
1508 PERESOURCE UnwindResource2
= NULL
;
1509 PLIST_ENTRY UnwindEntryList
= NULL
;
1510 PLARGE_MCB UnwindMcb
= NULL
;
1512 DebugTrace(+1, Dbg
, "FatCreateDcb\n", 0);
1518 // assert that the only time we are called is if wait is true
1521 ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
1524 // Allocate a new DCB, and zero it out
1527 UnwindStorage
[0] = Dcb
= FatAllocateFcb();
1529 RtlZeroMemory( Dcb
, sizeof(DCB
) );
1532 Dcb
->NonPaged
= FatAllocateNonPagedFcb();
1534 RtlZeroMemory( Dcb
->NonPaged
, sizeof( NON_PAGED_FCB
) );
1537 // Set the proper node type code, node byte size and call backs
1540 Dcb
->Header
.NodeTypeCode
= FAT_NTC_DCB
;
1541 Dcb
->Header
.NodeByteSize
= sizeof(DCB
);
1543 Dcb
->FcbCondition
= FcbGood
;
1546 // The initial state, open count, and directory change count fields are
1547 // already zero so we can skip setting them
1551 // Initialize the resource variable
1556 Dcb
->Header
.Resource
= FatAllocateResource();
1559 // Initialize the PagingIo Resource. We no longer use the FsRtl common
1560 // shared pool because this led to a) deadlocks due to cases where files
1561 // and their parent directories shared a resource and b) there is no way
1562 // to anticipate inter-driver induced deadlock via recursive operation.
1566 Dcb
->Header
.PagingIoResource
= FatAllocateResource();
1569 // Insert this Dcb into our parent dcb's queue
1571 // There is a deep reason why this goes on the head, to allow us
1572 // to easily enumerate all child directories before child files.
1573 // This is important to let us maintain whole-volume lockorder
1574 // via BottomUp enumeration.
1577 InsertHeadList( &ParentDcb
->Specific
.Dcb
.ParentDcbQueue
,
1578 &Dcb
->ParentDcbLinks
);
1579 UnwindEntryList
= &Dcb
->ParentDcbLinks
;
1582 // Point back to our parent dcb
1585 Dcb
->ParentDcb
= ParentDcb
;
1594 // Set the dirent offset within the directory
1597 Dcb
->LfnOffsetWithinDirectory
= LfnOffsetWithinDirectory
;
1598 Dcb
->DirentOffsetWithinDirectory
= DirentOffsetWithinDirectory
;
1601 // Set the DirentFatFlags and LastWriteTime
1604 Dcb
->DirentFatFlags
= Dirent
->Attributes
;
1606 Dcb
->LastWriteTime
= FatFatTimeToNtTime( IrpContext
,
1607 Dirent
->LastWriteTime
,
1611 // These fields are only non-zero when in Chicago mode.
1614 if (FatData
.ChicagoMode
) {
1616 LARGE_INTEGER FatSystemJanOne1980
;
1619 // If either date is possibly zero, get the system
1620 // version of 1/1/80.
1623 if ((((PUSHORT
)Dirent
)[9] & ((PUSHORT
)Dirent
)[8]) == 0) {
1625 ExLocalTimeToSystemTime( &FatJanOne1980
,
1626 &FatSystemJanOne1980
);
1630 // Only do the really hard work if this field is non-zero.
1633 if (((PUSHORT
)Dirent
)[9] != 0) {
1635 Dcb
->LastAccessTime
=
1636 FatFatDateToNtTime( IrpContext
,
1637 Dirent
->LastAccessDate
);
1641 Dcb
->LastAccessTime
= FatSystemJanOne1980
;
1645 // Only do the really hard work if this field is non-zero.
1648 if (((PUSHORT
)Dirent
)[8] != 0) {
1651 FatFatTimeToNtTime( IrpContext
,
1652 Dirent
->CreationTime
,
1653 Dirent
->CreationMSec
);
1657 Dcb
->CreationTime
= FatSystemJanOne1980
;
1662 // Initialize Advanced FCB Header fields
1665 ExInitializeFastMutex( &Dcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1666 FsRtlSetupAdvancedHeader( &Dcb
->Header
,
1667 &Dcb
->NonPaged
->AdvancedFcbHeaderMutex
);
1670 // Initialize the Mcb
1673 FsRtlInitializeLargeMcb( &Dcb
->Mcb
, PagedPool
);
1674 UnwindMcb
= &Dcb
->Mcb
;
1677 // Set the file size, first cluster of file, and allocation size
1678 // based on the information stored in the dirent
1681 Dcb
->FirstClusterOfFile
= (ULONG
)Dirent
->FirstClusterOfFile
;
1683 if ( FatIsFat32(Dcb
->Vcb
) ) {
1685 Dcb
->FirstClusterOfFile
+= Dirent
->FirstClusterOfFileHi
<< 16;
1688 if ( Dcb
->FirstClusterOfFile
== 0 ) {
1690 Dcb
->Header
.AllocationSize
.QuadPart
= 0;
1694 Dcb
->Header
.AllocationSize
.QuadPart
= FCB_LOOKUP_ALLOCATIONSIZE_HINT
;
1697 // initialize the notify queues, and the parent dcb queue.
1700 InitializeListHead( &Dcb
->Specific
.Dcb
.ParentDcbQueue
);
1703 // Setup the free dirent bitmap buffer. Since we don't know the
1704 // size of the directory, leave it zero for now.
1707 RtlInitializeBitMap( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
1712 // Set our two create dirent aids to represent that we have yet to
1713 // enumerate the directory for never used or deleted dirents.
1716 Dcb
->Specific
.Dcb
.UnusedDirentVbo
= 0xffffffff;
1717 Dcb
->Specific
.Dcb
.DeletedDirentHint
= 0xffffffff;
1720 // Postpone initializing the cache map until we need to do a read/write
1721 // of the directory file.
1725 // set the file names. This must be the last thing we do.
1728 FatConstructNamesInFcb( IrpContext
,
1735 DebugUnwind( FatCreateDcb
);
1738 // If this is an abnormal termination then undo our work
1741 if (_SEH2_AbnormalTermination()) {
1745 if (UnwindMcb
!= NULL
) { FsRtlUninitializeLargeMcb( UnwindMcb
); }
1746 if (UnwindEntryList
!= NULL
) { RemoveEntryList( UnwindEntryList
); }
1747 if (UnwindResource
!= NULL
) { FatFreeResource( UnwindResource
); }
1748 if (UnwindResource2
!= NULL
) { FatFreeResource( UnwindResource2
); }
1750 for (i
= 0; i
< sizeof(UnwindStorage
)/sizeof(PVOID
); i
+= 1) {
1751 if (UnwindStorage
[i
] != NULL
) { ExFreePool( UnwindStorage
[i
] ); }
1755 DebugTrace(-1, Dbg
, "FatCreateDcb -> %08lx\n", Dcb
);
1759 // return and tell the caller
1762 DebugTrace(-1, Dbg
, "FatCreateDcb -> %08lx\n", Dcb
);
1770 IN PIRP_CONTEXT IrpContext
,
1776 Routine Description:
1778 This routine deallocates and removes an FCB, DCB, or ROOT DCB record
1779 from Fat's in-memory data structures. It also will remove all
1780 associated underlings (i.e., Notify irps, and child FCB/DCB records).
1784 Fcb - Supplies the FCB/DCB/ROOT DCB to be removed
1793 DebugTrace(+1, Dbg
, "FatDeleteFcb, Fcb = %08lx\n", Fcb
);
1796 // We can only delete this record if the open count is zero.
1799 if (Fcb
->OpenCount
!= 0) {
1801 DebugDump("Error deleting Fcb, Still Open\n", 0, Fcb
);
1802 FatBugCheck( 0, 0, 0 );
1806 // If this is a DCB then remove every Notify record from the two
1810 if ((Fcb
->Header
.NodeTypeCode
== FAT_NTC_DCB
) ||
1811 (Fcb
->Header
.NodeTypeCode
== FAT_NTC_ROOT_DCB
)) {
1814 // If we allocated a free dirent bitmap buffer, free it.
1817 if ((Fcb
->Specific
.Dcb
.FreeDirentBitmap
.Buffer
!= NULL
) &&
1818 (Fcb
->Specific
.Dcb
.FreeDirentBitmap
.Buffer
!=
1819 &Fcb
->Specific
.Dcb
.FreeDirentBitmapBuffer
[0])) {
1821 ExFreePool(Fcb
->Specific
.Dcb
.FreeDirentBitmap
.Buffer
);
1824 ASSERT( Fcb
->Specific
.Dcb
.DirectoryFileOpenCount
== 0 );
1825 ASSERT( IsListEmpty(&Fcb
->Specific
.Dcb
.ParentDcbQueue
) );
1830 // Uninitialize the byte range file locks and opportunistic locks
1833 FsRtlUninitializeFileLock( &Fcb
->Specific
.Fcb
.FileLock
);
1834 FsRtlUninitializeOplock( &Fcb
->Specific
.Fcb
.Oplock
);
1838 // Release any Filter Context structures associated with this FCB
1841 FsRtlTeardownPerStreamContexts( &Fcb
->Header
);
1844 // Uninitialize the Mcb
1847 FsRtlUninitializeLargeMcb( &Fcb
->Mcb
);
1850 // If this is not the root dcb then we need to remove ourselves from
1851 // our parents Dcb queue
1854 if (Fcb
->Header
.NodeTypeCode
!= FAT_NTC_ROOT_DCB
) {
1856 RemoveEntryList( &(Fcb
->ParentDcbLinks
) );
1860 // Remove the entry from the splay table if there is still is one.
1863 if (FlagOn( Fcb
->FcbState
, FCB_STATE_NAMES_IN_SPLAY_TREE
)) {
1865 FatRemoveNames( IrpContext
, Fcb
);
1869 // Free the file name pool if allocated.
1872 if (Fcb
->Header
.NodeTypeCode
!= FAT_NTC_ROOT_DCB
) {
1875 // If we blew up at inconvenient times, the shortname
1876 // could be null even though you will *never* see this
1877 // normally. Rename is a good example of this case.
1880 if (Fcb
->ShortName
.Name
.Oem
.Buffer
) {
1882 ExFreePool( Fcb
->ShortName
.Name
.Oem
.Buffer
);
1885 if (Fcb
->FullFileName
.Buffer
) {
1887 ExFreePool( Fcb
->FullFileName
.Buffer
);
1891 if (Fcb
->ExactCaseLongName
.Buffer
) {
1893 ExFreePool(Fcb
->ExactCaseLongName
.Buffer
);
1896 #ifdef SYSCACHE_COMPILE
1898 if (Fcb
->WriteMask
) {
1900 ExFreePool( Fcb
->WriteMask
);
1906 // Finally deallocate the Fcb and non-paged fcb records
1909 FatFreeResource( Fcb
->Header
.Resource
);
1911 if (Fcb
->Header
.PagingIoResource
!= Fcb
->Header
.Resource
) {
1913 FatFreeResource( Fcb
->Header
.PagingIoResource
);
1917 // If an Event was allocated, get rid of it.
1920 if (Fcb
->NonPaged
->OutstandingAsyncEvent
) {
1922 ExFreePool( Fcb
->NonPaged
->OutstandingAsyncEvent
);
1925 FatFreeNonPagedFcb( Fcb
->NonPaged
);
1929 // and return to our caller
1932 DebugTrace(-1, Dbg
, "FatDeleteFcb -> VOID\n", 0);
1939 IN PIRP_CONTEXT IrpContext
1944 Routine Description:
1946 This routine creates a new CCB record
1952 CCB - returns a pointer to the newly allocate CCB
1959 DebugTrace(+1, Dbg
, "FatCreateCcb\n", 0);
1962 // Allocate a new CCB Record
1965 Ccb
= FatAllocateCcb();
1967 RtlZeroMemory( Ccb
, sizeof(CCB
) );
1970 // Set the proper node type code and node byte size
1973 Ccb
->NodeTypeCode
= FAT_NTC_CCB
;
1974 Ccb
->NodeByteSize
= sizeof(CCB
);
1977 // return and tell the caller
1980 DebugTrace(-1, Dbg
, "FatCreateCcb -> %08lx\n", Ccb
);
1982 UNREFERENCED_PARAMETER( IrpContext
);
1990 FatDeallocateCcbStrings(
1995 Routine Description:
1997 This routine deallocates CCB query templates
2001 Ccb - Supplies the CCB
2010 // If we allocated query template buffers, deallocate them now.
2013 if (FlagOn(Ccb
->Flags
, CCB_FLAG_FREE_UNICODE
)) {
2015 ASSERT( Ccb
->UnicodeQueryTemplate
.Buffer
);
2016 ASSERT( !FlagOn( Ccb
->Flags
, CCB_FLAG_CLOSE_CONTEXT
));
2017 RtlFreeUnicodeString( &Ccb
->UnicodeQueryTemplate
);
2020 if (FlagOn(Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
)) {
2022 ASSERT( Ccb
->OemQueryTemplate
.Wild
.Buffer
);
2023 ASSERT( !FlagOn( Ccb
->Flags
, CCB_FLAG_CLOSE_CONTEXT
));
2024 RtlFreeOemString( &Ccb
->OemQueryTemplate
.Wild
);
2027 ClearFlag( Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
| CCB_FLAG_FREE_UNICODE
);
2034 IN PIRP_CONTEXT IrpContext
,
2040 Routine Description:
2042 This routine deallocates and removes the specified CCB record
2043 from the Fat in memory data structures
2047 Ccb - Supplies the CCB to remove
2056 DebugTrace(+1, Dbg
, "FatDeleteCcb, Ccb = %08lx\n", Ccb
);
2058 FatDeallocateCcbStrings( Ccb
);
2061 // Deallocate the Ccb record
2067 // return and tell the caller
2070 DebugTrace(-1, Dbg
, "FatDeleteCcb -> VOID\n", 0);
2072 UNREFERENCED_PARAMETER( IrpContext
);
2079 FatCreateIrpContext (
2086 Routine Description:
2088 This routine creates a new IRP_CONTEXT record
2092 Irp - Supplies the originating Irp.
2094 Wait - Supplies the wait value to store in the context
2098 PIRP_CONTEXT - returns a pointer to the newly allocate IRP_CONTEXT Record
2103 PIRP_CONTEXT IrpContext
;
2104 PIO_STACK_LOCATION IrpSp
;
2106 DebugTrace(+1, Dbg
, "FatCreateIrpContext\n", 0);
2108 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2111 // The only operations a filesystem device object should ever receive
2112 // are create/teardown of fsdo handles and operations which do not
2113 // occur in the context of fileobjects (i.e., mount).
2116 if (FatDeviceIsFatFsdo( IrpSp
->DeviceObject
)) {
2118 if (IrpSp
->FileObject
!= NULL
&&
2119 IrpSp
->MajorFunction
!= IRP_MJ_CREATE
&&
2120 IrpSp
->MajorFunction
!= IRP_MJ_CLEANUP
&&
2121 IrpSp
->MajorFunction
!= IRP_MJ_CLOSE
) {
2123 ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST
);
2126 ASSERT( IrpSp
->FileObject
!= NULL
||
2128 (IrpSp
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
&&
2129 IrpSp
->MinorFunction
== IRP_MN_USER_FS_REQUEST
&&
2130 IrpSp
->Parameters
.FileSystemControl
.FsControlCode
== FSCTL_INVALIDATE_VOLUMES
) ||
2132 (IrpSp
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
&&
2133 IrpSp
->MinorFunction
== IRP_MN_MOUNT_VOLUME
) ||
2135 IrpSp
->MajorFunction
== IRP_MJ_SHUTDOWN
);
2139 // Attemtp to allocate from the region first and failing that allocate
2143 DebugDoit( FatFsdEntryCount
+= 1);
2145 IrpContext
= FatAllocateIrpContext();
2148 // Zero out the irp context.
2151 RtlZeroMemory( IrpContext
, sizeof(IRP_CONTEXT
) );
2154 // Set the proper node type code and node byte size
2157 IrpContext
->NodeTypeCode
= FAT_NTC_IRP_CONTEXT
;
2158 IrpContext
->NodeByteSize
= sizeof(IRP_CONTEXT
);
2161 // Set the originating Irp field
2164 IrpContext
->OriginatingIrp
= Irp
;
2167 // Major/Minor Function codes
2170 IrpContext
->MajorFunction
= IrpSp
->MajorFunction
;
2171 IrpContext
->MinorFunction
= IrpSp
->MinorFunction
;
2174 // Copy RealDevice for workque algorithms, and also set Write Through
2175 // and Removable Media if there is a file object. Only file system
2176 // control Irps won't have a file object, and they should all have
2177 // a Vpb as the first IrpSp location.
2180 if (IrpSp
->FileObject
!= NULL
) {
2185 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2187 IrpContext
->RealDevice
= FileObject
->DeviceObject
;
2189 Vcb
= IrpContext
->Vcb
= &((PVOLUME_DEVICE_OBJECT
)(IrpSp
->DeviceObject
))->Vcb
;
2191 IrpContext
->Vcb
= &((PVOLUME_DEVICE_OBJECT
)(IrpSp
->DeviceObject
))->Vcb
;
2195 // See if the request is Write Through.
2198 if (IsFileWriteThrough( FileObject
, Vcb
)) {
2200 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WRITE_THROUGH
);
2203 } else if (IrpContext
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
) {
2205 IrpContext
->RealDevice
= IrpSp
->Parameters
.MountVolume
.Vpb
->RealDevice
;
2209 // Set the wait parameter
2212 if (Wait
) { SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
); }
2215 // Set the recursive file system call parameter. We set it true if
2216 // the TopLevelIrp field in the thread local storage is not the current
2217 // irp, otherwise we leave it as FALSE.
2220 if ( IoGetTopLevelIrp() != Irp
) {
2222 SetFlag(IrpContext
->Flags
, IRP_CONTEXT_FLAG_RECURSIVE_CALL
);
2226 // return and tell the caller
2229 DebugTrace(-1, Dbg
, "FatCreateIrpContext -> %08lx\n", IrpContext
);
2237 FatDeleteIrpContext_Real (
2238 IN PIRP_CONTEXT IrpContext
2243 Routine Description:
2245 This routine deallocates and removes the specified IRP_CONTEXT record
2246 from the Fat in memory data structures. It should only be called
2247 by FatCompleteRequest.
2251 IrpContext - Supplies the IRP_CONTEXT to remove
2260 DebugTrace(+1, Dbg
, "FatDeleteIrpContext, IrpContext = %08lx\n", IrpContext
);
2262 ASSERT( IrpContext
->NodeTypeCode
== FAT_NTC_IRP_CONTEXT
);
2263 ASSERT( IrpContext
->PinCount
== 0 );
2266 // If there is a FatIoContext that was allocated, free it.
2269 if (IrpContext
->FatIoContext
!= NULL
) {
2271 if (!FlagOn(IrpContext
->Flags
, IRP_CONTEXT_STACK_IO_CONTEXT
)) {
2273 if (IrpContext
->FatIoContext
->ZeroMdl
) {
2274 IoFreeMdl( IrpContext
->FatIoContext
->ZeroMdl
);
2277 ExFreePool( IrpContext
->FatIoContext
);
2282 // Drop the IrpContext.
2285 FatFreeIrpContext( IrpContext
);
2288 // return and tell the caller
2291 DebugTrace(-1, Dbg
, "FatDeleteIrpContext -> VOID\n", 0);
2298 FatGetNextFcbBottomUp (
2299 IN PIRP_CONTEXT IrpContext
,
2300 IN PFCB Fcb OPTIONAL
,
2301 IN PFCB TerminationFcb
2306 Routine Description:
2308 This routine is used to iterate through Fcbs in a tree. In order to match
2309 the lockorder for getting multiple Fcbs (so this can be used for acquiring
2310 all Fcbs), this version does a bottom-up enumeration.
2312 This is different than the old one, now called TopDown. The problem with
2313 lockorder was very well hidden.
2315 The transition rule is still pretty simple:
2317 A) If you have an adjacent sibling, go to it
2318 1) Descend to its leftmost child
2319 B) Else go to your parent
2321 If this routine is called with in invalid TerminationFcb it will fail,
2324 The TerminationFcb is the last Fcb returned in the enumeration.
2326 This method is incompatible with the possibility that ancestors may vanish
2327 based on operations done on the last returned node. For instance,
2328 FatPurgeReferencedFileObjects cannot use BottomUp enumeration.
2332 Fcb - Supplies the current Fcb. This is NULL if enumeration is starting.
2334 TerminationFcb - The root Fcb of the tree in which the enumeration starts
2335 and at which it inclusively stops.
2339 The next Fcb in the enumeration, or NULL if Fcb was the final one.
2346 ASSERT( FatVcbAcquiredExclusive( IrpContext
, TerminationFcb
->Vcb
) ||
2347 FlagOn( TerminationFcb
->Vcb
->VcbState
, VCB_STATE_FLAG_LOCKED
) );
2350 // Do we need to begin the enumeration?
2359 if (Fcb
== TerminationFcb
) {
2365 // Do we have a sibling to return?
2368 NextFcb
= FatGetNextSibling( Fcb
);
2371 // If not, return our parent. We are done with this branch.
2374 if (NextFcb
== NULL
) {
2376 return Fcb
->ParentDcb
;
2381 NextFcb
= TerminationFcb
;
2385 // Decend to its furthest child (if it exists) and return it.
2389 NodeType( NextFcb
) != FAT_NTC_FCB
&& FatGetFirstChild( NextFcb
) != NULL
;
2390 NextFcb
= FatGetFirstChild( NextFcb
)) {
2397 FatGetNextFcbTopDown (
2398 IN PIRP_CONTEXT IrpContext
,
2400 IN PFCB TerminationFcb
2405 Routine Description:
2407 This routine is used to iterate through Fcbs in a tree, from the top down.
2409 The rule is very simple:
2411 A) If you have a child, go to it, else
2412 B) If you have an older sibling, go to it, else
2413 C) Go to your parent's older sibling.
2415 If this routine is called with in invalid TerminationFcb it will fail,
2418 The Termination Fcb is never returned. If it is the root of the tree you
2419 are traversing, visit it first.
2421 This routine never returns direct ancestors of Fcb, and thus is useful when
2422 making Fcb's go away (which may tear up the tree).
2426 Fcb - Supplies the current Fcb
2428 TerminationFcb - The Fcb at which the enumeration should (non-inclusivly)
2429 stop. Assumed to be a directory.
2433 The next Fcb in the enumeration, or NULL if Fcb was the final one.
2440 ASSERT( FatVcbAcquiredExclusive( IrpContext
, Fcb
->Vcb
) ||
2441 FlagOn( Fcb
->Vcb
->VcbState
, VCB_STATE_FLAG_LOCKED
) );
2444 // If this was a directory (ie. not a file), get the child. If
2445 // there aren't any children and this is our termination Fcb,
2449 if ( ((NodeType(Fcb
) == FAT_NTC_DCB
) ||
2450 (NodeType(Fcb
) == FAT_NTC_ROOT_DCB
)) &&
2451 !IsListEmpty(&Fcb
->Specific
.Dcb
.ParentDcbQueue
) ) {
2453 return FatGetFirstChild( Fcb
);
2457 // Were we only meant to do one iteration?
2460 if ( Fcb
== TerminationFcb
) {
2465 Sibling
= FatGetNextSibling(Fcb
);
2470 // Do we still have an "older" sibling in this directory who is
2471 // not the termination Fcb?
2474 if ( Sibling
!= NULL
) {
2476 return (Sibling
!= TerminationFcb
) ? Sibling
: NULL
;
2480 // OK, let's move on to out parent and see if he is the termination
2481 // node or has any older siblings.
2484 if ( Fcb
->ParentDcb
== TerminationFcb
) {
2489 Fcb
= Fcb
->ParentDcb
;
2491 Sibling
= FatGetNextSibling(Fcb
);
2497 IN PIRP_CONTEXT IrpContext
,
2503 Routine Description:
2505 This routine swaps the VPB for this VCB if it has not been done already.
2506 This means the device object will get our spare VPB and we will cleanup
2507 the one used while the volume was mounted.
2511 IrpContext - Supplies the context for the overall request.
2513 Vcb - Supplies the VCB to swap the VPB on.
2517 TRUE - If the VPB was actually swapped.
2519 FALSE - If the VPB was already swapped.
2524 BOOLEAN Result
= FALSE
;
2526 PIO_STACK_LOCATION IrpSp
;
2530 // Make sure we have not already swapped it.
2535 if (OldVpb
->RealDevice
->Vpb
== OldVpb
) {
2538 // If not the final reference and we are forcing the disconnect,
2539 // then swap out the Vpb. We must preserve the REMOVE_PENDING flag
2540 // so that the device is not remounted in the middle of a PnP remove
2544 ASSERT( Vcb
->SwapVpb
!= NULL
);
2546 Vcb
->SwapVpb
->Type
= IO_TYPE_VPB
;
2547 Vcb
->SwapVpb
->Size
= sizeof( VPB
);
2548 Vcb
->SwapVpb
->RealDevice
= OldVpb
->RealDevice
;
2550 Vcb
->SwapVpb
->RealDevice
->Vpb
= Vcb
->SwapVpb
;
2552 Vcb
->SwapVpb
->Flags
= FlagOn( OldVpb
->Flags
, VPB_REMOVE_PENDING
);
2555 // If we are working on a mount request, we need to make sure we update
2556 // the VPB in the IRP, since the one it points to may no longer be valid.
2559 if (IrpContext
->MajorFunction
== IRP_MJ_FILE_SYSTEM_CONTROL
&& IrpContext
->MinorFunction
== IRP_MN_MOUNT_VOLUME
) {
2562 // Get the IRP stack.
2565 IrpSp
= IoGetCurrentIrpStackLocation( IrpContext
->OriginatingIrp
);
2567 ASSERT( IrpSp
->FileObject
== NULL
);
2570 // Check the VPB in the IRP to see if it is the one we are swapping.
2573 if (IrpSp
->Parameters
.MountVolume
.Vpb
== OldVpb
) {
2576 // Change the IRP to point to the swap VPB.
2579 IrpSp
->Parameters
.MountVolume
.Vpb
= Vcb
->SwapVpb
;
2584 // We place the volume in the Bad state (as opposed to NotMounted) so
2585 // that it is not eligible for a remount. Also indicate we used up
2589 Vcb
->SwapVpb
= NULL
;
2590 FatSetVcbCondition( Vcb
, VcbBad
);
2591 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_VPB_MUST_BE_FREED
);
2601 FatCheckForDismount (
2602 IN PIRP_CONTEXT IrpContext
,
2609 Routine Description:
2611 This routine determines if a volume is ready for deletion. It
2612 correctly synchronizes with creates en-route to the file system.
2616 Vcb - Supplies the volume to examine
2618 Force - Specifies whether we want this Vcb forcibly disconnected
2619 from the driver stack if it will not be deleted (a new vpb will
2620 be installed if neccesary). Caller is responsible for making
2621 sure that the volume has been marked in such a way that attempts
2622 to operate through the realdevice are blocked (i.e., move the vcb
2623 out of the mounted state).
2627 BOOLEAN - TRUE if the volume was deleted, FALSE otherwise.
2633 BOOLEAN VcbDeleted
= FALSE
;
2636 // If the VCB condition is good and we are not forcing, just return.
2639 if (Vcb
->VcbCondition
== VcbGood
&& !Force
) {
2645 // Now check for a zero Vpb count on an unmounted volume. These
2646 // volumes will be deleted as they now have no file objects and
2647 // there are no creates en route to this volume.
2650 IoAcquireVpbSpinLock( &SavedIrql
);
2652 if (Vcb
->Vpb
->ReferenceCount
== Vcb
->ResidualOpenCount
&& Vcb
->OpenFileCount
== 0) {
2654 PVPB Vpb
= Vcb
->Vpb
;
2657 UNICODE_STRING VolumeLabel
;
2660 // Setup the VolumeLabel string
2663 VolumeLabel
.Length
= Vcb
->Vpb
->VolumeLabelLength
;
2664 VolumeLabel
.MaximumLength
= MAXIMUM_VOLUME_LABEL_LENGTH
;
2665 VolumeLabel
.Buffer
= &Vcb
->Vpb
->VolumeLabel
[0];
2667 KdPrintEx((DPFLTR_FASTFAT_ID
,
2669 "FASTFAT: Dismounting Volume %Z\n",
2674 // Swap this VCB's VPB.
2677 FatSwapVpb( IrpContext
,
2681 // Clear the VPB_MOUNTED bit. New opens should not come in due
2682 // to the swapped VPB, but having the flag cleared helps debugging.
2683 // Note that we must leave the Vpb->DeviceObject field set until
2684 // after the FatTearDownVcb call as closes will have to make their
2688 ClearFlag( Vpb
->Flags
, VPB_MOUNTED
);
2691 // If this Vpb was locked, clear this flag now.
2694 ClearFlag( Vpb
->Flags
, VPB_LOCKED
);
2696 IoReleaseVpbSpinLock( SavedIrql
);
2699 // We are going to attempt the dismount, so mark the VCB as having
2700 // a dismount in progress.
2703 ASSERT( !FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
) );
2704 SetFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
);
2707 // Close down our internal opens.
2710 FatTearDownVcb( IrpContext
,
2714 // Process any delayed closes.
2720 // Grab the VPB lock again so that we can recheck our counts.
2723 IoAcquireVpbSpinLock( &SavedIrql
);
2726 // See if we can delete this VCB.
2729 if (Vcb
->Vpb
->ReferenceCount
== 0 && Vcb
->InternalOpenCount
== 0) {
2731 Vpb
->DeviceObject
= NULL
;
2733 IoReleaseVpbSpinLock( SavedIrql
);
2735 FatDeleteVcb( IrpContext
, Vcb
);
2738 IoDeleteDevice( (PDEVICE_OBJECT
)
2739 CONTAINING_RECORD( Vcb
,
2740 VOLUME_DEVICE_OBJECT
,
2747 IoReleaseVpbSpinLock( SavedIrql
);
2749 ASSERT( FlagOn( Vcb
->VcbState
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
) );
2750 ClearFlag( Vcb
->VcbState
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
);
2756 // The requester is forcing the issue. We need to swap the VPB with our spare.
2759 FatSwapVpb( IrpContext
,
2762 IoReleaseVpbSpinLock( SavedIrql
);
2767 // Just drop the Vpb spinlock.
2770 IoReleaseVpbSpinLock( SavedIrql
);
2778 FatConstructNamesInFcb (
2779 IN PIRP_CONTEXT IrpContext
,
2782 PUNICODE_STRING Lfn OPTIONAL
2787 Routine Description:
2789 This routine places the short name in the dirent in the first set of
2790 STRINGs in the Fcb. If a long file name (Lfn) was specified, then
2791 we must decide whether we will store its Oem equivolent in the same
2792 prefix table as the short name, or rather just save the upcased
2793 version of the UNICODE string in the FCB.
2795 For looking up Fcbs, the first approach will be faster, so we want to
2796 do this as much as possible. Here are the rules that I have thought
2797 through extensively to determine when it is safe to store only Oem
2798 version of the UNICODE name.
2800 - If the UNICODE name contains no extended characters (>0x80), use Oem.
2802 - Let U be the upcased version of the UNICODE name.
2803 Let Up(x) be the function that upcases a UNICODE string.
2804 Let Down(x) be the function that upcases a UNICODE string.
2805 Let OemToUni(x) be the function that converts an Oem string to Unicode.
2806 Let UniToOem(x) be the function that converts a Unicode string to Oem.
2807 Let BestOemFit(x) be the function that creates the Best uppercase Oem
2808 fit for the UNICODE string x.
2810 BestOemFit(x) = UniToOem(Up(OemToUni(UniToOem(x)))) <1>
2812 if (BestOemFit(U) == BestOemFit(Down(U)) <2>
2814 then I know that there exists no UNICODE string Y such that:
2820 BestOemFit(U) != BestOemFit(Y) <4>
2822 Consider string U as a collection of one character strings. The
2823 conjecture is clearly true for each sub-string, thus it is true
2824 for the entire string.
2826 Equation <1> is what we use to convert an incoming unicode name in
2827 FatCommonCreate() to Oem. The double conversion is done to provide
2828 better visual best fitting for characters in the Ansi code page but
2829 not in the Oem code page. A single Nls routine is provided to do
2830 this conversion efficiently.
2832 The idea is that with U, I only have to worry about a case varient Y
2833 matching it in a unicode compare, and I have shown that any case varient
2834 of U (the set Y defined in equation <3>), when filtered through <1>
2835 (as in create), will match the Oem string defined in <1>.
2837 Thus I do not have to worry about another UNICODE string missing in
2838 the prefix lookup, but matching when comparing LFNs in the directory.
2842 Fcb - The Fcb we are supposed to fill in. Note that ParentDcb must
2843 already be filled in.
2845 Dirent - The gives up the short name.
2847 Lfn - If provided, this gives us the long name.
2865 UNICODE_STRING Unicode
;
2866 POEM_STRING ShortName
;
2867 POEM_STRING LongOemName
;
2868 PUNICODE_STRING LongUniName
;
2870 ShortName
= &Fcb
->ShortName
.Name
.Oem
;
2872 ASSERT( ShortName
->Buffer
== NULL
);
2877 // First do the short name.
2881 // Copy over the case flags for the short name of the file
2884 if (FlagOn(Dirent
->NtByte
, FAT_DIRENT_NT_BYTE_8_LOWER_CASE
)) {
2886 SetFlag(Fcb
->FcbState
, FCB_STATE_8_LOWER_CASE
);
2890 ClearFlag(Fcb
->FcbState
, FCB_STATE_8_LOWER_CASE
);
2893 if (FlagOn(Dirent
->NtByte
, FAT_DIRENT_NT_BYTE_3_LOWER_CASE
)) {
2895 SetFlag(Fcb
->FcbState
, FCB_STATE_3_LOWER_CASE
);
2899 ClearFlag(Fcb
->FcbState
, FCB_STATE_3_LOWER_CASE
);
2902 ShortName
->MaximumLength
= 16;
2903 ShortName
->Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
2905 TAG_FILENAME_BUFFER
);
2907 Fat8dot3ToString( IrpContext
, Dirent
, FALSE
, ShortName
);
2910 // If no Lfn was specified, we are done. In either case, set the
2911 // final name length.
2914 ASSERT( Fcb
->ExactCaseLongName
.Buffer
== NULL
);
2916 if (!ARGUMENT_PRESENT(Lfn
) || (Lfn
->Length
== 0)) {
2918 Fcb
->FinalNameLength
= (USHORT
) RtlOemStringToCountedUnicodeSize( ShortName
);
2919 Fcb
->ExactCaseLongName
.Length
= Fcb
->ExactCaseLongName
.MaximumLength
= 0;
2921 try_return( NOTHING
);
2925 // If we already set up the full filename, we could be in trouble. If the fast
2926 // path for doing it already fired, FatSetFullFileNameInFcb, it will have missed
2927 // this and could have built the full filename out of the shortname of the file.
2929 // At that point, disaster could be inevitable since the final name length will not
2930 // match. We use this to tell the notify package what to do - FatNotifyReportChange.
2933 ASSERT( Fcb
->FullFileName
.Buffer
== NULL
);
2936 // We know now we have an Lfn, save away a copy.
2939 Fcb
->FinalNameLength
= Lfn
->Length
;
2941 Fcb
->ExactCaseLongName
.Length
= Fcb
->ExactCaseLongName
.MaximumLength
= Lfn
->Length
;
2942 Fcb
->ExactCaseLongName
.Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
2944 TAG_FILENAME_BUFFER
);
2945 RtlCopyMemory(Fcb
->ExactCaseLongName
.Buffer
, Lfn
->Buffer
, Lfn
->Length
);
2948 // First check for no extended characters.
2951 for (i
=0; i
< Lfn
->Length
/sizeof(WCHAR
); i
++) {
2953 if (Lfn
->Buffer
[i
] >= 0x80) {
2959 if (i
== Lfn
->Length
/sizeof(WCHAR
)) {
2962 // Cool, I can go with the Oem, upcase it fast by hand.
2965 LongOemName
= &Fcb
->LongName
.Oem
.Name
.Oem
;
2968 LongOemName
->Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
2969 Lfn
->Length
/sizeof(WCHAR
),
2970 TAG_FILENAME_BUFFER
);
2971 LongOemName
->Length
=
2972 LongOemName
->MaximumLength
= Lfn
->Length
/sizeof(WCHAR
);
2974 for (i
=0; i
< Lfn
->Length
/sizeof(WCHAR
); i
++) {
2980 LongOemName
->Buffer
[i
] = c
< 'a' ?
2983 c
- (UCHAR
)('a'-'A') :
2988 // If this name happens to be exactly the same as the short
2989 // name, don't add it to the splay table.
2992 if (FatAreNamesEqual(IrpContext
, *ShortName
, *LongOemName
) ||
2993 (FatFindFcb( IrpContext
,
2994 &Fcb
->ParentDcb
->Specific
.Dcb
.RootOemNode
,
2998 ExFreePool( LongOemName
->Buffer
);
3000 LongOemName
->Buffer
= NULL
;
3001 LongOemName
->Length
=
3002 LongOemName
->MaximumLength
= 0;
3006 SetFlag( Fcb
->FcbState
, FCB_STATE_HAS_OEM_LONG_NAME
);
3009 try_return( NOTHING
);
3013 // Now we have the fun part. Make a copy of the Lfn.
3020 Unicode
.Buffer
= NULL
;
3023 Unicode
.MaximumLength
= Lfn
->Length
;
3024 Unicode
.Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3026 TAG_FILENAME_BUFFER
);
3028 RtlCopyMemory( Unicode
.Buffer
, Lfn
->Buffer
, Lfn
->Length
);
3031 Status
= STATUS_SUCCESS
;
3036 // Unfortunately, this next block of code breaks down when you have
3037 // two long Unicode filenames that both map to the same Oem (and are,
3038 // well, long, i.e. are not the short names). In this case, with one
3039 // in the prefix table first, the other will hit the common Oem
3040 // representation. This leads to several forms of user astonishment.
3042 // It isn't worth it, or probably even possible, to try to figure out
3043 // when this is really safe to go through. Simply omit the attempt.
3045 // Ex: ANSI 0x82 and 0x84 in the 1252 ANSI->UNI and 437 UNI->OEM codepages.
3047 // 0x82 => 0x201a => 0x2c
3048 // 0x84 => 0x201e => 0x2c
3050 // 0x2c is comma, so is FAT Oem illegal and forces shortname generation.
3051 // Since it is otherwise well-formed by the rules articulated previously,
3052 // we would have put 0x2c in the Oem prefix tree. In terms of the
3053 // argument given above, even though there exist no Y and U s.t.
3055 // Up(Y) == Up(U) && BestOemFit(U) != BestOemFit(Y)
3057 // there most certainly exist Y and U s.t.
3059 // Up(Y) != Up(U) && BestOemFit(U) == BestOemFit(Y)
3061 // and that is enough to keep us from doing this. Note that the < 0x80
3062 // case is OK since we know that the mapping in the OEM codepages are
3063 // the identity in that range.
3065 // We still need to monocase it, though. Do this through a full down/up
3069 (VOID
)RtlDowncaseUnicodeString( &Unicode
, &Unicode
, FALSE
);
3070 (VOID
)RtlUpcaseUnicodeString( &Unicode
, &Unicode
, FALSE
);
3073 // Downcase and convert to upcased Oem. Only continue if we can
3074 // convert without error. Any error other than UNMAPPABLE_CHAR
3075 // is a fatal error and we raise.
3077 // Note that even if the conversion fails, we must leave Unicode
3078 // in an upcased state.
3080 // NB: The Rtl doesn't NULL .Buffer on error.
3083 (VOID
)RtlDowncaseUnicodeString( &Unicode
, &Unicode
, FALSE
);
3084 Status
= RtlUpcaseUnicodeStringToCountedOemString( &OemA
, &Unicode
, TRUE
);
3085 (VOID
)RtlUpcaseUnicodeString( &Unicode
, &Unicode
, FALSE
);
3087 if (!NT_SUCCESS(Status
)) {
3089 if (Status
!= STATUS_UNMAPPABLE_CHARACTER
) {
3091 ASSERT( Status
== STATUS_NO_MEMORY
);
3092 ExFreePool(Unicode
.Buffer
);
3093 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
3099 // The same as above except upcase.
3102 Status
= RtlUpcaseUnicodeStringToCountedOemString( &OemB
, &Unicode
, TRUE
);
3104 if (!NT_SUCCESS(Status
)) {
3106 RtlFreeOemString( &OemA
);
3108 if (Status
!= STATUS_UNMAPPABLE_CHARACTER
) {
3110 ASSERT( Status
== STATUS_NO_MEMORY
);
3111 ExFreePool(Unicode
.Buffer
);
3112 FatNormalizeAndRaiseStatus( IrpContext
, Status
);
3118 // If the final OemNames are equal, I can use save only the Oem
3119 // name. If the name did not map, then I have to go with the UNICODE
3120 // name because I could get a case varient that didn't convert
3121 // in create, but did match the LFN.
3124 if (NT_SUCCESS(Status
) && FatAreNamesEqual( IrpContext
, OemA
, OemB
)) {
3127 // Cool, I can go with the Oem. If we didn't convert correctly,
3128 // get a fresh convert from the original LFN.
3131 ExFreePool(Unicode
.Buffer
);
3133 RtlFreeOemString( &OemB
);
3135 Fcb
->LongName
.Oem
.Name
.Oem
= OemA
;
3138 // If this name happens to be exactly the same as the short
3139 // name, or a similar short name already exists don't add it
3140 // to the splay table (note the final condition implies a
3144 if (FatAreNamesEqual(IrpContext
, *ShortName
, OemA
) ||
3145 (FatFindFcb( IrpContext
,
3146 &Fcb
->ParentDcb
->Specific
.Dcb
.RootOemNode
,
3150 RtlFreeOemString( &OemA
);
3154 SetFlag( Fcb
->FcbState
, FCB_STATE_HAS_OEM_LONG_NAME
);
3157 try_return( NOTHING
);
3161 // The long name must be left in UNICODE. Free the two Oem strings
3162 // if we got here just because they weren't equal.
3165 if (NT_SUCCESS(Status
)) {
3167 RtlFreeOemString( &OemA
);
3168 RtlFreeOemString( &OemB
);
3172 LongUniName
= &Fcb
->LongName
.Unicode
.Name
.Unicode
;
3174 LongUniName
->Length
=
3175 LongUniName
->MaximumLength
= Unicode
.Length
;
3176 LongUniName
->Buffer
= Unicode
.Buffer
;
3178 SetFlag(Fcb
->FcbState
, FCB_STATE_HAS_UNICODE_LONG_NAME
);
3183 if (_SEH2_AbnormalTermination()) {
3185 if (ShortName
->Buffer
!= NULL
) {
3187 ExFreePool( ShortName
->Buffer
);
3188 ShortName
->Buffer
= NULL
;
3194 // Creating all the names worked, so add all the names
3195 // to the splay tree.
3198 FatInsertName( IrpContext
,
3199 &Fcb
->ParentDcb
->Specific
.Dcb
.RootOemNode
,
3202 Fcb
->ShortName
.Fcb
= Fcb
;
3204 if (FlagOn(Fcb
->FcbState
, FCB_STATE_HAS_OEM_LONG_NAME
)) {
3206 FatInsertName( IrpContext
,
3207 &Fcb
->ParentDcb
->Specific
.Dcb
.RootOemNode
,
3208 &Fcb
->LongName
.Oem
);
3210 Fcb
->LongName
.Oem
.Fcb
= Fcb
;
3213 if (FlagOn(Fcb
->FcbState
, FCB_STATE_HAS_UNICODE_LONG_NAME
)) {
3215 FatInsertName( IrpContext
,
3216 &Fcb
->ParentDcb
->Specific
.Dcb
.RootUnicodeNode
,
3217 &Fcb
->LongName
.Unicode
);
3219 Fcb
->LongName
.Unicode
.Fcb
= Fcb
;
3222 SetFlag(Fcb
->FcbState
, FCB_STATE_NAMES_IN_SPLAY_TREE
);
3231 FatCheckFreeDirentBitmap (
3232 IN PIRP_CONTEXT IrpContext
,
3238 Routine Description:
3240 This routine checks if the size of the free dirent bitmap is
3241 sufficient to for the current directory size. It is called
3242 whenever we grow a directory.
3246 Dcb - Supplies the directory in question.
3255 ULONG OldNumberOfDirents
;
3256 ULONG NewNumberOfDirents
;
3259 // Setup the Bitmap buffer if it is not big enough already
3262 ASSERT( Dcb
->Header
.AllocationSize
.QuadPart
!= FCB_LOOKUP_ALLOCATIONSIZE_HINT
);
3264 OldNumberOfDirents
= Dcb
->Specific
.Dcb
.FreeDirentBitmap
.SizeOfBitMap
;
3265 NewNumberOfDirents
= Dcb
->Header
.AllocationSize
.LowPart
/ sizeof(DIRENT
);
3268 // Do the usual unsync/sync check.
3271 if (NewNumberOfDirents
> OldNumberOfDirents
) {
3273 FatAcquireDirectoryFileMutex( Dcb
->Vcb
);
3277 PULONG OldBitmapBuffer
;
3278 PULONG BitmapBuffer
;
3280 ULONG BytesInBitmapBuffer
;
3281 ULONG BytesInOldBitmapBuffer
;
3283 OldNumberOfDirents
= Dcb
->Specific
.Dcb
.FreeDirentBitmap
.SizeOfBitMap
;
3284 NewNumberOfDirents
= Dcb
->Header
.AllocationSize
.LowPart
/ sizeof(DIRENT
);
3286 if (NewNumberOfDirents
> OldNumberOfDirents
) {
3289 // Remember the old bitmap
3292 OldBitmapBuffer
= Dcb
->Specific
.Dcb
.FreeDirentBitmap
.Buffer
;
3295 // Now make a new bitmap bufffer
3298 BytesInBitmapBuffer
= NewNumberOfDirents
/ 8;
3300 BytesInOldBitmapBuffer
= OldNumberOfDirents
/ 8;
3302 if (DCB_UNION_SLACK_SPACE
>= BytesInBitmapBuffer
) {
3304 BitmapBuffer
= &Dcb
->Specific
.Dcb
.FreeDirentBitmapBuffer
[0];
3308 BitmapBuffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3309 BytesInBitmapBuffer
,
3310 TAG_DIRENT_BITMAP
);
3314 // Copy the old buffer to the new buffer, free the old one, and zero
3315 // the rest of the new one. Only do the first two steps though if
3316 // we moved out of the initial buffer.
3319 if ((OldNumberOfDirents
!= 0) &&
3320 (BitmapBuffer
!= &Dcb
->Specific
.Dcb
.FreeDirentBitmapBuffer
[0])) {
3322 RtlCopyMemory( BitmapBuffer
,
3324 BytesInOldBitmapBuffer
);
3326 if (OldBitmapBuffer
!= &Dcb
->Specific
.Dcb
.FreeDirentBitmapBuffer
[0]) {
3328 ExFreePool( OldBitmapBuffer
);
3332 ASSERT( BytesInBitmapBuffer
> BytesInOldBitmapBuffer
);
3334 RtlZeroMemory( (PUCHAR
)BitmapBuffer
+ BytesInOldBitmapBuffer
,
3335 BytesInBitmapBuffer
- BytesInOldBitmapBuffer
);
3338 // Now initialize the new bitmap.
3341 RtlInitializeBitMap( &Dcb
->Specific
.Dcb
.FreeDirentBitmap
,
3343 NewNumberOfDirents
);
3348 FatReleaseDirectoryFileMutex( Dcb
->Vcb
);
3355 FatIsHandleCountZero (
3356 IN PIRP_CONTEXT IrpContext
,
3362 Routine Description:
3364 This routine decides if the handle count on the volume is zero.
3368 Vcb - The volume in question
3372 BOOLEAN - TRUE if there are no open handles on the volume, FALSE
3382 while (Fcb
!= NULL
) {
3384 if (Fcb
->UncleanCount
!= 0) {
3389 Fcb
= FatGetNextFcbTopDown(IrpContext
, Fcb
, Vcb
->RootDcb
);
3397 FatPreallocateCloseContext (
3402 Routine Description:
3404 This routine preallocates a close context, presumeably on behalf
3405 of a fileobject which does not have a structure we can embed one
3419 PCLOSE_CONTEXT CloseContext
= FsRtlAllocatePoolWithTag( PagedPool
,
3420 sizeof(CLOSE_CONTEXT
),
3421 TAG_FAT_CLOSE_CONTEXT
);
3423 ExInterlockedPushEntrySList( &FatCloseContextSList
,
3424 (PSINGLE_LIST_ENTRY
) CloseContext
,
3425 &FatData
.GeneralSpinLock
);
3430 FatEnsureStringBufferEnough(
3431 IN OUT PVOID String
,
3432 IN USHORT DesiredBufferSize
3436 Routine Description:
3438 Ensures that a string string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING)
3439 has a buffer >= DesiredBufferSize, allocating from pool if neccessary. Any
3440 existing pool buffer will be freed if a new one is allocated.
3442 NOTE: No copy of old buffer contents is performed on reallocation.
3444 Will raise on allocation failure.
3448 String - pointer to string structure
3450 DesiredBufferSize - (bytes) minimum required buffer size
3454 PSTRING LocalString
= String
;
3456 if (LocalString
->MaximumLength
< DesiredBufferSize
) {
3458 FatFreeStringBuffer( LocalString
);
3460 LocalString
->Buffer
= FsRtlAllocatePoolWithTag( PagedPool
,
3462 TAG_DYNAMIC_NAME_BUFFER
);
3463 ASSERT( LocalString
->Buffer
);
3465 LocalString
->MaximumLength
= DesiredBufferSize
;
3471 FatFreeStringBuffer(
3476 Routine Description:
3478 Frees the buffer of an string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING)
3479 structure if it is not within the current thread's stack limits.
3481 Regardless of action performed, on exit String->Buffer will be set to NULL and
3482 String->MaximumLength to zero.
3486 String - pointer to string structure
3490 ULONG_PTR High
, Low
;
3491 PSTRING LocalString
= String
;
3493 if (NULL
!= LocalString
->Buffer
) {
3495 IoGetStackLimits( &Low
, &High
);
3497 if (((ULONG_PTR
)(LocalString
->Buffer
) < Low
) ||
3498 ((ULONG_PTR
)(LocalString
->Buffer
) > High
)) {
3500 ExFreePool( LocalString
->Buffer
);
3503 LocalString
->Buffer
= NULL
;
3506 LocalString
->MaximumLength
= LocalString
->Length
= 0;
3511 FatScanForDataTrack(
3512 IN PIRP_CONTEXT IrpContext
,
3513 IN PDEVICE_OBJECT TargetDeviceObject
3518 Routine Description:
3520 This routine is called to verify and process the TOC for this disk.
3522 FAT queries for the TOC to avoid trying to mount on CD-DA/CD-E media, Doing data reads on
3523 audio/leadin of that media sends a lot of drives into what could charitably be called
3524 "conniptions" which take a couple seconds to clear and would also convince FAT that the
3525 device was busted, and fail the mount (not letting CDFS get its crack).
3527 There is special handling of PD media. These things fail the TOC read, but return
3528 a special error code so FAT knows to continue to try the mount anyway.
3532 TargetDeviceObject - Device object to send TOC request to.
3536 BOOLEAN - TRUE if we found a TOC with a single data track.
3542 IO_STATUS_BLOCK Iosb
;
3544 ULONG LocalTrackCount
;
3545 ULONG LocalTocLength
;
3547 PCDROM_TOC CdromToc
;
3548 BOOLEAN Result
= FALSE
;
3552 CdromToc
= FsRtlAllocatePoolWithTag( PagedPool
,
3553 sizeof( CDROM_TOC
),
3556 RtlZeroMemory( CdromToc
, sizeof( CDROM_TOC
));
3561 // Go ahead and read the table of contents
3564 Status
= FatPerformDevIoCtrl( IrpContext
,
3565 IOCTL_CDROM_READ_TOC
,
3568 sizeof( CDROM_TOC
),
3574 // Nothing to process if this request fails.
3577 if (Status
!= STATUS_SUCCESS
) {
3580 // If we get the special error indicating a failed TOC read on PD media just
3581 // plow ahead with the mount (see comments above).
3584 if ((Status
== STATUS_IO_DEVICE_ERROR
) || (Status
== STATUS_INVALID_DEVICE_REQUEST
)) {
3590 try_leave( NOTHING
);
3594 // Get the number of tracks and stated size of this structure.
3597 LocalTrackCount
= CdromToc
->LastTrack
- CdromToc
->FirstTrack
+ 1;
3598 LocalTocLength
= PtrOffset( CdromToc
, &CdromToc
->TrackData
[LocalTrackCount
+ 1] );
3601 // Get out if there is an immediate problem with the TOC, or more than
3605 if ((LocalTocLength
> Iosb
.Information
) ||
3606 (CdromToc
->FirstTrack
> CdromToc
->LastTrack
) ||
3607 (LocalTrackCount
!= 1)) {
3609 try_leave( NOTHING
);
3613 // Is it a data track? DVD-RAM reports single, data, track.
3616 Result
= BooleanFlagOn( CdromToc
->TrackData
[ 0].Control
, 0x04 );
3620 ExFreePool( CdromToc
);