diff --git a/drivers/filesystems/fastfat_new/CMakeLists.txt b/drivers/filesystems/fastfat_new/CMakeLists.txt new file mode 100644 index 00000000000..f21d829e630 --- /dev/null +++ b/drivers/filesystems/fastfat_new/CMakeLists.txt @@ -0,0 +1,44 @@ + +list(APPEND SOURCE + acchksup.c + allocsup.c + cachesup.c + cleanup.c + close.c + create.c + devctrl.c + deviosup.c + dirctrl.c + dirsup.c + dumpsup.c + ea.c + easup.c + fatdata.c + fatinit.c + fatprocssrc.c + fileinfo.c + filobsup.c + flush.c + fsctrl.c + fspdisp.c + lockctrl.c + namesup.c + pnp.c + read.c + resrcsup.c + shutdown.c + splaysup.c + strucsup.c + timesup.c + verfysup.c + volinfo.c + workque.c + write.c + fatprocs.h) + +add_library(fastfat SHARED ${SOURCE} fastfat.rc) +set_module_type(fastfat kernelmodedriver) +target_link_libraries(fastfat ${PSEH_LIB} memcmp) +add_importlibs(fastfat ntoskrnl hal) +add_pch(fastfat fatprocs.h SOURCE) +add_cd_file(TARGET fastfat DESTINATION reactos/system32/drivers NO_CAB FOR all) diff --git a/drivers/filesystems/fastfat_new/acchksup.c b/drivers/filesystems/fastfat_new/acchksup.c new file mode 100644 index 00000000000..b0c78a81a45 --- /dev/null +++ b/drivers/filesystems/fastfat_new/acchksup.c @@ -0,0 +1,374 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + AcChkSup.c + +Abstract: + + This module implements the FAT access checking routine + + +--*/ + +#include "fatprocs.h" + +// +// Our debug trace level +// + +#define Dbg (DEBUG_TRACE_ACCHKSUP) + +NTSTATUS +FatCreateRestrictEveryoneToken( + IN PACCESS_TOKEN Token, + OUT PACCESS_TOKEN *RestrictedToken + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCheckFileAccess) +#pragma alloc_text(PAGE, FatCreateRestrictEveryoneToken) +#pragma alloc_text(PAGE, FatExplicitDeviceAccessGranted) +#endif + + +BOOLEAN +FatCheckFileAccess ( + PIRP_CONTEXT IrpContext, + IN UCHAR DirentAttributes, + IN PACCESS_MASK DesiredAccess + ) + +/*++ + +Routine Description: + + This routine checks if a desired access is allowed to a file represented + by the specified DirentAttriubutes. + +Arguments: + + DirentAttributes - Supplies the Dirent attributes to check access for + + DesiredAccess - Supplies the desired access mask that we are checking for + +Return Value: + + BOOLEAN - TRUE if access is allowed and FALSE otherwise + +--*/ + +{ + BOOLEAN Result; + + DebugTrace(+1, Dbg, "FatCheckFileAccess\n", 0); + DebugTrace( 0, Dbg, "DirentAttributes = %8lx\n", DirentAttributes); + DebugTrace( 0, Dbg, "DesiredAccess = %8lx\n", *DesiredAccess); + + // + // This procedures is programmed like a string of filters each + // filter checks to see if some access is allowed, if it is not allowed + // the filter return FALSE to the user without further checks otherwise + // it moves on to the next filter. The filter check is to check for + // desired access flags that are not allowed for a particular dirent + // + + Result = TRUE; + + _SEH2_TRY { + + // + // Check for Volume ID or Device Dirents, these are not allowed user + // access at all + // + + if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_VOLUME_ID) || + FlagOn(DirentAttributes, FAT_DIRENT_ATTR_DEVICE)) { + + DebugTrace(0, Dbg, "Cannot access volume id or device\n", 0); + + try_return( Result = FALSE ); + } + + // + // Check the desired access for the object - we only blackball that + // we do not understand. The model of filesystems using ACLs is that + // they do not type the ACL to the object the ACL is on. Permissions + // are not checked for consistency vs. the object type - dir/file. + // + + if (FlagOn(*DesiredAccess, ~(DELETE | + READ_CONTROL | + WRITE_OWNER | + WRITE_DAC | + SYNCHRONIZE | + ACCESS_SYSTEM_SECURITY | + FILE_WRITE_DATA | + FILE_READ_EA | + FILE_WRITE_EA | + FILE_READ_ATTRIBUTES | + FILE_WRITE_ATTRIBUTES | + FILE_LIST_DIRECTORY | + FILE_TRAVERSE | + FILE_DELETE_CHILD | + FILE_APPEND_DATA))) { + + DebugTrace(0, Dbg, "Cannot open object\n", 0); + + try_return( Result = FALSE ); + } + + // + // Check for a read-only Dirent + // + + if (FlagOn(DirentAttributes, FAT_DIRENT_ATTR_READ_ONLY)) { + + // + // Check the desired access for a read-only dirent, we blackball + // WRITE, FILE_APPEND_DATA, FILE_ADD_FILE, + // FILE_ADD_SUBDIRECTORY, and FILE_DELETE_CHILD + // + + if (FlagOn(*DesiredAccess, ~(DELETE | + READ_CONTROL | + WRITE_OWNER | + WRITE_DAC | + SYNCHRONIZE | + ACCESS_SYSTEM_SECURITY | + FILE_READ_DATA | + FILE_READ_EA | + FILE_WRITE_EA | + FILE_READ_ATTRIBUTES | + FILE_WRITE_ATTRIBUTES | + FILE_EXECUTE | + FILE_LIST_DIRECTORY | + FILE_TRAVERSE))) { + + DebugTrace(0, Dbg, "Cannot open readonly\n", 0); + + try_return( Result = FALSE ); + } + } + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatCheckFileAccess ); + + DebugTrace(-1, Dbg, "FatCheckFileAccess -> %08lx\n", Result); + } _SEH2_END; + + UNREFERENCED_PARAMETER( IrpContext ); + + return Result; +} + + +NTSTATUS +FatExplicitDeviceAccessGranted ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT DeviceObject, + IN PACCESS_STATE AccessState, + IN KPROCESSOR_MODE ProcessorMode + ) + +/*++ + +Routine Description: + + This function asks whether the SID described in the input access state has + been granted any explicit access to the given device object. It does this + by acquiring a token stripped of its ability to acquire access via the + Everyone SID and re-doing the access check. + +Arguments: + + DeviceObject - the device whose ACL will be checked + + AccessState - the access state describing the security context to be checked + + ProcessorMode - the mode this check should occur against + +Return Value: + + NTSTATUS - Indicating whether explicit access was granted. + +--*/ + +{ + NTSTATUS Status; +#ifndef __REACTOS__ + BOOLEAN Result; +#endif + + PACCESS_TOKEN OriginalAccessToken; + PACCESS_TOKEN RestrictedAccessToken; + + PACCESS_TOKEN *EffectiveToken; + + PRIVILEGE_SET PrivilegeSet; + + ACCESS_MASK GrantedAccess; + + // + // If the access state indicates that specific access other + // than traverse was acquired, either Everyone does have such + // access or explicit access was granted. In both cases, we're + // happy to let this proceed. + // + + if (AccessState->PreviouslyGrantedAccess & (SPECIFIC_RIGHTS_ALL ^ + FILE_TRAVERSE)) { + + return STATUS_SUCCESS; + } + + // + // If the manage volume privilege is held, this also permits access. + // + + PrivilegeSet.PrivilegeCount = 1; + PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY; + PrivilegeSet.Privilege[0].Luid = RtlConvertLongToLuid( SE_MANAGE_VOLUME_PRIVILEGE ); + PrivilegeSet.Privilege[0].Attributes = 0; + + if (SePrivilegeCheck( &PrivilegeSet, + &AccessState->SubjectSecurityContext, + ProcessorMode )) { + + return STATUS_SUCCESS; + } + + // + // Capture the subject context as a prelude to everything below. + // + + SeLockSubjectContext( &AccessState->SubjectSecurityContext ); + + // + // Convert the token in the subject context into one which does not + // acquire access through the Everyone SID. + // + // The logic for deciding which token is effective comes from + // SeQuerySubjectContextToken; since there is no natural way + // of getting a pointer to it, do it by hand. + // + + if (ARGUMENT_PRESENT( AccessState->SubjectSecurityContext.ClientToken )) { + EffectiveToken = &AccessState->SubjectSecurityContext.ClientToken; + } else { + EffectiveToken = &AccessState->SubjectSecurityContext.PrimaryToken; + } + + OriginalAccessToken = *EffectiveToken; + Status = FatCreateRestrictEveryoneToken( OriginalAccessToken, &RestrictedAccessToken ); + + if (!NT_SUCCESS(Status)) { + + SeReleaseSubjectContext( &AccessState->SubjectSecurityContext ); + return Status; + } + + // + // Now see if the resulting context has access to the device through + // its explicitly granted access. We swap in our restricted token + // for this check as the effective client token. + // + + *EffectiveToken = RestrictedAccessToken; + +#ifndef __REACTOS__ + Result = SeAccessCheck( DeviceObject->SecurityDescriptor, +#else + SeAccessCheck( DeviceObject->SecurityDescriptor, +#endif + &AccessState->SubjectSecurityContext, + FALSE, + AccessState->OriginalDesiredAccess, + 0, + NULL, + IoGetFileObjectGenericMapping(), + ProcessorMode, + &GrantedAccess, + &Status ); + + *EffectiveToken = OriginalAccessToken; + + // + // Cleanup and return. + // + + SeUnlockSubjectContext( &AccessState->SubjectSecurityContext ); + ObDereferenceObject( RestrictedAccessToken ); + + return Status; +} + + +NTSTATUS +FatCreateRestrictEveryoneToken ( + IN PACCESS_TOKEN Token, + OUT PACCESS_TOKEN *RestrictedToken + ) + +/*++ + +Routine Description: + + This function takes a token as the input and returns a new restricted token + from which Everyone sid has been disabled. The resulting token may be used + to find out if access is available to a user-sid by explicit means. + +Arguments: + + Token - Input token from which Everyone sid needs to be deactivated. + + RestrictedToken - Receives the the new restricted token. + This must be released using ObDereferenceObject(*RestrictedToken); + +Return Value: + + NTSTATUS - Returned by SeFilterToken. + +--*/ + +{ + // + // Array of sids to disable. + // + + TOKEN_GROUPS SidsToDisable; + + NTSTATUS Status = STATUS_SUCCESS; + + // + // Restricted token will contain the original sids with one change: + // If Everyone sid is present in the token, it will be marked for DenyOnly. + // + + *RestrictedToken = NULL; + + // + // Put Everyone sid in the array of sids to disable. This will mark it + // for SE_GROUP_USE_FOR_DENY_ONLY and it'll only be applicable for Deny aces. + // + + SidsToDisable.GroupCount = 1; + SidsToDisable.Groups[0].Attributes = 0; + SidsToDisable.Groups[0].Sid = SeExports->SeWorldSid; + + Status = SeFilterToken( + Token, // Token that needs to be restricted. + 0, // No flags + &SidsToDisable, // Disable everyone sid + NULL, // Do not create any restricted sids + NULL, // Do not delete any privileges + RestrictedToken // Restricted token + ); + + return Status; +} + diff --git a/drivers/filesystems/fastfat_new/allocsup.c b/drivers/filesystems/fastfat_new/allocsup.c new file mode 100644 index 00000000000..79a9e7865ab --- /dev/null +++ b/drivers/filesystems/fastfat_new/allocsup.c @@ -0,0 +1,5001 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + AllocSup.c + +Abstract: + + This module implements the Allocation support routines for Fat. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_ALLOCSUP) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_ALLOCSUP) + +#define FatMin(a, b) ((a) < (b) ? (a) : (b)) + +// +// This strucure is used by FatLookupFatEntry to remember a pinned page +// of fat. +// + +typedef struct _FAT_ENUMERATION_CONTEXT { + + VBO VboOfPinnedPage; + PBCB Bcb; + PVOID PinnedPage; + +} FAT_ENUMERATION_CONTEXT, *PFAT_ENUMERATION_CONTEXT; + +// +// Local support routine prototypes +// + +VOID +FatLookupFatEntry( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN OUT PULONG FatEntry, + IN OUT PFAT_ENUMERATION_CONTEXT Context + ); + +VOID +FatSetFatRun( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG StartingFatIndex, + IN ULONG ClusterCount, + IN BOOLEAN ChainTogether + ); + +UCHAR +FatLogOf( + IN ULONG Value + ); + +// +// Note that the KdPrint below will ONLY fire when the assert does. Leave it +// alone. +// + +#if DBG +#define ASSERT_CURRENT_WINDOW_GOOD(VCB) { \ + ULONG FreeClusterBitMapClear; \ + ASSERT( (VCB)->FreeClusterBitMap.Buffer != NULL ); \ + FreeClusterBitMapClear = RtlNumberOfClearBits(&(VCB)->FreeClusterBitMap); \ + if ((VCB)->CurrentWindow->ClustersFree != FreeClusterBitMapClear) { \ + KdPrint(("FAT: ClustersFree %x h != FreeClusterBitMapClear %x h\n", \ + (VCB)->CurrentWindow->ClustersFree, \ + FreeClusterBitMapClear)); \ + } \ + ASSERT( (VCB)->CurrentWindow->ClustersFree == FreeClusterBitMapClear ); \ +} +#else +#define ASSERT_CURRENT_WINDOW_GOOD(VCB) +#endif + +// +// The following macros provide a convenient way of hiding the details +// of bitmap allocation schemes. +// + + +// +// VOID +// FatLockFreeClusterBitMap ( +// IN PVCB Vcb +// ); +// + +#define FatLockFreeClusterBitMap(VCB) { \ + ASSERT(KeAreApcsDisabled()); \ + ExAcquireFastMutexUnsafe( &(VCB)->FreeClusterBitMapMutex ); \ + ASSERT_CURRENT_WINDOW_GOOD(VCB) \ +} + +// +// VOID +// FatUnlockFreeClusterBitMap ( +// IN PVCB Vcb +// ); +// + +#define FatUnlockFreeClusterBitMap(VCB) { \ + ASSERT_CURRENT_WINDOW_GOOD(VCB) \ + ASSERT(KeAreApcsDisabled()); \ + ExReleaseFastMutexUnsafe( &(VCB)->FreeClusterBitMapMutex ); \ +} + +// +// BOOLEAN +// FatIsClusterFree ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex +// ); +// + +#define FatIsClusterFree(IRPCONTEXT,VCB,FAT_INDEX) \ + (RtlCheckBit(&(VCB)->FreeClusterBitMap,(FAT_INDEX)-2) == 0) + +// +// VOID +// FatFreeClusters ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex, +// IN ULONG ClusterCount +// ); +// + +#define FatFreeClusters(IRPCONTEXT,VCB,FAT_INDEX,CLUSTER_COUNT) { \ + if ((CLUSTER_COUNT) == 1) { \ + FatSetFatEntry((IRPCONTEXT),(VCB),(FAT_INDEX),FAT_CLUSTER_AVAILABLE); \ + } else { \ + FatSetFatRun((IRPCONTEXT),(VCB),(FAT_INDEX),(CLUSTER_COUNT),FALSE); \ + } \ +} + +// +// VOID +// FatAllocateClusters ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex, +// IN ULONG ClusterCount +// ); +// + +#define FatAllocateClusters(IRPCONTEXT,VCB,FAT_INDEX,CLUSTER_COUNT) { \ + if ((CLUSTER_COUNT) == 1) { \ + FatSetFatEntry((IRPCONTEXT),(VCB),(FAT_INDEX),FAT_CLUSTER_LAST); \ + } else { \ + FatSetFatRun((IRPCONTEXT),(VCB),(FAT_INDEX),(CLUSTER_COUNT),TRUE); \ + } \ +} + +// +// VOID +// FatUnreserveClusters ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex, +// IN ULONG ClusterCount +// ); +// + +#define FatUnreserveClusters(IRPCONTEXT,VCB,FAT_INDEX,CLUSTER_COUNT) { \ + ASSERT( (FAT_INDEX) + (CLUSTER_COUNT) - 2 <= (VCB)->FreeClusterBitMap.SizeOfBitMap ); \ + ASSERT( (FAT_INDEX) >= 2); \ + RtlClearBits(&(VCB)->FreeClusterBitMap,(FAT_INDEX)-2,(CLUSTER_COUNT)); \ + if ((FAT_INDEX) < (VCB)->ClusterHint) { \ + (VCB)->ClusterHint = (FAT_INDEX); \ + } \ +} + +// +// VOID +// FatReserveClusters ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG FatIndex, +// IN ULONG ClusterCount +// ); +// +// Handle wrapping the hint back to the front. +// + +#define FatReserveClusters(IRPCONTEXT,VCB,FAT_INDEX,CLUSTER_COUNT) { \ + ULONG _AfterRun = (FAT_INDEX) + (CLUSTER_COUNT); \ + ASSERT( (FAT_INDEX) + (CLUSTER_COUNT) - 2 <= (VCB)->FreeClusterBitMap.SizeOfBitMap ); \ + ASSERT( (FAT_INDEX) >= 2); \ + RtlSetBits(&(VCB)->FreeClusterBitMap,(FAT_INDEX)-2,(CLUSTER_COUNT)); \ + \ + if (_AfterRun - 2 >= (VCB)->FreeClusterBitMap.SizeOfBitMap) { \ + _AfterRun = 2; \ + } \ + if (RtlCheckBit(&(VCB)->FreeClusterBitMap, _AfterRun - 2)) { \ + (VCB)->ClusterHint = RtlFindClearBits( &(VCB)->FreeClusterBitMap, 1, _AfterRun - 2) + 2; \ + if (1 == (VCB)->ClusterHint) { \ + (VCB)->ClusterHint = 2; \ + } \ + } \ + else { \ + (VCB)->ClusterHint = _AfterRun; \ + } \ +} + +// +// ULONG +// FatFindFreeClusterRun ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG ClusterCount, +// IN ULONG AlternateClusterHint +// ); +// +// Do a special check if only one cluster is desired. +// + +#define FatFindFreeClusterRun(IRPCONTEXT,VCB,CLUSTER_COUNT,CLUSTER_HINT) ( \ + (CLUSTER_COUNT == 1) && \ + FatIsClusterFree((IRPCONTEXT), (VCB), (CLUSTER_HINT)) ? \ + (CLUSTER_HINT) : \ + RtlFindClearBits( &(VCB)->FreeClusterBitMap, \ + (CLUSTER_COUNT), \ + (CLUSTER_HINT) - 2) + 2 \ +) + +// +// FAT32: Define the maximum size of the FreeClusterBitMap to be the +// maximum size of a FAT16 FAT. If there are more clusters on the +// volume than can be represented by this many bytes of bitmap, the +// FAT will be split into "buckets", each of which does fit. +// +// Note this count is in clusters/bits of bitmap. +// + +#define MAX_CLUSTER_BITMAP_SIZE (1 << 16) + +// +// Calculate the window a given cluster number is in. +// + +#define FatWindowOfCluster(C) (((C) - 2) / MAX_CLUSTER_BITMAP_SIZE) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatAddFileAllocation) +#pragma alloc_text(PAGE, FatAllocateDiskSpace) +#pragma alloc_text(PAGE, FatDeallocateDiskSpace) +#pragma alloc_text(PAGE, FatExamineFatEntries) +#pragma alloc_text(PAGE, FatInterpretClusterType) +#pragma alloc_text(PAGE, FatLogOf) +#pragma alloc_text(PAGE, FatLookupFatEntry) +#pragma alloc_text(PAGE, FatLookupFileAllocation) +#pragma alloc_text(PAGE, FatLookupFileAllocationSize) +#pragma alloc_text(PAGE, FatMergeAllocation) +#pragma alloc_text(PAGE, FatSetFatEntry) +#pragma alloc_text(PAGE, FatSetFatRun) +#pragma alloc_text(PAGE, FatSetupAllocationSupport) +#pragma alloc_text(PAGE, FatSplitAllocation) +#pragma alloc_text(PAGE, FatTearDownAllocationSupport) +#pragma alloc_text(PAGE, FatTruncateFileAllocation) +#endif + + +INLINE +ULONG +FatSelectBestWindow( + IN PVCB Vcb + ) +/*++ + +Routine Description: + + Choose a window to allocate clusters from. Order of preference is: + + 1. First window with >50% free clusters + 2. First empty window + 3. Window with greatest number of free clusters. + +Arguments: + + Vcb - Supplies the Vcb for the volume + +Return Value: + + 'Best window' number (index into Vcb->Windows[]) + +--*/ +{ + ULONG i, Fave = 0; + ULONG MaxFree = 0; + ULONG FirstEmpty = -1; + ULONG ClustersPerWindow = MAX_CLUSTER_BITMAP_SIZE; + + ASSERT( 1 != Vcb->NumberOfWindows); + + for (i = 0; i < Vcb->NumberOfWindows; i++) { + + if (Vcb->Windows[i].ClustersFree == ClustersPerWindow) { + + if (-1 == FirstEmpty) { + + // + // Keep note of the first empty window on the disc + // + + FirstEmpty = i; + } + } + else if (Vcb->Windows[i].ClustersFree > MaxFree) { + + // + // This window has the most free clusters, so far + // + + MaxFree = Vcb->Windows[i].ClustersFree; + Fave = i; + + // + // If this window has >50% free clusters, then we will take it, + // so don't bother considering more windows. + // + + if (MaxFree >= (ClustersPerWindow >> 1)) { + + break; + } + } + } + + // + // If there were no windows with 50% or more freespace, then select the + // first empty window on the disc, if any - otherwise we'll just go with + // the one with the most free clusters. + // + + if ((MaxFree < (ClustersPerWindow >> 1)) && (-1 != FirstEmpty)) { + + Fave = FirstEmpty; + } + + return Fave; +} + + +VOID +FatSetupAllocationSupport ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine fills in the Allocation Support structure in the Vcb. + Most entries are computed using fat.h macros supplied with data from + the Bios Parameter Block. The free cluster count, however, requires + going to the Fat and actually counting free sectors. At the same time + the free cluster bit map is initalized. + +Arguments: + + Vcb - Supplies the Vcb to fill in. + +--*/ + +{ +#ifndef __REACTOS__ + ULONG BitMapSize; + PVOID BitMapBuffer; +#endif + ULONG BitIndex; + +#ifndef __REACTOS__ + PBCB Bcb; + + ULONG Page; + ULONG Offset; + ULONG FatIndexBitSize; +#endif + ULONG ClustersDescribableByFat; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetupAllocationSupport\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + + // + // Compute a number of fields for Vcb.AllocationSupport + // + + Vcb->AllocationSupport.RootDirectoryLbo = FatRootDirectoryLbo( &Vcb->Bpb ); + Vcb->AllocationSupport.RootDirectorySize = FatRootDirectorySize( &Vcb->Bpb ); + + Vcb->AllocationSupport.FileAreaLbo = FatFileAreaLbo( &Vcb->Bpb ); + + Vcb->AllocationSupport.NumberOfClusters = FatNumberOfClusters( &Vcb->Bpb ); + + Vcb->AllocationSupport.FatIndexBitSize = FatIndexBitSize( &Vcb->Bpb ); + + Vcb->AllocationSupport.LogOfBytesPerSector = FatLogOf(Vcb->Bpb.BytesPerSector); + Vcb->AllocationSupport.LogOfBytesPerCluster = FatLogOf(FatBytesPerCluster( &Vcb->Bpb )); + Vcb->AllocationSupport.NumberOfFreeClusters = 0; + + // + // Deal with a bug in DOS 5 format, if the Fat is not big enough to + // describe all the clusters on the disk, reduce this number. We expect + // that fat32 volumes will not have this problem. + // + // Turns out this was not a good assumption. We have to do this always now. + // + + ClustersDescribableByFat = ( ((FatIsFat32(Vcb)? Vcb->Bpb.LargeSectorsPerFat : + Vcb->Bpb.SectorsPerFat) * + Vcb->Bpb.BytesPerSector * 8) + / FatIndexBitSize(&Vcb->Bpb) ) - 2; + + if (Vcb->AllocationSupport.NumberOfClusters > ClustersDescribableByFat) { + + Vcb->AllocationSupport.NumberOfClusters = ClustersDescribableByFat; + } + + // + // Extend the virtual volume file to include the Fat + // + + { + CC_FILE_SIZES FileSizes; + + FileSizes.AllocationSize.QuadPart = + FileSizes.FileSize.QuadPart = (FatReservedBytes( &Vcb->Bpb ) + + FatBytesPerFat( &Vcb->Bpb )); + FileSizes.ValidDataLength = FatMaxLarge; + + if ( Vcb->VirtualVolumeFile->PrivateCacheMap == NULL ) { + + CcInitializeCacheMap( Vcb->VirtualVolumeFile, + &FileSizes, + TRUE, + &FatData.CacheManagerNoOpCallbacks, + Vcb ); + + } else { + + CcSetFileSizes( Vcb->VirtualVolumeFile, &FileSizes ); + } + } + + _SEH2_TRY { + + if (FatIsFat32(Vcb) && + Vcb->AllocationSupport.NumberOfClusters > MAX_CLUSTER_BITMAP_SIZE) { + + Vcb->NumberOfWindows = (Vcb->AllocationSupport.NumberOfClusters + + MAX_CLUSTER_BITMAP_SIZE - 1) / + MAX_CLUSTER_BITMAP_SIZE; + +#ifndef __REACTOS__ + BitMapSize = MAX_CLUSTER_BITMAP_SIZE; +#endif + + } else { + + Vcb->NumberOfWindows = 1; +#ifndef __REACTOS__ + BitMapSize = Vcb->AllocationSupport.NumberOfClusters; +#endif + } + + Vcb->Windows = FsRtlAllocatePoolWithTag( PagedPool, + Vcb->NumberOfWindows * sizeof(FAT_WINDOW), + TAG_FAT_WINDOW ); + + RtlInitializeBitMap( &Vcb->FreeClusterBitMap, + NULL, + 0 ); + + // + // Chose a FAT window to begin operation in. + // + + if (Vcb->NumberOfWindows > 1) { + + // + // Read the fat and count up free clusters. We bias by the two reserved + // entries in the FAT. + // + + FatExamineFatEntries( IrpContext, Vcb, + 2, + Vcb->AllocationSupport.NumberOfClusters + 2 - 1, + TRUE, + NULL, + NULL); + + + // + // Pick a window to begin allocating from + // + + Vcb->CurrentWindow = &Vcb->Windows[ FatSelectBestWindow( Vcb)]; + + } else { + + Vcb->CurrentWindow = &Vcb->Windows[0]; + + // + // Carefully bias ourselves by the two reserved entries in the FAT. + // + + Vcb->CurrentWindow->FirstCluster = 2; + Vcb->CurrentWindow->LastCluster = Vcb->AllocationSupport.NumberOfClusters + 2 - 1; + } + + // + // Now transition to the FAT window we have chosen. + // + + FatExamineFatEntries( IrpContext, Vcb, + 0, + 0, + FALSE, + Vcb->CurrentWindow, + NULL); + + // + // Now set the ClusterHint to the first free bit in our favorite + // window (except the ClusterHint is off by two). + // + + Vcb->ClusterHint = + (BitIndex = RtlFindClearBits( &Vcb->FreeClusterBitMap, 1, 0 )) != -1 ? + BitIndex + 2 : 2; + + } _SEH2_FINALLY { + + DebugUnwind( FatSetupAllocationSupport ); + + // + // If we hit an exception, back out. + // + + if (_SEH2_AbnormalTermination()) { + + FatTearDownAllocationSupport( IrpContext, Vcb ); + } + } _SEH2_END; + + return; +} + + +VOID +FatTearDownAllocationSupport ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine prepares the volume for closing. Specifically, we must + release the free fat bit map buffer, and uninitialize the dirty fat + Mcb. + +Arguments: + + Vcb - Supplies the Vcb to fill in. + +Return Value: + + VOID + +--*/ + +{ + DebugTrace(+1, Dbg, "FatTearDownAllocationSupport\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + + PAGED_CODE(); + + // + // If there are FAT buckets, free them. + // + + if ( Vcb->Windows != NULL ) { + + ExFreePool( Vcb->Windows ); + Vcb->Windows = NULL; + } + + // + // Free the memory associated with the free cluster bitmap. + // + + if ( Vcb->FreeClusterBitMap.Buffer != NULL ) { + + ExFreePool( Vcb->FreeClusterBitMap.Buffer ); + + // + // NULL this field as an flag. + // + + Vcb->FreeClusterBitMap.Buffer = NULL; + } + + // + // And remove all the runs in the dirty fat Mcb + // + + FatRemoveMcbEntry( Vcb, &Vcb->DirtyFatMcb, 0, 0xFFFFFFFF ); + + DebugTrace(-1, Dbg, "FatTearDownAllocationSupport -> (VOID)\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +VOID +FatLookupFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount, + OUT PBOOLEAN Allocated, + OUT PBOOLEAN EndOnMax, + OUT PULONG Index + ) + +/*++ + +Routine Description: + + This routine looks up the existing mapping of VBO to LBO for a + file/directory. The information it queries is either stored in the + mcb field of the fcb/dcb or it is stored on in the fat table and + needs to be retrieved and decoded, and updated in the mcb. + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb of the file/directory being queried + + Vbo - Supplies the VBO whose LBO we want returned + + Lbo - Receives the LBO corresponding to the input Vbo if one exists + + ByteCount - Receives the number of bytes within the run the run + that correpond between the input vbo and output lbo. + + Allocated - Receives TRUE if the Vbo does have a corresponding Lbo + and FALSE otherwise. + + EndOnMax - Receives TRUE if the run ends in the maximal FAT cluster, + which results in a fractional bytecount. + + Index - Receives the Index of the run + +--*/ + +{ + VBO CurrentVbo; + LBO CurrentLbo; + LBO PriorLbo; + + VBO FirstVboOfCurrentRun; + LBO FirstLboOfCurrentRun; + + BOOLEAN LastCluster; + ULONG Runs; + + PVCB Vcb; + FAT_ENTRY FatEntry; + ULONG BytesPerCluster; + ULARGE_INTEGER BytesOnVolume; + + FAT_ENUMERATION_CONTEXT Context; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLookupFileAllocation\n", 0); + DebugTrace( 0, Dbg, " FcbOrDcb = %8lx\n", FcbOrDcb); + DebugTrace( 0, Dbg, " Vbo = %8lx\n", Vbo); + DebugTrace( 0, Dbg, " Lbo = %8lx\n", Lbo); + DebugTrace( 0, Dbg, " ByteCount = %8lx\n", ByteCount); + DebugTrace( 0, Dbg, " Allocated = %8lx\n", Allocated); + + Context.Bcb = NULL; + + Vcb = FcbOrDcb->Vcb; + + *EndOnMax = FALSE; + + // + // Check the trivial case that the mapping is already in our + // Mcb. + // + + if ( FatLookupMcbEntry(Vcb, &FcbOrDcb->Mcb, Vbo, Lbo, ByteCount, Index) ) { + + *Allocated = TRUE; + + ASSERT( ByteCount != 0); + + // + // Detect the overflow case, trim and claim the condition. + // + + if (Vbo + *ByteCount == 0) { + + *EndOnMax = TRUE; + } + + DebugTrace( 0, Dbg, "Found run in Mcb.\n", 0); + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> (VOID)\n", 0); + return; + } + + // + // Initialize the Vcb, the cluster size, LastCluster, and + // FirstLboOfCurrentRun (to be used as an indication of the first + // iteration through the following while loop). + // + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + BytesOnVolume.QuadPart = UInt32x32To64( Vcb->AllocationSupport.NumberOfClusters, BytesPerCluster ); + + LastCluster = FALSE; + FirstLboOfCurrentRun = 0; + + // + // Discard the case that the request extends beyond the end of + // allocation. Note that if the allocation size if not known + // AllocationSize is set to 0xffffffff. + // + + if ( Vbo >= FcbOrDcb->Header.AllocationSize.LowPart ) { + + *Allocated = FALSE; + + DebugTrace( 0, Dbg, "Vbo beyond end of file.\n", 0); + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> (VOID)\n", 0); + return; + } + + // + // The Vbo is beyond the last Mcb entry. So we adjust Current Vbo/Lbo + // and FatEntry to describe the beginning of the last entry in the Mcb. + // This is used as initialization for the following loop. + // + // If the Mcb was empty, we start at the beginning of the file with + // CurrentVbo set to 0 to indicate a new run. + // + + if (FatLookupLastMcbEntry( Vcb, &FcbOrDcb->Mcb, &CurrentVbo, &CurrentLbo, &Runs )) { + + DebugTrace( 0, Dbg, "Current Mcb size = %8lx.\n", CurrentVbo + 1); + + CurrentVbo -= (BytesPerCluster - 1); + CurrentLbo -= (BytesPerCluster - 1); + + // + // Convert an index to a count. + // + + Runs += 1; + + } else { + + DebugTrace( 0, Dbg, "Mcb empty.\n", 0); + + // + // Check for an FcbOrDcb that has no allocation + // + + if (FcbOrDcb->FirstClusterOfFile == 0) { + + *Allocated = FALSE; + + DebugTrace( 0, Dbg, "File has no allocation.\n", 0); + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> (VOID)\n", 0); + return; + + } else { + + CurrentVbo = 0; + CurrentLbo = FatGetLboFromIndex( Vcb, FcbOrDcb->FirstClusterOfFile ); + FirstVboOfCurrentRun = CurrentVbo; + FirstLboOfCurrentRun = CurrentLbo; + + Runs = 0; + + DebugTrace( 0, Dbg, "First Lbo of file = %8lx\n", CurrentLbo); + } + } + + // + // Now we know that we are looking up a valid Vbo, but it is + // not in the Mcb, which is a monotonically increasing list of + // Vbo's. Thus we have to go to the Fat, and update + // the Mcb as we go. We use a try-finally to unpin the page + // of fat hanging around. Also we mark *Allocated = FALSE, so that + // the caller wont try to use the data if we hit an exception. + // + + *Allocated = FALSE; + + _SEH2_TRY { + + FatEntry = (FAT_ENTRY)FatGetIndexFromLbo( Vcb, CurrentLbo ); + + // + // ASSERT that CurrentVbo and CurrentLbo are now cluster alligned. + // The assumption here, is that only whole clusters of Vbos and Lbos + // are mapped in the Mcb. + // + + ASSERT( ((CurrentLbo - Vcb->AllocationSupport.FileAreaLbo) + % BytesPerCluster == 0) && + (CurrentVbo % BytesPerCluster == 0) ); + + // + // Starting from the first Vbo after the last Mcb entry, scan through + // the Fat looking for our Vbo. We continue through the Fat until we + // hit a noncontiguity beyond the desired Vbo, or the last cluster. + // + + while ( !LastCluster ) { + + // + // Get the next fat entry, and update our Current variables. + // + +#ifndef __REACTOS__ + FatLookupFatEntry( IrpContext, Vcb, FatEntry, &FatEntry, &Context ); +#else + FatLookupFatEntry( IrpContext, Vcb, FatEntry, (PULONG)&FatEntry, &Context ); +#endif + + PriorLbo = CurrentLbo; + CurrentLbo = FatGetLboFromIndex( Vcb, FatEntry ); + CurrentVbo += BytesPerCluster; + + switch ( FatInterpretClusterType( Vcb, FatEntry )) { + + // + // Check for a break in the Fat allocation chain. + // + + case FatClusterAvailable: + case FatClusterReserved: + case FatClusterBad: + + DebugTrace( 0, Dbg, "Break in allocation chain, entry = %d\n", FatEntry); + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> Fat Corrupt. Raise Status.\n", 0); + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + break; + + // + // If this is the last cluster, we must update the Mcb and + // exit the loop. + // + + case FatClusterLast: + + // + // Assert we know where the current run started. If the + // Mcb was empty when we were called, thenFirstLboOfCurrentRun + // was set to the start of the file. If the Mcb contained an + // entry, then FirstLboOfCurrentRun was set on the first + // iteration through the loop. Thus if FirstLboOfCurrentRun + // is 0, then there was an Mcb entry and we are on our first + // iteration, meaing that the last cluster in the Mcb was + // really the last allocated cluster, but we checked Vbo + // against AllocationSize, and found it OK, thus AllocationSize + // must be too large. + // + // Note that, when we finally arrive here, CurrentVbo is actually + // the first Vbo beyond the file allocation and CurrentLbo is + // meaningless. + // + + DebugTrace( 0, Dbg, "Read last cluster of file.\n", 0); + + // + // Detect the case of the maximal file. Note that this really isn't + // a proper Vbo - those are zero-based, and this is a one-based number. + // The maximal file, of 2^32 - 1 bytes, has a maximum byte offset of + // 2^32 - 2. + // + // Just so we don't get confused here. + // + + if (CurrentVbo == 0) { + + *EndOnMax = TRUE; + CurrentVbo -= 1; + } + + LastCluster = TRUE; + + if (FirstLboOfCurrentRun != 0 ) { + + DebugTrace( 0, Dbg, "Adding a run to the Mcb.\n", 0); + DebugTrace( 0, Dbg, " Vbo = %08lx.\n", FirstVboOfCurrentRun); + DebugTrace( 0, Dbg, " Lbo = %08lx.\n", FirstLboOfCurrentRun); + DebugTrace( 0, Dbg, " Length = %08lx.\n", CurrentVbo - FirstVboOfCurrentRun); + + (VOID)FatAddMcbEntry( Vcb, + &FcbOrDcb->Mcb, + FirstVboOfCurrentRun, + FirstLboOfCurrentRun, + CurrentVbo - FirstVboOfCurrentRun ); + + Runs += 1; + } + + // + // Being at the end of allocation, make sure we have found + // the Vbo. If we haven't, seeing as we checked VBO + // against AllocationSize, the real disk allocation is less + // than that of AllocationSize. This comes about when the + // real allocation is not yet known, and AllocaitonSize + // contains MAXULONG. + // + // KLUDGE! - If we were called by FatLookupFileAllocationSize + // Vbo is set to MAXULONG - 1, and AllocationSize to the lookup + // hint. Thus we merrily go along looking for a match that isn't + // there, but in the meantime building an Mcb. If this is + // the case, fill in AllocationSize and return. + // + + if ( Vbo == MAXULONG - 1 ) { + + *Allocated = FALSE; + FcbOrDcb->Header.AllocationSize.QuadPart = CurrentVbo; + + DebugTrace( 0, Dbg, "New file allocation size = %08lx.\n", CurrentVbo); + try_return ( NOTHING ); + } + + // + // We will lie ever so slightly if we really terminated on the + // maximal byte of a file. It is really allocated. + // + + if (Vbo >= CurrentVbo && !*EndOnMax) { + + *Allocated = FALSE; + try_return ( NOTHING ); + } + + break; + + // + // This is a continuation in the chain. If the run has a + // discontiguity at this point, update the Mcb, and if we are beyond + // the desired Vbo, this is the end of the run, so set LastCluster + // and exit the loop. + // + + case FatClusterNext: + + // + // This is the loop check. The Vbo must not be bigger than the size of + // the volume, and the Vbo must not have a) wrapped and b) not been at the + // very last cluster in the chain, for the case of the maximal file. + // + + if ( CurrentVbo == 0 || + (BytesOnVolume.HighPart == 0 && CurrentVbo > BytesOnVolume.LowPart)) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + if ( PriorLbo + BytesPerCluster != CurrentLbo ) { + + // + // Note that on the first time through the loop + // (FirstLboOfCurrentRun == 0), we don't add the + // run to the Mcb since it curresponds to the last + // run already stored in the Mcb. + // + + if ( FirstLboOfCurrentRun != 0 ) { + + DebugTrace( 0, Dbg, "Adding a run to the Mcb.\n", 0); + DebugTrace( 0, Dbg, " Vbo = %08lx.\n", FirstVboOfCurrentRun); + DebugTrace( 0, Dbg, " Lbo = %08lx.\n", FirstLboOfCurrentRun); + DebugTrace( 0, Dbg, " Length = %08lx.\n", CurrentVbo - FirstVboOfCurrentRun); + + FatAddMcbEntry( Vcb, + &FcbOrDcb->Mcb, + FirstVboOfCurrentRun, + FirstLboOfCurrentRun, + CurrentVbo - FirstVboOfCurrentRun ); + + Runs += 1; + } + + // + // Since we are at a run boundry, with CurrentLbo and + // CurrentVbo being the first cluster of the next run, + // we see if the run we just added encompases the desired + // Vbo, and if so exit. Otherwise we set up two new + // First*boOfCurrentRun, and continue. + // + + if (CurrentVbo > Vbo) { + + LastCluster = TRUE; + + } else { + + FirstVboOfCurrentRun = CurrentVbo; + FirstLboOfCurrentRun = CurrentLbo; + } + } + break; + + default: + + DebugTrace(0, Dbg, "Illegal Cluster Type.\n", FatEntry); + + FatBugCheck( 0, 0, 0 ); + + break; + + } // switch() + } // while() + + // + // Load up the return parameters. + // + // On exit from the loop, Vbo still contains the desired Vbo, and + // CurrentVbo is the first byte after the run that contained the + // desired Vbo. + // + + *Allocated = TRUE; + + *Lbo = FirstLboOfCurrentRun + (Vbo - FirstVboOfCurrentRun); + + *ByteCount = CurrentVbo - Vbo; + + if (ARGUMENT_PRESENT(Index)) { + + // + // Note that Runs only needs to be accurate with respect to where we + // ended. Since partial-lookup cases will occur without exclusive + // synchronization, the Mcb itself may be much bigger by now. + // + + *Index = Runs - 1; + } + + try_exit: NOTHING; + + } _SEH2_FINALLY { + + DebugUnwind( FatLookupFileAllocation ); + + // + // We are done reading the Fat, so unpin the last page of fat + // that is hanging around + // + + FatUnpinBcb( IrpContext, Context.Bcb ); + + DebugTrace(-1, Dbg, "FatLookupFileAllocation -> (VOID)\n", 0); + } _SEH2_END; + + return; +} + + +VOID +FatAddFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN PFILE_OBJECT FileObject OPTIONAL, + IN ULONG DesiredAllocationSize + ) + +/*++ + +Routine Description: + + This routine adds additional allocation to the specified file/directory. + Additional allocation is added by appending clusters to the file/directory. + + If the file already has a sufficient allocation then this procedure + is effectively a noop. + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb of the file/directory being modified. + This parameter must not specify the root dcb. + + FileObject - If supplied inform the cache manager of the change. + + DesiredAllocationSize - Supplies the minimum size, in bytes, that we want + allocated to the file/directory. + +--*/ + +{ + PVCB Vcb; + LARGE_MCB NewMcb; + PLARGE_MCB McbToCleanup = NULL; + PDIRENT Dirent = NULL; + ULONG NewAllocation; + PBCB Bcb = NULL; + BOOLEAN UnwindWeAllocatedDiskSpace = FALSE; + BOOLEAN UnwindAllocationSizeSet = FALSE; + BOOLEAN UnwindCacheManagerInformed = FALSE; + BOOLEAN UnwindWeInitializedMcb = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatAddFileAllocation\n", 0); + DebugTrace( 0, Dbg, " FcbOrDcb = %8lx\n", FcbOrDcb); + DebugTrace( 0, Dbg, " DesiredAllocationSize = %8lx\n", DesiredAllocationSize); + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + // + // Check for the benign case that the desired allocation is already + // within the allocation size. + // + + if (DesiredAllocationSize <= FcbOrDcb->Header.AllocationSize.LowPart) { + + DebugTrace(0, Dbg, "Desired size within current allocation.\n", 0); + + DebugTrace(-1, Dbg, "FatAddFileAllocation -> (VOID)\n", 0); + return; + } + + DebugTrace( 0, Dbg, "InitialAllocation = %08lx.\n", FcbOrDcb->Header.AllocationSize.LowPart); + + // + // Get a chunk of disk space that will fullfill our needs. If there + // was no initial allocation, start from the hint in the Vcb, otherwise + // try to allocate from the cluster after the initial allocation. + // + // If there was no initial allocation to the file, we can just use the + // Mcb in the FcbOrDcb, otherwise we have to use a new one, and merge + // it to the one in the FcbOrDcb. + // + + Vcb = FcbOrDcb->Vcb; + + _SEH2_TRY { + + if (FcbOrDcb->Header.AllocationSize.LowPart == 0) { + + LBO FirstLboOfFile; + + ASSERT( FcbOrDcb->FcbCondition == FcbGood ); + + FatGetDirentFromFcbOrDcb( IrpContext, + FcbOrDcb, + &Dirent, + &Bcb ); + + ASSERT( Bcb != NULL ); + + // + // Set this dirty right now since this call can fail. + // + + FatSetDirtyBcb( IrpContext, Bcb, Vcb, TRUE ); + + + FatAllocateDiskSpace( IrpContext, + Vcb, + 0, + &DesiredAllocationSize, + FALSE, + &FcbOrDcb->Mcb ); + + UnwindWeAllocatedDiskSpace = TRUE; + McbToCleanup = &FcbOrDcb->Mcb; + + // + // We have to update the dirent and FcbOrDcb copies of + // FirstClusterOfFile since before it was 0 + // + + FatLookupMcbEntry( FcbOrDcb->Vcb, + &FcbOrDcb->Mcb, + 0, + &FirstLboOfFile, + (PULONG)NULL, + NULL ); + + DebugTrace( 0, Dbg, "First Lbo of file will be %08lx.\n", FirstLboOfFile ); + + FcbOrDcb->FirstClusterOfFile = FatGetIndexFromLbo( Vcb, FirstLboOfFile ); + + Dirent->FirstClusterOfFile = (USHORT)FcbOrDcb->FirstClusterOfFile; + + if ( FatIsFat32(Vcb) ) { + + Dirent->FirstClusterOfFileHi = (USHORT)(FcbOrDcb->FirstClusterOfFile >> 16); + } + + // + // Note the size of the allocation we need to tell the cache manager about. + // + + NewAllocation = DesiredAllocationSize; + + } else { + + LBO LastAllocatedLbo; + VBO DontCare; + + // + // Get the first cluster following the current allocation. It is possible + // the Mcb is empty (or short, etc.) so we need to be slightly careful + // about making sure we don't lie with the hint. + // + + (void)FatLookupLastMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb, &DontCare, &LastAllocatedLbo, NULL ); + + // + // Try to get some disk space starting from there. + // + + NewAllocation = DesiredAllocationSize - FcbOrDcb->Header.AllocationSize.LowPart; + + FsRtlInitializeLargeMcb( &NewMcb, PagedPool ); + UnwindWeInitializedMcb = TRUE; + McbToCleanup = &NewMcb; + + FatAllocateDiskSpace( IrpContext, + Vcb, + (LastAllocatedLbo != ~0 ? + FatGetIndexFromLbo(Vcb,LastAllocatedLbo + 1) : + 0), + &NewAllocation, + FALSE, + &NewMcb ); + + UnwindWeAllocatedDiskSpace = TRUE; + } + + // + // Now that we increased the allocation of the file, mark it in the + // FcbOrDcb. Carefully prepare to handle an inability to grow the cache + // structures. + // + + FcbOrDcb->Header.AllocationSize.LowPart += NewAllocation; + + // + // Handle the maximal file case, where we may have just wrapped. Note + // that this must be the precise boundary case wrap, i.e. by one byte, + // so that the new allocation is actually one byte "less" as far as we're + // concerned. This is important for the extension case. + // + + if (FcbOrDcb->Header.AllocationSize.LowPart == 0) { + + NewAllocation -= 1; + FcbOrDcb->Header.AllocationSize.LowPart = 0xffffffff; + } + + UnwindAllocationSizeSet = TRUE; + + // + // Inform the cache manager to increase the section size + // + + if ( ARGUMENT_PRESENT(FileObject) && CcIsFileCached(FileObject) ) { + + CcSetFileSizes( FileObject, + (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize ); + UnwindCacheManagerInformed = TRUE; + } + + // + // In the extension case, we have held off actually gluing the new + // allocation onto the file. This simplifies exception cleanup since + // if it was already added and the section grow failed, we'd have to + // do extra work to unglue it. This way, we can assume that if we + // raise the only thing we need to do is deallocate the disk space. + // + // Merge the allocation now. + // + + if (FcbOrDcb->Header.AllocationSize.LowPart != NewAllocation) { + + // + // Tack the new Mcb onto the end of the FcbOrDcb one. + // + + FatMergeAllocation( IrpContext, + Vcb, + &FcbOrDcb->Mcb, + &NewMcb ); + } + + } _SEH2_FINALLY { + + DebugUnwind( FatAddFileAllocation ); + + // + // Give FlushFileBuffer a clue here. + // + + SetFlag(FcbOrDcb->FcbState, FCB_STATE_FLUSH_FAT); + + // + // If we were dogged trying to complete this operation, we need to go + // back various things out. + // + + if (_SEH2_AbnormalTermination()) { + + // + // Pull off the allocation size we tried to add to this object if + // we failed to grow cache structures or Mcb structures. + // + + if (UnwindAllocationSizeSet) { + + FcbOrDcb->Header.AllocationSize.LowPart -= NewAllocation; + } + + if (UnwindCacheManagerInformed) { + + CcSetFileSizes( FileObject, + (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize ); + } + + // + // In the case of initial allocation, we used the Fcb's Mcb and have + // to clean that up as well as the FAT chain references. + // + + if (FcbOrDcb->Header.AllocationSize.LowPart == 0) { + + if (Dirent != NULL) { + + FcbOrDcb->FirstClusterOfFile = 0; + Dirent->FirstClusterOfFile = 0; + + if ( FatIsFat32(Vcb) ) { + + Dirent->FirstClusterOfFileHi = 0; + } + } + } + + // + // ... and drop the dirent Bcb if we got it. Do it now + // so we can afford to take the exception if we have to. + // + + FatUnpinBcb( IrpContext, Bcb ); + + _SEH2_TRY { + + // + // Note this can re-raise. + // + + if ( UnwindWeAllocatedDiskSpace ) { + + FatDeallocateDiskSpace( IrpContext, Vcb, McbToCleanup ); + } + + } _SEH2_FINALLY { + + // + // We always want to clean up the non-initial allocation temporary Mcb, + // otherwise we have the Fcb's Mcb and we just truncate it away. + // + + if (UnwindWeInitializedMcb == TRUE) { + + // + // Note that we already know a raise is in progress. No danger + // of encountering the normal case code below and doing this again. + // + + FsRtlUninitializeLargeMcb( McbToCleanup ); + + } else { + + if (McbToCleanup) { + + FsRtlTruncateLargeMcb( McbToCleanup, 0 ); + } + } + } _SEH2_END; + } + + DebugTrace(-1, Dbg, "FatAddFileAllocation -> (VOID)\n", 0); + } _SEH2_END; + + // + // Non-exceptional cleanup we always want to do. In handling the re-raise possibilities + // during exceptions we had to make sure these two steps always happened there beforehand. + // So now we handle the usual case. + // + + FatUnpinBcb( IrpContext, Bcb ); + + if (UnwindWeInitializedMcb == TRUE) { + + FsRtlUninitializeLargeMcb( &NewMcb ); + } +} + + +VOID +FatTruncateFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG DesiredAllocationSize + ) + +/*++ + +Routine Description: + + This routine truncates the allocation to the specified file/directory. + + If the file is already smaller than the indicated size then this procedure + is effectively a noop. + + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb of the file/directory being modified + This parameter must not specify the root dcb. + + DesiredAllocationSize - Supplies the maximum size, in bytes, that we want + allocated to the file/directory. It is rounded + up to the nearest cluster. + +Return Value: + + VOID - TRUE if the operation completed and FALSE if it had to + block but could not. + +--*/ + +{ + PVCB Vcb; + PBCB Bcb = NULL; + LARGE_MCB RemainingMcb; + ULONG BytesPerCluster; + PDIRENT Dirent = NULL; + BOOLEAN UpdatedDirent = FALSE; + + ULONG UnwindInitialAllocationSize; + ULONG UnwindInitialFirstClusterOfFile; + BOOLEAN UnwindWeAllocatedMcb = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatTruncateFileAllocation\n", 0); + DebugTrace( 0, Dbg, " FcbOrDcb = %8lx\n", FcbOrDcb); + DebugTrace( 0, Dbg, " DesiredAllocationSize = %8lx\n", DesiredAllocationSize); + + // + // If the Fcb isn't in good condition, we have no business whacking around on + // the disk after "its" clusters. + // + // Inspired by a Prefix complaint. + // + + ASSERT( FcbOrDcb->FcbCondition == FcbGood ); + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + // + // Round up the Desired Allocation Size to the next cluster size + // + + Vcb = FcbOrDcb->Vcb; + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // Note if the desired allocation is zero, to distinguish this from + // the wrap case below. + // + + if (DesiredAllocationSize != 0) { + + DesiredAllocationSize = (DesiredAllocationSize + (BytesPerCluster - 1)) & + ~(BytesPerCluster - 1); + // + // Check for the benign case that the file is already smaller than + // the desired truncation. Note that if it wraps, then a) it was + // specifying an offset in the maximally allocatable cluster and + // b) we're not asking to extend the file, either. So stop. + // + + if (DesiredAllocationSize == 0 || + DesiredAllocationSize >= FcbOrDcb->Header.AllocationSize.LowPart) { + + DebugTrace(0, Dbg, "Desired size within current allocation.\n", 0); + + DebugTrace(-1, Dbg, "FatTruncateFileAllocation -> (VOID)\n", 0); + return; + } + + } + + UnwindInitialAllocationSize = FcbOrDcb->Header.AllocationSize.LowPart; + UnwindInitialFirstClusterOfFile = FcbOrDcb->FirstClusterOfFile; + + // + // Update the FcbOrDcb allocation size. If it is now zero, we have the + // additional task of modifying the FcbOrDcb and Dirent copies of + // FirstClusterInFile. + // + // Note that we must pin the dirent before actually deallocating the + // disk space since, in unwind, it would not be possible to reallocate + // deallocated disk space as someone else may have reallocated it and + // may cause an exception when you try to get some more disk space. + // Thus FatDeallocateDiskSpace must be the final dangerous operation. + // + + _SEH2_TRY { + + FcbOrDcb->Header.AllocationSize.QuadPart = DesiredAllocationSize; + + // + // Special case 0 + // + + if (DesiredAllocationSize == 0) { + + // + // We have to update the dirent and FcbOrDcb copies of + // FirstClusterOfFile since before it was 0 + // + + ASSERT( FcbOrDcb->FcbCondition == FcbGood ); + + FatGetDirentFromFcbOrDcb( IrpContext, FcbOrDcb, &Dirent, &Bcb ); + + ASSERT( Dirent && Bcb ); + + Dirent->FirstClusterOfFile = 0; + + if (FatIsFat32(Vcb)) { + + Dirent->FirstClusterOfFileHi = 0; + } + + FcbOrDcb->FirstClusterOfFile = 0; + + FatSetDirtyBcb( IrpContext, Bcb, Vcb, TRUE ); + UpdatedDirent = TRUE; + + FatDeallocateDiskSpace( IrpContext, Vcb, &FcbOrDcb->Mcb ); + + FatRemoveMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb, 0, 0xFFFFFFFF ); + + } else { + + // + // Split the existing allocation into two parts, one we will keep, and + // one we will deallocate. + // + + FsRtlInitializeLargeMcb( &RemainingMcb, PagedPool ); + UnwindWeAllocatedMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &FcbOrDcb->Mcb, + DesiredAllocationSize, + &RemainingMcb ); + + FatDeallocateDiskSpace( IrpContext, Vcb, &RemainingMcb ); + + FsRtlUninitializeLargeMcb( &RemainingMcb ); + } + + } _SEH2_FINALLY { + + DebugUnwind( FatTruncateFileAllocation ); + + // + // Is this really the right backout strategy? It would be nice if we could + // pretend the truncate worked if we knew that the file had gotten into + // a consistent state. Leaving dangled clusters is probably quite preferable. + // + + if ( _SEH2_AbnormalTermination() ) { + + FcbOrDcb->Header.AllocationSize.LowPart = UnwindInitialAllocationSize; + + if ( (DesiredAllocationSize == 0) && (Dirent != NULL)) { + + if (UpdatedDirent) { + + // + // If the dirent has been updated ok and marked dirty, then we + // failed in deallocatediscspace, and don't know what state + // the on disc fat chain is in. So we throw away the mcb, + // and potentially loose a few clusters until the next + // chkdsk. The operation has succeeded, but the exception + // will still propogate. 5.1 + // + + FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb, 0, 0xFFFFFFFF ); + FcbOrDcb->Header.AllocationSize.QuadPart = 0; + } + else { + + Dirent->FirstClusterOfFile = (USHORT)UnwindInitialFirstClusterOfFile; + + if ( FatIsFat32(Vcb) ) { + + Dirent->FirstClusterOfFileHi = + (USHORT)(UnwindInitialFirstClusterOfFile >> 16); + } + + FcbOrDcb->FirstClusterOfFile = UnwindInitialFirstClusterOfFile; + } + } + + if ( UnwindWeAllocatedMcb ) { + + FsRtlUninitializeLargeMcb( &RemainingMcb ); + } + + // + // Note that in the non zero truncation case, we will also + // leak clusters. However, apart from this, the in memory and on disc + // structures will agree. + } + + FatUnpinBcb( IrpContext, Bcb ); + + // + // Give FlushFileBuffer a clue here. + // + + SetFlag(FcbOrDcb->FcbState, FCB_STATE_FLUSH_FAT); + + DebugTrace(-1, Dbg, "FatTruncateFileAllocation -> (VOID)\n", 0); + } _SEH2_END; +} + + +VOID +FatLookupFileAllocationSize ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb + ) + +/*++ + +Routine Description: + + This routine retrieves the current file allocatio size for the + specified file/directory. + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb of the file/directory being modified + +--*/ + +{ + LBO Lbo; + ULONG ByteCount; + BOOLEAN DontCare; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLookupAllocationSize\n", 0); + DebugTrace( 0, Dbg, " FcbOrDcb = %8lx\n", FcbOrDcb); + + // + // We call FatLookupFileAllocation with Vbo of 0xffffffff - 1. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + MAXULONG - 1, + &Lbo, + &ByteCount, + &DontCare, + &DontCare, + NULL ); + + // + // FileSize was set at Fcb creation time from the contents of the directory entry, + // and we are only now looking up the real length of the allocation chain. If it + // cannot be contained, this is trash. Probably more where that came from. + // + + if (FcbOrDcb->Header.FileSize.LowPart > FcbOrDcb->Header.AllocationSize.LowPart) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + DebugTrace(-1, Dbg, "FatLookupFileAllocationSize -> (VOID)\n", 0); + return; +} + + +VOID +FatAllocateDiskSpace ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG AbsoluteClusterHint, + IN PULONG ByteCount, + IN BOOLEAN ExactMatchRequired, + OUT PLARGE_MCB Mcb + ) + +/*++ + +Routine Description: + + This procedure allocates additional disk space and builds an mcb + representing the newly allocated space. If the space cannot be + allocated then this procedure raises an appropriate status. + + Searching starts from the hint index in the Vcb unless an alternative + non-zero hint is given in AlternateClusterHint. If we are using the + hint field in the Vcb, it is set to the cluster following our allocation + when we are done. + + Disk space can only be allocated in cluster units so this procedure + will round up any byte count to the next cluster boundary. + + Pictorially what is done is the following (where ! denotes the end of + the fat chain (i.e., FAT_CLUSTER_LAST)): + + + Mcb (empty) + + becomes + + Mcb |--a--|--b--|--c--! + + ^ + ByteCount ----------+ + +Arguments: + + Vcb - Supplies the VCB being modified + + AbsoluteClusterHint - Supplies an alternate hint index to start the + search from. If this is zero we use, and update, + the Vcb hint field. + + ByteCount - Supplies the number of bytes that we are requesting, and + receives the number of bytes that we got. + + ExactMatchRequired - Caller should set this to TRUE if only the precise run requested + is acceptable. + + Mcb - Receives the MCB describing the newly allocated disk space. The + caller passes in an initialized Mcb that is filled in by this procedure. + + Return Value: + + TRUE - Allocated ok + FALSE - Failed to allocate exactly as requested (=> ExactMatchRequired was TRUE) + +--*/ + +{ + UCHAR LogOfBytesPerCluster; + ULONG BytesPerCluster; + ULONG StartingCluster; + ULONG ClusterCount; + ULONG WindowRelativeHint; +#if DBG +#ifndef __REACTOS__ + ULONG i; +#endif + ULONG PreviousClear; +#endif + + PFAT_WINDOW Window; + BOOLEAN Wait; + BOOLEAN Result = TRUE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatAllocateDiskSpace\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + DebugTrace( 0, Dbg, " *ByteCount = %8lx\n", *ByteCount); + DebugTrace( 0, Dbg, " Mcb = %8lx\n", Mcb); + DebugTrace( 0, Dbg, " Hint = %8lx\n", AbsoluteClusterHint); + + ASSERT((AbsoluteClusterHint <= Vcb->AllocationSupport.NumberOfClusters + 2) && (1 != AbsoluteClusterHint)); + + // + // Make sure byte count is not zero + // + + if (*ByteCount == 0) { + + DebugTrace(0, Dbg, "Nothing to allocate.\n", 0); + + DebugTrace(-1, Dbg, "FatAllocateDiskSpace -> (VOID)\n", 0); + return; + } + + // + // Compute the cluster count based on the byte count, rounding up + // to the next cluster if there is any remainder. Note that the + // pathalogical case BytesCount == 0 has been eliminated above. + // + + LogOfBytesPerCluster = Vcb->AllocationSupport.LogOfBytesPerCluster; + BytesPerCluster = 1 << LogOfBytesPerCluster; + + *ByteCount = (*ByteCount + (BytesPerCluster - 1)) + & ~(BytesPerCluster - 1); + + // + // If ByteCount is NOW zero, then we were asked for the maximal + // filesize (or at least for bytes in the last allocatable sector). + // + + if (*ByteCount == 0) { + + *ByteCount = 0xffffffff; + ClusterCount = 1 << (32 - LogOfBytesPerCluster); + + } else { + + ClusterCount = (*ByteCount >> LogOfBytesPerCluster); + } + + // + // Make sure there are enough free clusters to start with, and + // take them now so that nobody else takes them from us. + // + + ExAcquireResourceSharedLite(&Vcb->ChangeBitMapResource, TRUE); + FatLockFreeClusterBitMap( Vcb ); + + if (ClusterCount <= Vcb->AllocationSupport.NumberOfFreeClusters) { + + Vcb->AllocationSupport.NumberOfFreeClusters -= ClusterCount; + + } else { + + FatUnlockFreeClusterBitMap( Vcb ); + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + + DebugTrace(0, Dbg, "Disk Full. Raise Status.\n", 0); + FatRaiseStatus( IrpContext, STATUS_DISK_FULL ); + } + + // + // Did the caller supply a hint? + // + + if ((0 != AbsoluteClusterHint) && (AbsoluteClusterHint < (Vcb->AllocationSupport.NumberOfClusters + 2))) { + + if (Vcb->NumberOfWindows > 1) { + + // + // If we're being called upon to allocate clusters outside the + // current window (which happens only via MoveFile), it's a problem. + // We address this by changing the current window to be the one which + // contains the alternate cluster hint. Note that if the user's + // request would cross a window boundary, he doesn't really get what + // he wanted. + // + + if (AbsoluteClusterHint < Vcb->CurrentWindow->FirstCluster || + AbsoluteClusterHint > Vcb->CurrentWindow->LastCluster) { + + ULONG BucketNum = FatWindowOfCluster( AbsoluteClusterHint ); + + ASSERT( BucketNum < Vcb->NumberOfWindows); + + // + // Drop our shared lock on the ChangeBitMapResource, and pick it up again + // exclusive in preparation for making the window swap. + // + + FatUnlockFreeClusterBitMap(Vcb); + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + ExAcquireResourceExclusiveLite(&Vcb->ChangeBitMapResource, TRUE); + FatLockFreeClusterBitMap(Vcb); + + Window = &Vcb->Windows[BucketNum]; + + // + // Again, test the current window against the one we want - some other + // thread could have sneaked in behind our backs and kindly set it to the one + // we need, when we dropped and reacquired the ChangeBitMapResource above. + // + + if (Window != Vcb->CurrentWindow) { + + _SEH2_TRY { + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Change to the new window (update Vcb->CurrentWindow) and scan it + // to build up a freespace bitmap etc. + // + + FatExamineFatEntries( IrpContext, Vcb, + 0, + 0, + FALSE, + Window, + NULL); + + } _SEH2_FINALLY { + + if (!Wait) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + if (_SEH2_AbnormalTermination()) { + + // + // We will have raised as a result of failing to pick up the + // chunk of the FAT for this window move. Release our resources + // and return the cluster count to the volume. + // + + Vcb->AllocationSupport.NumberOfFreeClusters += ClusterCount; + + FatUnlockFreeClusterBitMap( Vcb ); + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + } + } _SEH2_END; + } + } + + // + // Make the hint cluster number relative to the base of the current window... + // + // Currentwindow->Firstcluster is baised by +2 already, so we will lose the + // bias already in AbsoluteClusterHint. Put it back.... + // + + WindowRelativeHint = AbsoluteClusterHint - Vcb->CurrentWindow->FirstCluster + 2; + } + else { + + // + // Only one 'window', ie fat16/12. No modification necessary. + // + + WindowRelativeHint = AbsoluteClusterHint; + } + } + else { + + // + // Either no hint supplied, or it was out of range, so grab one from the Vcb + // + // NOTE: Clusterhint in the Vcb is not guaranteed to be set (may be -1) + // + + WindowRelativeHint = Vcb->ClusterHint; + AbsoluteClusterHint = 0; + + // + // Vcb hint may not have been initialized yet. Force to valid cluster. + // + + if (-1 == WindowRelativeHint) { + + WindowRelativeHint = 2; + } + } + + ASSERT((WindowRelativeHint >= 2) && (WindowRelativeHint < Vcb->FreeClusterBitMap.SizeOfBitMap + 2)); + + // + // Keep track of the window we're allocating from, so we can clean + // up correctly if the current window changes after we unlock the + // bitmap. + // + + Window = Vcb->CurrentWindow; + + // + // Try to find a run of free clusters large enough for us. + // + + StartingCluster = FatFindFreeClusterRun( IrpContext, + Vcb, + ClusterCount, + WindowRelativeHint ); + // + // If the above call was successful, we can just update the fat + // and Mcb and exit. Otherwise we have to look for smaller free + // runs. + // + // This test is a bit funky. Note that the error return from + // RtlFindClearBits is -1, and adding two to that is 1. + // + + if ((StartingCluster != 1) && + ((0 == AbsoluteClusterHint) || (StartingCluster == WindowRelativeHint)) + ) { + +#if DBG + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); +#endif // DBG + + // + // Take the clusters we found, and unlock the bit map. + // + + FatReserveClusters(IrpContext, Vcb, StartingCluster, ClusterCount); + + Window->ClustersFree -= ClusterCount; + + StartingCluster += Window->FirstCluster; + StartingCluster -= 2; + + ASSERT( PreviousClear - ClusterCount == Window->ClustersFree ); + + FatUnlockFreeClusterBitMap( Vcb ); + + // + // Note that this call will never fail since there is always + // room for one entry in an empty Mcb. + // + + FatAddMcbEntry( Vcb, Mcb, + 0, + FatGetLboFromIndex( Vcb, StartingCluster ), + *ByteCount); + _SEH2_TRY { + + // + // Update the fat. + // + + FatAllocateClusters(IrpContext, Vcb, + StartingCluster, + ClusterCount); + + } _SEH2_FINALLY { + + DebugUnwind( FatAllocateDiskSpace ); + + // + // If the allocate clusters failed, remove the run from the Mcb, + // unreserve the clusters, and reset the free cluster count. + // + + if (_SEH2_AbnormalTermination()) { + + FatRemoveMcbEntry( Vcb, Mcb, 0, *ByteCount ); + + FatLockFreeClusterBitMap( Vcb ); + + // Only clear bits if the bitmap window is the same. + + if (Window == Vcb->CurrentWindow) { + + // Both values (startingcluster and window->firstcluster) are + // already biased by 2, so will cancel, so we need to add in the 2 again. + + FatUnreserveClusters( IrpContext, Vcb, + StartingCluster - Window->FirstCluster + 2, + ClusterCount ); + } + + Window->ClustersFree += ClusterCount; + Vcb->AllocationSupport.NumberOfFreeClusters += ClusterCount; + + FatUnlockFreeClusterBitMap( Vcb ); + } + + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + } _SEH2_END; + + } else { + + // + // Note that Index is a zero-based window-relative number. When appropriate + // it'll get converted into a true cluster number and put in Cluster, which + // will be a volume relative true cluster number. + // + + ULONG Index; + ULONG Cluster; + ULONG CurrentVbo; + ULONG PriorLastCluster; + ULONG BytesFound; + + ULONG ClustersFound = 0; + ULONG ClustersRemaining; + + BOOLEAN LockedBitMap = FALSE; + BOOLEAN SelectNextContigWindow = FALSE; + + // + // Drop our shared lock on the ChangeBitMapResource, and pick it up again + // exclusive in preparation for making a window swap. + // + + FatUnlockFreeClusterBitMap(Vcb); + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + ExAcquireResourceExclusiveLite(&Vcb->ChangeBitMapResource, TRUE); + FatLockFreeClusterBitMap(Vcb); + LockedBitMap = TRUE; + + _SEH2_TRY { + + if ( ExactMatchRequired && (1 == Vcb->NumberOfWindows)) { + + // + // Give up right now, there are no more windows to search! RtlFindClearBits + // searchs the whole bitmap, so we would have found any contiguous run + // large enough. + // + + try_leave( Result = FALSE); + } + + // + // While the request is still incomplete, look for the largest + // run of free clusters, mark them taken, allocate the run in + // the Mcb and Fat, and if this isn't the first time through + // the loop link it to prior run on the fat. The Mcb will + // coalesce automatically. + // + + ClustersRemaining = ClusterCount; + CurrentVbo = 0; + PriorLastCluster = 0; + + while (ClustersRemaining != 0) { + + // + // If we just entered the loop, the bit map is already locked + // + + if ( !LockedBitMap ) { + + FatLockFreeClusterBitMap( Vcb ); + LockedBitMap = TRUE; + } + + // + // Find the largest run of free clusters. If the run is + // bigger than we need, only use what we need. Note that + // this will then be the last while() iteration. + // + + // 12/3/95: need to bias bitmap by 2 bits for the defrag + // hooks and the below macro became impossible to do without in-line + // procedures. + // + // ClustersFound = FatLongestFreeClusterRun( IrpContext, Vcb, &Index ); + + ClustersFound = 0; + + if (!SelectNextContigWindow) { + + if ( 0 != WindowRelativeHint) { + + ULONG Desired = Vcb->FreeClusterBitMap.SizeOfBitMap - (WindowRelativeHint - 2); + + // + // We will try to allocate contiguously. Try from the current hint the to + // end of current window. Don't try for more than we actually need. + // + + if (Desired > ClustersRemaining) { + + Desired = ClustersRemaining; + } + + if (RtlAreBitsClear( &Vcb->FreeClusterBitMap, + WindowRelativeHint - 2, + Desired)) + { + // + // Clusters from hint->...windowend are free. Take them. + // + + Index = WindowRelativeHint - 2; + ClustersFound = Desired; + + if (FatIsFat32(Vcb)) { + + // + // We're now up against the end of the current window, so indicate that we + // want the next window in the sequence next time around. (If we're not up + // against the end of the window, then we got what we needed and won't be + // coming around again anyway). + // + + SelectNextContigWindow = TRUE; + WindowRelativeHint = 2; + } + else { + + // + // FAT 12/16 - we've run up against the end of the volume. Clear the + // hint, since we now have no idea where to look. + // + + WindowRelativeHint = 0; + } +#if DBG + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); +#endif // DBG + } + else { + + if (ExactMatchRequired) { + + // + // If our caller required an exact match, then we're hosed. Bail out now. + // + + try_leave( Result = FALSE); + } + + // + // Hint failed, drop back to pot luck + // + + WindowRelativeHint = 0; + } + } + + if ((0 == WindowRelativeHint) && (0 == ClustersFound)) { + + if (ClustersRemaining <= Vcb->CurrentWindow->ClustersFree) { + + // + // The remaining allocation could be satisfied entirely from this + // window. We will ask only for what we need, to try and avoid + // unnecessarily fragmenting large runs of space by always using + // (part of) the largest run we can find. This call will return the + // first run large enough. + // + + Index = RtlFindClearBits( &Vcb->FreeClusterBitMap, ClustersRemaining, 0); + + if (-1 != Index) { + + ClustersFound = ClustersRemaining; + } + } + + if (0 == ClustersFound) { + + // + // Still nothing, so just take the largest free run we can find. + // + + ClustersFound = RtlFindLongestRunClear( &Vcb->FreeClusterBitMap, &Index ); + + } +#if DBG + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); +#endif // DBG + if (ClustersFound >= ClustersRemaining) { + + ClustersFound = ClustersRemaining; + } + else { + + // + // If we just ran up to the end of a window, set up a hint that + // we'd like the next consecutive window after this one. (FAT32 only) + // + + if ( ((Index + ClustersFound) == Vcb->FreeClusterBitMap.SizeOfBitMap) && + FatIsFat32( Vcb) + ) { + + SelectNextContigWindow = TRUE; + WindowRelativeHint = 2; + } + } + } + } + + if (ClustersFound == 0) { + + ULONG FaveWindow = 0; + BOOLEAN SelectedWindow; + + // + // If we found no free clusters on a single-window FAT, + // there was a bad problem with the free cluster count. + // + + if (1 == Vcb->NumberOfWindows) { + + FatBugCheck( 0, 5, 0 ); + } + + // + // Switch to a new bucket. Possibly the next one if we're + // currently on a roll (allocating contiguously) + // + + SelectedWindow = FALSE; + + if ( SelectNextContigWindow) { + + ULONG NextWindow; + + NextWindow = (((ULONG)((PUCHAR)Vcb->CurrentWindow - (PUCHAR)Vcb->Windows)) / sizeof( FAT_WINDOW)) + 1; + + if ((NextWindow < Vcb->NumberOfWindows) && + ( Vcb->Windows[ NextWindow].ClustersFree > 0) + ) { + + FaveWindow = NextWindow; + SelectedWindow = TRUE; + } + else { + + if (ExactMatchRequired) { + + // + // Some dope tried to allocate a run past the end of the volume... + // + + try_leave( Result = FALSE); + } + + // + // Give up on the contiguous allocation attempts + // + + WindowRelativeHint = 0; + } + + SelectNextContigWindow = FALSE; + } + + if (!SelectedWindow) { + + // + // Select a new window to begin allocating from + // + + FaveWindow = FatSelectBestWindow( Vcb); + } + + // + // By now we'd better have found a window with some free clusters + // + + if (0 == Vcb->Windows[ FaveWindow].ClustersFree) { + + FatBugCheck( 0, 5, 1 ); + } + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + FatExamineFatEntries( IrpContext, Vcb, + 0, + 0, + FALSE, + &Vcb->Windows[FaveWindow], + NULL); + + if (!Wait) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Now we'll just go around the loop again, having switched windows, + // and allocate.... + // +#if DBG + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); +#endif //DBG + } // if (clustersfound == 0) + else { + + // + // Take the clusters we found, convert our index to a cluster number + // and unlock the bit map. + // + + Window = Vcb->CurrentWindow; + + FatReserveClusters( IrpContext, Vcb, (Index + 2), ClustersFound ); + + Cluster = Index + Window->FirstCluster; + + Window->ClustersFree -= ClustersFound; + ASSERT( PreviousClear - ClustersFound == Window->ClustersFree ); + + FatUnlockFreeClusterBitMap( Vcb ); + LockedBitMap = FALSE; + + // + // Add the newly alloced run to the Mcb. + // + + BytesFound = ClustersFound << LogOfBytesPerCluster; + + FatAddMcbEntry( Vcb, Mcb, + CurrentVbo, + FatGetLboFromIndex( Vcb, Cluster ), + BytesFound ); + + // + // Connect the last allocated run with this one, and allocate + // this run on the Fat. + // + + if (PriorLastCluster != 0) { + + FatSetFatEntry( IrpContext, + Vcb, + PriorLastCluster, + (FAT_ENTRY)Cluster ); + } + + // + // Update the fat + // + + FatAllocateClusters( IrpContext, Vcb, Cluster, ClustersFound ); + + // + // Prepare for the next iteration. + // + + CurrentVbo += BytesFound; + ClustersRemaining -= ClustersFound; + PriorLastCluster = Cluster + ClustersFound - 1; + } + } // while (clustersremaining) + + } _SEH2_FINALLY { + + DebugUnwind( FatAllocateDiskSpace ); + + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + + // + // Is there any unwinding to do? + // + + if ( _SEH2_AbnormalTermination() || (FALSE == Result)) { + + // + // Flag to the caller that they're getting nothing + // + + *ByteCount = 0; + + // + // There are three places we could have taken this exception: + // when switching the window (FatExamineFatEntries), adding + // a found run to the Mcb (FatAddMcbEntry), or when writing + // the changes to the FAT (FatSetFatEntry). In the first case + // we don't have anything to unwind before deallocation, and + // can detect this by seeing if we have the ClusterBitmap + // mutex out. + + if (!LockedBitMap) { + + FatLockFreeClusterBitMap( Vcb ); + + // + // In these cases, we have the possiblity that the FAT + // window is still in place and we need to clear the bits. + // If the Mcb entry isn't there (we raised trying to add + // it), the effect of trying to remove it is a noop. + // + + if (Window == Vcb->CurrentWindow) { + + // + // Cluster reservation works on cluster 2 based window-relative + // numbers, so we must convert. The subtraction will lose the + // cluster 2 base, so bias the result. + // + + FatUnreserveClusters( IrpContext, Vcb, + (Cluster - Window->FirstCluster) + 2, + ClustersFound ); + } + + // + // Note that FatDeallocateDiskSpace will take care of adjusting + // to account for the entries in the Mcb. All we have to account + // for is the last run that didn't make it. + // + + Window->ClustersFree += ClustersFound; + Vcb->AllocationSupport.NumberOfFreeClusters += ClustersFound; + + FatUnlockFreeClusterBitMap( Vcb ); + + FatRemoveMcbEntry( Vcb, Mcb, CurrentVbo, BytesFound ); + + } else { + + // + // Just drop the mutex now - we didn't manage to do anything + // that needs to be backed out. + // + + FatUnlockFreeClusterBitMap( Vcb ); + } + + _SEH2_TRY { + + // + // Now we have tidied up, we are ready to just send the Mcb + // off to deallocate disk space + // + + FatDeallocateDiskSpace( IrpContext, Vcb, Mcb ); + + } _SEH2_FINALLY { + + // + // Now finally (really), remove all the entries from the mcb + // + + FatRemoveMcbEntry( Vcb, Mcb, 0, 0xFFFFFFFF ); + } _SEH2_END; + } + + DebugTrace(-1, Dbg, "FatAllocateDiskSpace -> (VOID)\n", 0); + + } _SEH2_END; // finally + } + + return; +} + + +VOID +FatDeallocateDiskSpace ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PLARGE_MCB Mcb + ) + +/*++ + +Routine Description: + + This procedure deallocates the disk space denoted by an input + mcb. Note that the input MCB does not need to necessarily describe + a chain that ends with a FAT_CLUSTER_LAST entry. + + Pictorially what is done is the following + + Fat |--a--|--b--|--c--| + Mcb |--a--|--b--|--c--| + + becomes + + Fat |--0--|--0--|--0--| + Mcb |--a--|--b--|--c--| + +Arguments: + + Vcb - Supplies the VCB being modified + + Mcb - Supplies the MCB describing the disk space to deallocate. Note + that Mcb is unchanged by this procedure. + + +Return Value: + + None. + +--*/ + +{ + LBO Lbo; + VBO Vbo; + + ULONG RunsInMcb; + ULONG ByteCount; + ULONG ClusterCount; + ULONG ClusterIndex; + ULONG McbIndex; + + UCHAR LogOfBytesPerCluster; + + PFAT_WINDOW Window; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeallocateDiskSpace\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + DebugTrace( 0, Dbg, " Mcb = %8lx\n", Mcb); + + LogOfBytesPerCluster = Vcb->AllocationSupport.LogOfBytesPerCluster; + + RunsInMcb = FsRtlNumberOfRunsInLargeMcb( Mcb ); + + if ( RunsInMcb == 0 ) { + + DebugTrace(-1, Dbg, "FatDeallocateDiskSpace -> (VOID)\n", 0); + return; + } + + _SEH2_TRY { + + // + // Run though the Mcb, freeing all the runs in the fat. + // + // We do this in two steps (first update the fat, then the bitmap + // (which can't fail)) to prevent other people from taking clusters + // that we need to re-allocate in the event of unwind. + // + + ExAcquireResourceSharedLite(&Vcb->ChangeBitMapResource, TRUE); + + RunsInMcb = FsRtlNumberOfRunsInLargeMcb( Mcb ); + + for ( McbIndex = 0; McbIndex < RunsInMcb; McbIndex++ ) { + + FatGetNextMcbEntry( Vcb, Mcb, McbIndex, &Vbo, &Lbo, &ByteCount ); + + // + // Assert that Fat files have no holes. + // + + ASSERT( Lbo != 0 ); + + // + // Write FAT_CLUSTER_AVAILABLE to each cluster in the run. + // + + ClusterCount = ByteCount >> LogOfBytesPerCluster; + ClusterIndex = FatGetIndexFromLbo( Vcb, Lbo ); + + FatFreeClusters( IrpContext, Vcb, ClusterIndex, ClusterCount ); + } + + // + // From now on, nothing can go wrong .... (as in raise) + // + + FatLockFreeClusterBitMap( Vcb ); + + for ( McbIndex = 0; McbIndex < RunsInMcb; McbIndex++ ) { + + ULONG ClusterEnd; + ULONG MyStart, MyLength, count; +#if DBG +#ifndef __REACTOS__ + ULONG PreviousClear, i; +#else + ULONG i; +#endif +#endif + + FatGetNextMcbEntry( Vcb, Mcb, McbIndex, &Vbo, &Lbo, &ByteCount ); + + // + // Mark the bits clear in the FreeClusterBitMap. + // + + ClusterCount = ByteCount >> LogOfBytesPerCluster; + ClusterIndex = FatGetIndexFromLbo( Vcb, Lbo ); + + Window = Vcb->CurrentWindow; + + // + // If we've divided the bitmap, elide bitmap manipulation for + // runs that are outside the current bucket. + // + + ClusterEnd = ClusterIndex + ClusterCount - 1; + + if (!(ClusterIndex > Window->LastCluster || + ClusterEnd < Window->FirstCluster)) { + + // + // The run being freed overlaps the current bucket, so we'll + // have to clear some bits. + // + + if (ClusterIndex < Window->FirstCluster && + ClusterEnd > Window->LastCluster) { + + MyStart = Window->FirstCluster; + MyLength = Window->LastCluster - Window->FirstCluster + 1; + + } else if (ClusterIndex < Window->FirstCluster) { + + MyStart = Window->FirstCluster; + MyLength = ClusterEnd - Window->FirstCluster + 1; + + } else { + + // + // The range being freed starts in the bucket, and may possibly + // extend beyond the bucket. + // + + MyStart = ClusterIndex; + + if (ClusterEnd <= Window->LastCluster) { + + MyLength = ClusterCount; + + } else { + + MyLength = Window->LastCluster - ClusterIndex + 1; + } + } + + if (MyLength == 0) { + + continue; + } + +#if DBG +#ifndef __REACTOS__ + PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ); +#endif + + + // + // Verify that the Bits are all really set. + // + + ASSERT( MyStart + MyLength - Window->FirstCluster <= Vcb->FreeClusterBitMap.SizeOfBitMap ); + + for (i = 0; i < MyLength; i++) { + + ASSERT( RtlCheckBit(&Vcb->FreeClusterBitMap, + MyStart - Window->FirstCluster + i) == 1 ); + } +#endif // DBG + + FatUnreserveClusters( IrpContext, Vcb, + MyStart - Window->FirstCluster + 2, + MyLength ); + } + + // + // Adjust the ClustersFree count for each bitmap window, even the ones + // that are not the current window. + // + + if (FatIsFat32(Vcb)) { + + Window = &Vcb->Windows[FatWindowOfCluster( ClusterIndex )]; + + } else { + + Window = &Vcb->Windows[0]; + } + + MyStart = ClusterIndex; + + for (MyLength = ClusterCount; MyLength > 0; MyLength -= count) { + + count = FatMin(Window->LastCluster - MyStart + 1, MyLength); + Window->ClustersFree += count; + + // + // If this was not the last window this allocation spanned, + // advance to the next. + // + + if (MyLength != count) { + + Window++; + MyStart = Window->FirstCluster; + } + } + + // + // Deallocation is now complete. Adjust the free cluster count. + // + + Vcb->AllocationSupport.NumberOfFreeClusters += ClusterCount; + } + +#if DBG + if (Vcb->CurrentWindow->ClustersFree != + RtlNumberOfClearBits(&Vcb->FreeClusterBitMap)) { + + DbgPrint("%x vs %x\n", Vcb->CurrentWindow->ClustersFree, + RtlNumberOfClearBits(&Vcb->FreeClusterBitMap)); + + DbgPrint("%x for %x\n", ClusterIndex, ClusterCount); + } +#endif + + FatUnlockFreeClusterBitMap( Vcb ); + + + } _SEH2_FINALLY { + + DebugUnwind( FatDeallocateDiskSpace ); + + // + // Is there any unwinding to do? + // + + ExReleaseResourceLite(&Vcb->ChangeBitMapResource); + + if ( _SEH2_AbnormalTermination() ) { + + LBO Lbo; + VBO Vbo; + + ULONG Index; + ULONG Clusters; + ULONG FatIndex; + ULONG PriorLastIndex; + + // + // For each entry we already deallocated, reallocate it, + // chaining together as nessecary. Note that we continue + // up to and including the last "for" iteration even though + // the SetFatRun could not have been successful. This + // allows us a convienent way to re-link the final successful + // SetFatRun. + // + // It is possible that the reason we got here will prevent us + // from succeeding in this operation. + // + + PriorLastIndex = 0; + + for (Index = 0; Index <= McbIndex; Index++) { + + FatGetNextMcbEntry(Vcb, Mcb, Index, &Vbo, &Lbo, &ByteCount); + + FatIndex = FatGetIndexFromLbo( Vcb, Lbo ); + Clusters = ByteCount >> LogOfBytesPerCluster; + + // + // We must always restore the prior iteration's last + // entry, pointing it to the first cluster of this run. + // + + if (PriorLastIndex != 0) { + + FatSetFatEntry( IrpContext, + Vcb, + PriorLastIndex, + (FAT_ENTRY)FatIndex ); + } + + // + // If this is not the last entry (the one that failed) + // then reallocate the disk space on the fat. + // + + if ( Index < McbIndex ) { + + FatAllocateClusters(IrpContext, Vcb, FatIndex, Clusters); + + PriorLastIndex = FatIndex + Clusters - 1; + } + } + } + + DebugTrace(-1, Dbg, "FatDeallocateDiskSpace -> (VOID)\n", 0); + } _SEH2_END; + + return; +} + + +VOID +FatSplitAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PLARGE_MCB Mcb, + IN VBO SplitAtVbo, + OUT PLARGE_MCB RemainingMcb + ) + +/*++ + +Routine Description: + + This procedure takes a single mcb and splits its allocation into + two separate allocation units. The separation must only be done + on cluster boundaries, otherwise we bugcheck. + + On the disk this actually works by inserting a FAT_CLUSTER_LAST into + the last index of the first part being split out. + + Pictorially what is done is the following (where ! denotes the end of + the fat chain (i.e., FAT_CLUSTER_LAST)): + + + Mcb |--a--|--b--|--c--|--d--|--e--|--f--| + + ^ + SplitAtVbo ---------------------+ + + RemainingMcb (empty) + + becomes + + Mcb |--a--|--b--|--c--! + + + RemainingMcb |--d--|--e--|--f--| + +Arguments: + + Vcb - Supplies the VCB being modified + + Mcb - Supplies the MCB describing the allocation being split into + two parts. Upon return this Mcb now contains the first chain. + + SplitAtVbo - Supplies the VBO of the first byte for the second chain + that we creating. + + RemainingMcb - Receives the MCB describing the second chain of allocated + disk space. The caller passes in an initialized Mcb that + is filled in by this procedure STARTING AT VBO 0. + +Return Value: + + VOID - TRUE if the operation completed and FALSE if it had to + block but could not. + +--*/ + +{ + VBO SourceVbo; + VBO TargetVbo; + VBO DontCare; + + LBO Lbo; + + ULONG ByteCount; + ULONG BytesPerCluster; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSplitAllocation\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + DebugTrace( 0, Dbg, " Mcb = %8lx\n", Mcb); + DebugTrace( 0, Dbg, " SplitAtVbo = %8lx\n", SplitAtVbo); + DebugTrace( 0, Dbg, " RemainingMcb = %8lx\n", RemainingMcb); + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // Assert that the split point is cluster alligned + // + + ASSERT( (SplitAtVbo & (BytesPerCluster - 1)) == 0 ); + + // + // We should never be handed an empty source MCB and asked to split + // at a non zero point. + // + + ASSERT( !((0 != SplitAtVbo) && (0 == FsRtlNumberOfRunsInLargeMcb( Mcb)))); + + // + // Assert we were given an empty target Mcb. + // + + // + // This assert is commented out to avoid hitting in the Ea error + // path. In that case we will be using the same Mcb's to split the + // allocation that we used to merge them. The target Mcb will contain + // the runs that the split will attempt to insert. + // + // + // ASSERT( FsRtlNumberOfRunsInMcb( RemainingMcb ) == 0 ); + // + + _SEH2_TRY { + + // + // Move the runs after SplitAtVbo from the souce to the target + // + + SourceVbo = SplitAtVbo; + TargetVbo = 0; + + while (FatLookupMcbEntry(Vcb, Mcb, SourceVbo, &Lbo, &ByteCount, NULL)) { + + FatAddMcbEntry( Vcb, RemainingMcb, TargetVbo, Lbo, ByteCount ); + + FatRemoveMcbEntry( Vcb, Mcb, SourceVbo, ByteCount ); + + TargetVbo += ByteCount; + SourceVbo += ByteCount; + + // + // If SourceVbo overflows, we were actually snipping off the end + // of the maximal file ... and are now done. + // + + if (SourceVbo == 0) { + + break; + } + } + + // + // Mark the last pre-split cluster as a FAT_LAST_CLUSTER + // + + if ( SplitAtVbo != 0 ) { + + FatLookupLastMcbEntry( Vcb, Mcb, &DontCare, &Lbo, NULL ); + + FatSetFatEntry( IrpContext, + Vcb, + FatGetIndexFromLbo( Vcb, Lbo ), + FAT_CLUSTER_LAST ); + } + + } _SEH2_FINALLY { + + DebugUnwind( FatSplitAllocation ); + + // + // If we got an exception, we must glue back together the Mcbs + // + + if ( _SEH2_AbnormalTermination() ) { + + TargetVbo = SplitAtVbo; + SourceVbo = 0; + + while (FatLookupMcbEntry(Vcb, RemainingMcb, SourceVbo, &Lbo, &ByteCount, NULL)) { + + FatAddMcbEntry( Vcb, Mcb, TargetVbo, Lbo, ByteCount ); + + FatRemoveMcbEntry( Vcb, RemainingMcb, SourceVbo, ByteCount ); + + TargetVbo += ByteCount; + SourceVbo += ByteCount; + } + } + + DebugTrace(-1, Dbg, "FatSplitAllocation -> (VOID)\n", 0); + } _SEH2_END; + + return; +} + + +VOID +FatMergeAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PLARGE_MCB Mcb, + IN PLARGE_MCB SecondMcb + ) + +/*++ + +Routine Description: + + This routine takes two separate allocations described by two MCBs and + joins them together into one allocation. + + Pictorially what is done is the following (where ! denotes the end of + the fat chain (i.e., FAT_CLUSTER_LAST)): + + + Mcb |--a--|--b--|--c--! + + SecondMcb |--d--|--e--|--f--| + + becomes + + Mcb |--a--|--b--|--c--|--d--|--e--|--f--| + + SecondMcb |--d--|--e--|--f--| + + +Arguments: + + Vcb - Supplies the VCB being modified + + Mcb - Supplies the MCB of the first allocation that is being modified. + Upon return this Mcb will also describe the newly enlarged + allocation + + SecondMcb - Supplies the ZERO VBO BASED MCB of the second allocation + that is being appended to the first allocation. This + procedure leaves SecondMcb unchanged. + +Return Value: + + VOID - TRUE if the operation completed and FALSE if it had to + block but could not. + +--*/ + +{ + VBO SpliceVbo; + LBO SpliceLbo; + + VBO SourceVbo; + VBO TargetVbo; + + LBO Lbo; + + ULONG ByteCount; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatMergeAllocation\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + DebugTrace( 0, Dbg, " Mcb = %8lx\n", Mcb); + DebugTrace( 0, Dbg, " SecondMcb = %8lx\n", SecondMcb); + + _SEH2_TRY { + + // + // Append the runs from SecondMcb to Mcb + // + + (void)FatLookupLastMcbEntry( Vcb, Mcb, &SpliceVbo, &SpliceLbo, NULL ); + + SourceVbo = 0; + TargetVbo = SpliceVbo + 1; + + while (FatLookupMcbEntry(Vcb, SecondMcb, SourceVbo, &Lbo, &ByteCount, NULL)) { + + FatAddMcbEntry( Vcb, Mcb, TargetVbo, Lbo, ByteCount ); + + SourceVbo += ByteCount; + TargetVbo += ByteCount; + } + + // + // Link the last pre-merge cluster to the first cluster of SecondMcb + // + + FatLookupMcbEntry( Vcb, SecondMcb, 0, &Lbo, (PULONG)NULL, NULL ); + + FatSetFatEntry( IrpContext, + Vcb, + FatGetIndexFromLbo( Vcb, SpliceLbo ), + (FAT_ENTRY)FatGetIndexFromLbo( Vcb, Lbo ) ); + + } _SEH2_FINALLY { + + DebugUnwind( FatMergeAllocation ); + + // + // If we got an exception, we must remove the runs added to Mcb + // + + if ( _SEH2_AbnormalTermination() ) { + + ULONG CutLength; + + if ((CutLength = TargetVbo - (SpliceVbo + 1)) != 0) { + + FatRemoveMcbEntry( Vcb, Mcb, SpliceVbo + 1, CutLength); + } + } + + DebugTrace(-1, Dbg, "FatMergeAllocation -> (VOID)\n", 0); + } _SEH2_END; + + return; +} + + +// +// Internal support routine +// + +CLUSTER_TYPE +FatInterpretClusterType ( + IN PVCB Vcb, + IN FAT_ENTRY Entry + ) + +/*++ + +Routine Description: + + This procedure tells the caller how to interpret the input fat table + entry. It will indicate if the fat cluster is available, resereved, + bad, the last one, or the another fat index. This procedure can deal + with both 12 and 16 bit fat. + +Arguments: + + Vcb - Supplies the Vcb to examine, yields 12/16 bit info + + Entry - Supplies the fat entry to examine + +Return Value: + + CLUSTER_TYPE - Is the type of the input Fat entry + +--*/ + +{ + DebugTrace(+1, Dbg, "InterpretClusterType\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + DebugTrace( 0, Dbg, " Entry = %8lx\n", Entry); + + PAGED_CODE(); + + switch(Vcb->AllocationSupport.FatIndexBitSize ) { + case 32: + Entry &= FAT32_ENTRY_MASK; + break; + + case 12: + ASSERT( Entry <= 0xfff ); + if (Entry >= 0x0ff0) { + Entry |= 0x0FFFF000; + } + break; + + default: + case 16: + ASSERT( Entry <= 0xffff ); + if (Entry >= 0x0fff0) { + Entry |= 0x0FFF0000; + } + break; + } + + if (Entry == FAT_CLUSTER_AVAILABLE) { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterAvailable\n", 0); + + return FatClusterAvailable; + + } else if (Entry < FAT_CLUSTER_RESERVED) { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterNext\n", 0); + + return FatClusterNext; + + } else if (Entry < FAT_CLUSTER_BAD) { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterReserved\n", 0); + + return FatClusterReserved; + + } else if (Entry == FAT_CLUSTER_BAD) { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterBad\n", 0); + + return FatClusterBad; + + } else { + + DebugTrace(-1, Dbg, "FatInterpretClusterType -> FatClusterLast\n", 0); + + return FatClusterLast; + } +} + + +// +// Internal support routine +// + +VOID +FatLookupFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN OUT PULONG FatEntry, + IN OUT PFAT_ENUMERATION_CONTEXT Context + ) + +/*++ + +Routine Description: + + This routine takes an index into the fat and gives back the value + in the Fat at this index. At any given time, for a 16 bit fat, this + routine allows only one page per volume of the fat to be pinned in + memory. For a 12 bit bit fat, the entire fat (max 6k) is pinned. This + extra layer of caching makes the vast majority of requests very + fast. The context for this caching stored in a structure in the Vcb. + +Arguments: + + Vcb - Supplies the Vcb to examine, yields 12/16 bit info, + fat access context, etc. + + FatIndex - Supplies the fat index to examine. + + FatEntry - Receives the fat entry pointed to by FatIndex. Note that + it must point to non-paged pool. + + Context - This structure keeps track of a page of pinned fat between calls. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLookupFatEntry\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + DebugTrace( 0, Dbg, " FatIndex = %4x\n", FatIndex); + DebugTrace( 0, Dbg, " FatEntry = %8lx\n", FatEntry); + + // + // Make sure they gave us a valid fat index. + // + + FatVerifyIndexIsValid(IrpContext, Vcb, FatIndex); + + // + // Case on 12 or 16 bit fats. + // + // In the 12 bit case (mostly floppies) we always have the whole fat + // (max 6k bytes) pinned during allocation operations. This is possibly + // a wee bit slower, but saves headaches over fat entries with 8 bits + // on one page, and 4 bits on the next. + // + // The 16 bit case always keeps the last used page pinned until all + // operations are done and it is unpinned. + // + + // + // DEAL WITH 12 BIT CASE + // + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + + // + // Check to see if the fat is already pinned, otherwise pin it. + // + + if (Context->Bcb == NULL) { + + FatReadVolumeFile( IrpContext, + Vcb, + FatReservedBytes( &Vcb->Bpb ), + FatBytesPerFat( &Vcb->Bpb ), + &Context->Bcb, + &Context->PinnedPage ); + } + + // + // Load the return value. + // + + + FatLookup12BitEntry( Context->PinnedPage, FatIndex, FatEntry ); + + } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) { + + // + // DEAL WITH 32 BIT CASE + // + + ULONG PageEntryOffset; + ULONG OffsetIntoVolumeFile; + + // + // Initialize two local variables that help us. + // + OffsetIntoVolumeFile = FatReservedBytes(&Vcb->Bpb) + FatIndex * sizeof(FAT_ENTRY); + PageEntryOffset = (OffsetIntoVolumeFile % PAGE_SIZE) / sizeof(FAT_ENTRY); + + // + // Check to see if we need to read in a new page of fat + // + + if ((Context->Bcb == NULL) || + (OffsetIntoVolumeFile / PAGE_SIZE != Context->VboOfPinnedPage / PAGE_SIZE)) { + + // + // The entry wasn't in the pinned page, so must we unpin the current + // page (if any) and read in a new page. + // + + FatUnpinBcb( IrpContext, Context->Bcb ); + + FatReadVolumeFile( IrpContext, + Vcb, + OffsetIntoVolumeFile & ~(PAGE_SIZE - 1), + PAGE_SIZE, + &Context->Bcb, + &Context->PinnedPage ); + + Context->VboOfPinnedPage = OffsetIntoVolumeFile & ~(PAGE_SIZE - 1); + } + + // + // Grab the fat entry from the pinned page, and return + // + + *FatEntry = ((PULONG)(Context->PinnedPage))[PageEntryOffset] & FAT32_ENTRY_MASK; + + } else { + + // + // DEAL WITH 16 BIT CASE + // + + ULONG PageEntryOffset; + ULONG OffsetIntoVolumeFile; + + // + // Initialize two local variables that help us. + // + + OffsetIntoVolumeFile = FatReservedBytes(&Vcb->Bpb) + FatIndex * sizeof(USHORT); + PageEntryOffset = (OffsetIntoVolumeFile % PAGE_SIZE) / sizeof(USHORT); + + // + // Check to see if we need to read in a new page of fat + // + + if ((Context->Bcb == NULL) || + (OffsetIntoVolumeFile / PAGE_SIZE != Context->VboOfPinnedPage / PAGE_SIZE)) { + + // + // The entry wasn't in the pinned page, so must we unpin the current + // page (if any) and read in a new page. + // + + FatUnpinBcb( IrpContext, Context->Bcb ); + + FatReadVolumeFile( IrpContext, + Vcb, + OffsetIntoVolumeFile & ~(PAGE_SIZE - 1), + PAGE_SIZE, + &Context->Bcb, + &Context->PinnedPage ); + + Context->VboOfPinnedPage = OffsetIntoVolumeFile & ~(PAGE_SIZE - 1); + } + + // + // Grab the fat entry from the pinned page, and return + // + + *FatEntry = ((PUSHORT)(Context->PinnedPage))[PageEntryOffset]; + } + + DebugTrace(-1, Dbg, "FatLookupFatEntry -> (VOID)\n", 0); + return; +} + + +VOID +FatSetFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN FAT_ENTRY FatEntry + ) + +/*++ + +Routine Description: + + This routine takes an index into the fat and puts a value in the Fat + at this index. The routine special cases 12, 16 and 32 bit fats. In + all cases we go to the cache manager for a piece of the fat. + + We have a special form of this call for setting the DOS-style dirty bit. + Unlike the dirty bit in the boot sector, we do not go to special effort + to make sure that this hits the disk synchronously - if the system goes + down in the window between the dirty bit being set in the boot sector + and the FAT index zero dirty bit being lazy written, then life is tough. + + The only possible scenario is that Win9x may see what it thinks is a clean + volume that really isn't (hopefully Memphis will pay attention to our dirty + bit as well). The dirty bit will get out quickly, and if heavy activity is + occurring, then the dirty bit should actually be there virtually all of the + time since the act of cleaning the volume is the "rare" occurance. + + There are synchronization concerns that would crop up if we tried to make + this synchronous. This thread may already own the Bcb shared for the first + sector of the FAT (so we can't get it exclusive for a writethrough). This + would require some more serious replumbing to work around than I want to + consider at this time. + + We can and do, however, synchronously set the bit clean. + + At this point the reader should understand why the NT dirty bit is where it is. + +Arguments: + + Vcb - Supplies the Vcb to examine, yields 12/16/32 bit info, etc. + + FatIndex - Supplies the destination fat index. + + FatEntry - Supplies the source fat entry. + +--*/ + +{ + LBO Lbo; + PBCB Bcb = NULL; + ULONG SectorSize; + ULONG OffsetIntoVolumeFile; + ULONG WasWait = TRUE; + BOOLEAN RegularOperation = TRUE; + BOOLEAN CleaningOperation = FALSE; + BOOLEAN ReleaseMutex = FALSE; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetFatEntry\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + DebugTrace( 0, Dbg, " FatIndex = %4x\n", FatIndex); + DebugTrace( 0, Dbg, " FatEntry = %4x\n", FatEntry); + + // + // Make sure they gave us a valid fat index if this isn't the special + // clean-bit modifying call. + // + + if (FatIndex == FAT_DIRTY_BIT_INDEX) { + + // + // We are setting the clean bit state. Of course, we could + // have corruption that would cause us to try to fiddle the + // reserved index - we guard against this by having the + // special entry values use the reserved high 4 bits that + // we know that we'll never try to set. + // + + // + // We don't want to repin the FAT pages involved here. Just + // let the lazy writer hit them when it can. + // + + RegularOperation = FALSE; + + switch (FatEntry) { + case FAT_CLEAN_VOLUME: + FatEntry = FAT_CLEAN_ENTRY; + CleaningOperation = TRUE; + break; + + case FAT_DIRTY_VOLUME: + switch (Vcb->AllocationSupport.FatIndexBitSize) { + case 12: + FatEntry = FAT12_DIRTY_ENTRY; + break; + + case 32: + FatEntry = FAT32_DIRTY_ENTRY; + break; + + default: + FatEntry = FAT16_DIRTY_ENTRY; + break; + } + break; + + default: + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + break; + } + + // + // Disable dirtying semantics for the duration of this operation. Force this + // operation to wait for the duration. + // + + WasWait = FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_DISABLE_DIRTY ); + + } else { + + ASSERT( !(FatEntry & ~FAT32_ENTRY_MASK) ); + FatVerifyIndexIsValid(IrpContext, Vcb, FatIndex); + } + + // + // Set Sector Size + // + + SectorSize = 1 << Vcb->AllocationSupport.LogOfBytesPerSector; + + // + // Case on 12 or 16 bit fats. + // + // In the 12 bit case (mostly floppies) we always have the whole fat + // (max 6k bytes) pinned during allocation operations. This is possibly + // a wee bit slower, but saves headaches over fat entries with 8 bits + // on one page, and 4 bits on the next. + // + // In the 16 bit case we only read the page that we need to set the fat + // entry. + // + + // + // DEAL WITH 12 BIT CASE + // + + _SEH2_TRY { + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + + PVOID PinnedFat; + + // + // Make sure we have a valid entry + // + + FatEntry &= 0xfff; + + // + // We read in the entire fat. Note that using prepare write marks + // the bcb pre-dirty, so we don't have to do it explicitly. + // + + OffsetIntoVolumeFile = FatReservedBytes( &Vcb->Bpb ) + FatIndex * 3 / 2; + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + FatReservedBytes( &Vcb->Bpb ), + FatBytesPerFat( &Vcb->Bpb ), + &Bcb, + &PinnedFat, + RegularOperation, + FALSE ); + + // + // Mark the sector(s) dirty in the DirtyFatMcb. This call is + // complicated somewhat for the 12 bit case since a single + // entry write can span two sectors (and pages). + // + // Get the Lbo for the sector where the entry starts, and add it to + // the dirty fat Mcb. + // + + Lbo = OffsetIntoVolumeFile & ~(SectorSize - 1); + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize); + + // + // If the entry started on the last byte of the sector, it continues + // to the next sector, so mark the next sector dirty as well. + // + // Note that this entry will simply coalese with the last entry, + // so this operation cannot fail. Also if we get this far, we have + // made it, so no unwinding will be needed. + // + + if ( (OffsetIntoVolumeFile & (SectorSize - 1)) == (SectorSize - 1) ) { + + Lbo += SectorSize; + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize ); + } + + // + // Store the entry into the fat; we need a little synchonization + // here and can't use a spinlock since the bytes might not be + // resident. + // + + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; + + FatSet12BitEntry( PinnedFat, FatIndex, FatEntry ); + + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; + + } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) { + + // + // DEAL WITH 32 BIT CASE + // + + PULONG PinnedFatEntry32; + + // + // Read in a new page of fat + // + + OffsetIntoVolumeFile = FatReservedBytes( &Vcb->Bpb ) + + FatIndex * sizeof( FAT_ENTRY ); + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + OffsetIntoVolumeFile, + sizeof(FAT_ENTRY), + &Bcb, + (PVOID *)&PinnedFatEntry32, + RegularOperation, + FALSE ); + // + // Mark the sector dirty in the DirtyFatMcb + // + + Lbo = OffsetIntoVolumeFile & ~(SectorSize - 1); + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize); + + // + // Store the FatEntry to the pinned page. + // + // Preserve the reserved bits in FAT32 entries in the file heap. + // + +#ifdef ALPHA + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; +#endif // ALPHA + + if (FatIndex != FAT_DIRTY_BIT_INDEX) { + + *PinnedFatEntry32 = ((*PinnedFatEntry32 & ~FAT32_ENTRY_MASK) | FatEntry); + + } else { + + *PinnedFatEntry32 = FatEntry; + } + +#ifdef ALPHA + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; +#endif // ALPHA + + } else { + + // + // DEAL WITH 16 BIT CASE + // + + PUSHORT PinnedFatEntry; + + // + // Read in a new page of fat + // + + OffsetIntoVolumeFile = FatReservedBytes( &Vcb->Bpb ) + + FatIndex * sizeof(USHORT); + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + OffsetIntoVolumeFile, + sizeof(USHORT), + &Bcb, + (PVOID *)&PinnedFatEntry, + RegularOperation, + FALSE ); + // + // Mark the sector dirty in the DirtyFatMcb + // + + Lbo = OffsetIntoVolumeFile & ~(SectorSize - 1); + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize); + + // + // Store the FatEntry to the pinned page. + // + // We need extra synchronization here for broken architectures + // like the ALPHA that don't support atomic 16 bit writes. + // + +#ifdef ALPHA + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; +#endif // ALPHA + + *PinnedFatEntry = (USHORT)FatEntry; + +#ifdef ALPHA + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; +#endif // ALPHA + } + + } _SEH2_FINALLY { + + DebugUnwind( FatSetFatEntry ); + + // + // Re-enable volume dirtying in case this was a dirty bit operation. + // + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_DIRTY ); + + // + // Make this operation asynchronous again if needed. + // + + if (!WasWait) { + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + } + + // + // If we still somehow have the Mutex, release it. + // + + if (ReleaseMutex) { + + ASSERT( _SEH2_AbnormalTermination() ); + + FatUnlockFreeClusterBitMap( Vcb ); + } + + // + // Unpin the Bcb. For cleaning operations, we make this write-through. + // + + if (CleaningOperation && Bcb) { + + IO_STATUS_BLOCK IgnoreStatus; + + CcRepinBcb( Bcb ); + CcUnpinData( Bcb ); + DbgDoit( IrpContext->PinCount -= 1 ); + CcUnpinRepinnedBcb( Bcb, TRUE, &IgnoreStatus ); + + } else { + + FatUnpinBcb(IrpContext, Bcb); + } + + DebugTrace(-1, Dbg, "FatSetFatEntry -> (VOID)\n", 0); + } _SEH2_END; + + return; +} + + +// +// Internal support routine +// + +VOID +FatSetFatRun ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG StartingFatIndex, + IN ULONG ClusterCount, + IN BOOLEAN ChainTogether + ) + +/*++ + +Routine Description: + + This routine sets a continuous run of clusters in the fat. If ChainTogether + is TRUE, then the clusters are linked together as in normal Fat fasion, + with the last cluster receiving FAT_CLUSTER_LAST. If ChainTogether is + FALSE, all the entries are set to FAT_CLUSTER_AVAILABLE, effectively + freeing all the clusters in the run. + +Arguments: + + Vcb - Supplies the Vcb to examine, yields 12/16 bit info, etc. + + StartingFatIndex - Supplies the destination fat index. + + ClusterCount - Supplies the number of contiguous clusters to work on. + + ChainTogether - Tells us whether to fill the entries with links, or + FAT_CLUSTER_AVAILABLE + + +Return Value: + + VOID + +--*/ + +{ +#define MAXCOUNTCLUS 0x10000 +#define COUNTSAVEDBCBS ((MAXCOUNTCLUS * sizeof(FAT_ENTRY) / PAGE_SIZE) + 2) + PBCB SavedBcbs[COUNTSAVEDBCBS][2]; + + ULONG SectorSize; + ULONG Cluster; + + LBO StartSectorLbo; + LBO FinalSectorLbo; + LBO Lbo; + + PVOID PinnedFat; + +#ifndef __REACTOS__ + ULONG StartingPage; +#endif + + BOOLEAN ReleaseMutex = FALSE; + + ULONG SavedStartingFatIndex = StartingFatIndex; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatSetFatRun\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + DebugTrace( 0, Dbg, " StartingFatIndex = %8x\n", StartingFatIndex); + DebugTrace( 0, Dbg, " ClusterCount = %8lx\n", ClusterCount); + DebugTrace( 0, Dbg, " ChainTogether = %s\n", ChainTogether ? "TRUE":"FALSE"); + + // + // Make sure they gave us a valid fat run. + // + + FatVerifyIndexIsValid(IrpContext, Vcb, StartingFatIndex); + FatVerifyIndexIsValid(IrpContext, Vcb, StartingFatIndex + ClusterCount - 1); + + // + // Check special case + // + + if (ClusterCount == 0) { + + DebugTrace(-1, Dbg, "FatSetFatRun -> (VOID)\n", 0); + return; + } + + // + // Set Sector Size + // + + SectorSize = 1 << Vcb->AllocationSupport.LogOfBytesPerSector; + + // + // Case on 12 or 16 bit fats. + // + // In the 12 bit case (mostly floppies) we always have the whole fat + // (max 6k bytes) pinned during allocation operations. This is possibly + // a wee bit slower, but saves headaches over fat entries with 8 bits + // on one page, and 4 bits on the next. + // + // In the 16 bit case we only read one page at a time, as needed. + // + + // + // DEAL WITH 12 BIT CASE + // + + _SEH2_TRY { + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + +#ifndef __REACTOS__ + StartingPage = 0; +#endif + + // + // We read in the entire fat. Note that using prepare write marks + // the bcb pre-dirty, so we don't have to do it explicitly. + // + + RtlZeroMemory( &SavedBcbs[0], 2 * sizeof(PBCB) * 2); + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + FatReservedBytes( &Vcb->Bpb ), + FatBytesPerFat( &Vcb->Bpb ), + &SavedBcbs[0][0], + &PinnedFat, + TRUE, + FALSE ); + + // + // Mark the affected sectors dirty. Note that FinalSectorLbo is + // the Lbo of the END of the entry (Thus * 3 + 2). This makes sure + // we catch the case of a dirty fat entry straddling a sector boundry. + // + // Note that if the first AddMcbEntry succeeds, all following ones + // will simply coalese, and thus also succeed. + // + + StartSectorLbo = (FatReservedBytes( &Vcb->Bpb ) + StartingFatIndex * 3 / 2) + & ~(SectorSize - 1); + + FinalSectorLbo = (FatReservedBytes( &Vcb->Bpb ) + ((StartingFatIndex + + ClusterCount) * 3 + 2) / 2) & ~(SectorSize - 1); + + for (Lbo = StartSectorLbo; Lbo <= FinalSectorLbo; Lbo += SectorSize) { + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize ); + } + + // + // Store the entries into the fat; we need a little + // synchonization here and can't use a spinlock since the bytes + // might not be resident. + // + + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; + + for (Cluster = StartingFatIndex; + Cluster < StartingFatIndex + ClusterCount - 1; + Cluster++) { + + FatSet12BitEntry( PinnedFat, + Cluster, + ChainTogether ? Cluster + 1 : FAT_CLUSTER_AVAILABLE ); + } + + // + // Save the last entry + // + + FatSet12BitEntry( PinnedFat, + Cluster, + ChainTogether ? + FAT_CLUSTER_LAST & 0xfff : FAT_CLUSTER_AVAILABLE ); + + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; + + } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) { + + // + // DEAL WITH 32 BIT CASE + // + + for (;;) { + + VBO StartOffsetInVolume; + VBO FinalOffsetInVolume; + + ULONG Page; + ULONG FinalCluster; + PULONG FatEntry; + ULONG ClusterCountThisRun; + + StartOffsetInVolume = FatReservedBytes(&Vcb->Bpb) + + StartingFatIndex * sizeof(FAT_ENTRY); + + if (ClusterCount > MAXCOUNTCLUS) { + ClusterCountThisRun = MAXCOUNTCLUS; + } else { + ClusterCountThisRun = ClusterCount; + } + + FinalOffsetInVolume = StartOffsetInVolume + + (ClusterCountThisRun - 1) * sizeof(FAT_ENTRY); + +#ifndef __REACTOS__ + StartingPage = StartOffsetInVolume / PAGE_SIZE; +#endif + + { + ULONG NumberOfPages; + ULONG Offset; + + NumberOfPages = (FinalOffsetInVolume / PAGE_SIZE) - + (StartOffsetInVolume / PAGE_SIZE) + 1; + + RtlZeroMemory( &SavedBcbs[0][0], (NumberOfPages + 1) * sizeof(PBCB) * 2 ); + + for ( Page = 0, Offset = StartOffsetInVolume & ~(PAGE_SIZE - 1); + Page < NumberOfPages; + Page++, Offset += PAGE_SIZE ) { + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + Offset, + PAGE_SIZE, + &SavedBcbs[Page][0], + (PVOID *)&SavedBcbs[Page][1], + TRUE, + FALSE ); + + if (Page == 0) { + + FatEntry = (PULONG)((PUCHAR)SavedBcbs[0][1] + + (StartOffsetInVolume % PAGE_SIZE)); + } + } + } + + // + // Mark the run dirty + // + + StartSectorLbo = StartOffsetInVolume & ~(SectorSize - 1); + FinalSectorLbo = FinalOffsetInVolume & ~(SectorSize - 1); + + for (Lbo = StartSectorLbo; Lbo <= FinalSectorLbo; Lbo += SectorSize) { + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO)Lbo, Lbo, SectorSize ); + } + + // + // Store the entries + // + // We need extra synchronization here for broken architectures + // like the ALPHA that don't support atomic 16 bit writes. + // + +#ifdef ALPHA + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; +#endif // ALPHA + + FinalCluster = StartingFatIndex + ClusterCountThisRun - 1; + Page = 0; + + for (Cluster = StartingFatIndex; + Cluster <= FinalCluster; + Cluster++, FatEntry++) { + + // + // If we just crossed a page boundry (as opposed to starting + // on one), update our idea of FatEntry. + + if ( (((ULONG_PTR)FatEntry & (PAGE_SIZE-1)) == 0) && + (Cluster != StartingFatIndex) ) { + + Page += 1; + FatEntry = (PULONG)SavedBcbs[Page][1]; + } + + *FatEntry = ChainTogether ? (FAT_ENTRY)(Cluster + 1) : + FAT_CLUSTER_AVAILABLE; + } + + // + // Fix up the last entry if we were chaining together + // + + if ((ClusterCount <= MAXCOUNTCLUS) && + ChainTogether ) { + + *(FatEntry-1) = FAT_CLUSTER_LAST; + } + +#ifdef ALPHA + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; +#endif // ALPHA + + { + ULONG i = 0; + // + // Unpin the Bcbs + // + + while ( SavedBcbs[i][0] != NULL ) { + + FatUnpinBcb( IrpContext, SavedBcbs[i][0] ); + SavedBcbs[i][0] = NULL; + + i += 1; + } + } + + if (ClusterCount <= MAXCOUNTCLUS) { + + break; + + } else { + + StartingFatIndex += MAXCOUNTCLUS; + ClusterCount -= MAXCOUNTCLUS; + } + } + + } else { + + // + // DEAL WITH 16 BIT CASE + // + + VBO StartOffsetInVolume; + VBO FinalOffsetInVolume; + + ULONG Page; + ULONG FinalCluster; + PUSHORT FatEntry; + + StartOffsetInVolume = FatReservedBytes(&Vcb->Bpb) + + StartingFatIndex * sizeof(USHORT); + + FinalOffsetInVolume = StartOffsetInVolume + + (ClusterCount - 1) * sizeof(USHORT); + +#ifndef __REACTOS__ + StartingPage = StartOffsetInVolume / PAGE_SIZE; +#endif + + // + // Read in one page of fat at a time. We cannot read in the + // all of the fat we need because of cache manager limitations. + // + // SavedBcb was initialized to be able to hold the largest + // possible number of pages in a fat plus and extra one to + // accomadate the boot sector, plus one more to make sure there + // is enough room for the RtlZeroMemory below that needs the mark + // the first Bcb after all the ones we will use as an end marker. + // + + { + ULONG NumberOfPages; + ULONG Offset; + + NumberOfPages = (FinalOffsetInVolume / PAGE_SIZE) - + (StartOffsetInVolume / PAGE_SIZE) + 1; + + RtlZeroMemory( &SavedBcbs[0][0], (NumberOfPages + 1) * sizeof(PBCB) * 2 ); + + for ( Page = 0, Offset = StartOffsetInVolume & ~(PAGE_SIZE - 1); + Page < NumberOfPages; + Page++, Offset += PAGE_SIZE ) { + + FatPrepareWriteVolumeFile( IrpContext, + Vcb, + Offset, + PAGE_SIZE, + &SavedBcbs[Page][0], + (PVOID *)&SavedBcbs[Page][1], + TRUE, + FALSE ); + + if (Page == 0) { + + FatEntry = (PUSHORT)((PUCHAR)SavedBcbs[0][1] + + (StartOffsetInVolume % PAGE_SIZE)); + } + } + } + + // + // Mark the run dirty + // + + StartSectorLbo = StartOffsetInVolume & ~(SectorSize - 1); + FinalSectorLbo = FinalOffsetInVolume & ~(SectorSize - 1); + + for (Lbo = StartSectorLbo; Lbo <= FinalSectorLbo; Lbo += SectorSize) { + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, (VBO) Lbo, Lbo, SectorSize ); + } + + // + // Store the entries + // + // We need extra synchronization here for broken architectures + // like the ALPHA that don't support atomic 16 bit writes. + // + +#ifdef ALPHA + FatLockFreeClusterBitMap( Vcb ); + ReleaseMutex = TRUE; +#endif // ALPHA + + FinalCluster = StartingFatIndex + ClusterCount - 1; + Page = 0; + + for (Cluster = StartingFatIndex; + Cluster <= FinalCluster; + Cluster++, FatEntry++) { + + // + // If we just crossed a page boundry (as opposed to starting + // on one), update our idea of FatEntry. + + if ( (((ULONG_PTR)FatEntry & (PAGE_SIZE-1)) == 0) && + (Cluster != StartingFatIndex) ) { + + Page += 1; + FatEntry = (PUSHORT)SavedBcbs[Page][1]; + } + + *FatEntry = (USHORT) (ChainTogether ? (FAT_ENTRY)(Cluster + 1) : + FAT_CLUSTER_AVAILABLE); + } + + // + // Fix up the last entry if we were chaining together + // + + if ( ChainTogether ) { + + *(FatEntry-1) = (USHORT)FAT_CLUSTER_LAST; + } +#ifdef ALPHA + FatUnlockFreeClusterBitMap( Vcb ); + ReleaseMutex = FALSE; +#endif // ALPHA + } + + } _SEH2_FINALLY { + + ULONG i = 0; + + DebugUnwind( FatSetFatRun ); + + // + // If we still somehow have the Mutex, release it. + // + + if (ReleaseMutex) { + + ASSERT( _SEH2_AbnormalTermination() ); + + FatUnlockFreeClusterBitMap( Vcb ); + } + + // + // Unpin the Bcbs + // + + while ( SavedBcbs[i][0] != NULL ) { + + FatUnpinBcb( IrpContext, SavedBcbs[i][0] ); + + i += 1; + } + + // + // At this point nothing in this finally clause should have raised. + // So, now comes the unsafe (sigh) stuff. + // + + if ( _SEH2_AbnormalTermination() && + (Vcb->AllocationSupport.FatIndexBitSize == 32) ) { + + // + // Fat32 unwind + // + // This case is more complex because the FAT12 and FAT16 cases + // pin all the needed FAT pages (128K max), after which it + // can't fail, before changing any FAT entries. In the Fat32 + // case, it may not be practical to pin all the needed FAT + // pages, because that could span many megabytes. So Fat32 + // attacks in chunks, and if a failure occurs once the first + // chunk has been updated, we have to back out the updates. + // + // The unwind consists of walking back over each FAT entry we + // have changed, setting it back to the previous value. Note + // that the previous value with either be FAT_CLUSTER_AVAILABLE + // (if ChainTogether==TRUE) or a simple link to the successor + // (if ChainTogether==FALSE). + // + // We concede that any one of these calls could fail too; our + // objective is to make this case no more likely than the case + // for a file consisting of multiple disjoint runs. + // + + while ( StartingFatIndex > SavedStartingFatIndex ) { + + StartingFatIndex--; + + FatSetFatEntry( IrpContext, Vcb, StartingFatIndex, + ChainTogether ? + StartingFatIndex + 1 : FAT_CLUSTER_AVAILABLE ); + } + } + + DebugTrace(-1, Dbg, "FatSetFatRun -> (VOID)\n", 0); + } _SEH2_END; + + return; +} + + +// +// Internal support routine +// + +UCHAR +FatLogOf ( + IN ULONG Value + ) + +/*++ + +Routine Description: + + This routine just computes the base 2 log of an integer. It is only used + on objects that are know to be powers of two. + +Arguments: + + Value - The value to take the base 2 log of. + +Return Value: + + UCHAR - The base 2 log of Value. + +--*/ + +{ + UCHAR Log = 0; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "LogOf\n", 0); + DebugTrace( 0, Dbg, " Value = %8lx\n", Value); + + // + // Knock bits off until we we get a one at position 0 + // + + while ( (Value & 0xfffffffe) != 0 ) { + + Log++; + Value >>= 1; + } + + // + // If there was more than one bit set, the file system messed up, + // Bug Check. + // + + if (Value != 0x1) { + + DebugTrace( 0, Dbg, "Received non power of 2.\n", 0); + + FatBugCheck( Value, Log, 0 ); + } + + DebugTrace(-1, Dbg, "LogOf -> %8lx\n", Log); + + return Log; +} + + +VOID +FatExamineFatEntries( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG StartIndex OPTIONAL, + IN ULONG EndIndex OPTIONAL, + IN BOOLEAN SetupWindows, + IN PFAT_WINDOW SwitchToWindow OPTIONAL, + IN PULONG BitMapBuffer OPTIONAL + ) +/*++ + +Routine Description: + + This routine handles scanning a segment of the FAT into in-memory structures. + + There are three fundamental cases, with variations depending on the FAT type: + + 1) During volume setup, FatSetupAllocations + + 1a) for FAT12/16, read the FAT into our free clusterbitmap + 1b) for FAT32, perform the initial scan for window free cluster counts + + 2) Switching FAT32 windows on the fly during system operation + + 3) Reading arbitrary segments of the FAT for the purposes of the GetVolumeBitmap + call (only for FAT32) + + There really is too much going on in here. At some point this should be + substantially rewritten. + +Arguments: + + Vcb - Supplies the volume involved + + StartIndex - Supplies the starting cluster, ignored if SwitchToWindow supplied + + EndIndex - Supplies the ending cluster, ignored if SwitchToWindow supplied + + SetupWindows - Indicates if we are doing the initial FAT32 scan + + SwitchToWindow - Supplies the FAT window we are examining and will switch to + + BitMapBuffer - Supplies a specific bitmap to fill in, if not supplied we fill + in the volume free cluster bitmap if !SetupWindows + +Return Value: + + None. Lots of side effects. + +--*/ +{ + ULONG FatIndexBitSize; + ULONG Page; + ULONG Offset; + ULONG FatIndex; + FAT_ENTRY FatEntry = FAT_CLUSTER_AVAILABLE; + FAT_ENTRY FirstFatEntry = FAT_CLUSTER_AVAILABLE; + PUSHORT FatBuffer; + PVOID pv; + PBCB Bcb; + ULONG EntriesPerWindow; +#ifndef __REACTOS__ + ULONG BitIndex; +#endif + + ULONG ClustersThisRun; + ULONG StartIndexOfThisRun; + + PULONG FreeClusterCount = NULL; + + PFAT_WINDOW CurrentWindow = NULL; + + PVOID NewBitMapBuffer = NULL; + PRTL_BITMAP BitMap = NULL; + RTL_BITMAP PrivateBitMap; + + enum RunType { + FreeClusters, + AllocatedClusters, + UnknownClusters + } CurrentRun; + + PAGED_CODE(); + + // + // Now assert correct usage. + // + + FatIndexBitSize = Vcb->AllocationSupport.FatIndexBitSize; + + ASSERT( !(SetupWindows && (SwitchToWindow || BitMapBuffer))); + ASSERT( !(SetupWindows && FatIndexBitSize != 32)); + + if (Vcb->NumberOfWindows > 1) { + + // + // FAT32: Calculate the number of FAT entries covered by a window. This is + // equal to the number of bits in the freespace bitmap, the size of which + // is hardcoded. + // + + EntriesPerWindow = MAX_CLUSTER_BITMAP_SIZE; + + } else { + + EntriesPerWindow = Vcb->AllocationSupport.NumberOfClusters; + } + + // + // We will also fill in the cumulative count of free clusters for + // the entire volume. If this is not appropriate, NULL it out + // shortly. + // + + FreeClusterCount = &Vcb->AllocationSupport.NumberOfFreeClusters; + + if (SetupWindows) { + + ASSERT(BitMapBuffer == NULL); + + // + // In this case we're just supposed to scan the fat and set up + // the information regarding where the buckets fall and how many + // free clusters are in each. + // + // It is fine to monkey with the real windows, we must be able + // to do this to activate the volume. + // + + BitMap = NULL; + + CurrentWindow = &Vcb->Windows[0]; + CurrentWindow->FirstCluster = StartIndex; + CurrentWindow->ClustersFree = 0; + + // + // We always wish to calculate total free clusters when + // setting up the FAT windows. + // + + } else if (BitMapBuffer == NULL) { + + // + // We will be filling in the free cluster bitmap for the volume. + // Careful, we can raise out of here and be hopelessly hosed if + // we built this up in the main bitmap/window itself. + // + // For simplicity's sake, we'll do the swap for everyone. FAT32 + // provokes the need since we can't tolerate partial results + // when switching windows. + // + + ASSERT( SwitchToWindow ); + + CurrentWindow = SwitchToWindow; + StartIndex = CurrentWindow->FirstCluster; + EndIndex = CurrentWindow->LastCluster; + + BitMap = &PrivateBitMap; + NewBitMapBuffer = FsRtlAllocatePoolWithTag( PagedPool, + (EntriesPerWindow + 7) / 8, + TAG_FAT_BITMAP ); + + RtlInitializeBitMap( &PrivateBitMap, + NewBitMapBuffer, + EndIndex - StartIndex + 1); + + if (FatIndexBitSize == 32) { + + // + // We do not wish count total clusters here. + // + + FreeClusterCount = NULL; + + } + + } else { + + BitMap = &PrivateBitMap; + RtlInitializeBitMap(&PrivateBitMap, + BitMapBuffer, + EndIndex - StartIndex + 1); + + // + // We do not count total clusters here. + // + + FreeClusterCount = NULL; + } + + // + // Now, our start index better be in the file heap. + // + + ASSERT( StartIndex >= 2 ); + + // + // Pick up the initial chunk of the FAT and first entry. + // + + if (FatIndexBitSize == 12) { + + // + // We read in the entire fat in the 12 bit case. + // + + FatReadVolumeFile( IrpContext, + Vcb, + FatReservedBytes( &Vcb->Bpb ), + FatBytesPerFat( &Vcb->Bpb ), + &Bcb, + (PVOID *)&FatBuffer ); + + FatLookup12BitEntry(FatBuffer, 0, &FirstFatEntry); + + } else { + + // + // Read in one page of fat at a time. We cannot read in the + // all of the fat we need because of cache manager limitations. + // + + ULONG BytesPerEntry = FatIndexBitSize >> 3; +#ifndef __REACTOS__ + ULONG EntriesPerPage = PAGE_SIZE / BytesPerEntry; +#endif + + Page = (FatReservedBytes(&Vcb->Bpb) + StartIndex * BytesPerEntry) / PAGE_SIZE; + + Offset = Page * PAGE_SIZE; + + FatReadVolumeFile( IrpContext, + Vcb, + Offset, + PAGE_SIZE, + &Bcb, + &pv); + + if (FatIndexBitSize == 32) { + + + FatBuffer = (PUSHORT)((PUCHAR)pv + + (FatReservedBytes(&Vcb->Bpb) + StartIndex * BytesPerEntry) % + PAGE_SIZE); + + FirstFatEntry = *((PULONG)FatBuffer); + FirstFatEntry = FirstFatEntry & FAT32_ENTRY_MASK; + + } else { + + FatBuffer = (PUSHORT)((PUCHAR)pv + + FatReservedBytes(&Vcb->Bpb) % PAGE_SIZE) + 2; + + FirstFatEntry = *FatBuffer; + } + + } + + CurrentRun = (FirstFatEntry == FAT_CLUSTER_AVAILABLE) ? + FreeClusters : AllocatedClusters; + + StartIndexOfThisRun = StartIndex; + + _SEH2_TRY { + + for (FatIndex = StartIndex; FatIndex <= EndIndex; FatIndex++) { + + + if (FatIndexBitSize == 12) { + + FatLookup12BitEntry(FatBuffer, FatIndex, &FatEntry); + + } else { + + // + // If we are setting up the FAT32 windows and have stepped into a new + // bucket, finalize this one and move forward. + // + + if (SetupWindows && + FatIndex > StartIndex && + (FatIndex - 2) % EntriesPerWindow == 0) { + + CurrentWindow->LastCluster = FatIndex - 1; + + if (CurrentRun == FreeClusters) { + + // + // We must be counting clusters in order to modify the + // contents of the window. + // + + ASSERT( FreeClusterCount ); + + + ClustersThisRun = FatIndex - StartIndexOfThisRun; + CurrentWindow->ClustersFree += ClustersThisRun; + + if (FreeClusterCount) { + *FreeClusterCount += ClustersThisRun; + } + + } else { + + ASSERT(CurrentRun == AllocatedClusters); + + ClustersThisRun = FatIndex - StartIndexOfThisRun; + } + + StartIndexOfThisRun = FatIndex; + CurrentRun = UnknownClusters; + + CurrentWindow++; + CurrentWindow->ClustersFree = 0; + CurrentWindow->FirstCluster = FatIndex; + } + + // + // If we just stepped onto a new page, grab a new pointer. + // + + if (((ULONG_PTR)FatBuffer & (PAGE_SIZE - 1)) == 0) { + + FatUnpinBcb( IrpContext, Bcb ); + + Page++; + Offset += PAGE_SIZE; + + FatReadVolumeFile( IrpContext, + Vcb, + Offset, + PAGE_SIZE, + &Bcb, + &pv ); + + FatBuffer = (PUSHORT)pv; + } + + if (FatIndexBitSize == 32) { + +#ifndef __REACTOS__ + FatEntry = *((PULONG)FatBuffer)++; +#else + FatEntry = *FatBuffer; + FatBuffer += 1; +#endif + FatEntry = FatEntry & FAT32_ENTRY_MASK; + + } else { + + FatEntry = *FatBuffer; + FatBuffer += 1; + } + } + + if (CurrentRun == UnknownClusters) { + + CurrentRun = (FatEntry == FAT_CLUSTER_AVAILABLE) ? + FreeClusters : AllocatedClusters; + } + + // + // Are we switching from a free run to an allocated run? + // + + if (CurrentRun == FreeClusters && + FatEntry != FAT_CLUSTER_AVAILABLE) { + + ClustersThisRun = FatIndex - StartIndexOfThisRun; + + if (FreeClusterCount) { + + *FreeClusterCount += ClustersThisRun; + CurrentWindow->ClustersFree += ClustersThisRun; + } + + if (BitMap) { + + RtlClearBits( BitMap, + StartIndexOfThisRun - StartIndex, + ClustersThisRun ); + } + + CurrentRun = AllocatedClusters; + StartIndexOfThisRun = FatIndex; + } + + // + // Are we switching from an allocated run to a free run? + // + + if (CurrentRun == AllocatedClusters && + FatEntry == FAT_CLUSTER_AVAILABLE) { + + ClustersThisRun = FatIndex - StartIndexOfThisRun; + + if (BitMap) { + + RtlSetBits( BitMap, + StartIndexOfThisRun - StartIndex, + ClustersThisRun ); + } + + CurrentRun = FreeClusters; + StartIndexOfThisRun = FatIndex; + } + } + + // + // Now we have to record the final run we encountered + // + + ClustersThisRun = FatIndex - StartIndexOfThisRun; + + if (CurrentRun == FreeClusters) { + + if (FreeClusterCount) { + + *FreeClusterCount += ClustersThisRun; + CurrentWindow->ClustersFree += ClustersThisRun; + } + + if (BitMap) { + + RtlClearBits( BitMap, + StartIndexOfThisRun - StartIndex, + ClustersThisRun ); + } + + } else { + + if (BitMap) { + + RtlSetBits( BitMap, + StartIndexOfThisRun - StartIndex, + ClustersThisRun ); + } + } + + // + // And finish the last window if we are in setup. + // + + if (SetupWindows) { + + CurrentWindow->LastCluster = FatIndex - 1; + } + + // + // Now switch the active window if required. We've succesfully gotten everything + // nailed down. + // + // If we were tracking the free cluster count, this means we should update the + // window. This is the case of FAT12/16 initialization. + // + + if (SwitchToWindow) { + + if (Vcb->FreeClusterBitMap.Buffer) { + + ExFreePool( Vcb->FreeClusterBitMap.Buffer ); + } + + RtlInitializeBitMap( &Vcb->FreeClusterBitMap, + NewBitMapBuffer, + EndIndex - StartIndex + 1 ); + + NewBitMapBuffer = NULL; + + Vcb->CurrentWindow = SwitchToWindow; + Vcb->ClusterHint = -1; + + if (FreeClusterCount) { + + ASSERT( !SetupWindows ); + ASSERT( FatIndexBitSize != 32 ); + + Vcb->CurrentWindow->ClustersFree = *FreeClusterCount; + } + } + + // + // Make sure plausible things occured ... + // + + if (!SetupWindows && BitMapBuffer == NULL) { + + ASSERT_CURRENT_WINDOW_GOOD( Vcb ); + } + + ASSERT(Vcb->AllocationSupport.NumberOfFreeClusters <= Vcb->AllocationSupport.NumberOfClusters); + + } _SEH2_FINALLY { + + // + // Unpin the last bcb and drop the temporary bitmap buffer if it exists. + // + + FatUnpinBcb( IrpContext, Bcb); + + if (NewBitMapBuffer) { + + ExFreePool( NewBitMapBuffer ); + } + } _SEH2_END; +} + diff --git a/drivers/filesystems/fastfat_new/cachesup.c b/drivers/filesystems/fastfat_new/cachesup.c new file mode 100644 index 00000000000..48ab9c354fa --- /dev/null +++ b/drivers/filesystems/fastfat_new/cachesup.c @@ -0,0 +1,1814 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + cache.c + +Abstract: + + This module implements the cache management routines for the Fat + FSD and FSP, by calling the Common Cache Manager. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_CACHESUP) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_CACHESUP) + +#if DBG + +BOOLEAN +FatIsCurrentOperationSynchedForDcbTeardown ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ); + +#endif + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCloseEaFile) +#pragma alloc_text(PAGE, FatCompleteMdl) +#pragma alloc_text(PAGE, FatOpenDirectoryFile) +#pragma alloc_text(PAGE, FatOpenEaFile) +#pragma alloc_text(PAGE, FatPinMappedData) +#pragma alloc_text(PAGE, FatPrepareWriteDirectoryFile) +#pragma alloc_text(PAGE, FatPrepareWriteVolumeFile) +#pragma alloc_text(PAGE, FatReadDirectoryFile) +#pragma alloc_text(PAGE, FatReadVolumeFile) +#pragma alloc_text(PAGE, FatRepinBcb) +#pragma alloc_text(PAGE, FatSyncUninitializeCacheMap) +#pragma alloc_text(PAGE, FatUnpinRepinnedBcbs) +#pragma alloc_text(PAGE, FatZeroData) +#if DBG +#pragma alloc_text(PAGE, FatIsCurrentOperationSynchedForDcbTeardown) +#endif +#endif + + +VOID +FatReadVolumeFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer + ) + +/*++ + +Routine Description: + + This routine is called when the specified range of sectors is to be + read into the cache. In fat, the volume file only contains the boot + sector, reserved sectors, and the "fat(s)." Thus the volume file is + of fixed size and only extends up to (but not not including) the root + directory entry, and will never move or change size. + + The fat volume file is also peculiar in that, since it starts at the + logical beginning of the disk, Vbo == Lbo. + +Arguments: + + Vcb - Pointer to the VCB for the volume + + StartingVbo - The virtual offset of the first desired byte + + ByteCount - Number of bytes desired + + Bcb - Returns a pointer to the BCB which is valid until unpinned + + Buffer - Returns a pointer to the sectors, which is valid until unpinned + +--*/ + +{ + LARGE_INTEGER Vbo; + + PAGED_CODE(); + + // + // Check to see that all references are within the Bios Parameter Block + // or the fat(s). A special case is made when StartingVbo == 0 at + // mounting time since we do not know how big the fat is. + // + + ASSERT( ((StartingVbo == 0) || ((StartingVbo + ByteCount) <= (ULONG) + (FatRootDirectoryLbo( &Vcb->Bpb ) + PAGE_SIZE)))); + + DebugTrace(+1, Dbg, "FatReadVolumeFile\n", 0); + DebugTrace( 0, Dbg, "Vcb = %08lx\n", Vcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + + // + // Call the Cache manager to attempt the transfer. + // + + Vbo.QuadPart = StartingVbo; + + if (!CcMapData( Vcb->VirtualVolumeFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb, + Buffer )) { + + ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // Could not read the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + DbgDoit( IrpContext->PinCount += 1 ) + + DebugTrace(-1, Dbg, "FatReadVolumeFile -> VOID, *BCB = %08lx\n", *Bcb); + + return; +} + + +VOID +FatPrepareWriteVolumeFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + IN BOOLEAN Reversible, + IN BOOLEAN Zero + ) + +/*++ + +Routine Description: + + This routine first looks to see if the specified range of sectors, + is already in the cache. If so, it increments the BCB PinCount, + sets the BCB dirty, and returns with the location of the sectors. + + If the sectors are not in the cache and Wait is TRUE, it finds a + free BCB (potentially causing a flush), and clears out the entire + buffer. Once this is done, it increments the BCB PinCount, sets the + BCB dirty, and returns with the location of the sectors. + + If the sectors are not in the cache and Wait is FALSE, this routine + raises STATUS_CANT_WAIT. + +Arguments: + + Vcb - Pointer to the VCB for the volume + + StartingVbo - The virtual offset of the first byte to be written + + ByteCount - Number of bytes to be written + + Bcb - Returns a pointer to the BCB which is valid until unpinned + + Buffer - Returns a pointer to the sectors, which is valid until unpinned + + Reversible - Supplies TRUE if the specified range of modification should + be repinned so that the operation can be reversed in a controlled + fashion if errors are encountered. + + Zero - Supplies TRUE if the specified range of bytes should be zeroed + +--*/ + +{ + LARGE_INTEGER Vbo; + + PAGED_CODE(); + + // + // Check to see that all references are within the Bios Parameter Block + // or the fat(s). + // + + ASSERT( ((StartingVbo + ByteCount) <= (ULONG) + (FatRootDirectoryLbo( &Vcb->Bpb )))); + + DebugTrace(+1, Dbg, "FatPrepareWriteVolumeFile\n", 0); + DebugTrace( 0, Dbg, "Vcb = %08lx\n", Vcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", (ULONG)StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + DebugTrace( 0, Dbg, "Zero = %08lx\n", Zero); + + // + // Call the Cache manager to attempt the transfer. + // + + Vbo.QuadPart = StartingVbo; + + if (!CcPinRead( Vcb->VirtualVolumeFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb, + Buffer )) { + + ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // Could not read the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // This keeps the data pinned until we complete the request + // and writes the dirty bit through to the disk. + // + + DbgDoit( IrpContext->PinCount += 1 ) + + _SEH2_TRY { + + if (Zero) { + + RtlZeroMemory( *Buffer, ByteCount ); + } + + FatSetDirtyBcb( IrpContext, *Bcb, Vcb, Reversible ); + + } _SEH2_FINALLY { + + if (_SEH2_AbnormalTermination()) { + + FatUnpinBcb(IrpContext, *Bcb); + } + } _SEH2_END; + + DebugTrace(-1, Dbg, "FatPrepareWriteVolumeFile -> VOID, *Bcb = %08lx\n", *Bcb); + + return; +} + + +VOID +FatReadDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + IN BOOLEAN Pin, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + OUT PNTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine is called when the specified range of sectors is to be + read into the cache. If the desired range falls beyond the current + cache mapping, the fat will be searched, and if the desired range can + be satisfied, the cache mapping will be extended and the MCB updated + accordingly. + +Arguments: + + Dcb - Pointer to the DCB for the directory + + StartingVbo - The virtual offset of the first desired byte + + ByteCount - Number of bytes desired + + Pin - Tells us if we should pin instead of just mapping. + + Bcb - Returns a pointer to the BCB which is valid until unpinned + + Buffer - Returns a pointer to the sectors, which is valid until unpinned + + Status - Returns the status of the operation. + +--*/ + +{ + LARGE_INTEGER Vbo; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatReadDirectoryFile\n", 0); + DebugTrace( 0, Dbg, "Dcb = %08lx\n", Dcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + + // + // Check for the zero case + // + + if (ByteCount == 0) { + + DebugTrace(0, Dbg, "Nothing to read\n", 0); + + *Bcb = NULL; + *Buffer = NULL; + *Status = STATUS_SUCCESS; + + DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID\n", 0); + return; + } + + // + // If we need to create a directory file and initialize the + // cachemap, do so. + // + + FatOpenDirectoryFile( IrpContext, Dcb ); + + // + // Now if the transfer is beyond the allocation size return EOF. + // + + if (StartingVbo >= Dcb->Header.AllocationSize.LowPart) { + + DebugTrace(0, Dbg, "End of file read for directory\n", 0); + + *Bcb = NULL; + *Buffer = NULL; + *Status = STATUS_END_OF_FILE; + + DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID\n", 0); + return; + } + + // + // If the caller is trying to read past the EOF, truncate the + // read. + // + + ByteCount = (Dcb->Header.AllocationSize.LowPart - StartingVbo < ByteCount) ? + Dcb->Header.AllocationSize.LowPart - StartingVbo : ByteCount; + + ASSERT( ByteCount != 0 ); + + // + // Call the Cache manager to attempt the transfer. + // + + Vbo.QuadPart = StartingVbo; + + if (Pin ? + + !CcPinRead( Dcb->Specific.Dcb.DirectoryFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb, + Buffer ) + : + + !CcMapData( Dcb->Specific.Dcb.DirectoryFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb, + Buffer ) ) { + + // + // Could not read the data without waiting (cache miss). + // + + *Bcb = NULL; + *Buffer = NULL; + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + DbgDoit( IrpContext->PinCount += 1 ) + + *Status = STATUS_SUCCESS; + + DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID, *BCB = %08lx\n", *Bcb); + + return; +} + + +VOID +FatPrepareWriteDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + IN BOOLEAN Zero, + IN BOOLEAN Reversible, + OUT PNTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine first looks to see if the specified range of sectors + is already in the cache. If so, it increments the BCB PinCount, + sets the BCB dirty, and returns TRUE with the location of the sectors. + + The IrpContext->Flags .. Wait == TRUE/FALSE actions of this routine are identical to + FatPrepareWriteVolumeFile() above. + +Arguments: + + Dcb - Pointer to the DCB for the directory + + StartingVbo - The virtual offset of the first byte to be written + + ByteCount - Number of bytes to be written + + Bcb - Returns a pointer to the BCB which is valid until unpinned + + Buffer - Returns a pointer to the sectors, which is valid until unpinned + + Zero - Supplies TRUE if the specified range of bytes should be zeroed + + Reversible - Supplies TRUE if the specified range of modification should + be repinned so that the operation can be reversed in a controlled + fashion if errors are encountered. + + Status - Returns the status of the operation. + +--*/ + +{ + LARGE_INTEGER Vbo; + ULONG InitialAllocation; + BOOLEAN UnwindWeAllocatedDiskSpace = FALSE; + ULONG ClusterSize; + + PVOID LocalBuffer; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatPrepareWriteDirectoryFile\n", 0); + DebugTrace( 0, Dbg, "Dcb = %08lx\n", Dcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", (ULONG)StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + DebugTrace( 0, Dbg, "Zero = %08lx\n", Zero); + + *Bcb = NULL; + *Buffer = NULL; + + // + // If we need to create a directory file and initialize the + // cachemap, do so. + // + + FatOpenDirectoryFile( IrpContext, Dcb ); + + // + // If the transfer is beyond the allocation size we need to + // extend the directory's allocation. The call to + // AddFileAllocation will raise a condition if + // it runs out of disk space. Note that the root directory + // cannot be extended. + // + + Vbo.QuadPart = StartingVbo; + + _SEH2_TRY { + + if (StartingVbo + ByteCount > Dcb->Header.AllocationSize.LowPart) { + + if (NodeType(Dcb) == FAT_NTC_ROOT_DCB && + !FatIsFat32(Dcb->Vcb)) { + + FatRaiseStatus( IrpContext, STATUS_DISK_FULL ); + } + + DebugTrace(0, Dbg, "Try extending normal directory\n", 0); + + InitialAllocation = Dcb->Header.AllocationSize.LowPart; + + FatAddFileAllocation( IrpContext, + Dcb, + Dcb->Specific.Dcb.DirectoryFile, + StartingVbo + ByteCount ); + + UnwindWeAllocatedDiskSpace = TRUE; + + // + // Inform the cache manager of the new allocation + // + + Dcb->Header.FileSize.LowPart = + Dcb->Header.AllocationSize.LowPart; + + CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile, + (PCC_FILE_SIZES)&Dcb->Header.AllocationSize ); + + // + // Set up the Bitmap buffer if it is not big enough already + // + + FatCheckFreeDirentBitmap( IrpContext, Dcb ); + + // + // The newly allocated clusters should be zeroed starting at + // the previous allocation size + // + + Zero = TRUE; + Vbo.QuadPart = InitialAllocation; + ByteCount = Dcb->Header.AllocationSize.LowPart - InitialAllocation; + } + + // + // Call the Cache Manager to attempt the transfer, going one cluster + // at a time to avoid pinning across a page boundary. + // + + ClusterSize = + 1 << Dcb->Vcb->AllocationSupport.LogOfBytesPerCluster; + + while (ByteCount > 0) { + + ULONG BytesToPin; + + *Bcb = NULL; + + if (ByteCount > ClusterSize) { + BytesToPin = ClusterSize; + } else { + BytesToPin = ByteCount; + } + + ASSERT( (Vbo.QuadPart / ClusterSize) == + (Vbo.QuadPart + BytesToPin - 1)/ClusterSize ); + + if (!CcPinRead( Dcb->Specific.Dcb.DirectoryFile, + &Vbo, + BytesToPin, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb, + &LocalBuffer )) { + + // + // Could not read the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Update our caller with the beginning of their request. + // + + if (*Buffer == NULL) { + + *Buffer = LocalBuffer; + } + + DbgDoit( IrpContext->PinCount += 1 ) + + if (Zero) { + + // + // We set this guy dirty right now so that we can raise CANT_WAIT when + // it needs to be done. It'd be beautiful if we could noop the read IO + // since we know we don't care about it. + // + + RtlZeroMemory( LocalBuffer, BytesToPin ); + CcSetDirtyPinnedData( *Bcb, NULL ); + } + + ByteCount -= BytesToPin; + Vbo.QuadPart += BytesToPin; + + + if (ByteCount > 0) { + + FatUnpinBcb( IrpContext, *Bcb ); + } + } + + // + // This lets us get the data pinned until we complete the request + // and writes the dirty bit through to the disk. + // + + FatSetDirtyBcb( IrpContext, *Bcb, Dcb->Vcb, Reversible ); + + *Status = STATUS_SUCCESS; + + } _SEH2_FINALLY { + + DebugUnwind( FatPrepareWriteDirectoryFile ); + + if (_SEH2_AbnormalTermination()) { + + // + // These steps are carefully arranged - FatTruncateFileAllocation can raise. + // Make sure we unpin the buffer. If FTFA raises, the effect should be benign. + // + + FatUnpinBcb(IrpContext, *Bcb); + + if (UnwindWeAllocatedDiskSpace == TRUE) { + + // + // Inform the cache manager of the change. + // + + FatTruncateFileAllocation( IrpContext, Dcb, InitialAllocation ); + + Dcb->Header.FileSize.LowPart = + Dcb->Header.AllocationSize.LowPart; + + CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile, + (PCC_FILE_SIZES)&Dcb->Header.AllocationSize ); + } + } + + DebugTrace(-1, Dbg, "FatPrepareWriteDirectoryFile -> (VOID), *Bcb = %08lx\n", *Bcb); + } _SEH2_END; + + return; +} + + +#if DBG +BOOLEAN FatDisableParentCheck = 0; + +BOOLEAN +FatIsCurrentOperationSynchedForDcbTeardown ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ) +{ + PIRP Irp = IrpContext->OriginatingIrp; + PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation( Irp ) ; +#ifndef __REACTOS__ + PFILE_OBJECT FileObject = Stack->FileObject; +#endif + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PFILE_OBJECT ToCheck[3]; + ULONG Index = 0; + + PAGED_CODE(); + + // + // While mounting, we're OK without having to own anything. + // + + if (Stack->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + Stack->MinorFunction == IRP_MN_MOUNT_VOLUME) { + + return TRUE; + } + + // + // With the Vcb held, the close path is blocked out. + // + + if (ExIsResourceAcquiredSharedLite( &Dcb->Vcb->Resource ) || + ExIsResourceAcquiredExclusiveLite( &Dcb->Vcb->Resource )) { + + return TRUE; + } + + // + // Accept this assertion at face value. It comes from GetDirentForFcbOrDcb, + // and is reliable. + // + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD )) { + + return TRUE; + } + + // + // Determine which fileobjects are around on this operation. + // + + if (Stack->MajorFunction == IRP_MJ_SET_INFORMATION && + Stack->Parameters.SetFile.FileObject) { + + ToCheck[Index++] = Stack->Parameters.SetFile.FileObject; + } + + if (Stack->FileObject) { + + ToCheck[Index++] = Stack->FileObject; + } + + ToCheck[Index] = NULL; + + // + // If the fileobjects we have are for this dcb or a child of it, we are + // also guaranteed that this dcb isn't going anywhere (even without + // the Vcb). + // + + for (Index = 0; ToCheck[Index] != NULL; Index++) { + + (VOID) FatDecodeFileObject( ToCheck[Index], &Vcb, &Fcb, &Ccb ); + + while ( Fcb ) { + + if (Fcb == Dcb) { + + return TRUE; + } + + Fcb = Fcb->ParentDcb; + } + } + + return FatDisableParentCheck; +} +#endif // DBG + +VOID +FatOpenDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ) + +/*++ + +Routine Description: + + This routine opens a new directory file if one is not already open. + +Arguments: + + Dcb - Pointer to the DCB for the directory + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenDirectoryFile\n", 0); + DebugTrace( 0, Dbg, "Dcb = %08lx\n", Dcb); + + // + // If we don't have some hold on this Dcb (there are several ways), there is nothing + // to prevent child files from closing and tearing this branch of the tree down in the + // midst of our slapping this reference onto it. + // + // I really wish we had a proper Fcb synchronization model (like CDFS/UDFS/NTFS). + // + + ASSERT( FatIsCurrentOperationSynchedForDcbTeardown( IrpContext, Dcb )); + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (Dcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, Dcb ); + + Dcb->Header.FileSize.LowPart = + Dcb->Header.AllocationSize.LowPart; + } + + // + // Setup the Bitmap buffer if it is not big enough already + // + + FatCheckFreeDirentBitmap( IrpContext, Dcb ); + + // + // Check if we need to create a directory file. + // + // We first do a spot check and then synchronize and check again. + // + + if (Dcb->Specific.Dcb.DirectoryFile == NULL) { + + PFILE_OBJECT DirectoryFileObject = NULL; + + FatAcquireDirectoryFileMutex( Dcb->Vcb ); + + _SEH2_TRY { + + if (Dcb->Specific.Dcb.DirectoryFile == NULL) { + + PDEVICE_OBJECT RealDevice; + + // + // Create the special file object for the directory file, and set + // up its pointers back to the Dcb and the section object pointer. + // Note that setting the DirectoryFile pointer in the Dcb has + // to be the last thing done. + // + // Preallocate a close context since we have no Ccb for this object. + // + + RealDevice = Dcb->Vcb->CurrentDevice; + + DirectoryFileObject = IoCreateStreamFileObject( NULL, RealDevice ); + FatPreallocateCloseContext(); + + FatSetFileObject( DirectoryFileObject, + DirectoryFile, + Dcb, + NULL ); + + // + // Remember this internal open. + // + +#ifndef __REACTOS__ + InterlockedIncrement( &(Dcb->Vcb->InternalOpenCount) ); +#else + InterlockedIncrement( (LONG*)&(Dcb->Vcb->InternalOpenCount) ); +#endif + + // + // If this is the root directory, it is also a residual open. + // + + if (NodeType( Dcb ) == FAT_NTC_ROOT_DCB) { + +#ifndef __REACTOS__ + InterlockedIncrement( &(Dcb->Vcb->ResidualOpenCount) ); +#else + InterlockedIncrement( (LONG*)&(Dcb->Vcb->ResidualOpenCount) ); +#endif + } + + DirectoryFileObject->SectionObjectPointer = &Dcb->NonPaged->SectionObjectPointers; + + DirectoryFileObject->ReadAccess = TRUE; + DirectoryFileObject->WriteAccess = TRUE; + DirectoryFileObject->DeleteAccess = TRUE; + +#ifndef __REACTOS__ + InterlockedIncrement( &Dcb->Specific.Dcb.DirectoryFileOpenCount ); +#else + InterlockedIncrement( (LONG*)&Dcb->Specific.Dcb.DirectoryFileOpenCount ); +#endif + + Dcb->Specific.Dcb.DirectoryFile = DirectoryFileObject; + + // + // Indicate we're happy with the fileobject now. + // + + DirectoryFileObject = NULL; + } + + } _SEH2_FINALLY { + + FatReleaseDirectoryFileMutex( Dcb->Vcb ); + + // + // Rip the object up if we couldn't get the close context. + // + + if (DirectoryFileObject) { + + ObDereferenceObject( DirectoryFileObject ); + } + } _SEH2_END; + } + + // + // Finally check if we need to initialize the Cache Map for the + // directory file. The size of the section we are going to map + // the current allocation size for the directory. Note that the + // cache manager will provide syncronization for us. + // + + if ( Dcb->Specific.Dcb.DirectoryFile->PrivateCacheMap == NULL ) { + + Dcb->Header.ValidDataLength = FatMaxLarge; + Dcb->ValidDataToDisk = MAXULONG; + + CcInitializeCacheMap( Dcb->Specific.Dcb.DirectoryFile, + (PCC_FILE_SIZES)&Dcb->Header.AllocationSize, + TRUE, + &FatData.CacheManagerNoOpCallbacks, + Dcb ); + } + + DebugTrace(-1, Dbg, "FatOpenDirectoryFile -> VOID\n", 0); + + return; +} + + +PFILE_OBJECT +FatOpenEaFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB EaFcb + ) + +/*++ + +Routine Description: + + This routine opens the Ea file. + +Arguments: + + EaFcb - Pointer to the Fcb for the Ea file. + +Return Value: + + Pointer to the new file object. + +--*/ + +{ + PFILE_OBJECT EaFileObject = NULL; + PDEVICE_OBJECT RealDevice; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatOpenEaFile\n", 0); + DebugTrace( 0, Dbg, "EaFcb = %08lx\n", EaFcb); + + // + // Create the special file object for the ea file, and set + // up its pointers back to the Fcb and the section object pointer + // + + RealDevice = EaFcb->Vcb->CurrentDevice; + + EaFileObject = IoCreateStreamFileObject( NULL, RealDevice ); + + _SEH2_TRY { + + FatPreallocateCloseContext(); + + FatSetFileObject( EaFileObject, + EaFile, + EaFcb, + NULL ); + + // + // Remember this internal, residual open. + // + +#ifndef __REACTOS__ + InterlockedIncrement( &(EaFcb->Vcb->InternalOpenCount) ); + InterlockedIncrement( &(EaFcb->Vcb->ResidualOpenCount) ); +#else + InterlockedIncrement( (LONG*)&(EaFcb->Vcb->InternalOpenCount) ); + InterlockedIncrement( (LONG*)&(EaFcb->Vcb->ResidualOpenCount) ); +#endif + + EaFileObject->SectionObjectPointer = &EaFcb->NonPaged->SectionObjectPointers; + + EaFileObject->ReadAccess = TRUE; + EaFileObject->WriteAccess = TRUE; + + // + // Finally check if we need to initialize the Cache Map for the + // ea file. The size of the section we are going to map + // the current allocation size for the Fcb. + // + + EaFcb->Header.ValidDataLength = FatMaxLarge; + + CcInitializeCacheMap( EaFileObject, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize, + TRUE, + &FatData.CacheManagerCallbacks, + EaFcb ); + + CcSetAdditionalCacheAttributes( EaFileObject, TRUE, TRUE ); + + } _SEH2_FINALLY { + + // + // Drop the fileobject if we're raising. Two cases: couldn't get + // the close context, and it is still an UnopenedFileObject, or + // we lost trying to build the cache map - in which case we're + // OK for the close context if we have to. + // + + if (_SEH2_AbnormalTermination()) { + + ObDereferenceObject( EaFileObject ); + } + } _SEH2_END; + + DebugTrace(-1, Dbg, "FatOpenEaFile -> %08lx\n", EaFileObject); + + UNREFERENCED_PARAMETER( IrpContext ); + + return EaFileObject; +} + + +VOID +FatCloseEaFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN FlushFirst + ) + +/*++ + +Routine Description: + + This routine shuts down the ea file. Usually this is required when the volume + begins to leave the system: after verify, dismount, deletion, pnp. + +Arguments: + + Vcb - the volume to close the ea file on + + FlushFirst - whether the file should be flushed + +Return Value: + + None. As a side effect, the EA fileobject in the Vcb is cleared. + + Caller must have the Vcb exclusive. + +--*/ + +{ + PFILE_OBJECT EaFileObject = Vcb->VirtualEaFile; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCloseEaFile\n", 0); + DebugTrace( 0, Dbg, "Vcb = %08lx\n", Vcb); + + ASSERT( FatVcbAcquiredExclusive(IrpContext, Vcb) ); + + if (EaFileObject != NULL) { + + EaFileObject = Vcb->VirtualEaFile; + + if (FlushFirst) { + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + } + + Vcb->VirtualEaFile = NULL; + + // + // Empty the Mcb for the Ea file. + // + + FatRemoveMcbEntry( Vcb, &Vcb->EaFcb->Mcb, 0, 0xFFFFFFFF ); + + // + // Uninitialize the cache for this file object and dereference it. + // + + FatSyncUninitializeCacheMap( IrpContext, EaFileObject ); + + ObDereferenceObject( EaFileObject ); + } + + DebugTrace(-1, Dbg, "FatCloseEaFile -> %08lx\n", EaFileObject); +} + + +VOID +FatSetDirtyBcb ( + IN PIRP_CONTEXT IrpContext, + IN PBCB Bcb, + IN PVCB Vcb OPTIONAL, + IN BOOLEAN Reversible + ) + +/*++ + +Routine Description: + + This routine saves a reference to the bcb in the irp context and + sets the bcb dirty. This will have the affect of keeping the page in + memory until we complete the request + + In addition, a DPC is set to fire in 5 seconds (or if one is pending, + pushed back 5 seconds) to mark the volume clean. + +Arguments: + + Bcb - Supplies the Bcb being set dirty + + Vcb - Supplies the volume being marked dirty + + Reversible - Supplies TRUE if the specified range of bcb should be repinned + so that the changes can be reversed in a controlled fashion if errors + are encountered. + +Return Value: + + None. + +--*/ + +{ + DebugTrace(+1, Dbg, "FatSetDirtyBcb\n", 0 ); + DebugTrace( 0, Dbg, "IrpContext = %08lx\n", IrpContext ); + DebugTrace( 0, Dbg, "Bcb = %08lx\n", Bcb ); + DebugTrace( 0, Dbg, "Vcb = %08lx\n", Vcb ); + + // + // Repin the bcb as required + // + + if (Reversible) { + + FatRepinBcb( IrpContext, Bcb ); + } + + // + // Set the bcb dirty + // + + CcSetDirtyPinnedData( Bcb, NULL ); + + // + // If volume dirtying isn't disabled for this operation (for + // instance, when we're changing the dirty state), set the + // volume dirty if we were given a Vcb that we want to perform + // clean volume processing on, and return. + // + // As a historical note, we used to key off of the old floppy + // (now deferred flush) bit to disable dirtying behavior. Since + // hotpluggable media can still be yanked while operations are + // in flight, recognize that its really the case that FAT12 + // doesn't have the dirty bit. + // + + if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_DIRTY) && + ARGUMENT_PRESENT(Vcb) && + !FatIsFat12(Vcb)) { + + KIRQL SavedIrql; + + BOOLEAN SetTimer; + + LARGE_INTEGER TimeSincePreviousCall; + LARGE_INTEGER CurrentTime; + + // + // "Borrow" the irp context spinlock. + // + + KeQuerySystemTime( &CurrentTime ); + + KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql ); + + TimeSincePreviousCall.QuadPart = + CurrentTime.QuadPart - Vcb->LastFatMarkVolumeDirtyCall.QuadPart; + + // + // If more than one second has elapsed since the prior call + // to here, bump the timer up again and see if we need to + // physically mark the volume dirty. + // + + if ( (TimeSincePreviousCall.HighPart != 0) || + (TimeSincePreviousCall.LowPart > (1000 * 1000 * 10)) ) { + + SetTimer = TRUE; + + } else { + + SetTimer = FALSE; + } + + KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql ); + + if ( SetTimer ) { + + LARGE_INTEGER CleanVolumeTimer; + + // + // We use a shorter volume clean timer for hot plug volumes. + // + + CleanVolumeTimer.QuadPart = FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) + ? (LONG)-1500*1000*10 + : (LONG)-8*1000*1000*10; + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + // + // We have now synchronized with anybody clearing the dirty + // flag, so we can now see if we really have to actually write + // out the physical bit. + // + + if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY) ) { + + // + // We want to really mark the volume dirty now. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + FatMarkVolume( IrpContext, Vcb, VolumeDirty ); + } + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + + // + // Lock the volume if it is removable. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); + } + } + + KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql ); + + KeQuerySystemTime( &Vcb->LastFatMarkVolumeDirtyCall ); + + KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql ); + + KeSetTimer( &Vcb->CleanVolumeTimer, + CleanVolumeTimer, + &Vcb->CleanVolumeDpc ); + } + } + + DebugTrace(-1, Dbg, "FatSetDirtyBcb -> VOID\n", 0 ); +} + + +VOID +FatRepinBcb ( + IN PIRP_CONTEXT IrpContext, + IN PBCB Bcb + ) + +/*++ + +Routine Description: + + This routine saves a reference to the bcb in the irp context. This will + have the affect of keeping the page in memory until we complete the + request + +Arguments: + + Bcb - Supplies the Bcb being referenced + +Return Value: + + None. + +--*/ + +{ + PREPINNED_BCBS Repinned; + ULONG i; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatRepinBcb\n", 0 ); + DebugTrace( 0, Dbg, "IrpContext = %08lx\n", IrpContext ); + DebugTrace( 0, Dbg, "Bcb = %08lx\n", Bcb ); + + // + // The algorithm is to search the list of repinned records until + // we either find a match for the bcb or we find a null slot. + // + + Repinned = &IrpContext->Repinned; + + while (TRUE) { + + // + // For every entry in the repinned record check if the bcb's + // match or if the entry is null. If the bcb's match then + // we've done because we've already repinned this bcb, if + // the entry is null then we know, because it's densely packed, + // that the bcb is not in the list so add it to the repinned + // record and repin it. + // + + for (i = 0; i < REPINNED_BCBS_ARRAY_SIZE; i += 1) { + + if (Repinned->Bcb[i] == Bcb) { + + DebugTrace(-1, Dbg, "FatRepinBcb -> VOID\n", 0 ); + return; + } + + if (Repinned->Bcb[i] == NULL) { + + Repinned->Bcb[i] = Bcb; + CcRepinBcb( Bcb ); + + DebugTrace(-1, Dbg, "FatRepinBcb -> VOID\n", 0 ); + return; + } + } + + // + // We finished checking one repinned record so now locate the next + // repinned record, If there isn't one then allocate and zero out + // a new one. + // + + if (Repinned->Next == NULL) { + + Repinned->Next = FsRtlAllocatePoolWithTag( PagedPool, + sizeof(REPINNED_BCBS), + TAG_REPINNED_BCB ); + + RtlZeroMemory( Repinned->Next, sizeof(REPINNED_BCBS) ); + } + + Repinned = Repinned->Next; + } +} + + +VOID +FatUnpinRepinnedBcbs ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine frees all of the repinned bcbs, stored in an IRP context. + +Arguments: + +Return Value: + + None. + +--*/ + +{ + IO_STATUS_BLOCK RaiseIosb; + PREPINNED_BCBS Repinned; + BOOLEAN WriteThroughToDisk; + PFILE_OBJECT FileObject = NULL; + BOOLEAN ForceVerify = FALSE; + ULONG i; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatUnpinRepinnedBcbs\n", 0 ); + DebugTrace( 0, Dbg, "IrpContext = %08lx\n", IrpContext ); + + // + // The algorithm for this procedure is to scan the entire list of + // repinned records unpinning any repinned bcbs. We start off + // with the first record in the irp context, and while there is a + // record to scan we do the following loop. + // + + Repinned = &IrpContext->Repinned; + RaiseIosb.Status = STATUS_SUCCESS; + + // + // If the request is write through or the media is deferred flush, + // unpin the bcb's write through. + // + + WriteThroughToDisk = (BOOLEAN) (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH) && + IrpContext->Vcb != NULL && + (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH) || + FlagOn(IrpContext->Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH))); + + while (Repinned != NULL) { + + // + // For every non-null entry in the repinned record unpin the + // repinned entry. + // + // If the this is removable media (therefore all requests write- + // through) and the write fails, purge the cache so that we throw + // away the modifications as we will be returning an error to the + // user. + // + + for (i = 0; i < REPINNED_BCBS_ARRAY_SIZE; i += 1) { + + if (Repinned->Bcb[i] != NULL) { + + IO_STATUS_BLOCK Iosb; + + if (WriteThroughToDisk && + FlagOn(IrpContext->Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH)) { + + FileObject = CcGetFileObjectFromBcb( Repinned->Bcb[i] ); + } + + CcUnpinRepinnedBcb( Repinned->Bcb[i], + WriteThroughToDisk, + &Iosb ); + + if ( !NT_SUCCESS(Iosb.Status) ) { + + if (RaiseIosb.Status == STATUS_SUCCESS) { + + RaiseIosb = Iosb; + } + + // + // If this was a writethrough device, purge the cache, + // except for Irp major codes that either don't handle + // the error paths correctly or are simple victims like + // cleanup.c. + // + + if (FileObject && + (IrpContext->MajorFunction != IRP_MJ_CLEANUP) && + (IrpContext->MajorFunction != IRP_MJ_FLUSH_BUFFERS) && + (IrpContext->MajorFunction != IRP_MJ_SET_INFORMATION)) { + + // + // The call to CcPurgeCacheSection() below will + // purge the entire file from memory. It will also + // block until all the file's BCB's are pinned. + // + // We end up in a deadlock situation of there + // are any other pinned BCB's in this IRP context + // so the first thing we do is search the list + // for BCB's pinned in the same file and unpin + // them. + // + // We are probably not going to lose data because + // it's safe to assume that all flushes will + // fail after the first one fails. + // + + ULONG j; + + for (j = i + 1; j < REPINNED_BCBS_ARRAY_SIZE; j++) { + + if (Repinned->Bcb[j] != NULL) { + + if (CcGetFileObjectFromBcb( Repinned->Bcb[j] ) == FileObject) { + + CcUnpinRepinnedBcb( Repinned->Bcb[j], + FALSE, + &Iosb ); + + Repinned->Bcb[j] = NULL; + } + } + } + + CcPurgeCacheSection( FileObject->SectionObjectPointer, + NULL, + 0, + FALSE ); + + // + // Force a verify operation here since who knows + // what state things are in. + // + + ForceVerify = TRUE; + } + } + + Repinned->Bcb[i] = NULL; + + } + } + + // + // Now find the next repinned record in the list, and possibly + // delete the one we've just processed. + // + + if (Repinned != &IrpContext->Repinned) { + + PREPINNED_BCBS Saved; + + Saved = Repinned->Next; + ExFreePool( Repinned ); + Repinned = Saved; + + } else { + + Repinned = Repinned->Next; + IrpContext->Repinned.Next = NULL; + } + } + + // + // Now if we weren't completely successful in the our unpin + // then raise the iosb we got + // + + if (!NT_SUCCESS(RaiseIosb.Status)) { + + if (ForceVerify && FileObject) { + + SetFlag(FileObject->DeviceObject->Flags, DO_VERIFY_VOLUME); + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + FileObject->DeviceObject ); + } + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE )) { + + IrpContext->OriginatingIrp->IoStatus = RaiseIosb; + FatNormalizeAndRaiseStatus( IrpContext, RaiseIosb.Status ); + } + } + + DebugTrace(-1, Dbg, "FatUnpinRepinnedBcbs -> VOID\n", 0 ); + + return; +} + + +FINISHED +FatZeroData ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject, + IN ULONG StartingZero, + IN ULONG ByteCount + ) + +/*++ + + **** Temporary function - Remove when CcZeroData is capable of handling + non sector aligned requests. + +--*/ +{ +#ifndef __REACTOS__ + LARGE_INTEGER ZeroStart = {0,0}; + LARGE_INTEGER BeyondZeroEnd = {0,0}; +#else + LARGE_INTEGER ZeroStart = {{0}}; + LARGE_INTEGER BeyondZeroEnd = {{0}}; +#endif + + ULONG SectorSize; + + BOOLEAN Finished; + + PAGED_CODE(); + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + ZeroStart.LowPart = (StartingZero + (SectorSize - 1)) & ~(SectorSize - 1); + + // + // Detect overflow if we were asked to zero in the last sector of the file, + // which must be "zeroed" already (or we're in trouble). + // + + if (StartingZero != 0 && ZeroStart.LowPart == 0) { + + return TRUE; + } + + // + // Note that BeyondZeroEnd can take the value 4gb. + // + + BeyondZeroEnd.QuadPart = ((ULONGLONG) StartingZero + ByteCount + (SectorSize - 1)) + & (~((LONGLONG) SectorSize - 1)); + + // + // If we were called to just zero part of a sector we are in trouble. + // + + if ( ZeroStart.QuadPart == BeyondZeroEnd.QuadPart ) { + + return TRUE; + } + + Finished = CcZeroData( FileObject, + &ZeroStart, + &BeyondZeroEnd, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + return Finished; +} + + +NTSTATUS +FatCompleteMdl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the function of completing Mdl read and write + requests. It should be called only from FatFsdRead and FatFsdWrite. + +Arguments: + + Irp - Supplies the originating Irp. + +Return Value: + + NTSTATUS - Will always be STATUS_PENDING or STATUS_SUCCESS. + +--*/ + +{ + PFILE_OBJECT FileObject; + PIO_STACK_LOCATION IrpSp; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCompleteMdl\n", 0 ); + DebugTrace( 0, Dbg, "IrpContext = %08lx\n", IrpContext ); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + + // + // Do completion processing. + // + + FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject; + + switch( IrpContext->MajorFunction ) { + + case IRP_MJ_READ: + + CcMdlReadComplete( FileObject, Irp->MdlAddress ); + break; + + case IRP_MJ_WRITE: + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )); + + CcMdlWriteComplete( FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + + break; + + default: + + DebugTrace( DEBUG_TRACE_ERROR, 0, "Illegal Mdl Complete.\n", 0); + FatBugCheck( IrpContext->MajorFunction, 0, 0 ); + } + + // + // Mdl is now deallocated. + // + + Irp->MdlAddress = NULL; + + // + // Complete the request and exit right away. + // + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatCompleteMdl -> STATUS_SUCCESS\n", 0 ); + + return STATUS_SUCCESS; +} + +VOID +FatSyncUninitializeCacheMap ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + The routine performs a CcUnitializeCacheMap to LargeZero synchronously. That + is it waits on the Cc event. This call is useful when we want to be certain + when a close will actually some in. + +Return Value: + + None. + +--*/ + +{ + CACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent; + NTSTATUS WaitStatus; + + PAGED_CODE(); + + KeInitializeEvent( &UninitializeCompleteEvent.Event, + SynchronizationEvent, + FALSE); + + CcUninitializeCacheMap( FileObject, + &FatLargeZero, + &UninitializeCompleteEvent ); + + // + // Now wait for the cache manager to finish purging the file. + // This will garentee that Mm gets the purge before we + // delete the Vcb. + // + + WaitStatus = KeWaitForSingleObject( &UninitializeCompleteEvent.Event, + Executive, + KernelMode, + FALSE, + NULL); + + ASSERT(WaitStatus == STATUS_SUCCESS); +} + +VOID +FatPinMappedData ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb + ) + +/*++ + +Routine Description: + + This routine pins data that was previously mapped before setting it dirty. + +Arguments: + + Dcb - Pointer to the DCB for the directory + + StartingVbo - The virtual offset of the first desired byte + + ByteCount - Number of bytes desired + + Bcb - Returns a pointer to the BCB which is valid until unpinned + +--*/ + +{ + LARGE_INTEGER Vbo; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatPinMappedData\n", 0); + DebugTrace( 0, Dbg, "Dcb = %08lx\n", Dcb); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + + // + // Call the Cache manager to perform the operation. + // + + Vbo.QuadPart = StartingVbo; + + if (!CcPinMappedData( Dcb->Specific.Dcb.DirectoryFile, + &Vbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + Bcb )) { + + // + // Could not pin the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID, *BCB = %08lx\n", *Bcb); + + return; +} + + diff --git a/drivers/filesystems/fastfat_new/cleanup.c b/drivers/filesystems/fastfat_new/cleanup.c new file mode 100644 index 00000000000..7c043a0866e --- /dev/null +++ b/drivers/filesystems/fastfat_new/cleanup.c @@ -0,0 +1,1027 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Cleanup.c + +Abstract: + + This module implements the File Cleanup routine for Fat called by the + dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_CLEANUP) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_CLEANUP) + +// +// The following little routine exists solely because it need a spin lock. +// + +VOID +FatAutoUnlock ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonCleanup) +#pragma alloc_text(PAGE, FatFsdCleanup) +#endif + + +NTSTATUS +NTAPI +FatFsdCleanup ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of closing down a handle to a + file object. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file being Cleanup exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + // + // If we were called with our file system device object instead of a + // volume device object, just complete this request with STATUS_SUCCESS + // + + if ( FatDeviceIsFatFsdo( VolumeDeviceObject)) { + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + + return STATUS_SUCCESS; + } + + DebugTrace(+1, Dbg, "FatFsdCleanup\n", 0); + + // + // Call the common Cleanup routine, with blocking allowed. + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, TRUE ); + + Status = FatCommonCleanup( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdCleanup -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonCleanup ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for cleanup of a file/directory called by both + the fsd and fsp threads. + + Cleanup is invoked whenever the last handle to a file object is closed. + This is different than the Close operation which is invoked when the last + reference to a file object is deleted. + + The function of cleanup is to essentially "cleanup" the file/directory + after a user is done with it. The Fcb/Dcb remains around (because MM + still has the file object referenced) but is now available for another + user to open (i.e., as far as the user is concerned the is now closed). + + See close for a more complete description of what close does. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN SendUnlockNotification = FALSE; + + PSHARE_ACCESS ShareAccess; + + PLARGE_INTEGER TruncateSize = NULL; + LARGE_INTEGER LocalTruncateSize; + + BOOLEAN AcquiredVcb = FALSE; + BOOLEAN AcquiredFcb = FALSE; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonCleanup\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject); + + // + // Extract and decode the file object + // + + FileObject = IrpSp->FileObject; + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + // + // Special case the unopened file object. This will occur only when + // we are initializing Vcb and IoCreateStreamFileObject is being + // called. + // + + if (TypeOfOpen == UnopenedFileObject) { + + DebugTrace(0, Dbg, "Unopened File Object\n", 0); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0); + return STATUS_SUCCESS; + } + + // + // If this is not our first time through (for whatever reason) + // only see if we have to flush the file. + // + + if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) { + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && + FlagOn(FileObject->Flags, FO_FILE_MODIFIED) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) && + (TypeOfOpen == UserFileOpen)) { + + // + // Flush the file. + // + + Status = FatFlushFile( IrpContext, Fcb, Flush ); + + if (!NT_SUCCESS(Status)) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatCommonCleanup -> STATUS_SUCCESS\n", 0); + return STATUS_SUCCESS; + } + + // + // If we call change the allocation or call CcUninitialize, + // we have to take the Fcb exclusive + // + + if ((TypeOfOpen == UserFileOpen) || (TypeOfOpen == UserDirectoryOpen)) { + + ASSERT( Fcb != NULL ); + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + + AcquiredFcb = TRUE; + + // + // Do a check here if this was a DELETE_ON_CLOSE FileObject, and + // set the Fcb flag appropriately. + // + + if (FlagOn(Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE)) { + + ASSERT( NodeType(Fcb) != FAT_NTC_ROOT_DCB ); + + SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE); + + // + // Report this to the dir notify package for a directory. + // + + if (TypeOfOpen == UserDirectoryOpen) { + + FsRtlNotifyFullChangeDirectory( Vcb->NotifySync, + &Vcb->DirNotifyList, + FileObject->FsContext, + NULL, + FALSE, + FALSE, + 0, + NULL, + NULL, + NULL ); + } + } + + // + // Now if we may delete the file, drop the Fcb and acquire the Vcb + // first. Note that while we own the Fcb exclusive, a file cannot + // become DELETE_ON_CLOSE and cannot be opened via CommonCreate. + // + + if ((Fcb->UncleanCount == 1) && + FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + (Fcb->FcbCondition != FcbBad) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + FatReleaseFcb( IrpContext, Fcb ); + AcquiredFcb = FALSE; + + (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb ); + AcquiredVcb = TRUE; + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + AcquiredFcb = TRUE; + } + } + + // + // For user DASD cleanups, grab the Vcb exclusive. + // + + if (TypeOfOpen == UserVolumeOpen) { + + (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb ); + AcquiredVcb = TRUE; + } + + // + // Complete any Notify Irps on this file handle. + // + + if (TypeOfOpen == UserDirectoryOpen) { + + FsRtlNotifyCleanup( Vcb->NotifySync, + &Vcb->DirNotifyList, + Ccb ); + } + + // + // Determine the Fcb state, Good or Bad, for better or for worse. + // + // We can only read the volume file if VcbCondition is good. + // + + if ( Fcb != NULL) { + + // + // Stop any raises from FatVerifyFcb, unless it is REAL bad. + // + + _SEH2_TRY { + + _SEH2_TRY { + + FatVerifyFcb( IrpContext, Fcb ); + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + } _SEH2_FINALLY { + + if ( _SEH2_AbnormalTermination() ) { + + // + // We will be raising out of here. + // + + if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); } + if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); } + } + } _SEH2_END; + } + + _SEH2_TRY { + + // + // Case on the type of open that we are trying to cleanup. + // For all cases we need to set the share access to point to the + // share access variable (if there is one). After the switch + // we then remove the share access and complete the Irp. + // In the case of UserFileOpen we actually have a lot more work + // to do and we have the FsdLockControl complete the Irp for us. + // + + switch (TypeOfOpen) { + + case DirectoryFile: + case VirtualVolumeFile: + + DebugTrace(0, Dbg, "Cleanup VirtualVolumeFile/DirectoryFile\n", 0); + + ShareAccess = NULL; + + break; + + case UserVolumeOpen: + + DebugTrace(0, Dbg, "Cleanup UserVolumeOpen\n", 0); + + if (FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) { + + FatCheckForDismount( IrpContext, Vcb, TRUE ); + + // + // If this handle had write access, and actually wrote something, + // flush the device buffers, and then set the verify bit now + // just to be safe (in case there is no dismount). + // + + } else if (FileObject->WriteAccess && + FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) { + + (VOID)FatHijackIrpAndFlushDevice( IrpContext, + Irp, + Vcb->TargetDeviceObject ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + } + + // + // If the volume is locked by this file object then release + // the volume and send notification. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED) && + (Vcb->FileObjectWithVcbLocked == FileObject)) { + + FatAutoUnlock( IrpContext, Vcb ); + SendUnlockNotification = TRUE; + } + + ShareAccess = &Vcb->ShareAccess; + + break; + + case EaFile: + + DebugTrace(0, Dbg, "Cleanup EaFileObject\n", 0); + + ShareAccess = NULL; + + break; + + case UserDirectoryOpen: + + DebugTrace(0, Dbg, "Cleanup UserDirectoryOpen\n", 0); + + ShareAccess = &Fcb->ShareAccess; + + // + // Determine here if we should try do delayed close. + // + + if ((Fcb->UncleanCount == 1) && + (Fcb->OpenCount == 1) && + (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0) && + !FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + Fcb->FcbCondition == FcbGood) { + + // + // Delay our close. + // + + SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE ); + } + + FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); + + // + // If the directory has a unclean count of 1 then we know + // that this is the last handle for the file object. If + // we are supposed to delete it, do so. + // + + if ((Fcb->UncleanCount == 1) && + (NodeType(Fcb) == FAT_NTC_DCB) && + (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) && + (Fcb->FcbCondition != FcbBad) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + if (!FatIsDirectoryEmpty(IrpContext, Fcb)) { + + // + // If there are files in the directory at this point, + // forget that we were trying to delete it. + // + + ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + + } else { + + // + // Even if something goes wrong, we cannot turn back! + // + + _SEH2_TRY { + + DELETE_CONTEXT DeleteContext; + + // + // Before truncating file allocation remember this + // info for FatDeleteDirent. + // + + DeleteContext.FileSize = Fcb->Header.FileSize.LowPart; + DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile; + + // + // Synchronize here with paging IO + // + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, + TRUE ); + + Fcb->Header.FileSize.LowPart = 0; + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + if (Vcb->VcbCondition == VcbGood) { + + // + // Truncate the file allocation down to zero + // + + DebugTrace(0, Dbg, "Delete File allocation\n", 0); + + FatTruncateFileAllocation( IrpContext, Fcb, 0 ); + + if (Fcb->Header.AllocationSize.LowPart == 0) { + + // + // Tunnel and remove the dirent for the directory + // + + DebugTrace(0, Dbg, "Delete the directory dirent\n", 0); + + FatTunnelFcbOrDcb( Fcb, NULL ); + + FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE ); + + // + // Report that we have removed an entry. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_DIR_NAME, + FILE_ACTION_REMOVED ); + } + } + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + // + // Remove the entry from the name table. + // This will ensure that + // we will not collide with the Dcb if the user wants + // to recreate the same file over again before we + // get a close irp. + // + + FatRemoveNames( IrpContext, Fcb ); + } + } + + // + // Decrement the unclean count. + // + + ASSERT( Fcb->UncleanCount != 0 ); + Fcb->UncleanCount -= 1; + + break; + + case UserFileOpen: + + DebugTrace(0, Dbg, "Cleanup UserFileOpen\n", 0); + + ShareAccess = &Fcb->ShareAccess; + + // + // Determine here if we should do a delayed close. + // + + if ((FileObject->SectionObjectPointer->DataSectionObject == NULL) && + (FileObject->SectionObjectPointer->ImageSectionObject == NULL) && + (Fcb->UncleanCount == 1) && + (Fcb->OpenCount == 1) && + !FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + Fcb->FcbCondition == FcbGood) { + + // + // Delay our close. + // + + SetFlag( Fcb->FcbState, FCB_STATE_DELAY_CLOSE ); + } + + // + // Unlock all outstanding file locks. + // + + (VOID) FsRtlFastUnlockAll( &Fcb->Specific.Fcb.FileLock, + FileObject, + IoGetRequestorProcess( Irp ), + NULL ); + + // + // We can proceed with on-disk updates only if the volume is mounted. + // Remember that we toss all sections in the failed-verify and dismount + // cases. + // + + if (Vcb->VcbCondition == VcbGood) { + + if (Fcb->FcbCondition != FcbBad) { + + FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); + } + + // + // If the file has a unclean count of 1 then we know + // that this is the last handle for the file object. + // + + if ( (Fcb->UncleanCount == 1) && (Fcb->FcbCondition != FcbBad) ) { + + DELETE_CONTEXT DeleteContext; + + // + // Check if we should be deleting the file. The + // delete operation really deletes the file but + // keeps the Fcb around for close to do away with. + // + + if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Before truncating file allocation remember this + // info for FatDeleteDirent. + // + + DeleteContext.FileSize = Fcb->Header.FileSize.LowPart; + DeleteContext.FirstClusterOfFile = Fcb->FirstClusterOfFile; + + DebugTrace(0, Dbg, "Delete File allocation\n", 0); + + // + // Synchronize here with paging IO + // + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, + TRUE ); + + Fcb->Header.FileSize.LowPart = 0; + Fcb->Header.ValidDataLength.LowPart = 0; + Fcb->ValidDataToDisk = 0; + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + _SEH2_TRY { + + FatSetFileSizeInDirent( IrpContext, Fcb, NULL ); + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; + + } else { + + // + // We must zero between ValidDataLength and FileSize + // + + if (!FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) && + (Fcb->Header.ValidDataLength.LowPart < Fcb->Header.FileSize.LowPart)) { + + ULONG ValidDataLength; + + ValidDataLength = Fcb->Header.ValidDataLength.LowPart; + + if (ValidDataLength < Fcb->ValidDataToDisk) { + ValidDataLength = Fcb->ValidDataToDisk; + } + + // + // Recheck, VDD can be >= FS + // + + if (ValidDataLength < Fcb->Header.FileSize.LowPart) { + + _SEH2_TRY { + + (VOID)FatZeroData( IrpContext, + Vcb, + FileObject, + ValidDataLength, + Fcb->Header.FileSize.LowPart - + ValidDataLength ); + + // + // Since we just zeroed this, we can now bump + // up VDL in the Fcb. + // + + Fcb->ValidDataToDisk = + Fcb->Header.ValidDataLength.LowPart = + Fcb->Header.FileSize.LowPart; + + // + // We inform Cc of the motion so that the cache map is updated. + // This prevents optimized zero-page faults in case the cache + // structures are re-used for another handle before they are torn + // down by our soon-to-occur uninitialize. If they were, a noncached + // producer could write into the region we just zeroed and Cc would + // be none the wiser, then our async cached reader comes in and takes + // the optimized path, and we get bad (zero) data. + // + // If this was memory mapped, we don't have to (can't) tell Cc, it'll + // figure it out when a cached handle is opened. + // + + if (CcIsFileCached( FileObject )) { + CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); + } + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + } + } + } + + // + // See if we are supposed to truncate the file on the last + // close. If we cannot wait we'll ship this off to the fsp + // + + _SEH2_TRY { + + if (FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE)) { + + DebugTrace(0, Dbg, "truncate file allocation\n", 0); + + if (Vcb->VcbCondition == VcbGood) { + + FatTruncateFileAllocation( IrpContext, + Fcb, + Fcb->Header.FileSize.LowPart ); + } + + // + // We also have to get rid of the Cache Map because + // this is the only way we have of trashing the + // truncated pages. + // + + LocalTruncateSize = Fcb->Header.FileSize; + TruncateSize = &LocalTruncateSize; + + // + // Mark the Fcb as having now been truncated, just incase + // we have to reship this off to the fsp. + // + + Fcb->FcbState &= ~FCB_STATE_TRUNCATE_ON_CLOSE; + } + + // + // Now check again if we are to delete the file and if + // so then we remove the file from the disk. + // + + if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE) && + Fcb->Header.AllocationSize.LowPart == 0) { + + DebugTrace(0, Dbg, "Delete File\n", 0); + + // + // Now tunnel and delete the dirent + // + + FatTunnelFcbOrDcb( Fcb, Ccb ); + + FatDeleteDirent( IrpContext, Fcb, &DeleteContext, TRUE ); + + // + // Report that we have removed an entry. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_FILE_NAME, + FILE_ACTION_REMOVED ); + } + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + if (FlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE)) { + + // + // Remove the entry from the splay table. This will + // ensure that we will not collide with the Fcb if the + // user wants to recreate the same file over again + // before we get a close irp. + // + // Note that we remove the name even if we couldn't + // truncate the allocation and remove the dirent above. + // + + FatRemoveNames( IrpContext, Fcb ); + } + } + } + + // + // We've just finished everything associated with an unclean + // fcb so now decrement the unclean count before releasing + // the resource. + // + + ASSERT( Fcb->UncleanCount != 0 ); + Fcb->UncleanCount -= 1; + if (!FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED )) { + ASSERT( Fcb->NonCachedUncleanCount != 0 ); + Fcb->NonCachedUncleanCount -= 1; + } + + // + // If this was the last cached open, and there are open + // non-cached handles, attempt a flush and purge operation + // to avoid cache coherency overhead from these non-cached + // handles later. We ignore any I/O errors from the flush. + // + + if (FlagOn( FileObject->Flags, FO_CACHE_SUPPORTED ) && + (Fcb->NonCachedUncleanCount != 0) && + (Fcb->NonCachedUncleanCount == Fcb->UncleanCount) && + (Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL)) { + + CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, NULL ); + + // + // Grab and release PagingIo to serialize ourselves with the lazy writer. + // This will work to ensure that all IO has completed on the cached + // data and we will succesfully tear away the cache section. + // + + ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE); + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers, + NULL, + 0, + FALSE ); + } + + // + // If the file is invalid, hint to the cache that we should throw everything out. + // + + if ( Fcb->FcbCondition == FcbBad ) { + + TruncateSize = &FatLargeZero; + } + + // + // Cleanup the cache map + // + + CcUninitializeCacheMap( FileObject, TruncateSize, NULL ); + + break; + + default: + + FatBugCheck( TypeOfOpen, 0, 0 ); + } + + // + // We must clean up the share access at this time, since we may not + // get a Close call for awhile if the file was mapped through this + // File Object. + // + + if (ShareAccess != NULL) { + + DebugTrace(0, Dbg, "Cleanup the Share access\n", 0); + IoRemoveShareAccess( FileObject, ShareAccess ); + } + + if (TypeOfOpen == UserFileOpen) { + + // + // Coordinate the cleanup operation with the oplock state. + // Cleanup operations can always cleanup immediately. + // + + FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock, + Irp, + IrpContext, + NULL, + NULL ); + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + } + + // + // First set the FO_CLEANUP_COMPLETE flag. + // + + SetFlag( FileObject->Flags, FO_CLEANUP_COMPLETE ); + + Status = STATUS_SUCCESS; + + // + // Now unpin any repinned Bcbs. + // + + FatUnpinRepinnedBcbs( IrpContext ); + + // + // If this was deferred flush media, flush the volume. + // We used to do this in lieu of write through for all removable + // media. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Flush the file. + // + + if ((TypeOfOpen == UserFileOpen) && + FlagOn(FileObject->Flags, FO_FILE_MODIFIED)) { + + Status = FatFlushFile( IrpContext, Fcb, Flush ); + } + + // + // If that worked ok, then see if we should flush the FAT as well. + // + + if (NT_SUCCESS(Status) && Fcb && !FatIsFat12( Vcb) && + FlagOn( Fcb->FcbState, FCB_STATE_FLUSH_FAT)) { + + Status = FatFlushFat( IrpContext, Vcb); + } + + if (!NT_SUCCESS(Status)) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + + } _SEH2_FINALLY { + + DebugUnwind( FatCommonCleanup ); + + if (AcquiredFcb) { FatReleaseFcb( IrpContext, Fcb ); } + if (AcquiredVcb) { FatReleaseVcb( IrpContext, Vcb ); } + + if (SendUnlockNotification) { + + FsRtlNotifyVolumeEvent( FileObject, FSRTL_VOLUME_UNLOCK ); + } + + // + // If this is a normal termination then complete the request + // + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonCleanup -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + +VOID +FatAutoUnlock ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) +{ + KIRQL SavedIrql; + + // + // Unlock the volume. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED ); + + Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED; + Vcb->FileObjectWithVcbLocked = NULL; + + IoReleaseVpbSpinLock( SavedIrql ); +} + + diff --git a/drivers/filesystems/fastfat_new/close.c b/drivers/filesystems/fastfat_new/close.c new file mode 100644 index 00000000000..b101c564ec2 --- /dev/null +++ b/drivers/filesystems/fastfat_new/close.c @@ -0,0 +1,1224 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Close.c + +Abstract: + + This module implements the File Close routine for Fat called by the + dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_CLOSE) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_CLOSE) + +ULONG FatMaxDelayedCloseCount; + + +#define FatAcquireCloseMutex() { \ + ASSERT(KeAreApcsDisabled()); \ + ExAcquireFastMutexUnsafe( &FatCloseQueueMutex ); \ +} + +#define FatReleaseCloseMutex() { \ + ASSERT(KeAreApcsDisabled()); \ + ExReleaseFastMutexUnsafe( &FatCloseQueueMutex ); \ +} + +// +// Local procedure prototypes +// + +VOID +FatQueueClose ( + IN PCLOSE_CONTEXT CloseContext, + IN BOOLEAN DelayClose + ); + +PCLOSE_CONTEXT +FatRemoveClose ( + PVCB Vcb OPTIONAL, + PVCB LastVcbHint OPTIONAL + ); + +VOID +NTAPI +FatCloseWorker ( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatFsdClose) +#pragma alloc_text(PAGE, FatFspClose) +#pragma alloc_text(PAGE, FatCommonClose) +#pragma alloc_text(PAGE, FatCloseWorker) +#endif + + +NTSTATUS +NTAPI +FatFsdClose ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of Close. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp; + PFILE_OBJECT FileObject; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + TYPE_OF_OPEN TypeOfOpen; + + BOOLEAN TopLevel; + + // + // If we were called with our file system device object instead of a + // volume device object, just complete this request with STATUS_SUCCESS + // + + if (FatDeviceIsFatFsdo( VolumeDeviceObject)) { + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + + return STATUS_SUCCESS; + } + + DebugTrace(+1, Dbg, "FatFsdClose\n", 0); + + // + // Call the common Close routine + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + // + // Get a pointer to the current stack location and the file object + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + FileObject = IrpSp->FileObject; + + // + // Decode the file object and set the read-only bit in the Ccb. + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + if (Ccb && IsFileObjectReadOnly(FileObject)) { + + SetFlag( Ccb->Flags, CCB_FLAG_READ_ONLY ); + } + + _SEH2_TRY { + + PCLOSE_CONTEXT CloseContext; + + // + // If we are top level, WAIT can be TRUE, otherwise make it FALSE + // to avoid deadlocks, unless this is a top + // level request not originating from the system process. + // + + BOOLEAN Wait = TopLevel && (PsGetCurrentProcess() != FatData.OurProcess); + + // + // Call the common Close routine if we are not delaying this close. + // + + if ((((TypeOfOpen == UserFileOpen) || + (TypeOfOpen == UserDirectoryOpen)) && + FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE) && + !FatData.ShutdownStarted) || + (FatCommonClose(Vcb, Fcb, Ccb, TypeOfOpen, Wait, NULL) == STATUS_PENDING)) { + + // + // Metadata streams have had close contexts preallocated. + // + + if (TypeOfOpen == VirtualVolumeFile || + TypeOfOpen == DirectoryFile || + TypeOfOpen == EaFile) { + + CloseContext = FatAllocateCloseContext(); + ASSERT( CloseContext != NULL ); + CloseContext->Free = TRUE; + + } else { + + // + // Free up any query template strings before using the close context fields, + // which overlap (union) + // + + FatDeallocateCcbStrings( Ccb); + + CloseContext = &Ccb->CloseContext; + CloseContext->Free = FALSE; + + SetFlag( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT ); + } + + // + // If the status is pending, then let's get the information we + // need into the close context we already have bagged, complete + // the request, and post it. It is important we allocate nothing + // in the close path. + // + + CloseContext->Vcb = Vcb; + CloseContext->Fcb = Fcb; + CloseContext->TypeOfOpen = TypeOfOpen; + + // + // Send it off, either to an ExWorkerThread or to the async + // close list. + // + + FatQueueClose( CloseContext, + (BOOLEAN)(Fcb && FlagOn(Fcb->FcbState, FCB_STATE_DELAY_CLOSE))); + } else { + + // + // The close proceeded synchronously, so for the metadata objects we + // can now drop the close context we preallocated. + // + + if (TypeOfOpen == VirtualVolumeFile || + TypeOfOpen == DirectoryFile || + TypeOfOpen == EaFile) { + + CloseContext = FatAllocateCloseContext(); + ASSERT( CloseContext != NULL ); + + ExFreePool( CloseContext ); + } + + } + + FatCompleteRequest( FatNull, Irp, Status ); + + } _SEH2_EXCEPT(FatExceptionFilter( NULL, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with the + // error status that we get back from the execption code. + // + + Status = FatProcessException( NULL, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdClose -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + +VOID +NTAPI +FatCloseWorker ( + IN PDEVICE_OBJECT DeviceObject, + IN PVOID Context + ) +/*++ + +Routine Description: + + This routine is a shim between the IO worker package and FatFspClose. + +Arguments: + + DeviceObject - Registration device object, unused + Context - Context value, unused + +Return Value: + + None. + +--*/ +{ + FsRtlEnterFileSystem(); + + FatFspClose (Context); + + FsRtlExitFileSystem(); +} + + +VOID +FatFspClose ( + IN PVCB Vcb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine implements the FSP part of Close. + +Arguments: + + Vcb - If present, tells us to only close file objects opened on the + specified volume. + +Return Value: + + None. + +--*/ + +{ + PCLOSE_CONTEXT CloseContext; + PVCB CurrentVcb = NULL; + PVCB LastVcb = NULL; + BOOLEAN FreeContext; + + ULONG LoopsWithVcbHeld; + + DebugTrace(+1, Dbg, "FatFspClose\n", 0); + + // + // Set the top level IRP for the true FSP operation. + // + + if (!ARGUMENT_PRESENT( Vcb )) { + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + } + +#ifndef __REACTOS__ + while (CloseContext = FatRemoveClose(Vcb, LastVcb)) { +#else + while ((CloseContext = FatRemoveClose(Vcb, LastVcb)) != NULL) { +#endif + + // + // If we are in the FSP (i.e. Vcb == NULL), then try to keep ahead of + // creates by doing several closes with one acquisition of the Vcb. + // + // Note that we cannot be holding the Vcb on entry to FatCommonClose + // if this is last close as we will try to acquire FatData, and + // worse the volume (and therefore the Vcb) may go away. + // + + if (!ARGUMENT_PRESENT(Vcb)) { + + if (!FatData.ShutdownStarted) { + + if (CloseContext->Vcb != CurrentVcb) { + + LoopsWithVcbHeld = 0; + + // + // Release a previously held Vcb, if any. + // + + if (CurrentVcb != NULL) { + + ExReleaseResourceLite( &CurrentVcb->Resource); + } + + // + // Get the new Vcb. + // + + CurrentVcb = CloseContext->Vcb; + (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE ); + + } else { + + // + // Share the resource occasionally if we seem to be finding a lot + // of closes for a single volume. + // + + if (++LoopsWithVcbHeld >= 20) { + + if (ExGetSharedWaiterCount( &CurrentVcb->Resource ) + + ExGetExclusiveWaiterCount( &CurrentVcb->Resource )) { + + ExReleaseResourceLite( &CurrentVcb->Resource); + (VOID)ExAcquireResourceExclusiveLite( &CurrentVcb->Resource, TRUE ); + } + + LoopsWithVcbHeld = 0; + } + } + + // + // Now check the Open count. We may be about to delete this volume! + // + // The test below must be <= 1 because there could still be outstanding + // stream references on this VCB that are not counted in the OpenFileCount. + // For example if there are no open files OpenFileCount could be zero and we would + // not release the resource here. The call to FatCommonClose() below may cause + // the VCB to be torn down and we will try to release memory we don't + // own later. + // + + if (CurrentVcb->OpenFileCount <= 1) { + + ExReleaseResourceLite( &CurrentVcb->Resource); + CurrentVcb = NULL; + } + // + // If shutdown has started while processing our list, drop the + // current Vcb resource. + // + + } else if (CurrentVcb != NULL) { + + ExReleaseResourceLite( &CurrentVcb->Resource); + CurrentVcb = NULL; + } + } + + LastVcb = CurrentVcb; + + // + // Call the common Close routine. Protected in a try {} except {} + // + + _SEH2_TRY { + + // + // The close context either is in the CCB, automatically freed, + // or was from pool for a metadata fileobject, CCB is NULL, and + // we'll need to free it. + // + + FreeContext = CloseContext->Free; + + (VOID)FatCommonClose( CloseContext->Vcb, + CloseContext->Fcb, + (FreeContext ? NULL : + CONTAINING_RECORD( CloseContext, CCB, CloseContext)), + CloseContext->TypeOfOpen, + TRUE, + NULL ); + + } _SEH2_EXCEPT(FatExceptionFilter( NULL, _SEH2_GetExceptionInformation() )) { + + // + // Ignore anything we expect. + // + + NOTHING; + } _SEH2_END; + + // + // Drop the context if it came from pool. + // + + if (FreeContext) { + + ExFreePool( CloseContext ); + } + } + + // + // Release a previously held Vcb, if any. + // + + if (CurrentVcb != NULL) { + + ExReleaseResourceLite( &CurrentVcb->Resource); + } + + // + // Clean up the top level IRP hint if we owned it. + // + + if (!ARGUMENT_PRESENT( Vcb )) { + + IoSetTopLevelIrp( NULL ); + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFspClose -> NULL\n", 0); + + return; +} + + +VOID +FatQueueClose ( + IN PCLOSE_CONTEXT CloseContext, + IN BOOLEAN DelayClose + ) + +/*++ + +Routine Description: + + Enqueue a deferred close to one of the two delayed close queues. + +Arguments: + + CloseContext - a close context to enqueue for the delayed close thread. + + DelayClose - whether this should go on the delayed close queue (unreferenced + objects). + +Return Value: + + None. + +--*/ + +{ + BOOLEAN StartWorker = FALSE; + + FatAcquireCloseMutex(); + + if (DelayClose) { + + InsertTailList( &FatData.DelayedCloseList, + &CloseContext->GlobalLinks ); + InsertTailList( &CloseContext->Vcb->DelayedCloseList, + &CloseContext->VcbLinks ); + + FatData.DelayedCloseCount += 1; + + if ((FatData.DelayedCloseCount > FatMaxDelayedCloseCount) && + !FatData.AsyncCloseActive) { + + FatData.AsyncCloseActive = TRUE; + StartWorker = TRUE; + } + + } else { + + InsertTailList( &FatData.AsyncCloseList, + &CloseContext->GlobalLinks ); + InsertTailList( &CloseContext->Vcb->AsyncCloseList, + &CloseContext->VcbLinks ); + + FatData.AsyncCloseCount += 1; + + if (!FatData.AsyncCloseActive) { + + FatData.AsyncCloseActive = TRUE; + StartWorker = TRUE; + } + } + + FatReleaseCloseMutex(); + + if (StartWorker) { + + IoQueueWorkItem( FatData.FatCloseItem, FatCloseWorker, CriticalWorkQueue, NULL ); + } +} + + +PCLOSE_CONTEXT +FatRemoveClose ( + PVCB Vcb OPTIONAL, + PVCB LastVcbHint OPTIONAL + ) + +/*++ + +Routine Description: + + Dequeue a deferred close from one of the two delayed close queues. + +Arguments: + + Vcb - if specified, only returns close for this volume. + + LastVcbHint - if specified and other starvation avoidance is required by + the system condition, will attempt to return closes for this volume. + +Return Value: + + A close to perform. + +--*/ + +{ + PLIST_ENTRY Entry; + PCLOSE_CONTEXT CloseContext; + BOOLEAN WorkerThread; + + FatAcquireCloseMutex(); + + // + // Remember if this is the worker thread, so we can pull down the active + // flag should we run everything out. + // + + WorkerThread = (Vcb == NULL); + + // + // If the queues are above the limits by a significant amount, we have + // to try hard to pull them down. To do this, we will aggresively try + // to find closes for the last volume the caller looked at. This will + // make sure we fully utilize the acquisition of the volume, which can + // be a hugely expensive resource to get (create/close/cleanup use it + // exclusively). + // + // Only do this in the delayed close thread. We will know this is the + // case by seeing a NULL mandatory Vcb. + // + + if (Vcb == NULL && LastVcbHint != NULL) { + + // + // Flip over to aggressive at twice the legal limit, and flip it + // off at the legal limit. + // + + if (!FatData.HighAsync && FatData.AsyncCloseCount > FatMaxDelayedCloseCount*2) { + + FatData.HighAsync = TRUE; + + } else if (FatData.HighAsync && FatData.AsyncCloseCount < FatMaxDelayedCloseCount) { + + FatData.HighAsync = FALSE; + } + + if (!FatData.HighDelayed && FatData.DelayedCloseCount > FatMaxDelayedCloseCount*2) { + + FatData.HighDelayed = TRUE; + + } else if (FatData.HighDelayed && FatData.DelayedCloseCount < FatMaxDelayedCloseCount) { + + FatData.HighDelayed = FALSE; + } + + if (FatData.HighAsync || FatData.HighDelayed) { + + Vcb = LastVcbHint; + } + } + + // + // Do the case when we don't care about which Vcb the close is on. + // This is the case when we are in an ExWorkerThread and aren't + // under pressure. + // + + if (Vcb == NULL) { + + AnyClose: + + // + // First check the list of async closes. + // + + if (!IsListEmpty( &FatData.AsyncCloseList )) { + + Entry = RemoveHeadList( &FatData.AsyncCloseList ); + FatData.AsyncCloseCount -= 1; + + CloseContext = CONTAINING_RECORD( Entry, + CLOSE_CONTEXT, + GlobalLinks ); + + RemoveEntryList( &CloseContext->VcbLinks ); + + // + // Do any delayed closes over half the limit, unless shutdown has + // started (then kill them all). + // + + } else if (!IsListEmpty( &FatData.DelayedCloseList ) && + (FatData.DelayedCloseCount > FatMaxDelayedCloseCount/2 || + FatData.ShutdownStarted)) { + + Entry = RemoveHeadList( &FatData.DelayedCloseList ); + FatData.DelayedCloseCount -= 1; + + CloseContext = CONTAINING_RECORD( Entry, + CLOSE_CONTEXT, + GlobalLinks ); + + RemoveEntryList( &CloseContext->VcbLinks ); + + // + // There are no more closes to perform; show that we are done. + // + + } else { + + CloseContext = NULL; + + if (WorkerThread) { + + FatData.AsyncCloseActive = FALSE; + } + } + + // + // We're running down a specific volume. + // + + } else { + + + // + // First check the list of async closes. + // + + if (!IsListEmpty( &Vcb->AsyncCloseList )) { + + Entry = RemoveHeadList( &Vcb->AsyncCloseList ); + FatData.AsyncCloseCount -= 1; + + CloseContext = CONTAINING_RECORD( Entry, + CLOSE_CONTEXT, + VcbLinks ); + + RemoveEntryList( &CloseContext->GlobalLinks ); + + // + // Do any delayed closes. + // + + } else if (!IsListEmpty( &Vcb->DelayedCloseList )) { + + Entry = RemoveHeadList( &Vcb->DelayedCloseList ); + FatData.DelayedCloseCount -= 1; + + CloseContext = CONTAINING_RECORD( Entry, + CLOSE_CONTEXT, + VcbLinks ); + + RemoveEntryList( &CloseContext->GlobalLinks ); + + // + // If we were trying to run down the queues but didn't find anything for this + // volume, flip over to accept anything and try again. + // + + } else if (LastVcbHint) { + + goto AnyClose; + + // + // There are no more closes to perform; show that we are done. + // + + } else { + + CloseContext = NULL; + } + } + + FatReleaseCloseMutex(); + + return CloseContext; +} + + +NTSTATUS +FatCommonClose ( + IN PVCB Vcb, + IN PFCB Fcb, + IN PCCB Ccb, + IN TYPE_OF_OPEN TypeOfOpen, + IN BOOLEAN Wait, + OUT PBOOLEAN VcbDeleted OPTIONAL + ) + +/*++ + +Routine Description: + + This is the common routine for closing a file/directory called by both + the fsd and fsp threads. + + Close is invoked whenever the last reference to a file object is deleted. + Cleanup is invoked when the last handle to a file object is closed, and + is called before close. + + The function of close is to completely tear down and remove the fcb/dcb/ccb + structures associated with the file object. + +Arguments: + + Fcb - Supplies the file to process. + + Wait - If this is TRUE we are allowed to block for the Vcb, if FALSE + then we must try to acquire the Vcb anyway. + + VcbDeleted - Returns whether the VCB was deleted by this call. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PDCB ParentDcb; + BOOLEAN RecursiveClose; + BOOLEAN LocalVcbDeleted; + IRP_CONTEXT IrpContext; + + DebugTrace(+1, Dbg, "FatCommonClose...\n", 0); + + // + // Initailize the callers variable, if needed. + // + + LocalVcbDeleted = FALSE; + + if (ARGUMENT_PRESENT( VcbDeleted )) { + + *VcbDeleted = LocalVcbDeleted; + } + + // + // Special case the unopened file object + // + + if (TypeOfOpen == UnopenedFileObject) { + + DebugTrace(0, Dbg, "Close unopened file object\n", 0); + + Status = STATUS_SUCCESS; + + DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); + return Status; + } + + // + // Set up our stack IrpContext. + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + + if (Wait) { + + SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); + } + + // + // Acquire exclusive access to the Vcb and enqueue the irp if we didn't + // get access. + // + + if (!ExAcquireResourceExclusiveLite( &Vcb->Resource, Wait )) { + + return STATUS_PENDING; + } + + // + // The following test makes sure that we don't blow away an Fcb if we + // are trying to do a Supersede/Overwrite open above us. This test + // does not apply for the EA file. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS) && + Vcb->EaFcb != Fcb) { + + ExReleaseResourceLite( &Vcb->Resource ); + + return STATUS_PENDING; + } + + // + // Setting the following flag prevents recursive closes of directory file + // objects, which are handled in a special case loop. + // + + if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS) ) { + + RecursiveClose = TRUE; + + } else { + + SetFlag(Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS); + RecursiveClose = FALSE; + + // + // Since we are at the top of the close chain, we need to add + // a reference to the VCB. This will keep it from going away + // on us until we are ready to check for a dismount below. + // + + Vcb->OpenFileCount += 1; + } + + _SEH2_TRY { + + // + // Case on the type of open that we are trying to close. + // + + switch (TypeOfOpen) { + + case VirtualVolumeFile: + + DebugTrace(0, Dbg, "Close VirtualVolumeFile\n", 0); + + // + // Remove this internal, residual open from the count + // + +#ifndef __REACTOS__ + InterlockedDecrement( &(Vcb->InternalOpenCount) ); + InterlockedDecrement( &(Vcb->ResidualOpenCount) ); +#else + + InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) ); + InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) ); +#endif + + try_return( Status = STATUS_SUCCESS ); + break; + + case UserVolumeOpen: + + DebugTrace(0, Dbg, "Close UserVolumeOpen\n", 0); + + Vcb->DirectAccessOpenCount -= 1; + Vcb->OpenFileCount -= 1; + if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } + + FatDeleteCcb( &IrpContext, Ccb ); + + try_return( Status = STATUS_SUCCESS ); + break; + + case EaFile: + + DebugTrace(0, Dbg, "Close EaFile\n", 0); + + // + // Remove this internal, residual open from the count + // + +#ifndef __REACTOS__ + InterlockedDecrement( &(Vcb->InternalOpenCount) ); + InterlockedDecrement( &(Vcb->ResidualOpenCount) ); +#else + + InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) ); + InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) ); +#endif + + try_return( Status = STATUS_SUCCESS ); + break; + + case DirectoryFile: + + DebugTrace(0, Dbg, "Close DirectoryFile\n", 0); + +#ifndef __REACTOS__ + InterlockedDecrement( &Fcb->Specific.Dcb.DirectoryFileOpenCount ); +#else + InterlockedDecrement( (LONG*)&Fcb->Specific.Dcb.DirectoryFileOpenCount ); +#endif + + // + // Remove this internal open from the count + // + +#ifndef __REACTOS__ + InterlockedDecrement( &(Vcb->InternalOpenCount) ); +#else + InterlockedDecrement( (LONG*)&(Vcb->InternalOpenCount) ); +#endif + + // + // If this is the root directory, it is a residual open + // as well + // + + if (NodeType( Fcb ) == FAT_NTC_ROOT_DCB) { + +#ifndef __REACTOS__ + InterlockedDecrement( &(Vcb->ResidualOpenCount) ); +#else + InterlockedDecrement( (LONG*)&(Vcb->ResidualOpenCount) ); +#endif + } + + // + // If this is a recursive close, just return here. + // + + if ( RecursiveClose ) { + + try_return( Status = STATUS_SUCCESS ); + + } else { + + break; + } + + case UserDirectoryOpen: + case UserFileOpen: + + DebugTrace(0, Dbg, "Close UserFileOpen/UserDirectoryOpen\n", 0); + + // + // Uninitialize the cache map if we no longer need to use it + // + + if ((NodeType(Fcb) == FAT_NTC_DCB) && + IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) && + (Fcb->OpenCount == 1) && + (Fcb->Specific.Dcb.DirectoryFile != NULL)) { + + PFILE_OBJECT DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile; + + DebugTrace(0, Dbg, "Uninitialize the stream file object\n", 0); + + CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); + + // + // Dereference the directory file. This may cause a close + // Irp to be processed, so we need to do this before we destory + // the Fcb. + // + + Fcb->Specific.Dcb.DirectoryFile = NULL; + ObDereferenceObject( DirectoryFileObject ); + } + + Fcb->OpenCount -= 1; + Vcb->OpenFileCount -= 1; + if (FlagOn(Ccb->Flags, CCB_FLAG_READ_ONLY)) { Vcb->ReadOnlyCount -= 1; } + + FatDeleteCcb( &IrpContext, Ccb ); + + break; + + default: + + FatBugCheck( TypeOfOpen, 0, 0 ); + } + + // + // At this point we've cleaned up any on-disk structure that needs + // to be done, and we can now update the in-memory structures. + // Now if this is an unreferenced FCB or if it is + // an unreferenced DCB (not the root) then we can remove + // the fcb and set our ParentDcb to non null. + // + + if (((NodeType(Fcb) == FAT_NTC_FCB) && + (Fcb->OpenCount == 0)) + + || + + ((NodeType(Fcb) == FAT_NTC_DCB) && + (IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) && + (Fcb->OpenCount == 0) && + (Fcb->Specific.Dcb.DirectoryFileOpenCount == 0))) { + + ParentDcb = Fcb->ParentDcb; + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); + + FatDeleteFcb( &IrpContext, Fcb ); + + // + // Uninitialize our parent's cache map if we no longer need + // to use it. + // + + while ((NodeType(ParentDcb) == FAT_NTC_DCB) && + IsListEmpty(&ParentDcb->Specific.Dcb.ParentDcbQueue) && + (ParentDcb->OpenCount == 0) && + (ParentDcb->Specific.Dcb.DirectoryFile != NULL)) { + + PFILE_OBJECT DirectoryFileObject; + + DirectoryFileObject = ParentDcb->Specific.Dcb.DirectoryFile; + + DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0); + + CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); + + ParentDcb->Specific.Dcb.DirectoryFile = NULL; + + ObDereferenceObject( DirectoryFileObject ); + + // + // Now, if the ObDereferenceObject() caused the final close + // to come in, then blow away the Fcb and continue up, + // otherwise wait for Mm to to dereference its file objects + // and stop here.. + // + + if ( ParentDcb->Specific.Dcb.DirectoryFileOpenCount == 0) { + + PDCB CurrentDcb; + + CurrentDcb = ParentDcb; + ParentDcb = CurrentDcb->ParentDcb; + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); + + FatDeleteFcb( &IrpContext, CurrentDcb ); + + } else { + + break; + } + } + } + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatCommonClose ); + + // + // We are done processing the close. If we are the top of the close + // chain, see if the VCB can go away. We have biased the open count by + // one, so we need to take that into account. + // + + if (!RecursiveClose) { + + // + // See if there is only one open left. If so, it is ours. We only want + // to check for a dismount if a dismount is not already in progress. + // + + if (Vcb->OpenFileCount == 1 && !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS )) { + + // + // We need the global lock, which must be acquired before the + // VCB. Since we already have the VCB, we have to drop and + // reaquire here. Note that we always want to wait from this + // point on. Note that the VCB cannot go away, since we have + // biased the open file count. + // + + FatReleaseVcb( &IrpContext, + Vcb ); + + SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); + + FatAcquireExclusiveGlobal( &IrpContext ); + + FatAcquireExclusiveVcb( &IrpContext, + Vcb ); + + // + // We have our locks in the correct order. Remove our + // extra open and check for a dismount. Note that if + // something changed while we dropped the lock, it will + // not matter, since the dismount code does the correct + // checks to make sure the volume can really go away. + // + + Vcb->OpenFileCount -= 1; + + LocalVcbDeleted = FatCheckForDismount( &IrpContext, + Vcb, + FALSE ); + + FatReleaseGlobal( &IrpContext ); + + // + // Let the caller know what happened, if they want this information. + // + + if (ARGUMENT_PRESENT( VcbDeleted )) { + + *VcbDeleted = LocalVcbDeleted; + } + + } else { + + // + // The volume cannot go away now. Just remove our extra reference. + // + + Vcb->OpenFileCount -= 1; + } + + // + // If the VCB is still around, clear our recursion flag. + // + + if (!LocalVcbDeleted) { + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_CLOSE_IN_PROGRESS ); + } + } + + // + // Only release the VCB if it did not go away. + // + + if (!LocalVcbDeleted) { + + FatReleaseVcb( &IrpContext, Vcb ); + } + + DebugTrace(-1, Dbg, "FatCommonClose -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + diff --git a/drivers/filesystems/fastfat_new/create.c b/drivers/filesystems/fastfat_new/create.c new file mode 100644 index 00000000000..b8bc949ee95 --- /dev/null +++ b/drivers/filesystems/fastfat_new/create.c @@ -0,0 +1,5684 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Create.c + +Abstract: + + This module implements the File Create routine for Fat called by the + dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_CREATE) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_CREATE) + + +// +// Macros for incrementing performance counters. +// + +#define CollectCreateHitStatistics(VCB) { \ + PFILE_SYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()]; \ + Stats->Fat.CreateHits += 1; \ +} + +#define CollectCreateStatistics(VCB,STATUS) { \ + PFILE_SYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()]; \ + if ((STATUS) == STATUS_SUCCESS) { \ + Stats->Fat.SuccessfulCreates += 1; \ + } else { \ + Stats->Fat.FailedCreates += 1; \ + } \ +} + +LUID FatSecurityPrivilege = { SE_SECURITY_PRIVILEGE, 0 }; + +// +// local procedure prototypes +// + +IO_STATUS_BLOCK +FatOpenVolume ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG CreateDisposition + ); + +IO_STATUS_BLOCK +FatOpenRootDcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG CreateDisposition + ); + +IO_STATUS_BLOCK +FatOpenExistingDcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB Dcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG CreateDisposition, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose + ); + +IO_STATUS_BLOCK +FatOpenExistingFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PFCB Fcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG AllocationSize, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN ULONG CreateDisposition, + IN BOOLEAN DeleteOnClose, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN FileNameOpenedDos, + OUT PBOOLEAN OplockPostIrp + ); + +IO_STATUS_BLOCK +FatOpenTargetDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PDCB Dcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN BOOLEAN DoesNameExist + ); + +IO_STATUS_BLOCK +FatOpenExistingDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN PDIRENT Dirent, + IN ULONG LfnByteOffset, + IN ULONG DirentByteOffset, + IN PUNICODE_STRING Lfn, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG CreateDisposition, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose + ); + +IO_STATUS_BLOCK +FatOpenExistingFile ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN PDIRENT Dirent, + IN ULONG LfnByteOffset, + IN ULONG DirentByteOffset, + IN PUNICODE_STRING Lfn, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG AllocationSize, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN ULONG CreateDisposition, + IN BOOLEAN IsPagingFile, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose, + IN BOOLEAN FileNameOpenedDos + ); + +IO_STATUS_BLOCK +FatCreateNewDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN POEM_STRING OemName, + IN PUNICODE_STRING UnicodeName, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose + ); + +IO_STATUS_BLOCK +FatCreateNewFile ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN POEM_STRING OemName, + IN PUNICODE_STRING UnicodeName, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG AllocationSize, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN PUNICODE_STRING LfnBuffer, + IN BOOLEAN IsPagingFile, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose, + IN BOOLEAN TemporaryFile + ); + +IO_STATUS_BLOCK +FatSupersedeOrOverwriteFile ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PFCB Fcb, + IN ULONG AllocationSize, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN ULONG CreateDisposition, + IN BOOLEAN NoEaKnowledge + ); + +NTSTATUS +FatCheckSystemSecurityAccess( + PIRP_CONTEXT IrpContext + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCheckSystemSecurityAccess) +#pragma alloc_text(PAGE, FatCommonCreate) +#pragma alloc_text(PAGE, FatCreateNewDirectory) +#pragma alloc_text(PAGE, FatCreateNewFile) +#pragma alloc_text(PAGE, FatFsdCreate) +#pragma alloc_text(PAGE, FatOpenExistingDcb) +#pragma alloc_text(PAGE, FatOpenExistingDirectory) +#pragma alloc_text(PAGE, FatOpenExistingFcb) +#pragma alloc_text(PAGE, FatOpenExistingFile) +#pragma alloc_text(PAGE, FatOpenRootDcb) +#pragma alloc_text(PAGE, FatOpenTargetDirectory) +#pragma alloc_text(PAGE, FatOpenVolume) +#pragma alloc_text(PAGE, FatSupersedeOrOverwriteFile) +#pragma alloc_text(PAGE, FatSetFullNameInFcb) +#endif + + +NTSTATUS +NTAPI +FatFsdCreate ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtCreateFile and NtOpenFile + API calls. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file/directory exists that we are trying to open/create + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + // + // If we were called with our file system device object instead of a + // volume device object, just complete this request with STATUS_SUCCESS + // + + if ( FatDeviceIsFatFsdo( VolumeDeviceObject)) { + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = FILE_OPENED; + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + + return STATUS_SUCCESS; + } + + TimerStart(Dbg); + + DebugTrace(+1, Dbg, "FatFsdCreate\n", 0); + + // + // Call the common create routine, with block allowed if the operation + // is synchronous. + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, TRUE ); + + Status = FatCommonCreate( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdCreate -> %08lx\n", Status ); + + TimerStop(Dbg,"FatFsdCreate"); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + + +NTSTATUS +FatCommonCreate ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for creating/opening a file called by + both the fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + NTSTATUS Status; + IO_STATUS_BLOCK Iosb; + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + PFILE_OBJECT RelatedFileObject; + UNICODE_STRING FileName; + ULONG AllocationSize; + PFILE_FULL_EA_INFORMATION EaBuffer; + PACCESS_MASK DesiredAccess; + ULONG Options; + UCHAR FileAttributes; + USHORT ShareAccess; + ULONG EaLength; + + BOOLEAN CreateDirectory; +#ifndef __REACTOS__ + BOOLEAN SequentialOnly; +#endif + BOOLEAN NoIntermediateBuffering; + BOOLEAN OpenDirectory; + BOOLEAN IsPagingFile; + BOOLEAN OpenTargetDirectory; + BOOLEAN DirectoryFile; + BOOLEAN NonDirectoryFile; + BOOLEAN NoEaKnowledge; + BOOLEAN DeleteOnClose; + BOOLEAN TemporaryFile; + BOOLEAN FileNameOpenedDos = FALSE; + + ULONG CreateDisposition; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + PDCB ParentDcb; + PDCB FinalDcb = NULL; + + UNICODE_STRING FinalName; + UNICODE_STRING RemainingPart; + UNICODE_STRING NextRemainingPart; + UNICODE_STRING UpcasedFinalName; + WCHAR UpcasedBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE]; + + OEM_STRING OemFinalName; + UCHAR OemBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE*2]; + +#ifndef __REACTOS__ + BOOLEAN FreeOemBuffer; + BOOLEAN FreeUpcasedBuffer; +#endif + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + ULONG LfnByteOffset; + ULONG DirentByteOffset; + + BOOLEAN PostIrp = FALSE; + BOOLEAN OplockPostIrp = FALSE; + BOOLEAN TrailingBackslash; + BOOLEAN FirstLoop = TRUE; + + CCB LocalCcb; + UNICODE_STRING Lfn; + WCHAR LfnBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE]; + + // + // Get the current IRP stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonCreate\n", 0 ); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "->Flags = %08lx\n", Irp->Flags ); + DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject ); + DebugTrace( 0, Dbg, " ->RelatedFileObject = %08lx\n", IrpSp->FileObject->RelatedFileObject ); + DebugTrace( 0, Dbg, " ->FileName = %Z\n", &IrpSp->FileObject->FileName ); + DebugTrace( 0, Dbg, "->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart ); + DebugTrace( 0, Dbg, "->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart ); + DebugTrace( 0, Dbg, "->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer ); + DebugTrace( 0, Dbg, "->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + DebugTrace( 0, Dbg, "->Options = %08lx\n", IrpSp->Parameters.Create.Options ); + DebugTrace( 0, Dbg, "->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes ); + DebugTrace( 0, Dbg, "->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess ); + DebugTrace( 0, Dbg, "->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength ); + + // + // This is here because the Win32 layer can't avoid sending me double + // beginning backslashes. + // + + if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) && + (IrpSp->FileObject->FileName.Buffer[1] == L'\\') && + (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) { + + IrpSp->FileObject->FileName.Length -= sizeof(WCHAR); + + RtlMoveMemory( &IrpSp->FileObject->FileName.Buffer[0], + &IrpSp->FileObject->FileName.Buffer[1], + IrpSp->FileObject->FileName.Length ); + + // + // If there are still two beginning backslashes, the name is bogus. + // + + if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) && + (IrpSp->FileObject->FileName.Buffer[1] == L'\\') && + (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) { + + FatCompleteRequest( IrpContext, Irp, STATUS_OBJECT_NAME_INVALID ); + + DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_OBJECT_NAME_INVALID\n", 0); + return STATUS_OBJECT_NAME_INVALID; + } + } + + // + // Reference our input parameters to make things easier + // + + ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL ); + + FileObject = IrpSp->FileObject; + FileName = FileObject->FileName; + RelatedFileObject = FileObject->RelatedFileObject; + AllocationSize = Irp->Overlay.AllocationSize.LowPart; + EaBuffer = Irp->AssociatedIrp.SystemBuffer; + DesiredAccess = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess; + Options = IrpSp->Parameters.Create.Options; + FileAttributes = (UCHAR)(IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL); + ShareAccess = IrpSp->Parameters.Create.ShareAccess; + EaLength = IrpSp->Parameters.Create.EaLength; + + + // + // Set up the file object's Vpb pointer in case anything happens. + // This will allow us to get a reasonable pop-up. + // + + if ( RelatedFileObject != NULL ) { + FileObject->Vpb = RelatedFileObject->Vpb; + } + + // + // Force setting the archive bit in the attributes byte to follow OS/2, + // & DOS semantics. Also mask out any extraneous bits, note that + // we can't use the ATTRIBUTE_VALID_FLAGS constant because that has + // the control and normal flags set. + // + // Delay setting ARCHIVE in case this is a directory: 2/16/95 + // + + FileAttributes &= (FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_ARCHIVE ); + + // + // Locate the volume device object and Vcb that we are trying to access + // + + Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb; + + // + // Decipher Option flags and values + // + + // + // If this is an open by fileid operation, just fail it explicitly. FAT's + // source of fileids is not reversible for open operations. + // + + if (BooleanFlagOn( Options, FILE_OPEN_BY_FILE_ID )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_NOT_IMPLEMENTED ); + return STATUS_NOT_IMPLEMENTED; + } + + DirectoryFile = BooleanFlagOn( Options, FILE_DIRECTORY_FILE ); + NonDirectoryFile = BooleanFlagOn( Options, FILE_NON_DIRECTORY_FILE ); +#ifndef __REACTOS__ + SequentialOnly = BooleanFlagOn( Options, FILE_SEQUENTIAL_ONLY ); +#endif + NoIntermediateBuffering = BooleanFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING ); + NoEaKnowledge = BooleanFlagOn( Options, FILE_NO_EA_KNOWLEDGE ); + DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE ); + + TemporaryFile = BooleanFlagOn( IrpSp->Parameters.Create.FileAttributes, + FILE_ATTRIBUTE_TEMPORARY ); + + CreateDisposition = (Options >> 24) & 0x000000ff; + + IsPagingFile = BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ); + OpenTargetDirectory = BooleanFlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY ); + + CreateDirectory = (BOOLEAN)(DirectoryFile && + ((CreateDisposition == FILE_CREATE) || + (CreateDisposition == FILE_OPEN_IF))); + + OpenDirectory = (BOOLEAN)(DirectoryFile && + ((CreateDisposition == FILE_OPEN) || + (CreateDisposition == FILE_OPEN_IF))); + + + // + // Make sure the input large integer is valid and that the dir/nondir + // indicates a storage type we understand. + // + + if (Irp->Overlay.AllocationSize.HighPart != 0 || + (DirectoryFile && NonDirectoryFile)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire exclusive access to the vcb, and enqueue the Irp if + // we didn't get it. + // + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); + + Iosb.Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonCreate -> %08lx\n", Iosb.Status ); + return Iosb.Status; + } + + // + // Initialize the DirentBcb to null + // + + DirentBcb = NULL; + + // + // Initialize our temp strings with their stack buffers. + // + + OemFinalName.Length = 0; + OemFinalName.MaximumLength = sizeof( OemBuffer); +#ifndef __REACTOS__ + OemFinalName.Buffer = OemBuffer; +#else + OemFinalName.Buffer = (PCHAR)OemBuffer; +#endif + + UpcasedFinalName.Length = 0; + UpcasedFinalName.MaximumLength = sizeof( UpcasedBuffer); + UpcasedFinalName.Buffer = UpcasedBuffer; + + Lfn.Length = 0; + Lfn.MaximumLength = sizeof( LfnBuffer); + Lfn.Buffer = LfnBuffer; + + _SEH2_TRY { + + // + // Make sure the vcb is in a usable condition. This will raise + // and error condition if the volume is unusable + // + + FatVerifyVcb( IrpContext, Vcb ); + + // + // If the Vcb is locked then we cannot open another file + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) { + + DebugTrace(0, Dbg, "Volume is locked\n", 0); + + Status = STATUS_ACCESS_DENIED; + if (Vcb->VcbCondition != VcbGood) { + + Status = STATUS_VOLUME_DISMOUNTED; + } + try_return( Iosb.Status = Status ); + } + + // + // Don't allow the DELETE_ON_CLOSE option if the volume is + // write-protected. + // + + if (DeleteOnClose && FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + + // + // If this is a fat32 volume, EA's are not supported. + // + + if (EaBuffer != NULL && + FatIsFat32(Vcb)) { + + try_return( Iosb.Status = STATUS_EAS_NOT_SUPPORTED ); + } + + // + // Check if we are opening the volume and not a file/directory. + // We are opening the volume if the name is empty and there + // isn't a related file object. If there is a related file object + // then it is the Vcb itself. + // + + if (FileName.Length == 0) { + + PVCB DecodeVcb; + + if (RelatedFileObject == NULL || + FatDecodeFileObject( RelatedFileObject, + &DecodeVcb, + &Fcb, + &Ccb ) == UserVolumeOpen) { + + ASSERT( RelatedFileObject == NULL || Vcb == DecodeVcb ); + + // + // Check if we were to open a directory + // + + if (DirectoryFile) { + + DebugTrace(0, Dbg, "Cannot open volume as a directory\n", 0); + + try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY ); + } + + // + // Can't open the TargetDirectory of the DASD volume. + // + + if (OpenTargetDirectory) { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + + DebugTrace(0, Dbg, "Opening the volume, Vcb = %08lx\n", Vcb); + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenVolume( IrpContext, + FileObject, + Vcb, + DesiredAccess, + ShareAccess, + CreateDisposition ); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + } + + // + // If there is a related file object then this is a relative open. + // The related file object is the directory to start our search at. + // Return an error if it is not a directory. + // + + if (RelatedFileObject != NULL) { + + PVCB RelatedVcb; + PDCB RelatedDcb; + PCCB RelatedCcb; + TYPE_OF_OPEN TypeOfOpen; + + TypeOfOpen = FatDecodeFileObject( RelatedFileObject, + &RelatedVcb, + &RelatedDcb, + &RelatedCcb ); + + if (TypeOfOpen != UserFileOpen && + TypeOfOpen != UserDirectoryOpen) { + + DebugTrace(0, Dbg, "Invalid related file object\n", 0); + + try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + + // + // A relative open must be via a relative path. + // + + if (FileName.Length != 0 && + FileName.Buffer[0] == L'\\') { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // Set up the file object's Vpb pointer in case anything happens. + // + + ASSERT( Vcb == RelatedVcb ); + + FileObject->Vpb = RelatedFileObject->Vpb; + + ParentDcb = RelatedDcb; + + } else { + + // + // This is not a relative open, so check if we're + // opening the root dcb + // + + if ((FileName.Length == sizeof(WCHAR)) && + (FileName.Buffer[0] == L'\\')) { + + // + // Check if we were not supposed to open a directory + // + + if (NonDirectoryFile) { + + DebugTrace(0, Dbg, "Cannot open root directory as a file\n", 0); + + try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + // + // Can't open the TargetDirectory of the root directory. + // + + if (OpenTargetDirectory) { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + + // + // Not allowed to delete root directory. + // + + if (DeleteOnClose) { + + try_return( Iosb.Status = STATUS_CANNOT_DELETE ); + } + + DebugTrace(0, Dbg, "Opening root dcb\n", 0); + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenRootDcb( IrpContext, + FileObject, + Vcb, + DesiredAccess, + ShareAccess, + CreateDisposition ); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + // + // Nope, we will be opening relative to the root directory. + // + + ParentDcb = Vcb->RootDcb; + } + + // + // FatCommonCreate(): trailing backslash check + // + + + if ((FileName.Length != 0) && + (FileName.Buffer[FileName.Length/sizeof(WCHAR)-1] == L'\\')) { + + FileName.Length -= sizeof(WCHAR); + TrailingBackslash = TRUE; + + } else { + + TrailingBackslash = FALSE; + } + + // + // Check for max path. We might want to tighten this down to DOS MAX_PATH + // for maximal interchange with non-NT platforms, but for now defer to the + // possibility of something depending on it. + // + + if (ParentDcb->FullFileName.Buffer == NULL) { + + FatSetFullFileNameInFcb( IrpContext, ParentDcb ); + } + + if ((USHORT) (ParentDcb->FullFileName.Length + sizeof(WCHAR) + FileName.Length) <= FileName.Length) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // We loop here until we land on an Fcb that is in a good + // condition. This way we can reopen files that have stale handles + // to files of the same name but are now different. + // + + while ( TRUE ) { + + Fcb = ParentDcb; + RemainingPart = FileName; + + // + // Now walk down the Dcb tree looking for the longest prefix. + // This one exit condition in the while() is to handle a + // special case condition (relative NULL name open), the main + // exit conditions are at the bottom of the loop. + // + + while (RemainingPart.Length != 0) { + + PFCB NextFcb; + + FsRtlDissectName( RemainingPart, + &FinalName, + &NextRemainingPart ); + + // + // If RemainingPart starts with a backslash the name is + // invalid. + // Check for no more than 255 characters in FinalName + // + + if (((NextRemainingPart.Length != 0) && (NextRemainingPart.Buffer[0] == L'\\')) || + (FinalName.Length > 255*sizeof(WCHAR))) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // Now, try to convert this one component into Oem and search + // the splay tree. If it works then that's great, otherwise + // we have to try with the UNICODE name instead. + // + + FatEnsureStringBufferEnough( &OemFinalName, + FinalName.Length); + + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE ); + + if (NT_SUCCESS(Status)) { + + NextFcb = FatFindFcb( IrpContext, + &Fcb->Specific.Dcb.RootOemNode, + (PSTRING)&OemFinalName, + &FileNameOpenedDos ); + + } else { + + NextFcb = NULL; + OemFinalName.Length = 0; + + if (Status != STATUS_UNMAPPABLE_CHARACTER) { + + try_return( Iosb.Status = Status ); + } + } + + // + // If we didn't find anything searching the Oem space, we + // have to try the Unicode space. To save cycles in the + // common case that this tree is empty, we do a quick check + // here. + // + + if ((NextFcb == NULL) && Fcb->Specific.Dcb.RootUnicodeNode) { + + // + // First downcase, then upcase the string, because this + // is what happens when putting names into the tree (see + // strucsup.c, FatConstructNamesInFcb()). + // + + FatEnsureStringBufferEnough( &UpcasedFinalName, + FinalName.Length); + + Status = RtlDowncaseUnicodeString(&UpcasedFinalName, &FinalName, FALSE ); + ASSERT( NT_SUCCESS( Status )); + + Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &UpcasedFinalName, FALSE ); + ASSERT( NT_SUCCESS( Status )); + + NextFcb = FatFindFcb( IrpContext, + &Fcb->Specific.Dcb.RootUnicodeNode, + (PSTRING)&UpcasedFinalName, + &FileNameOpenedDos ); + } + + // + // If we got back an Fcb then we consumed the FinalName + // legitimately, so the remaining name is now RemainingPart. + // + + if (NextFcb != NULL) { + Fcb = NextFcb; + RemainingPart = NextRemainingPart; + } + + if ((NextFcb == NULL) || + (NodeType(NextFcb) == FAT_NTC_FCB) || + (NextRemainingPart.Length == 0)) { + + break; + } + } + + // + // Remaining name cannot start with a backslash + // + + if (RemainingPart.Length && (RemainingPart.Buffer[0] == L'\\')) { + + RemainingPart.Length -= sizeof(WCHAR); + RemainingPart.Buffer += 1; + } + + // + // Now verify that everybody up to the longest found prefix is valid. + // + + _SEH2_TRY { + + FatVerifyFcb( IrpContext, Fcb ); + + } _SEH2_EXCEPT( (_SEH2_GetExceptionCode() == STATUS_FILE_INVALID) ? + EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + if ( Fcb->FcbCondition == FcbGood ) { + + // + // If we are trying to open a paging file and have happened + // upon the DelayedCloseFcb, make it go away, and try again. + // + + if (IsPagingFile && FirstLoop && + (NodeType(Fcb) == FAT_NTC_FCB) && + (!IsListEmpty( &FatData.AsyncCloseList ) || + !IsListEmpty( &FatData.DelayedCloseList ))) { + + FatFspClose(Vcb); + + FirstLoop = FALSE; + + continue; + + } else { + + break; + } + + } else { + + FatRemoveNames( IrpContext, Fcb ); + } + } + + ASSERT( Fcb->FcbCondition == FcbGood ); + + // + // If there is already an Fcb for a paging file open and + // it was not already opened as a paging file, we cannot + // continue as it is too difficult to move a live Fcb to + // non-paged pool. + // + + if (IsPagingFile) { + + if (NodeType(Fcb) == FAT_NTC_FCB && + !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + try_return( Iosb.Status = STATUS_SHARING_VIOLATION ); + } + + // + // Check for a system file. + // + + } else if (FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) { + + try_return( Iosb.Status = STATUS_ACCESS_DENIED ); + } + + // + // If the longest prefix is pending delete (either the file or + // some higher level directory), we cannot continue. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) { + + try_return( Iosb.Status = STATUS_DELETE_PENDING ); + } + + // + // Now that we've found the longest matching prefix we'll + // check if there isn't any remaining part because that means + // we've located an existing fcb/dcb to open and we can do the open + // without going to the disk + // + + if (RemainingPart.Length == 0) { + + // + // First check if the user wanted to open the target directory + // and if so then call the subroutine to finish the open. + // + + if (OpenTargetDirectory) { + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenTargetDirectory( IrpContext, + FileObject, + Fcb->ParentDcb, + DesiredAccess, + ShareAccess, + TRUE ); + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + // + // We can open an existing fcb/dcb, now we only need to case + // on which type to open. + // + + if (NodeType(Fcb) == FAT_NTC_DCB || NodeType(Fcb) == FAT_NTC_ROOT_DCB) { + + // + // This is a directory we're opening up so check if + // we were not to open a directory + // + + if (NonDirectoryFile) { + + DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0); + + try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + DebugTrace(0, Dbg, "Open existing dcb, Dcb = %08lx\n", Fcb); + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenExistingDcb( IrpContext, + FileObject, + Vcb, + (PDCB)Fcb, + DesiredAccess, + ShareAccess, + CreateDisposition, + NoEaKnowledge, + DeleteOnClose ); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + // + // Check if we're trying to open an existing Fcb and that + // the user didn't want to open a directory. Note that this + // call might actually come back with status_pending because + // the user wanted to supersede or overwrite the file and we + // cannot block. If it is pending then we do not complete the + // request, and we fall through the bottom to the code that + // dispatches the request to the fsp. + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + // + // Check if we were only to open a directory + // + + if (OpenDirectory) { + + DebugTrace(0, Dbg, "Cannot open file as directory\n", 0); + + try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY ); + } + + DebugTrace(0, Dbg, "Open existing fcb, Fcb = %08lx\n", Fcb); + + if ( TrailingBackslash ) { + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + CollectCreateHitStatistics(Vcb); + + Iosb = FatOpenExistingFcb( IrpContext, + FileObject, + Vcb, + Fcb, + DesiredAccess, + ShareAccess, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + CreateDisposition, + NoEaKnowledge, + DeleteOnClose, + FileNameOpenedDos, + &OplockPostIrp ); + + if (Iosb.Status != STATUS_PENDING) { + + // + // Check if we need to set the cache support flag in + // the file object + // + + if (NT_SUCCESS( Iosb.Status) && !NoIntermediateBuffering) { + + FileObject->Flags |= FO_CACHE_SUPPORTED; + } + + Irp->IoStatus.Information = Iosb.Information; + + } else { + + DebugTrace(0, Dbg, "Enqueue Irp to FSP\n", 0); + + PostIrp = TRUE; + } + + try_return( Iosb.Status ); + } + + // + // Not and Fcb or a Dcb so we bug check + // + + FatBugCheck( NodeType(Fcb), (ULONG_PTR) Fcb, 0 ); + } + + // + // There is more in the name to parse than we have in existing + // fcbs/dcbs. So now make sure that fcb we got for the largest + // matching prefix is really a dcb otherwise we can't go any + // further + // + + if ((NodeType(Fcb) != FAT_NTC_DCB) && (NodeType(Fcb) != FAT_NTC_ROOT_DCB)) { + + DebugTrace(0, Dbg, "Cannot open file as subdirectory, Fcb = %08lx\n", Fcb); + + try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND ); + } + + // + // Otherwise we continue on processing the Irp and allowing ourselves + // to block for I/O as necessary. Find/create additional dcb's for + // the one we're trying to open. We loop until either remaining part + // is empty or we get a bad filename. When we exit FinalName is + // the last name in the string we're after, and ParentDcb is the + // parent directory that will contain the opened/created + // file/directory. + // + // Make sure the rest of the name is valid in at least the LFN + // character set (which just happens to be that of HPFS). + // + // If we are not in ChicagoMode, use FAT symantics. + // + + ParentDcb = Fcb; + FirstLoop = TRUE; + + while (TRUE) { + + // + // We do one little optimization here on the first itterration of + // the loop since we know that we have already tried to convert + // FinalOemName from the original UNICODE. + // + + if (FirstLoop) { + + FirstLoop = FALSE; + RemainingPart = NextRemainingPart; + Status = OemFinalName.Length ? STATUS_SUCCESS : STATUS_UNMAPPABLE_CHARACTER; + + } else { + + // + // Dissect the remaining part. + // + + DebugTrace(0, Dbg, "Dissecting the name %Z\n", &RemainingPart); + + FsRtlDissectName( RemainingPart, + &FinalName, + &RemainingPart ); + + // + // If RemainingPart starts with a backslash the name is + // invalid. + // Check for no more than 255 characters in FinalName + // + + if (((RemainingPart.Length != 0) && (RemainingPart.Buffer[0] == L'\\')) || + (FinalName.Length > 255*sizeof(WCHAR))) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // Now, try to convert this one component into Oem. If it works + // then that's great, otherwise we have to try with the UNICODE + // name instead. + // + + FatEnsureStringBufferEnough( &OemFinalName, + FinalName.Length); + + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE ); + } + + if (NT_SUCCESS(Status)) { + + // + // We'll start by trying to locate the dirent for the name. Note + // that we already know that there isn't an Fcb/Dcb for the file + // otherwise we would have found it when we did our prefix lookup. + // + + if (FatIsNameShortOemValid( IrpContext, OemFinalName, FALSE, FALSE, FALSE )) { + + FatStringTo8dot3( IrpContext, + OemFinalName, + &LocalCcb.OemQueryTemplate.Constant ); + + LocalCcb.Flags = 0; + + } else { + + LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE; + } + + } else { + + LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE; + + if (Status != STATUS_UNMAPPABLE_CHARACTER) { + + try_return( Iosb.Status = Status ); + } + } + + // + // Now we know a lot about the final name, so do legal name + // checking here. + // + + if (FatData.ChicagoMode) { + + if (!FatIsNameLongUnicodeValid( IrpContext, &FinalName, FALSE, FALSE, FALSE )) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + } else { + + if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + } + + DebugTrace(0, Dbg, "FinalName is %Z\n", &FinalName); + DebugTrace(0, Dbg, "RemainingPart is %Z\n", &RemainingPart); + + FatEnsureStringBufferEnough( &UpcasedFinalName, + FinalName.Length); + + if (!NT_SUCCESS(Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &FinalName, FALSE))) { + + try_return( Iosb.Status = Status ); + } + + LocalCcb.UnicodeQueryTemplate = UpcasedFinalName; + LocalCcb.ContainsWildCards = FALSE; + + Lfn.Length = 0; + + FatLocateDirent( IrpContext, + ParentDcb, + &LocalCcb, + 0, + &Dirent, + &DirentBcb, +#ifndef __REACTOS__ + &DirentByteOffset, +#else + (PVBO)&DirentByteOffset, +#endif + &FileNameOpenedDos, + &Lfn); + // + // Remember we read this Dcb for error recovery. + // + + FinalDcb = ParentDcb; + + // + // If the remaining part is now empty then this is the last name + // in the string and the one we want to open + // + + if (RemainingPart.Length == 0) { break; } + + // + // We didn't find a dirent, bail. + // + + if (Dirent == NULL) { + + Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND; + try_return( Iosb.Status ); + } + + // + // We now have a dirent, make sure it is a directory + // + + if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { + + Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND; + try_return( Iosb.Status ); + } + + // + // Compute the LfnByteOffset. + // + + LfnByteOffset = DirentByteOffset - + FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT); + + // + // Create a dcb for the new directory + // + + ParentDcb = FatCreateDcb( IrpContext, + Vcb, + ParentDcb, + LfnByteOffset, + DirentByteOffset, + Dirent, + &Lfn ); + + // + // Remember we created this Dcb for error recovery. + // + + FinalDcb = ParentDcb; + + FatSetFullNameInFcb( IrpContext, ParentDcb, &FinalName ); + } + + // + // First check if the user wanted to open the target directory + // and if so then call the subroutine to finish the open. + // + + if (OpenTargetDirectory) { + + Iosb = FatOpenTargetDirectory( IrpContext, + FileObject, + ParentDcb, + DesiredAccess, + ShareAccess, + Dirent ? TRUE : FALSE); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + if (Dirent != NULL) { + + // + // Compute the LfnByteOffset. + // + + LfnByteOffset = DirentByteOffset - + FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT); + + // + // We were able to locate an existing dirent entry, so now + // see if it is a directory that we're trying to open. + // + + if (FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { + + // + // Make sure its okay to open a directory + // + + if (NonDirectoryFile) { + + DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0); + + try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY ); + } + + DebugTrace(0, Dbg, "Open existing directory\n", 0); + + Iosb = FatOpenExistingDirectory( IrpContext, + FileObject, + Vcb, + ParentDcb, + Dirent, + LfnByteOffset, + DirentByteOffset, + &Lfn, + DesiredAccess, + ShareAccess, + CreateDisposition, + NoEaKnowledge, + DeleteOnClose ); + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + // + // Otherwise we're trying to open and existing file, and we + // need to check if the user only wanted to open a directory. + // + + if (OpenDirectory) { + + DebugTrace(0, Dbg, "Cannot open file as directory\n", 0); + + try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY ); + } + + DebugTrace(0, Dbg, "Open existing file\n", 0); + + if ( TrailingBackslash ) { + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + Iosb = FatOpenExistingFile( IrpContext, + FileObject, + Vcb, + ParentDcb, + Dirent, + LfnByteOffset, + DirentByteOffset, + &Lfn, + DesiredAccess, + ShareAccess, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + CreateDisposition, + IsPagingFile, + NoEaKnowledge, + DeleteOnClose, + FileNameOpenedDos ); + + // + // Check if we need to set the cache support flag in + // the file object + // + + if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) { + + FileObject->Flags |= FO_CACHE_SUPPORTED; + } + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + // + // We can't locate a dirent so this is a new file. + // + + // + // Now check to see if we wanted to only open an existing file. + // And then case on whether we wanted to create a file or a directory. + // + + if ((CreateDisposition == FILE_OPEN) || + (CreateDisposition == FILE_OVERWRITE)) { + + DebugTrace( 0, Dbg, "Cannot open nonexisting file\n", 0); + + try_return( Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND ); + } + + // + // Skip a few cycles later if we know now that the Oem name is not + // valid 8.3. + // + + if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) { + + OemFinalName.Length = 0; + } + + // + // Determine the granted access for this operation now. + // + + if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) { + + try_return( Iosb ); + } + + if (CreateDirectory) { + + DebugTrace(0, Dbg, "Create new directory\n", 0); + + // + // If this media is write protected, don't even try the create. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + + // + // Don't allow people to create directories with the + // temporary bit set. + // + + if (TemporaryFile) { + + try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); + } + + Iosb = FatCreateNewDirectory( IrpContext, + FileObject, + Vcb, + ParentDcb, + &OemFinalName, + &FinalName, + DesiredAccess, + ShareAccess, + EaBuffer, + EaLength, + FileAttributes, + NoEaKnowledge, + DeleteOnClose ); + + Irp->IoStatus.Information = Iosb.Information; + try_return( Iosb.Status ); + } + + DebugTrace(0, Dbg, "Create new file\n", 0); + + if ( TrailingBackslash ) { + + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // If this media is write protected, don't even try the create. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + + Iosb = FatCreateNewFile( IrpContext, + FileObject, + Vcb, + ParentDcb, + &OemFinalName, + &FinalName, + DesiredAccess, + ShareAccess, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + &Lfn, + IsPagingFile, + NoEaKnowledge, + DeleteOnClose, + TemporaryFile ); + + // + // Check if we need to set the cache support flag in + // the file object + // + + if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) { + + FileObject->Flags |= FO_CACHE_SUPPORTED; + } + + Irp->IoStatus.Information = Iosb.Information; + + try_exit: NOTHING; + + // + // This is a Beta Fix. Do this at a better place later. + // + + if (NT_SUCCESS(Iosb.Status) && !OpenTargetDirectory) { + + PFCB Fcb; + + // + // If there is an Fcb/Dcb, set the long file name. + // + + Fcb = FileObject->FsContext; + + if (Fcb && + ((NodeType(Fcb) == FAT_NTC_FCB) || + (NodeType(Fcb) == FAT_NTC_DCB)) && + (Fcb->FullFileName.Buffer == NULL)) { + + FatSetFullNameInFcb( IrpContext, Fcb, &FinalName ); + } + } + + } _SEH2_FINALLY { + + DebugUnwind( FatCommonCreate ); + + // + // There used to be a test here - the ASSERT replaces it. We will + // never have begun enumerating directories if we post the IRP for + // oplock reasons. + // + + ASSERT( !OplockPostIrp || DirentBcb == NULL ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // If we are in an error path, check for any created subdir Dcbs that + // have to be unwound. Don't whack the root directory. + // + // Note this will leave a branch of Dcbs dangling if the directory file + // had not been built on the leaf (case: opening path which has an + // element containing an invalid character name). + // + + if ((_SEH2_AbnormalTermination() || !NT_SUCCESS(Iosb.Status)) && + (FinalDcb != NULL) && + (NodeType(FinalDcb) == FAT_NTC_DCB) && + IsListEmpty(&FinalDcb->Specific.Dcb.ParentDcbQueue) && + (FinalDcb->OpenCount == 0) && + (FinalDcb->Specific.Dcb.DirectoryFile != NULL)) { + + PFILE_OBJECT DirectoryFileObject; + ULONG SavedFlags; + + // + // Before doing the uninitialize, we have to unpin anything + // that has been repinned, but disable writethrough first. We + // disable raise from unpin-repin since we're already failing. + // + + SavedFlags = IrpContext->Flags; + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE | + IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH ); + + FatUnpinRepinnedBcbs( IrpContext ); + + DirectoryFileObject = FinalDcb->Specific.Dcb.DirectoryFile; + + FinalDcb->Specific.Dcb.DirectoryFile = NULL; + + CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); + + ObDereferenceObject( DirectoryFileObject ); + + IrpContext->Flags = SavedFlags; + } + + if (_SEH2_AbnormalTermination()) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + // + // Free up any string buffers we allocated + // + + FatFreeStringBuffer( &OemFinalName); + + FatFreeStringBuffer( &UpcasedFinalName); + + FatFreeStringBuffer( &Lfn); + } _SEH2_END; + + // + // The following code is only executed if we are exiting the + // procedure through a normal termination. We complete the request + // and if for any reason that bombs out then we need to unreference + // and possibly delete the fcb and ccb. + // + + _SEH2_TRY { + + if (PostIrp) { + + // + // If the Irp hasn't already been posted, do it now. + // + + if (!OplockPostIrp) { + + Iosb.Status = FatFsdPostRequest( IrpContext, Irp ); + } + + } else { + + FatUnpinRepinnedBcbs( IrpContext ); + } + + } _SEH2_FINALLY { + + DebugUnwind( FatCommonCreate-in-FatCompleteRequest ); + + if (_SEH2_AbnormalTermination()) { + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + PFILE_OBJECT DirectoryFileObject; + + // + // Unwind all of our counts. Note that if a write failed, then + // the volume has been marked for verify, and all volume + // structures will be cleaned up automatically. + // + + (VOID) FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + Fcb->UncleanCount -= 1; + Fcb->OpenCount -= 1; + Vcb->OpenFileCount -= 1; + + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; } + + // + // If we leafed out on a new Fcb we should get rid of it at this point. + // + // Since the object isn't being opened, we have to do all of the teardown + // here. Our close path will not occur for this fileobject. Note this + // will leave a branch of Dcbs dangling since we do it by hand and don't + // chase to the root. + // + + if (Fcb->OpenCount == 0 && + (NodeType( Fcb ) == FAT_NTC_FCB || + IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue))) { + + ASSERT( NodeType( Fcb ) != FAT_NTC_ROOT_DCB ); + + if ( (NodeType( Fcb ) == FAT_NTC_DCB) && + (Fcb->Specific.Dcb.DirectoryFile != NULL) ) { + + FatSyncUninitializeCacheMap( IrpContext, + Fcb->Specific.Dcb.DirectoryFile ); + + DirectoryFileObject = Fcb->Specific.Dcb.DirectoryFile; + Fcb->Specific.Dcb.DirectoryFile = NULL; + ObDereferenceObject( DirectoryFileObject ); + } else { + + FatDeleteFcb( IrpContext, Fcb ); + } + } + + FatDeleteCcb( IrpContext, Ccb ); + + FatReleaseVcb( IrpContext, Vcb ); + + } else { + + FatReleaseVcb( IrpContext, Vcb ); + + if ( !PostIrp ) { + + // + // If this request is successful and the file was opened + // for FILE_EXECUTE access, then set the FileObject bit. + // + + ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL ); + if (FlagOn( *DesiredAccess, FILE_EXECUTE )) { + + SetFlag( FileObject->Flags, FO_FILE_FAST_IO_READ ); + } + + // + // Lock volume in drive if we opened a paging file, allocating a + // reserve MDL to guarantee paging file operations can always + // go forward. + // + + if (IsPagingFile && NT_SUCCESS(Iosb.Status)) { + + if (!FatReserveMdl) { + + PMDL ReserveMdl = IoAllocateMdl( NULL, + FAT_RESERVE_MDL_SIZE * PAGE_SIZE, + TRUE, + FALSE, + NULL ); + + // + // Stash the MDL, and if it turned out there was already one there + // just free what we got. + // + +#ifndef __REACTOS__ + InterlockedCompareExchangePointer( &FatReserveMdl, ReserveMdl, NULL ); +#else + InterlockedCompareExchangePointer( (void * volatile*)&FatReserveMdl, ReserveMdl, NULL ); +#endif + + if (FatReserveMdl != ReserveMdl) { + + IoFreeMdl( ReserveMdl ); + } + } + + SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE); + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); + } + } + + // + // Complete the request. + // + + FatCompleteRequest( IrpContext, Irp, Iosb.Status ); + } + } + + DebugTrace(-1, Dbg, "FatCommonCreate -> %08lx\n", Iosb.Status); + } _SEH2_END; + + CollectCreateStatistics(Vcb, Iosb.Status); + + return Iosb.Status; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatOpenVolume ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG CreateDisposition + ) + +/*++ + +Routine Description: + + This routine opens the specified volume for DASD access + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume being opened + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + CreateDisposition - Supplies the create disposition for this operation + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + +#ifndef __REACTOS__ + IO_STATUS_BLOCK Iosb = {0,0}; +#else + IO_STATUS_BLOCK Iosb = {{0}}; +#endif + + BOOLEAN CleanedVolume = FALSE; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN UnwindCounts = FALSE; + BOOLEAN UnwindVolumeLock = FALSE; + + DebugTrace(+1, Dbg, "FatOpenVolume...\n", 0); + + _SEH2_TRY { + + // + // Check for proper desired access and rights + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + try_return( Iosb.Status = STATUS_ACCESS_DENIED ); + } + + // + // If the user does not want to share write or delete then we will try + // and take out a lock on the volume. + // + + if (!FlagOn(ShareAccess, FILE_SHARE_WRITE) && + !FlagOn(ShareAccess, FILE_SHARE_DELETE)) { + + // + // Do a quick check here for handles on exclusive open. + // + + if (!FlagOn(ShareAccess, FILE_SHARE_READ) && + !FatIsHandleCountZero( IrpContext, Vcb )) { + + try_return( Iosb.Status = STATUS_SHARING_VIOLATION ); + } + + // + // Force Mm to get rid of its referenced file objects. + // + + FatFlushFat( IrpContext, Vcb ); + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush ); + + // + // If the user also does not want to share read then we check + // if anyone is already using the volume, and if so then we + // deny the access. If the user wants to share read then + // we allow the current opens to stay provided they are only + // readonly opens and deny further opens. + // + + if (!FlagOn(ShareAccess, FILE_SHARE_READ)) { + + if (Vcb->OpenFileCount != 0) { + + try_return( Iosb.Status = STATUS_SHARING_VIOLATION ); + } + + } else { + + if (Vcb->ReadOnlyCount != Vcb->OpenFileCount) { + + try_return( Iosb.Status = STATUS_SHARING_VIOLATION ); + } + } + + // + // Lock the volume + // + + Vcb->VcbState |= VCB_STATE_FLAG_LOCKED; + Vcb->FileObjectWithVcbLocked = FileObject; + UnwindVolumeLock = TRUE; + + // + // Clean the volume + // + + CleanedVolume = TRUE; + + } else if (FlagOn( *DesiredAccess, FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA )) { + + // + // Flush the volume and let ourselves push the clean bit out if everything + // worked. + // + + if (NT_SUCCESS( FatFlushVolume( IrpContext, Vcb, Flush ))) { + + CleanedVolume = TRUE; + } + } + + // + // Clean the volume if we believe it safe and reasonable. + // + + if (CleanedVolume && + FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) && + !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) && + !CcIsThereDirtyData(Vcb->Vpb)) { + + // + // Cancel any pending clean volumes. + // + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + } + + // + // If the volume is already opened by someone then we need to check + // the share access + // + + if (Vcb->DirectAccessOpenCount > 0) { + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Vcb->ShareAccess, + TRUE ))) { + + try_return( Iosb.Status ); + } + + } else { + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Vcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // Set up the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserVolumeOpen, + Vcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &Vcb->SectionObjectPointers; + + Vcb->DirectAccessOpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + UnwindCounts = TRUE; + FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING; + + // + // At this point the open will succeed, so check if the user is getting explicit access + // to the device. If not, we will note this so we can deny modifying FSCTL to it. + // + + IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + Status = FatExplicitDeviceAccessGranted( IrpContext, + Vcb->Vpb->RealDevice, + IrpSp->Parameters.Create.SecurityContext->AccessState, + (KPROCESSOR_MODE)( FlagOn( IrpSp->Flags, SL_FORCE_ACCESS_CHECK ) ? + UserMode : + IrpContext->OriginatingIrp->RequestorMode )); + + if (NT_SUCCESS( Status )) { + + SetFlag( UnwindCcb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS ); + } + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatOpenVolume ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination() || !NT_SUCCESS(Iosb.Status)) { + + if (UnwindCounts) { + Vcb->DirectAccessOpenCount -= 1; + Vcb->OpenFileCount -= 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; } + } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Vcb->ShareAccess ); } + if (UnwindVolumeLock) { Vcb->VcbState &= ~VCB_STATE_FLAG_LOCKED; } + } + + DebugTrace(-1, Dbg, "FatOpenVolume -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatOpenRootDcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG CreateDisposition + ) + +/*++ + +Routine Description: + + This routine opens the root dcb for the volume + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume whose dcb is being opened. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + CreateDisposition - Supplies the create disposition for this operation + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +Arguments: + +--*/ + +{ + PDCB RootDcb; + IO_STATUS_BLOCK Iosb; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN UnwindCounts = FALSE; + BOOLEAN RootDcbAcquired = FALSE; + + DebugTrace(+1, Dbg, "FatOpenRootDcb...\n", 0); + + // + // Locate the root dcb + // + + RootDcb = Vcb->RootDcb; + + // + // Get the Dcb exlcusive. This is important as cleanup does not + // acquire the Vcb. + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, RootDcb ); + RootDcbAcquired = TRUE; + + _SEH2_TRY { + + // + // Check the create disposition and desired access + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + if (!FatCheckFileAccess( IrpContext, + RootDcb->DirentFatFlags, + DesiredAccess)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // If the Root dcb is already opened by someone then we need + // to check the share access + // + + if (RootDcb->OpenCount > 0) { + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &RootDcb->ShareAccess, + TRUE ))) { + + try_return( Iosb ); + } + + } else { + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &RootDcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + RootDcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + RootDcb->UncleanCount += 1; + RootDcb->OpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + UnwindCounts = TRUE; + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatOpenRootDcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + if (UnwindCounts) { + RootDcb->UncleanCount -= 1; + RootDcb->OpenCount -= 1; + Vcb->OpenFileCount -= 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount -= 1; } + } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &RootDcb->ShareAccess ); } + } + + if (RootDcbAcquired) { + + FatReleaseFcb( IrpContext, RootDcb ); + } + + DebugTrace(-1, Dbg, "FatOpenRootDcb -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatOpenExistingDcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB Dcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG CreateDisposition, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose + ) + +/*++ + +Routine Description: + + This routine opens the specified existing dcb + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume containing the dcb + + Dcb - Supplies the already existing dcb + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + CreateDisposition - Supplies the create disposition for this operation + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + PBCB DirentBcb = NULL; + PDIRENT Dirent; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN DcbAcquired = FALSE; + + DebugTrace(+1, Dbg, "FatOpenExistingDcb...\n", 0); + + // + // Get the Dcb exlcusive. This is important as cleanup does not + // acquire the Vcb. + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, Dcb ); + DcbAcquired = TRUE; + + _SEH2_TRY { + + // + // Before spending any noticeable effort, see if we have the odd case + // of someone trying to delete-on-close the root dcb. This will only + // happen if we're hit with a null-filename relative open via the root. + // + + if (NodeType(Dcb) == FAT_NTC_ROOT_DCB && DeleteOnClose) { + + Iosb.Status = STATUS_CANNOT_DELETE; + try_return( Iosb ); + } + + // + // If the caller has no Ea knowledge, we immediately check for + // Need Ea's on the file. We don't need to check for ea's on the + // root directory, because it never has any. Fat32 doesn't have + // any, either. + // + + if (NoEaKnowledge && NodeType(Dcb) != FAT_NTC_ROOT_DCB && + !FatIsFat32(Vcb)) { + + ULONG NeedEaCount; + + // + // Get the dirent for the file and then check that the need + // ea count is 0. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Dcb, + &Dirent, + &DirentBcb ); + + ASSERT( Dirent && DirentBcb ); + + FatGetNeedEaCount( IrpContext, + Vcb, + Dirent, + &NeedEaCount ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + if (NeedEaCount != 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + } + + // + // Check the create disposition and desired access + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + try_return( Iosb ); + } + + if (!FatCheckFileAccess( IrpContext, + Dcb->DirentFatFlags, + DesiredAccess)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // If the dcb is already opened by someone then we need + // to check the share access + // + + if (Dcb->OpenCount > 0) { + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess, + TRUE ))) { + + try_return( Iosb ); + } + + } else { + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + Dcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + Dcb->UncleanCount += 1; + Dcb->OpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + // + // Mark the delete on close bit if the caller asked for that. + // + + { + PCCB Ccb = (PCCB)FileObject->FsContext2; + + + if (DeleteOnClose) { + + SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + + } + + // + // In case this was set, clear it now. + // + + ClearFlag(Dcb->FcbState, FCB_STATE_DELAY_CLOSE); + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatOpenExistingDcb ); + + // + // Unpin the Dirent Bcb if pinned. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Dcb->ShareAccess ); } + } + + if (DcbAcquired) { + + FatReleaseFcb( IrpContext, Dcb ); + } + + DebugTrace(-1, Dbg, "FatOpenExistingDcb -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatOpenExistingFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PFCB Fcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG AllocationSize, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN ULONG CreateDisposition, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose, + IN BOOLEAN FileNameOpenedDos, + OUT PBOOLEAN OplockPostIrp + ) + +/*++ + +Routine Description: + + This routine opens the specified existing fcb + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume containing the Fcb + + Fcb - Supplies the already existing fcb + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + AllocationSize - Supplies the initial allocation if the file is being + superseded or overwritten + + EaBuffer - Supplies the Ea set if the file is being superseded or + overwritten + + EaLength - Supplies the size, in byte, of the EaBuffer + + FileAttributes - Supplies file attributes to use if the file is being + superseded or overwritten + + CreateDisposition - Supplies the create disposition for this operation + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + + FileNameOpenedDos - The caller hit the short side of the name pair finding + this file + + OplockPostIrp - Address to store boolean indicating if the Irp needs to + be posted to the Fsp. + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + PBCB DirentBcb = NULL; + PDIRENT Dirent; + + ACCESS_MASK AddedAccess = 0; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN DecrementFcbOpenCount = FALSE; + BOOLEAN FcbAcquired = FALSE; + + DebugTrace(+1, Dbg, "FatOpenExistingFcb...\n", 0); + + // + // Get the Fcb exlcusive. This is important as cleanup does not + // acquire the Vcb. + + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + FcbAcquired = TRUE; + + _SEH2_TRY { + + *OplockPostIrp = FALSE; + + // + // Take special action if there is a current batch oplock or + // batch oplock break in process on the Fcb. + // + + if (FsRtlCurrentBatchOplock( &Fcb->Specific.Fcb.Oplock )) { + + // + // We remember if a batch oplock break is underway for the + // case where the sharing check fails. + // + + Iosb.Information = FILE_OPBATCH_BREAK_UNDERWAY; + + Iosb.Status = FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock, + IrpContext->OriginatingIrp, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + if (Iosb.Status != STATUS_SUCCESS + && Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS) { + + *OplockPostIrp = TRUE; + try_return( NOTHING ); + } + } + + // + // Check if the user wanted to create the file, also special case + // the supersede and overwrite options. Those add additional, + // possibly only implied, desired accesses to the caller, which + // we must be careful to pull back off if the caller did not actually + // request them. + // + // In other words, check against the implied access, but do not modify + // share access as a result. + // + + if (CreateDisposition == FILE_CREATE) { + + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + try_return( Iosb ); + + } else if (CreateDisposition == FILE_SUPERSEDE) { + + SetFlag( AddedAccess, + DELETE & ~(*DesiredAccess) ); + + *DesiredAccess |= DELETE; + + } else if ((CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) { + + SetFlag( AddedAccess, + (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) ); + + *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES; + } + + // + // Check the desired access + // + + if (!FatCheckFileAccess( IrpContext, + Fcb->DirentFatFlags, + DesiredAccess )) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // Check for trying to delete a read only file. + // + + if (DeleteOnClose && + FlagOn( Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY )) { + + Iosb.Status = STATUS_CANNOT_DELETE; + try_return( Iosb ); + } + + // + // If we are asked to do an overwrite or supersede operation then + // deny access for files where the file attributes for system and + // hidden do not match + // + + if ((CreateDisposition == FILE_SUPERSEDE) || + (CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) { + + BOOLEAN Hidden; + BOOLEAN System; + + Hidden = BooleanFlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_HIDDEN ); + System = BooleanFlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_SYSTEM ); + + if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) || + (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))) { + + DebugTrace(0, Dbg, "The hidden and/or system bits do not match\n", 0); + + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // If this media is write protected, don't even try the create. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + } + + // + // Check if the Fcb has the proper share access + // + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Fcb->ShareAccess, + FALSE ))) { + + try_return( Iosb ); + } + + // + // Now check that we can continue based on the oplock state of the + // file. + // + // It is important that we modified the DesiredAccess in place so + // that the Oplock check proceeds against any added access we had + // to give the caller. + // + + Iosb.Status = FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock, + IrpContext->OriginatingIrp, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + if (Iosb.Status != STATUS_SUCCESS + && Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS) { + + *OplockPostIrp = TRUE; + try_return( NOTHING ); + } + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + // + // If the user wants write access access to the file make sure there + // is not a process mapping this file as an image. Any attempt to + // delete the file will be stopped in fileinfo.c + // + // If the user wants to delete on close, we must check at this + // point though. + // + + if (FlagOn(*DesiredAccess, FILE_WRITE_DATA) || DeleteOnClose) { + + Fcb->OpenCount += 1; + DecrementFcbOpenCount = TRUE; + + if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers, + MmFlushForWrite )) { + + Iosb.Status = DeleteOnClose ? STATUS_CANNOT_DELETE : + STATUS_SHARING_VIOLATION; + try_return( Iosb ); + } + } + + // + // If this is a non-cached open on a non-paging file, and there + // are no open cached handles, but there is a still a data + // section, attempt a flush and purge operation to avoid cache + // coherency overhead later. We ignore any I/O errors from + // the flush. + // + // We set the CREATE_IN_PROGRESS flag to prevent the Fcb from + // going away out from underneath us. + // + + if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING ) && + (Fcb->UncleanCount == Fcb->NonCachedUncleanCount) && + (Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) && + !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + SetFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS); + + CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, NULL ); + + // + // Grab and release PagingIo to serialize ourselves with the lazy writer. + // This will work to ensure that all IO has completed on the cached + // data and we will succesfully tear away the cache section. + // + + ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE); + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers, + NULL, + 0, + FALSE ); + + ClearFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS); + } + + // + // Check if the user only wanted to open the file + // + + if ((CreateDisposition == FILE_OPEN) || + (CreateDisposition == FILE_OPEN_IF)) { + + DebugTrace(0, Dbg, "Doing open operation\n", 0); + + // + // If the caller has no Ea knowledge, we immediately check for + // Need Ea's on the file. + // + + if (NoEaKnowledge && !FatIsFat32(Vcb)) { + + ULONG NeedEaCount; + + // + // Get the dirent for the file and then check that the need + // ea count is 0. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &DirentBcb ); + + FatGetNeedEaCount( IrpContext, + Vcb, + Dirent, + &NeedEaCount ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + if (NeedEaCount != 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + } + + // + // Everything checks out okay, so setup the context and + // section object pointers. + // + + FatSetFileObject( FileObject, + UserFileOpen, + Fcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers; + + // + // Fill in the information field, the status field is already + // set. + // + + Iosb.Information = FILE_OPENED; + + try_return( Iosb ); + } + + // + // Check if we are to supersede/overwrite the file, we can wait for + // any I/O at this point + // + + if ((CreateDisposition == FILE_SUPERSEDE) || + (CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) { + + NTSTATUS OldStatus; + + DebugTrace(0, Dbg, "Doing supersede/overwrite operation\n", 0); + + // + // Determine the granted access for this operation now. + // + + if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) { + + try_return( Iosb ); + } + + // + // And overwrite the file. We remember the previous status + // code because it may contain information about + // the oplock status. + // + + OldStatus = Iosb.Status; + + Iosb = FatSupersedeOrOverwriteFile( IrpContext, + FileObject, + Fcb, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + CreateDisposition, + NoEaKnowledge ); + + if (Iosb.Status == STATUS_SUCCESS) { + + Iosb.Status = OldStatus; + } + + try_return( Iosb ); + } + + // + // If we ever get here then the I/O system gave us some bad input + // + + FatBugCheck( CreateDisposition, 0, 0 ); + + try_exit: NOTHING; + + // + // Update the share access and counts if successful + // + + if ((Iosb.Status != STATUS_PENDING) && NT_SUCCESS(Iosb.Status)) { + + // + // Now, we may have added some access bits above to indicate the access + // this caller would conflict with (as opposed to what they get) in order + // to perform the overwrite/supersede. We need to make a call to that will + // recalculate the bits in the fileobject to reflect the real access they + // will get. + // + + if (AddedAccess) { + + NTSTATUS Status; + + ClearFlag( *DesiredAccess, AddedAccess ); + Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Fcb->ShareAccess, + TRUE ); + + // + // It must be the case that we are really asking for less access, so + // any conflict must have been detected before this point. + // + + ASSERT( Status == STATUS_SUCCESS ); + + } else { + + IoUpdateShareAccess( FileObject, &Fcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // In case this was set, clear it now. + // + + ClearFlag(Fcb->FcbState, FCB_STATE_DELAY_CLOSE); + + Fcb->UncleanCount += 1; + Fcb->OpenCount += 1; + if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) { + Fcb->NonCachedUncleanCount += 1; + } + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + { + PCCB Ccb; + Ccb = (PCCB)FileObject->FsContext2; + + // + // Mark the DeleteOnClose bit if the operation was successful. + // + + if ( DeleteOnClose ) { + + SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + + // + // Mark the OpenedByShortName bit if the operation was successful. + // + + if ( FileNameOpenedDos ) { + + SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + } + } + } + + } _SEH2_FINALLY { + + DebugUnwind( FatOpenExistingFcb ); + + // + // Unpin the Dirent Bcb if pinned. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Fcb->ShareAccess ); } + } + + if (DecrementFcbOpenCount) { + + Fcb->OpenCount -= 1; + if (Fcb->OpenCount == 0) { FatDeleteFcb( IrpContext, Fcb ); } + } + + if (FcbAcquired) { + + FatReleaseFcb( IrpContext, Fcb ); + } + + DebugTrace(-1, Dbg, "FatOpenExistingFcb -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + return Iosb; +} + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatOpenTargetDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PDCB Dcb, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN BOOLEAN DoesNameExist + ) + +/*++ + +Routine Description: + + This routine opens the target directory and replaces the name in the + file object with the remaining name. + +Arguments: + + FileObject - Supplies the File object + + Dcb - Supplies an already existing dcb that we are going to open + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + DoesNameExist - Indicates if the file name already exists in the + target directory. + + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + // + // The following variables are for abnormal termination + // + + BOOLEAN UnwindShareAccess = FALSE; + PCCB UnwindCcb = NULL; + BOOLEAN DcbAcquired = FALSE; + + DebugTrace(+1, Dbg, "FatOpenTargetDirectory...\n", 0); + + // + // Get the Dcb exlcusive. This is important as cleanup does not + // acquire the Vcb. + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, Dcb ); + DcbAcquired = TRUE; + + _SEH2_TRY { + + ULONG i; + + // + // If the Dcb is already opened by someone then we need + // to check the share access + // + + if (Dcb->OpenCount > 0) { + + if (!NT_SUCCESS(Iosb.Status = IoCheckShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess, + TRUE ))) { + + try_return( Iosb ); + } + + } else { + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess ); + } + + UnwindShareAccess = TRUE; + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + Dcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + Dcb->UncleanCount += 1; + Dcb->OpenCount += 1; + Dcb->Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Dcb->Vcb->ReadOnlyCount += 1; } + + // + // Update the name in the file object, by definition the remaining + // part must be shorter than the original file name so we'll just + // overwrite the file name. + // + + i = FileObject->FileName.Length/sizeof(WCHAR) - 1; + + // + // Get rid of a trailing backslash + // + + if (FileObject->FileName.Buffer[i] == L'\\') { + + ASSERT(i != 0); + + FileObject->FileName.Length -= sizeof(WCHAR); + i -= 1; + } + + // + // Find the first non-backslash character. i will be its index. + // + + while (TRUE) { + + if (FileObject->FileName.Buffer[i] == L'\\') { + + i += 1; + break; + } + + if (i == 0) { + break; + } + + i--; + } + + if (i) { + + FileObject->FileName.Length -= (USHORT)(i * sizeof(WCHAR)); + + RtlCopyMemory( &FileObject->FileName.Buffer[0], + &FileObject->FileName.Buffer[i], + FileObject->FileName.Length ); + } + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = (DoesNameExist ? FILE_EXISTS : FILE_DOES_NOT_EXIST); + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatOpenTargetDirectory ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, UnwindCcb ); } + if (UnwindShareAccess) { IoRemoveShareAccess( FileObject, &Dcb->ShareAccess ); } + } + + if (DcbAcquired) { + + FatReleaseFcb( IrpContext, Dcb ); + } + + DebugTrace(-1, Dbg, "FatOpenTargetDirectory -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + return Iosb; +} + + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatOpenExistingDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN PDIRENT Dirent, + IN ULONG LfnByteOffset, + IN ULONG DirentByteOffset, + IN PUNICODE_STRING Lfn, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG CreateDisposition, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose + ) + +/*++ + +Routine Description: + + This routine opens the specified directory. The directory has not + previously been opened. + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume containing the dcb + + ParentDcb - Supplies the parent directory containing the subdirectory + to be opened + + DirectoryName - Supplies the file name of the directory being opened. + + Dirent - Supplies the dirent for the directory being opened + + LfnByteOffset - Tells where the Lfn begins. If there is no Lfn + this field is the same as DirentByteOffset. + + DirentByteOffset - Supplies the Vbo of the dirent within its parent + directory + + Lfn - May supply a long name for the file. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + CreateDisposition - Supplies the create disposition for this operation + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + PDCB Dcb; + + // + // The following variables are for abnormal termination + // + + PDCB UnwindDcb = NULL; + PCCB UnwindCcb = NULL; + + DebugTrace(+1, Dbg, "FatOpenExistingDirectory...\n", 0); + + _SEH2_TRY { + + // + // If the caller has no Ea knowledge, we immediately check for + // Need Ea's on the file. + // + + if (NoEaKnowledge && !FatIsFat32(Vcb)) { + + ULONG NeedEaCount; + + FatGetNeedEaCount( IrpContext, + Vcb, + Dirent, + &NeedEaCount ); + + if (NeedEaCount != 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + } + + // + // Check the create disposition and desired access + // + + if ((CreateDisposition != FILE_OPEN) && + (CreateDisposition != FILE_OPEN_IF)) { + + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + try_return( Iosb ); + } + + if (!FatCheckFileAccess( IrpContext, + Dirent->Attributes, + DesiredAccess)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // Create a new dcb for the directory + // + + Dcb = UnwindDcb = FatCreateDcb( IrpContext, + Vcb, + ParentDcb, + LfnByteOffset, + DirentByteOffset, + Dirent, + Lfn ); + + // + // Setup our share access + // + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess ); + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + Dcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + Dcb->UncleanCount += 1; + Dcb->OpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatOpenExistingDirectory ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + if (UnwindDcb != NULL) { FatDeleteFcb( IrpContext, UnwindDcb ); } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, UnwindCcb ); } + } + + DebugTrace(-1, Dbg, "FatOpenExistingDirectory -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatOpenExistingFile ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN PDIRENT Dirent, + IN ULONG LfnByteOffset, + IN ULONG DirentByteOffset, + IN PUNICODE_STRING Lfn, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG AllocationSize, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN ULONG CreateDisposition, + IN BOOLEAN IsPagingFile, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose, + IN BOOLEAN FileNameOpenedDos + ) + +/*++ + +Routine Description: + + This routine opens the specified file. The file has not previously + been opened. + +Arguments: + + FileObject - Supplies the File object + + Vcb - Supplies the Vcb denoting the volume containing the file + + ParentFcb - Supplies the parent directory containing the file to be + opened + + Dirent - Supplies the dirent for the file being opened + + LfnByteOffset - Tells where the Lfn begins. If there is no Lfn + this field is the same as DirentByteOffset. + + DirentByteOffset - Supplies the Vbo of the dirent within its parent + directory + + Lfn - May supply a long name for the file. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the share access of the caller + + AllocationSize - Supplies the initial allocation if the file is being + superseded, overwritten, or created. + + EaBuffer - Supplies the Ea set if the file is being superseded, + overwritten, or created. + + EaLength - Supplies the size, in byte, of the EaBuffer + + FileAttributes - Supplies file attributes to use if the file is being + superseded, overwritten, or created + + CreateDisposition - Supplies the create disposition for this operation + + IsPagingFile - Indicates if this is the paging file being opened. + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + + FileNameOpenedDos - The caller opened this file by hitting the 8.3 side + of the Lfn/8.3 pair + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + PFCB Fcb; + + ACCESS_MASK AddedAccess = 0; + + // + // The following variables are for abnormal termination + // + + PFCB UnwindFcb = NULL; + PCCB UnwindCcb = NULL; + + DebugTrace(+1, Dbg, "FatOpenExistingFile...\n", 0); + + _SEH2_TRY { + + // + // Check if the user wanted to create the file or if access is + // denied + // + + if (CreateDisposition == FILE_CREATE) { + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + try_return( Iosb ); + + } else if ((CreateDisposition == FILE_SUPERSEDE) && !IsPagingFile) { + + SetFlag( AddedAccess, + DELETE & ~(*DesiredAccess) ); + + *DesiredAccess |= DELETE; + + } else if (((CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) && !IsPagingFile) { + + SetFlag( AddedAccess, + (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) & ~(*DesiredAccess) ); + + *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES; + } + + if (!FatCheckFileAccess( IrpContext, + Dirent->Attributes, + DesiredAccess)) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + + // + // Check for trying to delete a read only file. + // + + if (DeleteOnClose && + FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY )) { + + Iosb.Status = STATUS_CANNOT_DELETE; + try_return( Iosb ); + } + + // + // IF we are asked to do an overwrite or supersede operation then + // deny access for files where the file attributes for system and + // hidden do not match + // + + if ((CreateDisposition == FILE_SUPERSEDE) || + (CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) { + + BOOLEAN Hidden; + BOOLEAN System; + + Hidden = BooleanFlagOn(Dirent->Attributes, FAT_DIRENT_ATTR_HIDDEN ); + System = BooleanFlagOn(Dirent->Attributes, FAT_DIRENT_ATTR_SYSTEM ); + + if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) || + (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM))) { + + DebugTrace(0, Dbg, "The hidden and/or system bits do not match\n", 0); + + if ( !IsPagingFile ) { + + Iosb.Status = STATUS_ACCESS_DENIED; + try_return( Iosb ); + } + } + + // + // If this media is write protected, don't even try the create. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + } + + // + // Create a new Fcb for the file, and set the file size in + // the fcb. + // + + Fcb = UnwindFcb = FatCreateFcb( IrpContext, + Vcb, + ParentDcb, + LfnByteOffset, + DirentByteOffset, + Dirent, + Lfn, + IsPagingFile, + FALSE ); + + // + // If this is a paging file, lookup the allocation size so that + // the Mcb is always valid + // + + if (IsPagingFile) { + + FatLookupFileAllocationSize( IrpContext, Fcb ); + } + + // + // Now case on whether we are to simply open, supersede, or + // overwrite the file. + // + + switch (CreateDisposition) { + + case FILE_OPEN: + case FILE_OPEN_IF: + + DebugTrace(0, Dbg, "Doing only an open operation\n", 0); + + // + // If the caller has no Ea knowledge, we immediately check for + // Need Ea's on the file. + // + + if (NoEaKnowledge && !FatIsFat32(Vcb)) { + + ULONG NeedEaCount; + + FatGetNeedEaCount( IrpContext, + Vcb, + Dirent, + &NeedEaCount ); + + if (NeedEaCount != 0) { + + FatRaiseStatus( IrpContext, STATUS_ACCESS_DENIED ); + } + } + + // + // Setup the context and section object pointers. + // + + FatSetFileObject( FileObject, + UserFileOpen, + Fcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers; + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + break; + + case FILE_SUPERSEDE: + case FILE_OVERWRITE: + case FILE_OVERWRITE_IF: + + DebugTrace(0, Dbg, "Doing supersede/overwrite operation\n", 0); + + // + // Determine the granted access for this operation now. + // + + if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) { + + try_return( Iosb ); + } + + Iosb = FatSupersedeOrOverwriteFile( IrpContext, + FileObject, + Fcb, + AllocationSize, + EaBuffer, + EaLength, + FileAttributes, + CreateDisposition, + NoEaKnowledge ); + break; + + default: + + DebugTrace(0, Dbg, "Illegal Create Disposition\n", 0); + + FatBugCheck( CreateDisposition, 0, 0 ); + break; + } + + try_exit: NOTHING; + + // + // Setup our share access and counts if things were successful. + // + + if ((Iosb.Status != STATUS_PENDING) && NT_SUCCESS(Iosb.Status)) { + + // + // Remove any virtual access the caller needed to check against, but will + // not really receive. Overwrite/supersede is a bit of a special case. + // + + ClearFlag( *DesiredAccess, AddedAccess ); + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Fcb->ShareAccess ); + + Fcb->UncleanCount += 1; + Fcb->OpenCount += 1; + if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) { + Fcb->NonCachedUncleanCount += 1; + } + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + } + + { + PCCB Ccb; + Ccb = (PCCB)FileObject->FsContext2; + + if ( NT_SUCCESS(Iosb.Status) ) { + + // + // Mark the DeleteOnClose bit if the operation was successful. + // + + if ( DeleteOnClose ) { + + SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + + // + // Mark the OpenedByShortName bit if the operation was successful. + // + + if ( FileNameOpenedDos ) { + + SetFlag( Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + } + } + } + + + } _SEH2_FINALLY { + + DebugUnwind( FatOpenExistingFile ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + if (UnwindFcb != NULL) { FatDeleteFcb( IrpContext, UnwindFcb ); } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, UnwindCcb ); } + } + + DebugTrace(-1, Dbg, "FatOpenExistingFile -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatCreateNewDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN POEM_STRING OemName, + IN PUNICODE_STRING UnicodeName, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose + ) + +/*++ + +Routine Description: + + This routine creates a new directory. The directory has already been + verified not to exist yet. + +Arguments: + + FileObject - Supplies the file object for the newly created directory + + Vcb - Supplies the Vcb denote the volume to contain the new directory + + ParentDcb - Supplies the parent directory containg the newly created + directory + + OemName - Supplies the Oem name for the newly created directory. It may + or maynot be 8.3 complient, but will be upcased. + + UnicodeName - Supplies the Unicode name for the newly created directory. + It may or maynot be 8.3 complient. This name contains the original + case information. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the shared access of the caller + + EaBuffer - Supplies the Ea set for the newly created directory + + EaLength - Supplies the length, in bytes, of EaBuffer + + FileAttributes - Supplies the file attributes for the newly created + directory. + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + PDCB Dcb = NULL; + PCCB Ccb = NULL; + + PDIRENT Dirent = NULL; + PBCB DirentBcb = NULL; + ULONG DirentsNeeded; + ULONG DirentByteOffset; + + PDIRENT ShortDirent; + ULONG ShortDirentByteOffset; + + USHORT EaHandle; + + BOOLEAN AllLowerComponent; + BOOLEAN AllLowerExtension; + BOOLEAN CreateLfn; + + ULONG BytesInFirstPage; + ULONG DirentsInFirstPage; + PDIRENT FirstPageDirent; + + PBCB SecondPageBcb = NULL; + ULONG SecondPageOffset; + PDIRENT SecondPageDirent; + + BOOLEAN DirentFromPool = FALSE; + + OEM_STRING ShortName; + UCHAR ShortNameBuffer[12]; + + ULONG LocalAbnormalTermination = FALSE; + + DebugTrace(+1, Dbg, "FatCreateNewDirectory...\n", 0); + + ShortName.Length = 0; + ShortName.MaximumLength = 12; +#ifndef __REACTOS__ + ShortName.Buffer = &ShortNameBuffer[0]; +#else + ShortName.Buffer = (PCHAR)&ShortNameBuffer[0]; +#endif + + EaHandle = 0; + + // + // We fail this operation if the caller doesn't understand Ea's. + // + + if (NoEaKnowledge + && EaLength > 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + + DebugTrace(-1, Dbg, "FatCreateNewDirectory -> Iosb.Status = %08lx\n", Iosb.Status); + return Iosb; + } + + // + // DeleteOnClose and ReadOnly are not compatible. + // + + if (DeleteOnClose && FlagOn(FileAttributes, FAT_DIRENT_ATTR_READ_ONLY)) { + + Iosb.Status = STATUS_CANNOT_DELETE; + return Iosb; + } + + // Now get the names that we will be using. + // + + FatSelectNames( IrpContext, + ParentDcb, + OemName, + UnicodeName, + &ShortName, + NULL, + &AllLowerComponent, + &AllLowerExtension, + &CreateLfn ); + + // + // If we are not in Chicago mode, ignore the magic bits. + // + + if (!FatData.ChicagoMode) { + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + CreateLfn = FALSE; + } + + // + // Create/allocate a new dirent + // + + DirentsNeeded = CreateLfn ? FAT_LFN_DIRENTS_NEEDED(UnicodeName) + 1 : 1; + + DirentByteOffset = FatCreateNewDirent( IrpContext, + ParentDcb, + DirentsNeeded ); + _SEH2_TRY { + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDcb, + DirentByteOffset, + sizeof(DIRENT), + &DirentBcb, +#ifndef __REACTOS__ + &Dirent, +#else + (PVOID *)&Dirent, +#endif + FALSE, + TRUE, + &Iosb.Status ); + + ASSERT( NT_SUCCESS( Iosb.Status ) && DirentBcb && Dirent ); + + // + // Deal with the special case of an LFN + Dirent structure crossing + // a page boundry. + // + + if ((DirentByteOffset / PAGE_SIZE) != + ((DirentByteOffset + (DirentsNeeded - 1) * sizeof(DIRENT)) / PAGE_SIZE)) { + + SecondPageBcb; + SecondPageOffset; + SecondPageDirent; + + SecondPageOffset = (DirentByteOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE; + + BytesInFirstPage = SecondPageOffset - DirentByteOffset; + + DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT); + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDcb, + SecondPageOffset, + sizeof(DIRENT), + &SecondPageBcb, +#ifndef __REACTOS__ + &SecondPageDirent, +#else + (PVOID *)&SecondPageDirent, +#endif + FALSE, + TRUE, + &Iosb.Status ); + + ASSERT( NT_SUCCESS( Iosb.Status ) && SecondPageBcb && SecondPageDirent ); + + FirstPageDirent = Dirent; + + Dirent = FsRtlAllocatePoolWithTag( PagedPool, + DirentsNeeded * sizeof(DIRENT), + TAG_DIRENT ); + + DirentFromPool = TRUE; + } + + // + // Bump up Dirent and DirentByteOffset + // + + ShortDirent = Dirent + DirentsNeeded - 1; + ShortDirentByteOffset = DirentByteOffset + + (DirentsNeeded - 1) * sizeof(DIRENT); + + ASSERT( NT_SUCCESS( Iosb.Status )); + + + // + // Fill in the fields of the dirent. + // + + FatConstructDirent( IrpContext, + ShortDirent, + &ShortName, + AllLowerComponent, + AllLowerExtension, + CreateLfn ? UnicodeName : NULL, + (UCHAR)(FileAttributes | FAT_DIRENT_ATTR_DIRECTORY), + TRUE, + NULL ); + + // + // If the dirent crossed pages, we have to do some real gross stuff. + // + + if (DirentFromPool) { + + RtlCopyMemory( FirstPageDirent, Dirent, BytesInFirstPage ); + + RtlCopyMemory( SecondPageDirent, + Dirent + DirentsInFirstPage, + DirentsNeeded*sizeof(DIRENT) - BytesInFirstPage ); + + ShortDirent = SecondPageDirent + (DirentsNeeded - DirentsInFirstPage) - 1; + } + + // + // Create a new dcb for the directory. + // + + Dcb = FatCreateDcb( IrpContext, + Vcb, + ParentDcb, + DirentByteOffset, + ShortDirentByteOffset, + ShortDirent, + CreateLfn ? UnicodeName : NULL ); + + // + // Tentatively add the new Ea's, + // + + if (EaLength > 0) { + + // + // This returns false if we are trying to create a file + // with Need Ea's and don't understand EA's. + // + + FatCreateEa( IrpContext, + Dcb->Vcb, + (PUCHAR) EaBuffer, + EaLength, + &Dcb->ShortName.Name.Oem, + &EaHandle ); + } + + + if (!FatIsFat32(Dcb->Vcb)) { + + ShortDirent->ExtendedAttributes = EaHandle; + } + + // + // After this point we cannot just simply mark the dirent deleted, + // we have to deal with the directory file object. + // + + // + // Make the dirent into a directory. Note that even if this call + // raises because of disk space, the diectory file object has been + // created. + // + + FatInitializeDirectoryDirent( IrpContext, Dcb, ShortDirent ); + + // + // Setup the context and section object pointers, and update + // our reference counts. Note that this call cannot fail. + // + + FatSetFileObject( FileObject, + UserDirectoryOpen, + Dcb, + Ccb = FatCreateCcb( IrpContext ) ); + + // + // Initialize the LongFileName if it has not already been set, so that + // FatNotify below won't have to. If there are filesystem filters + // attached to FAT, the LongFileName could have gotten set if the + // filter queried for name information on this file object while + // watching the IO needed in FatInitializeDirectoryDirent. + // + + if (Dcb->FullFileName.Buffer == NULL) { + + FatSetFullNameInFcb( IrpContext, Dcb, UnicodeName ); + } + + // + // We call the notify package to report that the + // we added a file. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Dcb, + FILE_NOTIFY_CHANGE_DIR_NAME, + FILE_ACTION_ADDED ); + + // + // Setup our share access + // + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Dcb->ShareAccess ); + + // + // From this point on, nothing can raise. + // + + Dcb->UncleanCount += 1; + Dcb->OpenCount += 1; + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + if (DeleteOnClose) { + + SetFlag( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + + // + // And set our return status + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_CREATED; + + } _SEH2_FINALLY { + + DebugUnwind( FatCreateNewDirectory ); + + LocalAbnormalTermination = _SEH2_AbnormalTermination(); + + // + // If this is an abnormal termination then undo our work + // + + if (LocalAbnormalTermination) { + + // + // We always have to delete the Ccb if we created one. + // + + if ( Ccb != NULL ) { + + FatDeleteCcb( IrpContext, Ccb ); + } + + if ( Dcb == NULL) { + + ASSERT( (ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || + RtlAreBitsSet( &ParentDcb->Specific.Dcb.FreeDirentBitmap, + DirentByteOffset / sizeof(DIRENT), + DirentsNeeded ) ); + + RtlClearBits( &ParentDcb->Specific.Dcb.FreeDirentBitmap, + DirentByteOffset / sizeof(DIRENT), + DirentsNeeded ); + + // + // Mark the dirents deleted. The codes is complex because of + // dealing with an LFN than crosses a page boundry. + // + + if (Dirent != NULL) { + + ULONG i; + + // + // We failed before creating a directory file object. + // We can just mark the dirent deleted and exit. + // + + for (i = 0; i < DirentsNeeded; i++) { + + if (DirentFromPool == FALSE) { + + // + // Simple case. + // + + Dirent[i].FileName[0] = FAT_DIRENT_DELETED; + + } else { + + // + // If the second CcPreparePinWrite failed, we have + // to stop early. + // + + if ((SecondPageBcb == NULL) && + (i == DirentsInFirstPage)) { + + break; + } + + // + // Now conditionally update either page. + // + + if (i < DirentsInFirstPage) { + + FirstPageDirent[i].FileName[0] = FAT_DIRENT_DELETED; + + } else { + + SecondPageDirent[i - DirentsInFirstPage].FileName[0] = FAT_DIRENT_DELETED; + } + } + } + } + } + } + + // + // Just drop the Bcbs we have in the parent right now so if this + // was abnormal termination and we take the path to rip apart + // the partially created child, when we sync-uninit we won't cause + // a lazy writer processing the parent to block on us. This would + // consume one of the lazy writers, one of which must be running free + // in order for us to come back from the sync-uninit. + // + // Neat, huh? + // + // Granted, the delete dirent below will be marginally less efficient + // since the Bcb may be reclaimed by the time it executes. Life is + // tough. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + FatUnpinBcb( IrpContext, SecondPageBcb ); + + if (DirentFromPool) { + + ExFreePool( Dirent ); + } + + if (LocalAbnormalTermination) { + + if (Dcb != NULL) { + + // + // We have created the Dcb. If an error occurred while + // creating the Ea's, there will be no directory file + // object. + // + + PFILE_OBJECT DirectoryFileObject; + + DirectoryFileObject = Dcb->Specific.Dcb.DirectoryFile; + + // + // Knock down all of the repinned data so we can begin to destroy + // this failed child. We don't care about any raising here - we're + // already got a fire going. + // + // Note that if we failed to do this, the repinned initial pieces + // of the child would cause the sync-uninit to block forever. + // + // A previous spin on this fix had us not make the ./.. creation + // "reversible" (bad term) and thus avoid having the Bcb still + // outstanding. This wound up causing very bad things to happen + // on DMF floppies when we tried to do a similar yank-down in the + // create path - we want the purge it does to make sure we never + // try to write the bytes out ... it is just a lot cleaner to + // unpinrepin. I'll leave the reversible logic in place if it ever + // proves useful. + // + + // + // There is a possibility that this may be a generally good idea + // for "live" finally clauses - set in ExceptionFilter, clear in + // ProcessException. Think about this. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE ); + FatUnpinRepinnedBcbs( IrpContext ); + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE ); + + if (DirectoryFileObject != NULL) { + + FatSyncUninitializeCacheMap( IrpContext, + DirectoryFileObject ); + } + + _SEH2_TRY { + + // + // Now zap the allocation backing it. Do it after removing the cachemap so that + // pending writes don't get perplexed looking at free FAT entries. + // + + FatTruncateFileAllocation( IrpContext, Dcb, 0); + + } _SEH2_FINALLY { + + _SEH2_TRY { + + // + // Remove the directory entry we made in the parent Dcb. + // + + FatDeleteDirent( IrpContext, Dcb, NULL, TRUE ); + + } _SEH2_FINALLY { + + // + // Finaly, dereference the directory file object. This will + // cause a close Irp to be processed, blowing away the Fcb. + // + + if (DirectoryFileObject != NULL) { + + // + // Dereference the file object for this DCB. The DCB will + // go away when this file object is closed. + // + + Dcb->Specific.Dcb.DirectoryFile = NULL; + ObDereferenceObject( DirectoryFileObject ); + } else { + + // + // This was also a PDK fix. If the stream file exists, this would + // be done during the dereference file object operation. Otherwise + // we have to remove the Dcb and check if we should remove the parent. + // For now we will just leave the parent lying around. + // + + FatDeleteFcb( IrpContext, Dcb ); + } + } _SEH2_END; + } _SEH2_END; + } + + } + + DebugTrace(-1, Dbg, "FatCreateNewDirectory -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + UNREFERENCED_PARAMETER( EaBuffer ); + UNREFERENCED_PARAMETER( EaLength ); + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatCreateNewFile ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN POEM_STRING OemName, + IN PUNICODE_STRING UnicodeName, + IN PACCESS_MASK DesiredAccess, + IN USHORT ShareAccess, + IN ULONG AllocationSize, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN PUNICODE_STRING LfnBuffer, + IN BOOLEAN IsPagingFile, + IN BOOLEAN NoEaKnowledge, + IN BOOLEAN DeleteOnClose, + IN BOOLEAN TemporaryFile + ) + +/*++ + +Routine Description: + + This routine creates a new file. The file has already been verified + not to exist yet. + +Arguments: + + FileObject - Supplies the file object for the newly created file + + Vcb - Supplies the Vcb denote the volume to contain the new file + + ParentDcb - Supplies the parent directory containg the newly created + File + + OemName - Supplies the Oem name for the newly created file. It may + or maynot be 8.3 complient, but will be upcased. + + UnicodeName - Supplies the Unicode name for the newly created file. + It may or maynot be 8.3 complient. This name contains the original + case information. + + DesiredAccess - Supplies the desired access of the caller + + ShareAccess - Supplies the shared access of the caller + + AllocationSize - Supplies the initial allocation size for the file + + EaBuffer - Supplies the Ea set for the newly created file + + EaLength - Supplies the length, in bytes, of EaBuffer + + FileAttributes - Supplies the file attributes for the newly created + file + + LfnBuffer - A MAX_LFN sized buffer for directory searching + + IsPagingFile - Indicates if this is the paging file being created + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + + DeleteOnClose - The caller wants the file gone when the handle is closed + + TemporaryFile - Signals the lazywriter to not write dirty data unless + absolutely has to. + + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + PFCB Fcb; + + PDIRENT Dirent = NULL; + PBCB DirentBcb = NULL; + ULONG DirentsNeeded; + ULONG DirentByteOffset; + + PDIRENT ShortDirent; + ULONG ShortDirentByteOffset; + + USHORT EaHandle; + + BOOLEAN AllLowerComponent; + BOOLEAN AllLowerExtension; + BOOLEAN CreateLfn; + + ULONG BytesInFirstPage; + ULONG DirentsInFirstPage; + PDIRENT FirstPageDirent; + + PBCB SecondPageBcb = NULL; + ULONG SecondPageOffset; + PDIRENT SecondPageDirent; + + BOOLEAN DirentFromPool = FALSE; + + OEM_STRING ShortName; + UCHAR ShortNameBuffer[12]; + + UNICODE_STRING UniTunneledShortName; + WCHAR UniTunneledShortNameBuffer[12]; + UNICODE_STRING UniTunneledLongName; + WCHAR UniTunneledLongNameBuffer[26]; + LARGE_INTEGER TunneledCreationTime; + ULONG TunneledDataSize; + BOOLEAN HaveTunneledInformation; + BOOLEAN UsingTunneledLfn = FALSE; + + PUNICODE_STRING RealUnicodeName; + + // + // The following variables are for abnormal termination + // + + PDIRENT UnwindDirent = NULL; + PFCB UnwindFcb = NULL; + BOOLEAN UnwindAllocation = FALSE; + PCCB UnwindCcb = NULL; + + ULONG LocalAbnormalTermination = FALSE; + + DebugTrace(+1, Dbg, "FatCreateNewFile...\n", 0); + + ShortName.Length = 0; + ShortName.MaximumLength = sizeof(ShortNameBuffer); +#ifndef __REACTOS__ + ShortName.Buffer = &ShortNameBuffer[0]; +#else + ShortName.Buffer = (PCHAR)&ShortNameBuffer[0]; +#endif + + UniTunneledShortName.Length = 0; + UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer); + UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0]; + + UniTunneledLongName.Length = 0; + UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer); + UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0]; + + EaHandle = 0; + + // + // We fail this operation if the caller doesn't understand Ea's. + // + + if (NoEaKnowledge + && EaLength > 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + + DebugTrace(-1, Dbg, "FatCreateNewFile -> Iosb.Status = %08lx\n", Iosb.Status); + return Iosb; + } + + // + // DeleteOnClose and ReadOnly are not compatible. + // + + if (DeleteOnClose && FlagOn(FileAttributes, FAT_DIRENT_ATTR_READ_ONLY)) { + + Iosb.Status = STATUS_CANNOT_DELETE; + return Iosb; + } + + // + // Look in the tunnel cache for names and timestamps to restore + // + + TunneledDataSize = sizeof(LARGE_INTEGER); + HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel, + FatDirectoryKey(ParentDcb), + UnicodeName, + &UniTunneledShortName, + &UniTunneledLongName, + &TunneledDataSize, + &TunneledCreationTime ); + ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER)); + + // + // Now get the names that we will be using. + // + + FatSelectNames( IrpContext, + ParentDcb, + OemName, + UnicodeName, + &ShortName, + (HaveTunneledInformation? &UniTunneledShortName : NULL), + &AllLowerComponent, + &AllLowerExtension, + &CreateLfn ); + + // + // If we are not in Chicago mode, ignore the magic bits. + // + + RealUnicodeName = UnicodeName; + + if (!FatData.ChicagoMode) { + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + CreateLfn = FALSE; + + } else { + + // + // If the Unicode name was legal for a short name and we got + // a tunneling hit which had a long name associated which is + // avaliable for use, use it. + // + + if (!CreateLfn && + UniTunneledLongName.Length && + !FatLfnDirentExists(IrpContext, ParentDcb, &UniTunneledLongName, LfnBuffer)) { + + UsingTunneledLfn = TRUE; + CreateLfn = TRUE; + + RealUnicodeName = &UniTunneledLongName; + + // + // Short names are always upcase if an LFN exists + // + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + } + } + + // + // Create/allocate a new dirent + // + + DirentsNeeded = CreateLfn ? FAT_LFN_DIRENTS_NEEDED(RealUnicodeName) + 1 : 1; + + DirentByteOffset = FatCreateNewDirent( IrpContext, + ParentDcb, + DirentsNeeded ); + + _SEH2_TRY { + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDcb, + DirentByteOffset, + sizeof(DIRENT), + &DirentBcb, +#ifndef __REACTOS__ + &Dirent, +#else + (PVOID *)&Dirent, +#endif + FALSE, + TRUE, + &Iosb.Status ); + + ASSERT( NT_SUCCESS( Iosb.Status ) ); + + UnwindDirent = Dirent; + + // + // Deal with the special case of an LFN + Dirent structure crossing + // a page boundry. + // + + if ((DirentByteOffset / PAGE_SIZE) != + ((DirentByteOffset + (DirentsNeeded - 1) * sizeof(DIRENT)) / PAGE_SIZE)) { + + SecondPageBcb; + SecondPageOffset; + SecondPageDirent; + + SecondPageOffset = (DirentByteOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE; + + BytesInFirstPage = SecondPageOffset - DirentByteOffset; + + DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT); + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDcb, + SecondPageOffset, + sizeof(DIRENT), + &SecondPageBcb, +#ifndef __REACTOS__ + &SecondPageDirent, +#else + (PVOID *)&SecondPageDirent, +#endif + FALSE, + TRUE, + &Iosb.Status ); + + ASSERT( NT_SUCCESS( Iosb.Status ) ); + + FirstPageDirent = Dirent; + + Dirent = FsRtlAllocatePoolWithTag( PagedPool, + DirentsNeeded * sizeof(DIRENT), + TAG_DIRENT ); + + DirentFromPool = TRUE; + } + + // + // Bump up Dirent and DirentByteOffset + // + + ShortDirent = Dirent + DirentsNeeded - 1; + ShortDirentByteOffset = DirentByteOffset + + (DirentsNeeded - 1) * sizeof(DIRENT); + + ASSERT( NT_SUCCESS( Iosb.Status )); + + + // + // Fill in the fields of the dirent. + // + + FatConstructDirent( IrpContext, + ShortDirent, + &ShortName, + AllLowerComponent, + AllLowerExtension, + CreateLfn ? RealUnicodeName : NULL, + (UCHAR)(FileAttributes | FILE_ATTRIBUTE_ARCHIVE), + TRUE, + (HaveTunneledInformation ? &TunneledCreationTime : NULL) ); + + // + // If the dirent crossed pages, we have to do some real gross stuff. + // + + if (DirentFromPool) { + + RtlCopyMemory( FirstPageDirent, Dirent, BytesInFirstPage ); + + RtlCopyMemory( SecondPageDirent, + Dirent + DirentsInFirstPage, + DirentsNeeded*sizeof(DIRENT) - BytesInFirstPage ); + + ShortDirent = SecondPageDirent + (DirentsNeeded - DirentsInFirstPage) - 1; + } + + // + // Create a new Fcb for the file. Once the Fcb is created we + // will not need to unwind dirent because delete dirent will + // now do the work. + // + + Fcb = UnwindFcb = FatCreateFcb( IrpContext, + Vcb, + ParentDcb, + DirentByteOffset, + ShortDirentByteOffset, + ShortDirent, + CreateLfn ? RealUnicodeName : NULL, + IsPagingFile, + FALSE ); + UnwindDirent = NULL; + + // + // If this is a temporary file, note it in the FcbState + // + + if (TemporaryFile) { + + SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY ); + } + + // + // Add some initial file allocation + // + + FatAddFileAllocation( IrpContext, Fcb, FileObject, AllocationSize ); + UnwindAllocation = TRUE; + + Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; + + // + // Tentatively add the new Ea's + // + + if ( EaLength > 0 ) { + + FatCreateEa( IrpContext, + Fcb->Vcb, + (PUCHAR) EaBuffer, + EaLength, + &Fcb->ShortName.Name.Oem, + &EaHandle ); + } + + + if (!FatIsFat32(Fcb->Vcb)) { + + ShortDirent->ExtendedAttributes = EaHandle; + } + + // + // Initialize the LongFileName right now so that FatNotify + // below won't have to. + // + + FatSetFullNameInFcb( IrpContext, Fcb, RealUnicodeName ); + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserFileOpen, + Fcb, + UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers; + + // + // We call the notify package to report that the + // we added a file. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_FILE_NAME, + FILE_ACTION_ADDED ); + + // + // Setup our share access + // + + IoSetShareAccess( *DesiredAccess, + ShareAccess, + FileObject, + &Fcb->ShareAccess ); + + Fcb->UncleanCount += 1; + Fcb->OpenCount += 1; + if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) { + Fcb->NonCachedUncleanCount += 1; + } + Vcb->OpenFileCount += 1; + if (IsFileObjectReadOnly(FileObject)) { Vcb->ReadOnlyCount += 1; } + + // + // And set our return status + // + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_CREATED; + + if ( NT_SUCCESS(Iosb.Status) ) { + + // + // Mark the DeleteOnClose bit if the operation was successful. + // + + if ( DeleteOnClose ) { + + SetFlag( UnwindCcb->Flags, CCB_FLAG_DELETE_ON_CLOSE ); + } + + // + // Mark the OpenedByShortName bit if the operation was successful. + // If we created an Lfn, we have some sort of generated short name + // and thus don't consider ourselves to have opened it - though we + // may have had a case mix Lfn "Foo.bar" and generated "FOO.BAR" + // + // Unless, of course, we wanted to create a short name and hit an + // associated Lfn in the tunnel cache + // + + if ( !CreateLfn && !UsingTunneledLfn ) { + + SetFlag( UnwindCcb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME ); + } + } + + } _SEH2_FINALLY { + + DebugUnwind( FatCreateNewFile ); + + if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) { + + // + // Tunneling package grew the buffer from pool + // + + ExFreePool( UniTunneledLongName.Buffer ); + } + + // + // If this is an abnormal termination then undo our work. + // + // The extra exception handling here is so nasty. We've got + // two places here where an exception can be thrown again. + // + + LocalAbnormalTermination = _SEH2_AbnormalTermination(); + + if (LocalAbnormalTermination) { + + if (UnwindFcb == NULL) { + + ASSERT( (ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || + RtlAreBitsSet( &ParentDcb->Specific.Dcb.FreeDirentBitmap, + DirentByteOffset / sizeof(DIRENT), + DirentsNeeded ) ); + + RtlClearBits( &ParentDcb->Specific.Dcb.FreeDirentBitmap, + DirentByteOffset / sizeof(DIRENT), + DirentsNeeded ); + } + + // + // Mark the dirents deleted. The code is complex because of + // dealing with an LFN than crosses a page boundry. + // + + if (UnwindDirent != NULL) { + + ULONG i; + + for (i = 0; i < DirentsNeeded; i++) { + + if (DirentFromPool == FALSE) { + + // + // Simple case. + // + + Dirent[i].FileName[0] = FAT_DIRENT_DELETED; + + } else { + + // + // If the second CcPreparePinWrite failed, we have + // to stop early. + // + + if ((SecondPageBcb == NULL) && + (i == DirentsInFirstPage)) { + + break; + } + + // + // Now conditionally update either page. + // + + if (i < DirentsInFirstPage) { + + FirstPageDirent[i].FileName[0] = FAT_DIRENT_DELETED; + + } else { + + SecondPageDirent[i - DirentsInFirstPage].FileName[0] = FAT_DIRENT_DELETED; + } + } + } + } + } + + // + // We must handle exceptions in the following fragments and plow on with the + // unwind of this create operation. This is basically inverted from the + // previous state of the code. Since AbnormalTermination() changes when we + // enter a new enclosure, we cached the original state ... + // + + _SEH2_TRY { + + if (LocalAbnormalTermination) { + if (UnwindAllocation) { + FatTruncateFileAllocation( IrpContext, Fcb, 0 ); + } + } + + } _SEH2_FINALLY { + + _SEH2_TRY { + + if (LocalAbnormalTermination) { + if (UnwindFcb != NULL) { + FatDeleteDirent( IrpContext, UnwindFcb, NULL, TRUE ); + } + } + + } _SEH2_FINALLY { + + if (LocalAbnormalTermination) { + if (UnwindFcb != NULL) { FatDeleteFcb( IrpContext, UnwindFcb ); } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, UnwindCcb ); } + } + + // + // This is the normal cleanup code. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + FatUnpinBcb( IrpContext, SecondPageBcb ); + + if (DirentFromPool) { + + ExFreePool( Dirent ); + } + + } _SEH2_END; + } _SEH2_END; + + DebugTrace(-1, Dbg, "FatCreateNewFile -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + return Iosb; +} + + +// +// Internal support routine +// + +IO_STATUS_BLOCK +FatSupersedeOrOverwriteFile ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PFCB Fcb, + IN ULONG AllocationSize, + IN PFILE_FULL_EA_INFORMATION EaBuffer, + IN ULONG EaLength, + IN UCHAR FileAttributes, + IN ULONG CreateDisposition, + IN BOOLEAN NoEaKnowledge + ) + +/*++ + +Routine Description: + + This routine performs a file supersede or overwrite operation. + +Arguments: + + FileObject - Supplies a pointer to the file object + + Fcb - Supplies a pointer to the Fcb + + AllocationSize - Supplies an initial allocation size + + EaBuffer - Supplies the Ea set for the superseded/overwritten file + + EaLength - Supplies the length, in bytes, of EaBuffer + + FileAttributes - Supplies the supersede/overwrite file attributes + + CreateDisposition - Supplies the create disposition for the file + It must be either supersede, overwrite, or overwrite if. + + NoEaKnowledge - This opener doesn't understand Ea's and we fail this + open if the file has NeedEa's. + +Return Value: + + IO_STATUS_BLOCK - Returns the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + PDIRENT Dirent; + PBCB DirentBcb; + + USHORT EaHandle = 0; + BOOLEAN EaChange = FALSE; + BOOLEAN ReleasePaging = FALSE; + + PCCB Ccb; + + ULONG NotifyFilter; + + // + // The following variables are for abnormal termination + // + + PCCB UnwindCcb = NULL; + USHORT UnwindEa = 0; + + DebugTrace(+1, Dbg, "FatSupersedeOrOverwriteFile...\n", 0); + + DirentBcb = NULL; + + // + // We fail this operation if the caller doesn't understand Ea's. + // + + if (NoEaKnowledge + && EaLength > 0) { + + Iosb.Status = STATUS_ACCESS_DENIED; + + DebugTrace(-1, Dbg, "FatSupersedeOrOverwriteFile -> Iosb.Status = %08lx\n", Iosb.Status); + return Iosb; + } + + _SEH2_TRY { + + // + // Before we actually truncate, check to see if the purge + // is going to fail. + // + + if (!MmCanFileBeTruncated( &Fcb->NonPaged->SectionObjectPointers, + &FatLargeZero )) { + + try_return( Iosb.Status = STATUS_USER_MAPPED_FILE ); + } + + // + // Setup the context and section object pointers, and update + // our reference counts + // + + FatSetFileObject( FileObject, + UserFileOpen, + Fcb, + Ccb = UnwindCcb = FatCreateCcb( IrpContext )); + + FileObject->SectionObjectPointer = &Fcb->NonPaged->SectionObjectPointers; + + // + // Since this is an supersede/overwrite, purge the section so + // that mappers will see zeros. We set the CREATE_IN_PROGRESS flag + // to prevent the Fcb from going away out from underneath us. + // + + SetFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS); + + CcPurgeCacheSection( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, FALSE ); + + // + // Tentatively add the new Ea's + // + + if (EaLength > 0) { + + FatCreateEa( IrpContext, + Fcb->Vcb, + (PUCHAR) EaBuffer, + EaLength, + &Fcb->ShortName.Name.Oem, + &EaHandle ); + + UnwindEa = EaHandle; + EaChange = TRUE; + } + + // + // Now set the new allocation size, we do that by first + // zeroing out the current file size. Then we truncate and + // allocate up to the new allocation size + // + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, + TRUE ); + ReleasePaging = TRUE; + + Fcb->Header.FileSize.LowPart = 0; + Fcb->Header.ValidDataLength.LowPart = 0; + Fcb->ValidDataToDisk = 0; + + // + // Tell the cache manager the size went to zero + // This call is unconditional, because MM always wants to know. + // + + CcSetFileSizes( FileObject, + (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); + + FatTruncateFileAllocation( IrpContext, Fcb, AllocationSize ); + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + ReleasePaging = FALSE; + + FatAddFileAllocation( IrpContext, Fcb, FileObject, AllocationSize ); + + Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; + + // + // Modify the attributes and time of the file, by first reading + // in the dirent for the file and then updating its attributes + // and time fields. Note that for supersede we replace the file + // attributes as opposed to adding to them. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &DirentBcb ); + + // + // We must get the dirent since this Fcb is in good condition, verified as + // we crawled down the prefix tree. Prefix (no relation) isn't noticing + // this guarantee, so I'll help. + // + + ASSERT( Dirent && DirentBcb ); + + // + // Update the appropriate dirent fields, and the fcb fields + // + + Dirent->FileSize = 0; + + FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + + if (CreateDisposition == FILE_SUPERSEDE) { + + Dirent->Attributes = FileAttributes; + + } else { + + Dirent->Attributes |= FileAttributes; + } + + Fcb->DirentFatFlags = Dirent->Attributes; + + KeQuerySystemTime( &Fcb->LastWriteTime ); + + (VOID)FatNtTimeToFatTime( IrpContext, + &Fcb->LastWriteTime, + TRUE, + &Dirent->LastWriteTime, + NULL ); + + if (FatData.ChicagoMode) { + + Dirent->LastAccessDate = Dirent->LastWriteTime.Date; + } + + NotifyFilter = FILE_NOTIFY_CHANGE_LAST_WRITE + | FILE_NOTIFY_CHANGE_ATTRIBUTES + | FILE_NOTIFY_CHANGE_SIZE; + + // + // And now delete the previous Ea set if there was one. + // + + if (!FatIsFat32(Fcb->Vcb) && Dirent->ExtendedAttributes != 0) { + + // + // **** SDK fix, we won't fail this if there is + // an error in the Ea's, we'll just leave + // the orphaned Ea's in the file. + // + + EaChange = TRUE; + + _SEH2_TRY { + + FatDeleteEa( IrpContext, + Fcb->Vcb, + Dirent->ExtendedAttributes, + &Fcb->ShortName.Name.Oem ); + + } _SEH2_EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + } + + // + // Update the extended attributes handle in the dirent. + // + + if (EaChange) { + + ASSERT(!FatIsFat32(Fcb->Vcb)); + + Dirent->ExtendedAttributes = EaHandle; + + NotifyFilter |= FILE_NOTIFY_CHANGE_EA; + } + + // + // Now update the dirent to the new ea handle and set the bcb dirty + // Once we do this we can no longer back out the Ea + // + + FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); + UnwindEa = 0; + + // + // Indicate that the Eas for this file have changed. + // + + Ccb->EaModificationCount += Fcb->EaModificationCount; + + // + // Check to see if we need to notify outstanding Irps for full + // changes only (i.e., we haven't added, deleted, or renamed the file). + // + + FatNotifyReportChange( IrpContext, + Fcb->Vcb, + Fcb, + NotifyFilter, + FILE_ACTION_MODIFIED ); + + // + // And set our status to success + // + + Iosb.Status = STATUS_SUCCESS; + + if (CreateDisposition == FILE_SUPERSEDE) { + + Iosb.Information = FILE_SUPERSEDED; + + } else { + + Iosb.Information = FILE_OVERWRITTEN; + } + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatSupersedeOfOverwriteFile ); + + if (ReleasePaging) { ExReleaseResourceLite( Fcb->Header.PagingIoResource ); } + + // + // If this is an abnormal termination then undo our work. + // + + if (_SEH2_AbnormalTermination()) { + + if (UnwindEa != 0) { FatDeleteEa( IrpContext, Fcb->Vcb, UnwindEa, &Fcb->ShortName.Name.Oem ); } + if (UnwindCcb != NULL) { FatDeleteCcb( IrpContext, UnwindCcb ); } + } + + FatUnpinBcb( IrpContext, DirentBcb ); + + ClearFlag(Fcb->Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS); + + DebugTrace(-1, Dbg, "FatSupersedeOrOverwriteFile -> Iosb.Status = %08lx\n", Iosb.Status); + } _SEH2_END; + + return Iosb; +} + +VOID +FatSetFullNameInFcb( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PUNICODE_STRING FinalName + ) + +/*++ + +Routine Description: + + This routine attempts a quick form of the full FatSetFullFileNameInFcb + operation. + + NOTE: this routine is probably not worth the code duplication involved, + and is not equipped to handle the cases where the parent doesn't have + the full name set up. + +Arguments: + + Fcb - Supplies a pointer to the Fcb + + FinalName - Supplies the last component of the path to this Fcb's dirent + +Return Value: + + None. May silently fail. + +--*/ + +{ + ASSERT( Fcb->FullFileName.Buffer == NULL ); + + // + // Prefer the ExactCaseLongName of the file for this operation, if set. In + // this way we avoid building the fullname with a short filename. Several + // operations assume this - the FinalNameLength in particular is the Lfn + // (if existant) length, and we use this to crack the fullname in paths + // such as the FsRtlNotify caller. + // + // If the caller specified a particular name and it is short, it is the + // case that the long name was set up. + // + + if (Fcb->ExactCaseLongName.Buffer) { + + ASSERT( Fcb->ExactCaseLongName.Length != 0 ); + FinalName = &Fcb->ExactCaseLongName; + } + + // + // Special case the root. + // + + if (NodeType(Fcb->ParentDcb) == FAT_NTC_ROOT_DCB) { + + Fcb->FullFileName.Length = + Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + FinalName->Length; + + Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Fcb->FullFileName.Length, + TAG_FILENAME_BUFFER ); + + Fcb->FullFileName.Buffer[0] = L'\\'; + + RtlCopyMemory( &Fcb->FullFileName.Buffer[1], + &FinalName->Buffer[0], + FinalName->Length ); + + } else { + + PUNICODE_STRING Prefix; + + Prefix = &Fcb->ParentDcb->FullFileName; + + // + // It is possible our parent's full filename is not set. Simply fail + // this attempt. + // + + if (Prefix->Buffer == NULL) { + + return; + } + + Fcb->FullFileName.Length = + Fcb->FullFileName.MaximumLength = Prefix->Length + sizeof(WCHAR) + FinalName->Length; + + Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Fcb->FullFileName.Length, + TAG_FILENAME_BUFFER ); + + RtlCopyMemory( &Fcb->FullFileName.Buffer[0], + &Prefix->Buffer[0], + Prefix->Length ); + + Fcb->FullFileName.Buffer[Prefix->Length / sizeof(WCHAR)] = L'\\'; + + RtlCopyMemory( &Fcb->FullFileName.Buffer[(Prefix->Length / sizeof(WCHAR)) + 1], + &FinalName->Buffer[0], + FinalName->Length ); + + } +} + + +NTSTATUS +FatCheckSystemSecurityAccess( + PIRP_CONTEXT IrpContext + ) +{ + PACCESS_STATE AccessState; + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + + // + // We check if the caller wants ACCESS_SYSTEM_SECURITY access on this + // object and fail the request if he does. + // + + ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL ); + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + // + // Check if the remaining privilege includes ACCESS_SYSTEM_SECURITY. + // + + if (FlagOn( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY )) { + + if (!SeSinglePrivilegeCheck( FatSecurityPrivilege, + UserMode )) { + + return STATUS_ACCESS_DENIED; + } + + // + // Move this privilege from the Remaining access to Granted access. + // + + ClearFlag( AccessState->RemainingDesiredAccess, ACCESS_SYSTEM_SECURITY ); + SetFlag( AccessState->PreviouslyGrantedAccess, ACCESS_SYSTEM_SECURITY ); + } + + return STATUS_SUCCESS; +} + + diff --git a/drivers/filesystems/fastfat_new/devctrl.c b/drivers/filesystems/fastfat_new/devctrl.c new file mode 100644 index 00000000000..deda8de7bbf --- /dev/null +++ b/drivers/filesystems/fastfat_new/devctrl.c @@ -0,0 +1,308 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DevCtrl.c + +Abstract: + + This module implements the File System Device Control routines for Fat + called by the dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_DEVCTRL) + +// +// Local procedure prototypes +// + +NTSTATUS +NTAPI +FatDeviceControlCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonDeviceControl) +#pragma alloc_text(PAGE, FatFsdDeviceControl) +#endif + + +NTSTATUS +NTAPI +FatFsdDeviceControl ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of Device control operations + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdDeviceControl\n", 0); + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp )); + + Status = FatCommonDeviceControl( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdDeviceControl -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonDeviceControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing Device control operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + + InFsp - Indicates if this is the fsp thread or someother thread + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + KEVENT WaitEvent; + PVOID CompletionContext = NULL; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + // + // Get a pointer to the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonDeviceControl\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatCommonDeviceControl -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + // + // A few IOCTLs actually require some intervention on our part + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + + case IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES: + + // + // This is sent by the Volume Snapshot driver (Lovelace). + // We flush the volume, and hold all file resources + // to make sure that nothing more gets dirty. Then we wait + // for the IRP to complete or cancel. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + FatAcquireExclusiveVolume( IrpContext, Vcb ); + + FatFlushAndCleanVolume( IrpContext, + Irp, + Vcb, + FlushWithoutPurge ); + + KeInitializeEvent( &WaitEvent, NotificationEvent, FALSE ); + CompletionContext = &WaitEvent; + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + IoSetCompletionRoutine( Irp, + FatDeviceControlCompletionRoutine, + CompletionContext, + TRUE, + TRUE, + TRUE ); + break; + + default: + + // + // FAT doesn't need to see this on the way back, so skip ourselves. + // + + IoSkipCurrentIrpStackLocation( Irp ); + break; + } + + // + // Send the request. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING && CompletionContext) { + + KeWaitForSingleObject( &WaitEvent, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // If we had a context, the IRP remains for us and we will complete it. + // Handle it appropriately. + // + + if (CompletionContext) { + + // + // Release all the resources that we held because of a + // VOLSNAP_FLUSH_AND_HOLD. + // + + ASSERT( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_VOLSNAP_FLUSH_AND_HOLD_WRITES ); + + FatReleaseVolume( IrpContext, Vcb ); + + // + // If we had no context, the IRP will complete asynchronously. + // + + } else { + + Irp = NULL; + } + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatCommonDeviceControl -> %08lx\n", Status); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +NTAPI +FatDeviceControlCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +{ + PKEVENT Event = (PKEVENT) Contxt; + + // + // If there is an event, this is a synch request. Signal and + // let I/O know this isn't done yet. + // + + if (Event) { + + KeSetEvent( Event, 0, FALSE ); + return STATUS_MORE_PROCESSING_REQUIRED; + } + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); + + return STATUS_SUCCESS; +} + diff --git a/drivers/filesystems/fastfat_new/deviosup.c b/drivers/filesystems/fastfat_new/deviosup.c new file mode 100644 index 00000000000..2e223dda103 --- /dev/null +++ b/drivers/filesystems/fastfat_new/deviosup.c @@ -0,0 +1,3284 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DevIoSup.c + +Abstract: + + This module implements the low lever disk read/write support for Fat. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_DEVIOSUP) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_DEVIOSUP) + +#define CollectDiskIoStats(VCB,FUNCTION,IS_USER_IO,COUNT) { \ + PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()].Common; \ + if (IS_USER_IO) { \ + if ((FUNCTION) == IRP_MJ_WRITE) { \ + Stats->UserDiskWrites += (COUNT); \ + } else { \ + Stats->UserDiskReads += (COUNT); \ + } \ + } else { \ + if ((FUNCTION) == IRP_MJ_WRITE) { \ + Stats->MetaDataDiskWrites += (COUNT); \ + } else { \ + Stats->MetaDataDiskReads += (COUNT); \ + } \ + } \ +} + +// +// Completion Routine declarations +// + +NTSTATUS +NTAPI +FatMultiSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +NTSTATUS +NTAPI +FatMultiAsyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +NTSTATUS +NTAPI +FatSpecialSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +NTSTATUS +NTAPI +FatSingleSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +NTSTATUS +NTAPI +FatSingleAsyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +NTSTATUS +NTAPI +FatPagingFileCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID MasterIrp + ); + +NTSTATUS +NTAPI +FatPagingFileCompletionRoutineCatch ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +VOID +FatSingleNonAlignedSync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PUCHAR Buffer, + IN LBO Lbo, + IN ULONG ByteCount, + IN PIRP Irp + ); + +// +// The following macro decides whether to send a request directly to +// the device driver, or to other routines. It was meant to +// replace IoCallDriver as transparently as possible. It must only be +// called with a read or write Irp. +// +// NTSTATUS +// FatLowLevelReadWrite ( +// PIRP_CONTEXT IrpContext, +// PDEVICE_OBJECT DeviceObject, +// PIRP Irp, +// PVCB Vcb +// ); +// + +#define FatLowLevelReadWrite(IRPCONTEXT,DO,IRP,VCB) ( \ + IoCallDriver((DO),(IRP)) \ +) + +// +// The following macro handles completion-time zeroing of buffers. +// + +#define FatDoCompletionZero( I, C ) \ + if ((C)->ZeroMdl) { \ + ASSERT( (C)->ZeroMdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA | \ + MDL_SOURCE_IS_NONPAGED_POOL));\ + if (NT_SUCCESS((I)->IoStatus.Status)) { \ + RtlZeroMemory( (C)->ZeroMdl->MappedSystemVa, \ + (C)->ZeroMdl->ByteCount ); \ + } \ + IoFreeMdl((C)->ZeroMdl); \ + (C)->ZeroMdl = NULL; \ + } + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatMultipleAsync) +#pragma alloc_text(PAGE, FatSingleAsync) +#pragma alloc_text(PAGE, FatSingleNonAlignedSync) +#pragma alloc_text(PAGE, FatWaitSync) +#pragma alloc_text(PAGE, FatLockUserBuffer) +#pragma alloc_text(PAGE, FatBufferUserBuffer) +#pragma alloc_text(PAGE, FatMapUserBuffer) +#pragma alloc_text(PAGE, FatNonCachedIo) +#pragma alloc_text(PAGE, FatSingleNonAlignedSync) +#pragma alloc_text(PAGE, FatNonCachedNonAlignedRead) +#endif + +typedef struct FAT_PAGING_FILE_CONTEXT { + KEVENT Event; + PMDL RestoreMdl; +} FAT_PAGING_FILE_CONTEXT, *PFAT_PAGING_FILE_CONTEXT; + + +VOID +FatPagingFileIo ( + IN PIRP Irp, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine performs the non-cached disk io described in its parameters. + This routine nevers blocks, and should only be used with the paging + file since no completion processing is performed. + +Arguments: + + Irp - Supplies the requesting Irp. + + Fcb - Supplies the file to act on. + +Return Value: + + None. + +--*/ + +{ + // + // Declare some local variables for enumeration through the + // runs of the file. + // + + VBO Vbo; + ULONG ByteCount; + + PMDL Mdl; + LBO NextLbo; + VBO NextVbo; + ULONG NextByteCount; + ULONG RemainingByteCount; + BOOLEAN MustSucceed; + + ULONG FirstIndex; + ULONG CurrentIndex; + ULONG LastIndex; + + LBO LastLbo; + ULONG LastByteCount; + + BOOLEAN MdlIsReserve = FALSE; + BOOLEAN IrpIsMaster = FALSE; + FAT_PAGING_FILE_CONTEXT Context; + LONG IrpCount; + + PIRP AssocIrp; + PIO_STACK_LOCATION IrpSp; + PIO_STACK_LOCATION NextIrpSp; + ULONG BufferOffset; + PDEVICE_OBJECT DeviceObject; + + DebugTrace(+1, Dbg, "FatPagingFileIo\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "Fcb = %08lx\n", Fcb ); + + ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )); + + // + // Initialize some locals. + // + + BufferOffset = 0; + DeviceObject = Fcb->Vcb->TargetDeviceObject; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + Vbo = IrpSp->Parameters.Read.ByteOffset.LowPart; + ByteCount = IrpSp->Parameters.Read.Length; + + MustSucceed = FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb, + Vbo, + &NextLbo, + &NextByteCount, + &FirstIndex); + + // + // If this run isn't present, something is very wrong. + // + + if (!MustSucceed) { + + FatBugCheck( Vbo, ByteCount, 0 ); + } + + // + // See if the write covers a single valid run, and if so pass + // it on. + // + + if ( NextByteCount >= ByteCount ) { + + DebugTrace( 0, Dbg, "Passing Irp on to Disk Driver\n", 0 ); + + // + // Setup the next IRP stack location for the disk driver beneath us. + // + + NextIrpSp = IoGetNextIrpStackLocation( Irp ); + + NextIrpSp->MajorFunction = IrpSp->MajorFunction; + NextIrpSp->Parameters.Read.Length = ByteCount; + NextIrpSp->Parameters.Read.ByteOffset.QuadPart = NextLbo; + + // + // Since this is Paging file IO, we'll just ignore the verify bit. + // + + SetFlag( NextIrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + // + // Set up the completion routine address in our stack frame. + // This is only invoked on error or cancel, and just copies + // the error Status into master irp's iosb. + // + // If the error implies a media problem, it also enqueues a + // worker item to write out the dirty bit so that the next + // time we run we will do a autochk /r + // + + IoSetCompletionRoutine( Irp, + &FatPagingFileCompletionRoutine, + Irp, + FALSE, + TRUE, + TRUE ); + + // + // Issue the read/write request + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be dealt with as a normal IO error. + // + + (VOID)IoCallDriver( DeviceObject, Irp ); + + DebugTrace(-1, Dbg, "FatPagingFileIo -> VOID\n", 0); + return; + } + + // + // Find out how may runs there are. + // + + MustSucceed = FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb, + Vbo + ByteCount - 1, + &LastLbo, + &LastByteCount, + &LastIndex); + + // + // If this run isn't present, something is very wrong. + // + + if (!MustSucceed) { + + FatBugCheck( Vbo + ByteCount - 1, 1, 0 ); + } + + CurrentIndex = FirstIndex; + + // + // Now set up the Irp->IoStatus. It will be modified by the + // multi-completion routine in case of error or verify required. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = ByteCount; + + // + // Loop while there are still byte writes to satisfy. The way we'll work this + // is to hope for the best - one associated IRP per run, which will let us be + // completely async after launching all the IO. + // + // IrpCount will indicate the remaining number of associated Irps to launch. + // + // All we have to do is make sure IrpCount doesn't hit zero before we're building + // the very last Irp. If it is positive when we're done, it means we have to + // wait for the rest of the associated Irps to come back before we complete the + // master by hand. + // + // This will keep the master from completing early. + // + + Irp->AssociatedIrp.IrpCount = IrpCount = LastIndex - FirstIndex + 1; + + while (CurrentIndex <= LastIndex) { + + // + // Reset this for unwinding purposes + // + + AssocIrp = NULL; + + // + // If next run is larger than we need, "ya get what ya need". + // + + if (NextByteCount > ByteCount) { + NextByteCount = ByteCount; + } + + RemainingByteCount = 0; + + // + // Allocate and build a partial Mdl for the request. + // + + Mdl = IoAllocateMdl( (PCHAR)Irp->UserBuffer + BufferOffset, + NextByteCount, + FALSE, + FALSE, + AssocIrp ); + + if (Mdl == NULL) { + + // + // Pick up the reserve MDL + // + + KeWaitForSingleObject( &FatReserveEvent, Executive, KernelMode, FALSE, NULL ); + + Mdl = FatReserveMdl; + MdlIsReserve = TRUE; + + // + // Trim to fit the size of the reserve MDL. + // + + if (NextByteCount > FAT_RESERVE_MDL_SIZE * PAGE_SIZE) { + + RemainingByteCount = NextByteCount - FAT_RESERVE_MDL_SIZE * PAGE_SIZE; + NextByteCount = FAT_RESERVE_MDL_SIZE * PAGE_SIZE; + } + } + + IoBuildPartialMdl( Irp->MdlAddress, + Mdl, + (PCHAR)Irp->UserBuffer + BufferOffset, + NextByteCount ); + + // + // Now that we have properly bounded this piece of the transfer, it is + // time to read/write it. We can simplify life slightly by always + // re-using the master IRP for cases where we use the reserve MDL, + // since we'll always be synchronous for those and can use a single + // completion context on our local stack. + // + // We also must prevent ourselves from issuing an associated IRP that would + // complete the master UNLESS this is the very last IRP we'll issue. + // + // This logic looks a bit nasty, but is hopefully straightforward. + // + + if (!MdlIsReserve && + (IrpCount != 1 || + (CurrentIndex == LastIndex && + RemainingByteCount == 0))) { + + AssocIrp = IoMakeAssociatedIrp( Irp, (CCHAR)(DeviceObject->StackSize + 1) ); + } + + if (AssocIrp == NULL) { + + AssocIrp = Irp; + IrpIsMaster = TRUE; + + // + // We need to drain the associated Irps so we can reliably figure out if + // the master Irp is showing a failed status, in which case we bail out + // immediately - as opposed to putting the value in the status field in + // jeopardy due to our re-use of the master Irp. + // + + while (Irp->AssociatedIrp.IrpCount != IrpCount) { + + KeDelayExecutionThread (KernelMode, FALSE, &Fat30Milliseconds); + } + + // + // Note that since we failed to launch this associated Irp, that the completion + // code at the bottom will take care of completing the master Irp. + // + + if (!NT_SUCCESS(Irp->IoStatus.Status)) { + + ASSERT( IrpCount ); + break; + } + + } else { + + // + // Indicate we used an associated Irp. + // + + IrpCount -= 1; + } + + // + // With an associated IRP, we must take over the first stack location so + // we can have one to put the completion routine on. When re-using the + // master IRP, its already there. + // + + if (!IrpIsMaster) { + + // + // Get the first IRP stack location in the associated Irp + // + + IoSetNextIrpStackLocation( AssocIrp ); + NextIrpSp = IoGetCurrentIrpStackLocation( AssocIrp ); + + // + // Setup the Stack location to describe our read. + // + + NextIrpSp->MajorFunction = IrpSp->MajorFunction; + NextIrpSp->Parameters.Read.Length = NextByteCount; + NextIrpSp->Parameters.Read.ByteOffset.QuadPart = Vbo; + + // + // We also need the VolumeDeviceObject in the Irp stack in case + // we take the failure path. + // + + NextIrpSp->DeviceObject = IrpSp->DeviceObject; + + } else { + + // + // Save the MDL in the IRP and prepare the stack + // context for the completion routine. + // + + KeInitializeEvent( &Context.Event, SynchronizationEvent, FALSE ); + Context.RestoreMdl = Irp->MdlAddress; + } + + // + // And drop our Mdl into the Irp. + // + + AssocIrp->MdlAddress = Mdl; + + // + // Set up the completion routine address in our stack frame. + // For true associated IRPs, this is only invoked on error or + // cancel, and just copies the error Status into master irp's + // iosb. + // + // If the error implies a media problem, it also enqueues a + // worker item to write out the dirty bit so that the next + // time we run we will do a autochk /r + // + + if (IrpIsMaster) { + + IoSetCompletionRoutine( AssocIrp, + FatPagingFileCompletionRoutineCatch, + &Context, + TRUE, + TRUE, + TRUE ); + + } else { + + IoSetCompletionRoutine( AssocIrp, + FatPagingFileCompletionRoutine, + Irp, + FALSE, + TRUE, + TRUE ); + } + + // + // Setup the next IRP stack location for the disk driver beneath us. + // + + NextIrpSp = IoGetNextIrpStackLocation( AssocIrp ); + + // + // Since this is paging file IO, we'll just ignore the verify bit. + // + + SetFlag( NextIrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + // + // Setup the Stack location to do a read from the disk driver. + // + + NextIrpSp->MajorFunction = IrpSp->MajorFunction; + NextIrpSp->Parameters.Read.Length = NextByteCount; + NextIrpSp->Parameters.Read.ByteOffset.QuadPart = NextLbo; + + (VOID)IoCallDriver( DeviceObject, AssocIrp ); + + // + // Wait for the Irp in the catch case and drop the flags. + // + + if (IrpIsMaster) { + + KeWaitForSingleObject( &Context.Event, Executive, KernelMode, FALSE, NULL ); + IrpIsMaster = MdlIsReserve = FALSE; + + // + // If the Irp is showing a failed status, there is no point in continuing. + // In doing so, we get to avoid squirreling away the failed status in case + // we were to re-use the master irp again. + // + // Note that since we re-used the master, we must not have issued the "last" + // associated Irp, and thus the completion code at the bottom will take care + // of that for us. + // + + if (!NT_SUCCESS(Irp->IoStatus.Status)) { + + ASSERT( IrpCount ); + break; + } + } + + // + // Now adjust everything for the next pass through the loop. + // + + Vbo += NextByteCount; + BufferOffset += NextByteCount; + ByteCount -= NextByteCount; + + // + // Try to lookup the next run, if we are not done and we got + // all the way through the current run. + // + + if (RemainingByteCount) { + + // + // Advance the Lbo/Vbo if we have more to do in the current run. + // + + NextLbo += NextByteCount; + NextVbo += NextByteCount; + + NextByteCount = RemainingByteCount; + + } else { + + CurrentIndex += 1; + + if ( CurrentIndex <= LastIndex ) { + + ASSERT( ByteCount != 0 ); + + FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb, + CurrentIndex, + &NextVbo, + &NextLbo, + &NextByteCount ); + + ASSERT( NextVbo == Vbo ); + } + } + } // while ( CurrentIndex <= LastIndex ) + + // + // If we didn't get enough associated Irps going to make this asynchronous, we + // twiddle our thumbs and wait for those we did launch to complete. + // + + if (IrpCount) { + + while (Irp->AssociatedIrp.IrpCount != IrpCount) { + + KeDelayExecutionThread (KernelMode, FALSE, &Fat30Milliseconds); + } + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + } + + DebugTrace(-1, Dbg, "FatPagingFileIo -> VOID\n", 0); + return; +} + + +NTSTATUS +FatNonCachedIo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB FcbOrDcb, + IN ULONG StartingVbo, + IN ULONG ByteCount, + IN ULONG UserByteCount + ) + +/*++ + +Routine Description: + + This routine performs the non-cached disk io described in its parameters. + The choice of a single run is made if possible, otherwise multiple runs + are executed. + +Arguments: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Irp - Supplies the requesting Irp. + + FcbOrDcb - Supplies the file to act on. + + StartingVbo - The starting point for the operation. + + ByteCount - The lengh of the operation. + + UserByteCount - The last byte the user can see, rest to be zeroed. + +Return Value: + + None. + +--*/ + +{ + // + // Declare some local variables for enumeration through the + // runs of the file, and an array to store parameters for + // parallel I/Os + // + + BOOLEAN Wait; + + LBO NextLbo; + VBO NextVbo; + ULONG NextByteCount; + BOOLEAN NextIsAllocated; + + LBO LastLbo; + ULONG LastByteCount; + BOOLEAN LastIsAllocated; + + BOOLEAN EndOnMax; + + ULONG FirstIndex; + ULONG CurrentIndex; + ULONG LastIndex; + + ULONG NextRun; + ULONG BufferOffset; + ULONG OriginalByteCount; + + IO_RUN StackIoRuns[FAT_MAX_IO_RUNS_ON_STACK]; + PIO_RUN IoRuns; + + DebugTrace(+1, Dbg, "FatNonCachedIo\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "FcbOrDcb = %08lx\n", FcbOrDcb ); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo ); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount ); + + if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { + + PFILE_SYSTEM_STATISTICS Stats = + &FcbOrDcb->Vcb->Statistics[KeGetCurrentProcessorNumber()]; + + if (IrpContext->MajorFunction == IRP_MJ_READ) { + Stats->Fat.NonCachedReads += 1; + Stats->Fat.NonCachedReadBytes += ByteCount; + } else { + Stats->Fat.NonCachedWrites += 1; + Stats->Fat.NonCachedWriteBytes += ByteCount; + } + } + + // + // Initialize some locals. + // + + NextRun = 0; + BufferOffset = 0; + OriginalByteCount = ByteCount; + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // For nonbuffered I/O, we need the buffer locked in all + // cases. + // + // This call may raise. If this call succeeds and a subsequent + // condition is raised, the buffers are unlocked automatically + // by the I/O system when the request is completed, via the + // Irp->MdlAddress field. + // + + FatLockUserBuffer( IrpContext, + Irp, + (IrpContext->MajorFunction == IRP_MJ_READ) ? + IoWriteAccess : IoReadAccess, + ByteCount ); + + // + // Setup the required zeroing for read requests. + // + + if (UserByteCount != ByteCount) { + + PMDL Mdl; + + ASSERT( ByteCount > UserByteCount ); + + Mdl = IoAllocateMdl( (PUCHAR) Irp->UserBuffer + UserByteCount, + ByteCount - UserByteCount, + FALSE, + FALSE, + NULL ); + + if (Mdl == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoBuildPartialMdl( Irp->MdlAddress, + Mdl, + (PUCHAR) Irp->UserBuffer + UserByteCount, + ByteCount - UserByteCount ); + + IrpContext->FatIoContext->ZeroMdl = Mdl; + + // + // Map the MDL now so we can't fail at IO completion time. Note + // that this will be only a single page. + // + + if (MmGetSystemAddressForMdlSafe( Mdl, NormalPagePriority ) == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + } + +#if 0 // The corruption was happening on the SCSI bus. (1/11/93) + + // + // If we are writing a directory, add a spot check here that + // what we are writing is really a directory. + // + + if ( !FlagOn(FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + (NodeType(FcbOrDcb) != FAT_NTC_FCB) && + (IrpContext->MajorFunction == IRP_MJ_WRITE) ) { + + PDIRENT Dirent; + + Dirent = FatMapUserBuffer( IrpContext, Irp ); + + // + // For the first page of a non-root directory, make sure that + // . and .. are present. + // + + if ( (StartingVbo == 0) && + (NodeType(FcbOrDcb) != FAT_NTC_ROOT_DCB) ) { + + if ( (!RtlEqualMemory( (PUCHAR)Dirent++, + ". ", + 11 )) || + (!RtlEqualMemory( (PUCHAR)Dirent, + ".. ", + 11 )) ) { + + FatBugCheck( 0, 0, 0 ); + } + + } else { + + // + // Check that all the reserved bit in the second dirent are + // zero. (The first one contains our dirty bit in the root dir) + // + + PULONG Zeros; + + Dirent++; + + Zeros = (PULONG)&Dirent->Reserved[0]; + + if ( (Dirent->FileName[0] != 0xE5) && + ((*Zeros != 0) || (*(Zeros+1) != 0)) ) { + + FatBugCheck( 0, 0, 0 ); + } + } + } +#endif //0 + + // + // Try to lookup the first run. If there is just a single run, + // we may just be able to pass it on. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + StartingVbo, + &NextLbo, + &NextByteCount, + &NextIsAllocated, + &EndOnMax, + &FirstIndex ); + + // + // We just added the allocation, thus there must be at least + // one entry in the mcb corresponding to our write, ie. + // NextIsAllocated must be true. If not, the pre-existing file + // must have an allocation error. + // + + if ( !NextIsAllocated ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + ASSERT( NextByteCount != 0 ); + + // + // If the request was not aligned correctly, read in the first + // part first. + // + + + // + // See if the write covers a single valid run, and if so pass + // it on. We must bias this by the byte that is lost at the + // end of the maximal file. + // + + if ( NextByteCount >= ByteCount - (EndOnMax ? 1 : 0)) { + + if (FlagOn(Irp->Flags, IRP_PAGING_IO)) { + CollectDiskIoStats(FcbOrDcb->Vcb, IrpContext->MajorFunction, + FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO), 1); + } else { + + PFILE_SYSTEM_STATISTICS Stats = + &FcbOrDcb->Vcb->Statistics[KeGetCurrentProcessorNumber()]; + + if (IrpContext->MajorFunction == IRP_MJ_READ) { + Stats->Fat.NonCachedDiskReads += 1; + } else { + Stats->Fat.NonCachedDiskWrites += 1; + } + } + + DebugTrace( 0, Dbg, "Passing 1 Irp on to Disk Driver\n", 0 ); + + FatSingleAsync( IrpContext, + FcbOrDcb->Vcb, + NextLbo, + ByteCount, + Irp ); + + } else { + + // + // If there we can't wait, and there are more runs than we can handle, + // we will have to post this request. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + StartingVbo + ByteCount - 1, + &LastLbo, + &LastByteCount, + &LastIsAllocated, + &EndOnMax, + &LastIndex ); + + // + // Since we already added the allocation for the whole + // write, assert that we find runs until ByteCount == 0 + // Otherwise this file is corrupt. + // + + if ( !LastIsAllocated ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + if (LastIndex - FirstIndex + 1 > FAT_MAX_IO_RUNS_ON_STACK) { + + IoRuns = FsRtlAllocatePoolWithTag( PagedPool, + (LastIndex - FirstIndex + 1) * sizeof(IO_RUN), + TAG_IO_RUNS ); + + } else { + + IoRuns = StackIoRuns; + } + + ASSERT( LastIndex != FirstIndex ); + + CurrentIndex = FirstIndex; + + // + // Loop while there are still byte writes to satisfy. + // + + while (CurrentIndex <= LastIndex) { + + + ASSERT( NextByteCount != 0); + ASSERT( ByteCount != 0); + + // + // If next run is larger than we need, "ya get what you need". + // + + if (NextByteCount > ByteCount) { + NextByteCount = ByteCount; + } + + // + // Now that we have properly bounded this piece of the + // transfer, it is time to write it. + // + // We remember each piece of a parallel run by saving the + // essential information in the IoRuns array. The tranfers + // are started up in parallel below. + // + + IoRuns[NextRun].Vbo = StartingVbo; + IoRuns[NextRun].Lbo = NextLbo; + IoRuns[NextRun].Offset = BufferOffset; + IoRuns[NextRun].ByteCount = NextByteCount; + NextRun += 1; + + // + // Now adjust everything for the next pass through the loop. + // + + StartingVbo += NextByteCount; + BufferOffset += NextByteCount; + ByteCount -= NextByteCount; + + // + // Try to lookup the next run (if we are not done). + // + + CurrentIndex += 1; + + if ( CurrentIndex <= LastIndex ) { + + ASSERT( ByteCount != 0 ); + + FatGetNextMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb, + CurrentIndex, + &NextVbo, + &NextLbo, + &NextByteCount ); + + ASSERT( NextVbo == StartingVbo ); + } + + } // while ( CurrentIndex <= LastIndex ) + + // + // Now set up the Irp->IoStatus. It will be modified by the + // multi-completion routine in case of error or verify required. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = OriginalByteCount; + + if (FlagOn(Irp->Flags, IRP_PAGING_IO)) { + CollectDiskIoStats(FcbOrDcb->Vcb, IrpContext->MajorFunction, + FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO), NextRun); + } + + // + // OK, now do the I/O. + // + + _SEH2_TRY { + + DebugTrace( 0, Dbg, "Passing Multiple Irps on to Disk Driver\n", 0 ); + + FatMultipleAsync( IrpContext, + FcbOrDcb->Vcb, + Irp, + NextRun, + IoRuns ); + + } _SEH2_FINALLY { + + if (IoRuns != StackIoRuns) { + + ExFreePool( IoRuns ); + } + } _SEH2_END; + } + + if (!Wait) { + + DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0); + return STATUS_PENDING; + } + + FatWaitSync( IrpContext ); + + DebugTrace(-1, Dbg, "FatNonCachedIo -> 0x%08lx\n", Irp->IoStatus.Status); + return Irp->IoStatus.Status; +} + + +VOID +FatNonCachedNonAlignedRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB FcbOrDcb, + IN ULONG StartingVbo, + IN ULONG ByteCount + ) + +/*++ + +Routine Description: + + This routine performs the non-cached disk io described in its parameters. + This routine differs from the above in that the range does not have to be + sector aligned. This accomplished with the use of intermediate buffers. + +Arguments: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Irp - Supplies the requesting Irp. + + FcbOrDcb - Supplies the file to act on. + + StartingVbo - The starting point for the operation. + + ByteCount - The lengh of the operation. + +Return Value: + + None. + +--*/ + +{ + // + // Declare some local variables for enumeration through the + // runs of the file, and an array to store parameters for + // parallel I/Os + // + + LBO NextLbo; + ULONG NextByteCount; + BOOLEAN NextIsAllocated; + + ULONG SectorSize; + ULONG BytesToCopy; + ULONG OriginalByteCount; + ULONG OriginalStartingVbo; + + BOOLEAN EndOnMax; + + PUCHAR UserBuffer; + PUCHAR DiskBuffer = NULL; + + PMDL Mdl; + PMDL SavedMdl; + PVOID SavedUserBuffer; + + DebugTrace(+1, Dbg, "FatNonCachedNonAlignedRead\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "FcbOrDcb = %08lx\n", FcbOrDcb ); + DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo ); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount ); + + // + // Initialize some locals. + // + + OriginalByteCount = ByteCount; + OriginalStartingVbo = StartingVbo; + SectorSize = FcbOrDcb->Vcb->Bpb.BytesPerSector; + + ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // For nonbuffered I/O, we need the buffer locked in all + // cases. + // + // This call may raise. If this call succeeds and a subsequent + // condition is raised, the buffers are unlocked automatically + // by the I/O system when the request is completed, via the + // Irp->MdlAddress field. + // + + FatLockUserBuffer( IrpContext, + Irp, + IoWriteAccess, + ByteCount ); + + UserBuffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // Allocate the local buffer + // + + DiskBuffer = FsRtlAllocatePoolWithTag( NonPagedPoolCacheAligned, + (ULONG) ROUND_TO_PAGES( SectorSize ), + TAG_IO_BUFFER ); + + // + // We use a try block here to ensure the buffer is freed, and to + // fill in the correct byte count in the Iosb.Information field. + // + + _SEH2_TRY { + + // + // If the beginning of the request was not aligned correctly, read in + // the first part first. + // + + if ( StartingVbo & (SectorSize - 1) ) { + + VBO Hole; + + // + // Try to lookup the first run. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + StartingVbo, + &NextLbo, + &NextByteCount, + &NextIsAllocated, + &EndOnMax, + NULL ); + + // + // We just added the allocation, thus there must be at least + // one entry in the mcb corresponding to our write, ie. + // NextIsAllocated must be true. If not, the pre-existing file + // must have an allocation error. + // + + if ( !NextIsAllocated ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + FatSingleNonAlignedSync( IrpContext, + FcbOrDcb->Vcb, + DiskBuffer, + NextLbo & ~((LONG)SectorSize - 1), + SectorSize, + Irp ); + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + try_return( NOTHING ); + } + + // + // Now copy the part of the first sector that we want to the user + // buffer. + // + + Hole = StartingVbo & (SectorSize - 1); + + BytesToCopy = ByteCount >= SectorSize - Hole ? + SectorSize - Hole : ByteCount; + + RtlCopyMemory( UserBuffer, DiskBuffer + Hole, BytesToCopy ); + + StartingVbo += BytesToCopy; + ByteCount -= BytesToCopy; + + if ( ByteCount == 0 ) { + + try_return( NOTHING ); + } + } + + ASSERT( (StartingVbo & (SectorSize - 1)) == 0 ); + + // + // If there is a tail part that is not sector aligned, read it. + // + + if ( ByteCount & (SectorSize - 1) ) { + + VBO LastSectorVbo; + + LastSectorVbo = StartingVbo + (ByteCount & ~(SectorSize - 1)); + + // + // Try to lookup the last part of the requested range. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + LastSectorVbo, + &NextLbo, + &NextByteCount, + &NextIsAllocated, + &EndOnMax, + NULL ); + + // + // We just added the allocation, thus there must be at least + // one entry in the mcb corresponding to our write, ie. + // NextIsAllocated must be true. If not, the pre-existing file + // must have an allocation error. + // + + if ( !NextIsAllocated ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + FatSingleNonAlignedSync( IrpContext, + FcbOrDcb->Vcb, + DiskBuffer, + NextLbo, + SectorSize, + Irp ); + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + try_return( NOTHING ); + } + + // + // Now copy over the part of this last sector that we need. + // + + BytesToCopy = ByteCount & (SectorSize - 1); + + UserBuffer += LastSectorVbo - OriginalStartingVbo; + + RtlCopyMemory( UserBuffer, DiskBuffer, BytesToCopy ); + + ByteCount -= BytesToCopy; + + if ( ByteCount == 0 ) { + + try_return( NOTHING ); + } + } + + ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 ); + + // + // Now build a Mdl describing the sector aligned balance of the transfer, + // and put it in the Irp, and read that part. + // + + SavedMdl = Irp->MdlAddress; + Irp->MdlAddress = NULL; + + SavedUserBuffer = Irp->UserBuffer; + + Irp->UserBuffer = (PUCHAR)MmGetMdlVirtualAddress( SavedMdl ) + + (StartingVbo - OriginalStartingVbo); + + Mdl = IoAllocateMdl( Irp->UserBuffer, + ByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + Irp->MdlAddress = SavedMdl; + Irp->UserBuffer = SavedUserBuffer; + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoBuildPartialMdl( SavedMdl, + Mdl, + Irp->UserBuffer, + ByteCount ); + + // + // Try to read in the pages. + // + + _SEH2_TRY { + + FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + ByteCount, + ByteCount ); + + } _SEH2_FINALLY { + + IoFreeMdl( Irp->MdlAddress ); + + Irp->MdlAddress = SavedMdl; + Irp->UserBuffer = SavedUserBuffer; + } _SEH2_END; + + try_exit: NOTHING; + + } _SEH2_FINALLY { + + ExFreePool( DiskBuffer ); + + if ( !_SEH2_AbnormalTermination() && NT_SUCCESS(Irp->IoStatus.Status) ) { + + Irp->IoStatus.Information = OriginalByteCount; + + // + // We now flush the user's buffer to memory. + // + + KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE ); + } + } _SEH2_END; + + DebugTrace(-1, Dbg, "FatNonCachedNonAlignedRead -> VOID\n", 0); + return; +} + + +VOID +FatMultipleAsync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PIRP MasterIrp, + IN ULONG MultipleIrpCount, + IN PIO_RUN IoRuns + ) + +/*++ + +Routine Description: + + This routine first does the initial setup required of a Master IRP that is + going to be completed using associated IRPs. This routine should not + be used if only one async request is needed, instead the single read/write + async routines should be called. + + A context parameter is initialized, to serve as a communications area + between here and the common completion routine. This initialization + includes allocation of a spinlock. The spinlock is deallocated in the + FatWaitSync routine, so it is essential that the caller insure that + this routine is always called under all circumstances following a call + to this routine. + + Next this routine reads or writes one or more contiguous sectors from + a device asynchronously, and is used if there are multiple reads for a + master IRP. A completion routine is used to synchronize with the + completion of all of the I/O requests started by calls to this routine. + + Also, prior to calling this routine the caller must initialize the + IoStatus field in the Context, with the correct success status and byte + count which are expected if all of the parallel transfers complete + successfully. After return this status will be unchanged if all requests + were, in fact, successful. However, if one or more errors occur, the + IoStatus will be modified to reflect the error status and byte count + from the first run (by Vbo) which encountered an error. I/O status + from all subsequent runs will not be indicated. + +Arguments: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Vcb - Supplies the device to be read + + MasterIrp - Supplies the master Irp. + + MulitpleIrpCount - Supplies the number of multiple async requests + that will be issued against the master irp. + + IoRuns - Supplies an array containing the Vbo, Lbo, BufferOffset, and + ByteCount for all the runs to executed in parallel. + +Return Value: + + None. + +--*/ + +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PMDL Mdl; + BOOLEAN Wait; + PFAT_IO_CONTEXT Context; + + ULONG UnwindRunCount = 0; + + BOOLEAN ExceptionExpected = TRUE; + + BOOLEAN CalledByFatVerifyVolume = FALSE; + + DebugTrace(+1, Dbg, "FatMultipleAsync\n", 0); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "Vcb = %08lx\n", Vcb ); + DebugTrace( 0, Dbg, "MasterIrp = %08lx\n", MasterIrp ); + DebugTrace( 0, Dbg, "MultipleIrpCount = %08lx\n", MultipleIrpCount ); + DebugTrace( 0, Dbg, "IoRuns = %08lx\n", IoRuns ); + + // + // If this I/O originating during FatVerifyVolume, bypass the + // verify logic. + // + + if ( Vcb->VerifyThread == KeGetCurrentThread() ) { + + CalledByFatVerifyVolume = TRUE; + } + + // + // Set up things according to whether this is truely async. + // + + Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + Context = IrpContext->FatIoContext; + + // + // Finish initializing Context, for use in Read/Write Multiple Asynch. + // + + Context->MasterIrp = MasterIrp; + + _SEH2_TRY { + + // + // Itterate through the runs, doing everything that can fail + // + + for ( UnwindRunCount = 0; + UnwindRunCount < MultipleIrpCount; + UnwindRunCount++ ) { + + // + // Create an associated IRP, making sure there is one stack entry for + // us, as well. + // + + IoRuns[UnwindRunCount].SavedIrp = 0; + + Irp = IoMakeAssociatedIrp( MasterIrp, + (CCHAR)(Vcb->TargetDeviceObject->StackSize + 1) ); + + if (Irp == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + IoRuns[UnwindRunCount].SavedIrp = Irp; + + // + // Allocate and build a partial Mdl for the request. + // + + Mdl = IoAllocateMdl( (PCHAR)MasterIrp->UserBuffer + + IoRuns[UnwindRunCount].Offset, + IoRuns[UnwindRunCount].ByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Sanity Check + // + + ASSERT( Mdl == Irp->MdlAddress ); + + IoBuildPartialMdl( MasterIrp->MdlAddress, + Mdl, + (PCHAR)MasterIrp->UserBuffer + + IoRuns[UnwindRunCount].Offset, + IoRuns[UnwindRunCount].ByteCount ); + + // + // Get the first IRP stack location in the associated Irp + // + + IoSetNextIrpStackLocation( Irp ); + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Setup the Stack location to describe our read. + // + + IrpSp->MajorFunction = IrpContext->MajorFunction; + IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].ByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].Vbo; + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + Wait ? + &FatMultiSyncCompletionRoutine : + &FatMultiAsyncCompletionRoutine, + Context, + TRUE, + TRUE, + TRUE ); + + // + // Setup the next IRP stack location in the associated Irp for the disk + // driver beneath us. + // + + IrpSp = IoGetNextIrpStackLocation( Irp ); + + // + // Setup the Stack location to do a read from the disk driver. + // + + IrpSp->MajorFunction = IrpContext->MajorFunction; + IrpSp->Parameters.Read.Length = IoRuns[UnwindRunCount].ByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = IoRuns[UnwindRunCount].Lbo; + + // + // If this Irp is the result of a WriteThough operation, + // tell the device to write it through. + // + + if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) { + + SetFlag( IrpSp->Flags, SL_WRITE_THROUGH ); + } + + // + // If this I/O originating during FatVerifyVolume, bypass the + // verify logic. + // + + if ( CalledByFatVerifyVolume ) { + + SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + } + } + + // + // Now we no longer expect an exception. If the driver raises, we + // must bugcheck, because we do not know how to recover from that + // case. + // + + ExceptionExpected = FALSE; + + // + // We only need to set the associated IRP count in the master irp to + // make it a master IRP. But we set the count to one more than our + // caller requested, because we do not want the I/O system to complete + // the I/O. We also set our own count. + // + + Context->IrpCount = MultipleIrpCount; + MasterIrp->AssociatedIrp.IrpCount = MultipleIrpCount; + + if (Wait) { + + MasterIrp->AssociatedIrp.IrpCount += 1; + } + + // + // Now that all the dangerous work is done, issue the read requests + // + + for (UnwindRunCount = 0; + UnwindRunCount < MultipleIrpCount; + UnwindRunCount++) { + + Irp = IoRuns[UnwindRunCount].SavedIrp; + + DebugDoit( FatIoCallDriverCount += 1); + + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be caught by our completion routines + // and dealt with as a normal IO error. + // + + (VOID)FatLowLevelReadWrite( IrpContext, + Vcb->TargetDeviceObject, + Irp, + Vcb ); + } + + } _SEH2_FINALLY { + + ULONG i; + + DebugUnwind( FatMultipleAsync ); + + // + // Only allocating the spinlock, making the associated Irps + // and allocating the Mdls can fail. + // + + if ( _SEH2_AbnormalTermination() ) { + + // + // If the driver raised, we are hosed. He is not supposed to raise, + // and it is impossible for us to figure out how to clean up. + // + + if (!ExceptionExpected) { + ASSERT( ExceptionExpected ); + FatBugCheck( 0, 0, 0 ); + } + + // + // Unwind + // + + for (i = 0; i <= UnwindRunCount; i++) { + + if ( (Irp = IoRuns[i].SavedIrp) != NULL ) { + + if ( Irp->MdlAddress != NULL ) { + + IoFreeMdl( Irp->MdlAddress ); + } + + IoFreeIrp( Irp ); + } + } + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatMultipleAsync -> VOID\n", 0); + } _SEH2_END; + + return; +} + + +VOID +FatSingleAsync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN LBO Lbo, + IN ULONG ByteCount, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine reads or writes one or more contiguous sectors from a device + asynchronously, and is used if there is only one read necessary to + complete the IRP. It implements the read by simply filling + in the next stack frame in the Irp, and passing it on. The transfer + occurs to the single buffer originally specified in the user request. + +Arguments: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Vcb - Supplies the device to read + + Lbo - Supplies the starting Logical Byte Offset to begin reading from + + ByteCount - Supplies the number of bytes to read from the device + + Irp - Supplies the master Irp to associated with the async + request. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + DebugTrace(+1, Dbg, "FatSingleAsync\n", 0); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "Vcb = %08lx\n", Vcb ); + DebugTrace( 0, Dbg, "Lbo = %08lx\n", Lbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ? + &FatSingleSyncCompletionRoutine : + &FatSingleAsyncCompletionRoutine, + IrpContext->FatIoContext, + TRUE, + TRUE, + TRUE ); + + // + // Setup the next IRP stack location in the associated Irp for the disk + // driver beneath us. + // + + IrpSp = IoGetNextIrpStackLocation( Irp ); + + // + // Setup the Stack location to do a read from the disk driver. + // + + IrpSp->MajorFunction = IrpContext->MajorFunction; + IrpSp->Parameters.Read.Length = ByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = Lbo; + + // + // If this Irp is the result of a WriteThough operation, + // tell the device to write it through. + // + + if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) { + + SetFlag( IrpSp->Flags, SL_WRITE_THROUGH ); + } + + // + // If this I/O originating during FatVerifyVolume, bypass the + // verify logic. + // + + if ( Vcb->VerifyThread == KeGetCurrentThread() ) { + + SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + } + + // + // Issue the read request + // + + DebugDoit( FatIoCallDriverCount += 1); + + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be caught by our completion routines + // and dealt with as a normal IO error. + // + + (VOID)FatLowLevelReadWrite( IrpContext, + Vcb->TargetDeviceObject, + Irp, + Vcb ); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatSingleAsync -> VOID\n", 0); + + return; +} + + +VOID +FatSingleNonAlignedSync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PUCHAR Buffer, + IN LBO Lbo, + IN ULONG ByteCount, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine reads or writes one or more contiguous sectors from a device + Synchronously, and does so to a buffer that must come from non paged + pool. It saves a pointer to the Irp's original Mdl, and creates a new + one describing the given buffer. It implements the read by simply filling + in the next stack frame in the Irp, and passing it on. The transfer + occurs to the single buffer originally specified in the user request. + +Arguments: + + IrpContext->MajorFunction - Supplies either IRP_MJ_READ or IRP_MJ_WRITE. + + Vcb - Supplies the device to read + + Buffer - Supplies a buffer from non-paged pool. + + Lbo - Supplies the starting Logical Byte Offset to begin reading from + + ByteCount - Supplies the number of bytes to read from the device + + Irp - Supplies the master Irp to associated with the async + request. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + PMDL Mdl; + PMDL SavedMdl; + + DebugTrace(+1, Dbg, "FatSingleNonAlignedAsync\n", 0); + DebugTrace( 0, Dbg, "MajorFunction = %08lx\n", IrpContext->MajorFunction ); + DebugTrace( 0, Dbg, "Vcb = %08lx\n", Vcb ); + DebugTrace( 0, Dbg, "Buffer = %08lx\n", Buffer ); + DebugTrace( 0, Dbg, "Lbo = %08lx\n", Lbo); + DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + + // + // Create a new Mdl describing the buffer, saving the current one in the + // Irp + // + + SavedMdl = Irp->MdlAddress; + + Irp->MdlAddress = 0; + + Mdl = IoAllocateMdl( Buffer, + ByteCount, + FALSE, + FALSE, + Irp ); + + if (Mdl == NULL) { + + Irp->MdlAddress = SavedMdl; + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Lock the new Mdl in memory. + // + + _SEH2_TRY { + + MmProbeAndLockPages( Mdl, KernelMode, IoWriteAccess ); + + } _SEH2_FINALLY { + + if ( _SEH2_AbnormalTermination() ) { + + IoFreeMdl( Mdl ); + Irp->MdlAddress = SavedMdl; + } + } _SEH2_END; + + // + // Set up the completion routine address in our stack frame. + // + + IoSetCompletionRoutine( Irp, + &FatSingleSyncCompletionRoutine, + IrpContext->FatIoContext, + TRUE, + TRUE, + TRUE ); + + // + // Setup the next IRP stack location in the associated Irp for the disk + // driver beneath us. + // + + IrpSp = IoGetNextIrpStackLocation( Irp ); + + // + // Setup the Stack location to do a read from the disk driver. + // + + IrpSp->MajorFunction = IrpContext->MajorFunction; + IrpSp->Parameters.Read.Length = ByteCount; + IrpSp->Parameters.Read.ByteOffset.QuadPart = Lbo; + + // + // If this I/O originating during FatVerifyVolume, bypass the + // verify logic. + // + + if ( Vcb->VerifyThread == KeGetCurrentThread() ) { + + SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + } + + // + // Issue the read request + // + + DebugDoit( FatIoCallDriverCount += 1); + + // + // If IoCallDriver returns an error, it has completed the Irp + // and the error will be caught by our completion routines + // and dealt with as a normal IO error. + // + + _SEH2_TRY { + + (VOID)FatLowLevelReadWrite( IrpContext, + Vcb->TargetDeviceObject, + Irp, + Vcb ); + + FatWaitSync( IrpContext ); + + } _SEH2_FINALLY { + + MmUnlockPages( Mdl ); + IoFreeMdl( Mdl ); + Irp->MdlAddress = SavedMdl; + } _SEH2_END; + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatSingleNonAlignedSync -> VOID\n", 0); + + return; +} + + +VOID +FatWaitSync ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine waits for one or more previously started I/O requests + from the above routines, by simply waiting on the event. + +Arguments: + +Return Value: + + None + +--*/ + +{ + DebugTrace(+1, Dbg, "FatWaitSync, Context = %08lx\n", IrpContext->FatIoContext ); + + KeWaitForSingleObject( &IrpContext->FatIoContext->Wait.SyncEvent, + Executive, KernelMode, FALSE, NULL ); + + KeClearEvent( &IrpContext->FatIoContext->Wait.SyncEvent ); + + DebugTrace(-1, Dbg, "FatWaitSync -> VOID\n", 0 ); +} + + +// +// Internal Support Routine +// + +NTSTATUS +NTAPI +FatMultiSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatRead/WriteMultipleAsynch. It must synchronize its operation for + multiprocessor environments with itself on all other processors, via + a spin lock found via the Context parameter. + + The completion routine has the following responsibilities: + + If the individual request was completed with an error, then + this completion routine must see if this is the first error + (essentially by Vbo), and if so it must correctly reduce the + byte count and remember the error status in the Context. + + If the IrpCount goes to 1, then it sets the event in the Context + parameter to signal the caller that all of the asynch requests + are done. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the associated Irp which is being completed. (This + Irp will no longer be accessible after this routine returns.) + + Contxt - The context parameter which was specified for all of + the multiple asynch I/O requests for this MasterIrp. + +Return Value: + + The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can + immediately complete the Master Irp without being in a race condition + with the IoCompleteRequest thread trying to decrement the IrpCount in + the Master Irp. + +--*/ + +{ + + PFAT_IO_CONTEXT Context = Contxt; + PIRP MasterIrp = Context->MasterIrp; + + DebugTrace(+1, Dbg, "FatMultiSyncCompletionRoutine, Context = %08lx\n", Context ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + MasterIrp = Context->MasterIrp; + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + ASSERT( NT_SUCCESS( FatAssertNotStatus ) || Irp->IoStatus.Status != FatAssertNotStatus ); + +#ifdef SYSCACHE_COMPILE + DbgPrint( "FAT SYSCACHE: MultiSync (IRP %08x for Master %08x) -> %08x\n", Irp, MasterIrp, Irp->IoStatus ); +#endif + + MasterIrp->IoStatus = Irp->IoStatus; + } + + ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 )); + + // + // We must do this here since IoCompleteRequest won't get a chance + // on this associated Irp. + // + + IoFreeMdl( Irp->MdlAddress ); + IoFreeIrp( Irp ); + + if (InterlockedDecrement(&Context->IrpCount) == 0) { + + FatDoCompletionZero( MasterIrp, Context ); + KeSetEvent( &Context->Wait.SyncEvent, 0, FALSE ); + } + + DebugTrace(-1, Dbg, "FatMultiSyncCompletionRoutine -> SUCCESS\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +// +// Internal Support Routine +// + +NTSTATUS +NTAPI +FatMultiAsyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatRead/WriteMultipleAsynch. It must synchronize its operation for + multiprocessor environments with itself on all other processors, via + a spin lock found via the Context parameter. + + The completion routine has has the following responsibilities: + + If the individual request was completed with an error, then + this completion routine must see if this is the first error + (essentially by Vbo), and if so it must correctly reduce the + byte count and remember the error status in the Context. + + If the IrpCount goes to 1, then it sets the event in the Context + parameter to signal the caller that all of the asynch requests + are done. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the associated Irp which is being completed. (This + Irp will no longer be accessible after this routine returns.) + + Contxt - The context parameter which was specified for all of + the multiple asynch I/O requests for this MasterIrp. + +Return Value: + + The routine returns STATUS_MORE_PROCESSING_REQUIRED so that we can + immediately complete the Master Irp without being in a race condition + with the IoCompleteRequest thread trying to decrement the IrpCount in + the Master Irp. + +--*/ + +{ + + PFAT_IO_CONTEXT Context = Contxt; + PIRP MasterIrp = Context->MasterIrp; + + DebugTrace(+1, Dbg, "FatMultiAsyncCompletionRoutine, Context = %08lx\n", Context ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + MasterIrp = Context->MasterIrp; + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + ASSERT( NT_SUCCESS( FatAssertNotStatus ) || Irp->IoStatus.Status != FatAssertNotStatus ); + +#ifdef SYSCACHE_COMPILE + DbgPrint( "FAT SYSCACHE: MultiAsync (IRP %08x for Master %08x) -> %08x\n", Irp, MasterIrp, Irp->IoStatus ); +#endif + + MasterIrp->IoStatus = Irp->IoStatus; + + } + + ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 )); + + if (InterlockedDecrement(&Context->IrpCount) == 0) { + + FatDoCompletionZero( MasterIrp, Context ); + + if (NT_SUCCESS(MasterIrp->IoStatus.Status)) { + + MasterIrp->IoStatus.Information = + Context->Wait.Async.RequestedByteCount; + + ASSERT(MasterIrp->IoStatus.Information != 0); + + // + // Now if this wasn't PagingIo, set either the read or write bit. + // + + if (!FlagOn(MasterIrp->Flags, IRP_PAGING_IO)) { + + SetFlag( Context->Wait.Async.FileObject->Flags, + IoGetCurrentIrpStackLocation(MasterIrp)->MajorFunction == IRP_MJ_READ ? + FO_FILE_FAST_IO_READ : FO_FILE_MODIFIED ); + } + } + + // + // If this was a special async write, decrement the count. Set the + // event if this was the final outstanding I/O for the file. We will + // also want to queue an APC to deal with any error conditionions. + // + + if ((Context->Wait.Async.NonPagedFcb) && + (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites, + 0xffffffff, + &FatData.GeneralSpinLock ) == 1)) { + + KeSetEvent( Context->Wait.Async.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE ); + } + + // + // Now release the resources. + // + + if (Context->Wait.Async.Resource != NULL) { + + ExReleaseResourceForThreadLite( Context->Wait.Async.Resource, + Context->Wait.Async.ResourceThreadId ); + } + + if (Context->Wait.Async.Resource2 != NULL) { + + ExReleaseResourceForThreadLite( Context->Wait.Async.Resource2, + Context->Wait.Async.ResourceThreadId ); + } + + // + // Mark the master Irp pending + // + + IoMarkIrpPending( MasterIrp ); + + // + // and finally, free the context record. + // + + ExFreePool( Context ); + } + + DebugTrace(-1, Dbg, "FatMultiAsyncCompletionRoutine -> SUCCESS\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_SUCCESS; +} + + +NTSTATUS +FatPagingFileErrorHandler ( + IN PIRP Irp, + IN PKEVENT Event OPTIONAL + ) + +/*++ + +Routine Description: + + This routine attempts to guarantee that the media is marked dirty + with the surface test bit if a paging file IO fails. + + The work done here has several basic problems + + 1) when paging file writes start failing, this is a good sign + that the rest of the system is about to fall down around us + + 2) it has no forward progress guarantee + + With Whistler, it is actually quite intentional that we're rejiggering + the paging file write path to make forward progress at all times. This + means that the cases where it *does* fail, we're truly seeing media errors + and this is probably going to mean the paging file is going to stop working + very soon. + + It'd be nice to make this guarantee progress. It would need + + 1) a guaranteed worker thread which can only be used by items which + will make forward progress (i.e., not block out this one) + + 2) the virtual volume file's pages containing the boot sector and + 1st FAT entry would have to be pinned resident and have a guaranteed + mapping address + + 3) mark volume would have to have a stashed irp/mdl and roll the write + irp, or use a generalized mechanism to guarantee issue of the irp + + 4) the lower stack would have to guarantee progress + + Of these, 1 and 4 may actually exist shortly. + +Arguments: + + Irp - Pointer to the associated Irp which is being failed. + + Event - Pointer to optional event to be signalled instead of completing + the IRP + +Return Value: + + Returns STATUS_MORE_PROCESSING_REQUIRED if we managed to queue off the workitem, + STATUS_SUCCESS otherwise. + +--*/ + +{ + NTSTATUS Status; + + // + // If this was a media error, we want to chkdsk /r the next time we boot. + // + + if (FsRtlIsTotalDeviceFailure(Irp->IoStatus.Status)) { + + Status = STATUS_SUCCESS; + + } else { + + PCLEAN_AND_DIRTY_VOLUME_PACKET Packet; + + // + // We are going to try to mark the volume needing recover. + // If we can't get pool, oh well.... + // + + Packet = ExAllocatePool(NonPagedPool, sizeof(CLEAN_AND_DIRTY_VOLUME_PACKET)); + + if ( Packet ) { + + Packet->Vcb = &((PVOLUME_DEVICE_OBJECT)IoGetCurrentIrpStackLocation(Irp)->DeviceObject)->Vcb; + Packet->Irp = Irp; + Packet->Event = Event; + + ExInitializeWorkItem( &Packet->Item, + &FatFspMarkVolumeDirtyWithRecover, + Packet ); + + ExQueueWorkItem( &Packet->Item, CriticalWorkQueue ); + + Status = STATUS_MORE_PROCESSING_REQUIRED; + + } else { + + Status = STATUS_SUCCESS; + } + } + + return Status; +} + + +// +// Internal Support Routine +// + +NTSTATUS +NTAPI +FatPagingFileCompletionRoutineCatch ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatPagingFileIo that reuse the master irp (that we have to catch + on the way back). It is always invoked. + + The completion routine has has the following responsibility: + + If the error implies a media problem, it enqueues a + worker item to write out the dirty bit so that the next + time we run we will do a autochk /r. This is not forward + progress guaranteed at the moment. + + Clean up the Mdl used for this partial request. + + Note that if the Irp is failing, the error code is already where + we want it. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the associated Irp which is being completed. (This + Irp will no longer be accessible after this routine returns.) + + MasterIrp - Pointer to the master Irp. + +Return Value: + + Always returns STATUS_MORE_PROCESSING_REQUIRED. + +--*/ + +{ + PFAT_PAGING_FILE_CONTEXT Context = (PFAT_PAGING_FILE_CONTEXT) Contxt; + + DebugTrace(+1, Dbg, "FatPagingFileCompletionRoutineCatch, Context = %08lx\n", Context ); + + // + // Cleanup the existing Mdl, perhaps by returning the reserve. + // + + if (Irp->MdlAddress == FatReserveMdl) { + + MmPrepareMdlForReuse( Irp->MdlAddress ); + KeSetEvent( &FatReserveEvent, 0, FALSE ); + + } else { + + IoFreeMdl( Irp->MdlAddress ); + } + + // + // Restore the original Mdl. + // + + Irp->MdlAddress = Context->RestoreMdl; + + DebugTrace(-1, Dbg, "FatPagingFileCompletionRoutine => (done)\n", 0 ); + + // + // If the IRP is succeeding or the failure handler did not post off the + // completion, we're done and should set the event to let the master + // know the IRP is his again. + // + + if (NT_SUCCESS( Irp->IoStatus.Status ) || + FatPagingFileErrorHandler( Irp, &Context->Event ) == STATUS_SUCCESS) { + + KeSetEvent( &Context->Event, 0, FALSE ); + } + + return STATUS_MORE_PROCESSING_REQUIRED; + +} + + +// +// Internal Support Routine +// + +NTSTATUS +NTAPI +FatPagingFileCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID MasterIrp + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatPagingFileIo. It should only be invoked on error or cancel. + + The completion routine has has the following responsibility: + + Since the individual request was completed with an error, + this completion routine must stuff it into the master irp. + + If the error implies a media problem, it also enqueues a + worker item to write out the dirty bit so that the next + time we run we will do a autochk /r + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the associated Irp which is being completed. (This + Irp will no longer be accessible after this routine returns.) + + MasterIrp - Pointer to the master Irp. + +Return Value: + + Always returns STATUS_SUCCESS. + +--*/ + +{ +#ifndef __REACTOS__ + NTSTATUS Status; +#endif + + DebugTrace(+1, Dbg, "FatPagingFileCompletionRoutine, MasterIrp = %08lx\n", MasterIrp ); + + // + // If we got an error (or verify required), remember it in the Irp + // + + ASSERT( !NT_SUCCESS( Irp->IoStatus.Status )); + + // + // If we were invoked with an assoicated Irp, copy the error over. + // + + if (Irp != MasterIrp) { + + ((PIRP)MasterIrp)->IoStatus = Irp->IoStatus; + } + + DebugTrace(-1, Dbg, "FatPagingFileCompletionRoutine => (done)\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return FatPagingFileErrorHandler( Irp, NULL ); +} + + +// +// Internal Support Routine +// + +NTSTATUS +NTAPI +FatSpecialSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for a special set of sub irps + that have to work at APC level. + + The completion routine has has the following responsibilities: + + It sets the event passed as the context to signal that the + request is done. + + By doing this, the caller will be released before final APC + completion with knowledge that the IRP is finished. Final + completion will occur at an indeterminate time after this + occurs, and by using this completion routine the caller expects + to not have any output or status returned. A junk user Iosb + should be used to capture the status without forcing Io to take + an exception on NULL. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the Irp for this request. (This Irp will no longer + be accessible after this routine returns.) + + Contxt - The context parameter which was specified in the call to + FatRead/WriteSingleAsynch. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + PKEVENT Event = (PKEVENT)Contxt; + + DebugTrace(+1, Dbg, "FatSpecialSyncCompletionRoutine, Context = %08lx\n", Contxt ); + + KeSetEvent( Event, 0, FALSE ); + + DebugTrace(-1, Dbg, "FatSpecialSyncCompletionRoutine -> STATUS_SUCCESS\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_SUCCESS; +} + + +// +// Internal Support Routine +// + +NTSTATUS +NTAPI +FatSingleSyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatRead/WriteSingleAsynch. + + The completion routine has has the following responsibilities: + + Copy the I/O status from the Irp to the Context, since the Irp + will no longer be accessible. + + It sets the event in the Context parameter to signal the caller + that all of the asynch requests are done. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the Irp for this request. (This Irp will no longer + be accessible after this routine returns.) + + Contxt - The context parameter which was specified in the call to + FatRead/WriteSingleAsynch. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + PFAT_IO_CONTEXT Context = Contxt; + + DebugTrace(+1, Dbg, "FatSingleSyncCompletionRoutine, Context = %08lx\n", Context ); + + FatDoCompletionZero( Irp, Context ); + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + ASSERT( NT_SUCCESS( FatAssertNotStatus ) || Irp->IoStatus.Status != FatAssertNotStatus ); + } + + ASSERT( !(NT_SUCCESS( Irp->IoStatus.Status ) && Irp->IoStatus.Information == 0 )); + + KeSetEvent( &Context->Wait.SyncEvent, 0, FALSE ); + + DebugTrace(-1, Dbg, "FatSingleSyncCompletionRoutine -> STATUS_MORE_PROCESSING_REQUIRED\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + +// +// Internal Support Routine +// + +NTSTATUS +NTAPI +FatSingleAsyncCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +/*++ + +Routine Description: + + This is the completion routine for all reads and writes started via + FatRead/WriteSingleAsynch. + + The completion routine has has the following responsibilities: + + Copy the I/O status from the Irp to the Context, since the Irp + will no longer be accessible. + + It sets the event in the Context parameter to signal the caller + that all of the asynch requests are done. + +Arguments: + + DeviceObject - Pointer to the file system device object. + + Irp - Pointer to the Irp for this request. (This Irp will no longer + be accessible after this routine returns.) + + Contxt - The context parameter which was specified in the call to + FatRead/WriteSingleAsynch. + +Return Value: + + Currently always returns STATUS_SUCCESS. + +--*/ + +{ + PFAT_IO_CONTEXT Context = Contxt; + + DebugTrace(+1, Dbg, "FatSingleAsyncCompletionRoutine, Context = %08lx\n", Context ); + + // + // Fill in the information field correctedly if this worked. + // + + FatDoCompletionZero( Irp, Context ); + + if (NT_SUCCESS(Irp->IoStatus.Status)) { + + ASSERT( Irp->IoStatus.Information != 0 ); + Irp->IoStatus.Information = Context->Wait.Async.RequestedByteCount; + ASSERT( Irp->IoStatus.Information != 0 ); + + // + // Now if this wasn't PagingIo, set either the read or write bit. + // + + if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { + + SetFlag( Context->Wait.Async.FileObject->Flags, + IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_READ ? + FO_FILE_FAST_IO_READ : FO_FILE_MODIFIED ); + } + + } else { + + ASSERT( NT_SUCCESS( FatAssertNotStatus ) || Irp->IoStatus.Status != FatAssertNotStatus ); + +#ifdef SYSCACHE_COMPILE + DbgPrint( "FAT SYSCACHE: SingleAsync (IRP %08x) -> %08x\n", Irp, Irp->IoStatus ); +#endif + + } + + // + // If this was a special async write, decrement the count. Set the + // event if this was the final outstanding I/O for the file. We will + // also want to queue an APC to deal with any error conditionions. + // + + if ((Context->Wait.Async.NonPagedFcb) && + (ExInterlockedAddUlong( &Context->Wait.Async.NonPagedFcb->OutstandingAsyncWrites, + 0xffffffff, + &FatData.GeneralSpinLock ) == 1)) { + + KeSetEvent( Context->Wait.Async.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE ); + } + + // + // Now release the resources + // + + if (Context->Wait.Async.Resource != NULL) { + + ExReleaseResourceForThreadLite( Context->Wait.Async.Resource, + Context->Wait.Async.ResourceThreadId ); + } + + if (Context->Wait.Async.Resource2 != NULL) { + + ExReleaseResourceForThreadLite( Context->Wait.Async.Resource2, + Context->Wait.Async.ResourceThreadId ); + } + + // + // Mark the Irp pending + // + + IoMarkIrpPending( Irp ); + + // + // and finally, free the context record. + // + + ExFreePool( Context ); + + DebugTrace(-1, Dbg, "FatSingleAsyncCompletionRoutine -> STATUS_MORE_PROCESSING_REQUIRED\n", 0 ); + + UNREFERENCED_PARAMETER( DeviceObject ); + + return STATUS_SUCCESS; +} + + +VOID +FatLockUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp, + IN LOCK_OPERATION Operation, + IN ULONG BufferLength + ) + +/*++ + +Routine Description: + + This routine locks the specified buffer for the specified type of + access. The file system requires this routine since it does not + ask the I/O system to lock its buffers for direct I/O. This routine + may only be called from the Fsd while still in the user context. + + Note that this is the *input/output* buffer. + +Arguments: + + Irp - Pointer to the Irp for which the buffer is to be locked. + + Operation - IoWriteAccess for read operations, or IoReadAccess for + write operations. + + BufferLength - Length of user buffer. + +Return Value: + + None + +--*/ + +{ + PMDL Mdl = NULL; + + if (Irp->MdlAddress == NULL) { + + // + // Allocate the Mdl, and Raise if we fail. + // + + Mdl = IoAllocateMdl( Irp->UserBuffer, BufferLength, FALSE, FALSE, Irp ); + + if (Mdl == NULL) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Now probe the buffer described by the Irp. If we get an exception, + // deallocate the Mdl and return the appropriate "expected" status. + // + + _SEH2_TRY { + + MmProbeAndLockPages( Mdl, + Irp->RequestorMode, + Operation ); + + } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + + NTSTATUS Status; + + Status = _SEH2_GetExceptionCode(); + + IoFreeMdl( Mdl ); + Irp->MdlAddress = NULL; + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? Status : STATUS_INVALID_USER_BUFFER ); + } _SEH2_END; + } + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +PVOID +FatMapUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp + ) + +/*++ + +Routine Description: + + This routine conditionally maps the user buffer for the current I/O + request in the specified mode. If the buffer is already mapped, it + just returns its address. + + Note that this is the *input/output* buffer. + +Arguments: + + Irp - Pointer to the Irp for the request. + +Return Value: + + Mapped address + +--*/ + +{ + UNREFERENCED_PARAMETER( IrpContext ); + + // + // If there is no Mdl, then we must be in the Fsd, and we can simply + // return the UserBuffer field from the Irp. + // + + if (Irp->MdlAddress == NULL) { + + return Irp->UserBuffer; + + } else { + + PVOID Address = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, NormalPagePriority ); + + if (Address == NULL) { + + ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); + } + + return Address; + } +} + + +PVOID +FatBufferUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp, + IN ULONG BufferLength + ) + +/*++ + +Routine Description: + + This routine conditionally buffers the user buffer for the current I/O + request. If the buffer is already buffered, it just returns its address. + + Note that this is the *input* buffer. + +Arguments: + + Irp - Pointer to the Irp for the request. + + BufferLength - Length of user buffer. + +Return Value: + + Buffered address. + +--*/ + +{ + PUCHAR UserBuffer; + + UNREFERENCED_PARAMETER( IrpContext ); + + // + // Handle the no buffer case. + // + + if (BufferLength == 0) { + + return NULL; + } + + // + // If there is no system buffer we must have been supplied an Mdl + // describing the users input buffer, which we will now snapshot. + // + + if (Irp->AssociatedIrp.SystemBuffer == NULL) { + + UserBuffer = FatMapUserBuffer( IrpContext, Irp ); + + Irp->AssociatedIrp.SystemBuffer = FsRtlAllocatePoolWithQuotaTag( NonPagedPool, + BufferLength, + TAG_IO_USER_BUFFER ); + + // + // Set the flags so that the completion code knows to deallocate the + // buffer. + // + + Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER); + + _SEH2_TRY { + + RtlCopyMemory( Irp->AssociatedIrp.SystemBuffer, + UserBuffer, + BufferLength ); + + } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { + + NTSTATUS Status; + + Status = _SEH2_GetExceptionCode(); + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? Status : STATUS_INVALID_USER_BUFFER ); + } _SEH2_END; + } + + return Irp->AssociatedIrp.SystemBuffer; +} + + +NTSTATUS +FatToggleMediaEjectDisable ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN PreventRemoval + ) + +/*++ + +Routine Description: + + The routine either enables or disables the eject button on removable + media. + +Arguments: + + Vcb - Descibes the volume to operate on + + PreventRemoval - TRUE if we should disable the media eject button. FALSE + if we want to enable it. + +Return Value: + + Status of the operation. + +--*/ + +{ + PIRP Irp; + KEVENT Event; + KIRQL SavedIrql; + NTSTATUS Status; + IO_STATUS_BLOCK Iosb; + PREVENT_MEDIA_REMOVAL Prevent; + + // + // If PreventRemoval is the same as VCB_STATE_FLAG_REMOVAL_PREVENTED, + // no-op this call, otherwise toggle the state of the flag. + // + + KeAcquireSpinLock( &FatData.GeneralSpinLock, &SavedIrql ); + + if ((PreventRemoval ^ + BooleanFlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVAL_PREVENTED)) == 0) { + + KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql ); + + return STATUS_SUCCESS; + + } else { + + Vcb->VcbState ^= VCB_STATE_FLAG_REMOVAL_PREVENTED; + + KeReleaseSpinLock( &FatData.GeneralSpinLock, SavedIrql ); + } + + Prevent.PreventMediaRemoval = PreventRemoval; + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // We build this IRP using a junk Iosb that will receive the final + // completion status since we won't be around for it. + // + + Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_MEDIA_REMOVAL, + Vcb->TargetDeviceObject, + &Prevent, + sizeof(PREVENT_MEDIA_REMOVAL), + NULL, + 0, + FALSE, + NULL, + &Iosb ); + + if ( Irp != NULL ) { + + // + // Use our special completion routine which will remove the requirement that + // the caller must be below APC level. All it tells us is that the Irp got + // back, but will not tell us if it was succesful or not. We don't care, + // and there is of course no fallback if the attempt to prevent removal + // doesn't work for some mysterious reason. + // + // Normally, all IO is done at passive level. However, MM needs to be able + // to issue IO with fast mutexes locked down, which raises us to APC. The + // overlying IRP is set up to complete in yet another magical fashion even + // though APCs are disabled, and any IRPage we do in these cases has to do + // the same. Marking media dirty (and toggling eject state) is one. + // + + IoSetCompletionRoutine( Irp, + FatSpecialSyncCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + Status = IoCallDriver( Vcb->TargetDeviceObject, Irp ); + + if (Status == STATUS_PENDING) { + + (VOID) KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Iosb.Status; + } + + return Status; + } + + return STATUS_INSUFFICIENT_RESOURCES; +} + + +NTSTATUS +FatPerformDevIoCtrl ( + IN PIRP_CONTEXT IrpContext, + IN ULONG IoControlCode, + IN PDEVICE_OBJECT Device, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN BOOLEAN InternalDeviceIoControl, + IN BOOLEAN OverrideVerify, + OUT PIO_STATUS_BLOCK Iosb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine is called to perform DevIoCtrl functions internally within + the filesystem. We take the status from the driver and return it to our + caller. + +Arguments: + + IoControlCode - Code to send to driver. + + Device - This is the device to send the request to. + + OutPutBuffer - Pointer to output buffer. + + OutputBufferLength - Length of output buffer above. + + InternalDeviceIoControl - Indicates if this is an internal or external + Io control code. + + OverrideVerify - Indicates if we should tell the driver not to return + STATUS_VERIFY_REQUIRED for mount and verify. + + Iosb - If specified, we return the results of the operation here. + +Return Value: + + NTSTATUS - Status returned by next lower driver. + +--*/ + +{ + NTSTATUS Status; + PIRP Irp; + KEVENT Event; + IO_STATUS_BLOCK LocalIosb; + PIO_STATUS_BLOCK IosbToUse = &LocalIosb; + + PAGED_CODE(); + + // + // Check if the user gave us an Iosb. + // + + if (ARGUMENT_PRESENT( Iosb )) { + + IosbToUse = Iosb; + } + + IosbToUse->Status = 0; + IosbToUse->Information = 0; + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + Irp = IoBuildDeviceIoControlRequest( IoControlCode, + Device, + NULL, + 0, + OutputBuffer, + OutputBufferLength, + InternalDeviceIoControl, + &Event, + IosbToUse ); + + if (Irp == NULL) { + + return STATUS_INSUFFICIENT_RESOURCES; + } + + if (OverrideVerify) { + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + } + + Status = IoCallDriver( Device, Irp ); + + // + // We check for device not ready by first checking Status + // and then if status pending was returned, the Iosb status + // value. + // + + if (Status == STATUS_PENDING) { + + (VOID) KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = IosbToUse->Status; + } + + return Status; +} + + diff --git a/drivers/filesystems/fastfat_new/dirctrl.c b/drivers/filesystems/fastfat_new/dirctrl.c new file mode 100644 index 00000000000..9a6ccb9d284 --- /dev/null +++ b/drivers/filesystems/fastfat_new/dirctrl.c @@ -0,0 +1,1526 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DirCtrl.c + +Abstract: + + This module implements the File Directory Control routines for Fat called + by the dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_DIRCTRL) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_DIRCTRL) + +WCHAR Fat8QMdot3QM[12] = { DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, + L'.', DOS_QM, DOS_QM, DOS_QM}; + +// +// Local procedure prototypes +// + +NTSTATUS +FatQueryDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +FatGetDirTimes( + PIRP_CONTEXT IrpContext, + PDIRENT Dirent, + PFILE_DIRECTORY_INFORMATION DirInfo + ); + +NTSTATUS +FatNotifyChangeDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonDirectoryControl) +#pragma alloc_text(PAGE, FatFsdDirectoryControl) +#pragma alloc_text(PAGE, FatNotifyChangeDirectory) +#pragma alloc_text(PAGE, FatQueryDirectory) +#pragma alloc_text(PAGE, FatGetDirTimes) + +#endif + + +NTSTATUS +NTAPI +FatFsdDirectoryControl ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of directory control + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdDirectoryControl\n", 0); + + // + // Call the common directory Control routine, with blocking allowed if + // synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonDirectoryControl( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdDirectoryControl -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonDirectoryControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing directory control operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + // + // Get a pointer to the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonDirectoryControl\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction ); + + // + // We know this is a directory control so we'll case on the + // minor function, and call a internal worker routine to complete + // the irp. + // + + switch ( IrpSp->MinorFunction ) { + + case IRP_MN_QUERY_DIRECTORY: + + Status = FatQueryDirectory( IrpContext, Irp ); + break; + + case IRP_MN_NOTIFY_CHANGE_DIRECTORY: + + Status = FatNotifyChangeDirectory( IrpContext, Irp ); + break; + + default: + + DebugTrace(0, Dbg, "Invalid Directory Control Minor Function %08lx\n", IrpSp->MinorFunction); + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + DebugTrace(-1, Dbg, "FatCommonDirectoryControl -> %08lx\n", Status); + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatQueryDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the query directory operation. It is responsible + for either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PDCB Dcb; + PCCB Ccb; + PBCB Bcb; + + ULONG i; + PUCHAR Buffer; + CLONG UserBufferLength; + + PUNICODE_STRING UniArgFileName; + WCHAR LongFileNameBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE]; + UNICODE_STRING LongFileName; + FILE_INFORMATION_CLASS FileInformationClass; + ULONG FileIndex; + BOOLEAN RestartScan; + BOOLEAN ReturnSingleEntry; + BOOLEAN IndexSpecified; + + BOOLEAN InitialQuery; + VBO CurrentVbo; + BOOLEAN UpdateCcb; + PDIRENT Dirent; + UCHAR Fat8Dot3Buffer[12]; + OEM_STRING Fat8Dot3String; + ULONG DiskAllocSize; + + ULONG NextEntry; + ULONG LastEntry; + + PFILE_DIRECTORY_INFORMATION DirInfo; + PFILE_FULL_DIR_INFORMATION FullDirInfo; + PFILE_BOTH_DIR_INFORMATION BothDirInfo; + PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo; + PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo; + PFILE_NAMES_INFORMATION NamesInfo; + + // + // Get the current Stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Display the input values. + // + DebugTrace(+1, Dbg, "FatQueryDirectory...\n", 0); + DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryDirectory.Length); + DebugTrace( 0, Dbg, " ->FileName = %08lx\n", IrpSp->Parameters.QueryDirectory.FileName); + DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass); + DebugTrace( 0, Dbg, " ->FileIndex = %08lx\n", IrpSp->Parameters.QueryDirectory.FileIndex); + DebugTrace( 0, Dbg, " ->UserBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); + DebugTrace( 0, Dbg, " ->RestartScan = %08lx\n", FlagOn( IrpSp->Flags, SL_RESTART_SCAN )); + DebugTrace( 0, Dbg, " ->ReturnSingleEntry = %08lx\n", FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )); + DebugTrace( 0, Dbg, " ->IndexSpecified = %08lx\n", FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )); + + // + // Reference our input parameters to make things easier + // + + UserBufferLength = IrpSp->Parameters.QueryDirectory.Length; + + FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass; + FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex; + + UniArgFileName = (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName; + + RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); + ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); + IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); + + // + // Check on the type of open. We return invalid parameter for all + // but UserDirectoryOpens. Also check that the filename is a valid + // UNICODE string. + // + + if (FatDecodeFileObject( IrpSp->FileObject, + &Vcb, + &Dcb, + &Ccb) != UserDirectoryOpen || + (UniArgFileName && + UniArgFileName->Length % sizeof(WCHAR))) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0); + + return STATUS_INVALID_PARAMETER; + } + + // + // Initialize the local variables. + // + + Bcb = NULL; + UpdateCcb = TRUE; + Dirent = NULL; + + Fat8Dot3String.MaximumLength = 12; +#ifndef __REACTOS__ + Fat8Dot3String.Buffer = Fat8Dot3Buffer; +#else + Fat8Dot3String.Buffer = (PCHAR)Fat8Dot3Buffer; +#endif + + LongFileName.Length = 0; + LongFileName.MaximumLength = sizeof( LongFileNameBuffer); + LongFileName.Buffer = LongFileNameBuffer; + + InitialQuery = (BOOLEAN)((Ccb->UnicodeQueryTemplate.Buffer == NULL) && + !FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL)); + Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + DiskAllocSize = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // If this is the initial query, then grab exclusive access in + // order to update the search string in the Ccb. We may + // discover that we are not the initial query once we grab the Fcb + // and downgrade our status. + // + + if (InitialQuery) { + + if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) { + + DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0); + Status = FatFsdPostRequest( IrpContext, Irp ); + DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status); + + return Status; + } + + if (Ccb->UnicodeQueryTemplate.Buffer != NULL) { + + InitialQuery = FALSE; + + FatConvertToSharedFcb( IrpContext, Dcb ); + } + + } else { + + if (!FatAcquireSharedFcb( IrpContext, Dcb )) { + + DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0); + Status = FatFsdPostRequest( IrpContext, Irp ); + DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status); + + return Status; + + } + } + + _SEH2_TRY { + + ULONG BaseLength; + ULONG BytesConverted; + + // + // If we are in the Fsp now because we had to wait earlier, + // we must map the user buffer, otherwise we can use the + // user's buffer directly. + // + + Buffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // Make sure the Dcb is still good. + // + + FatVerifyFcb( IrpContext, Dcb ); + + // + // Determine where to start the scan. Highest priority is given + // to the file index. Lower priority is the restart flag. If + // neither of these is specified, then the Vbo offset field in the + // Ccb is used. + // + + if (IndexSpecified) { + + CurrentVbo = FileIndex + sizeof( DIRENT ); + + } else if (RestartScan) { + + CurrentVbo = 0; + + } else { + + CurrentVbo = Ccb->OffsetToStartSearchFrom; + + } + + // + // If this is the first try then allocate a buffer for the file + // name. + // + + if (InitialQuery) { + + // + // If either: + // + // - No name was specified + // - An empty name was specified + // - We received a '*' + // - The user specified the DOS equivolent of ????????.??? + // + // then match all names. + // + + if ((UniArgFileName == NULL) || + (UniArgFileName->Length == 0) || + (UniArgFileName->Buffer == NULL) || + ((UniArgFileName->Length == sizeof(WCHAR)) && + (UniArgFileName->Buffer[0] == L'*')) || + ((UniArgFileName->Length == 12*sizeof(WCHAR)) && + (RtlEqualMemory( UniArgFileName->Buffer, + Fat8QMdot3QM, + 12*sizeof(WCHAR) )))) { + + Ccb->ContainsWildCards = TRUE; + + SetFlag( Ccb->Flags, CCB_FLAG_MATCH_ALL ); + + } else { + + BOOLEAN ExtendedName = FALSE; + OEM_STRING LocalBestFit; + + // + // First and formost, see if the name has wild cards. + // + + Ccb->ContainsWildCards = + FsRtlDoesNameContainWildCards( UniArgFileName ); + + // + // Now check to see if the name contains any extended + // characters + // + + for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) { + + if (UniArgFileName->Buffer[i] >= 0x80) { + + ExtendedName = TRUE; + break; + } + } + + // + // OK, now do the conversions we need. + // + + if (ExtendedName) { + + Status = RtlUpcaseUnicodeString( &Ccb->UnicodeQueryTemplate, + UniArgFileName, + TRUE ); + + if (!NT_SUCCESS(Status)) { + + try_return( Status ); + } + + SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE ); + + // + // Upcase the name and convert it to the Oem code page. + // + + Status = RtlUpcaseUnicodeStringToCountedOemString( &LocalBestFit, + UniArgFileName, + TRUE ); + + // + // If this conversion failed for any reason other than + // an unmappable character fail the request. + // + + if (!NT_SUCCESS(Status)) { + + if (Status == STATUS_UNMAPPABLE_CHARACTER) { + + SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); + + } else { + + try_return( Status ); + } + + } else { + + SetFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); + } + + } else { + + PVOID Buffers; + + // + // This case is optimized because I know I only have to + // worry about a-z. + // + + Buffers = FsRtlAllocatePoolWithTag( PagedPool, + UniArgFileName->Length + + UniArgFileName->Length / sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + + Ccb->UnicodeQueryTemplate.Buffer = Buffers; + Ccb->UnicodeQueryTemplate.Length = UniArgFileName->Length; + Ccb->UnicodeQueryTemplate.MaximumLength = UniArgFileName->Length; + +#ifndef __REACTOS__ + LocalBestFit.Buffer = (PUCHAR)Buffers + UniArgFileName->Length; +#else + LocalBestFit.Buffer = (PCHAR)Buffers + UniArgFileName->Length; +#endif + LocalBestFit.Length = UniArgFileName->Length / sizeof(WCHAR); + LocalBestFit.MaximumLength = LocalBestFit.Length; + + SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE ); + + for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) { + + WCHAR c = UniArgFileName->Buffer[i]; + + LocalBestFit.Buffer[i] = (UCHAR) + (Ccb->UnicodeQueryTemplate.Buffer[i] = + (c < 'a' ? c : c <= 'z' ? c - ('a' - 'A') : c)); + } + } + + // + // At this point we now have the upcased unicode name, + // and the two Oem names if they could be represented in + // this code page. + // + // Now determine if the Oem names are legal for what we + // going to try and do. Mark them as not usable is they + // are not legal. Note that we can optimize extended names + // since they are actually both the same string. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ) && + !FatIsNameShortOemValid( IrpContext, + LocalBestFit, + Ccb->ContainsWildCards, + FALSE, + FALSE )) { + + if (ExtendedName) { + + RtlFreeOemString( &LocalBestFit ); + ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); + } + + SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); + } + + // + // OK, now both locals oem strings correctly reflect their + // usability. Now we want to load up the Ccb structure. + // + // Now we will branch on two paths of wheather the name + // is wild or not. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) { + + if (Ccb->ContainsWildCards) { + + Ccb->OemQueryTemplate.Wild = LocalBestFit; + + } else { + + FatStringTo8dot3( IrpContext, + LocalBestFit, + &Ccb->OemQueryTemplate.Constant ); + + if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) { + + RtlFreeOemString( &LocalBestFit ); + ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT ); + } + } + } + } + + // + // We convert to shared access. + // + + FatConvertToSharedFcb( IrpContext, Dcb ); + } + + LastEntry = 0; + NextEntry = 0; + + switch (FileInformationClass) { + + case FileDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, + FileName[0] ); + break; + + case FileFullDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, + FileName[0] ); + break; + + case FileIdFullDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, + FileName[0] ); + break; + + case FileNamesInformation: + + BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, + FileName[0] ); + break; + + case FileBothDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, + FileName[0] ); + break; + + case FileIdBothDirectoryInformation: + + BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, + FileName[0] ); + break; + + default: + + try_return( Status = STATUS_INVALID_INFO_CLASS ); + } + + // + // At this point we are about to enter our query loop. We have + // determined the index into the directory file to begin the + // search. LastEntry and NextEntry are used to index into the user + // buffer. LastEntry is the last entry we've added, NextEntry is + // current one we're working on. If NextEntry is non-zero, then + // at least one entry was added. + // + + while ( TRUE ) { + + VBO NextVbo; + ULONG FileNameLength; + ULONG BytesRemainingInBuffer; + + + DebugTrace(0, Dbg, "FatQueryDirectory -> Top of loop\n", 0); + + // + // If the user had requested only a single match and we have + // returned that, then we stop at this point. + // + + if (ReturnSingleEntry && NextEntry != 0) { + + try_return( Status ); + } + + // + // We call FatLocateDirent to lock down the next matching dirent. + // + + FatLocateDirent( IrpContext, + Dcb, + Ccb, + CurrentVbo, + &Dirent, + &Bcb, + &NextVbo, + NULL, + &LongFileName); + + // + // If we didn't receive a dirent, then we are at the end of the + // directory. If we have returned any files, we exit with + // success, otherwise we return STATUS_NO_MORE_FILES. + // + + if (!Dirent) { + + DebugTrace(0, Dbg, "FatQueryDirectory -> No dirent\n", 0); + + if (NextEntry == 0) { + + UpdateCcb = FALSE; + + if (InitialQuery) { + + Status = STATUS_NO_SUCH_FILE; + + } else { + + Status = STATUS_NO_MORE_FILES; + } + } + + try_return( Status ); + } + + // + // Protect access to the user buffer with an exception handler. + // Since (at our request) IO doesn't buffer these requests, we have + // to guard against a user messing with the page protection and other + // such trickery. + // + + _SEH2_TRY { + + if (LongFileName.Length == 0) { + + // + // Now we have an entry to return to our caller. We'll convert + // the name from the form in the dirent to a . form. + // We'll case on the type of information requested and fill up + // the user buffer if everything fits. + // + + Fat8dot3ToString( IrpContext, Dirent, TRUE, &Fat8Dot3String ); + + // + // Determine the UNICODE length of the file name. + // + + FileNameLength = RtlOemStringToCountedUnicodeSize(&Fat8Dot3String); + + // + // Here are the rules concerning filling up the buffer: + // + // 1. The Io system garentees that there will always be + // enough room for at least one base record. + // + // 2. If the full first record (including file name) cannot + // fit, as much of the name as possible is copied and + // STATUS_BUFFER_OVERFLOW is returned. + // + // 3. If a subsequent record cannot completely fit into the + // buffer, none of it (as in 0 bytes) is copied, and + // STATUS_SUCCESS is returned. A subsequent query will + // pick up with this record. + // + + BytesRemainingInBuffer = UserBufferLength - NextEntry; + + if ( (NextEntry != 0) && + ( (BaseLength + FileNameLength > BytesRemainingInBuffer) || + (UserBufferLength < NextEntry) ) ) { + + DebugTrace(0, Dbg, "Next entry won't fit\n", 0); + + try_return( Status = STATUS_SUCCESS ); + } + + ASSERT( BytesRemainingInBuffer >= BaseLength ); + + // + // Zero the base part of the structure. + // + + RtlZeroMemory( &Buffer[NextEntry], BaseLength ); + + switch ( FileInformationClass ) { + + // + // Now fill the base parts of the strucure that are applicable. + // + + case FileBothDirectoryInformation: + case FileFullDirectoryInformation: + case FileIdBothDirectoryInformation: + case FileIdFullDirectoryInformation: + + DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0); + + // + // Get the Ea file length. + // + + FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry]; + + // + // If the EAs are corrupt, ignore the error. We don't want + // to abort the directory query. + // + + _SEH2_TRY { + + FatGetEaLength( IrpContext, + Vcb, + Dirent, + &FullDirInfo->EaSize ); + + } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + + FatResetExceptionState( IrpContext ); + FullDirInfo->EaSize = 0; + } _SEH2_END; + + case FileDirectoryInformation: + + DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry]; + + FatGetDirTimes( IrpContext, Dirent, DirInfo ); + + DirInfo->EndOfFile.QuadPart = Dirent->FileSize; + + if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { + + DirInfo->AllocationSize.QuadPart = + (((Dirent->FileSize + DiskAllocSize - 1) / DiskAllocSize) * + DiskAllocSize ); + } + + DirInfo->FileAttributes = Dirent->Attributes != 0 ? + Dirent->Attributes : + FILE_ATTRIBUTE_NORMAL; + + DirInfo->FileIndex = NextVbo; + + DirInfo->FileNameLength = FileNameLength; + + DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String); + + break; + + case FileNamesInformation: + + DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0); + + NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry]; + + NamesInfo->FileIndex = NextVbo; + + NamesInfo->FileNameLength = FileNameLength; + + DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String ); + + break; + + default: + + FatBugCheck( FileInformationClass, 0, 0 ); + } + + BytesConverted = 0; + + Status = RtlOemToUnicodeN( (PWCH)&Buffer[NextEntry + BaseLength], + BytesRemainingInBuffer - BaseLength, + &BytesConverted, + Fat8Dot3String.Buffer, + Fat8Dot3String.Length ); + + // + // Check for the case that a single entry doesn't fit. + // This should only get this far on the first entry + // + + if (BytesConverted < FileNameLength) { + + ASSERT( NextEntry == 0 ); + Status = STATUS_BUFFER_OVERFLOW; + } + + // + // Set up the previous next entry offset + // + + *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry; + + // + // And indicate how much of the user buffer we have currently + // used up. We must compute this value before we long align + // ourselves for the next entry + // + + Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) + + BaseLength + BytesConverted; + + // + // If something happened with the conversion, bail here. + // + + if ( !NT_SUCCESS( Status ) ) { + + try_return( NOTHING ); + } + + } else { + + ULONG ShortNameLength; + + FileNameLength = LongFileName.Length; + + // + // Here are the rules concerning filling up the buffer: + // + // 1. The Io system garentees that there will always be + // enough room for at least one base record. + // + // 2. If the full first record (including file name) cannot + // fit, as much of the name as possible is copied and + // STATUS_BUFFER_OVERFLOW is returned. + // + // 3. If a subsequent record cannot completely fit into the + // buffer, none of it (as in 0 bytes) is copied, and + // STATUS_SUCCESS is returned. A subsequent query will + // pick up with this record. + // + + BytesRemainingInBuffer = UserBufferLength - NextEntry; + + if ( (NextEntry != 0) && + ( (BaseLength + FileNameLength > BytesRemainingInBuffer) || + (UserBufferLength < NextEntry) ) ) { + + DebugTrace(0, Dbg, "Next entry won't fit\n", 0); + + try_return( Status = STATUS_SUCCESS ); + } + + ASSERT( BytesRemainingInBuffer >= BaseLength ); + + // + // Zero the base part of the structure. + // + + RtlZeroMemory( &Buffer[NextEntry], BaseLength ); + + switch ( FileInformationClass ) { + + // + // Now fill the base parts of the strucure that are applicable. + // + + case FileBothDirectoryInformation: + case FileIdBothDirectoryInformation: + + BothDirInfo = (PFILE_BOTH_DIR_INFORMATION)&Buffer[NextEntry]; + + // + // Now we have an entry to return to our caller. We'll convert + // the name from the form in the dirent to a . form. + // We'll case on the type of information requested and fill up + // the user buffer if everything fits. + // + + Fat8dot3ToString( IrpContext, Dirent, FALSE, &Fat8Dot3String ); + + ASSERT( Fat8Dot3String.Length <= 12 ); + + Status = RtlOemToUnicodeN( &BothDirInfo->ShortName[0], + 12*sizeof(WCHAR), + &ShortNameLength, + Fat8Dot3String.Buffer, + Fat8Dot3String.Length ); + + ASSERT( Status != STATUS_BUFFER_OVERFLOW ); + ASSERT( BothDirInfo->ShortNameLength <= 12*sizeof(WCHAR) ); + + // + // Copy the length into the dirinfo structure. Note + // that the LHS below is a USHORT, so it can not + // be specificed as the OUT parameter above. + // + + BothDirInfo->ShortNameLength = (UCHAR)ShortNameLength; + + // + // If something happened with the conversion, bail here. + // + + if ( !NT_SUCCESS( Status ) ) { + + try_return( NOTHING ); + } + + case FileFullDirectoryInformation: + case FileIdFullDirectoryInformation: + + DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0); + + // + // Get the Ea file length. + // + + FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry]; + + // + // If the EAs are corrupt, ignore the error. We don't want + // to abort the directory query. + // + + _SEH2_TRY { + + FatGetEaLength( IrpContext, + Vcb, + Dirent, + &FullDirInfo->EaSize ); + + } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + + FatResetExceptionState( IrpContext ); + FullDirInfo->EaSize = 0; + } _SEH2_END; + + case FileDirectoryInformation: + + DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry]; + + FatGetDirTimes( IrpContext, Dirent, DirInfo ); + + DirInfo->EndOfFile.QuadPart = Dirent->FileSize; + + if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) { + + DirInfo->AllocationSize.QuadPart = ( + (( Dirent->FileSize + + DiskAllocSize - 1 ) + / DiskAllocSize ) + * DiskAllocSize ); + } + + DirInfo->FileAttributes = Dirent->Attributes != 0 ? + Dirent->Attributes : + FILE_ATTRIBUTE_NORMAL; + + DirInfo->FileIndex = NextVbo; + + DirInfo->FileNameLength = FileNameLength; + + DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String); + + break; + + case FileNamesInformation: + + DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0); + + NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry]; + + NamesInfo->FileIndex = NextVbo; + + NamesInfo->FileNameLength = FileNameLength; + + DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String ); + + break; + + default: + + FatBugCheck( FileInformationClass, 0, 0 ); + } + + BytesConverted = BytesRemainingInBuffer - BaseLength >= FileNameLength ? + FileNameLength : + BytesRemainingInBuffer - BaseLength; + + RtlCopyMemory( &Buffer[NextEntry + BaseLength], + &LongFileName.Buffer[0], + BytesConverted ); + + // + // Set up the previous next entry offset + // + + *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry; + + // + // And indicate how much of the user buffer we have currently + // used up. We must compute this value before we long align + // ourselves for the next entry + // + + Irp->IoStatus.Information += BaseLength + BytesConverted; + + // + // Check for the case that a single entry doesn't fit. + // This should only get this far on the first entry. + // + + if (BytesConverted < FileNameLength) { + + ASSERT( NextEntry == 0 ); + + try_return( Status = STATUS_BUFFER_OVERFLOW ); + } + } + + // + // Finish up by filling in the FileId + // + + switch ( FileInformationClass ) { + + case FileIdBothDirectoryInformation: + + IdBothDirInfo = (PFILE_ID_BOTH_DIR_INFORMATION)&Buffer[NextEntry]; + IdBothDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo ); + break; + + case FileIdFullDirectoryInformation: + + IdFullDirInfo = (PFILE_ID_FULL_DIR_INFORMATION)&Buffer[NextEntry]; + IdFullDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo ); + break; + + default: + break; + } + + } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { + + // + // We had a problem filling in the user's buffer, so stop and + // fail this request. This is the only reason any exception + // would have occured at this level. + // + + Irp->IoStatus.Information = 0; + UpdateCcb = FALSE; + try_return( Status = _SEH2_GetExceptionCode()); + } _SEH2_END; + + // + // Set ourselves up for the next iteration + // + + LastEntry = NextEntry; + NextEntry += (ULONG)QuadAlign(BaseLength + BytesConverted); + + CurrentVbo = NextVbo + sizeof( DIRENT ); + } + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatQueryDirectory ); + + FatReleaseFcb( IrpContext, Dcb ); + + // + // Unpin data in cache if still held. + // + + FatUnpinBcb( IrpContext, Bcb ); + + // + // Free any dynamically allocated string buffer + // + + FatFreeStringBuffer( &LongFileName); + + // + // Perform any cleanup. If this is the first query, then store + // the filename in the Ccb if successful. Also update the + // VBO index for the next search. This is done by transferring + // from shared access to exclusive access and copying the + // data from the local copies. + // + + if (!_SEH2_AbnormalTermination()) { + + if (UpdateCcb) { + + // + // Store the most recent VBO to use as a starting point for + // the next search. + // + + Ccb->OffsetToStartSearchFrom = CurrentVbo; + } + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status); + + } _SEH2_END; + + return Status; +} + + +// +// Local Support Routine +// + +VOID +FatGetDirTimes( + PIRP_CONTEXT IrpContext, + PDIRENT Dirent, + PFILE_DIRECTORY_INFORMATION DirInfo + ) + +/*++ + +Routine Description: + + This routine pulls the date/time information from a dirent and fills + in the DirInfo structure. + +Arguments: + + Dirent - Supplies the dirent + DirInfo - Supplies the target structure + +Return Value: + + VOID + +--*/ + + +{ + // + // Start with the Last Write Time. + // + + DirInfo->LastWriteTime = + FatFatTimeToNtTime( IrpContext, + Dirent->LastWriteTime, + 0 ); + + // + // These fields are only non-zero when in Chicago mode. + // + + if (FatData.ChicagoMode) { + + // + // Do a quick check here for Creation and LastAccess + // times that are the same as the LastWriteTime. + // + + if (*((UNALIGNED LONG *)&Dirent->CreationTime) == + *((UNALIGNED LONG *)&Dirent->LastWriteTime)) { + + DirInfo->CreationTime.QuadPart = + + DirInfo->LastWriteTime.QuadPart + + Dirent->CreationMSec * 10 * 1000 * 10; + + } else { + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[8] != 0) { + + DirInfo->CreationTime = + FatFatTimeToNtTime( IrpContext, + Dirent->CreationTime, + Dirent->CreationMSec ); + + } else { + + ExLocalTimeToSystemTime( &FatJanOne1980, + &DirInfo->CreationTime ); + } + } + + // + // Do a quick check for LastAccessDate. + // + + if (*((PUSHORT)&Dirent->LastAccessDate) == + *((PUSHORT)&Dirent->LastWriteTime.Date)) { + + PFAT_TIME WriteTime; + + WriteTime = &Dirent->LastWriteTime.Time; + + DirInfo->LastAccessTime.QuadPart = + DirInfo->LastWriteTime.QuadPart - + UInt32x32To64(((WriteTime->DoubleSeconds * 2) + + (WriteTime->Minute * 60) + + (WriteTime->Hour * 60 * 60)), + 1000 * 1000 * 10); + + } else { + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[9] != 0) { + + DirInfo->LastAccessTime = + FatFatDateToNtTime( IrpContext, + Dirent->LastAccessDate ); + + } else { + + ExLocalTimeToSystemTime( &FatJanOne1980, + &DirInfo->LastAccessTime ); + } + } + } +} + + +// +// Local Support Routine +// + +NTSTATUS +FatNotifyChangeDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the notify change directory operation. It is + responsible for either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PVCB Vcb; + PDCB Dcb; + PCCB Ccb; + ULONG CompletionFilter; + BOOLEAN WatchTree; + + BOOLEAN CompleteRequest; + + // + // Get the current Stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatNotifyChangeDirectory...\n", 0); + DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, " ->CompletionFilter = %08lx\n", IrpSp->Parameters.NotifyDirectory.CompletionFilter); + + // + // Always set the wait flag in the Irp context for the original request. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Assume we don't complete request. + // + + CompleteRequest = FALSE; + + // + // Check on the type of open. We return invalid parameter for all + // but UserDirectoryOpens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, + &Vcb, + &Dcb, + &Ccb ) != UserDirectoryOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0); + + return STATUS_INVALID_PARAMETER; + + } + + // + // Reference our input parameter to make things easier + // + + CompletionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter; + WatchTree = BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE ); + + // + // Try to acquire exclusive access to the Dcb and enqueue the Irp to the + // Fsp if we didn't get access + // + + if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) { + + DebugTrace(0, Dbg, "FatNotifyChangeDirectory -> Cannot Acquire Fcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status); + return Status; + } + + _SEH2_TRY { + + // + // Make sure the Fcb is still good + // + + FatVerifyFcb( IrpContext, Dcb ); + + // + // We need the full name. + // + + FatSetFullFileNameInFcb( IrpContext, Dcb ); + + // + // If the file is marked as DELETE_PENDING then complete this + // request immediately. + // + + if (FlagOn( Dcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) { + + FatRaiseStatus( IrpContext, STATUS_DELETE_PENDING ); + } + + // + // Call the Fsrtl package to process the request. + // + + FsRtlNotifyFullChangeDirectory( Vcb->NotifySync, + &Vcb->DirNotifyList, + Ccb, + (PSTRING)&Dcb->FullFileName, + WatchTree, + FALSE, + CompletionFilter, + Irp, + NULL, + NULL ); + + Status = STATUS_PENDING; + + CompleteRequest = TRUE; + + } _SEH2_FINALLY { + + DebugUnwind( FatNotifyChangeDirectory ); + + FatReleaseFcb( IrpContext, Dcb ); + + // + // If the dir notify package is holding the Irp, we discard the + // the IrpContext. + // + + if (CompleteRequest) { + + FatCompleteRequest( IrpContext, FatNull, 0 ); + } + + DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + diff --git a/drivers/filesystems/fastfat_new/dirsup.c b/drivers/filesystems/fastfat_new/dirsup.c new file mode 100644 index 00000000000..71689e8d902 --- /dev/null +++ b/drivers/filesystems/fastfat_new/dirsup.c @@ -0,0 +1,3643 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DirSup.c + +Abstract: + + This module implements the dirent support routines for Fat. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_DIRSUP) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_DIRSUP) + +// +// The following three macro all assume the input dirent has been zeroed. +// + +// +// VOID +// FatConstructDot ( +// IN PIRP_CONTEXT IrpContext, +// IN PDCB Directory, +// IN PDIRENT ParentDirent, +// IN OUT PDIRENT Dirent +// ); +// +// The following macro is called to initalize the "." dirent. +// +// Always setting FirstClusterOfFileHi is OK because it will be zero +// unless we're working on a FAT 32 disk. +// + +#define FatConstructDot(IRPCONTEXT,DCB,PARENT,DIRENT) { \ + \ + RtlCopyMemory( (PUCHAR)(DIRENT), ". ", 11 ); \ + (DIRENT)->Attributes = FAT_DIRENT_ATTR_DIRECTORY; \ + (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \ + if (FatData.ChicagoMode) { \ + (DIRENT)->CreationTime = (PARENT)->CreationTime; \ + (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \ + (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \ + } \ + (DIRENT)->FirstClusterOfFile = \ + (USHORT)(DCB)->FirstClusterOfFile; \ + (DIRENT)->FirstClusterOfFileHi = \ + (USHORT)((DCB)->FirstClusterOfFile/0x10000); \ +} + +// +// VOID +// FatConstructDotDot ( +// IN PIRP_CONTEXT IrpContext, +// IN PDCB Directory, +// IN PDIRENT ParentDirent, +// IN OUT PDIRENT Dirent +// ); +// +// The following macro is called to initalize the ".." dirent. +// +// Always setting FirstClusterOfFileHi is OK because it will be zero +// unless we're working on a FAT 32 disk. +// + +#define FatConstructDotDot(IRPCONTEXT,DCB,PARENT,DIRENT) { \ + \ + RtlCopyMemory( (PUCHAR)(DIRENT), ".. ", 11 ); \ + (DIRENT)->Attributes = FAT_DIRENT_ATTR_DIRECTORY; \ + (DIRENT)->LastWriteTime = (PARENT)->LastWriteTime; \ + if (FatData.ChicagoMode) { \ + (DIRENT)->CreationTime = (PARENT)->CreationTime; \ + (DIRENT)->CreationMSec = (PARENT)->CreationMSec; \ + (DIRENT)->LastAccessDate = (PARENT)->LastAccessDate; \ + } \ + if (NodeType((DCB)->ParentDcb) == FAT_NTC_ROOT_DCB) { \ + (DIRENT)->FirstClusterOfFile = 0; \ + (DIRENT)->FirstClusterOfFileHi = 0; \ + } else { \ + (DIRENT)->FirstClusterOfFile = (USHORT) \ + ((DCB)->ParentDcb->FirstClusterOfFile); \ + (DIRENT)->FirstClusterOfFileHi = (USHORT) \ + ((DCB)->ParentDcb->FirstClusterOfFile/0x10000); \ + } \ +} + +// +// VOID +// FatConstructEndDirent ( +// IN PIRP_CONTEXT IrpContext, +// IN OUT PDIRENT Dirent +// ); +// +// The following macro created the end dirent. Note that since the +// dirent was zeroed, the first byte of the name already contains 0x0, +// so there is nothing to do. +// + +#define FatConstructEndDirent(IRPCONTEXT,DIRENT) NOTHING + +// +// VOID +// FatReadDirent ( +// IN PIRP_CONTEXT IrpContext, +// IN PDCB Dcb, +// IN VBO Vbo, +// OUT PBCB *Bcb, +// OUT PVOID *Dirent, +// OUT PNTSTATUS Status +// ); +// + +// +// This macro reads in a page of dirents when we step onto a new page, +// or this is the first iteration of a loop and Bcb is NULL. +// + +#define FatReadDirent(IRPCONTEXT,DCB,VBO,BCB,DIRENT,STATUS) \ +if ((VBO) >= (DCB)->Header.AllocationSize.LowPart) { \ + *(STATUS) = STATUS_END_OF_FILE; \ + FatUnpinBcb( (IRPCONTEXT), *(BCB) ); \ +} else if ( ((VBO) % PAGE_SIZE == 0) || (*(BCB) == NULL) ) { \ + FatUnpinBcb( (IRPCONTEXT), *(BCB) ); \ + FatReadDirectoryFile( (IRPCONTEXT), \ + (DCB), \ + (VBO) & ~(PAGE_SIZE - 1), \ + PAGE_SIZE, \ + FALSE, \ + (BCB), \ + (PVOID *)(DIRENT), \ + (STATUS) ); \ + *(DIRENT) = (PVOID)((PUCHAR)*(DIRENT) + ((VBO) % PAGE_SIZE)); \ +} + +// +// Internal support routines +// + +UCHAR +FatComputeLfnChecksum ( + PDIRENT Dirent + ); + +VOID +FatRescanDirectory ( + PIRP_CONTEXT IrpContext, + PDCB Dcb + ); + +ULONG +FatDefragDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN ULONG DirentsNeeded + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatComputeLfnChecksum) +#pragma alloc_text(PAGE, FatConstructDirent) +#pragma alloc_text(PAGE, FatConstructLabelDirent) +#pragma alloc_text(PAGE, FatCreateNewDirent) +#pragma alloc_text(PAGE, FatDefragDirectory) +#pragma alloc_text(PAGE, FatDeleteDirent) +#pragma alloc_text(PAGE, FatGetDirentFromFcbOrDcb) +#pragma alloc_text(PAGE, FatInitializeDirectoryDirent) +#pragma alloc_text(PAGE, FatIsDirectoryEmpty) +#pragma alloc_text(PAGE, FatLfnDirentExists) +#pragma alloc_text(PAGE, FatLocateDirent) +#pragma alloc_text(PAGE, FatLocateSimpleOemDirent) +#pragma alloc_text(PAGE, FatLocateVolumeLabel) +#pragma alloc_text(PAGE, FatRescanDirectory) +#pragma alloc_text(PAGE, FatSetFileSizeInDirent) +#pragma alloc_text(PAGE, FatTunnelFcbOrDcb) +#pragma alloc_text(PAGE, FatUpdateDirentFromFcb) +#endif + + +ULONG +FatCreateNewDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN ULONG DirentsNeeded + ) + +/*++ + +Routine Description: + + This routine allocates on the disk a new dirent inside of the + parent directory. If a new dirent cannot be allocated (i.e., + because the disk is full or the root directory is full) then + it raises the appropriate status. The dirent itself is + neither initialized nor pinned by this procedure. + +Arguments: + + ParentDirectory - Supplies the DCB for the directory in which + to create the new dirent + + DirentsNeeded - This is the number of continginous dirents required + +Return Value: + + ByteOffset - Returns the VBO within the Parent directory where + the dirent has been allocated + +--*/ + +{ + VBO UnusedVbo; + VBO DeletedHint; + ULONG ByteOffset; + + PBCB Bcb = NULL; + PDIRENT Dirent; + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatCreateNewDirent\n", 0); + + DebugTrace( 0, Dbg, " ParentDirectory = %08lx\n", ParentDirectory); + + // + // If UnusedDirentVbo is within our current file allocation then we + // don't have to search through the directory at all; we know just + // where to put it. + // + // If UnusedDirentVbo is beyond the current file allocation then + // there are no more unused dirents in the current allocation, though + // upon adding another cluster of allocation UnusedDirentVbo + // will point to an unused dirent. Haveing found no unused dirents + // we use the DeletedDirentHint to try and find a deleted dirent in + // the current allocation. In this also runs off the end of the file, + // we finally have to break down and allocate another sector. Note + // that simply writing beyond the current allocation will automatically + // do just this. + // + // We also must deal with the special case where UnusedDirentVbo and + // DeletedDirentHint have yet to be initialized. In this case we must + // first walk through the directory looking for the first deleted entry + // first unused dirent. After this point we continue as before. + // This virgin state is denoted by the special value of 0xffffffff. + // + + UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo; + DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint; + + // + // Check for our first call to this routine with this Dcb. If so + // we have to correctly set the two hints in the Dcb. + // + + if (UnusedVbo == 0xffffffff) { + + FatRescanDirectory( IrpContext, ParentDirectory ); + + UnusedVbo = ParentDirectory->Specific.Dcb.UnusedDirentVbo; + DeletedHint = ParentDirectory->Specific.Dcb.DeletedDirentHint; + } + + // + // Now we know that UnusedDirentVbo and DeletedDirentHint are correctly + // set so we check if there is already an unused dirent in the the + // current allocation. This is the easy case. + // + + DebugTrace( 0, Dbg, " UnusedVbo = %08lx\n", UnusedVbo); + DebugTrace( 0, Dbg, " DeletedHint = %08lx\n", DeletedHint); + + if ( UnusedVbo + (DirentsNeeded * sizeof(DIRENT)) <= + ParentDirectory->Header.AllocationSize.LowPart ) { + + // + // Get this unused dirent for the caller. We have a + // sporting chance that we won't have to wait. + // + + DebugTrace( 0, Dbg, "There is a never used entry.\n", 0); + + ByteOffset = UnusedVbo; + + UnusedVbo += DirentsNeeded * sizeof(DIRENT); + + } else { + + // + // Life is tough. We have to march from the DeletedDirentHint + // looking for a deleted dirent. If we get to EOF without finding + // one, we will have to allocate a new cluster. + // + + ByteOffset = + RtlFindClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + DirentsNeeded, + DeletedHint / sizeof(DIRENT) ); + + // + // Do a quick check for a root directory allocation that failed + // simply because of fragmentation. Also, only attempt to defrag + // if the length is less that 0x40000. This is to avoid + // complications arising from crossing a MM view boundary (256kb). + // By default on DOS the root directory is only 0x2000 long. + // + // Don't try to defrag fat32 root dirs. + // + + if (!FatIsFat32(ParentDirectory->Vcb) && + (ByteOffset == -1) && + (NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB) && + (ParentDirectory->Header.AllocationSize.LowPart <= 0x40000)) { + + ByteOffset = FatDefragDirectory( IrpContext, ParentDirectory, DirentsNeeded ); + } + + if (ByteOffset != -1) { + + // + // If we consuemed deleted dirents at Deleted Hint, update. + // We also may have consumed some un-used dirents as well, + // so be sure to check for that as well. + // + + ByteOffset *= sizeof(DIRENT); + + if (ByteOffset == DeletedHint) { + + DeletedHint += DirentsNeeded * sizeof(DIRENT); + } + + if (ByteOffset + DirentsNeeded * sizeof(DIRENT) > UnusedVbo) { + + UnusedVbo = ByteOffset + DirentsNeeded * sizeof(DIRENT); + } + + } else { + + // + // We are going to have to allocate another cluster. Do + // so, update both the UnusedVbo and the DeletedHint and bail. + // + + DebugTrace( 0, Dbg, "We have to allocate another cluster.\n", 0); + + // + // A reason why we might fail, unrelated to physical reasons, + // is that we constrain to 64k directory entries to match the + // restriction on Win95. There are fundamental reasons to do + // this since searching a FAT directory is a linear operation + // and to allow FAT32 to toss us over the cliff is not permissable. + // + + if (ParentDirectory->Header.AllocationSize.LowPart >= (64 * 1024 * sizeof(DIRENT)) || + + // + // Make sure we are not trying to expand the root directory on non + // FAT32. FAT16 and FAT12 have fixed size allocations. + // + + (!FatIsFat32(ParentDirectory->Vcb) && + NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB)) { + + DebugTrace(0, Dbg, "Full root directory or too big on FAT32. Raise Status.\n", 0); + + FatRaiseStatus( IrpContext, STATUS_CANNOT_MAKE ); + } + + // + // Take the last dirent(s) in this cluster. We will allocate + // more clusters below. + // + + ByteOffset = UnusedVbo; + UnusedVbo += DirentsNeeded * sizeof(DIRENT); + + // + // Touch the directory file to cause space for the new dirents + // to be allocated. + // + + Bcb = NULL; + + _SEH2_TRY { + +#ifndef __REACTOS__ + ULONG ClusterSize; +#endif + PVOID Buffer; + +#ifndef __REACTOS__ + ClusterSize = + 1 << ParentDirectory->Vcb->AllocationSupport.LogOfBytesPerCluster; +#endif + + FatPrepareWriteDirectoryFile( IrpContext, + ParentDirectory, + UnusedVbo, + 1, + &Bcb, + &Buffer, + FALSE, + TRUE, + &Status ); + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, Bcb ); + } _SEH2_END; + } + } + + // + // If we are only requesting a single dirent, and we did not get the + // first dirent in a directory, then check that the preceding dirent + // is not an orphaned LFN. If it is, then mark it deleted. Thus + // reducing the possibility of an accidental pairing. + // + // Only do this when we are in Chicago Mode. + // + + Bcb = NULL; + + if (FatData.ChicagoMode && + (DirentsNeeded == 1) && + (ByteOffset > (NodeType(ParentDirectory) == FAT_NTC_ROOT_DCB ? + 0 : 2 * sizeof(DIRENT)))) { + _SEH2_TRY { + + FatReadDirent( IrpContext, + ParentDirectory, + ByteOffset - sizeof(DIRENT), + &Bcb, + &Dirent, + &Status ); + + if ((Status != STATUS_SUCCESS) || + (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + FatPopUpFileCorrupt( IrpContext, ParentDirectory ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + if ((Dirent->Attributes == FAT_DIRENT_ATTR_LFN) && + (Dirent->FileName[0] != FAT_DIRENT_DELETED)) { + + // + // Pin it, mark it, and set it dirty. + // + + FatPinMappedData( IrpContext, + ParentDirectory, + ByteOffset - sizeof(DIRENT), + sizeof(DIRENT), + &Bcb ); + + Dirent->FileName[0] = FAT_DIRENT_DELETED; + + FatSetDirtyBcb( IrpContext, Bcb, ParentDirectory->Vcb, TRUE ); + + ASSERT( RtlAreBitsSet( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT), + DirentsNeeded ) ); + + RtlClearBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + (ByteOffset - sizeof(DIRENT))/ sizeof(DIRENT), + DirentsNeeded ); + + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, Bcb ); + } _SEH2_END; + } + + // + // Assert that the dirents are in fact unused + // + + _SEH2_TRY { + + ULONG i; + + Bcb = NULL; + + for (i = 0; i < DirentsNeeded; i++) { + + FatReadDirent( IrpContext, + ParentDirectory, + ByteOffset + i*sizeof(DIRENT), + &Bcb, + &Dirent, + &Status ); + + if ((Status != STATUS_SUCCESS) || + ((Dirent->FileName[0] != FAT_DIRENT_NEVER_USED) && + (Dirent->FileName[0] != FAT_DIRENT_DELETED))) { + + FatPopUpFileCorrupt( IrpContext, ParentDirectory ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, Bcb ); + } _SEH2_END; + + // + // Set the Bits in the bitmap and move the Unused Dirent Vbo. + // + + ASSERT( RtlAreBitsClear( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + ByteOffset / sizeof(DIRENT), + DirentsNeeded ) ); + + RtlSetBits( &ParentDirectory->Specific.Dcb.FreeDirentBitmap, + ByteOffset / sizeof(DIRENT), + DirentsNeeded ); + + // + // Save the newly computed values in the Parent Directory Fcb + // + + ParentDirectory->Specific.Dcb.UnusedDirentVbo = UnusedVbo; + ParentDirectory->Specific.Dcb.DeletedDirentHint = DeletedHint; + + DebugTrace(-1, Dbg, "FatCreateNewDirent -> (VOID)\n", 0); + + return ByteOffset; +} + + +VOID +FatInitializeDirectoryDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN PDIRENT ParentDirent + ) + +/*++ + +Routine Description: + + This routine converts a dirent into a directory on the disk. It does this + setting the directory flag in the dirent, and by allocating the necessary + space for the "." and ".." dirents and initializing them. + + If a new dirent cannot be allocated (i.e., because the disk is full) then + it raises the appropriate status. + +Arguments: + + Dcb - Supplies the Dcb denoting the file that is to be made into a + directory. This must be input a completely empty file with + an allocation size of zero. + + ParentDirent - Provides the parent Dirent for a time-stamp model. + +Return Value: + + None. + +--*/ + +{ + PBCB Bcb; + PVOID Buffer; + NTSTATUS DontCare; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatInitializeDirectoryDirent\n", 0); + + DebugTrace( 0, Dbg, " Dcb = %08lx\n", Dcb); + + // + // Assert that we are not attempting this on the root directory. + // + + ASSERT( NodeType(Dcb) != FAT_NTC_ROOT_DCB ); + + // + // Assert that this is only attempted on newly created directories. + // + + ASSERT( Dcb->Header.AllocationSize.LowPart == 0 ); + + // + // Prepare the directory file for writing. Note that we can use a single + // Bcb for these two entries because we know they are the first two in + // the directory, and thus together do not span a page boundry. Also + // note that we prepare write 2 entries: one for "." and one for "..". + // The end of directory marker is automatically set since the whole + // directory is initially zero (DIRENT_NEVER_USED). + // + + FatPrepareWriteDirectoryFile( IrpContext, + Dcb, + 0, + 2 * sizeof(DIRENT), + &Bcb, + &Buffer, + FALSE, + TRUE, + &DontCare ); + + ASSERT( NT_SUCCESS( DontCare )); + + // + // Add the . and .. entries + // + + _SEH2_TRY { + + FatConstructDot( IrpContext, Dcb, ParentDirent, (PDIRENT)Buffer + 0); + + FatConstructDotDot( IrpContext, Dcb, ParentDirent, (PDIRENT)Buffer + 1); + + // + // Unpin the buffer and return to the caller. + // + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, Bcb ); + } _SEH2_END; + + DebugTrace(-1, Dbg, "FatInitializeDirectoryDirent -> (VOID)\n", 0); + return; +} + + +VOID +FatTunnelFcbOrDcb ( + IN PFCB FcbOrDcb, + IN PCCB Ccb OPTIONAL + ) +/*++ + +Routine Description: + + This routine handles tunneling of an Fcb or Dcb associated with + an object whose name is disappearing from a directory. + +Arguments: + + FcbOrDcb - Supplies the Fcb/Dcb whose name will be going away + + Ccb - Supplies the Ccb for the Fcb (not reqired for a Dcb) so + that we know which name the Fcb was opened by + +Return Value: + + None. + +--*/ +{ + UNICODE_STRING ShortNameWithCase; + UNICODE_STRING DownCaseSeg; + WCHAR ShortNameBuffer[8+1+3]; + NTSTATUS Status; + USHORT i; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatTunnelFcbOrDcb\n", 0); + + if (NodeType(FcbOrDcb) == FAT_NTC_DCB) { + + // + // Directory deletion. Flush all entries from this directory in + // the cache for this volume + // + + FsRtlDeleteKeyFromTunnelCache( &FcbOrDcb->Vcb->Tunnel, + FatDirectoryKey(FcbOrDcb) ); + + } else { + + // + // Was a file, so throw it into the tunnel cache + // + + // + // Get the short name into UNICODE + // + + ShortNameWithCase.Length = 0; + ShortNameWithCase.MaximumLength = sizeof(ShortNameBuffer); + ShortNameWithCase.Buffer = ShortNameBuffer; + + Status = RtlOemStringToCountedUnicodeString( &ShortNameWithCase, + &FcbOrDcb->ShortName.Name.Oem, + FALSE); + + ASSERT(ShortNameWithCase.Length != 0); + + ASSERT(NT_SUCCESS(Status)); + + if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_8_LOWER_CASE | FCB_STATE_3_LOWER_CASE)) { + + // + // Have to repair the case of the short name + // + + for (i = 0; i < (ShortNameWithCase.Length/sizeof(WCHAR)) && + ShortNameWithCase.Buffer[i] != L'.'; i++); + + // + // Now pointing at the '.', or otherwise the end of name component + // + + if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_8_LOWER_CASE)) { + + DownCaseSeg.Buffer = ShortNameWithCase.Buffer; + DownCaseSeg.MaximumLength = DownCaseSeg.Length = i*sizeof(WCHAR); + + RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE); + } + + i++; + + // + // Now pointing at first wchar of the extension. + // + + if (FlagOn(FcbOrDcb->FcbState, FCB_STATE_3_LOWER_CASE)) { + + // + // It is not neccesarily the case that we can rely on the flag + // indicating that we really have an extension. + // + + if ((i*sizeof(WCHAR)) < ShortNameWithCase.Length) { + DownCaseSeg.Buffer = &ShortNameWithCase.Buffer[i]; + DownCaseSeg.MaximumLength = DownCaseSeg.Length = ShortNameWithCase.Length - i*sizeof(WCHAR); + + RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE); + } + } + } + + // + // ... and add it in + // + + FsRtlAddToTunnelCache( &FcbOrDcb->Vcb->Tunnel, + FatDirectoryKey(FcbOrDcb->ParentDcb), + &ShortNameWithCase, + &FcbOrDcb->ExactCaseLongName, + BooleanFlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME), + sizeof(LARGE_INTEGER), + &FcbOrDcb->CreationTime ); + } + + DebugTrace(-1, Dbg, "FatTunnelFcbOrDcb -> (VOID)\n", 0); + + return; +} + + +VOID +FatDeleteDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN PDELETE_CONTEXT DeleteContext OPTIONAL, + IN BOOLEAN DeleteEa + ) + +/*++ + +Routine Description: + + This routine Deletes on the disk the indicated dirent. It does + this by marking the dirent as deleted. + +Arguments: + + FcbOrDcb - Supplies the FCB/DCB for the file/directory being + deleted. For a file the file size and allocation must be zero. + (Zero allocation is implied by a zero cluster index). + For a directory the allocation must be zero. + + DeleteContext - This variable, if speicified, may be used to preserve + the file size and first cluster of file information in the dirent + fot the benefit of unerase utilities. + + DeleteEa - Tells us whether to delete the EA and whether to check + for no allocation/ Mainly TRUE. FALSE passed in from rename. + +Return Value: + + None. + +--*/ + +{ + PBCB Bcb = NULL; + PDIRENT Dirent; + NTSTATUS DontCare; + ULONG Offset; + ULONG DirentsToDelete; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatDeleteDirent\n", 0); + + DebugTrace( 0, Dbg, " FcbOrDcb = %08lx\n", FcbOrDcb); + + // + // We must be holding the vcb exclusive here to deal with the locate dirent + // cases where it cannot be holding the parent simply. This is actually + // a true statement from olden daze, lets just wire in our assertion. + // + // Among other reasons, it'd be darn unfortunate if this raced with the + // rename path. + // + + ASSERT( ExIsResourceAcquiredExclusiveLite( &FcbOrDcb->Vcb->Resource )); + + // + // Assert that we are not attempting this on the root directory. + // + + ASSERT( NodeType(FcbOrDcb) != FAT_NTC_ROOT_DCB ); + + // + // Make sure all requests have zero allocation/file size + // + + if (DeleteEa && + ((FcbOrDcb->Header.AllocationSize.LowPart != 0) || + ((NodeType(FcbOrDcb) == FAT_NTC_FCB) && + (FcbOrDcb->Header.FileSize.LowPart != 0)))) { + + DebugTrace( 0, Dbg, "Called with non zero allocation/file size.\n", 0); + FatBugCheck( 0, 0, 0 ); + } + + // + // Now, mark the dirents deleted, unpin the Bcb, and return to the caller. + // Assert that there isn't any allocation associated with this dirent. + // + // Note that this loop will end with Dirent pointing to the short name. + // + + _SEH2_TRY { + + // + // We must acquire our parent exclusive to synchronize with enumerators + // who do not hold the vcb (ex: dirctrl). + // + // This relies on our bottom up lockorder. + // + + ExAcquireResourceExclusiveLite( FcbOrDcb->ParentDcb->Header.Resource, TRUE ); + + for ( Offset = FcbOrDcb->LfnOffsetWithinDirectory; + Offset <= FcbOrDcb->DirentOffsetWithinDirectory; + Offset += sizeof(DIRENT), Dirent += 1 ) { + + // + // If we stepped onto a new page, or this is the first iteration, + // unpin the old page, and pin the new one. + // + + if ((Offset == FcbOrDcb->LfnOffsetWithinDirectory) || + ((Offset & (PAGE_SIZE - 1)) == 0)) { + + FatUnpinBcb( IrpContext, Bcb ); + + FatPrepareWriteDirectoryFile( IrpContext, + FcbOrDcb->ParentDcb, + Offset, + sizeof(DIRENT), + &Bcb, + (PVOID *)&Dirent, + FALSE, + TRUE, + &DontCare ); + } + + ASSERT( (Dirent->FirstClusterOfFile == 0) || !DeleteEa ); + Dirent->FileName[0] = FAT_DIRENT_DELETED; + } + + // + // Back Dirent off by one to point back to the short dirent. + // + + Dirent -= 1; + + // + // If there are extended attributes for this dirent, we will attempt + // to remove them. We ignore any errors in removing Eas. + // + + if (!FatIsFat32(FcbOrDcb->Vcb) && + DeleteEa && (Dirent->ExtendedAttributes != 0)) { + + _SEH2_TRY { + + FatDeleteEa( IrpContext, + FcbOrDcb->Vcb, + Dirent->ExtendedAttributes, + &FcbOrDcb->ShortName.Name.Oem ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We catch all exceptions that Fat catches, but don't do + // anything with them. + // + } _SEH2_END; + } + + // + // Now clear the bits in the free dirent mask. + // + + DirentsToDelete = (FcbOrDcb->DirentOffsetWithinDirectory - + FcbOrDcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1; + + + ASSERT( (FcbOrDcb->ParentDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || + RtlAreBitsSet( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap, + FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT), + DirentsToDelete ) ); + + RtlClearBits( &FcbOrDcb->ParentDcb->Specific.Dcb.FreeDirentBitmap, + FcbOrDcb->LfnOffsetWithinDirectory / sizeof(DIRENT), + DirentsToDelete ); + + // + // Now, if the caller specified a DeleteContext, use it. + // + + if ( ARGUMENT_PRESENT( DeleteContext ) ) { + + Dirent->FileSize = DeleteContext->FileSize; + Dirent->FirstClusterOfFile = (USHORT)DeleteContext->FirstClusterOfFile; + } + + // + // If this newly deleted dirent is before the DeletedDirentHint, change + // the DeletedDirentHint to point here. + // + + if (FcbOrDcb->DirentOffsetWithinDirectory < + FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint) { + + FcbOrDcb->ParentDcb->Specific.Dcb.DeletedDirentHint = + FcbOrDcb->LfnOffsetWithinDirectory; + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, Bcb ); + + // + // Release our parent. + // + + ExReleaseResourceLite( FcbOrDcb->ParentDcb->Header.Resource ); + } _SEH2_END; + + DebugTrace(-1, Dbg, "FatDeleteDirent -> (VOID)\n", 0); + return; +} + +BOOLEAN +FatLfnDirentExists ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN PUNICODE_STRING Lfn, + IN PUNICODE_STRING LfnTmp + ) +/*++ + +Routine Description: + + This routine looks for a given Lfn in a directory + +Arguments: + + Dcb - The directory to search + + Lfn - The Lfn to look for + + Lfn - Temporary buffer to use to search for Lfn with (if < MAX_LFN then this + function may cause it to be allocated from pool if not large enough. + +Retrn Value: + + BOOLEAN TRUE if it exists, FALSE if not + +--*/ +{ + CCB Ccb; + PDIRENT Dirent; + PBCB DirentBcb = NULL; + VBO DirentByteOffset; + BOOLEAN Result = FALSE; + + PAGED_CODE(); + + // + // Pay performance penalty by forcing the compares to be case insensitive as + // opposed to grabbing more pool for a monocased copy of the Lfn. This is slight. + // + + Ccb.UnicodeQueryTemplate = *Lfn; + Ccb.ContainsWildCards = FALSE; + Ccb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE | CCB_FLAG_QUERY_TEMPLATE_MIXED; + + _SEH2_TRY { + + FatLocateDirent( IrpContext, + Dcb, + &Ccb, + 0, + &Dirent, + &DirentBcb, + &DirentByteOffset, + NULL, + LfnTmp); + + } _SEH2_FINALLY { + + if (DirentBcb) { + + Result = TRUE; + } + + FatUnpinBcb(IrpContext, DirentBcb); + } _SEH2_END; + + return Result; +} + +VOID +FatLocateDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN PCCB Ccb, + IN VBO OffsetToStartSearchFrom, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset, + OUT PBOOLEAN FileNameDos OPTIONAL, + IN OUT PUNICODE_STRING LongFileName OPTIONAL + ) + +/*++ + +Routine Description: + + This routine locates on the disk an undeleted dirent matching a given name. + +Arguments: + + ParentDirectory - Supplies the DCB for the directory to search + + Ccb - Contains a context control block with all matching information. + + OffsetToStartSearchFrom - Supplies the VBO within the parent directory + from which to start looking for another real dirent. + + Dirent - Receives a pointer to the located dirent if one was found + or NULL otherwise. + + Bcb - Receives the Bcb for the located dirent if one was found or + NULL otherwise. + + ByteOffset - Receives the VBO within the Parent directory for + the located dirent if one was found, or 0 otherwise. + + FileNameDos - Receives TRUE if the element of the dirent we hit on + was the short (non LFN) side + + LongFileName - If specified, this parameter returns the long file name + associated with the returned dirent. Note that it is the caller's + responsibility to provide the buffer (and set MaximumLength + accordingly) for this unicode string. The Length field is reset + to 0 by this routine on invocation. If the supplied buffer is not + large enough, a new one will be allocated from pool. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + OEM_STRING Name; + UCHAR NameBuffer[12]; + + UNICODE_STRING UpcasedLfn; + + WCHAR LocalLfnBuffer[32]; + + BOOLEAN LfnInProgress = FALSE; + UCHAR LfnChecksum; + ULONG LfnSize; + ULONG LfnIndex; + UCHAR Ordinal; + VBO LfnByteOffset; + + TimerStart(Dbg); + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLocateDirent\n", 0); + + DebugTrace( 0, Dbg, " ParentDirectory = %08lx\n", ParentDirectory); + DebugTrace( 0, Dbg, " OffsetToStartSearchFrom = %08lx\n", OffsetToStartSearchFrom); + DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent); + DebugTrace( 0, Dbg, " Bcb = %08lx\n", Bcb); + DebugTrace( 0, Dbg, " ByteOffset = %08lx\n", ByteOffset); + + // + // We must have acquired the parent or the vcb to synchronize with deletion. This + // is important since we can't survive racing a thread marking a series of lfn + // dirents deleted - we'd get a bogus ordinal, and otherwise get really messed up. + // + // This routine cannot do the acquire since it would be out-of-order with respect + // to the Bcb resources on iterative calls. Our order has Bcbs as the inferior resource. + // + // Deletion always grabs the parent (safely - this used to not be possible until the + // multiple fcb lockorder was fixed to be bottom up!). Deletion always occurs with + // the vcb held exclusive as well, and this will cover the cases where we can't easily + // hold the parent here, see above. + // + + ASSERT( ExIsResourceAcquiredSharedLite( ParentDirectory->Header.Resource ) || + ExIsResourceAcquiredExclusiveLite( ParentDirectory->Header.Resource ) || + ExIsResourceAcquiredSharedLite( &ParentDirectory->Vcb->Resource ) || + ExIsResourceAcquiredExclusiveLite( &ParentDirectory->Vcb->Resource )); + + // + // The algorithm here is pretty simple. We just walk through the + // parent directory until we: + // + // A) Find a matching entry. + // B) Can't Wait + // C) Hit the End of Directory + // D) Hit Eof + // + // In the first case we found it, in the latter three cases we did not. + // + + // + // Set up the strings that receives file names from our search + // + + Name.MaximumLength = 12; +#ifndef __REACTOS__ + Name.Buffer = NameBuffer; +#else + Name.Buffer = (PCHAR)NameBuffer; +#endif + + UpcasedLfn.Length = 0; + UpcasedLfn.MaximumLength = sizeof( LocalLfnBuffer); + UpcasedLfn.Buffer = LocalLfnBuffer; + + // + // If we were given a non-NULL Bcb, compute the new Dirent address + // from the prior one, or unpin the Bcb if the new Dirent is not pinned. + // + + if (*Bcb != NULL) { + + if ((OffsetToStartSearchFrom / PAGE_SIZE) == (*ByteOffset / PAGE_SIZE)) { + + *Dirent += (OffsetToStartSearchFrom - *ByteOffset) / sizeof(DIRENT); + + } else { + + FatUnpinBcb( IrpContext, *Bcb ); + } + } + + // + // Init the Lfn if we were given one. + // + + if (ARGUMENT_PRESENT(LongFileName)) { + + LongFileName->Length = 0; + } + + // + // Init the FileNameDos flag + // + + if (FileNameDos) { + + *FileNameDos = FALSE; + } + + // + // Round up OffsetToStartSearchFrom to the nearest Dirent, and store + // in ByteOffset. Note that this wipes out the prior value. + // + + *ByteOffset = (OffsetToStartSearchFrom + (sizeof(DIRENT) - 1)) + & ~(sizeof(DIRENT) - 1); + + _SEH2_TRY { + + while ( TRUE ) { + + BOOLEAN FoundValidLfn; + + // + // Try to read in the dirent + // + + FatReadDirent( IrpContext, + ParentDirectory, + *ByteOffset, + Bcb, + Dirent, + &Status ); + + // + // If End Directory dirent or EOF, set all out parameters to + // indicate entry not found and, like, bail. + // + // Note that the order of evaluation here is important since we + // cannot check the first character of the dirent until after we + // know we are not beyond EOF + // + + if ((Status == STATUS_END_OF_FILE) || + ((*Dirent)->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + DebugTrace( 0, Dbg, "End of directory: entry not found.\n", 0); + + // + // If there is a Bcb, unpin it and set it to null + // + + FatUnpinBcb( IrpContext, *Bcb ); + + *Dirent = NULL; + *ByteOffset = 0; + break; + } + + // + // If the entry is marked deleted, skip. If there was an Lfn in + // progress we throw it out at this point. + // + + if ((*Dirent)->FileName[0] == FAT_DIRENT_DELETED) { + + LfnInProgress = FALSE; + goto GetNextDirent; + } + + // + // If we have wandered onto an LFN entry, try to interpret it. + // + + if (FatData.ChicagoMode && + ARGUMENT_PRESENT(LongFileName) && + ((*Dirent)->Attributes == FAT_DIRENT_ATTR_LFN)) { + + PLFN_DIRENT Lfn; + + Lfn = (PLFN_DIRENT)*Dirent; + + if (LfnInProgress) { + + // + // Check for a proper continuation of the Lfn in progress. + // + + if ((Lfn->Ordinal & FAT_LAST_LONG_ENTRY) || + (Lfn->Ordinal == 0) || + (Lfn->Ordinal != Ordinal - 1) || + (Lfn->Checksum != LfnChecksum) || + (Lfn->MustBeZero != 0)) { + + // + // The Lfn is not proper, stop constructing it. + // + + LfnInProgress = FALSE; + + } else { + + ASSERT( ((LfnIndex % 13) == 0) && LfnIndex ); + + LfnIndex -= 13; + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0], + &Lfn->Name1[0], + 5*sizeof(WCHAR) ); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5], + &Lfn->Name2[0], + 6 * sizeof(WCHAR) ); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11], + &Lfn->Name3[0], + 2 * sizeof(WCHAR) ); + + Ordinal = Lfn->Ordinal; + LfnByteOffset = *ByteOffset; + } + } + + // + // Now check (maybe again) if we should analyze this entry + // for a possible last entry. + // + + if ((!LfnInProgress) && + (Lfn->Ordinal & FAT_LAST_LONG_ENTRY) && + ((Lfn->Ordinal & ~FAT_LAST_LONG_ENTRY) <= MAX_LFN_DIRENTS) && + (Lfn->MustBeZero == 0)) { + + BOOLEAN CheckTail = FALSE; + + Ordinal = Lfn->Ordinal & ~FAT_LAST_LONG_ENTRY; + + // + // We're usually permissive (following the lead of Win9x) when we find + // malformation of the LFN dirent pile. I'm not sure this is a good idea, + // so I'm going to trigger corruption on this particularly ugly one. Perhaps + // we should come back and redo the original code here with this in mind in the + // future. + // + + if (Ordinal == 0) { + + // + // First LFN in the pile was zero marked as the last. This is never + // possible since oridinals are 1-based. + // + + FatPopUpFileCorrupt( IrpContext, ParentDirectory ); + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + LfnIndex = (Ordinal - 1) * 13; + + FatEnsureStringBufferEnough( LongFileName, + (USHORT)((LfnIndex + 13) << 1)); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+0], + &Lfn->Name1[0], + 5*sizeof(WCHAR)); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+5], + &Lfn->Name2[0], + 6 * sizeof(WCHAR) ); + + RtlCopyMemory( &LongFileName->Buffer[LfnIndex+11], + &Lfn->Name3[0], + 2 * sizeof(WCHAR) ); + + // + // Now compute the Lfn size and make sure that the tail + // bytes are correct. + // + + while (LfnIndex != (ULONG)Ordinal * 13) { + + if (!CheckTail) { + + if (LongFileName->Buffer[LfnIndex] == 0x0000) { + + LfnSize = LfnIndex; + CheckTail = TRUE; + } + + } else { + + if (LongFileName->Buffer[LfnIndex] != 0xffff) { + + break; + } + } + + LfnIndex += 1; + } + + // + // If we exited this loop prematurely, the LFN is not valid. + // + + if (LfnIndex == (ULONG)Ordinal * 13) { + + // + // If we didn't find the NULL terminator, then the size + // is LfnIndex. + // + + if (!CheckTail) { + + LfnSize = LfnIndex; + } + + LfnIndex -= 13; + LfnInProgress = TRUE; + LfnChecksum = Lfn->Checksum; + LfnByteOffset = *ByteOffset; + } + } + + // + // Move on to the next dirent. + // + + goto GetNextDirent; + } + + // + // If this is the volume label, skip. Note that we never arrive here + // while building the LFN. If we did, we weren't asked to find LFNs + // and that is another good reason to skip this LFN fragment. + // + + if (FlagOn((*Dirent)->Attributes, FAT_DIRENT_ATTR_VOLUME_ID)) { + + // + // If we actually were asked to hand back volume labels, + // do it. + // + + if (FlagOn(Ccb->Flags, CCB_FLAG_MATCH_VOLUME_ID)) { + + break; + } + + goto GetNextDirent; + } + + // + // We may have just stepped off a valid Lfn run. Check to see if + // it is indeed valid for the following dirent. + // + + if (LfnInProgress && + (*ByteOffset == LfnByteOffset + sizeof(DIRENT)) && + (LfnIndex == 0) && + (FatComputeLfnChecksum(*Dirent) == LfnChecksum)) { + + ASSERT( Ordinal == 1); + + FoundValidLfn = TRUE; + LongFileName->Length = (USHORT)(LfnSize * sizeof(WCHAR)); + + } else { + + FoundValidLfn = FALSE; + } + + // + // If we are supposed to match all entries, then match this entry. + // + + if (FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL)) { + + break; + } + + // + // Check against the short name given if one was. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) { + + if (Ccb->ContainsWildCards) { + + // + // If we get one, note that all out parameters are already set. + // + + (VOID)Fat8dot3ToString( IrpContext, (*Dirent), FALSE, &Name ); + + // + // For fat we special case the ".." dirent because we want it to + // match ????????.??? and to do that we change ".." to "." before + // calling the Fsrtl routine. But only do this if the expression + // is greater than one character long. + // + + if ((Name.Length == 2) && + (Name.Buffer[0] == '.') && + (Name.Buffer[1] == '.') && + (Ccb->OemQueryTemplate.Wild.Length > 1)) { + + Name.Length = 1; + } + + if (FatIsNameInExpression( IrpContext, + Ccb->OemQueryTemplate.Wild, + Name)) { + + DebugTrace( 0, Dbg, "Entry found: Name = \"%Z\"\n", &Name); + DebugTrace( 0, Dbg, " VBO = %08lx\n", *ByteOffset); + + if (FileNameDos) { + + *FileNameDos = TRUE; + } + + break; + } + + } else { + + // + // Do the quickest 8.3 equivalency check possible + // + + if (!FlagOn((*Dirent)->Attributes, FAT_DIRENT_ATTR_VOLUME_ID) && + (*(PULONG)&(Ccb->OemQueryTemplate.Constant[0]) == *(PULONG)&((*Dirent)->FileName[0])) && + (*(PULONG)&(Ccb->OemQueryTemplate.Constant[4]) == *(PULONG)&((*Dirent)->FileName[4])) && + (*(PUSHORT)&(Ccb->OemQueryTemplate.Constant[8]) == *(PUSHORT)&((*Dirent)->FileName[8])) && + (*(PUCHAR)&(Ccb->OemQueryTemplate.Constant[10]) == *(PUCHAR)&((*Dirent)->FileName[10]))) { + + DebugTrace( 0, Dbg, "Entry found.\n", 0); + + if (FileNameDos) { + + *FileNameDos = TRUE; + } + + break; + } + } + } + + // + // No matches were found with the short name. If an LFN exists, + // use it for the search. + // + + if (FoundValidLfn) { + + // + // First do a quick check here for different sized constant + // name and expression before upcasing. + // + + if (!Ccb->ContainsWildCards && + Ccb->UnicodeQueryTemplate.Length != (USHORT)(LfnSize * sizeof(WCHAR))) { + + // + // Move on to the next dirent. + // + + FoundValidLfn = FALSE; + LongFileName->Length = 0; + + goto GetNextDirent; + } + + // + // We need to upcase the name we found. + // We need a buffer. Try to avoid doing an allocation. + // + + FatEnsureStringBufferEnough( &UpcasedLfn, + LongFileName->Length); + + Status = RtlUpcaseUnicodeString( &UpcasedLfn, + LongFileName, + FALSE ); + + if (!NT_SUCCESS(Status)) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // Do the compare + // + + if (Ccb->ContainsWildCards) { + + if (FsRtlIsNameInExpression( &Ccb->UnicodeQueryTemplate, + &UpcasedLfn, + TRUE, + NULL )) { + + break; + } + + } else { + + if (FsRtlAreNamesEqual( &Ccb->UnicodeQueryTemplate, + &UpcasedLfn, + BooleanFlagOn( Ccb->Flags, CCB_FLAG_QUERY_TEMPLATE_MIXED ), + NULL )) { + + break; + } + } + } + + // + // This long name was not a match. Zero out the Length field. + // + + if (FoundValidLfn) { + + FoundValidLfn = FALSE; + LongFileName->Length = 0; + } + +GetNextDirent: + + // + // Move on to the next dirent. + // + + *ByteOffset += sizeof(DIRENT); + *Dirent += 1; + } + + } _SEH2_FINALLY { + + FatFreeStringBuffer( &UpcasedLfn); + } _SEH2_END; + + DebugTrace(-1, Dbg, "FatLocateDirent -> (VOID)\n", 0); + + TimerStop(Dbg,"FatLocateDirent"); + + return; +} + + +VOID +FatLocateSimpleOemDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN POEM_STRING FileName, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset + ) + +/*++ + +Routine Description: + + This routine locates on the disk an undelted simple Oem dirent. By simple + I mean that FileName cannot contain any extended characters, and we do + not search LFNs or return them. + +Arguments: + + ParentDirectory - Supplies the DCB for the directory in which + to search + + FileName - Supplies the filename to search for. The name may contain + wild cards + + OffsetToStartSearchFrom - Supplies the VBO within the parent directory + from which to start looking for another real dirent. + + Dirent - Receives a pointer to the located dirent if one was found + or NULL otherwise. + + Bcb - Receives the Bcb for the located dirent if one was found or + NULL otherwise. + + ByteOffset - Receives the VBO within the Parent directory for + the located dirent if one was found, or 0 otherwise. + +Return Value: + + None. + +--*/ + +{ + CCB LocalCcb; + + PAGED_CODE(); + + // + // Note, this routine is called rarely, so performance is not critical. + // Just fill in a Ccb structure on my stack with the values that are + // required. + // + + FatStringTo8dot3( IrpContext, + *FileName, + &LocalCcb.OemQueryTemplate.Constant ); + LocalCcb.ContainsWildCards = FALSE; + LocalCcb.Flags = 0; + + FatLocateDirent( IrpContext, + ParentDirectory, + &LocalCcb, + 0, + Dirent, + Bcb, + ByteOffset, + NULL, + NULL); + + return; +} + + +VOID +FatLocateVolumeLabel ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset + ) + +/*++ + +Routine Description: + + This routine locates on the disk a dirent representing the volume + label. It does this by searching the root directory for a special + volume label dirent. + +Arguments: + + Vcb - Supplies the VCB for the volume to search + + Dirent - Receives a pointer to the located dirent if one was found + or NULL otherwise. + + Bcb - Receives the Bcb for the located dirent if one was found or + NULL otherwise. + + ByteOffset - Receives the VBO within the Parent directory for + the located dirent if one was found, or 0 otherwise. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatLocateVolumeLabel\n", 0); + + DebugTrace( 0, Dbg, " Vcb = %08lx\n", Vcb); + DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent); + DebugTrace( 0, Dbg, " Bcb = %08lx\n", Bcb); + DebugTrace( 0, Dbg, " ByteOffset = %08lx\n", ByteOffset); + + // + // The algorithm here is really simple. We just walk through the + // root directory until we: + // + // A) Find the non-deleted volume label + // B) Can't Wait + // C) Hit the End of Directory + // D) Hit Eof + // + // In the first case we found it, in the latter three cases we did not. + // + + *Bcb = NULL; + *ByteOffset = 0; + + while ( TRUE ) { + + // + // Try to read in the dirent + // + + FatReadDirent( IrpContext, + Vcb->RootDcb, + *ByteOffset, + Bcb, + Dirent, + &Status ); + + // + // If End Directory dirent or EOF, set all out parameters to + // indicate volume label not found and, like, bail. + // + // Note that the order of evaluation here is important since we cannot + // check the first character of the dirent until after we know we + // are not beyond EOF + // + + if ((Status == STATUS_END_OF_FILE) || + ((*Dirent)->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + DebugTrace( 0, Dbg, "Volume label not found.\n", 0); + + // + // If there is a Bcb, unpin it and set it to null + // + + FatUnpinBcb( IrpContext, *Bcb ); + + *Dirent = NULL; + *ByteOffset = 0; + break; + } + + // + // If the entry is the non-deleted volume label break from the loop. + // + // Note that all out parameters are already correctly set. + // + + if ((((*Dirent)->Attributes & ~FAT_DIRENT_ATTR_ARCHIVE) == FAT_DIRENT_ATTR_VOLUME_ID) && + ((*Dirent)->FileName[0] != FAT_DIRENT_DELETED)) { + + DebugTrace( 0, Dbg, "Volume label found at VBO = %08lx\n", *ByteOffset); + + // + // We may set this dirty, so pin it. + // + + FatPinMappedData( IrpContext, + Vcb->RootDcb, + *ByteOffset, + sizeof(DIRENT), + Bcb ); + + break; + } + + // + // Move on to the next dirent. + // + + *ByteOffset += sizeof(DIRENT); + *Dirent += 1; + } + + + DebugTrace(-1, Dbg, "FatLocateVolumeLabel -> (VOID)\n", 0); + + return; +} + + +VOID +FatGetDirentFromFcbOrDcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb + ) + +/*++ + +Routine Description: + + This routine reads locates on the disk the dirent denoted by the + specified Fcb/Dcb. + +Arguments: + + FcbOrDcb - Supplies the FCB/DCB for the file/directory whose dirent + we are trying to read in. This must not be the root dcb. + + Dirent - Receives a pointer to the dirent + + Bcb - Receives the Bcb for the dirent + +Return Value: + + None. + +--*/ + +{ + NTSTATUS DontCare; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatGetDirentFromFcbOrDcb\n", 0); + + DebugTrace( 0, Dbg, " FcbOrDcb = %08lx\n", FcbOrDcb); + DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent); + DebugTrace( 0, Dbg, " Bcb = %08lx\n", Bcb); + + // + // Assert that we are not attempting this on the root directory. + // + + ASSERT( NodeType(FcbOrDcb) != FAT_NTC_ROOT_DCB ); + + // + // We know the offset of the dirent within the directory file, + // so we just read it (with pinning). + // + + FatReadDirectoryFile( IrpContext, + FcbOrDcb->ParentDcb, + FcbOrDcb->DirentOffsetWithinDirectory, + sizeof(DIRENT), + TRUE, + Bcb, + (PVOID *)Dirent, + &DontCare ); + + // + // Previous call can fail. We used to assert success, but we use this + // as part of volume verification (DetermineAndMarkFcbCondition) after + // media has been removed. Clearly the directory could shrink and we + // would try to read beyond filesize. + // + // The caller will note this via NULL pointers for Bcb/Buffer. Note that + // both asserts below are OK since this should never happen fixed media. + // + // This was a Prefix catch. + // + + ASSERT( FlagOn( FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) || + NT_SUCCESS( DontCare )); + + // + // Note also that the only way this could fail is if the Fcb was being + // verified. This can't happen if the Fcb is in good condition. + // + // Also a Prefix catch. + // + + ASSERT( NT_SUCCESS( DontCare ) || FcbOrDcb->FcbCondition == FcbNeedsToBeVerified ); + + DebugTrace(-1, Dbg, "FatGetDirentFromFcbOrDcb -> (VOID)\n", 0); + return; +} + + +BOOLEAN +FatIsDirectoryEmpty ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ) + +/*++ + +Routine Description: + + This routine indicates to the caller if the specified directory + is empty. (i.e., it is not the root dcb and it only contains + the "." and ".." entries, or deleted files). + +Arguments: + + Dcb - Supplies the DCB for the directory being queried. + +Return Value: + + BOOLEAN - Returns TRUE if the directory is empty and + FALSE if the directory and is not empty. + +--*/ + +{ + PBCB Bcb; + ULONG ByteOffset; + PDIRENT Dirent; + + BOOLEAN IsDirectoryEmpty; + + NTSTATUS Status; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatIsDirectoryEmpty\n", 0); + + DebugTrace( 0, Dbg, " Dcb = %08lx\n", Dcb); + DebugTrace( 0, Dbg, " IsDirectoryEmpty = %08lx\n", IsDirectoryEmpty); + + // + // Check to see if the first entry is an and of directory marker. + // For the root directory we check at Vbo = 0, for normal directories + // we check after the "." and ".." entries. + // + + ByteOffset = (NodeType(Dcb) == FAT_NTC_ROOT_DCB) ? 0 : 2*sizeof(DIRENT); + + // + // We just march through the directory looking for anything other + // than deleted files, LFNs, an EOF, or end of directory marker. + // + + Bcb = NULL; + + _SEH2_TRY { + + while ( TRUE ) { + + // + // Try to read in the dirent + // + + FatReadDirent( IrpContext, + Dcb, + ByteOffset, + &Bcb, + &Dirent, + &Status ); + + // + // If End Directory dirent or EOF, set IsDirectoryEmpty to TRUE and, + // like, bail. + // + // Note that the order of evaluation here is important since we cannot + // check the first character of the dirent until after we know we + // are not beyond EOF + // + + if ((Status == STATUS_END_OF_FILE) || + (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + DebugTrace( 0, Dbg, "Empty. Last exempt entry at VBO = %08lx\n", ByteOffset); + + IsDirectoryEmpty = TRUE; + break; + } + + // + // If this dirent is NOT deleted or an LFN set IsDirectoryEmpty to + // FALSE and, like, bail. + // + + if ((Dirent->FileName[0] != FAT_DIRENT_DELETED) && + (Dirent->Attributes != FAT_DIRENT_ATTR_LFN)) { + + DebugTrace( 0, Dbg, "Not Empty. First entry at VBO = %08lx\n", ByteOffset); + + IsDirectoryEmpty = FALSE; + break; + } + + // + // Move on to the next dirent. + // + + ByteOffset += sizeof(DIRENT); + Dirent += 1; + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, Bcb ); + } _SEH2_END; + + DebugTrace(-1, Dbg, "FatIsDirectoryEmpty -> %ld\n", IsDirectoryEmpty); + + return IsDirectoryEmpty; +} + + +VOID +FatConstructDirent ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN POEM_STRING FileName, + IN BOOLEAN ComponentReallyLowercase, + IN BOOLEAN ExtensionReallyLowercase, + IN PUNICODE_STRING Lfn OPTIONAL, + IN UCHAR Attributes, + IN BOOLEAN ZeroAndSetTimeFields, + IN PLARGE_INTEGER SetCreationTime OPTIONAL + ) + +/*++ + +Routine Description: + + This routine modifies the fields of a dirent. + +Arguments: + + Dirent - Supplies the dirent being modified. + + FileName - Supplies the name to store in the Dirent. This + name must not contain wildcards. + + ComponentReallyLowercase - This boolean indicates that the User Specified + compoent name was really all a-z and < 0x80 characters. We set the + magic bit in this case. + + ExtensionReallyLowercase - Same as above, but for the extension. + + Lfn - May supply a long file name. + + Attributes - Supplies the attributes to store in the dirent + + ZeroAndSetTimeFields - Tells whether or not to initially zero the dirent + and update the time fields. + + SetCreationTime - If specified, contains a timestamp to use as the creation + time of this dirent + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatConstructDirent\n", 0); + + DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent); + DebugTrace( 0, Dbg, " FileName = %Z\n", FileName); + DebugTrace( 0, Dbg, " Attributes = %08lx\n", Attributes); + + if (ZeroAndSetTimeFields) { + + RtlZeroMemory( Dirent, sizeof(DIRENT) ); + } + + // + // We just merrily go and fill up the dirent with the fields given. + // + + FatStringTo8dot3( IrpContext, *FileName, (PFAT8DOT3)&Dirent->FileName[0] ); + + if (ZeroAndSetTimeFields || SetCreationTime) { + + LARGE_INTEGER Time, SaveTime; + + KeQuerySystemTime( &Time ); + + if (FatData.ChicagoMode) { + + if (!SetCreationTime || !FatNtTimeToFatTime( IrpContext, + SetCreationTime, + FALSE, + &Dirent->CreationTime, + &Dirent->CreationMSec )) { + + // + // No tunneled time or the tunneled time was bogus. Since we aren't + // responsible for initializing the to-be-created Fcb with creation + // time, we can't do the usual thing and let NtTimeToFatTime perform + // rounding on the timestamp - this would mess up converting to the + // LastWriteTime below. + // + + SaveTime = Time; + + if (!FatNtTimeToFatTime( IrpContext, + &SaveTime, + FALSE, + &Dirent->CreationTime, + &Dirent->CreationMSec )) { + + // + // Failed again. Wow. + // + + RtlZeroMemory( &Dirent->CreationTime, sizeof(FAT_TIME_STAMP)); + Dirent->CreationMSec = 0; + } + } + } + + if (ZeroAndSetTimeFields) { + + // + // We only touch the other timestamps if we are initializing the dirent + // + + if (!FatNtTimeToFatTime( IrpContext, + &Time, + TRUE, + &Dirent->LastWriteTime, + NULL )) { + + DebugTrace( 0, Dbg, "Current time invalid.\n", 0); + + RtlZeroMemory( &Dirent->LastWriteTime, sizeof(FAT_TIME_STAMP) ); + } + + if (FatData.ChicagoMode) { + + Dirent->LastAccessDate = Dirent->LastWriteTime.Date; + } + } + } + + // + // Copy the attributes + // + + Dirent->Attributes = Attributes; + + // + // Set the magic bit here, to tell dirctrl.c that this name is really + // lowercase. + // + + Dirent->NtByte = 0; + + if (ComponentReallyLowercase) { + + SetFlag( Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE ); + } + + if (ExtensionReallyLowercase) { + + SetFlag( Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE ); + } + + // + // See if we have to create an Lfn entry + // + + if (ARGUMENT_PRESENT(Lfn)) { + + UCHAR DirentChecksum; + UCHAR DirentsInLfn; + UCHAR LfnOrdinal; + PWCHAR LfnBuffer; + PLFN_DIRENT LfnDirent; + + ASSERT( FatData.ChicagoMode ); + + DirentChecksum = FatComputeLfnChecksum( Dirent ); + + LfnOrdinal = + DirentsInLfn = FAT_LFN_DIRENTS_NEEDED(Lfn); + + LfnBuffer = &Lfn->Buffer[(DirentsInLfn - 1) * 13]; + + ASSERT( DirentsInLfn <= MAX_LFN_DIRENTS ); + + for (LfnDirent = (PLFN_DIRENT)Dirent - DirentsInLfn; + LfnDirent < (PLFN_DIRENT)Dirent; + LfnDirent += 1, LfnOrdinal -= 1, LfnBuffer -= 13) { + + WCHAR FinalLfnBuffer[13]; + PWCHAR Buffer; + + // + // We need to special case the "final" dirent. + // + + if (LfnOrdinal == DirentsInLfn) { + + ULONG i; + ULONG RemainderChars; + + RemainderChars = (Lfn->Length / sizeof(WCHAR)) % 13; + + LfnDirent->Ordinal = LfnOrdinal | FAT_LAST_LONG_ENTRY; + + if (RemainderChars != 0) { + + RtlCopyMemory( &FinalLfnBuffer, + LfnBuffer, + RemainderChars * sizeof(WCHAR) ); + + for (i = RemainderChars; i < 13; i++) { + + // + // Figure out which character to use. + // + + if (i == RemainderChars) { + + FinalLfnBuffer[i] = 0x0000; + + } else { + + FinalLfnBuffer[i] = 0xffff; + } + } + + Buffer = FinalLfnBuffer; + + } else { + + Buffer = LfnBuffer; + } + + } else { + + LfnDirent->Ordinal = LfnOrdinal; + + Buffer = LfnBuffer; + } + + // + // Now fill in the name. + // + + RtlCopyMemory( &LfnDirent->Name1[0], + &Buffer[0], + 5 * sizeof(WCHAR) ); + + RtlCopyMemory( &LfnDirent->Name2[0], + &Buffer[5], + 6 * sizeof(WCHAR) ); + + RtlCopyMemory( &LfnDirent->Name3[0], + &Buffer[11], + 2 * sizeof(WCHAR) ); + + // + // And the other fields + // + + LfnDirent->Attributes = FAT_DIRENT_ATTR_LFN; + + LfnDirent->Type = 0; + + LfnDirent->Checksum = DirentChecksum; + + LfnDirent->MustBeZero = 0; + } + } + + DebugTrace(-1, Dbg, "FatConstructDirent -> (VOID)\n", 0); + return; +} + + +VOID +FatConstructLabelDirent ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN POEM_STRING Label + ) + +/*++ + +Routine Description: + + This routine modifies the fields of a dirent to be used for a label. + +Arguments: + + Dirent - Supplies the dirent being modified. + + Label - Supplies the name to store in the Dirent. This + name must not contain wildcards. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatConstructLabelDirent\n", 0); + + DebugTrace( 0, Dbg, " Dirent = %08lx\n", Dirent); + DebugTrace( 0, Dbg, " Label = %Z\n", Label); + + RtlZeroMemory( Dirent, sizeof(DIRENT) ); + + // + // We just merrily go and fill up the dirent with the fields given. + // + + RtlCopyMemory( Dirent->FileName, Label->Buffer, Label->Length ); + + // + // Pad the label with spaces, not nulls. + // + + RtlFillMemory( &Dirent->FileName[Label->Length], 11 - Label->Length, ' '); + + Dirent->LastWriteTime = FatGetCurrentFatTime( IrpContext ); + + Dirent->Attributes = FAT_DIRENT_ATTR_VOLUME_ID; + Dirent->ExtendedAttributes = 0; + Dirent->FileSize = 0; + + DebugTrace(-1, Dbg, "FatConstructLabelDirent -> (VOID)\n", 0); + return; +} + + +VOID +FatSetFileSizeInDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PULONG AlternativeFileSize OPTIONAL + ) + +/*++ + +Routine Description: + + This routine saves the file size in an fcb into its dirent. + +Arguments: + + Fcb - Supplies the Fcb being referenced + + AlternativeFileSize - If non-null we use the ULONG it points to as + the new file size. Otherwise we use the one in the Fcb. + +Return Value: + + None. + +--*/ + +{ + PDIRENT Dirent; + PBCB DirentBcb; + + PAGED_CODE(); + + ASSERT( Fcb->FcbCondition == FcbGood ); + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &DirentBcb ); + + ASSERT( Dirent && DirentBcb ); + + _SEH2_TRY { + + Dirent->FileSize = ARGUMENT_PRESENT( AlternativeFileSize ) ? + *AlternativeFileSize : Fcb->Header.FileSize.LowPart; + + FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, DirentBcb ); + } _SEH2_END; +} + + +VOID +FatUpdateDirentFromFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PFCB FcbOrDcb, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine modifies an objects directory entry based on the hints + that have been built up over previous operations on a handle. Notify + change filters are built and fired as a result of these updates. + +Arguments: + + FileObject - Fileobject representing the handle involved + + FcbOrDcb - File/Dir involved + + Ccb - User context involved + +Return Value: + + None. + +--*/ + +{ + BOOLEAN SetArchiveBit; + + BOOLEAN UpdateFileSize; + BOOLEAN UpdateLastWriteTime; + BOOLEAN UpdateLastAccessTime; + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + ULONG NotifyFilter = 0; + FAT_TIME_STAMP CurrentFatTime; + + LARGE_INTEGER CurrentTime; + LARGE_INTEGER CurrentDay; + LARGE_INTEGER LastAccessDay; + + PAGED_CODE(); + + // + // Nothing to do if the fcb is bad, volume is readonly or we got the + // root dir. + // + + if (FcbOrDcb->FcbCondition != FcbGood || + NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB || + FlagOn(FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + return; + } + + // + // Check if we should be changing the time or file size and set + // the archive bit on the file. + // + + KeQuerySystemTime( &CurrentTime ); + + // + // Note that we HAVE to use BooleanFlagOn() here because + // FO_FILE_SIZE_CHANGED > 0x80 (i.e., not in the first byte). + // + + SetArchiveBit = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED); + + UpdateLastWriteTime = FlagOn(FileObject->Flags, FO_FILE_MODIFIED) && + !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE); + + UpdateFileSize = NodeType(FcbOrDcb) == FAT_NTC_FCB && + BooleanFlagOn(FileObject->Flags, FO_FILE_SIZE_CHANGED); + + // + // Do one further check here of access time. Only update it if + // the current version is at least one day old. We know that + // the current FcbOrDcb->LastAccessTime corresponds to 12 midnight local + // time, so just see if the current time is on the same day. + // + + if (FatData.ChicagoMode && + (UpdateLastWriteTime || + FlagOn(FileObject->Flags, FO_FILE_FAST_IO_READ)) && + !FlagOn(Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS)) { + + ExSystemTimeToLocalTime( &FcbOrDcb->LastAccessTime, &LastAccessDay ); + ExSystemTimeToLocalTime( &CurrentTime, &CurrentDay ); + + LastAccessDay.QuadPart /= FatOneDay.QuadPart; + CurrentDay.QuadPart /= FatOneDay.QuadPart; + + if (LastAccessDay.LowPart != CurrentDay.LowPart) { + + UpdateLastAccessTime = TRUE; + + } else { + + UpdateLastAccessTime = FALSE; + } + + } else { + + UpdateLastAccessTime = FALSE; + } + + if (SetArchiveBit || + UpdateFileSize || + UpdateLastWriteTime || + UpdateLastAccessTime) { + + DebugTrace(0, Dbg, "Update Time and/or file size on File/Dir\n", 0); + + _SEH2_TRY { + + _SEH2_TRY { + + // + // Get the dirent + // + + FatGetDirentFromFcbOrDcb( IrpContext, + FcbOrDcb, + &Dirent, + &DirentBcb ); + + ASSERT( Dirent && DirentBcb ); + + if (UpdateLastWriteTime || UpdateLastAccessTime) { + + (VOID)FatNtTimeToFatTime( IrpContext, + &CurrentTime, + TRUE, + &CurrentFatTime, + NULL ); + } + + if (SetArchiveBit) { + + Dirent->Attributes |= FILE_ATTRIBUTE_ARCHIVE; + FcbOrDcb->DirentFatFlags |= FILE_ATTRIBUTE_ARCHIVE; + + NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; + } + + if (UpdateLastWriteTime) { + + // + // Update its time of last write + // + + FcbOrDcb->LastWriteTime = CurrentTime; + Dirent->LastWriteTime = CurrentFatTime; + + // + // We call the notify package to report that the + // last modification time has changed. + // + + NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; + } + + if (UpdateLastAccessTime) { + + // + // Now we have to truncate the local time down + // to the current day, then convert back to UTC. + // + + FcbOrDcb->LastAccessTime.QuadPart = + CurrentDay.QuadPart * FatOneDay.QuadPart; + + ExLocalTimeToSystemTime( &FcbOrDcb->LastAccessTime, + &FcbOrDcb->LastAccessTime ); + + Dirent->LastAccessDate = CurrentFatTime.Date; + + // + // We call the notify package to report that the + // last access time has changed. + // + + NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; + } + + if (UpdateFileSize) { + + // + // Perhaps we were called to make certain that the + // filesize on disc was updated - don't bother updating + // and firing the filter if nothing changed. + // + + ASSERT( NodeType(FcbOrDcb) == FAT_NTC_FCB ); + + if (Dirent->FileSize != FcbOrDcb->Header.FileSize.LowPart) { + + // + // Update the dirent file size + // + + Dirent->FileSize = FcbOrDcb->Header.FileSize.LowPart; + + // + // We call the notify package to report that the + // size has changed. + // + + NotifyFilter |= FILE_NOTIFY_CHANGE_SIZE; + } + } + + FatNotifyReportChange( IrpContext, + FcbOrDcb->Vcb, + FcbOrDcb, + NotifyFilter, + FILE_ACTION_MODIFIED ); + + // + // If all we did was update last access time, + // don't mark the volume dirty. + // + + FatSetDirtyBcb( IrpContext, + DirentBcb, + NotifyFilter == FILE_NOTIFY_CHANGE_LAST_ACCESS ? + NULL : FcbOrDcb->Vcb, + TRUE ); + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, DirentBcb ); + } _SEH2_END; + } +} + + +// +// Internal support routine +// + +UCHAR +FatComputeLfnChecksum ( + PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine computes the Chicago long file name checksum. + +Arguments: + + Dirent - Specifies the dirent that we are to compute a checksum for. + +Return Value: + + The checksum. + +--*/ + +{ + ULONG i; + UCHAR Checksum; + + PAGED_CODE(); + + Checksum = Dirent->FileName[0]; + + for (i=1; i < 11; i++) { + + Checksum = ((Checksum & 1) ? 0x80 : 0) + + (Checksum >> 1) + + Dirent->FileName[i]; + } + + return Checksum; +} + + + +#if 0 // It turns out Win95 is still creating short names without a ~ + +// +// Internal support routine +// + +BOOLEAN +FatIsLfnPairValid ( + PWCHAR Lfn, + ULONG LfnSize, + PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine does a few more checks to make sure that a LFN/short + name pairing is legitimate. Basically this is the test: + + Pairing is valid if: + + DIRENT has a ~ character || + (LFN is 8.3 compliant && + (LFN has extended character(s) ? TRUE : + LFN upcases to DIRENT)) + + When checking for the presence of a tilda character in the short + name, note that we purposely do a single byte search instead of + converting the name to UNICODE and looking there for the tilda. + This protects us from accidently missing the tilda if the + preceding byte is a lead byte in the current Oem code page, + but wasn't in the Oem code page that created the file. + + Also note that if the LFN is longer than 12 characters, then the + second clause of the OR must be false. + +Arguments: + + Lfn - Points to a buffer of UNICODE chars. + + LfnSize - This is the size of the LFN in characters. + + Dirent - Specifies the dirent we are to consider. + +Return Value: + + TRUE if the Lfn/DIRENT form a legitimate pair, FALSE otherwise. + +--*/ + +{ + ULONG i; + BOOLEAN ExtendedChars; + ULONG DirentBuffer[3]; + PUCHAR DirentName; + ULONG DirentIndex; + BOOLEAN DotEncountered; + + // + // First, look for a tilda + // + + for (i=0; i<11; i++) { + if (Dirent->FileName[i] == '~') { + return TRUE; + } + } + + // + // No tilda. If the LFN is longer than 12 characters, then it can + // neither upcase to the DIRENT nor be 8.3 complient. + // + + if (LfnSize > 12) { + return FALSE; + } + + // + // Now see if the name is 8.3, and build an upcased DIRENT as well. + // + + DirentBuffer[0] = 0x20202020; + DirentBuffer[1] = 0x20202020; + DirentBuffer[2] = 0x20202020; + + DirentName = (PUCHAR)DirentBuffer; + + ExtendedChars = FALSE; + DirentIndex = 0; + DotEncountered = FALSE; + + for (i=0; i < LfnSize; i++) { + + // + // Do dot transition work + // + + if (Lfn[i] == L'.') { + if (DotEncountered || + (i > 8) || + ((LfnSize - i) > 4) || + (i && Lfn[i-1] == L' ')) { + return FALSE; + } + DotEncountered = TRUE; + DirentIndex = 8; + continue; + } + + // + // The character must be legal in order to be 8.3 + // + + if ((Lfn[i] < 0x80) && + !FsRtlIsAnsiCharacterLegalFat((UCHAR)Lfn[i], FALSE)) { + return FALSE; + } + + // + // If the name contains no extended chars, continue building DIRENT + // + + if (!ExtendedChars) { + if (Lfn[i] > 0x7f) { + ExtendedChars = TRUE; + } else { + DirentName[DirentIndex++] = (UCHAR) ( + Lfn[i] < 'a' ? Lfn[i] : Lfn[i] <= 'z' ? Lfn[i] - ('a' - 'A') : Lfn[i]); + } + } + } + + // + // If the LFN ended in a space, or there was no dot and the name + // has more than 8 characters, then it is not 8.3 compliant. + // + + if ((Lfn[LfnSize - 1] == L' ') || + (!DotEncountered && (LfnSize > 8))) { + return FALSE; + } + + // + // OK, now if we got this far then the LFN is 8dot3. If there are + // no extended characters, then we can also check to make sure that + // the LFN is only a case varient of the DIRENT. + // + + if (!ExtendedChars && + !RtlEqualMemory(Dirent->FileName, DirentName, 11)) { + + return FALSE; + } + + // + // We have now verified this pairing the very best we can without + // knowledge of the code page that the file was created under. + // + + return TRUE; +} +#endif //0 + +// +// Internal support routine +// + +VOID +FatRescanDirectory ( + PIRP_CONTEXT IrpContext, + PDCB Dcb + ) + +/*++ + +Routine Description: + + This routine rescans the given directory, finding the first unused + dirent, first deleted dirent, and setting the free dirent bitmap + appropriately. + +Arguments: + + Dcb - Supplies the directory to rescan. + +Return Value: + + None. + +--*/ + +{ + PBCB Bcb = NULL; + PDIRENT Dirent; + NTSTATUS Status; + + ULONG UnusedVbo; + ULONG DeletedHint; + ULONG DirentIndex; + ULONG DirentsThisRun; + ULONG StartIndexOfThisRun; + + enum RunType { + InitialRun, + FreeDirents, + AllocatedDirents, + } CurrentRun; + + PAGED_CODE(); + + DebugTrace( 0, Dbg, "We must scan the whole directory.\n", 0); + + UnusedVbo = 0; + DeletedHint = 0xffffffff; + + // + // To start with, we have to find out if the first dirent is free. + // + + CurrentRun = InitialRun; + DirentIndex = + StartIndexOfThisRun = 0; + + _SEH2_TRY { + + while ( TRUE ) { + + BOOLEAN DirentDeleted; + + // + // Read a dirent + // + + FatReadDirent( IrpContext, + Dcb, + UnusedVbo, + &Bcb, + &Dirent, + &Status ); + + // + // If EOF, or we found a NEVER_USED entry, we exit the loop + // + + if ( (Status == STATUS_END_OF_FILE ) || + (Dirent->FileName[0] == FAT_DIRENT_NEVER_USED)) { + + break; + } + + // + // If the dirent is DELETED, and it is the first one we found, set + // it in the deleted hint. + // + + if (Dirent->FileName[0] == FAT_DIRENT_DELETED) { + + DirentDeleted = TRUE; + + if (DeletedHint == 0xffffffff) { + + DeletedHint = UnusedVbo; + } + + } else { + + DirentDeleted = FALSE; + } + + // + // Check for the first time through the loop, and determine + // the current run type. + // + + if (CurrentRun == InitialRun) { + + CurrentRun = DirentDeleted ? + FreeDirents : AllocatedDirents; + + } else { + + // + // Are we switching from a free run to an allocated run? + // + + if ((CurrentRun == FreeDirents) && !DirentDeleted) { + + DirentsThisRun = DirentIndex - StartIndexOfThisRun; + + RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun ); + + CurrentRun = AllocatedDirents; + StartIndexOfThisRun = DirentIndex; + } + + // + // Are we switching from an allocated run to a free run? + // + + if ((CurrentRun == AllocatedDirents) && DirentDeleted) { + + DirentsThisRun = DirentIndex - StartIndexOfThisRun; + + RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun ); + + CurrentRun = FreeDirents; + StartIndexOfThisRun = DirentIndex; + } + } + + // + // Move on to the next dirent. + // + + UnusedVbo += sizeof(DIRENT); + Dirent += 1; + DirentIndex += 1; + } + + // + // Now we have to record the final run we encoutered + // + + DirentsThisRun = DirentIndex - StartIndexOfThisRun; + + if ((CurrentRun == FreeDirents) || (CurrentRun == InitialRun)) { + + RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun ); + + } else { + + RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun ); + } + + // + // Now if there we bailed prematurely out of the loop because + // we hit an unused entry, set all the rest as free. + // + + if (UnusedVbo < Dcb->Header.AllocationSize.LowPart) { + + StartIndexOfThisRun = UnusedVbo / sizeof(DIRENT); + + DirentsThisRun = (Dcb->Header.AllocationSize.LowPart - + UnusedVbo) / sizeof(DIRENT); + + RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + StartIndexOfThisRun, + DirentsThisRun); + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, Bcb ); + } _SEH2_END; + + // + // If there weren't any DELETED entries, set the index to our current + // position. + // + + if (DeletedHint == 0xffffffff) { DeletedHint = UnusedVbo; } + + Dcb->Specific.Dcb.UnusedDirentVbo = UnusedVbo; + Dcb->Specific.Dcb.DeletedDirentHint = DeletedHint; + + return; +} + + +// +// Internal support routine +// + +ULONG +FatDefragDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN ULONG DirentsNeeded + ) + +/*++ + +Routine Description: + + This routine determines if the requested number of dirents can be found + in the directory, looking for deleted dirents and orphaned LFNs. If the + request can be satisifed, orphaned LFNs are marked as deleted, and deleted + dirents are all grouped together at the end of the directory. + + Note that this routine is currently used only on the root directory, but + it is completely general and could be used on any directory. + +Arguments: + + Dcb - Supplies the directory to defrag. + +Return Value: + + The Index of the first dirent available for use, or -1 if the + request cannot be satisfied. + +--*/ + +{ + ULONG SavedIrpContextFlag; + PLIST_ENTRY Links; + ULONG ReturnValue; + PFCB Fcb; + + PBCB Bcb = NULL; + PDIRENT Dirent = NULL; + UNICODE_STRING Lfn = {0,0,NULL}; + + LARGE_MCB Mcb; + BOOLEAN McbInitialized = FALSE; + BOOLEAN InvalidateFcbs = FALSE; + + PUCHAR Directory; + PUCHAR UnusedDirents; + PUCHAR UnusedDirentBuffer = NULL; + PUCHAR UsedDirents; + PUCHAR UsedDirentBuffer = NULL; + + PBCB *Bcbs = NULL; + ULONG Page; + ULONG PagesPinned; + + ULONG DcbSize; + ULONG TotalBytesAllocated = 0; + + PAGED_CODE(); + + // + // We assume we own the Vcb. + // + + ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) ); + + // + // We will only attempt this on directories less than 0x40000 bytes + // long (by default on DOS the root directory is only 0x2000 long). + // This is to avoid a cache manager complication. + // + + DcbSize = Dcb->Header.AllocationSize.LowPart; + + if (DcbSize > 0x40000) { + + return (ULONG)-1; + } + + // + // Force wait to TRUE + // + + SavedIrpContextFlag = IrpContext->Flags; + + SetFlag( IrpContext->Flags, + IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_WRITE_THROUGH ); + + // + // Now acquire all open Fcbs in the Dcb exclusive. + // + + for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Dcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.Resource, TRUE ); + } + + _SEH2_TRY { + + CCB Ccb; + ULONG QueryOffset = 0; + ULONG FoundOffset = 0; + ULONGLONG BytesUsed = 0; + + NTSTATUS DontCare; + ULONG Run; + ULONG TotalRuns; + BOOLEAN Result; + PUCHAR Char; + + // + // We are going to build a new bitmap that will show all orphaned + // LFNs as well as deleted dirents as available. + // + // Initialize our local CCB that will match all files and even + // a label if it is here. + // + + RtlZeroMemory( &Ccb, sizeof(CCB) ); + Ccb.Flags = CCB_FLAG_MATCH_ALL | CCB_FLAG_MATCH_VOLUME_ID; + + // + // Init the Long File Name string. + // + + Lfn.MaximumLength = 260 * sizeof(WCHAR); + Lfn.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + 260*sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + + // + // Initalize the Mcb. We use this structure to keep track of runs + // of free and allocated dirents. Runs are identity allocations, and + // holes are free dirents. + // + + FsRtlInitializeLargeMcb( &Mcb, PagedPool ); + + McbInitialized = TRUE; + + do { + + FatLocateDirent( IrpContext, + Dcb, + &Ccb, + QueryOffset, + &Dirent, + &Bcb, +#ifndef __REACTOS__ + &FoundOffset, +#else + (PVBO)&FoundOffset, +#endif + NULL, + &Lfn); + + if (Dirent != NULL) { + + ULONG LfnByteOffset; + + // + // Compute the LfnByteOffset. + // + + LfnByteOffset = FoundOffset - + FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT); + + BytesUsed = FoundOffset - LfnByteOffset + sizeof(DIRENT); + + // + // Set a run to represent all the dirents used for this + // file in the Dcb dir. + // + + Result = FsRtlAddLargeMcbEntry( &Mcb, + LfnByteOffset, + LfnByteOffset, + BytesUsed ); + + ASSERT( Result ); + + // + // Move on to the next dirent. + // + + TotalBytesAllocated += (ULONG) BytesUsed; + QueryOffset = FoundOffset + sizeof(DIRENT); + } + + } while ((Dirent != NULL) && (QueryOffset < DcbSize)); + + if (Bcb != NULL) { + + FatUnpinBcb( IrpContext, Bcb ); + } + + // + // If we need more dirents than are available, bail. + // + + if (DirentsNeeded > (DcbSize - TotalBytesAllocated)/sizeof(DIRENT)) { + + try_return(ReturnValue = (ULONG)-1); + } + + // + // Now we are going to copy all the used and un-used parts of the + // directory to separate pool. + // + // Allocate these buffers and pin the entire directory. + // + + UnusedDirents = + UnusedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool, + DcbSize - TotalBytesAllocated, + TAG_DIRENT ); + + UsedDirents = + UsedDirentBuffer = FsRtlAllocatePoolWithTag( PagedPool, + TotalBytesAllocated, + TAG_DIRENT ); + + PagesPinned = (DcbSize + (PAGE_SIZE - 1 )) / PAGE_SIZE; + + Bcbs = FsRtlAllocatePoolWithTag( PagedPool, + PagesPinned * sizeof(PBCB), + TAG_BCB ); + + RtlZeroMemory( Bcbs, PagesPinned * sizeof(PBCB) ); + + for (Page = 0; Page < PagesPinned; Page += 1) { + + ULONG PinSize; + + // + // Don't try to pin beyond the Dcb size. + // + + if ((Page + 1) * PAGE_SIZE > DcbSize) { + + PinSize = DcbSize - (Page * PAGE_SIZE); + + } else { + + PinSize = PAGE_SIZE; + } + + FatPrepareWriteDirectoryFile( IrpContext, + Dcb, + Page * PAGE_SIZE, + PinSize, + &Bcbs[Page], +#ifndef __REACTOS__ + &Dirent, +#else + (PVOID *)&Dirent, +#endif + FALSE, + TRUE, + &DontCare ); + + if (Page == 0) { + Directory = (PUCHAR)Dirent; + } + } + + TotalRuns = FsRtlNumberOfRunsInLargeMcb( &Mcb ); + + for (Run = 0; Run < TotalRuns; Run++) { + + LBO Vbo; + LBO Lbo; + + Result = FsRtlGetNextLargeMcbEntry( &Mcb, + Run, + &Vbo, + &Lbo, +#ifndef __REACTOS__ + &BytesUsed ); +#else + (PLONGLONG)&BytesUsed ); +#endif + + ASSERT(Result); + + // + // Copy each run to their specific pool. + // + + if (Lbo != -1) { + + RtlCopyMemory( UsedDirents, + Directory + Vbo, + (ULONG) BytesUsed ); + + UsedDirents += BytesUsed; + + } else { + + RtlCopyMemory( UnusedDirents, + Directory + Vbo, + (ULONG) BytesUsed ); + + UnusedDirents += BytesUsed; + } + } + + // + // Marking all the un-used dirents as "deleted". This will reclaim + // storage used by orphaned LFNs. + // + + for (Char = UnusedDirentBuffer; Char < UnusedDirents; Char += sizeof(DIRENT)) { + + *Char = FAT_DIRENT_DELETED; + } + + // + // Now, for the permanent step. Copy the two pool buffer back to the + // real Dcb directory, and flush the Dcb directory + // + + ASSERT( TotalBytesAllocated == (ULONG)(UsedDirents - UsedDirentBuffer) ); + + RtlCopyMemory( Directory, UsedDirentBuffer, TotalBytesAllocated ); + + RtlCopyMemory( Directory + TotalBytesAllocated, + UnusedDirentBuffer, + UnusedDirents - UnusedDirentBuffer ); + + // + // We need to unpin here so that the UnpinRepinned won't deadlock. + // + + if (Bcbs) { + for (Page = 0; Page < PagesPinned; Page += 1) { + FatUnpinBcb( IrpContext, Bcbs[Page] ); + } + ExFreePool(Bcbs); + Bcbs = NULL; + } + + // + // Now make the free dirent bitmap reflect the new state of the Dcb + // directory. + // + + RtlSetBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + 0, + TotalBytesAllocated / sizeof(DIRENT) ); + + RtlClearBits( &Dcb->Specific.Dcb.FreeDirentBitmap, + TotalBytesAllocated / sizeof(DIRENT), + (DcbSize - TotalBytesAllocated) / sizeof(DIRENT) ); + + ReturnValue = TotalBytesAllocated / sizeof(DIRENT); + + // + // Flush the directory to disk. If we raise, we will need to invalidate + // all of the children. Sorry, guys, but I can't figure out where you are + // now - if this failed I probably can't read the media either. And we + // probably purged the cache to boot. + // + + _SEH2_TRY { + + FatUnpinRepinnedBcbs( IrpContext ); + + } _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + + InvalidateFcbs = TRUE; + } _SEH2_END; + + // + // OK, now nothing can go wrong. We have two more things to do. + // First, we have to fix up all the dirent offsets in any open Fcbs. + // If we cannot now find the Fcb, the file is marked invalid. Also, + // we skip deleted files. + // + + for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Dcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + PBCB TmpBcb = NULL; + ULONG TmpOffset; + PDIRENT TmpDirent = NULL; + ULONG PreviousLfnSpread; + + Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + if (IsFileDeleted( IrpContext, Fcb )) { + + continue; + } + + // + // If we aren't already giving up, safely try to pick up the dirent + // to update the Fcb. If this raises, we have to give up and blow + // evenyone else away too. + // + + if (!InvalidateFcbs) { + + _SEH2_TRY { + + FatLocateSimpleOemDirent( IrpContext, + Dcb, + &Fcb->ShortName.Name.Oem, + &TmpDirent, + &TmpBcb, +#ifndef __REACTOS__ + &TmpOffset ); +#else + (PVBO)&TmpOffset ); +#endif + + } _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + + InvalidateFcbs = TRUE; + } _SEH2_END; + } + + if (TmpBcb == NULL || InvalidateFcbs) { + + FatUnpinBcb( IrpContext, TmpBcb ); + FatMarkFcbCondition( IrpContext, Fcb, FcbBad, TRUE ); + + } else { + + FatUnpinBcb( IrpContext, TmpBcb ); + + PreviousLfnSpread = Fcb->DirentOffsetWithinDirectory - + Fcb->LfnOffsetWithinDirectory; + + Fcb->DirentOffsetWithinDirectory = TmpOffset; + Fcb->LfnOffsetWithinDirectory = TmpOffset - PreviousLfnSpread; + } + } + + try_exit: NOTHING; + } _SEH2_FINALLY { + + // + // Free all our resources and stuff. + // + + if (McbInitialized) { + FsRtlUninitializeLargeMcb( &Mcb ); + } + + if (Lfn.Buffer) { + ExFreePool( Lfn.Buffer ); + } + + if (UnusedDirentBuffer) { + ExFreePool( UnusedDirentBuffer ); + } + + if (UsedDirentBuffer) { + ExFreePool( UsedDirentBuffer ); + } + + if (Bcbs) { + for (Page = 0; Page < PagesPinned; Page += 1) { + FatUnpinBcb( IrpContext, Bcbs[Page] ); + } + ExFreePool(Bcbs); + } + + FatUnpinBcb( IrpContext, Bcb ); + + for (Links = Dcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Dcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + ExReleaseResourceLite( Fcb->Header.Resource ); + } + + IrpContext->Flags = SavedIrpContextFlag; + } _SEH2_END; + + // + // Now return the offset of the first free dirent to the caller. + // + + return ReturnValue; +} + + + diff --git a/drivers/filesystems/fastfat_new/dumpsup.c b/drivers/filesystems/fastfat_new/dumpsup.c new file mode 100644 index 00000000000..02398e86f59 --- /dev/null +++ b/drivers/filesystems/fastfat_new/dumpsup.c @@ -0,0 +1,381 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + DumpSup.c + +Abstract: + + This module implements a collection of data structure dump routines + for debugging the Fat file system + + +--*/ + +#include "fatprocs.h" + +#ifdef FASTFATDBG + +VOID FatDump(IN PVOID Ptr); + +VOID FatDumpDataHeader(); +VOID FatDumpVcb(IN PVCB Ptr); +VOID FatDumpFcb(IN PFCB Ptr); +VOID FatDumpCcb(IN PCCB Ptr); + +ULONG FatDumpCurrentColumn; + +#define DumpNewLine() { \ + DbgPrint("\n"); \ + FatDumpCurrentColumn = 1; \ +} + +#define DumpLabel(Label,Width) { \ + ULONG i, LastPeriod=0; \ + CHAR _Str[20]; \ + for(i=0;i<2;i++) { _Str[i] = UCHAR_SP;} \ + for(i=0;i 80) {DumpNewLine();} \ + FatDumpCurrentColumn += 18 + 9 + 9; \ + DumpLabel(Field,18); \ + DbgPrint(":%8lx", Ptr->Field); \ + DbgPrint(" "); \ +} + +#define DumpListEntry(Links) { \ + if ((FatDumpCurrentColumn + 18 + 9 + 9) > 80) {DumpNewLine();} \ + FatDumpCurrentColumn += 18 + 9 + 9; \ + DumpLabel(Links,18); \ + DbgPrint(":%8lx", Ptr->Links.Flink); \ + DbgPrint(":%8lx", Ptr->Links.Blink); \ +} + +#define DumpName(Field,Width) { \ + ULONG i; \ + CHAR _String[256]; \ + if ((FatDumpCurrentColumn + 18 + Width) > 80) {DumpNewLine();} \ + FatDumpCurrentColumn += 18 + Width; \ + DumpLabel(Field,18); \ + for(i=0;iField[i];} \ + _String[Width] = '\0'; \ + DbgPrint("%s", _String); \ +} + +#define TestForNull(Name) { \ + if (Ptr == NULL) { \ + DbgPrint("%s - Cannot dump a NULL pointer\n", Name); \ + return; \ + } \ +} + + +VOID +FatDump ( + IN PVOID Ptr + ) + +/*++ + +Routine Description: + + This routine determines the type of internal record reference by ptr and + calls the appropriate dump routine. + +Arguments: + + Ptr - Supplies the pointer to the record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull("FatDump"); + + switch (NodeType(Ptr)) { + + case FAT_NTC_DATA_HEADER: + + FatDumpDataHeader(); + break; + + case FAT_NTC_VCB: + + FatDumpVcb(Ptr); + break; + + case FAT_NTC_FCB: + case FAT_NTC_DCB: + case FAT_NTC_ROOT_DCB: + + FatDumpFcb(Ptr); + break; + + case FAT_NTC_CCB: + + FatDumpCcb(Ptr); + break; + + default : + + DbgPrint("FatDump - Unknown Node type code %8lx\n", *((PNODE_TYPE_CODE)(Ptr))); + break; + } + + return; +} + + +VOID +FatDumpDataHeader ( + ) + +/*++ + +Routine Description: + + Dump the top data structures and all Device structures + +Arguments: + + None + +Return Value: + + None + +--*/ + +{ + PFAT_DATA Ptr; + PLIST_ENTRY Links; + + Ptr = &FatData; + + TestForNull("FatDumpDataHeader"); + + DumpNewLine(); + DbgPrint("FatData@ %lx", (Ptr)); + DumpNewLine(); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpListEntry (VcbQueue); + DumpField (DriverObject); + DumpField (OurProcess); + DumpNewLine(); + + for (Links = Ptr->VcbQueue.Flink; + Links != &Ptr->VcbQueue; + Links = Links->Flink) { + + FatDumpVcb(CONTAINING_RECORD(Links, VCB, VcbLinks)); + } + + return; +} + + +VOID +FatDumpVcb ( + IN PVCB Ptr + ) + +/*++ + +Routine Description: + + Dump an Device structure, its Fcb queue amd direct access queue. + +Arguments: + + Ptr - Supplies the Device record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull("FatDumpVcb"); + + DumpNewLine(); + DbgPrint("Vcb@ %lx", (Ptr)); + DumpNewLine(); + + DumpField (VolumeFileHeader.NodeTypeCode); + DumpField (VolumeFileHeader.NodeByteSize); + DumpListEntry (VcbLinks); + DumpField (TargetDeviceObject); + DumpField (Vpb); + DumpField (VcbState); + DumpField (VcbCondition); + DumpField (RootDcb); + DumpField (DirectAccessOpenCount); + DumpField (OpenFileCount); + DumpField (ReadOnlyCount); + DumpField (AllocationSupport); + DumpField (AllocationSupport.RootDirectoryLbo); + DumpField (AllocationSupport.RootDirectorySize); + DumpField (AllocationSupport.FileAreaLbo); + DumpField (AllocationSupport.NumberOfClusters); + DumpField (AllocationSupport.NumberOfFreeClusters); + DumpField (AllocationSupport.FatIndexBitSize); + DumpField (AllocationSupport.LogOfBytesPerSector); + DumpField (AllocationSupport.LogOfBytesPerCluster); + DumpField (DirtyFatMcb); + DumpField (FreeClusterBitMap); + DumpField (VirtualVolumeFile); + DumpField (SectionObjectPointers.DataSectionObject); + DumpField (SectionObjectPointers.SharedCacheMap); + DumpField (SectionObjectPointers.ImageSectionObject); + DumpField (ClusterHint); + DumpNewLine(); + + FatDumpFcb(Ptr->RootDcb); + + return; +} + + +VOID +FatDumpFcb ( + IN PFCB Ptr + ) + +/*++ + +Routine Description: + + Dump an Fcb structure, its various queues + +Arguments: + + Ptr - Supplies the Fcb record to be dumped + +Return Value: + + None + +--*/ + +{ + PLIST_ENTRY Links; + + TestForNull("FatDumpFcb"); + + DumpNewLine(); + if (NodeType(&Ptr->Header) == FAT_NTC_FCB) {DbgPrint("Fcb@ %lx", (Ptr));} + else if (NodeType(&Ptr->Header) == FAT_NTC_DCB) {DbgPrint("Dcb@ %lx", (Ptr));} + else if (NodeType(&Ptr->Header) == FAT_NTC_ROOT_DCB) {DbgPrint("RootDcb@ %lx", (Ptr));} + else {DbgPrint("NonFcb NodeType @ %lx", (Ptr));} + DumpNewLine(); + + DumpField (Header.NodeTypeCode); + DumpField (Header.NodeByteSize); + DumpListEntry (ParentDcbLinks); + DumpField (ParentDcb); + DumpField (Vcb); + DumpField (FcbState); + DumpField (FcbCondition); + DumpField (UncleanCount); + DumpField (OpenCount); + DumpField (DirentOffsetWithinDirectory); + DumpField (DirentFatFlags); + DumpField (FullFileName.Length); + DumpField (FullFileName.Buffer); + DumpName (FullFileName.Buffer, 32); + DumpField (ShortName.Name.Oem.Length); + DumpField (ShortName.Name.Oem.Buffer); + DumpField (NonPaged); + DumpField (Header.AllocationSize.LowPart); + DumpField (NonPaged->SectionObjectPointers.DataSectionObject); + DumpField (NonPaged->SectionObjectPointers.SharedCacheMap); + DumpField (NonPaged->SectionObjectPointers.ImageSectionObject); + + if ((Ptr->Header.NodeTypeCode == FAT_NTC_DCB) || + (Ptr->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) { + + DumpListEntry (Specific.Dcb.ParentDcbQueue); + DumpField (Specific.Dcb.DirectoryFileOpenCount); + DumpField (Specific.Dcb.DirectoryFile); + + } else if (Ptr->Header.NodeTypeCode == FAT_NTC_FCB) { + + DumpField (Header.FileSize.LowPart); + + } else { + + DumpNewLine(); + DbgPrint("Illegal Node type code"); + + } + DumpNewLine(); + + if ((Ptr->Header.NodeTypeCode == FAT_NTC_DCB) || + (Ptr->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) { + + for (Links = Ptr->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Ptr->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + FatDumpFcb(CONTAINING_RECORD(Links, FCB, ParentDcbLinks)); + } + } + + return; +} + + +VOID +FatDumpCcb ( + IN PCCB Ptr + ) + +/*++ + +Routine Description: + + Dump a Ccb structure + +Arguments: + + Ptr - Supplies the Ccb record to be dumped + +Return Value: + + None + +--*/ + +{ + TestForNull("FatDumpCcb"); + + DumpNewLine(); + DbgPrint("Ccb@ %lx", (Ptr)); + DumpNewLine(); + + DumpField (NodeTypeCode); + DumpField (NodeByteSize); + DumpField (UnicodeQueryTemplate.Length); + DumpName (UnicodeQueryTemplate.Buffer, 32); + DumpField (OffsetToStartSearchFrom); + DumpNewLine(); + + return; +} + +#endif // FASTFATDBG + diff --git a/drivers/filesystems/fastfat_new/ea.c b/drivers/filesystems/fastfat_new/ea.c new file mode 100644 index 00000000000..52b12e8faaa --- /dev/null +++ b/drivers/filesystems/fastfat_new/ea.c @@ -0,0 +1,2013 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Ea.c + +Abstract: + + This module implements the EA routines for Fat called by + the dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_EA) + +// +// Local procedure prototypes +// + +IO_STATUS_BLOCK +FatQueryEaUserEaList ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN PUCHAR UserEaList, + IN ULONG UserEaListLength, + IN BOOLEAN ReturnSingleEntry + ); + +IO_STATUS_BLOCK +FatQueryEaIndexSpecified ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN ULONG UserEaIndex, + IN BOOLEAN ReturnSingleEntry + ); + +IO_STATUS_BLOCK +FatQueryEaSimpleScan ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN BOOLEAN ReturnSingleEntry, + ULONG StartOffset + ); + +BOOLEAN +FatIsDuplicateEaName ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_GET_EA_INFORMATION GetEa, + IN PUCHAR UserBuffer + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonQueryEa) +#pragma alloc_text(PAGE, FatCommonSetEa) +#pragma alloc_text(PAGE, FatFsdQueryEa) +#pragma alloc_text(PAGE, FatFsdSetEa) +#pragma alloc_text(PAGE, FatIsDuplicateEaName) +#pragma alloc_text(PAGE, FatQueryEaIndexSpecified) +#pragma alloc_text(PAGE, FatQueryEaSimpleScan) +#pragma alloc_text(PAGE, FatQueryEaUserEaList) +#endif + + +NTSTATUS +NTAPI +FatFsdQueryEa ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the Fsd part of the NtQueryEa API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being queried exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdQueryEa\n", 0); + + // + // Call the common query routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonQueryEa( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdQueryEa -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +NTAPI +FatFsdSetEa ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtSetEa API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being set exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdSetEa\n", 0); + + // + // Call the common set routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonSetEa( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdSetEa -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonQueryEa ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for querying File ea called by both + the fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + NTSTATUS Status; + + PUCHAR Buffer; + ULONG UserBufferLength; + + PUCHAR UserEaList; + ULONG UserEaListLength; + ULONG UserEaIndex; + BOOLEAN RestartScan; + BOOLEAN ReturnSingleEntry; + BOOLEAN IndexSpecified; + + PVCB Vcb; + PCCB Ccb; + + PFCB Fcb; + PDIRENT Dirent; + PBCB Bcb; + + PDIRENT EaDirent; + PBCB EaBcb; + BOOLEAN LockedEaFcb; + + PEA_SET_HEADER EaSetHeader; + EA_RANGE EaSetRange; + + USHORT ExtendedAttributes; + + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonQueryEa...\n", 0); + DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, " ->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer ); + DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.QueryEa.Length ); + DebugTrace( 0, Dbg, " ->EaList = %08lx\n", IrpSp->Parameters.QueryEa.EaList ); + DebugTrace( 0, Dbg, " ->EaListLength = %08lx\n", IrpSp->Parameters.QueryEa.EaListLength ); + DebugTrace( 0, Dbg, " ->EaIndex = %08lx\n", IrpSp->Parameters.QueryEa.EaIndex ); + DebugTrace( 0, Dbg, " ->RestartScan = %08lx\n", FlagOn(IrpSp->Flags, SL_RESTART_SCAN)); + DebugTrace( 0, Dbg, " ->ReturnSingleEntry = %08lx\n", FlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY)); + DebugTrace( 0, Dbg, " ->IndexSpecified = %08lx\n", FlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED)); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + // + // Check that the file object is associated with either a user file + // or directory open. We don't allow Ea operations on the root + // directory. + // + + { + TYPE_OF_OPEN OpenType; + + if (((OpenType = FatDecodeFileObject( IrpSp->FileObject, + &Vcb, + &Fcb, + &Ccb )) != UserFileOpen + && OpenType != UserDirectoryOpen) || + + (NodeType( Fcb )) == FAT_NTC_ROOT_DCB) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, + "FatCommonQueryEa -> %08lx\n", + STATUS_INVALID_PARAMETER); + + return STATUS_INVALID_PARAMETER; + } + } + + // + // Fat32 does not support ea's. + // + + if (FatIsFat32(Vcb)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED ); + DebugTrace(-1, Dbg, + "FatCommonQueryEa -> %08lx\n", + STATUS_EAS_NOT_SUPPORTED); + return STATUS_EAS_NOT_SUPPORTED; + } + + // + // Acquire shared access to the Fcb and enqueue the Irp if we didn't + // get access. + // + + if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) { + + DebugTrace(0, Dbg, "FatCommonQueryEa: Thread can't wait\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status ); + + return Status; + } + + FatAcquireSharedFcb( IrpContext, Fcb ); + + // + // Reference our input parameters to make things easier + // + + UserBufferLength = IrpSp->Parameters.QueryEa.Length; + UserEaList = IrpSp->Parameters.QueryEa.EaList; + UserEaListLength = IrpSp->Parameters.QueryEa.EaListLength; + UserEaIndex = IrpSp->Parameters.QueryEa.EaIndex; + RestartScan = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN); + ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY); + IndexSpecified = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED); + + // + // Initialize our local values. + // + + LockedEaFcb = FALSE; + Bcb = NULL; + EaBcb = NULL; + + Status = STATUS_SUCCESS; + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + _SEH2_TRY { + + PPACKED_EA FirstPackedEa; + ULONG PackedEasLength; + + Buffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // We verify that the Fcb is still valid. + // + + FatVerifyFcb( IrpContext, Fcb ); + + // + // We need to get the dirent for the Fcb to recover the Ea handle. + // + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb ); + + // + // Verify that the Ea file is in a consistant state. If the + // Ea modification count in the Fcb doesn't match that in + // the CCB, then the Ea file has been changed from under + // us. If we are not starting the search from the beginning + // of the Ea set, we return an error. + // + + if (UserEaList == NULL + && Ccb->OffsetOfNextEaToReturn != 0 + && !IndexSpecified + && !RestartScan + && Fcb->EaModificationCount != Ccb->EaModificationCount) { + + DebugTrace(0, Dbg, + "FatCommonQueryEa: Ea file in unknown state\n", 0); + + Status = STATUS_EA_CORRUPT_ERROR; + + try_return( Status ); + } + + // + // Show that the Ea's for this file are consistant for this + // file handle. + // + + Ccb->EaModificationCount = Fcb->EaModificationCount; + + // + // If the handle value is 0, then the file has no Eas. We dummy up + // an ea list to use below. + // + + ExtendedAttributes = Dirent->ExtendedAttributes; + + FatUnpinBcb( IrpContext, Bcb ); + + if (ExtendedAttributes == 0) { + + DebugTrace(0, Dbg, + "FatCommonQueryEa: Zero handle, no Ea's for this file\n", 0); + + FirstPackedEa = (PPACKED_EA) NULL; + + PackedEasLength = 0; + + } else { + + // + // We need to get the Ea file for this volume. If the + // operation doesn't complete due to blocking, then queue the + // Irp to the Fsp. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + FALSE ); + + LockedEaFcb = TRUE; + + // + // If the above operation completed and the Ea file did not exist, + // the disk has been corrupted. There is an existing Ea handle + // without any Ea data. + // + + if (Vcb->VirtualEaFile == NULL) { + + DebugTrace(0, Dbg, + "FatCommonQueryEa: No Ea file found when expected\n", 0); + + Status = STATUS_NO_EAS_ON_FILE; + + try_return( Status ); + } + + // + // We need to try to get the Ea set for the desired file. If + // blocking is necessary then we'll post the request to the Fsp. + // + + FatReadEaSet( IrpContext, + Vcb, + ExtendedAttributes, + &Fcb->ShortName.Name.Oem, + TRUE, + &EaSetRange ); + + EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // Find the start and length of the Eas. + // + + FirstPackedEa = (PPACKED_EA) EaSetHeader->PackedEas; + + PackedEasLength = GetcbList( EaSetHeader ) - 4; + } + + // + // Protect our access to the user buffer since IO dosn't do this + // for us in this path unless we had specified that our driver + // requires buffering for these large requests. We don't, so ... + // + + _SEH2_TRY { + + // + // Let's clear the output buffer. + // + + RtlZeroMemory( Buffer, UserBufferLength ); + + // + // We now satisfy the user's request depending on whether he + // specified an Ea name list, an Ea index or restarting the + // search. + // + + // + // The user has supplied a list of Ea names. + // + + if (UserEaList != NULL) { + + Irp->IoStatus = FatQueryEaUserEaList( IrpContext, + Ccb, + FirstPackedEa, + PackedEasLength, + Buffer, + UserBufferLength, + UserEaList, + UserEaListLength, + ReturnSingleEntry ); + + // + // The user supplied an index into the Ea list. + // + + } else if (IndexSpecified) { + + Irp->IoStatus = FatQueryEaIndexSpecified( IrpContext, + Ccb, + FirstPackedEa, + PackedEasLength, + Buffer, + UserBufferLength, + UserEaIndex, + ReturnSingleEntry ); + + // + // Else perform a simple scan, taking into account the restart + // flag and the position of the next Ea stored in the Ccb. + // + + } else { + + Irp->IoStatus = FatQueryEaSimpleScan( IrpContext, + Ccb, + FirstPackedEa, + PackedEasLength, + Buffer, + UserBufferLength, + ReturnSingleEntry, + RestartScan + ? 0 + : Ccb->OffsetOfNextEaToReturn ); + } + + } _SEH2_EXCEPT (!FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + + // + // We must have had a problem filling in the user's buffer, so fail. + // + + Irp->IoStatus.Status = _SEH2_GetExceptionCode(); + Irp->IoStatus.Information = 0; + } _SEH2_END; + + Status = Irp->IoStatus.Status; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatCommonQueryEa ); + + // + // Release the Fcb for the file object, and the Ea Fcb if + // successfully locked. + // + + FatReleaseFcb( IrpContext, Fcb ); + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + // + // Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary. + // + + FatUnpinBcb( IrpContext, Bcb ); + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonQueryEa -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + + +NTSTATUS +FatCommonSetEa ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the common Set Ea File Api called by the + the Fsd and Fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The appropriate status for the Irp + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + NTSTATUS Status; + + USHORT ExtendedAttributes; + + PUCHAR Buffer; + ULONG UserBufferLength; + + PVCB Vcb; + PCCB Ccb; + + PFCB Fcb; + PDIRENT Dirent; + PBCB Bcb = NULL; + + PDIRENT EaDirent = NULL; + PBCB EaBcb = NULL; + + PEA_SET_HEADER EaSetHeader = NULL; + + PEA_SET_HEADER PrevEaSetHeader; + PEA_SET_HEADER NewEaSetHeader; + EA_RANGE EaSetRange; + + BOOLEAN AcquiredVcb = FALSE; + BOOLEAN AcquiredFcb = FALSE; + BOOLEAN AcquiredParentDcb = FALSE; + BOOLEAN AcquiredRootDcb = FALSE; + BOOLEAN AcquiredEaFcb = FALSE; + + // + // The following booleans are used in the unwind process. + // + + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonSetEa...\n", 0); + DebugTrace( 0, Dbg, " Wait = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + DebugTrace( 0, Dbg, " Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, " ->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer ); + DebugTrace( 0, Dbg, " ->Length = %08lx\n", IrpSp->Parameters.SetEa.Length ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = 0; + + // + // Check that the file object is associated with either a user file + // or directory open. + // + + { + TYPE_OF_OPEN OpenType; + + if (((OpenType = FatDecodeFileObject( IrpSp->FileObject, + &Vcb, + &Fcb, + &Ccb )) != UserFileOpen + && OpenType != UserDirectoryOpen) || + + (NodeType( Fcb )) == FAT_NTC_ROOT_DCB) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, + "FatCommonSetEa -> %08lx\n", + STATUS_INVALID_PARAMETER); + + return STATUS_INVALID_PARAMETER; + } + } + + // + // Fat32 does not support ea's. + // + + if (FatIsFat32(Vcb)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_EAS_NOT_SUPPORTED ); + DebugTrace(-1, Dbg, + "FatCommonSetEa -> %08lx\n", + STATUS_EAS_NOT_SUPPORTED); + return STATUS_EAS_NOT_SUPPORTED; + } + + // + // Reference our input parameters to make things easier + // + + UserBufferLength = IrpSp->Parameters.SetEa.Length; + + // + // Since we ask for no outside help (direct or buffered IO), it + // is our responsibility to insulate ourselves from the + // deviousness of the user above. Now, buffer and validate the + // contents. + // + + Buffer = FatBufferUserBuffer( IrpContext, Irp, UserBufferLength ); + + // + // Check the validity of the buffer with the new eas. We really + // need to do this always since we don't know, if it was already + // buffered, that we buffered and checked it or some overlying + // filter buffered without checking. + // + + Status = IoCheckEaBufferValidity( (PFILE_FULL_EA_INFORMATION) Buffer, + UserBufferLength, + (PULONG)&Irp->IoStatus.Information ); + + if (!NT_SUCCESS( Status )) { + + FatCompleteRequest( IrpContext, Irp, Status ); + DebugTrace(-1, Dbg, + "FatCommonSetEa -> %08lx\n", + Status); + return Status; + } + + // + // Acquire exclusive access to the Fcb. If this is a write-through operation + // we will need to pick up the other possible streams that can be modified in + // this operation so that the locking order is preserved - the root directory + // (dirent addition if EA database doesn't already exist) and the parent + // directory (addition of the EA handle to the object's dirent). + // + // We are primarily synchronizing with directory enumeration here. + // + // If we cannot wait need to send things off to the fsp. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + DebugTrace(0, Dbg, "FatCommonSetEa: Set Ea must be waitable\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status ); + + return Status; + } + + // + // Set this handle as having modified the file + // + + IrpSp->FileObject->Flags |= FO_FILE_MODIFIED; + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + _SEH2_TRY { + + ULONG PackedEasLength; + BOOLEAN PreviousEas; + ULONG AllocationLength; + ULONG BytesPerCluster; + USHORT EaHandle; + + PFILE_FULL_EA_INFORMATION FullEa; + + // + // Now go pick up everything + // + + FatAcquireSharedVcb( IrpContext, Fcb->Vcb ); + AcquiredVcb = TRUE; + FatAcquireExclusiveFcb( IrpContext, Fcb ); + AcquiredFcb = TRUE; + + if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) { + + if (Fcb->ParentDcb) { + + FatAcquireExclusiveFcb( IrpContext, Fcb->ParentDcb ); + AcquiredParentDcb = TRUE; + } + + FatAcquireExclusiveFcb( IrpContext, Fcb->Vcb->RootDcb ); + AcquiredRootDcb = TRUE; + } + + // + // We verify that the Fcb is still valid. + // + + FatVerifyFcb( IrpContext, Fcb ); + + // + // We need to get the dirent for the Fcb to recover the Ea handle. + // + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb ); + + DebugTrace(0, Dbg, "FatCommonSetEa: Dirent Address -> %08lx\n", + Dirent ); + DebugTrace(0, Dbg, "FatCommonSetEa: Dirent Bcb -> %08lx\n", + Bcb); + + // + // If the handle value is 0, then the file has no Eas. In that + // case we allocate memory to hold the Eas to be added. If there + // are existing Eas for the file, then we must read from the + // file and copy the Eas. + // + + ExtendedAttributes = Dirent->ExtendedAttributes; + + FatUnpinBcb( IrpContext, Bcb ); + + if (ExtendedAttributes == 0) { + + PreviousEas = FALSE; + + DebugTrace(0, Dbg, + "FatCommonSetEa: File has no current Eas\n", 0 ); + + } else { + + PreviousEas = TRUE; + + DebugTrace(0, Dbg, "FatCommonSetEa: File has previous Eas\n", 0 ); + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + TRUE ); + + AcquiredEaFcb = TRUE; + + // + // If we didn't get the file then there is an error on + // the disk. + // + + if (Vcb->VirtualEaFile == NULL) { + + Status = STATUS_NO_EAS_ON_FILE; + try_return( Status ); + } + } + + DebugTrace(0, Dbg, "FatCommonSetEa: EaBcb -> %08lx\n", EaBcb); + + DebugTrace(0, Dbg, "FatCommonSetEa: EaDirent -> %08lx\n", EaDirent); + + // + // If the file has existing ea's, we need to read them to + // determine the size of the buffer allocation. + // + + if (PreviousEas) { + + // + // We need to try to get the Ea set for the desired file. + // + + FatReadEaSet( IrpContext, + Vcb, + ExtendedAttributes, + &Fcb->ShortName.Name.Oem, + TRUE, + &EaSetRange ); + + PrevEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // We now must allocate pool memory for our copy of the + // EaSetHeader and then copy the Ea data into it. At that + // time we can unpin the EaSet. + // + + PackedEasLength = GetcbList( PrevEaSetHeader ) - 4; + + // + // Else we will create a dummy EaSetHeader. + // + + } else { + + PackedEasLength = 0; + } + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + AllocationLength = (PackedEasLength + + SIZE_OF_EA_SET_HEADER + + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool, + AllocationLength, + TAG_EA_SET_HEADER ); + + // + // Copy the existing Eas over to pool memory. + // + + if (PreviousEas) { + + RtlCopyMemory( EaSetHeader, PrevEaSetHeader, AllocationLength ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + } else { + + RtlZeroMemory( EaSetHeader, AllocationLength ); + + RtlCopyMemory( EaSetHeader->OwnerFileName, + Fcb->ShortName.Name.Oem.Buffer, + Fcb->ShortName.Name.Oem.Length ); + } + + + AllocationLength -= SIZE_OF_EA_SET_HEADER; + + DebugTrace(0, Dbg, "FatCommonSetEa: Initial Ea set -> %08lx\n", + EaSetHeader); + + // + // At this point we have either read in the current eas for the file + // or we have initialized a new empty buffer for the eas. Now for + // each full ea in the input user buffer we do the specified operation + // on the ea + // + + for (FullEa = (PFILE_FULL_EA_INFORMATION) Buffer; + FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[UserBufferLength]; + FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ? + &Buffer[UserBufferLength] : + (PUCHAR) FullEa + FullEa->NextEntryOffset)) { + + OEM_STRING EaName; + ULONG Offset; + + EaName.MaximumLength = EaName.Length = FullEa->EaNameLength; + EaName.Buffer = &FullEa->EaName[0]; + + DebugTrace(0, Dbg, "FatCommonSetEa: Next Ea name -> %Z\n", + &EaName); + + // + // Make sure the ea name is valid + // + + if (!FatIsEaNameValid( IrpContext,EaName )) { + + Irp->IoStatus.Information = (PUCHAR)FullEa - Buffer; + Status = STATUS_INVALID_EA_NAME; + try_return( Status ); + } + + // + // Check that no invalid ea flags are set. + // + + // + // TEMPCODE We are returning STATUS_INVALID_EA_NAME + // until a more appropriate error code exists. + // + + if (FullEa->Flags != 0 + && FullEa->Flags != FILE_NEED_EA) { + + Irp->IoStatus.Information = (PUCHAR)FullEa - (PUCHAR)Buffer; + try_return( Status = STATUS_INVALID_EA_NAME ); + } + + // + // See if we can locate the ea name in the ea set + // + + if (FatLocateEaByName( IrpContext, + (PPACKED_EA) EaSetHeader->PackedEas, + PackedEasLength, + &EaName, + &Offset )) { + + DebugTrace(0, Dbg, "FatCommonSetEa: Found Ea name\n", 0); + + // + // We found the ea name so now delete the current entry, + // and if the new ea value length is not zero then we + // replace if with the new ea + // + + FatDeletePackedEa( IrpContext, + EaSetHeader, + &PackedEasLength, + Offset ); + } + + if (FullEa->EaValueLength != 0) { + + FatAppendPackedEa( IrpContext, + &EaSetHeader, + &PackedEasLength, + &AllocationLength, + FullEa, + BytesPerCluster ); + } + } + + // + // If there are any ea's not removed, we + // call 'AddEaSet' to insert them into the Fat chain. + // + + if (PackedEasLength != 0) { + +#ifndef __REACTOS__ + LARGE_INTEGER EaOffset; + + EaOffset.HighPart = 0; +#endif + + // + // If the packed eas length (plus 4 bytes) is greater + // than the maximum allowed ea size, we return an error. + // + + if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) { + + DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 ); + + try_return( Status = STATUS_EA_TOO_LARGE ); + } + + // + // We need to now read the ea file if we haven't already. + // + + if (EaDirent == NULL) { + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + TRUE, + TRUE ); + + AcquiredEaFcb = TRUE; + } + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb ); + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + FatAddEaSet( IrpContext, + Vcb, + PackedEasLength + SIZE_OF_EA_SET_HEADER, + EaBcb, + EaDirent, + &EaHandle, + &EaSetRange ); + + NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + DebugTrace(0, Dbg, "FatCommonSetEa: Adding an ea set\n", 0); + + // + // Store the length of the new Ea's into the EaSetHeader. + // This is the PackedEasLength + 4. + // + + PackedEasLength += 4; + + CopyU4char( EaSetHeader->cbList, &PackedEasLength ); + + // + // Copy all but the first four bytes of EaSetHeader into + // NewEaSetHeader. The signature and index fields have + // already been filled in. + // + + RtlCopyMemory( &NewEaSetHeader->NeedEaCount, + &EaSetHeader->NeedEaCount, + PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 ); + + FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + + } else { + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &Dirent, &Bcb ); + + EaHandle = 0; + } + + // + // Now we do a wholesale replacement of the ea for the file + // + + if (PreviousEas) { + + FatDeleteEaSet( IrpContext, + Vcb, + EaBcb, + EaDirent, + ExtendedAttributes, + &Fcb->ShortName.Name.Oem ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + } + + if (PackedEasLength != 0 ) { + + Fcb->EaModificationCount++; + } + + // + // Mark the dirent with the new ea's + // + + Dirent->ExtendedAttributes = EaHandle; + + FatSetDirtyBcb( IrpContext, Bcb, Vcb, TRUE ); + + // + // We call the notify package to report that the ea's were + // modified. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_EA, + FILE_ACTION_MODIFIED ); + + Irp->IoStatus.Information = 0; + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + // + // Unpin the dirents for the Fcb and EaFcb if necessary. + // + + FatUnpinBcb( IrpContext, Bcb ); + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinRepinnedBcbs( IrpContext ); + + } _SEH2_FINALLY { + + DebugUnwind( FatCommonSetEa ); + + // + // If this is an abnormal termination, we need to clean up + // any locked resources. + // + + if (_SEH2_AbnormalTermination()) { + + // + // Unpin the dirents for the Fcb, EaFcb and EaSetFcb if necessary. + // + + FatUnpinBcb( IrpContext, Bcb ); + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + } + + // + // Release the Fcbs/Vcb acquired. + // + + if (AcquiredEaFcb) { + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + if (AcquiredFcb) { + FatReleaseFcb( IrpContext, Fcb ); + } + + if (AcquiredParentDcb) { + FatReleaseFcb( IrpContext, Fcb->ParentDcb ); + } + + if (AcquiredRootDcb) { + FatReleaseFcb( IrpContext, Fcb->Vcb->RootDcb ); + } + + if (AcquiredVcb) { + FatReleaseVcb( IrpContext, Fcb->Vcb ); + } + + // + // Deallocate our Ea buffer. + // + + if (EaSetHeader != NULL) { + + ExFreePool( EaSetHeader ); + } + + // + // Complete the irp. + // + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonSetEa -> %08lx\n", Status); + } _SEH2_END; + + // + // And return to our caller + // + + return Status; +} + + + +// +// Local Support Routine +// + +IO_STATUS_BLOCK +FatQueryEaUserEaList ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN PUCHAR UserEaList, + IN ULONG UserEaListLength, + IN BOOLEAN ReturnSingleEntry + ) + +/*++ + +Routine Description: + + This routine is the work routine for querying EAs given an ea index + +Arguments: + + Ccb - Supplies the Ccb for the query + + FirstPackedEa - Supplies the first ea for the file being queried + + PackedEasLength - Supplies the length of the ea data + + UserBuffer - Supplies the buffer to receive the full eas + + UserBufferLength - Supplies the length, in bytes, of the user buffer + + UserEaList - Supplies the user specified ea name list + + UserEaListLength - Supplies the length, in bytes, of the user ea list + + ReturnSingleEntry - Indicates if we are to return a single entry or not + +Return Value: + + IO_STATUS_BLOCK - Receives the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + ULONG Offset; + ULONG RemainingUserBufferLength; + + PPACKED_EA PackedEa; + ULONG PackedEaSize; + + PFILE_FULL_EA_INFORMATION LastFullEa; + ULONG LastFullEaSize; + PFILE_FULL_EA_INFORMATION NextFullEa; + + PFILE_GET_EA_INFORMATION GetEa; + + BOOLEAN Overflow; + + DebugTrace(+1, Dbg, "FatQueryEaUserEaList...\n", 0); + + LastFullEa = NULL; + NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer; + RemainingUserBufferLength = UserBufferLength; + + Overflow = FALSE; + + for (GetEa = (PFILE_GET_EA_INFORMATION) &UserEaList[0]; + GetEa < (PFILE_GET_EA_INFORMATION) ((PUCHAR) UserEaList + + UserEaListLength); + GetEa = (GetEa->NextEntryOffset == 0 + ? (PFILE_GET_EA_INFORMATION) MAXUINT_PTR + : (PFILE_GET_EA_INFORMATION) ((PUCHAR) GetEa + + GetEa->NextEntryOffset))) { + + OEM_STRING Str; + OEM_STRING OutputEaName; + + DebugTrace(0, Dbg, "Top of loop, GetEa = %08lx\n", GetEa); + DebugTrace(0, Dbg, "LastFullEa = %08lx\n", LastFullEa); + DebugTrace(0, Dbg, "NextFullEa = %08lx\n", NextFullEa); + DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength); + + // + // Make a string reference to the GetEa and see if we can + // locate the ea by name + // + + Str.MaximumLength = Str.Length = GetEa->EaNameLength; + Str.Buffer = &GetEa->EaName[0]; + + // + // Check for a valid name. + // + + if (!FatIsEaNameValid( IrpContext, Str )) { + + DebugTrace(-1, Dbg, + "FatQueryEaUserEaList: Invalid Ea Name -> %Z\n", + &Str); + + Iosb.Information = (PUCHAR)GetEa - UserEaList; + Iosb.Status = STATUS_INVALID_EA_NAME; + return Iosb; + } + + // + // If this is a duplicate name, we skip to the next. + // + + if (FatIsDuplicateEaName( IrpContext, GetEa, UserEaList )) { + + DebugTrace(0, Dbg, "FatQueryEaUserEaList: Duplicate name\n", 0); + continue; + } + + if (!FatLocateEaByName( IrpContext, + FirstPackedEa, + PackedEasLength, + &Str, + &Offset )) { + + Offset = 0xffffffff; + + DebugTrace(0, Dbg, "Need to dummy up an ea\n", 0); + + // + // We were not able to locate the name therefore we must + // dummy up a entry for the query. The needed Ea size is + // the size of the name + 4 (next entry offset) + 1 (flags) + // + 1 (name length) + 2 (value length) + the name length + + // 1 (null byte). + // + + if ((ULONG)(4+1+1+2+GetEa->EaNameLength+1) + > RemainingUserBufferLength) { + + Overflow = TRUE; + break; + } + + // + // Everything is going to work fine, so copy over the name, + // set the name length and zero out the rest of the ea. + // + + NextFullEa->NextEntryOffset = 0; + NextFullEa->Flags = 0; + NextFullEa->EaNameLength = GetEa->EaNameLength; + NextFullEa->EaValueLength = 0; + RtlCopyMemory( &NextFullEa->EaName[0], + &GetEa->EaName[0], + GetEa->EaNameLength ); + + // + // Upcase the name in the buffer. + // + + OutputEaName.MaximumLength = OutputEaName.Length = Str.Length; + OutputEaName.Buffer = NextFullEa->EaName; + + FatUpcaseEaName( IrpContext, &OutputEaName, &OutputEaName ); + + NextFullEa->EaName[GetEa->EaNameLength] = 0; + + } else { + + DebugTrace(0, Dbg, "Located the ea, Offset = %08lx\n", Offset); + + // + // We were able to locate the packed ea + // Reference the packed ea + // + + PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + Offset); + SizeOfPackedEa( PackedEa, &PackedEaSize ); + + DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize); + + // + // We know that the packed ea is 4 bytes smaller than its + // equivalent full ea so we need to check the remaining + // user buffer length against the computed full ea size. + // + + if (PackedEaSize + 4 > RemainingUserBufferLength) { + + Overflow = TRUE; + break; + } + + // + // Everything is going to work fine, so copy over the packed + // ea to the full ea and zero out the next entry offset field. + // + + RtlCopyMemory( &NextFullEa->Flags, + &PackedEa->Flags, + PackedEaSize ); + + NextFullEa->NextEntryOffset = 0; + } + + // + // At this point we've copied a new full ea into the next full ea + // location. So now go back and set the set full eas entry offset + // field to be the difference between out two pointers. + // + + if (LastFullEa != NULL) { + + LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa + - (PUCHAR) LastFullEa); + } + + // + // Set the last full ea to the next full ea, compute + // where the next full should be, and decrement the remaining user + // buffer length appropriately + // + + LastFullEa = NextFullEa; + LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa )); + RemainingUserBufferLength -= LastFullEaSize; + NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa + + LastFullEaSize); + + // + // Remember the offset of the next ea in case we're asked to + // resume the iteration + // + + Ccb->OffsetOfNextEaToReturn = FatLocateNextEa( IrpContext, + FirstPackedEa, + PackedEasLength, + Offset ); + + // + // If we were to return a single entry then break out of our loop + // now + // + + if (ReturnSingleEntry) { + + break; + } + } + + // + // Now we've iterated all that can and we've exited the preceding loop + // with either all, some or no information stored in the return buffer. + // We can decide if we got everything to fit by checking the local + // Overflow variable + // + + if (Overflow) { + + Iosb.Information = 0; + Iosb.Status = STATUS_BUFFER_OVERFLOW; + + } else { + + // + // Otherwise we've been successful in returing at least one + // ea so we'll compute the number of bytes used to store the + // full ea information. The number of bytes used is the difference + // between the LastFullEa and the start of the buffer, and the + // non-aligned size of the last full ea. + // + + Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer) + + SizeOfFullEa(LastFullEa); + + Iosb.Status = STATUS_SUCCESS; + } + + DebugTrace(-1, Dbg, "FatQueryEaUserEaList -> Iosb.Status = %08lx\n", + Iosb.Status); + + return Iosb; +} + + +// +// Local Support Routine +// + +IO_STATUS_BLOCK +FatQueryEaIndexSpecified ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN ULONG UserEaIndex, + IN BOOLEAN ReturnSingleEntry + ) + +/*++ + +Routine Description: + + This routine is the work routine for querying EAs given an ea index + +Arguments: + + Ccb - Supplies the Ccb for the query + + FirstPackedEa - Supplies the first ea for the file being queried + + PackedEasLength - Supplies the length of the ea data + + UserBuffer - Supplies the buffer to receive the full eas + + UserBufferLength - Supplies the length, in bytes, of the user buffer + + UserEaIndex - Supplies the index of the first ea to return. + + RestartScan - Indicates if the first item to return is at the + beginning of the packed ea list or if we should resume our + previous iteration + +Return Value: + + IO_STATUS_BLOCK - Receives the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + ULONG i; + ULONG Offset; + + DebugTrace(+1, Dbg, "FatQueryEaIndexSpecified...\n", 0); + + // + // Zero out the information field of the iosb + // + + Iosb.Information = 0; + + // + // If the index value is zero or there are no Eas on the file, then + // the specified index can't be returned. + // + + if (UserEaIndex == 0 + || PackedEasLength == 0) { + + DebugTrace( -1, Dbg, "FatQueryEaIndexSpecified: Non-existant entry\n", 0 ); + + Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY; + + return Iosb; + } + + // + // Iterate the eas until we find the index we're after. + // + + for (i = 1, Offset = 0; + (i < UserEaIndex) && (Offset < PackedEasLength); + i += 1, Offset = FatLocateNextEa( IrpContext, + FirstPackedEa, + PackedEasLength, Offset )) { + + NOTHING; + } + + // + // Make sure the offset we're given to the ea is a real offset otherwise + // the ea doesn't exist + // + + if (Offset >= PackedEasLength) { + + // + // If we just passed the last Ea, we will return STATUS_NO_MORE_EAS. + // This is for the caller who may be enumerating the Eas. + // + + if (i == UserEaIndex) { + + Iosb.Status = STATUS_NO_MORE_EAS; + + // + // Otherwise we report that this is a bad ea index. + // + + } else { + + Iosb.Status = STATUS_NONEXISTENT_EA_ENTRY; + } + + DebugTrace(-1, Dbg, "FatQueryEaIndexSpecified -> %08lx\n", Iosb.Status); + return Iosb; + } + + // + // We now have the offset of the first Ea to return to the user. + // We simply call our EaSimpleScan routine to do the actual work. + // + + Iosb = FatQueryEaSimpleScan( IrpContext, + Ccb, + FirstPackedEa, + PackedEasLength, + UserBuffer, + UserBufferLength, + ReturnSingleEntry, + Offset ); + + DebugTrace(-1, Dbg, "FatQueryEaIndexSpecified -> %08lx\n", Iosb.Status); + + return Iosb; + +} + + +// +// Local Support Routine +// + +IO_STATUS_BLOCK +FatQueryEaSimpleScan ( + IN PIRP_CONTEXT IrpContext, + OUT PCCB Ccb, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + OUT PUCHAR UserBuffer, + IN ULONG UserBufferLength, + IN BOOLEAN ReturnSingleEntry, + ULONG StartOffset + ) + +/*++ + +Routine Description: + + This routine is the work routine for querying EAs from the beginning of + the ea list. + +Arguments: + + Ccb - Supplies the Ccb for the query + + FirstPackedEa - Supplies the first ea for the file being queried + + PackedEasLength - Supplies the length of the ea data + + UserBuffer - Supplies the buffer to receive the full eas + + UserBufferLength - Supplies the length, in bytes, of the user buffer + + ReturnSingleEntry - Indicates if we are to return a single entry or not + + StartOffset - Indicates the offset within the Ea data to return the + first block of data. + +Return Value: + + IO_STATUS_BLOCK - Receives the completion status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + + ULONG RemainingUserBufferLength; + + PPACKED_EA PackedEa; + ULONG PackedEaSize; + + PFILE_FULL_EA_INFORMATION LastFullEa; + ULONG LastFullEaSize; + PFILE_FULL_EA_INFORMATION NextFullEa; + BOOLEAN BufferOverflow = FALSE; + + + DebugTrace(+1, Dbg, "FatQueryEaSimpleScan...\n", 0); + + // + // Zero out the information field in the Iosb + // + + Iosb.Information = 0; + + LastFullEa = NULL; + NextFullEa = (PFILE_FULL_EA_INFORMATION) UserBuffer; + RemainingUserBufferLength = UserBufferLength; + + while (StartOffset < PackedEasLength) { + + DebugTrace(0, Dbg, "Top of loop, Offset = %08lx\n", StartOffset); + DebugTrace(0, Dbg, "LastFullEa = %08lx\n", LastFullEa); + DebugTrace(0, Dbg, "NextFullEa = %08lx\n", NextFullEa); + DebugTrace(0, Dbg, "RemainingUserBufferLength = %08lx\n", RemainingUserBufferLength); + + // + // Reference the packed ea of interest. + // + + PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + StartOffset); + + SizeOfPackedEa( PackedEa, &PackedEaSize ); + + DebugTrace(0, Dbg, "PackedEaSize = %08lx\n", PackedEaSize); + + // + // We know that the packed ea is 4 bytes smaller than its + // equivalent full ea so we need to check the remaining + // user buffer length against the computed full ea size. + // + + if (PackedEaSize + 4 > RemainingUserBufferLength) { + + BufferOverflow = TRUE; + break; + } + + // + // Everything is going to work fine, so copy over the packed + // ea to the full ea and zero out the next entry offset field. + // Then go back and set the last full eas entry offset field + // to be the difference between the two pointers. + // + + RtlCopyMemory( &NextFullEa->Flags, &PackedEa->Flags, PackedEaSize ); + NextFullEa->NextEntryOffset = 0; + + if (LastFullEa != NULL) { + + LastFullEa->NextEntryOffset = (ULONG)((PUCHAR) NextFullEa + - (PUCHAR) LastFullEa); + } + + // + // Set the last full ea to the next full ea, compute + // where the next full should be, and decrement the remaining user + // buffer length appropriately + // + + LastFullEa = NextFullEa; + LastFullEaSize = LongAlign( SizeOfFullEa( LastFullEa )); + RemainingUserBufferLength -= LastFullEaSize; + NextFullEa = (PFILE_FULL_EA_INFORMATION) ((PUCHAR) NextFullEa + + LastFullEaSize); + + // + // Remember the offset of the next ea in case we're asked to + // resume the teration + // + + StartOffset = FatLocateNextEa( IrpContext, + FirstPackedEa, + PackedEasLength, + StartOffset ); + + Ccb->OffsetOfNextEaToReturn = StartOffset; + + // + // If we were to return a single entry then break out of our loop + // now + // + + if (ReturnSingleEntry) { + + break; + } + } + + // + // Now we've iterated all that can and we've exited the preceding loop + // with either some or no information stored in the return buffer. + // We can decide which it is by checking if the last full ea is null + // + + if (LastFullEa == NULL) { + + Iosb.Information = 0; + + // + // We were not able to return a single ea entry, now we need to find + // out if it is because we didn't have an entry to return or the + // buffer is too small. If the Offset variable is less than + // PackedEaList->UsedSize then the user buffer is too small + // + + if (PackedEasLength == 0) { + + Iosb.Status = STATUS_NO_EAS_ON_FILE; + + } else if (StartOffset >= PackedEasLength) { + + Iosb.Status = STATUS_NO_MORE_EAS; + + } else { + + Iosb.Status = STATUS_BUFFER_TOO_SMALL; + } + + } else { + + // + // Otherwise we've been successful in returing at least one + // ea so we'll compute the number of bytes used to store the + // full ea information. The number of bytes used is the difference + // between the LastFullEa and the start of the buffer, and the + // non-aligned size of the last full ea. + // + + Iosb.Information = ((PUCHAR) LastFullEa - UserBuffer) + + SizeOfFullEa( LastFullEa ); + + // + // If there are more to return, report the buffer was too small. + // Otherwise return STATUS_SUCCESS. + // + + if (BufferOverflow) { + + Iosb.Status = STATUS_BUFFER_OVERFLOW; + + } else { + + Iosb.Status = STATUS_SUCCESS; + } + } + + DebugTrace(-1, Dbg, "FatQueryEaSimpleScan -> Iosb.Status = %08lx\n", + Iosb.Status); + + return Iosb; + +} + + +// +// Local Support Routine +// + +BOOLEAN +FatIsDuplicateEaName ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_GET_EA_INFORMATION GetEa, + IN PUCHAR UserBuffer + ) + +/*++ + +Routine Description: + + This routine walks through a list of ea names to find a duplicate name. + 'GetEa' is an actual position in the list. We are only interested in + previous matching ea names, as the ea information for that ea name + would have been returned with the previous instance. + +Arguments: + + GetEa - Supplies the Ea name structure for the ea name to match. + + UserBuffer - Supplies a pointer to the user buffer with the list + of ea names to search for. + +Return Value: + + BOOLEAN - TRUE if a previous match is found, FALSE otherwise. + +--*/ + +{ + PFILE_GET_EA_INFORMATION ThisGetEa; + + BOOLEAN DuplicateFound; + OEM_STRING EaString; + + DebugTrace(+1, Dbg, "FatIsDuplicateEaName...\n", 0); + + EaString.MaximumLength = EaString.Length = GetEa->EaNameLength; + EaString.Buffer = &GetEa->EaName[0]; + + FatUpcaseEaName( IrpContext, &EaString, &EaString ); + + DuplicateFound = FALSE; + + for (ThisGetEa = (PFILE_GET_EA_INFORMATION) &UserBuffer[0]; + ThisGetEa < GetEa + && ThisGetEa->NextEntryOffset != 0; + ThisGetEa = (PFILE_GET_EA_INFORMATION) ((PUCHAR) ThisGetEa + + ThisGetEa->NextEntryOffset)) { + + OEM_STRING Str; + + DebugTrace(0, Dbg, "Top of loop, ThisGetEa = %08lx\n", ThisGetEa); + + // + // Make a string reference to the GetEa and see if we can + // locate the ea by name + // + + Str.MaximumLength = Str.Length = ThisGetEa->EaNameLength; + Str.Buffer = &ThisGetEa->EaName[0]; + + DebugTrace(0, Dbg, "FatIsDuplicateEaName: Next Name -> %Z\n", &Str); + + if ( FatAreNamesEqual(IrpContext, Str, EaString) ) { + + DebugTrace(0, Dbg, "FatIsDuplicateEaName: Duplicate found\n", 0); + DuplicateFound = TRUE; + break; + } + } + + DebugTrace(-1, Dbg, "FatIsDuplicateEaName: Exit -> %04x\n", DuplicateFound); + + return DuplicateFound; +} + diff --git a/drivers/filesystems/fastfat_new/easup.c b/drivers/filesystems/fastfat_new/easup.c new file mode 100644 index 00000000000..f2c48c76635 --- /dev/null +++ b/drivers/filesystems/fastfat_new/easup.c @@ -0,0 +1,3811 @@ +/*++ + +Copyright (c) 1990-2000 Microsoft Corporation + +Module Name: + + EaSup.c + +Abstract: + + This module implements the cluster operations on the EA file for Fat. + + +--*/ + +#include "fatprocs.h" + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_EA) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatAddEaSet) +#pragma alloc_text(PAGE, FatAppendPackedEa) +#pragma alloc_text(PAGE, FatCreateEa) +#pragma alloc_text(PAGE, FatDeleteEa) +#pragma alloc_text(PAGE, FatDeleteEaSet) +#pragma alloc_text(PAGE, FatDeletePackedEa) +#pragma alloc_text(PAGE, FatGetEaFile) +#pragma alloc_text(PAGE, FatGetEaLength) +#pragma alloc_text(PAGE, FatGetNeedEaCount) +#pragma alloc_text(PAGE, FatIsEaNameValid) +#pragma alloc_text(PAGE, FatLocateEaByName) +#pragma alloc_text(PAGE, FatLocateNextEa) +#pragma alloc_text(PAGE, FatReadEaSet) +#pragma alloc_text(PAGE, FatPinEaRange) +#pragma alloc_text(PAGE, FatMarkEaRangeDirty) +#pragma alloc_text(PAGE, FatUnpinEaRange) +#endif + +#define Add2Ptr(P,I) ((PVOID)((PUCHAR)(P) + (I))) + +// +// Any access to the Ea file must recognize when a section boundary is being +// crossed. +// + +#define EA_SECTION_SIZE (0x00040000) + + +VOID +FatGetEaLength ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDIRENT Dirent, + OUT PULONG EaLength + ) + +/*++ + +Routine Description: + + This routine looks up the Ea length for the Eas of the file. This + length is the length of the packed eas, including the 4 bytes which + contain the Ea length. + + This routine pins down the Ea set for the desired file and copies + this field from the Ea set header. + +Arguments: + + Vcb - Vcb for the volume containing the Eas. + + Dirent - Supplies a pointer to the dirent for the file in question. + + EaLength - Supplies the address to store the length of the Eas. + +Return Value: + + None + +--*/ + +{ + PBCB EaBcb; + BOOLEAN LockedEaFcb; + EA_RANGE EaSetRange; + + DebugTrace(+1, Dbg, "FatGetEaLength ...\n", 0); + + // + // If this is Fat32 volume, or if the handle is 0 then the Ea length is 0. + // + + if (FatIsFat32( Vcb ) || + Dirent->ExtendedAttributes == 0) { + + *EaLength = 0; + DebugTrace(-1, Dbg, "FatGetEaLength -> %08lx\n", TRUE); + return; + } + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + // + // Use a try to facilitate cleanup. + // + + _SEH2_TRY { + + PDIRENT EaDirent; + OEM_STRING ThisFilename; + UCHAR Buffer[12]; + PEA_SET_HEADER EaSetHeader; + + // + // Initial the local values. + // + + EaBcb = NULL; + LockedEaFcb = FALSE; + + // + // Try to get the Ea file object. Return FALSE on failure. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + FALSE ); + + LockedEaFcb = TRUE; + + // + // If we didn't get the file because it doesn't exist, then the + // disk is corrupted. + // + + if (Vcb->VirtualEaFile == NULL) { + + DebugTrace(0, Dbg, "FatGetEaLength: Ea file doesn't exist\n", 0); + FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE ); + } + + // + // Try to pin down the Ea set header for the index in the + // dirent. If the operation doesn't complete, return FALSE + // from this routine. + // + +#ifndef __REACTOS__ + ThisFilename.Buffer = Buffer; +#else + ThisFilename.Buffer = (PCHAR)Buffer; +#endif + Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename ); + + FatReadEaSet( IrpContext, + Vcb, + Dirent->ExtendedAttributes, + &ThisFilename, + FALSE, + &EaSetRange ); + + EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // We now have the Ea set header for this file. We simply copy + // the Ea length field. + // + + CopyUchar4( EaLength, EaSetHeader->cbList ); + DebugTrace(0, Dbg, "FatGetEaLength: Length of Ea is -> %08lx\n", + *EaLength); + + } _SEH2_FINALLY { + + DebugUnwind( FatGetEaLength ); + + // + // Unpin the EaDirent and the EaSetHeader if pinned. + // + + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Release the Fcb for the Ea file if locked. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + DebugTrace(-1, Dbg, "FatGetEaLength: Ea length -> %08lx\n", *EaLength); + } _SEH2_END; + + return; +} + + +VOID +FatGetNeedEaCount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDIRENT Dirent, + OUT PULONG NeedEaCount + ) + +/*++ + +Routine Description: + + This routine looks up the Need Ea count for the file. The value is the + in the ea header for the file. + +Arguments: + + Vcb - Vcb for the volume containing the Eas. + + Dirent - Supplies a pointer to the dirent for the file in question. + + NeedEaCount - Supplies the address to store the Need Ea count. + +Return Value: + + None + +--*/ + +{ + PBCB EaBcb; + BOOLEAN LockedEaFcb; + EA_RANGE EaSetRange; + + DebugTrace(+1, Dbg, "FatGetNeedEaCount ...\n", 0); + + // + // If the handle is 0 then the Need Ea count is 0. + // + + if (Dirent->ExtendedAttributes == 0) { + + *NeedEaCount = 0; + DebugTrace(-1, Dbg, "FatGetNeedEaCount -> %08lx\n", TRUE); + return; + } + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + // + // Use a try to facilitate cleanup. + // + + _SEH2_TRY { + + PDIRENT EaDirent; + OEM_STRING ThisFilename; + UCHAR Buffer[12]; + PEA_SET_HEADER EaSetHeader; + + // + // Initial the local values. + // + + EaBcb = NULL; + LockedEaFcb = FALSE; + + // + // Try to get the Ea file object. Return FALSE on failure. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + FALSE ); + + LockedEaFcb = TRUE; + + // + // If we didn't get the file because it doesn't exist, then the + // disk is corrupted. + // + + if (Vcb->VirtualEaFile == NULL) { + + DebugTrace(0, Dbg, "FatGetNeedEaCount: Ea file doesn't exist\n", 0); + FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE ); + } + + // + // Try to pin down the Ea set header for the index in the + // dirent. If the operation doesn't complete, return FALSE + // from this routine. + // + +#ifndef __REACTOS__ + ThisFilename.Buffer = Buffer; +#else + ThisFilename.Buffer = (PCHAR)Buffer; +#endif + Fat8dot3ToString( IrpContext, Dirent, FALSE, &ThisFilename ); + + FatReadEaSet( IrpContext, + Vcb, + Dirent->ExtendedAttributes, + &ThisFilename, + FALSE, + &EaSetRange ); + + EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // We now have the Ea set header for this file. We simply copy + // the Need Ea field. + // + + *NeedEaCount = EaSetHeader->NeedEaCount; + + } _SEH2_FINALLY { + + DebugUnwind( FatGetNeedEaCount ); + + // + // Unpin the EaDirent and the EaSetHeader if pinned. + // + + FatUnpinBcb( IrpContext, EaBcb ); + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Release the Fcb for the Ea file if locked. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + DebugTrace(-1, Dbg, "FatGetNeedEaCount: NeedEaCount -> %08lx\n", *NeedEaCount); + } _SEH2_END; + + return; +} + + +VOID +FatCreateEa ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PUCHAR Buffer, + IN ULONG Length, + IN POEM_STRING FileName, + OUT PUSHORT EaHandle + ) + +/*++ + +Routine Description: + + This routine adds an entire ea set to the Ea file. The owning file + is specified in 'FileName'. This is used to replace the Ea set attached + to an existing file during a supersede operation. + + NOTE: This routine may block, it should not be called unless the + thread is waitable. + +Arguments: + + Vcb - Supplies the Vcb for the volume. + + Buffer - Buffer with the Ea list to add. + + Length - Length of the buffer. + + FileName - The Ea's will be attached to this file. + + EaHandle - The new ea handle will be assigned to this address. + +Return Value: + + None + +--*/ + +{ + PBCB EaBcb; + BOOLEAN LockedEaFcb; + + PEA_SET_HEADER EaSetHeader; + EA_RANGE EaSetRange; + + DebugTrace(+1, Dbg, "FatCreateEa...\n", 0); + + EaBcb = NULL; + LockedEaFcb = FALSE; + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + // + // Use 'try' to facilitate cleanup. + // + + _SEH2_TRY { + + PDIRENT EaDirent; + + ULONG PackedEasLength; + ULONG AllocationLength; + ULONG BytesPerCluster; + + PFILE_FULL_EA_INFORMATION FullEa; + + // + // We will allocate a buffer and copy the Ea list from the user's + // buffer to a FAT packed Ea list. Initial allocation is one + // cluster, our starting offset into the packed Ea list is 0. + // + + PackedEasLength = 0; + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + AllocationLength = (PackedEasLength + + SIZE_OF_EA_SET_HEADER + + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + // + // Allocate the memory and store the file name into it. + // + + EaSetHeader = FsRtlAllocatePoolWithTag( PagedPool, + AllocationLength, + TAG_EA_SET_HEADER ); + + RtlZeroMemory( EaSetHeader, AllocationLength ); + + RtlCopyMemory( EaSetHeader->OwnerFileName, + FileName->Buffer, + FileName->Length ); + + AllocationLength -= SIZE_OF_EA_SET_HEADER; + + // + // Loop through the user's Ea list. Catch any error for invalid + // name or non-existent Ea value. + // + + for ( FullEa = (PFILE_FULL_EA_INFORMATION) Buffer; + FullEa < (PFILE_FULL_EA_INFORMATION) &Buffer[Length]; + FullEa = (PFILE_FULL_EA_INFORMATION) (FullEa->NextEntryOffset == 0 ? + &Buffer[Length] : + (PUCHAR) FullEa + FullEa->NextEntryOffset)) { + + OEM_STRING EaName; + ULONG EaOffset; + + EaName.Length = FullEa->EaNameLength; + EaName.Buffer = &FullEa->EaName[0]; + + // + // Make sure the ea name is valid + // + + if (!FatIsEaNameValid( IrpContext, EaName )) { + + DebugTrace(0, Dbg, + "FatCreateEa: Invalid Ea Name -> %Z\n", + EaName); + + IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer; + IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME; + FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME ); + } + + // + // Check that no invalid ea flags are set. + // + + // + // TEMPCODE We are returning STATUS_INVALID_EA_NAME + // until a more appropriate error code exists. + // + + if (FullEa->Flags != 0 + && FullEa->Flags != FILE_NEED_EA) { + + IrpContext->OriginatingIrp->IoStatus.Information = (PUCHAR)FullEa - Buffer; + IrpContext->OriginatingIrp->IoStatus.Status = STATUS_INVALID_EA_NAME; + FatRaiseStatus( IrpContext, STATUS_INVALID_EA_NAME ); + } + + // + // If this is a duplicate name then delete the current ea + // value. + // + + if (FatLocateEaByName( IrpContext, + (PPACKED_EA) EaSetHeader->PackedEas, + PackedEasLength, + &EaName, + &EaOffset )) { + + DebugTrace(0, Dbg, "FatCreateEa: Duplicate name found\n", 0); + + FatDeletePackedEa( IrpContext, + EaSetHeader, + &PackedEasLength, + EaOffset ); + } + + // + // We ignore this value if the eavalue length is zero. + // + + if (FullEa->EaValueLength == 0) { + + DebugTrace(0, Dbg, + "FatCreateEa: Empty ea\n", + 0); + + continue; + } + + FatAppendPackedEa( IrpContext, + &EaSetHeader, + &PackedEasLength, + &AllocationLength, + FullEa, + BytesPerCluster ); + } + + // + // If the resulting length isn't zero, then allocate a FAT cluster + // to store the data. + // + + if (PackedEasLength != 0) { + + PEA_SET_HEADER NewEaSetHeader; + + // + // If the packed eas length (plus 4 bytes) is greater + // than the maximum allowed ea size, we return an error. + // + + if (PackedEasLength + 4 > MAXIMUM_EA_SIZE) { + + DebugTrace( 0, Dbg, "Ea length is greater than maximum\n", 0 ); + + FatRaiseStatus( IrpContext, STATUS_EA_TOO_LARGE ); + } + + // + // Get the Ea file. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + TRUE, + TRUE ); + + LockedEaFcb = TRUE; + + FatAddEaSet( IrpContext, + Vcb, + PackedEasLength + SIZE_OF_EA_SET_HEADER, + EaBcb, + EaDirent, + EaHandle, + &EaSetRange ); + + NewEaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // Store the length of the new Ea's into the NewEaSetHeader. + // This is the PackedEasLength + 4. + // + + PackedEasLength += 4; + + CopyU4char( EaSetHeader->cbList, &PackedEasLength ); + + // + // Copy all but the first four bytes of EaSetHeader into + // the new ea. The signature and index fields have + // already been filled in. + // + + RtlCopyMemory( &NewEaSetHeader->NeedEaCount, + &EaSetHeader->NeedEaCount, + PackedEasLength + SIZE_OF_EA_SET_HEADER - 8 ); + + FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + + // + // There was no data added to the Ea file. Return a handle + // of 0. + // + + } else { + + *EaHandle = 0; + } + + } _SEH2_FINALLY { + + DebugUnwind( FatCreateEa ); + + // + // Deallocate the EaSetHeader if present. + // + + if (EaSetHeader) { + + ExFreePool( EaSetHeader ); + } + + // + // Release the EaFcb if held. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + // + // Unpin the dirents for the EaFcb and EaSetFcb if necessary. + // + + FatUnpinBcb( IrpContext, EaBcb ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + DebugTrace(-1, Dbg, "FatCreateEa -> Exit\n", 0); + } _SEH2_END; + + return; +} + +VOID +FatDeleteEa ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN USHORT EaHandle, + IN POEM_STRING FileName + ) + +/*++ + +Routine Description: + + This routine is called to remove an entire ea set. Most of the work + is done in the call to 'FatDeleteEaSet'. This routine opens the + Ea file and then calls the support routine. + + NOTE: This routine may block, it should not be called unless the + thread is waitable. + +Arguments: + + Vcb - Vcb for the volume + + EaHandle - The handle for the Ea's to remove. This handle will be + verified during this operation. + + FileName - The name of the file whose Ea's are being removed. This + name is compared against the Ea owner's name in the Ea set. + +Return Value: + + None. + +--*/ + +{ + PBCB EaBcb; + BOOLEAN LockedEaFcb; + + DebugTrace(+1, Dbg, "FatDeleteEa...\n", 0); + + // + // Initialize local values. + // + + EaBcb = NULL; + LockedEaFcb = FALSE; + + // + // Use a try statement to facilitate cleanup. + // + + _SEH2_TRY { + + PDIRENT EaDirent; + + // + // Get the Ea stream file. If the file doesn't exist on the disk + // then the disk has been corrupted. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + TRUE ); + + LockedEaFcb = TRUE; + + // + // If we didn't get the Ea file, then the disk is corrupt. + // + + if ( EaBcb == NULL ) { + + + DebugTrace(0, Dbg, + "FatDeleteEa: No Ea file exists\n", + 0); + + FatRaiseStatus( IrpContext, STATUS_NO_EAS_ON_FILE ); + } + + // + // We now have everything we need to delete the ea set. Call the + // support routine to do this. + // + + FatDeleteEaSet( IrpContext, + Vcb, + EaBcb, + EaDirent, + EaHandle, + FileName ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + + } _SEH2_FINALLY { + + DebugUnwind( FatDeleteEa ); + + // + // Release the EaFcb if held. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + // + // Unpin the dirent for the Ea file if pinned. + // + + FatUnpinBcb( IrpContext, EaBcb ); + + DebugTrace(-1, Dbg, "FatDeleteEa -> Exit\n", 0); + } _SEH2_END; + + return; +} + + +VOID +FatGetEaFile ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb, + OUT PDIRENT *EaDirent, + OUT PBCB *EaBcb, + IN BOOLEAN CreateFile, + IN BOOLEAN ExclusiveFcb + ) + +/*++ + +Routine Description: + + This routine is used to completely initialize the Vcb and + the Ea file for the Vcb. + + If the Vcb doesn't have the Ea file object, then we first try to + lookup the Ea data file in the root directory and if that fails + we try to create the file. The 'CreateFile' flag is used to check + whether it is necessary to create the Ea file. + + This routine will lock down the Fcb for exclusive or shared access before + performing any operations. If the operation does not complete due + to blocking, exclusive or shared access will be given up before returning. + + If we are creating the Ea file and marking sections of it dirty, + we can't use the repin feature through the cache map. In that case + we use a local IrpContext and then unpin all of the Bcb's before + continuing. + + Note: If this routine will be creating the Ea file, we are guaranteed + to be waitable. + +Arguments: + + Vcb - Vcb for the volume + + EaDirent - Location to store the address of the pinned dirent for the + Ea file. + + EaBcb - Location to store the address of the Bcb for the pinned dirent. + + CreateFile - Boolean indicating whether we should create the Ea file + on the disk. + + ExclusiveFcb - Indicates whether shared or exclusive access is desired + for the EaFcb. + +Return Value: + + None. + +--*/ + +{ + PFILE_OBJECT EaStreamFile = NULL; + EA_RANGE EaFileRange; + + BOOLEAN UnwindLockedEaFcb = FALSE; + BOOLEAN UnwindLockedRootDcb = FALSE; + BOOLEAN UnwindAllocatedDiskSpace = FALSE; + BOOLEAN UnwindEaDirentCreated = FALSE; + BOOLEAN UnwindUpdatedSizes = FALSE; + + DebugTrace(+1, Dbg, "FatGetEaFile ...\n", 0); + + RtlZeroMemory( &EaFileRange, sizeof( EA_RANGE )); + + // + // Use a try to facilitate cleanup + // + + _SEH2_TRY { + + OEM_STRING EaFileName; + LARGE_INTEGER SectionSize; + + // + // Check if the Vcb already has the file object. If it doesn't, then + // we need to search the root directory for the Ea data file. + // + + if (Vcb->VirtualEaFile == NULL) { + + // + // Always lock the Ea file exclusively if we have to create the file. + // + + if ( !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) { + + DebugTrace(0, Dbg, "FatGetEaFile: Can't grab exclusive\n", 0); + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + UnwindLockedEaFcb = TRUE; + + // + // Otherwise we acquire the Fcb as the caller requested. + // + + } else { + + if ((ExclusiveFcb && !FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) + || (!ExclusiveFcb && !FatAcquireSharedFcb( IrpContext, Vcb->EaFcb))) { + + DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0); + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + UnwindLockedEaFcb = TRUE; + + // + // If the file now does not exist we need to release the Fcb and + // reacquire exclusive if we acquired shared. + // + + if ((Vcb->VirtualEaFile == NULL) && !ExclusiveFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + UnwindLockedEaFcb = FALSE; + + if (!FatAcquireExclusiveFcb( IrpContext, Vcb->EaFcb )) { + + DebugTrace(0, Dbg, "FatGetEaFile: Can't grab EaFcb\n", 0); + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + UnwindLockedEaFcb = TRUE; + } + } + + // + // If the file object is now there we only need to get the + // dirent for the Ea file. + // + + if (Vcb->VirtualEaFile != NULL) { + + FatVerifyFcb( IrpContext, Vcb->EaFcb ); + + FatGetDirentFromFcbOrDcb( IrpContext, + Vcb->EaFcb, + EaDirent, + EaBcb ); + + try_return( NOTHING ); + + } else { + + VBO ByteOffset; + + // + // Always mark the ea fcb as good. + // + + Vcb->EaFcb->FcbCondition = FcbGood; + + // + // We try to lookup the dirent for the Ea Fcb. + // + + EaFileName.Buffer = "EA DATA. SF"; + EaFileName.Length = 11; + EaFileName.MaximumLength = 12; + + // + // Now pick up the root directory to be synchronized with + // deletion/creation of entries. If we may create the file, + // get it exclusive right now. + // + // Again, note how we are relying on bottom-up lockorder. We + // already got the EaFcb. + // + + if (CreateFile) { + ExAcquireResourceExclusiveLite( Vcb->RootDcb->Header.Resource, TRUE ); + } else { + ExAcquireResourceSharedLite( Vcb->RootDcb->Header.Resource, TRUE ); + } + UnwindLockedRootDcb = TRUE; + + FatLocateSimpleOemDirent( IrpContext, + Vcb->EaFcb->ParentDcb, + &EaFileName, + EaDirent, + EaBcb, + &ByteOffset ); + + // + // If the file exists, we need to create the virtual file + // object for it. + // + + if (*EaDirent != NULL) { + + // + // Since we may be modifying the dirent, pin the data now. + // + + FatPinMappedData( IrpContext, + Vcb->EaFcb->ParentDcb, + ByteOffset, + sizeof(DIRENT), + EaBcb ); + + // + // Update the Fcb with information on the file size + // and disk location. Also increment the open/unclean + // counts in the EaFcb and the open count in the + // Vcb. + // + + Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile; + Vcb->EaFcb->DirentOffsetWithinDirectory = ByteOffset; + + // + // Find the allocation size. The purpose here is + // really to completely fill in the Mcb for the + // file. + // + + Vcb->EaFcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + + FatLookupFileAllocationSize( IrpContext, Vcb->EaFcb ); + + // + // Start by computing the section size for the cache + // manager. + // + + SectionSize.QuadPart = (*EaDirent)->FileSize; + Vcb->EaFcb->Header.AllocationSize = SectionSize; + Vcb->EaFcb->Header.FileSize = SectionSize; + + // + // Create and initialize the file object for the + // Ea virtual file. + // + + EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb ); + + Vcb->VirtualEaFile = EaStreamFile; + + // + // Else there was no dirent. If we were instructed to + // create the file object, we will try to create the dirent, + // allocate disk space, initialize the Ea file header and + // return this information to the user. + // + + } else if (CreateFile) { + + ULONG BytesPerCluster; + ULONG OffsetTableSize; + ULONG AllocationSize; + PEA_FILE_HEADER FileHeader; + USHORT AllocatedClusters; + PUSHORT CurrentIndex; + ULONG Index; + NTSTATUS Status; + + DebugTrace(0, Dbg, "FatGetEaFile: Creating local IrpContext\n", 0); + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + AllocationSize = (((ULONG) sizeof( EA_FILE_HEADER ) << 1) + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + AllocatedClusters = (USHORT) (AllocationSize + >> Vcb->AllocationSupport.LogOfBytesPerCluster); + + OffsetTableSize = AllocationSize - sizeof( EA_FILE_HEADER ); + + // + // Allocate disk space, the space allocated is 1024 bytes + // rounded up to the nearest cluster size. + // + + FatAllocateDiskSpace( IrpContext, + Vcb, + 0, + &AllocationSize, + FALSE, + &Vcb->EaFcb->Mcb ); + + UnwindAllocatedDiskSpace = TRUE; + + // + // Allocate and initialize a dirent in the root directory + // to describe this new file. + // + + Vcb->EaFcb->DirentOffsetWithinDirectory = + FatCreateNewDirent( IrpContext, + Vcb->EaFcb->ParentDcb, + 1 ); + + FatPrepareWriteDirectoryFile( IrpContext, + Vcb->EaFcb->ParentDcb, + Vcb->EaFcb->DirentOffsetWithinDirectory, + sizeof(DIRENT), + EaBcb, +#ifndef __REACTOS__ + EaDirent, +#else + (PVOID *)EaDirent, +#endif + FALSE, + TRUE, + &Status ); + + ASSERT( NT_SUCCESS( Status )); + + UnwindEaDirentCreated = TRUE; + + FatConstructDirent( IrpContext, + *EaDirent, + &EaFileName, + FALSE, + FALSE, + NULL, + FAT_DIRENT_ATTR_READ_ONLY + | FAT_DIRENT_ATTR_HIDDEN + | FAT_DIRENT_ATTR_SYSTEM + | FAT_DIRENT_ATTR_ARCHIVE, + TRUE, + NULL ); + + (*EaDirent)->FileSize = AllocationSize; + + // + // Initialize the Fcb for this file and initialize the + // cache map as well. + // + + // + // Start by computing the section size for the cache + // manager. + // + + SectionSize.QuadPart = (*EaDirent)->FileSize; + Vcb->EaFcb->Header.AllocationSize = SectionSize; + Vcb->EaFcb->Header.FileSize = SectionSize; + UnwindUpdatedSizes = TRUE; + + // + // Create and initialize the file object for the + // Ea virtual file. + // + + EaStreamFile = FatOpenEaFile( IrpContext, Vcb->EaFcb ); + + // + // Update the Fcb with information on the file size + // and disk location. Also increment the open/unclean + // counts in the EaFcb and the open count in the + // Vcb. + // + + { + LBO FirstLboOfFile; + + FatLookupMcbEntry( Vcb, &Vcb->EaFcb->Mcb, + 0, + &FirstLboOfFile, + NULL, + NULL ); + + // + // The discerning reader will note that this doesn't take + // FAT32 into account, which is of course intentional. + // + + (*EaDirent)->FirstClusterOfFile = + (USHORT) FatGetIndexFromLbo( Vcb, FirstLboOfFile ); + } + + Vcb->EaFcb->FirstClusterOfFile = (*EaDirent)->FirstClusterOfFile; + + // + // Initialize the Ea file header and mark the Bcb as dirty. + // + + FatPinEaRange( IrpContext, + EaStreamFile, + Vcb->EaFcb, + &EaFileRange, + 0, + AllocationSize, + STATUS_DATA_ERROR ); + + FileHeader = (PEA_FILE_HEADER) EaFileRange.Data; + + RtlZeroMemory( FileHeader, AllocationSize ); + FileHeader->Signature = EA_FILE_SIGNATURE; + + for (Index = MAX_EA_BASE_INDEX, CurrentIndex = FileHeader->EaBaseTable; + Index; + Index--, CurrentIndex++) { + + *CurrentIndex = AllocatedClusters; + } + + // + // Initialize the offset table with the offset set to + // after the just allocated clusters. + // + + for (Index = OffsetTableSize >> 1, + CurrentIndex = (PUSHORT) ((PUCHAR) FileHeader + sizeof( EA_FILE_HEADER )); + Index; + Index--, CurrentIndex++) { + + *CurrentIndex = UNUSED_EA_HANDLE; + } + + // + // Unpin the file header and offset table. + // + + FatMarkEaRangeDirty( IrpContext, EaStreamFile, &EaFileRange ); + FatUnpinEaRange( IrpContext, &EaFileRange ); + + CcFlushCache( EaStreamFile->SectionObjectPointer, NULL, 0, NULL ); + + // + // Return the Ea file object to the user. + // + + Vcb->VirtualEaFile = EaStreamFile; + } + } + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatGetEaFile ); + + // + // If this is abnormal termination and disk space has been + // allocated. We deallocate it now. + // + + if (_SEH2_AbnormalTermination()) { + + // + // Deallocate the Ea file + // + + if (UnwindAllocatedDiskSpace) { + + FatDeallocateDiskSpace( IrpContext, + Vcb, + &Vcb->EaFcb->Mcb ); + } + + // + // Delete the dirent for the Ea file, if created. + // + + if (UnwindEaDirentCreated) { + + if (UnwindUpdatedSizes) { + + Vcb->EaFcb->Header.AllocationSize.QuadPart = 0; + Vcb->EaFcb->Header.FileSize.QuadPart = 0; + } + + FatUnpinBcb( IrpContext, *EaBcb ); + FatDeleteDirent( IrpContext, Vcb->EaFcb, NULL, TRUE ); + } + + // + // Release the EA Fcb if held + // + + if (UnwindLockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + + // + // Dereference the Ea stream file if created. + // + + if (EaStreamFile != NULL) { + + ObDereferenceObject( EaStreamFile ); + } + } + + // + // Always release the root Dcb (our caller releases the EA Fcb if we + // do not raise). + // + + if (UnwindLockedRootDcb) { + + FatReleaseFcb( IrpContext, Vcb->RootDcb ); + } + + // + // If the Ea file header is locked down. We unpin it now. + // + + FatUnpinEaRange( IrpContext, &EaFileRange ); + + DebugTrace(-1, Dbg, "FatGetEaFile: Exit\n", 0); + } _SEH2_END; + + return; +} + + +VOID +FatReadEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN USHORT EaHandle, + IN POEM_STRING FileName, + IN BOOLEAN ReturnEntireSet, + OUT PEA_RANGE EaSetRange + ) + +/*++ + +Routine Description: + + This routine pins the Ea set for the given ea handle within the + Ea stream file. The EaHandle, after first comparing against valid + index values, is used to compute the cluster offset for this + this Ea set. The Ea set is then verified as belonging to this + index and lying within the Ea data file. + + The caller of this function will have verified that the Ea file + exists and that the Vcb field points to an initialized cache file. + The caller will already have gained exclusive access to the + EaFcb. + +Arguments: + + Vcb - Supplies the Vcb for the volume. + + EaHandle - Supplies the handle for the Ea's to read. + + FileName - Name of the file whose Ea's are being read. + + ReturnEntireSet - Indicates if the caller needs the entire set + as opposed to just the header. + + EaSetRange - Pointer to the EaRange structure which will describe the Ea + on return. + +Return Value: + + None + +--*/ + +{ + ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + ULONG EaOffsetVbo; + EA_RANGE EaOffsetRange; + USHORT EaOffsetCluster; + + EA_RANGE EaHeaderRange; + PEA_FILE_HEADER EaHeader; + + ULONG EaSetVbo; + PEA_SET_HEADER EaSet; + + ULONG CbList; + + DebugTrace(+1, Dbg, "FatReadEaSet\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + + // + // Verify that the Ea index has a legal value. Raise status + // STATUS_NONEXISTENT_EA_ENTRY if illegal. + // + + if (EaHandle < MIN_EA_HANDLE + || EaHandle > MAX_EA_HANDLE) { + + DebugTrace(-1, Dbg, "FatReadEaSet: Illegal handle value\n", 0); + FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); + } + + // + // Verify that the virtual Ea file is large enough for us to read + // the EaOffet table for this index. + // + + EaOffsetVbo = sizeof( EA_FILE_HEADER ) + ((EaHandle >> 7) << 8); + + // + // Zero the Ea range structures. + // + + RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); + RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); + + // + // Use a try statement to clean up on exit. + // + + _SEH2_TRY { + + // + // Pin down the EA file header. + // + + FatPinEaRange( IrpContext, + Vcb->VirtualEaFile, + Vcb->EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_NONEXISTENT_EA_ENTRY ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Pin down the Ea offset table for the particular index. + // + + FatPinEaRange( IrpContext, + Vcb->VirtualEaFile, + Vcb->EaFcb, + &EaOffsetRange, + EaOffsetVbo, + sizeof( EA_OFF_TABLE ), + STATUS_NONEXISTENT_EA_ENTRY ); + + // + // Check if the specifific handle is currently being used. + // + + EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data + + (EaHandle & (MAX_EA_OFFSET_INDEX - 1))); + + if (EaOffsetCluster == UNUSED_EA_HANDLE) { + + DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0); + FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); + } + + // + // Compute the file offset for the Ea data. + // + + EaSetVbo = (EaHeader->EaBaseTable[EaHandle >> 7] + EaOffsetCluster) + << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // Unpin the file header and offset table. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + // + // Pin the ea set. + // + + FatPinEaRange( IrpContext, + Vcb->VirtualEaFile, + Vcb->EaFcb, + EaSetRange, + EaSetVbo, + BytesPerCluster, + STATUS_DATA_ERROR ); + + // + // Verify that the Ea set is valid and belongs to this index. + // Raise STATUS_DATA_ERROR if there is a data conflict. + // + + EaSet = (PEA_SET_HEADER) EaSetRange->Data; + + if (EaSet->Signature != EA_SET_SIGNATURE + || EaSet->OwnEaHandle != EaHandle ) { + + DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0); + FatRaiseStatus( IrpContext, STATUS_DATA_ERROR ); + } + + // + // At this point we have pinned a single cluster of Ea data. If + // this represents the entire Ea data for the Ea index, we are + // done. Otherwise we need to check on the entire size of + // of the Ea set header and whether it is contained in the allocated + // size of the Ea virtual file. At that point we can unpin + // the partial Ea set header and repin the entire header. + // + + CbList = GetcbList( EaSet ); + + if (ReturnEntireSet + && CbList > BytesPerCluster ) { + + // + // Round up to the cluster size. + // + + CbList = (CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + FatUnpinEaRange( IrpContext, EaSetRange ); + + RtlZeroMemory( EaSetRange, sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + Vcb->VirtualEaFile, + Vcb->EaFcb, + EaSetRange, + EaSetVbo, + CbList, + STATUS_DATA_ERROR ); + } + + } _SEH2_FINALLY { + + DebugUnwind( FatReadEaSet ); + + // + // Unpin the Ea base and offset tables if locked down. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + DebugTrace(-1, Dbg, "FatReadEaSet: Exit\n", 0); + } _SEH2_END; + + return; +} + + +VOID +FatDeleteEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PBCB EaBcb, + OUT PDIRENT EaDirent, + IN USHORT EaHandle, + IN POEM_STRING FileName + ) + +/*++ + +Routine Description: + + This routines clips the Ea set for a particular index out of the + Ea file for a volume. The index is verified as belonging to a valid + handle. The clusters are removed and the Ea stream file along with + the Ea base and offset files are updated. + + The caller of this function will have verified that the Ea file + exists and that the Vcb field points to an initialized cache file. + The caller will already have gained exclusive access to the + EaFcb. + +Arguments: + + Vcb - Supplies the Vcb for the volume. + + VirtualEeFile - Pointer to the file object for the virtual Ea file. + + EaFcb - Supplies the pointer to the Fcb for the Ea file. + + EaBcb - Supplies a pointer to the Bcb for the Ea dirent. + + EaDirent - Supplies a pointer to the dirent for the Ea file. + + EaHandle - Supplies the handle for the Ea's to read. + + FileName - Name of the file whose Ea's are being read. + +Return Value: + + None. + +--*/ + +{ + ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + ULONG CbList; + LARGE_INTEGER FileOffset; + + LARGE_MCB DataMcb; + BOOLEAN UnwindInitializeDataMcb = FALSE; + BOOLEAN UnwindSplitData = FALSE; + + LARGE_MCB TailMcb; + BOOLEAN UnwindInitializeTailMcb = FALSE; + BOOLEAN UnwindSplitTail = FALSE; + BOOLEAN UnwindMergeTail = FALSE; + + BOOLEAN UnwindModifiedEaHeader = FALSE; + BOOLEAN UnwindCacheValues = FALSE; + ULONG UnwindPrevFileSize = 0; + + ULONG EaOffsetVbo; + USHORT EaOffsetIndex; + EA_RANGE EaOffsetRange; + USHORT EaOffsetCluster; + + PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile; + PFCB EaFcb = Vcb->EaFcb; + + EA_RANGE EaHeaderRange; + PEA_FILE_HEADER EaHeader; + USHORT EaHeaderBaseIndex; + + ULONG EaSetVbo; + ULONG EaSetLength; + EA_RANGE EaSetRange; + PEA_SET_HEADER EaSet; + USHORT EaSetClusterCount; + + // + // Verify that the Ea index has a legal value. Raise status + // STATUS_INVALID_HANDLE if illegal. + // + + if (EaHandle < MIN_EA_HANDLE + || EaHandle > MAX_EA_HANDLE) { + + DebugTrace(-1, Dbg, "FatDeleteEaSet: Illegal handle value\n", 0); + FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); + } + + // + // Verify that the virtual Ea file is large enough for us to read + // the EaOffet table for this index. + // + + EaOffsetVbo = sizeof( EA_FILE_HEADER ) + ((EaHandle >> 7) << 8); + + // + // Zero the Ea range structures. + // + + RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); + RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + // + // Use a try to facilitate cleanup. + // + + _SEH2_TRY { + + // + // Pin down the EA file header. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_NONEXISTENT_EA_ENTRY ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Pin down the Ea offset table for the particular index. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaOffsetRange, + EaOffsetVbo, + sizeof( EA_OFF_TABLE ), + STATUS_NONEXISTENT_EA_ENTRY ); + + // + // Check if the specifific handle is currently being used. + // + + EaOffsetIndex = EaHandle & (MAX_EA_OFFSET_INDEX - 1); + EaOffsetCluster = *((PUSHORT) EaOffsetRange.Data + EaOffsetIndex); + + if (EaOffsetCluster == UNUSED_EA_HANDLE) { + + DebugTrace(0, Dbg, "FatReadEaSet: Ea handle is unused\n", 0); + FatRaiseStatus( IrpContext, STATUS_NONEXISTENT_EA_ENTRY ); + } + + // + // Compute the file offset for the Ea data. + // + + EaHeaderBaseIndex = EaHandle >> 7; + EaSetVbo = (EaHeader->EaBaseTable[EaHeaderBaseIndex] + EaOffsetCluster) + << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // Unpin the file header and offset table. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + // + // Try to pin the requested Ea set. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaSetRange, + EaSetVbo, + BytesPerCluster, + STATUS_DATA_ERROR ); + + EaSet = (PEA_SET_HEADER) EaSetRange.Data; + + if (EaSet->Signature != EA_SET_SIGNATURE + || EaSet->OwnEaHandle != EaHandle ) { + + DebugTrace(0, Dbg, "FatReadEaSet: Ea set header is corrupt\n", 0); + FatRaiseStatus( IrpContext, STATUS_DATA_ERROR ); + } + + // + // At this point we have pinned a single cluster of Ea data. If + // this represents the entire Ea data for the Ea index, we know + // the number of clusters to remove. Otherwise we need to check + // on the entire size of the Ea set header and whether it is + // contained in the allocated size of the Ea virtual file. At + // that point we unpin the partial Ea set header and remember the + // starting cluster offset and number of clusters in both cluster + // and Vbo formats. + // + // At that point the following variables have the described + // values. + // + // EaSetVbo - Vbo to start splice at. + // EaSetLength - Number of bytes to splice. + // EaSetClusterCount - Number of clusters to splice. + // + + CbList = GetcbList( EaSet ); + + EaSetClusterCount = (USHORT) ((CbList + EA_CBLIST_OFFSET + BytesPerCluster - 1) + >> Vcb->AllocationSupport.LogOfBytesPerCluster); + + EaSetLength = EaSetClusterCount << Vcb->AllocationSupport.LogOfBytesPerCluster; + + if (EaSetLength > BytesPerCluster) { + + if (EaFcb->Header.FileSize.LowPart - EaSetVbo < EaSetLength) { + + DebugTrace(0, Dbg, "FatDeleteEaSet: Full Ea set not contained in file\n", 0); + + FatRaiseStatus( IrpContext, STATUS_DATA_ERROR ); + } + } + + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Update the cache manager for this file. This is done by + // truncating to the point where the data was spliced and + // reinitializing with the modified size of the file. + // + // NOTE: Even if the all the EA's are removed the Ea file will + // always exist and the header area will never shrink. + // + + FileOffset.LowPart = EaSetVbo; + FileOffset.HighPart = 0; + + // + // Round the cache map down to a system page boundary. + // + + FileOffset.LowPart &= ~(PAGE_SIZE - 1); + + // + // Make sure all the data gets out to the disk. + // + + { + IO_STATUS_BLOCK Iosb; + ULONG PurgeCount = 5; + + while (--PurgeCount) { + + Iosb.Status = STATUS_SUCCESS; + + CcFlushCache( VirtualEaFile->SectionObjectPointer, + NULL, + 0, + &Iosb ); + + ASSERT( Iosb.Status == STATUS_SUCCESS ); + + // + // We do not have to worry about a lazy writer firing in parallel + // with our CcFlushCache since we have the EaFcb exclusive. Thus + // we know all data is out. + // + + // + // We throw the unwanted pages out of the cache and then + // truncate the Ea File for the new size. + // + + if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer, + &FileOffset, + 0, + FALSE )) { + + break; + } + } + + if (!PurgeCount) { + + FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION ); + } + } + + FileOffset.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength; + + // + // Perform the splice operation on the FAT chain. This is done + // by splitting the target clusters out and merging the remaining + // clusters around them. We can ignore the return value from + // the merge and splice functions because we are guaranteed + // to be able to block. + // + + { + FsRtlInitializeLargeMcb( &DataMcb, PagedPool ); + + UnwindInitializeDataMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaSetVbo, + &DataMcb ); + + UnwindSplitData = TRUE; + + if (EaSetLength + EaSetVbo != EaFcb->Header.FileSize.LowPart) { + + FsRtlInitializeLargeMcb( &TailMcb, PagedPool ); + + UnwindInitializeTailMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &DataMcb, + EaSetLength, + &TailMcb ); + + UnwindSplitTail = TRUE; + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &TailMcb ); + + UnwindMergeTail = TRUE; + } + } + + // + // Update the Fcb for the Ea file + // + + UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart; + + (VOID)ExAcquireResourceExclusiveLite( EaFcb->Header.PagingIoResource, + TRUE ); + + EaFcb->Header.FileSize.LowPart = EaFcb->Header.FileSize.LowPart - EaSetLength; + EaFcb->Header.AllocationSize = EaFcb->Header.FileSize; + + + CcSetFileSizes( VirtualEaFile, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); + + ExReleaseResourceLite( EaFcb->Header.PagingIoResource ); + + UnwindCacheValues = TRUE; + + EaDirent->FileSize = EaFcb->Header.FileSize.LowPart; + + FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE ); + + // + // Update the Ea base and offset tables. For the Ea base table, + // all subsequent index values must be decremented by the number + // of clusters removed. + // + // For the entries in the relevant Ea offset table, all entries + // after this index must also be decreased by the number of + // clusters removed. + // + + // + // Pin down the EA file header. + // + + RtlZeroMemory( &EaHeaderRange, + sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_NONEXISTENT_EA_ENTRY ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Pin down the Ea offset table for the particular index. + // + + RtlZeroMemory( &EaOffsetRange, + sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaOffsetRange, + EaOffsetVbo, + sizeof( EA_OFF_TABLE ), + STATUS_NONEXISTENT_EA_ENTRY ); + + { + ULONG Count; + PUSHORT NextEaIndex; + + Count = MAX_EA_BASE_INDEX - EaHeaderBaseIndex - 1; + + NextEaIndex = &EaHeader->EaBaseTable[EaHeaderBaseIndex + 1]; + + while (Count--) { + + *(NextEaIndex++) -= EaSetClusterCount; + } + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange ); + + Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1; + NextEaIndex = (PUSHORT) EaOffsetRange.Data + EaOffsetIndex; + + *(NextEaIndex++) = UNUSED_EA_HANDLE; + + while (Count--) { + + if (*NextEaIndex != UNUSED_EA_HANDLE) { + + *NextEaIndex -= EaSetClusterCount; + } + + NextEaIndex++; + } + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange ); + } + + UnwindModifiedEaHeader = TRUE; + + // + // Deallocate the ea set removed + // + + FatDeallocateDiskSpace( IrpContext, + Vcb, + &DataMcb ); + + } _SEH2_FINALLY { + + DebugUnwind( FatDeleteEaSet ); + + // + // Restore file if abnormal termination. + // + // If we have modified the ea file header we ignore this + // error. Otherwise we walk through the state variables. + // + + if (_SEH2_AbnormalTermination() + && !UnwindModifiedEaHeader) { + + // + // If we modified the Ea dirent or Fcb, recover the previous + // values. + // + + if (UnwindPrevFileSize) { + + EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize; + EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize; + EaDirent->FileSize = UnwindPrevFileSize; + + if (UnwindCacheValues) { + + CcSetFileSizes( VirtualEaFile, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); + } + } + + // + // If we merged the tail with the + // ea file header. We split it out + // again. + // + + if (UnwindMergeTail) { + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaSetVbo, + &TailMcb ); + } + + // + // If we split the tail off we merge the tail back + // with the ea data to remove. + // + + if (UnwindSplitTail) { + + FatMergeAllocation( IrpContext, + Vcb, + &DataMcb, + &TailMcb ); + } + + // + // If the ea set has been split out, we merge that + // cluster string back in the file. Otherwise we + // simply uninitialize the local Mcb. + // + + if (UnwindSplitData) { + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &DataMcb ); + } + } + + // + // Unpin any Bcb's still active. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Uninitialize any initialized Mcbs + // + + if (UnwindInitializeDataMcb) { + + FsRtlUninitializeLargeMcb( &DataMcb ); + } + + if (UnwindInitializeTailMcb) { + + FsRtlUninitializeLargeMcb( &TailMcb ); + } + + DebugTrace(-1, Dbg, "FatDeleteEaSet -> Exit\n", 0); + } _SEH2_END; + + return; +} + + +VOID +FatAddEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG EaSetLength, + IN PBCB EaBcb, + OUT PDIRENT EaDirent, + OUT PUSHORT EaHandle, + OUT PEA_RANGE EaSetRange + ) + +/*++ + +Routine Description: + + This routine will add the necessary clusters to support a new + Ea set of the given size. This is done by splicing a chain of + clusters into the existing Ea file. An Ea index is assigned to + this new chain and the Ea base and offset tables are updated to + include this new handle. This routine also pins the added + clusters and returns their address and a Bcb. + + The caller of this function will have verified that the Ea file + exists and that the Vcb field points to an initialized cache file. + The caller will already have gained exclusive access to the + EaFcb. + +Arguments: + + Vcb - Supplies the Vcb to fill in. + + EaSetLength - The number of bytes needed to contain the Ea set. This + routine will round this up the next cluster size. + + EaBcb - Supplies a pointer to the Bcb for the Ea dirent. + + EaDirent - Supplies a pointer to the dirent for the Ea file. + + EaHandle - Supplies the address to store the ea index generated here. + + EaSetRange - This is the structure that describes new range in the Ea file. + +Return Value: + + None. + +--*/ + +{ + ULONG BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + EA_RANGE EaHeaderRange; + USHORT EaHeaderIndex; + PEA_FILE_HEADER EaHeader; + + EA_RANGE EaOffsetRange; + ULONG EaNewOffsetVbo; + USHORT EaOffsetIndex; + ULONG EaOffsetTableSize; + PUSHORT EaOffsetTable; + + ULONG EaSetClusterOffset; + ULONG EaSetVbo; + USHORT EaSetClusterCount; + PEA_SET_HEADER EaSet; + + PFILE_OBJECT VirtualEaFile = Vcb->VirtualEaFile; + PFCB EaFcb = Vcb->EaFcb; + + LARGE_MCB EaSetMcb; + BOOLEAN UnwindInitializedEaSetMcb = FALSE; + BOOLEAN UnwindAllocatedNewAllocation = FALSE; + BOOLEAN UnwindMergedNewEaSet = FALSE; + + LARGE_MCB EaOffsetMcb; + BOOLEAN UnwindInitializedOffsetMcb = FALSE; + BOOLEAN UnwindSplitNewAllocation = FALSE; + BOOLEAN UnwindMergedNewOffset = FALSE; + + LARGE_MCB EaTailMcb; + BOOLEAN UnwindInitializedTailMcb = FALSE; + BOOLEAN UnwindSplitTail = FALSE; + BOOLEAN UnwindMergedTail = FALSE; + + LARGE_MCB EaInitialEaMcb; + BOOLEAN UnwindInitializedInitialEaMcb = FALSE; + BOOLEAN UnwindSplitInitialEa = FALSE; + BOOLEAN UnwindMergedInitialEa = FALSE; + + USHORT NewEaIndex; + PUSHORT NextEaOffset; + + ULONG NewAllocation; + LARGE_INTEGER FileOffset; + ULONG Count; + + ULONG UnwindPrevFileSize = 0; + BOOLEAN UnwindCacheValues = FALSE; + + BOOLEAN TailExists = FALSE; + BOOLEAN AddedOffsetTableCluster = FALSE; + BOOLEAN UnwindPurgeCacheMap = FALSE; + + DebugTrace(+1, Dbg, "FatAddEaSet\n", 0); + DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb); + DebugTrace( 0, Dbg, " EaSetLength = %ul\n", EaSetLength ); + + // + // Zero the Ea range structures. + // + + RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); + RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); + + // + // Use a try statement to facilitate cleanup. + // + + _SEH2_TRY { + + // + // Pin down the file header. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_DATA_ERROR ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Compute the size of the offset table. + // + + EaNewOffsetVbo = EaHeader->EaBaseTable[0] << Vcb->AllocationSupport.LogOfBytesPerCluster; + EaOffsetTableSize = EaNewOffsetVbo - sizeof( EA_FILE_HEADER ); + + // + // Pin down the entire offset table. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaOffsetRange, + sizeof( EA_FILE_HEADER ), + EaOffsetTableSize, + STATUS_DATA_ERROR ); + + // + // We now look for a valid handle out of the existing offset table. + // We start at the last entry and walk backwards. We stop at the + // first unused handle which is preceded by a used handle (or handle + // 1). + // + // As we walk backwards, we need to remember the file offset of the + // cluster which will follow the clusters we add. We initially + // remember the end of the file. If the end of the offset table + // consists of a string of used handles, we remember the offset of + // the handle prior to the transition from used to unused handles. + // + + EaSetClusterOffset = EaFcb->Header.FileSize.LowPart + >> Vcb->AllocationSupport.LogOfBytesPerCluster; + + NewEaIndex = (USHORT) ((EaOffsetTableSize >> 1) - 1); + + NextEaOffset = (PUSHORT) EaOffsetRange.Data + NewEaIndex; + + // + // Walk through the used handles at the end of the offset table. + // + + if (*NextEaOffset != UNUSED_EA_HANDLE) { + + while (NewEaIndex != 0) { + + if (*(NextEaOffset - 1) == UNUSED_EA_HANDLE) { + + // + // If the handle is 1, we take no action. Otherwise + // we save the cluster offset of the current handle + // knowing we will use a previous handle and insert + // a chain of clusters. + // + + if (NewEaIndex != 1) { + + EaSetClusterOffset = *NextEaOffset + + EaHeader->EaBaseTable[NewEaIndex >> 7]; + + TailExists = TRUE; + } + + NewEaIndex--; + NextEaOffset--; + + break; + } + + NewEaIndex--; + NextEaOffset--; + } + } + + // + // Walk through looking for the first unused handle in a string + // of unused handles. + // + + while (NewEaIndex) { + + if (*(NextEaOffset - 1) != UNUSED_EA_HANDLE) { + + break; + } + + NextEaOffset--; + NewEaIndex--; + } + + // + // If the handle is zero, we do a special test to see if handle 1 + // is available. Otherwise we will use the first handle of a new + // cluster. A non-zero handle now indicates that a handle was found + // in an existing offset table cluster. + // + + if (NewEaIndex == 0) { + + if (*(NextEaOffset + 1) == UNUSED_EA_HANDLE) { + + NewEaIndex = 1; + + } else { + + NewEaIndex = (USHORT) EaOffsetTableSize >> 1; + AddedOffsetTableCluster = TRUE; + } + } + + // + // If the Ea index is outside the legal range then raise an + // exception. + // + + if (NewEaIndex > MAX_EA_HANDLE) { + + DebugTrace(-1, Dbg, + "FatAddEaSet: Illegal handle value for new handle\n", 0); + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Compute the base and offset indexes. + // + + EaHeaderIndex = NewEaIndex >> 7; + EaOffsetIndex = NewEaIndex & (MAX_EA_OFFSET_INDEX - 1); + + // + // Compute the byte offset of the new ea data in the file. + // + + EaSetVbo = EaSetClusterOffset << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // Allocate all the required disk space together to insure this + // operation is atomic. We don't want to allocate one block + // of disk space and then fail on a second allocation. + // + + EaSetLength = (EaSetLength + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + NewAllocation = EaSetLength + + (AddedOffsetTableCluster ? BytesPerCluster : 0); + + // + // Verify that adding these clusters will not grow the Ea file + // beyond its legal value. The maximum number of clusters is + // 2^16 since the Ea sets are referenced by a 16 bit cluster + // offset value. + // + + if ((ULONG) ((0x0000FFFF << Vcb->AllocationSupport.LogOfBytesPerCluster) + - EaFcb->Header.FileSize.LowPart) + < NewAllocation) { + + DebugTrace(-1, Dbg, + "FatAddEaSet: New Ea file size is too large\n", 0); + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool ); + + UnwindInitializedEaSetMcb = TRUE; + + FatAllocateDiskSpace( IrpContext, + Vcb, + 0, + &NewAllocation, + FALSE, + &EaSetMcb ); + + UnwindAllocatedNewAllocation = TRUE; + + EaSetClusterCount = (USHORT) (EaSetLength >> Vcb->AllocationSupport.LogOfBytesPerCluster); + + if (AddedOffsetTableCluster) { + + FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool ); + + UnwindInitializedOffsetMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &EaSetMcb, + EaSetLength, + &EaOffsetMcb ); + + UnwindSplitNewAllocation = TRUE; + } + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + if (AddedOffsetTableCluster) { + + FileOffset.LowPart = EaNewOffsetVbo; + + } else { + + FileOffset.LowPart = EaSetVbo; + } + + FileOffset.HighPart = 0; + + // + // Round the cache map down to a system page boundary. + // + + FileOffset.LowPart &= ~(PAGE_SIZE - 1); + + { + IO_STATUS_BLOCK Iosb; + ULONG PurgeCount = 5; + + while (--PurgeCount) { + + Iosb.Status = STATUS_SUCCESS; + + CcFlushCache( VirtualEaFile->SectionObjectPointer, + NULL, + 0, + &Iosb ); + + ASSERT( Iosb.Status == STATUS_SUCCESS ); + + // + // We do not have to worry about a lazy writer firing in parallel + // with our CcFlushCache since we have the EaFcb exclusive. Thus + // we know all data is out. + // + + // + // We throw the unwanted pages out of the cache and then + // truncate the Ea File for the new size. + // + // + + if (CcPurgeCacheSection( VirtualEaFile->SectionObjectPointer, + &FileOffset, + 0, + FALSE )) { + + break; + } + } + + if (!PurgeCount) { + + FatRaiseStatus( IrpContext, STATUS_UNABLE_TO_DELETE_SECTION ); + } + } + + UnwindPurgeCacheMap = TRUE; + + FileOffset.LowPart = EaFcb->Header.FileSize.LowPart + NewAllocation; + + // + // If there is a tail to the file, then we initialize an Mcb + // for the file section and split the tail from the file. + // + + if (TailExists) { + + FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool ); + + UnwindInitializedTailMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaSetVbo, + &EaTailMcb ); + + UnwindSplitTail = TRUE; + } + + // + // If there is an initial section of ea data, we initialize an + // Mcb for that section. + // + + if (AddedOffsetTableCluster + && EaSetVbo != EaNewOffsetVbo) { + + FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool ); + + UnwindInitializedInitialEaMcb = TRUE; + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaNewOffsetVbo, + &EaInitialEaMcb ); + + UnwindSplitInitialEa = TRUE; + } + + // + // We have now split the new file allocation into the new + // ea set and possibly a new offset table. + // + // We have also split the existing file data into a file + // header, an initial section of ea data and the tail of the + // file. These last 2 may not exist. + // + // Each section is described by an Mcb. + // + + // + // Merge the new offset information if it exists. + // + + if (AddedOffsetTableCluster) { + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &EaOffsetMcb ); + + FsRtlUninitializeLargeMcb( &EaOffsetMcb ); + FsRtlInitializeLargeMcb( &EaOffsetMcb, PagedPool ); + + UnwindMergedNewOffset = TRUE; + } + + // + // Merge the existing initial ea data if it exists. + // + + if (UnwindInitializedInitialEaMcb) { + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &EaInitialEaMcb ); + + FsRtlUninitializeLargeMcb( &EaInitialEaMcb ); + FsRtlInitializeLargeMcb( &EaInitialEaMcb, PagedPool ); + + UnwindMergedInitialEa = TRUE; + } + + // + // We modify the offset of the new ea set by one cluster if + // we added one to the offset table. + // + + if (AddedOffsetTableCluster) { + + EaSetClusterOffset += 1; + EaSetVbo += BytesPerCluster; + } + + // + // Merge the new ea set. + // + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &EaSetMcb ); + + FsRtlUninitializeLargeMcb( &EaSetMcb ); + FsRtlInitializeLargeMcb( &EaSetMcb, PagedPool ); + + UnwindMergedNewEaSet = TRUE; + + // + // Merge the tail if it exists. + // + + if (UnwindInitializedTailMcb) { + + FatMergeAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + &EaTailMcb ); + + FsRtlUninitializeLargeMcb( &EaTailMcb ); + FsRtlInitializeLargeMcb( &EaTailMcb, PagedPool ); + + UnwindMergedTail = TRUE; + } + + // + // If we added a new cluster for the offset table, we need to + // lock the entire cluster down and initialize all the handles to + // the unused state except the first one. + // + + // + // Update the Fcb information. + // + + UnwindPrevFileSize = EaFcb->Header.FileSize.LowPart; + + EaFcb->Header.FileSize.LowPart += NewAllocation; + EaFcb->Header.AllocationSize = EaFcb->Header.FileSize; + EaDirent->FileSize = EaFcb->Header.FileSize.LowPart; + + FatSetDirtyBcb( IrpContext, EaBcb, Vcb, TRUE ); + + // + // Let Mm and Cc know the new file sizes. + // + + CcSetFileSizes( VirtualEaFile, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); + + UnwindCacheValues = TRUE; + + // + // Pin down the file header. + // + + RtlZeroMemory( &EaHeaderRange, sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaHeaderRange, + 0, + sizeof( EA_FILE_HEADER ), + STATUS_DATA_ERROR ); + + EaHeader = (PEA_FILE_HEADER) EaHeaderRange.Data; + + // + // Pin down the entire offset table. + // + + + RtlZeroMemory( &EaOffsetRange, sizeof( EA_RANGE )); + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + &EaOffsetRange, + sizeof( EA_FILE_HEADER ) + ((NewEaIndex >> 7) << 8), + sizeof( EA_OFF_TABLE ), + STATUS_DATA_ERROR ); + + EaOffsetTable = (PUSHORT) EaOffsetRange.Data; + + // + // Pin the Ea set header for the added clusters and initialize + // the fields of interest. These are the signature field, the + // owning handle field, the need Ea field and the cbList field. + // Also mark the data as dirty. + // + + // + // Pin the ea set. + // + + FatPinEaRange( IrpContext, + VirtualEaFile, + EaFcb, + EaSetRange, + EaSetVbo, + EaSetLength, + STATUS_DATA_ERROR ); + + EaSet = (PEA_SET_HEADER) EaSetRange->Data; + + EaSet->Signature = EA_SET_SIGNATURE; + EaSet->OwnEaHandle = NewEaIndex; + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, EaSetRange ); + + // + // Update the Ea base and offset tables. For the Ea base table, + // all subsequent index values must be incremented by the number + // of clusters added. + // + // For the entries in the relevant Ea offset table, all entries + // after this index must also be increased by the number of + // clusters added. + // + // If we added another cluster to the offset table, then we increment + // all the base table values by 1. + // + + Count = MAX_EA_BASE_INDEX - EaHeaderIndex - 1; + + NextEaOffset = &EaHeader->EaBaseTable[EaHeaderIndex + 1]; + + while (Count--) { + + *(NextEaOffset++) += EaSetClusterCount; + } + + if (AddedOffsetTableCluster) { + + Count = MAX_EA_BASE_INDEX; + + NextEaOffset = &EaHeader->EaBaseTable[0]; + + while (Count--) { + + *(NextEaOffset++) += 1; + } + } + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaHeaderRange ); + + // + // If we added an offset table cluster, we need to initialize + // the handles to unused. + // + + if (AddedOffsetTableCluster) { + + Count = (BytesPerCluster >> 1) - 1; + NextEaOffset = EaOffsetTable; + + *NextEaOffset++ = 0; + + while (Count--) { + + *NextEaOffset++ = UNUSED_EA_HANDLE; + } + } + + // + // We need to compute the offset of the added Ea set clusters + // from their base. + // + + NextEaOffset = EaOffsetTable + EaOffsetIndex; + + *NextEaOffset++ = (USHORT) (EaSetClusterOffset + - EaHeader->EaBaseTable[EaHeaderIndex]); + + Count = MAX_EA_OFFSET_INDEX - EaOffsetIndex - 1; + + while (Count--) { + + if (*NextEaOffset != UNUSED_EA_HANDLE) { + + *NextEaOffset += EaSetClusterCount; + } + + NextEaOffset++; + } + + FatMarkEaRangeDirty( IrpContext, VirtualEaFile, &EaOffsetRange ); + + // + // Update the callers parameters. + // + + *EaHandle = NewEaIndex; + + DebugTrace(0, Dbg, "FatAddEaSet: Return values\n", 0); + + DebugTrace(0, Dbg, "FatAddEaSet: New Handle -> %x\n", + *EaHandle); + + } _SEH2_FINALLY { + + DebugUnwind( FatAddEaSet ); + + // + // Handle cleanup for abnormal termination only if we allocated + // disk space for the new ea set. + // + + if (_SEH2_AbnormalTermination() && UnwindAllocatedNewAllocation) { + + // + // If we modified the Ea dirent or Fcb, recover the previous + // values. Even though we are decreasing FileSize here, we + // don't need to synchronize to synchronize with paging Io + // because there was no dirty data generated in the new allocation. + // + + if (UnwindPrevFileSize) { + + EaFcb->Header.FileSize.LowPart = UnwindPrevFileSize; + EaFcb->Header.AllocationSize.LowPart = UnwindPrevFileSize; + EaDirent->FileSize = UnwindPrevFileSize; + + if (UnwindCacheValues) { + + CcSetFileSizes( VirtualEaFile, + (PCC_FILE_SIZES)&EaFcb->Header.AllocationSize ); + } + } + + // + // If we merged the tail then split it off. + // + + if (UnwindMergedTail) { + + VBO NewTailPosition; + + NewTailPosition = EaSetVbo + EaSetLength; + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + NewTailPosition, + &EaTailMcb ); + } + + // + // If we merged the new ea data then split it out. + // + + if (UnwindMergedNewEaSet) { + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaSetVbo, + &EaSetMcb ); + } + + // + // If we merged the initial ea data then split it out. + // + + if (UnwindMergedInitialEa) { + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaNewOffsetVbo + BytesPerCluster, + &EaInitialEaMcb ); + } + + // + // If we added a new offset cluster, then split it out. + // + + if (UnwindMergedNewOffset) { + + FatSplitAllocation( IrpContext, + Vcb, + &EaFcb->Mcb, + EaNewOffsetVbo, + &EaOffsetMcb ); + } + + // + // If there is an initial ea section prior to the new section, merge + // it with the rest of the file. + // + + if (UnwindSplitInitialEa) { + + FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaInitialEaMcb ); + } + + // + // If there is a file tail split off, merge it with the + // rest of the file. + // + + if (UnwindSplitTail) { + + FatMergeAllocation( IrpContext, Vcb, &EaFcb->Mcb, &EaTailMcb ); + } + + // + // If we modified the cache initialization for the ea file, + // then throw away the ea file object. + // + + if (UnwindPurgeCacheMap) { + + Vcb->VirtualEaFile = NULL; + ObDereferenceObject( VirtualEaFile ); + } + + // + // If we split the allocation, then deallocate the block for + // the new offset information. + // + + if (UnwindSplitNewAllocation) { + + FatDeallocateDiskSpace( IrpContext, Vcb, &EaOffsetMcb ); + } + + // + // Deallocate the disk space. + // + + FatDeallocateDiskSpace( IrpContext, Vcb, &EaSetMcb ); + } + + // + // Unpin the Ea ranges. + // + + FatUnpinEaRange( IrpContext, &EaHeaderRange ); + FatUnpinEaRange( IrpContext, &EaOffsetRange ); + + // + // Uninitialize any local Mcbs + // + + if (UnwindInitializedEaSetMcb) { + + FsRtlUninitializeLargeMcb( &EaSetMcb ); + } + + if (UnwindInitializedOffsetMcb) { + + FsRtlUninitializeLargeMcb( &EaOffsetMcb ); + } + + if (UnwindInitializedTailMcb) { + + FsRtlUninitializeLargeMcb( &EaTailMcb ); + } + + if (UnwindInitializedInitialEaMcb) { + + FsRtlUninitializeLargeMcb( &EaInitialEaMcb ); + } + + DebugTrace(-1, Dbg, "FatAddEaSet -> Exit\n", 0); + } _SEH2_END; + + return; +} + + +VOID +FatAppendPackedEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_SET_HEADER *EaSetHeader, + IN OUT PULONG PackedEasLength, + IN OUT PULONG AllocationLength, + IN PFILE_FULL_EA_INFORMATION FullEa, + IN ULONG BytesPerCluster + ) + +/*++ + +Routine Description: + + This routine appends a new packed ea onto an existing packed ea list, + it also will allocate/dealloate pool as necessary to hold the ea list. + +Arguments: + + EaSetHeader - Supplies the address to store the pointer to pool memory + which contains the Ea list for a file. + + PackedEasLength - Supplies the length of the actual Ea data. The + new Ea data will be appended at this point. + + AllocationLength - Supplies the allocated length available for Ea + data. + + FullEa - Supplies a pointer to the new full ea that is to be appended + (in packed form) to the packed ea list. + + BytesPerCluster - Number of bytes per cluster on this volume. + + NOTE: The EaSetHeader refers to the entire block of Ea data for a + file. This includes the Ea's and their values as well as the + header information. The PackedEasLength and AllocationLength + parameters refer to the name/value pairs only. + +Return Value: + + None. + +--*/ + +{ + ULONG PackedEaSize; + PPACKED_EA ThisPackedEa; + OEM_STRING EaName; + + DebugTrace(+1, Dbg, "FatAppendPackedEa...\n", 0); + + // + // As a quick check see if the computed packed ea size plus the + // current packed ea list size will overflow the buffer. Full Ea and + // packed Ea only differ by 4 in their size + // + + PackedEaSize = SizeOfFullEa( FullEa ) - 4; + + if ( PackedEaSize + *PackedEasLength > *AllocationLength ) { + + // + // We will overflow our current work buffer so allocate a larger + // one and copy over the current buffer + // + + PVOID Temp; + ULONG NewAllocationSize; + ULONG OldAllocationSize; + + DebugTrace(0, Dbg, "Allocate a new ea list buffer\n", 0); + + // + // Compute a new size and allocate space. Always increase the + // allocation in cluster increments. + // + + NewAllocationSize = (SIZE_OF_EA_SET_HEADER + + PackedEaSize + + *PackedEasLength + + BytesPerCluster - 1) + & ~(BytesPerCluster - 1); + + Temp = FsRtlAllocatePoolWithTag( PagedPool, + NewAllocationSize, + TAG_EA_SET_HEADER ); + + // + // Move over the existing ea list, and deallocate the old one + // + + RtlCopyMemory( Temp, + *EaSetHeader, + OldAllocationSize = *AllocationLength + + SIZE_OF_EA_SET_HEADER ); + + ExFreePool( *EaSetHeader ); + + // + // Set up so we will use the new packed ea list + // + + *EaSetHeader = Temp; + + // + // Zero out the added memory. + // + + RtlZeroMemory( &(*EaSetHeader)->PackedEas[*AllocationLength], + NewAllocationSize - OldAllocationSize ); + + *AllocationLength = NewAllocationSize - SIZE_OF_EA_SET_HEADER; + } + + // + // Determine if we need to increment our need ea changes count + // + + if ( FlagOn(FullEa->Flags, FILE_NEED_EA )) { + + // + // The NeedEaCount field is long aligned so we will write + // directly to it. + // + + (*EaSetHeader)->NeedEaCount++; + } + + // + // Now copy over the ea, full ea's and packed ea are identical except + // that full ea also have a next ea offset that we skip over + // + // Before: + // UsedSize Allocated + // | | + // V V + // +xxxxxxxx+-----------------------------+ + // + // After: + // UsedSize Allocated + // | | + // V V + // +xxxxxxxx+yyyyyyyyyyyyyyyy+------------+ + // + + ThisPackedEa = (PPACKED_EA) (RtlOffsetToPointer( (*EaSetHeader)->PackedEas, + *PackedEasLength )); + + RtlCopyMemory( ThisPackedEa, + (PUCHAR) FullEa + 4, + PackedEaSize ); + + // + // Now convert the name to uppercase. + // + + EaName.MaximumLength = EaName.Length = FullEa->EaNameLength; + EaName.Buffer = ThisPackedEa->EaName; + + FatUpcaseEaName( IrpContext, &EaName, &EaName ); + + // + // Increment the used size in the packed ea list structure + // + + *PackedEasLength += PackedEaSize; + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatAppendPackedEa -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +VOID +FatDeletePackedEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_SET_HEADER EaSetHeader, + IN OUT PULONG PackedEasLength, + IN ULONG Offset + ) + +/*++ + +Routine Description: + + This routine deletes an individual packed ea from the supplied + packed ea list. + +Arguments: + + EaSetHeader - Supplies the address to store the pointer to pool memory + which contains the Ea list for a file. + + PackedEasLength - Supplies the length of the actual Ea data. The + new Ea data will be appended at this point. + + Offset - Supplies the offset to the individual ea in the list to delete + + NOTE: The EaSetHeader refers to the entire block of Ea data for a + file. This includes the Ea's and their values as well as the + header information. The PackedEasLength parameter refer to the + name/value pairs only. + +Return Value: + + None. + +--*/ + +{ + PPACKED_EA PackedEa; + ULONG PackedEaSize; + + DebugTrace(+1, Dbg, "FatDeletePackedEa, Offset = %08lx\n", Offset); + + // + // Get a reference to the packed ea and figure out its size + // + + PackedEa = (PPACKED_EA) (&EaSetHeader->PackedEas[Offset]); + + SizeOfPackedEa( PackedEa, &PackedEaSize ); + + // + // Determine if we need to decrement our need ea changes count + // + + if (FlagOn(PackedEa->Flags, EA_NEED_EA_FLAG)) { + + EaSetHeader->NeedEaCount--; + } + + // + // Shrink the ea list over the deleted ea. The amount to copy is the + // total size of the ea list minus the offset to the end of the ea + // we're deleting. + // + // Before: + // Offset Offset+PackedEaSize UsedSize Allocated + // | | | | + // V V V V + // +xxxxxxxx+yyyyyyyyyyyyyyyy+zzzzzzzzzzzzzzzzzz+------------+ + // + // After + // Offset UsedSize Allocated + // | | | + // V V V + // +xxxxxxxx+zzzzzzzzzzzzzzzzzz+-----------------------------+ + // + + RtlCopyMemory( PackedEa, + (PUCHAR) PackedEa + PackedEaSize, + *PackedEasLength - (Offset + PackedEaSize) ); + + // + // And zero out the remaing part of the ea list, to make things + // nice and more robust + // + + RtlZeroMemory( &EaSetHeader->PackedEas[*PackedEasLength - PackedEaSize], + PackedEaSize ); + + // + // Decrement the used size by the amount we just removed + // + + *PackedEasLength -= PackedEaSize; + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatDeletePackedEa -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +ULONG +FatLocateNextEa ( + IN PIRP_CONTEXT IrpContext, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + IN ULONG PreviousOffset + ) + +/*++ + +Routine Description: + + This routine locates the offset for the next individual packed ea + inside of a packed ea list, given the offset to a previous Ea. + Instead of returing boolean to indicate if we've found the next one + we let the return offset be so large that it overuns the used size + of the packed ea list, and that way it's an easy construct to use + in a for loop. + +Arguments: + + FirstPackedEa - Supplies a pointer to the packed ea list structure + + PackedEasLength - Supplies the length of the packed ea list + + PreviousOffset - Supplies the offset to a individual packed ea in the + list + +Return Value: + + ULONG - The offset to the next ea in the list or 0xffffffff of one + does not exist. + +--*/ + +{ + PPACKED_EA PackedEa; + ULONG PackedEaSize; + ULONG Offset; + + DebugTrace(+1, Dbg, "FatLocateNextEa, PreviousOffset = %08lx\n", + PreviousOffset); + + // + // Make sure the previous offset is within the used size range + // + + if ( PreviousOffset >= PackedEasLength ) { + + DebugTrace(-1, Dbg, "FatLocateNextEa -> 0xffffffff\n", 0); + return 0xffffffff; + } + + // + // Get a reference to the previous packed ea, and compute its size + // + + PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + PreviousOffset ); + SizeOfPackedEa( PackedEa, &PackedEaSize ); + + // + // Compute to the next ea + // + + Offset = PreviousOffset + PackedEaSize; + + // + // Now, if the new offset is beyond the ea size then we know + // that there isn't one so, we return an offset of 0xffffffff. + // otherwise we'll leave the new offset alone. + // + + if ( Offset >= PackedEasLength ) { + + Offset = 0xffffffff; + } + + DebugTrace(-1, Dbg, "FatLocateNextEa -> %08lx\n", Offset); + + UNREFERENCED_PARAMETER( IrpContext ); + + return Offset; +} + + +BOOLEAN +FatLocateEaByName ( + IN PIRP_CONTEXT IrpContext, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + IN POEM_STRING EaName, + OUT PULONG Offset + ) + +/*++ + +Routine Description: + + This routine locates the offset for the next individual packed ea + inside of a packed ea list, given the name of the ea to locate + +Arguments: + + FirstPackedEa - Supplies a pointer to the packed ea list structure + + PackedEasLength - Supplies the length of the packed ea list + + EaName - Supplies the name of the ea search for + + Offset - Receives the offset to the located individual ea in the list + if one exists. + +Return Value: + + BOOLEAN - TRUE if the named packed ea exists in the list and FALSE + otherwise. + +--*/ + +{ + PPACKED_EA PackedEa; + OEM_STRING Name; + + DebugTrace(+1, Dbg, "FatLocateEaByName, EaName = %Z\n", EaName); + + // + // For each packed ea in the list check its name against the + // ea name we're searching for + // + + for ( *Offset = 0; + *Offset < PackedEasLength; + *Offset = FatLocateNextEa( IrpContext, + FirstPackedEa, + PackedEasLength, + *Offset )) { + + // + // Reference the packed ea and get a string to its name + // + + PackedEa = (PPACKED_EA) ((PUCHAR) FirstPackedEa + *Offset); + + Name.Buffer = &PackedEa->EaName[0]; + Name.Length = PackedEa->EaNameLength; + Name.MaximumLength = PackedEa->EaNameLength; + + // + // Compare the two strings, if they are equal then we've + // found the caller's ea + // + + if ( RtlCompareString( EaName, &Name, TRUE ) == 0 ) { + + DebugTrace(-1, Dbg, "FatLocateEaByName -> TRUE, *Offset = %08lx\n", *Offset); + return TRUE; + } + } + + // + // We've exhausted the ea list without finding a match so return false + // + + DebugTrace(-1, Dbg, "FatLocateEaByName -> FALSE\n", 0); + return FALSE; +} + + +BOOLEAN +FatIsEaNameValid ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING Name + ) + +/*++ + +Routine Description: + + This routine simple returns whether the specified file names conforms + to the file system specific rules for legal Ea names. + + For Ea names, the following rules apply: + + A. An Ea name may not contain any of the following characters: + + 0x0000 - 0x001F \ / : * ? " < > | , + = [ ] ; + +Arguments: + + Name - Supllies the name to check. + +Return Value: + + BOOLEAN - TRUE if the name is legal, FALSE otherwise. + +--*/ + +{ + ULONG Index; + + UCHAR Char; + + // + // Empty names are not valid. + // + + if ( Name.Length == 0 ) { return FALSE; } + + // + // At this point we should only have a single name, which can't have + // more than 254 characters + // + + if ( Name.Length > 254 ) { return FALSE; } + + for ( Index = 0; Index < (ULONG)Name.Length; Index += 1 ) { + + Char = Name.Buffer[ Index ]; + + // + // Skip over and Dbcs chacters + // + + if ( FsRtlIsLeadDbcsCharacter( Char ) ) { + + ASSERT( Index != (ULONG)(Name.Length - 1) ); + + Index += 1; + + continue; + } + + // + // Make sure this character is legal, and if a wild card, that + // wild cards are permissible. + // + + if ( !FsRtlIsAnsiCharacterLegalFat(Char, FALSE) ) { + + return FALSE; + } + } + + return TRUE; +} + + +VOID +FatPinEaRange ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT VirtualEaFile, + IN PFCB EaFcb, + IN OUT PEA_RANGE EaRange, + IN ULONG StartingVbo, + IN ULONG Length, + IN NTSTATUS ErrorStatus + ) + +/*++ + +Routine Description: + + This routine is called to pin a range within the Ea file. It will follow all the + rules required by the cache manager so that we don't have overlapping pin operations. + If the range being pinned spans a section then the desired data will be copied into + an auxilary buffer. FatMarkEaRangeDirty will know whether to copy the data back + into the cache or whether to simply mark the pinned data dirty. + +Arguments: + + VirtualEaFile - This is the stream file for the Ea file. + + EaFcb - This is the Fcb for the Ea file. + + EaRange - This is the Ea range structure for this request. + + StartingVbo - This is the starting offset in the Ea file to read from. + + Length - This is the length of the read. + + ErrorStatus - This is the error status to use if we are reading outside + of the file. + +Return Value: + + None. + +--*/ + +{ + LARGE_INTEGER LargeVbo; + ULONG ByteCount; + PBCB *NextBcb; + PVOID Buffer; + PCHAR DestinationBuffer; + BOOLEAN FirstPage = TRUE; + + // + // Verify that the entire read is contained within the Ea file. + // + + if (Length == 0 + || StartingVbo >= EaFcb->Header.AllocationSize.LowPart + || (EaFcb->Header.AllocationSize.LowPart - StartingVbo) < Length) { + + FatRaiseStatus( IrpContext, ErrorStatus ); + } + + // + // If the read will span a section, the system addresses may not be contiguous. + // Allocate a separate buffer in this case. + // + + if (((StartingVbo & (EA_SECTION_SIZE - 1)) + Length) > EA_SECTION_SIZE) { + + EaRange->Data = FsRtlAllocatePoolWithTag( PagedPool, + Length, + TAG_EA_DATA ); + EaRange->AuxilaryBuffer = TRUE; + + DestinationBuffer = EaRange->Data; + + } else { + + // + // PREfix correctly notes that if we don't decide here to have an aux buffer + // and the flag is up in the EaRange, we'll party on random memory since + // DestinationBuffer won't be set; however, this will never happen due to + // initialization of ea ranges and the cleanup in UnpinEaRange. + // + + ASSERT( EaRange->AuxilaryBuffer == FALSE ); + } + + + // + // If the read will require more pages than our structure will hold then + // allocate an auxilary buffer. We have to figure the number of pages + // being requested so we have to include the page offset of the first page of + // the request. + // + + EaRange->BcbChainLength = (USHORT) (((StartingVbo & (PAGE_SIZE - 1)) + Length + PAGE_SIZE - 1) / PAGE_SIZE); + + if (EaRange->BcbChainLength > EA_BCB_ARRAY_SIZE) { + + EaRange->BcbChain = FsRtlAllocatePoolWithTag( PagedPool, + sizeof( PBCB ) * EaRange->BcbChainLength, + TAG_BCB ); + + RtlZeroMemory( EaRange->BcbChain, sizeof( PBCB ) * EaRange->BcbChainLength ); + + } else { + + EaRange->BcbChain = (PBCB *) &EaRange->BcbArray; + } + + // + // Store the byte range data in the Ea Range structure. + // + + EaRange->StartingVbo = StartingVbo; + EaRange->Length = Length; + + // + // Compute the initial pin length. + // + + ByteCount = PAGE_SIZE - (StartingVbo & (PAGE_SIZE - 1)); + + // + // For each page in the range; pin the page and update the Bcb count, copy to + // the auxiliary buffer. + // + + NextBcb = EaRange->BcbChain; + + while (Length != 0) { + + // + // Pin the page and remember the data start. + // + + LargeVbo.QuadPart = StartingVbo; + + if (ByteCount > Length) { + + ByteCount = Length; + } + + if (!CcPinRead( VirtualEaFile, + &LargeVbo, + ByteCount, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + NextBcb, + &Buffer )) { + + // + // Could not read the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Increment the Bcb pointer and copy to the auxilary buffer if necessary. + // + + NextBcb += 1; + + if (EaRange->AuxilaryBuffer == TRUE) { + + RtlCopyMemory( DestinationBuffer, + Buffer, + ByteCount ); + + DestinationBuffer = (PCHAR) Add2Ptr( DestinationBuffer, ByteCount ); + } + + StartingVbo += ByteCount; + Length -= ByteCount; + + // + // If this is the first page then update the Ea Range structure. + // + + if (FirstPage) { + + FirstPage = FALSE; + ByteCount = PAGE_SIZE; + + if (EaRange->AuxilaryBuffer == FALSE) { + + EaRange->Data = Buffer; + } + } + } + + return; +} + + +VOID +FatMarkEaRangeDirty ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT EaFileObject, + IN OUT PEA_RANGE EaRange + ) + +/*++ + +Routine Description: + + This routine is called to mark a range of the Ea file as dirty. If the modified + data is sitting in an auxilary buffer then we will copy it back into the cache. + In any case we will go through the list of Bcb's and mark them dirty. + +Arguments: + + EaFileObject - This is the file object for the Ea file. + + EaRange - This is the Ea range structure for this request. + +Return Value: + + None. + +--*/ + +{ + PBCB *NextBcb; + ULONG BcbCount; + + // + // If there is an auxilary buffer we need to copy the data back into the cache. + // + + if (EaRange->AuxilaryBuffer == TRUE) { + + LARGE_INTEGER LargeVbo; + + LargeVbo.QuadPart = EaRange->StartingVbo; + + CcCopyWrite( EaFileObject, + &LargeVbo, + EaRange->Length, + TRUE, + EaRange->Data ); + } + + // + // Now walk through the Bcb chain and mark everything dirty. + // + + BcbCount = EaRange->BcbChainLength; + NextBcb = EaRange->BcbChain; + + while (BcbCount--) { + + if (*NextBcb != NULL) { + + CcSetDirtyPinnedData( *NextBcb, NULL ); + } + + NextBcb += 1; + } + + return; +} + + +VOID +FatUnpinEaRange ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_RANGE EaRange + ) + +/*++ + +Routine Description: + + This routine is called to unpin a range in the Ea file. Any structures allocated + will be deallocated here. + +Arguments: + + EaRange - This is the Ea range structure for this request. + +Return Value: + + None. + +--*/ + +{ + PBCB *NextBcb; + ULONG BcbCount; + + // + // If we allocated a auxilary buffer, deallocate it here. + // + + if (EaRange->AuxilaryBuffer == TRUE) { + + ExFreePool( EaRange->Data ); + EaRange->AuxilaryBuffer = FALSE; + } + + // + // Walk through the Bcb chain and unpin the data. + // + + if (EaRange->BcbChain != NULL) { + + BcbCount = EaRange->BcbChainLength; + NextBcb = EaRange->BcbChain; + + while (BcbCount--) { + + if (*NextBcb != NULL) { + + CcUnpinData( *NextBcb ); + *NextBcb = NULL; + } + + NextBcb += 1; + } + + // + // If we allocated a Bcb chain, deallocate it here. + // + + if (EaRange->BcbChain != &EaRange->BcbArray[0]) { + + ExFreePool( EaRange->BcbChain ); + } + + EaRange->BcbChain = NULL; + } + + return; +} + diff --git a/drivers/filesystems/fastfat_new/fastfat.rc b/drivers/filesystems/fastfat_new/fastfat.rc new file mode 100644 index 00000000000..10e3dbd4b22 --- /dev/null +++ b/drivers/filesystems/fastfat_new/fastfat.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "VFAT IFS Driver" +#define REACTOS_STR_INTERNAL_NAME "vfatfs" +#define REACTOS_STR_ORIGINAL_FILENAME "vfatfs.sys" +#include diff --git a/drivers/filesystems/fastfat_new/fat.h b/drivers/filesystems/fastfat_new/fat.h new file mode 100644 index 00000000000..6cc0ac76eca --- /dev/null +++ b/drivers/filesystems/fastfat_new/fat.h @@ -0,0 +1,729 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Fat.h + +Abstract: + + This module defines the on-disk structure of the Fat file system. + + +--*/ + +#ifndef _FAT_ +#define _FAT_ + +// +// The following nomenclature is used to describe the Fat on-disk +// structure: +// +// LBN - is the number of a sector relative to the start of the disk. +// +// VBN - is the number of a sector relative to the start of a file, +// directory, or allocation. +// +// LBO - is a byte offset relative to the start of the disk. +// +// VBO - is a byte offset relative to the start of a file, directory +// or allocation. +// + +typedef LONGLONG LBO; /* for Fat32, LBO is >32 bits */ + +typedef LBO *PLBO; + +typedef ULONG32 VBO; +typedef VBO *PVBO; + + +// +// The boot sector is the first physical sector (LBN == 0) on the volume. +// Part of the sector contains a BIOS Parameter Block. The BIOS in the +// sector is packed (i.e., unaligned) so we'll supply a unpacking macro +// to translate a packed BIOS into its unpacked equivalent. The unpacked +// BIOS structure is already defined in ntioapi.h so we only need to define +// the packed BIOS. +// + +// +// Define the Packed and Unpacked BIOS Parameter Block +// + +typedef struct _PACKED_BIOS_PARAMETER_BLOCK { + UCHAR BytesPerSector[2]; // offset = 0x000 0 + UCHAR SectorsPerCluster[1]; // offset = 0x002 2 + UCHAR ReservedSectors[2]; // offset = 0x003 3 + UCHAR Fats[1]; // offset = 0x005 5 + UCHAR RootEntries[2]; // offset = 0x006 6 + UCHAR Sectors[2]; // offset = 0x008 8 + UCHAR Media[1]; // offset = 0x00A 10 + UCHAR SectorsPerFat[2]; // offset = 0x00B 11 + UCHAR SectorsPerTrack[2]; // offset = 0x00D 13 + UCHAR Heads[2]; // offset = 0x00F 15 + UCHAR HiddenSectors[4]; // offset = 0x011 17 + UCHAR LargeSectors[4]; // offset = 0x015 21 +} PACKED_BIOS_PARAMETER_BLOCK; // sizeof = 0x019 25 +typedef PACKED_BIOS_PARAMETER_BLOCK *PPACKED_BIOS_PARAMETER_BLOCK; + +typedef struct _PACKED_BIOS_PARAMETER_BLOCK_EX { + UCHAR BytesPerSector[2]; // offset = 0x000 0 + UCHAR SectorsPerCluster[1]; // offset = 0x002 2 + UCHAR ReservedSectors[2]; // offset = 0x003 3 + UCHAR Fats[1]; // offset = 0x005 5 + UCHAR RootEntries[2]; // offset = 0x006 6 + UCHAR Sectors[2]; // offset = 0x008 8 + UCHAR Media[1]; // offset = 0x00A 10 + UCHAR SectorsPerFat[2]; // offset = 0x00B 11 + UCHAR SectorsPerTrack[2]; // offset = 0x00D 13 + UCHAR Heads[2]; // offset = 0x00F 15 + UCHAR HiddenSectors[4]; // offset = 0x011 17 + UCHAR LargeSectors[4]; // offset = 0x015 21 + UCHAR LargeSectorsPerFat[4]; // offset = 0x019 25 + UCHAR ExtendedFlags[2]; // offset = 0x01D 29 + UCHAR FsVersion[2]; // offset = 0x01F 31 + UCHAR RootDirFirstCluster[4]; // offset = 0x021 33 + UCHAR FsInfoSector[2]; // offset = 0x025 37 + UCHAR BackupBootSector[2]; // offset = 0x027 39 + UCHAR Reserved[12]; // offset = 0x029 41 +} PACKED_BIOS_PARAMETER_BLOCK_EX; // sizeof = 0x035 53 + +typedef PACKED_BIOS_PARAMETER_BLOCK_EX *PPACKED_BIOS_PARAMETER_BLOCK_EX; + +// +// The IsBpbFat32 macro is defined to work with both packed and unpacked +// BPB structures. Since we are only checking for zero, the byte order +// does not matter. +// + +#define IsBpbFat32(bpb) (*(USHORT *)(&(bpb)->SectorsPerFat) == 0) + +typedef struct BIOS_PARAMETER_BLOCK { + USHORT BytesPerSector; + UCHAR SectorsPerCluster; + USHORT ReservedSectors; + UCHAR Fats; + USHORT RootEntries; + USHORT Sectors; + UCHAR Media; + USHORT SectorsPerFat; + USHORT SectorsPerTrack; + USHORT Heads; + ULONG32 HiddenSectors; + ULONG32 LargeSectors; + ULONG32 LargeSectorsPerFat; + union { + USHORT ExtendedFlags; + struct { + ULONG ActiveFat:4; + ULONG Reserved0:3; + ULONG MirrorDisabled:1; + ULONG Reserved1:8; + }; + }; + USHORT FsVersion; + ULONG32 RootDirFirstCluster; + USHORT FsInfoSector; + USHORT BackupBootSector; +} BIOS_PARAMETER_BLOCK, *PBIOS_PARAMETER_BLOCK; + +// +// This macro takes a Packed BIOS and fills in its Unpacked equivalent +// + +#define FatUnpackBios(Bios,Pbios) { \ + CopyUchar2(&(Bios)->BytesPerSector, &(Pbios)->BytesPerSector[0] ); \ + CopyUchar1(&(Bios)->SectorsPerCluster, &(Pbios)->SectorsPerCluster[0]); \ + CopyUchar2(&(Bios)->ReservedSectors, &(Pbios)->ReservedSectors[0] ); \ + CopyUchar1(&(Bios)->Fats, &(Pbios)->Fats[0] ); \ + CopyUchar2(&(Bios)->RootEntries, &(Pbios)->RootEntries[0] ); \ + CopyUchar2(&(Bios)->Sectors, &(Pbios)->Sectors[0] ); \ + CopyUchar1(&(Bios)->Media, &(Pbios)->Media[0] ); \ + CopyUchar2(&(Bios)->SectorsPerFat, &(Pbios)->SectorsPerFat[0] ); \ + CopyUchar2(&(Bios)->SectorsPerTrack, &(Pbios)->SectorsPerTrack[0] ); \ + CopyUchar2(&(Bios)->Heads, &(Pbios)->Heads[0] ); \ + CopyUchar4(&(Bios)->HiddenSectors, &(Pbios)->HiddenSectors[0] ); \ + CopyUchar4(&(Bios)->LargeSectors, &(Pbios)->LargeSectors[0] ); \ + CopyUchar4(&(Bios)->LargeSectorsPerFat,&((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->LargeSectorsPerFat[0] ); \ + CopyUchar2(&(Bios)->ExtendedFlags, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->ExtendedFlags[0] ); \ + CopyUchar2(&(Bios)->FsVersion, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->FsVersion[0] ); \ + CopyUchar4(&(Bios)->RootDirFirstCluster, \ + &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->RootDirFirstCluster[0] ); \ + CopyUchar2(&(Bios)->FsInfoSector, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->FsInfoSector[0] ); \ + CopyUchar2(&(Bios)->BackupBootSector, &((PPACKED_BIOS_PARAMETER_BLOCK_EX)Pbios)->BackupBootSector[0] ); \ +} + +// +// Define the boot sector +// + +typedef struct _PACKED_BOOT_SECTOR { + UCHAR Jump[3]; // offset = 0x000 0 + UCHAR Oem[8]; // offset = 0x003 3 + PACKED_BIOS_PARAMETER_BLOCK PackedBpb; // offset = 0x00B 11 + UCHAR PhysicalDriveNumber; // offset = 0x024 36 + UCHAR CurrentHead; // offset = 0x025 37 + UCHAR Signature; // offset = 0x026 38 + UCHAR Id[4]; // offset = 0x027 39 + UCHAR VolumeLabel[11]; // offset = 0x02B 43 + UCHAR SystemId[8]; // offset = 0x036 54 +} PACKED_BOOT_SECTOR; // sizeof = 0x03E 62 + +typedef PACKED_BOOT_SECTOR *PPACKED_BOOT_SECTOR; + +typedef struct _PACKED_BOOT_SECTOR_EX { + UCHAR Jump[3]; // offset = 0x000 0 + UCHAR Oem[8]; // offset = 0x003 3 + PACKED_BIOS_PARAMETER_BLOCK_EX PackedBpb; // offset = 0x00B 11 + UCHAR PhysicalDriveNumber; // offset = 0x040 64 + UCHAR CurrentHead; // offset = 0x041 65 + UCHAR Signature; // offset = 0x042 66 + UCHAR Id[4]; // offset = 0x043 67 + UCHAR VolumeLabel[11]; // offset = 0x047 71 + UCHAR SystemId[8]; // offset = 0x058 88 +} PACKED_BOOT_SECTOR_EX; // sizeof = 0x060 96 + +typedef PACKED_BOOT_SECTOR_EX *PPACKED_BOOT_SECTOR_EX; + +// +// Define the FAT32 FsInfo sector. +// + +typedef struct _FSINFO_SECTOR { + ULONG SectorBeginSignature; // offset = 0x000 0 + UCHAR ExtraBootCode[480]; // offset = 0x004 4 + ULONG FsInfoSignature; // offset = 0x1e4 484 + ULONG FreeClusterCount; // offset = 0x1e8 488 + ULONG NextFreeCluster; // offset = 0x1ec 492 + UCHAR Reserved[12]; // offset = 0x1f0 496 + ULONG SectorEndSignature; // offset = 0x1fc 508 +} FSINFO_SECTOR, *PFSINFO_SECTOR; + +#define FSINFO_SECTOR_BEGIN_SIGNATURE 0x41615252 +#define FSINFO_SECTOR_END_SIGNATURE 0xAA550000 + +#define FSINFO_SIGNATURE 0x61417272 + +// +// We use the CurrentHead field for our dirty partition info. +// + +#define FAT_BOOT_SECTOR_DIRTY 0x01 +#define FAT_BOOT_SECTOR_TEST_SURFACE 0x02 + +// +// Define a Fat Entry type. +// +// This type is used when representing a fat table entry. It also used +// to be used when dealing with a fat table index and a count of entries, +// but the ensuing type casting nightmare sealed this fate. These other +// two types are represented as ULONGs. +// + +typedef ULONG32 FAT_ENTRY; + +#define FAT32_ENTRY_MASK 0x0FFFFFFFUL + +// +// We use these special index values to set the dirty info for +// DOS/Win9x compatibility. +// + +#define FAT_CLEAN_VOLUME (~FAT32_ENTRY_MASK | 0) +#define FAT_DIRTY_VOLUME (~FAT32_ENTRY_MASK | 1) + +#define FAT_DIRTY_BIT_INDEX 1 + +// +// Physically, the entry is fully set if clean, and the high +// bit knocked out if it is dirty (i.e., it is really a clean +// bit). This means it is different per-FAT size. +// + +#define FAT_CLEAN_ENTRY (~0) + +#define FAT12_DIRTY_ENTRY 0x7ff +#define FAT16_DIRTY_ENTRY 0x7fff +#define FAT32_DIRTY_ENTRY 0x7fffffff + +// +// The following constants the are the valid Fat index values. +// + +#define FAT_CLUSTER_AVAILABLE (FAT_ENTRY)0x00000000 +#define FAT_CLUSTER_RESERVED (FAT_ENTRY)0x0ffffff0 +#define FAT_CLUSTER_BAD (FAT_ENTRY)0x0ffffff7 +#define FAT_CLUSTER_LAST (FAT_ENTRY)0x0fffffff + +// +// Fat files have the following time/date structures. Note that the +// following structure is a 32 bits long but USHORT aligned. +// + +typedef struct _FAT_TIME { + + USHORT DoubleSeconds : 5; + USHORT Minute : 6; + USHORT Hour : 5; + +} FAT_TIME; +typedef FAT_TIME *PFAT_TIME; + +typedef struct _FAT_DATE { + + USHORT Day : 5; + USHORT Month : 4; + USHORT Year : 7; // Relative to 1980 + +} FAT_DATE; +typedef FAT_DATE *PFAT_DATE; + +typedef struct _FAT_TIME_STAMP { + + FAT_TIME Time; + FAT_DATE Date; + +} FAT_TIME_STAMP; +typedef FAT_TIME_STAMP *PFAT_TIME_STAMP; + +// +// Fat files have 8 character file names and 3 character extensions +// + +typedef UCHAR FAT8DOT3[11]; +typedef FAT8DOT3 *PFAT8DOT3; + + +// +// The directory entry record exists for every file/directory on the +// disk except for the root directory. +// + +typedef struct _PACKED_DIRENT { + FAT8DOT3 FileName; // offset = 0 + UCHAR Attributes; // offset = 11 + UCHAR NtByte; // offset = 12 + UCHAR CreationMSec; // offset = 13 + FAT_TIME_STAMP CreationTime; // offset = 14 + FAT_DATE LastAccessDate; // offset = 18 + union { + USHORT ExtendedAttributes; // offset = 20 + USHORT FirstClusterOfFileHi; // offset = 20 + }; + FAT_TIME_STAMP LastWriteTime; // offset = 22 + USHORT FirstClusterOfFile; // offset = 26 + ULONG32 FileSize; // offset = 28 +} PACKED_DIRENT; // sizeof = 32 +typedef PACKED_DIRENT *PPACKED_DIRENT; + +// +// A packed dirent is already quadword aligned so simply declare a dirent as a +// packed dirent +// + +typedef PACKED_DIRENT DIRENT; +typedef DIRENT *PDIRENT; + +// +// The first byte of a dirent describes the dirent. There is also a routine +// to help in deciding how to interpret the dirent. +// + +#define FAT_DIRENT_NEVER_USED 0x00 +#define FAT_DIRENT_REALLY_0E5 0x05 +#define FAT_DIRENT_DIRECTORY_ALIAS 0x2e +#define FAT_DIRENT_DELETED 0xe5 + +// +// Define the NtByte bits. +// + +#define FAT_DIRENT_NT_BYTE_8_LOWER_CASE 0x08 +#define FAT_DIRENT_NT_BYTE_3_LOWER_CASE 0x10 + +// +// Define the various dirent attributes +// + +#define FAT_DIRENT_ATTR_READ_ONLY 0x01 +#define FAT_DIRENT_ATTR_HIDDEN 0x02 +#define FAT_DIRENT_ATTR_SYSTEM 0x04 +#define FAT_DIRENT_ATTR_VOLUME_ID 0x08 +#define FAT_DIRENT_ATTR_DIRECTORY 0x10 +#define FAT_DIRENT_ATTR_ARCHIVE 0x20 +#define FAT_DIRENT_ATTR_DEVICE 0x40 +#define FAT_DIRENT_ATTR_LFN (FAT_DIRENT_ATTR_READ_ONLY | \ + FAT_DIRENT_ATTR_HIDDEN | \ + FAT_DIRENT_ATTR_SYSTEM | \ + FAT_DIRENT_ATTR_VOLUME_ID) + + +// +// These macros convert a number of fields in the Bpb to bytes from sectors +// +// ULONG +// FatBytesPerCluster ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// +// ULONG +// FatBytesPerFat ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// +// ULONG +// FatReservedBytes ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +#define FatBytesPerCluster(B) ((ULONG)((B)->BytesPerSector * (B)->SectorsPerCluster)) + +#define FatBytesPerFat(B) (IsBpbFat32(B)? \ + ((ULONG)((B)->BytesPerSector * (B)->LargeSectorsPerFat)) : \ + ((ULONG)((B)->BytesPerSector * (B)->SectorsPerFat))) + +#define FatReservedBytes(B) ((ULONG)((B)->BytesPerSector * (B)->ReservedSectors)) + +// +// This macro returns the size of the root directory dirent area in bytes +// For Fat32, the root directory is variable in length. This macro returns +// 0 because it is also used to determine the location of cluster 2. +// +// ULONG +// FatRootDirectorySize ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +#define FatRootDirectorySize(B) ((ULONG)((B)->RootEntries * sizeof(DIRENT))) + + +// +// This macro returns the first Lbo (zero based) of the root directory on +// the device. This area is after the reserved and fats. +// +// For Fat32, the root directory is moveable. This macro returns the LBO +// for cluster 2 because it is used to determine the location of cluster 2. +// FatRootDirectoryLbo32() returns the actual LBO of the beginning of the +// actual root directory. +// +// LBO +// FatRootDirectoryLbo ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +#define FatRootDirectoryLbo(B) (FatReservedBytes(B) + ((B)->Fats * FatBytesPerFat(B))) +#define FatRootDirectoryLbo32(B) (FatFileAreaLbo(B)+((B)->RootDirFirstCluster-2)*FatBytesPerCluster(B)) + +// +// This macro returns the first Lbo (zero based) of the file area on the +// the device. This area is after the reserved, fats, and root directory. +// +// LBO +// FatFirstFileAreaLbo ( +// IN PBIOS_PARAMTER_BLOCK Bios +// ); +// + +#define FatFileAreaLbo(B) (FatRootDirectoryLbo(B) + FatRootDirectorySize(B)) + +// +// This macro returns the number of clusters on the disk. This value is +// computed by taking the total sectors on the disk subtracting up to the +// first file area sector and then dividing by the sectors per cluster count. +// Note that I don't use any of the above macros since far too much +// superfluous sector/byte conversion would take place. +// +// ULONG +// FatNumberOfClusters ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +// +// for prior to MS-DOS Version 3.2 +// +// After DOS 4.0, at least one of these, Sectors or LargeSectors, will be zero. +// but DOS version 3.2 case, both of these value might contains some value, +// because, before 3.2, we don't have Large Sector entry, some disk might have +// unexpected value in the field, we will use LargeSectors if Sectors eqaul to zero. +// + +#define FatNumberOfClusters(B) ( \ + \ + IsBpbFat32(B) ? \ + \ + ((((B)->Sectors ? (B)->Sectors : (B)->LargeSectors) \ + \ + - ((B)->ReservedSectors + \ + (B)->Fats * (B)->LargeSectorsPerFat )) \ + \ + / \ + \ + (B)->SectorsPerCluster) \ + : \ + ((((B)->Sectors ? (B)->Sectors : (B)->LargeSectors) \ + \ + - ((B)->ReservedSectors + \ + (B)->Fats * (B)->SectorsPerFat + \ + (B)->RootEntries * sizeof(DIRENT) / (B)->BytesPerSector ) ) \ + \ + / \ + \ + (B)->SectorsPerCluster) \ +) + +// +// This macro returns the fat table bit size (i.e., 12 or 16 bits) +// +// ULONG +// FatIndexBitSize ( +// IN PBIOS_PARAMETER_BLOCK Bios +// ); +// + +#define FatIndexBitSize(B) \ + ((UCHAR)(IsBpbFat32(B) ? 32 : (FatNumberOfClusters(B) < 4087 ? 12 : 16))) + +// +// This macro raises STATUS_FILE_CORRUPT and marks the Fcb bad if an +// index value is not within the proper range. +// Note that the first two index values are invalid (0, 1), so we must +// add two from the top end to make sure the everything is within range +// +// VOID +// FatVerifyIndexIsValid ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN ULONG Index +// ); +// + +#define FatVerifyIndexIsValid(IC,V,I) { \ + if (((I) < 2) || ((I) > ((V)->AllocationSupport.NumberOfClusters + 1))) { \ + FatRaiseStatus(IC,STATUS_FILE_CORRUPT_ERROR); \ + } \ +} + +// +// These two macros are used to translate between Logical Byte Offsets, +// and fat entry indexes. Note the use of variables stored in the Vcb. +// These two macros are used at a higher level than the other macros +// above. +// +// Note, these indexes are true cluster numbers. +// +// LBO +// GetLboFromFatIndex ( +// IN FAT_ENTRY Fat_Index, +// IN PVCB Vcb +// ); +// +// FAT_ENTRY +// GetFatIndexFromLbo ( +// IN LBO Lbo, +// IN PVCB Vcb +// ); +// + +#define FatGetLboFromIndex(VCB,FAT_INDEX) ( \ + ( (LBO) \ + (VCB)->AllocationSupport.FileAreaLbo + \ + (((LBO)((FAT_INDEX) - 2)) << (VCB)->AllocationSupport.LogOfBytesPerCluster) \ + ) \ +) + +#define FatGetIndexFromLbo(VCB,LBO) ( \ + (ULONG) ( \ + (((LBO) - (VCB)->AllocationSupport.FileAreaLbo) >> \ + (VCB)->AllocationSupport.LogOfBytesPerCluster) + 2 \ + ) \ +) + +// +// The following macro does the shifting and such to lookup an entry +// +// VOID +// FatLookup12BitEntry( +// IN PVOID Fat, +// IN FAT_ENTRY Index, +// OUT PFAT_ENTRY Entry +// ); +// + +#define FatLookup12BitEntry(FAT,INDEX,ENTRY) { \ + \ + CopyUchar2((PUCHAR)(ENTRY), (PUCHAR)(FAT) + (INDEX) * 3 / 2); \ + \ + *ENTRY = (FAT_ENTRY)(0xfff & (((INDEX) & 1) ? (*(ENTRY) >> 4) : \ + *(ENTRY))); \ +} + +// +// The following macro does the tmp shifting and such to store an entry +// +// VOID +// FatSet12BitEntry( +// IN PVOID Fat, +// IN FAT_ENTRY Index, +// IN FAT_ENTRY Entry +// ); +// + +#define FatSet12BitEntry(FAT,INDEX,ENTRY) { \ + \ + FAT_ENTRY TmpFatEntry; \ + \ + CopyUchar2((PUCHAR)&TmpFatEntry, (PUCHAR)(FAT) + (INDEX) * 3 / 2); \ + \ + TmpFatEntry = (FAT_ENTRY) \ + (((INDEX) & 1) ? ((ENTRY) << 4) | (TmpFatEntry & 0xf) \ + : (ENTRY) | (TmpFatEntry & 0xf000)); \ + \ + *((UNALIGNED UCHAR2 *)((PUCHAR)(FAT) + (INDEX) * 3 / 2)) = *((UNALIGNED UCHAR2 *)(&TmpFatEntry)); \ +} + +// +// The following macro compares two FAT_TIME_STAMPs +// + +#define FatAreTimesEqual(TIME1,TIME2) ( \ + RtlEqualMemory((TIME1),(TIME2), sizeof(FAT_TIME_STAMP)) \ +) + + +#define EA_FILE_SIGNATURE (0x4445) // "ED" +#define EA_SET_SIGNATURE (0x4145) // "EA" + +// +// If the volume contains any ea data then there is one EA file called +// "EA DATA. SF" located in the root directory as Hidden, System and +// ReadOnly. +// + +typedef struct _EA_FILE_HEADER { + USHORT Signature; // offset = 0 + USHORT FormatType; // offset = 2 + USHORT LogType; // offset = 4 + USHORT Cluster1; // offset = 6 + USHORT NewCValue1; // offset = 8 + USHORT Cluster2; // offset = 10 + USHORT NewCValue2; // offset = 12 + USHORT Cluster3; // offset = 14 + USHORT NewCValue3; // offset = 16 + USHORT Handle; // offset = 18 + USHORT NewHOffset; // offset = 20 + UCHAR Reserved[10]; // offset = 22 + USHORT EaBaseTable[240]; // offset = 32 +} EA_FILE_HEADER; // sizeof = 512 + +typedef EA_FILE_HEADER *PEA_FILE_HEADER; + +typedef USHORT EA_OFF_TABLE[128]; + +typedef EA_OFF_TABLE *PEA_OFF_TABLE; + +// +// Every file with an extended attribute contains in its dirent an index +// into the EaMapTable. The map table contains an offset within the ea +// file (cluster aligned) of the ea data for the file. The individual +// ea data for each file is prefaced with an Ea Data Header. +// + +typedef struct _EA_SET_HEADER { + USHORT Signature; // offset = 0 + USHORT OwnEaHandle; // offset = 2 + ULONG32 NeedEaCount; // offset = 4 + UCHAR OwnerFileName[14]; // offset = 8 + UCHAR Reserved[4]; // offset = 22 + UCHAR cbList[4]; // offset = 26 + UCHAR PackedEas[1]; // offset = 30 +} EA_SET_HEADER; // sizeof = 30 +typedef EA_SET_HEADER *PEA_SET_HEADER; + +#define SIZE_OF_EA_SET_HEADER 30 + +#define MAXIMUM_EA_SIZE 0x0000ffff + +#define GetcbList(EASET) (((EASET)->cbList[0] << 0) + \ + ((EASET)->cbList[1] << 8) + \ + ((EASET)->cbList[2] << 16) + \ + ((EASET)->cbList[3] << 24)) + +#define SetcbList(EASET,CB) { \ + (EASET)->cbList[0] = (CB >> 0) & 0x0ff; \ + (EASET)->cbList[1] = (CB >> 8) & 0x0ff; \ + (EASET)->cbList[2] = (CB >> 16) & 0x0ff; \ + (EASET)->cbList[3] = (CB >> 24) & 0x0ff; \ +} + +// +// Every individual ea in an ea set is declared the following packed ea +// + +typedef struct _PACKED_EA { + UCHAR Flags; + UCHAR EaNameLength; + UCHAR EaValueLength[2]; + CHAR EaName[1]; +} PACKED_EA; +typedef PACKED_EA *PPACKED_EA; + +// +// The following two macros are used to get and set the ea value length +// field of a packed ea +// +// VOID +// GetEaValueLength ( +// IN PPACKED_EA Ea, +// OUT PUSHORT ValueLength +// ); +// +// VOID +// SetEaValueLength ( +// IN PPACKED_EA Ea, +// IN USHORT ValueLength +// ); +// + +#define GetEaValueLength(EA,LEN) { \ + *(LEN) = 0; \ + CopyUchar2( (LEN), (EA)->EaValueLength ); \ +} + +#define SetEaValueLength(EA,LEN) { \ + CopyUchar2( &((EA)->EaValueLength), (LEN) ); \ +} + +// +// The following macro is used to get the size of a packed ea +// +// VOID +// SizeOfPackedEa ( +// IN PPACKED_EA Ea, +// OUT PUSHORT EaSize +// ); +// + +#define SizeOfPackedEa(EA,SIZE) { \ + ULONG _NL,_DL; _NL = 0; _DL = 0; \ + CopyUchar1(&_NL, &(EA)->EaNameLength); \ + GetEaValueLength(EA, &_DL); \ + *(SIZE) = 1 + 1 + 2 + _NL + 1 + _DL; \ +} + +#define EA_NEED_EA_FLAG 0x80 +#define MIN_EA_HANDLE 1 +#define MAX_EA_HANDLE 30719 +#define UNUSED_EA_HANDLE 0xffff +#define EA_CBLIST_OFFSET 0x1a +#define MAX_EA_BASE_INDEX 240 +#define MAX_EA_OFFSET_INDEX 128 + + +#endif // _FAT_ + diff --git a/drivers/filesystems/fastfat_new/fatdata.c b/drivers/filesystems/fastfat_new/fatdata.c new file mode 100644 index 00000000000..c7b41a2d167 --- /dev/null +++ b/drivers/filesystems/fastfat_new/fatdata.c @@ -0,0 +1,1424 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FatData.c + +Abstract: + + This module declares the global data used by the Fat file system. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FATDATA) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS) + +#ifdef ALLOC_PRAGMA + +#if DBG +#pragma alloc_text(PAGE, FatBugCheckExceptionFilter) +#endif + +#pragma alloc_text(PAGE, FatCompleteRequest_Real) +#pragma alloc_text(PAGE, FatFastIoCheckIfPossible) +#pragma alloc_text(PAGE, FatFastQueryBasicInfo) +#pragma alloc_text(PAGE, FatFastQueryNetworkOpenInfo) +#pragma alloc_text(PAGE, FatFastQueryStdInfo) +#pragma alloc_text(PAGE, FatIsIrpTopLevel) +#pragma alloc_text(PAGE, FatPopUpFileCorrupt) +#pragma alloc_text(PAGE, FatProcessException) +#endif + + +// +// The global fsd data record, and zero large integer +// + +FAT_DATA FatData; + +PDEVICE_OBJECT FatDiskFileSystemDeviceObject; +PDEVICE_OBJECT FatCdromFileSystemDeviceObject; + +#ifndef __REACTOS__ +LARGE_INTEGER FatLargeZero = {0,0}; +LARGE_INTEGER FatMaxLarge = {MAXULONG,MAXLONG}; +#else +LARGE_INTEGER FatLargeZero = {{0}}; +LARGE_INTEGER FatMaxLarge = {.LowPart = MAXULONG, .HighPart = MAXLONG}; +#endif + +#ifndef __REACTOS__ +LARGE_INTEGER Fat30Milliseconds = {(ULONG)(-30 * 1000 * 10), -1}; +LARGE_INTEGER Fat100Milliseconds = {(ULONG)(-30 * 1000 * 10), -1}; +LARGE_INTEGER FatOneDay = {0x2a69c000, 0xc9}; +LARGE_INTEGER FatJanOne1980 = {0xe1d58000,0x01a8e79f}; +LARGE_INTEGER FatDecThirtyOne1979 = {0xb76bc000,0x01a8e6d6}; +#else +LARGE_INTEGER Fat30Milliseconds = {.LowPart = (ULONG)(-30 * 1000 * 10), .HighPart = -1}; +LARGE_INTEGER Fat100Milliseconds = {.LowPart = (ULONG)(-30 * 1000 * 10), .HighPart = -1}; +LARGE_INTEGER FatOneDay = {.LowPart = 0x2a69c000, .HighPart = 0xc9}; +LARGE_INTEGER FatJanOne1980 = {.LowPart = 0xe1d58000, .HighPart = 0x01a8e79f}; +LARGE_INTEGER FatDecThirtyOne1979 = {.LowPart = 0xb76bc000, .HighPart = 0x01a8e6d6}; +#endif + +FAT_TIME_STAMP FatTimeJanOne1980 = {{0,0,0},{1,1,0}}; + +#ifndef __REACTOS__ +LARGE_INTEGER FatMagic10000 = {0xe219652c, 0xd1b71758}; +LARGE_INTEGER FatMagic86400000 = {0xfa67b90e, 0xc6d750eb}; +#else +LARGE_INTEGER FatMagic10000 = {.LowPart = 0xe219652c, .HighPart = 0xd1b71758}; +LARGE_INTEGER FatMagic86400000 = {.LowPart = 0xfa67b90e, .HighPart = 0xc6d750eb}; +#endif + +FAST_IO_DISPATCH FatFastIoDispatch; + +// +// Our lookaside lists. +// + +NPAGED_LOOKASIDE_LIST FatIrpContextLookasideList; +NPAGED_LOOKASIDE_LIST FatNonPagedFcbLookasideList; +NPAGED_LOOKASIDE_LIST FatEResourceLookasideList; + +SLIST_HEADER FatCloseContextSList; + +// +// Synchronization for the close queue +// + +FAST_MUTEX FatCloseQueueMutex; + +// +// Reserve MDL for paging file operations. +// + +#ifndef __REACTOS__ +PMDL FatReserveMdl = NULL; +#else +volatile PMDL FatReserveMdl = NULL; +#endif +KEVENT FatReserveEvent; + +#ifdef FASTFATDBG + +LONG FatDebugTraceLevel = 0x00000009; +LONG FatDebugTraceIndent = 0; + +ULONG FatFsdEntryCount = 0; +ULONG FatFspEntryCount = 0; +ULONG FatIoCallDriverCount = 0; + +LONG FatPerformanceTimerLevel = 0x00000000; + +ULONG FatTotalTicks[32] = { 0 }; + +// +// I need this because C can't support conditional compilation within +// a macro. +// + +PVOID FatNull = NULL; + +#endif // FASTFATDBG + +#if DBG + +NTSTATUS FatAssertNotStatus = STATUS_SUCCESS; +BOOLEAN FatTestRaisedStatus = FALSE; + +#endif + + +#if DBG +ULONG +FatBugCheckExceptionFilter ( + IN PEXCEPTION_POINTERS ExceptionPointer + ) + +/*++ + +Routine Description: + + An exception filter which acts as an assert that the exception should + never occur. + + This is only valid on debug builds, we don't want the overhead on retail. + +Arguments: + + ExceptionPointers - The result of GetExceptionInformation() in the context + of the exception. + +Return Value: + + Bugchecks. + +--*/ + +{ + FatBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord, + (ULONG_PTR)ExceptionPointer->ContextRecord, + (ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress ); + + return EXCEPTION_EXECUTE_HANDLER; +} +#endif + + +ULONG +FatExceptionFilter ( + IN PIRP_CONTEXT IrpContext, + IN PEXCEPTION_POINTERS ExceptionPointer + ) + +/*++ + +Routine Description: + + This routine is used to decide if we should or should not handle + an exception status that is being raised. It inserts the status + into the IrpContext and either indicates that we should handle + the exception or bug check the system. + +Arguments: + + ExceptionPointers - The result of GetExceptionInformation() in the context + of the exception. + +Return Value: + + ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks + +--*/ + +{ + NTSTATUS ExceptionCode; + + ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode; + DebugTrace(0, DEBUG_TRACE_UNWIND, "FatExceptionFilter %X\n", ExceptionCode); + DebugDump("FatExceptionFilter\n", Dbg, NULL ); + + // + // If the exception is STATUS_IN_PAGE_ERROR, get the I/O error code + // from the exception record. + // + + if (ExceptionCode == STATUS_IN_PAGE_ERROR) { + if (ExceptionPointer->ExceptionRecord->NumberParameters >= 3) { + ExceptionCode = (NTSTATUS)ExceptionPointer->ExceptionRecord->ExceptionInformation[2]; + } + } + + // + // If there is not an irp context, we must have had insufficient resources. + // + + if ( !ARGUMENT_PRESENT( IrpContext ) ) { + + if (!FsRtlIsNtstatusExpected( ExceptionCode )) { + + FatBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord, + (ULONG_PTR)ExceptionPointer->ContextRecord, + (ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress ); + } + + return EXCEPTION_EXECUTE_HANDLER; + } + + // + // For the purposes of processing this exception, let's mark this + // request as being able to wait and disable write through if we + // aren't posting it. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + if ( (ExceptionCode != STATUS_CANT_WAIT) && + (ExceptionCode != STATUS_VERIFY_REQUIRED) ) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH); + } + + if ( IrpContext->ExceptionStatus == 0 ) { + + if (FsRtlIsNtstatusExpected( ExceptionCode )) { + + IrpContext->ExceptionStatus = ExceptionCode; + + return EXCEPTION_EXECUTE_HANDLER; + + } else { + + FatBugCheck( (ULONG_PTR)ExceptionPointer->ExceptionRecord, + (ULONG_PTR)ExceptionPointer->ContextRecord, + (ULONG_PTR)ExceptionPointer->ExceptionRecord->ExceptionAddress ); + } + + } else { + + // + // We raised this code explicitly ourselves, so it had better be + // expected. + // + + ASSERT( IrpContext->ExceptionStatus == ExceptionCode ); + ASSERT( FsRtlIsNtstatusExpected( ExceptionCode ) ); + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +NTSTATUS +FatProcessException ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN NTSTATUS ExceptionCode + ) + +/*++ + +Routine Description: + + This routine process an exception. It either completes the request + with the saved exception status or it sends it off to IoRaiseHardError() + +Arguments: + + Irp - Supplies the Irp being processed + + ExceptionCode - Supplies the normalized exception status being handled + +Return Value: + + NTSTATUS - Returns the results of either posting the Irp or the + saved completion status. + +--*/ + +{ + PVCB Vcb; + PIO_STACK_LOCATION IrpSp; + FAT_VOLUME_STATE TransitionState = VolumeDirty; + ULONG SavedFlags; + + DebugTrace(0, Dbg, "FatProcessException\n", 0); + + // + // If there is not an irp context, we must have had insufficient resources. + // + + if ( !ARGUMENT_PRESENT( IrpContext ) ) { + + FatCompleteRequest( FatNull, Irp, ExceptionCode ); + + return ExceptionCode; + } + + // + // Get the real exception status from IrpContext->ExceptionStatus, and + // reset it. + // + + ExceptionCode = IrpContext->ExceptionStatus; + FatResetExceptionState( IrpContext ); + + // + // If we are going to post the request, we may have to lock down the + // user's buffer, so do it here in a try except so that we failed the + // request if the LockPages fails. + // + // Also unpin any repinned Bcbs, protected by the try {} except {} filter. + // + + _SEH2_TRY { + + SavedFlags = IrpContext->Flags; + + // + // Make sure we don't try to write through Bcbs + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH); + + FatUnpinRepinnedBcbs( IrpContext ); + + IrpContext->Flags = SavedFlags; + + // + // If we will have to post the request, do it here. Note + // that the last thing FatPrePostIrp() does is mark the Irp pending, + // so it is critical that we actually return PENDING. Nothing + // from this point to return can fail, so we are OK. + // + // We cannot do a verify operations at APC level because we + // have to wait for Io operations to complete. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) && + (((ExceptionCode == STATUS_VERIFY_REQUIRED) && (KeGetCurrentIrql() >= APC_LEVEL)) || + (ExceptionCode == STATUS_CANT_WAIT))) { + + ExceptionCode = FatFsdPostRequest( IrpContext, Irp ); + } + + } _SEH2_EXCEPT( FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() ) ) { + + ExceptionCode = IrpContext->ExceptionStatus; + IrpContext->ExceptionStatus = 0; + + IrpContext->Flags = SavedFlags; + } _SEH2_END; + + // + // If we posted the request, just return here. + // + + if (ExceptionCode == STATUS_PENDING) { + + return ExceptionCode; + } + + Irp->IoStatus.Status = ExceptionCode; + + // + // If this request is not a "top-level" irp, just complete it. + // + + if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)) { + + // + // If there is a cache operation above us, commute verify + // to a lock conflict. This will cause retries so that + // we have a chance of getting through without needing + // to return an unaesthetic error for the operation. + // + + if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP && + ExceptionCode == STATUS_VERIFY_REQUIRED) { + + ExceptionCode = STATUS_FILE_LOCK_CONFLICT; + } + + FatCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + } + + if (IoIsErrorUserInduced(ExceptionCode)) { + + // + // Check for the various error conditions that can be caused by, + // and possibly resolved by the user. + // + + if (ExceptionCode == STATUS_VERIFY_REQUIRED) { + + PDEVICE_OBJECT Device; + + DebugTrace(0, Dbg, "Perform Verify Operation\n", 0); + + // + // Now we are at the top level file system entry point. + // + // Grab the device to verify from the thread local storage + // and stick it in the information field for transportation + // to the fsp. We also clear the field at this time. + // + + Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread ); + IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL ); + + if ( Device == NULL ) { + + Device = IoGetDeviceToVerify( PsGetCurrentThread() ); + IoSetDeviceToVerify( PsGetCurrentThread(), NULL ); + + ASSERT( Device != NULL ); + } + + // + // Let's not BugCheck just because the driver messed up. + // + + if (Device == NULL) { + + ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR; + + FatCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + } + + // + // FatPerformVerify() will do the right thing with the Irp. + + return FatPerformVerify( IrpContext, Irp, Device ); + } + + // + // The other user induced conditions generate an error unless + // they have been disabled for this request. + // + + if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS)) { + + FatCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + + } else { + + // + // Generate a pop-up + // + + PDEVICE_OBJECT RealDevice; + PVPB Vpb; + PETHREAD Thread; + + if (IoGetCurrentIrpStackLocation(Irp)->FileObject != NULL) { + + Vpb = IoGetCurrentIrpStackLocation(Irp)->FileObject->Vpb; + + } else { + + Vpb = NULL; + } + + // + // The device to verify is either in my thread local storage + // or that of the thread that owns the Irp. + // + + Thread = Irp->Tail.Overlay.Thread; + RealDevice = IoGetDeviceToVerify( Thread ); + + if ( RealDevice == NULL ) { + + Thread = PsGetCurrentThread(); + RealDevice = IoGetDeviceToVerify( Thread ); + + ASSERT( RealDevice != NULL ); + } + + // + // Let's not BugCheck just because the driver messed up. + // + + if (RealDevice == NULL) { + + FatCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; + } + + // + // This routine actually causes the pop-up. It usually + // does this by queuing an APC to the callers thread, + // but in some cases it will complete the request immediately, + // so it is very important to IoMarkIrpPending() first. + // + + IoMarkIrpPending( Irp ); + IoRaiseHardError( Irp, Vpb, RealDevice ); + + // + // We will be handing control back to the caller here, so + // reset the saved device object. + // + + IoSetDeviceToVerify( Thread, NULL ); + + // + // The Irp will be completed by Io or resubmitted. In either + // case we must clean up the IrpContext here. + // + + FatDeleteIrpContext( IrpContext ); + return STATUS_PENDING; + } + } + + // + // This is just a run of the mill error. If is a STATUS that we + // raised ourselves, and the information would be use for the + // user, raise an informational pop-up. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + Vcb = IrpContext->Vcb; + + // + // Now, if the Vcb is unknown to us this means that the error was raised + // in the process of a mount and before we even had a chance to build + // a full Vcb - and was really handled there. + // + + if (Vcb != NULL) { + + if ( !FatDeviceIsFatFsdo( IrpSp->DeviceObject) && + !NT_SUCCESS(ExceptionCode) && + !FsRtlIsTotalDeviceFailure(ExceptionCode) ) { + + TransitionState = VolumeDirtyWithSurfaceTest; + } + + // + // If this was a STATUS_FILE_CORRUPT or similar error indicating some + // nastiness out on the media, then mark the volume permanently dirty. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS) && + ( TransitionState == VolumeDirtyWithSurfaceTest || + (ExceptionCode == STATUS_FILE_CORRUPT_ERROR) || + (ExceptionCode == STATUS_DISK_CORRUPT_ERROR) || + (ExceptionCode == STATUS_EA_CORRUPT_ERROR) || + (ExceptionCode == STATUS_INVALID_EA_NAME) || + (ExceptionCode == STATUS_EA_LIST_INCONSISTENT) || + (ExceptionCode == STATUS_NO_EAS_ON_FILE) )) { + + ASSERT( NodeType(Vcb) == FAT_NTC_VCB ); + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + + // + // Do the "dirty" work, ignoring any error. + // + + _SEH2_TRY { + + FatMarkVolume( IrpContext, Vcb, TransitionState ); + + } _SEH2_EXCEPT( FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() ) ) { + + NOTHING; + } _SEH2_END; + } + } + + FatCompleteRequest( IrpContext, Irp, ExceptionCode ); + + return ExceptionCode; +} + + +VOID +FatCompleteRequest_Real ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PIRP Irp OPTIONAL, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine completes a Irp + +Arguments: + + Irp - Supplies the Irp being processed + + Status - Supplies the status to complete the Irp with + +Return Value: + + None. + +--*/ + +{ + // + // If we have an Irp Context then unpin all of the repinned bcbs + // we might have collected. + // + + if (IrpContext != NULL) { + + ASSERT( IrpContext->Repinned.Bcb[0] == NULL ); + + FatUnpinRepinnedBcbs( IrpContext ); + } + + // + // Delete the Irp context before completing the IRP so if + // we run into some of the asserts, we can still backtrack + // through the IRP. + // + + if (IrpContext != NULL) { + + FatDeleteIrpContext( IrpContext ); + } + + // + // If we have an Irp then complete the irp. + // + + if (Irp != NULL) { + + // + // We got an error, so zero out the information field before + // completing the request if this was an input operation. + // Otherwise IopCompleteRequest will try to copy to the user's buffer. + // + + if ( NT_ERROR(Status) && + FlagOn(Irp->Flags, IRP_INPUT_OPERATION) ) { + + Irp->IoStatus.Information = 0; + } + + Irp->IoStatus.Status = Status; + + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + } + + return; +} + +BOOLEAN +FatIsIrpTopLevel ( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine detects if an Irp is the Top level requestor, ie. if it os OK + to do a verify or pop-up now. If TRUE is returned, then no file system + resources are held above us. + +Arguments: + + Irp - Supplies the Irp being processed + + Status - Supplies the status to complete the Irp with + +Return Value: + + None. + +--*/ + +{ + if ( IoGetTopLevelIrp() == NULL ) { + + IoSetTopLevelIrp( Irp ); + + return TRUE; + + } else { + + return FALSE; + } +} + + +BOOLEAN +NTAPI +FatFastIoCheckIfPossible ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + IN BOOLEAN CheckForReadOperation, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine checks if fast i/o is possible for a read/write operation + +Arguments: + + FileObject - Supplies the file object used in the query + + FileOffset - Supplies the starting byte offset for the read/write operation + + Length - Supplies the length, in bytes, of the read/write operation + + Wait - Indicates if we can wait + + LockKey - Supplies the lock key + + CheckForReadOperation - Indicates if this is a check for a read or write + operation + + IoStatus - Receives the status of the operation if our return value is + FastIoReturnError + +Return Value: + + BOOLEAN - TRUE if fast I/O is possible and FALSE if the caller needs + to take the long route. + +--*/ + +{ + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + LARGE_INTEGER LargeLength; + + // + // Decode the file object to get our fcb, the only one we want + // to deal with is a UserFileOpen + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + return FALSE; + } + + LargeLength.QuadPart = Length; + + // + // Based on whether this is a read or write operation we call + // fsrtl check for read/write + // + + if (CheckForReadOperation) { + + if (FsRtlFastCheckLockForRead( &Fcb->Specific.Fcb.FileLock, + FileOffset, + &LargeLength, + LockKey, + FileObject, + PsGetCurrentProcess() )) { + + return TRUE; + } + + } else { + + // + // Also check for a write-protected volume here. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) && + FsRtlFastCheckLockForWrite( &Fcb->Specific.Fcb.FileLock, + FileOffset, + &LargeLength, + LockKey, + FileObject, + PsGetCurrentProcess() )) { + + return TRUE; + } + } + + return FALSE; +} + + +BOOLEAN +NTAPI +FatFastQueryBasicInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_BASIC_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine is for the fast query call for basic file information. + +Arguments: + + FileObject - Supplies the file object used in this operation + + Wait - Indicates if we are allowed to wait for the information + + Buffer - Supplies the output buffer to receive the basic information + + IoStatus - Receives the final status of the operation + +Return Value: + + BOOLEAN - TRUE if the operation succeeded and FALSE if the caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + IRP_CONTEXT IrpContext; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN FcbAcquired = FALSE; + + // + // Prepare the dummy irp context + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext.NodeByteSize = sizeof(IRP_CONTEXT); + + if (Wait) { + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + + } else { + + ClearFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Determine the type of open for the input file object and only accept + // the user file or directory open + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) { + + return Results; + } + + FsRtlEnterFileSystem(); + + // + // Get access to the Fcb but only if it is not the paging file + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + if (!ExAcquireResourceSharedLite( Fcb->Header.Resource, Wait )) { + + FsRtlExitFileSystem(); + return Results; + } + + FcbAcquired = TRUE; + } + + _SEH2_TRY { + + // + // If the Fcb is not in a good state, return FALSE. + // + + if (Fcb->FcbCondition != FcbGood) { + + try_return( Results ); + } + + Buffer->FileAttributes = 0; + + // + // If the fcb is not the root dcb then we will fill in the + // buffer otherwise it is all setup for us. + // + + if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) { + + // + // Extract the data and fill in the non zero fields of the output + // buffer + // + + Buffer->LastWriteTime = Fcb->LastWriteTime; + Buffer->CreationTime = Fcb->CreationTime; + Buffer->LastAccessTime = Fcb->LastAccessTime; + + // + // Zero out the field we don't support. + // + + Buffer->ChangeTime.QuadPart = 0; + Buffer->FileAttributes = Fcb->DirentFatFlags; + + } else { + + Buffer->LastWriteTime.QuadPart = 0; + Buffer->CreationTime.QuadPart = 0; + Buffer->LastAccessTime.QuadPart = 0; + Buffer->ChangeTime.QuadPart = 0; + + Buffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY; + } + + // + // If the temporary flag is set, then set it in the buffer. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { + + SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); + } + + // + // If no attributes were set, set the normal bit. + // + + if (Buffer->FileAttributes == 0) { + + Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; + } + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof(FILE_BASIC_INFORMATION); + + Results = TRUE; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + if (FcbAcquired) { ExReleaseResourceLite( Fcb->Header.Resource ); } + + FsRtlExitFileSystem(); + } _SEH2_END; + + // + // And return to our caller + // + + return Results; +} + + +BOOLEAN +NTAPI +FatFastQueryStdInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine is for the fast query call for standard file information. + +Arguments: + + FileObject - Supplies the file object used in this operation + + Wait - Indicates if we are allowed to wait for the information + + Buffer - Supplies the output buffer to receive the basic information + + IoStatus - Receives the final status of the operation + +Return Value: + + BOOLEAN - TRUE if the operation succeeded and FALSE if the caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + IRP_CONTEXT IrpContext; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN FcbAcquired = FALSE; + + // + // Prepare the dummy irp context + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext.NodeByteSize = sizeof(IRP_CONTEXT); + + if (Wait) { + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + + } else { + + ClearFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Determine the type of open for the input file object and only accept + // the user file or directory open + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) { + + return Results; + } + + // + // Get access to the Fcb but only if it is not the paging file + // + + FsRtlEnterFileSystem(); + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + if (!ExAcquireResourceSharedLite( Fcb->Header.Resource, Wait )) { + + FsRtlExitFileSystem(); + return Results; + } + + FcbAcquired = TRUE; + } + + _SEH2_TRY { + + // + // If the Fcb is not in a good state, return FALSE. + // + + if (Fcb->FcbCondition != FcbGood) { + + try_return( Results ); + } + + Buffer->NumberOfLinks = 1; + Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + + // + // Case on whether this is a file or a directory, and extract + // the information and fill in the fcb/dcb specific parts + // of the output buffer. + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + // + // If we don't alread know the allocation size, we cannot look + // it up in the fast path. + // + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + try_return( Results ); + } + + Buffer->AllocationSize = Fcb->Header.AllocationSize; + Buffer->EndOfFile = Fcb->Header.FileSize; + + Buffer->Directory = FALSE; + + } else { + + Buffer->AllocationSize = FatLargeZero; + Buffer->EndOfFile = FatLargeZero; + + Buffer->Directory = TRUE; + } + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof(FILE_STANDARD_INFORMATION); + + Results = TRUE; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + if (FcbAcquired) { ExReleaseResourceLite( Fcb->Header.Resource ); } + + FsRtlExitFileSystem(); + } _SEH2_END; + + // + // And return to our caller + // + + return Results; +} + +BOOLEAN +NTAPI +FatFastQueryNetworkOpenInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This routine is for the fast query call for network open information. + +Arguments: + + FileObject - Supplies the file object used in this operation + + Wait - Indicates if we are allowed to wait for the information + + Buffer - Supplies the output buffer to receive the information + + IoStatus - Receives the final status of the operation + +Return Value: + + BOOLEAN - TRUE if the operation succeeded and FALSE if the caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results = FALSE; + IRP_CONTEXT IrpContext; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN FcbAcquired = FALSE; + + // + // Prepare the dummy irp context + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext.NodeByteSize = sizeof(IRP_CONTEXT); + + if (Wait) { + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + + } else { + + ClearFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Determine the type of open for the input file object and only accept + // the user file or directory open + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) { + + return Results; + } + + FsRtlEnterFileSystem(); + + // + // Get access to the Fcb but only if it is not the paging file + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + if (!ExAcquireResourceSharedLite( Fcb->Header.Resource, Wait )) { + + FsRtlExitFileSystem(); + return Results; + } + + FcbAcquired = TRUE; + } + + _SEH2_TRY { + + // + // If the Fcb is not in a good state, return FALSE. + // + + if (Fcb->FcbCondition != FcbGood) { + + try_return( Results ); + } + + // + // Extract the data and fill in the non zero fields of the output + // buffer + // + + // + // Default the field we don't support to a reasonable value. + // + + ExLocalTimeToSystemTime( &FatJanOne1980, + &Buffer->ChangeTime ); + + if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) { + + // + // Reuse the default for the root dir. + // + + Buffer->CreationTime = + Buffer->LastAccessTime = + Buffer->LastWriteTime = Buffer->ChangeTime; + + } else { + + Buffer->LastWriteTime = Fcb->LastWriteTime; + Buffer->CreationTime = Fcb->CreationTime; + Buffer->LastAccessTime = Fcb->LastAccessTime; + } + + Buffer->FileAttributes = Fcb->DirentFatFlags; + + // + // If the temporary flag is set, then set it in the buffer. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { + + SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); + } + + // + // If no attributes were set, set the normal bit. + // + + if (Buffer->FileAttributes == 0) { + + Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; + } + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + // + // If we don't already know the allocation size, we cannot + // lock it up in the fast path. + // + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + try_return( Results ); + } + + Buffer->AllocationSize = Fcb->Header.AllocationSize; + Buffer->EndOfFile = Fcb->Header.FileSize; + + } else { + + Buffer->AllocationSize = FatLargeZero; + Buffer->EndOfFile = FatLargeZero; + } + + IoStatus->Status = STATUS_SUCCESS; + IoStatus->Information = sizeof(FILE_NETWORK_OPEN_INFORMATION); + + Results = TRUE; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + if (FcbAcquired) { ExReleaseResourceLite( Fcb->Header.Resource ); } + + FsRtlExitFileSystem(); + } _SEH2_END; + + // + // And return to our caller + // + + return Results; +} + +VOID +FatPopUpFileCorrupt ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + The Following routine makes an informational popup that the file + is corrupt. + +Arguments: + + Fcb - The file that is corrupt. + +Return Value: + + None. + +--*/ + +{ + PKTHREAD Thread; + + // + // Disable the popup on the root directory. It is important not + // to generate them on objects which are part of the mount process. + // + + if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) { + + return; + } + + // + // Got to grab the full filename now. + // + + if (Fcb->FullFileName.Buffer == NULL) { + + FatSetFullFileNameInFcb( IrpContext, Fcb ); + } + + // + // We never want to block a system thread waiting for the user to + // press OK. + // + + if (IoIsSystemThread(IrpContext->OriginatingIrp->Tail.Overlay.Thread)) { + + Thread = NULL; + + } else { + +#ifndef __REACTOS__ + Thread = IrpContext->OriginatingIrp->Tail.Overlay.Thread; +#else + Thread = (PKTHREAD)IrpContext->OriginatingIrp->Tail.Overlay.Thread; +#endif + } + + IoRaiseInformationalHardError( STATUS_FILE_CORRUPT_ERROR, + &Fcb->FullFileName, + Thread); +} + diff --git a/drivers/filesystems/fastfat_new/fatdata.h b/drivers/filesystems/fastfat_new/fatdata.h new file mode 100644 index 00000000000..0d0493f4e7d --- /dev/null +++ b/drivers/filesystems/fastfat_new/fatdata.h @@ -0,0 +1,328 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FatData.c + +Abstract: + + This module declares the global data used by the Fat file system. + + +--*/ + +#ifndef _FATDATA_ +#define _FATDATA_ + +// +// The global fsd data record, and a global zero large integer +// + +extern FAT_DATA FatData; + +extern IO_STATUS_BLOCK FatGarbageIosb; + +extern NPAGED_LOOKASIDE_LIST FatIrpContextLookasideList; +extern NPAGED_LOOKASIDE_LIST FatNonPagedFcbLookasideList; +extern NPAGED_LOOKASIDE_LIST FatEResourceLookasideList; + +extern PAGED_LOOKASIDE_LIST FatFcbLookasideList; +extern PAGED_LOOKASIDE_LIST FatCcbLookasideList; + +extern SLIST_HEADER FatCloseContextSList; +extern FAST_MUTEX FatCloseQueueMutex; + +extern PDEVICE_OBJECT FatDiskFileSystemDeviceObject; +extern PDEVICE_OBJECT FatCdromFileSystemDeviceObject; + +extern LARGE_INTEGER FatLargeZero; +extern LARGE_INTEGER FatMaxLarge; +extern LARGE_INTEGER Fat30Milliseconds; +extern LARGE_INTEGER Fat100Milliseconds; +extern LARGE_INTEGER FatOneSecond; +extern LARGE_INTEGER FatOneDay; +extern LARGE_INTEGER FatJanOne1980; +extern LARGE_INTEGER FatDecThirtyOne1979; + +extern FAT_TIME_STAMP FatTimeJanOne1980; + +extern LARGE_INTEGER FatMagic10000; +#define FAT_SHIFT10000 13 + +extern LARGE_INTEGER FatMagic86400000; +#define FAT_SHIFT86400000 26 + +#define FatConvert100nsToMilliseconds(LARGE_INTEGER) ( \ + RtlExtendedMagicDivide( (LARGE_INTEGER), FatMagic10000, FAT_SHIFT10000 )\ + ) + +#define FatConvertMillisecondsToDays(LARGE_INTEGER) ( \ + RtlExtendedMagicDivide( (LARGE_INTEGER), FatMagic86400000, FAT_SHIFT86400000 ) \ + ) + +#define FatConvertDaysToMilliseconds(DAYS) ( \ + Int32x32To64( (DAYS), 86400000 ) \ + ) + +// +// Reserve MDL for paging file io forward progress. +// + +#define FAT_RESERVE_MDL_SIZE 16 + +#ifndef __REACTOS__ +extern PMDL FatReserveMdl; +#else +extern volatile PMDL FatReserveMdl; +#endif +extern KEVENT FatReserveEvent; + +// +// The global structure used to contain our fast I/O callbacks +// + +extern FAST_IO_DISPATCH FatFastIoDispatch; + +// +// Read ahead amount used for normal data files +// + +#define READ_AHEAD_GRANULARITY (0x10000) + +// +// Define maximum number of parallel Reads or Writes that will be generated +// per one request. +// + +#define FAT_MAX_IO_RUNS_ON_STACK ((ULONG) 5) + +// +// Define the maximum number of delayed closes. +// + +#define FAT_MAX_DELAYED_CLOSES ((ULONG)16) + +extern ULONG FatMaxDelayedCloseCount; + +// +// The maximum chunk size we use when defragmenting files. +// + +#define FAT_DEFAULT_DEFRAG_CHUNK_IN_BYTES (0x10000) + +// +// Define constant for time rounding. +// + +#define TenMSec (10*1000*10) +#define TwoSeconds (2*1000*1000*10) +#define AlmostTenMSec (TenMSec - 1) +#define AlmostTwoSeconds (TwoSeconds - 1) + +// too big #define HighPartPerDay (24*60*60*1000*1000*10 >> 32) + +#define HighPartPerDay (52734375 >> 18) + +// +// The global Fat debug level variable, its values are: +// +// 0x00000000 Always gets printed (used when about to bug check) +// +// 0x00000001 Error conditions +// 0x00000002 Debug hooks +// 0x00000004 Catch exceptions before completing Irp +// 0x00000008 +// +// 0x00000010 +// 0x00000020 +// 0x00000040 +// 0x00000080 +// +// 0x00000100 +// 0x00000200 +// 0x00000400 +// 0x00000800 +// +// 0x00001000 +// 0x00002000 +// 0x00004000 +// 0x00008000 +// +// 0x00010000 +// 0x00020000 +// 0x00040000 +// 0x00080000 +// +// 0x00100000 +// 0x00200000 +// 0x00400000 +// 0x00800000 +// +// 0x01000000 +// 0x02000000 +// 0x04000000 +// 0x08000000 +// +// 0x10000000 +// 0x20000000 +// 0x40000000 +// 0x80000000 +// + +#ifdef FASTFATDBG + +#define DEBUG_TRACE_ERROR (0x00000001) +#define DEBUG_TRACE_DEBUG_HOOKS (0x00000002) +#define DEBUG_TRACE_CATCH_EXCEPTIONS (0x00000004) +#define DEBUG_TRACE_UNWIND (0x00000008) +#define DEBUG_TRACE_CLEANUP (0x00000010) +#define DEBUG_TRACE_CLOSE (0x00000020) +#define DEBUG_TRACE_CREATE (0x00000040) +#define DEBUG_TRACE_DIRCTRL (0x00000080) +#define DEBUG_TRACE_EA (0x00000100) +#define DEBUG_TRACE_FILEINFO (0x00000200) +#define DEBUG_TRACE_FSCTRL (0x00000400) +#define DEBUG_TRACE_LOCKCTRL (0x00000800) +#define DEBUG_TRACE_READ (0x00001000) +#define DEBUG_TRACE_VOLINFO (0x00002000) +#define DEBUG_TRACE_WRITE (0x00004000) +#define DEBUG_TRACE_FLUSH (0x00008000) +#define DEBUG_TRACE_DEVCTRL (0x00010000) +#define DEBUG_TRACE_SHUTDOWN (0x00020000) +#define DEBUG_TRACE_FATDATA (0x00040000) +#define DEBUG_TRACE_PNP (0x00080000) +#define DEBUG_TRACE_ACCHKSUP (0x00100000) +#define DEBUG_TRACE_ALLOCSUP (0x00200000) +#define DEBUG_TRACE_DIRSUP (0x00400000) +#define DEBUG_TRACE_FILOBSUP (0x00800000) +#define DEBUG_TRACE_NAMESUP (0x01000000) +#define DEBUG_TRACE_VERFYSUP (0x02000000) +#define DEBUG_TRACE_CACHESUP (0x04000000) +#define DEBUG_TRACE_SPLAYSUP (0x08000000) +#define DEBUG_TRACE_DEVIOSUP (0x10000000) +#define DEBUG_TRACE_STRUCSUP (0x20000000) +#define DEBUG_TRACE_FSP_DISPATCHER (0x40000000) +#define DEBUG_TRACE_FSP_DUMP (0x80000000) + +extern LONG FatDebugTraceLevel; +extern LONG FatDebugTraceIndent; + +#define DebugTrace(INDENT,LEVEL,X,Y) { \ + LONG _i; \ + if (((LEVEL) == 0) || (FatDebugTraceLevel & (LEVEL))) { \ + _i = (ULONG)PsGetCurrentThread(); \ + DbgPrint("%08lx:",_i); \ + if ((INDENT) < 0) { \ + FatDebugTraceIndent += (INDENT); \ + } \ + if (FatDebugTraceIndent < 0) { \ + FatDebugTraceIndent = 0; \ + } \ + for (_i = 0; _i < FatDebugTraceIndent; _i += 1) { \ + DbgPrint(" "); \ + } \ + DbgPrint(X,Y); \ + if ((INDENT) > 0) { \ + FatDebugTraceIndent += (INDENT); \ + } \ + } \ +} + +#define DebugDump(STR,LEVEL,PTR) { \ + ULONG _i; \ + VOID FatDump(IN PVOID Ptr); \ + if (((LEVEL) == 0) || (FatDebugTraceLevel & (LEVEL))) { \ + _i = (ULONG)PsGetCurrentThread(); \ + DbgPrint("%08lx:",_i); \ + DbgPrint(STR); \ + if (PTR != NULL) {FatDump(PTR);} \ + DbgBreakPoint(); \ + } \ +} + +#define DebugUnwind(X) { \ + if (AbnormalTermination()) { \ + DebugTrace(0, DEBUG_TRACE_UNWIND, #X ", Abnormal termination.\n", 0); \ + } \ +} + +// +// The following variables are used to keep track of the total amount +// of requests processed by the file system, and the number of requests +// that end up being processed by the Fsp thread. The first variable +// is incremented whenever an Irp context is created (which is always +// at the start of an Fsd entry point) and the second is incremented +// by read request. +// + +extern ULONG FatFsdEntryCount; +extern ULONG FatFspEntryCount; +extern ULONG FatIoCallDriverCount; +extern ULONG FatTotalTicks[]; + +#define DebugDoit(X) {X;} + +extern LONG FatPerformanceTimerLevel; + +#define TimerStart(LEVEL) { \ + LARGE_INTEGER TStart, TEnd; \ + LARGE_INTEGER TElapsed; \ + TStart = KeQueryPerformanceCounter( NULL ); \ + +#define TimerStop(LEVEL,s) \ + TEnd = KeQueryPerformanceCounter( NULL ); \ + TElapsed.QuadPart = TEnd.QuadPart - TStart.QuadPart; \ + FatTotalTicks[FatLogOf(LEVEL)] += TElapsed.LowPart; \ + if (FlagOn( FatPerformanceTimerLevel, (LEVEL))) { \ + DbgPrint("Time of %s %ld\n", (s), TElapsed.LowPart ); \ + } \ +} + +// +// I need this because C can't support conditional compilation within +// a macro. +// + +extern PVOID FatNull; + +#else + +#define DebugTrace(INDENT,LEVEL,X,Y) {NOTHING;} +#define DebugDump(STR,LEVEL,PTR) {NOTHING;} +#define DebugUnwind(X) {NOTHING;} +#define DebugDoit(X) {NOTHING;} + +#define TimerStart(LEVEL) +#define TimerStop(LEVEL,s) + +#define FatNull NULL + +#endif // FASTFATDBG + +// +// The following macro is for all people who compile with the DBG switch +// set, not just fastfat dbg users +// + +#if DBG + +#define DbgDoit(X) {X;} + +#else + +#define DbgDoit(X) {NOTHING;} + +#endif // DBG + +#if DBG + +extern NTSTATUS FatAssertNotStatus; +extern BOOLEAN FatTestRaisedStatus; + +#endif + +#endif // _FATDATA_ + + diff --git a/drivers/filesystems/fastfat_new/fatinit.c b/drivers/filesystems/fastfat_new/fatinit.c new file mode 100644 index 00000000000..9cbfbc634c2 --- /dev/null +++ b/drivers/filesystems/fastfat_new/fatinit.c @@ -0,0 +1,675 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FatInit.c + +Abstract: + + This module implements the DRIVER_INITIALIZATION routine for Fat + + +--*/ + +#include "fatprocs.h" + +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +NTAPI +FatUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +FatGetCompatibilityModeValue( + IN PUNICODE_STRING ValueName, + IN OUT PULONG Value + ); + +BOOLEAN +FatIsFujitsuFMR ( + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT, DriverEntry) +#pragma alloc_text(INIT, FatGetCompatibilityModeValue) +#pragma alloc_text(INIT, FatIsFujitsuFMR) +//#pragma alloc_text(PAGE, FatUnload) +#endif + +#define COMPATIBILITY_MODE_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\FileSystem" +#define COMPATIBILITY_MODE_VALUE_NAME L"Win31FileSystem" +#define CODE_PAGE_INVARIANCE_VALUE_NAME L"FatDisableCodePageInvariance" + +#define KEY_WORK_AREA ((sizeof(KEY_VALUE_FULL_INFORMATION) + \ + sizeof(ULONG)) + 64) + +#define REGISTRY_HARDWARE_DESCRIPTION_W \ + L"\\Registry\\Machine\\Hardware\\DESCRIPTION\\System" + +#define REGISTRY_MACHINE_IDENTIFIER_W L"Identifier" + +#define FUJITSU_FMR_NAME_W L"FUJITSU FMR-" + + +NTSTATUS +NTAPI +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This is the initialization routine for the Fat file system + device driver. This routine creates the device object for the FileSystem + device and performs all other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + NTSTATUS - The function value is the final status from the initialization + operation. + +--*/ + +{ + USHORT MaxDepth; + NTSTATUS Status; + UNICODE_STRING UnicodeString; + + UNICODE_STRING ValueName; + ULONG Value; + + // + // Create the device object for disks. To avoid problems with filters who + // know this name, we must keep it. + // + + RtlInitUnicodeString( &UnicodeString, L"\\Fat" ); + Status = IoCreateDevice( DriverObject, + 0, + &UnicodeString, + FILE_DEVICE_DISK_FILE_SYSTEM, + 0, + FALSE, + &FatDiskFileSystemDeviceObject ); + + if (!NT_SUCCESS( Status )) { + return Status; + } + + // + // Create the device object for "cdroms". + // + + RtlInitUnicodeString( &UnicodeString, L"\\FatCdrom" ); + Status = IoCreateDevice( DriverObject, + 0, + &UnicodeString, + FILE_DEVICE_CD_ROM_FILE_SYSTEM, + 0, + FALSE, + &FatCdromFileSystemDeviceObject ); + + if (!NT_SUCCESS( Status )) { + IoDeleteDevice( FatDiskFileSystemDeviceObject); + return Status; + } + + + DriverObject->DriverUnload = FatUnload; + +#ifdef _PNP_POWER_ + // + // This driver doesn't talk directly to a device, and (at the moment) + // isn't otherwise concerned about power management. + // + + FatDiskFileSystemDeviceObject->DeviceObjectExtension->PowerControlNeeded = FALSE; + FatCdromFileSystemDeviceObject->DeviceObjectExtension->PowerControlNeeded = FALSE; +#endif + + // + // Note that because of the way data caching is done, we set neither + // the Direct I/O or Buffered I/O bit in DeviceObject->Flags. If + // data is not in the cache, or the request is not buffered, we may, + // set up for Direct I/O by hand. + // + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)FatFsdCreate; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)FatFsdClose; + DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)FatFsdRead; + DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)FatFsdWrite; + DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)FatFsdQueryInformation; + DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)FatFsdSetInformation; + DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = (PDRIVER_DISPATCH)FatFsdQueryEa; + DriverObject->MajorFunction[IRP_MJ_SET_EA] = (PDRIVER_DISPATCH)FatFsdSetEa; + DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)FatFsdFlushBuffers; + DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)FatFsdQueryVolumeInformation; + DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)FatFsdSetVolumeInformation; + DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)FatFsdCleanup; + DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)FatFsdDirectoryControl; + DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)FatFsdFileSystemControl; + DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)FatFsdLockControl; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)FatFsdDeviceControl; + DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)FatFsdShutdown; + DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)FatFsdPnp; + + DriverObject->FastIoDispatch = &FatFastIoDispatch; + + RtlZeroMemory(&FatFastIoDispatch, sizeof(FatFastIoDispatch)); + + FatFastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH); + FatFastIoDispatch.FastIoCheckIfPossible = FatFastIoCheckIfPossible; // CheckForFastIo + FatFastIoDispatch.FastIoRead = FsRtlCopyRead; // Read + FatFastIoDispatch.FastIoWrite = FsRtlCopyWrite; // Write + FatFastIoDispatch.FastIoQueryBasicInfo = FatFastQueryBasicInfo; // QueryBasicInfo + FatFastIoDispatch.FastIoQueryStandardInfo = FatFastQueryStdInfo; // QueryStandardInfo + FatFastIoDispatch.FastIoLock = FatFastLock; // Lock + FatFastIoDispatch.FastIoUnlockSingle = FatFastUnlockSingle; // UnlockSingle + FatFastIoDispatch.FastIoUnlockAll = FatFastUnlockAll; // UnlockAll + FatFastIoDispatch.FastIoUnlockAllByKey = FatFastUnlockAllByKey; // UnlockAllByKey + FatFastIoDispatch.FastIoQueryNetworkOpenInfo = FatFastQueryNetworkOpenInfo; + FatFastIoDispatch.AcquireForCcFlush = FatAcquireForCcFlush; + FatFastIoDispatch.ReleaseForCcFlush = FatReleaseForCcFlush; + + // + // Initialize the global data structures + // + + // + // The FatData record + // + + RtlZeroMemory( &FatData, sizeof(FAT_DATA)); + + FatData.NodeTypeCode = FAT_NTC_DATA_HEADER; + FatData.NodeByteSize = sizeof(FAT_DATA); + + InitializeListHead(&FatData.VcbQueue); + + FatData.DriverObject = DriverObject; + FatData.DiskFileSystemDeviceObject = FatDiskFileSystemDeviceObject; + FatData.CdromFileSystemDeviceObject = FatCdromFileSystemDeviceObject; + + // + // This list head keeps track of closes yet to be done. + // + + InitializeListHead( &FatData.AsyncCloseList ); + InitializeListHead( &FatData.DelayedCloseList ); + + FatData.FatCloseItem = IoAllocateWorkItem( FatDiskFileSystemDeviceObject); + + if (FatData.FatCloseItem == NULL) { + IoDeleteDevice (FatDiskFileSystemDeviceObject); + IoDeleteDevice (FatCdromFileSystemDeviceObject); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Now initialize our general purpose spinlock (gag) and figure out how + // deep and wide we want our delayed lists (along with fooling ourselves + // about the lookaside depths). + // + + KeInitializeSpinLock( &FatData.GeneralSpinLock ); + + switch ( MmQuerySystemSize() ) { + + case MmSmallSystem: + + MaxDepth = 4; + FatMaxDelayedCloseCount = FAT_MAX_DELAYED_CLOSES; + break; + + case MmMediumSystem: + + MaxDepth = 8; + FatMaxDelayedCloseCount = 4 * FAT_MAX_DELAYED_CLOSES; + break; + + case MmLargeSystem: + + MaxDepth = 16; + FatMaxDelayedCloseCount = 16 * FAT_MAX_DELAYED_CLOSES; + break; + } + + + // + // Initialize the cache manager callback routines + // + + FatData.CacheManagerCallbacks.AcquireForLazyWrite = &FatAcquireFcbForLazyWrite; + FatData.CacheManagerCallbacks.ReleaseFromLazyWrite = &FatReleaseFcbFromLazyWrite; + FatData.CacheManagerCallbacks.AcquireForReadAhead = &FatAcquireFcbForReadAhead; + FatData.CacheManagerCallbacks.ReleaseFromReadAhead = &FatReleaseFcbFromReadAhead; + + FatData.CacheManagerNoOpCallbacks.AcquireForLazyWrite = &FatNoOpAcquire; + FatData.CacheManagerNoOpCallbacks.ReleaseFromLazyWrite = &FatNoOpRelease; + FatData.CacheManagerNoOpCallbacks.AcquireForReadAhead = &FatNoOpAcquire; + FatData.CacheManagerNoOpCallbacks.ReleaseFromReadAhead = &FatNoOpRelease; + + // + // Set up global pointer to our process. + // + + FatData.OurProcess = PsGetCurrentProcess(); + + // + // Read the registry to determine if we are in ChicagoMode. + // + + ValueName.Buffer = COMPATIBILITY_MODE_VALUE_NAME; + ValueName.Length = sizeof(COMPATIBILITY_MODE_VALUE_NAME) - sizeof(WCHAR); + ValueName.MaximumLength = sizeof(COMPATIBILITY_MODE_VALUE_NAME); + + Status = FatGetCompatibilityModeValue( &ValueName, &Value ); + + if (NT_SUCCESS(Status) && FlagOn(Value, 1)) { + + FatData.ChicagoMode = FALSE; + + } else { + + FatData.ChicagoMode = TRUE; + } + + // + // Read the registry to determine if we are going to generate LFNs + // for valid 8.3 names with extended characters. + // + + ValueName.Buffer = CODE_PAGE_INVARIANCE_VALUE_NAME; + ValueName.Length = sizeof(CODE_PAGE_INVARIANCE_VALUE_NAME) - sizeof(WCHAR); + ValueName.MaximumLength = sizeof(CODE_PAGE_INVARIANCE_VALUE_NAME); + + Status = FatGetCompatibilityModeValue( &ValueName, &Value ); + + if (NT_SUCCESS(Status) && FlagOn(Value, 1)) { + + FatData.CodePageInvariant = FALSE; + + } else { + + FatData.CodePageInvariant = TRUE; + } + + // + // Initialize our global resource and fire up the lookaside lists. + // + + ExInitializeResourceLite( &FatData.Resource ); + + ExInitializeNPagedLookasideList( &FatIrpContextLookasideList, + NULL, + NULL, + POOL_RAISE_IF_ALLOCATION_FAILURE, + sizeof(IRP_CONTEXT), + TAG_IRP_CONTEXT, + MaxDepth ); + + ExInitializeNPagedLookasideList( &FatNonPagedFcbLookasideList, + NULL, + NULL, + POOL_RAISE_IF_ALLOCATION_FAILURE, + sizeof(NON_PAGED_FCB), + TAG_FCB_NONPAGED, + MaxDepth ); + + ExInitializeNPagedLookasideList( &FatEResourceLookasideList, + NULL, + NULL, + POOL_RAISE_IF_ALLOCATION_FAILURE, + sizeof(ERESOURCE), + TAG_ERESOURCE, + MaxDepth ); + + ExInitializeSListHead( &FatCloseContextSList ); + ExInitializeFastMutex( &FatCloseQueueMutex ); + KeInitializeEvent( &FatReserveEvent, SynchronizationEvent, TRUE ); + + // + // Register the file system with the I/O system + // + + IoRegisterFileSystem(FatDiskFileSystemDeviceObject); + ObReferenceObject (FatDiskFileSystemDeviceObject); + IoRegisterFileSystem(FatCdromFileSystemDeviceObject); + ObReferenceObject (FatCdromFileSystemDeviceObject); + + // + // Find out if we are running an a FujitsuFMR machine. + // + + FatData.FujitsuFMR = FatIsFujitsuFMR(); + + // + // And return to our caller + // + + return( STATUS_SUCCESS ); +} + + +VOID +NTAPI +FatUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This is the unload routine for the filesystem + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None + +--*/ + +{ + ExDeleteNPagedLookasideList (&FatEResourceLookasideList); + ExDeleteNPagedLookasideList (&FatNonPagedFcbLookasideList); + ExDeleteNPagedLookasideList (&FatIrpContextLookasideList); + ExDeleteResourceLite( &FatData.Resource ); + IoFreeWorkItem (FatData.FatCloseItem); + ObDereferenceObject( FatDiskFileSystemDeviceObject); + ObDereferenceObject( FatCdromFileSystemDeviceObject); +} + + +// +// Local Support routine +// + +NTSTATUS +FatGetCompatibilityModeValue ( + IN PUNICODE_STRING ValueName, + IN OUT PULONG Value + ) + +/*++ + +Routine Description: + + Given a unicode value name this routine will go into the registry + location for the Chicago compatibilitymode information and get the + value. + +Arguments: + + ValueName - the unicode name for the registry value located in the registry. + Value - a pointer to the ULONG for the result. + +Return Value: + + NTSTATUS + + If STATUS_SUCCESSFUL is returned, the location *Value will be + updated with the DWORD value from the registry. If any failing + status is returned, this value is untouched. + +--*/ + +{ + HANDLE Handle; + NTSTATUS Status; + ULONG RequestLength; + ULONG ResultLength; + UCHAR Buffer[KEY_WORK_AREA]; + UNICODE_STRING KeyName; + OBJECT_ATTRIBUTES ObjectAttributes; + PKEY_VALUE_FULL_INFORMATION KeyValueInformation; + + KeyName.Buffer = COMPATIBILITY_MODE_KEY_NAME; + KeyName.Length = sizeof(COMPATIBILITY_MODE_KEY_NAME) - sizeof(WCHAR); + KeyName.MaximumLength = sizeof(COMPATIBILITY_MODE_KEY_NAME); + + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwOpenKey(&Handle, + KEY_READ, + &ObjectAttributes); + + if (!NT_SUCCESS(Status)) { + + return Status; + } + + RequestLength = KEY_WORK_AREA; + + KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)Buffer; + + while (1) { + + Status = ZwQueryValueKey(Handle, + ValueName, + KeyValueFullInformation, + KeyValueInformation, + RequestLength, + &ResultLength); + + ASSERT( Status != STATUS_BUFFER_OVERFLOW ); + + if (Status == STATUS_BUFFER_OVERFLOW) { + + // + // Try to get a buffer big enough. + // + + if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) { + + ExFreePool(KeyValueInformation); + } + + RequestLength += 256; + + KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) + ExAllocatePoolWithTag(PagedPool, + RequestLength, + ' taF'); + + if (!KeyValueInformation) { + return STATUS_NO_MEMORY; + } + + } else { + + break; + } + } + + ZwClose(Handle); + + if (NT_SUCCESS(Status)) { + + if (KeyValueInformation->DataLength != 0) { + + PULONG DataPtr; + + // + // Return contents to the caller. + // + + DataPtr = (PULONG) + ((PUCHAR)KeyValueInformation + KeyValueInformation->DataOffset); + *Value = *DataPtr; + + } else { + + // + // Treat as if no value was found + // + + Status = STATUS_OBJECT_NAME_NOT_FOUND; + } + } + + if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) { + + ExFreePool(KeyValueInformation); + } + + return Status; +} + +// +// Local Support routine +// + +BOOLEAN +FatIsFujitsuFMR ( + ) + +/*++ + +Routine Description: + + This routine tells if is we running on a FujitsuFMR machine. + +Arguments: + + +Return Value: + + BOOLEAN - TRUE is we are and FALSE otherwise + +--*/ + +{ +#ifndef __REACTOS__ + ULONG Value; +#endif + BOOLEAN Result; + HANDLE Handle; + NTSTATUS Status; + ULONG RequestLength; + ULONG ResultLength; + UCHAR Buffer[KEY_WORK_AREA]; + UNICODE_STRING KeyName; + UNICODE_STRING ValueName; + OBJECT_ATTRIBUTES ObjectAttributes; + PKEY_VALUE_FULL_INFORMATION KeyValueInformation; + + // + // Set default as PC/AT + // + + KeyName.Buffer = REGISTRY_HARDWARE_DESCRIPTION_W; + KeyName.Length = sizeof(REGISTRY_HARDWARE_DESCRIPTION_W) - sizeof(WCHAR); + KeyName.MaximumLength = sizeof(REGISTRY_HARDWARE_DESCRIPTION_W); + + InitializeObjectAttributes(&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = ZwOpenKey(&Handle, + KEY_READ, + &ObjectAttributes); + + if (!NT_SUCCESS(Status)) { + + return FALSE; + } + + ValueName.Buffer = REGISTRY_MACHINE_IDENTIFIER_W; + ValueName.Length = sizeof(REGISTRY_MACHINE_IDENTIFIER_W) - sizeof(WCHAR); + ValueName.MaximumLength = sizeof(REGISTRY_MACHINE_IDENTIFIER_W); + + RequestLength = KEY_WORK_AREA; + + KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)Buffer; + + while (1) { + + Status = ZwQueryValueKey(Handle, + &ValueName, + KeyValueFullInformation, + KeyValueInformation, + RequestLength, + &ResultLength); + + // ASSERT( Status != STATUS_BUFFER_OVERFLOW ); + + if (Status == STATUS_BUFFER_OVERFLOW) { + + // + // Try to get a buffer big enough. + // + + if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) { + + ExFreePool(KeyValueInformation); + } + + RequestLength += 256; + + KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) + ExAllocatePool(PagedPool, RequestLength); + + if (!KeyValueInformation) { + return FALSE; + } + + } else { + + break; + } + } + + ZwClose(Handle); + + if (NT_SUCCESS(Status) && + (KeyValueInformation->DataLength >= sizeof(FUJITSU_FMR_NAME_W)) && + (RtlCompareMemory((PUCHAR)KeyValueInformation + KeyValueInformation->DataOffset, + FUJITSU_FMR_NAME_W, + sizeof(FUJITSU_FMR_NAME_W) - sizeof(WCHAR)) == + sizeof(FUJITSU_FMR_NAME_W) - sizeof(WCHAR))) { + + Result = TRUE; + + } else { + + Result = FALSE; + } + + if (KeyValueInformation != (PKEY_VALUE_FULL_INFORMATION)Buffer) { + + ExFreePool(KeyValueInformation); + } + + return Result; +} + diff --git a/drivers/filesystems/fastfat_new/fatprocs.h b/drivers/filesystems/fastfat_new/fatprocs.h new file mode 100644 index 00000000000..36d482235b4 --- /dev/null +++ b/drivers/filesystems/fastfat_new/fatprocs.h @@ -0,0 +1,2817 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + fatprocs.h + +Abstract: + + This module defines all of the globally used procedures in the FAT + file system. + + +--*/ + +#ifndef _FATPROCS_ +#define _FATPROCS_ + +#include +#include +#include +#include +#ifdef __REACTOS__ +#include +#endif + +#ifdef __REACTOS__ +typedef enum _TYPE_OF_OPEN { + + UnopenedFileObject = 1, + UserFileOpen, + UserDirectoryOpen, + UserVolumeOpen, + VirtualVolumeFile, + DirectoryFile, + EaFile + +} TYPE_OF_OPEN; +#endif + +#include "nodetype.h" +#include "fat.h" +#include "lfn.h" +#include "fatstruc.h" +#include "fatdata.h" + +#ifndef INLINE +#define INLINE __inline +#endif + +// +// We must explicitly tag our allocations. +// + +#undef FsRtlAllocatePool +#undef FsRtlAllocatePoolWithQuota + +// +// A function that returns finished denotes if it was able to complete the +// operation (TRUE) or could not complete the operation (FALSE) because the +// wait value stored in the irp context was false and we would have had +// to block for a resource or I/O +// + +typedef BOOLEAN FINISHED; + +// +// Size (characters) of stack allocated name component buffers in +// the create/rename paths. +// + +#define FAT_CREATE_INITIAL_NAME_BUF_SIZE 32 + +// +// Some string buffer handling functions, implemented in strucsup.c +// + +VOID +FatFreeStringBuffer( + IN PVOID String + ); + +VOID +FatEnsureStringBufferEnough( + IN OUT PVOID String, + IN USHORT DesiredBufferSize + ); + + + +BOOLEAN +FatAddMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + IN LBO Lbo, + IN ULONG SectorCount + ); + +BOOLEAN +FatLookupMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG SectorCount OPTIONAL, + OUT PULONG Index OPTIONAL + ); + +BOOLEAN +FatLookupLastMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + OUT PVBO Vbo, + OUT PLBO Lbo, + OUT PULONG Index OPTIONAL + ); + +BOOLEAN +FatGetNextMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN ULONG RunIndex, + OUT PVBO Vbo, + OUT PLBO Lbo, + OUT PULONG SectorCount + ); + +VOID +FatRemoveMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + IN ULONG SectorCount + ); + + +// +// File access check routine, implemented in AcChkSup.c +// + +BOOLEAN +FatCheckFileAccess ( + PIRP_CONTEXT IrpContext, + IN UCHAR DirentAttributes, + IN PACCESS_MASK DesiredAccess + ); + +NTSTATUS +FatExplicitDeviceAccessGranted ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT DeviceObject, + IN PACCESS_STATE AccessState, + IN KPROCESSOR_MODE ProcessorMode + ); + + +// +// Allocation support routines, implemented in AllocSup.c +// + +static +INLINE +BOOLEAN +FatIsIoRangeValid ( + IN PVCB Vcb, + IN LARGE_INTEGER Start, + IN ULONG Length + ) + +/*++ + +Routine Description: + + This routine enforces the restriction that object space must be + representable in 32 bits. + +Arguments: + + Vcb - the volume the range is on + + Start - starting byte (zero based) of the range + + Length - size of the range + +Return Value: + + BOOLEAN - if, considering the cluster size, the neccesary size of + the object to contain the range can be represented in 32 bits. + +--*/ + +{ + // + // The only restriction on a FAT object is that the filesize must + // fit in 32bits, i.e. <= 0xffffffff. This then implies that the + // range of valid byte offsets is [0, fffffffe]. + // + // Two phases which check for illegality + // + // - if the high 32bits are nonzero + // - if the length would cause a 32bit overflow + // + + return !(Start.HighPart || + Start.LowPart + Length < Start.LowPart); +} + +VOID +FatSetupAllocationSupport ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatTearDownAllocationSupport ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatLookupFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG ByteCount, + OUT PBOOLEAN Allocated, + OUT PBOOLEAN EndOnMax, + OUT PULONG Index OPTIONAL + ); + +VOID +FatAddFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN PFILE_OBJECT FileObject OPTIONAL, + IN ULONG AllocationSize + ); + +VOID +FatTruncateFileAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG AllocationSize + ); + +VOID +FatLookupFileAllocationSize ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb + ); + +VOID +FatAllocateDiskSpace ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG AbsoluteClusterHint, + IN OUT PULONG ByteCount, + IN BOOLEAN ExactMatchRequired, + OUT PLARGE_MCB Mcb + ); + +VOID +FatDeallocateDiskSpace ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PLARGE_MCB Mcb + ); + +VOID +FatSplitAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PLARGE_MCB Mcb, + IN VBO SplitAtVbo, + OUT PLARGE_MCB RemainingMcb + ); + +VOID +FatMergeAllocation ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN OUT PLARGE_MCB Mcb, + IN PLARGE_MCB SecondMcb + ); + +VOID +FatSetFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN FAT_ENTRY FatEntry + ); + +UCHAR +FatLogOf( + IN ULONG Value + ); + + +// +// Buffer control routines for data caching, implemented in CacheSup.c +// + +VOID +FatReadVolumeFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer + ); + +VOID +FatPrepareWriteVolumeFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + IN BOOLEAN Reversible, + IN BOOLEAN Zero + ); + +VOID +FatReadDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + IN BOOLEAN Pin, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + OUT PNTSTATUS Status + ); + +VOID +FatPrepareWriteDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb, + OUT PVOID *Buffer, + IN BOOLEAN Zero, + IN BOOLEAN Reversible, + OUT PNTSTATUS Status + ); + +VOID +FatOpenDirectoryFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ); + +PFILE_OBJECT +FatOpenEaFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB EaFcb + ); + +VOID +FatCloseEaFile ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN FlushFirst + ); + +VOID +FatSetDirtyBcb ( + IN PIRP_CONTEXT IrpContext, + IN PBCB Bcb, + IN PVCB Vcb OPTIONAL, + IN BOOLEAN Reversible + ); + +VOID +FatRepinBcb ( + IN PIRP_CONTEXT IrpContext, + IN PBCB Bcb + ); + +VOID +FatUnpinRepinnedBcbs ( + IN PIRP_CONTEXT IrpContext + ); + +FINISHED +FatZeroData ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject, + IN ULONG StartingZero, + IN ULONG ByteCount + ); + +NTSTATUS +FatCompleteMdl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +FatPinMappedData ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN VBO StartingVbo, + IN ULONG ByteCount, + OUT PBCB *Bcb + ); + +// +// VOID +// FatUnpinBcb ( +// IN PIRP_CONTEXT IrpContext, +// IN OUT PBCB Bcb, +// ); +// + +// +// This macro unpins a Bcb, in the checked build make sure all +// requests unpin all Bcbs before leaving. +// + +#if DBG + +#define FatUnpinBcb(IRPCONTEXT,BCB) { \ + if ((BCB) != NULL) { \ + CcUnpinData((BCB)); \ + ASSERT( (IRPCONTEXT)->PinCount ); \ + (IRPCONTEXT)->PinCount -= 1; \ + (BCB) = NULL; \ + } \ +} + +#else + +#define FatUnpinBcb(IRPCONTEXT,BCB) { \ + if ((BCB) != NULL) { \ + CcUnpinData((BCB)); \ + (BCB) = NULL; \ + } \ +} + +#endif // DBG + +VOID +FatSyncUninitializeCacheMap ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject + ); + + +// +// Device I/O routines, implemented in DevIoSup.c +// +// These routines perform the actual device read and writes. They only affect +// the on disk structure and do not alter any other data structures. +// + +VOID +FatPagingFileIo ( + IN PIRP Irp, + IN PFCB Fcb + ); + +NTSTATUS +FatNonCachedIo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB FcbOrDcb, + IN ULONG StartingVbo, + IN ULONG ByteCount, + IN ULONG UserByteCount + ); + +VOID +FatNonCachedNonAlignedRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB FcbOrDcb, + IN ULONG StartingVbo, + IN ULONG ByteCount + ); + +VOID +FatMultipleAsync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PIRP Irp, + IN ULONG MultipleIrpCount, + IN PIO_RUN IoRuns + ); + +VOID +FatSingleAsync ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN LBO Lbo, + IN ULONG ByteCount, + IN PIRP Irp + ); + +VOID +FatWaitSync ( + IN PIRP_CONTEXT IrpContext + ); + +VOID +FatLockUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp, + IN LOCK_OPERATION Operation, + IN ULONG BufferLength + ); + +PVOID +FatBufferUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp, + IN ULONG BufferLength + ); + +PVOID +FatMapUserBuffer ( + IN PIRP_CONTEXT IrpContext, + IN OUT PIRP Irp + ); + +NTSTATUS +FatToggleMediaEjectDisable ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN BOOLEAN PreventRemoval + ); + +NTSTATUS +FatPerformDevIoCtrl ( + IN PIRP_CONTEXT IrpContext, + IN ULONG IoControlCode, + IN PDEVICE_OBJECT Device, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength, + IN BOOLEAN InternalDeviceIoControl, + IN BOOLEAN OverrideVerify, + OUT PIO_STATUS_BLOCK Iosb OPTIONAL + ); + +// +// Dirent support routines, implemented in DirSup.c +// + +// +// Tunneling is a deletion precursor (all tunneling cases do +// not involve deleting dirents, however) +// + +VOID +FatTunnelFcbOrDcb ( + IN PFCB FcbOrDcb, + IN PCCB Ccb OPTIONAL + ); + +ULONG +FatCreateNewDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN ULONG DirentsNeeded + ); + +VOID +FatInitializeDirectoryDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN PDIRENT ParentDirent + ); + +VOID +FatDeleteDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN PDELETE_CONTEXT DeleteContext OPTIONAL, + IN BOOLEAN DeleteEa + ); + +VOID +FatLocateDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN PCCB Ccb, + IN VBO OffsetToStartSearchFrom, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset, + OUT PBOOLEAN FileNameDos OPTIONAL, + OUT PUNICODE_STRING Lfn OPTIONAL + ); + +VOID +FatLocateSimpleOemDirent ( + IN PIRP_CONTEXT IrpContext, + IN PDCB ParentDirectory, + IN POEM_STRING FileName, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset + ); + +BOOLEAN +FatLfnDirentExists ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN PUNICODE_STRING Lfn, + IN PUNICODE_STRING LfnTmp + ); + +VOID +FatLocateVolumeLabel ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb, + OUT PVBO ByteOffset + ); + +VOID +FatGetDirentFromFcbOrDcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + OUT PDIRENT *Dirent, + OUT PBCB *Bcb + ); + +BOOLEAN +FatIsDirectoryEmpty ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ); + +VOID +FatConstructDirent ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN POEM_STRING FileName, + IN BOOLEAN ComponentReallyLowercase, + IN BOOLEAN ExtensionReallyLowercase, + IN PUNICODE_STRING Lfn OPTIONAL, + IN UCHAR Attributes, + IN BOOLEAN ZeroAndSetTimeFields, + IN PLARGE_INTEGER SetCreationTime OPTIONAL + ); + +VOID +FatConstructLabelDirent ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN POEM_STRING Label + ); + +VOID +FatSetFileSizeInDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PULONG AlternativeFileSize OPTIONAL + ); + +VOID +FatUpdateDirentFromFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN PFCB FcbOrDcb, + IN PCCB Ccb + ); + +// +// Generate a relatively unique static 64bit ID from a FAT Fcb/Dcb +// +// ULONGLONG +// FatDirectoryKey (FcbOrDcb); +// + +#define FatDirectoryKey(FcbOrDcb) ((ULONGLONG)((FcbOrDcb)->CreationTime.QuadPart ^ (FcbOrDcb)->FirstClusterOfFile)) + + +// +// The following routines are used to access and manipulate the +// clusters containing EA data in the ea data file. They are +// implemented in EaSup.c +// + +// +// VOID +// FatUpcaseEaName ( +// IN PIRP_CONTEXT IrpContext, +// IN POEM_STRING EaName, +// OUT POEM_STRING UpcasedEaName +// ); +// + +#define FatUpcaseEaName( IRPCONTEXT, NAME, UPCASEDNAME ) \ + RtlUpperString( UPCASEDNAME, NAME ) + +VOID +FatGetEaLength ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDIRENT Dirent, + OUT PULONG EaLength + ); + +VOID +FatGetNeedEaCount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDIRENT Dirent, + OUT PULONG NeedEaCount + ); + +VOID +FatCreateEa ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PUCHAR Buffer, + IN ULONG Length, + IN POEM_STRING FileName, + OUT PUSHORT EaHandle + ); + +VOID +FatDeleteEa ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN USHORT EaHandle, + IN POEM_STRING FileName + ); + +VOID +FatGetEaFile ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb, + OUT PDIRENT *EaDirent, + OUT PBCB *EaBcb, + IN BOOLEAN CreateFile, + IN BOOLEAN ExclusiveFcb + ); + +VOID +FatReadEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN USHORT EaHandle, + IN POEM_STRING FileName, + IN BOOLEAN ReturnEntireSet, + OUT PEA_RANGE EaSetRange + ); + +VOID +FatDeleteEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PBCB EaBcb, + OUT PDIRENT EaDirent, + IN USHORT EaHandle, + IN POEM_STRING Filename + ); + +VOID +FatAddEaSet ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG EaSetLength, + IN PBCB EaBcb, + OUT PDIRENT EaDirent, + OUT PUSHORT EaHandle, + OUT PEA_RANGE EaSetRange + ); + +VOID +FatDeletePackedEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_SET_HEADER EaSetHeader, + IN OUT PULONG PackedEasLength, + IN ULONG Offset + ); + +VOID +FatAppendPackedEa ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_SET_HEADER *EaSetHeader, + IN OUT PULONG PackedEasLength, + IN OUT PULONG AllocationLength, + IN PFILE_FULL_EA_INFORMATION FullEa, + IN ULONG BytesPerCluster + ); + +ULONG +FatLocateNextEa ( + IN PIRP_CONTEXT IrpContext, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + IN ULONG PreviousOffset + ); + +BOOLEAN +FatLocateEaByName ( + IN PIRP_CONTEXT IrpContext, + IN PPACKED_EA FirstPackedEa, + IN ULONG PackedEasLength, + IN POEM_STRING EaName, + OUT PULONG Offset + ); + +BOOLEAN +FatIsEaNameValid ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING Name + ); + +VOID +FatPinEaRange ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT VirtualEaFile, + IN PFCB EaFcb, + IN OUT PEA_RANGE EaRange, + IN ULONG StartingVbo, + IN ULONG Length, + IN NTSTATUS ErrorStatus + ); + +VOID +FatMarkEaRangeDirty ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT EaFileObject, + IN OUT PEA_RANGE EaRange + ); + +VOID +FatUnpinEaRange ( + IN PIRP_CONTEXT IrpContext, + IN OUT PEA_RANGE EaRange + ); + +// +// The following macro computes the size of a full ea (not including +// padding to bring it to a longword. A full ea has a 4 byte offset, +// folowed by 1 byte flag, 1 byte name length, 2 bytes value length, +// the name, 1 null byte, and the value. +// +// ULONG +// SizeOfFullEa ( +// IN PFILE_FULL_EA_INFORMATION FullEa +// ); +// + +#define SizeOfFullEa(EA) (4+1+1+2+(EA)->EaNameLength+1+(EA)->EaValueLength) + + +// +// The following routines are used to manipulate the fscontext fields +// of the file object, implemented in FilObSup.c +// + +#ifndef __REACTOS__ +typedef enum _TYPE_OF_OPEN { + + UnopenedFileObject = 1, + UserFileOpen, + UserDirectoryOpen, + UserVolumeOpen, + VirtualVolumeFile, + DirectoryFile, + EaFile + +} TYPE_OF_OPEN; +#endif + +typedef enum _FAT_FLUSH_TYPE { + + NoFlush = 0, + Flush, + FlushAndInvalidate, + FlushWithoutPurge + +} FAT_FLUSH_TYPE; + +VOID +FatSetFileObject ( + IN PFILE_OBJECT FileObject OPTIONAL, + IN TYPE_OF_OPEN TypeOfOpen, + IN PVOID VcbOrFcbOrDcb, + IN PCCB Ccb OPTIONAL + ); + +TYPE_OF_OPEN +FatDecodeFileObject ( + IN PFILE_OBJECT FileObject, + OUT PVCB *Vcb, + OUT PFCB *FcbOrDcb, + OUT PCCB *Ccb + ); + +VOID +FatPurgeReferencedFileObjects ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN FAT_FLUSH_TYPE FlushType + ); + +VOID +FatForceCacheMiss ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ); + + +// +// File system control routines, implemented in FsCtrl.c +// + +VOID +FatFlushAndCleanVolume( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PVCB Vcb, + IN FAT_FLUSH_TYPE FlushType + ); + +BOOLEAN +FatIsBootSectorFat ( + IN PPACKED_BOOT_SECTOR BootSector + ); + +NTSTATUS +FatLockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ); + +NTSTATUS +FatUnlockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ); + + +// +// Name support routines, implemented in NameSup.c +// + +// +// VOID +// FatDissectName ( +// IN PIRP_CONTEXT IrpContext, +// IN OEM_STRING InputString, +// OUT POEM_STRING FirstPart, +// OUT POEM_STRING RemainingPart +// ) +// +// /*++ +// +// Routine Description: +// +// This routine takes an input string and dissects it into two substrings. +// The first output string contains the name that appears at the beginning +// of the input string, the second output string contains the remainder of +// the input string. +// +// In the input string backslashes are used to separate names. The input +// string must not start with a backslash. Both output strings will not +// begin with a backslash. +// +// If the input string does not contain any names then both output strings +// are empty. If the input string contains only one name then the first +// output string contains the name and the second string is empty. +// +// Note that both output strings use the same string buffer memory of the +// input string. +// +// Example of its results are: +// +// //. . InputString FirstPart RemainingPart +// // +// //. . empty empty empty +// // +// //. . A A empty +// // +// //. . A\B\C\D\E A B\C\D\E +// // +// //. . *A? *A? empty +// // +// //. . \A A empty +// // +// //. . A[,] A[,] empty +// // +// //. . A\\B+;\C A \B+;\C +// +// Arguments: +// +// InputString - Supplies the input string being dissected +// +// FirstPart - Receives the first name in the input string +// +// RemainingPart - Receives the remaining part of the input string +// +// Return Value: +// +// BOOLEAN - TRUE if the input string is well formed and its first part +// does not contain any illegal characters, and FALSE otherwise. +// +// --*/ +// + +#define FatDissectName(IRPCONTEXT,INPUT_STRING,FIRST_PART,REMAINING_PART) { \ + FsRtlDissectDbcs( (INPUT_STRING), \ + (FIRST_PART), \ + (REMAINING_PART) ); \ +} + +// +// BOOLEAN +// FatDoesNameContainWildCards ( +// IN PIRP_CONTEXT IrpContext, +// IN OEM_STRING Name +// ) +// +// /*++ +// +// Routine Description: +// +// This routine checks if the input name contains any wild card characters. +// +// Arguments: +// +// Name - Supplies the name to examine +// +// Return Value: +// +// BOOLEAN - TRUE if the input name contains any wildcard characters and +// FALSE otherwise. +// +// --*/ +// + +#define FatDoesNameContainWildCards(IRPCONTEXT,NAME) ( \ + FsRtlDoesDbcsContainWildCards( &(NAME) ) \ +) + +// +// BOOLEAN +// FatAreNamesEqual ( +// IN PIRP_CONTEXT IrpContext, +// IN OEM_STRING ConstantNameA, +// IN OEM_STRING ConstantNameB +// ) +// +// /*++ +// +// Routine Description: +// +// This routine simple returns whether the two names are exactly equal. +// If the two names are known to be constant, this routine is much +// faster than FatIsDbcsInExpression. +// +// Arguments: +// +// ConstantNameA - Constant name. +// +// ConstantNameB - Constant name. +// +// Return Value: +// +// BOOLEAN - TRUE if the two names are lexically equal. +// + +#define FatAreNamesEqual(IRPCONTEXT,NAMEA,NAMEB) ( \ + ((ULONG)(NAMEA).Length == (ULONG)(NAMEB).Length) && \ + (RtlEqualMemory( &(NAMEA).Buffer[0], \ + &(NAMEB).Buffer[0], \ + (NAMEA).Length )) \ +) + +// +// BOOLEAN +// FatIsNameShortOemValid ( +// IN PIRP_CONTEXT IrpContext, +// IN OEM_STRING Name, +// IN BOOLEAN CanContainWildCards, +// IN BOOLEAN PathNamePermissible, +// IN BOOLEAN LeadingBackslashPermissible +// ) +// +// /*++ +// +// Routine Description: +// +// This routine scans the input name and verifies that if only +// contains valid characters +// +// Arguments: +// +// Name - Supplies the input name to check. +// +// CanContainWildCards - Indicates if the name can contain wild cards +// (i.e., * and ?). +// +// Return Value: +// +// BOOLEAN - Returns TRUE if the name is valid and FALSE otherwise. +// +// --*/ +// +// The FatIsNameLongOemValid and FatIsNameLongUnicodeValid are similar. +// + +#define FatIsNameShortOemValid(IRPCONTEXT,NAME,CAN_CONTAIN_WILD_CARDS,PATH_NAME_OK,LEADING_BACKSLASH_OK) ( \ + FsRtlIsFatDbcsLegal((NAME), \ + (CAN_CONTAIN_WILD_CARDS), \ + (PATH_NAME_OK), \ + (LEADING_BACKSLASH_OK)) \ +) + +#define FatIsNameLongOemValid(IRPCONTEXT,NAME,CAN_CONTAIN_WILD_CARDS,PATH_NAME_OK,LEADING_BACKSLASH_OK) ( \ + FsRtlIsHpfsDbcsLegal((NAME), \ + (CAN_CONTAIN_WILD_CARDS), \ + (PATH_NAME_OK), \ + (LEADING_BACKSLASH_OK)) \ +) + +static +INLINE +BOOLEAN +FatIsNameLongUnicodeValid ( + PIRP_CONTEXT IrpContext, + PUNICODE_STRING Name, + BOOLEAN CanContainWildcards, + BOOLEAN PathNameOk, + BOOLEAN LeadingBackslashOk + ) +{ + ULONG i; + + // + // I'm not bothering to do the whole thing, just enough to make this call look + // the same as the others. + // + + ASSERT( !PathNameOk && !LeadingBackslashOk ); + + for (i=0; i < Name->Length/sizeof(WCHAR); i++) { + + if ((Name->Buffer[i] < 0x80) && + !(FsRtlIsAnsiCharacterLegalHpfs(Name->Buffer[i], CanContainWildcards))) { + + return FALSE; + } + } + + return TRUE; +} + +BOOLEAN +FatIsNameInExpression ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING Expression, + IN OEM_STRING Name + ); + +VOID +FatStringTo8dot3 ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING InputString, + OUT PFAT8DOT3 Output8dot3 + ); + +VOID +Fat8dot3ToString ( + IN PIRP_CONTEXT IrpContext, + IN PDIRENT Dirent, + IN BOOLEAN RestoreCase, + OUT POEM_STRING OutputString + ); + +VOID +FatGetUnicodeNameFromFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PUNICODE_STRING Lfn + ); + +VOID +FatSetFullFileNameInFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +VOID +FatSetFullNameInFcb( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PUNICODE_STRING FinalName + ); + +VOID +FatUnicodeToUpcaseOem ( + IN PIRP_CONTEXT IrpContext, + IN POEM_STRING OemString, + IN PUNICODE_STRING UnicodeString + ); + +VOID +FatSelectNames ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Parent, + IN POEM_STRING OemName, + IN PUNICODE_STRING UnicodeName, + IN OUT POEM_STRING ShortName, + IN PUNICODE_STRING SuggestedShortName OPTIONAL, + IN OUT BOOLEAN *AllLowerComponent, + IN OUT BOOLEAN *AllLowerExtension, + IN OUT BOOLEAN *CreateLfn + ); + +VOID +FatEvaluateNameCase ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING Name, + IN OUT BOOLEAN *AllLowerComponent, + IN OUT BOOLEAN *AllLowerExtension, + IN OUT BOOLEAN *CreateLfn + ); + +BOOLEAN +FatSpaceInName ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING UnicodeName + ); + + +// +// Resources support routines/macros, implemented in ResrcSup.c +// +// The following routines/macros are used for gaining shared and exclusive +// access to the global/vcb data structures. The routines are implemented +// in ResrcSup.c. There is a global resources that everyone tries to take +// out shared to do their work, with the exception of mount/dismount which +// take out the global resource exclusive. All other resources only work +// on their individual item. For example, an Fcb resource does not take out +// a Vcb resource. But the way the file system is structured we know +// that when we are processing an Fcb other threads cannot be trying to remove +// or alter the Fcb, so we do not need to acquire the Vcb. +// +// The procedures/macros are: +// +// Macro FatData Vcb Fcb Subsequent macros +// +// AcquireExclusiveGlobal Read/Write None None ReleaseGlobal +// +// AcquireSharedGlobal Read None None ReleaseGlobal +// +// AcquireExclusiveVcb Read Read/Write None ReleaseVcb +// +// AcquireSharedVcb Read Read None ReleaseVcb +// +// AcquireExclusiveFcb Read None Read/Write ConvertToSharFcb +// ReleaseFcb +// +// AcquireSharedFcb Read None Read ReleaseFcb +// +// ConvertToSharedFcb Read None Read ReleaseFcb +// +// ReleaseGlobal +// +// ReleaseVcb +// +// ReleaseFcb +// + +// +// FINISHED +// FatAcquireExclusiveGlobal ( +// IN PIRP_CONTEXT IrpContext +// ); +// +// FINISHED +// FatAcquireSharedGlobal ( +// IN PIRP_CONTEXT IrpContext +// ); +// + +#define FatAcquireExclusiveGlobal(IRPCONTEXT) ( \ + ExAcquireResourceExclusiveLite( &FatData.Resource, BooleanFlagOn((IRPCONTEXT)->Flags, IRP_CONTEXT_FLAG_WAIT) ) \ +) + +#define FatAcquireSharedGlobal(IRPCONTEXT) ( \ + ExAcquireResourceSharedLite( &FatData.Resource, BooleanFlagOn((IRPCONTEXT)->Flags, IRP_CONTEXT_FLAG_WAIT) ) \ +) + +// +// The following macro must only be called when Wait is TRUE! +// +// FatAcquireExclusiveVolume ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb +// ); +// +// FatReleaseVolume ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb +// ); +// + +#define FatAcquireExclusiveVolume(IRPCONTEXT,VCB) { \ + PFCB Fcb = NULL; \ + ASSERT(FlagOn((IRPCONTEXT)->Flags, IRP_CONTEXT_FLAG_WAIT)); \ + (VOID)FatAcquireExclusiveVcb( (IRPCONTEXT), (VCB) ); \ + while ( (Fcb = FatGetNextFcbBottomUp((IRPCONTEXT), Fcb, (VCB)->RootDcb)) != NULL) { \ + (VOID)FatAcquireExclusiveFcb((IRPCONTEXT), Fcb ); \ + } \ +} + +#define FatReleaseVolume(IRPCONTEXT,VCB) { \ + PFCB Fcb = NULL; \ + ASSERT(FlagOn((IRPCONTEXT)->Flags, IRP_CONTEXT_FLAG_WAIT)); \ + while ( (Fcb = FatGetNextFcbBottomUp((IRPCONTEXT), Fcb, (VCB)->RootDcb)) != NULL) { \ + (VOID)ExReleaseResourceLite( Fcb->Header.Resource ); \ + } \ + FatReleaseVcb((IRPCONTEXT), (VCB)); \ +} + +// +// Macro to enable easy tracking of Vcb state transitions. +// + +#ifdef FASTFATDBG +#define FatSetVcbCondition( V, X) { \ + DebugTrace(0,DEBUG_TRACE_VERFYSUP,"%d -> ",(V)->VcbCondition); \ + DebugTrace(0,DEBUG_TRACE_VERFYSUP,"%x\n",(X)); \ + (V)->VcbCondition = (X); \ + } +#else +#define FatSetVcbCondition( V, X) (V)->VcbCondition = (X) +#endif + +// +// These macros can be used to determine what kind of FAT we have for an +// initialized Vcb. It is somewhat more elegant to use these (visually). +// + +#define FatIsFat32(VCB) ((BOOLEAN)((VCB)->AllocationSupport.FatIndexBitSize == 32)) +#define FatIsFat16(VCB) ((BOOLEAN)((VCB)->AllocationSupport.FatIndexBitSize == 16)) +#define FatIsFat12(VCB) ((BOOLEAN)((VCB)->AllocationSupport.FatIndexBitSize == 12)) + +FINISHED +FatAcquireExclusiveVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +FINISHED +FatAcquireSharedVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +FINISHED +FatAcquireExclusiveFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +FINISHED +FatAcquireSharedFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +FINISHED +FatAcquireSharedFcbWaitForEx ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +#define FatVcbAcquiredExclusive(IRPCONTEXT,VCB) ( \ + ExIsResourceAcquiredExclusiveLite(&(VCB)->Resource) || \ + ExIsResourceAcquiredExclusiveLite(&FatData.Resource) \ +) + +#define FatFcbAcquiredShared(IRPCONTEXT,FCB) ( \ + ExIsResourceAcquiredSharedLite((FCB)->Header.Resource) \ +) + +#define FatAcquireDirectoryFileMutex(VCB) { \ + ASSERT(KeAreApcsDisabled()); \ + ExAcquireFastMutexUnsafe(&(VCB)->DirectoryFileCreationMutex); \ +} + +#define FatReleaseDirectoryFileMutex(VCB) { \ + ASSERT(KeAreApcsDisabled()); \ + ExReleaseFastMutexUnsafe(&(VCB)->DirectoryFileCreationMutex); \ +} + +// +// The following are cache manager call backs + +BOOLEAN +FatAcquireVolumeForClose ( + IN PVOID Vcb, + IN BOOLEAN Wait + ); + +VOID +FatReleaseVolumeFromClose ( + IN PVOID Vcb + ); + +BOOLEAN +NTAPI +FatAcquireFcbForLazyWrite ( + IN PVOID Null, + IN BOOLEAN Wait + ); + +VOID +NTAPI +FatReleaseFcbFromLazyWrite ( + IN PVOID Null + ); + +BOOLEAN +NTAPI +FatAcquireFcbForReadAhead ( + IN PVOID Null, + IN BOOLEAN Wait + ); + +VOID +NTAPI +FatReleaseFcbFromReadAhead ( + IN PVOID Null + ); + +NTSTATUS +NTAPI +FatAcquireForCcFlush ( + IN PFILE_OBJECT FileObject, + IN PDEVICE_OBJECT DeviceObject + ); + +NTSTATUS +NTAPI +FatReleaseForCcFlush ( + IN PFILE_OBJECT FileObject, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +NTAPI +FatNoOpAcquire ( + IN PVOID Fcb, + IN BOOLEAN Wait + ); + +VOID +NTAPI +FatNoOpRelease ( + IN PVOID Fcb + ); + +// +// VOID +// FatConvertToSharedFcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// + +#define FatConvertToSharedFcb(IRPCONTEXT,Fcb) { \ + ExConvertExclusiveToSharedLite( (Fcb)->Header.Resource ); \ + } + +// +// VOID +// FatReleaseGlobal ( +// IN PIRP_CONTEXT IrpContext +// ); +// +// VOID +// FatReleaseVcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb +// ); +// +// VOID +// FatReleaseFcb ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb +// ); +// + +#define FatDeleteResource(RESRC) { \ + ExDeleteResourceLite( (RESRC) ); \ +} + +#define FatReleaseGlobal(IRPCONTEXT) { \ + ExReleaseResourceLite( &(FatData.Resource) ); \ + } + +#define FatReleaseVcb(IRPCONTEXT,Vcb) { \ + ExReleaseResourceLite( &((Vcb)->Resource) ); \ + } + +#define FatReleaseFcb(IRPCONTEXT,Fcb) { \ + ExReleaseResourceLite( (Fcb)->Header.Resource ); \ + } + + +// +// In-memory structure support routine, implemented in StrucSup.c +// + +VOID +FatInitializeVcb ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb, + IN PDEVICE_OBJECT TargetDeviceObject, + IN PVPB Vpb, + IN PDEVICE_OBJECT FsDeviceObject + ); +VOID +FatTearDownVcb( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatDeleteVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatCreateRootDcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +PFCB +FatCreateFcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN ULONG LfnOffsetWithinDirectory, + IN ULONG DirentOffsetWithinDirectory, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn OPTIONAL, + IN BOOLEAN IsPagingFile, + IN BOOLEAN SingleResource + ); + +PDCB +FatCreateDcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN ULONG LfnOffsetWithinDirectory, + IN ULONG DirentOffsetWithinDirectory, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn OPTIONAL + ); + +VOID +FatDeleteFcb_Real ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +#ifdef FASTFATDBG +#define FatDeleteFcb(IRPCONTEXT,FCB) { \ + FatDeleteFcb_Real((IRPCONTEXT),(FCB)); \ + (FCB) = NULL; \ +} +#else +#define FatDeleteFcb(IRPCONTEXT,VCB) { \ + FatDeleteFcb_Real((IRPCONTEXT),(VCB)); \ +} +#endif // FASTFAT_DBG + +PCCB +FatCreateCcb ( + IN PIRP_CONTEXT IrpContext + ); + +VOID +FatDeallocateCcbStrings( + IN PCCB Ccb + ); + +VOID +FatDeleteCcb_Real ( + IN PIRP_CONTEXT IrpContext, + IN PCCB Ccb + ); + +#ifdef FASTFATDBG +#define FatDeleteCcb(IRPCONTEXT,CCB) { \ + FatDeleteCcb_Real((IRPCONTEXT),(CCB)); \ + (CCB) = NULL; \ +} +#else +#define FatDeleteCcb(IRPCONTEXT,VCB) { \ + FatDeleteCcb_Real((IRPCONTEXT),(VCB)); \ +} +#endif // FASTFAT_DBG + +PIRP_CONTEXT +FatCreateIrpContext ( + IN PIRP Irp, + IN BOOLEAN Wait + ); + +VOID +FatDeleteIrpContext_Real ( + IN PIRP_CONTEXT IrpContext + ); + +#ifdef FASTFATDBG +#define FatDeleteIrpContext(IRPCONTEXT) { \ + FatDeleteIrpContext_Real((IRPCONTEXT)); \ + (IRPCONTEXT) = NULL; \ +} +#else +#define FatDeleteIrpContext(IRPCONTEXT) { \ + FatDeleteIrpContext_Real((IRPCONTEXT)); \ +} +#endif // FASTFAT_DBG + +PFCB +FatGetNextFcbTopDown ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFCB TerminationFcb + ); + +PFCB +FatGetNextFcbBottomUp ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFCB TerminationFcb + ); + +// +// These two macros just make the code a bit cleaner. +// + +#define FatGetFirstChild(DIR) ((PFCB)( \ + IsListEmpty(&(DIR)->Specific.Dcb.ParentDcbQueue) ? NULL : \ + CONTAINING_RECORD((DIR)->Specific.Dcb.ParentDcbQueue.Flink, \ + DCB, \ + ParentDcbLinks.Flink))) + +#define FatGetNextSibling(FILE) ((PFCB)( \ + &(FILE)->ParentDcb->Specific.Dcb.ParentDcbQueue.Flink == \ + (PVOID)(FILE)->ParentDcbLinks.Flink ? NULL : \ + CONTAINING_RECORD((FILE)->ParentDcbLinks.Flink, \ + FCB, \ + ParentDcbLinks.Flink))) + +BOOLEAN +FatCheckForDismount ( + IN PIRP_CONTEXT IrpContext, + PVCB Vcb, + IN BOOLEAN Force + ); + +VOID +FatConstructNamesInFcb ( + IN PIRP_CONTEXT IrpContext, + PFCB Fcb, + PDIRENT Dirent, + PUNICODE_STRING Lfn OPTIONAL + ); + +VOID +FatCheckFreeDirentBitmap ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ); + +ULONG +FatVolumeUncleanCount ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatPreallocateCloseContext ( + ); + +// VOID +// FatAllocateCloseContext ( +// ) +// +// This routine allocates a close context, presumeably on behalf +// of a fileobject which does not have a structure we can embed one +// in. + +#define FatAllocateCloseContext() (PCLOSE_CONTEXT) \ + ExInterlockedPopEntrySList( &FatCloseContextSList, \ + &FatData.GeneralSpinLock ) + + +// +// BOOLEAN +// FatIsRawDevice ( +// IN PIRP_CONTEXT IrpContext, +// IN NTSTATUS Status +// ); +// + +#define FatIsRawDevice(IC,S) ( \ + ((S) == STATUS_DEVICE_NOT_READY) || \ + ((S) == STATUS_NO_MEDIA_IN_DEVICE) \ +) + + +// +// Routines to support managing file names Fcbs and Dcbs. +// Implemented in SplaySup.c +// + +VOID +FatInsertName ( + IN PIRP_CONTEXT IrpContext, + IN PRTL_SPLAY_LINKS *RootNode, + IN PFILE_NAME_NODE Name + ); + +VOID +FatRemoveNames ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +PFCB +FatFindFcb ( + IN PIRP_CONTEXT IrpContext, + IN OUT PRTL_SPLAY_LINKS *RootNode, + IN PSTRING Name, + OUT PBOOLEAN FileNameDos OPTIONAL + ); + +BOOLEAN +FatIsHandleCountZero ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +// +// Time conversion support routines, implemented in TimeSup.c +// + +BOOLEAN +FatNtTimeToFatTime ( + IN PIRP_CONTEXT IrpContext, + IN PLARGE_INTEGER NtTime, + IN BOOLEAN Rounding, + OUT PFAT_TIME_STAMP FatTime, +#ifndef __REACTOS__ + OUT OPTIONAL PCHAR TenMsecs +#else + OUT OPTIONAL PUCHAR TenMsecs +#endif + ); + +LARGE_INTEGER +FatFatTimeToNtTime ( + IN PIRP_CONTEXT IrpContext, + IN FAT_TIME_STAMP FatTime, + IN UCHAR TenMilliSeconds + ); + +LARGE_INTEGER +FatFatDateToNtTime ( + IN PIRP_CONTEXT IrpContext, + IN FAT_DATE FatDate + ); + +FAT_TIME_STAMP +FatGetCurrentFatTime ( + IN PIRP_CONTEXT IrpContext + ); + + +// +// Low level verification routines, implemented in VerfySup.c +// +// The first routine is called to help process a verify IRP. Its job is +// to walk every Fcb/Dcb and mark them as need to be verified. +// +// The other routines are used by every dispatch routine to verify that +// an Vcb/Fcb/Dcb is still good. The routine walks as much of the opened +// file/directory tree as necessary to make sure that the path is still valid. +// The function result indicates if the procedure needed to block for I/O. +// If the structure is bad the procedure raise the error condition +// STATUS_FILE_INVALID, otherwise they simply return to their caller +// + +typedef enum _FAT_VOLUME_STATE { + VolumeClean, + VolumeDirty, + VolumeDirtyWithSurfaceTest +} FAT_VOLUME_STATE, *PFAT_VOLUME_STATE; + +VOID +FatMarkFcbCondition ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FCB_CONDITION FcbCondition, + IN BOOLEAN Recursive + ); + +VOID +FatVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatVerifyFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +VOID +NTAPI +FatCleanVolumeDpc ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +VOID +FatMarkVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN FAT_VOLUME_STATE VolumeState + ); + +VOID +NTAPI +FatFspMarkVolumeDirtyWithRecover ( + PVOID Parameter + ); + +VOID +FatCheckDirtyBit ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatQuickVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +VOID +FatVerifyOperationIsLegal ( + IN PIRP_CONTEXT IrpContext + ); + +NTSTATUS +FatPerformVerify ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PDEVICE_OBJECT Device + ); + + +// +// Work queue routines for posting and retrieving an Irp, implemented in +// workque.c +// + +VOID +NTAPI +FatOplockComplete ( + IN PVOID Context, + IN PIRP Irp + ); + +VOID +NTAPI +FatPrePostIrp ( + IN PVOID Context, + IN PIRP Irp + ); + +VOID +FatAddToWorkque ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatFsdPostRequest ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +// +// Miscellaneous support routines +// + +// +// ULONG +// PtrOffset ( +// IN PVOID BasePtr, +// IN PVOID OffsetPtr +// ); +// + +#define PtrOffset(BASE,OFFSET) ((ULONG)((ULONG_PTR)(OFFSET) - (ULONG_PTR)(BASE))) + +// +// This macro takes a pointer (or ulong) and returns its rounded up word +// value +// + +#define WordAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 1) & 0xfffffffe) \ + ) + +// +// This macro takes a pointer (or ulong) and returns its rounded up longword +// value +// + +#define LongAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 3) & 0xfffffffc) \ + ) + +// +// This macro takes a pointer (or ulong) and returns its rounded up quadword +// value +// + +#define QuadAlign(Ptr) ( \ + ((((ULONG)(Ptr)) + 7) & 0xfffffff8) \ + ) + +// +// The following types and macros are used to help unpack the packed and +// misaligned fields found in the Bios parameter block +// + +typedef union _UCHAR1 { + UCHAR Uchar[1]; + UCHAR ForceAlignment; +} UCHAR1, *PUCHAR1; + +typedef union _UCHAR2 { + UCHAR Uchar[2]; + USHORT ForceAlignment; +} UCHAR2, *PUCHAR2; + +typedef union _UCHAR4 { + UCHAR Uchar[4]; + ULONG ForceAlignment; +} UCHAR4, *PUCHAR4; + +// +// This macro copies an unaligned src byte to an aligned dst byte +// + +#define CopyUchar1(Dst,Src) { \ + *((UCHAR1 *)(Dst)) = *((UNALIGNED UCHAR1 *)(Src)); \ + } + +// +// This macro copies an unaligned src word to an aligned dst word +// + +#define CopyUchar2(Dst,Src) { \ + *((UCHAR2 *)(Dst)) = *((UNALIGNED UCHAR2 *)(Src)); \ + } + +// +// This macro copies an unaligned src longword to an aligned dsr longword +// + +#define CopyUchar4(Dst,Src) { \ + *((UCHAR4 *)(Dst)) = *((UNALIGNED UCHAR4 *)(Src)); \ + } + +#define CopyU4char(Dst,Src) { \ + *((UNALIGNED UCHAR4 *)(Dst)) = *((UCHAR4 *)(Src)); \ + } + +// +// VOID +// FatNotifyReportChange ( +// IN PIRP_CONTEXT IrpContext, +// IN PVCB Vcb, +// IN PFCB Fcb, +// IN ULONG Filter, +// IN ULONG Action +// ); +// + +#define FatNotifyReportChange(I,V,F,FL,A) { \ + if ((F)->FullFileName.Buffer == NULL) { \ + FatSetFullFileNameInFcb((I),(F)); \ + } \ + ASSERT( (F)->FullFileName.Length != 0 ); \ + ASSERT( (F)->FinalNameLength != 0 ); \ + ASSERT( (F)->FullFileName.Length > (F)->FinalNameLength ); \ + ASSERT( (F)->FullFileName.Buffer[((F)->FullFileName.Length - (F)->FinalNameLength)/sizeof(WCHAR) - 1] == L'\\' ); \ + FsRtlNotifyFullReportChange( (V)->NotifySync, \ + &(V)->DirNotifyList, \ + (PSTRING)&(F)->FullFileName, \ + (USHORT) ((F)->FullFileName.Length - \ + (F)->FinalNameLength), \ + (PSTRING)NULL, \ + (PSTRING)NULL, \ + (ULONG)FL, \ + (ULONG)A, \ + (PVOID)NULL ); \ +} + + +// +// The FSD Level dispatch routines. These routines are called by the +// I/O system via the dispatch table in the Driver Object. +// +// They each accept as input a pointer to a device object (actually most +// expect a volume device object, with the exception of the file system +// control function which can also take a file system device object), and +// a pointer to the IRP. They either perform the function at the FSD level +// or post the request to the FSP work queue for FSP level processing. +// + +NTSTATUS +NTAPI +FatFsdCleanup ( // implemented in Cleanup.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdClose ( // implemented in Close.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdCreate ( // implemented in Create.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdDeviceControl ( // implemented in DevCtrl.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdDirectoryControl ( // implemented in DirCtrl.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdQueryEa ( // implemented in Ea.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdSetEa ( // implemented in Ea.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdQueryInformation ( // implemented in FileInfo.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdSetInformation ( // implemented in FileInfo.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdFlushBuffers ( // implemented in Flush.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdFileSystemControl ( // implemented in FsCtrl.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdLockControl ( // implemented in LockCtrl.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdPnp ( // implemented in Pnp.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdRead ( // implemented in Read.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdShutdown ( // implemented in Shutdown.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdQueryVolumeInformation ( // implemented in VolInfo.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdSetVolumeInformation ( // implemented in VolInfo.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NTAPI +FatFsdWrite ( // implemented in Write.c + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ); + +// +// The following macro is used to determine if an FSD thread can block +// for I/O or wait for a resource. It returns TRUE if the thread can +// block and FALSE otherwise. This attribute can then be used to call +// the FSD & FSP common work routine with the proper wait value. +// + +#define CanFsdWait(IRP) IoIsOperationSynchronous(Irp) + + +// +// The FSP level dispatch/main routine. This is the routine that takes +// IRP's off of the work queue and calls the appropriate FSP level +// work routine. +// + +VOID +NTAPI +FatFspDispatch ( // implemented in FspDisp.c + IN PVOID Context + ); + +// +// The following routines are the FSP work routines that are called +// by the preceding FatFspDispath routine. Each takes as input a pointer +// to the IRP, perform the function, and return a pointer to the volume +// device object that they just finished servicing (if any). The return +// pointer is then used by the main Fsp dispatch routine to check for +// additional IRPs in the volume's overflow queue. +// +// Each of the following routines is also responsible for completing the IRP. +// We moved this responsibility from the main loop to the individual routines +// to allow them the ability to complete the IRP and continue post processing +// actions. +// + +NTSTATUS +FatCommonCleanup ( // implemented in Cleanup.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonClose ( // implemented in Close.c + IN PVCB Vcb, + IN PFCB Fcb, + IN PCCB Ccb, + IN TYPE_OF_OPEN TypeOfOpen, + IN BOOLEAN Wait, + OUT PBOOLEAN VcbDeleted OPTIONAL + ); + +VOID +FatFspClose ( // implemented in Close.c + IN PVCB Vcb OPTIONAL + ); + +NTSTATUS +FatCommonCreate ( // implemented in Create.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonDirectoryControl ( // implemented in DirCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonDeviceControl ( // implemented in DevCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonQueryEa ( // implemented in Ea.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonSetEa ( // implemented in Ea.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonQueryInformation ( // implemented in FileInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonSetInformation ( // implemented in FileInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonFlushBuffers ( // implemented in Flush.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonFileSystemControl ( // implemented in FsCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonLockControl ( // implemented in LockCtrl.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonPnp ( // implemented in Pnp.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonRead ( // implemented in Read.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonShutdown ( // implemented in Shutdown.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonQueryVolumeInfo ( // implemented in VolInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonSetVolumeInfo ( // implemented in VolInfo.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatCommonWrite ( // implemented in Write.c + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +// +// The following is implemented in Flush.c, and does what is says. +// + +NTSTATUS +FatFlushFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ); + +NTSTATUS +FatFlushDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN FAT_FLUSH_TYPE FlushType + ); + +NTSTATUS +FatFlushFat ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ); + +NTSTATUS +FatFlushVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN FAT_FLUSH_TYPE FlushType + ); + +NTSTATUS +FatHijackIrpAndFlushDevice ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PDEVICE_OBJECT TargetDeviceObject + ); + +VOID +FatFlushFatEntries ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG Cluster, + IN ULONG Count +); + +VOID +FatFlushDirentForFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb +); + + + +// +// The following procedure is used by the FSP and FSD routines to complete +// an IRP. +// +// Note that this macro allows either the Irp or the IrpContext to be +// null, however the only legal order to do this in is: +// +// FatCompleteRequest( NULL, Irp, Status ); // completes Irp & preserves context +// ... +// FatCompleteRequest( IrpContext, NULL, DontCare ); // deallocates context +// +// This would typically be done in order to pass a "naked" IrpContext off to +// the Fsp for post processing, such as read ahead. +// + +VOID +FatCompleteRequest_Real ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN NTSTATUS Status + ); + +#define FatCompleteRequest(IRPCONTEXT,IRP,STATUS) { \ + FatCompleteRequest_Real(IRPCONTEXT,IRP,STATUS); \ +} + +BOOLEAN +FatIsIrpTopLevel ( + IN PIRP Irp + ); + +// +// The Following routine makes a popup +// + +VOID +FatPopUpFileCorrupt ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +// +// Here are the callbacks used by the I/O system for checking for fast I/O or +// doing a fast query info call, or doing fast lock calls. +// + +BOOLEAN +NTAPI +FatFastIoCheckIfPossible ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN ULONG LockKey, + IN BOOLEAN CheckForReadOperation, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +NTAPI +FatFastQueryBasicInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_BASIC_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +NTAPI +FatFastQueryStdInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +NTAPI +FatFastQueryNetworkOpenInfo ( + IN PFILE_OBJECT FileObject, + IN BOOLEAN Wait, + IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +NTAPI +FatFastLock ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + BOOLEAN FailImmediately, + BOOLEAN ExclusiveLock, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +NTAPI +FatFastUnlockSingle ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +NTAPI +FatFastUnlockAll ( + IN PFILE_OBJECT FileObject, + PEPROCESS ProcessId, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + +BOOLEAN +NTAPI +FatFastUnlockAllByKey ( + IN PFILE_OBJECT FileObject, + PVOID ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ); + + +VOID +FatExamineFatEntries( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG StartIndex OPTIONAL, + IN ULONG EndIndex OPTIONAL, + IN BOOLEAN SetupWindows, + IN PFAT_WINDOW SwitchToWindow OPTIONAL, + IN PULONG BitMapBuffer OPTIONAL + ); + +BOOLEAN +FatScanForDataTrack( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject + ); + +// +// The following macro is used to determine is a file has been deleted. +// +// BOOLEAN +// IsFileDeleted ( +// IN PIRP_CONTEXT IrpContext, +// IN PFCB Fcb +// ); +// + +#define IsFileDeleted(IRPCONTEXT,FCB) \ + (FlagOn((FCB)->FcbState, FCB_STATE_DELETE_ON_CLOSE) && \ + ((FCB)->UncleanCount == 0)) + +// +// The following macro is used by the dispatch routines to determine if +// an operation is to be done with or without Write Through. +// +// BOOLEAN +// IsFileWriteThrough ( +// IN PFILE_OBJECT FileObject, +// IN PVCB Vcb +// ); +// + +#define IsFileWriteThrough(FO,VCB) ( \ + BooleanFlagOn((FO)->Flags, FO_WRITE_THROUGH) \ +) + +// +// The following macro is used to set the is fast i/o possible field in +// the common part of the nonpaged fcb +// +// +// BOOLEAN +// FatIsFastIoPossible ( +// IN PFCB Fcb +// ); +// + +#define FatIsFastIoPossible(FCB) ((BOOLEAN) \ + (((FCB)->FcbCondition != FcbGood || !FsRtlOplockIsFastIoPossible( &(FCB)->Specific.Fcb.Oplock )) ? \ + FastIoIsNotPossible \ + : \ + (!FsRtlAreThereCurrentFileLocks( &(FCB)->Specific.Fcb.FileLock ) && \ + ((FCB)->NonPaged->OutstandingAsyncWrites == 0) && \ + !FlagOn( (FCB)->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ) ? \ + FastIoIsPossible \ + : \ + FastIoIsQuestionable \ + ) \ + ) \ +) + +// +// The following macro is used to detemine if the file object is opened +// for read only access (i.e., it is not also opened for write access or +// delete access). +// +// BOOLEAN +// IsFileObjectReadOnly ( +// IN PFILE_OBJECT FileObject +// ); +// + +#define IsFileObjectReadOnly(FO) (!((FO)->WriteAccess | (FO)->DeleteAccess)) + + +// +// The following two macro are used by the Fsd/Fsp exception handlers to +// process an exception. The first macro is the exception filter used in the +// Fsd/Fsp to decide if an exception should be handled at this level. +// The second macro decides if the exception is to be finished off by +// completing the IRP, and cleaning up the Irp Context, or if we should +// bugcheck. Exception values such as STATUS_FILE_INVALID (raised by +// VerfySup.c) cause us to complete the Irp and cleanup, while exceptions +// such as accvio cause us to bugcheck. +// +// The basic structure for fsd/fsp exception handling is as follows: +// +// FatFsdXxx(...) +// { +// try { +// +// ... +// +// } except(FatExceptionFilter( IrpContext, GetExceptionCode() )) { +// +// Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); +// } +// +// Return Status; +// } +// +// To explicitly raise an exception that we expect, such as +// STATUS_FILE_INVALID, use the below macro FatRaiseStatus(). To raise a +// status from an unknown origin (such as CcFlushCache()), use the macro +// FatNormalizeAndRaiseStatus. This will raise the status if it is expected, +// or raise STATUS_UNEXPECTED_IO_ERROR if it is not. +// +// If we are vicariously handling exceptions without using FatProcessException(), +// if there is the possibility that we raised that exception, one *must* +// reset the IrpContext so a subsequent raise in the course of handling this +// request that is *not* explicit, i.e. like a pagein error, does not get +// spoofed into believing that the first raise status is the reason the second +// occured. This could have really nasty consequences. +// +// It is an excellent idea to always FatResetExceptionState in these cases. +// +// Note that when using these two macros, the original status is placed in +// IrpContext->ExceptionStatus, signaling FatExceptionFilter and +// FatProcessException that the status we actually raise is by definition +// expected. +// + +ULONG +FatExceptionFilter ( + IN PIRP_CONTEXT IrpContext, + IN PEXCEPTION_POINTERS ExceptionPointer + ); + +#if DBG +ULONG +FatBugCheckExceptionFilter ( + IN PEXCEPTION_POINTERS ExceptionPointer + ); +#endif + +NTSTATUS +FatProcessException ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN NTSTATUS ExceptionCode + ); + +// +// VOID +// FatRaiseStatus ( +// IN PRIP_CONTEXT IrpContext, +// IN NT_STATUS Status +// ); +// +// + +#if DBG +#define DebugBreakOnStatus(S) { \ + if (FatTestRaisedStatus) { \ + if ((S) == STATUS_DISK_CORRUPT_ERROR || (S) == STATUS_FILE_CORRUPT_ERROR) { \ + DbgPrint( "FAT: Breaking on interesting raised status (%08x)\n", (S) ); \ + DbgPrint( "FAT: Set FatTestRaisedStatus @ %08x to 0 to disable\n", \ + &FatTestRaisedStatus ); \ + DbgBreakPoint(); \ + } \ + } \ +} +#else +#define DebugBreakOnStatus(S) +#endif + +#define FatRaiseStatus(IRPCONTEXT,STATUS) { \ + (IRPCONTEXT)->ExceptionStatus = (STATUS); \ + DebugBreakOnStatus( (STATUS) ) \ + ExRaiseStatus( (STATUS) ); \ +} + +#define FatResetExceptionState( IRPCONTEXT ) { \ + (IRPCONTEXT)->ExceptionStatus = STATUS_SUCCESS; \ +} + +// +// VOID +// FatNormalAndRaiseStatus ( +// IN PRIP_CONTEXT IrpContext, +// IN NT_STATUS Status +// ); +// + +#define FatNormalizeAndRaiseStatus(IRPCONTEXT,STATUS) { \ + (IRPCONTEXT)->ExceptionStatus = (STATUS); \ + ExRaiseStatus(FsRtlNormalizeNtstatus((STATUS),STATUS_UNEXPECTED_IO_ERROR)); \ +} + + +// +// The following macros are used to establish the semantics needed +// to do a return from within a try-finally clause. As a rule every +// try clause must end with a label call try_exit. For example, +// +// try { +// : +// : +// +// try_exit: NOTHING; +// } finally { +// +// : +// : +// } +// +// Every return statement executed inside of a try clause should use the +// try_return macro. If the compiler fully supports the try-finally construct +// then the macro should be +// +// #define try_return(S) { return(S); } +// +// If the compiler does not support the try-finally construct then the macro +// should be +// +// #define try_return(S) { S; goto try_exit; } +// + +#define try_return(S) { S; goto try_exit; } +#define try_leave(S) { S; _SEH2_LEAVE; } + + +CLUSTER_TYPE +FatInterpretClusterType ( + IN PVCB Vcb, + IN FAT_ENTRY Entry + ); + + +// +// These routines define the FileId for FAT. Lacking a fixed/uniquifiable +// notion, we simply come up with one which is unique in a given snapshot +// of the volume. As long as the parent directory is not moved or compacted, +// it may even be permanent. +// + +// +// The internal information used to identify the fcb/dcb on the +// volume is the byte offset of the dirent of the file on disc. +// Our root always has fileid 0. FAT32 roots are chains and can +// use the LBO of the cluster, 12/16 roots use the lbo in the Vcb. +// + +#define FatGenerateFileIdFromDirentOffset(ParentDcb,DirentOffset) \ + ((ParentDcb) ? ((NodeType(ParentDcb) != FAT_NTC_ROOT_DCB || FatIsFat32((ParentDcb)->Vcb)) ? \ + FatGetLboFromIndex( (ParentDcb)->Vcb, \ + (ParentDcb)->FirstClusterOfFile ) : \ + (ParentDcb)->Vcb->AllocationSupport.RootDirectoryLbo) + \ + (DirentOffset) \ + : \ + 0) + +// +// + +#define FatGenerateFileIdFromFcb(Fcb) \ + FatGenerateFileIdFromDirentOffset( (Fcb)->ParentDcb, (Fcb)->DirentOffsetWithinDirectory ) + +// +// Wrap to handle the ./.. cases appropriately. Note that we commute NULL parent to 0. This would +// only occur in an illegal root ".." entry. +// + +#define FATDOT ((ULONG)0x2020202E) +#define FATDOTDOT ((ULONG)0x20202E2E) + +#define FatGenerateFileIdFromDirentAndOffset(Dcb,Dirent,DirentOffset) \ + ((*((PULONG)(Dirent)->FileName)) == FATDOT ? FatGenerateFileIdFromFcb(Dcb) : \ + ((*((PULONG)(Dirent)->FileName)) == FATDOTDOT ? ((Dcb)->ParentDcb ? \ + FatGenerateFileIdFromFcb((Dcb)->ParentDcb) : \ + 0) : \ + FatGenerateFileIdFromDirentOffset(Dcb,DirentOffset))) + + +// +// BOOLEAN +// FatDeviceIsFatFsdo( +// IN PDEVICE_OBJECT D +// ); +// +// Evaluates to TRUE if the supplied device object is one of the file system devices +// we created at initialisation. +// + +#define FatDeviceIsFatFsdo( D) (((D) == FatData.DiskFileSystemDeviceObject) || ((D) == FatData.CdromFileSystemDeviceObject)) + +#endif // _FATPROCS_ + + diff --git a/drivers/filesystems/fastfat_new/fatprocssrc.c b/drivers/filesystems/fastfat_new/fatprocssrc.c new file mode 100644 index 00000000000..b181cd1c076 --- /dev/null +++ b/drivers/filesystems/fastfat_new/fatprocssrc.c @@ -0,0 +1 @@ +#include "fatprocs.h" \ No newline at end of file diff --git a/drivers/filesystems/fastfat_new/fatstruc.h b/drivers/filesystems/fastfat_new/fatstruc.h new file mode 100644 index 00000000000..3ef985fad67 --- /dev/null +++ b/drivers/filesystems/fastfat_new/fatstruc.h @@ -0,0 +1,1614 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FatStruc.h + +Abstract: + + This module defines the data structures that make up the major internal + part of the Fat file system. + + +--*/ + +#ifndef _FATSTRUC_ +#define _FATSTRUC_ + +typedef PVOID PBCB; //**** Bcb's are now part of the cache module + + +// +// The FAT_DATA record is the top record in the Fat file system in-memory +// data structure. This structure must be allocated from non-paged pool. +// + +typedef struct _FAT_DATA { + + // + // The type and size of this record (must be FAT_NTC_DATA_HEADER) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + + PVOID LazyWriteThread; + + + // + // A queue of all the devices that are mounted by the file system. + // + + LIST_ENTRY VcbQueue; + + // + // A pointer to the Driver object we were initialized with + // + + PDRIVER_OBJECT DriverObject; + + // + // A pointer to the filesystem device objects we created. + // + + PVOID DiskFileSystemDeviceObject; + PVOID CdromFileSystemDeviceObject; + + // + // A resource variable to control access to the global Fat data record + // + + ERESOURCE Resource; + + // + // A pointer to our EPROCESS struct, which is a required input to the + // Cache Management subsystem. + // + + PEPROCESS OurProcess; + + // + // The following tells us if we should use Chicago extensions. + // + + BOOLEAN ChicagoMode:1; + + // + // The following field tells us if we are running on a Fujitsu + // FMR Series. These machines supports extra formats on the + // FAT file system. + // + + BOOLEAN FujitsuFMR:1; + + // + // Inidicates that FspClose is currently processing closes. + // + + BOOLEAN AsyncCloseActive:1; + + // + // The following BOOLEAN says shutdown has started on FAT. It + // instructs FspClose to not keep the Vcb resources anymore. + // + + BOOLEAN ShutdownStarted:1; + + // + // The following flag tells us if we are going to generate LFNs + // for valid 8.3 names with extended characters. + // + + BOOLEAN CodePageInvariant:1; + + // + // The following flags tell us if we are in an aggresive push to lower + // the size of the deferred close queues. + // + + BOOLEAN HighAsync:1; + BOOLEAN HighDelayed:1; + + // + // The following list entry is used for performing closes that can't + // be done in the context of the original caller. + // + + ULONG AsyncCloseCount; + LIST_ENTRY AsyncCloseList; + + // + // The following two fields record if we are delaying a close. + // + + ULONG DelayedCloseCount; + LIST_ENTRY DelayedCloseList; + + // + // This is the ExWorkerItem that does both kinds of deferred closes. + // + + PIO_WORKITEM FatCloseItem; + + // + // This spinlock protects several rapid-fire operations. NOTE: this is + // pretty horrible style. + // + + KSPIN_LOCK GeneralSpinLock; + + // + // Cache manager call back structures, which must be passed on each call + // to CcInitializeCacheMap. + // + + CACHE_MANAGER_CALLBACKS CacheManagerCallbacks; + CACHE_MANAGER_CALLBACKS CacheManagerNoOpCallbacks; + +} FAT_DATA; +typedef FAT_DATA *PFAT_DATA; + +// +// An array of these structures will keep + +typedef struct _FAT_WINDOW { + + ULONG FirstCluster; // The first cluster in this window. + ULONG LastCluster; // The last cluster in this window. + ULONG ClustersFree; // The number of clusters free in this window. + +} FAT_WINDOW; +typedef FAT_WINDOW *PFAT_WINDOW; + + +// +// The Vcb (Volume control Block) record corresponds to every volume mounted +// by the file system. They are ordered in a queue off of FatData.VcbQueue. +// This structure must be allocated from non-paged pool +// + +typedef enum _VCB_CONDITION { + VcbGood = 1, + VcbNotMounted, + VcbBad +} VCB_CONDITION; + +typedef struct _VCB { + + // + // This is a common head for the FAT volume file + // + + FSRTL_ADVANCED_FCB_HEADER VolumeFileHeader; + + // + // The links for the device queue off of FatData.VcbQueue + // + + LIST_ENTRY VcbLinks; + + // + // A pointer the device object passed in by the I/O system on a mount + // This is the target device object that the file system talks to when it + // needs to do any I/O (e.g., the disk stripper device object). + // + // + + PDEVICE_OBJECT TargetDeviceObject; + + // + // A pointer to the VPB for the volume passed in by the I/O system on + // a mount. + // + + PVPB Vpb; + + // + // The internal state of the device. This is a collection of fsd device + // state flags. + // + + ULONG VcbState; + VCB_CONDITION VcbCondition; + + // + // A pointer to the root DCB for this volume + // + + struct _FCB *RootDcb; + + // + // If the FAT has so many entries that the free cluster bitmap would + // be too large, we split the FAT into buckets, and only one bucket's + // worth of bits are kept in the bitmap. + // + + ULONG NumberOfWindows; + PFAT_WINDOW Windows; + PFAT_WINDOW CurrentWindow; + + // + // A count of the number of file objects that have opened the volume + // for direct access, and their share access state. + // + + CLONG DirectAccessOpenCount; + SHARE_ACCESS ShareAccess; + + // + // A count of the number of file objects that have any file/directory + // opened on this volume, not including direct access. And also the + // count of the number of file objects that have a file opened for + // only read access (i.e., they cannot be modifying the disk). + // + + CLONG OpenFileCount; + CLONG ReadOnlyCount; + + // + // A count of the number of internal opens on this VCB. + // + + ULONG InternalOpenCount; + + // + // A count of the number of residual opens on this volume. + // This is usually two or three. One is for the virtual volume + // file. One is for the root directory. And one is for the + // EA file, if there is one. + // + + ULONG ResidualOpenCount; + + // + // The bios parameter block field contains + // an unpacked copy of the bpb for the volume, it is initialized + // during mount time and can be read by everyone else after that. + // + + BIOS_PARAMETER_BLOCK Bpb; + + PUCHAR First0x24BytesOfBootSector; + + // + // The following structure contains information useful to the + // allocation support routines. Many of them are computed from + // elements of the Bpb, but are too involved to recompute every time + // they are needed. + // + + struct { + + LBO RootDirectoryLbo; // Lbo of beginning of root directory + LBO FileAreaLbo; // Lbo of beginning of file area + ULONG RootDirectorySize; // size of root directory in bytes + + ULONG NumberOfClusters; // total number of clusters on the volume + ULONG NumberOfFreeClusters; // number of free clusters on the volume + + UCHAR FatIndexBitSize; // indicates if 12, 16, or 32 bit fat table + + UCHAR LogOfBytesPerSector; // Log(Bios->BytesPerSector) + UCHAR LogOfBytesPerCluster; // Log(Bios->SectorsPerCluster) + + } AllocationSupport; + + // + // The following Mcb is used to keep track of dirty sectors in the Fat. + // Runs of holes denote clean sectors while runs of LBO == VBO denote + // dirty sectors. The VBOs are that of the volume file, starting at + // 0. The granuality of dirt is one sectors, and additions are only + // made in sector chunks to prevent problems with several simultaneous + // updaters. + // + + LARGE_MCB DirtyFatMcb; + + // + // The FreeClusterBitMap keeps track of all the clusters in the fat. + // A 1 means occupied while a 0 means free. It allows quick location + // of contiguous runs of free clusters. It is initialized on mount + // or verify. + // + + RTL_BITMAP FreeClusterBitMap; + + // + // The following fast mutex controls access to the free cluster bit map + // and the buckets. + // + + FAST_MUTEX FreeClusterBitMapMutex; + + // + // A resource variable to control access to the volume specific data + // structures + // + + ERESOURCE Resource; + + // + // A resource to make sure no one changes the volume bitmap while + // you're using it. Only for volumes with NumberOfWindows > 1. + // + + ERESOURCE ChangeBitMapResource; + + + // + // The following field points to the file object used to do I/O to + // the virtual volume file. The virtual volume file maps sectors + // 0 through the end of fat and is of a fixed size (determined during + // mount) + // + + PFILE_OBJECT VirtualVolumeFile; + + // + // The following field contains a record of special pointers used by + // MM and Cache to manipluate section objects. Note that the values + // are set outside of the file system. However the file system on an + // open/create will set the file object's SectionObject field to point + // to this field + // + + SECTION_OBJECT_POINTERS SectionObjectPointers; + + // + // The following fields is a hint cluster index used by the file system + // when allocating a new cluster. + // + + ULONG ClusterHint; + + // + // This field contains the "DeviceObject" that this volume is + // currently mounted on. Note Vcb->Vpb->RealDevice is constant. + // + + PDEVICE_OBJECT CurrentDevice; + + // + // This is a pointer to the file object and the Fcb which represent the ea data. + // + + PFILE_OBJECT VirtualEaFile; + struct _FCB *EaFcb; + + // + // The following field is a pointer to the file object that has the + // volume locked. if the VcbState has the locked flag set. + // + + PFILE_OBJECT FileObjectWithVcbLocked; + + // + // The following is the head of a list of notify Irps. + // + + LIST_ENTRY DirNotifyList; + + // + // The following is used to synchronize the dir notify list. + // + + PNOTIFY_SYNC NotifySync; + + // + // The following fast mutex is used to synchronize directory stream + // file object creation. + // + + FAST_MUTEX DirectoryFileCreationMutex; + + // + // This field holds the thread address of the current (or most recent + // depending on VcbState) thread doing a verify operation on this volume. + // + + PKTHREAD VerifyThread; + + // + // The following two structures are used for CleanVolume callbacks. + // + + KDPC CleanVolumeDpc; + KTIMER CleanVolumeTimer; + + // + // This field records the last time FatMarkVolumeDirty was called, and + // avoids excessive calls to push the CleanVolume forward in time. + // + + LARGE_INTEGER LastFatMarkVolumeDirtyCall; + + // + // The following fields holds a pointer to a struct which is used to + // hold performance counters. + // + + struct _FILE_SYSTEM_STATISTICS *Statistics; + + // + // The property tunneling cache for this volume + // + + TUNNEL Tunnel; + + // + // The media change count is returned by IOCTL_CHECK_VERIFY and + // is used to verify that no user-mode app has swallowed a media change + // notification. This is only meaningful for removable media. + // + + ULONG ChangeCount; + + // + // Preallocated VPB for swapout, so we are not forced to consider + // must succeed pool. + // + + PVPB SwapVpb; + + // + // Per volume threading of the close queues. + // + + LIST_ENTRY AsyncCloseList; + LIST_ENTRY DelayedCloseList; + + // + // Fast mutex used by the ADVANCED FCB HEADER in this structure + // + + FAST_MUTEX AdvancedFcbHeaderMutex; + +} VCB; +typedef VCB *PVCB; + +#define VCB_STATE_FLAG_LOCKED (0x00000001) +#define VCB_STATE_FLAG_REMOVABLE_MEDIA (0x00000002) +#define VCB_STATE_FLAG_VOLUME_DIRTY (0x00000004) +#define VCB_STATE_FLAG_MOUNTED_DIRTY (0x00000010) +#define VCB_STATE_FLAG_SHUTDOWN (0x00000040) +#define VCB_STATE_FLAG_CLOSE_IN_PROGRESS (0x00000080) +#define VCB_STATE_FLAG_DELETED_FCB (0x00000100) +#define VCB_STATE_FLAG_CREATE_IN_PROGRESS (0x00000200) +#define VCB_STATE_FLAG_BOOT_OR_PAGING_FILE (0x00000800) +#define VCB_STATE_FLAG_DEFERRED_FLUSH (0x00001000) +#define VCB_STATE_FLAG_ASYNC_CLOSE_ACTIVE (0x00002000) +#define VCB_STATE_FLAG_WRITE_PROTECTED (0x00004000) +#define VCB_STATE_FLAG_REMOVAL_PREVENTED (0x00008000) +#define VCB_STATE_FLAG_VOLUME_DISMOUNTED (0x00010000) +#define VCB_STATE_FLAG_VPB_MUST_BE_FREED (0x00040000) +#define VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS (0x00080000) + +// +// N.B - VOLUME_DISMOUNTED is an indication that FSCTL_DISMOUNT volume was +// executed on a volume. It does not replace VcbCondition as an indication +// that the volume is invalid/unrecoverable. +// + +// +// Define the file system statistics struct. Vcb->Statistics points to an +// array of these (one per processor) and they must be 64 byte aligned to +// prevent cache line tearing. +// + +typedef struct _FILE_SYSTEM_STATISTICS { + + // + // This contains the actual data. + // + + FILESYSTEM_STATISTICS Common; + FAT_STATISTICS Fat; + + // + // Pad this structure to a multiple of 64 bytes. + // + + UCHAR Pad[64-(sizeof(FILESYSTEM_STATISTICS)+sizeof(FAT_STATISTICS))%64]; + +} FILE_SYSTEM_STATISTICS; + +typedef FILE_SYSTEM_STATISTICS *PFILE_SYSTEM_STATISTICS; + + +// +// The Volume Device Object is an I/O system device object with a workqueue +// and an VCB record appended to the end. There are multiple of these +// records, one for every mounted volume, and are created during +// a volume mount operation. The work queue is for handling an overload of +// work requests to the volume. +// + +typedef struct _VOLUME_DEVICE_OBJECT { + + DEVICE_OBJECT DeviceObject; + + // + // The following field tells how many requests for this volume have + // either been enqueued to ExWorker threads or are currently being + // serviced by ExWorker threads. If the number goes above + // a certain threshold, put the request on the overflow queue to be + // executed later. + // + + ULONG PostedRequestCount; + + // + // The following field indicates the number of IRP's waiting + // to be serviced in the overflow queue. + // + + ULONG OverflowQueueCount; + + // + // The following field contains the queue header of the overflow queue. + // The Overflow queue is a list of IRP's linked via the IRP's ListEntry + // field. + // + + LIST_ENTRY OverflowQueue; + + // + // The following spinlock protects access to all the above fields. + // + + KSPIN_LOCK OverflowQueueSpinLock; + + // + // This is a common head for the FAT volume file + // + + FSRTL_COMMON_FCB_HEADER VolumeFileHeader; + + // + // This is the file system specific volume control block. + // + + VCB Vcb; + +} VOLUME_DEVICE_OBJECT; + +typedef VOLUME_DEVICE_OBJECT *PVOLUME_DEVICE_OBJECT; + + +// +// This is the structure used to contains the short name for a file +// + +typedef struct _FILE_NAME_NODE { + + // + // This points back to the Fcb for this file. + // + + struct _FCB *Fcb; + + // + // This is the name of this node. + // + + union { + + OEM_STRING Oem; + + UNICODE_STRING Unicode; + + } Name; + + // + // Marker so we can figure out what kind of name we opened up in + // Fcb searches + // + + BOOLEAN FileNameDos; + + // + // And the links. Our parent Dcb has a pointer to the root entry. + // + + RTL_SPLAY_LINKS Links; + +} FILE_NAME_NODE; +typedef FILE_NAME_NODE *PFILE_NAME_NODE; + +// +// This structure contains fields which must be in non-paged pool. +// + +typedef struct _NON_PAGED_FCB { + + // + // The following field contains a record of special pointers used by + // MM and Cache to manipluate section objects. Note that the values + // are set outside of the file system. However the file system on an + // open/create will set the file object's SectionObject field to point + // to this field + // + + SECTION_OBJECT_POINTERS SectionObjectPointers; + + // + // This context is non-zero only if the file currently has asynchronous + // non-cached valid data length extending writes. It allows + // synchronization between pending writes and other operations. + // + + ULONG OutstandingAsyncWrites; + + // + // This event is set when OutstandingAsyncWrites transitions to zero. + // + + PKEVENT OutstandingAsyncEvent; + + // + // This is the mutex that is inserted into the FCB_ADVANCED_HEADER + // FastMutex field + // + + FAST_MUTEX AdvancedFcbHeaderMutex; + +} NON_PAGED_FCB; + +typedef NON_PAGED_FCB *PNON_PAGED_FCB; + +// +// The Fcb/Dcb record corresponds to every open file and directory, and to +// every directory on an opened path. They are ordered in two queues, one +// queue contains every Fcb/Dcb record off of FatData.FcbQueue, the other +// queue contains only device specific records off of Vcb.VcbSpecificFcbQueue +// + +typedef enum _FCB_CONDITION { + FcbGood = 1, + FcbBad, + FcbNeedsToBeVerified +} FCB_CONDITION; + +typedef struct _FCB { + + // + // The following field is used for fast I/O + // + // The following comments refer to the use of the AllocationSize field + // of the FsRtl-defined header to the nonpaged Fcb. + // + // For a directory when we create a Dcb we will not immediately + // initialize the cache map, instead we will postpone it until our first + // call to FatReadDirectoryFile or FatPrepareWriteDirectoryFile. + // At that time we will search the Fat to find out the current allocation + // size (by calling FatLookupFileAllocationSize) and then initialize the + // cache map to this allocation size. + // + // For a file when we create an Fcb we will not immediately initialize + // the cache map, instead we will postpone it until we need it and + // then we determine the allocation size from either searching the + // fat to determine the real file allocation, or from the allocation + // that we've just allocated if we're creating a file. + // + // A value of -1 indicates that we do not know what the current allocation + // size really is, and need to examine the fat to find it. A value + // of than -1 is the real file/directory allocation size. + // + // Whenever we need to extend the allocation size we call + // FatAddFileAllocation which (if we're really extending the allocation) + // will modify the Fat, Mcb, and update this field. The caller + // of FatAddFileAllocation is then responsible for altering the Cache + // map size. + // + // We are now using the ADVANCED fcb header to support filter contexts + // at the stream level + // + + FSRTL_ADVANCED_FCB_HEADER Header; + + // + // This structure contains fields which must be in non-paged pool. + // + + PNON_PAGED_FCB NonPaged; + + // + // The head of the fat alloaction chain. FirstClusterOfFile == 0 + // means that the file has no current allocation. + // + + ULONG FirstClusterOfFile; + + // + // The links for the queue of all fcbs for a specific dcb off of + // Dcb.ParentDcbQueue. For the root directory this queue is empty + // For a non-existent fcb this queue is off of the non existent + // fcb queue entry in the vcb. + // + + LIST_ENTRY ParentDcbLinks; + + // + // A pointer to the Dcb that is the parent directory containing + // this fcb. If this record itself is the root dcb then this field + // is null. + // + + struct _FCB *ParentDcb; + + // + // A pointer to the Vcb containing this Fcb + // + + PVCB Vcb; + + // + // The internal state of the Fcb. This is a collection Fcb state flags. + // Also the shared access for each time this file/directory is opened. + // + + ULONG FcbState; + FCB_CONDITION FcbCondition; + SHARE_ACCESS ShareAccess; + +#ifdef SYSCACHE_COMPILE + + // + // For syscache we keep a bitmask that tells us if we have dispatched IO for + // the page aligned chunks of the stream. + // + + PULONG WriteMask; + ULONG WriteMaskData; + +#endif + + // + // A count of the number of file objects that have been opened for + // this file/directory, but not yet been cleaned up yet. This count + // is only used for data file objects, not for the Acl or Ea stream + // file objects. This count gets decremented in FatCommonCleanup, + // while the OpenCount below gets decremented in FatCommonClose. + // + + CLONG UncleanCount; + + // + // A count of the number of file objects that have opened + // this file/directory. For files & directories the FsContext of the + // file object points to this record. + // + + CLONG OpenCount; + + // + // A count of how many of "UncleanCount" handles were opened for + // non-cached I/O. + // + + CLONG NonCachedUncleanCount; + + // + // The following field is used to locate the dirent for this fcb/dcb. + // All directory are opened as mapped files so the only additional + // information we need to locate this dirent (beside its parent directory) + // is the byte offset for the dirent. Note that for the root dcb + // this field is not used. + // + + VBO DirentOffsetWithinDirectory; + + // + // The following field is filled in when there is an Lfn associated + // with this file. It is the STARTING offset of the Lfn. + // + + VBO LfnOffsetWithinDirectory; + + // + // Thess entries is kept in ssync with the dirent. It allows a more + // accurate verify capability and speeds up FatFastQueryBasicInfo(). + // + + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + + // + // Valid data to disk + // + + ULONG ValidDataToDisk; + + // + // The following field contains the retrieval mapping structure + // for the file/directory. Note that for the Root Dcb this + // structure is set at mount time. Also note that in this + // implementation of Fat the Mcb really maps VBOs to LBOs and not + // VBNs to LBNs. + // + + LARGE_MCB Mcb; + + // + // The following union is cased off of the node type code for the fcb. + // There is a seperate case for the directory versus file fcbs. + // + + union { + + // + // A Directory Control Block (Dcb) + // + + struct { + + // + // A queue of all the fcbs/dcbs that are opened under this + // Dcb. + // + + LIST_ENTRY ParentDcbQueue; + + // + // The following field points to the file object used to do I/O to + // the directory file for this dcb. The directory file maps the + // sectors for the directory. This field is initialized by + // CreateRootDcb but is left null by CreateDcb. It isn't + // until we try to read/write the directory file that we + // create the stream file object for non root dcbs. + // + + ULONG DirectoryFileOpenCount; + PFILE_OBJECT DirectoryFile; + + // + // If the UnusedDirentVbo is != 0xffffffff, then the dirent at this + // offset is guarenteed to unused. A value of 0xffffffff means + // it has yet to be initialized. Note that a value beyond the + // end of allocation means that there an unused dirent, but we + // will have to allocate another cluster to use it. + // + // DeletedDirentHint contains lowest possible VBO of a deleted + // dirent (assuming as above that it is not 0xffffffff). + // + + VBO UnusedDirentVbo; + VBO DeletedDirentHint; + + // + // The following two entries links together all the Fcbs + // opened under this Dcb sorted in a splay tree by name. + // + // I'd like to go into why we have (and must have) two separate + // splay trees within the current fastfat architecture. I will + // provide some insight into what would have to change if we + // wanted to have a single UNICODE tree. + // + // What makes FAT unique is that both Oem and Unicode names sit + // side by side on disk. Several unique UNICODE names coming + // into fastfat can match a single OEM on-disk name, and there + // is really no way to enumerate all the possible UNICODE + // source strings that can map to a given OEM name. This argues + // for converting the incomming UNICODE name into OEM, and then + // running through an OEM splay tree of the open files. This + // works well when there are only OEM names on disk. + // + // The UNICODE name on disk can be VERY different from the short + // name in the DIRENT and not even representable in the OEM code + // page. Even if it were representable in OEM, it is possible + // that a case varient of the original UNICODE name would match + // a different OEM name, causing us to miss the Fcb in the + // prefix lookup phase. In these cases, we must put UNICODE + // name in the splay to guarentee that we find any case varient + // of the input UNICODE name. See the routine description of + // FatConstructNamesInFcb() for a detailed analysis of how we + // detect this case. + // + // The fundamental limitation we are imposing here is that if + // an Fcb exists for an open file, we MUST find it during the + // prefix stage. This is a basic premise of the create path + // in fastfat. In fact if we later find it gravelling through + // the disk (but not the splay tree), we will bug check if we + // try to add a duplicate entry to the splay tree (not to + // mention having two Fcbs). If we had some mechanism to deal + // with cases (and they would be rare) that we don't find the + // entry in the splay tree, but the Fcb is actually in there, + // then we could go to a single UNICODE splay tree. While + // this uses more pool for the splay tree, and makes string + // compares maybe take a bit as longer, it would eliminate the + // need for any NLS conversion during the prefix phase, so it + // might really be a net win. + // + // The current scheme was optimized for non-extended names + // (i.e. US names). As soon as you start using extended + // characters, then it is clearly a win as many code paths + // become active that would otherwise not be needed if we + // only had a single UNICODE splay tree. + // + // We may think about changing this someday. + // + + PRTL_SPLAY_LINKS RootOemNode; + PRTL_SPLAY_LINKS RootUnicodeNode; + + // + // The following field keeps track of free dirents, i.e., + // dirents that are either unallocated for deleted. + // + + RTL_BITMAP FreeDirentBitmap; + + // + // Since the FCB specific part of this union is larger, use + // the slack here for an initial bitmap buffer. Currently + // there is enough space here for an 8K cluster. + // + + ULONG FreeDirentBitmapBuffer[1]; + + } Dcb; + + // + // A File Control Block (Fcb) + // + + struct { + + // + // The following field is used by the filelock module + // to maintain current byte range locking information. + // + + FILE_LOCK FileLock; + + // + // The following field is used by the oplock module + // to maintain current oplock information. + // + + OPLOCK Oplock; + + // + // This pointer is used to detect writes that eminated in the + // cache manager's lazywriter. It prevents lazy writer threads, + // who already have the Fcb shared, from trying to acquire it + // exclusive, and thus causing a deadlock. + // + + PVOID LazyWriteThread; + + } Fcb; + + } Specific; + + // + // The following field is used to verify that the Ea's for a file + // have not changed between calls to query for Ea's. It is compared + // with a similar field in a Ccb. + // + // IMPORTANT!! **** DO NOT MOVE THIS FIELD **** + // + // The slack space in the union above is computed from + // the field offset of the EaModificationCount. + // + + ULONG EaModificationCount; + + // + // The following field is the fully qualified file name for this FCB/DCB + // starting from the root of the volume, and last file name in the + // fully qualified name. + // + + FILE_NAME_NODE ShortName; + + // + // The following field is only filled in if it is needed with the user's + // opened path + // + + UNICODE_STRING FullFileName; + + USHORT FinalNameLength; + + // + // To make life simpler we also keep in the Fcb/Dcb a current copy of + // the fat attribute byte for the file/directory. This field must + // also be updated when we create the Fcb, modify the File, or verify + // the Fcb + // + + UCHAR DirentFatFlags; + + // + // The case preserved long filename + // + + UNICODE_STRING ExactCaseLongName; + + // + // If the UNICODE Lfn is fully expressible in the system Oem code + // page, then we will store it in a prefix table, otherwise we will + // store the last UNICODE name in the Fcb. In both cases the name + // has been upcased. + // + // Note that we may need neither of these fields if an LFN was strict + // 8.3 or differed only in case. Indeed if there wasn't an LFN, we + // don't need them at all. + // + + union { + + // + // This first field is present if FCB_STATE_HAS_OEM_LONG_NAME + // is set in the FcbState. + // + + FILE_NAME_NODE Oem; + + // + // This first field is present if FCB_STATE_HAS_UNICODE_LONG_NAME + // is set in the FcbState. + // + + FILE_NAME_NODE Unicode; + + } LongName; + + // + // Defragmentation / ReallocateOnWrite synchronization object. This + // is filled in by FatMoveFile() and affects the read and write paths. + // + + PKEVENT MoveFileEvent; + +} FCB, *PFCB; + +#ifndef BUILDING_FSKDEXT +// +// DCB clashes with a type defined outside the filesystems, in headers +// pulled in by FSKD. We don't need this typedef for fskd anyway.... +// +typedef FCB DCB; +typedef DCB *PDCB; +#endif + + +// +// Here are the Fcb state fields. +// + +#define FCB_STATE_DELETE_ON_CLOSE (0x00000001) +#define FCB_STATE_TRUNCATE_ON_CLOSE (0x00000002) +#define FCB_STATE_PAGING_FILE (0x00000004) +#define FCB_STATE_FORCE_MISS_IN_PROGRESS (0x00000008) +#define FCB_STATE_FLUSH_FAT (0x00000010) +#define FCB_STATE_TEMPORARY (0x00000020) +#define FCB_STATE_SYSTEM_FILE (0x00000080) +#define FCB_STATE_NAMES_IN_SPLAY_TREE (0x00000100) +#define FCB_STATE_HAS_OEM_LONG_NAME (0x00000200) +#define FCB_STATE_HAS_UNICODE_LONG_NAME (0x00000400) +#define FCB_STATE_DELAY_CLOSE (0x00000800) + +// +// Copies of the dirent's FAT_DIRENT_NT_BYTE_* flags for +// preserving case of the short name of a file +// + +#define FCB_STATE_8_LOWER_CASE (0x00001000) +#define FCB_STATE_3_LOWER_CASE (0x00002000) + +// +// This is the slack allocation in the Dcb part of the UNION above +// + +#define DCB_UNION_SLACK_SPACE ((ULONG) \ + (FIELD_OFFSET(DCB, EaModificationCount) - \ + FIELD_OFFSET(DCB, Specific.Dcb.FreeDirentBitmapBuffer)) \ +) + +// +// This is the special (64bit) allocation size that indicates the +// real size must be retrieved from disk. Define it here so we +// avoid excessive magic numbering around the driver. +// + +#define FCB_LOOKUP_ALLOCATIONSIZE_HINT ((LONGLONG) -1) + + +// +// The Ccb record is allocated for every file object. Note that this +// record is exactly 0x34 long on x86 so that it will fit into a 0x40 +// piece of pool. Please carefully consider modifications. +// +// Define the Flags field. +// + +#define CCB_FLAG_MATCH_ALL (0x0001) +#define CCB_FLAG_SKIP_SHORT_NAME_COMPARE (0x0002) + +// +// This tells us whether we allocated buffers to hold search templates. +// + +#define CCB_FLAG_FREE_OEM_BEST_FIT (0x0004) +#define CCB_FLAG_FREE_UNICODE (0x0008) + +// +// These flags prevents cleanup from updating the modify time, etc. +// + +#define CCB_FLAG_USER_SET_LAST_WRITE (0x0010) +#define CCB_FLAG_USER_SET_LAST_ACCESS (0x0020) +#define CCB_FLAG_USER_SET_CREATION (0x0040) + +// +// This bit says the file object associated with this Ccb was opened for +// read only access. +// + +#define CCB_FLAG_READ_ONLY (0x0080) + +// +// These flags, are used is DASD handles in read and write. +// + +#define CCB_FLAG_DASD_FLUSH_DONE (0x0100) +#define CCB_FLAG_DASD_PURGE_DONE (0x0200) + +// +// This flag keeps track of a handle that was opened for +// DELETE_ON_CLOSE. +// + +#define CCB_FLAG_DELETE_ON_CLOSE (0x0400) + +// +// This flag keeps track of which side of the name pair on the file +// associated with the handle was opened +// + +#define CCB_FLAG_OPENED_BY_SHORTNAME (0x0800) + +// +// This flag indicates that the query template has not been upcased +// (i.e., query should be case-insensitive) +// + +#define CCB_FLAG_QUERY_TEMPLATE_MIXED (0x1000) + +// +// This flag indicates that reads and writes via this DASD handle +// are allowed to start or extend past the end of file. +// + +#define CCB_FLAG_ALLOW_EXTENDED_DASD_IO (0x2000) + +// +// This flag indicates we want to match volume labels in directory +// searches (important for the root dir defrag). +// + +#define CCB_FLAG_MATCH_VOLUME_ID (0x4000) + +// +// This flag indicates the ccb has been converted over into a +// close context for asynchronous/delayed closing of the handle. +// + +#define CCB_FLAG_CLOSE_CONTEXT (0x8000) + +// +// This flag indicates that when the handle is closed, we want +// a physical dismount to occur. +// + +#define CCB_FLAG_COMPLETE_DISMOUNT (0x10000) + +// +// This flag indicates the handle may not call priveleged +// FSCTL which modify the volume. +// + +#define CCB_FLAG_MANAGE_VOLUME_ACCESS (0x20000) + +// +// This structure is used to keep track of information needed to do a +// deferred close. It is now embedded in a CCB so we don't have to +// allocate one in the close path (with mustsucceed). +// + +typedef struct _CLOSE_CONTEXT { + + // + // Two sets of links, one for the global list and one for closes + // on a particular volume. + // + + LIST_ENTRY GlobalLinks; + LIST_ENTRY VcbLinks; + + PVCB Vcb; + PFCB Fcb; + enum _TYPE_OF_OPEN TypeOfOpen; + BOOLEAN Free; + +} CLOSE_CONTEXT; + +typedef CLOSE_CONTEXT *PCLOSE_CONTEXT; + +typedef struct _CCB { + + // + // Type and size of this record (must be FAT_NTC_CCB) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // Define a 24bit wide field for Flags, but a UCHAR for Wild Cards Present + // since it is used so often. Line these up on byte boundaries for grins. + // + + ULONG Flags:24; + BOOLEAN ContainsWildCards; + + // + // Overlay a close context on the data of the CCB. The remaining + // fields are not useful during close, and we would like to avoid + // paying extra pool for it. + // + + union { + + struct { + + // + // Save the offset to start search from. + // + + VBO OffsetToStartSearchFrom; + + // + // The query template is used to filter directory query requests. + // It originally is set to null and on the first call the NtQueryDirectory + // it is set to the input filename or "*" if the name is not supplied. + // All subsquent queries then use this template. + // + // The Oem structure are unions because if the name is wild we store + // the arbitrary length string, while if the name is constant we store + // 8.3 representation for fast comparison. + // + + union { + + // + // If the template contains a wild card use this. + // + + OEM_STRING Wild; + + // + // If the name is constant, use this part. + // + + FAT8DOT3 Constant; + + } OemQueryTemplate; + + UNICODE_STRING UnicodeQueryTemplate; + + // + // The field is compared with the similar field in the Fcb to determine + // if the Ea's for a file have been modified. + // + + ULONG EaModificationCount; + + // + // The following field is used as an offset into the Eas for a + // particular file. This will be the offset for the next + // Ea to return. A value of 0xffffffff indicates that the + // Ea's are exhausted. + // + + ULONG OffsetOfNextEaToReturn; + + }; + + CLOSE_CONTEXT CloseContext; + }; + +} CCB; +typedef CCB *PCCB; + +// +// The Irp Context record is allocated for every orginating Irp. It is +// created by the Fsd dispatch routines, and deallocated by the FatComplete +// request routine. It contains a structure called of type REPINNED_BCBS +// which is used to retain pinned bcbs needed to handle abnormal termination +// unwinding. +// + +#define REPINNED_BCBS_ARRAY_SIZE (4) + +typedef struct _REPINNED_BCBS { + + // + // A pointer to the next structure contains additional repinned bcbs + // + + struct _REPINNED_BCBS *Next; + + // + // A fixed size array of pinned bcbs. Whenever a new bcb is added to + // the repinned bcb structure it is added to this array. If the + // array is already full then another repinned bcb structure is allocated + // and pointed to with Next. + // + + PBCB Bcb[ REPINNED_BCBS_ARRAY_SIZE ]; + +} REPINNED_BCBS; +typedef REPINNED_BCBS *PREPINNED_BCBS; + +typedef struct _IRP_CONTEXT { + + // + // Type and size of this record (must be FAT_NTC_IRP_CONTEXT) + // + + NODE_TYPE_CODE NodeTypeCode; + NODE_BYTE_SIZE NodeByteSize; + + // + // This structure is used for posting to the Ex worker threads. + // + + WORK_QUEUE_ITEM WorkQueueItem; + + // + // A pointer to the originating Irp. + // + + PIRP OriginatingIrp; + + // + // Originating Device (required for workque algorithms) + // + + PDEVICE_OBJECT RealDevice; + + // + // Originating Vcb (required for exception handling) + // On mounts, this will be set before any exceptions + // indicating corruption can be thrown. + // + + PVCB Vcb; + + // + // Major and minor function codes copied from the Irp + // + + UCHAR MajorFunction; + UCHAR MinorFunction; + + // + // The following fields indicate if we can wait/block for a resource + // or I/O, if we are to do everything write through, and if this + // entry into the Fsd is a recursive call. + // + + UCHAR PinCount; + + ULONG Flags; + + // + // The following field contains the NTSTATUS value used when we are + // unwinding due to an exception + // + + NTSTATUS ExceptionStatus; + + // + // The following context block is used for non-cached Io + // + + struct _FAT_IO_CONTEXT *FatIoContext; + + // + // For a abnormal termination unwinding this field contains the Bcbs + // that are kept pinned until the Irp is completed. + // + + REPINNED_BCBS Repinned; + +} IRP_CONTEXT; +typedef IRP_CONTEXT *PIRP_CONTEXT; + +#define IRP_CONTEXT_FLAG_DISABLE_DIRTY (0x00000001) +#define IRP_CONTEXT_FLAG_WAIT (0x00000002) +#define IRP_CONTEXT_FLAG_WRITE_THROUGH (0x00000004) +#define IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH (0x00000008) +#define IRP_CONTEXT_FLAG_RECURSIVE_CALL (0x00000010) +#define IRP_CONTEXT_FLAG_DISABLE_POPUPS (0x00000020) +#define IRP_CONTEXT_FLAG_DEFERRED_WRITE (0x00000040) +#define IRP_CONTEXT_FLAG_VERIFY_READ (0x00000080) +#define IRP_CONTEXT_STACK_IO_CONTEXT (0x00000100) +#define IRP_CONTEXT_FLAG_IN_FSP (0x00000200) +#define IRP_CONTEXT_FLAG_USER_IO (0x00000400) // for performance counters +#define IRP_CONTEXT_FLAG_DISABLE_RAISE (0x00000800) +#define IRP_CONTEXT_FLAG_PARENT_BY_CHILD (0x80000000) + + +// +// Context structure for non-cached I/O calls. Most of these fields +// are actually only required for the Read/Write Multiple routines, but +// the caller must allocate one as a local variable anyway before knowing +// whether there are multiple requests are not. Therefore, a single +// structure is used for simplicity. +// + +typedef struct _FAT_IO_CONTEXT { + + // + // These two field are used for multiple run Io + // + + LONG IrpCount; + PIRP MasterIrp; + + // + // MDL to describe partial sector zeroing + // + + PMDL ZeroMdl; + + union { + + // + // This element handles the asychronous non-cached Io + // + + struct { + PERESOURCE Resource; + PERESOURCE Resource2; + ERESOURCE_THREAD ResourceThreadId; + ULONG RequestedByteCount; + PFILE_OBJECT FileObject; + PNON_PAGED_FCB NonPagedFcb; + } Async; + + // + // and this element the sycnrhonous non-cached Io + // + + KEVENT SyncEvent; + + } Wait; + +} FAT_IO_CONTEXT; + +typedef FAT_IO_CONTEXT *PFAT_IO_CONTEXT; + +// +// An array of these structures is passed to FatMultipleAsync describing +// a set of runs to execute in parallel. +// + +typedef struct _IO_RUNS { + + LBO Lbo; + VBO Vbo; + ULONG Offset; + ULONG ByteCount; + PIRP SavedIrp; + +} IO_RUN; + +typedef IO_RUN *PIO_RUN; + +// +// This structure is used by FatDeleteDirent to preserve the first cluster +// and file size info for undelete utilities. +// + +typedef struct _DELETE_CONTEXT { + + ULONG FileSize; + ULONG FirstClusterOfFile; + +} DELETE_CONTEXT; + +typedef DELETE_CONTEXT *PDELETE_CONTEXT; + +// +// This record is used with to set a flush to go off one second after the +// first write on slow devices with a physical indication of activity, like +// a floppy. This is an attempt to keep the red light on. +// + +typedef struct _DEFERRED_FLUSH_CONTEXT { + + KDPC Dpc; + KTIMER Timer; + WORK_QUEUE_ITEM Item; + + PFILE_OBJECT File; + +} DEFERRED_FLUSH_CONTEXT; + +typedef DEFERRED_FLUSH_CONTEXT *PDEFERRED_FLUSH_CONTEXT; + +// +// This structure is used for the FatMarkVolumeClean callbacks. +// + +typedef struct _CLEAN_AND_DIRTY_VOLUME_PACKET { + + WORK_QUEUE_ITEM Item; + PIRP Irp; + PVCB Vcb; + PKEVENT Event; +} CLEAN_AND_DIRTY_VOLUME_PACKET, *PCLEAN_AND_DIRTY_VOLUME_PACKET; + +// +// This structure is used when a page fault is running out of stack. +// + +typedef struct _PAGING_FILE_OVERFLOW_PACKET { + PIRP Irp; + PFCB Fcb; +} PAGING_FILE_OVERFLOW_PACKET, *PPAGING_FILE_OVERFLOW_PACKET; + +// +// This structure is used to access the EaFile. +// + +#define EA_BCB_ARRAY_SIZE 8 + +typedef struct _EA_RANGE { + + PCHAR Data; + ULONG StartingVbo; + ULONG Length; + USHORT BcbChainLength; + BOOLEAN AuxilaryBuffer; + PBCB *BcbChain; + PBCB BcbArray[EA_BCB_ARRAY_SIZE]; + +} EA_RANGE, *PEA_RANGE; + +#define EA_RANGE_HEADER_SIZE (FIELD_OFFSET( EA_RANGE, BcbArray )) + +// +// These symbols are used by the upcase/downcase routines. +// + +#define WIDE_LATIN_CAPITAL_A (0xff21) +#define WIDE_LATIN_CAPITAL_Z (0xff3a) +#define WIDE_LATIN_SMALL_A (0xff41) +#define WIDE_LATIN_SMALL_Z (0xff5a) + +// +// These values are returned by FatInterpretClusterType. +// + +typedef enum _CLUSTER_TYPE { + FatClusterAvailable, + FatClusterReserved, + FatClusterBad, + FatClusterLast, + FatClusterNext +} CLUSTER_TYPE; + + +#endif // _FATSTRUC_ + + diff --git a/drivers/filesystems/fastfat_new/fileinfo.c b/drivers/filesystems/fastfat_new/fileinfo.c new file mode 100644 index 00000000000..2190a31f688 --- /dev/null +++ b/drivers/filesystems/fastfat_new/fileinfo.c @@ -0,0 +1,4635 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FileInfo.c + +Abstract: + + This module implements the File Information routines for Fat called by + the dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FILEINFO) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_FILEINFO) + +VOID +FatQueryBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_BASIC_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryStandardInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryInternalInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_INTERNAL_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryEaInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_EA_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_POSITION_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCCB Ccb, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryShortNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PLONG Length + ); + +VOID +FatQueryNetworkInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + IN OUT PLONG Length + ); + +NTSTATUS +FatSetBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PCCB Ccb + ); + +NTSTATUS +FatSetDispositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PFCB Fcb + ); + +NTSTATUS +FatSetRenameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PVCB Vcb, + IN PFCB Fcb, + IN PCCB Ccb + ); + +NTSTATUS +FatSetPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject + ); + +NTSTATUS +FatSetAllocationInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject + ); + +NTSTATUS +FatSetEndOfFileInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PFCB Fcb + ); + +VOID +FatDeleteFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB TargetDcb, + IN ULONG LfnOffset, + IN ULONG DirentOffset, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn + ); + +VOID +FatRenameEAs ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN USHORT ExtendedAttributes, + IN POEM_STRING OldOemName + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonQueryInformation) +#pragma alloc_text(PAGE, FatCommonSetInformation) +#pragma alloc_text(PAGE, FatFsdQueryInformation) +#pragma alloc_text(PAGE, FatFsdSetInformation) +#pragma alloc_text(PAGE, FatQueryBasicInfo) +#pragma alloc_text(PAGE, FatQueryEaInfo) +#pragma alloc_text(PAGE, FatQueryInternalInfo) +#pragma alloc_text(PAGE, FatQueryNameInfo) +#pragma alloc_text(PAGE, FatQueryNetworkInfo) +#pragma alloc_text(PAGE, FatQueryShortNameInfo) +#pragma alloc_text(PAGE, FatQueryPositionInfo) +#pragma alloc_text(PAGE, FatQueryStandardInfo) +#pragma alloc_text(PAGE, FatSetAllocationInfo) +#pragma alloc_text(PAGE, FatSetBasicInfo) +#pragma alloc_text(PAGE, FatSetDispositionInfo) +#pragma alloc_text(PAGE, FatSetEndOfFileInfo) +#pragma alloc_text(PAGE, FatSetPositionInfo) +#pragma alloc_text(PAGE, FatSetRenameInfo) +#pragma alloc_text(PAGE, FatDeleteFile) +#pragma alloc_text(PAGE, FatRenameEAs) +#endif + + +NTSTATUS +NTAPI +FatFsdQueryInformation ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the Fsd part of the NtQueryInformationFile API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being queried exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdQueryInformation\n", 0); + + // + // Call the common query routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonQueryInformation( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdQueryInformation -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +NTAPI +FatFsdSetInformation ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtSetInformationFile API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being set exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdSetInformation\n", 0); + + // + // Call the common set routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonSetInformation( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdSetInformation -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonQueryInformation ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for querying file information called by both + the fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + + LONG Length; + FILE_INFORMATION_CLASS FileInformationClass; + PVOID Buffer; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN FcbAcquired; + + PFILE_ALL_INFORMATION AllInfo; + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + FileObject = IrpSp->FileObject; + + DebugTrace(+1, Dbg, "FatCommonQueryInformation...\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.QueryFile.Length); + DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass); + DebugTrace( 0, Dbg, "->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Reference our input parameters to make things easier + // + + Length = (LONG)IrpSp->Parameters.QueryFile.Length; + FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Decode the file object + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + FcbAcquired = FALSE; + Status = STATUS_SUCCESS; + + _SEH2_TRY { + + // + // Case on the type of open we're dealing with + // + + switch (TypeOfOpen) { + + case UserVolumeOpen: + + // + // We cannot query the user volume open. + // + + Status = STATUS_INVALID_PARAMETER; + break; + + case UserFileOpen: + + case UserDirectoryOpen: + + case DirectoryFile: + + // + // Acquire shared access to the fcb, except for a paging file + // in order to avoid deadlocks with Mm. + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + if (!FatAcquireSharedFcb( IrpContext, Fcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + IrpContext = NULL; + Irp = NULL; + + try_return( Status ); + } + + FcbAcquired = TRUE; + } + + // + // Make sure the Fcb is in a usable condition. This + // will raise an error condition if the fcb is unusable + // + + FatVerifyFcb( IrpContext, Fcb ); + + // + // Based on the information class we'll do different + // actions. Each of hte procedures that we're calling fills + // up the output buffer, if possible. They will raise the + // status STATUS_BUFFER_OVERFLOW for an insufficient buffer. + // This is considered a somewhat unusual case and is handled + // more cleanly with the exception mechanism rather than + // testing a return status value for each call. + // + + switch (FileInformationClass) { + + case FileAllInformation: + + // + // For the all information class we'll typecast a local + // pointer to the output buffer and then call the + // individual routines to fill in the buffer. + // + + AllInfo = Buffer; + Length -= (sizeof(FILE_ACCESS_INFORMATION) + + sizeof(FILE_MODE_INFORMATION) + + sizeof(FILE_ALIGNMENT_INFORMATION)); + + FatQueryBasicInfo( IrpContext, Fcb, FileObject, &AllInfo->BasicInformation, &Length ); + FatQueryStandardInfo( IrpContext, Fcb, &AllInfo->StandardInformation, &Length ); + FatQueryInternalInfo( IrpContext, Fcb, &AllInfo->InternalInformation, &Length ); + FatQueryEaInfo( IrpContext, Fcb, &AllInfo->EaInformation, &Length ); + FatQueryPositionInfo( IrpContext, FileObject, &AllInfo->PositionInformation, &Length ); + FatQueryNameInfo( IrpContext, Fcb, Ccb, &AllInfo->NameInformation, &Length ); + + break; + + case FileBasicInformation: + + FatQueryBasicInfo( IrpContext, Fcb, FileObject, Buffer, &Length ); + break; + + case FileStandardInformation: + + FatQueryStandardInfo( IrpContext, Fcb, Buffer, &Length ); + break; + + case FileInternalInformation: + + FatQueryInternalInfo( IrpContext, Fcb, Buffer, &Length ); + break; + + case FileEaInformation: + + FatQueryEaInfo( IrpContext, Fcb, Buffer, &Length ); + break; + + case FilePositionInformation: + + FatQueryPositionInfo( IrpContext, FileObject, Buffer, &Length ); + break; + + case FileNameInformation: + + FatQueryNameInfo( IrpContext, Fcb, Ccb, Buffer, &Length ); + break; + + case FileAlternateNameInformation: + + FatQueryShortNameInfo( IrpContext, Fcb, Buffer, &Length ); + break; + + case FileNetworkOpenInformation: + + FatQueryNetworkInfo( IrpContext, Fcb, FileObject, Buffer, &Length ); + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + break; + + default: + + KdPrintEx((DPFLTR_FASTFAT_ID, + DPFLTR_INFO_LEVEL, + "FATQueryFile, Illegal TypeOfOpen = %08lx\n", + TypeOfOpen)); + + Status = STATUS_INVALID_PARAMETER; + break; + } + + // + // If we overflowed the buffer, set the length to 0 and change the + // status to STATUS_BUFFER_OVERFLOW. + // + + if ( Length < 0 ) { + + Status = STATUS_BUFFER_OVERFLOW; + + Length = 0; + } + + // + // Set the information field to the number of bytes actually filled in + // and then complete the request + // + + Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatCommonQueryInformation ); + + if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + + +NTSTATUS +FatCommonSetInformation ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for setting file information called by both + the fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + FILE_INFORMATION_CLASS FileInformationClass; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN VcbAcquired = FALSE; + BOOLEAN FcbAcquired = FALSE; + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonSetInformation...\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.SetFile.Length); + DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass); + DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->Parameters.SetFile.FileObject); + DebugTrace( 0, Dbg, "->ReplaceIfExists = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists); + DebugTrace( 0, Dbg, "->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Reference our input parameters to make things easier + // + + FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass; + FileObject = IrpSp->FileObject; + + // + // Decode the file object + // + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + _SEH2_TRY { + + // + // Case on the type of open we're dealing with + // + + switch (TypeOfOpen) { + + case UserVolumeOpen: + + // + // We cannot query the user volume open. + // + + try_return( Status = STATUS_INVALID_PARAMETER ); + break; + + case UserFileOpen: + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) && + ((FileInformationClass == FileEndOfFileInformation) || + (FileInformationClass == FileAllocationInformation))) { + + // + // We check whether we can proceed + // based on the state of the file oplocks. + // + + Status = FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock, + Irp, + IrpContext, + NULL, + NULL ); + + if (Status != STATUS_SUCCESS) { + + try_return( Status ); + } + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + } + break; + + case UserDirectoryOpen: + + break; + + default: + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // We can only do a set on a nonroot dcb, so we do the test + // and then fall through to the user file open code. + // + + if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) { + + if (FileInformationClass == FileDispositionInformation) { + + try_return( Status = STATUS_CANNOT_DELETE ); + } + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // In the following two cases, we cannot have creates occuring + // while we are here, so acquire the volume exclusive. + // + + if ((FileInformationClass == FileDispositionInformation) || + (FileInformationClass == FileRenameInformation)) { + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + Irp = NULL; + IrpContext = NULL; + + try_return( Status ); + } + + VcbAcquired = TRUE; + } + + // + // We need to look here to check whether the oplock state + // will allow us to continue. We may have to loop to prevent + // an oplock being granted between the time we check the oplock + // and obtain the Fcb. + // + + // + // Acquire exclusive access to the Fcb, We use exclusive + // because it is probable that one of the subroutines + // that we call will need to monkey with file allocation, + // create/delete extra fcbs. So we're willing to pay the + // cost of exclusive Fcb access. + // + // Note that we do not acquire the resource for paging file + // operations in order to avoid deadlock with Mm. + // + + if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + Irp = NULL; + IrpContext = NULL; + + try_return( Status ); + } + + FcbAcquired = TRUE; + } + + Status = STATUS_SUCCESS; + + // + // Make sure the Fcb is in a usable condition. This + // will raise an error condition if the fcb is unusable + // + + FatVerifyFcb( IrpContext, Fcb ); + + // + // Based on the information class we'll do different + // actions. Each of the procedures that we're calling will either + // complete the request of send the request off to the fsp + // to do the work. + // + + switch (FileInformationClass) { + + case FileBasicInformation: + + Status = FatSetBasicInfo( IrpContext, Irp, Fcb, Ccb ); + break; + + case FileDispositionInformation: + + // + // If this is on deferred flush media, we have to be able to wait. + // + + if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) && + !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + Irp = NULL; + IrpContext = NULL; + + } else { + + Status = FatSetDispositionInfo( IrpContext, Irp, FileObject, Fcb ); + } + + break; + + case FileRenameInformation: + + // + // We proceed with this operation only if we can wait + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + Irp = NULL; + IrpContext = NULL; + + } else { + + Status = FatSetRenameInfo( IrpContext, Irp, Vcb, Fcb, Ccb ); + + // + // If STATUS_PENDING is returned it means the oplock + // package has the Irp. Don't complete the request here. + // + + if (Status == STATUS_PENDING) { + Irp = NULL; + IrpContext = NULL; + } + } + + break; + + case FilePositionInformation: + + Status = FatSetPositionInfo( IrpContext, Irp, FileObject ); + break; + + case FileLinkInformation: + + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + + case FileAllocationInformation: + + Status = FatSetAllocationInfo( IrpContext, Irp, Fcb, FileObject ); + break; + + case FileEndOfFileInformation: + + Status = FatSetEndOfFileInfo( IrpContext, Irp, FileObject, Vcb, Fcb ); + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + if ( IrpContext != NULL ) { + + FatUnpinRepinnedBcbs( IrpContext ); + } + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatCommonSetInformation ); + + if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } + if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); } + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonSetInformation -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_BASIC_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + Description: + + This routine performs the query basic information function for fat. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + FileObject - Supplies the flag bit that indicates the file was modified. + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + DebugTrace(+1, Dbg, "FatQueryBasicInfo...\n", 0); + + // + // Zero out the output buffer, and set it to indicate that + // the query is a normal file. Later we might overwrite the + // attribute. + // + + RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) ); + + // + // Extract the data and fill in the non zero fields of the output + // buffer + // + + if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) { + + // + // We have to munge a lie on the fly. Every time we have to + // use 1/1/80 we need to convert to GMT since the TZ may have + // changed on us. + // + + ExLocalTimeToSystemTime( &FatJanOne1980, + &Buffer->LastWriteTime ); + Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime; + + } else { + + Buffer->LastWriteTime = Fcb->LastWriteTime; + Buffer->CreationTime = Fcb->CreationTime; + Buffer->LastAccessTime = Fcb->LastAccessTime; + } + + Buffer->FileAttributes = Fcb->DirentFatFlags; + + // + // If the temporary flag is set, then set it in the buffer. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { + + SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); + } + + // + // If no attributes were set, set the normal bit. + // + + if (Buffer->FileAttributes == 0) { + + Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_BASIC_INFORMATION ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryBasicInfo -> VOID\n", 0); + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryStandardInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_STANDARD_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query standard information function for fat. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + DebugTrace(+1, Dbg, "FatQueryStandardInfo...\n", 0); + + // + // Zero out the output buffer, and fill in the number of links + // and the delete pending flag. + // + + RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) ); + + Buffer->NumberOfLinks = 1; + Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + + // + // Case on whether this is a file or a directory, and extract + // the information and fill in the fcb/dcb specific parts + // of the output buffer + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, Fcb ); + } + + Buffer->AllocationSize = Fcb->Header.AllocationSize; + Buffer->EndOfFile = Fcb->Header.FileSize; + + Buffer->Directory = FALSE; + + } else { + + Buffer->Directory = TRUE; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_STANDARD_INFORMATION ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryStandardInfo -> VOID\n", 0); + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryInternalInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_INTERNAL_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query internal information function for fat. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + DebugTrace(+1, Dbg, "FatQueryInternalInfo...\n", 0); + + _SEH2_TRY { + + Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb ); + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_INTERNAL_INFORMATION ); + + } _SEH2_FINALLY { + + DebugUnwind( FatQueryInternalInfo ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0); + } _SEH2_END; + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryEaInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_EA_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query Ea information function for fat. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + PBCB Bcb; + + DebugTrace(+1, Dbg, "FatQueryEaInfo...\n", 0); + + Bcb = NULL; + + _SEH2_TRY { + + // + // Zero out the output buffer + // + + RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) ); + + // + // The Root dcb does not have any EAs so don't look for any. Fat32 + // doesn't have any, either. + // + + if ( NodeType( Fcb ) != FAT_NTC_ROOT_DCB && + !FatIsFat32( Fcb->Vcb )) { + + PDIRENT Dirent; + + // + // Try to get the dirent for this file. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &Bcb ); + + if (Dirent != NULL) { + + // + // Get a the size needed to store the full eas for the file. + // + + FatGetEaLength( IrpContext, + Fcb->Vcb, + Dirent, + &Buffer->EaSize ); + } + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_EA_INFORMATION ); + + } _SEH2_FINALLY { + + DebugUnwind( FatQueryEaInfo ); + + // + // Unpin the dirent if pinned. + // + + FatUnpinBcb( IrpContext, Bcb ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryEaInfo -> VOID\n", 0); + } _SEH2_END; + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_POSITION_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query position information function for fat. + +Arguments: + + FileObject - Supplies the File object being queried + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + DebugTrace(+1, Dbg, "FatQueryPositionInfo...\n", 0); + + // + // Get the current position found in the file object. + // + + Buffer->CurrentByteOffset = FileObject->CurrentByteOffset; + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_POSITION_INFORMATION ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryPositionInfo -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCCB Ccb, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine performs the query name information function for fat. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Ccb - Supplies the Ccb for the context of the user open + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + ULONG BytesToCopy; + LONG TrimLength; + BOOLEAN Overflow = FALSE; + + DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0); + + // + // Convert the name to UNICODE + // + + *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]); + + // + // Use the full filename to build the path up. If we wanted to be + // slick in the future, we'd just build the path directly into the + // return buffer and avoid constructing the full filename, but since + // the full filename winds up being required so often lets not + // over optimize this case yet. + // + + if (Fcb->FullFileName.Buffer == NULL) { + + FatSetFullFileNameInFcb( IrpContext, Fcb ); + } + + // + // Here is where it gets a smidge tricky. FinalNameLength is the length + // of the LFN element if it exists, and since the long name is always used + // to build FullFileName, we have two cases: + // + // 1) short name: use FinalNameLength to tear off the path from FullFileName + // and append the UNICODE converted short name. + // 2) long name: just use FullFileName + // + // We bias to the name the user thinks they opened by. This winds + // up fixing some oddball tunneling cases where intermediate filters + // translate operations like delete into renames - this lets them + // do the operation in the context of the name the user was using. + // + // It also matches what NTFS does, and so we have the definition of + // correct behavior. + // + + // + // + // Assume there is no long name and we are just going to use + // FullFileName. + // + + TrimLength = 0; + + // + // If a LongName exists and the original open was by the short name + // then set TrimLength to point to the place where the short name goes. + // + // + // Note: The Ccb can be NULL. The lazy writer calls to get the name of + // a DirectoryOpen FILE_OBJECT that it wants to display in the lost + // delayed write popup. Handle this case by just using the FileFullName. + // + + if (Fcb->LongName.Unicode.Name.Unicode.Buffer != NULL) { + + if ((Ccb != NULL) && FlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME)) { + + TrimLength = Fcb->FinalNameLength; + + } + + } + + if (*Length < Fcb->FullFileName.Length - TrimLength) { + + BytesToCopy = *Length; + Overflow = TRUE; + + } else { + + BytesToCopy = Fcb->FullFileName.Length - TrimLength; + *Length -= BytesToCopy; + } + + RtlCopyMemory( &Buffer->FileName[0], + Fcb->FullFileName.Buffer, + BytesToCopy ); + + // + // Note that this is just the amount of name we've copied so far. It'll + // either be all of it (long) or the path element including the \ (short). + // + + Buffer->FileNameLength = Fcb->FullFileName.Length - TrimLength; + + // + // If we trimmed off the name element, this is the short name case. Pick + // up the UNICODE conversion and append it. + // + + if (TrimLength != 0) { + + UNICODE_STRING ShortName; + WCHAR ShortNameBuffer[12]; + NTSTATUS Status; + + // + // Convert the short name to UNICODE and figure out how much + // of it can fit. Again, we always bump the returned length + // to indicate how much is available even if we can't return it. + // + + ShortName.Length = 0; + ShortName.MaximumLength = sizeof(ShortNameBuffer); + ShortName.Buffer = ShortNameBuffer; + + Status = RtlOemStringToCountedUnicodeString( &ShortName, + &Fcb->ShortName.Name.Oem, + FALSE ); + + ASSERT( Status == STATUS_SUCCESS ); + + if (!Overflow) { + + if (*Length < ShortName.Length) { + + BytesToCopy = *Length; + Overflow = TRUE; + + } else { + + BytesToCopy = ShortName.Length; + *Length -= BytesToCopy; + } + + RtlCopyMemory( (PUCHAR)&Buffer->FileName[0] + Buffer->FileNameLength, + ShortName.Buffer, + BytesToCopy ); + } + + Buffer->FileNameLength += ShortName.Length; + } + + if (Overflow) { + + *Length = -1; + } + + // + // Return to caller + // + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryShortNameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_NAME_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + +Routine Description: + + This routine queries the short name of the file. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + NTSTATUS Status; + + ULONG BytesToCopy; + WCHAR ShortNameBuffer[12]; + UNICODE_STRING ShortName; + + DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0); + + // + // Convert the name to UNICODE + // + + ShortName.Length = 0; + ShortName.MaximumLength = sizeof(ShortNameBuffer); + ShortName.Buffer = ShortNameBuffer; + + *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]); + + Status = RtlOemStringToCountedUnicodeString( &ShortName, + &Fcb->ShortName.Name.Oem, + FALSE ); + + ASSERT( Status == STATUS_SUCCESS ); + + // + // If we overflow, set *Length to -1 as a flag. + // + + if (*Length < ShortName.Length) { + + BytesToCopy = *Length; + *Length = -1; + + } else { + + BytesToCopy = ShortName.Length; + *Length -= ShortName.Length; + } + + RtlCopyMemory( &Buffer->FileName[0], + &ShortName.Buffer[0], + BytesToCopy ); + + Buffer->FileNameLength = ShortName.Length; + + // + // Return to caller + // + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +// +// Internal Support Routine +// + +VOID +FatQueryNetworkInfo ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject, + IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer, + IN OUT PLONG Length + ) + +/*++ + Description: + + This routine performs the query network open information function for fat. + +Arguments: + + Fcb - Supplies the Fcb being queried, it has been verified + + FileObject - Supplies the flag bit that indicates the file was modified. + + Buffer - Supplies a pointer to the buffer where the information is to + be returned + + Length - Supplies the length of the buffer in bytes, and receives the + remaining bytes free in the buffer upon return. + +Return Value: + + None + +--*/ + +{ + DebugTrace(+1, Dbg, "FatQueryNetworkInfo...\n", 0); + + // + // Zero out the output buffer, and set it to indicate that + // the query is a normal file. Later we might overwrite the + // attribute. + // + + RtlZeroMemory( Buffer, sizeof(FILE_NETWORK_OPEN_INFORMATION) ); + + // + // Extract the data and fill in the non zero fields of the output + // buffer + // + + if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) { + + // + // We have to munge a lie on the fly. Every time we have to + // use 1/1/80 we need to convert to GMT since the TZ may have + // changed on us. + // + + ExLocalTimeToSystemTime( &FatJanOne1980, + &Buffer->LastWriteTime ); + Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime; + + } else { + + Buffer->LastWriteTime.QuadPart = Fcb->LastWriteTime.QuadPart; + Buffer->CreationTime.QuadPart = Fcb->CreationTime.QuadPart; + Buffer->LastAccessTime.QuadPart = Fcb->LastAccessTime.QuadPart; + } + + Buffer->FileAttributes = Fcb->DirentFatFlags; + + // + // If the temporary flag is set, then set it in the buffer. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) { + + SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); + } + + // + // If no attributes were set, set the normal bit. + // + + if (Buffer->FileAttributes == 0) { + + Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL; + } + // + // Case on whether this is a file or a directory, and extract + // the information and fill in the fcb/dcb specific parts + // of the output buffer + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, Fcb ); + } + + Buffer->AllocationSize.QuadPart = Fcb->Header.AllocationSize.QuadPart; + Buffer->EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart; + } + + // + // Update the length and status output variables + // + + *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION ); + + DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length); + + DebugTrace(-1, Dbg, "FatQueryNetworkInfo -> VOID\n", 0); + + return; +} + + +// +// Internal Support routine +// + +NTSTATUS +FatSetBasicInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine performs the set basic information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + + Ccb - Supplies the flag bit that control updating the last modify + time on cleanup. + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + NTSTATUS Status; + + PFILE_BASIC_INFORMATION Buffer; + + PDIRENT Dirent; + PBCB DirentBcb; + + FAT_TIME_STAMP CreationTime; + UCHAR CreationMSec; + FAT_TIME_STAMP LastWriteTime; + FAT_TIME_STAMP LastAccessTime; + FAT_DATE LastAccessDate; + UCHAR Attributes; + + BOOLEAN ModifyCreation = FALSE; + BOOLEAN ModifyLastWrite = FALSE; + BOOLEAN ModifyLastAccess = FALSE; + + LARGE_INTEGER LargeCreationTime; + LARGE_INTEGER LargeLastWriteTime; + LARGE_INTEGER LargeLastAccessTime; + + + ULONG NotifyFilter = 0; + + DebugTrace(+1, Dbg, "FatSetBasicInfo...\n", 0); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // If the user is specifying -1 for a field, that means + // we should leave that field unchanged, even if we might + // have otherwise set it ourselves. We'll set the Ccb flag + // saying that the user set the field so that we + // don't do our default updating. + // + // We set the field to 0 then so we know not to actually + // set the field to the user-specified (and in this case, + // illegal) value. + // + + if (Buffer->LastWriteTime.QuadPart == -1) { + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); + Buffer->LastWriteTime.QuadPart = 0; + } + + if (Buffer->LastAccessTime.QuadPart == -1) { + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS ); + Buffer->LastAccessTime.QuadPart = 0; + } + + if (Buffer->CreationTime.QuadPart == -1) { + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION ); + Buffer->CreationTime.QuadPart = 0; + } + + DirentBcb = NULL; + + Status = STATUS_SUCCESS; + + _SEH2_TRY { + + LARGE_INTEGER FatLocalDecThirtyOne1979; + LARGE_INTEGER FatLocalJanOne1980; + + ExLocalTimeToSystemTime( &FatDecThirtyOne1979, + &FatLocalDecThirtyOne1979 ); + + ExLocalTimeToSystemTime( &FatJanOne1980, + &FatLocalJanOne1980 ); + + // + // Get a pointer to the dirent + // + + ASSERT( Fcb->FcbCondition == FcbGood ); + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &DirentBcb ); + + ASSERT( Dirent && DirentBcb ); + + // + // Check if the user specified a non-zero creation time + // + + if (FatData.ChicagoMode && (Buffer->CreationTime.QuadPart != 0)) { + + LargeCreationTime = Buffer->CreationTime; + + // + // Convert the Nt time to a Fat time + // + + if ( !FatNtTimeToFatTime( IrpContext, + &LargeCreationTime, + FALSE, + &CreationTime, + &CreationMSec )) { + + // + // Special case the value 12/31/79 and treat this as 1/1/80. + // This '79 value can happen because of time zone issues. + // + + if ((LargeCreationTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && + (LargeCreationTime.QuadPart < FatLocalJanOne1980.QuadPart)) { + + CreationTime = FatTimeJanOne1980; + LargeCreationTime = FatLocalJanOne1980; + + } else { + + DebugTrace(0, Dbg, "Invalid CreationTime\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Don't worry about CreationMSec + // + + CreationMSec = 0; + } + + ModifyCreation = TRUE; + } + + // + // Check if the user specified a non-zero last access time + // + + if (FatData.ChicagoMode && (Buffer->LastAccessTime.QuadPart != 0)) { + + LargeLastAccessTime = Buffer->LastAccessTime; + + // + // Convert the Nt time to a Fat time + // + + if ( !FatNtTimeToFatTime( IrpContext, + &LargeLastAccessTime, + TRUE, + &LastAccessTime, + NULL )) { + + // + // Special case the value 12/31/79 and treat this as 1/1/80. + // This '79 value can happen because of time zone issues. + // + + if ((LargeLastAccessTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && + (LargeLastAccessTime.QuadPart < FatLocalJanOne1980.QuadPart)) { + + LastAccessTime = FatTimeJanOne1980; + LargeLastAccessTime = FatLocalJanOne1980; + + } else { + + DebugTrace(0, Dbg, "Invalid LastAccessTime\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + } + + LastAccessDate = LastAccessTime.Date; + ModifyLastAccess = TRUE; + } + + // + // Check if the user specified a non-zero last write time + // + + if (Buffer->LastWriteTime.QuadPart != 0) { + + // + // First do a quick check here if the this time is the same + // time as LastAccessTime. + // + + if (ModifyLastAccess && + (Buffer->LastWriteTime.QuadPart == Buffer->LastAccessTime.QuadPart)) { + + ModifyLastWrite = TRUE; + LastWriteTime = LastAccessTime; + LargeLastWriteTime = LargeLastAccessTime; + + } else { + + LargeLastWriteTime = Buffer->LastWriteTime; + + // + // Convert the Nt time to a Fat time + // + + if ( !FatNtTimeToFatTime( IrpContext, + &LargeLastWriteTime, + TRUE, + &LastWriteTime, + NULL )) { + + + // + // Special case the value 12/31/79 and treat this as 1/1/80. + // This '79 value can happen because of time zone issues. + // + + if ((LargeLastWriteTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) && + (LargeLastWriteTime.QuadPart < FatLocalJanOne1980.QuadPart)) { + + LastWriteTime = FatTimeJanOne1980; + LargeLastWriteTime = FatLocalJanOne1980; + + } else { + + DebugTrace(0, Dbg, "Invalid LastWriteTime\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + } + + ModifyLastWrite = TRUE; + } + } + + + // + // Check if the user specified a non zero file attributes byte + // + + if (Buffer->FileAttributes != 0) { + + // + // Only permit the attributes that FAT understands. The rest are silently + // dropped on the floor. + // + + Attributes = (UCHAR)(Buffer->FileAttributes & (FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_DIRECTORY | + FILE_ATTRIBUTE_ARCHIVE)); + + // + // Make sure that for a file the directory bit is not set + // and that for a directory the bit is set. + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) { + + DebugTrace(0, Dbg, "Attempt to set dir attribute on file\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + } else { + + Attributes |= FAT_DIRENT_ATTR_DIRECTORY; + } + + // + // Mark the FcbState temporary flag correctly. + // + + if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) { + + // + // Don't allow the temporary bit to be set on directories. + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + DebugTrace(0, Dbg, "No temporary directories\n", 0); + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY ); + + SetFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags, + FO_TEMPORARY_FILE ); + + } else { + + ClearFlag( Fcb->FcbState, FCB_STATE_TEMPORARY ); + + ClearFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags, + FO_TEMPORARY_FILE ); + } + + // + // Set the new attributes byte, and mark the bcb dirty + // + + Fcb->DirentFatFlags = Attributes; + + Dirent->Attributes = Attributes; + + NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES; + } + + if ( ModifyCreation ) { + + // + // Set the new last write time in the dirent, and mark + // the bcb dirty + // + + Fcb->CreationTime = LargeCreationTime; + Dirent->CreationTime = CreationTime; + Dirent->CreationMSec = CreationMSec; + + + NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION; + // + // Now we have to round the time in the Fcb up to the + // nearest tem msec. + // + + Fcb->CreationTime.QuadPart = + + ((Fcb->CreationTime.QuadPart + AlmostTenMSec) / + TenMSec) * TenMSec; + + // + // Now because the user just set the creation time we + // better not set the creation time on close + // + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION ); + } + + if ( ModifyLastAccess ) { + + // + // Set the new last write time in the dirent, and mark + // the bcb dirty + // + + Fcb->LastAccessTime = LargeLastAccessTime; + Dirent->LastAccessDate = LastAccessDate; + + NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS; + + // + // Now we have to truncate the time in the Fcb down to the + // current day. This has to be in LocalTime though, so first + // convert to local, trunacate, then set back to GMT. + // + + ExSystemTimeToLocalTime( &Fcb->LastAccessTime, + &Fcb->LastAccessTime ); + + Fcb->LastAccessTime.QuadPart = + + (Fcb->LastAccessTime.QuadPart / + FatOneDay.QuadPart) * FatOneDay.QuadPart; + + ExLocalTimeToSystemTime( &Fcb->LastAccessTime, + &Fcb->LastAccessTime ); + + // + // Now because the user just set the last access time we + // better not set the last access time on close + // + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS ); + } + + if ( ModifyLastWrite ) { + + // + // Set the new last write time in the dirent, and mark + // the bcb dirty + // + + Fcb->LastWriteTime = LargeLastWriteTime; + Dirent->LastWriteTime = LastWriteTime; + + NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE; + + // + // Now we have to round the time in the Fcb up to the + // nearest two seconds. + // + + Fcb->LastWriteTime.QuadPart = + + ((Fcb->LastWriteTime.QuadPart + AlmostTwoSeconds) / + TwoSeconds) * TwoSeconds; + + // + // Now because the user just set the last write time we + // better not set the last write time on close + // + + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); + } + + // + // If we modified any of the values, we report this to the notify + // package. + // + // We also take this opportunity to set the current file size and + // first cluster in the Dirent in order to support a server hack. + // + + if (NotifyFilter != 0) { + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + Dirent->FileSize = Fcb->Header.FileSize.LowPart; + + Dirent->FirstClusterOfFile = (USHORT)Fcb->FirstClusterOfFile; + + if (FatIsFat32(Fcb->Vcb)) { + + Dirent->FirstClusterOfFileHi = + (USHORT)(Fcb->FirstClusterOfFile >> 16); + } + } + + FatNotifyReportChange( IrpContext, + Fcb->Vcb, + Fcb, + NotifyFilter, + FILE_ACTION_MODIFIED ); + + FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); + } + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatSetBasicInfo ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + +// +// Internal Support Routine +// + +NTSTATUS +FatSetDispositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine performs the set disposition information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + FileObject - Supplies the file object being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + PFILE_DISPOSITION_INFORMATION Buffer; + PBCB Bcb; + PDIRENT Dirent; + + DebugTrace(+1, Dbg, "FatSetDispositionInfo...\n", 0); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Check if the user wants to delete the file or not delete + // the file + // + + if (Buffer->DeleteFile) { + + // + // Check if the file is marked read only + // + + if (FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY)) { + + DebugTrace(-1, Dbg, "Cannot delete readonly file\n", 0); + + return STATUS_CANNOT_DELETE; + } + + // + // Make sure there is no process mapping this file as an image. + // + + if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers, + MmFlushForDelete )) { + + DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0); + + return STATUS_CANNOT_DELETE; + } + + // + // Check if this is a dcb and if so then only allow + // the request if the directory is empty. + // + + if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) { + + DebugTrace(-1, Dbg, "Cannot delete root Directory\n", 0); + + return STATUS_CANNOT_DELETE; + } + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + DebugTrace(-1, Dbg, "User wants to delete a directory\n", 0); + + // + // Check if the directory is empty + // + + if ( !FatIsDirectoryEmpty(IrpContext, Fcb) ) { + + DebugTrace(-1, Dbg, "Directory is not empty\n", 0); + + return STATUS_DIRECTORY_NOT_EMPTY; + } + } + + // + // If this is a floppy, touch the volume so to verify that it + // is not write protected. + // + + if ( FlagOn(Fcb->Vcb->Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) { + + PVCB Vcb; + PBCB Bcb = NULL; + UCHAR *Buffer; + UCHAR TmpChar; + ULONG BytesToMap; + + IO_STATUS_BLOCK Iosb; + + Vcb = Fcb->Vcb; + + BytesToMap = Vcb->AllocationSupport.FatIndexBitSize == 12 ? + FatReservedBytes(&Vcb->Bpb) + + FatBytesPerFat(&Vcb->Bpb):PAGE_SIZE; + + FatReadVolumeFile( IrpContext, + Vcb, + 0, + BytesToMap, + &Bcb, + (PVOID *)&Buffer ); + + _SEH2_TRY { + + if (!CcPinMappedData( Vcb->VirtualVolumeFile, + &FatLargeZero, + BytesToMap, + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), + &Bcb )) { + + // + // Could not pin the data without waiting (cache miss). + // + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // Make Mm, myself, and Cc think the byte is dirty, and then + // force a writethrough. + // + + Buffer += FatReservedBytes(&Vcb->Bpb); + + TmpChar = Buffer[0]; + Buffer[0] = TmpChar; + + FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb, + FatReservedBytes( &Vcb->Bpb ), + FatReservedBytes( &Vcb->Bpb ), + Vcb->Bpb.BytesPerSector ); + + } _SEH2_FINALLY { + + if (_SEH2_AbnormalTermination() && (Bcb != NULL)) { + + FatUnpinBcb( IrpContext, Bcb ); + } + } _SEH2_END; + + CcRepinBcb( Bcb ); + CcSetDirtyPinnedData( Bcb, NULL ); + CcUnpinData( Bcb ); + DbgDoit( ASSERT( IrpContext->PinCount )); + DbgDoit( IrpContext->PinCount -= 1 ); + CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb ); + + // + // If this was not successful, raise the status. + // + + if ( !NT_SUCCESS(Iosb.Status) ) { + + FatNormalizeAndRaiseStatus( IrpContext, Iosb.Status ); + } + + } else { + + // + // Just set a Bcb dirty here. The above code was only there to + // detect a write protected floppy, while the below code works + // for any write protected media and only takes a hit when the + // volume in clean. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &Bcb ); + + // + // This has to work for the usual reasons (we verified the Fcb within + // volume synch). + // + + ASSERT( Bcb != NULL ); + + _SEH2_TRY { + + FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE ); + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, Bcb ); + } _SEH2_END; + } + + // + // At this point either we have a file or an empty directory + // so we know the delete can proceed. + // + + SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + FileObject->DeletePending = TRUE; + + // + // If this is a directory then report this delete pending to + // the dir notify package. + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + FsRtlNotifyFullChangeDirectory( Fcb->Vcb->NotifySync, + &Fcb->Vcb->DirNotifyList, + FileObject->FsContext, + NULL, + FALSE, + FALSE, + 0, + NULL, + NULL, + NULL ); + } + } else { + + // + // The user doesn't want to delete the file so clear + // the delete on close bit + // + + DebugTrace(0, Dbg, "User want to not delete file\n", 0); + + ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + FileObject->DeletePending = FALSE; + } + + DebugTrace(-1, Dbg, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0); + + return STATUS_SUCCESS; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatSetRenameInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PVCB Vcb, + IN PFCB Fcb, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine performs the set name information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + Vcb - Supplies the Vcb being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + + Ccb - Supplies the Ccb corresponding to the handle opening the source + file + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + BOOLEAN AllLowerComponent; + BOOLEAN AllLowerExtension; + BOOLEAN CaseOnlyRename; + BOOLEAN ContinueWithRename; + BOOLEAN CreateLfn; + BOOLEAN DeleteSourceDirent; + BOOLEAN DeleteTarget; + BOOLEAN NewDirentFromPool; + BOOLEAN RenamedAcrossDirectories; + BOOLEAN ReplaceIfExists; + + CCB LocalCcb; + PCCB SourceCcb; + + DIRENT SourceDirent; + + NTSTATUS Status; + + OEM_STRING OldOemName; + OEM_STRING NewOemName; + UCHAR OemNameBuffer[24*2]; + + PBCB DotDotBcb; + PBCB NewDirentBcb; + PBCB OldDirentBcb; + PBCB SecondPageBcb; + PBCB TargetDirentBcb; + + PDCB TargetDcb; + PDCB OldParentDcb; + + PDIRENT DotDotDirent; + PDIRENT FirstPageDirent; + PDIRENT NewDirent; + PDIRENT OldDirent; + PDIRENT SecondPageDirent; + PDIRENT ShortDirent; + PDIRENT TargetDirent; + + PFCB TempFcb; + + PFILE_OBJECT TargetFileObject; + PFILE_OBJECT FileObject; + + PIO_STACK_LOCATION IrpSp; + + PLIST_ENTRY Links; + + ULONG BytesInFirstPage; + ULONG DirentsInFirstPage; + ULONG DirentsRequired; + ULONG NewOffset; + ULONG NotifyAction; + ULONG SecondPageOffset; + ULONG ShortDirentOffset; + ULONG TargetDirentOffset; + ULONG TargetLfnOffset; + + UNICODE_STRING NewName; + UNICODE_STRING NewUpcasedName; + UNICODE_STRING OldName; + UNICODE_STRING OldUpcasedName; + UNICODE_STRING TargetLfn; + + PWCHAR UnicodeBuffer; + + UNICODE_STRING UniTunneledShortName; + WCHAR UniTunneledShortNameBuffer[12]; + UNICODE_STRING UniTunneledLongName; + WCHAR UniTunneledLongNameBuffer[26]; + LARGE_INTEGER TunneledCreationTime; + ULONG TunneledDataSize; + BOOLEAN HaveTunneledInformation; + BOOLEAN UsingTunneledLfn = FALSE; + + BOOLEAN InvalidateFcbOnRaise = FALSE; + + DebugTrace(+1, Dbg, "FatSetRenameInfo...\n", 0); + + // + // P H A S E 0: Initialize some variables. + // + + CaseOnlyRename = FALSE; + ContinueWithRename = FALSE; + DeleteSourceDirent = FALSE; + DeleteTarget = FALSE; + NewDirentFromPool = FALSE; + RenamedAcrossDirectories = FALSE; + + DotDotBcb = NULL; + NewDirentBcb = NULL; + OldDirentBcb = NULL; + SecondPageBcb = NULL; + TargetDirentBcb = NULL; + + NewOemName.Length = 0; + NewOemName.MaximumLength = 24; +#ifndef __REACTOS__ + NewOemName.Buffer = &OemNameBuffer[0]; +#else + NewOemName.Buffer = (PCHAR)&OemNameBuffer[0]; +#endif + + OldOemName.Length = 0; + OldOemName.MaximumLength = 24; +#ifndef __REACTOS__ + OldOemName.Buffer = &OemNameBuffer[24]; +#else + OldOemName.Buffer = (PCHAR)&OemNameBuffer[24]; +#endif + + UnicodeBuffer = FsRtlAllocatePoolWithTag( PagedPool, + 4 * MAX_LFN_CHARACTERS * sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + + NewUpcasedName.Length = 0; + NewUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + NewUpcasedName.Buffer = &UnicodeBuffer[0]; + + OldName.Length = 0; + OldName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + OldName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS]; + + OldUpcasedName.Length = 0; + OldUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + OldUpcasedName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 2]; + + TargetLfn.Length = 0; + TargetLfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + TargetLfn.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 3]; + + UniTunneledShortName.Length = 0; + UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer); + UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0]; + + UniTunneledLongName.Length = 0; + UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer); + UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0]; + + // + // Remember the name in case we have to modify the name + // value in the ea. + // + + RtlCopyMemory( OldOemName.Buffer, + Fcb->ShortName.Name.Oem.Buffer, + OldOemName.Length ); + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Extract information from the Irp to make our life easier + // + + FileObject = IrpSp->FileObject; + SourceCcb = FileObject->FsContext2; + TargetFileObject = IrpSp->Parameters.SetFile.FileObject; + ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists; + + RtlZeroMemory( &LocalCcb, sizeof(CCB) ); + + // + // P H A S E 1: + // + // Test if rename is legal. Only small side-effects are not undone. + // + + _SEH2_TRY { + + // + // Can't rename the root directory + // + + if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Check that we were not given a dcb with open handles beneath + // it. If there are only UncleanCount == 0 Fcbs beneath us, then + // remove them from the prefix table, and they will just close + // and go away naturally. + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + PFCB BatchOplockFcb; + ULONG BatchOplockCount; + + // + // Loop until there are no batch oplocks in the subtree below + // this directory. + // + + while (TRUE) { + + BatchOplockFcb = NULL; + BatchOplockCount = 0; + + // + // First look for any UncleanCount != 0 Fcbs, and fail if we + // find any. + // + + for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb); + TempFcb != Fcb; + TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) { + + if ( TempFcb->UncleanCount != 0 ) { + + // + // If there is a batch oplock on this file then + // increment our count and remember the Fcb if + // this is the first. + // + + if ( (NodeType(TempFcb) == FAT_NTC_FCB) && + FsRtlCurrentBatchOplock( &TempFcb->Specific.Fcb.Oplock ) ) { + + BatchOplockCount += 1; + if ( BatchOplockFcb == NULL ) { + + BatchOplockFcb = TempFcb; + } + + } else { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + } + } + + // + // If this is not the first pass for rename and the number + // of batch oplocks has not decreased then give up. + // + + if ( BatchOplockFcb != NULL ) { + + if ( (Irp->IoStatus.Information != 0) && + (BatchOplockCount >= Irp->IoStatus.Information) ) { + + try_return( Status = STATUS_ACCESS_DENIED ); + } + + // + // Try to break this batch oplock. + // + + Irp->IoStatus.Information = BatchOplockCount; + Status = FsRtlCheckOplock( &BatchOplockFcb->Specific.Fcb.Oplock, + Irp, + IrpContext, + FatOplockComplete, + NULL ); + + // + // If the oplock was already broken then look for more + // batch oplocks. + // + + if (Status == STATUS_SUCCESS) { + + continue; + } + + // + // Otherwise the oplock package will post or complete the + // request. + // + + try_return( Status = STATUS_PENDING ); + } + + break; + } + + // + // Now try to get as many of these file object, and thus Fcbs + // to go away as possible, flushing first, of course. + // + + FatPurgeReferencedFileObjects( IrpContext, Fcb, TRUE ); + + // + // OK, so there are no UncleanCount != 0, Fcbs. Infact, there + // shouldn't really be any Fcbs left at all, except obstinate + // ones from user mapped sections ....oh well, he shouldn't have + // closed his handle if he wanted the file to stick around. So + // remove any Fcbs beneath us from the splay table and mark them + // DELETE_ON_CLOSE so that any future operations will fail. + // + + for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb); + TempFcb != Fcb; + TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) { + + FatRemoveNames( IrpContext, TempFcb ); + + SetFlag( TempFcb->FcbState, FCB_STATE_DELETE_ON_CLOSE ); + } + } + + // + // Check if this is a simple rename or a fully-qualified rename + // In both cases we need to figure out what the TargetDcb, and + // NewName are. + // + + if (TargetFileObject == NULL) { + + // + // In the case of a simple rename the target dcb is the + // same as the source file's parent dcb, and the new file name + // is taken from the system buffer + // + + PFILE_RENAME_INFORMATION Buffer; + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + TargetDcb = Fcb->ParentDcb; + + NewName.Length = (USHORT) Buffer->FileNameLength; + NewName.Buffer = (PWSTR) &Buffer->FileName; + + // + // Make sure the name is of legal length. + // + + if (NewName.Length >= 255*sizeof(WCHAR)) { + + try_return( Status = STATUS_OBJECT_NAME_INVALID ); + } + + } else { + + // + // For a fully-qualified rename the target dcb is taken from + // the target file object, which must be on the same vcb as + // the source. + // + + PVCB TargetVcb; + PCCB TargetCcb; + + if ((FatDecodeFileObject( TargetFileObject, + &TargetVcb, + &TargetDcb, + &TargetCcb ) != UserDirectoryOpen) || + (TargetVcb != Vcb)) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // This name is by definition legal. + // + + NewName = *((PUNICODE_STRING)&TargetFileObject->FileName); + } + + // + // We will need an upcased version of the unicode name and the + // old name as well. + // + + Status = RtlUpcaseUnicodeString( &NewUpcasedName, &NewName, FALSE ); + + if (!NT_SUCCESS(Status)) { + + try_return( Status ); + } + + FatGetUnicodeNameFromFcb( IrpContext, Fcb, &OldName ); + + Status = RtlUpcaseUnicodeString( &OldUpcasedName, &OldName, FALSE ); + + if (!NT_SUCCESS(Status)) { + try_return(Status); + } + + // + // Check if the current name and new name are equal, and the + // DCBs are equal. If they are then our work is already done. + // + + if (TargetDcb == Fcb->ParentDcb) { + + // + // OK, now if we found something then check if it was an exact + // match or just a case match. If it was an exact match, then + // we can bail here. + // + + if (FsRtlAreNamesEqual( &NewName, + &OldName, + FALSE, + NULL )) { + + try_return( Status = STATUS_SUCCESS ); + } + + // + // Check now for a case only rename. + // + + + if (FsRtlAreNamesEqual( &NewUpcasedName, + &OldUpcasedName, + FALSE, + NULL )) { + + CaseOnlyRename = TRUE; + } + + } else { + + RenamedAcrossDirectories = TRUE; + } + + // + // Upcase the name and convert it to the Oem code page. + // + // If the new UNICODE name is already more than 12 characters, + // then we know the Oem name will not be valid + // + + if (NewName.Length <= 12*sizeof(WCHAR)) { + + FatUnicodeToUpcaseOem( IrpContext, &NewOemName, &NewName ); + + // + // If the name is not valid 8.3, zero the length. + // + + if (FatSpaceInName( IrpContext, &NewName ) || + !FatIsNameShortOemValid( IrpContext, NewOemName, FALSE, FALSE, FALSE)) { + + NewOemName.Length = 0; + } + + } else { + + NewOemName.Length = 0; + } + + // + // Look in the tunnel cache for names and timestamps to restore + // + + TunneledDataSize = sizeof(LARGE_INTEGER); + HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel, + FatDirectoryKey(TargetDcb), + &NewName, + &UniTunneledShortName, + &UniTunneledLongName, + &TunneledDataSize, + &TunneledCreationTime ); + ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER)); + + // + // Now we need to determine how many dirents this new name will + // require. + // + + if ((NewOemName.Length == 0) || + (FatEvaluateNameCase( IrpContext, + &NewName, + &AllLowerComponent, + &AllLowerExtension, + &CreateLfn ), + CreateLfn)) { + + DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&NewName) + 1; + + } else { + + // + // The user-given name is a short name, but we might still have + // a tunneled long name we want to use. See if we can. + // + + if (UniTunneledLongName.Length && + !FatLfnDirentExists(IrpContext, TargetDcb, &UniTunneledLongName, &TargetLfn)) { + + UsingTunneledLfn = CreateLfn = TRUE; + DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&UniTunneledLongName) + 1; + + } else { + + // + // This really is a simple dirent. Note that the two AllLower BOOLEANs + // are correctly set now. + // + + DirentsRequired = 1; + } + } + + // + // Do some extra checks here if we are not in Chicago mode. + // + + if (!FatData.ChicagoMode) { + + // + // If the name was not 8.3 valid, fail the rename. + // + + if (NewOemName.Length == 0) { + + try_return( Status = STATUS_OBJECT_NAME_INVALID ); + } + + // + // Don't use the magic bits. + // + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + CreateLfn = FALSE; + UsingTunneledLfn = FALSE; + } + + if (!CaseOnlyRename) { + + // + // Check if the new name already exists, wait is known to be + // true. + // + + if (NewOemName.Length != 0) { + + FatStringTo8dot3( IrpContext, + NewOemName, + &LocalCcb.OemQueryTemplate.Constant ); + + } else { + + SetFlag( LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ); + } + + LocalCcb.UnicodeQueryTemplate = NewUpcasedName; + LocalCcb.ContainsWildCards = FALSE; + + FatLocateDirent( IrpContext, + TargetDcb, + &LocalCcb, + 0, + &TargetDirent, + &TargetDirentBcb, +#ifndef __REACTOS__ + &TargetDirentOffset, +#else + (PVBO)&TargetDirentOffset, +#endif + NULL, + &TargetLfn); + + if (TargetDirent != NULL) { + + // + // The name already exists, check if the user wants + // to overwrite the name, and has access to do the overwrite + // We cannot overwrite a directory. + // + + if ((!ReplaceIfExists) || + (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY)) || + (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY))) { + + try_return( Status = STATUS_OBJECT_NAME_COLLISION ); + } + + // + // Check that the file has no open user handles, if it does + // then we will deny access. We do the check by searching + // down the list of fcbs opened under our parent Dcb, and making + // sure none of the maching Fcbs have a non-zero unclean count or + // outstanding image sections. + // + + for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; ) { + + TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + // + // Advance now. The image section flush may cause the final + // close, which will recursively happen underneath of us here. + // It would be unfortunate if we looked through free memory. + // + + Links = Links->Flink; + + if ((TempFcb->DirentOffsetWithinDirectory == TargetDirentOffset) && + ((TempFcb->UncleanCount != 0) || + !MmFlushImageSection( &TempFcb->NonPaged->SectionObjectPointers, + MmFlushForDelete))) { + + // + // If there are batch oplocks on this file then break the + // oplocks before failing the rename. + // + + Status = STATUS_ACCESS_DENIED; + + if ((NodeType(TempFcb) == FAT_NTC_FCB) && + FsRtlCurrentBatchOplock( &TempFcb->Specific.Fcb.Oplock )) { + + // + // Do all of our cleanup now since the IrpContext + // could go away when this request is posted. + // + + FatUnpinBcb( IrpContext, TargetDirentBcb ); + + Status = FsRtlCheckOplock( &TempFcb->Specific.Fcb.Oplock, + Irp, + IrpContext, + FatOplockComplete, + NULL ); + + if (Status != STATUS_PENDING) { + + Status = STATUS_ACCESS_DENIED; + } + } + + try_return( NOTHING ); + } + } + + // + // OK, this target is toast. Remember the Lfn offset. + // + + TargetLfnOffset = TargetDirentOffset - + FAT_LFN_DIRENTS_NEEDED(&TargetLfn) * + sizeof(DIRENT); + + DeleteTarget = TRUE; + } + } + + // + // If we will need more dirents than we have, allocate them now. + // + + if ((TargetDcb != Fcb->ParentDcb) || + (DirentsRequired != + (Fcb->DirentOffsetWithinDirectory - + Fcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1)) { + + // + // Get some new allocation + // + + NewOffset = FatCreateNewDirent( IrpContext, + TargetDcb, + DirentsRequired ); + + DeleteSourceDirent = TRUE; + + } else { + + NewOffset = Fcb->LfnOffsetWithinDirectory; + } + + ContinueWithRename = TRUE; + + try_exit: NOTHING; + + } _SEH2_FINALLY { + + if (!ContinueWithRename) { + + // + // Undo everything from above. + // + + ExFreePool( UnicodeBuffer ); + FatUnpinBcb( IrpContext, TargetDirentBcb ); + } + } _SEH2_END; + + // + // Now, if we are already done, return here. + // + + if (!ContinueWithRename) { + + return Status; + } + + // + // P H A S E 2: Actually perform the rename. + // + + _SEH2_TRY { + + // + // Report the fact that we are going to remove this entry. + // If we renamed within the same directory and the new name for the + // file did not previously exist, we report this as a rename old + // name. Otherwise this is a removed file. + // + + if (!RenamedAcrossDirectories && !DeleteTarget) { + + NotifyAction = FILE_ACTION_RENAMED_OLD_NAME; + + } else { + + NotifyAction = FILE_ACTION_REMOVED; + } + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + ((NodeType( Fcb ) == FAT_NTC_FCB) + ? FILE_NOTIFY_CHANGE_FILE_NAME + : FILE_NOTIFY_CHANGE_DIR_NAME ), + NotifyAction ); + + // + // Capture a copy of the source dirent. + // + + FatGetDirentFromFcbOrDcb( IrpContext, Fcb, &OldDirent, &OldDirentBcb ); + SourceDirent = *OldDirent; + + _SEH2_TRY { + + // + // Tunnel the source Fcb - the names are disappearing regardless of + // whether the dirent allocation physically changed + // + + FatTunnelFcbOrDcb( Fcb, SourceCcb ); + + // + // From here until very nearly the end of the operation, if we raise there + // is no reasonable way to suppose we'd be able to undo the damage. Not + // being a transactional filesystem, FAT is at the mercy of a lot of things + // (as the astute reader has no doubt realized by now). + // + + InvalidateFcbOnRaise = TRUE; + + // + // Delete our current dirent(s) if we got a new one. + // + + if (DeleteSourceDirent) { + + FatDeleteDirent( IrpContext, Fcb, NULL, FALSE ); + } + + // + // Delete a target conflict if we were meant to. + // + + if (DeleteTarget) { + + FatDeleteFile( IrpContext, + TargetDcb, + TargetLfnOffset, + TargetDirentOffset, + TargetDirent, + &TargetLfn ); + } + + // + // We need to evaluate any short names required. If there were any + // conflicts in existing short names, they would have been deleted above. + // + // It isn't neccesary to worry about the UsingTunneledLfn case. Since we + // actually already know whether CreateLfn will be set either NewName is + // an Lfn and !UsingTunneledLfn is implied or NewName is a short name and + // we can handle that externally. + // + + FatSelectNames( IrpContext, + TargetDcb, + &NewOemName, + &NewName, + &NewOemName, + (HaveTunneledInformation ? &UniTunneledShortName : NULL), + &AllLowerComponent, + &AllLowerExtension, + &CreateLfn ); + + if (!CreateLfn && UsingTunneledLfn) { + + CreateLfn = TRUE; + NewName = UniTunneledLongName; + + // + // Short names are always upcase if an LFN exists + // + + AllLowerComponent = FALSE; + AllLowerExtension = FALSE; + } + + // + // OK, now setup the new dirent(s) for the new name. + // + + FatPrepareWriteDirectoryFile( IrpContext, + TargetDcb, + NewOffset, + sizeof(DIRENT), + &NewDirentBcb, +#ifndef __REACTOS__ + &NewDirent, +#else + (PVOID *)&NewDirent, +#endif + FALSE, + TRUE, + &Status ); + + ASSERT( NT_SUCCESS( Status ) ); + + // + // Deal with the special case of an LFN + Dirent structure crossing + // a page boundry. + // + + if ((NewOffset / PAGE_SIZE) != + ((NewOffset + (DirentsRequired - 1) * sizeof(DIRENT)) / PAGE_SIZE)) { + + SecondPageOffset = (NewOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE; + + BytesInFirstPage = SecondPageOffset - NewOffset; + + DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT); + + FatPrepareWriteDirectoryFile( IrpContext, + TargetDcb, + SecondPageOffset, + sizeof(DIRENT), + &SecondPageBcb, +#ifndef __REACTOS__ + &SecondPageDirent, +#else + (PVOID *)&SecondPageDirent, +#endif + FALSE, + TRUE, + &Status ); + + ASSERT( NT_SUCCESS( Status ) ); + + FirstPageDirent = NewDirent; + + NewDirent = FsRtlAllocatePoolWithTag( PagedPool, + DirentsRequired * sizeof(DIRENT), + TAG_DIRENT ); + + NewDirentFromPool = TRUE; + } + + // + // Bump up Dirent and DirentOffset + // + + ShortDirent = NewDirent + DirentsRequired - 1; + ShortDirentOffset = NewOffset + (DirentsRequired - 1) * sizeof(DIRENT); + + // + // Fill in the fields of the dirent. + // + + *ShortDirent = SourceDirent; + + FatConstructDirent( IrpContext, + ShortDirent, + &NewOemName, + AllLowerComponent, + AllLowerExtension, + CreateLfn ? &NewName : NULL, + SourceDirent.Attributes, + FALSE, + (HaveTunneledInformation ? &TunneledCreationTime : NULL) ); + + if (HaveTunneledInformation) { + + // + // Need to go in and fix the timestamps in the FCB. Note that we can't use + // the TunneledCreationTime since the conversions may have failed. + // + + Fcb->CreationTime = FatFatTimeToNtTime(IrpContext, ShortDirent->CreationTime, ShortDirent->CreationMSec); + Fcb->LastWriteTime = FatFatTimeToNtTime(IrpContext, ShortDirent->LastWriteTime, 0); + Fcb->LastAccessTime = FatFatDateToNtTime(IrpContext, ShortDirent->LastAccessDate); + } + + // + // If the dirent crossed pages, split the contents of the + // temporary pool between the two pages. + // + + if (NewDirentFromPool) { + + RtlCopyMemory( FirstPageDirent, NewDirent, BytesInFirstPage ); + + RtlCopyMemory( SecondPageDirent, + NewDirent + DirentsInFirstPage, + DirentsRequired*sizeof(DIRENT) - BytesInFirstPage ); + + ShortDirent = SecondPageDirent + + (DirentsRequired - DirentsInFirstPage) - 1; + } + + } _SEH2_FINALLY { + + // + // Remove the entry from the splay table, and then remove the + // full file name and exact case lfn. It is important that we + // always remove the name from the prefix table regardless of + // other errors. + // + + FatRemoveNames( IrpContext, Fcb ); + + if (Fcb->FullFileName.Buffer != NULL) { + + ExFreePool( Fcb->FullFileName.Buffer ); + Fcb->FullFileName.Buffer = NULL; + } + + if (Fcb->ExactCaseLongName.Buffer) { + + ExFreePool( Fcb->ExactCaseLongName.Buffer ); + Fcb->ExactCaseLongName.Buffer = NULL; + } + } _SEH2_END; + + // + // Now we need to update the location of the file's directory + // offset and move the fcb from its current parent dcb to + // the target dcb. + // + + Fcb->LfnOffsetWithinDirectory = NewOffset; + Fcb->DirentOffsetWithinDirectory = ShortDirentOffset; + + RemoveEntryList( &Fcb->ParentDcbLinks ); + + // + // There is a deep reason we put files on the tail, others on the head, + // which is to allow us to easily enumerate all child directories before + // child files. This is important to let us maintain whole-volume lockorder + // via BottomUp enumeration. + // + + if (NodeType(Fcb) == FAT_NTC_FCB) { + + InsertTailList( &TargetDcb->Specific.Dcb.ParentDcbQueue, + &Fcb->ParentDcbLinks ); + + } else { + + InsertHeadList( &TargetDcb->Specific.Dcb.ParentDcbQueue, + &Fcb->ParentDcbLinks ); + } + + OldParentDcb = Fcb->ParentDcb; + Fcb->ParentDcb = TargetDcb; + + // + // If we renamed across directories, some cleanup is now in order. + // + + if (RenamedAcrossDirectories) { + + // + // See if we need to uninitialize the cachemap for the source directory. + // Do this now in case we get unlucky and raise trying to finalize the + // operation. + // + + if (IsListEmpty(&OldParentDcb->Specific.Dcb.ParentDcbQueue) && + (OldParentDcb->OpenCount == 0) && + (OldParentDcb->Specific.Dcb.DirectoryFile != NULL)) { + + PFILE_OBJECT DirectoryFileObject; + + ASSERT( NodeType(OldParentDcb) == FAT_NTC_DCB ); + + DirectoryFileObject = OldParentDcb->Specific.Dcb.DirectoryFile; + + DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0); + + CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL ); + + OldParentDcb->Specific.Dcb.DirectoryFile = NULL; + + ObDereferenceObject( DirectoryFileObject ); + } + + // + // If we move a directory across directories, we have to change + // the cluster number in its .. entry + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + FatPrepareWriteDirectoryFile( IrpContext, + Fcb, + sizeof(DIRENT), + sizeof(DIRENT), + &DotDotBcb, +#ifndef __REACTOS__ + &DotDotDirent, +#else + (PVOID *)&DotDotDirent, +#endif + FALSE, + TRUE, + &Status ); + + ASSERT( NT_SUCCESS( Status ) ); + + DotDotDirent->FirstClusterOfFile = (USHORT) + ( NodeType(TargetDcb) == FAT_NTC_ROOT_DCB ? + 0 : TargetDcb->FirstClusterOfFile); + + if (FatIsFat32( Vcb )) { + + DotDotDirent->FirstClusterOfFileHi = (USHORT) + ( NodeType( TargetDcb ) == FAT_NTC_ROOT_DCB ? + 0 : (TargetDcb->FirstClusterOfFile >> 16)); + } + } + } + + // + // Now we need to setup the splay table and the name within + // the fcb. Free the old short name at this point. + // + + ExFreePool( Fcb->ShortName.Name.Oem.Buffer ); + Fcb->ShortName.Name.Oem.Buffer = NULL; + + FatConstructNamesInFcb( IrpContext, + Fcb, + ShortDirent, + CreateLfn ? &NewName : NULL ); + + FatSetFullNameInFcb( IrpContext, Fcb, &NewName ); + + // + // The rest of the actions taken are not related to correctness of + // the in-memory structures, so we shouldn't toast the Fcb if we + // raise from here to the end. + // + + InvalidateFcbOnRaise = FALSE; + + // + // If a file, set the file as modified so that the archive bit + // is set. We prevent this from adjusting the write time by + // indicating the user flag in the ccb. + // + + if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) { + + SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); + SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE ); + } + + // + // We have three cases to report. + // + // 1. If we overwrote an existing file, we report this as + // a modified file. + // + // 2. If we renamed to a new directory, then we added a file. + // + // 3. If we renamed in the same directory, then we report the + // the renamednewname. + // + + if (DeleteTarget) { + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_ATTRIBUTES + | FILE_NOTIFY_CHANGE_SIZE + | FILE_NOTIFY_CHANGE_LAST_WRITE + | FILE_NOTIFY_CHANGE_LAST_ACCESS + | FILE_NOTIFY_CHANGE_CREATION + | FILE_NOTIFY_CHANGE_EA, + FILE_ACTION_MODIFIED ); + + } else if (RenamedAcrossDirectories) { + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + ((NodeType( Fcb ) == FAT_NTC_FCB) + ? FILE_NOTIFY_CHANGE_FILE_NAME + : FILE_NOTIFY_CHANGE_DIR_NAME ), + FILE_ACTION_ADDED ); + + } else { + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + ((NodeType( Fcb ) == FAT_NTC_FCB) + ? FILE_NOTIFY_CHANGE_FILE_NAME + : FILE_NOTIFY_CHANGE_DIR_NAME ), + FILE_ACTION_RENAMED_NEW_NAME ); + } + + // + // We need to update the file name in the dirent. This value + // is never used elsewhere, so we don't concern ourselves + // with any error we may encounter. We let chkdsk fix the + // disk at some later time. + // + + if (!FatIsFat32(Vcb) && + ShortDirent->ExtendedAttributes != 0) { + + FatRenameEAs( IrpContext, + Fcb, + ShortDirent->ExtendedAttributes, + &OldOemName ); + } + + // + // Set our final status + // + + Status = STATUS_SUCCESS; + + } _SEH2_FINALLY { + + DebugUnwind( FatSetRenameInfo ); + + ExFreePool( UnicodeBuffer ); + + if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) { + + // + // Free pool if the buffer was grown on tunneling lookup + // + + ExFreePool(UniTunneledLongName.Buffer); + } + + FatUnpinBcb( IrpContext, OldDirentBcb ); + FatUnpinBcb( IrpContext, TargetDirentBcb ); + FatUnpinBcb( IrpContext, NewDirentBcb ); + FatUnpinBcb( IrpContext, SecondPageBcb ); + FatUnpinBcb( IrpContext, DotDotBcb ); + + + // + // If this was an abnormal termination, then we are in trouble. + // Should the operation have been in a sensitive state there is + // nothing we can do but invalidate the Fcb. + // + + if (_SEH2_AbnormalTermination() && InvalidateFcbOnRaise) { + + Fcb->FcbCondition = FcbBad; + } + + DebugTrace(-1, Dbg, "FatSetRenameInfo -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatSetPositionInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This routine performs the set position information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + FileObject - Supplies the file object being processed + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + PFILE_POSITION_INFORMATION Buffer; + + DebugTrace(+1, Dbg, "FatSetPositionInfo...\n", 0); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Check if the file does not use intermediate buffering. If it + // does not use intermediate buffering then the new position we're + // supplied must be aligned properly for the device + // + + if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) { + + PDEVICE_OBJECT DeviceObject; + + DeviceObject = IoGetCurrentIrpStackLocation( Irp )->DeviceObject; + + if ((Buffer->CurrentByteOffset.LowPart & DeviceObject->AlignmentRequirement) != 0) { + + DebugTrace(0, Dbg, "Cannot set position due to aligment conflict\n", 0); + DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_INVALID_PARAMETER); + + return STATUS_INVALID_PARAMETER; + } + } + + // + // The input parameter is fine so set the current byte offset and + // complete the request + // + + DebugTrace(0, Dbg, "Set the new position to %08lx\n", Buffer->CurrentByteOffset); + + FileObject->CurrentByteOffset = Buffer->CurrentByteOffset; + + DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_SUCCESS); + + UNREFERENCED_PARAMETER( IrpContext ); + + return STATUS_SUCCESS; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatSetAllocationInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb, + IN PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This routine performs the set Allocation information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + + FileObject - Supplies the FileObject being processed, already known not to + be the root dcb + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PFILE_ALLOCATION_INFORMATION Buffer; + ULONG NewAllocationSize; + + BOOLEAN FileSizeTruncated = FALSE; + BOOLEAN CacheMapInitialized = FALSE; + BOOLEAN ResourceAcquired = FALSE; + ULONG OriginalFileSize; + ULONG OriginalValidDataLength; + ULONG OriginalValidDataToDisk; + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + NewAllocationSize = Buffer->AllocationSize.LowPart; + + DebugTrace(+1, Dbg, "FatSetAllocationInfo.. to %08lx\n", NewAllocationSize); + + // + // Allocation is only allowed on a file and not a directory + // + + if (NodeType(Fcb) == FAT_NTC_DCB) { + + DebugTrace(-1, Dbg, "Cannot change allocation of a directory\n", 0); + + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Check that the new file allocation is legal + // + + if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->AllocationSize, 0 )) { + + DebugTrace(-1, Dbg, "Illegal allocation size\n", 0); + + return STATUS_DISK_FULL; + } + + // + // If we haven't yet looked up the correct AllocationSize, do so. + // + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, Fcb ); + } + + // + // This is kinda gross, but if the file is not cached, but there is + // a data section, we have to cache the file to avoid a bunch of + // extra work. + // + + if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) && + (FileObject->SectionObjectPointer->SharedCacheMap == NULL) && + !FlagOn(Irp->Flags, IRP_PAGING_IO)) { + + ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) ); + + // + // Now initialize the cache map. + // + + CcInitializeCacheMap( FileObject, + (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, + FALSE, + &FatData.CacheManagerCallbacks, + Fcb ); + + CacheMapInitialized = TRUE; + } + + // + // Now mark the fact that the file needs to be truncated on close + // + + Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE; + + // + // Now mark that the time on the dirent needs to be updated on close. + // + + SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); + + _SEH2_TRY { + + // + // Increase or decrease the allocation size. + // + + if (NewAllocationSize > Fcb->Header.AllocationSize.LowPart) { + + FatAddFileAllocation( IrpContext, Fcb, FileObject, NewAllocationSize); + + } else { + + // + // Check here if we will be decreasing file size and synchonize with + // paging IO. + // + + if ( Fcb->Header.FileSize.LowPart > NewAllocationSize ) { + + // + // Before we actually truncate, check to see if the purge + // is going to fail. + // + + if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, + &Buffer->AllocationSize )) { + + try_return( Status = STATUS_USER_MAPPED_FILE ); + } + + FileSizeTruncated = TRUE; + + OriginalFileSize = Fcb->Header.FileSize.LowPart; + OriginalValidDataLength = Fcb->Header.ValidDataLength.LowPart; + OriginalValidDataToDisk = Fcb->ValidDataToDisk; + + (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); + ResourceAcquired = TRUE; + + Fcb->Header.FileSize.LowPart = NewAllocationSize; + + // + // If we reduced the file size to less than the ValidDataLength, + // adjust the VDL. Likewise ValidDataToDisk. + // + + if (Fcb->Header.ValidDataLength.LowPart > Fcb->Header.FileSize.LowPart) { + + Fcb->Header.ValidDataLength.LowPart = Fcb->Header.FileSize.LowPart; + } + if (Fcb->ValidDataToDisk > Fcb->Header.FileSize.LowPart) { + + Fcb->ValidDataToDisk = Fcb->Header.FileSize.LowPart; + } + + } + + // + // Now that File Size is down, actually do the truncate. + // + + FatTruncateFileAllocation( IrpContext, Fcb, NewAllocationSize); + + // + // Now check if we needed to decrease the file size accordingly. + // + + if ( FileSizeTruncated ) { + + // + // Tell the cache manager we reduced the file size. + // The call is unconditional, because MM always wants to know. + // + +#if DBG + _SEH2_TRY { +#endif + + CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); + +#if DBG + } _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) { + + NOTHING; + } _SEH2_END; +#endif + + ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess ); + + // + // There is no going back from this. If we run into problems updating + // the dirent we will have to live with the consequences. Not sending + // the notifies is likewise pretty benign compared to failing the entire + // operation and trying to back out everything, which could fail for the + // same reasons. + // + // If you want a transacted filesystem, use NTFS ... + // + + FileSizeTruncated = FALSE; + + FatSetFileSizeInDirent( IrpContext, Fcb, NULL ); + + // + // Report that we just reduced the file size. + // + + FatNotifyReportChange( IrpContext, + Fcb->Vcb, + Fcb, + FILE_NOTIFY_CHANGE_SIZE, + FILE_ACTION_MODIFIED ); + } + } + + try_exit: NOTHING; + + } _SEH2_FINALLY { + + if ( _SEH2_AbnormalTermination() && FileSizeTruncated ) { + + Fcb->Header.FileSize.LowPart = OriginalFileSize; + Fcb->Header.ValidDataLength.LowPart = OriginalValidDataLength; + Fcb->ValidDataToDisk = OriginalValidDataToDisk; + + // + // Make sure Cc knows the right filesize. + // + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { + + *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; + } + + ASSERT( Fcb->Header.FileSize.LowPart <= Fcb->Header.AllocationSize.LowPart ); + } + + if (CacheMapInitialized) { + + CcUninitializeCacheMap( FileObject, NULL, NULL ); + } + + if (ResourceAcquired) { + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + + } + + } _SEH2_END; + + DebugTrace(-1, Dbg, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS); + + return Status; +} + + +// +// Internal Support Routine +// + +NTSTATUS +FatSetEndOfFileInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFILE_OBJECT FileObject, + IN PVCB Vcb, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine performs the set End of File information for fat. It either + completes the request or enqueues it off to the fsp. + +Arguments: + + Irp - Supplies the irp being processed + + FileObject - Supplies the file object being processed + + Vcb - Supplies the Vcb being processed + + Fcb - Supplies the Fcb or Dcb being processed, already known not to + be the root dcb + +Return Value: + + NTSTATUS - The result of this operation if it completes without + an exception. + +--*/ + +{ + NTSTATUS Status; + + PFILE_END_OF_FILE_INFORMATION Buffer; + + ULONG NewFileSize; + ULONG InitialFileSize; + ULONG InitialValidDataLength; + ULONG InitialValidDataToDisk; + + BOOLEAN CacheMapInitialized = FALSE; + BOOLEAN UnwindFileSizes = FALSE; + BOOLEAN ResourceAcquired = FALSE; + + DebugTrace(+1, Dbg, "FatSetEndOfFileInfo...\n", 0); + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + _SEH2_TRY { + + // + // File Size changes are only allowed on a file and not a directory + // + + if (NodeType(Fcb) != FAT_NTC_FCB) { + + DebugTrace(0, Dbg, "Cannot change size of a directory\n", 0); + + try_return( Status = STATUS_INVALID_DEVICE_REQUEST ); + } + + // + // Check that the new file size is legal + // + + if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->EndOfFile, 0 )) { + + DebugTrace(0, Dbg, "Illegal allocation size\n", 0); + + try_return( Status = STATUS_DISK_FULL ); + } + + NewFileSize = Buffer->EndOfFile.LowPart; + + // + // If we haven't yet looked up the correct AllocationSize, do so. + // + + if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, Fcb ); + } + + // + // This is kinda gross, but if the file is not cached, but there is + // a data section, we have to cache the file to avoid a bunch of + // extra work. + // + + if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) && + (FileObject->SectionObjectPointer->SharedCacheMap == NULL) && + !FlagOn(Irp->Flags, IRP_PAGING_IO)) { + + if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE )) { + + // + // This IRP has raced (and lost) with a close (=>cleanup) + // on the same fileobject. We don't want to reinitialise the + // cachemap here now because we'll leak it (unless we do so & + // then tear it down again here, which is too much of a change at + // this stage). So we'll just say the file is closed - which + // is arguably the right thing to do anyway, since a caller + // racing operations in this way is broken. The only stumbling + // block is possibly filters - do they operate on cleaned + // up fileobjects? + // + + FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED); + } + + // + // Now initialize the cache map. + // + + CcInitializeCacheMap( FileObject, + (PCC_FILE_SIZES)&Fcb->Header.AllocationSize, + FALSE, + &FatData.CacheManagerCallbacks, + Fcb ); + + CacheMapInitialized = TRUE; + } + + // + // Do a special case here for the lazy write of file sizes. + // + + if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.AdvanceOnly) { + + // + // Only attempt this if the file hasn't been "deleted on close" and + // this is a good FCB. + // + + if (!IsFileDeleted( IrpContext, Fcb ) && (Fcb->FcbCondition == FcbGood)) { + + PDIRENT Dirent; + PBCB DirentBcb; + + // + // Never have the dirent filesize larger than the fcb filesize + // + + if (NewFileSize >= Fcb->Header.FileSize.LowPart) { + + NewFileSize = Fcb->Header.FileSize.LowPart; + } + + // + // Make sure we don't set anything higher than the alloc size. + // + + ASSERT( NewFileSize <= Fcb->Header.AllocationSize.LowPart ); + + // + // Only advance the file size, never reduce it with this call + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &DirentBcb ); + + ASSERT( Dirent && DirentBcb ); + + _SEH2_TRY { + + if ( NewFileSize > Dirent->FileSize ) { + + Dirent->FileSize = NewFileSize; + + FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE ); + + // + // Report that we just changed the file size. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_SIZE, + FILE_ACTION_MODIFIED ); + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, DirentBcb ); + } _SEH2_END; + + } else { + + DebugTrace(0, Dbg, "Cannot set size on deleted file.\n", 0); + } + + try_return( Status = STATUS_SUCCESS ); + } + + // + // Check if the new file size is greater than the current + // allocation size. If it is then we need to increase the + // allocation size. + // + + if ( NewFileSize > Fcb->Header.AllocationSize.LowPart ) { + + // + // Change the file allocation + // + + FatAddFileAllocation( IrpContext, Fcb, FileObject, NewFileSize ); + } + + // + // At this point we have enough allocation for the file. + // So check if we are really changing the file size + // + + if (Fcb->Header.FileSize.LowPart != NewFileSize) { + + if ( NewFileSize < Fcb->Header.FileSize.LowPart ) { + + // + // Before we actually truncate, check to see if the purge + // is going to fail. + // + + if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer, + &Buffer->EndOfFile )) { + + try_return( Status = STATUS_USER_MAPPED_FILE ); + } + + // + // This call is unconditional, because MM always wants to know. + // Also serialize here with paging io since we are truncating + // the file size. + // + + ResourceAcquired = + ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE ); + } + + // + // Set the new file size + // + + InitialFileSize = Fcb->Header.FileSize.LowPart; + InitialValidDataLength = Fcb->Header.ValidDataLength.LowPart; + InitialValidDataToDisk = Fcb->ValidDataToDisk; + UnwindFileSizes = TRUE; + + Fcb->Header.FileSize.LowPart = NewFileSize; + + // + // If we reduced the file size to less than the ValidDataLength, + // adjust the VDL. Likewise ValidDataToDisk. + // + + if (Fcb->Header.ValidDataLength.LowPart > NewFileSize) { + + Fcb->Header.ValidDataLength.LowPart = NewFileSize; + } + + if (Fcb->ValidDataToDisk > NewFileSize) { + + Fcb->ValidDataToDisk = NewFileSize; + } + + DebugTrace(0, Dbg, "New file size is 0x%08lx.\n", NewFileSize); + + // + // We must now update the cache mapping (benign if not cached). + // + + CcSetFileSizes( FileObject, + (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); + + FatSetFileSizeInDirent( IrpContext, Fcb, NULL ); + + // + // Report that we just changed the file size. + // + + FatNotifyReportChange( IrpContext, + Vcb, + Fcb, + FILE_NOTIFY_CHANGE_SIZE, + FILE_ACTION_MODIFIED ); + + // + // Mark the fact that the file will need to checked for + // truncation on cleanup. + // + + SetFlag( Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE ); + } + + // + // Set this handle as having modified the file + // + + FileObject->Flags |= FO_FILE_MODIFIED; + + // + // Set our return status to success + // + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + FatUnpinRepinnedBcbs( IrpContext ); + + } _SEH2_FINALLY { + + DebugUnwind( FatSetEndOfFileInfo ); + + if (_SEH2_AbnormalTermination() && UnwindFileSizes) { + + Fcb->Header.FileSize.LowPart = InitialFileSize; + Fcb->Header.ValidDataLength.LowPart = InitialValidDataLength; + Fcb->ValidDataToDisk = InitialValidDataToDisk; + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { + + *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize; + } + } + + if (CacheMapInitialized) { + + CcUninitializeCacheMap( FileObject, NULL, NULL ); + } + + if ( ResourceAcquired ) { + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + } + + DebugTrace(-1, Dbg, "FatSetEndOfFileInfo -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + + +// +// Internal Support Routine +// + +VOID +FatDeleteFile ( + IN PIRP_CONTEXT IrpContext, + IN PDCB TargetDcb, + IN ULONG LfnOffset, + IN ULONG DirentOffset, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn + ) +{ + PFCB Fcb; + PLIST_ENTRY Links; + + // + // We can do the replace by removing the other Fcb(s) from + // the prefix table. + // + + for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + if (FlagOn(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE) && + (Fcb->DirentOffsetWithinDirectory == DirentOffset)) { + + ASSERT( NodeType(Fcb) == FAT_NTC_FCB ); + ASSERT( Fcb->LfnOffsetWithinDirectory == LfnOffset ); + + if ( Fcb->UncleanCount != 0 ) { + + FatBugCheck(0,0,0); + + } else { + + PERESOURCE Resource; + + // + // Make this fcb "appear" deleted, synchronizing with + // paging IO. + // + + FatRemoveNames( IrpContext, Fcb ); + + Resource = Fcb->Header.PagingIoResource; + + (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE ); + + SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE); + + Fcb->ValidDataToDisk = 0; + Fcb->Header.FileSize.QuadPart = + Fcb->Header.ValidDataLength.QuadPart = 0; + + Fcb->FirstClusterOfFile = 0; + + ExReleaseResourceLite( Resource ); + } + } + } + + // + // The file is not currently opened so we can delete the file + // that is being overwritten. To do the operation we dummy + // up an fcb, truncate allocation, delete the fcb, and delete + // the dirent. + // + + Fcb = FatCreateFcb( IrpContext, + TargetDcb->Vcb, + TargetDcb, + LfnOffset, + DirentOffset, + Dirent, + Lfn, + FALSE, + FALSE ); + + Fcb->Header.FileSize.LowPart = 0; + + _SEH2_TRY { + + FatTruncateFileAllocation( IrpContext, Fcb, 0 ); + + FatDeleteDirent( IrpContext, Fcb, NULL, TRUE ); + + } _SEH2_FINALLY { + + FatDeleteFcb( IrpContext, Fcb ); + } _SEH2_END; +} + +// +// Internal Support Routine +// + +VOID +FatRenameEAs ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN USHORT ExtendedAttributes, + IN POEM_STRING OldOemName + ) +{ + BOOLEAN LockedEaFcb = FALSE; + + PBCB EaBcb = NULL; + PDIRENT EaDirent; + EA_RANGE EaSetRange; + PEA_SET_HEADER EaSetHeader; + + PVCB Vcb; + + RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE )); + + Vcb = Fcb->Vcb; + + _SEH2_TRY { + + // + // Use a try-except to catch any errors. + // + + _SEH2_TRY { + + + // + // Try to get the Ea file object. Return FALSE on failure. + // + + FatGetEaFile( IrpContext, + Vcb, + &EaDirent, + &EaBcb, + FALSE, + FALSE ); + + LockedEaFcb = TRUE; + + // + // If we didn't get the file because it doesn't exist, then the + // disk is corrupted. We do nothing here. + // + + if (Vcb->VirtualEaFile != NULL) { + + // + // Try to pin down the Ea set header for the index in the + // dirent. If the operation doesn't complete, return FALSE + // from this routine. + // + + FatReadEaSet( IrpContext, + Vcb, + ExtendedAttributes, + OldOemName, + FALSE, + &EaSetRange ); + + EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data; + + // + // We now have the Ea set header for this file. We simply + // overwrite the owning file name. + // + + RtlZeroMemory( EaSetHeader->OwnerFileName, 14 ); + + RtlCopyMemory( EaSetHeader->OwnerFileName, + Fcb->ShortName.Name.Oem.Buffer, + Fcb->ShortName.Name.Oem.Length ); + + FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL ); + } + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We catch all exceptions that Fat catches, but don't do + // anything with them. + // + } _SEH2_END; + + } _SEH2_FINALLY { + + // + // Unpin the EaDirent and the EaSetHeader if pinned. + // + + FatUnpinBcb( IrpContext, EaBcb ); + FatUnpinEaRange( IrpContext, &EaSetRange ); + + // + // Release the Fcb for the Ea file if locked. + // + + if (LockedEaFcb) { + + FatReleaseFcb( IrpContext, Vcb->EaFcb ); + } + } _SEH2_END; + + return; +} + diff --git a/drivers/filesystems/fastfat_new/filobsup.c b/drivers/filesystems/fastfat_new/filobsup.c new file mode 100644 index 00000000000..3aa6a5df118 --- /dev/null +++ b/drivers/filesystems/fastfat_new/filobsup.c @@ -0,0 +1,547 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FilObSup.c + +Abstract: + + This module implements the Fat File object support routines. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FILOBSUP) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_FILOBSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatForceCacheMiss) +#pragma alloc_text(PAGE, FatPurgeReferencedFileObjects) +#pragma alloc_text(PAGE, FatSetFileObject) +#pragma alloc_text(PAGE, FatDecodeFileObject) +#endif + + +VOID +FatSetFileObject ( + IN PFILE_OBJECT FileObject OPTIONAL, + IN TYPE_OF_OPEN TypeOfOpen, + IN PVOID VcbOrFcbOrDcb, + IN PCCB Ccb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine sets the file system pointers within the file object + +Arguments: + + FileObject - Supplies a pointer to the file object being modified, and + can optionally be null. + + TypeOfOpen - Supplies the type of open denoted by the file object. + This is only used by this procedure for sanity checking. + + VcbOrFcbOrDcb - Supplies a pointer to either a vcb, fcb, or dcb + + Ccb - Optionally supplies a pointer to a ccb + +Return Value: + + None. + +--*/ + +{ + DebugTrace(+1, Dbg, "FatSetFileObject, FileObject = %08lx\n", FileObject ); + + ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); + + ASSERT(((TypeOfOpen == UnopenedFileObject)) + + || + + ((TypeOfOpen == UserFileOpen) && + (NodeType(VcbOrFcbOrDcb) == FAT_NTC_FCB) && + (Ccb != NULL)) + + || + + ((TypeOfOpen == EaFile) && + (NodeType(VcbOrFcbOrDcb) == FAT_NTC_FCB) && + (Ccb == NULL)) + + || + + ((TypeOfOpen == UserDirectoryOpen) && + ((NodeType(VcbOrFcbOrDcb) == FAT_NTC_DCB) || (NodeType(VcbOrFcbOrDcb) == FAT_NTC_ROOT_DCB)) && + (Ccb != NULL)) + + || + + ((TypeOfOpen == UserVolumeOpen) && + (NodeType(VcbOrFcbOrDcb) == FAT_NTC_VCB) && + (Ccb != NULL)) + + || + + ((TypeOfOpen == VirtualVolumeFile) && + (NodeType(VcbOrFcbOrDcb) == FAT_NTC_VCB) && + (Ccb == NULL)) + + || + + ((TypeOfOpen == DirectoryFile) && + ((NodeType(VcbOrFcbOrDcb) == FAT_NTC_DCB) || (NodeType(VcbOrFcbOrDcb) == FAT_NTC_ROOT_DCB)) && + (Ccb == NULL))); + + // + // If we were given an Fcb, Dcb, or Vcb, we have some processing to do. + // + + ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); + + if ( VcbOrFcbOrDcb != NULL ) { + + // + // Set the Vpb field in the file object, and if we were given an + // Fcb or Dcb move the field over to point to the nonpaged Fcb/Dcb + // + + if (NodeType(VcbOrFcbOrDcb) == FAT_NTC_VCB) { + + FileObject->Vpb = ((PVCB)VcbOrFcbOrDcb)->Vpb; + + } else { + + FileObject->Vpb = ((PFCB)VcbOrFcbOrDcb)->Vcb->Vpb; + + // + // If this is a temporary file, note it in the FcbState + // + + if (FlagOn(((PFCB)VcbOrFcbOrDcb)->FcbState, FCB_STATE_TEMPORARY)) { + + SetFlag(FileObject->Flags, FO_TEMPORARY_FILE); + } + } + } + + ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); + + // + // Now set the fscontext fields of the file object + // + + if (ARGUMENT_PRESENT( FileObject )) { + + FileObject->FsContext = VcbOrFcbOrDcb; + FileObject->FsContext2 = Ccb; + } + + ASSERT((Ccb == NULL) || (NodeType(Ccb) == FAT_NTC_CCB)); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatSetFileObject -> VOID\n", 0); + + return; +} + + +TYPE_OF_OPEN +FatDecodeFileObject ( + IN PFILE_OBJECT FileObject, + OUT PVCB *Vcb, + OUT PFCB *FcbOrDcb, + OUT PCCB *Ccb + ) + +/*++ + +Routine Description: + + This procedure takes a pointer to a file object, that has already been + opened by the Fat file system and figures out what really is opened. + +Arguments: + + FileObject - Supplies the file object pointer being interrogated + + Vcb - Receives a pointer to the Vcb for the file object. + + FcbOrDcb - Receives a pointer to the Fcb/Dcb for the file object, if + one exists. + + Ccb - Receives a pointer to the Ccb for the file object, if one exists. + +Return Value: + + TYPE_OF_OPEN - returns the type of file denoted by the input file object. + + UserFileOpen - The FO represents a user's opened data file. + Ccb, FcbOrDcb, and Vcb are set. FcbOrDcb points to an Fcb. + + UserDirectoryOpen - The FO represents a user's opened directory. + Ccb, FcbOrDcb, and Vcb are set. FcbOrDcb points to a Dcb/RootDcb + + UserVolumeOpen - The FO represents a user's opened volume. + Ccb and Vcb are set. FcbOrDcb is null. + + VirtualVolumeFile - The FO represents the special virtual volume file. + Vcb is set, and Ccb and FcbOrDcb are null. + + DirectoryFile - The FO represents a special directory file. + Vcb and FcbOrDcb are set. Ccb is null. FcbOrDcb points to a + Dcb/RootDcb. + + EaFile - The FO represents an Ea Io stream file. + FcbOrDcb, and Vcb are set. FcbOrDcb points to an Fcb, and Ccb is + null. + +--*/ + +{ + TYPE_OF_OPEN TypeOfOpen; + PVOID FsContext; + PVOID FsContext2; + + DebugTrace(+1, Dbg, "FatDecodeFileObject, FileObject = %08lx\n", FileObject); + + // + // Reference the fs context fields of the file object, and zero out + // the out pointer parameters. + // + + FsContext = FileObject->FsContext; + FsContext2 = FileObject->FsContext2; + + // + // Special case the situation where FsContext is null + // + + if (FsContext == NULL) { + + *Ccb = NULL; + *FcbOrDcb = NULL; + *Vcb = NULL; + + TypeOfOpen = UnopenedFileObject; + + } else { + + // + // Now we can case on the node type code of the fscontext pointer + // and set the appropriate out pointers + // + + switch (NodeType(FsContext)) { + + case FAT_NTC_VCB: + + *Ccb = FsContext2; + *FcbOrDcb = NULL; + *Vcb = FsContext; + + TypeOfOpen = ( *Ccb == NULL ? VirtualVolumeFile : UserVolumeOpen ); + + break; + + case FAT_NTC_ROOT_DCB: + case FAT_NTC_DCB: + + *Ccb = FsContext2; + *FcbOrDcb = FsContext; + *Vcb = (*FcbOrDcb)->Vcb; + + TypeOfOpen = ( *Ccb == NULL ? DirectoryFile : UserDirectoryOpen ); + + DebugTrace(0, Dbg, "Referencing directory: %Z\n", &(*FcbOrDcb)->FullFileName); + + break; + + case FAT_NTC_FCB: + + *Ccb = FsContext2; + *FcbOrDcb = FsContext; + *Vcb = (*FcbOrDcb)->Vcb; + + TypeOfOpen = ( *Ccb == NULL ? EaFile : UserFileOpen ); + + DebugTrace(0, Dbg, "Referencing file: %Z\n", &(*FcbOrDcb)->FullFileName); + + break; + + default: + + FatBugCheck( NodeType(FsContext), 0, 0 ); + } + } + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "FatDecodeFileObject -> %08lx\n", TypeOfOpen); + + return TypeOfOpen; +} + +VOID +FatPurgeReferencedFileObjects ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + This routine non-recursively walks from the given FcbOrDcb and trys + to force Cc or Mm to close any sections it may be holding on to. + +Arguments: + + Fcb - Supplies a pointer to either an fcb or a dcb + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + None. + +--*/ + +{ + PFCB OriginalFcb = Fcb; + PFCB NextFcb; + + DebugTrace(+1, Dbg, "FatPurgeReferencedFileObjects, Fcb = %08lx\n", Fcb ); + + ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // First, if we have a delayed close, force it closed. + // + + FatFspClose(Fcb->Vcb); + + // + // Walk the directory tree forcing sections closed. + // + // Note that it very important to get the next node to visit before + // acting on the current node. This is because acting on a node may + // make it, and an arbitrary number of direct ancestors, vanish. + // Since we never visit ancestors in our top-down enumeration scheme, we + // can safely continue the enumeration even when the tree is vanishing + // beneath us. This is way cool. + // + + while ( Fcb != NULL ) { + + NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, OriginalFcb); + + // + // Check for the EA file fcb + // + + if ( !FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_VOLUME_ID) ) { + + FatForceCacheMiss( IrpContext, Fcb, FlushType ); + } + + Fcb = NextFcb; + } + + DebugTrace(-1, Dbg, "FatPurgeReferencedFileObjects (VOID)\n", 0 ); + + return; +} + + +VOID +FatForceCacheMiss ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + The following routine asks either Cc or Mm to get rid of any cached + pages on a file. Note that this will fail if a user has mapped a file. + + If there is a shared cache map, purge the cache section. Otherwise + we have to go and ask Mm to blow away the section. + + NOTE: This caller MUST own the Vcb exclusive. + +Arguments: + + Fcb - Supplies a pointer to an fcb + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + None. + +--*/ + +{ + PVCB Vcb; + BOOLEAN ChildrenAcquired = FALSE; + + // + // If we can't wait, bail. + // + + ASSERT( FatVcbAcquiredExclusive( IrpContext, Fcb->Vcb ) || + FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) ); + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); + } + + // + // If we are purging a directory file object, we must acquire all the + // FCBs exclusive so that the parent directory is not being pinned. + // Careful, we can collide with something acquiring up the tree like + // an unpin repinned flush (FsRtlAcquireFileForCcFlush ...) of a parent + // dir on extending writethrough of a child file (oops). So get things + // going up the tree, not down. + // + + if ((NodeType(Fcb) != FAT_NTC_FCB) && + !IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue)) { + + PLIST_ENTRY Links; + PFCB TempFcb; + + ChildrenAcquired = TRUE; + + for (Links = Fcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Fcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + (VOID)FatAcquireExclusiveFcb( IrpContext, TempFcb ); + } + } + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + + // + // We use this flag to indicate to a close beneath us that + // the Fcb resource should be freed before deleting the Fcb. + // + + Vcb = Fcb->Vcb; + + SetFlag( Fcb->FcbState, FCB_STATE_FORCE_MISS_IN_PROGRESS ); + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); + + _SEH2_TRY { + + BOOLEAN DataSectionExists; + BOOLEAN ImageSectionExists; + + PSECTION_OBJECT_POINTERS Section; + + if ( FlushType ) { + + (VOID)FatFlushFile( IrpContext, Fcb, FlushType ); + } + + // + // The Flush may have made the Fcb go away + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB)) { + + Section = &Fcb->NonPaged->SectionObjectPointers; + + DataSectionExists = (BOOLEAN)(Section->DataSectionObject != NULL); + ImageSectionExists = (BOOLEAN)(Section->ImageSectionObject != NULL); + + // + // Note, it is critical to do the Image section first as the + // purge of the data section may cause the image section to go + // away, but the opposite is not true. + // + + if (ImageSectionExists) { + + (VOID)MmFlushImageSection( Section, MmFlushForWrite ); + } + + if (DataSectionExists) { + + CcPurgeCacheSection( Section, NULL, 0, FALSE ); + } + } + + } _SEH2_FINALLY { + + // + // If we purging a directory file object, release all the Fcb + // resources that we acquired above. The Dcb cannot have vanished + // if there were Fcbs underneath it, and the Fcbs couldn't have gone + // away since I own the Vcb. + // + + if (ChildrenAcquired) { + + PLIST_ENTRY Links; + PFCB TempFcb; + + for (Links = Fcb->Specific.Dcb.ParentDcbQueue.Flink; + Links != &Fcb->Specific.Dcb.ParentDcbQueue; + Links = Links->Flink) { + + TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks ); + + FatReleaseFcb( IrpContext, TempFcb ); + } + } + + // + // Since we have the Vcb exclusive we know that if any closes + // come in it is because the CcPurgeCacheSection caused the + // Fcb to go away. Also in close, the Fcb was released + // before being freed. + // + + if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB) ) { + + ClearFlag( Fcb->FcbState, FCB_STATE_FORCE_MISS_IN_PROGRESS ); + + FatReleaseFcb( (IRPCONTEXT), Fcb ); + } + } _SEH2_END; +} + + diff --git a/drivers/filesystems/fastfat_new/flush.c b/drivers/filesystems/fastfat_new/flush.c new file mode 100644 index 00000000000..267fc78ad75 --- /dev/null +++ b/drivers/filesystems/fastfat_new/flush.c @@ -0,0 +1,1261 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Flush.c + +Abstract: + + This module implements the File Flush buffers routine for Fat called by the + dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FLUSH) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_FLUSH) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonFlushBuffers) +#pragma alloc_text(PAGE, FatFlushDirectory) +#pragma alloc_text(PAGE, FatFlushFat) +#pragma alloc_text(PAGE, FatFlushFile) +#pragma alloc_text(PAGE, FatFlushVolume) +#pragma alloc_text(PAGE, FatFsdFlushBuffers) +#pragma alloc_text(PAGE, FatFlushDirentForFile) +#pragma alloc_text(PAGE, FatFlushFatEntries) +#pragma alloc_text(PAGE, FatHijackIrpAndFlushDevice) +#endif + +// +// Local procedure prototypes +// + +NTSTATUS +NTAPI +FatFlushCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +NTSTATUS +NTAPI +FatHijackCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + + +NTSTATUS +NTAPI +FatFsdFlushBuffers ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of Flush buffers. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file being flushed exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "FatFsdFlushBuffers\n", 0); + + // + // Call the common Cleanup routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonFlushBuffers( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdFlushBuffers -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonFlushBuffers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for flushing a buffer. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN VcbAcquired = FALSE; + BOOLEAN FcbAcquired = FALSE; + +#ifndef __REACTOS__ + PDIRENT Dirent; +#endif + PBCB DirentBcb = NULL; + + PAGED_CODE(); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonFlushBuffers\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject); + + // + // Extract and decode the file object + // + + FileObject = IrpSp->FileObject; + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + + // + // CcFlushCache is always synchronous, so if we can't wait enqueue + // the irp to the Fsp. + // + + if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status ); + return Status; + } + + Status = STATUS_SUCCESS; + + _SEH2_TRY { + + // + // Case on the type of open that we are trying to flush + // + + switch (TypeOfOpen) { + + case VirtualVolumeFile: + case EaFile: + case DirectoryFile: + + DebugTrace(0, Dbg, "Flush that does nothing\n", 0); + break; + + case UserFileOpen: + + DebugTrace(0, Dbg, "Flush User File Open\n", 0); + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + + FcbAcquired = TRUE; + + FatVerifyFcb( IrpContext, Fcb ); + + // + // If the file is cached then flush its cache + // + + Status = FatFlushFile( IrpContext, Fcb, Flush ); + + // + // Also update and flush the file's dirent in the parent directory if the + // file flush worked. + // + + if (NT_SUCCESS( Status )) { + + // + // Insure that we get the filesize to disk correctly. This is + // benign if it was already good. + // + // (why do we need to do this?) + // + + SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED); + + FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb ); + + // + // Flush the volume file to get any allocation information + // updates to disk. + // + + if (FlagOn(Fcb->FcbState, FCB_STATE_FLUSH_FAT)) { + + Status = FatFlushFat( IrpContext, Vcb ); + + ClearFlag(Fcb->FcbState, FCB_STATE_FLUSH_FAT); + } + + // + // Set the write through bit so that these modifications + // will be completed with the request. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); + } + + break; + + case UserDirectoryOpen: + + // + // If the user had opened the root directory then we'll + // oblige by flushing the volume. + // + + if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) { + + DebugTrace(0, Dbg, "Flush a directory does nothing\n", 0); + break; + } + + case UserVolumeOpen: + + DebugTrace(0, Dbg, "Flush User Volume Open, or root dcb\n", 0); + + // + // Acquire exclusive access to the Vcb. + // + + { + BOOLEAN Finished; + Finished = FatAcquireExclusiveVcb( IrpContext, Vcb ); + ASSERT( Finished ); + } + + VcbAcquired = TRUE; + + // + // Mark the volume clean and then flush the volume file, + // and then all directories + // + + Status = FatFlushVolume( IrpContext, Vcb, Flush ); + + // + // If the volume was dirty, do the processing that the delayed + // callback would have done. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) { + + // + // Cancel any pending clean volumes. + // + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + // + // The volume is now clean, note it. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + } + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + } + + break; + + default: + + FatBugCheck( TypeOfOpen, 0, 0 ); + } + + FatUnpinBcb( IrpContext, DirentBcb ); + + FatUnpinRepinnedBcbs( IrpContext ); + + } _SEH2_FINALLY { + + DebugUnwind( FatCommonFlushBuffers ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); } + + if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); } + + // + // If this is a normal termination then pass the request on + // to the target device object. + // + + if (!_SEH2_AbnormalTermination()) { + + NTSTATUS DriverStatus; + PIO_STACK_LOCATION NextIrpSp; + + // + // Get the next stack location, and copy over the stack location + // + + NextIrpSp = IoGetNextIrpStackLocation( Irp ); + + *NextIrpSp = *IrpSp; + + // + // Set up the completion routine + // + + IoSetCompletionRoutine( Irp, + FatFlushCompletionRoutine, + ULongToPtr( Status ), + TRUE, + TRUE, + TRUE ); + + // + // Send the request. + // + + DriverStatus = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + Status = (DriverStatus == STATUS_INVALID_DEVICE_REQUEST) ? + Status : DriverStatus; + + // + // Free the IrpContext and return to the caller. + // + + FatCompleteRequest( IrpContext, FatNull, STATUS_SUCCESS ); + } + + DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + + +NTSTATUS +FatFlushDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + This routine non-recursively flushes a dcb tree. + +Arguments: + + Dcb - Supplies the Dcb being flushed + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + VOID + +--*/ + +{ + PFCB Fcb; + PVCB Vcb; + PFCB NextFcb; + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + + NTSTATUS Status; + NTSTATUS ReturnStatus = STATUS_SUCCESS; + + BOOLEAN ClearWriteThroughOnExit = FALSE; + BOOLEAN ClearWaitOnExit = FALSE; + + PAGED_CODE(); + + ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) ); + + DebugTrace(+1, Dbg, "FatFlushDirectory, Dcb = %08lx\n", Dcb); + + // + // First flush all the files, then the directories, to make sure all the + // file sizes and times get sets correctly on disk. + // + // We also have to check here if the "Ea Data. Sf" fcb really + // corressponds to an existing file. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) { + + ClearWriteThroughOnExit = TRUE; + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); + } + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + ClearWaitOnExit = TRUE; + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + Vcb = Dcb->Vcb; + Fcb = Dcb; + + while (Fcb != NULL) { + + NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb); + + if ( (NodeType( Fcb ) == FAT_NTC_FCB) && + (Vcb->EaFcb != Fcb) && + !IsFileDeleted(IrpContext, Fcb)) { + + (VOID)FatAcquireExclusiveFcb( IrpContext, Fcb ); + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB ); + + // + // Exception handler to catch and commute errors encountered + // doing the flush dance. We may encounter corruption, and + // should continue flushing the volume as much as possible. + // + + _SEH2_TRY { + + // + // Standard handler to release resources, etc. + // + + _SEH2_TRY { + + // + // Make sure the Fcb is OK. + // + + _SEH2_TRY { + + FatVerifyFcb( IrpContext, Fcb ); + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + // + // If this Fcb is not good skip it. Note that a 'continue' + // here would be very expensive as we inside a try{} body. + // + + if (Fcb->FcbCondition != FcbGood) { + + goto NextFcb; + } + + // + // In case a handle was never closed and the FS and AS are more + // than a cluster different, do this truncate. + // + + if ( FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE) ) { + + FatTruncateFileAllocation( IrpContext, + Fcb, + Fcb->Header.FileSize.LowPart ); + } + + // + // Also compare the file's dirent in the parent directory + // with the size information in the Fcb and update + // it if neccessary. Note that we don't mark the Bcb dirty + // because we will be flushing the file object presently, and + // Mm knows what's really dirty. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &DirentBcb ); + + if (Dirent->FileSize != Fcb->Header.FileSize.LowPart) { + + Dirent->FileSize = Fcb->Header.FileSize.LowPart; + } + + // + // We must unpin the Bcb before the flush since we recursively tear up + // the tree if Mm decides that the data section is no longer referenced + // and the final close comes in for this file. If this parent has no + // more children as a result, we will try to initiate teardown on it + // and Cc will deadlock against the active count of this Bcb. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // Now flush the file. Note that this may make the Fcb + // go away if Mm dereferences its file object. + // + + Status = FatFlushFile( IrpContext, Fcb, FlushType ); + + if (!NT_SUCCESS(Status)) { + + ReturnStatus = Status; + } + + NextFcb: NOTHING; + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // Since we have the Vcb exclusive we know that if any closes + // come in it is because the CcPurgeCacheSection caused the + // Fcb to go away. + // + + if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB) ) { + + FatReleaseFcb( (IRPCONTEXT), Fcb ); + } + } _SEH2_END; + + } _SEH2_EXCEPT( (ReturnStatus = FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode())) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + } + + Fcb = NextFcb; + } + + // + // OK, now flush the directories. + // + + Fcb = Dcb; + + while (Fcb != NULL) { + + NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb); + + if ( (NodeType( Fcb ) != FAT_NTC_FCB) && + !IsFileDeleted(IrpContext, Fcb) ) { + + // + // Make sure the Fcb is OK. + // + + _SEH2_TRY { + + FatVerifyFcb( IrpContext, Fcb ); + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + if (Fcb->FcbCondition == FcbGood) { + + Status = FatFlushFile( IrpContext, Fcb, FlushType ); + + if (!NT_SUCCESS(Status)) { + + ReturnStatus = Status; + } + } + } + + Fcb = NextFcb; + } + + _SEH2_TRY { + + FatUnpinRepinnedBcbs( IrpContext ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + ReturnStatus = IrpContext->ExceptionStatus; + } _SEH2_END; + + if (ClearWriteThroughOnExit) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); + } + if (ClearWaitOnExit) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + DebugTrace(-1, Dbg, "FatFlushDirectory -> 0x%08lx\n", ReturnStatus); + + return ReturnStatus; +} + + +NTSTATUS +FatFlushFat ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + The function carefully flushes the entire FAT for a volume. It is + nessecary to dance around a bit because of complicated synchronization + reasons. + +Arguments: + + Vcb - Supplies the Vcb whose FAT is being flushed + +Return Value: + + VOID + +--*/ + +{ + PBCB Bcb; + PVOID DontCare; + IO_STATUS_BLOCK Iosb; + LARGE_INTEGER Offset; + + NTSTATUS ReturnStatus = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // If this volume is write protected, no need to flush. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + return STATUS_SUCCESS; + } + + // + // Make sure the Vcb is OK. + // + + _SEH2_TRY { + + FatVerifyVcb( IrpContext, Vcb ); + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + if (Vcb->VcbCondition != VcbGood) { + + return STATUS_FILE_INVALID; + } + + // + // The only way we have to correctly synchronize things is to + // repin stuff, and then unpin repin it. + // + // With NT 5.0, we can use some new cache manager support to make + // this a lot more efficient (important for FAT32). Since we're + // only worried about ranges that are dirty - and since we're a + // modified-no-write stream - we can assume that if there is no + // BCB, there is no work to do in the range. I.e., the lazy writer + // beat us to it. + // + // This is much better than reading the entire FAT in and trying + // to punch it out (see the test in the write path to blow + // off writes that don't correspond to dirty ranges of the FAT). + // For FAT32, this would be a *lot* of reading. + // + + if (Vcb->AllocationSupport.FatIndexBitSize != 12) { + + // + // Walk through the Fat, one page at a time. + // + + ULONG NumberOfPages; + ULONG Page; + + NumberOfPages = ( FatReservedBytes(&Vcb->Bpb) + + FatBytesPerFat(&Vcb->Bpb) + + (PAGE_SIZE - 1) ) / PAGE_SIZE; + + + for ( Page = 0, Offset.QuadPart = 0; + Page < NumberOfPages; + Page++, Offset.LowPart += PAGE_SIZE ) { + + _SEH2_TRY { + + if (CcPinRead( Vcb->VirtualVolumeFile, + &Offset, + PAGE_SIZE, + PIN_WAIT | PIN_IF_BCB, + &Bcb, + &DontCare )) { + + CcSetDirtyPinnedData( Bcb, NULL ); + CcRepinBcb( Bcb ); + CcUnpinData( Bcb ); + CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb ); + + if (!NT_SUCCESS(Iosb.Status)) { + + ReturnStatus = Iosb.Status; + } + } + + } _SEH2_EXCEPT(FatExceptionFilter(IrpContext, _SEH2_GetExceptionInformation())) { + + ReturnStatus = IrpContext->ExceptionStatus; + continue; + } _SEH2_END; + } + + } else { + + // + // We read in the entire fat in the 12 bit case. + // + + Offset.QuadPart = FatReservedBytes( &Vcb->Bpb ); + + _SEH2_TRY { + + if (CcPinRead( Vcb->VirtualVolumeFile, + &Offset, + FatBytesPerFat( &Vcb->Bpb ), + PIN_WAIT | PIN_IF_BCB, + &Bcb, + &DontCare )) { + + CcSetDirtyPinnedData( Bcb, NULL ); + CcRepinBcb( Bcb ); + CcUnpinData( Bcb ); + CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb ); + + if (!NT_SUCCESS(Iosb.Status)) { + + ReturnStatus = Iosb.Status; + } + } + + } _SEH2_EXCEPT(FatExceptionFilter(IrpContext, _SEH2_GetExceptionInformation())) { + + ReturnStatus = IrpContext->ExceptionStatus; + } _SEH2_END; + } + + return ReturnStatus; +} + + +NTSTATUS +FatFlushVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + The following routine is used to flush a volume to disk, including the + volume file, and ea file. + +Arguments: + + Vcb - Supplies the volume being flushed + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + NTSTATUS - The Status from the flush. + +--*/ + +{ + NTSTATUS Status; + NTSTATUS ReturnStatus = STATUS_SUCCESS; + + PAGED_CODE(); + + // + // If this volume is write protected, no need to flush. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) { + + return STATUS_SUCCESS; + } + + // + // Flush all the files and directories. + // + + Status = FatFlushDirectory( IrpContext, Vcb->RootDcb, FlushType ); + + if (!NT_SUCCESS(Status)) { + + ReturnStatus = Status; + } + + // + // Now Flush the FAT + // + + Status = FatFlushFat( IrpContext, Vcb ); + + if (!NT_SUCCESS(Status)) { + + ReturnStatus = Status; + } + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + + return ReturnStatus; +} + + +NTSTATUS +FatFlushFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FAT_FLUSH_TYPE FlushType + ) + +/*++ + +Routine Description: + + This routine simply flushes the data section on a file. + +Arguments: + + Fcb - Supplies the file being flushed + + FlushType - Specifies the kind of flushing to perform + +Return Value: + + NTSTATUS - The Status from the flush. + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + PVCB Vcb = Fcb->Vcb; + + PAGED_CODE(); + + CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, &Iosb ); + + if ( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB )) { + + // + // Grab and release PagingIo to serialize ourselves with the lazy writer. + // This will work to ensure that all IO has completed on the cached + // data. + // + // If we are to invalidate the file, now is the right time to do it. Do + // it non-recursively so we don't thump children before their time. + // + + ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE); + + if (FlushType == FlushAndInvalidate) { + + FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE ); + } + + ExReleaseResourceLite( Fcb->Header.PagingIoResource ); + } + + return Iosb.Status; +} + + +NTSTATUS +FatHijackIrpAndFlushDevice ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PDEVICE_OBJECT TargetDeviceObject + ) + +/*++ + +Routine Description: + + This routine is called when we need to send a flush to a device but + we don't have a flush Irp. What this routine does is make a copy + of its current Irp stack location, but changes the Irp Major code + to a IRP_MJ_FLUSH_BUFFERS amd then send it down, but cut it off at + the knees in the completion routine, fix it up and return to the + user as if nothing had happened. + +Arguments: + + Irp - The Irp to hijack + + TargetDeviceObject - The device to send the request to. + +Return Value: + + NTSTATUS - The Status from the flush in case anybody cares. + +--*/ + +{ + KEVENT Event; + NTSTATUS Status; + PIO_STACK_LOCATION NextIrpSp; + + PAGED_CODE(); + + // + // Get the next stack location, and copy over the stack location + // + + NextIrpSp = IoGetNextIrpStackLocation( Irp ); + + *NextIrpSp = *IoGetCurrentIrpStackLocation( Irp ); + + NextIrpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS; + NextIrpSp->MinorFunction = 0; + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + IoSetCompletionRoutine( Irp, + FatHijackCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request. + // + + Status = IoCallDriver( TargetDeviceObject, Irp ); + + if (Status == STATUS_PENDING) { + + KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // If the driver doesn't support flushes, return SUCCESS. + // + + if (Status == STATUS_INVALID_DEVICE_REQUEST) { + Status = STATUS_SUCCESS; + } + + Irp->IoStatus.Status = 0; + Irp->IoStatus.Information = 0; + + return Status; +} + + +VOID +FatFlushFatEntries ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG Cluster, + IN ULONG Count +) + +/*++ + +Routine Description: + + This macro flushes the FAT page(s) containing the passed in run. + +Arguments: + + Vcb - Supplies the volume being flushed + + Cluster - The starting cluster + + Count - The number of FAT entries in the run + +Return Value: + + VOID + +--*/ + +{ + ULONG ByteCount; + LARGE_INTEGER FileOffset; + + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + FileOffset.HighPart = 0; + FileOffset.LowPart = FatReservedBytes( &Vcb->Bpb ); + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + + FileOffset.LowPart += Cluster * 3 / 2; + ByteCount = (Count * 3 / 2) + 1; + + } else if (Vcb->AllocationSupport.FatIndexBitSize == 32) { + + FileOffset.LowPart += Cluster * sizeof(ULONG); + ByteCount = Count * sizeof(ULONG); + + } else { + + FileOffset.LowPart += Cluster * sizeof( USHORT ); + ByteCount = Count * sizeof( USHORT ); + + } + + CcFlushCache( &Vcb->SectionObjectPointers, + &FileOffset, + ByteCount, + &Iosb ); + + if (NT_SUCCESS(Iosb.Status)) { + Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext, + IrpContext->OriginatingIrp, + Vcb->TargetDeviceObject ); + } + + if (!NT_SUCCESS(Iosb.Status)) { + FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status); + } +} + + +VOID +FatFlushDirentForFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb +) + +/*++ + +Routine Description: + + This macro flushes the page containing a file's DIRENT in its parent. + +Arguments: + + Fcb - Supplies the file whose DIRENT is being flushed + +Return Value: + + VOID + +--*/ + +{ + LARGE_INTEGER FileOffset; + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + FileOffset.QuadPart = Fcb->DirentOffsetWithinDirectory; + + CcFlushCache( &Fcb->ParentDcb->NonPaged->SectionObjectPointers, + &FileOffset, + sizeof( DIRENT ), + &Iosb ); + + if (NT_SUCCESS(Iosb.Status)) { + Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext, + IrpContext->OriginatingIrp, + Fcb->Vcb->TargetDeviceObject ); + } + + if (!NT_SUCCESS(Iosb.Status)) { + FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status); + } +} + + +// +// Local support routine +// + +NTSTATUS +NTAPI +FatFlushCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +{ + NTSTATUS Status = (NTSTATUS) (ULONG_PTR) Contxt; + + // + // Add the hack-o-ramma to fix formats. + // + + if ( Irp->PendingReturned ) { + + IoMarkIrpPending( Irp ); + } + + // + // If the Irp got STATUS_INVALID_DEVICE_REQUEST, normalize it + // to STATUS_SUCCESS. + // + + if (Irp->IoStatus.Status == STATUS_INVALID_DEVICE_REQUEST) { + + Irp->IoStatus.Status = Status; + } + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Contxt ); + + return STATUS_SUCCESS; +} + +// +// Local support routine +// + +NTSTATUS +NTAPI +FatHijackCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +{ + // + // Set the event so that our call will wake up. + // + + KeSetEvent( (PKEVENT)Contxt, 0, FALSE ); + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + diff --git a/drivers/filesystems/fastfat_new/fsctrl.c b/drivers/filesystems/fastfat_new/fsctrl.c new file mode 100644 index 00000000000..2dfe56daba2 --- /dev/null +++ b/drivers/filesystems/fastfat_new/fsctrl.c @@ -0,0 +1,6726 @@ +/*++ + + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FsCtrl.c + +Abstract: + + This module implements the File System Control routines for Fat called + by the dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_FSCTRL) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_FSCTRL) + +// +// Local procedure prototypes +// + +NTSTATUS +FatMountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject, + IN PVPB Vpb, + IN PDEVICE_OBJECT FsDeviceObject + ); + +NTSTATUS +FatVerifyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +BOOLEAN +FatIsMediaWriteProtected ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject + ); + +NTSTATUS +FatUserFsCtrl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatOplockRequest ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatLockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatUnlockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatDismountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatDirtyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatIsVolumeDirty ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatIsVolumeMounted ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatIsPathnameValid ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatInvalidateVolumes ( + IN PIRP Irp + ); + +VOID +FatScanForDismountedVcb ( + IN PIRP_CONTEXT IrpContext + ); + +BOOLEAN +FatPerformVerifyDiskRead ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PVOID Buffer, + IN LBO Lbo, + IN ULONG NumberOfBytesToRead, + IN BOOLEAN ReturnOnError + ); + +NTSTATUS +FatQueryRetrievalPointers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatQueryBpb ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatGetStatistics ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatAllowExtendedDasdIo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +// +// Local support routine prototypes +// + +NTSTATUS +FatGetVolumeBitmap ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatGetRetrievalPointers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +NTSTATUS +FatMoveFile ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ); + +VOID +FatComputeMoveFileSplicePoints ( + PIRP_CONTEXT IrpContext, + PFCB FcbOrDcb, + ULONG FileOffset, + ULONG TargetCluster, + ULONG BytesToReallocate, + PULONG FirstSpliceSourceCluster, + PULONG FirstSpliceTargetCluster, + PULONG SecondSpliceSourceCluster, + PULONG SecondSpliceTargetCluster, + PLARGE_MCB SourceMcb +); + +VOID +FatComputeMoveFileParameter ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG BufferSize, + IN ULONG FileOffset, + IN OUT PULONG ByteCount, + OUT PULONG BytesToReallocate, + OUT PULONG BytesToWrite, + OUT PLARGE_INTEGER SourceLbo +); + +NTSTATUS +FatSearchBufferForLabel( + IN PIRP_CONTEXT IrpContext, + IN PVPB Vpb, + IN PVOID Buffer, + IN ULONG Size, + OUT PBOOLEAN LabelFound +); + +VOID +FatVerifyLookupFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN OUT PULONG FatEntry + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatAddMcbEntry) +#pragma alloc_text(PAGE, FatAllowExtendedDasdIo) +#pragma alloc_text(PAGE, FatCommonFileSystemControl) +#pragma alloc_text(PAGE, FatComputeMoveFileParameter) +#pragma alloc_text(PAGE, FatComputeMoveFileSplicePoints) +#pragma alloc_text(PAGE, FatDirtyVolume) +#pragma alloc_text(PAGE, FatDismountVolume) +#pragma alloc_text(PAGE, FatFsdFileSystemControl) +#pragma alloc_text(PAGE, FatGetRetrievalPointers) +#pragma alloc_text(PAGE, FatGetStatistics) +#pragma alloc_text(PAGE, FatGetVolumeBitmap) +#pragma alloc_text(PAGE, FatIsMediaWriteProtected) +#pragma alloc_text(PAGE, FatIsPathnameValid) +#pragma alloc_text(PAGE, FatIsVolumeDirty) +#pragma alloc_text(PAGE, FatIsVolumeMounted) +#pragma alloc_text(PAGE, FatLockVolume) +#pragma alloc_text(PAGE, FatLookupLastMcbEntry) +#pragma alloc_text(PAGE, FatMountVolume) +#pragma alloc_text(PAGE, FatMoveFile) +#pragma alloc_text(PAGE, FatOplockRequest) +#pragma alloc_text(PAGE, FatPerformVerifyDiskRead) +#pragma alloc_text(PAGE, FatQueryBpb) +#pragma alloc_text(PAGE, FatQueryRetrievalPointers) +#pragma alloc_text(PAGE, FatRemoveMcbEntry) +#pragma alloc_text(PAGE, FatScanForDismountedVcb) +#pragma alloc_text(PAGE, FatSearchBufferForLabel) +#pragma alloc_text(PAGE, FatUnlockVolume) +#pragma alloc_text(PAGE, FatUserFsCtrl) +#pragma alloc_text(PAGE, FatVerifyLookupFatEntry) +#pragma alloc_text(PAGE, FatVerifyVolume) +#endif + +#if DBG + +BOOLEAN FatMoveFileDebug = 0; + +#endif + +// +// These wrappers go around the MCB package; we scale the LBO's passed +// in (which can be bigger than 32 bits on fat32) by the volume's sector +// size. +// +// Note we now use the real large mcb package. This means these shims +// now also convert the -1 unused LBN number to the 0 of the original +// mcb package. +// + +#define MCB_SCALE_LOG2 (Vcb->AllocationSupport.LogOfBytesPerSector) +#define MCB_SCALE (1 << MCB_SCALE_LOG2) +#define MCB_SCALE_MODULO (MCB_SCALE - 1) + + +BOOLEAN +FatAddMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + IN LBO Lbo, + IN ULONG SectorCount + ) + +{ + PAGED_CODE(); + + if (SectorCount) { + + // + // Round up sectors, but be careful as SectorCount approaches 4Gb. + // Note that for x>0, (x+m-1)/m = ((x-1)/m)+(m/m) = ((x-1)/m)+1 + // + + SectorCount--; + SectorCount >>= MCB_SCALE_LOG2; + SectorCount++; + } + + Vbo >>= MCB_SCALE_LOG2; + Lbo >>= MCB_SCALE_LOG2; + + return FsRtlAddLargeMcbEntry( Mcb, + ((LONGLONG) Vbo), + ((LONGLONG) Lbo), + ((LONGLONG) SectorCount) ); +} + + +BOOLEAN +FatLookupMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + OUT PLBO Lbo, + OUT PULONG SectorCount OPTIONAL, + OUT PULONG Index OPTIONAL + ) +{ + BOOLEAN Results; + LONGLONG LiLbo; + LONGLONG LiSectorCount; + ULONG Remainder; + + LiLbo = 0; + LiSectorCount = 0; + + Remainder = Vbo & MCB_SCALE_MODULO; + + Results = FsRtlLookupLargeMcbEntry( Mcb, + (Vbo >> MCB_SCALE_LOG2), + &LiLbo, + ARGUMENT_PRESENT(SectorCount) ? &LiSectorCount : NULL, + NULL, + NULL, + Index ); + + if ((ULONG) LiLbo != -1) { + + *Lbo = (((LBO) LiLbo) << MCB_SCALE_LOG2); + + if (Results) { + + *Lbo += Remainder; + } + + } else { + + *Lbo = 0; + } + + if (ARGUMENT_PRESENT(SectorCount)) { + + *SectorCount = (ULONG) LiSectorCount; + + if (*SectorCount) { + + *SectorCount <<= MCB_SCALE_LOG2; + + if (*SectorCount == 0) { + + *SectorCount = (ULONG) -1; + } + + if (Results) { + + *SectorCount -= Remainder; + } + } + + } + + return Results; +} + +// +// NOTE: Vbo/Lbn undefined if MCB is empty & return code false. +// + +BOOLEAN +FatLookupLastMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + OUT PVBO Vbo, + OUT PLBO Lbo, + OUT PULONG Index + ) + +{ + BOOLEAN Results; + LONGLONG LiVbo; + LONGLONG LiLbo; + ULONG LocalIndex; + + PAGED_CODE(); + + LiVbo = LiLbo = 0; + LocalIndex = 0; + + Results = FsRtlLookupLastLargeMcbEntryAndIndex( Mcb, + &LiVbo, + &LiLbo, + &LocalIndex ); + + *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2; + + if (((ULONG) LiLbo) != -1) { + + *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2; + + *Lbo += (MCB_SCALE - 1); + *Vbo += (MCB_SCALE - 1); + + } else { + + *Lbo = 0; + } + + if (Index) { + *Index = LocalIndex; + } + + return Results; +} + + +BOOLEAN +FatGetNextMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN ULONG RunIndex, + OUT PVBO Vbo, + OUT PLBO Lbo, + OUT PULONG SectorCount + ) + +{ + BOOLEAN Results; + LONGLONG LiVbo; + LONGLONG LiLbo; + LONGLONG LiSectorCount; + + PAGED_CODE(); + + LiVbo = LiLbo = 0; + + Results = FsRtlGetNextLargeMcbEntry( Mcb, + RunIndex, + &LiVbo, + &LiLbo, + &LiSectorCount ); + + if (Results) { + + *Vbo = ((VBO) LiVbo) << MCB_SCALE_LOG2; + + if (((ULONG) LiLbo) != -1) { + + *Lbo = ((LBO) LiLbo) << MCB_SCALE_LOG2; + + } else { + + *Lbo = 0; + } + + *SectorCount = ((ULONG) LiSectorCount) << MCB_SCALE_LOG2; + + if ((*SectorCount == 0) && (LiSectorCount != 0)) { + *SectorCount = (ULONG) -1; /* it overflowed */ + } + } + + return Results; +} + + +VOID +FatRemoveMcbEntry ( + IN PVCB Vcb, + IN PLARGE_MCB Mcb, + IN VBO Vbo, + IN ULONG SectorCount + ) +{ + + if ((SectorCount) && (SectorCount != 0xFFFFFFFF)) { + + SectorCount--; + SectorCount >>= MCB_SCALE_LOG2; + SectorCount++; + } + + Vbo >>= MCB_SCALE_LOG2; + +#if DBG + _SEH2_TRY { +#endif + + FsRtlRemoveLargeMcbEntry( Mcb, + (LONGLONG) Vbo, + (LONGLONG) SectorCount); + +#if DBG + } _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) { + + NOTHING; + } _SEH2_END; +#endif + +} + + +NTSTATUS +NTAPI +FatFsdFileSystemControl ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of FileSystem control operations + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + BOOLEAN Wait; + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg,"FatFsdFileSystemControl\n", 0); + + // + // Call the common FileSystem Control routine, with blocking allowed if + // synchronous. This opeation needs to special case the mount + // and verify suboperations because we know they are allowed to block. + // We identify these suboperations by looking at the file object field + // and seeing if its null. + // + + if (IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL) { + + Wait = TRUE; + + } else { + + Wait = CanFsdWait( Irp ); + } + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + PIO_STACK_LOCATION IrpSp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // We need to made a special check here for the InvalidateVolumes + // FSCTL as that comes in with a FileSystem device object instead + // of a volume device object. + // + + if (FatDeviceIsFatFsdo( IrpSp->DeviceObject) && + (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && + (IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST) && + (IrpSp->Parameters.FileSystemControl.FsControlCode == + FSCTL_INVALIDATE_VOLUMES)) { + + Status = FatInvalidateVolumes( Irp ); + + } else { + + IrpContext = FatCreateIrpContext( Irp, Wait ); + + Status = FatCommonFileSystemControl( IrpContext, Irp ); + } + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdFileSystemControl -> %08lx\n", Status); + + return Status; +} + + +NTSTATUS +FatCommonFileSystemControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing FileSystem control operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + // + // Get a pointer to the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg,"FatCommonFileSystemControl\n", 0); + DebugTrace( 0, Dbg,"Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg,"MinorFunction = %08lx\n", IrpSp->MinorFunction); + + // + // We know this is a file system control so we'll case on the + // minor function, and call a internal worker routine to complete + // the irp. + // + + switch (IrpSp->MinorFunction) { + + case IRP_MN_USER_FS_REQUEST: + + Status = FatUserFsCtrl( IrpContext, Irp ); + break; + + case IRP_MN_MOUNT_VOLUME: + + Status = FatMountVolume( IrpContext, + IrpSp->Parameters.MountVolume.DeviceObject, + IrpSp->Parameters.MountVolume.Vpb, + IrpSp->DeviceObject ); + + // + // Complete the request. + // + // We do this here because FatMountVolume can be called recursively, + // but the Irp is only to be completed once. + // + // NOTE: I don't think this is true anymore (danlo 3/15/1999). Probably + // an artifact of the old doublespace attempt. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + break; + + case IRP_MN_VERIFY_VOLUME: + + Status = FatVerifyVolume( IrpContext, Irp ); + break; + + default: + + DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction); + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + DebugTrace(-1, Dbg, "FatCommonFileSystemControl -> %08lx\n", Status); + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatMountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject, + IN PVPB Vpb, + IN PDEVICE_OBJECT FsDeviceObject + ) + +/*++ + +Routine Description: + + This routine performs the mount volume operation. It is responsible for + either completing of enqueuing the input Irp. + + Its job is to verify that the volume denoted in the IRP is a Fat volume, + and create the VCB and root DCB structures. The algorithm it uses is + essentially as follows: + + 1. Create a new Vcb Structure, and initialize it enough to do cached + volume file I/O. + + 2. Read the disk and check if it is a Fat volume. + + 3. If it is not a Fat volume then free the cached volume file, delete + the VCB, and complete the IRP with STATUS_UNRECOGNIZED_VOLUME + + 4. Check if the volume was previously mounted and if it was then do a + remount operation. This involves reinitializing the cached volume + file, checking the dirty bit, resetting up the allocation support, + deleting the VCB, hooking in the old VCB, and completing the IRP. + + 5. Otherwise create a root DCB, create Fsp threads as necessary, and + complete the IRP. + +Arguments: + + TargetDeviceObject - This is where we send all of our requests. + + Vpb - This gives us additional information needed to complete the mount. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + NTSTATUS Status; + + PBCB BootBcb; + PPACKED_BOOT_SECTOR BootSector; + + PBCB DirentBcb; + PDIRENT Dirent; + ULONG ByteOffset; + + BOOLEAN MountNewVolume = FALSE; + BOOLEAN WeClearedVerifyRequiredBit = FALSE; + BOOLEAN DoARemount = FALSE; + + PVCB OldVcb; + PVPB OldVpb; + + PDEVICE_OBJECT RealDevice; + PVOLUME_DEVICE_OBJECT VolDo = NULL; + PVCB Vcb = NULL; + PFILE_OBJECT RootDirectoryFile = NULL; + + PLIST_ENTRY Links; + + IO_STATUS_BLOCK Iosb; + ULONG ChangeCount = 0; + + DISK_GEOMETRY Geometry; + + PARTITION_INFORMATION_EX PartitionInformation; + NTSTATUS StatusPartInfo; + + DebugTrace(+1, Dbg, "FatMountVolume\n", 0); + DebugTrace( 0, Dbg, "TargetDeviceObject = %08lx\n", TargetDeviceObject); + DebugTrace( 0, Dbg, "Vpb = %08lx\n", Vpb); + + ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + ASSERT( FatDeviceIsFatFsdo( FsDeviceObject)); + + // + // Verify that there is a disk here and pick up the change count. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_CHECK_VERIFY, + TargetDeviceObject, + &ChangeCount, + sizeof(ULONG), + FALSE, + TRUE, + &Iosb ); + + if (!NT_SUCCESS( Status )) { + + // + // If we will allow a raw mount then avoid sending the popup. + // + // Only send this on "true" disk devices to handle the accidental + // legacy of FAT. No other FS will throw a harderror on empty + // drives. + // + // Cmd should really handle this per 9x. + // + + if (!FlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT ) && + Vpb->RealDevice->DeviceType == FILE_DEVICE_DISK) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + return Status; + } + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + ChangeCount = 0; + } + + // + // If this is a CD class device, then check to see if there is a + // 'data track' or not. This is to avoid issuing paging reads which will + // fail later in the mount process (e.g. CD-DA or blank CD media) + // + + if ((TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) && + !FatScanForDataTrack( IrpContext, TargetDeviceObject)) { + + return STATUS_UNRECOGNIZED_VOLUME; + } + + // + // Ping the volume with a partition query and pick up the partition + // type. We'll check this later to avoid some scurrilous volumes. + // + + StatusPartInfo = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_GET_PARTITION_INFO_EX, + TargetDeviceObject, + &PartitionInformation, + sizeof(PARTITION_INFORMATION_EX), + FALSE, + TRUE, + &Iosb ); + + // + // Make sure we can wait. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Do a quick check to see if there any Vcb's which can be removed. + // + + FatScanForDismountedVcb( IrpContext ); + + // + // Initialize the Bcbs and our final state so that the termination + // handlers will know what to free or unpin + // + + BootBcb = NULL; + DirentBcb = NULL; + + Vcb = NULL; + VolDo = NULL; + MountNewVolume = FALSE; + + _SEH2_TRY { + + // + // Synchronize with FatCheckForDismount(), which modifies the vpb. + // + + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + + // + // Create a new volume device object. This will have the Vcb + // hanging off of its end, and set its alignment requirement + // from the device we talk to. + // + + if (!NT_SUCCESS(Status = IoCreateDevice( FatData.DriverObject, + sizeof(VOLUME_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT), + NULL, + FILE_DEVICE_DISK_FILE_SYSTEM, + 0, + FALSE, + (PDEVICE_OBJECT *)&VolDo))) { + + try_return( Status ); + } + +#ifdef _PNP_POWER_ + // + // This driver doesn't talk directly to a device, and (at the moment) + // isn't otherwise concerned about power management. + // + + VolDo->DeviceObject.DeviceObjectExtension->PowerControlNeeded = FALSE; +#endif + + // + // Our alignment requirement is the larger of the processor alignment requirement + // already in the volume device object and that in the TargetDeviceObject + // + + if (TargetDeviceObject->AlignmentRequirement > VolDo->DeviceObject.AlignmentRequirement) { + + VolDo->DeviceObject.AlignmentRequirement = TargetDeviceObject->AlignmentRequirement; + } + + // + // Initialize the overflow queue for the volume + // + + VolDo->OverflowQueueCount = 0; + InitializeListHead( &VolDo->OverflowQueue ); + + VolDo->PostedRequestCount = 0; + KeInitializeSpinLock( &VolDo->OverflowQueueSpinLock ); + + // + // We must initialize the stack size in our device object before + // the following reads, because the I/O system has not done it yet. + // This must be done before we clear the device initializing flag + // otherwise a filter could attach and copy the wrong stack size into + // it's device object. + // + + VolDo->DeviceObject.StackSize = (CCHAR)(TargetDeviceObject->StackSize + 1); + + // + // We must also set the sector size correctly in our device object + // before clearing the device initializing flag. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + TargetDeviceObject, + &Geometry, + sizeof( DISK_GEOMETRY ), + FALSE, + TRUE, + NULL ); + + VolDo->DeviceObject.SectorSize = (USHORT)Geometry.BytesPerSector; + + // + // Indicate that this device object is now completely initialized + // + + ClearFlag(VolDo->DeviceObject.Flags, DO_DEVICE_INITIALIZING); + + // + // Now Before we can initialize the Vcb we need to set up the device + // object field in the Vpb to point to our new volume device object. + // This is needed when we create the virtual volume file's file object + // in initialize vcb. + // + + Vpb->DeviceObject = (PDEVICE_OBJECT)VolDo; + + // + // If the real device needs verification, temporarily clear the + // field. + // + + RealDevice = Vpb->RealDevice; + + if ( FlagOn(RealDevice->Flags, DO_VERIFY_VOLUME) ) { + + ClearFlag(RealDevice->Flags, DO_VERIFY_VOLUME); + + WeClearedVerifyRequiredBit = TRUE; + } + + // + // Initialize the new vcb + // + + FatInitializeVcb( IrpContext, + &VolDo->Vcb, + TargetDeviceObject, + Vpb, + FsDeviceObject); + // + // Get a reference to the Vcb hanging off the end of the device object + // + + Vcb = &VolDo->Vcb; + + // + // Read in the boot sector, and have the read be the minumum size + // needed. We know we can wait. + // + + // + // We need to commute errors on CD so that CDFS will get its crack. Audio + // and even data media may not be universally readable on sector zero. + // + + _SEH2_TRY { + + FatReadVolumeFile( IrpContext, + Vcb, + 0, // Starting Byte + sizeof(PACKED_BOOT_SECTOR), + &BootBcb, + (PVOID *)&BootSector ); + + } _SEH2_EXCEPT( Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + NOTHING; + } _SEH2_END; + + // + // Call a routine to check the boot sector to see if it is fat + // + + if (BootBcb == NULL || !FatIsBootSectorFat( BootSector)) { + + DebugTrace(0, Dbg, "Not a Fat Volume\n", 0); + + // + // Complete the request and return to our caller + // + + try_return( Status = STATUS_UNRECOGNIZED_VOLUME ); + } + + // + // Unpack the BPB. We used to do some sanity checking of the FATs at + // this point, but authoring errors on third-party devices prevent + // us from continuing to safeguard ourselves. We can only hope the + // boot sector check is good enough. + // + // (read: digital cameras) + // + // Win9x does the same. + // + + FatUnpackBios( &Vcb->Bpb, &BootSector->PackedBpb ); + + // + // Check if we have an OS/2 Boot Manager partition and treat it as an + // unknown file system. We'll check the partition type in from the + // partition table and we ensure that it has less than 0x80 sectors, + // which is just a heuristic that will capture all real OS/2 BM partitions + // and avoid the chance we'll discover partitions which erroneously + // (but to this point, harmlessly) put down the OS/2 BM type. + // + // Note that this is only conceivable on good old MBR media. + // + // The OS/2 Boot Manager boot format mimics a FAT16 partition in sector + // zero but does is not a real FAT16 file system. For example, the boot + // sector indicates it has 2 FATs but only really has one, with the boot + // manager code overlaying the second FAT. If we then set clean bits in + // FAT[0] we'll corrupt that code. + // + + if (NT_SUCCESS( StatusPartInfo ) && + (PartitionInformation.PartitionStyle == PARTITION_STYLE_MBR && + PartitionInformation.Mbr.PartitionType == PARTITION_OS2BOOTMGR) && + (Vcb->Bpb.Sectors != 0 && + Vcb->Bpb.Sectors < 0x80)) { + + DebugTrace( 0, Dbg, "OS/2 Boot Manager volume detected, volume not mounted. \n", 0 ); + + // + // Complete the request and return to our caller + // + + try_return( Status = STATUS_UNRECOGNIZED_VOLUME ); + } + + // + // Verify that the sector size recorded in the Bpb matches what the + // device currently reports it's sector size to be. + // + + if ( !NT_SUCCESS( Status) || + (Geometry.BytesPerSector != Vcb->Bpb.BytesPerSector)) { + + try_return( Status = STATUS_UNRECOGNIZED_VOLUME ); + } + + // + // This is a fat volume, so extract the bpb, serial number. The + // label we'll get later after we've created the root dcb. + // + // Note that the way data caching is done, we set neither the + // direct I/O or Buffered I/O bit in the device object flags. + // + + if (Vcb->Bpb.Sectors != 0) { Vcb->Bpb.LargeSectors = 0; } + + if (IsBpbFat32(&BootSector->PackedBpb)) { + + CopyUchar4( &Vpb->SerialNumber, ((PPACKED_BOOT_SECTOR_EX)BootSector)->Id ); + + } else { + + CopyUchar4( &Vpb->SerialNumber, BootSector->Id ); + + // + // Allocate space for the stashed boot sector chunk. This only has meaning on + // FAT12/16 volumes since this only is kept for the FSCTL_QUERY_FAT_BPB and it and + // its users are a bit wierd, thinking that a BPB exists wholly in the first 0x24 + // bytes. + // + + Vcb->First0x24BytesOfBootSector = + FsRtlAllocatePoolWithTag( PagedPool, + 0x24, + TAG_STASHED_BPB ); + + // + // Stash a copy of the first 0x24 bytes + // + + RtlCopyMemory( Vcb->First0x24BytesOfBootSector, + BootSector, + 0x24 ); + } + + // + // Now unpin the boot sector, so when we set up allocation eveything + // works. + // + + FatUnpinBcb( IrpContext, BootBcb ); + + // + // Compute a number of fields for Vcb.AllocationSupport + // + + FatSetupAllocationSupport( IrpContext, Vcb ); + + // + // Sanity check the FsInfo information for FAT32 volumes. Silently deal + // with messed up information by effectively disabling FsInfo updates. + // + + if (FatIsFat32( Vcb )) { + + if (Vcb->Bpb.FsInfoSector >= Vcb->Bpb.ReservedSectors) { + + Vcb->Bpb.FsInfoSector = 0; + } + } + + // + // Create a root Dcb so we can read in the volume label. If this is FAT32, we can + // discover corruption in the FAT chain. + // + // NOTE: this exception handler presumes that this is the only spot where we can + // discover corruption in the mount process. If this ever changes, this handler + // MUST be expanded. The reason we have this guy here is because we have to rip + // the structures down now (in the finally below) and can't wait for the outer + // exception handling to do it for us, at which point everything will have vanished. + // + + _SEH2_TRY { + + FatCreateRootDcb( IrpContext, Vcb ); + + } _SEH2_EXCEPT (_SEH2_GetExceptionCode() == STATUS_FILE_CORRUPT_ERROR ? EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH) { + + // + // The volume needs to be dirtied, do it now. Note that at this point we have built + // enough of the Vcb to pull this off. + // + + FatMarkVolume( IrpContext, Vcb, VolumeDirty ); + + // + // Now keep bailing out ... + // + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } _SEH2_END; + + FatLocateVolumeLabel( IrpContext, + Vcb, + &Dirent, + &DirentBcb, +#ifndef __REACTOS__ + &ByteOffset ); +#else + (PVBO)&ByteOffset ); +#endif + + if (Dirent != NULL) { + + OEM_STRING OemString; + UNICODE_STRING UnicodeString; + + // + // Compute the length of the volume name + // + +#ifndef __REACTOS__ + OemString.Buffer = &Dirent->FileName[0]; +#else + OemString.Buffer = (PCHAR)&Dirent->FileName[0]; +#endif + OemString.MaximumLength = 11; + + for ( OemString.Length = 11; + OemString.Length > 0; + OemString.Length -= 1) { + + if ( (Dirent->FileName[OemString.Length-1] != 0x00) && + (Dirent->FileName[OemString.Length-1] != 0x20) ) { break; } + } + + UnicodeString.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH; + UnicodeString.Buffer = &Vcb->Vpb->VolumeLabel[0]; + + Status = RtlOemStringToCountedUnicodeString( &UnicodeString, + &OemString, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + + try_return( Status ); + } + + Vpb->VolumeLabelLength = UnicodeString.Length; + + } else { + + Vpb->VolumeLabelLength = 0; + } + + // + // Use the change count we noted initially *before* doing any work. + // If something came along in the midst of this operation, we'll + // verify and discover the problem. + // + + Vcb->ChangeCount = ChangeCount; + + // + // Now scan the list of previously mounted volumes and compare + // serial numbers and volume labels off not currently mounted + // volumes to see if we have a match. + // + + for (Links = FatData.VcbQueue.Flink; + Links != &FatData.VcbQueue; + Links = Links->Flink) { + + OldVcb = CONTAINING_RECORD( Links, VCB, VcbLinks ); + OldVpb = OldVcb->Vpb; + + // + // Skip over ourselves since we're already in the VcbQueue + // + + if (OldVpb == Vpb) { continue; } + + // + // Check for a match: + // + // Serial Number, VolumeLabel and Bpb must all be the same. + // Also the volume must have failed a verify before (ie. + // VolumeNotMounted), and it must be in the same physical + // drive than it was mounted in before. + // + + if ( (OldVpb->SerialNumber == Vpb->SerialNumber) && + (OldVcb->VcbCondition == VcbNotMounted) && + (OldVpb->RealDevice == RealDevice) && + (OldVpb->VolumeLabelLength == Vpb->VolumeLabelLength) && + (RtlEqualMemory(&OldVpb->VolumeLabel[0], + &Vpb->VolumeLabel[0], + Vpb->VolumeLabelLength)) && + (RtlEqualMemory(&OldVcb->Bpb, + &Vcb->Bpb, + IsBpbFat32(&Vcb->Bpb) ? + sizeof(BIOS_PARAMETER_BLOCK) : + FIELD_OFFSET(BIOS_PARAMETER_BLOCK, + LargeSectorsPerFat) ))) { + + DoARemount = TRUE; + + break; + } + } + + if ( DoARemount ) { + + PVPB *IrpVpb; + + DebugTrace(0, Dbg, "Doing a remount\n", 0); + DebugTrace(0, Dbg, "Vcb = %08lx\n", Vcb); + DebugTrace(0, Dbg, "Vpb = %08lx\n", Vpb); + DebugTrace(0, Dbg, "OldVcb = %08lx\n", OldVcb); + DebugTrace(0, Dbg, "OldVpb = %08lx\n", OldVpb); + + // + // Swap target device objects between the VCBs. That way + // the old VCB will start using the new target device object, + // and the new VCB will be torn down and deference the old + // target device object. + // + + Vcb->TargetDeviceObject = OldVcb->TargetDeviceObject; + OldVcb->TargetDeviceObject = TargetDeviceObject; + + // + // This is a remount, so link the old vpb in place + // of the new vpb. + + ASSERT( !FlagOn( OldVcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ) ); + + OldVpb->RealDevice = Vpb->RealDevice; + OldVpb->RealDevice->Vpb = OldVpb; + + OldVcb->VcbCondition = VcbGood; + + // + // Use the new changecount. + // + + OldVcb->ChangeCount = Vcb->ChangeCount; + + // + // If the new VPB is the VPB referenced in the original Irp, set + // that reference back to the old VPB. + // + + IrpVpb = &IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.MountVolume.Vpb; + + if (*IrpVpb == Vpb) { + + *IrpVpb = OldVpb; + } + + // + // We do not want to touch this VPB again. It will get cleaned up when + // the new VCB is cleaned up. + // + + ASSERT( Vcb->Vpb == Vpb ); + + Vpb = NULL; + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ); + FatSetVcbCondition( Vcb, VcbBad ); + + // + // Reinitialize the volume file cache and allocation support. + // + + { + CC_FILE_SIZES FileSizes; + + FileSizes.AllocationSize.QuadPart = + FileSizes.FileSize.QuadPart = ( 0x40000 + 0x1000 ); + FileSizes.ValidDataLength = FatMaxLarge; + + DebugTrace(0, Dbg, "Truncate and reinitialize the volume file\n", 0); + + CcInitializeCacheMap( OldVcb->VirtualVolumeFile, + &FileSizes, + TRUE, + &FatData.CacheManagerNoOpCallbacks, + Vcb ); + + // + // Redo the allocation support + // + + FatSetupAllocationSupport( IrpContext, OldVcb ); + + // + // Get the state of the dirty bit. + // + + FatCheckDirtyBit( IrpContext, OldVcb ); + + // + // Check for write protected media. + // + + if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) { + + SetFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + + } else { + + ClearFlag( OldVcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + } + } + + // + // Complete the request and return to our caller + // + + try_return( Status = STATUS_SUCCESS ); + } + + DebugTrace(0, Dbg, "Mount a new volume\n", 0); + + // + // This is a new mount + // + // Create a blank ea data file fcb, just not for Fat32. + // + + if (!FatIsFat32(Vcb)) { + + DIRENT TempDirent; + PFCB EaFcb; + + RtlZeroMemory( &TempDirent, sizeof(DIRENT) ); + RtlCopyMemory( &TempDirent.FileName[0], "EA DATA SF", 11 ); + + EaFcb = FatCreateFcb( IrpContext, + Vcb, + Vcb->RootDcb, + 0, + 0, + &TempDirent, + NULL, + FALSE, + TRUE ); + + // + // Deny anybody who trys to open the file. + // + + SetFlag( EaFcb->FcbState, FCB_STATE_SYSTEM_FILE ); + + Vcb->EaFcb = EaFcb; + } + + // + // Get the state of the dirty bit. + // + + FatCheckDirtyBit( IrpContext, Vcb ); + + // + // Check for write protected media. + // + + if (FatIsMediaWriteProtected(IrpContext, TargetDeviceObject)) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + + } else { + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + } + + // + // Lock volume in drive if we just mounted the boot drive. + // + + if (FlagOn(RealDevice->Flags, DO_SYSTEM_BOOT_PARTITION)) { + + SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE); + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); + } + } + + // + // Indicate to our termination handler that we have mounted + // a new volume. + // + + MountNewVolume = TRUE; + + // + // Complete the request + // + + Status = STATUS_SUCCESS; + + // + // Ref the root dir stream object so we can send mount notification. + // + + RootDirectoryFile = Vcb->RootDcb->Specific.Dcb.DirectoryFile; + ObReferenceObject( RootDirectoryFile ); + + // + // Remove the extra reference to this target DO made on behalf of us + // by the IO system. In the remount case, we permit regular Vcb + // deletion to do this work. + // + + ObDereferenceObject( TargetDeviceObject ); + + + try_exit: NOTHING; + + } _SEH2_FINALLY { + + DebugUnwind( FatMountVolume ); + + FatUnpinBcb( IrpContext, BootBcb ); + FatUnpinBcb( IrpContext, DirentBcb ); + + // + // Check if a volume was mounted. If not then we need to + // mark the Vpb not mounted again. + // + + if ( !MountNewVolume ) { + + if ( Vcb != NULL ) { + + // + // A VCB was created and initialized. we need to try to tear it down. + // + + FatCheckForDismount( IrpContext, + Vcb, + TRUE ); + + IrpContext->Vcb = NULL; + } else if ( VolDo != NULL ) { + // + // The VCB was never initialized, so we need to delete the + // device right here. + // + + IoDeleteDevice( &VolDo->DeviceObject ); + } + + // + // see if a remount failed. + // + + if (DoARemount && _SEH2_AbnormalTermination()) { + + // + // The remount failed. Try to tear down the old VCB as well. + // + + FatCheckForDismount( IrpContext, + OldVcb, + TRUE ); + } + } + + if ( WeClearedVerifyRequiredBit == TRUE ) { + + SetFlag(RealDevice->Flags, DO_VERIFY_VOLUME); + } + + FatReleaseGlobal( IrpContext ); + + DebugTrace(-1, Dbg, "FatMountVolume -> %08lx\n", Status); + } _SEH2_END; + + // + // Now send mount notification. Note that since this is outside of any + // synchronization since the synchronous delivery of this may go to + // folks that provoke re-entrance to the FS. + // + + if (RootDirectoryFile != NULL) { + + FsRtlNotifyVolumeEvent( RootDirectoryFile, FSRTL_VOLUME_MOUNT ); + ObDereferenceObject( RootDirectoryFile ); + } + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatVerifyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the verify volume operation by checking the volume + label and serial number physically on the media with the the Vcb + currently claiming to have the volume mounted. It is responsible for + either completing or enqueuing the input Irp. + + Regardless of whether the verify operation succeeds, the following + operations are performed: + + - Set Vcb->VirtualEaFile back to its virgin state. + - Purge all cached data (flushing first if verify succeeds) + - Mark all Fcbs as needing verification + + If the volumes verifies correctly we also must: + + - Check the volume dirty bit. + - Reinitialize the allocation support + - Flush any dirty data + + If the volume verify fails, it may never be mounted again. If it is + mounted again, it will happen as a remount operation. In preparation + for that, and to leave the volume in a state that can be "lazy deleted" + the following operations are performed: + + - Set the Vcb condition to VcbNotMounted + - Uninitialize the volume file cachemap + - Tear down the allocation support + + In the case of an abnormal termination we haven't determined the state + of the volume, so we set the Device Object as needing verification again. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - If the verify operation completes, it will return either + STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. If an IO or + other error is encountered, that status will be returned. + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + + PIO_STACK_LOCATION IrpSp; + + PDIRENT RootDirectory = NULL; + PPACKED_BOOT_SECTOR BootSector = NULL; + + BIOS_PARAMETER_BLOCK Bpb; + + PVOLUME_DEVICE_OBJECT VolDo; + PVCB Vcb; + PVPB Vpb; + + ULONG SectorSize; + BOOLEAN ClearVerify = FALSE; + BOOLEAN ReleaseEntireVolume = FALSE; + BOOLEAN VerifyAlreadyDone = FALSE; + + DISK_GEOMETRY DiskGeometry; + + LBO RootDirectoryLbo; + ULONG RootDirectorySize; + BOOLEAN LabelFound; + + ULONG ChangeCount = 0; + IO_STATUS_BLOCK Iosb; + + // + // Get the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatVerifyVolume\n", 0); + DebugTrace( 0, Dbg, "DeviceObject = %08lx\n", IrpSp->Parameters.VerifyVolume.DeviceObject); + DebugTrace( 0, Dbg, "Vpb = %08lx\n", IrpSp->Parameters.VerifyVolume.Vpb); + + // + // Save some references to make our life a little easier. Note the Vcb for the purposes + // of exception handling. + // + + VolDo = (PVOLUME_DEVICE_OBJECT)IrpSp->Parameters.VerifyVolume.DeviceObject; + + Vpb = IrpSp->Parameters.VerifyVolume.Vpb; + IrpContext->Vcb = Vcb = &VolDo->Vcb; + + // + // If we cannot wait then enqueue the irp to the fsp and + // return the status to our caller. + // + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) { + + DebugTrace(0, Dbg, "Cannot wait for verify.\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status ); + return Status; + } + + // + // We are serialized at this point allowing only one thread to + // actually perform the verify operation. Any others will just + // wait and then no-op when checking if the volume still needs + // verification. + // + + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + (VOID)FatAcquireExclusiveVcb( IrpContext, Vcb ); + + _SEH2_TRY { + + BOOLEAN AllowRawMount = BooleanFlagOn( IrpSp->Flags, SL_ALLOW_RAW_MOUNT ); + + // + // Mark ourselves as verifying this volume so that recursive I/Os + // will be able to complete. + // + + ASSERT( Vcb->VerifyThread == NULL ); + Vcb->VerifyThread = KeGetCurrentThread(); + + // + // Check if the real device still needs to be verified. If it doesn't + // then obviously someone beat us here and already did the work + // so complete the verify irp with success. Otherwise reenable + // the real device and get to work. + // + + if (!FlagOn(Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) { + + DebugTrace(0, Dbg, "RealDevice has already been verified\n", 0); + + VerifyAlreadyDone = TRUE; + try_return( Status = STATUS_SUCCESS ); + } + + // + // Ping the volume with a partition query to make Jeff happy. + // + + { + PARTITION_INFORMATION_EX PartitionInformation; + + (VOID) FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_GET_PARTITION_INFO_EX, + Vcb->TargetDeviceObject, + &PartitionInformation, + sizeof(PARTITION_INFORMATION_EX), + FALSE, + TRUE, + &Iosb ); + } + + // + // Verify that there is a disk here and pick up the change count. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_CHECK_VERIFY, + Vcb->TargetDeviceObject, + &ChangeCount, + sizeof(ULONG), + FALSE, + TRUE, + &Iosb ); + + if (!NT_SUCCESS( Status )) { + + // + // If we will allow a raw mount then return WRONG_VOLUME to + // allow the volume to be mounted by raw. + // + + if (AllowRawMount) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + if (Iosb.Information != sizeof(ULONG)) { + + // + // Be safe about the count in case the driver didn't fill it in + // + + ChangeCount = 0; + } + + // + // Whatever happens we will have verified this volume at this change + // count, so record that fact. + // + + Vcb->ChangeCount = ChangeCount; + + // + // If this is a CD class device, then check to see if there is a + // 'data track' or not. This is to avoid issuing paging reads which will + // fail later in the mount process (e.g. CD-DA or blank CD media) + // + + if ((Vcb->TargetDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) && + !FatScanForDataTrack( IrpContext, Vcb->TargetDeviceObject)) { + + try_return( Status = STATUS_WRONG_VOLUME); + } + + // + // Some devices can change sector sizes on the fly. Obviously, it + // isn't the same volume if that happens. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + Vcb->TargetDeviceObject, + &DiskGeometry, + sizeof( DISK_GEOMETRY ), + FALSE, + TRUE, + NULL ); + + if (!NT_SUCCESS( Status )) { + + // + // If we will allow a raw mount then return WRONG_VOLUME to + // allow the volume to be mounted by raw. + // + + if (AllowRawMount) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // Read in the boot sector + // + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + if (SectorSize != DiskGeometry.BytesPerSector) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + BootSector = FsRtlAllocatePoolWithTag(NonPagedPoolCacheAligned, + (ULONG) ROUND_TO_PAGES( SectorSize ), + TAG_VERIFY_BOOTSECTOR); + + // + // If this verify is on behalf of a DASD open, allow a RAW mount. + // + + if (!FatPerformVerifyDiskRead( IrpContext, + Vcb, + BootSector, + 0, + SectorSize, + AllowRawMount )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // Call a routine to check the boot sector to see if it is fat. + // If it is not fat then mark the vcb as not mounted tell our + // caller its the wrong volume + // + + if (!FatIsBootSectorFat( BootSector )) { + + DebugTrace(0, Dbg, "Not a Fat Volume\n", 0); + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // This is a fat volume, so extract serial number and see if it is + // ours. + // + + { + ULONG SerialNumber; + + if (IsBpbFat32(&BootSector->PackedBpb)) { + CopyUchar4( &SerialNumber, ((PPACKED_BOOT_SECTOR_EX)BootSector)->Id ); + } else { + CopyUchar4( &SerialNumber, BootSector->Id ); + } + + if (SerialNumber != Vpb->SerialNumber) { + + DebugTrace(0, Dbg, "Not our serial number\n", 0); + + try_return( Status = STATUS_WRONG_VOLUME ); + } + } + + // + // Make sure the Bpbs are not different. We have to zero out our + // stack version of the Bpb since unpacking leaves holes. + // + + RtlZeroMemory( &Bpb, sizeof(BIOS_PARAMETER_BLOCK) ); + + FatUnpackBios( &Bpb, &BootSector->PackedBpb ); + if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; } + + if ( !RtlEqualMemory( &Bpb, + &Vcb->Bpb, + IsBpbFat32(&Bpb) ? + sizeof(BIOS_PARAMETER_BLOCK) : + FIELD_OFFSET(BIOS_PARAMETER_BLOCK, + LargeSectorsPerFat) )) { + + DebugTrace(0, Dbg, "Bpb is different\n", 0); + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + // + // Check the volume label. We do this by trying to locate the + // volume label, making two strings one for the saved volume label + // and the other for the new volume label and then we compare the + // two labels. + // + + if (FatRootDirectorySize(&Bpb) > 0) { + + RootDirectorySize = FatRootDirectorySize(&Bpb); + + } else { + + RootDirectorySize = FatBytesPerCluster(&Bpb); + } + + RootDirectory = FsRtlAllocatePoolWithTag( NonPagedPoolCacheAligned, + (ULONG) ROUND_TO_PAGES( RootDirectorySize ), + TAG_VERIFY_ROOTDIR); + + if (!IsBpbFat32(&BootSector->PackedBpb)) { + + // + // The Fat12/16 case is simple -- read the root directory in and + // search it. + // + + RootDirectoryLbo = FatRootDirectoryLbo(&Bpb); + + if (!FatPerformVerifyDiskRead( IrpContext, + Vcb, + RootDirectory, + RootDirectoryLbo, + RootDirectorySize, + AllowRawMount )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + Status = FatSearchBufferForLabel(IrpContext, Vpb, + RootDirectory, RootDirectorySize, + &LabelFound); + + if (!NT_SUCCESS(Status)) { + + try_return( Status ); + } + + if (!LabelFound && Vpb->VolumeLabelLength > 0) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + } else { + + ULONG RootDirectoryCluster; + + RootDirectoryCluster = Bpb.RootDirFirstCluster; + + while (RootDirectoryCluster != FAT_CLUSTER_LAST) { + + RootDirectoryLbo = FatGetLboFromIndex(Vcb, RootDirectoryCluster); + + if (!FatPerformVerifyDiskRead( IrpContext, + Vcb, + RootDirectory, + RootDirectoryLbo, + RootDirectorySize, + AllowRawMount )) { + + try_return( Status = STATUS_WRONG_VOLUME ); + } + + Status = FatSearchBufferForLabel(IrpContext, Vpb, + RootDirectory, RootDirectorySize, + &LabelFound); + + if (!NT_SUCCESS(Status)) { + + try_return( Status ); + } + + if (LabelFound) { + + // + // Found a matching label. + // + + break; + } + + // + // Set ourselves up for the next loop iteration. + // + + FatVerifyLookupFatEntry( IrpContext, Vcb, + RootDirectoryCluster, + &RootDirectoryCluster ); + + switch (FatInterpretClusterType(Vcb, RootDirectoryCluster)) { + + case FatClusterAvailable: + case FatClusterReserved: + case FatClusterBad: + + // + // Bail all the way out if we have a bad root. + // + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + break; + + default: + + break; + } + + } + + if (RootDirectoryCluster == FAT_CLUSTER_LAST && + Vpb->VolumeLabelLength > 0) { + + // + // Should have found a label, didn't find any. + // + + try_return( Status = STATUS_WRONG_VOLUME ); + } + } + + + try_exit: NOTHING; + + // + // Note that we have previously acquired the Vcb to serialize + // the EA file stuff the marking all the Fcbs as NeedToBeVerified. + // + // Put the Ea file back in a virgin state. + // + + FatCloseEaFile( IrpContext, Vcb, (BOOLEAN)(Status == STATUS_SUCCESS) ); + + // + // Mark all Fcbs as needing verification, but only if we really have + // to do it. + // + + if (!VerifyAlreadyDone) { + + FatMarkFcbCondition( IrpContext, Vcb->RootDcb, FcbNeedsToBeVerified, TRUE ); + } + + // + // If the verify didn't succeed, get the volume ready for a + // remount or eventual deletion. + // + + if (Vcb->VcbCondition == VcbNotMounted) { + + // + // If the volume was already in an unmounted state, just bail + // and make sure we return STATUS_WRONG_VOLUME. + // + + Status = STATUS_WRONG_VOLUME; + + } else if ( Status == STATUS_WRONG_VOLUME ) { + + // + // Grab everything so we can safely transition the volume state without + // having a thread stumble into the torn-down allocation engine. + // + + FatAcquireExclusiveVolume( IrpContext, Vcb ); + ReleaseEntireVolume = TRUE; + + // + // Get rid of any cached data, without flushing + // + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, NoFlush ); + + // + // Uninitialize the volume file cache map. Note that we cannot + // do a "FatSyncUninit" because of deadlock problems. However, + // since this FileObject is referenced by us, and thus included + // in the Vpb residual count, it is OK to do a normal CcUninit. + // + + CcUninitializeCacheMap( Vcb->VirtualVolumeFile, + &FatLargeZero, + NULL ); + + FatTearDownAllocationSupport( IrpContext, Vcb ); + + Vcb->VcbCondition = VcbNotMounted; + + ClearVerify = TRUE; + + } else if (!VerifyAlreadyDone) { + + // + // Grab everything so we can safely transition the volume state without + // having a thread stumble into the torn-down allocation engine. + // + + FatAcquireExclusiveVolume( IrpContext, Vcb ); + ReleaseEntireVolume = TRUE; + + // + // Get rid of any cached data, flushing first. + // + // Future work (and for bonus points, around the other flush points) + // could address the possibility that the dirent filesize hasn't been + // updated yet, causing us to fail the re-verification of a file in + // DetermineAndMark. This is pretty subtle and very very uncommon. + // + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush ); + + // + // Flush and Purge the volume file. + // + + (VOID)FatFlushFat( IrpContext, Vcb ); + CcPurgeCacheSection( &Vcb->SectionObjectPointers, NULL, 0, FALSE ); + + // + // Redo the allocation support with newly paged stuff. + // + + FatTearDownAllocationSupport( IrpContext, Vcb ); + FatSetupAllocationSupport( IrpContext, Vcb ); + + FatCheckDirtyBit( IrpContext, Vcb ); + + // + // Check for write protected media. + // + + if (FatIsMediaWriteProtected(IrpContext, Vcb->TargetDeviceObject)) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + + } else { + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED ); + } + + ClearVerify = TRUE; + } + + if (ClearVerify) { + + // + // Mark the device as no longer needing verification. + // + + ClearFlag( Vpb->RealDevice->Flags, DO_VERIFY_VOLUME ); + } + + } _SEH2_FINALLY { + + DebugUnwind( FatVerifyVolume ); + + // + // Free any buffer we may have allocated + // + + if ( BootSector != NULL ) { ExFreePool( BootSector ); } + if ( RootDirectory != NULL ) { ExFreePool( RootDirectory ); } + + // + // Show that we are done with this volume. + // + + ASSERT( Vcb->VerifyThread == KeGetCurrentThread() ); + Vcb->VerifyThread = NULL; + + if (ReleaseEntireVolume) { + + FatReleaseVolume( IrpContext, Vcb ); + } + + FatReleaseVcb( IrpContext, Vcb ); + FatReleaseGlobal( IrpContext ); + + // + // If this was not an abnormal termination, complete the irp. + // + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatVerifyVolume -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + + +// +// Local Support Routine +// + +BOOLEAN +FatIsBootSectorFat ( + IN PPACKED_BOOT_SECTOR BootSector + ) + +/*++ + +Routine Description: + + This routine checks if the boot sector is for a fat file volume. + +Arguments: + + BootSector - Supplies the packed boot sector to check + +Return Value: + + BOOLEAN - TRUE if the volume is Fat and FALSE otherwise. + +--*/ + +{ + BOOLEAN Result; + BIOS_PARAMETER_BLOCK Bpb; + + DebugTrace(+1, Dbg, "FatIsBootSectorFat, BootSector = %08lx\n", BootSector); + + // + // The result is true unless we decide that it should be false + // + + Result = TRUE; + + // + // Unpack the bios and then test everything + // + + FatUnpackBios( &Bpb, &BootSector->PackedBpb ); + if (Bpb.Sectors != 0) { Bpb.LargeSectors = 0; } + + if ((BootSector->Jump[0] != 0xe9) && + (BootSector->Jump[0] != 0xeb) && + (BootSector->Jump[0] != 0x49)) { + + Result = FALSE; + + // + // Enforce some sanity on the sector size (easy check) + // + + } else if ((Bpb.BytesPerSector != 128) && + (Bpb.BytesPerSector != 256) && + (Bpb.BytesPerSector != 512) && + (Bpb.BytesPerSector != 1024) && + (Bpb.BytesPerSector != 2048) && + (Bpb.BytesPerSector != 4096)) { + + Result = FALSE; + + // + // Likewise on the clustering. + // + + } else if ((Bpb.SectorsPerCluster != 1) && + (Bpb.SectorsPerCluster != 2) && + (Bpb.SectorsPerCluster != 4) && + (Bpb.SectorsPerCluster != 8) && + (Bpb.SectorsPerCluster != 16) && + (Bpb.SectorsPerCluster != 32) && + (Bpb.SectorsPerCluster != 64) && + (Bpb.SectorsPerCluster != 128)) { + + Result = FALSE; + + // + // Likewise on the reserved sectors (must reflect at least the boot sector!) + // + + } else if (Bpb.ReservedSectors == 0) { + + Result = FALSE; + + // + // No FATs? Wrong ... + // + + } else if (Bpb.Fats == 0) { + + Result = FALSE; + + // + // Prior to DOS 3.2 might contains value in both of Sectors and + // Sectors Large. + // + + } else if ((Bpb.Sectors == 0) && (Bpb.LargeSectors == 0)) { + + Result = FALSE; + + // + // Check that FAT32 (SectorsPerFat == 0) claims some FAT space and + // is of a version we recognize, currently Version 0.0. + // + + } else if (Bpb.SectorsPerFat == 0 && ( Bpb.LargeSectorsPerFat == 0 || + Bpb.FsVersion != 0 )) { + + Result = FALSE; + + } else if ((Bpb.Media != 0xf0) && + (Bpb.Media != 0xf8) && + (Bpb.Media != 0xf9) && + (Bpb.Media != 0xfb) && + (Bpb.Media != 0xfc) && + (Bpb.Media != 0xfd) && + (Bpb.Media != 0xfe) && + (Bpb.Media != 0xff) && + (!FatData.FujitsuFMR || ((Bpb.Media != 0x00) && + (Bpb.Media != 0x01) && + (Bpb.Media != 0xfa)))) { + + Result = FALSE; + + // + // If this isn't FAT32, then there better be a claimed root directory + // size here ... + // + + } else if (Bpb.SectorsPerFat != 0 && Bpb.RootEntries == 0) { + + Result = FALSE; + + // + // If this is FAT32 (i.e., extended BPB), look for and refuse to mount + // mirror-disabled volumes. If we did, we would need to only write to + // the FAT# indicated in the ActiveFat field. The only user of this is + // the FAT->FAT32 converter after the first pass of protected mode work + // (booting into realmode) and NT should absolutely not be attempting + // to mount such an in-transition volume. + // + + } else if (Bpb.SectorsPerFat == 0 && Bpb.MirrorDisabled) { + + Result = FALSE; + } + + DebugTrace(-1, Dbg, "FatIsBootSectorFat -> %08lx\n", Result); + + return Result; +} + + +// +// Local Support Routine +// + +BOOLEAN +FatIsMediaWriteProtected ( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject + ) + +/*++ + +Routine Description: + + This routine determines if the target media is write protected. + +Arguments: + + TargetDeviceObject - The target of the query + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + IO_STATUS_BLOCK Iosb; + + // + // Query the partition table + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // See if the media is write protected. On success or any kind + // of error (possibly illegal device function), assume it is + // writeable, and only complain if he tells us he is write protected. + // + + Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE, + TargetDeviceObject, + NULL, + 0, + NULL, + 0, + FALSE, + &Event, + &Iosb ); + + // + // Just return FALSE in the unlikely event we couldn't allocate an Irp. + // + + if ( Irp == NULL ) { + + return FALSE; + } + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + Status = IoCallDriver( TargetDeviceObject, Irp ); + + if ( Status == STATUS_PENDING ) { + + (VOID) KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = Iosb.Status; + } + + return (BOOLEAN)(Status == STATUS_MEDIA_WRITE_PROTECTED); +} + + +// +// Local Support Routine +// + +NTSTATUS +FatUserFsCtrl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for implementing the user's requests made + through NtFsControlFile. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + ULONG FsControlCode; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Save some references to make our life a little easier + // + + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + DebugTrace(+1, Dbg,"FatUserFsCtrl...\n", 0); + DebugTrace( 0, Dbg,"FsControlCode = %08lx\n", FsControlCode); + + // + // Some of these Fs Controls use METHOD_NEITHER buffering. If the previous mode + // of the caller was userspace and this is a METHOD_NEITHER, we have the choice + // of realy buffering the request through so we can possibly post, or making the + // request synchronous. Since the former was not done by design, do the latter. + // + + if (Irp->RequestorMode != KernelMode && (FsControlCode & 3) == METHOD_NEITHER) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + // + // Case on the control code. + // + + switch ( FsControlCode ) { + + case FSCTL_REQUEST_OPLOCK_LEVEL_1: + case FSCTL_REQUEST_OPLOCK_LEVEL_2: + case FSCTL_REQUEST_BATCH_OPLOCK: + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: + case FSCTL_OPBATCH_ACK_CLOSE_PENDING: + case FSCTL_OPLOCK_BREAK_NOTIFY: + case FSCTL_OPLOCK_BREAK_ACK_NO_2: + case FSCTL_REQUEST_FILTER_OPLOCK : + + Status = FatOplockRequest( IrpContext, Irp ); + break; + + case FSCTL_LOCK_VOLUME: + + Status = FatLockVolume( IrpContext, Irp ); + break; + + case FSCTL_UNLOCK_VOLUME: + + Status = FatUnlockVolume( IrpContext, Irp ); + break; + + case FSCTL_DISMOUNT_VOLUME: + + Status = FatDismountVolume( IrpContext, Irp ); + break; + + case FSCTL_MARK_VOLUME_DIRTY: + + Status = FatDirtyVolume( IrpContext, Irp ); + break; + + case FSCTL_IS_VOLUME_DIRTY: + + Status = FatIsVolumeDirty( IrpContext, Irp ); + break; + + case FSCTL_IS_VOLUME_MOUNTED: + + Status = FatIsVolumeMounted( IrpContext, Irp ); + break; + + case FSCTL_IS_PATHNAME_VALID: + Status = FatIsPathnameValid( IrpContext, Irp ); + break; + + case FSCTL_QUERY_RETRIEVAL_POINTERS: + Status = FatQueryRetrievalPointers( IrpContext, Irp ); + break; + + case FSCTL_QUERY_FAT_BPB: + Status = FatQueryBpb( IrpContext, Irp ); + break; + + case FSCTL_FILESYSTEM_GET_STATISTICS: + Status = FatGetStatistics( IrpContext, Irp ); + break; + + case FSCTL_GET_VOLUME_BITMAP: + Status = FatGetVolumeBitmap( IrpContext, Irp ); + break; + + case FSCTL_GET_RETRIEVAL_POINTERS: + Status = FatGetRetrievalPointers( IrpContext, Irp ); + break; + + case FSCTL_MOVE_FILE: + Status = FatMoveFile( IrpContext, Irp ); + break; + + case FSCTL_ALLOW_EXTENDED_DASD_IO: + Status = FatAllowExtendedDasdIo( IrpContext, Irp ); + break; + + default : + + DebugTrace(0, Dbg, "Invalid control code -> %08lx\n", FsControlCode ); + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + Status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + DebugTrace(-1, Dbg, "FatUserFsCtrl -> %08lx\n", Status ); + return Status; +} + + + +// +// Local support routine +// + +NTSTATUS +FatOplockRequest ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine to handle oplock requests made via the + NtFsControlFile call. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + ULONG FsControlCode; + PFCB Fcb; + PVCB Vcb; + PCCB Ccb; + + ULONG OplockCount = 0; + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + BOOLEAN AcquiredVcb = FALSE; + BOOLEAN AcquiredFcb = FALSE; + + // + // Save some references to make our life a little easier + // + + FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode; + + DebugTrace(+1, Dbg, "FatOplockRequest...\n", 0); + DebugTrace( 0, Dbg, "FsControlCode = %08lx\n", FsControlCode); + + // + // We only permit oplock requests on files. + // + + if ( FatDecodeFileObject( IrpSp->FileObject, + &Vcb, + &Fcb, + &Ccb ) != UserFileOpen ) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + DebugTrace(-1, Dbg, "FatOplockRequest -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + + // + // Make this a waitable Irpcontext so we don't fail to acquire + // the resources. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Use a try finally to free the Fcb/Vcb + // + + _SEH2_TRY { + + // + // Switch on the function control code. We grab the Fcb exclusively + // for oplock requests, shared for oplock break acknowledgement. + // + + switch ( FsControlCode ) { + + case FSCTL_REQUEST_OPLOCK_LEVEL_1: + case FSCTL_REQUEST_OPLOCK_LEVEL_2: + case FSCTL_REQUEST_BATCH_OPLOCK: + case FSCTL_REQUEST_FILTER_OPLOCK : + + FatAcquireSharedVcb( IrpContext, Fcb->Vcb ); + AcquiredVcb = TRUE; + FatAcquireExclusiveFcb( IrpContext, Fcb ); + AcquiredFcb = TRUE; + + if (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) { + + OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( &Fcb->Specific.Fcb.FileLock ); + + } else { + + OplockCount = Fcb->UncleanCount; + } + + break; + + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: + case FSCTL_OPBATCH_ACK_CLOSE_PENDING : + case FSCTL_OPLOCK_BREAK_NOTIFY: + case FSCTL_OPLOCK_BREAK_ACK_NO_2: + + FatAcquireSharedFcb( IrpContext, Fcb ); + AcquiredFcb = TRUE; + break; + + default: + + FatBugCheck( FsControlCode, 0, 0 ); + } + + // + // Call the FsRtl routine to grant/acknowledge oplock. + // + + Status = FsRtlOplockFsctrl( &Fcb->Specific.Fcb.Oplock, + Irp, + OplockCount ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + } _SEH2_FINALLY { + + DebugUnwind( FatOplockRequest ); + + // + // Release all of our resources + // + + if (AcquiredVcb) { + + FatReleaseVcb( IrpContext, Fcb->Vcb ); + } + + if (AcquiredFcb) { + + FatReleaseFcb( IrpContext, Fcb ); + } + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, FatNull, 0 ); + } + + DebugTrace(-1, Dbg, "FatOplockRequest -> %08lx\n", Status ); + } _SEH2_END; + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatLockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the lock volume operation. It is responsible for + either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatLockVolume...\n", 0); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + // + // Send our notification so that folks that like to hold handles on + // volumes can get out of the way. + // + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK ); + + // + // Acquire exclusive access to the Vcb and enqueue the Irp if we + // didn't get access. + // + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status); + return Status; + } + + _SEH2_TRY { + + Status = FatLockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject ); + + } _SEH2_FINALLY { + + // + // Since we drop and release the vcb while trying to punch the volume + // down, it may be the case that we decide the operation should not + // continue if the user raced a CloeseHandle() with us (and it finished + // the cleanup) while we were waiting for our closes to finish. + // + // In this case, we will have been raised out of the acquire logic with + // STATUS_FILE_CLOSED, and the volume will not be held. + // + + if (!_SEH2_AbnormalTermination() || ExIsResourceAcquiredExclusiveLite( &Vcb->Resource )) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + if (!NT_SUCCESS( Status ) || _SEH2_AbnormalTermination()) { + + // + // The volume lock will be failing. + // + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_LOCK_FAILED ); + } + } _SEH2_END; + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatLockVolume -> %08lx\n", Status); + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatUnlockVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the unlock volume operation. It is responsible for + either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatUnlockVolume...\n", 0); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + Status = FatUnlockVolumeInternal( IrpContext, Vcb, IrpSp->FileObject ); + + // + // Send notification that the volume is avaliable. + // + + if (NT_SUCCESS( Status )) { + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_UNLOCK ); + } + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatUnlockVolume -> %08lx\n", Status); + + return Status; +} + + +NTSTATUS +FatLockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ) + +/*++ + +Routine Description: + + This routine performs the actual lock volume operation. It will be called + by anyone wishing to try to protect the volume for a long duration. PNP + operations are such a user. + + The volume must be held exclusive by the caller. + +Arguments: + + Vcb - The volume being locked. + + FileObject - File corresponding to the handle locking the volume. If this + is not specified, a system lock is assumed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + KIRQL SavedIrql; + ULONG RemainingUserReferences = (FileObject? 1: 0); + + ASSERT( ExIsResourceAcquiredExclusiveLite( &Vcb->Resource ) && + !ExIsResourceAcquiredExclusiveLite( &FatData.Resource )); + // + // Go synchronous for the rest of the lock operation. It may be + // reasonable to try to revisit this in the future, but for now + // the purge below expects to be able to wait. + // + // We know it is OK to leave the flag up given how we're used at + // the moment. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // If there are any open handles, this will fail. + // + + if (!FatIsHandleCountZero( IrpContext, Vcb )) { + + return STATUS_ACCESS_DENIED; + } + + // + // Force Mm to get rid of its referenced file objects. + // + + FatFlushFat( IrpContext, Vcb ); + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush ); + + FatCloseEaFile( IrpContext, Vcb, TRUE ); + + // + // Now back out of our synchronization and wait for the lazy writer + // to finish off any lazy closes that could have been outstanding. + // + // Since we flushed, we know that the lazy writer will issue all + // possible lazy closes in the next tick - if we hadn't, an otherwise + // unopened file with a large amount of dirty data could have hung + // around for a while as the data trickled out to the disk. + // + // This is even more important now since we send notification to + // alert other folks that this style of check is about to happen so + // that they can close their handles. We don't want to enter a fast + // race with the lazy writer tearing down his references to the file. + // + + FatReleaseVcb( IrpContext, Vcb ); + + Status = CcWaitForCurrentLazyWriterActivity(); + + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + if (!NT_SUCCESS( Status )) { + + return Status; + } + + // + // Now rundown the delayed closes one last time. We appear to be able + // to have additional collisions. + // + + FatFspClose( Vcb ); + + // + // Check if the Vcb is already locked, or if the open file count + // is greater than 1 (which implies that someone else also is + // currently using the volume, or a file on the volume), and that the + // VPB reference count only includes our residual and the handle (as + // appropriate). + // + // We used to only check for the vpb refcount. This is unreliable since + // the vpb refcount is dropped immediately before final close, meaning + // that even though we had a good refcount, the close was inflight and + // subsequent operations could get confused. Especially if the PNP path + // was the lock caller, we delete the VCB with an outstanding opencount! + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (!FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) && + (Vcb->Vpb->ReferenceCount <= 2 + RemainingUserReferences) && + (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 ))) { + + SetFlag(Vcb->Vpb->Flags, VPB_LOCKED); + SetFlag(Vcb->VcbState, VCB_STATE_FLAG_LOCKED); + Vcb->FileObjectWithVcbLocked = FileObject; + + } else { + + Status = STATUS_ACCESS_DENIED; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + // + // If we successully locked the volume, see if it is clean now. + // + + if (NT_SUCCESS( Status ) && + FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ) && + !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ) && + !CcIsThereDirtyData(Vcb->Vpb)) { + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + } + + ASSERT( !NT_SUCCESS(Status) || (Vcb->OpenFileCount == (CLONG)( FileObject? 1: 0 ))); + + return Status; +} + + +NTSTATUS +FatUnlockVolumeInternal ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_OBJECT FileObject OPTIONAL + ) + +/*++ + +Routine Description: + + This routine performs the actual unlock volume operation. + + The volume must be held exclusive by the caller. + +Arguments: + + Vcb - The volume being locked. + + FileObject - File corresponding to the handle locking the volume. If this + is not specified, a system lock is assumed. + +Return Value: + + NTSTATUS - The return status for the operation + + Attempting to remove a system lock that did not exist is OK. + +--*/ + +{ + KIRQL SavedIrql; + NTSTATUS Status = STATUS_NOT_LOCKED; + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (FlagOn(Vcb->Vpb->Flags, VPB_LOCKED) && FileObject == Vcb->FileObjectWithVcbLocked) { + + // + // This one locked it, unlock the volume + // + + ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_LOCKED ); + Vcb->FileObjectWithVcbLocked = NULL; + + Status = STATUS_SUCCESS; + } + + IoReleaseVpbSpinLock( SavedIrql ); + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatDismountVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the dismount volume operation. It is responsible for + either completing of enqueuing the input Irp. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + NTSTATUS Status; + BOOLEAN VcbHeld = FALSE; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatDismountVolume...\n", 0); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens on media that is not boot/paging and is not + // already dismounted ... (but we need to check that stuff while + // synchronized) + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + Status = STATUS_INVALID_PARAMETER; + goto fn_return; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + // + // Make some unsynchronized checks to see if this operation is possible. + // We will repeat the appropriate ones inside synchronization, but it is + // good to avoid bogus notifications. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE )) { + + Status = STATUS_ACCESS_DENIED; + goto fn_return; + } + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) { + + Status = STATUS_VOLUME_DISMOUNTED; + goto fn_return; + } + + // + // A bit of historical comment is in order. + // + // In all versions prior to NT5, we only permitted dismount if the volume had + // previously been locked. Now we must permit a forced dismount, meaning that + // we grab ahold of the whole kit-n-kaboodle - regardless of activity, open + // handles, etc. - to flush and invalidate the volume. + // + // Previously, dismount assumed that lock had come along earlier and done some + // of the work that we are now going to do - i.e., flush, tear down the eas. All + // we had to do here is flush the device out and kill off as many of the orphan + // fcbs as possible. This now changes. + // + // In fact, everything is a forced dismount now. This changes one interesting + // aspect, which is that it used to be the case that the handle used to dismount + // could come back, read, and induce a verify/remount. This is just not possible + // now. The point of forced dismount is that very shortly someone will come along + // and be destructive to the possibility of using the media further - format, eject, + // etc. By using this path, callers are expected to tolerate the consequences. + // + // Note that the volume can still be successfully unlocked by this handle. + // + + // + // Send notification. + // + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT ); + + // + // Force ourselves to wait and grab everything. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + + _SEH2_TRY { + + // + // Guess what? This can raise if a cleanup on the fileobject we + // got races in ahead of us. + // + + FatAcquireExclusiveVolume( IrpContext, Vcb ); + VcbHeld = TRUE; + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) { + + try_return( Status = STATUS_VOLUME_DISMOUNTED ); + } + + FatFlushAndCleanVolume( IrpContext, Irp, Vcb, FlushAndInvalidate ); + + // + // We defer the physical dismount until this handle is closed, per symmetric + // implemntation in the other FS. This permits a dismounter to issue IOCTL + // through this handle and perform device manipulation without racing with + // creates attempting to mount the volume again. + // + // Raise a flag to tell the cleanup path to complete the dismount. + // + + SetFlag( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT ); + + // + // Indicate that the volume was dismounted so that we may return the + // correct error code when operations are attempted via open handles. + // + + Vcb->VcbCondition = VcbBad; + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED ); + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + } _SEH2_FINALLY { + + if (VcbHeld) { + + FatReleaseVolume( IrpContext, Vcb ); + } + + FatReleaseGlobal( IrpContext ); + + // + // I do not believe it is possible to raise, but for completeness + // notice and send notification of failure. We absolutely + // cannot have raised in CheckForDismount. + // + // We decline to call an attempt to dismount a dismounted volume + // a failure to do so. + // + + if ((!NT_SUCCESS( Status ) && Status != STATUS_VOLUME_DISMOUNTED) + || _SEH2_AbnormalTermination()) { + + FsRtlNotifyVolumeEvent( IrpSp->FileObject, FSRTL_VOLUME_DISMOUNT_FAILED ); + } + } _SEH2_END; + + fn_return: + + FatCompleteRequest( IrpContext, Irp, Status ); + DebugTrace(-1, Dbg, "FatDismountVolume -> %08lx\n", Status); + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatDirtyVolume ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine marks the volume as dirty. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatDirtyVolume...\n", 0); + + // + // Decode the file object, the only type of opens we accept are + // user volume opens. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatDirtyVolume -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + + // + // Disable popups, we will just return any error. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS); + + // + // Verify the Vcb. We want to make sure we don't dirty some + // random chunk of media that happens to be in the drive now. + // + + FatVerifyVcb( IrpContext, Vcb ); + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + + FatMarkVolume( IrpContext, Vcb, VolumeDirty ); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatDirtyVolume -> STATUS_SUCCESS\n", 0); + + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatIsVolumeDirty ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if a volume is currently dirty. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PULONG VolumeState; + + // + // Get the current stack location and extract the output + // buffer information. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Get a pointer to the output buffer. Look at the system buffer field in the + // irp first. Then the Irp Mdl. + // + + if (Irp->AssociatedIrp.SystemBuffer != NULL) { + + VolumeState = Irp->AssociatedIrp.SystemBuffer; + + } else if (Irp->MdlAddress != NULL) { + + VolumeState = MmGetSystemAddressForMdlSafe( Irp->MdlAddress, LowPagePriority ); + + if (VolumeState == NULL) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INSUFFICIENT_RESOURCES ); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_USER_BUFFER ); + return STATUS_INVALID_USER_BUFFER; + } + + // + // Make sure the output buffer is large enough and then initialize + // the answer to be that the volume isn't dirty. + // + + if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + *VolumeState = 0; + + // + // Decode the file object + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + if (TypeOfOpen != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if (Vcb->VcbCondition != VcbGood) { + + FatCompleteRequest( IrpContext, Irp, STATUS_VOLUME_DISMOUNTED ); + return STATUS_VOLUME_DISMOUNTED; + } + + // + // Disable PopUps, we want to return any error. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS); + + // + // Verify the Vcb. We want to make double sure that this volume + // is around so that we know our information is good. + // + + FatVerifyVcb( IrpContext, Vcb ); + + // + // Now set the returned information. We can avoid probing the disk since + // we know our internal state is in sync. + // + + if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY | VCB_STATE_FLAG_MOUNTED_DIRTY) ) { + + SetFlag( *VolumeState, VOLUME_IS_DIRTY ); + } + + Irp->IoStatus.Information = sizeof( ULONG ); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatIsVolumeMounted ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if a volume is currently mounted. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb = NULL; + PFCB Fcb; + PCCB Ccb; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + Status = STATUS_SUCCESS; + + DebugTrace(+1, Dbg, "FatIsVolumeMounted...\n", 0); + + // + // Decode the file object. + // + + (VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + ASSERT( Vcb != NULL ); + + // + // Disable PopUps, we want to return any error. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS); + + // + // Verify the Vcb. + // + + FatVerifyVcb( IrpContext, Vcb ); + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatIsVolumeMounted -> %08lx\n", Status); + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatIsPathnameValid ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine determines if a pathname is a-priori illegal by inspecting + the the characters used. It is required to be correct on a FALSE return. + + N.B.: current implementation is intentioanlly a no-op. This may change + in the future. A careful reader of the previous implementation of this + FSCTL in FAT would discover that it violated the requirement stated above + and could return FALSE for a valid (createable) pathname. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + DebugTrace(+1, Dbg, "FatIsPathnameValid...\n", 0); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatIsPathnameValid -> %08lx\n", STATUS_SUCCESS); + + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatQueryBpb ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine simply returns the first 0x24 bytes of sector 0. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + + PFSCTL_QUERY_FAT_BPB_BUFFER BpbBuffer; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatQueryBpb...\n", 0); + + // + // Get the Vcb. If we didn't keep the information needed for this call, + // we had a reason ... + // + + Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb; + + if (Vcb->First0x24BytesOfBootSector == NULL) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST ); + return STATUS_INVALID_DEVICE_REQUEST; + } + + // + // Extract the buffer + // + + BpbBuffer = (PFSCTL_QUERY_FAT_BPB_BUFFER)Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure the buffer is big enough. + // + + if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < 0x24) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Fill in the output buffer + // + + RtlCopyMemory( BpbBuffer->First0x24BytesOfBootSector, + Vcb->First0x24BytesOfBootSector, + 0x24 ); + + Irp->IoStatus.Information = 0x24; + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + DebugTrace(-1, Dbg, "FatQueryBpb -> %08lx\n", STATUS_SUCCESS); + return STATUS_SUCCESS; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatInvalidateVolumes ( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine searches for all the volumes mounted on the same real device + of the current DASD handle, and marks them all bad. The only operation + that can be done on such handles is cleanup and close. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + IRP_CONTEXT IrpContext; + PIO_STACK_LOCATION IrpSp; + + LUID TcbPrivilege = {SE_TCB_PRIVILEGE, 0}; + + HANDLE Handle; + + PLIST_ENTRY Links; + + PFILE_OBJECT FileToMarkBad; + PDEVICE_OBJECT DeviceToMarkBad; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatInvalidateVolumes...\n", 0); + + // + // Check for the correct security access. + // The caller must have the SeTcbPrivilege. + // + + if (!SeSinglePrivilegeCheck(TcbPrivilege, Irp->RequestorMode)) { + + FatCompleteRequest( FatNull, Irp, STATUS_PRIVILEGE_NOT_HELD ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_PRIVILEGE_NOT_HELD); + return STATUS_PRIVILEGE_NOT_HELD; + } + + // + // Try to get a pointer to the device object from the handle passed in. + // + +#if defined(_WIN64) + if (IoIs32bitProcess( Irp )) { + + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(UINT32)) { + + FatCompleteRequest( FatNull, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + Handle = (HANDLE) LongToHandle( (*(PUINT32)Irp->AssociatedIrp.SystemBuffer) ); + } else { +#endif + if (IrpSp->Parameters.FileSystemControl.InputBufferLength != sizeof(HANDLE)) { + + FatCompleteRequest( FatNull, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + Handle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; +#if defined(_WIN64) + } +#endif + + + Status = ObReferenceObjectByHandle( Handle, + 0, + *IoFileObjectType, + KernelMode, +#ifndef __REACTOS__ + &FileToMarkBad, +#else + (PVOID *)&FileToMarkBad, +#endif + NULL ); + + if (!NT_SUCCESS(Status)) { + + FatCompleteRequest( FatNull, Irp, Status ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> %08lx\n", Status); + return Status; + + } else { + + // + // We only needed the pointer, not a reference. + // + + ObDereferenceObject( FileToMarkBad ); + + // + // Grab the DeviceObject from the FileObject. + // + + DeviceToMarkBad = FileToMarkBad->DeviceObject; + } + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT) ); + + SetFlag( IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT ); + IrpContext.MajorFunction = IrpSp->MajorFunction; + IrpContext.MinorFunction = IrpSp->MinorFunction; + + FatAcquireExclusiveGlobal( &IrpContext ); + + // + // First acquire the FatData resource shared, then walk through all the + // mounted VCBs looking for candidates to mark BAD. + // + // On volumes we mark bad, check for dismount possibility (which is + // why we have to get the next link early). + // + + Links = FatData.VcbQueue.Flink; + + while (Links != &FatData.VcbQueue) { + + PVCB ExistingVcb; + + ExistingVcb = CONTAINING_RECORD(Links, VCB, VcbLinks); + + Links = Links->Flink; + + // + // If we get a match, mark the volume Bad, and also check to + // see if the volume should go away. + // + + if (ExistingVcb->Vpb->RealDevice == DeviceToMarkBad) { + + BOOLEAN VcbDeleted; + + VcbDeleted = FALSE; + + // + // Here we acquire the Vcb exclusive and try to purge + // all the open files. The idea is to have as little as + // possible stale data visible and to hasten the volume + // going away. + // + + (VOID)FatAcquireExclusiveVcb( &IrpContext, ExistingVcb ); + + if (ExistingVcb->Vpb == DeviceToMarkBad->Vpb) { + + KIRQL OldIrql; + + IoAcquireVpbSpinLock( &OldIrql ); + + if (FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_MOUNTED )) { + + PVPB NewVpb; + + NewVpb = ExistingVcb->SwapVpb; + ExistingVcb->SwapVpb = NULL; + SetFlag( ExistingVcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ); + + RtlZeroMemory( NewVpb, sizeof( VPB ) ); + NewVpb->Type = IO_TYPE_VPB; + NewVpb->Size = sizeof( VPB ); + NewVpb->RealDevice = DeviceToMarkBad; + NewVpb->Flags = FlagOn( DeviceToMarkBad->Vpb->Flags, VPB_REMOVE_PENDING ); + + DeviceToMarkBad->Vpb = NewVpb; + } + + ASSERT( DeviceToMarkBad->Vpb->DeviceObject == NULL ); + + IoReleaseVpbSpinLock( OldIrql ); + } + + FatSetVcbCondition( ExistingVcb, VcbBad ); + + // + // Process the root directory, if it is present. + // + + if (ExistingVcb->RootDcb != NULL) { + + FatMarkFcbCondition( &IrpContext, ExistingVcb->RootDcb, FcbBad, TRUE ); + + // + // Purging the file objects on this volume could result in the memory manager + // dereferencing it's file pointer which could be the last reference and + // trigger object deletion and VCB deletion. Protect against that here by + // temporarily biasing the file count, and later checking for dismount. + // + + ExistingVcb->OpenFileCount += 1; + + FatPurgeReferencedFileObjects( &IrpContext, + ExistingVcb->RootDcb, + NoFlush ); + + ExistingVcb->OpenFileCount -= 1; + + VcbDeleted = FatCheckForDismount( &IrpContext, ExistingVcb, FALSE ); + } + + // + // Drop the resource. + // + + if (VcbDeleted != TRUE) { + FatReleaseVcb( &IrpContext, ExistingVcb ); + } + } + } + + FatReleaseGlobal( &IrpContext ); + + FatCompleteRequest( FatNull, Irp, STATUS_SUCCESS ); + + DebugTrace(-1, Dbg, "FatInvalidateVolumes -> STATUS_SUCCESS\n", 0); + + return STATUS_SUCCESS; +} + + +// +// Local Support routine +// + +BOOLEAN +FatPerformVerifyDiskRead ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PVOID Buffer, + IN LBO Lbo, + IN ULONG NumberOfBytesToRead, + IN BOOLEAN ReturnOnError + ) + +/*++ + +Routine Description: + + This routine is used to read in a range of bytes from the disk. It + bypasses all of the caching and regular I/O logic, and builds and issues + the requests itself. It does this operation overriding the verify + volume flag in the device object. + +Arguments: + + Vcb - Supplies the target device object for this operation. + + Buffer - Supplies the buffer that will recieve the results of this operation + + Lbo - Supplies the byte offset of where to start reading + + NumberOfBytesToRead - Supplies the number of bytes to read, this must + be in multiple of bytes units acceptable to the disk driver. + + ReturnOnError - Indicates that we should return on an error, instead + of raising. + +Return Value: + + BOOLEAN - TRUE if the operation succeded, FALSE otherwise. + +--*/ + +{ + KEVENT Event; + PIRP Irp; + LARGE_INTEGER ByteOffset; + NTSTATUS Status; + IO_STATUS_BLOCK Iosb; + + DebugTrace(0, Dbg, "FatPerformVerifyDiskRead, Lbo = %08lx\n", Lbo ); + + // + // Initialize the event we're going to use + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Build the irp for the operation and also set the overrride flag + // + + ByteOffset.QuadPart = Lbo; + + Irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ, + Vcb->TargetDeviceObject, + Buffer, + NumberOfBytesToRead, + &ByteOffset, + &Event, + &Iosb ); + + if ( Irp == NULL ) { + + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME ); + + // + // Call the device to do the read and wait for it to finish. + // + + Status = IoCallDriver( Vcb->TargetDeviceObject, Irp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL ); + + Status = Iosb.Status; + } + + ASSERT( Status != STATUS_VERIFY_REQUIRED ); + + // + // Special case this error code because this probably means we used + // the wrong sector size and we want to reject STATUS_WRONG_VOLUME. + // + + if (Status == STATUS_INVALID_PARAMETER) { + + return FALSE; + } + + // + // If it doesn't succeed then either return or raise the error. + // + + if (!NT_SUCCESS(Status)) { + + if (ReturnOnError) { + + return FALSE; + + } else { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + + // + // And return to our caller + // + + return TRUE; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatQueryRetrievalPointers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the query retrieval pointers operation. + It returns the retrieval pointers for the specified input + file from the start of the file to the request map size specified + in the input buffer. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + PLARGE_INTEGER RequestedMapSize; + PLARGE_INTEGER *MappingPairs; + + ULONG Index; + ULONG i; + ULONG SectorCount; + LBO Lbo; + ULONG Vbo; + ULONG MapSize; + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Decode the file object and ensure that it is the paging file + // + // Only Kernel mode clients may query retrieval pointer information about + // a file. Ensure that this is the case for this caller. + // + + (VOID)FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + if (Irp->RequestorMode != KernelMode || + Fcb == NULL || + !FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE) ) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Extract the input and output buffer information. The input contains + // the requested size of the mappings in terms of VBO. The output + // parameter will receive a pointer to nonpaged pool where the mapping + // pairs are stored. + // + + ASSERT( IrpSp->Parameters.FileSystemControl.InputBufferLength == sizeof(LARGE_INTEGER) ); + ASSERT( IrpSp->Parameters.FileSystemControl.OutputBufferLength == sizeof(PVOID) ); + + RequestedMapSize = IrpSp->Parameters.FileSystemControl.Type3InputBuffer; + MappingPairs = Irp->UserBuffer; + + // + // Acquire exclusive access to the Fcb + // + + if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) { + + return FatFsdPostRequest( IrpContext, Irp ); + } + + _SEH2_TRY { + + // + // Verify the Fcb is still OK + // + + FatVerifyFcb( IrpContext, Fcb ); + + // + // Check if the mapping the caller requested is too large + // + + if ((*RequestedMapSize).QuadPart > Fcb->Header.FileSize.QuadPart) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // Now get the index for the mcb entry that will contain the + // callers request and allocate enough pool to hold the + // output mapping pairs + // + + (VOID)FatLookupMcbEntry( Fcb->Vcb, &Fcb->Mcb, RequestedMapSize->LowPart - 1, &Lbo, NULL, &Index ); + + *MappingPairs = FsRtlAllocatePoolWithTag( NonPagedPool, + (Index + 2) * (2 * sizeof(LARGE_INTEGER)), + TAG_OUTPUT_MAPPINGPAIRS ); + + // + // Now copy over the mapping pairs from the mcb + // to the output buffer. We store in [sector count, lbo] + // mapping pairs and end with a zero sector count. + // + + MapSize = RequestedMapSize->LowPart; + + for (i = 0; i <= Index; i += 1) { + +#ifndef __REACTOS__ + (VOID)FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb, i, &Vbo, &Lbo, &SectorCount ); +#else + (VOID)FatGetNextMcbEntry( Fcb->Vcb, &Fcb->Mcb, i, (PVBO)&Vbo, &Lbo, &SectorCount ); +#endif + + if (SectorCount > MapSize) { + SectorCount = MapSize; + } + + (*MappingPairs)[ i*2 + 0 ].QuadPart = SectorCount; + (*MappingPairs)[ i*2 + 1 ].QuadPart = Lbo; + + MapSize -= SectorCount; + } + + (*MappingPairs)[ i*2 + 0 ].QuadPart = 0; + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatQueryRetrievalPointers ); + + // + // Release all of our resources + // + + FatReleaseFcb( IrpContext, Fcb ); + + // + // If this is an abnormal termination then undo our work, otherwise + // complete the irp + // + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + } _SEH2_END; + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatGetStatistics ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine returns the filesystem performance counters from the + appropriate VCB. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + NTSTATUS Status; + PVCB Vcb; + + PFILE_SYSTEM_STATISTICS Buffer; + ULONG BufferLength; + ULONG StatsSize; + ULONG BytesToCopy; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatGetStatistics...\n", 0); + + // + // Extract the buffer + // + + Buffer = Irp->AssociatedIrp.SystemBuffer; + BufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + // + // Get a pointer to the output buffer. + // + + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Make sure the buffer is big enough for at least the common part. + // + + if (BufferLength < sizeof(FILESYSTEM_STATISTICS)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + + DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", STATUS_BUFFER_TOO_SMALL ); + + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Now see how many bytes we can copy. + // + + StatsSize = sizeof(FILE_SYSTEM_STATISTICS) * KeNumberProcessors; + + if (BufferLength < StatsSize) { + + BytesToCopy = BufferLength; + Status = STATUS_BUFFER_OVERFLOW; + + } else { + + BytesToCopy = StatsSize; + Status = STATUS_SUCCESS; + } + + // + // Get the Vcb. + // + + Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb; + + // + // Fill in the output buffer + // + + RtlCopyMemory( Buffer, Vcb->Statistics, BytesToCopy ); + + Irp->IoStatus.Information = BytesToCopy; + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatGetStatistics -> %08lx\n", Status); + + return Status; +} + +// +// Local Support Routine +// + +NTSTATUS +FatGetVolumeBitmap( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine returns the volume allocation bitmap. + + Input = the STARTING_LCN_INPUT_BUFFER data structure is passed in + through the input buffer. + Output = the VOLUME_BITMAP_BUFFER data structure is returned through + the output buffer. + + We return as much as the user buffer allows starting the specified input + LCN (trucated to a byte). If there is no input buffer, we start at zero. + +Arguments: + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + ULONG BytesToCopy; + ULONG TotalClusters; + ULONG DesiredClusters; + ULONG StartingCluster; + ULONG InputBufferLength; + ULONG OutputBufferLength; + LARGE_INTEGER StartingLcn; + PVOLUME_BITMAP_BUFFER OutputBuffer; + + // + // Get the current Irp stack location and save some references. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatGetVolumeBitmap, FsControlCode = %08lx\n", + IrpSp->Parameters.FileSystemControl.FsControlCode); + + // + // Extract and decode the file object and check for type of open. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + OutputBuffer = (PVOLUME_BITMAP_BUFFER)FatMapUserBuffer( IrpContext, Irp ); + + // + // Check for a minimum length on the input and output buffers. + // + + if ((InputBufferLength < sizeof(STARTING_LCN_INPUT_BUFFER)) || + (OutputBufferLength < sizeof(VOLUME_BITMAP_BUFFER))) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Check if a starting cluster was specified. + // + + TotalClusters = Vcb->AllocationSupport.NumberOfClusters; + + // + // Check for valid buffers + // + + _SEH2_TRY { + + if (Irp->RequestorMode != KernelMode) { + + ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer, + InputBufferLength, + sizeof(UCHAR) ); + + ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) ); + } + + StartingLcn = ((PSTARTING_LCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingLcn; + + } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = _SEH2_GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } _SEH2_END; + + if (StartingLcn.HighPart || StartingLcn.LowPart >= TotalClusters) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + + } else { + + StartingCluster = StartingLcn.LowPart & ~7; + } + + // + // Acquire exclusive access to the Vcb and enqueue the Irp if we + // didn't get access. + // + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0); + + ASSERT( Irp->RequestorMode == KernelMode ); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> %08lx\n", Status); + return Status; + } + + // + // Only return what will fit in the user buffer. + // + + OutputBufferLength -= FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer); + DesiredClusters = TotalClusters - StartingCluster; + + if (OutputBufferLength < (DesiredClusters + 7) / 8) { + + BytesToCopy = OutputBufferLength; + Status = STATUS_BUFFER_OVERFLOW; + + } else { + + BytesToCopy = (DesiredClusters + 7) / 8; + Status = STATUS_SUCCESS; + } + + // + // Use try/finally for cleanup. + // + + _SEH2_TRY { + + _SEH2_TRY { + + // + // Verify the Vcb is still OK + // + + FatQuickVerifyVcb( IrpContext, Vcb ); + + // + // Fill in the fixed part of the output buffer + // + + OutputBuffer->StartingLcn.QuadPart = StartingCluster; + OutputBuffer->BitmapSize.QuadPart = DesiredClusters; + + if (Vcb->NumberOfWindows == 1) { + + // + // Just copy the volume bitmap into the user buffer. + // + + ASSERT( Vcb->FreeClusterBitMap.Buffer != NULL ); + + RtlCopyMemory( &OutputBuffer->Buffer[0], + (PUCHAR)Vcb->FreeClusterBitMap.Buffer + StartingCluster/8, + BytesToCopy ); + } else { + + // + // Call out to analyze the FAT. We must bias by two to account for + // the zero base of this API and FAT's physical reality of starting + // the file heap at cluster 2. + // + // Note that the end index is inclusive - we need to subtract one to + // calculcate it. + // + // I.e.: StartingCluster 0 for one byte of bitmap means a start cluster + // of 2 and end cluster of 9, a run of eight clusters. + // + + FatExamineFatEntries( IrpContext, + Vcb, + StartingCluster + 2, + StartingCluster + BytesToCopy * 8 + 2 - 1, + FALSE, + NULL, + (PULONG)&OutputBuffer->Buffer[0] ); + } + + } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = _SEH2_GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } _SEH2_END; + + } _SEH2_FINALLY { + + FatReleaseVcb( IrpContext, Vcb ); + } _SEH2_END; + + Irp->IoStatus.Information = FIELD_OFFSET(VOLUME_BITMAP_BUFFER, Buffer) + + BytesToCopy; + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatGetVolumeBitmap -> VOID\n", 0); + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatGetRetrievalPointers ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine scans the MCB and builds an extent list. The first run in + the output extent list will start at the begining of the contiguous + run specified by the input parameter. + + Input = STARTING_VCN_INPUT_BUFFER; + Output = RETRIEVAL_POINTERS_BUFFER. + +Arguments: + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + TYPE_OF_OPEN TypeOfOpen; + + ULONG Index; + ULONG ClusterShift; + ULONG AllocationSize; + + ULONG Run; + ULONG RunCount; + ULONG StartingRun; + LARGE_INTEGER StartingVcn; + + ULONG InputBufferLength; + ULONG OutputBufferLength; + + PRETRIEVAL_POINTERS_BUFFER OutputBuffer; + + BOOLEAN FcbLocked; + + // + // Get the current Irp stack location and save some references. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatGetRetrievalPointers, FsControlCode = %08lx\n", + IrpSp->Parameters.FileSystemControl.FsControlCode); + + // + // Extract and decode the file object and check for type of open. + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen) && (TypeOfOpen != UserDirectoryOpen)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + // + // Get the input and output buffer lengths and pointers. + // Initialize some variables. + // + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + OutputBuffer = (PRETRIEVAL_POINTERS_BUFFER)FatMapUserBuffer( IrpContext, Irp ); + + // + // Check for a minimum length on the input and ouput buffers. + // + + if ((InputBufferLength < sizeof(STARTING_VCN_INPUT_BUFFER)) || + (OutputBufferLength < sizeof(RETRIEVAL_POINTERS_BUFFER))) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Acquire the Fcb and enqueue the Irp if we didn't get access. Go for + // shared on read-only media so we can allow prototype XIP to get + // recursive, as well as recognizing this is safe anyway. + // + + if (FlagOn( FcbOrDcb->Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED )) { + + FcbLocked = FatAcquireSharedFcb( IrpContext, FcbOrDcb ); + + } else { + + FcbLocked = FatAcquireExclusiveFcb( IrpContext, FcbOrDcb ); + } + + if (!FcbLocked) { + + DebugTrace( 0, Dbg, "Cannot acquire Vcb\n", 0); + + ASSERT( Irp->RequestorMode == KernelMode ); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatGetRetrievalPointers -> %08lx\n", Status); + return Status; + } + + _SEH2_TRY { + + // + // Verify the Fcb is still OK + // + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + + // + // If this is a non-root directory, we have a bit more to + // do since it has not gone through FatOpenDirectoryFile(). + // + + if (NodeType(FcbOrDcb) == FAT_NTC_DCB || + (NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && FatIsFat32(Vcb))) { + + FcbOrDcb->Header.FileSize.LowPart = + FcbOrDcb->Header.AllocationSize.LowPart; + } + } + + // + // Check if a starting cluster was specified. + // + + ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster; + AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart; + + _SEH2_TRY { + + if (Irp->RequestorMode != KernelMode) { + + ProbeForRead( IrpSp->Parameters.FileSystemControl.Type3InputBuffer, + InputBufferLength, + sizeof(UCHAR) ); + + ProbeForWrite( OutputBuffer, OutputBufferLength, sizeof(UCHAR) ); + } + + StartingVcn = ((PSTARTING_VCN_INPUT_BUFFER)IrpSp->Parameters.FileSystemControl.Type3InputBuffer)->StartingVcn; + + } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = _SEH2_GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } _SEH2_END; + + if (StartingVcn.HighPart || + StartingVcn.LowPart >= (AllocationSize >> ClusterShift)) { + + try_return( Status = STATUS_END_OF_FILE ); + + } else { + + // + // If we don't find the run, something is very wrong. + // + + LBO Lbo; + + if (!FatLookupMcbEntry( FcbOrDcb->Vcb, &FcbOrDcb->Mcb, + StartingVcn.LowPart << ClusterShift, + &Lbo, + NULL, + &StartingRun)) { + + FatBugCheck( (ULONG_PTR)FcbOrDcb, (ULONG_PTR)&FcbOrDcb->Mcb, StartingVcn.LowPart ); + } + } + + // + // Now go fill in the ouput buffer with run information + // + + RunCount = FsRtlNumberOfRunsInLargeMcb( &FcbOrDcb->Mcb ); + + for (Index = 0, Run = StartingRun; Run < RunCount; Index++, Run++) { + + ULONG Vcn; + LBO Lbo; + ULONG ByteLength; + + // + // Check for an exhausted output buffer. + // + + if ((ULONG)FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index+1]) > OutputBufferLength) { + + + // + // We've run out of space, so we won't be storing as many runs to the + // user's buffer as we had originally planned. We need to return the + // number of runs that we did have room for. + // + + _SEH2_TRY { + + OutputBuffer->ExtentCount = Index; + + } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = _SEH2_GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } _SEH2_END; + + Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]); + try_return( Status = STATUS_BUFFER_OVERFLOW ); + } + + // + // Get the extent. If it's not there or malformed, something is very wrong. + // + +#ifndef __REACTOS__ + if (!FatGetNextMcbEntry(Vcb, &FcbOrDcb->Mcb, Run, &Vcn, &Lbo, &ByteLength)) { +#else + if (!FatGetNextMcbEntry(Vcb, &FcbOrDcb->Mcb, Run, (PVBO)&Vcn, &Lbo, &ByteLength)) { +#endif + FatBugCheck( (ULONG_PTR)FcbOrDcb, (ULONG_PTR)&FcbOrDcb->Mcb, Run ); + } + + // + // Fill in the next array element. + // + + _SEH2_TRY { + + OutputBuffer->Extents[Index].NextVcn.QuadPart = (Vcn + ByteLength) >> ClusterShift; + OutputBuffer->Extents[Index].Lcn.QuadPart = FatGetIndexFromLbo( Vcb, Lbo ) - 2; + + // + // If this is the first run, fill in the starting Vcn + // + + if (Index == 0) { + OutputBuffer->ExtentCount = RunCount - StartingRun; + OutputBuffer->StartingVcn.QuadPart = Vcn >> ClusterShift; + } + + } _SEH2_EXCEPT( Irp->RequestorMode != KernelMode ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH ) { + + Status = _SEH2_GetExceptionCode(); + + FatRaiseStatus( IrpContext, + FsRtlIsNtstatusExpected(Status) ? + Status : STATUS_INVALID_USER_BUFFER ); + } _SEH2_END; + } + + // + // We successfully retrieved extent info to the end of the allocation. + // + + Irp->IoStatus.Information = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[Index]); + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + } _SEH2_FINALLY { + + DebugUnwind( FatGetRetrievalPointers ); + + // + // Release resources + // + + FatReleaseFcb( IrpContext, FcbOrDcb ); + + // + // If nothing raised then complete the irp. + // + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatGetRetrievalPointers -> VOID\n", 0); + } _SEH2_END; + + return Status; +} + + +// +// Local Support Routine +// + +NTSTATUS +FatMoveFile ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + Routine moves a file to the requested Starting Lcn from Starting Vcn for the length + of cluster count. These values are passed in through the the input buffer as a + MOVE_DATA structure. + + The call must be made with a DASD handle. The file to move is passed in as a + parameter. + +Arguments: + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + TYPE_OF_OPEN TypeOfOpen; + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + + ULONG InputBufferLength; + PMOVE_FILE_DATA InputBuffer; + + ULONG ClusterShift; + ULONG MaxClusters; + + ULONG FileOffset; + + LBO TargetLbo; + ULONG TargetCluster; + LARGE_INTEGER LargeSourceLbo; + LARGE_INTEGER LargeTargetLbo; + + ULONG ByteCount; + ULONG BytesToWrite; + ULONG BytesToReallocate; +#ifndef __REACTOS__ + ULONG TargetAllocation; +#endif + + ULONG FirstSpliceSourceCluster; + ULONG FirstSpliceTargetCluster; + ULONG SecondSpliceSourceCluster; + ULONG SecondSpliceTargetCluster; + + LARGE_MCB SourceMcb; + LARGE_MCB TargetMcb; + + KEVENT StackEvent; + + PVOID Buffer; + ULONG BufferSize; + + BOOLEAN SourceMcbInitialized = FALSE; + BOOLEAN TargetMcbInitialized = FALSE; + + BOOLEAN FcbAcquired = FALSE; + BOOLEAN EventArmed = FALSE; + BOOLEAN DiskSpaceAllocated = FALSE; + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + +#if defined(_WIN64) + MOVE_FILE_DATA LocalMoveFileData; + PMOVE_FILE_DATA32 MoveFileData32; +#endif + + ULONG LocalAbnormalTermination = 0; + + // + // Get the current Irp stack location and save some references. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatMoveFile, FsControlCode = %08lx\n", + IrpSp->Parameters.FileSystemControl.FsControlCode); + + // + // Force WAIT to true. We have a handle in the input buffer which can only + // be referenced within the originating process. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Extract and decode the file object and check for type of open. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + InputBuffer = (PMOVE_FILE_DATA)Irp->AssociatedIrp.SystemBuffer; + + // + // Do a quick check on the input buffer. + // + +#if defined(_WIN64) + if (IoIs32bitProcess( Irp )) { + + if (InputBuffer == NULL || InputBufferLength < sizeof(MOVE_FILE_DATA32)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } + + MoveFileData32 = (PMOVE_FILE_DATA32) InputBuffer; + + LocalMoveFileData.FileHandle = (HANDLE) LongToHandle( MoveFileData32->FileHandle ); + LocalMoveFileData.StartingVcn = MoveFileData32->StartingVcn; + LocalMoveFileData.StartingLcn = MoveFileData32->StartingLcn; + LocalMoveFileData.ClusterCount = MoveFileData32->ClusterCount; + + InputBuffer = &LocalMoveFileData; + + } else { +#endif + if (InputBuffer == NULL || InputBufferLength < sizeof(MOVE_FILE_DATA)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_BUFFER_TOO_SMALL ); + return STATUS_BUFFER_TOO_SMALL; + } +#if defined(_WIN64) + } +#endif + + MaxClusters = Vcb->AllocationSupport.NumberOfClusters; + TargetCluster = InputBuffer->StartingLcn.LowPart + 2; + + if (InputBuffer->StartingVcn.HighPart || + InputBuffer->StartingLcn.HighPart || + (TargetCluster < 2) || + (TargetCluster + InputBuffer->ClusterCount < TargetCluster) || + (TargetCluster + InputBuffer->ClusterCount > MaxClusters + 2) || + (InputBuffer->StartingVcn.LowPart >= MaxClusters)) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + // + // Try to get a pointer to the file object from the handle passed in. + // + + Status = ObReferenceObjectByHandle( InputBuffer->FileHandle, + 0, + *IoFileObjectType, + Irp->RequestorMode, +#ifndef __REACTOS__ + &FileObject, +#else + (PVOID *)&FileObject, +#endif + NULL ); + + if (!NT_SUCCESS(Status)) { + + FatCompleteRequest( IrpContext, Irp, Status ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", Status); + return Status; + } + + // + // There are three basic ways this could be an invalid attempt, so + // we need to + // + // - check that this file object is opened on the same volume as the + // DASD handle used to call this routine. + // + // - extract and decode the file object and check for type of open. + // + // - if this is a directory, verify that it's not the root and that + // we are not trying to move the first cluster. We cannot move the + // first cluster because sub-directories have this cluster number + // in them and there is no safe way to simultaneously update them + // all. + // + // We'll allow movefile on the root dir if its fat32, since the root dir + // is a real chained file there. + // + + if (FileObject->Vpb != Vcb->Vpb) { + + ObDereferenceObject( FileObject ); + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &FcbOrDcb, &Ccb ); + + if ((TypeOfOpen != UserFileOpen && + TypeOfOpen != UserDirectoryOpen) || + + ((TypeOfOpen == UserDirectoryOpen) && + ((NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && !FatIsFat32(Vcb)) || + (InputBuffer->StartingVcn.QuadPart == 0)))) { + + ObDereferenceObject( FileObject ); + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + // + // Indicate we're getting to parents of this fcb by their child, and that + // this is a sufficient assertion of our ability to by synchronized + // with respect to the parent directory going away. + // + // The defrag path is an example of one which arrives at an Fcb by + // a means which would be unreasonable to duplicate in the assertion + // code. See FatOpenDirectoryFile. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD ); + + ClusterShift = Vcb->AllocationSupport.LogOfBytesPerCluster; + + _SEH2_TRY { + + // + // Initialize our state variables and the event. + // + + FileOffset = InputBuffer->StartingVcn.LowPart << ClusterShift; + + ByteCount = InputBuffer->ClusterCount << ClusterShift; + + TargetLbo = FatGetLboFromIndex( Vcb, TargetCluster ); + LargeTargetLbo.QuadPart = TargetLbo; + + Buffer = NULL; + + // + // Do a quick check on parameters here + // + + if (FileOffset + ByteCount < FileOffset) { + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + KeInitializeEvent( &StackEvent, NotificationEvent, FALSE ); + + // + // Initialize two MCBs we will be using + // + + FsRtlInitializeLargeMcb( &SourceMcb, PagedPool ); + SourceMcbInitialized = TRUE; + + FsRtlInitializeLargeMcb( &TargetMcb, PagedPool ); + TargetMcbInitialized = TRUE; + + // + // Ok, now if this is a directory open we need to switch to the internal + // stream fileobject since it is set up for caching. The top-level + // fileobject has no section object pointers in order prevent folks from + // mapping it. + // + + if (TypeOfOpen == UserDirectoryOpen) { + + PFILE_OBJECT DirStreamFileObject; + + // + // Open the stream fileobject if neccesary. We must acquire the Fcb + // now to synchronize with other operations (such as dismount ripping + // apart the allocator). + // + + (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb ); + FcbAcquired = TRUE; + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + FatOpenDirectoryFile( IrpContext, FcbOrDcb ); + DirStreamFileObject = FcbOrDcb->Specific.Dcb.DirectoryFile; + + // + // Transfer our reference to the internal stream and proceed. Note that + // if we dereferenced first, the user could sneak a teardown through since + // we'd have no references. + // + + ObReferenceObject( DirStreamFileObject ); + ObDereferenceObject( FileObject ); + FileObject = DirStreamFileObject; + } + + // + // Determine the size of the buffer we will use to move data. + // + + BufferSize = FAT_DEFAULT_DEFRAG_CHUNK_IN_BYTES; + + if (BufferSize < (ULONG)(1 << ClusterShift)) { + + BufferSize = (1 << ClusterShift); + } + + while (ByteCount) { + + VBO TempVbo; + LBO TempLbo; + ULONG TempByteCount; + + // + // We must throttle our writes. + // + + CcCanIWrite( FileObject, + BufferSize, + TRUE, + FALSE ); + + // + // Aqcuire file resource exclusive to freeze FileSize and block + // user non-cached I/O. Verify the integrity of the fcb - the + // media may have changed (or been dismounted) on us. + // + + if (FcbAcquired == FALSE) { + + (VOID)FatAcquireExclusiveFcb( IrpContext, FcbOrDcb ); + FcbAcquired = TRUE; + + FatVerifyFcb( IrpContext, FcbOrDcb ); + } + + // + // Allocate our buffer, if we need to. + // + + if (Buffer == NULL) { + + Buffer = FsRtlAllocatePoolWithTag( NonPagedPool, + BufferSize, + TAG_DEFRAG_BUFFER ); + } + + // + // Analyzes the range of file allocation we are moving + // and determines the actual amount of allocation to be + // moved and how much needs to be written. In addition + // it guarantees that the Mcb in the file is large enough + // so that later MCB operations cannot fail. + // + + FatComputeMoveFileParameter( IrpContext, + FcbOrDcb, + BufferSize, + FileOffset, + &ByteCount, + &BytesToReallocate, + &BytesToWrite, + &LargeSourceLbo ); + + // + // If ByteCount comes back zero, break here. + // + + if (ByteCount == 0) { + break; + } + + // + // At this point (before actually doing anything with the disk + // meta data), calculate the FAT splice clusters and build an + // MCB describing the space to be deallocated. + // + + FatComputeMoveFileSplicePoints( IrpContext, + FcbOrDcb, + FileOffset, + TargetCluster, + BytesToReallocate, + &FirstSpliceSourceCluster, + &FirstSpliceTargetCluster, + &SecondSpliceSourceCluster, + &SecondSpliceTargetCluster, + &SourceMcb ); + + // + // Now attempt to allocate the new disk storage using the + // Target Lcn as a hint. + // + + TempByteCount = BytesToReallocate; + FatAllocateDiskSpace( IrpContext, + Vcb, + TargetCluster, + &TempByteCount, + TRUE, + &TargetMcb ); + + DiskSpaceAllocated = TRUE; + + // + // If we didn't get EXACTLY what we wanted, return immediately. + // + + if ((FsRtlNumberOfRunsInLargeMcb( &TargetMcb ) != 1) || + !FatGetNextMcbEntry( Vcb, &TargetMcb, 0, &TempVbo, &TempLbo, &TempByteCount ) || + (FatGetIndexFromLbo( Vcb, TempLbo) != TargetCluster ) || + (TempByteCount != BytesToReallocate)) { + + // + // It would be nice if we could be more specific, but such is life. + // + try_return( Status = STATUS_INVALID_PARAMETER ); + } + +#if DBG + // + // We are going to attempt a move, note it. + // + + if (FatMoveFileDebug) { + DbgPrint("%lx: Vcn 0x%lx, Lcn 0x%lx, Count 0x%lx.\n", + PsGetCurrentThread(), + FileOffset >> ClusterShift, + TargetCluster, + BytesToReallocate >> ClusterShift ); + } +#endif + + // + // Now attempt to commit the new allocation to disk. If this + // raises, the allocation will be deallocated. + // + + FatFlushFatEntries( IrpContext, + Vcb, + TargetCluster, + BytesToReallocate >> ClusterShift ); + + // + // Aqcuire both resources exclusive now, guaranteeing that NOBODY + // is in either the read or write paths. + // + + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + + // + // This is the first part of some tricky synchronization. + // + // Set the Event pointer in the FCB. Any paging I/O will block on + // this event (if set in FCB) after acquiring the PagingIo resource. + // + // This is how I keep ALL I/O out of this path without holding the + // PagingIo resource exclusive for an extended time. + // + + FcbOrDcb->MoveFileEvent = &StackEvent; + EventArmed = TRUE; + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + + // + // Now write out the data, but only if we have to. We don't have + // to copy any file data if the range being reallocated is wholly + // beyond valid data length. + // + + if (BytesToWrite) { + + PIRP IoIrp; + KEVENT IoEvent; + IO_STATUS_BLOCK Iosb; + + KeInitializeEvent( &IoEvent, + NotificationEvent, + FALSE ); + + ASSERT( LargeTargetLbo.QuadPart >= Vcb->AllocationSupport.FileAreaLbo); + + // + // Read in the data that is being moved. + // + + IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_READ, + Vcb->TargetDeviceObject, + Buffer, + BytesToWrite, + &LargeSourceLbo, + &IoEvent, + &Iosb ); + + if (IoIrp == NULL) { + + FatRaiseStatus( IrpContext, + STATUS_INSUFFICIENT_RESOURCES ); + } + + Status = IoCallDriver( Vcb->TargetDeviceObject, + IoIrp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &IoEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = Iosb.Status; + } + + if (!NT_SUCCESS( Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, + Status ); + } + + // + // Write the data to its new location. + // + + KeInitializeEvent( &IoEvent, NotificationEvent, FALSE ); + + IoIrp = IoBuildSynchronousFsdRequest( IRP_MJ_WRITE, + Vcb->TargetDeviceObject, + Buffer, + BytesToWrite, + &LargeTargetLbo, + &IoEvent, + &Iosb ); + + if (!IoIrp) { + FatRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES ); + } + + // + // Set a flag indicating that we want to write through any + // cache on the controller. This eliminates the need for + // an explicit flush-device after the write. + // + + SetFlag( IoGetNextIrpStackLocation(IoIrp)->Flags, SL_WRITE_THROUGH ); + + Status = IoCallDriver( Vcb->TargetDeviceObject, IoIrp ); + + if (Status == STATUS_PENDING) { + (VOID)KeWaitForSingleObject( &IoEvent, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL ); + Status = Iosb.Status; + } + + if (!NT_SUCCESS(Status)) { + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + + // + // Now that the file data has been moved successfully, we'll go + // to fix up the links in the FAT table and perhaps change the + // entry in the parent directory. + // + // First we'll do the second splice and commit it. At that point, + // while the volume is in an inconsistent state, the file is + // still OK. + // + + FatSetFatEntry( IrpContext, + Vcb, + SecondSpliceSourceCluster, + (FAT_ENTRY)SecondSpliceTargetCluster ); + + FatFlushFatEntries( IrpContext, Vcb, SecondSpliceSourceCluster, 1 ); + + // + // Now do the first splice OR update the dirent in the parent + // and flush the respective object. After this flush the file + // now points to the new allocation. + // + + if (FirstSpliceSourceCluster == 0) { + + ASSERT( NodeType(FcbOrDcb) == FAT_NTC_FCB ); + + // + // We are moving the first cluster of the file, so we need + // to update our parent directory. + // + + FatGetDirentFromFcbOrDcb( IrpContext, FcbOrDcb, &Dirent, &DirentBcb ); + Dirent->FirstClusterOfFile = (USHORT)FirstSpliceTargetCluster; + + if (FatIsFat32(Vcb)) { + + Dirent->FirstClusterOfFileHi = + (USHORT)(FirstSpliceTargetCluster >> 16); + + } + + FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); + + FatUnpinBcb( IrpContext, DirentBcb ); + DirentBcb = NULL; + + FatFlushDirentForFile( IrpContext, FcbOrDcb ); + + FcbOrDcb->FirstClusterOfFile = FirstSpliceTargetCluster; + + } else { + + FatSetFatEntry( IrpContext, + Vcb, + FirstSpliceSourceCluster, + (FAT_ENTRY)FirstSpliceTargetCluster ); + + FatFlushFatEntries( IrpContext, Vcb, FirstSpliceSourceCluster, 1 ); + } + + // + // This was successfully committed. We no longer want to free + // this allocation on error. + // + + DiskSpaceAllocated = FALSE; + + // + // Now we just have to free the orphaned space. We don't have + // to commit this right now as the integrity of the file doesn't + // depend on it. + // + + FatDeallocateDiskSpace( IrpContext, Vcb, &SourceMcb ); + + FatUnpinRepinnedBcbs( IrpContext ); + + Status = FatHijackIrpAndFlushDevice( IrpContext, + Irp, + Vcb->TargetDeviceObject ); + + if (!NT_SUCCESS(Status)) { + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // Finally we must replace the old MCB extent information with + // the new. If this fails from pool allocation, we fix it in + // the finally clause by resetting the file's Mcb. + // + + FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb, + FileOffset, + BytesToReallocate ); + + FatAddMcbEntry( Vcb, &FcbOrDcb->Mcb, + FileOffset, + TargetLbo, + BytesToReallocate ); + + // + // Now this is the second part of the tricky synchronization. + // + // We drop the paging I/O here and signal the notification + // event which allows all waiters (present or future) to proceed. + // Then we block again on the PagingIo exclusive. When + // we have it, we again know that there can be nobody in the + // read/write path and thus nobody touching the event, so we + // NULL the pointer to it and then drop the PagingIo resource. + // + // This combined with our synchronization before the write above + // guarantees that while we were moving the allocation, there + // was no other I/O to this file and because we do not hold + // the paging resource across a flush, we are not exposed to + // a deadlock. + // + + KeSetEvent( &StackEvent, 0, FALSE ); + + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + + FcbOrDcb->MoveFileEvent = NULL; + EventArmed = FALSE; + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + + // + // Release the resources and let anyone else access the file before + // looping back. + // + + FatReleaseFcb( IrpContext, FcbOrDcb ); + FcbAcquired = FALSE; + + // + // Advance the state variables. + // + + TargetCluster += BytesToReallocate >> ClusterShift; + + FileOffset += BytesToReallocate; + TargetLbo += BytesToReallocate; + ByteCount -= BytesToReallocate; + + LargeTargetLbo.QuadPart += BytesToReallocate; + + // + // Clear the two Mcbs + // + + FatRemoveMcbEntry( Vcb, &SourceMcb, 0, 0xFFFFFFFF ); + FatRemoveMcbEntry( Vcb, &TargetMcb, 0, 0xFFFFFFFF ); + + // + // Make the event blockable again. + // + + KeClearEvent( &StackEvent ); + } + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + + } _SEH2_FINALLY { + + DebugUnwind( FatMoveFile ); + + LocalAbnormalTermination |= _SEH2_AbnormalTermination(); + + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_PARENT_BY_CHILD ); + + // + // Free the data buffer, if it was allocated. + // + + if (Buffer != NULL) { + + ExFreePool( Buffer ); + } + + // + // Use a nested try-finally for cleanup if our unpinrepinned + // encounters write-through errors. This may even be a re-raise. + // + + _SEH2_TRY { + + // + // If we have some new allocation hanging around, remove it. The + // pages needed to do this are guaranteed to be resident because + // we have already repinned them. + // + + if (DiskSpaceAllocated) { + FatDeallocateDiskSpace( IrpContext, Vcb, &TargetMcb ); + FatUnpinRepinnedBcbs( IrpContext ); + } + + } _SEH2_FINALLY { + + LocalAbnormalTermination |= _SEH2_AbnormalTermination(); + + // + // Check on the directory Bcb + // + + if (DirentBcb != NULL) { + FatUnpinBcb( IrpContext, DirentBcb ); + } + + // + // Uninitialize our MCBs + // + + if (SourceMcbInitialized) { + FsRtlUninitializeLargeMcb( &SourceMcb ); + } + + if (TargetMcbInitialized) { + FsRtlUninitializeLargeMcb( &TargetMcb ); + } + + // + // If this is an abnormal termination then presumably something + // bad happened. Set the Allocation size to unknown and clear + // the Mcb, but only if we still own the Fcb. + // + // It is important to make sure we use a 64bit form of -1. This is + // what will convince the fastIO path that it cannot extend the file + // in the cache until we have picked up the mapping pairs again. + // + // Also, we have to do this while owning PagingIo or we can tear the + // Mcb down in the midst of the noncached IO path looking up extents + // (after we drop it and let them all in). + // + + if (LocalAbnormalTermination && FcbAcquired) { + + if (FcbOrDcb->FirstClusterOfFile == 0) { + + FcbOrDcb->Header.AllocationSize.QuadPart = 0; + } else { + + FcbOrDcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + } + + FatRemoveMcbEntry( Vcb, &FcbOrDcb->Mcb, 0, 0xFFFFFFFF ); + } + + // + // If we broke out of the loop with the Event armed, defuse it + // in the same way we do it after a write. + // + + if (EventArmed) { + KeSetEvent( &StackEvent, 0, FALSE ); + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + FcbOrDcb->MoveFileEvent = NULL; + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + } + + // + // Finally release the main file resource. + // + + if (FcbAcquired) { + FatReleaseFcb( IrpContext, FcbOrDcb ); + } + + // + // Now dereference the fileobject. If the user was a wacko they could have + // tried to nail us by closing the handle right after they threw this move + // down, so we had to keep the fileobject referenced across the entire + // operation. + // + + ObDereferenceObject( FileObject ); + + } _SEH2_END; + } _SEH2_END; + + // + // Complete the irp if we terminated normally. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +// +// Local Support Routine +// + +VOID +FatComputeMoveFileParameter ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG BufferSize, + IN ULONG FileOffset, + IN OUT PULONG ByteCount, + OUT PULONG BytesToReallocate, + OUT PULONG BytesToWrite, + OUT PLARGE_INTEGER SourceLbo +) + +/*++ + +Routine Description: + + This is a helper routine for FatMoveFile that analyses the range of + file allocation we are moving and determines the actual amount + of allocation to be moved and how much needs to be written. + +Arguments: + + FcbOrDcb - Supplies the file and its various sizes. + + BufferSize - Supplies the size of the buffer we are using to store the data + being moved. + + FileOffset - Supplies the beginning Vbo of the reallocation zone. + + ByteCount - Supplies the request length to reallocate. This will + be bounded by allocation size on return. + + BytesToReallocate - Receives ByteCount bounded by the file allocation size + and buffer size. + + BytesToWrite - Receives BytesToReallocate bounded by ValidDataLength. + + SourceLbo - Receives the logical byte offset of the source data on the volume. + +Return Value: + + VOID + +--*/ + +{ + ULONG ClusterSize; + + ULONG AllocationSize; + ULONG ValidDataLength; + ULONG ClusterAlignedVDL; + LBO RunLbo; + ULONG RunByteCount; + ULONG RunIndex; + BOOLEAN RunAllocated; + BOOLEAN RunEndOnMax; + + + // + // If we haven't yet set the correct AllocationSize, do so. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + + // + // If this is a non-root directory, we have a bit more to + // do since it has not gone through FatOpenDirectoryFile(). + // + + if (NodeType(FcbOrDcb) == FAT_NTC_DCB || + (NodeType(FcbOrDcb) == FAT_NTC_ROOT_DCB && FatIsFat32(FcbOrDcb->Vcb))) { + + FcbOrDcb->Header.FileSize.LowPart = + FcbOrDcb->Header.AllocationSize.LowPart; + } + } + + // + // Get the number of bytes left to write and ensure that it does + // not extend beyond allocation size. We return here if FileOffset + // is beyond AllocationSize which can happn on a truncation. + // + + AllocationSize = FcbOrDcb->Header.AllocationSize.LowPart; + ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart; + + if (FileOffset + *ByteCount > AllocationSize) { + + if (FileOffset >= AllocationSize) { + *ByteCount = 0; + *BytesToReallocate = 0; + *BytesToWrite = 0; + + return; + } + + *ByteCount = AllocationSize - FileOffset; + } + + // + // If there is more than our max, then reduce the byte count for this + // pass to our maximum. We must also align the file offset to a + // buffer size byte boundary. + // + + if ((FileOffset & (BufferSize - 1)) + *ByteCount > BufferSize) { + + *BytesToReallocate = BufferSize - (FileOffset & (BufferSize - 1)); + + } else { + + *BytesToReallocate = *ByteCount; + } + + // + // Find where this data exists on the volume. + // + + FatLookupFileAllocation( IrpContext, + FcbOrDcb, + FileOffset, + &RunLbo, + &RunByteCount, + &RunAllocated, + &RunEndOnMax, + &RunIndex ); + + ASSERT( RunAllocated ); + + // + // Limit this run to the contiguous length. + // + + if (RunByteCount < *BytesToReallocate) { + + *BytesToReallocate = RunByteCount; + } + + // + // Set the starting offset of the source. + // + + SourceLbo->QuadPart = RunLbo; + + // + // We may be able to skip some (or all) of the write + // if allocation size is significantly greater than valid data length. + // + + ClusterSize = 1 << FcbOrDcb->Vcb->AllocationSupport.LogOfBytesPerCluster; + + ASSERT( ClusterSize <= BufferSize ); + + ClusterAlignedVDL = (ValidDataLength + (ClusterSize - 1)) & ~(ClusterSize - 1); + + if ((NodeType(FcbOrDcb) == FAT_NTC_FCB) && + (FileOffset + *BytesToReallocate > ClusterAlignedVDL)) { + + if (FileOffset > ClusterAlignedVDL) { + + *BytesToWrite = 0; + + } else { + + *BytesToWrite = ClusterAlignedVDL - FileOffset; + } + + } else { + + *BytesToWrite = *BytesToReallocate; + } +} + + +// +// Local Support Routine +// + +VOID +FatComputeMoveFileSplicePoints ( + IN PIRP_CONTEXT IrpContext, + IN PFCB FcbOrDcb, + IN ULONG FileOffset, + IN ULONG TargetCluster, + IN ULONG BytesToReallocate, + OUT PULONG FirstSpliceSourceCluster, + OUT PULONG FirstSpliceTargetCluster, + OUT PULONG SecondSpliceSourceCluster, + OUT PULONG SecondSpliceTargetCluster, + IN OUT PLARGE_MCB SourceMcb +) + +/*++ + +Routine Description: + + This is a helper routine for FatMoveFile that analyzes the range of + file allocation we are moving and generates the splice points in the + FAT table. + +Arguments: + + FcbOrDcb - Supplies the file and thus Mcb. + + FileOffset - Supplies the beginning Vbo of the reallocation zone. + + TargetCluster - Supplies the beginning cluster of the reallocation target. + + BytesToReallocate - Suppies the length of the reallocation zone. + + FirstSpliceSourceCluster - Receives the last cluster in previous allocation + or zero if we are reallocating from VBO 0. + + FirstSpliceTargetCluster - Receives the target cluster (i.e. new allocation) + + SecondSpliceSourceCluster - Receives the final target cluster. + + SecondSpliceTargetCluster - Receives the first cluster of the remaining + source allocation or FAT_CLUSTER_LAST if the reallocation zone + extends to the end of the file. + + SourceMcb - This supplies an MCB that will be filled in with run + information describing the file allocation being replaced. The Mcb + must be initialized by the caller. + +Return Value: + + VOID + +--*/ + +{ + VBO SourceVbo; + LBO SourceLbo; + ULONG SourceIndex; + ULONG SourceBytesInRun; + ULONG SourceBytesRemaining; + + ULONG SourceMcbVbo; + ULONG SourceMcbBytesInRun; + + PVCB Vcb; + + Vcb = FcbOrDcb->Vcb; + + // + // Get information on the final cluster in the previous allocation and + // prepare to enumerate it in the follow loop. + // + + if (FileOffset == 0) { + + SourceIndex = 0; + *FirstSpliceSourceCluster = 0; + FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb, + 0, + &SourceVbo, + &SourceLbo, + &SourceBytesInRun ); + + } else { + + FatLookupMcbEntry( Vcb, &FcbOrDcb->Mcb, + FileOffset-1, + &SourceLbo, + &SourceBytesInRun, + &SourceIndex); + + *FirstSpliceSourceCluster = FatGetIndexFromLbo( Vcb, SourceLbo ); + + if (SourceBytesInRun == 1) { + + SourceIndex += 1; + FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb, + SourceIndex, + &SourceVbo, + &SourceLbo, + &SourceBytesInRun); + + } else { + + SourceVbo = FileOffset; + SourceLbo += 1; + SourceBytesInRun -= 1; + } + } + + // + // At this point the variables: + // + // - SourceIndex - SourceLbo - SourceBytesInRun - + // + // all correctly decribe the allocation to be removed. In the loop + // below we will start here and continue enumerating the Mcb runs + // until we are finished with the allocation to be relocated. + // + + *FirstSpliceTargetCluster = TargetCluster; + + *SecondSpliceSourceCluster = + *FirstSpliceTargetCluster + + (BytesToReallocate >> Vcb->AllocationSupport.LogOfBytesPerCluster) - 1; + + for (SourceBytesRemaining = BytesToReallocate, SourceMcbVbo = 0; + + SourceBytesRemaining > 0; + + SourceIndex += 1, + SourceBytesRemaining -= SourceMcbBytesInRun, + SourceMcbVbo += SourceMcbBytesInRun) { + + if (SourceMcbVbo != 0) { + FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb, + SourceIndex, + &SourceVbo, + &SourceLbo, + &SourceBytesInRun ); + } + + ASSERT( SourceVbo == SourceMcbVbo + FileOffset ); + + SourceMcbBytesInRun = + SourceBytesInRun < SourceBytesRemaining ? + SourceBytesInRun : SourceBytesRemaining; + + FatAddMcbEntry( Vcb, SourceMcb, + SourceMcbVbo, + SourceLbo, + SourceMcbBytesInRun ); + } + + // + // Now compute the cluster of the target of the second + // splice. If the final run in the above loop was + // more than we needed, then we can just do arithmetic, + // otherwise we have to look up the next run. + // + + if (SourceMcbBytesInRun < SourceBytesInRun) { + + *SecondSpliceTargetCluster = + FatGetIndexFromLbo( Vcb, SourceLbo + SourceMcbBytesInRun ); + + } else { + + if (FatGetNextMcbEntry( Vcb, &FcbOrDcb->Mcb, + SourceIndex, + &SourceVbo, + &SourceLbo, + &SourceBytesInRun )) { + + *SecondSpliceTargetCluster = FatGetIndexFromLbo( Vcb, SourceLbo ); + + } else { + + *SecondSpliceTargetCluster = FAT_CLUSTER_LAST; + } + } +} + + +NTSTATUS +FatAllowExtendedDasdIo( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine marks the CCB to indicate that the handle + may be used to read past the end of the volume file. The + handle must be a dasd handle. + +Arguments: + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + PIO_STACK_LOCATION IrpSp; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + // + // Get the current Irp stack location and save some references. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Extract and decode the file object and check for type of open. + // + + if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ) != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + return STATUS_INVALID_PARAMETER; + } + + if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatAllowExtendedDasdIo -> %08lx\n", STATUS_INVALID_PARAMETER); + return STATUS_INVALID_PARAMETER; + } + + SetFlag( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO ); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; +} + + + +VOID +FatFlushAndCleanVolume( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PVCB Vcb, + IN FAT_FLUSH_TYPE FlushType + ) +/*++ + +Routine Description: + + This routine flushes and otherwise preparse a volume to be eligible + for deletion. The dismount and PNP paths share the need for this + common work. + + The Vcb will always be valid on return from this function. It is the + caller's responsibility to attempt the dismount/deletion, and to setup + allocation support again if the volume will be brought back from the + brink. + +Arguments: + + Irp - Irp for the overlying request + + Vcb - the volume being operated on + + FlushType - specifies the kind of flushing desired + +Return Value: + + NTSTATUS - The return status for the operation. + +--*/ +{ + // + // The volume must be held exclusive. + // + + ASSERT( FatVcbAcquiredExclusive( IrpContext, Vcb )); + + // + // There is no fail, flush everything. If invalidating, it is important + // that we invalidate as we flush (eventually, w/ paging io held) so that we + // error out the maximum number of late writes. + // + + if (FlushType != NoFlush) { + + (VOID) FatFlushVolume( IrpContext, Vcb, FlushType ); + } + + FatCloseEaFile( IrpContext, Vcb, FALSE ); + + // + // Now, tell the device to flush its buffers. + // + + if (FlushType != NoFlush) { + + (VOID)FatHijackIrpAndFlushDevice( IrpContext, Irp, Vcb->TargetDeviceObject ); + } + + // + // Now purge everything in sight. We're trying to provoke as many closes as + // soon as possible, this volume may be on its way out. + // + + if (FlushType != FlushWithoutPurge) { + + (VOID) FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, NoFlush ); + } + + // + // If the volume was dirty and we were allowed to flush, do the processing that + // the delayed callback would have done. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) { + + // + // Cancel any pending clean volumes. + // + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + + if (FlushType != NoFlush) { + + // + // The volume is now clean, note it. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + } + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + } + } + } + + +NTSTATUS +FatSearchBufferForLabel( + IN PIRP_CONTEXT IrpContext, + IN PVPB Vpb, + IN PVOID Buffer, + IN ULONG Size, + OUT PBOOLEAN LabelFound + ) +/*++ + +Routine Description: + + Search a buffer (taken from the root directory) for a volume label + matching the label in the + +Arguments: + + IrpContext - Supplies our irp context + Vpb - Vpb supplying the volume label + Buffer - Supplies the buffer we'll search + Size - The size of the buffer in bytes. + LabelFound - Returns whether a label was found. + +Return Value: + + There are four interesting cases: + + 1) Some random error occurred - that error returned as status, LabelFound + is indeterminate. + + 2) No label was found - STATUS_SUCCESS returned, LabelFound is FALSE. + + 3) A matching label was found - STATUS_SUCCESS returned, LabelFound is TRUE. + + 4) A non-matching label found - STATUS_WRONG_VOLUME returned, LabelFound + is indeterminate. + +--*/ + +{ + NTSTATUS Status; + WCHAR UnicodeBuffer[11]; + + PDIRENT Dirent; + PDIRENT TerminationDirent; + ULONG VolumeLabelLength; + OEM_STRING OemString; + UNICODE_STRING UnicodeString; + + Dirent = Buffer; + + TerminationDirent = Dirent + Size / sizeof(DIRENT); + + while ( Dirent < TerminationDirent ) { + + if ( Dirent->FileName[0] == FAT_DIRENT_NEVER_USED ) { + + Dirent = TerminationDirent; + break; + } + + // + // If the entry is the non-deleted volume label break from the loop. + // + // Note that all out parameters are already correctly set. + // + + if (((Dirent->Attributes & ~FAT_DIRENT_ATTR_ARCHIVE) == + FAT_DIRENT_ATTR_VOLUME_ID) && + (Dirent->FileName[0] != FAT_DIRENT_DELETED)) { + + break; + } + + Dirent += 1; + } + + if (Dirent >= TerminationDirent) { + + // + // We've run out of buffer. + // + + *LabelFound = FALSE; + return STATUS_SUCCESS; + } + + + // + // Compute the length of the volume name + // + +#ifndef __REACTOS__ + OemString.Buffer = &Dirent->FileName[0]; +#else + OemString.Buffer = (PCHAR)&Dirent->FileName[0]; +#endif + OemString.MaximumLength = 11; + + for ( OemString.Length = 11; + OemString.Length > 0; + OemString.Length -= 1) { + + if ( (Dirent->FileName[OemString.Length-1] != 0x00) && + (Dirent->FileName[OemString.Length-1] != 0x20) ) { break; } + } + + UnicodeString.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH; + UnicodeString.Buffer = &UnicodeBuffer[0]; + + Status = RtlOemStringToCountedUnicodeString( &UnicodeString, + &OemString, + FALSE ); + + if ( !NT_SUCCESS( Status ) ) { + + return Status; + } + + VolumeLabelLength = UnicodeString.Length; + + if ( (VolumeLabelLength != (ULONG)Vpb->VolumeLabelLength) || + (!RtlEqualMemory(&UnicodeBuffer[0], + &Vpb->VolumeLabel[0], + VolumeLabelLength)) ) { + + return STATUS_WRONG_VOLUME; + } + + // + // We found a matching label. + // + + *LabelFound = TRUE; + return STATUS_SUCCESS; +} + + +VOID +FatVerifyLookupFatEntry ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN ULONG FatIndex, + IN OUT PULONG FatEntry + ) +{ + ULONG PageEntryOffset; + ULONG OffsetIntoVolumeFile; + PVOID Buffer; + + ASSERT(Vcb->AllocationSupport.FatIndexBitSize == 32); + + FatVerifyIndexIsValid( IrpContext, Vcb, FatIndex); + + Buffer = FsRtlAllocatePoolWithTag( NonPagedPoolCacheAligned, + PAGE_SIZE, + TAG_ENTRY_LOOKUP_BUFFER ); + + OffsetIntoVolumeFile = FatReservedBytes(&Vcb->Bpb) + FatIndex * sizeof(ULONG); + PageEntryOffset = (OffsetIntoVolumeFile % PAGE_SIZE) / sizeof(ULONG); + + _SEH2_TRY { + + FatPerformVerifyDiskRead( IrpContext, + Vcb, + Buffer, + OffsetIntoVolumeFile & ~(PAGE_SIZE - 1), + PAGE_SIZE, + TRUE ); + + *FatEntry = ((PULONG)(Buffer))[PageEntryOffset]; + + } _SEH2_FINALLY { + + ExFreePool( Buffer ); + } _SEH2_END; +} + +// +// Local support routine +// + +VOID +FatScanForDismountedVcb ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine walks through the list of Vcb's looking for any which may + now be deleted. They may have been left on the list because there were + outstanding references. + +Arguments: + +Return Value: + + None + +--*/ + +{ + PVCB Vcb; + PLIST_ENTRY Links; + BOOLEAN VcbDeleted; + + PAGED_CODE(); + + // + // Walk through all of the Vcb's attached to the global data. + // + + FatAcquireExclusiveGlobal( IrpContext ); + + Links = FatData.VcbQueue.Flink; + + while (Links != &FatData.VcbQueue) { + + Vcb = CONTAINING_RECORD( Links, VCB, VcbLinks ); + + // + // Move to the next link now since the current Vcb may be deleted. + // + + Links = Links->Flink; + + // + // Try to acquire the VCB for exclusive access. If we cannot, just skip + // it for now. + // + + if (!ExAcquireResourceExclusiveLite( &(Vcb->Resource), FALSE )) { + + continue; + } + + // + // Check if this Vcb can go away. + // + + VcbDeleted = FatCheckForDismount ( IrpContext, + Vcb, + FALSE ); + + // + // If the VCB was not deleted, release it. + // + + if (!VcbDeleted) { + + ExReleaseResourceLite( &(Vcb->Resource) ); + } + } + + FatReleaseGlobal( IrpContext ); + + return; +} + + diff --git a/drivers/filesystems/fastfat_new/fspdisp.c b/drivers/filesystems/fastfat_new/fspdisp.c new file mode 100644 index 00000000000..51f6d2fbd6d --- /dev/null +++ b/drivers/filesystems/fastfat_new/fspdisp.c @@ -0,0 +1,472 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + FspDisp.c + +Abstract: + + This module implements the main dispatch procedure/thread for the Fat + Fsp + + +--*/ + +#include "fatprocs.h" + +// +// Internal support routine, spinlock wrapper. +// + +PVOID +FatRemoveOverflowEntry ( + IN PVOLUME_DEVICE_OBJECT VolDo + ); + +// +// Define our local debug trace level +// + +#define Dbg (DEBUG_TRACE_FSP_DISPATCHER) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatFspDispatch) +#endif + + +VOID +NTAPI +FatFspDispatch ( + IN PVOID Context + ) + +/*++ + +Routine Description: + + This is the main FSP thread routine that is executed to receive + and dispatch IRP requests. Each FSP thread begins its execution here. + There is one thread created at system initialization time and subsequent + threads created as needed. + +Arguments: + + + Context - Supplies the thread id. + +Return Value: + + None - This routine never exits + +--*/ + +{ + NTSTATUS Status; + + PIRP Irp; + PIRP_CONTEXT IrpContext; + PIO_STACK_LOCATION IrpSp; + BOOLEAN VcbDeleted; + + PVOLUME_DEVICE_OBJECT VolDo; + + IrpContext = (PIRP_CONTEXT)Context; + + Irp = IrpContext->OriginatingIrp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Now because we are the Fsp we will force the IrpContext to + // indicate true on Wait. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_IN_FSP); + + // + // If this request has an associated volume device object, remember it. + // + + if ( IrpSp->FileObject != NULL ) { + + VolDo = CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject ); + } else { + + VolDo = NULL; + } + + // + // Now case on the function code. For each major function code, + // either call the appropriate FSP routine or case on the minor + // function and then call the FSP routine. The FSP routine that + // we call is responsible for completing the IRP, and not us. + // That way the routine can complete the IRP and then continue + // post processing as required. For example, a read can be + // satisfied right away and then read can be done. + // + // We'll do all of the work within an exception handler that + // will be invoked if ever some underlying operation gets into + // trouble (e.g., if FatReadSectorsSync has trouble). + // + + while ( TRUE ) { + + DebugTrace(0, Dbg, "FatFspDispatch: Irp = 0x%08lx\n", Irp); + + // + // If this Irp was top level, note it in our thread local storage. + // + + FsRtlEnterFileSystem(); + + if ( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) ) { + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + + } else { + + IoSetTopLevelIrp( Irp ); + } + + _SEH2_TRY { + + switch ( IrpContext->MajorFunction ) { + + // + // For Create Operation, + // + + case IRP_MJ_CREATE: + + (VOID) FatCommonCreate( IrpContext, Irp ); + break; + + // + // For close operations. We do a little kludge here in case + // this close causes a volume to go away. It will NULL the + // VolDo local variable so that we will not try to look at + // the overflow queue. + // + + case IRP_MJ_CLOSE: + + { + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + TYPE_OF_OPEN TypeOfOpen; + + // + // Extract and decode the file object + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + // + // Do the close. We have a slightly different format + // for this call because of the async closes. + // + + Status = FatCommonClose( Vcb, + Fcb, + Ccb, + TypeOfOpen, + TRUE, + &VcbDeleted ); + + // + // If the VCB was deleted, do not try to access it later. + // + + if (VcbDeleted) { + + VolDo = NULL; + } + + ASSERT(Status == STATUS_SUCCESS); + + FatCompleteRequest( IrpContext, Irp, Status ); + + break; + } + + // + // For read operations + // + + case IRP_MJ_READ: + + (VOID) FatCommonRead( IrpContext, Irp ); + break; + + // + // For write operations, + // + + case IRP_MJ_WRITE: + + (VOID) FatCommonWrite( IrpContext, Irp ); + break; + + // + // For Query Information operations, + // + + case IRP_MJ_QUERY_INFORMATION: + + (VOID) FatCommonQueryInformation( IrpContext, Irp ); + break; + + // + // For Set Information operations, + // + + case IRP_MJ_SET_INFORMATION: + + (VOID) FatCommonSetInformation( IrpContext, Irp ); + break; + + // + // For Query EA operations, + // + + case IRP_MJ_QUERY_EA: + + (VOID) FatCommonQueryEa( IrpContext, Irp ); + break; + + // + // For Set EA operations, + // + + case IRP_MJ_SET_EA: + + (VOID) FatCommonSetEa( IrpContext, Irp ); + break; + + // + // For Flush buffers operations, + // + + case IRP_MJ_FLUSH_BUFFERS: + + (VOID) FatCommonFlushBuffers( IrpContext, Irp ); + break; + + // + // For Query Volume Information operations, + // + + case IRP_MJ_QUERY_VOLUME_INFORMATION: + + (VOID) FatCommonQueryVolumeInfo( IrpContext, Irp ); + break; + + // + // For Set Volume Information operations, + // + + case IRP_MJ_SET_VOLUME_INFORMATION: + + (VOID) FatCommonSetVolumeInfo( IrpContext, Irp ); + break; + + // + // For File Cleanup operations, + // + + case IRP_MJ_CLEANUP: + + (VOID) FatCommonCleanup( IrpContext, Irp ); + break; + + // + // For Directory Control operations, + // + + case IRP_MJ_DIRECTORY_CONTROL: + + (VOID) FatCommonDirectoryControl( IrpContext, Irp ); + break; + + // + // For File System Control operations, + // + + case IRP_MJ_FILE_SYSTEM_CONTROL: + + (VOID) FatCommonFileSystemControl( IrpContext, Irp ); + break; + + // + // For Lock Control operations, + // + + case IRP_MJ_LOCK_CONTROL: + + (VOID) FatCommonLockControl( IrpContext, Irp ); + break; + + // + // For Device Control operations, + // + + case IRP_MJ_DEVICE_CONTROL: + + (VOID) FatCommonDeviceControl( IrpContext, Irp ); + break; + + // + // For the Shutdown operation, + // + + case IRP_MJ_SHUTDOWN: + + (VOID) FatCommonShutdown( IrpContext, Irp ); + break; + + // + // For plug and play operations. + // + + case IRP_MJ_PNP: + + // + // I don't believe this should ever occur, but allow for the unexpected. + // + + (VOID) FatCommonPnp( IrpContext, Irp ); + break; + + // + // For any other major operations, return an invalid + // request. + // + + default: + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST ); + break; + + } + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code. + // + + (VOID) FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + IoSetTopLevelIrp( NULL ); + + FsRtlExitFileSystem(); + + // + // If there are any entries on this volume's overflow queue, service + // them. + // + + if ( VolDo != NULL ) { + + PVOID Entry; + + // + // We have a volume device object so see if there is any work + // left to do in its overflow queue. + // + + Entry = FatRemoveOverflowEntry( VolDo ); + + // + // There wasn't an entry, break out of the loop and return to + // the Ex Worker thread. + // + + if ( Entry == NULL ) { + + break; + } + + // + // Extract the IrpContext, Irp, and IrpSp, and loop. + // + + IrpContext = CONTAINING_RECORD( Entry, + IRP_CONTEXT, + WorkQueueItem.List ); + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT | IRP_CONTEXT_FLAG_IN_FSP); + + Irp = IrpContext->OriginatingIrp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + continue; + + } else { + + break; + } + } + + // + // Decrement the PostedRequestCount. + // + + if ( VolDo ) { + + ExInterlockedAddUlong( &VolDo->PostedRequestCount, + 0xffffffff, + &VolDo->OverflowQueueSpinLock ); + } + + return; +} + + +// +// Internal support routine, spinlock wrapper. +// + +PVOID +FatRemoveOverflowEntry ( + IN PVOLUME_DEVICE_OBJECT VolDo + ) +{ + PVOID Entry; + KIRQL SavedIrql; + + KeAcquireSpinLock( &VolDo->OverflowQueueSpinLock, &SavedIrql ); + + if (VolDo->OverflowQueueCount > 0) { + + // + // There is overflow work to do in this volume so we'll + // decrement the Overflow count, dequeue the IRP, and release + // the Event + // + + VolDo->OverflowQueueCount -= 1; + + Entry = RemoveHeadList( &VolDo->OverflowQueue ); + + } else { + + Entry = NULL; + } + + KeReleaseSpinLock( &VolDo->OverflowQueueSpinLock, SavedIrql ); + + return Entry; +} + + diff --git a/drivers/filesystems/fastfat_new/lfn.h b/drivers/filesystems/fastfat_new/lfn.h new file mode 100644 index 00000000000..e9ee5c622d2 --- /dev/null +++ b/drivers/filesystems/fastfat_new/lfn.h @@ -0,0 +1,56 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Lfn.h + +Abstract: + + This module defines the on-disk structure of long file names on FAT. + + +--*/ + +#ifndef _LFN_ +#define _LFN_ + +// +// This strucure defines the on disk format on long file name dirents. +// + +typedef struct _PACKED_LFN_DIRENT { + UCHAR Ordinal; // offset = 0 + UCHAR Name1[10]; // offset = 1 (Really 5 chars, but not WCHAR aligned) + UCHAR Attributes; // offset = 11 + UCHAR Type; // offset = 12 + UCHAR Checksum; // offset = 13 + WCHAR Name2[6]; // offset = 14 + USHORT MustBeZero; // offset = 26 + WCHAR Name3[2]; // offset = 28 +} PACKED_LFN_DIRENT; // sizeof = 32 +typedef PACKED_LFN_DIRENT *PPACKED_LFN_DIRENT; + +#define FAT_LAST_LONG_ENTRY 0x40 // Ordinal field +#define FAT_LONG_NAME_COMP 0x0 // Type field + +// +// A packed lfn dirent is already quadword aligned so simply declare a +// lfn dirent as a packed lfn dirent. +// + +typedef PACKED_LFN_DIRENT LFN_DIRENT; +typedef LFN_DIRENT *PLFN_DIRENT; + +// +// This is the largest size buffer we would ever need to read an Lfn +// + +#define MAX_LFN_CHARACTERS 260 +#define MAX_LFN_DIRENTS 20 + +#define FAT_LFN_DIRENTS_NEEDED(NAME) (((NAME)->Length/sizeof(WCHAR) + 12)/13) + +#endif // _LFN_ + diff --git a/drivers/filesystems/fastfat_new/lockctrl.c b/drivers/filesystems/fastfat_new/lockctrl.c new file mode 100644 index 00000000000..5ba771f827c --- /dev/null +++ b/drivers/filesystems/fastfat_new/lockctrl.c @@ -0,0 +1,726 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + LockCtrl.c + +Abstract: + + This module implements the Lock Control routines for Fat called + by the dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_LOCKCTRL) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonLockControl) +#pragma alloc_text(PAGE, FatFastLock) +#pragma alloc_text(PAGE, FatFastUnlockAll) +#pragma alloc_text(PAGE, FatFastUnlockAllByKey) +#pragma alloc_text(PAGE, FatFastUnlockSingle) +#pragma alloc_text(PAGE, FatFsdLockControl) +#endif + + +NTSTATUS +NTAPI +FatFsdLockControl ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of Lock control operations + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdLockControl\n", 0); + + // + // Call the common Lock Control routine, with blocking allowed if + // synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonLockControl( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdLockControl -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +BOOLEAN +NTAPI +FatFastLock ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + BOOLEAN FailImmediately, + BOOLEAN ExclusiveLock, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast lock call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + FileOffset - Supplies the file offset used in this operation + + Length - Supplies the length used in this operation + + ProcessId - Supplies the process ID used in this operation + + Key - Supplies the key used in this operation + + FailImmediately - Indicates if the request should fail immediately + if the lock cannot be granted. + + ExclusiveLock - Indicates if this is a request for an exclusive or + shared lock + + IoStatus - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + DebugTrace(+1, Dbg, "FatFastLock\n", 0); + + // + // Decode the type of file object we're being asked to process and make + // sure it is only a user file open. + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + IoStatus->Information = 0; + + DebugTrace(-1, Dbg, "FatFastLock -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); + return TRUE; + } + + // + // Acquire exclusive access to the Fcb this operation can always wait + // + + FsRtlEnterFileSystem(); + + _SEH2_TRY { + + // + // We check whether we can proceed + // based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( &(Fcb)->Specific.Fcb.Oplock )) { + + try_return( Results = FALSE ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request + // + +#ifndef __REACTOS__ + if (Results = FsRtlFastLock( &Fcb->Specific.Fcb.FileLock, +#else + Results = FsRtlFastLock( &Fcb->Specific.Fcb.FileLock, +#endif + FileObject, + FileOffset, + Length, + ProcessId, + Key, + FailImmediately, + ExclusiveLock, + IoStatus, + NULL, +#ifndef __REACTOS__ + FALSE )) { +#else + FALSE ); + + if (Results) { +#endif + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + } + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatFastLock ); + + // + // Release the Fcb, and return to our caller + // + + FsRtlExitFileSystem(); + + DebugTrace(-1, Dbg, "FatFastLock -> %08lx\n", Results); + } _SEH2_END; + + return Results; +} + + +BOOLEAN +NTAPI +FatFastUnlockSingle ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN PLARGE_INTEGER Length, + PEPROCESS ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast unlock single call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + FileOffset - Supplies the file offset used in this operation + + Length - Supplies the length used in this operation + + ProcessId - Supplies the process ID used in this operation + + Key - Supplies the key used in this operation + + Status - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + DebugTrace(+1, Dbg, "FatFastUnlockSingle\n", 0); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and make sure + // it is only a user file open + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "FatFastUnlockSingle -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); + return TRUE; + } + + // + // Acquire exclusive access to the Fcb this operation can always wait + // + + FsRtlEnterFileSystem(); + + _SEH2_TRY { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( &(Fcb)->Specific.Fcb.Oplock )) { + + try_return( Results = FALSE ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request. The call will always succeed. + // + + Results = TRUE; + IoStatus->Status = FsRtlFastUnlockSingle( &Fcb->Specific.Fcb.FileLock, + FileObject, + FileOffset, + Length, + ProcessId, + Key, + NULL, + FALSE ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatFastUnlockSingle ); + + // + // Release the Fcb, and return to our caller + // + + FsRtlExitFileSystem(); + + DebugTrace(-1, Dbg, "FatFastUnlockSingle -> %08lx\n", Results); + } _SEH2_END; + + return Results; +} + + +BOOLEAN +NTAPI +FatFastUnlockAll ( + IN PFILE_OBJECT FileObject, + PEPROCESS ProcessId, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast unlock all call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + ProcessId - Supplies the process ID used in this operation + + Status - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + DebugTrace(+1, Dbg, "FatFastUnlockAll\n", 0); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and make sure + // it is only a user file open. + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "FatFastUnlockAll -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); + return TRUE; + } + + // + // Acquire exclusive access to the Fcb this operation can always wait + // + + FsRtlEnterFileSystem(); + + (VOID) ExAcquireResourceSharedLite( Fcb->Header.Resource, TRUE ); + + _SEH2_TRY { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( &(Fcb)->Specific.Fcb.Oplock )) { + + try_return( Results = FALSE ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request. The call will always succeed. + // + + Results = TRUE; + IoStatus->Status = FsRtlFastUnlockAll( &Fcb->Specific.Fcb.FileLock, + FileObject, + ProcessId, + NULL ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatFastUnlockAll ); + + // + // Release the Fcb, and return to our caller + // + + ExReleaseResourceLite( (Fcb)->Header.Resource ); + + FsRtlExitFileSystem(); + + DebugTrace(-1, Dbg, "FatFastUnlockAll -> %08lx\n", Results); + } _SEH2_END; + + return Results; +} + + +BOOLEAN +NTAPI +FatFastUnlockAllByKey ( + IN PFILE_OBJECT FileObject, + PVOID ProcessId, + ULONG Key, + OUT PIO_STATUS_BLOCK IoStatus, + IN PDEVICE_OBJECT DeviceObject + ) + +/*++ + +Routine Description: + + This is a call back routine for doing the fast unlock all by key call. + +Arguments: + + FileObject - Supplies the file object used in this operation + + ProcessId - Supplies the process ID used in this operation + + Key - Supplies the key used in this operation + + Status - Receives the Status if this operation is successful + +Return Value: + + BOOLEAN - TRUE if this operation completed and FALSE if caller + needs to take the long route. + +--*/ + +{ + BOOLEAN Results; + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + DebugTrace(+1, Dbg, "FatFastUnlockAllByKey\n", 0); + + IoStatus->Information = 0; + + // + // Decode the type of file object we're being asked to process and make sure + // it is only a user file open. + // + + if (FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ) != UserFileOpen) { + + IoStatus->Status = STATUS_INVALID_PARAMETER; + + DebugTrace(-1, Dbg, "FatFastUnlockAll -> TRUE (STATUS_INVALID_PARAMETER)\n", 0); + return TRUE; + } + + // + // Acquire exclusive access to the Fcb this operation can always wait + // + + FsRtlEnterFileSystem(); + + (VOID) ExAcquireResourceSharedLite( Fcb->Header.Resource, TRUE ); + + _SEH2_TRY { + + // + // We check whether we can proceed based on the state of the file oplocks. + // + + if (!FsRtlOplockIsFastIoPossible( &(Fcb)->Specific.Fcb.Oplock )) { + + try_return( Results = FALSE ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request. The call will always succeed. + // + + Results = TRUE; + IoStatus->Status = FsRtlFastUnlockAllByKey( &Fcb->Specific.Fcb.FileLock, + FileObject, + ProcessId, + Key, + NULL ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatFastUnlockAllByKey ); + + // + // Release the Fcb, and return to our caller + // + + ExReleaseResourceLite( (Fcb)->Header.Resource ); + + FsRtlExitFileSystem(); + + DebugTrace(-1, Dbg, "FatFastUnlockAllByKey -> %08lx\n", Results); + } _SEH2_END; + + return Results; +} + + +NTSTATUS +FatCommonLockControl ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing Lock control operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + TYPE_OF_OPEN TypeOfOpen; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + BOOLEAN OplockPostIrp = FALSE; + + // + // Get a pointer to the current Irp stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonLockControl\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp); + DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction); + + // + // Decode the type of file object we're being asked to process + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + // + // If the file is not a user file open then we reject the request + // as an invalid parameter + // + + if (TypeOfOpen != UserFileOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER ); + + DebugTrace(-1, Dbg, "FatCommonLockControl -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + + // + // Acquire exclusive access to the Fcb and enqueue the Irp if we didn't + // get access + // + + if (!FatAcquireSharedFcb( IrpContext, Fcb )) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonLockControl -> %08lx\n", Status); + return Status; + } + + _SEH2_TRY { + + // + // We check whether we can proceed + // based on the state of the file oplocks. + // + + Status = FsRtlCheckOplock( &Fcb->Specific.Fcb.Oplock, + Irp, + IrpContext, + FatOplockComplete, + NULL ); + + if (Status != STATUS_SUCCESS) { + + OplockPostIrp = TRUE; + try_return( NOTHING ); + } + + // + // Now call the FsRtl routine to do the actual processing of the + // Lock request + // + + Status = FsRtlProcessFileLock( &Fcb->Specific.Fcb.FileLock, Irp, NULL ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatCommonLockControl ); + + // + // Only if this is not an abnormal termination do we delete the + // irp context + // + + if (!_SEH2_AbnormalTermination() && !OplockPostIrp) { + + FatCompleteRequest( IrpContext, FatNull, 0 ); + } + + // + // Release the Fcb, and return to our caller + // + + FatReleaseFcb( IrpContext, Fcb ); + + DebugTrace(-1, Dbg, "FatCommonLockControl -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + diff --git a/drivers/filesystems/fastfat_new/namesup.c b/drivers/filesystems/fastfat_new/namesup.c new file mode 100644 index 00000000000..2f4e839a1da --- /dev/null +++ b/drivers/filesystems/fastfat_new/namesup.c @@ -0,0 +1,1021 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + NameSup.c + +Abstract: + + This module implements the Fat Name support routines + + +--*/ + +#include "fatprocs.h" + +#define Dbg (DEBUG_TRACE_NAMESUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, Fat8dot3ToString) +#pragma alloc_text(PAGE, FatIsNameInExpression) +#pragma alloc_text(PAGE, FatStringTo8dot3) +#pragma alloc_text(PAGE, FatSetFullFileNameInFcb) +#pragma alloc_text(PAGE, FatGetUnicodeNameFromFcb) +#pragma alloc_text(PAGE, FatUnicodeToUpcaseOem) +#pragma alloc_text(PAGE, FatSelectNames) +#pragma alloc_text(PAGE, FatEvaluateNameCase) +#pragma alloc_text(PAGE, FatSpaceInName) +#endif + + +BOOLEAN +FatIsNameInExpression ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING Expression, + IN OEM_STRING Name + ) + +/*++ + +Routine Description: + + This routine compare a name and an expression and tells the caller if + the name is equal to or not equal to the expression. The input name + cannot contain wildcards, while the expression may contain wildcards. + +Arguments: + + Expression - Supplies the input expression to check against + The caller must have already upcased the Expression. + + Name - Supplies the input name to check for. The caller must have + already upcased the name. + +Return Value: + + BOOLEAN - TRUE if Name is an element in the set of strings denoted + by the input Expression and FALSE otherwise. + +--*/ + +{ + // + // Call the appropriate FsRtl routine do to the real work + // + + return FsRtlIsDbcsInExpression( &Expression, + &Name ); + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +VOID +FatStringTo8dot3 ( + IN PIRP_CONTEXT IrpContext, + IN OEM_STRING InputString, + OUT PFAT8DOT3 Output8dot3 + ) + +/*++ + +Routine Description: + + Convert a string into fat 8.3 format. The string must not contain + any wildcards. + +Arguments: + + InputString - Supplies the input string to convert + + Output8dot3 - Receives the converted string, the memory must be supplied + by the caller. + +Return Value: + + None. + +--*/ + +{ + ULONG i; + ULONG j; + + DebugTrace(+1, Dbg, "FatStringTo8dot3\n", 0); + DebugTrace( 0, Dbg, "InputString = %Z\n", &InputString); + + ASSERT( InputString.Length <= 12 ); + + // + // Make the output name all blanks + // + + RtlFillMemory( Output8dot3, 11, UCHAR_SP ); + + // + // Copy over the first part of the file name. Stop when we get to + // the end of the input string or a dot. + // + + for (i = 0; + (i < (ULONG)InputString.Length) && (InputString.Buffer[i] != '.'); + i += 1) { + + (*Output8dot3)[i] = InputString.Buffer[i]; + } + + // + // Check if we need to process an extension + // + + if (i < (ULONG)InputString.Length) { + + // + // Make sure we have a dot and then skip over it. + // + + ASSERT( (InputString.Length - i) <= 4 ); + ASSERT( InputString.Buffer[i] == '.' ); + + i += 1; + + // + // Copy over the extension. Stop when we get to the + // end of the input string. + // + + for (j = 8; (i < (ULONG)InputString.Length); j += 1, i += 1) { + + (*Output8dot3)[j] = InputString.Buffer[i]; + } + } + + // + // Before we return check if we should translate the first character + // from 0xe5 to 0x5. + // + + if ((*Output8dot3)[0] == 0xe5) { + + (*Output8dot3)[0] = FAT_DIRENT_REALLY_0E5; + } + + DebugTrace(-1, Dbg, "FatStringTo8dot3 -> (VOID)\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +VOID +Fat8dot3ToString ( + IN PIRP_CONTEXT IrpContext, + IN PDIRENT Dirent, + IN BOOLEAN RestoreCase, + OUT POEM_STRING OutputString + ) + +/*++ + +Routine Description: + + Convert fat 8.3 format into a string. The 8.3 name must be well formed. + +Arguments: + + Dirent - Supplies the input 8.3 name to convert + + RestoreCase - If TRUE, then the magic reserved bits are used to restore + the original case. + + OutputString - Receives the converted name, the memory must be supplied + by the caller. + +Return Value: + + None + +--*/ + +{ +#ifndef __REACTOS__ + ULONG DirentIndex, StringIndex; +#else + ULONG StringIndex; +#endif + ULONG BaseLength, ExtensionLength; + + DebugTrace(+1, Dbg, "Fat8dot3ToString\n", 0); + + // + // First, find the length of the base component. + // + + for (BaseLength = 8; BaseLength > 0; BaseLength -= 1) { + + if (Dirent->FileName[BaseLength - 1] != UCHAR_SP) { + + break; + } + } + + // + // Now find the length of the extension. + // + + for (ExtensionLength = 3; ExtensionLength > 0; ExtensionLength -= 1) { + + if (Dirent->FileName[8 + ExtensionLength - 1] != UCHAR_SP) { + + break; + } + } + + // + // If there was a base part, copy it and check the case. Don't forget + // if the first character needs to be changed from 0x05 to 0xe5. + // + + if (BaseLength != 0) { + + RtlCopyMemory( OutputString->Buffer, Dirent->FileName, BaseLength ); + + if (OutputString->Buffer[0] == FAT_DIRENT_REALLY_0E5) { + + OutputString->Buffer[0] = (CHAR)0xe5; + } + + // + // Now if we are to restore case, look for A-Z + // + + if (FatData.ChicagoMode && + RestoreCase && + FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) { + + for (StringIndex = 0; StringIndex < BaseLength; StringIndex += 1) { + + // + // Depending on whether the media was built in a system that was + // running with "code page invariance" (see FatEvaluateNameCase), + // there could be double-byte OEM characters lying in wait here. + // Gotta skip them. + // + + if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) { + + StringIndex += 1; + continue; + } + + if ((OutputString->Buffer[StringIndex] >= 'A') && + (OutputString->Buffer[StringIndex] <= 'Z')) { + + OutputString->Buffer[StringIndex] += 'a' - 'A'; + } + } + } + } + + // + // If there was an extension, copy that over. Else we now know the + // size of the string. + // + + if (ExtensionLength != 0) { + + PUCHAR o, d; + + // + // Now add the dot + // + + OutputString->Buffer[BaseLength++] = '.'; + + // + // Copy over the extension into the output buffer. + // + +#ifndef __REACTOS__ + o = &OutputString->Buffer[BaseLength]; +#else + o = (PUCHAR)&OutputString->Buffer[BaseLength]; +#endif + d = &Dirent->FileName[8]; + + switch (ExtensionLength) { + case 3: + *o++ = *d++; + case 2: + *o++ = *d++; + case 1: + *o++ = *d++; + } + + // + // Set the output string length + // + + OutputString->Length = (USHORT)(BaseLength + ExtensionLength); + + // + // Now if we are to restore case, look for A-Z + // + + if (FatData.ChicagoMode && + RestoreCase && + FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) { + + for (StringIndex = BaseLength; + StringIndex < OutputString->Length; + StringIndex++ ) { + + // + // Depending on whether the media was built in a system that was + // running with "code page invariance" (see FatEvaluateNameCase), + // there could be double-byte OEM characters lying in wait here. + // Gotta skip them. + // + + if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) { + + StringIndex += 1; + continue; + } + + if ((OutputString->Buffer[StringIndex] >= 'A') && + (OutputString->Buffer[StringIndex] <= 'Z')) { + + OutputString->Buffer[StringIndex] += 'a' - 'A'; + } + } + } + + } else { + + // + // Set the output string length + // + + OutputString->Length = (USHORT)BaseLength; + } + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "Fat8dot3ToString, OutputString = \"%Z\" -> VOID\n", OutputString); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + +VOID +FatGetUnicodeNameFromFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PUNICODE_STRING Lfn + ) + +/*++ + +Routine Description: + + This routine will return the unicode name for a given Fcb. If the + file has an LFN, it will return this. Otherwise it will return + the UNICODE conversion of the Oem name, properly cased. + +Arguments: + + Fcb - Supplies the Fcb to query. + + Lfn - Supplies a string that already has enough storage for the + full unicode name. + +Return Value: + + None + +--*/ + +{ + PDIRENT Dirent; + PBCB DirentBcb = NULL; + ULONG DirentByteOffset; + + CCB LocalCcb; + + ASSERT((MAX_LFN_CHARACTERS * sizeof( WCHAR)) == Lfn->MaximumLength); + + // + // We'll start by locating the dirent for the name. + // + + FatStringTo8dot3( IrpContext, + Fcb->ShortName.Name.Oem, + &LocalCcb.OemQueryTemplate.Constant ); + + LocalCcb.Flags = 0; + LocalCcb.UnicodeQueryTemplate.Length = 0; + LocalCcb.ContainsWildCards = FALSE; + + FatLocateDirent( IrpContext, + Fcb->ParentDcb, + &LocalCcb, + Fcb->LfnOffsetWithinDirectory, + &Dirent, + &DirentBcb, +#ifndef __REACTOS__ + &DirentByteOffset, +#else + (PVBO)&DirentByteOffset, +#endif + NULL, + Lfn); + _SEH2_TRY { + + // + // If we didn't find the Dirent, something is terribly wrong. + // + + if ((DirentBcb == NULL) || + (DirentByteOffset != Fcb->DirentOffsetWithinDirectory)) { + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + + // + // Check for the easy case. + // + + if (Lfn->Length == 0) { + + NTSTATUS Status; + OEM_STRING ShortName; + UCHAR ShortNameBuffer[12]; + + // + // If we thought that there was an LFN here and didn't find one, + // we're as dead. This shouldn't happen in normal operation, but + // if someone scrambles a directory by hand ... + // + + ASSERT( Fcb->LfnOffsetWithinDirectory == Fcb->DirentOffsetWithinDirectory ); + + if (Fcb->LfnOffsetWithinDirectory != Fcb->DirentOffsetWithinDirectory) { + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + + // + // There is no LFN, so manufacture a UNICODE name. + // + + ShortName.Length = 0; + ShortName.MaximumLength = 12; +#ifndef __REACTOS__ + ShortName.Buffer = ShortNameBuffer; +#else + ShortName.Buffer = (PCHAR)ShortNameBuffer; +#endif + + Fat8dot3ToString( IrpContext, Dirent, TRUE, &ShortName ); + + // + // OK, now convert this string to UNICODE + // + + Status = RtlOemStringToCountedUnicodeString( Lfn, + &ShortName, + FALSE ); + + ASSERT( Status == STATUS_SUCCESS ); + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, DirentBcb ); + } _SEH2_END; +} + +VOID +FatSetFullFileNameInFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + If the FullFileName field in the Fcb has not yet been filled in, we + proceed to do so. + +Arguments: + + Fcb - Supplies the file. + +Return Value: + + None + +--*/ + +{ + if (Fcb->FullFileName.Buffer == NULL) { + + UNICODE_STRING Lfn; + PFCB TmpFcb = Fcb; + PFCB StopFcb; + PWCHAR TmpBuffer; + ULONG PathLength = 0; + + // + // We will assume we do this infrequently enough, that it's OK to + // to a pool allocation here. + // + + Lfn.Length = 0; + Lfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR); + Lfn.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + MAX_LFN_CHARACTERS * sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + + _SEH2_TRY { + + // + // First determine how big the name will be. If we find an + // ancestor with a FullFileName, our work is easier. + // + + while (TmpFcb != Fcb->Vcb->RootDcb) { + + if ((TmpFcb != Fcb) && (TmpFcb->FullFileName.Buffer != NULL)) { + + PathLength += TmpFcb->FullFileName.Length; + + Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + PathLength, + TAG_FILENAME_BUFFER ); + + RtlCopyMemory( Fcb->FullFileName.Buffer, + TmpFcb->FullFileName.Buffer, + TmpFcb->FullFileName.Length ); + + break; + } + + PathLength += TmpFcb->FinalNameLength + sizeof(WCHAR); + + TmpFcb = TmpFcb->ParentDcb; + } + + // + // If FullFileName.Buffer is still NULL, allocate it. + // + + if (Fcb->FullFileName.Buffer == NULL) { + + Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + PathLength, + TAG_FILENAME_BUFFER ); + } + + StopFcb = TmpFcb; + + TmpFcb = Fcb; + TmpBuffer = Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR); + + Fcb->FullFileName.Length = + Fcb->FullFileName.MaximumLength = (USHORT)PathLength; + + while (TmpFcb != StopFcb) { + + FatGetUnicodeNameFromFcb( IrpContext, + TmpFcb, + &Lfn ); + + TmpBuffer -= Lfn.Length / sizeof(WCHAR); + + RtlCopyMemory( TmpBuffer, Lfn.Buffer, Lfn.Length ); + + TmpBuffer -= 1; + + *TmpBuffer = L'\\'; + + TmpFcb = TmpFcb->ParentDcb; + } + + } _SEH2_FINALLY { + + if (_SEH2_AbnormalTermination()) { + + if (Fcb->FullFileName.Buffer) { + + ExFreePool( Fcb->FullFileName.Buffer ); + Fcb->FullFileName.Buffer = NULL; + } + } + + ExFreePool( Lfn.Buffer ); + } _SEH2_END; + } +} + +VOID +FatUnicodeToUpcaseOem ( + IN PIRP_CONTEXT IrpContext, + IN POEM_STRING OemString, + IN PUNICODE_STRING UnicodeString + ) + +/*++ + +Routine Description: + + This routine is our standard routine for trying to use stack space + if possible when calling RtlUpcaseUnicodeStringToCountedOemString(). + + If an unmappable character is encountered, we set the destination + length to 0. + +Arguments: + + OemString - Specifies the destination string. Space is already assumed to + be allocated. If there is not enough, then we allocate enough + space. + + UnicodeString - Specifies the source string. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + + Status = RtlUpcaseUnicodeStringToCountedOemString( OemString, + UnicodeString, + FALSE ); + + if (Status == STATUS_BUFFER_OVERFLOW) { + + OemString->Buffer = NULL; + OemString->Length = 0; + OemString->MaximumLength = 0; + + Status = RtlUpcaseUnicodeStringToCountedOemString( OemString, + UnicodeString, + TRUE ); + } + + if (!NT_SUCCESS(Status)) { + + if (Status == STATUS_UNMAPPABLE_CHARACTER) { + + OemString->Length = 0; + + } else { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + + return; +} + + +VOID +FatSelectNames ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Parent, + IN POEM_STRING OemName, + IN PUNICODE_STRING UnicodeName, + IN OUT POEM_STRING ShortName, + IN PUNICODE_STRING SuggestedShortName OPTIONAL, + IN OUT BOOLEAN *AllLowerComponent, + IN OUT BOOLEAN *AllLowerExtension, + IN OUT BOOLEAN *CreateLfn + ) + +/*++ + +Routine Description: + + This routine takes the original UNICODE string that the user specified, + and the upcased Oem equivolent. This routine then decides if the OemName + is acceptable for dirent, or whether a short name has to be manufactured. + + Two values are returned to the caller. One tells the caller if the name + happens to be all lower case < 0x80. In this special case we don't + have to create an Lfn. Also we tell the caller if it must create an LFN. + +Arguments: + + OemName - Supplies the proposed short Oem name. + + ShortName - If this OemName is OK for storeage in a dirent it is copied to + this string, otherwise this string is filled with a name that is OK. + If OemName and ShortName are the same string, no copy is done. + + UnicodeName - Provides the original final name. + + SuggestedShortName - a first-try short name to try before auto-generation + is used + + AllLowerComponent - Returns whether this component was all lower case. + + AllLowerExtension - Returns wheather the extension was all lower case. + + CreateLfn - Tells the caller if we must create an LFN for the UnicodeName or + SuggestedLongName + +Return Value: + + None. + +--*/ + +{ + BOOLEAN GenerateShortName; + + PAGED_CODE(); + + // + // First see if we must generate a short name. + // + + if ((OemName->Length == 0) || + !FatIsNameShortOemValid( IrpContext, *OemName, FALSE, FALSE, FALSE ) || + FatSpaceInName( IrpContext, UnicodeName )) { + + WCHAR ShortNameBuffer[12]; + UNICODE_STRING ShortUnicodeName; + GENERATE_NAME_CONTEXT Context; + BOOLEAN TrySuggestedShortName; + + PDIRENT Dirent; + PBCB Bcb = NULL; + ULONG ByteOffset; + NTSTATUS Status; + + GenerateShortName = TRUE; + + TrySuggestedShortName = (SuggestedShortName != NULL); + + // + // Now generate a short name. + // + + ShortUnicodeName.Length = 0; + ShortUnicodeName.MaximumLength = 12 * sizeof(WCHAR); + ShortUnicodeName.Buffer = ShortNameBuffer; + + RtlZeroMemory( &Context, sizeof( GENERATE_NAME_CONTEXT ) ); + + _SEH2_TRY { + + while ( TRUE ) { + + FatUnpinBcb( IrpContext, Bcb ); + + if (TrySuggestedShortName) { + + // + // Try our caller's candidate first. Note that this must have + // been uppercased previously. + // + + ShortUnicodeName.Length = SuggestedShortName->Length; + ShortUnicodeName.MaximumLength = SuggestedShortName->MaximumLength; + ShortUnicodeName.Buffer = SuggestedShortName->Buffer; + + TrySuggestedShortName = FALSE; + + } else { + + RtlGenerate8dot3Name( UnicodeName, TRUE, &Context, &ShortUnicodeName ); + } + + // + // We have a candidate, make sure it doesn't exist. + // + + Status = RtlUnicodeStringToCountedOemString( ShortName, + &ShortUnicodeName, + FALSE ); + + ASSERT( Status == STATUS_SUCCESS ); + + FatLocateSimpleOemDirent( IrpContext, + Parent, + ShortName, + &Dirent, + &Bcb, +#ifndef __REACTOS__ + &ByteOffset ); +#else + (PVBO)&ByteOffset ); +#endif + + if (Bcb == NULL) { + + _SEH2_LEAVE; + + } + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, Bcb ); + } _SEH2_END; + + } else { + + // + // Only do this copy if the two string are indeed different. + // + + if (ShortName != OemName) { + + ShortName->Length = OemName->Length; + RtlCopyMemory( ShortName->Buffer, OemName->Buffer, OemName->Length ); + } + + GenerateShortName = FALSE; + } + + // + // Now see if the caller will have to use unicode string as an LFN + // + + if (GenerateShortName) { + + *CreateLfn = TRUE; + *AllLowerComponent = FALSE; + *AllLowerExtension = FALSE; + + } else { + + FatEvaluateNameCase( IrpContext, + UnicodeName, + AllLowerComponent, + AllLowerExtension, + CreateLfn ); + } + + return; +} + + +VOID +FatEvaluateNameCase ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING UnicodeName, + IN OUT BOOLEAN *AllLowerComponent, + IN OUT BOOLEAN *AllLowerExtension, + IN OUT BOOLEAN *CreateLfn + ) + +/*++ + +Routine Description: + + This routine takes a UNICODE string and sees if it is eligible for + the special case optimization. + +Arguments: + + UnicodeName - Provides the original final name. + + AllLowerComponent - Returns whether this compoent was all lower case. + + AllLowerExtension - Returns wheather the extension was all lower case. + + CreateLfn - Tells the call if we must create an LFN for the UnicodeName. + +Return Value: + + None. + +--*/ + +{ + ULONG i; + UCHAR Uppers = 0; + UCHAR Lowers = 0; + + BOOLEAN ExtensionPresent = FALSE; + + *CreateLfn = FALSE; + + for (i = 0; i < UnicodeName->Length / sizeof(WCHAR); i++) { + + WCHAR c; + + c = UnicodeName->Buffer[i]; + + if ((c >= 'A') && (c <= 'Z')) { + + Uppers += 1; + + } else if ((c >= 'a') && (c <= 'z')) { + + Lowers += 1; + + } else if ((c >= 0x0080) && FatData.CodePageInvariant) { + + break; + } + + // + // If we come to a period, figure out if the extension was + // all one case. + // + + if (c == L'.') { + + *CreateLfn = (Lowers != 0) && (Uppers != 0); + + *AllLowerComponent = !(*CreateLfn) && (Lowers != 0); + + ExtensionPresent = TRUE; + + // + // Now reset the uppers and lowers count. + // + + Uppers = Lowers = 0; + } + } + + // + // Now check again for creating an LFN. + // + + *CreateLfn = (*CreateLfn || + (i != UnicodeName->Length / sizeof(WCHAR)) || + ((Lowers != 0) && (Uppers != 0))); + + // + // Now we know the final state of CreateLfn, update the two + // "AllLower" booleans. + // + + if (ExtensionPresent) { + + *AllLowerComponent = !(*CreateLfn) && *AllLowerComponent; + *AllLowerExtension = !(*CreateLfn) && (Lowers != 0); + + } else { + + *AllLowerComponent = !(*CreateLfn) && (Lowers != 0); + *AllLowerExtension = FALSE; + } + + return; +} + + +BOOLEAN +FatSpaceInName ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING UnicodeName + ) + +/*++ + +Routine Description: + + This routine takes a UNICODE string and sees if it contains any spaces. + +Arguments: + + UnicodeName - Provides the final name. + +Return Value: + + BOOLEAN - TRUE if it does, FALSE if it doesn't. + +--*/ + +{ + ULONG i; + + for (i=0; i < UnicodeName->Length/sizeof(WCHAR); i++) { + + if (UnicodeName->Buffer[i] == L' ') { + return TRUE; + } + } + + return FALSE; +} + + diff --git a/drivers/filesystems/fastfat_new/nodetype.h b/drivers/filesystems/fastfat_new/nodetype.h new file mode 100644 index 00000000000..ec593e96bf0 --- /dev/null +++ b/drivers/filesystems/fastfat_new/nodetype.h @@ -0,0 +1,193 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + NodeType.h + +Abstract: + + This module defines all of the node type codes used in this development + shell. Every major data structure in the file system is assigned a node + type code that is. This code is the first CSHORT in the structure and is + followed by a CSHORT containing the size, in bytes, of the structure. + + +--*/ + +#ifndef _FATNODETYPE_ +#define _FATNODETYPE_ + +typedef CSHORT NODE_TYPE_CODE; +typedef NODE_TYPE_CODE *PNODE_TYPE_CODE; + +#define NTC_UNDEFINED ((NODE_TYPE_CODE)0x0000) + +#define FAT_NTC_DATA_HEADER ((NODE_TYPE_CODE)0x0500) +#define FAT_NTC_VCB ((NODE_TYPE_CODE)0x0501) +#define FAT_NTC_FCB ((NODE_TYPE_CODE)0x0502) +#define FAT_NTC_DCB ((NODE_TYPE_CODE)0x0503) +#define FAT_NTC_ROOT_DCB ((NODE_TYPE_CODE)0x0504) +#define FAT_NTC_CCB ((NODE_TYPE_CODE)0x0507) +#define FAT_NTC_IRP_CONTEXT ((NODE_TYPE_CODE)0x0508) + +typedef CSHORT NODE_BYTE_SIZE; + +#ifndef NodeType + +// +// So all records start with +// +// typedef struct _RECORD_NAME { +// NODE_TYPE_CODE NodeTypeCode; +// NODE_BYTE_SIZE NodeByteSize; +// : +// } RECORD_NAME; +// typedef RECORD_NAME *PRECORD_NAME; +// + +#define NodeType(Ptr) (*((PNODE_TYPE_CODE)(Ptr))) +#endif + + +// +// The following definitions are used to generate meaningful blue bugcheck +// screens. On a bugcheck the file system can output 4 ulongs of useful +// information. The first ulong will have encoded in it a source file id +// (in the high word) and the line number of the bugcheck (in the low word). +// The other values can be whatever the caller of the bugcheck routine deems +// necessary. +// +// Each individual file that calls bugcheck needs to have defined at the +// start of the file a constant called BugCheckFileId with one of the +// FAT_BUG_CHECK_ values defined below and then use FatBugCheck to bugcheck +// the system. +// + + +#define FAT_BUG_CHECK_ACCHKSUP (0x00010000) +#define FAT_BUG_CHECK_ALLOCSUP (0x00020000) +#define FAT_BUG_CHECK_CACHESUP (0x00030000) +#define FAT_BUG_CHECK_CLEANUP (0x00040000) +#define FAT_BUG_CHECK_CLOSE (0x00050000) +#define FAT_BUG_CHECK_CREATE (0x00060000) +#define FAT_BUG_CHECK_DEVCTRL (0x00070000) +#define FAT_BUG_CHECK_DEVIOSUP (0x00080000) +#define FAT_BUG_CHECK_DIRCTRL (0x00090000) +#define FAT_BUG_CHECK_DIRSUP (0x000a0000) +#define FAT_BUG_CHECK_DUMPSUP (0x000b0000) +#define FAT_BUG_CHECK_EA (0x000c0000) +#define FAT_BUG_CHECK_EASUP (0x000d0000) +#define FAT_BUG_CHECK_FATDATA (0x000e0000) +#define FAT_BUG_CHECK_FATINIT (0x000f0000) +#define FAT_BUG_CHECK_FILEINFO (0x00100000) +#define FAT_BUG_CHECK_FILOBSUP (0x00110000) +#define FAT_BUG_CHECK_FLUSH (0x00120000) +#define FAT_BUG_CHECK_FSCTRL (0x00130000) +#define FAT_BUG_CHECK_FSPDISP (0x00140000) +#define FAT_BUG_CHECK_LOCKCTRL (0x00150000) +#define FAT_BUG_CHECK_NAMESUP (0x00160000) +#define FAT_BUG_CHECK_PNP (0x00170000) +#define FAT_BUG_CHECK_READ (0x00180000) +#define FAT_BUG_CHECK_RESRCSUP (0x00190000) +#define FAT_BUG_CHECK_SHUTDOWN (0x001a0000) +#define FAT_BUG_CHECK_SPLAYSUP (0x001b0000) +#define FAT_BUG_CHECK_STRUCSUP (0x001c0000) +#define FAT_BUG_CHECK_TIMESUP (0x001d0000) +#define FAT_BUG_CHECK_VERFYSUP (0x001e0000) +#define FAT_BUG_CHECK_VOLINFO (0x001f0000) +#define FAT_BUG_CHECK_WORKQUE (0x00200000) +#define FAT_BUG_CHECK_WRITE (0x00210000) + +#define FatBugCheck(A,B,C) { KeBugCheckEx(FAT_FILE_SYSTEM, BugCheckFileId | __LINE__, A, B, C ); } + + +// +// In this module we'll also define some globally known constants +// + +#define UCHAR_NUL 0x00 +#define UCHAR_SOH 0x01 +#define UCHAR_STX 0x02 +#define UCHAR_ETX 0x03 +#define UCHAR_EOT 0x04 +#define UCHAR_ENQ 0x05 +#define UCHAR_ACK 0x06 +#define UCHAR_BEL 0x07 +#define UCHAR_BS 0x08 +#define UCHAR_HT 0x09 +#define UCHAR_LF 0x0a +#define UCHAR_VT 0x0b +#define UCHAR_FF 0x0c +#define UCHAR_CR 0x0d +#define UCHAR_SO 0x0e +#define UCHAR_SI 0x0f +#define UCHAR_DLE 0x10 +#define UCHAR_DC1 0x11 +#define UCHAR_DC2 0x12 +#define UCHAR_DC3 0x13 +#define UCHAR_DC4 0x14 +#define UCHAR_NAK 0x15 +#define UCHAR_SYN 0x16 +#define UCHAR_ETB 0x17 +#define UCHAR_CAN 0x18 +#define UCHAR_EM 0x19 +#define UCHAR_SUB 0x1a +#define UCHAR_ESC 0x1b +#define UCHAR_FS 0x1c +#define UCHAR_GS 0x1d +#define UCHAR_RS 0x1e +#define UCHAR_US 0x1f +#define UCHAR_SP 0x20 + +#ifndef BUILDING_FSKDEXT + +// +// These are the tags we use to uniquely identify pool allocations. +// + +#define TAG_CCB 'CtaF' +#define TAG_FCB 'FtaF' +#define TAG_FCB_NONPAGED 'NtaF' +#define TAG_ERESOURCE 'EtaF' +#define TAG_IRP_CONTEXT 'ItaF' + +#define TAG_BCB 'btaF' +#define TAG_DIRENT 'DtaF' +#define TAG_DIRENT_BITMAP 'TtaF' +#define TAG_EA_DATA 'dtaF' +#define TAG_EA_SET_HEADER 'etaF' +#define TAG_EVENT 'vtaF' +#define TAG_FAT_BITMAP 'BtaF' +#define TAG_FAT_CLOSE_CONTEXT 'xtaF' +#define TAG_FAT_IO_CONTEXT 'XtaF' +#define TAG_FAT_WINDOW 'WtaF' +#define TAG_FILENAME_BUFFER 'ntaF' +#define TAG_IO_RUNS 'itaF' +#define TAG_REPINNED_BCB 'RtaF' +#define TAG_STASHED_BPB 'StaF' +#define TAG_VCB_STATS 'VtaF' +#define TAG_DEFERRED_FLUSH_CONTEXT 'ftaF' + +#define TAG_VPB 'vtaF' + +#define TAG_VERIFY_BOOTSECTOR 'staF' +#define TAG_VERIFY_ROOTDIR 'rtaF' + +#define TAG_OUTPUT_MAPPINGPAIRS 'PtaF' + +#define TAG_ENTRY_LOOKUP_BUFFER 'LtaF' + +#define TAG_IO_BUFFER 'OtaF' +#define TAG_IO_USER_BUFFER 'QtaF' + +#define TAG_DYNAMIC_NAME_BUFFER 'ctaF' + +#define TAG_DEFRAG_BUFFER 'GtaF' + +#endif + +#endif // _NODETYPE_ + + diff --git a/drivers/filesystems/fastfat_new/pnp.c b/drivers/filesystems/fastfat_new/pnp.c new file mode 100644 index 00000000000..9604a961e8b --- /dev/null +++ b/drivers/filesystems/fastfat_new/pnp.c @@ -0,0 +1,811 @@ +/*++ + +Copyright (c) 1997-2000 Microsoft Corporation + +Module Name: + + Pnp.c + +Abstract: + + This module implements the Plug and Play routines for FAT called by + the dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_PNP) + +NTSTATUS +FatPnpQueryRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +NTSTATUS +FatPnpRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +NTSTATUS +FatPnpSurpriseRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +NTSTATUS +FatPnpCancelRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ); + +NTSTATUS +NTAPI +FatPnpCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonPnp) +#pragma alloc_text(PAGE, FatFsdPnp) +#pragma alloc_text(PAGE, FatPnpCancelRemove) +#pragma alloc_text(PAGE, FatPnpQueryRemove) +#pragma alloc_text(PAGE, FatPnpRemove) +#pragma alloc_text(PAGE, FatPnpSurpriseRemove) +#endif + + +NTSTATUS +NTAPI +FatFsdPnp ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of PnP operations + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + BOOLEAN Wait; + + DebugTrace(+1, Dbg, "FatFsdPnp\n", 0); + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + // + // We expect there to never be a fileobject, in which case we will always + // wait. Since at the moment we don't have any concept of pending Pnp + // operations, this is a bit nitpicky. + // + + if (IoGetCurrentIrpStackLocation( Irp )->FileObject == NULL) { + + Wait = TRUE; + + } else { + + Wait = CanFsdWait( Irp ); + } + + IrpContext = FatCreateIrpContext( Irp, Wait ); + + Status = FatCommonPnp( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdPnp -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonPnp ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for doing PnP operations called + by both the fsd and fsp threads + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + PIO_STACK_LOCATION IrpSp; + + PVOLUME_DEVICE_OBJECT OurDeviceObject; + PVCB Vcb; + + // + // Get the current Irp stack location. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Find our Vcb. This is tricky since we have no file object in the Irp. + // + + OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject; + + // + // Make sure this device object really is big enough to be a volume device + // object. If it isn't, we need to get out before we try to reference some + // field that takes us past the end of an ordinary device object. + // + + if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) || + NodeType( &OurDeviceObject->Vcb ) != FAT_NTC_VCB) { + + // + // We were called with something we don't understand. + // + + Status = STATUS_INVALID_PARAMETER; + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // Force everything to wait. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + Vcb = &OurDeviceObject->Vcb; + + // + // Case on the minor code. + // + + switch ( IrpSp->MinorFunction ) { + + case IRP_MN_QUERY_REMOVE_DEVICE: + + Status = FatPnpQueryRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_SURPRISE_REMOVAL: + + Status = FatPnpSurpriseRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_REMOVE_DEVICE: + + Status = FatPnpRemove( IrpContext, Irp, Vcb ); + break; + + case IRP_MN_CANCEL_REMOVE_DEVICE: + + Status = FatPnpCancelRemove( IrpContext, Irp, Vcb ); + break; + + default: + + // + // Just pass the IRP on. As we do not need to be in the + // way on return, ellide ourselves out of the stack. + // + + IoSkipCurrentIrpStackLocation( Irp ); + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + // + // Cleanup our Irp Context. The driver has completed the Irp. + // + + FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + break; + } + + return Status; +} + + +NTSTATUS +FatPnpQueryRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP query remove operation. The filesystem + is responsible for answering whether there are any reasons it sees + that the volume can not go away (and the device removed). Initiation + of the dismount begins when we answer yes to this question. + + Query will be followed by a Cancel or Remove. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being queried. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + BOOLEAN VcbDeleted = FALSE; + BOOLEAN GlobalHeld = FALSE; + + // + // Having said yes to a QUERY, any communication with the + // underlying storage stack is undefined (and may block) + // until the bounding CANCEL or REMOVE is sent. + // + + // + // Acquire the global resource so that we can try to vaporize + // the volume, and the vcb resource itself. + // + + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + _SEH2_TRY { + + Status = FatLockVolumeInternal( IrpContext, Vcb, NULL ); + + // + // Reacquire the resources in the right order. + // + + FatReleaseVcb( IrpContext, Vcb ); + FatAcquireExclusiveGlobal( IrpContext ); + GlobalHeld = TRUE; + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + if (NT_SUCCESS( Status )) { + + // + // With the volume held locked, note that we must finalize as much + // as possible right now. + // + + FatFlushAndCleanVolume( IrpContext, Irp, Vcb, Flush ); + + // + // We need to pass this down before starting the dismount, which + // could disconnect us immediately from the stack. + // + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + IoSetCompletionRoutine( Irp, + FatPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + // + // Now if no one below us failed already, initiate the dismount + // on this volume, make it go away. PnP needs to see our internal + // streams close and drop their references to the target device. + // + // Since we were able to lock the volume, we are guaranteed to + // move this volume into dismount state and disconnect it from + // the underlying storage stack. The force on our part is actually + // unnecesary, though complete. + // + // What is not strictly guaranteed, though, is that the closes + // for the metadata streams take effect synchronously underneath + // of this call. This would leave references on the target device + // even though we are disconnected! + // + + if (NT_SUCCESS( Status )) { + + VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE ); + + ASSERT( VcbDeleted || Vcb->VcbCondition == VcbBad ); + + } + } + + } _SEH2_FINALLY { + + // + // Release the Vcb if it could still remain. + // + + if (!VcbDeleted) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + if (GlobalHeld) { + + FatReleaseGlobal( IrpContext ); + } + } _SEH2_END; + + // + // Cleanup our IrpContext and complete the IRP if neccesary. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +NTSTATUS +FatPnpRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP remove operation. This is our notification + that the underlying storage device for the volume we have is gone, and + an excellent indication that the volume will never reappear. The filesystem + is responsible for initiation or completion of the dismount. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being removed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + BOOLEAN VcbDeleted; + + // + // REMOVE - a storage device is now gone. We either got + // QUERY'd and said yes OR got a SURPRISE OR a storage + // stack failed to spin back up from a sleep/stop state + // (the only case in which this will be the first warning). + // + // Note that it is entirely unlikely that we will be around + // for a REMOVE in the first two cases, as we try to intiate + // dismount. + // + + // + // Acquire the global resource so that we can try to vaporize + // the volume, and the vcb resource itself. + // + + FatAcquireExclusiveGlobal( IrpContext ); + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + // + // The device will be going away. Remove our lock (benign + // if we never had it). + // + + (VOID) FatUnlockVolumeInternal( IrpContext, Vcb, NULL ); + + // + // We need to pass this down before starting the dismount, which + // could disconnect us immediately from the stack. + // + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + IoSetCompletionRoutine( Irp, + FatPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + _SEH2_TRY { + + // + // Knock as many files down for this volume as we can. + // + + FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush ); + + // + // Now make our dismount happen. This may not vaporize the + // Vcb, of course, since there could be any number of handles + // outstanding if we were not preceeded by a QUERY. + // + // PnP will take care of disconnecting this stack if we + // couldn't get off of it immediately. + // + + VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE ); + + } _SEH2_FINALLY { + + // + // Release the Vcb if it could still remain. + // + + if (!VcbDeleted) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + FatReleaseGlobal( IrpContext ); + } _SEH2_END; + + // + // Cleanup our IrpContext and complete the IRP. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +NTSTATUS +FatPnpSurpriseRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP surprise remove operation. This is another + type of notification that the underlying storage device for the volume we + have is gone, and is excellent indication that the volume will never reappear. + The filesystem is responsible for initiation or completion the dismount. + + For the most part, only "real" drivers care about the distinction of a + surprise remove, which is a result of our noticing that a user (usually) + physically reached into the machine and pulled something out. + + Surprise will be followed by a Remove when all references have been shut down. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being removed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + KEVENT Event; + BOOLEAN VcbDeleted; + + // + // SURPRISE - a device was physically yanked away without + // any warning. This means external forces. + // + + FatAcquireExclusiveGlobal( IrpContext ); + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + // + // We need to pass this down before starting the dismount, which + // could disconnect us immediately from the stack. + // + + // + // Get the next stack location, and copy over the stack location + // + + IoCopyCurrentIrpStackLocationToNext( Irp ); + + // + // Set up the completion routine + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + IoSetCompletionRoutine( Irp, + FatPnpCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Send the request and wait. + // + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + + if (Status == STATUS_PENDING) { + + KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + NULL ); + + Status = Irp->IoStatus.Status; + } + + _SEH2_TRY { + + // + // Knock as many files down for this volume as we can. + // + + FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush ); + + // + // Now make our dismount happen. This may not vaporize the + // Vcb, of course, since there could be any number of handles + // outstanding since this is an out of band notification. + // + + VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE ); + + } _SEH2_FINALLY { + + // + // Release the Vcb if it could still remain. + // + + if (!VcbDeleted) { + + FatReleaseVcb( IrpContext, Vcb ); + } + + FatReleaseGlobal( IrpContext ); + } _SEH2_END; + + // + // Cleanup our IrpContext and complete the IRP. + // + + FatCompleteRequest( IrpContext, Irp, Status ); + + return Status; +} + + +NTSTATUS +FatPnpCancelRemove ( + PIRP_CONTEXT IrpContext, + PIRP Irp, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine handles the PnP cancel remove operation. This is our + notification that a previously proposed remove (query) was eventually + vetoed by a component. The filesystem is responsible for cleaning up + and getting ready for more IO. + +Arguments: + + Irp - Supplies the Irp to process + + Vcb - Supplies the volume being removed. + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + + // + // CANCEL - a previous QUERY has been rescinded as a result + // of someone vetoing. Since PnP cannot figure out who may + // have gotten the QUERY (think about it: stacked drivers), + // we must expect to deal with getting a CANCEL without having + // seen the QUERY. + // + // For FAT, this is quite easy. In fact, we can't get a + // CANCEL if the underlying drivers succeeded the QUERY since + // we disconnect the Vpb on our dismount initiation. This is + // actually pretty important because if PnP could get to us + // after the disconnect we'd be thoroughly unsynchronized + // with respect to the Vcb getting torn apart - merely referencing + // the volume device object is insufficient to keep us intact. + // + + FatAcquireExclusiveVcb( IrpContext, Vcb ); + + // + // Unlock the volume. This is benign if we never had seen + // a QUERY. + // + + Status = FatUnlockVolumeInternal( IrpContext, Vcb, NULL ); + + _SEH2_TRY { + + // + // Send the request. The underlying driver will complete the + // IRP. Since we don't need to be in the way, simply ellide + // ourselves out of the IRP stack. + // + + IoSkipCurrentIrpStackLocation( Irp ); + + Status = IoCallDriver(Vcb->TargetDeviceObject, Irp); + } + _SEH2_FINALLY { + + FatReleaseVcb( IrpContext, Vcb ); + } _SEH2_END; + + FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); + + return Status; +} + + +// +// Local support routine +// + +NTSTATUS +NTAPI +FatPnpCompletionRoutine ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) +{ + PKEVENT Event = (PKEVENT) Contxt; + + KeSetEvent( Event, 0, FALSE ); + + return STATUS_MORE_PROCESSING_REQUIRED; + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Contxt ); +} + + diff --git a/drivers/filesystems/fastfat_new/read.c b/drivers/filesystems/fastfat_new/read.c new file mode 100644 index 00000000000..8f4712ea7c3 --- /dev/null +++ b/drivers/filesystems/fastfat_new/read.c @@ -0,0 +1,1649 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Read.c + +Abstract: + + This module implements the File Read routine for Read called by the + dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_READ) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_READ) + +// +// Define stack overflow read threshhold. For the x86 we'll use a smaller +// threshold than for a risc platform. +// +// Empirically, the limit is a result of the (large) amount of stack +// neccesary to throw an exception. +// + +#if defined(_M_IX86) +#define OVERFLOW_READ_THRESHHOLD (0xE00) +#else +#define OVERFLOW_READ_THRESHHOLD (0x1000) +#endif // defined(_M_IX86) + + +// +// The following procedures handles read stack overflow operations. +// + +VOID +NTAPI +FatStackOverflowRead ( + IN PVOID Context, + IN PKEVENT Event + ); + +NTSTATUS +FatPostStackOverflowRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb + ); + +VOID +NTAPI +FatOverflowPagingFileRead ( + IN PVOID Context, + IN PKEVENT Event + ); + +// +// VOID +// SafeZeroMemory ( +// IN PUCHAR At, +// IN ULONG ByteCount +// ); +// + +// +// This macro just puts a nice little try-except around RtlZeroMemory +// + +#define SafeZeroMemory(AT,BYTE_COUNT) { \ + _SEH2_TRY { \ + RtlZeroMemory((AT), (BYTE_COUNT)); \ + } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { \ + FatRaiseStatus( IrpContext, STATUS_INVALID_USER_BUFFER ); \ + } _SEH2_END \ +} + +// +// Macro to increment appropriate performance counters. +// + +#define CollectReadStats(VCB,OPEN_TYPE,BYTE_COUNT) { \ + PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()].Common; \ + if (((OPEN_TYPE) == UserFileOpen)) { \ + Stats->UserFileReads += 1; \ + Stats->UserFileReadBytes += (ULONG)(BYTE_COUNT); \ + } else if (((OPEN_TYPE) == VirtualVolumeFile || ((OPEN_TYPE) == DirectoryFile))) { \ + Stats->MetaDataReads += 1; \ + Stats->MetaDataReadBytes += (ULONG)(BYTE_COUNT); \ + } \ +} + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatStackOverflowRead) +#pragma alloc_text(PAGE, FatPostStackOverflowRead) +#pragma alloc_text(PAGE, FatCommonRead) +#endif + + +NTSTATUS +NTAPI +FatFsdRead ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the driver entry to the common read routine for NtReadFile calls. + For synchronous requests, the CommonRead is called with Wait == TRUE, + which means the request will always be completed in the current thread, + and never passed to the Fsp. If it is not a synchronous request, + CommonRead is called with Wait == FALSE, which means the request + will be passed to the Fsp only if there is a need to block. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file being Read exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + PFCB Fcb; + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdRead\n", 0); + + // + // Call the common Read routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + // + // We are first going to do a quick check for paging file IO. Since this + // is a fast path, we must replicate the check for the fsdo. + // + + if (!FatDeviceIsFatFsdo( IoGetCurrentIrpStackLocation(Irp)->DeviceObject)) { + + Fcb = (PFCB)(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext); + + if ((NodeType(Fcb) == FAT_NTC_FCB) && + FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) { + + // + // Do the usual STATUS_PENDING things. + // + + IoMarkIrpPending( Irp ); + + // + // If there is not enough stack to do this read, then post this + // read to the overflow queue. + // + + if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD) { + + KEVENT Event; + PAGING_FILE_OVERFLOW_PACKET Packet; + + Packet.Irp = Irp; + Packet.Fcb = Fcb; + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + FsRtlPostPagingFileStackOverflow( &Packet, &Event, FatOverflowPagingFileRead ); + + // + // And wait for the worker thread to complete the item + // + + (VOID) KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); + + } else { + + // + // Perform the actual IO, it will be completed when the io finishes. + // + + FatPagingFileIo( Irp, Fcb ); + } + + FsRtlExitFileSystem(); + + return STATUS_PENDING; + } + } + + _SEH2_TRY { + + TopLevel = FatIsIrpTopLevel( Irp ); + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + // + // If this is an Mdl complete request, don't go through + // common read. + // + + if ( FlagOn(IrpContext->MinorFunction, IRP_MN_COMPLETE) ) { + + DebugTrace(0, Dbg, "Calling FatCompleteMdl\n", 0 ); + try_return( Status = FatCompleteMdl( IrpContext, Irp )); + } + + // + // Check if we have enough stack space to process this request. If there + // isn't enough then we will pass the request off to the stack overflow thread. + // + + if (IoGetRemainingStackSize() < OVERFLOW_READ_THRESHHOLD) { + + DebugTrace(0, Dbg, "Passing StackOverflowRead off\n", 0 ); + try_return( Status = FatPostStackOverflowRead( IrpContext, Irp, Fcb ) ); + } + + Status = FatCommonRead( IrpContext, Irp ); + + try_exit: NOTHING; + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdRead -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +FatPostStackOverflowRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine posts a read request that could not be processed by + the fsp thread because of stack overflow potential. + +Arguments: + + Irp - Supplies the request to process. + + Fcb - Supplies the file. + +Return Value: + + STATUS_PENDING. + +--*/ + +{ + KEVENT Event; + PERESOURCE Resource; + PVCB Vcb; + + DebugTrace(0, Dbg, "Getting too close to stack limit pass request to Fsp\n", 0 ); + + // + // Initialize an event and get shared on the resource we will + // be later using the common read. + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Preacquire the resource the read path will require so we know the + // worker thread can proceed without waiting. + // + + if (FlagOn(Irp->Flags, IRP_PAGING_IO) && (Fcb->Header.PagingIoResource != NULL)) { + + Resource = Fcb->Header.PagingIoResource; + + } else { + + Resource = Fcb->Header.Resource; + } + + // + // If there are no resources assodicated with the file (case: the virtual + // volume file), it is OK. No resources will be acquired on the other side + // as well. + // + + if (Resource) { + + ExAcquireResourceSharedLite( Resource, TRUE ); + } + + if (NodeType( Fcb ) == FAT_NTC_VCB) { + + Vcb = (PVCB) Fcb; + + } else { + + Vcb = Fcb->Vcb; + } + + _SEH2_TRY { + + // + // Make the Irp just like a regular post request and + // then send the Irp to the special overflow thread. + // After the post we will wait for the stack overflow + // read routine to set the event that indicates we can + // now release the scb resource and return. + // + + FatPrePostIrp( IrpContext, Irp ); + + // + // If this read is the result of a verify, we have to + // tell the overflow read routne to temporarily + // hijack the Vcb->VerifyThread field so that reads + // can go through. + // + + if (Vcb->VerifyThread == KeGetCurrentThread()) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_VERIFY_READ); + } + + FsRtlPostStackOverflow( IrpContext, &Event, FatStackOverflowRead ); + + // + // And wait for the worker thread to complete the item + // + + KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); + + } _SEH2_FINALLY { + + if (Resource) { + + ExReleaseResourceLite( Resource ); + } + } _SEH2_END; + + return STATUS_PENDING; +} + + +// +// Internal support routine +// + +VOID +NTAPI +FatStackOverflowRead ( + IN PVOID Context, + IN PKEVENT Event + ) + +/*++ + +Routine Description: + + This routine processes a read request that could not be processed by + the fsp thread because of stack overflow potential. + +Arguments: + + Context - Supplies the IrpContext being processed + + Event - Supplies the event to be signaled when we are done processing this + request. + +Return Value: + + None. + +--*/ + +{ + PIRP_CONTEXT IrpContext = Context; + PKTHREAD SavedVerifyThread = NULL; + PVCB Vcb; + + // + // Make it now look like we can wait for I/O to complete + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // If this read was as the result of a verify we have to fake out the + // the Vcb->VerifyThread field. + // + + if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_VERIFY_READ)) { + + PFCB Fcb = (PFCB)IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)-> + FileObject->FsContext; + + if (NodeType( Fcb ) == FAT_NTC_VCB) { + + Vcb = (PVCB) Fcb; + + } else { + + Vcb = Fcb->Vcb; + } + + ASSERT( Vcb->VerifyThread != NULL ); + SavedVerifyThread = Vcb->VerifyThread; + Vcb->VerifyThread = KeGetCurrentThread(); + } + + // + // Do the read operation protected by a try-except clause + // + + _SEH2_TRY { + + (VOID) FatCommonRead( IrpContext, IrpContext->OriginatingIrp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + NTSTATUS ExceptionCode; + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + ExceptionCode = _SEH2_GetExceptionCode(); + + if (ExceptionCode == STATUS_FILE_DELETED) { + + IrpContext->ExceptionStatus = ExceptionCode = STATUS_END_OF_FILE; + IrpContext->OriginatingIrp->IoStatus.Information = 0; + } + + (VOID) FatProcessException( IrpContext, IrpContext->OriginatingIrp, ExceptionCode ); + } _SEH2_END; + + // + // Restore the original VerifyVolumeThread + // + + if (SavedVerifyThread != NULL) { + + ASSERT( Vcb->VerifyThread == KeGetCurrentThread() ); + Vcb->VerifyThread = SavedVerifyThread; + } + + // + // Set the stack overflow item's event to tell the original + // thread that we're done. + // + + KeSetEvent( Event, 0, FALSE ); +} + + +NTSTATUS +FatCommonRead ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common read routine for NtReadFile, called from both + the Fsd, or from the Fsp if a request could not be completed without + blocking in the Fsd. This routine has no code where it determines + whether it is running in the Fsd or Fsp. Instead, its actions are + conditionalized by the Wait input parameter, which determines whether + it is allowed to block or not. If a blocking condition is encountered + with Wait == FALSE, however, the request is posted to the Fsp, who + always calls with WAIT == TRUE. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + + VBO StartingVbo; + ULONG ByteCount; + ULONG RequestedByteCount; + + PIO_STACK_LOCATION IrpSp; + PFILE_OBJECT FileObject; + TYPE_OF_OPEN TypeOfOpen; + + BOOLEAN PostIrp = FALSE; + BOOLEAN OplockPostIrp = FALSE; + + BOOLEAN FcbOrDcbAcquired = FALSE; + + BOOLEAN Wait; + BOOLEAN PagingIo; + BOOLEAN NonCachedIo; + BOOLEAN SynchronousIo; + + NTSTATUS Status; + + FAT_IO_CONTEXT StackFatIoContext; + + // + // A system buffer is only used if we have to access the + // buffer directly from the Fsp to clear a portion or to + // do a synchronous I/O, or a cached transfer. It is + // possible that our caller may have already mapped a + // system buffer, in which case we must remember this so + // we do not unmap it on the way out. + // + + PVOID SystemBuffer = NULL; + + LARGE_INTEGER StartingByte; + + // + // Get current Irp stack location. + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + FileObject = IrpSp->FileObject; + + // + // Initialize the appropriate local variables. + // + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + NonCachedIo = BooleanFlagOn(Irp->Flags,IRP_NOCACHE); + SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO); + + DebugTrace(+1, Dbg, "CommonRead\n", 0); + DebugTrace( 0, Dbg, " Irp = %8lx\n", Irp); + DebugTrace( 0, Dbg, " ->ByteCount = %8lx\n", IrpSp->Parameters.Read.Length); + DebugTrace( 0, Dbg, " ->ByteOffset.LowPart = %8lx\n", IrpSp->Parameters.Read.ByteOffset.LowPart); + DebugTrace( 0, Dbg, " ->ByteOffset.HighPart = %8lx\n", IrpSp->Parameters.Read.ByteOffset.HighPart); + + // + // Extract starting Vbo and offset. + // + + StartingByte = IrpSp->Parameters.Read.ByteOffset; + + StartingVbo = StartingByte.LowPart; + + ByteCount = IrpSp->Parameters.Read.Length; + RequestedByteCount = ByteCount; + + // + // Check for a null request, and return immediately + // + + if (ByteCount == 0) { + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // Extract the nature of the read from the file object, and case on it + // + + TypeOfOpen = FatDecodeFileObject(FileObject, &Vcb, &FcbOrDcb, &Ccb); + + ASSERT( Vcb != NULL ); + + // + // Save callers who try to do cached IO to the raw volume from themselves. + // + + if (TypeOfOpen == UserVolumeOpen) { + + NonCachedIo = TRUE; + } + + ASSERT(!(NonCachedIo == FALSE && TypeOfOpen == VirtualVolumeFile)); + + // + // Collect interesting statistics. The FLAG_USER_IO bit will indicate + // what type of io we're doing in the FatNonCachedIo function. + // + + if (PagingIo) { + CollectReadStats(Vcb, TypeOfOpen, ByteCount); + + if (TypeOfOpen == UserFileOpen) { + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO); + } else { + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO); + } + } + + ASSERT(!FlagOn( IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT )); + + // + // Allocate if necessary and initialize a FAT_IO_CONTEXT block for + // all non cached Io. For synchronous Io we use stack storage, + // otherwise we allocate pool. + // + + if (NonCachedIo) { + + if (IrpContext->FatIoContext == NULL) { + + if (!Wait) { + + IrpContext->FatIoContext = + FsRtlAllocatePoolWithTag( NonPagedPool, + sizeof(FAT_IO_CONTEXT), + TAG_FAT_IO_CONTEXT ); + + } else { + + IrpContext->FatIoContext = &StackFatIoContext; + + SetFlag( IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT ); + } + } + + RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) ); + + if (Wait) { + + KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent, + NotificationEvent, + FALSE ); + + } else { + + IrpContext->FatIoContext->Wait.Async.ResourceThreadId = + ExGetCurrentResourceThread(); + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + + IrpContext->FatIoContext->Wait.Async.FileObject = FileObject; + } + } + + + // + // These two cases correspond to either a general opened volume, ie. + // open ("a:"), or a read of the volume file (boot sector + fat(s)) + // + + if ((TypeOfOpen == VirtualVolumeFile) || + (TypeOfOpen == UserVolumeOpen)) { + + LBO StartingLbo; + + StartingLbo = StartingByte.QuadPart; + + DebugTrace(0, Dbg, "Type of read is User Volume or virtual volume file\n", 0); + + if (TypeOfOpen == UserVolumeOpen) { + + // + // Verify that the volume for this handle is still valid + // + + // + // Verify that the volume for this handle is still valid, permitting + // operations to proceed on dismounted volumes via the handle which + // performed the dismount. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) { + + FatQuickVerifyVcb( IrpContext, Vcb ); + } + + if (!FlagOn( Ccb->Flags, CCB_FLAG_DASD_FLUSH_DONE )) { + + (VOID)ExAcquireResourceExclusiveLite( &Vcb->Resource, TRUE ); + + _SEH2_TRY { + + // + // If the volume isn't locked, flush it. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) { + + FatFlushVolume( IrpContext, Vcb, Flush ); + } + + } _SEH2_FINALLY { + + ExReleaseResourceLite( &Vcb->Resource ); + } _SEH2_END; + + SetFlag( Ccb->Flags, CCB_FLAG_DASD_FLUSH_DONE ); + } + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) { + + LBO VolumeSize; + + // + // Make sure we don't try to read past end of volume, + // reducing the byte count if necessary. + // + + VolumeSize = (LBO) Vcb->Bpb.BytesPerSector * + (Vcb->Bpb.Sectors != 0 ? Vcb->Bpb.Sectors : + Vcb->Bpb.LargeSectors); + + if (StartingLbo >= VolumeSize) { + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_END_OF_FILE ); + return STATUS_END_OF_FILE; + } + + if (ByteCount > VolumeSize - StartingLbo) { + + ByteCount = RequestedByteCount = (ULONG) (VolumeSize - StartingLbo); + + // + // For async reads we had set the byte count in the FatIoContext + // above, so fix that here. + // + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + } + } + } + + // + // For DASD we have to probe and lock the user's buffer + // + + FatLockUserBuffer( IrpContext, Irp, IoWriteAccess, ByteCount ); + + + } else { + + // + // Virtual volume file open -- increment performance counters. + // + + Vcb->Statistics[KeGetCurrentProcessorNumber()].Common.MetaDataDiskReads += 1; + + } + + // + // Read the data and wait for the results + // + + FatSingleAsync( IrpContext, + Vcb, + StartingLbo, + ByteCount, + Irp ); + + if (!Wait) { + + // + // We, nor anybody else, need the IrpContext any more. + // + + IrpContext->FatIoContext = NULL; + + FatDeleteIrpContext( IrpContext ); + + DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0); + + return STATUS_PENDING; + } + + FatWaitSync( IrpContext ); + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + ASSERT( KeGetCurrentThread() != Vcb->VerifyThread || Status != STATUS_VERIFY_REQUIRED ); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // Update the current file position + // + + if (SynchronousIo && !PagingIo) { + FileObject->CurrentByteOffset.QuadPart = + StartingLbo + Irp->IoStatus.Information; + } + + DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status ); + + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // At this point we know there is an Fcb/Dcb. + // + + ASSERT( FcbOrDcb != NULL ); + + // + // Check for a non-zero high part offset + // + + if ( StartingByte.HighPart != 0 ) { + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_END_OF_FILE ); + return STATUS_END_OF_FILE; + } + + // + // Use a try-finally to free Fcb/Dcb and buffers on the way out. + // + + _SEH2_TRY { + + // + // This case corresponds to a normal user read file. + // + + if ( TypeOfOpen == UserFileOpen) { + + ULONG FileSize; + ULONG ValidDataLength; + + DebugTrace(0, Dbg, "Type of read is user file open\n", 0); + + // + // If this is a noncached transfer and is not a paging I/O, and + // the file has a data section, then we will do a flush here + // to avoid stale data problems. Note that we must flush before + // acquiring the Fcb shared since the write may try to acquire + // it exclusive. + // + + if (!PagingIo && NonCachedIo + + && + + (FileObject->SectionObjectPointer->DataSectionObject != NULL)) { + +#ifndef REDUCE_SYNCHRONIZATION + if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) { + + try_return( PostIrp = TRUE ); + } + + ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, TRUE); +#endif //REDUCE_SYNCHRONIZATION + + CcFlushCache( FileObject->SectionObjectPointer, + &StartingByte, + ByteCount, + &Irp->IoStatus ); + +#ifndef REDUCE_SYNCHRONIZATION + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + FatReleaseFcb( IrpContext, FcbOrDcb ); +#endif //REDUCE_SYNCHRONIZATION + + if (!NT_SUCCESS( Irp->IoStatus.Status)) { + + try_return( Irp->IoStatus.Status ); + } + +#ifndef REDUCE_SYNCHRONIZATION + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); +#endif //REDUCE_SYNCHRONIZATION + } + + // + // We need shared access to the Fcb/Dcb before proceeding. + // + + if ( PagingIo ) { + + if (!ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, + Wait )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.Resource = + FcbOrDcb->Header.PagingIoResource; + } + + } else { + + // + // If this is async I/O, we will wait if there is an + // exclusive waiter. + // + + if (!Wait && NonCachedIo) { + + if (!FatAcquireSharedFcbWaitForEx( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, + Dbg, + "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", + FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + IrpContext->FatIoContext->Wait.Async.Resource = + FcbOrDcb->Header.Resource; + + } else { + + if (!FatAcquireSharedFcb( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, + Dbg, + "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", + FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + } + } + + FcbOrDcbAcquired = TRUE; + + // + // Make sure the FcbOrDcb is still good + // + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + // + // We now check whether we can proceed based on the state of + // the file oplocks. + // + + if (!PagingIo) { + + Status = FsRtlCheckOplock( &FcbOrDcb->Specific.Fcb.Oplock, + Irp, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + if (Status != STATUS_SUCCESS) { + + OplockPostIrp = TRUE; + PostIrp = TRUE; + try_return( NOTHING ); + } + + // + // Reset the flag indicating if Fast I/O is possible since the oplock + // check could have broken existing (conflicting) oplocks. + // + + FcbOrDcb->Header.IsFastIoPossible = FatIsFastIoPossible( FcbOrDcb ); + + // + // We have to check for read access according to the current + // state of the file locks, and set FileSize from the Fcb. + // + + if (!PagingIo && + !FsRtlCheckLockForReadAccess( &FcbOrDcb->Specific.Fcb.FileLock, + Irp )) { + + try_return( Status = STATUS_FILE_LOCK_CONFLICT ); + } + } + + // + // Pick up our sizes and check/trim the IO. + // + + FileSize = FcbOrDcb->Header.FileSize.LowPart; + ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart; + + // + // If the read starts beyond End of File, return EOF. + // + + if (StartingVbo >= FileSize) { + + DebugTrace( 0, Dbg, "End of File\n", 0 ); + + try_return ( Status = STATUS_END_OF_FILE ); + } + + // + // If the read extends beyond EOF, truncate the read + // + + if (ByteCount > FileSize - StartingVbo) { + + ByteCount = RequestedByteCount = FileSize - StartingVbo; + + if (NonCachedIo && !Wait) { + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + RequestedByteCount; + + } + } + + // + // HANDLE THE NON-CACHED CASE + // + + if ( NonCachedIo ) { + + ULONG SectorSize; + ULONG BytesToRead; + + DebugTrace(0, Dbg, "Non cached read.\n", 0); + + // + // Get the sector size + // + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + // + // Start by zeroing any part of the read after Valid Data + // + + if (ValidDataLength < FcbOrDcb->ValidDataToDisk) { + + ValidDataLength = FcbOrDcb->ValidDataToDisk; + } + + if ( StartingVbo + ByteCount > ValidDataLength ) { + + SystemBuffer = FatMapUserBuffer( IrpContext, Irp ); + + if (StartingVbo < ValidDataLength) { + + ULONG ZeroingOffset; + + // + // Now zero out the user's request sector aligned beyond + // vdl. We will handle the straddling sector at completion + // time via the bytecount reduction which immediately + // follows this check. + // + // Note that we used to post in this case for async requests. + // Note also that if the request was wholly beyond VDL that + // we did not post, therefore this is consistent. Synchronous + // zeroing is fine for async requests. + // + + ZeroingOffset = ((ValidDataLength - StartingVbo) + (SectorSize - 1)) + & ~(SectorSize - 1); + + // + // If the offset is at or above the byte count, no harm: just means + // that the read ends in the last sector and the zeroing will be + // done at completion. + // + + if (ByteCount > ZeroingOffset) { + + SafeZeroMemory( (PUCHAR) SystemBuffer + ZeroingOffset, + ByteCount - ZeroingOffset); + } + + } else { + + // + // All we have to do now is sit here and zero the + // user's buffer, no reading is required. + // + + SafeZeroMemory( (PUCHAR)SystemBuffer, ByteCount ); + + Irp->IoStatus.Information = ByteCount; + + try_return ( Status = STATUS_SUCCESS ); + } + } + + // + // Reduce the byte count to actually read if it extends beyond + // Valid Data Length + // + + ByteCount = (ValidDataLength - StartingVbo < ByteCount) ? + ValidDataLength - StartingVbo : ByteCount; + // + // Round up to a sector boundary, and remember that if we are + // reading extra bytes we will zero them out during completion. + // + + BytesToRead = (ByteCount + (SectorSize - 1)) + & ~(SectorSize - 1); + + // + // Just to help alleviate confusion. At this point: + // + // RequestedByteCount - is the number of bytes originally + // taken from the Irp, but constrained + // to filesize. + // + // ByteCount - is RequestedByteCount constrained to + // ValidDataLength. + // + // BytesToRead - is ByteCount rounded up to sector + // boundry. This is the number of bytes + // that we must physically read. + // + + // + // If this request is not properly aligned, or extending + // to a sector boundary would overflow the buffer, send it off + // on a special-case path. + // + + if ( (StartingVbo & (SectorSize - 1)) || + (BytesToRead > IrpSp->Parameters.Read.Length) ) { + + // + // If we can't wait, we must post this. + // + + if (!Wait) { + + try_return( PostIrp = TRUE ); + } + + // + // Do the physical read + // + + FatNonCachedNonAlignedRead( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + ByteCount ); + + // + // Set BytesToRead to ByteCount to satify the following ASSERT. + // + + BytesToRead = ByteCount; + + } else { + + // + // Perform the actual IO + // + + if (FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + BytesToRead, + ByteCount ) == STATUS_PENDING) { + + IrpContext->FatIoContext = NULL; + + Irp = NULL; + + try_return( Status = STATUS_PENDING ); + } + } + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + ASSERT( KeGetCurrentThread() != Vcb->VerifyThread || Status != STATUS_VERIFY_REQUIRED ); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + + } else { + + // + // Else set the Irp information field to reflect the + // entire desired read. + // + + ASSERT( Irp->IoStatus.Information == BytesToRead ); + + Irp->IoStatus.Information = RequestedByteCount; + } + + // + // The transfer is complete. + // + + try_return( Status ); + + } // if No Intermediate Buffering + + + // + // HANDLE CACHED CASE + // + + else { + + // + // We delay setting up the file cache until now, in case the + // caller never does any I/O to the file, and thus + // FileObject->PrivateCacheMap == NULL. + // + + if (FileObject->PrivateCacheMap == NULL) { + + DebugTrace(0, Dbg, "Initialize cache mapping.\n", 0); + + // + // Get the file allocation size, and if it is less than + // the file size, raise file corrupt error. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + if ( FileSize > FcbOrDcb->Header.AllocationSize.LowPart ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Now initialize the cache map. + // + + CcInitializeCacheMap( FileObject, + (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize, + FALSE, + &FatData.CacheManagerCallbacks, + FcbOrDcb ); + + CcSetReadAheadGranularity( FileObject, READ_AHEAD_GRANULARITY ); + } + + + // + // DO A NORMAL CACHED READ, if the MDL bit is not set, + // + + DebugTrace(0, Dbg, "Cached read.\n", 0); + + if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) { + + // + // Get hold of the user's buffer. + // + + SystemBuffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // Now try to do the copy. + // + + if (!CcCopyRead( FileObject, + &StartingByte, + ByteCount, + Wait, + SystemBuffer, + &Irp->IoStatus )) { + + DebugTrace( 0, Dbg, "Cached Read could not wait\n", 0 ); + + try_return( PostIrp = TRUE ); + } + + Status = Irp->IoStatus.Status; + + ASSERT( NT_SUCCESS( Status )); + + try_return( Status ); + } + + + // + // HANDLE A MDL READ + // + + else { + + DebugTrace(0, Dbg, "MDL read.\n", 0); + + ASSERT( Wait ); + + CcMdlRead( FileObject, + &StartingByte, + ByteCount, + &Irp->MdlAddress, + &Irp->IoStatus ); + + Status = Irp->IoStatus.Status; + + ASSERT( NT_SUCCESS( Status )); + + try_return( Status ); + } + } + } + + // + // These two cases correspond to a system read directory file and + // ea file. + // + + if (( TypeOfOpen == DirectoryFile ) || ( TypeOfOpen == EaFile)) { + + ULONG SectorSize; + + DebugTrace(0, Dbg, "Read Directory or Ea file.\n", 0); + + // + // For the noncached case, assert that everything is sector + // alligned. + // + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + // + // We make several assumptions about these two types of files. + // Make sure all of them are true. + // + + ASSERT( NonCachedIo && PagingIo ); + ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 ); + + // + // These calls must allways be within the allocation size + // + + if (StartingVbo >= FcbOrDcb->Header.AllocationSize.LowPart) { + + DebugTrace( 0, Dbg, "PagingIo dirent started beyond EOF.\n", 0 ); + + Irp->IoStatus.Information = 0; + + try_return( Status = STATUS_SUCCESS ); + } + + if ( StartingVbo + ByteCount > FcbOrDcb->Header.AllocationSize.LowPart ) { + + DebugTrace( 0, Dbg, "PagingIo dirent extending beyond EOF.\n", 0 ); + ByteCount = FcbOrDcb->Header.AllocationSize.LowPart - StartingVbo; + } + + // + // Perform the actual IO + // + + if (FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + ByteCount, + ByteCount ) == STATUS_PENDING) { + + IrpContext->FatIoContext = NULL; + + Irp = NULL; + + try_return( Status = STATUS_PENDING ); + } + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + ASSERT( KeGetCurrentThread() != Vcb->VerifyThread || Status != STATUS_VERIFY_REQUIRED ); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + + } else { + + ASSERT( Irp->IoStatus.Information == ByteCount ); + } + + try_return( Status ); + } + + // + // This is the case of a user who openned a directory. No reading is + // allowed. + // + + if ( TypeOfOpen == UserDirectoryOpen ) { + + DebugTrace( 0, Dbg, "CommonRead -> STATUS_INVALID_PARAMETER\n", 0); + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // If we get this far, something really serious is wrong. + // + + DebugDump("Illegal TypeOfOpen\n", 0, FcbOrDcb ); + + FatBugCheck( TypeOfOpen, (ULONG_PTR) FcbOrDcb, 0 ); + + try_exit: NOTHING; + + // + // If the request was not posted and there's an Irp, deal with it. + // + + if ( Irp ) { + + if ( !PostIrp ) { + + ULONG ActualBytesRead; + + DebugTrace( 0, Dbg, "Completing request with status = %08lx\n", + Status); + + DebugTrace( 0, Dbg, " Information = %08lx\n", + Irp->IoStatus.Information); + + // + // Record the total number of bytes actually read + // + + ActualBytesRead = (ULONG)Irp->IoStatus.Information; + + // + // If the file was opened for Synchronous IO, update the current + // file position. + // + + if (SynchronousIo && !PagingIo) { + + FileObject->CurrentByteOffset.LowPart = + StartingVbo + ActualBytesRead; + } + + // + // If this was not PagingIo, mark that the last access + // time on the dirent needs to be updated on close. + // + + if (NT_SUCCESS(Status) && !PagingIo) { + + SetFlag( FileObject->Flags, FO_FILE_FAST_IO_READ ); + } + + } else { + + DebugTrace( 0, Dbg, "Passing request to Fsp\n", 0 ); + + if (!OplockPostIrp) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + } + } + } + + } _SEH2_FINALLY { + + DebugUnwind( FatCommonRead ); + + // + // If the FcbOrDcb has been acquired, release it. + // + + if (FcbOrDcbAcquired && Irp) { + + if ( PagingIo ) { + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + + } else { + + FatReleaseFcb( NULL, FcbOrDcb ); + } + } + + // + // Complete the request if we didn't post it and no exception + // + // Note that FatCompleteRequest does the right thing if either + // IrpContext or Irp are NULL + // + + if (!PostIrp) { + + // + // If we had a stack io context, we have to make sure the contents + // are cleaned up before we leave. + // + // At present with zero mdls, this will only really happen on exceptional + // termination where we failed to dispatch the IO. Cleanup of zero mdls + // normally occurs during completion, but when we bail we must make sure + // the cleanup occurs here or the fatiocontext will go out of scope. + // + // If the operation was posted, cleanup occured there. + // + + if (FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) { + + if (IrpContext->FatIoContext->ZeroMdl) { + IoFreeMdl( IrpContext->FatIoContext->ZeroMdl ); + } + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT); + IrpContext->FatIoContext = NULL; + } + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + } + + DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status ); + } _SEH2_END; + + return Status; +} + +// +// Local support routine +// + +VOID +NTAPI +FatOverflowPagingFileRead ( + IN PVOID Context, + IN PKEVENT Event + ) + +/*++ + +Routine Description: + + The routine simply call FatPagingFileIo. It is invoked in cases when + there was not enough stack space to perform the pagefault in the + original thread. It is also responsible for freeing the packet pool. + +Arguments: + + Irp - Supplies the Irp being processed + + Fcb - Supplies the paging file Fcb, since we have it handy. + +Return Value: + + VOID + +--*/ + +{ + PPAGING_FILE_OVERFLOW_PACKET Packet = Context; + + FatPagingFileIo( Packet->Irp, Packet->Fcb ); + + // + // Set the stack overflow item's event to tell the original + // thread that we're done. + // + + KeSetEvent( Event, 0, FALSE ); + + return; +} + + + diff --git a/drivers/filesystems/fastfat_new/resrcsup.c b/drivers/filesystems/fastfat_new/resrcsup.c new file mode 100644 index 00000000000..c18ba7d1c48 --- /dev/null +++ b/drivers/filesystems/fastfat_new/resrcsup.c @@ -0,0 +1,807 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + ResrcSup.c + +Abstract: + + This module implements the Fat Resource acquisition routines + + +--*/ + +#include "fatprocs.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatAcquireExclusiveVcb) +#pragma alloc_text(PAGE, FatAcquireFcbForLazyWrite) +#pragma alloc_text(PAGE, FatAcquireFcbForReadAhead) +#pragma alloc_text(PAGE, FatAcquireExclusiveFcb) +#pragma alloc_text(PAGE, FatAcquireSharedFcb) +#pragma alloc_text(PAGE, FatAcquireSharedFcbWaitForEx) +#pragma alloc_text(PAGE, FatAcquireExclusiveVcb) +#pragma alloc_text(PAGE, FatAcquireSharedVcb) +#pragma alloc_text(PAGE, FatNoOpAcquire) +#pragma alloc_text(PAGE, FatNoOpRelease) +#pragma alloc_text(PAGE, FatReleaseFcbFromLazyWrite) +#pragma alloc_text(PAGE, FatReleaseFcbFromReadAhead) +#pragma alloc_text(PAGE, FatAcquireForCcFlush) +#pragma alloc_text(PAGE, FatReleaseForCcFlush) +#endif + + +FINISHED +FatAcquireExclusiveVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine acquires exclusive access to the Vcb. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Vcb - Supplies the Vcb to acquire + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + if (ExAcquireResourceExclusiveLite( &Vcb->Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) { + + _SEH2_TRY { + + FatVerifyOperationIsLegal( IrpContext ); + + } _SEH2_FINALLY { + + if ( _SEH2_AbnormalTermination() ) { + + FatReleaseVcb( IrpContext, Vcb ); + + } + } _SEH2_END; + + return TRUE; + + } else { + + return FALSE; + } +} + + +FINISHED +FatAcquireSharedVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine acquires shared access to the Vcb. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Vcb - Supplies the Vcb to acquire + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + if (ExAcquireResourceSharedLite( &Vcb->Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) { + + _SEH2_TRY { + + FatVerifyOperationIsLegal( IrpContext ); + + } _SEH2_FINALLY { + + if ( _SEH2_AbnormalTermination() ) { + + FatReleaseVcb( IrpContext, Vcb ); + } + } _SEH2_END; + + return TRUE; + + } else { + + return FALSE; + } +} + + +FINISHED +FatAcquireExclusiveFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine acquires exclusive access to the Fcb. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Fcb - Supplies the Fcb to acquire + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + +RetryFcbExclusive: + + if (ExAcquireResourceExclusiveLite( Fcb->Header.Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) { + + // + // Check for anything other than a non-cached write if the + // async count is non-zero in the Fcb, or if others are waiting + // for the resource. Then wait for all outstanding I/O to finish, + // drop the resource, and wait again. + // + + if ((Fcb->NonPaged->OutstandingAsyncWrites != 0) && + ((IrpContext->MajorFunction != IRP_MJ_WRITE) || + !FlagOn(IrpContext->OriginatingIrp->Flags, IRP_NOCACHE) || + (ExGetSharedWaiterCount(Fcb->Header.Resource) != 0) || + (ExGetExclusiveWaiterCount(Fcb->Header.Resource) != 0))) { + + KeWaitForSingleObject( Fcb->NonPaged->OutstandingAsyncEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER) NULL ); + + FatReleaseFcb( IrpContext, Fcb ); + + goto RetryFcbExclusive; + } + + _SEH2_TRY { + + FatVerifyOperationIsLegal( IrpContext ); + + } _SEH2_FINALLY { + + if ( _SEH2_AbnormalTermination() ) { + + FatReleaseFcb( IrpContext, Fcb ); + } + } _SEH2_END; + + return TRUE; + + } else { + + return FALSE; + } +} + + +FINISHED +FatAcquireSharedFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine acquires shared access to the Fcb. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Fcb - Supplies the Fcb to acquire + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + +RetryFcbShared: + + if (ExAcquireResourceSharedLite( Fcb->Header.Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) { + + // + // Check for anything other than a non-cached write if the + // async count is non-zero in the Fcb, or if others are waiting + // for the resource. Then wait for all outstanding I/O to finish, + // drop the resource, and wait again. + // + + if ((Fcb->NonPaged->OutstandingAsyncWrites != 0) && + ((IrpContext->MajorFunction != IRP_MJ_WRITE) || + !FlagOn(IrpContext->OriginatingIrp->Flags, IRP_NOCACHE) || + (ExGetSharedWaiterCount(Fcb->Header.Resource) != 0) || + (ExGetExclusiveWaiterCount(Fcb->Header.Resource) != 0))) { + + KeWaitForSingleObject( Fcb->NonPaged->OutstandingAsyncEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER) NULL ); + + FatReleaseFcb( IrpContext, Fcb ); + + goto RetryFcbShared; + } + + _SEH2_TRY { + + FatVerifyOperationIsLegal( IrpContext ); + + } _SEH2_FINALLY { + + if ( _SEH2_AbnormalTermination() ) { + + FatReleaseFcb( IrpContext, Fcb ); + } + } _SEH2_END; + + + return TRUE; + + } else { + + return FALSE; + } +} + + +FINISHED +FatAcquireSharedFcbWaitForEx ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine acquires shared access to the Fcb, waiting first for any + exclusive accessors to get the Fcb first. + + After we acquire the resource check to see if this operation is legal. + If it isn't (ie. we get an exception), release the resource. + +Arguments: + + Fcb - Supplies the Fcb to acquire + +Return Value: + + FINISHED - TRUE if we have the resource and FALSE if we needed to block + for the resource but Wait is FALSE. + +--*/ + +{ + + ASSERT( FlagOn(IrpContext->OriginatingIrp->Flags, IRP_NOCACHE) ); + ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + +RetryFcbSharedWaitEx: + + if (ExAcquireSharedWaitForExclusive( Fcb->Header.Resource, FALSE )) { + + // + // Check for anything other than a non-cached write if the + // async count is non-zero in the Fcb. Then wait for all + // outstanding I/O to finish, drop the resource, and wait again. + // + + if ((Fcb->NonPaged->OutstandingAsyncWrites != 0) && + (IrpContext->MajorFunction != IRP_MJ_WRITE)) { + + KeWaitForSingleObject( Fcb->NonPaged->OutstandingAsyncEvent, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER) NULL ); + + FatReleaseFcb( IrpContext, Fcb ); + + goto RetryFcbSharedWaitEx; + } + + _SEH2_TRY { + + FatVerifyOperationIsLegal( IrpContext ); + + } _SEH2_FINALLY { + + if ( _SEH2_AbnormalTermination() ) { + + FatReleaseFcb( IrpContext, Fcb ); + } + } _SEH2_END; + + + return TRUE; + + } else { + + return FALSE; + } +} + + +BOOLEAN +NTAPI +FatAcquireFcbForLazyWrite ( + IN PVOID Fcb, + IN BOOLEAN Wait + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a file. It is subsequently called by the Lazy Writer prior to its + performing lazy writes to the file. + +Arguments: + + Fcb - The Fcb which was specified as a context parameter for this + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + FALSE - if Wait was specified as FALSE and blocking would have + been required. The Fcb is not acquired. + + TRUE - if the Fcb has been acquired + +--*/ + +{ + // + // Check here for the EA File. It turns out we need the normal + // resource shared in this case. Otherwise we take the paging + // I/O resource shared. + // + + if (!ExAcquireResourceSharedLite( Fcb == ((PFCB)Fcb)->Vcb->EaFcb ? + ((PFCB)Fcb)->Header.Resource : + ((PFCB)Fcb)->Header.PagingIoResource, + Wait )) { + + return FALSE; + } + + // + // We assume the Lazy Writer only acquires this Fcb once. + // Therefore, it should be guaranteed that this flag is currently + // clear (the ASSERT), and then we will set this flag, to insure + // that the Lazy Writer will never try to advance Valid Data, and + // also not deadlock by trying to get the Fcb exclusive. + // + + + ASSERT( NodeType(((PFCB)Fcb)) == FAT_NTC_FCB ); + ASSERT( ((PFCB)Fcb)->Specific.Fcb.LazyWriteThread == NULL ); + + ((PFCB)Fcb)->Specific.Fcb.LazyWriteThread = PsGetCurrentThread(); + + ASSERT( NULL != PsGetCurrentThread() ); + + if (NULL == FatData.LazyWriteThread) { + + FatData.LazyWriteThread = PsGetCurrentThread(); + } + + // + // This is a kludge because Cc is really the top level. When it + // enters the file system, we will think it is a resursive call + // and complete the request with hard errors or verify. It will + // then have to deal with them, somehow.... + // + + ASSERT(IoGetTopLevelIrp() == NULL); + + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + return TRUE; +} + + +VOID +NTAPI +FatReleaseFcbFromLazyWrite ( + IN PVOID Fcb + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a file. It is subsequently called by the Lazy Writer after its + performing lazy writes to the file. + +Arguments: + + Fcb - The Fcb which was specified as a context parameter for this + routine. + +Return Value: + + None + +--*/ + +{ + // + // Assert that this really is an fcb and that this thread really owns + // the lazy writer mark in the fcb. + // + + ASSERT( NodeType(((PFCB)Fcb)) == FAT_NTC_FCB ); + ASSERT( NULL != PsGetCurrentThread() ); + ASSERT( ((PFCB)Fcb)->Specific.Fcb.LazyWriteThread == PsGetCurrentThread() ); + + // + // Release the lazy writer mark. + // + + ((PFCB)Fcb)->Specific.Fcb.LazyWriteThread = NULL; + + // + // Check here for the EA File. It turns out we needed the normal + // resource shared in this case. Otherwise it was the PagingIoResource. + // + + ExReleaseResourceLite( Fcb == ((PFCB)Fcb)->Vcb->EaFcb ? + ((PFCB)Fcb)->Header.Resource : + ((PFCB)Fcb)->Header.PagingIoResource ); + + // + // Clear the kludge at this point. + // + + ASSERT(IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + IoSetTopLevelIrp( NULL ); + + return; +} + + +BOOLEAN +NTAPI +FatAcquireFcbForReadAhead ( + IN PVOID Fcb, + IN BOOLEAN Wait + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a file. It is subsequently called by the Lazy Writer prior to its + performing read ahead to the file. + +Arguments: + + Fcb - The Fcb which was specified as a context parameter for this + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + FALSE - if Wait was specified as FALSE and blocking would have + been required. The Fcb is not acquired. + + TRUE - if the Fcb has been acquired + +--*/ + +{ + // + // We acquire the normal file resource shared here to synchronize + // correctly with purges. + // + + if (!ExAcquireResourceSharedLite( ((PFCB)Fcb)->Header.Resource, + Wait )) { + + return FALSE; + } + + // + // This is a kludge because Cc is really the top level. We it + // enters the file system, we will think it is a resursive call + // and complete the request with hard errors or verify. It will + // have to deal with them, somehow.... + // + + ASSERT(IoGetTopLevelIrp() == NULL); + + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + return TRUE; +} + + +VOID +NTAPI +FatReleaseFcbFromReadAhead ( + IN PVOID Fcb + ) + +/*++ + +Routine Description: + + The address of this routine is specified when creating a CacheMap for + a file. It is subsequently called by the Lazy Writer after its + read ahead. + +Arguments: + + Fcb - The Fcb which was specified as a context parameter for this + routine. + +Return Value: + + None + +--*/ + +{ + // + // Clear the kludge at this point. + // + + ASSERT(IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + IoSetTopLevelIrp( NULL ); + + ExReleaseResourceLite( ((PFCB)Fcb)->Header.Resource ); + + return; +} + + +NTSTATUS +NTAPI +FatAcquireForCcFlush ( + IN PFILE_OBJECT FileObject, + IN PDEVICE_OBJECT DeviceObject + ) +{ + PFCB Fcb; + PCCB Ccb; + PVCB Vcb; + PFSRTL_COMMON_FCB_HEADER Header; + TYPE_OF_OPEN Type; + + // + // Once again, the hack for making this look like + // a recursive call if needed. We cannot let ourselves + // verify under something that has resources held. + // + // This value is good. We should never try to acquire + // the file this way underneath of the cache. + // + + ASSERT( IoGetTopLevelIrp() != (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP ); + + if (IoGetTopLevelIrp() == NULL) { + + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + } + + // + // Time for some exposition. + // + // Lockorder for FAT is main->bcb->pagingio. Invert this at your obvious peril. + // The default logic for AcquireForCcFlush breaks this since in writethrough + // unpinrepinned we will grab the bcb then Mm will use the callback (which + // orders us with respect to the MmCollidedFlushEvent) to help us. If for + // directories/ea we then grab the main we are out of order. + // + // Fortunately, we do not need main. We only need paging - just look at the write + // path. This is basic pre-acquisition. + // + // Regular files require both resources, and are safe since we never pin them. + // + + Type = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + Header = (PFSRTL_COMMON_FCB_HEADER) FileObject->FsContext; + + if (Type < DirectoryFile) { + + if (Header->Resource) { + + if (!ExIsResourceAcquiredSharedLite( Header->Resource )) { + + ExAcquireResourceExclusiveLite( Header->Resource, TRUE ); + + } else { + + ExAcquireResourceSharedLite( Header->Resource, TRUE ); + } + } + } + + if (Header->PagingIoResource) { + + ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE ); + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +NTAPI +FatReleaseForCcFlush ( + IN PFILE_OBJECT FileObject, + IN PDEVICE_OBJECT DeviceObject + ) +{ + PFCB Fcb; + PCCB Ccb; + PVCB Vcb; + PFSRTL_COMMON_FCB_HEADER Header; + TYPE_OF_OPEN Type; + + // + // Clear up our hint. + // + + if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP) { + + IoSetTopLevelIrp( NULL ); + } + + Type = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb ); + Header = (PFSRTL_COMMON_FCB_HEADER) FileObject->FsContext; + + if (Type < DirectoryFile) { + + if (Header->Resource) { + + ExReleaseResourceLite( Header->Resource ); + } + } + + if (Header->PagingIoResource) { + + ExReleaseResourceLite( Header->PagingIoResource ); + } + + return STATUS_SUCCESS; +} + + +BOOLEAN +NTAPI +FatNoOpAcquire ( + IN PVOID Fcb, + IN BOOLEAN Wait + ) + +/*++ + +Routine Description: + + This routine does nothing. + +Arguments: + + Fcb - The Fcb/Dcb/Vcb which was specified as a context parameter for this + routine. + + Wait - TRUE if the caller is willing to block. + +Return Value: + + TRUE + +--*/ + +{ + UNREFERENCED_PARAMETER( Fcb ); + UNREFERENCED_PARAMETER( Wait ); + + // + // This is a kludge because Cc is really the top level. We it + // enters the file system, we will think it is a resursive call + // and complete the request with hard errors or verify. It will + // have to deal with them, somehow.... + // + + ASSERT(IoGetTopLevelIrp() == NULL); + + IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + return TRUE; +} + + +VOID +NTAPI +FatNoOpRelease ( + IN PVOID Fcb + ) + +/*++ + +Routine Description: + + This routine does nothing. + +Arguments: + + Fcb - The Fcb/Dcb/Vcb which was specified as a context parameter for this + routine. + +Return Value: + + None + +--*/ + +{ + // + // Clear the kludge at this point. + // + + ASSERT(IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); + + IoSetTopLevelIrp( NULL ); + + UNREFERENCED_PARAMETER( Fcb ); + + return; +} + + diff --git a/drivers/filesystems/fastfat_new/shutdown.c b/drivers/filesystems/fastfat_new/shutdown.c new file mode 100644 index 00000000000..3fec0213326 --- /dev/null +++ b/drivers/filesystems/fastfat_new/shutdown.c @@ -0,0 +1,304 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Shutdown.c + +Abstract: + + This module implements the file system shutdown routine for Fat + + +--*/ + +#include "fatprocs.h" + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_SHUTDOWN) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonShutdown) +#pragma alloc_text(PAGE, FatFsdShutdown) +#endif + + +NTSTATUS +NTAPI +FatFsdShutdown ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of shutdown. Note that Shutdown will + never be done asynchronously so we will never need the Fsp counterpart + to shutdown. + + This is the shutdown routine for the Fat file system device driver. + This routine locks the global file system lock and then syncs all the + mounted volumes. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - Always STATUS_SUCCESS + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdShutdown\n", 0); + + // + // Call the common shutdown routine. + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, TRUE ); + + Status = FatCommonShutdown( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdShutdown -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonShutdown ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for shutdown called by both the fsd and + fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PKEVENT Event; + + PLIST_ENTRY Links; + PVCB Vcb; + PIRP NewIrp; + IO_STATUS_BLOCK Iosb; + BOOLEAN VcbDeleted; + + // + // Make sure we don't get any pop-ups, and write everything through. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS | + IRP_CONTEXT_FLAG_WRITE_THROUGH); + + // + // Allocate an initialize an event for doing calls down to + // our target deivce objects + // + + Event = FsRtlAllocatePoolWithTag( NonPagedPool, + sizeof(KEVENT), + TAG_EVENT ); + KeInitializeEvent( Event, NotificationEvent, FALSE ); + + // + // Indicate that shutdown has started. This is used in FatFspClose. + // + + FatData.ShutdownStarted = TRUE; + + // + // Get everyone else out of the way + // + + (VOID) FatAcquireExclusiveGlobal( IrpContext ); + + _SEH2_TRY { + + // + // For every volume that is mounted we will flush the + // volume and then shutdown the target device objects. + // + + Links = FatData.VcbQueue.Flink; + while (Links != &FatData.VcbQueue) { + + Vcb = CONTAINING_RECORD(Links, VCB, VcbLinks); + + Links = Links->Flink; + + // + // If we have already been called before for this volume + // (and yes this does happen), skip this volume as no writes + // have been allowed since the first shutdown. + // + + if ( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN) || + (Vcb->VcbCondition != VcbGood) ) { + + continue; + } + + FatAcquireExclusiveVolume( IrpContext, Vcb ); + + _SEH2_TRY { + + (VOID)FatFlushVolume( IrpContext, Vcb, Flush ); + + // + // The volume is now clean, note it. We purge the + // volume file cache map before marking the volume + // clean incase there is a stale Bpb in the cache. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + CcPurgeCacheSection( &Vcb->SectionObjectPointers, + NULL, + 0, + FALSE ); + + FatMarkVolume( IrpContext, Vcb, VolumeClean ); + } + + } _SEH2_EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + // + // Sometimes we take an excepion while flushing the volume, such + // as when autoconv has converted the volume and is rebooting. + // Even in that case we want to send the shutdown irp to the + // target device so it can know to flush its cache, if it has one. + // + + _SEH2_TRY { + + NewIrp = IoBuildSynchronousFsdRequest( IRP_MJ_SHUTDOWN, + Vcb->TargetDeviceObject, + NULL, + 0, + NULL, + Event, + &Iosb ); + + if (NewIrp != NULL) { + + if (NT_SUCCESS(IoCallDriver( Vcb->TargetDeviceObject, NewIrp ))) { + + (VOID) KeWaitForSingleObject( Event, + Executive, + KernelMode, + FALSE, + NULL ); + + KeClearEvent( Event ); + } + } + + } _SEH2_EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN ); + + + // + // Attempt to punch the volume down. + // + + VcbDeleted = FatCheckForDismount( IrpContext, + Vcb, + FALSE ); + if (!VcbDeleted) { + + FatReleaseVolume( IrpContext, Vcb ); + } + } + + } _SEH2_FINALLY { + + ExFreePool( Event ); + + FatReleaseGlobal( IrpContext ); + + // + // Unregister the file system. + // + + IoUnregisterFileSystem( FatDiskFileSystemDeviceObject); + IoUnregisterFileSystem( FatCdromFileSystemDeviceObject); + IoDeleteDevice( FatDiskFileSystemDeviceObject); + IoDeleteDevice( FatCdromFileSystemDeviceObject); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + } _SEH2_END; + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdShutdown -> STATUS_SUCCESS\n", 0); + + return STATUS_SUCCESS; +} + diff --git a/drivers/filesystems/fastfat_new/sources b/drivers/filesystems/fastfat_new/sources new file mode 100644 index 00000000000..fb427f33a5f --- /dev/null +++ b/drivers/filesystems/fastfat_new/sources @@ -0,0 +1,45 @@ +NT_UP=0 +TARGETNAME=fastfat +TARGETTYPE=DRIVER +DRIVERTYPE=FS + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=AcChkSup.c \ + AllocSup.c \ + CacheSup.c \ + Cleanup.c \ + Close.c \ + Create.c \ + DevCtrl.c \ + DevIoSup.c \ + DirCtrl.c \ + DirSup.c \ + DumpSup.c \ + Ea.c \ + EaSup.c \ + FastFat.rc \ + FatData.c \ + FatInit.c \ + FileInfo.c \ + FilObSup.c \ + Flush.c \ + FsCtrl.c \ + FspDisp.c \ + LockCtrl.c \ + NameSup.c \ + Pnp.c \ + Read.c \ + ResrcSup.c \ + Shutdown.c \ + StrucSup.c \ + SplaySup.c \ + TimeSup.c \ + VerfySup.c \ + VolInfo.c \ + WorkQue.c \ + Write.c + +PRECOMPILED_INCLUDE=fatprocs.h +PRECOMPILED_OBJ=fatprocs.obj + diff --git a/drivers/filesystems/fastfat_new/splaysup.c b/drivers/filesystems/fastfat_new/splaysup.c new file mode 100644 index 00000000000..7f9be8d1507 --- /dev/null +++ b/drivers/filesystems/fastfat_new/splaysup.c @@ -0,0 +1,504 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + PrefxSup.c + +Abstract: + + This module implements the Fat Name lookup Suport routines + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_SPLAYSUP) + +// +// The debug trace level for this module +// + +#define Dbg (DEBUG_TRACE_SPLAYSUP) + +// +// Local procedures and types used only in this package +// + +typedef enum _COMPARISON { + IsLessThan, + IsGreaterThan, + IsEqual +} COMPARISON; + +COMPARISON +FatCompareNames ( + IN PSTRING NameA, + IN PSTRING NameB + ); + +// +// Do a macro here to check for a common case. +// + +#define CompareNames(NAMEA,NAMEB) ( \ + *(PUCHAR)(NAMEA)->Buffer != *(PUCHAR)(NAMEB)->Buffer ? \ + *(PUCHAR)(NAMEA)->Buffer < *(PUCHAR)(NAMEB)->Buffer ? \ + IsLessThan : IsGreaterThan : \ + FatCompareNames((PSTRING)(NAMEA), (PSTRING)(NAMEB)) \ +) + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatInsertName) +#pragma alloc_text(PAGE, FatRemoveNames) +#pragma alloc_text(PAGE, FatFindFcb) +#pragma alloc_text(PAGE, FatCompareNames) +#endif + + +VOID +FatInsertName ( + IN PIRP_CONTEXT IrpContext, + IN PRTL_SPLAY_LINKS *RootNode, + IN PFILE_NAME_NODE Name + ) + +/*++ + +Routine Description: + + This routine will insert a name in the splay tree pointed to + by RootNode. + + The name must not already exist in the splay tree. + +Arguments: + + RootNode - Supplies a pointer to the table. + + Name - Contains the New name to enter. + +Return Value: + + None. + +--*/ + +{ + COMPARISON Comparison; + PFILE_NAME_NODE Node; + + RtlInitializeSplayLinks(&Name->Links); + + // + // If we are the first entry in the tree, just become the root. + // + + if (*RootNode == NULL) { + + *RootNode = &Name->Links; + + return; + } + +Restart: + + Node = CONTAINING_RECORD( *RootNode, FILE_NAME_NODE, Links ); + + while (TRUE) { + + // + // Compare the prefix in the tree with the prefix we want + // to insert. Note that Oem here doesn't mean anything. + // + + Comparison = CompareNames(&Node->Name.Oem, &Name->Name.Oem); + + // + // We should never find the name in the table already. + // + + if (Comparison == IsEqual) { + + // + // Almost. If the removable media was taken to another machine and + // back, and we have something like: + // + // Old: foobar~1 / foobarbaz + // New: foobar~1 / foobarbazbaz + // + // but a handle was kept open to foobarbaz so we couldn't purge + // away the Fcb in the verify path ... opening foobarbazbaz will + // try to insert a duplicate shortname. Bang! + // + // Invalidate it and the horse it came in on. This new one wins. + // The old one is gone. Only if the old one is in normal state + // do we really have a problem. + // + + if (Node->Fcb->FcbState == FcbGood) { + + FatBugCheck( (ULONG_PTR)*RootNode, (ULONG_PTR)Name, (ULONG_PTR)Node ); + } + + // + // Note, once we zap the prefix links we need to restart our walk + // of the tree. + // + + FatMarkFcbCondition( IrpContext, Node->Fcb, FcbBad, TRUE ); + FatRemoveNames( IrpContext, Node->Fcb ); + + goto Restart; + } + + // + // If the tree prefix is greater than the new prefix then + // we go down the left subtree + // + + if (Comparison == IsGreaterThan) { + + // + // We want to go down the left subtree, first check to see + // if we have a left subtree + // + + if (RtlLeftChild(&Node->Links) == NULL) { + + // + // there isn't a left child so we insert ourselves as the + // new left child + // + + RtlInsertAsLeftChild(&Node->Links, &Name->Links); + + // + // and exit the while loop + // + + break; + + } else { + + // + // there is a left child so simply go down that path, and + // go back to the top of the loop + // + + Node = CONTAINING_RECORD( RtlLeftChild(&Node->Links), + FILE_NAME_NODE, + Links ); + } + + } else { + + // + // The tree prefix is either less than or a proper prefix + // of the new string. We treat both cases a less than when + // we do insert. So we want to go down the right subtree, + // first check to see if we have a right subtree + // + + if (RtlRightChild(&Node->Links) == NULL) { + + // + // These isn't a right child so we insert ourselves as the + // new right child + // + + RtlInsertAsRightChild(&Node->Links, &Name->Links); + + // + // and exit the while loop + // + + break; + + } else { + + // + // there is a right child so simply go down that path, and + // go back to the top of the loop + // + + Node = CONTAINING_RECORD( RtlRightChild(&Node->Links), + FILE_NAME_NODE, + Links ); + } + + } + } + + return; +} + +VOID +FatRemoveNames ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine will remove the short name and any long names associated + with the files from their repsective splay tree. + +Arguments: + + Name - Supplies the Fcb to process. + +Return Value: + + None. + +--*/ + +{ + PDCB Parent; + PRTL_SPLAY_LINKS NewRoot; + + Parent = Fcb->ParentDcb; + + // + // We used to assert this condition, but it really isn't good. If + // someone rapidly renames a directory multiple times and we can't + // flush the lower fcbs fast enough (that didn't go away synch.) + // well, well hit some of them again. + // + // ASSERT( FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )); + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )) { + + // + // Delete the node short name. + // + + NewRoot = RtlDelete(&Fcb->ShortName.Links); + + Parent->Specific.Dcb.RootOemNode = NewRoot; + + // + // Now check for the presence of long name and delete it. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME )) { + + NewRoot = RtlDelete(&Fcb->LongName.Oem.Links); + + Parent->Specific.Dcb.RootOemNode = NewRoot; + + RtlFreeOemString( &Fcb->LongName.Oem.Name.Oem ); + + ClearFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME ); + } + + if (FlagOn( Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME )) { + + NewRoot = RtlDelete(&Fcb->LongName.Unicode.Links); + + Parent->Specific.Dcb.RootUnicodeNode = NewRoot; + + RtlFreeUnicodeString( &Fcb->LongName.Unicode.Name.Unicode ); + + ClearFlag( Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME ); + } + + ClearFlag( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE ); + } + + return; +} + + +PFCB +FatFindFcb ( + IN PIRP_CONTEXT IrpContext, + IN OUT PRTL_SPLAY_LINKS *RootNode, + IN PSTRING Name, + OUT PBOOLEAN FileNameDos OPTIONAL + ) + +/*++ + +Routine Description: + + This routine searches either the Oem or Unicode splay tree looking + for an Fcb with the specified name. In the case the Fcb is found, + rebalance the tree. + +Arguments: + + RootNode - Supplies the parent to search. + + Name - If present, search the Oem tree. + + UnicodeName - If present, search the Unicode tree. + +Return Value: + + PFCB - The Fcb, or NULL if none was found. + +--*/ + +{ + COMPARISON Comparison; + PFILE_NAME_NODE Node; + PRTL_SPLAY_LINKS Links; + + Links = *RootNode; + + while (Links != NULL) { + + Node = CONTAINING_RECORD(Links, FILE_NAME_NODE, Links); + + // + // Compare the prefix in the tree with the full name + // + + Comparison = CompareNames(&Node->Name.Oem, Name); + + // + // See if they don't match + // + + if (Comparison == IsGreaterThan) { + + // + // The prefix is greater than the full name + // so we go down the left child + // + + Links = RtlLeftChild(Links); + + // + // And continue searching down this tree + // + + } else if (Comparison == IsLessThan) { + + // + // The prefix is less than the full name + // so we go down the right child + // + + Links = RtlRightChild(Links); + + // + // And continue searching down this tree + // + + } else { + + // + // We found it. + // + // Splay the tree and save the new root. + // + + *RootNode = RtlSplay(Links); + + // + // Tell the caller what kind of name we hit + // + + if (FileNameDos) { + + *FileNameDos = Node->FileNameDos; + } + + return Node->Fcb; + } + } + + // + // We didn't find the Fcb. + // + + return NULL; +} + + +// +// Local support routine +// + +COMPARISON +FatCompareNames ( + IN PSTRING NameA, + IN PSTRING NameB + ) + +/*++ + +Routine Description: + + This function compares two names as fast as possible. Note that since + this comparison is case sensitive, I neither know nor case if the names + are UNICODE or OEM. All that is important is that the result is + deterministic. + +Arguments: + + NameA & NameB - The names to compare. + +Return Value: + + COMPARISON - returns + + IsLessThan if NameA < NameB lexicalgraphically, + IsGreaterThan if NameA > NameB lexicalgraphically, + IsEqual if NameA is equal to NameB + +--*/ + +{ + ULONG i; + ULONG MinLength; + + PAGED_CODE(); + + // + // Figure out the minimum of the two lengths + // + + MinLength = NameA->Length < NameB->Length ? NameA->Length : + NameB->Length; + + // + // Loop through looking at all of the characters in both strings + // testing for equalilty, less than, and greater than + // + + i = (ULONG)RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength ); + + + if (i < MinLength) { + + return NameA->Buffer[i] < NameB->Buffer[i] ? IsLessThan : + IsGreaterThan; + } + + if (NameA->Length < NameB->Length) { + + return IsLessThan; + } + + if (NameA->Length > NameB->Length) { + + return IsGreaterThan; + } + + return IsEqual; +} + diff --git a/drivers/filesystems/fastfat_new/strucsup.c b/drivers/filesystems/fastfat_new/strucsup.c new file mode 100644 index 00000000000..7ccb51c9d6c --- /dev/null +++ b/drivers/filesystems/fastfat_new/strucsup.c @@ -0,0 +1,3626 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + StrucSup.c + +Abstract: + + This module implements the Fat in-memory data structure manipulation + routines + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_STRUCSUP) + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_STRUCSUP) + +#define FillMemory(BUF,SIZ,MASK) { \ + ULONG i; \ + for (i = 0; i < (((SIZ)/4) - 1); i += 2) { \ + ((PULONG)(BUF))[i] = (MASK); \ + ((PULONG)(BUF))[i+1] = (ULONG)PsGetCurrentThread(); \ + } \ +} + +#define IRP_CONTEXT_HEADER (sizeof( IRP_CONTEXT ) * 0x10000 + FAT_NTC_IRP_CONTEXT) + +// +// Local macros. +// +// Define our lookaside list allocators. For the time being, and perhaps +// permanently, the paged structures don't come off of lookasides. This +// is due to complications with clean unload as FAT can be in the paging +// path, making it really hard to find the right time to empty them. +// +// Fortunately, the hit rates on the Fcb/Ccb lists weren't stunning. +// + +#define FAT_FILL_FREE 0 + +INLINE +PCCB +FatAllocateCcb ( + ) +{ + return (PCCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(CCB), TAG_CCB ); +} + +INLINE +VOID +FatFreeCcb ( + IN PCCB Ccb + ) +{ +#if FAT_FILL_FREE + RtlFillMemoryUlong(Ccb, sizeof(CCB), FAT_FILL_FREE); +#endif + + ExFreePool( Ccb ); +} + +INLINE +PFCB +FatAllocateFcb ( + ) +{ + return (PFCB) FsRtlAllocatePoolWithTag( PagedPool, sizeof(FCB), TAG_FCB ); +} + +INLINE +VOID +FatFreeFcb ( + IN PFCB Fcb + ) +{ +#if FAT_FILL_FREE + RtlFillMemoryUlong(Fcb, sizeof(FCB), FAT_FILL_FREE); +#endif + + ExFreePool( Fcb ); +} + +INLINE +PNON_PAGED_FCB +FatAllocateNonPagedFcb ( + ) +{ + return (PNON_PAGED_FCB) ExAllocateFromNPagedLookasideList( &FatNonPagedFcbLookasideList ); +} + +INLINE +VOID +FatFreeNonPagedFcb ( + PNON_PAGED_FCB NonPagedFcb + ) +{ +#if FAT_FILL_FREE + RtlFillMemoryUlong(NonPagedFcb, sizeof(NON_PAGED_FCB), FAT_FILL_FREE); +#endif + + ExFreeToNPagedLookasideList( &FatNonPagedFcbLookasideList, (PVOID) NonPagedFcb ); +} + +INLINE +PERESOURCE +FatAllocateResource ( + ) +{ + PERESOURCE Resource; + + Resource = (PERESOURCE) ExAllocateFromNPagedLookasideList( &FatEResourceLookasideList ); + + ExInitializeResourceLite( Resource ); + + return Resource; +} + +INLINE +VOID +FatFreeResource ( + IN PERESOURCE Resource + ) +{ + ExDeleteResourceLite( Resource ); + +#if FAT_FILL_FREE + RtlFillMemoryUlong(Resource, sizeof(ERESOURCE), FAT_FILL_FREE); +#endif + + ExFreeToNPagedLookasideList( &FatEResourceLookasideList, (PVOID) Resource ); +} + +INLINE +PIRP_CONTEXT +FatAllocateIrpContext ( + ) +{ + return (PIRP_CONTEXT) ExAllocateFromNPagedLookasideList( &FatIrpContextLookasideList ); +} + +INLINE +VOID +FatFreeIrpContext ( + IN PIRP_CONTEXT IrpContext + ) +{ +#if FAT_FILL_FREE + RtlFillMemoryUlong(IrpContext, sizeof(IRP_CONTEXT), FAT_FILL_FREE); +#endif + + ExFreeToNPagedLookasideList( &FatIrpContextLookasideList, (PVOID) IrpContext ); +} + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatInitializeVcb) +#pragma alloc_text(PAGE, FatTearDownVcb) +#pragma alloc_text(PAGE, FatDeleteVcb) +#pragma alloc_text(PAGE, FatCreateRootDcb) +#pragma alloc_text(PAGE, FatCreateFcb) +#pragma alloc_text(PAGE, FatCreateDcb) +#pragma alloc_text(PAGE, FatDeleteFcb_Real) +#pragma alloc_text(PAGE, FatCreateCcb) +#pragma alloc_text(PAGE, FatDeallocateCcbStrings) +#pragma alloc_text(PAGE, FatDeleteCcb_Real) +#pragma alloc_text(PAGE, FatGetNextFcbTopDown) +#pragma alloc_text(PAGE, FatGetNextFcbBottomUp) +#pragma alloc_text(PAGE, FatConstructNamesInFcb) +#pragma alloc_text(PAGE, FatCheckFreeDirentBitmap) +#pragma alloc_text(PAGE, FatCreateIrpContext) +#pragma alloc_text(PAGE, FatDeleteIrpContext_Real) +#pragma alloc_text(PAGE, FatIsHandleCountZero) +#pragma alloc_text(PAGE, FatPreallocateCloseContext) +#endif + + +VOID +FatInitializeVcb ( + IN PIRP_CONTEXT IrpContext, + IN OUT PVCB Vcb, + IN PDEVICE_OBJECT TargetDeviceObject, + IN PVPB Vpb, + IN PDEVICE_OBJECT FsDeviceObject + ) + +/*++ + +Routine Description: + + This routine initializes and inserts a new Vcb record into the in-memory + data structure. The Vcb record "hangs" off the end of the Volume device + object and must be allocated by our caller. + +Arguments: + + Vcb - Supplies the address of the Vcb record being initialized. + + TargetDeviceObject - Supplies the address of the target device object to + associate with the Vcb record. + + Vpb - Supplies the address of the Vpb to associate with the Vcb record. + + FsDeviceObject - The filesystem device object that the mount was directed + too. + +Return Value: + + None. + +--*/ + +{ + CC_FILE_SIZES FileSizes; + PDEVICE_OBJECT RealDevice; + LONG i; + + STORAGE_HOTPLUG_INFO HotplugInfo; + NTSTATUS Status; + + // + // The following variables are used for abnormal unwind + // + + PLIST_ENTRY UnwindEntryList = NULL; + PERESOURCE UnwindResource = NULL; + PERESOURCE UnwindResource2 = NULL; + PFILE_OBJECT UnwindFileObject = NULL; + PFILE_OBJECT UnwindCacheMap = NULL; + BOOLEAN UnwindWeAllocatedMcb = FALSE; + PFILE_SYSTEM_STATISTICS UnwindStatistics = NULL; + + DebugTrace(+1, Dbg, "FatInitializeVcb, Vcb = %08lx\n", Vcb); + + _SEH2_TRY { + + // + // We start by first zeroing out all of the VCB, this will guarantee + // that any stale data is wiped clean + // + + RtlZeroMemory( Vcb, sizeof(VCB) ); + + // + // Set the proper node type code and node byte size + // + + Vcb->VolumeFileHeader.NodeTypeCode = FAT_NTC_VCB; + Vcb->VolumeFileHeader.NodeByteSize = sizeof(VCB); + + // + // Initialize the tunneling cache + // + + FsRtlInitializeTunnelCache(&Vcb->Tunnel); + + // + // Insert this Vcb record on the FatData.VcbQueue + // + + ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + InsertTailList( &FatData.VcbQueue, &Vcb->VcbLinks ); + FatReleaseGlobal( IrpContext ); + UnwindEntryList = &Vcb->VcbLinks; + + // + // Set the Target Device Object, Vpb, and Vcb State fields + // + + + ObReferenceObject( TargetDeviceObject ); + Vcb->TargetDeviceObject = TargetDeviceObject; + Vcb->Vpb = Vpb; + + Vcb->CurrentDevice = Vpb->RealDevice; + + // + // Set the removable media and defflush flags based on the storage + // inquiry and the old characteristic bits. + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_STORAGE_GET_HOTPLUG_INFO, + TargetDeviceObject, + &HotplugInfo, + sizeof(HotplugInfo), + FALSE, + TRUE, + NULL ); + + if (NT_SUCCESS( Status )) { + + if (HotplugInfo.MediaRemovable) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA ); + } + + if (!HotplugInfo.WriteCacheEnableOverride) { + + // + // If the device or media is hotplug and the override is not + // set, force defflush behavior for the device. + // + + if (HotplugInfo.MediaHotplug || HotplugInfo.DeviceHotplug) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH ); + + // + // Now, for removables that claim to be lockable, lob a lock + // request and see if it works. There can unfortunately be + // transient, media dependent reasons that it can fail. If + // it does not, we must force defflush on. + // + + } else if (HotplugInfo.MediaRemovable && + !HotplugInfo.MediaHotplug) { + + Status = FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); + + if (!NT_SUCCESS( Status )) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH ); + + } + + Status = FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE ); + } + } + } + + if (FlagOn(Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA)) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA ); + } + + // + // Make sure we turn on deferred flushing for floppies like we always + // have. + // + + if (FlagOn(Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH ); + } + + Vcb->VcbCondition = VcbGood; + + // + // Initialize the resource variable for the Vcb + // + + ExInitializeResourceLite( &Vcb->Resource ); + UnwindResource = &Vcb->Resource; + + ExInitializeResourceLite( &Vcb->ChangeBitMapResource ); + UnwindResource2 = &Vcb->ChangeBitMapResource; + + // + // Initialize the free cluster bitmap mutex. + // + + ExInitializeFastMutex( &Vcb->FreeClusterBitMapMutex ); + + // + // Create the special file object for the virtual volume file with a close + // context, its pointers back to the Vcb and the section object pointer. + // + // We don't have to unwind the close context. That will happen in the close + // path automatically. + // + + RealDevice = Vcb->CurrentDevice; + + Vcb->VirtualVolumeFile = UnwindFileObject = IoCreateStreamFileObject( NULL, RealDevice ); + FatPreallocateCloseContext(); + + FatSetFileObject( Vcb->VirtualVolumeFile, + VirtualVolumeFile, + Vcb, + NULL ); + + // + // Remember this internal, residual open. + // + +#ifndef __REACTOS__ + InterlockedIncrement( &(Vcb->InternalOpenCount) ); + InterlockedIncrement( &(Vcb->ResidualOpenCount) ); +#else + + InterlockedIncrement( (LONG*)&(Vcb->InternalOpenCount) ); + InterlockedIncrement( (LONG*)&(Vcb->ResidualOpenCount) ); +#endif + + Vcb->VirtualVolumeFile->SectionObjectPointer = &Vcb->SectionObjectPointers; + + Vcb->VirtualVolumeFile->ReadAccess = TRUE; + Vcb->VirtualVolumeFile->WriteAccess = TRUE; + Vcb->VirtualVolumeFile->DeleteAccess = TRUE; + + // + // Initialize the notify structures. + // + + InitializeListHead( &Vcb->DirNotifyList ); + + FsRtlNotifyInitializeSync( &Vcb->NotifySync ); + + // + // Initialize the Cache Map for the volume file. The size is + // initially set to that of our first read. It will be extended + // when we know how big the Fat is. + // + + FileSizes.AllocationSize.QuadPart = + FileSizes.FileSize.QuadPart = sizeof(PACKED_BOOT_SECTOR); + FileSizes.ValidDataLength = FatMaxLarge; + + CcInitializeCacheMap( Vcb->VirtualVolumeFile, + &FileSizes, + TRUE, + &FatData.CacheManagerNoOpCallbacks, + Vcb ); + UnwindCacheMap = Vcb->VirtualVolumeFile; + + // + // Initialize the structure that will keep track of dirty fat sectors. + // The largest possible Mcb structures are less than 1K, so we use + // non paged pool. + // + + FsRtlInitializeLargeMcb( &Vcb->DirtyFatMcb, PagedPool ); + + UnwindWeAllocatedMcb = TRUE; + + // + // Set the cluster index hint to the first valid cluster of a fat: 2 + // + + Vcb->ClusterHint = 2; + + // + // Initialize the directory stream file object creation event. + // This event is also "borrowed" for async non-cached writes. + // + + ExInitializeFastMutex( &Vcb->DirectoryFileCreationMutex ); + + // + // Initialize the clean volume callback Timer and DPC. + // + + KeInitializeTimer( &Vcb->CleanVolumeTimer ); + + KeInitializeDpc( &Vcb->CleanVolumeDpc, FatCleanVolumeDpc, Vcb ); + + // + // Initialize the performance counters. + // + + Vcb->Statistics = FsRtlAllocatePoolWithTag( NonPagedPool, + sizeof(FILE_SYSTEM_STATISTICS) * KeNumberProcessors, + TAG_VCB_STATS ); + UnwindStatistics = Vcb->Statistics; + + RtlZeroMemory( Vcb->Statistics, sizeof(FILE_SYSTEM_STATISTICS) * KeNumberProcessors ); + + for (i = 0; i < KeNumberProcessors; i += 1) { + Vcb->Statistics[i].Common.FileSystemType = FILESYSTEM_STATISTICS_TYPE_FAT; + Vcb->Statistics[i].Common.Version = 1; + Vcb->Statistics[i].Common.SizeOfCompleteStructure = + sizeof(FILE_SYSTEM_STATISTICS); + } + + // + // Pick up a VPB right now so we know we can pull this filesystem stack off + // of the storage stack on demand. + // + + Vcb->SwapVpb = FsRtlAllocatePoolWithTag( NonPagedPool, + sizeof( VPB ), + TAG_VPB ); + + RtlZeroMemory( Vcb->SwapVpb, sizeof( VPB ) ); + + // + // Initialize the close queue listheads. + // + + InitializeListHead( &Vcb->AsyncCloseList ); + InitializeListHead( &Vcb->DelayedCloseList ); + + // + // Initialize the Advanced FCB Header + // + + ExInitializeFastMutex( &Vcb->AdvancedFcbHeaderMutex ); + FsRtlSetupAdvancedHeader( &Vcb->VolumeFileHeader, + &Vcb->AdvancedFcbHeaderMutex ); + + // + // With the Vcb now set up, set the IrpContext Vcb field. + // + + IrpContext->Vcb = Vcb; + + } _SEH2_FINALLY { + + DebugUnwind( FatInitializeVcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + if (UnwindCacheMap != NULL) { FatSyncUninitializeCacheMap( IrpContext, UnwindCacheMap ); } + if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); } + if (UnwindResource != NULL) { FatDeleteResource( UnwindResource ); } + if (UnwindResource2 != NULL) { FatDeleteResource( UnwindResource2 ); } + if (UnwindWeAllocatedMcb) { FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb ); } + if (UnwindEntryList != NULL) { + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + RemoveEntryList( UnwindEntryList ); + FatReleaseGlobal( IrpContext ); + } + if (UnwindStatistics != NULL) { ExFreePool( UnwindStatistics ); } + } + + DebugTrace(-1, Dbg, "FatInitializeVcb -> VOID\n", 0); + } _SEH2_END; + + // + // and return to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + +VOID +FatTearDownVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine tries to remove all internal opens from the volume. + +Arguments: + + IrpContext - Supplies the context for the overall request. + + Vcb - Supplies the Vcb to be torn down. + +Return Value: + + None + +--*/ + +{ + PFILE_OBJECT DirectoryFileObject; + + + PAGED_CODE(); + + // + // Get rid of the virtual volume file, if we need to. + // + + if (Vcb->VirtualVolumeFile != NULL) { + + // + // Uninitialize the cache + // + + FatSyncUninitializeCacheMap( IrpContext, Vcb->VirtualVolumeFile ); + + // + // Dereference the virtual volume file. This will cause a close + // Irp to be processed, so we need to do this before we destory + // the Vcb + // + + FsRtlTeardownPerStreamContexts( &Vcb->VolumeFileHeader ); + + ObDereferenceObject( Vcb->VirtualVolumeFile ); + + Vcb->VirtualVolumeFile = NULL; + } + + // + // Close down the EA file. + // + + FatCloseEaFile( IrpContext, Vcb, FALSE ); + + // + // Close down the root directory stream.. + // + + if (Vcb->RootDcb != NULL) { + + DirectoryFileObject = Vcb->RootDcb->Specific.Dcb.DirectoryFile; + + if (DirectoryFileObject != NULL) { + + // + // Tear down this directory file. + // + + FatSyncUninitializeCacheMap( IrpContext, + DirectoryFileObject ); + + Vcb->RootDcb->Specific.Dcb.DirectoryFile = NULL; + ObDereferenceObject( DirectoryFileObject ); + } + } + + // + // The VCB can no longer be used. + // + + FatSetVcbCondition( Vcb, VcbBad ); +} + + +VOID +FatDeleteVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine removes the Vcb record from Fat's in-memory data + structures. It also will remove all associated underlings + (i.e., FCB records). + +Arguments: + + Vcb - Supplies the Vcb to be removed + +Return Value: + + None + +--*/ + +{ + PFCB Fcb; + + DebugTrace(+1, Dbg, "FatDeleteVcb, Vcb = %08lx\n", Vcb); + + // + // If the IrpContext points to the VCB being deleted NULL out the stall + // pointer. + // + + if (IrpContext->Vcb == Vcb) { + + IrpContext->Vcb = NULL; + + } + + + // + // Check the backpocket Vpb we kept just in case. + // + + if (Vcb->SwapVpb) { + + ExFreePool( Vcb->SwapVpb ); + } + + // + // Free the VPB, if we need to. + // + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED )) { + + // + // We swapped the VPB, so we need to free the main one. + // + + ExFreePool( Vcb->Vpb ); + } + + // + // Remove this record from the global list of all Vcb records. + // Note that the global lock must already be held when calling + // this function. + // + + RemoveEntryList( &(Vcb->VcbLinks) ); + + // + // Make sure the direct access open count is zero, and the open file count + // is also zero. + // + + if ((Vcb->DirectAccessOpenCount != 0) || (Vcb->OpenFileCount != 0)) { + + FatBugCheck( 0, 0, 0 ); + } + + // + // Remove the EaFcb and dereference the Fcb for the Ea file if it + // exists. + // + + if (Vcb->EaFcb != NULL) { + + Vcb->EaFcb->OpenCount = 0; + FatDeleteFcb( IrpContext, Vcb->EaFcb ); + + Vcb->EaFcb = NULL; + } + + // + // Remove the Root Dcb + // + + if (Vcb->RootDcb != NULL) { + + // + // Rundown stale child Fcbs that may be hanging around. Yes, this + // can happen. No, the create path isn't perfectly defensive about + // tearing down branches built up on creates that don't wind up + // succeeding. Normal system operation usually winds up having + // cleaned them out through re-visiting, but ... + // + // Just pick off Fcbs from the bottom of the tree until we run out. + // Then we delete the root Dcb. + // + + while( (Fcb = FatGetNextFcbBottomUp( IrpContext, NULL, Vcb->RootDcb )) != Vcb->RootDcb ) { + + FatDeleteFcb( IrpContext, Fcb ); + } + + FatDeleteFcb( IrpContext, Vcb->RootDcb ); + } + + // + // Uninitialize the notify sychronization object. + // + + FsRtlNotifyUninitializeSync( &Vcb->NotifySync ); + + // + // Uninitialize the resource variable for the Vcb + // + + FatDeleteResource( &Vcb->Resource ); + FatDeleteResource( &Vcb->ChangeBitMapResource ); + + // + // If allocation support has been setup, free it. + // + + if (Vcb->FreeClusterBitMap.Buffer != NULL) { + + FatTearDownAllocationSupport( IrpContext, Vcb ); + } + + // + // UnInitialize the Mcb structure that kept track of dirty fat sectors. + // + + FsRtlUninitializeLargeMcb( &Vcb->DirtyFatMcb ); + + // + // Free the pool for the stached copy of the boot sector + // + + if ( Vcb->First0x24BytesOfBootSector ) { + + ExFreePool( Vcb->First0x24BytesOfBootSector ); + } + + // + // Cancel the CleanVolume Timer and Dpc + // + + (VOID)KeCancelTimer( &Vcb->CleanVolumeTimer ); + + (VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc ); + + // + // Free the performance counters memory + // + + ExFreePool( Vcb->Statistics ); + + // + // Clean out the tunneling cache + // + + FsRtlDeleteTunnelCache(&Vcb->Tunnel); + + // + // Dereference the target device object. + // + + ObDereferenceObject( Vcb->TargetDeviceObject ); + + // + // And zero out the Vcb, this will help ensure that any stale data is + // wiped clean + // + + RtlZeroMemory( Vcb, sizeof(VCB) ); + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatDeleteVcb -> VOID\n", 0); + + return; +} + + +VOID +FatCreateRootDcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine allocates, initializes, and inserts a new root DCB record + into the in memory data structure. + +Arguments: + + Vcb - Supplies the Vcb to associate the new DCB under + +Return Value: + + None. The Vcb is modified in-place. + +--*/ + +{ + PDCB Dcb; + + // + // The following variables are used for abnormal unwind + // + + PVOID UnwindStorage[2] = { NULL, NULL }; + PERESOURCE UnwindResource = NULL; + PERESOURCE UnwindResource2 = NULL; + PLARGE_MCB UnwindMcb = NULL; + PFILE_OBJECT UnwindFileObject = NULL; + + DebugTrace(+1, Dbg, "FatCreateRootDcb, Vcb = %08lx\n", Vcb); + + _SEH2_TRY { + + // + // Make sure we don't already have a root dcb for this vcb + // + + if (Vcb->RootDcb != NULL) { + + DebugDump("Error trying to create multiple root dcbs\n", 0, Vcb); + FatBugCheck( 0, 0, 0 ); + } + + // + // Allocate a new DCB and zero it out, we use Dcb locally so we don't + // have to continually reference through the Vcb + // + + UnwindStorage[0] = Dcb = Vcb->RootDcb = FsRtlAllocatePoolWithTag( NonPagedPool, + sizeof(DCB), + TAG_FCB ); + + RtlZeroMemory( Dcb, sizeof(DCB)); + + UnwindStorage[1] = + Dcb->NonPaged = FatAllocateNonPagedFcb(); + + RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) ); + + // + // Set the proper node type code, node byte size, and call backs + // + + Dcb->Header.NodeTypeCode = FAT_NTC_ROOT_DCB; + Dcb->Header.NodeByteSize = sizeof(DCB); + + Dcb->FcbCondition = FcbGood; + + // + // The parent Dcb, initial state, open count, dirent location + // information, and directory change count fields are already zero so + // we can skip setting them + // + + // + // Initialize the resource variable + // + + UnwindResource = + Dcb->Header.Resource = FatAllocateResource(); + + // + // Initialize the PagingIo Resource. We no longer use the FsRtl common + // shared pool because this led to a) deadlocks due to cases where files + // and their parent directories shared a resource and b) there is no way + // to anticipate inter-driver induced deadlock via recursive operation. + // + + UnwindResource2 = + Dcb->Header.PagingIoResource = FatAllocateResource(); + + // + // The root Dcb has an empty parent dcb links field + // + + InitializeListHead( &Dcb->ParentDcbLinks ); + + // + // Set the Vcb + // + + Dcb->Vcb = Vcb; + + // + // initialize the parent dcb queue. + // + + InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue ); + + // + // Set the full file name up. + // + + Dcb->FullFileName.Buffer = L"\\"; + Dcb->FullFileName.Length = (USHORT)2; + Dcb->FullFileName.MaximumLength = (USHORT)4; + + Dcb->ShortName.Name.Oem.Buffer = "\\"; + Dcb->ShortName.Name.Oem.Length = (USHORT)1; + Dcb->ShortName.Name.Oem.MaximumLength = (USHORT)2; + + // + // Construct a lie about file properties since we don't + // have a proper "." entry to look at. + // + + Dcb->DirentFatFlags = FILE_ATTRIBUTE_DIRECTORY; + + // + // Initialize Advanced FCB Header fields + // + + ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex ); + FsRtlSetupAdvancedHeader( &Dcb->Header, + &Dcb->NonPaged->AdvancedFcbHeaderMutex ); + + // + // Initialize the Mcb, and setup its mapping. Note that the root + // directory is a fixed size so we can set it everything up now. + // + + FsRtlInitializeLargeMcb( &Dcb->Mcb, NonPagedPool ); + UnwindMcb = &Dcb->Mcb; + + if (FatIsFat32(Vcb)) { + + // + // The first cluster of fat32 roots comes from the BPB + // + + Dcb->FirstClusterOfFile = Vcb->Bpb.RootDirFirstCluster; + + } else { + + FatAddMcbEntry( Vcb, &Dcb->Mcb, + 0, + FatRootDirectoryLbo( &Vcb->Bpb ), + FatRootDirectorySize( &Vcb->Bpb )); + } + + if (FatIsFat32(Vcb)) { + + // + // Find the size of the fat32 root. As a side-effect, this will create + // MCBs for the entire root. In the process of doing this, we may + // discover that the FAT chain is bogus and raise corruption. + // + + Dcb->Header.AllocationSize.LowPart = 0xFFFFFFFF; + FatLookupFileAllocationSize( IrpContext, Dcb); + + Dcb->Header.FileSize.QuadPart = + Dcb->Header.AllocationSize.QuadPart; + } else { + + // + // set the allocation size to real size of the root directory + // + + Dcb->Header.FileSize.QuadPart = + Dcb->Header.AllocationSize.QuadPart = FatRootDirectorySize( &Vcb->Bpb ); + + } + + // + // Set our two create dirent aids to represent that we have yet to + // enumerate the directory for never used or deleted dirents. + // + + Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff; + Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff; + + // + // Setup the free dirent bitmap buffer. + // + + RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap, + NULL, + 0 ); + + FatCheckFreeDirentBitmap( IrpContext, Dcb ); + + } _SEH2_FINALLY { + + DebugUnwind( FatCreateRootDcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + ULONG i; + + if (UnwindFileObject != NULL) { ObDereferenceObject( UnwindFileObject ); } + if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); } + if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); } + if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); } + + for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) { + if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } + } + + // + // Re-zero the entry in the Vcb. + // + + Vcb->RootDcb = NULL; + } + + DebugTrace(-1, Dbg, "FatCreateRootDcb -> %8lx\n", Dcb); + } _SEH2_END; + + return; +} + + +PFCB +FatCreateFcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN ULONG LfnOffsetWithinDirectory, + IN ULONG DirentOffsetWithinDirectory, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn OPTIONAL, + IN BOOLEAN IsPagingFile, + IN BOOLEAN SingleResource + ) + +/*++ + +Routine Description: + + This routine allocates, initializes, and inserts a new Fcb record into + the in-memory data structures. + +Arguments: + + Vcb - Supplies the Vcb to associate the new FCB under. + + ParentDcb - Supplies the parent dcb that the new FCB is under. + + LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is + no LFN associated with this file then this value is same as + DirentOffsetWithinDirectory. + + DirentOffsetWithinDirectory - Supplies the offset, in bytes from the + start of the directory file where the dirent for the fcb is located + + Dirent - Supplies the dirent for the fcb being created + + Lfn - Supplies a long UNICODE name associated with this file. + + IsPagingFile - Indicates if we are creating an FCB for a paging file + or some other type of file. + + SingleResource - Indicates if this Fcb should share a single resource + as both main and paging. + +Return Value: + + PFCB - Returns a pointer to the newly allocated FCB + +--*/ + +{ + PFCB Fcb; + POOL_TYPE PoolType; + + // + // The following variables are used for abnormal unwind + // + + PVOID UnwindStorage[2] = { NULL, NULL }; + PERESOURCE UnwindResource = NULL; + PERESOURCE UnwindResource2 = NULL; + PLIST_ENTRY UnwindEntryList = NULL; + PLARGE_MCB UnwindMcb = NULL; + PFILE_LOCK UnwindFileLock = NULL; + POPLOCK UnwindOplock = NULL; + + DebugTrace(+1, Dbg, "FatCreateFcb\n", 0); + + _SEH2_TRY { + + // + // Determine the pool type we should be using for the fcb and the + // mcb structure + // + + if (IsPagingFile) { + + PoolType = NonPagedPool; + Fcb = UnwindStorage[0] = FsRtlAllocatePoolWithTag( NonPagedPool, + sizeof(FCB), + TAG_FCB ); + } else { + + PoolType = PagedPool; + Fcb = UnwindStorage[0] = FatAllocateFcb(); + + } + + // + // ... and zero it out + // + + RtlZeroMemory( Fcb, sizeof(FCB) ); + + UnwindStorage[1] = + Fcb->NonPaged = FatAllocateNonPagedFcb(); + + RtlZeroMemory( Fcb->NonPaged, sizeof( NON_PAGED_FCB ) ); + + // + // Set the proper node type code, node byte size, and call backs + // + + Fcb->Header.NodeTypeCode = FAT_NTC_FCB; + Fcb->Header.NodeByteSize = sizeof(FCB); + + Fcb->FcbCondition = FcbGood; + + // + // Check to see if we need to set the Fcb state to indicate that this + // is a paging/system file. This will prevent it from being opened + // again. + // + + if (IsPagingFile) { + + SetFlag( Fcb->FcbState, FCB_STATE_PAGING_FILE | FCB_STATE_SYSTEM_FILE ); + } + + // + // The initial state, open count, and segment objects fields are already + // zero so we can skip setting them + // + + // + // Initialize the resource variable + // + + + UnwindResource = + Fcb->Header.Resource = FatAllocateResource(); + + // + // Initialize the PagingIo Resource. We no longer use the FsRtl common + // shared pool because this led to a) deadlocks due to cases where files + // and their parent directories shared a resource and b) there is no way + // to anticipate inter-driver induced deadlock via recursive operation. + // + + if (SingleResource) { + + Fcb->Header.PagingIoResource = Fcb->Header.Resource; + + } else { + + UnwindResource2 = + Fcb->Header.PagingIoResource = FatAllocateResource(); + } + + // + // Insert this fcb into our parent dcb's queue. + // + // There is a deep reason why this goes on the tail, to allow us + // to easily enumerate all child directories before child files. + // This is important to let us maintain whole-volume lockorder + // via BottomUp enumeration. + // + + InsertTailList( &ParentDcb->Specific.Dcb.ParentDcbQueue, + &Fcb->ParentDcbLinks ); + UnwindEntryList = &Fcb->ParentDcbLinks; + + // + // Point back to our parent dcb + // + + Fcb->ParentDcb = ParentDcb; + + // + // Set the Vcb + // + + Fcb->Vcb = Vcb; + + // + // Set the dirent offset within the directory + // + + Fcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory; + Fcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory; + + // + // Set the DirentFatFlags and LastWriteTime + // + + Fcb->DirentFatFlags = Dirent->Attributes; + + Fcb->LastWriteTime = FatFatTimeToNtTime( IrpContext, + Dirent->LastWriteTime, + 0 ); + + // + // These fields are only non-zero when in Chicago mode. + // + + if (FatData.ChicagoMode) { + + LARGE_INTEGER FatSystemJanOne1980; + + // + // If either date is possibly zero, get the system + // version of 1/1/80. + // + + if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) { + + ExLocalTimeToSystemTime( &FatJanOne1980, + &FatSystemJanOne1980 ); + } + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[9] != 0) { + + Fcb->LastAccessTime = + FatFatDateToNtTime( IrpContext, + Dirent->LastAccessDate ); + + } else { + + Fcb->LastAccessTime = FatSystemJanOne1980; + } + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[8] != 0) { + + Fcb->CreationTime = + FatFatTimeToNtTime( IrpContext, + Dirent->CreationTime, + Dirent->CreationMSec ); + + } else { + + Fcb->CreationTime = FatSystemJanOne1980; + } + } + + // + // Initialize Advanced FCB Header fields + // + + ExInitializeFastMutex( &Fcb->NonPaged->AdvancedFcbHeaderMutex ); + FsRtlSetupAdvancedHeader( &Fcb->Header, + &Fcb->NonPaged->AdvancedFcbHeaderMutex ); + + // + // To make FAT match the present functionality of NTFS, disable + // stream contexts on paging files (nealch 7/2/01) + // + + if (IsPagingFile) { + + ClearFlag( Fcb->Header.Flags2, FSRTL_FLAG2_SUPPORTS_FILTER_CONTEXTS ); + } + + // + // Initialize the Mcb + // + + FsRtlInitializeLargeMcb( &Fcb->Mcb, PoolType ); + UnwindMcb = &Fcb->Mcb; + + // + // Set the file size, valid data length, first cluster of file, + // and allocation size based on the information stored in the dirent + // + + Fcb->Header.FileSize.LowPart = Dirent->FileSize; + + Fcb->Header.ValidDataLength.LowPart = Dirent->FileSize; + + Fcb->ValidDataToDisk = Dirent->FileSize; + + Fcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile; + + if ( FatIsFat32(Vcb) ) { + + Fcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16; + } + + if ( Fcb->FirstClusterOfFile == 0 ) { + + Fcb->Header.AllocationSize.QuadPart = 0; + + } else { + + Fcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + } + + // + // Initialize the Fcb's file lock record + // + + FsRtlInitializeFileLock( &Fcb->Specific.Fcb.FileLock, NULL, NULL ); + UnwindFileLock = &Fcb->Specific.Fcb.FileLock; + + // + // Initialize the oplock structure. + // + + FsRtlInitializeOplock( &Fcb->Specific.Fcb.Oplock ); + UnwindOplock = &Fcb->Specific.Fcb.Oplock; + + // + // Indicate that Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = TRUE; + + // + // Set the file names. This must be the last thing we do. + // + + FatConstructNamesInFcb( IrpContext, + Fcb, + Dirent, + Lfn ); + + // + // Drop the shortname hint so prefix searches can figure out + // what they found + // + + Fcb->ShortName.FileNameDos = TRUE; + + } _SEH2_FINALLY { + + DebugUnwind( FatCreateFcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + ULONG i; + + if (UnwindOplock != NULL) { FsRtlUninitializeOplock( UnwindOplock ); } + if (UnwindFileLock != NULL) { FsRtlUninitializeFileLock( UnwindFileLock ); } + if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); } + if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); } + if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); } + if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); } + + for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) { + if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } + } + } + + DebugTrace(-1, Dbg, "FatCreateFcb -> %08lx\n", Fcb); + } _SEH2_END; + + // + // return and tell the caller + // + + return Fcb; +} + + +PDCB +FatCreateDcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB ParentDcb, + IN ULONG LfnOffsetWithinDirectory, + IN ULONG DirentOffsetWithinDirectory, + IN PDIRENT Dirent, + IN PUNICODE_STRING Lfn OPTIONAL + ) + +/*++ + +Routine Description: + + This routine allocates, initializes, and inserts a new Dcb record into + the in memory data structures. + +Arguments: + + Vcb - Supplies the Vcb to associate the new DCB under. + + ParentDcb - Supplies the parent dcb that the new DCB is under. + + LfnOffsetWithinDirectory - Supplies the offset of the LFN. If there is + no LFN associated with this file then this value is same as + DirentOffsetWithinDirectory. + + DirentOffsetWithinDirectory - Supplies the offset, in bytes from the + start of the directory file where the dirent for the fcb is located + + Dirent - Supplies the dirent for the dcb being created + + FileName - Supplies the file name of the file relative to the directory + it's in (e.g., the file \config.sys is called "CONFIG.SYS" without + the preceding backslash). + + Lfn - Supplies a long UNICODE name associated with this directory. + +Return Value: + + PDCB - Returns a pointer to the newly allocated DCB + +--*/ + +{ + PDCB Dcb; + + // + // The following variables are used for abnormal unwind + // + + PVOID UnwindStorage[2] = { NULL, NULL }; + PERESOURCE UnwindResource = NULL; + PERESOURCE UnwindResource2 = NULL; + PLIST_ENTRY UnwindEntryList = NULL; + PLARGE_MCB UnwindMcb = NULL; + + DebugTrace(+1, Dbg, "FatCreateDcb\n", 0); + + + _SEH2_TRY { + + // + // assert that the only time we are called is if wait is true + // + + ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + // + // Allocate a new DCB, and zero it out + // + + UnwindStorage[0] = Dcb = FatAllocateFcb(); + + RtlZeroMemory( Dcb, sizeof(DCB) ); + + UnwindStorage[1] = + Dcb->NonPaged = FatAllocateNonPagedFcb(); + + RtlZeroMemory( Dcb->NonPaged, sizeof( NON_PAGED_FCB ) ); + + // + // Set the proper node type code, node byte size and call backs + // + + Dcb->Header.NodeTypeCode = FAT_NTC_DCB; + Dcb->Header.NodeByteSize = sizeof(DCB); + + Dcb->FcbCondition = FcbGood; + + // + // The initial state, open count, and directory change count fields are + // already zero so we can skip setting them + // + + // + // Initialize the resource variable + // + + + UnwindResource = + Dcb->Header.Resource = FatAllocateResource(); + + // + // Initialize the PagingIo Resource. We no longer use the FsRtl common + // shared pool because this led to a) deadlocks due to cases where files + // and their parent directories shared a resource and b) there is no way + // to anticipate inter-driver induced deadlock via recursive operation. + // + + UnwindResource2 = + Dcb->Header.PagingIoResource = FatAllocateResource(); + + // + // Insert this Dcb into our parent dcb's queue + // + // There is a deep reason why this goes on the head, to allow us + // to easily enumerate all child directories before child files. + // This is important to let us maintain whole-volume lockorder + // via BottomUp enumeration. + // + + InsertHeadList( &ParentDcb->Specific.Dcb.ParentDcbQueue, + &Dcb->ParentDcbLinks ); + UnwindEntryList = &Dcb->ParentDcbLinks; + + // + // Point back to our parent dcb + // + + Dcb->ParentDcb = ParentDcb; + + // + // Set the Vcb + // + + Dcb->Vcb = Vcb; + + // + // Set the dirent offset within the directory + // + + Dcb->LfnOffsetWithinDirectory = LfnOffsetWithinDirectory; + Dcb->DirentOffsetWithinDirectory = DirentOffsetWithinDirectory; + + // + // Set the DirentFatFlags and LastWriteTime + // + + Dcb->DirentFatFlags = Dirent->Attributes; + + Dcb->LastWriteTime = FatFatTimeToNtTime( IrpContext, + Dirent->LastWriteTime, + 0 ); + + // + // These fields are only non-zero when in Chicago mode. + // + + if (FatData.ChicagoMode) { + + LARGE_INTEGER FatSystemJanOne1980; + + // + // If either date is possibly zero, get the system + // version of 1/1/80. + // + + if ((((PUSHORT)Dirent)[9] & ((PUSHORT)Dirent)[8]) == 0) { + + ExLocalTimeToSystemTime( &FatJanOne1980, + &FatSystemJanOne1980 ); + } + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[9] != 0) { + + Dcb->LastAccessTime = + FatFatDateToNtTime( IrpContext, + Dirent->LastAccessDate ); + + } else { + + Dcb->LastAccessTime = FatSystemJanOne1980; + } + + // + // Only do the really hard work if this field is non-zero. + // + + if (((PUSHORT)Dirent)[8] != 0) { + + Dcb->CreationTime = + FatFatTimeToNtTime( IrpContext, + Dirent->CreationTime, + Dirent->CreationMSec ); + + } else { + + Dcb->CreationTime = FatSystemJanOne1980; + } + } + + // + // Initialize Advanced FCB Header fields + // + + ExInitializeFastMutex( &Dcb->NonPaged->AdvancedFcbHeaderMutex ); + FsRtlSetupAdvancedHeader( &Dcb->Header, + &Dcb->NonPaged->AdvancedFcbHeaderMutex ); + + // + // Initialize the Mcb + // + + FsRtlInitializeLargeMcb( &Dcb->Mcb, PagedPool ); + UnwindMcb = &Dcb->Mcb; + + // + // Set the file size, first cluster of file, and allocation size + // based on the information stored in the dirent + // + + Dcb->FirstClusterOfFile = (ULONG)Dirent->FirstClusterOfFile; + + if ( FatIsFat32(Dcb->Vcb) ) { + + Dcb->FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16; + } + + if ( Dcb->FirstClusterOfFile == 0 ) { + + Dcb->Header.AllocationSize.QuadPart = 0; + + } else { + + Dcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + } + + // initialize the notify queues, and the parent dcb queue. + // + + InitializeListHead( &Dcb->Specific.Dcb.ParentDcbQueue ); + + // + // Setup the free dirent bitmap buffer. Since we don't know the + // size of the directory, leave it zero for now. + // + + RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap, + NULL, + 0 ); + + // + // Set our two create dirent aids to represent that we have yet to + // enumerate the directory for never used or deleted dirents. + // + + Dcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff; + Dcb->Specific.Dcb.DeletedDirentHint = 0xffffffff; + + // + // Postpone initializing the cache map until we need to do a read/write + // of the directory file. + + + // + // set the file names. This must be the last thing we do. + // + + FatConstructNamesInFcb( IrpContext, + Dcb, + Dirent, + Lfn ); + + } _SEH2_FINALLY { + + DebugUnwind( FatCreateDcb ); + + // + // If this is an abnormal termination then undo our work + // + + if (_SEH2_AbnormalTermination()) { + + ULONG i; + + if (UnwindMcb != NULL) { FsRtlUninitializeLargeMcb( UnwindMcb ); } + if (UnwindEntryList != NULL) { RemoveEntryList( UnwindEntryList ); } + if (UnwindResource != NULL) { FatFreeResource( UnwindResource ); } + if (UnwindResource2 != NULL) { FatFreeResource( UnwindResource2 ); } + + for (i = 0; i < sizeof(UnwindStorage)/sizeof(PVOID); i += 1) { + if (UnwindStorage[i] != NULL) { ExFreePool( UnwindStorage[i] ); } + } + } + + DebugTrace(-1, Dbg, "FatCreateDcb -> %08lx\n", Dcb); + } _SEH2_END; + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatCreateDcb -> %08lx\n", Dcb); + + return Dcb; +} + + +VOID +FatDeleteFcb_Real ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine deallocates and removes an FCB, DCB, or ROOT DCB record + from Fat's in-memory data structures. It also will remove all + associated underlings (i.e., Notify irps, and child FCB/DCB records). + +Arguments: + + Fcb - Supplies the FCB/DCB/ROOT DCB to be removed + +Return Value: + + None + +--*/ + +{ + DebugTrace(+1, Dbg, "FatDeleteFcb, Fcb = %08lx\n", Fcb); + + // + // We can only delete this record if the open count is zero. + // + + if (Fcb->OpenCount != 0) { + + DebugDump("Error deleting Fcb, Still Open\n", 0, Fcb); + FatBugCheck( 0, 0, 0 ); + } + + // + // If this is a DCB then remove every Notify record from the two + // notify queues + // + + if ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) || + (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) { + + // + // If we allocated a free dirent bitmap buffer, free it. + // + + if ((Fcb->Specific.Dcb.FreeDirentBitmap.Buffer != NULL) && + (Fcb->Specific.Dcb.FreeDirentBitmap.Buffer != + &Fcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) { + + ExFreePool(Fcb->Specific.Dcb.FreeDirentBitmap.Buffer); + } + + ASSERT( Fcb->Specific.Dcb.DirectoryFileOpenCount == 0 ); + ASSERT( IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) ); + + } else { + + // + // Uninitialize the byte range file locks and opportunistic locks + // + + FsRtlUninitializeFileLock( &Fcb->Specific.Fcb.FileLock ); + FsRtlUninitializeOplock( &Fcb->Specific.Fcb.Oplock ); + } + + // + // Release any Filter Context structures associated with this FCB + // + + FsRtlTeardownPerStreamContexts( &Fcb->Header ); + + // + // Uninitialize the Mcb + // + + FsRtlUninitializeLargeMcb( &Fcb->Mcb ); + + // + // If this is not the root dcb then we need to remove ourselves from + // our parents Dcb queue + // + + if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) { + + RemoveEntryList( &(Fcb->ParentDcbLinks) ); + } + + // + // Remove the entry from the splay table if there is still is one. + // + + if (FlagOn( Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE )) { + + FatRemoveNames( IrpContext, Fcb ); + } + + // + // Free the file name pool if allocated. + // + + if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB) { + + // + // If we blew up at inconvenient times, the shortname + // could be null even though you will *never* see this + // normally. Rename is a good example of this case. + // + + if (Fcb->ShortName.Name.Oem.Buffer) { + + ExFreePool( Fcb->ShortName.Name.Oem.Buffer ); + } + + if (Fcb->FullFileName.Buffer) { + + ExFreePool( Fcb->FullFileName.Buffer ); + } + } + + if (Fcb->ExactCaseLongName.Buffer) { + + ExFreePool(Fcb->ExactCaseLongName.Buffer); + } + +#ifdef SYSCACHE_COMPILE + + if (Fcb->WriteMask) { + + ExFreePool( Fcb->WriteMask ); + } + +#endif + + // + // Finally deallocate the Fcb and non-paged fcb records + // + + FatFreeResource( Fcb->Header.Resource ); + + if (Fcb->Header.PagingIoResource != Fcb->Header.Resource) { + + FatFreeResource( Fcb->Header.PagingIoResource ); + } + + // + // If an Event was allocated, get rid of it. + // + + if (Fcb->NonPaged->OutstandingAsyncEvent) { + + ExFreePool( Fcb->NonPaged->OutstandingAsyncEvent ); + } + + FatFreeNonPagedFcb( Fcb->NonPaged ); + FatFreeFcb( Fcb ); + + // + // and return to our caller + // + + DebugTrace(-1, Dbg, "FatDeleteFcb -> VOID\n", 0); + + return; +} + +PCCB +FatCreateCcb ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine creates a new CCB record + +Arguments: + +Return Value: + + CCB - returns a pointer to the newly allocate CCB + +--*/ + +{ + PCCB Ccb; + + DebugTrace(+1, Dbg, "FatCreateCcb\n", 0); + + // + // Allocate a new CCB Record + // + + Ccb = FatAllocateCcb(); + + RtlZeroMemory( Ccb, sizeof(CCB) ); + + // + // Set the proper node type code and node byte size + // + + Ccb->NodeTypeCode = FAT_NTC_CCB; + Ccb->NodeByteSize = sizeof(CCB); + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatCreateCcb -> %08lx\n", Ccb); + + UNREFERENCED_PARAMETER( IrpContext ); + + return Ccb; +} + + + +VOID +FatDeallocateCcbStrings( + IN PCCB Ccb + ) +/*++ + +Routine Description: + + This routine deallocates CCB query templates + +Arguments: + + Ccb - Supplies the CCB + +Return Value: + + None + +--*/ +{ + // + // If we allocated query template buffers, deallocate them now. + // + + if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_UNICODE)) { + + ASSERT( Ccb->UnicodeQueryTemplate.Buffer); + ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT)); + RtlFreeUnicodeString( &Ccb->UnicodeQueryTemplate ); + } + + if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) { + + ASSERT( Ccb->OemQueryTemplate.Wild.Buffer ); + ASSERT( !FlagOn( Ccb->Flags, CCB_FLAG_CLOSE_CONTEXT)); + RtlFreeOemString( &Ccb->OemQueryTemplate.Wild ); + } + + ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT | CCB_FLAG_FREE_UNICODE); +} + + + +VOID +FatDeleteCcb_Real ( + IN PIRP_CONTEXT IrpContext, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + This routine deallocates and removes the specified CCB record + from the Fat in memory data structures + +Arguments: + + Ccb - Supplies the CCB to remove + +Return Value: + + None + +--*/ + +{ + DebugTrace(+1, Dbg, "FatDeleteCcb, Ccb = %08lx\n", Ccb); + + FatDeallocateCcbStrings( Ccb); + + // + // Deallocate the Ccb record + // + + FatFreeCcb( Ccb ); + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatDeleteCcb -> VOID\n", 0); + + UNREFERENCED_PARAMETER( IrpContext ); + + return; +} + + +PIRP_CONTEXT +FatCreateIrpContext ( + IN PIRP Irp, + IN BOOLEAN Wait + ) + +/*++ + +Routine Description: + + This routine creates a new IRP_CONTEXT record + +Arguments: + + Irp - Supplies the originating Irp. + + Wait - Supplies the wait value to store in the context + +Return Value: + + PIRP_CONTEXT - returns a pointer to the newly allocate IRP_CONTEXT Record + +--*/ + +{ + PIRP_CONTEXT IrpContext; + PIO_STACK_LOCATION IrpSp; + + DebugTrace(+1, Dbg, "FatCreateIrpContext\n", 0); + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // The only operations a filesystem device object should ever receive + // are create/teardown of fsdo handles and operations which do not + // occur in the context of fileobjects (i.e., mount). + // + + if (FatDeviceIsFatFsdo( IrpSp->DeviceObject)) { + + if (IrpSp->FileObject != NULL && + IrpSp->MajorFunction != IRP_MJ_CREATE && + IrpSp->MajorFunction != IRP_MJ_CLEANUP && + IrpSp->MajorFunction != IRP_MJ_CLOSE) { + + ExRaiseStatus( STATUS_INVALID_DEVICE_REQUEST ); + } + + ASSERT( IrpSp->FileObject != NULL || + + (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST && + IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_INVALIDATE_VOLUMES) || + + (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + IrpSp->MinorFunction == IRP_MN_MOUNT_VOLUME ) || + + IrpSp->MajorFunction == IRP_MJ_SHUTDOWN ); + } + + // + // Attemtp to allocate from the region first and failing that allocate + // from pool. + // + + DebugDoit( FatFsdEntryCount += 1); + + IrpContext = FatAllocateIrpContext(); + + // + // Zero out the irp context. + // + + RtlZeroMemory( IrpContext, sizeof(IRP_CONTEXT) ); + + // + // Set the proper node type code and node byte size + // + + IrpContext->NodeTypeCode = FAT_NTC_IRP_CONTEXT; + IrpContext->NodeByteSize = sizeof(IRP_CONTEXT); + + // + // Set the originating Irp field + // + + IrpContext->OriginatingIrp = Irp; + + // + // Major/Minor Function codes + // + + IrpContext->MajorFunction = IrpSp->MajorFunction; + IrpContext->MinorFunction = IrpSp->MinorFunction; + + // + // Copy RealDevice for workque algorithms, and also set Write Through + // and Removable Media if there is a file object. Only file system + // control Irps won't have a file object, and they should all have + // a Vpb as the first IrpSp location. + // + + if (IrpSp->FileObject != NULL) { + +#ifndef __REACTOS__ + PVCB Vcb; +#endif + PFILE_OBJECT FileObject = IrpSp->FileObject; + + IrpContext->RealDevice = FileObject->DeviceObject; +#ifndef __REACTOS__ + Vcb = IrpContext->Vcb = &((PVOLUME_DEVICE_OBJECT)(IrpSp->DeviceObject))->Vcb; +#else + IrpContext->Vcb = &((PVOLUME_DEVICE_OBJECT)(IrpSp->DeviceObject))->Vcb; +#endif + + // + // See if the request is Write Through. + // + + if (IsFileWriteThrough( FileObject, Vcb )) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); + } + + } else if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) { + + IrpContext->RealDevice = IrpSp->Parameters.MountVolume.Vpb->RealDevice; + } + + // + // Set the wait parameter + // + + if (Wait) { SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); } + + // + // Set the recursive file system call parameter. We set it true if + // the TopLevelIrp field in the thread local storage is not the current + // irp, otherwise we leave it as FALSE. + // + + if ( IoGetTopLevelIrp() != Irp) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL); + } + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatCreateIrpContext -> %08lx\n", IrpContext); + + return IrpContext; +} + + + +VOID +FatDeleteIrpContext_Real ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine deallocates and removes the specified IRP_CONTEXT record + from the Fat in memory data structures. It should only be called + by FatCompleteRequest. + +Arguments: + + IrpContext - Supplies the IRP_CONTEXT to remove + +Return Value: + + None + +--*/ + +{ + DebugTrace(+1, Dbg, "FatDeleteIrpContext, IrpContext = %08lx\n", IrpContext); + + ASSERT( IrpContext->NodeTypeCode == FAT_NTC_IRP_CONTEXT ); + ASSERT( IrpContext->PinCount == 0 ); + + // + // If there is a FatIoContext that was allocated, free it. + // + + if (IrpContext->FatIoContext != NULL) { + + if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) { + + if (IrpContext->FatIoContext->ZeroMdl) { + IoFreeMdl( IrpContext->FatIoContext->ZeroMdl ); + } + + ExFreePool( IrpContext->FatIoContext ); + } + } + + // + // Drop the IrpContext. + // + + FatFreeIrpContext( IrpContext ); + + // + // return and tell the caller + // + + DebugTrace(-1, Dbg, "FatDeleteIrpContext -> VOID\n", 0); + + return; +} + + +PFCB +FatGetNextFcbBottomUp ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb OPTIONAL, + IN PFCB TerminationFcb + ) + +/*++ + +Routine Description: + + This routine is used to iterate through Fcbs in a tree. In order to match + the lockorder for getting multiple Fcbs (so this can be used for acquiring + all Fcbs), this version does a bottom-up enumeration. + + This is different than the old one, now called TopDown. The problem with + lockorder was very well hidden. + + The transition rule is still pretty simple: + + A) If you have an adjacent sibling, go to it + 1) Descend to its leftmost child + B) Else go to your parent + + If this routine is called with in invalid TerminationFcb it will fail, + badly. + + The TerminationFcb is the last Fcb returned in the enumeration. + + This method is incompatible with the possibility that ancestors may vanish + based on operations done on the last returned node. For instance, + FatPurgeReferencedFileObjects cannot use BottomUp enumeration. + +Arguments: + + Fcb - Supplies the current Fcb. This is NULL if enumeration is starting. + + TerminationFcb - The root Fcb of the tree in which the enumeration starts + and at which it inclusively stops. + +Return Value: + + The next Fcb in the enumeration, or NULL if Fcb was the final one. + +--*/ + +{ + PFCB NextFcb; + + ASSERT( FatVcbAcquiredExclusive( IrpContext, TerminationFcb->Vcb ) || + FlagOn( TerminationFcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) ); + + // + // Do we need to begin the enumeration? + // + + if (Fcb != NULL) { + + // + // Did we complete? + // + + if (Fcb == TerminationFcb) { + + return NULL; + } + + // + // Do we have a sibling to return? + // + + NextFcb = FatGetNextSibling( Fcb ); + + // + // If not, return our parent. We are done with this branch. + // + + if (NextFcb == NULL) { + + return Fcb->ParentDcb; + } + + } else { + + NextFcb = TerminationFcb; + } + + // + // Decend to its furthest child (if it exists) and return it. + // + + for (; + NodeType( NextFcb ) != FAT_NTC_FCB && FatGetFirstChild( NextFcb ) != NULL; + NextFcb = FatGetFirstChild( NextFcb )) { + } + + return NextFcb; +} + +PFCB +FatGetNextFcbTopDown ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFCB TerminationFcb + ) + +/*++ + +Routine Description: + + This routine is used to iterate through Fcbs in a tree, from the top down. + + The rule is very simple: + + A) If you have a child, go to it, else + B) If you have an older sibling, go to it, else + C) Go to your parent's older sibling. + + If this routine is called with in invalid TerminationFcb it will fail, + badly. + + The Termination Fcb is never returned. If it is the root of the tree you + are traversing, visit it first. + + This routine never returns direct ancestors of Fcb, and thus is useful when + making Fcb's go away (which may tear up the tree). + +Arguments: + + Fcb - Supplies the current Fcb + + TerminationFcb - The Fcb at which the enumeration should (non-inclusivly) + stop. Assumed to be a directory. + +Return Value: + + The next Fcb in the enumeration, or NULL if Fcb was the final one. + +--*/ + +{ + PFCB Sibling; + + ASSERT( FatVcbAcquiredExclusive( IrpContext, Fcb->Vcb ) || + FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_LOCKED ) ); + + // + // If this was a directory (ie. not a file), get the child. If + // there aren't any children and this is our termination Fcb, + // return NULL. + // + + if ( ((NodeType(Fcb) == FAT_NTC_DCB) || + (NodeType(Fcb) == FAT_NTC_ROOT_DCB)) && + !IsListEmpty(&Fcb->Specific.Dcb.ParentDcbQueue) ) { + + return FatGetFirstChild( Fcb ); + } + + // + // Were we only meant to do one iteration? + // + + if ( Fcb == TerminationFcb ) { + + return NULL; + } + + Sibling = FatGetNextSibling(Fcb); + + while (TRUE) { + + // + // Do we still have an "older" sibling in this directory who is + // not the termination Fcb? + // + + if ( Sibling != NULL ) { + + return (Sibling != TerminationFcb) ? Sibling : NULL; + } + + // + // OK, let's move on to out parent and see if he is the termination + // node or has any older siblings. + // + + if ( Fcb->ParentDcb == TerminationFcb ) { + + return NULL; + } + + Fcb = Fcb->ParentDcb; + + Sibling = FatGetNextSibling(Fcb); + } +} + +BOOLEAN +FatSwapVpb ( + IN PIRP_CONTEXT IrpContext, + PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine swaps the VPB for this VCB if it has not been done already. + This means the device object will get our spare VPB and we will cleanup + the one used while the volume was mounted. + +Arguments: + + IrpContext - Supplies the context for the overall request. + + Vcb - Supplies the VCB to swap the VPB on. + +Return Value: + + TRUE - If the VPB was actually swapped. + + FALSE - If the VPB was already swapped. + +--*/ + +{ + BOOLEAN Result = FALSE; + PVPB OldVpb; + PIO_STACK_LOCATION IrpSp; + + + // + // Make sure we have not already swapped it. + // + + OldVpb = Vcb->Vpb; + + if (OldVpb->RealDevice->Vpb == OldVpb) { + + // + // If not the final reference and we are forcing the disconnect, + // then swap out the Vpb. We must preserve the REMOVE_PENDING flag + // so that the device is not remounted in the middle of a PnP remove + // operation. + // + + ASSERT( Vcb->SwapVpb != NULL ); + + Vcb->SwapVpb->Type = IO_TYPE_VPB; + Vcb->SwapVpb->Size = sizeof( VPB ); + Vcb->SwapVpb->RealDevice = OldVpb->RealDevice; + + Vcb->SwapVpb->RealDevice->Vpb = Vcb->SwapVpb; + + Vcb->SwapVpb->Flags = FlagOn( OldVpb->Flags, VPB_REMOVE_PENDING ); + + // + // If we are working on a mount request, we need to make sure we update + // the VPB in the IRP, since the one it points to may no longer be valid. + // + + if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) { + + // + // Get the IRP stack. + // + + IrpSp = IoGetCurrentIrpStackLocation( IrpContext->OriginatingIrp ); + + ASSERT( IrpSp->FileObject == NULL ); + + // + // Check the VPB in the IRP to see if it is the one we are swapping. + // + + if (IrpSp->Parameters.MountVolume.Vpb == OldVpb) { + + // + // Change the IRP to point to the swap VPB. + // + + IrpSp->Parameters.MountVolume.Vpb = Vcb->SwapVpb; + } + } + + // + // We place the volume in the Bad state (as opposed to NotMounted) so + // that it is not eligible for a remount. Also indicate we used up + // the swap. + // + + Vcb->SwapVpb = NULL; + FatSetVcbCondition( Vcb, VcbBad); + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_VPB_MUST_BE_FREED ); + + Result = TRUE; + } + + return Result; +} + + +BOOLEAN +FatCheckForDismount ( + IN PIRP_CONTEXT IrpContext, + PVCB Vcb, + IN BOOLEAN Force + ) + +/*++ + +Routine Description: + + This routine determines if a volume is ready for deletion. It + correctly synchronizes with creates en-route to the file system. + +Arguments: + + Vcb - Supplies the volume to examine + + Force - Specifies whether we want this Vcb forcibly disconnected + from the driver stack if it will not be deleted (a new vpb will + be installed if neccesary). Caller is responsible for making + sure that the volume has been marked in such a way that attempts + to operate through the realdevice are blocked (i.e., move the vcb + out of the mounted state). + +Return Value: + + BOOLEAN - TRUE if the volume was deleted, FALSE otherwise. + +--*/ + +{ + KIRQL SavedIrql; + BOOLEAN VcbDeleted = FALSE; + + // + // If the VCB condition is good and we are not forcing, just return. + // + + if (Vcb->VcbCondition == VcbGood && !Force) { + + return FALSE; + } + + // + // Now check for a zero Vpb count on an unmounted volume. These + // volumes will be deleted as they now have no file objects and + // there are no creates en route to this volume. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + if (Vcb->Vpb->ReferenceCount == Vcb->ResidualOpenCount && Vcb->OpenFileCount == 0) { + + PVPB Vpb = Vcb->Vpb; + +#if DBG + UNICODE_STRING VolumeLabel; + + // + // Setup the VolumeLabel string + // + + VolumeLabel.Length = Vcb->Vpb->VolumeLabelLength; + VolumeLabel.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH; + VolumeLabel.Buffer = &Vcb->Vpb->VolumeLabel[0]; + + KdPrintEx((DPFLTR_FASTFAT_ID, + DPFLTR_INFO_LEVEL, + "FASTFAT: Dismounting Volume %Z\n", + &VolumeLabel)); +#endif // DBG + + // + // Swap this VCB's VPB. + // + + FatSwapVpb( IrpContext, + Vcb ); + + // + // Clear the VPB_MOUNTED bit. New opens should not come in due + // to the swapped VPB, but having the flag cleared helps debugging. + // Note that we must leave the Vpb->DeviceObject field set until + // after the FatTearDownVcb call as closes will have to make their + // way back to us. + // + + ClearFlag( Vpb->Flags, VPB_MOUNTED ); + + // + // If this Vpb was locked, clear this flag now. + // + + ClearFlag( Vpb->Flags, VPB_LOCKED ); + + IoReleaseVpbSpinLock( SavedIrql ); + + // + // We are going to attempt the dismount, so mark the VCB as having + // a dismount in progress. + // + + ASSERT( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) ); + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ); + + // + // Close down our internal opens. + // + + FatTearDownVcb( IrpContext, + Vcb ); + + // + // Process any delayed closes. + // + + FatFspClose( Vcb ); + + // + // Grab the VPB lock again so that we can recheck our counts. + // + + IoAcquireVpbSpinLock( &SavedIrql ); + + // + // See if we can delete this VCB. + // + + if (Vcb->Vpb->ReferenceCount == 0 && Vcb->InternalOpenCount == 0) { + + Vpb->DeviceObject = NULL; + + IoReleaseVpbSpinLock( SavedIrql ); + + FatDeleteVcb( IrpContext, Vcb ); + + + IoDeleteDevice( (PDEVICE_OBJECT) + CONTAINING_RECORD( Vcb, + VOLUME_DEVICE_OBJECT, + Vcb ) ); + + VcbDeleted = TRUE; + + } else { + + IoReleaseVpbSpinLock( SavedIrql ); + + ASSERT( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ) ); + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS ); + } + + } else if (Force) { + + // + // The requester is forcing the issue. We need to swap the VPB with our spare. + // + + FatSwapVpb( IrpContext, + Vcb ); + + IoReleaseVpbSpinLock( SavedIrql ); + + } else { + + // + // Just drop the Vpb spinlock. + // + + IoReleaseVpbSpinLock( SavedIrql ); + } + + return VcbDeleted; +} + + +VOID +FatConstructNamesInFcb ( + IN PIRP_CONTEXT IrpContext, + PFCB Fcb, + PDIRENT Dirent, + PUNICODE_STRING Lfn OPTIONAL + ) + +/*++ + +Routine Description: + + This routine places the short name in the dirent in the first set of + STRINGs in the Fcb. If a long file name (Lfn) was specified, then + we must decide whether we will store its Oem equivolent in the same + prefix table as the short name, or rather just save the upcased + version of the UNICODE string in the FCB. + + For looking up Fcbs, the first approach will be faster, so we want to + do this as much as possible. Here are the rules that I have thought + through extensively to determine when it is safe to store only Oem + version of the UNICODE name. + + - If the UNICODE name contains no extended characters (>0x80), use Oem. + + - Let U be the upcased version of the UNICODE name. + Let Up(x) be the function that upcases a UNICODE string. + Let Down(x) be the function that upcases a UNICODE string. + Let OemToUni(x) be the function that converts an Oem string to Unicode. + Let UniToOem(x) be the function that converts a Unicode string to Oem. + Let BestOemFit(x) be the function that creates the Best uppercase Oem + fit for the UNICODE string x. + + BestOemFit(x) = UniToOem(Up(OemToUni(UniToOem(x)))) <1> + + if (BestOemFit(U) == BestOemFit(Down(U)) <2> + + then I know that there exists no UNICODE string Y such that: + + Up(Y) == Up(U) <3> + + AND + + BestOemFit(U) != BestOemFit(Y) <4> + + Consider string U as a collection of one character strings. The + conjecture is clearly true for each sub-string, thus it is true + for the entire string. + + Equation <1> is what we use to convert an incoming unicode name in + FatCommonCreate() to Oem. The double conversion is done to provide + better visual best fitting for characters in the Ansi code page but + not in the Oem code page. A single Nls routine is provided to do + this conversion efficiently. + + The idea is that with U, I only have to worry about a case varient Y + matching it in a unicode compare, and I have shown that any case varient + of U (the set Y defined in equation <3>), when filtered through <1> + (as in create), will match the Oem string defined in <1>. + + Thus I do not have to worry about another UNICODE string missing in + the prefix lookup, but matching when comparing LFNs in the directory. + +Arguments: + + Fcb - The Fcb we are supposed to fill in. Note that ParentDcb must + already be filled in. + + Dirent - The gives up the short name. + + Lfn - If provided, this gives us the long name. + +Return Value: + + None + +--*/ + +{ +#ifndef __REACTOS__ + NTSTATUS Status; +#endif + ULONG i; + +#ifndef __REACTOS__ + OEM_STRING OemA; + OEM_STRING OemB; +#endif + UNICODE_STRING Unicode; + POEM_STRING ShortName; + POEM_STRING LongOemName; + PUNICODE_STRING LongUniName; + + ShortName = &Fcb->ShortName.Name.Oem; + + ASSERT( ShortName->Buffer == NULL ); + + _SEH2_TRY { + + // + // First do the short name. + // + + // + // Copy over the case flags for the short name of the file + // + + if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) { + + SetFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE); + + } else { + + ClearFlag(Fcb->FcbState, FCB_STATE_8_LOWER_CASE); + } + + if (FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) { + + SetFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE); + + } else { + + ClearFlag(Fcb->FcbState, FCB_STATE_3_LOWER_CASE); + } + + ShortName->MaximumLength = 16; + ShortName->Buffer = FsRtlAllocatePoolWithTag( PagedPool, + 16, + TAG_FILENAME_BUFFER ); + + Fat8dot3ToString( IrpContext, Dirent, FALSE, ShortName ); + + // + // If no Lfn was specified, we are done. In either case, set the + // final name length. + // + + ASSERT( Fcb->ExactCaseLongName.Buffer == NULL ); + + if (!ARGUMENT_PRESENT(Lfn) || (Lfn->Length == 0)) { + + Fcb->FinalNameLength = (USHORT) RtlOemStringToCountedUnicodeSize( ShortName ); + Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = 0; + + try_return( NOTHING ); + } + + // + // If we already set up the full filename, we could be in trouble. If the fast + // path for doing it already fired, FatSetFullFileNameInFcb, it will have missed + // this and could have built the full filename out of the shortname of the file. + // + // At that point, disaster could be inevitable since the final name length will not + // match. We use this to tell the notify package what to do - FatNotifyReportChange. + // + + ASSERT( Fcb->FullFileName.Buffer == NULL ); + + // + // We know now we have an Lfn, save away a copy. + // + + Fcb->FinalNameLength = Lfn->Length; + + Fcb->ExactCaseLongName.Length = Fcb->ExactCaseLongName.MaximumLength = Lfn->Length; + Fcb->ExactCaseLongName.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Lfn->Length, + TAG_FILENAME_BUFFER ); + RtlCopyMemory(Fcb->ExactCaseLongName.Buffer, Lfn->Buffer, Lfn->Length); + + // + // First check for no extended characters. + // + + for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) { + + if (Lfn->Buffer[i] >= 0x80) { + + break; + } + } + + if (i == Lfn->Length/sizeof(WCHAR)) { + + // + // Cool, I can go with the Oem, upcase it fast by hand. + // + + LongOemName = &Fcb->LongName.Oem.Name.Oem; + + + LongOemName->Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Lfn->Length/sizeof(WCHAR), + TAG_FILENAME_BUFFER ); + LongOemName->Length = + LongOemName->MaximumLength = Lfn->Length/sizeof(WCHAR); + + for (i=0; i < Lfn->Length/sizeof(WCHAR); i++) { + + WCHAR c; + + c = Lfn->Buffer[i]; + + LongOemName->Buffer[i] = c < 'a' ? + (UCHAR)c : + c <= 'z' ? + c - (UCHAR)('a'-'A') : + (UCHAR) c; + } + + // + // If this name happens to be exactly the same as the short + // name, don't add it to the splay table. + // + + if (FatAreNamesEqual(IrpContext, *ShortName, *LongOemName) || + (FatFindFcb( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootOemNode, + LongOemName, + NULL) != NULL)) { + + ExFreePool( LongOemName->Buffer ); + + LongOemName->Buffer = NULL; + LongOemName->Length = + LongOemName->MaximumLength = 0; + + } else { + + SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME ); + } + + try_return( NOTHING ); + } + + // + // Now we have the fun part. Make a copy of the Lfn. + // + +#ifndef __REACTOS__ + OemA.Buffer = NULL; + OemB.Buffer = NULL; +#endif + Unicode.Buffer = NULL; + + Unicode.Length = + Unicode.MaximumLength = Lfn->Length; + Unicode.Buffer = FsRtlAllocatePoolWithTag( PagedPool, + Lfn->Length, + TAG_FILENAME_BUFFER ); + + RtlCopyMemory( Unicode.Buffer, Lfn->Buffer, Lfn->Length ); + +#ifndef __REACTOS__ + Status = STATUS_SUCCESS; +#endif + +#if TRUE + // + // Unfortunately, this next block of code breaks down when you have + // two long Unicode filenames that both map to the same Oem (and are, + // well, long, i.e. are not the short names). In this case, with one + // in the prefix table first, the other will hit the common Oem + // representation. This leads to several forms of user astonishment. + // + // It isn't worth it, or probably even possible, to try to figure out + // when this is really safe to go through. Simply omit the attempt. + // + // Ex: ANSI 0x82 and 0x84 in the 1252 ANSI->UNI and 437 UNI->OEM codepages. + // + // 0x82 => 0x201a => 0x2c + // 0x84 => 0x201e => 0x2c + // + // 0x2c is comma, so is FAT Oem illegal and forces shortname generation. + // Since it is otherwise well-formed by the rules articulated previously, + // we would have put 0x2c in the Oem prefix tree. In terms of the + // argument given above, even though there exist no Y and U s.t. + // + // Up(Y) == Up(U) && BestOemFit(U) != BestOemFit(Y) + // + // there most certainly exist Y and U s.t. + // + // Up(Y) != Up(U) && BestOemFit(U) == BestOemFit(Y) + // + // and that is enough to keep us from doing this. Note that the < 0x80 + // case is OK since we know that the mapping in the OEM codepages are + // the identity in that range. + // + // We still need to monocase it, though. Do this through a full down/up + // transition. + // + + (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE ); + (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE ); +#else + // + // Downcase and convert to upcased Oem. Only continue if we can + // convert without error. Any error other than UNMAPPABLE_CHAR + // is a fatal error and we raise. + // + // Note that even if the conversion fails, we must leave Unicode + // in an upcased state. + // + // NB: The Rtl doesn't NULL .Buffer on error. + // + + (VOID)RtlDowncaseUnicodeString( &Unicode, &Unicode, FALSE ); + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemA, &Unicode, TRUE ); + (VOID)RtlUpcaseUnicodeString( &Unicode, &Unicode, FALSE ); + + if (!NT_SUCCESS(Status)) { + + if (Status != STATUS_UNMAPPABLE_CHARACTER) { + + ASSERT( Status == STATUS_NO_MEMORY ); + ExFreePool(Unicode.Buffer); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + } else { + + // + // The same as above except upcase. + // + + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemB, &Unicode, TRUE ); + + if (!NT_SUCCESS(Status)) { + + RtlFreeOemString( &OemA ); + + if (Status != STATUS_UNMAPPABLE_CHARACTER) { + + ASSERT( Status == STATUS_NO_MEMORY ); + ExFreePool(Unicode.Buffer); + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + } + } + + // + // If the final OemNames are equal, I can use save only the Oem + // name. If the name did not map, then I have to go with the UNICODE + // name because I could get a case varient that didn't convert + // in create, but did match the LFN. + // + + if (NT_SUCCESS(Status) && FatAreNamesEqual( IrpContext, OemA, OemB )) { + + // + // Cool, I can go with the Oem. If we didn't convert correctly, + // get a fresh convert from the original LFN. + // + + ExFreePool(Unicode.Buffer); + + RtlFreeOemString( &OemB ); + + Fcb->LongName.Oem.Name.Oem = OemA; + + // + // If this name happens to be exactly the same as the short + // name, or a similar short name already exists don't add it + // to the splay table (note the final condition implies a + // corrupt disk. + // + + if (FatAreNamesEqual(IrpContext, *ShortName, OemA) || + (FatFindFcb( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootOemNode, + &OemA, + NULL) != NULL)) { + + RtlFreeOemString( &OemA ); + + } else { + + SetFlag( Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME ); + } + + try_return( NOTHING ); + } + + // + // The long name must be left in UNICODE. Free the two Oem strings + // if we got here just because they weren't equal. + // + + if (NT_SUCCESS(Status)) { + + RtlFreeOemString( &OemA ); + RtlFreeOemString( &OemB ); + } +#endif + + LongUniName = &Fcb->LongName.Unicode.Name.Unicode; + + LongUniName->Length = + LongUniName->MaximumLength = Unicode.Length; + LongUniName->Buffer = Unicode.Buffer; + + SetFlag(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME); + + try_exit: NOTHING; + } _SEH2_FINALLY { + + if (_SEH2_AbnormalTermination()) { + + if (ShortName->Buffer != NULL) { + + ExFreePool( ShortName->Buffer ); + ShortName->Buffer = NULL; + } + + } else { + + // + // Creating all the names worked, so add all the names + // to the splay tree. + // + + FatInsertName( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootOemNode, + &Fcb->ShortName ); + + Fcb->ShortName.Fcb = Fcb; + + if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_OEM_LONG_NAME)) { + + FatInsertName( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootOemNode, + &Fcb->LongName.Oem ); + + Fcb->LongName.Oem.Fcb = Fcb; + } + + if (FlagOn(Fcb->FcbState, FCB_STATE_HAS_UNICODE_LONG_NAME)) { + + FatInsertName( IrpContext, + &Fcb->ParentDcb->Specific.Dcb.RootUnicodeNode, + &Fcb->LongName.Unicode ); + + Fcb->LongName.Unicode.Fcb = Fcb; + } + + SetFlag(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE); + } + } _SEH2_END; + + return; +} + + +VOID +FatCheckFreeDirentBitmap ( + IN PIRP_CONTEXT IrpContext, + IN PDCB Dcb + ) + +/*++ + +Routine Description: + + This routine checks if the size of the free dirent bitmap is + sufficient to for the current directory size. It is called + whenever we grow a directory. + +Arguments: + + Dcb - Supplies the directory in question. + +Return Value: + + None + +--*/ + +{ + ULONG OldNumberOfDirents; + ULONG NewNumberOfDirents; + + // + // Setup the Bitmap buffer if it is not big enough already + // + + ASSERT( Dcb->Header.AllocationSize.QuadPart != FCB_LOOKUP_ALLOCATIONSIZE_HINT ); + + OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap; + NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT); + + // + // Do the usual unsync/sync check. + // + + if (NewNumberOfDirents > OldNumberOfDirents) { + + FatAcquireDirectoryFileMutex( Dcb->Vcb ); + + _SEH2_TRY { + + PULONG OldBitmapBuffer; + PULONG BitmapBuffer; + + ULONG BytesInBitmapBuffer; + ULONG BytesInOldBitmapBuffer; + + OldNumberOfDirents = Dcb->Specific.Dcb.FreeDirentBitmap.SizeOfBitMap; + NewNumberOfDirents = Dcb->Header.AllocationSize.LowPart / sizeof(DIRENT); + + if (NewNumberOfDirents > OldNumberOfDirents) { + + // + // Remember the old bitmap + // + + OldBitmapBuffer = Dcb->Specific.Dcb.FreeDirentBitmap.Buffer; + + // + // Now make a new bitmap bufffer + // + + BytesInBitmapBuffer = NewNumberOfDirents / 8; + + BytesInOldBitmapBuffer = OldNumberOfDirents / 8; + + if (DCB_UNION_SLACK_SPACE >= BytesInBitmapBuffer) { + + BitmapBuffer = &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0]; + + } else { + + BitmapBuffer = FsRtlAllocatePoolWithTag( PagedPool, + BytesInBitmapBuffer, + TAG_DIRENT_BITMAP ); + } + + // + // Copy the old buffer to the new buffer, free the old one, and zero + // the rest of the new one. Only do the first two steps though if + // we moved out of the initial buffer. + // + + if ((OldNumberOfDirents != 0) && + (BitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0])) { + + RtlCopyMemory( BitmapBuffer, + OldBitmapBuffer, + BytesInOldBitmapBuffer ); + + if (OldBitmapBuffer != &Dcb->Specific.Dcb.FreeDirentBitmapBuffer[0]) { + + ExFreePool( OldBitmapBuffer ); + } + } + + ASSERT( BytesInBitmapBuffer > BytesInOldBitmapBuffer ); + + RtlZeroMemory( (PUCHAR)BitmapBuffer + BytesInOldBitmapBuffer, + BytesInBitmapBuffer - BytesInOldBitmapBuffer ); + + // + // Now initialize the new bitmap. + // + + RtlInitializeBitMap( &Dcb->Specific.Dcb.FreeDirentBitmap, + BitmapBuffer, + NewNumberOfDirents ); + } + + } _SEH2_FINALLY { + + FatReleaseDirectoryFileMutex( Dcb->Vcb ); + } _SEH2_END; + } +} + + +BOOLEAN +FatIsHandleCountZero ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine decides if the handle count on the volume is zero. + +Arguments: + + Vcb - The volume in question + +Return Value: + + BOOLEAN - TRUE if there are no open handles on the volume, FALSE + otherwise. + +--*/ + +{ + PFCB Fcb; + + Fcb = Vcb->RootDcb; + + while (Fcb != NULL) { + + if (Fcb->UncleanCount != 0) { + + return FALSE; + } + + Fcb = FatGetNextFcbTopDown(IrpContext, Fcb, Vcb->RootDcb); + } + + return TRUE; +} + + +VOID +FatPreallocateCloseContext ( + ) + +/*++ + +Routine Description: + + This routine preallocates a close context, presumeably on behalf + of a fileobject which does not have a structure we can embed one + in. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + PCLOSE_CONTEXT CloseContext = FsRtlAllocatePoolWithTag( PagedPool, + sizeof(CLOSE_CONTEXT), + TAG_FAT_CLOSE_CONTEXT ); + + ExInterlockedPushEntrySList( &FatCloseContextSList, + (PSINGLE_LIST_ENTRY) CloseContext, + &FatData.GeneralSpinLock ); +} + + +VOID +FatEnsureStringBufferEnough( + IN OUT PVOID String, + IN USHORT DesiredBufferSize + ) +/*++ + +Routine Description: + + Ensures that a string string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING) + has a buffer >= DesiredBufferSize, allocating from pool if neccessary. Any + existing pool buffer will be freed if a new one is allocated. + + NOTE: No copy of old buffer contents is performed on reallocation. + + Will raise on allocation failure. + +Arguments: + + String - pointer to string structure + + DesiredBufferSize - (bytes) minimum required buffer size + +--*/ +{ + PSTRING LocalString = String; + + if (LocalString->MaximumLength < DesiredBufferSize) { + + FatFreeStringBuffer( LocalString); + + LocalString->Buffer = FsRtlAllocatePoolWithTag( PagedPool, + DesiredBufferSize, + TAG_DYNAMIC_NAME_BUFFER); + ASSERT( LocalString->Buffer); + + LocalString->MaximumLength = DesiredBufferSize; + } +} + + +VOID +FatFreeStringBuffer( + IN PVOID String + ) +/*++ + +Routine Description: + + Frees the buffer of an string (STRING, UNICODE_STRING, ANSI_STRING, OEM_STRING) + structure if it is not within the current thread's stack limits. + + Regardless of action performed, on exit String->Buffer will be set to NULL and + String->MaximumLength to zero. + +Arguments: + + String - pointer to string structure + +--*/ +{ + ULONG_PTR High, Low; + PSTRING LocalString = String; + + if (NULL != LocalString->Buffer) { + + IoGetStackLimits( &Low, &High ); + + if (((ULONG_PTR)(LocalString->Buffer) < Low) || + ((ULONG_PTR)(LocalString->Buffer) > High)) { + + ExFreePool( LocalString->Buffer); + } + + LocalString->Buffer = NULL; + } + + LocalString->MaximumLength = LocalString->Length = 0; +} + + +BOOLEAN +FatScanForDataTrack( + IN PIRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDeviceObject + ) + +/*++ + +Routine Description: + + This routine is called to verify and process the TOC for this disk. + + FAT queries for the TOC to avoid trying to mount on CD-DA/CD-E media, Doing data reads on + audio/leadin of that media sends a lot of drives into what could charitably be called + "conniptions" which take a couple seconds to clear and would also convince FAT that the + device was busted, and fail the mount (not letting CDFS get its crack). + + There is special handling of PD media. These things fail the TOC read, but return + a special error code so FAT knows to continue to try the mount anyway. + +Arguments: + + TargetDeviceObject - Device object to send TOC request to. + +Return Value: + + BOOLEAN - TRUE if we found a TOC with a single data track. + +--*/ + +{ + NTSTATUS Status; + IO_STATUS_BLOCK Iosb; + + ULONG LocalTrackCount; + ULONG LocalTocLength; + + PCDROM_TOC CdromToc; + BOOLEAN Result = FALSE; + + PAGED_CODE(); + + CdromToc = FsRtlAllocatePoolWithTag( PagedPool, + sizeof( CDROM_TOC ), + TAG_IO_BUFFER ); + + RtlZeroMemory( CdromToc, sizeof( CDROM_TOC )); + + _SEH2_TRY { + + // + // Go ahead and read the table of contents + // + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_CDROM_READ_TOC, + TargetDeviceObject, + CdromToc, + sizeof( CDROM_TOC ), + FALSE, + TRUE, + &Iosb ); + + // + // Nothing to process if this request fails. + // + + if (Status != STATUS_SUCCESS) { + + // + // If we get the special error indicating a failed TOC read on PD media just + // plow ahead with the mount (see comments above). + // + + if ((Status == STATUS_IO_DEVICE_ERROR) || (Status == STATUS_INVALID_DEVICE_REQUEST)) { + + Result = TRUE; + + } + + try_leave( NOTHING ); + } + + // + // Get the number of tracks and stated size of this structure. + // + + LocalTrackCount = CdromToc->LastTrack - CdromToc->FirstTrack + 1; + LocalTocLength = PtrOffset( CdromToc, &CdromToc->TrackData[LocalTrackCount + 1] ); + + // + // Get out if there is an immediate problem with the TOC, or more than + // one track. + // + + if ((LocalTocLength > Iosb.Information) || + (CdromToc->FirstTrack > CdromToc->LastTrack) || + (LocalTrackCount != 1)) { + + try_leave( NOTHING); + } + + // + // Is it a data track? DVD-RAM reports single, data, track. + // + + Result = BooleanFlagOn( CdromToc->TrackData[ 0].Control, 0x04 ); + } + _SEH2_FINALLY { + + ExFreePool( CdromToc); + } _SEH2_END; + + return Result; +} + + diff --git a/drivers/filesystems/fastfat_new/timesup.c b/drivers/filesystems/fastfat_new/timesup.c new file mode 100644 index 00000000000..59ebe3840c1 --- /dev/null +++ b/drivers/filesystems/fastfat_new/timesup.c @@ -0,0 +1,364 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + TimeSup.c + +Abstract: + + This module implements the Fat Time conversion support routines + + +--*/ + +#include "fatprocs.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatNtTimeToFatTime) +#pragma alloc_text(PAGE, FatFatDateToNtTime) +#pragma alloc_text(PAGE, FatFatTimeToNtTime) +#pragma alloc_text(PAGE, FatGetCurrentFatTime) +#endif + +BOOLEAN +FatNtTimeToFatTime ( + IN PIRP_CONTEXT IrpContext, + IN PLARGE_INTEGER NtTime, + IN BOOLEAN Rounding, + OUT PFAT_TIME_STAMP FatTime, +#ifndef __REACTOS__ + OUT OPTIONAL PCHAR TenMsecs +#else + OUT OPTIONAL PUCHAR TenMsecs +#endif + ) + +/*++ + +Routine Description: + + This routine converts an NtTime value to its corresponding Fat time value. + +Arguments: + + NtTime - Supplies the Nt GMT Time value to convert from + + Rounding - Indicates whether the NT time should be rounded up to a FAT boundary. + This should only be done *once* in the lifetime of a timestamp (important + for tunneling, which will cause a timestamp to pass through at least twice). + If true, rounded up. If false, rounded down to 10ms boundary. This obeys + the rules for non-creation time and creation times (respectively). + + FatTime - Receives the equivalent Fat time value + + TenMsecs - Optionally receive the number of tens of milliseconds the NtTime, after + any rounding, is greater than the FatTime + +Return Value: + + BOOLEAN - TRUE if the Nt time value is within the range of Fat's + time range, and FALSE otherwise + +--*/ + +{ + TIME_FIELDS TimeFields; + + // + // Convert the input to the a time field record. + // + + if (Rounding) { + + // + // Add almost two seconds to round up to the nearest double second. + // + + NtTime->QuadPart = NtTime->QuadPart + AlmostTwoSeconds; + } + + ExSystemTimeToLocalTime( NtTime, NtTime ); + + RtlTimeToTimeFields( NtTime, &TimeFields ); + + // + // Check the range of the date found in the time field record + // + + if ((TimeFields.Year < 1980) || (TimeFields.Year > (1980 + 127))) { + + ExLocalTimeToSystemTime( NtTime, NtTime ); + + return FALSE; + } + + // + // The year will fit in Fat so simply copy over the information + // + + FatTime->Time.DoubleSeconds = (USHORT)(TimeFields.Second / 2); + FatTime->Time.Minute = (USHORT)(TimeFields.Minute); + FatTime->Time.Hour = (USHORT)(TimeFields.Hour); + + FatTime->Date.Year = (USHORT)(TimeFields.Year - 1980); + FatTime->Date.Month = (USHORT)(TimeFields.Month); + FatTime->Date.Day = (USHORT)(TimeFields.Day); + + if (TenMsecs) { + + if (!Rounding) { + + // + // If the number of seconds was not divisible by two, then there + // is another second of time (1 sec, 3 sec, etc.) Note we round down + // the number of milleconds onto tens of milleseconds boundaries. + // + + *TenMsecs = (TimeFields.Milliseconds / 10) + + ((TimeFields.Second % 2) * 100); + + } else { + + // + // If we rounded up, we have in effect changed the NT time. Therefore, + // it does not differ from the FAT time. + // + + *TenMsecs = 0; + } + } + + if (Rounding) { + + // + // Slice off non-FAT boundary time and convert back to 64bit form + // + + TimeFields.Milliseconds = 0; + TimeFields.Second -= TimeFields.Second % 2; + + } else { + + // + // Round down to 10ms boundary + // + + TimeFields.Milliseconds -= TimeFields.Milliseconds % 10; + } + + // + // Convert back to NT time + // + + (VOID) RtlTimeFieldsToTime(&TimeFields, NtTime); + + ExLocalTimeToSystemTime( NtTime, NtTime ); + + UNREFERENCED_PARAMETER( IrpContext ); + + return TRUE; +} + + +LARGE_INTEGER +FatFatDateToNtTime ( + IN PIRP_CONTEXT IrpContext, + IN FAT_DATE FatDate + ) + +/*++ + +Routine Description: + + This routine converts a Fat datev value to its corresponding Nt GMT + Time value. + +Arguments: + + FatDate - Supplies the Fat Date to convert from + +Return Value: + + LARGE_INTEGER - Receives the corresponding Nt Time value + +--*/ + +{ + TIME_FIELDS TimeFields; + LARGE_INTEGER Time; + + // + // Pack the input time/date into a time field record + // + + TimeFields.Year = (USHORT)(FatDate.Year + 1980); + TimeFields.Month = (USHORT)(FatDate.Month); + TimeFields.Day = (USHORT)(FatDate.Day); + TimeFields.Hour = (USHORT)0; + TimeFields.Minute = (USHORT)0; + TimeFields.Second = (USHORT)0; + TimeFields.Milliseconds = (USHORT)0; + + // + // Convert the time field record to Nt LARGE_INTEGER, and set it to zero + // if we were given a bogus time. + // + + if (!RtlTimeFieldsToTime( &TimeFields, &Time )) { + + Time.LowPart = 0; + Time.HighPart = 0; + + } else { + + ExLocalTimeToSystemTime( &Time, &Time ); + } + + return Time; + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +LARGE_INTEGER +FatFatTimeToNtTime ( + IN PIRP_CONTEXT IrpContext, + IN FAT_TIME_STAMP FatTime, + IN UCHAR TenMilliSeconds + ) + +/*++ + +Routine Description: + + This routine converts a Fat time value pair to its corresponding Nt GMT + Time value. + +Arguments: + + FatTime - Supplies the Fat Time to convert from + + TenMilliSeconds - A 10 Milisecond resolution + +Return Value: + + LARGE_INTEGER - Receives the corresponding Nt GMT Time value + +--*/ + +{ + TIME_FIELDS TimeFields; + LARGE_INTEGER Time; + + // + // Pack the input time/date into a time field record + // + + TimeFields.Year = (USHORT)(FatTime.Date.Year + 1980); + TimeFields.Month = (USHORT)(FatTime.Date.Month); + TimeFields.Day = (USHORT)(FatTime.Date.Day); + TimeFields.Hour = (USHORT)(FatTime.Time.Hour); + TimeFields.Minute = (USHORT)(FatTime.Time.Minute); + TimeFields.Second = (USHORT)(FatTime.Time.DoubleSeconds * 2); + + if (TenMilliSeconds != 0) { + + TimeFields.Second += (USHORT)(TenMilliSeconds / 100); + TimeFields.Milliseconds = (USHORT)((TenMilliSeconds % 100) * 10); + + } else { + + TimeFields.Milliseconds = (USHORT)0; + } + + // + // If the second value is greater than 59 then we truncate it to 0. + // Note that this can't happen with a proper FAT timestamp. + // + + if (TimeFields.Second > 59) { + + TimeFields.Second = 0; + } + + // + // Convert the time field record to Nt LARGE_INTEGER, and set it to zero + // if we were given a bogus time. + // + + if (!RtlTimeFieldsToTime( &TimeFields, &Time )) { + + Time.LowPart = 0; + Time.HighPart = 0; + + } else { + + ExLocalTimeToSystemTime( &Time, &Time ); + } + + return Time; + + UNREFERENCED_PARAMETER( IrpContext ); +} + + +FAT_TIME_STAMP +FatGetCurrentFatTime ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine returns the current system time in Fat time + +Arguments: + +Return Value: + + FAT_TIME_STAMP - Receives the current system time + +--*/ + +{ + LARGE_INTEGER Time; + TIME_FIELDS TimeFields; + FAT_TIME_STAMP FatTime; + + // + // Get the current system time, and map it into a time field record. + // + + KeQuerySystemTime( &Time ); + + ExSystemTimeToLocalTime( &Time, &Time ); + + // + // Always add almost two seconds to round up to the nearest double second. + // + + Time.QuadPart = Time.QuadPart + AlmostTwoSeconds; + + (VOID)RtlTimeToTimeFields( &Time, &TimeFields ); + + // + // Now simply copy over the information + // + + FatTime.Time.DoubleSeconds = (USHORT)(TimeFields.Second / 2); + FatTime.Time.Minute = (USHORT)(TimeFields.Minute); + FatTime.Time.Hour = (USHORT)(TimeFields.Hour); + + FatTime.Date.Year = (USHORT)(TimeFields.Year - 1980); + FatTime.Date.Month = (USHORT)(TimeFields.Month); + FatTime.Date.Day = (USHORT)(TimeFields.Day); + + UNREFERENCED_PARAMETER( IrpContext ); + + return FatTime; +} + + diff --git a/drivers/filesystems/fastfat_new/verfysup.c b/drivers/filesystems/fastfat_new/verfysup.c new file mode 100644 index 00000000000..c7de5ea2bed --- /dev/null +++ b/drivers/filesystems/fastfat_new/verfysup.c @@ -0,0 +1,1853 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + VerfySup.c + +Abstract: + + This module implements the Fat Verify volume and fcb/dcb support + routines + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_VERFYSUP) + +// +// The Debug trace level for this module +// + +#define Dbg (DEBUG_TRACE_VERFYSUP) + +// +// Local procedure prototypes +// + +VOID +FatResetFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +VOID +FatDetermineAndMarkFcbCondition ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ); + +VOID +NTAPI +FatDeferredCleanVolume ( + PVOID Parameter + ); + +NTSTATUS +NTAPI +FatMarkVolumeCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCheckDirtyBit) +#pragma alloc_text(PAGE, FatVerifyOperationIsLegal) +#pragma alloc_text(PAGE, FatDeferredCleanVolume) +#pragma alloc_text(PAGE, FatDetermineAndMarkFcbCondition) +#pragma alloc_text(PAGE, FatQuickVerifyVcb) +#pragma alloc_text(PAGE, FatPerformVerify) +#pragma alloc_text(PAGE, FatMarkFcbCondition) +#pragma alloc_text(PAGE, FatResetFcb) +#pragma alloc_text(PAGE, FatVerifyVcb) +#pragma alloc_text(PAGE, FatVerifyFcb) +#endif + + +VOID +FatMarkFcbCondition ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN FCB_CONDITION FcbCondition, + IN BOOLEAN Recursive + ) + +/*++ + +Routine Description: + + This routines marks the entire Fcb/Dcb structure from Fcb down with + FcbCondition. + +Arguments: + + Fcb - Supplies the Fcb/Dcb being marked + + FcbCondition - Supplies the setting to use for the Fcb Condition + + Recursive - Specifies whether this condition should be applied to + all children (see the case where we are invalidating a live volume + for a case where this is now desireable). + +Return Value: + + None. + +--*/ + +{ + DebugTrace(+1, Dbg, "FatMarkFcbCondition, Fcb = %08lx\n", Fcb ); + + // + // If we are marking this Fcb something other than Good, we will need + // to have the Vcb exclusive. + // + + ASSERT( FcbCondition != FcbNeedsToBeVerified ? TRUE : + FatVcbAcquiredExclusive(IrpContext, Fcb->Vcb) ); + + // + // If this is a PagingFile it has to be good. + // + + if (FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) { + + Fcb->FcbCondition = FcbGood; + return; + } + + // + // Update the condition of the Fcb. + // + + Fcb->FcbCondition = FcbCondition; + + DebugTrace(0, Dbg, "MarkFcb: %Z\n", &Fcb->FullFileName); + + // + // This FastIo flag is based on FcbCondition, so update it now. This only + // applies to regular FCBs, of course. + // + + if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) { + + Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb ); + } + + // + // Now if we marked NeedsVerify or Bad a directory then we also need to + // go and mark all of our children with the same condition. + // + + if ( ((FcbCondition == FcbNeedsToBeVerified) || + (FcbCondition == FcbBad)) && + Recursive && + ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) || + (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB)) ) { + + PFCB OriginalFcb = Fcb; + + while ( (Fcb = FatGetNextFcbTopDown(IrpContext, Fcb, OriginalFcb)) != NULL ) { + + DebugTrace(0, Dbg, "MarkFcb: %Z\n", &Fcb->FullFileName); + + Fcb->FcbCondition = FcbCondition; + + // + // We already know that FastIo is not possible since we are propagating + // a parent's bad/verify flag down the tree - IO to the children must + // take the long route for now. + // + + Fcb->Header.IsFastIoPossible = FastIoIsNotPossible; + } + } + + DebugTrace(-1, Dbg, "FatMarkFcbCondition -> VOID\n", 0); + + return; +} + + +VOID +FatVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routines verifies that the Vcb still denotes a valid Volume + If the Vcb is bad it raises an error condition. + +Arguments: + + Vcb - Supplies the Vcb being verified + +Return Value: + + None. + +--*/ + +{ + ULONG ChangeCount = 0; + + DebugTrace(+1, Dbg, "FatVerifyVcb, Vcb = %08lx\n", Vcb ); + + // + // If the media is removable and the verify volume flag in the + // device object is not set then we want to ping the device + // to see if it needs to be verified. + // + // Note that we only force this ping for create operations. + // For others we take a sporting chance. If in the end we + // have to physically access the disk, the right thing will happen. + // + + if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME) ) { + + IO_STATUS_BLOCK Iosb; + NTSTATUS Status; + + Status = FatPerformDevIoCtrl( IrpContext, + IOCTL_DISK_CHECK_VERIFY, + Vcb->TargetDeviceObject, + &ChangeCount, + sizeof(ULONG), + FALSE, + TRUE, + &Iosb ); + + // + // Verify potentially empty devices, explicit verify requests and + // when we get the secondary indication of media change via the + // device change count. + // + + if ((Vcb->VcbCondition == VcbGood && + FatIsRawDevice( IrpContext, Status )) || + (Status == STATUS_VERIFY_REQUIRED) || + (NT_SUCCESS(Status) && + (Vcb->ChangeCount != ChangeCount))) { + + SetFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME ); + } + + // + // Raise the error condition otherwise. + // + + else if (!NT_SUCCESS( Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + } + + // + // Now that the verify bit has been appropriately set, check the Vcb. + // + + FatQuickVerifyVcb( IrpContext, Vcb ); + + DebugTrace(-1, Dbg, "FatVerifyVcb -> VOID\n", 0); + + return; +} + + +VOID +FatVerifyFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routines verifies that the Fcb still denotes the same file. + If the Fcb is bad it raises a error condition. + +Arguments: + + Fcb - Supplies the Fcb being verified + +Return Value: + + None. + +--*/ + +{ + PFCB CurrentFcb; + + DebugTrace(+1, Dbg, "FatVerifyFcb, Vcb = %08lx\n", Fcb ); + + // + // Always refuse operations on dismounted volumes. + // + + if (FlagOn( Fcb->Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) { + + FatRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED ); + } + + // + // If this is the Fcb of a deleted dirent or our parent is deleted, + // no-op this call with the hope that the caller will do the right thing. + // The only caller we really have to worry about is the AdvanceOnly + // callback for setting valid data length from Cc, this will happen after + // cleanup (and file deletion), just before the SCM is ripped down. + // + + if (IsFileDeleted( IrpContext, Fcb ) || + ((NodeType(Fcb) != FAT_NTC_ROOT_DCB) && + IsFileDeleted( IrpContext, Fcb->ParentDcb ))) { + + return; + } + + // + // If we are not in the process of doing a verify, + // first do a quick spot check on the Vcb. + // + + if ( Fcb->Vcb->VerifyThread != KeGetCurrentThread() ) { + + FatQuickVerifyVcb( IrpContext, Fcb->Vcb ); + } + + // + // Now based on the condition of the Fcb we'll either return + // immediately to the caller, raise a condition, or do some work + // to verify the Fcb. + // + + switch (Fcb->FcbCondition) { + + case FcbGood: + + DebugTrace(0, Dbg, "The Fcb is good\n", 0); + break; + + case FcbBad: + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + break; + + case FcbNeedsToBeVerified: + + // + // We loop here checking our ancestors until we hit an Fcb which + // is either good or bad. + // + + CurrentFcb = Fcb; + + while (CurrentFcb->FcbCondition == FcbNeedsToBeVerified) { + + FatDetermineAndMarkFcbCondition(IrpContext, CurrentFcb); + + // + // If this Fcb didn't make it, or it was the Root Dcb, exit + // the loop now, else continue with out parent. + // + + if ( (CurrentFcb->FcbCondition != FcbGood) || + (NodeType(CurrentFcb) == FAT_NTC_ROOT_DCB) ) { + + break; + } + + CurrentFcb = CurrentFcb->ParentDcb; + } + + // + // Now we can just look at ourselves to see how we did. + // + + if (Fcb->FcbCondition != FcbGood) { + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + + break; + + default: + + DebugDump("Invalid FcbCondition\n", 0, Fcb); + FatBugCheck( Fcb->FcbCondition, 0, 0 ); + } + + DebugTrace(-1, Dbg, "FatVerifyFcb -> VOID\n", 0); + + return; +} + +VOID +NTAPI +FatDeferredCleanVolume ( + PVOID Parameter + ) + +/*++ + +Routine Description: + + This is the routine that performs the actual FatMarkVolumeClean call. + It assures that the target volume still exists as there ia a race + condition between queueing the ExWorker item and volumes going away. + +Arguments: + + Parameter - Points to a clean volume packet that was allocated from pool + +Return Value: + + None. + +--*/ + +{ + PCLEAN_AND_DIRTY_VOLUME_PACKET Packet; + PLIST_ENTRY Links; + PVCB Vcb; + IRP_CONTEXT IrpContext; + BOOLEAN VcbExists = FALSE; + + DebugTrace(+1, Dbg, "FatDeferredCleanVolume\n", 0); + + Packet = (PCLEAN_AND_DIRTY_VOLUME_PACKET)Parameter; + + Vcb = Packet->Vcb; + + // + // Make us appear as a top level FSP request so that we will + // receive any errors from the operation. + // + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + + // + // Dummy up and Irp Context so we can call our worker routines + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT)); + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // Acquire shared access to the global lock and make sure this volume + // still exists. + // + + FatAcquireSharedGlobal( &IrpContext ); + + for (Links = FatData.VcbQueue.Flink; + Links != &FatData.VcbQueue; + Links = Links->Flink) { + + PVCB ExistingVcb; + + ExistingVcb = CONTAINING_RECORD(Links, VCB, VcbLinks); + + if ( Vcb == ExistingVcb ) { + + VcbExists = TRUE; + break; + } + } + + // + // If the vcb is good then mark it clean. Ignore any problems. + // + + if ( VcbExists && + (Vcb->VcbCondition == VcbGood) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN) ) { + + _SEH2_TRY { + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + FatMarkVolume( &IrpContext, Vcb, VolumeClean ); + } + + // + // Check for a pathological race condition, and fix it. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) { + + FatMarkVolume( &IrpContext, Vcb, VolumeDirty ); + + } else { + + // + // Unlock the volume if it is removable. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) && + !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) { + + FatToggleMediaEjectDisable( &IrpContext, Vcb, FALSE ); + } + } + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + NOTHING; + } _SEH2_END; + } + + // + // Release the global resource, unpin and repinned Bcbs and return. + // + + FatReleaseGlobal( &IrpContext ); + + _SEH2_TRY { + + FatUnpinRepinnedBcbs( &IrpContext ); + + } _SEH2_EXCEPT( FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + NOTHING; + } _SEH2_END; + + IoSetTopLevelIrp( NULL ); + + // + // and finally free the packet. + // + + ExFreePool( Packet ); + + return; +} + + +VOID +NTAPI +FatCleanVolumeDpc ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is dispatched 5 seconds after the last disk structure was + modified in a specific volume, and exqueues an execuative worker thread + to perform the actual task of marking the volume dirty. + +Arguments: + + DefferedContext - Contains the Vcb to process. + +Return Value: + + None. + +--*/ + +{ + PVCB Vcb; + PCLEAN_AND_DIRTY_VOLUME_PACKET Packet; + + Vcb = (PVCB)DeferredContext; + + // + // If there is still dirty data (highly unlikely), set the timer for a + // second in the future. + // + + if (CcIsThereDirtyData(Vcb->Vpb)) { + + LARGE_INTEGER TwoSecondsFromNow; + + TwoSecondsFromNow.QuadPart = (LONG)-2*1000*1000*10; + + KeSetTimer( &Vcb->CleanVolumeTimer, + TwoSecondsFromNow, + &Vcb->CleanVolumeDpc ); + + return; + } + + // + // If we couldn't get pool, oh well.... + // + + Packet = ExAllocatePool(NonPagedPool, sizeof(CLEAN_AND_DIRTY_VOLUME_PACKET)); + + if ( Packet ) { + + Packet->Vcb = Vcb; + Packet->Irp = NULL; + + // + // Clear the dirty flag now since we cannot synchronize after this point. + // + + ClearFlag( Packet->Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY ); + + ExInitializeWorkItem( &Packet->Item, &FatDeferredCleanVolume, Packet ); + + ExQueueWorkItem( &Packet->Item, CriticalWorkQueue ); + } + + return; +} + + +VOID +FatMarkVolume ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN FAT_VOLUME_STATE VolumeState + ) + +/*++ + +Routine Description: + + This routine moves the physically marked volume state between the clean + and dirty states. For compatibility with Win9x, we manipulate both the + historical DOS (on==clean in index 1 of the FAT) and NT (on==dirty in + the CurrentHead field of the BPB) dirty bits. + +Arguments: + + Vcb - Supplies the Vcb being modified + + VolumeState - Supplies the state the volume is transitioning to + +Return Value: + + None. + +--*/ + +{ + PCHAR Sector; + PBCB Bcb = NULL; + KEVENT Event; + PIRP Irp = NULL; + NTSTATUS Status; + BOOLEAN FsInfoUpdate = FALSE; + ULONG FsInfoOffset; + ULONG ThisPass; + LARGE_INTEGER Offset; + + DebugTrace(+1, Dbg, "FatMarkVolume, Vcb = %08lx\n", Vcb); + + // + // We had best not be trying to scribble dirty/clean bits if the + // volume is write protected. The responsibility lies with the + // callers to make sure that operations that could cause a state + // change cannot happen. There are a few, though, that show it + // just doesn't make sense to force everyone to do the dinky + // check. + // + + // + // If we were called for FAT12 or readonly media, return immediately. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) || + FatIsFat12( Vcb )) { + + return; + } + + // + // We have two possible additional tasks to do to mark a volume + // + // Pass 0) Flip the dirty bit in the Bpb + // Pass 1) Rewrite the FsInfo sector for FAT32 if needed + // + // In most cases we can collapse these two either because the volume + // is either not FAT32 or the FsInfo sector is adjacent to the boot sector. + // + + for (ThisPass = 0; ThisPass < 2; ThisPass++) { + + // + // If this volume is being dirtied, or isn't FAT32, or if it is and + // we were able to perform the fast update, or the bpb lied to us + // about where the FsInfo went, we're done - no FsInfo to update in + // a seperate write. + // + + if (ThisPass == 1 && (!FatIsFat32( Vcb ) || + VolumeState != VolumeClean || + FsInfoUpdate || + Vcb->Bpb.FsInfoSector == 0)) { + + break; + } + + // + // Bail if we get an IO error. + // + + _SEH2_TRY { + + ULONG PinLength; + ULONG WriteLength; + + // + // If the FAT table is 12-bit then our strategy is to pin the entire + // thing when any of it is modified. Here we're going to pin the + // first page, so in the 12-bit case we also want to pin the rest + // of the FAT table. + // + + Offset.QuadPart = 0; + + if (Vcb->AllocationSupport.FatIndexBitSize == 12) { + + // + // But we only write back the first sector. + // + + PinLength = FatReservedBytes(&Vcb->Bpb) + FatBytesPerFat(&Vcb->Bpb); + WriteLength = Vcb->Bpb.BytesPerSector; + + } else { + + WriteLength = PinLength = Vcb->Bpb.BytesPerSector; + + // + // If this is a FAT32 volume going into the clean state, + // see about doing the FsInfo sector. + // + + if (FatIsFat32( Vcb ) && VolumeState == VolumeClean) { + + // + // If the FsInfo sector immediately follows the boot sector, + // we can do this in a single operation by rewriting both + // sectors at once. + // + + if (Vcb->Bpb.FsInfoSector == 1) { + + ASSERT( ThisPass == 0 ); + + FsInfoUpdate = TRUE; + FsInfoOffset = Vcb->Bpb.BytesPerSector; + WriteLength = PinLength = Vcb->Bpb.BytesPerSector * 2; + + } else if (ThisPass == 1) { + + // + // We are doing an explicit write to the FsInfo sector. + // + + FsInfoUpdate = TRUE; + FsInfoOffset = 0; + + Offset.QuadPart = Vcb->Bpb.BytesPerSector * Vcb->Bpb.FsInfoSector; + } + } + } + + // + // Call Cc directly here so that we can avoid overhead and push this + // right down to the disk. + // + + CcPinRead( Vcb->VirtualVolumeFile, + &Offset, + PinLength, + TRUE, + &Bcb, + (PVOID *)&Sector ); + + DbgDoit( IrpContext->PinCount += 1 ) + + // + // Set the Bpb on Pass 0 always + // + + if (ThisPass == 0) { + + PCHAR CurrentHead; + + // + // Before we do anything, doublecheck that this still looks like a + // FAT bootsector. If it doesn't, something remarkable happened + // and we should avoid touching the volume. + // + // THIS IS TEMPORARY (but may last a while) + // + + if (!FatIsBootSectorFat( (PPACKED_BOOT_SECTOR) Sector )) { + + return; + } + + if (FatIsFat32( Vcb )) { + +#ifndef __REACTOS__ + CurrentHead = &((PPACKED_BOOT_SECTOR_EX) Sector)->CurrentHead; +#else + CurrentHead = (PCHAR)&((PPACKED_BOOT_SECTOR_EX) Sector)->CurrentHead; +#endif + + } else { + +#ifndef __REACTOS__ + CurrentHead = &((PPACKED_BOOT_SECTOR) Sector)->CurrentHead; +#else + CurrentHead = (PCHAR)&((PPACKED_BOOT_SECTOR) Sector)->CurrentHead; +#endif + } + + if (VolumeState == VolumeClean) { + + ClearFlag( *CurrentHead, FAT_BOOT_SECTOR_DIRTY ); + + } else { + + SetFlag( *CurrentHead, FAT_BOOT_SECTOR_DIRTY ); + + // + // In addition, if this request received an error that may indicate + // media corruption, have autochk perform a surface test. + // + + if ( VolumeState == VolumeDirtyWithSurfaceTest ) { + + SetFlag( *CurrentHead, FAT_BOOT_SECTOR_TEST_SURFACE ); + } + } + } + + // + // Update the FsInfo as appropriate. + // + + if (FsInfoUpdate) { + + PFSINFO_SECTOR FsInfoSector = (PFSINFO_SECTOR) ((PCHAR)Sector + FsInfoOffset); + + // + // We just rewrite all of the spec'd fields. Note that we don't + // care to synchronize with the allocation package - this will be + // quickly taken care of by a re-dirtying of the volume if a change + // is racing with us. Remember that this is all a compatibility + // deference for Win9x FAT32 - NT will never look at this information. + // + + FsInfoSector->SectorBeginSignature = FSINFO_SECTOR_BEGIN_SIGNATURE; + FsInfoSector->FsInfoSignature = FSINFO_SIGNATURE; + FsInfoSector->FreeClusterCount = Vcb->AllocationSupport.NumberOfFreeClusters; + FsInfoSector->NextFreeCluster = Vcb->ClusterHint; + FsInfoSector->SectorEndSignature = FSINFO_SECTOR_END_SIGNATURE; + } + + // + // Initialize the event we're going to use + // + + KeInitializeEvent( &Event, NotificationEvent, FALSE ); + + // + // Build the irp for the operation and also set the override flag. + // Note that we may be at APC level, so do this asyncrhonously and + // use an event for synchronization as normal request completion + // cannot occur at APC level. + // + + Irp = IoBuildAsynchronousFsdRequest( IRP_MJ_WRITE, + Vcb->TargetDeviceObject, + (PVOID)Sector, + WriteLength, + &Offset, + NULL ); + + if ( Irp == NULL ) { + + try_return(NOTHING); + } + + // + // Make this operation write-through. It never hurts to try to be + // safer about this, even though we aren't logged. + // + + SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_WRITE_THROUGH ); + + // + // Set up the completion routine + // + + IoSetCompletionRoutine( Irp, + FatMarkVolumeCompletionRoutine, + &Event, + TRUE, + TRUE, + TRUE ); + + // + // Call the device to do the write and wait for it to finish. + // Igmore any return status. + // + + Status = IoCallDriver( Vcb->TargetDeviceObject, Irp ); + + if (Status == STATUS_PENDING) { + + (VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL ); + } + + try_exit: NOTHING; + } _SEH2_FINALLY { + + // + // Clean up the Irp and Mdl + // + + if (Irp) { + + // + // If there is an MDL (or MDLs) associated with this I/O + // request, Free it (them) here. This is accomplished by + // walking the MDL list hanging off of the IRP and deallocating + // each MDL encountered. + // + + while (Irp->MdlAddress != NULL) { + + PMDL NextMdl; + + NextMdl = Irp->MdlAddress->Next; + + MmUnlockPages( Irp->MdlAddress ); + + IoFreeMdl( Irp->MdlAddress ); + + Irp->MdlAddress = NextMdl; + } + + IoFreeIrp( Irp ); + } + + if (Bcb != NULL) { + + FatUnpinBcb( IrpContext, Bcb ); + } + } _SEH2_END; + } + + // + // Flip the dirty bit in the FAT + // + + if (VolumeState == VolumeDirty) { + + FatSetFatEntry( IrpContext, Vcb, FAT_DIRTY_BIT_INDEX, FAT_DIRTY_VOLUME); + + } else { + + FatSetFatEntry( IrpContext, Vcb, FAT_DIRTY_BIT_INDEX, FAT_CLEAN_VOLUME); + } + + DebugTrace(-1, Dbg, "FatMarkVolume -> VOID\n", 0); + + return; +} + + +VOID +NTAPI +FatFspMarkVolumeDirtyWithRecover( + PVOID Parameter + ) + +/*++ + +Routine Description: + + This is the routine that performs the actual FatMarkVolume Dirty call + on a paging file Io that encounters a media error. It is responsible + for completing the PagingIo Irp as soon as this is done. + + Note: this routine (and thus FatMarkVolume()) must be resident as + the paging file might be damaged at this point. + +Arguments: + + Parameter - Points to a dirty volume packet that was allocated from pool + +Return Value: + + None. + +--*/ + +{ + PCLEAN_AND_DIRTY_VOLUME_PACKET Packet; + PVCB Vcb; + IRP_CONTEXT IrpContext; + PIRP Irp; +#ifndef __REACTOS__ + BOOLEAN VcbExists = FALSE; +#endif + + DebugTrace(+1, Dbg, "FatDeferredCleanVolume\n", 0); + + Packet = (PCLEAN_AND_DIRTY_VOLUME_PACKET)Parameter; + + Vcb = Packet->Vcb; + Irp = Packet->Irp; + + // + // Dummy up the IrpContext so we can call our worker routines + // + + RtlZeroMemory( &IrpContext, sizeof(IRP_CONTEXT)); + + SetFlag(IrpContext.Flags, IRP_CONTEXT_FLAG_WAIT); + IrpContext.OriginatingIrp = Irp; + + // + // Make us appear as a top level FSP request so that we will + // receive any errors from the operation. + // + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + + // + // Try to write out the dirty bit. If something goes wrong, we + // tried. + // + + _SEH2_TRY { + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + + FatMarkVolume( &IrpContext, Vcb, VolumeDirtyWithSurfaceTest ); + + } _SEH2_EXCEPT(FatExceptionFilter( &IrpContext, _SEH2_GetExceptionInformation() )) { + + NOTHING; + } _SEH2_END; + + IoSetTopLevelIrp( NULL ); + + // + // Now complete the originating Irp or set the synchronous event. + // + + if (Packet->Event) { + KeSetEvent( Packet->Event, 0, FALSE ); + } else { + IoCompleteRequest( Irp, IO_DISK_INCREMENT ); + } +} + + +VOID +FatCheckDirtyBit ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine looks at the volume dirty bit, and depending on the state of + VCB_STATE_FLAG_MOUNTED_DIRTY, the appropriate action is taken. + +Arguments: + + Vcb - Supplies the Vcb being queried. + +Return Value: + + None. + +--*/ + +{ + BOOLEAN Dirty; + + PPACKED_BOOT_SECTOR BootSector; + PBCB BootSectorBcb; + + UNICODE_STRING VolumeLabel; + + // + // Look in the boot sector + // + + FatReadVolumeFile( IrpContext, + Vcb, + 0, + sizeof(PACKED_BOOT_SECTOR), + &BootSectorBcb, + (PVOID *)&BootSector ); + + _SEH2_TRY { + + // + // Check if the magic bit is set + // + + if (IsBpbFat32(&BootSector->PackedBpb)) { + Dirty = BooleanFlagOn( ((PPACKED_BOOT_SECTOR_EX)BootSector)->CurrentHead, + FAT_BOOT_SECTOR_DIRTY ); + } else { + Dirty = BooleanFlagOn( BootSector->CurrentHead, FAT_BOOT_SECTOR_DIRTY ); + } + + // + // Setup the VolumeLabel string + // + + VolumeLabel.Length = Vcb->Vpb->VolumeLabelLength; + VolumeLabel.MaximumLength = MAXIMUM_VOLUME_LABEL_LENGTH; + VolumeLabel.Buffer = &Vcb->Vpb->VolumeLabel[0]; + + if ( Dirty ) { + + // + // Do not trigger the mounted dirty bit if this is a verify + // and the volume is a boot or paging device. We know that + // a boot or paging device cannot leave the system, and thus + // that on its mount we will have figured this out correctly. + // + + // This logic is a reasonable change. Why? + // 'cause setup cracked a non-exclusive DASD handle near the + // end of setup, wrote some data, closed the handle and we + // set the verify bit ... came back around and saw that other + // arbitrary activity had left the volume in a temporarily dirty + // state. + // + // Of course, the real problem is that we don't have a journal. + // + + if (!(IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME && + FlagOn( Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE))) { + + KdPrintEx((DPFLTR_FASTFAT_ID, + DPFLTR_INFO_LEVEL, + "FASTFAT: WARNING! Mounting Dirty Volume %Z\n", + &VolumeLabel)); + + SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + } + + } else { + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) { + + KdPrintEx((DPFLTR_FASTFAT_ID, + DPFLTR_INFO_LEVEL, + "FASTFAT: Volume %Z has been cleaned.\n", + &VolumeLabel)); + + ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); + + } else { + + (VOID)FsRtlBalanceReads( Vcb->TargetDeviceObject ); + } + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, BootSectorBcb ); + } _SEH2_END; +} + + +VOID +FatVerifyOperationIsLegal ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This routine determines is the requested operation should be allowed to + continue. It either returns to the user if the request is Okay, or + raises an appropriate status. + +Arguments: + + Irp - Supplies the Irp to check + +Return Value: + + None. + +--*/ + +{ + PIRP Irp; + PFILE_OBJECT FileObject; + + Irp = IrpContext->OriginatingIrp; + + // + // If the Irp is not present, then we got here via close. + // + // + + if ( Irp == NULL ) { + + return; + } + + FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject; + + // + // If there is not a file object, we cannot continue. + // + + if ( FileObject == NULL ) { + + return; + } + + // + // If the file object has already been cleaned up, and + // + // A) This request is a paging io read or write, or + // B) This request is a close operation, or + // C) This request is a set or query info call (for Lou) + // D) This is an MDL complete + // + // let it pass, otherwise return STATUS_FILE_CLOSED. + // + + if ( FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE) ) { + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) || + (IrpSp->MajorFunction == IRP_MJ_CLOSE ) || + (IrpSp->MajorFunction == IRP_MJ_SET_INFORMATION) || + (IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) || + ( ( (IrpSp->MajorFunction == IRP_MJ_READ) || + (IrpSp->MajorFunction == IRP_MJ_WRITE) ) && + FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) { + + NOTHING; + + } else { + + FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED ); + } + } + + return; +} + + + +// +// Internal support routine +// + +VOID +FatResetFcb ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine is called when an Fcb has been marked as needs to be verified. + + It does the following tasks: + + - Reset Mcb mapping information + - For directories, reset dirent hints + - Set allocation size to unknown + +Arguments: + + Fcb - Supplies the Fcb to reset + +Return Value: + + None. + +--*/ + +{ + // + // Don't do the two following operations for the Root Dcb + // or paging files. Paging files!? Yes, if someone diddles + // a volume we try to reverify all of the Fcbs just in case; + // however, there is no safe way to chuck and retrieve the + // mapping pair information for the paging file. Lose it and + // die. + // + + if ( NodeType(Fcb) != FAT_NTC_ROOT_DCB && + !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) { + + // + // Reset the mcb mapping. + // + + FsRtlRemoveLargeMcbEntry( &Fcb->Mcb, 0, 0xFFFFFFFF ); + + // + // Reset the allocation size to 0 or unknown + // + + if ( Fcb->FirstClusterOfFile == 0 ) { + + Fcb->Header.AllocationSize.QuadPart = 0; + + } else { + + Fcb->Header.AllocationSize.QuadPart = FCB_LOOKUP_ALLOCATIONSIZE_HINT; + } + } + + // + // If this is a directory, reset the hints. + // + + if ( (NodeType(Fcb) == FAT_NTC_DCB) || + (NodeType(Fcb) == FAT_NTC_ROOT_DCB) ) { + + // + // Force a rescan of the directory + // + + Fcb->Specific.Dcb.UnusedDirentVbo = 0xffffffff; + Fcb->Specific.Dcb.DeletedDirentHint = 0xffffffff; + } +} + + + +// +// Internal support routine +// + +VOID +FatDetermineAndMarkFcbCondition ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine checks a specific Fcb to see if it is different from what's + on the disk. The following things are checked: + + - File Name + - File Size (if not directory) + - First Cluster Of File + - Dirent Attributes + +Arguments: + + Fcb - Supplies the Fcb to examine + +Return Value: + + None. + +--*/ + +{ + PDIRENT Dirent; + PBCB DirentBcb; + ULONG FirstClusterOfFile; + + OEM_STRING Name; + CHAR Buffer[16]; + + // + // If this is the Root Dcb, special case it. That is, we know + // by definition that it is good since it is fixed in the volume + // structure. + // + + if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) { + + FatResetFcb( IrpContext, Fcb ); + + FatMarkFcbCondition( IrpContext, Fcb, FcbGood, FALSE ); + + return; + } + + // The first thing we need to do to verify ourselves is + // locate the dirent on the disk. + // + + FatGetDirentFromFcbOrDcb( IrpContext, + Fcb, + &Dirent, + &DirentBcb ); + + // + // If we couldn't get the dirent, this fcb must be bad (case of + // enclosing directory shrinking during the time it was ejected). + // + + if (DirentBcb == NULL) { + + FatMarkFcbCondition( IrpContext, Fcb, FcbBad, TRUE ); + return; + } + + // + // We located the dirent for ourselves now make sure it + // is really ours by comparing the Name and FatFlags. + // Then for a file we also check the file size. + // + // Note that we have to unpin the Bcb before calling FatResetFcb + // in order to avoid a deadlock in CcUninitializeCacheMap. + // + + _SEH2_TRY { + + Name.MaximumLength = 16; + Name.Buffer = &Buffer[0]; + + Fat8dot3ToString( IrpContext, Dirent, FALSE, &Name ); + + // + // We need to calculate the first cluster 'cause FAT32 splits + // this field across the dirent. + // + + FirstClusterOfFile = Dirent->FirstClusterOfFile; + + if (FatIsFat32( Fcb->Vcb )) { + + FirstClusterOfFile += Dirent->FirstClusterOfFileHi << 16; + } + + if (!RtlEqualString( &Name, &Fcb->ShortName.Name.Oem, TRUE ) + + || + + ( (NodeType(Fcb) == FAT_NTC_FCB) && + (Fcb->Header.FileSize.LowPart != Dirent->FileSize) ) + + || + + (FirstClusterOfFile != Fcb->FirstClusterOfFile) + + || + + (Dirent->Attributes != Fcb->DirentFatFlags) ) { + + FatMarkFcbCondition( IrpContext, Fcb, FcbBad, TRUE ); + + } else { + + // + // We passed. Get the Fcb ready to use again. + // + + FatResetFcb( IrpContext, Fcb ); + + FatMarkFcbCondition( IrpContext, Fcb, FcbGood, FALSE ); + } + + } _SEH2_FINALLY { + + FatUnpinBcb( IrpContext, DirentBcb ); + } _SEH2_END; + + return; +} + + + +// +// Internal support routine +// + +VOID +FatQuickVerifyVcb ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routines just checks the verify bit in the real device and the + Vcb condition and raises an appropriate exception if so warented. + It is called when verifying both Fcbs and Vcbs. + +Arguments: + + Vcb - Supplies the Vcb to check the condition of. + +Return Value: + + None. + +--*/ + +{ + // + // If the real device needs to be verified we'll set the + // DeviceToVerify to be our real device and raise VerifyRequired. + // + + if (FlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) { + + DebugTrace(0, Dbg, "The Vcb needs to be verified\n", 0); + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + FatRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED ); + } + + // + // Based on the condition of the Vcb we'll either return to our + // caller or raise an error condition + // + + switch (Vcb->VcbCondition) { + + case VcbGood: + + DebugTrace(0, Dbg, "The Vcb is good\n", 0); + + // + // Do a check here of an operation that would try to modify a + // write protected media. + // + + if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED) && + ((IrpContext->MajorFunction == IRP_MJ_WRITE) || + (IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION) || + (IrpContext->MajorFunction == IRP_MJ_SET_EA) || + (IrpContext->MajorFunction == IRP_MJ_FLUSH_BUFFERS) || + (IrpContext->MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION) || + (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST && + IoGetCurrentIrpStackLocation(IrpContext->OriginatingIrp)->Parameters.FileSystemControl.FsControlCode == + FSCTL_MARK_VOLUME_DIRTY))) { + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); + } + + break; + + case VcbNotMounted: + + DebugTrace(0, Dbg, "The Vcb is not mounted\n", 0); + + // + // Set the real device for the pop-up info, and set the verify + // bit in the device object, so that we will force a verify + // in case the user put the correct media back in. + // + + IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, + Vcb->Vpb->RealDevice ); + + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + FatRaiseStatus( IrpContext, STATUS_WRONG_VOLUME ); + + break; + + case VcbBad: + + DebugTrace(0, Dbg, "The Vcb is bad\n", 0); + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DISMOUNTED )) { + + FatRaiseStatus( IrpContext, STATUS_VOLUME_DISMOUNTED ); + + } else { + + FatRaiseStatus( IrpContext, STATUS_FILE_INVALID ); + } + break; + + default: + + DebugDump("Invalid VcbCondition\n", 0, Vcb); + FatBugCheck( Vcb->VcbCondition, 0, 0 ); + } +} + +NTSTATUS +FatPerformVerify ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp, + IN PDEVICE_OBJECT Device + ) + +/*++ + +Routine Description: + + This routines performs an IoVerifyVolume operation and takes the + appropriate action. After the Verify is complete the originating + Irp is sent off to an Ex Worker Thread. This routine is called + from the exception handler. + +Arguments: + + Irp - The irp to send off after all is well and done. + + Device - The real device needing verification. + +Return Value: + + None. + +--*/ + +{ + PVCB Vcb; + NTSTATUS Status = STATUS_SUCCESS; + PIO_STACK_LOCATION IrpSp; + BOOLEAN VcbDeleted = FALSE; + + // + // Check if this Irp has a status of Verify required and if it does + // then call the I/O system to do a verify. + // + // Skip the IoVerifyVolume if this is a mount or verify request + // itself. Trying a recursive mount will cause a deadlock with + // the DeviceObject->DeviceLock. + // + + if ( (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) + + && + + ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) || + (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME)) ) { + + return FatFsdPostRequest( IrpContext, Irp ); + } + + DebugTrace(0, Dbg, "Verify Required, DeviceObject = %08lx\n", Device); + + // + // Extract a pointer to the Vcb from the VolumeDeviceObject. + // Note that since we have specifically excluded mount, + // requests, we know that IrpSp->DeviceObject is indeed a + // volume device object. + // + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + + Vcb = &CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject )->Vcb; + + // + // Check if the volume still thinks it needs to be verified, + // if it doesn't then we can skip doing a verify because someone + // else beat us to it. + // + + _SEH2_TRY { + + if (FlagOn(Device->Flags, DO_VERIFY_VOLUME)) { + + PFILE_OBJECT FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject; + BOOLEAN AllowRawMount; + + // + // We will allow Raw to mount this volume if we were doing a + // a DASD open. + // + + if ( (IrpContext->MajorFunction == IRP_MJ_CREATE) && + (IrpSp->FileObject->FileName.Length == 0) && + (IrpSp->FileObject->RelatedFileObject == NULL) ) { + + AllowRawMount = TRUE; + + } else { + + AllowRawMount = FALSE; + } + + // + // If the IopMount in IoVerifyVolume did something, and + // this is an absolute open, force a reparse. + // + + Status = IoVerifyVolume( Device, AllowRawMount ); + + // + // If the verify operation completed it will return + // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. + // + // If FatVerifyVolume encountered an error during + // processing, it will return that error. If we got + // STATUS_WRONG_VOLUME from the verfy, and our volume + // is now mounted, commute the status to STATUS_SUCCESS. + // + + if ( (Status == STATUS_WRONG_VOLUME) && + (Vcb->VcbCondition == VcbGood) ) { + + Status = STATUS_SUCCESS; + } + + // + // Do a quick unprotected check here. The routine will do + // a safe check. After here we can release the resource. + // Note that if the volume really went away, we will be taking + // the Reparse path. + // + + (VOID)FatAcquireExclusiveGlobal( IrpContext ); + + if ( ((Vcb->VcbCondition == VcbNotMounted) || + (Vcb->VcbCondition == VcbBad)) && + (Vcb->OpenFileCount == 0) ) { + + FatAcquireExclusiveVcb( IrpContext, + Vcb ); + + VcbDeleted = FatCheckForDismount( IrpContext, + Vcb, + FALSE ); + if (!VcbDeleted) { + FatReleaseVcb( IrpContext, + Vcb ); + } + } + + FatReleaseGlobal( IrpContext ); + + if ((IrpContext->MajorFunction == IRP_MJ_CREATE) && + (FileObject->RelatedFileObject == NULL) && + ((Status == STATUS_SUCCESS) || (Status == STATUS_WRONG_VOLUME))) { + + Irp->IoStatus.Information = IO_REMOUNT; + + FatCompleteRequest( IrpContext, Irp, STATUS_REPARSE ); + Status = STATUS_REPARSE; + Irp = NULL; + } + + if ( (Irp != NULL) && !NT_SUCCESS(Status) ) { + + // + // Fill in the device object if required. + // + + if ( IoIsErrorUserInduced( Status ) ) { + + IoSetHardErrorOrVerifyDevice( Irp, Device ); + } + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + } else { + + DebugTrace(0, Dbg, "Volume no longer needs verification\n", 0); + } + + // + // If there is still an Irp, send it off to an Ex Worker thread. + // + + if ( Irp != NULL ) { + + Status = FatFsdPostRequest( IrpContext, Irp ); + } + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the verify or raised + // an error ourselves. So we'll abort the I/O request with + // the error status that we get back from the execption code. + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + return Status; +} + +// +// Local support routine +// + +NTSTATUS +NTAPI +FatMarkVolumeCompletionRoutine( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Contxt + ) + +{ + // + // Set the event so that our call will wake up. + // + + KeSetEvent( (PKEVENT)Contxt, 0, FALSE ); + + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); + + return STATUS_MORE_PROCESSING_REQUIRED; +} + + diff --git a/drivers/filesystems/fastfat_new/volinfo.c b/drivers/filesystems/fastfat_new/volinfo.c new file mode 100644 index 00000000000..40e438a2917 --- /dev/null +++ b/drivers/filesystems/fastfat_new/volinfo.c @@ -0,0 +1,1272 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + VolInfo.c + +Abstract: + + This module implements the volume information routines for Fat called by + the dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_VOLINFO) + +NTSTATUS +FatQueryFsVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_VOLUME_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatQueryFsSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatQueryFsDeviceInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_DEVICE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatQueryFsAttributeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatQueryFsFullSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_FULL_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ); + +NTSTATUS +FatSetFsLabelInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_LABEL_INFORMATION Buffer + ); + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatCommonQueryVolumeInfo) +#pragma alloc_text(PAGE, FatCommonSetVolumeInfo) +#pragma alloc_text(PAGE, FatFsdQueryVolumeInformation) +#pragma alloc_text(PAGE, FatFsdSetVolumeInformation) +#pragma alloc_text(PAGE, FatQueryFsAttributeInfo) +#pragma alloc_text(PAGE, FatQueryFsDeviceInfo) +#pragma alloc_text(PAGE, FatQueryFsSizeInfo) +#pragma alloc_text(PAGE, FatQueryFsVolumeInfo) +#pragma alloc_text(PAGE, FatQueryFsFullSizeInfo) +#pragma alloc_text(PAGE, FatSetFsLabelInfo) +#endif + + +NTSTATUS +NTAPI +FatFsdQueryVolumeInformation ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the Fsd part of the NtQueryVolumeInformation API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being queried exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdQueryVolumeInformation\n", 0); + + // + // Call the common query routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonQueryVolumeInfo( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdQueryVolumeInformation -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +NTAPI +FatFsdSetVolumeInformation ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtSetVolumeInformation API + call. + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the file + being set exists. + + Irp - Supplies the Irp being processed. + +Return Value: + + NTSTATUS - The FSD status for the Irp. + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdSetVolumeInformation\n", 0); + + // + // Call the common set routine + // + + FsRtlEnterFileSystem(); + + TopLevel = FatIsIrpTopLevel( Irp ); + + _SEH2_TRY { + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + Status = FatCommonSetVolumeInfo( IrpContext, Irp ); + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdSetVolumeInformation -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonQueryVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for querying volume information called by both + the fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + + ULONG Length; + FS_INFORMATION_CLASS FsInformationClass; + PVOID Buffer; + + BOOLEAN WeAcquiredVcb = FALSE; + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonQueryVolumeInfo...\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.QueryVolume.Length); + DebugTrace( 0, Dbg, "->FsInformationClass = %08lx\n", IrpSp->Parameters.QueryVolume.FsInformationClass); + DebugTrace( 0, Dbg, "->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Reference our input parameters to make things easier + // + + Length = IrpSp->Parameters.QueryVolume.Length; + FsInformationClass = IrpSp->Parameters.QueryVolume.FsInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Decode the file object to get the Vcb + // + + (VOID) FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + ASSERT( Vcb != NULL ); + + _SEH2_TRY { + + // + // Make sure the vcb is in a usable condition. This will raise + // and error condition if the volume is unusable + // + // Also verify the Root Dcb since we need info from there. + // + + FatVerifyFcb( IrpContext, Vcb->RootDcb ); + + // + // Based on the information class we'll do different actions. Each + // of the procedures that we're calling fills up the output buffer + // if possible and returns true if it successfully filled the buffer + // and false if it couldn't wait for any I/O to complete. + // + + switch (FsInformationClass) { + + case FileFsVolumeInformation: + + // + // This is the only routine we need the Vcb shared because of + // copying the volume label. All other routines copy fields that + // cannot change or are just manifest constants. + // + + if (!FatAcquireSharedVcb( IrpContext, Vcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + IrpContext = NULL; + Irp = NULL; + + } else { + + WeAcquiredVcb = TRUE; + + Status = FatQueryFsVolumeInfo( IrpContext, Vcb, Buffer, &Length ); + } + + break; + + case FileFsSizeInformation: + + Status = FatQueryFsSizeInfo( IrpContext, Vcb, Buffer, &Length ); + break; + + case FileFsDeviceInformation: + + Status = FatQueryFsDeviceInfo( IrpContext, Vcb, Buffer, &Length ); + break; + + case FileFsAttributeInformation: + + Status = FatQueryFsAttributeInfo( IrpContext, Vcb, Buffer, &Length ); + break; + + case FileFsFullSizeInformation: + + Status = FatQueryFsFullSizeInfo( IrpContext, Vcb, Buffer, &Length ); + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + // + // Set the information field to the number of bytes actually filled in. + // + + if (Irp != NULL) { + + Irp->IoStatus.Information = IrpSp->Parameters.QueryVolume.Length - Length; + } + + } _SEH2_FINALLY { + + DebugUnwind( FatCommonQueryVolumeInfo ); + + if ( WeAcquiredVcb ) { FatReleaseVcb( IrpContext, Vcb ); } + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonQueryVolumeInfo -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + + +NTSTATUS +FatCommonSetVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common routine for setting Volume Information called by both + the fsd and fsp threads. + +Arguments: + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + + PVCB Vcb; + PFCB Fcb; + PCCB Ccb; + TYPE_OF_OPEN TypeOfOpen; + +#ifndef __REACTOS__ + ULONG Length; +#endif + FS_INFORMATION_CLASS FsInformationClass; + PVOID Buffer; + + // + // Get the current stack location + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "FatCommonSetVolumeInfo...\n", 0); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "->Length = %08lx\n", IrpSp->Parameters.SetVolume.Length); + DebugTrace( 0, Dbg, "->FsInformationClass = %08lx\n", IrpSp->Parameters.SetVolume.FsInformationClass); + DebugTrace( 0, Dbg, "->Buffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer); + + // + // Reference our input parameters to make things easier + // + +#ifndef __REACTOS__ + Length = IrpSp->Parameters.SetVolume.Length; +#endif + FsInformationClass = IrpSp->Parameters.SetVolume.FsInformationClass; + Buffer = Irp->AssociatedIrp.SystemBuffer; + + // + // Decode the file object to get the Vcb + // + + TypeOfOpen = FatDecodeFileObject( IrpSp->FileObject, &Vcb, &Fcb, &Ccb ); + + if (TypeOfOpen != UserVolumeOpen) { + + FatCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED ); + + DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> STATUS_ACCESS_DENIED\n", 0); + + return STATUS_ACCESS_DENIED; + } + + // + // Acquire exclusive access to the Vcb and enqueue the Irp if we didn't + // get access + // + + if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) { + + DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0); + + Status = FatFsdPostRequest( IrpContext, Irp ); + + DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status ); + return Status; + } + + _SEH2_TRY { + + // + // Make sure the vcb is in a usable condition. This will raise + // and error condition if the volume is unusable + // + // Also verify the Root Dcb since we need info from there. + // + + FatVerifyFcb( IrpContext, Vcb->RootDcb ); + + // + // Based on the information class we'll do different actions. Each + // of the procedures that we're calling performs the action if + // possible and returns true if it successful and false if it couldn't + // wait for any I/O to complete. + // + + switch (FsInformationClass) { + + case FileFsLabelInformation: + + Status = FatSetFsLabelInfo( IrpContext, Vcb, Buffer ); + break; + + default: + + Status = STATUS_INVALID_PARAMETER; + break; + } + + FatUnpinRepinnedBcbs( IrpContext ); + + } _SEH2_FINALLY { + + DebugUnwind( FatCommonSetVolumeInfo ); + + FatReleaseVcb( IrpContext, Vcb ); + + if (!_SEH2_AbnormalTermination()) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonSetVolumeInfo -> %08lx\n", Status); + } _SEH2_END; + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsVolumeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_VOLUME_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume info call + +Arguments: + + Vcb - Supplies the Vcb being queried + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + NTSTATUS - Returns the status for the query + +--*/ + +{ + ULONG BytesToCopy; + + NTSTATUS Status; + + DebugTrace(0, Dbg, "FatQueryFsVolumeInfo...\n", 0); + + // + // Zero out the buffer, then extract and fill up the non zero fields. + // + + RtlZeroMemory( Buffer, sizeof(FILE_FS_VOLUME_INFORMATION) ); + + Buffer->VolumeSerialNumber = Vcb->Vpb->SerialNumber; + + Buffer->SupportsObjects = FALSE; + + *Length -= FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel[0]); + + // + // Check if the buffer we're given is long enough + // + + if ( *Length >= (ULONG)Vcb->Vpb->VolumeLabelLength ) { + + BytesToCopy = Vcb->Vpb->VolumeLabelLength; + + Status = STATUS_SUCCESS; + + } else { + + BytesToCopy = *Length; + + Status = STATUS_BUFFER_OVERFLOW; + } + + // + // Copy over what we can of the volume label, and adjust *Length + // + + Buffer->VolumeLabelLength = Vcb->Vpb->VolumeLabelLength; + + RtlCopyMemory( &Buffer->VolumeLabel[0], + &Vcb->Vpb->VolumeLabel[0], + BytesToCopy ); + + *Length -= BytesToCopy; + + // + // Set our status and return to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume size call + +Arguments: + + Vcb - Supplies the Vcb being queried + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + DebugTrace(0, Dbg, "FatQueryFsSizeInfo...\n", 0); + + RtlZeroMemory( Buffer, sizeof(FILE_FS_SIZE_INFORMATION) ); + + // + // Set the output buffer. + // + + Buffer->TotalAllocationUnits.LowPart = + Vcb->AllocationSupport.NumberOfClusters; + Buffer->AvailableAllocationUnits.LowPart = + Vcb->AllocationSupport.NumberOfFreeClusters; + + Buffer->SectorsPerAllocationUnit = Vcb->Bpb.SectorsPerCluster; + Buffer->BytesPerSector = Vcb->Bpb.BytesPerSector; + + // + // Adjust the length variable + // + + *Length -= sizeof(FILE_FS_SIZE_INFORMATION); + + // + // And return success to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsDeviceInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_DEVICE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume device call + +Arguments: + + Vcb - Supplies the Vcb being queried + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + DebugTrace(0, Dbg, "FatQueryFsDeviceInfo...\n", 0); + + RtlZeroMemory( Buffer, sizeof(FILE_FS_DEVICE_INFORMATION) ); + + // + // Set the output buffer + // + + Buffer->DeviceType = FILE_DEVICE_DISK; + + Buffer->Characteristics = Vcb->TargetDeviceObject->Characteristics; + + // + // Adjust the length variable + // + + *Length -= sizeof(FILE_FS_DEVICE_INFORMATION); + + // + // And return success to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsAttributeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume attribute call + +Arguments: + + Vcb - Supplies the Vcb being queried + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + ULONG BytesToCopy; + + NTSTATUS Status; + + DebugTrace(0, Dbg, "FatQueryFsAttributeInfo...\n", 0); + + // + // Set the output buffer + // + + Buffer->FileSystemAttributes = FILE_CASE_PRESERVED_NAMES | + FILE_UNICODE_ON_DISK; + + if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED )) { + + SetFlag( Buffer->FileSystemAttributes, FILE_READ_ONLY_VOLUME ); + } + + Buffer->MaximumComponentNameLength = FatData.ChicagoMode ? 255 : 12; + + if (FatIsFat32(Vcb)) { + + // + // Determine how much of the file system name will fit. + // + + if ( (*Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] )) >= 10 ) { + + BytesToCopy = 10; + *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] ) + 10; + Status = STATUS_SUCCESS; + + } else { + + BytesToCopy = *Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0]); + *Length = 0; + + Status = STATUS_BUFFER_OVERFLOW; + } + + RtlCopyMemory( &Buffer->FileSystemName[0], L"FAT32", BytesToCopy ); + + } else { + + // + // Determine how much of the file system name will fit. + // + + if ( (*Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] )) >= 6 ) { + + BytesToCopy = 6; + *Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0] ) + 6; + Status = STATUS_SUCCESS; + + } else { + + BytesToCopy = *Length - FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, + FileSystemName[0]); + *Length = 0; + + Status = STATUS_BUFFER_OVERFLOW; + } + + + RtlCopyMemory( &Buffer->FileSystemName[0], L"FAT", BytesToCopy ); + } + + Buffer->FileSystemNameLength = BytesToCopy; + + // + // And return success to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + UNREFERENCED_PARAMETER( Vcb ); + + return Status; +} + + +// +// Internal support routine +// + +NTSTATUS +FatQueryFsFullSizeInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_FULL_SIZE_INFORMATION Buffer, + IN OUT PULONG Length + ) + +/*++ + +Routine Description: + + This routine implements the query volume full size call + +Arguments: + + Vcb - Supplies the Vcb being queried + + Buffer - Supplies a pointer to the output buffer where the information + is to be returned + + Length - Supplies the length of the buffer in byte. This variable + upon return recieves the remaining bytes free in the buffer + +Return Value: + + Status - Returns the status for the query + +--*/ + +{ + DebugTrace(0, Dbg, "FatQueryFsSizeInfo...\n", 0); + + RtlZeroMemory( Buffer, sizeof(FILE_FS_FULL_SIZE_INFORMATION) ); + + Buffer->TotalAllocationUnits.LowPart = + Vcb->AllocationSupport.NumberOfClusters; + Buffer->CallerAvailableAllocationUnits.LowPart = + Vcb->AllocationSupport.NumberOfFreeClusters; + Buffer->ActualAvailableAllocationUnits.LowPart = + Buffer->CallerAvailableAllocationUnits.LowPart; + Buffer->SectorsPerAllocationUnit = Vcb->Bpb.SectorsPerCluster; + Buffer->BytesPerSector = Vcb->Bpb.BytesPerSector; + + // + // Adjust the length variable + // + + *Length -= sizeof(FILE_FS_FULL_SIZE_INFORMATION); + + // + // And return success to our caller + // + + UNREFERENCED_PARAMETER( IrpContext ); + + return STATUS_SUCCESS; +} + + +// +// Internal support routine +// + +NTSTATUS +FatSetFsLabelInfo ( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PFILE_FS_LABEL_INFORMATION Buffer + ) + +/*++ + +Routine Description: + + This routine implements the set volume label call + +Arguments: + + Vcb - Supplies the Vcb being queried + + Buffer - Supplies the input where the information is stored. + +Return Value: + + NTSTATUS - Returns the status for the operation + +--*/ + +{ + NTSTATUS Status; + + PDIRENT Dirent; + PBCB DirentBcb = NULL; + ULONG ByteOffset; + + WCHAR TmpBuffer[11]; + UCHAR OemBuffer[11]; + OEM_STRING OemLabel; + UNICODE_STRING UnicodeString; + UNICODE_STRING UpcasedLabel; + + DebugTrace(+1, Dbg, "FatSetFsLabelInfo...\n", 0); + + // + // Setup our local variable + // + + UnicodeString.Length = (USHORT)Buffer->VolumeLabelLength; + UnicodeString.MaximumLength = UnicodeString.Length; + UnicodeString.Buffer = (PWSTR) &Buffer->VolumeLabel[0]; + + // + // Make sure the name can fit into the stack buffer + // + + if ( UnicodeString.Length > 11*sizeof(WCHAR) ) { + + return STATUS_INVALID_VOLUME_LABEL; + } + + // + // Upcase the name and convert it to the Oem code page. + // + +#ifndef __REACTOS__ + OemLabel.Buffer = &OemBuffer[0]; +#else + OemLabel.Buffer = (PCHAR)&OemBuffer[0]; +#endif + OemLabel.Length = 0; + OemLabel.MaximumLength = 11; + + Status = RtlUpcaseUnicodeStringToCountedOemString( &OemLabel, + &UnicodeString, + FALSE ); + + // + // Volume label that fits in 11 unicode character length limit + // is not necessary within 11 characters in OEM character set. + // + + if (!NT_SUCCESS( Status )) { + + DebugTrace(-1, Dbg, "FatSetFsLabelInfo: Label must be too long. %08lx\n", Status ); + + return STATUS_INVALID_VOLUME_LABEL; + } + + // + // Strip spaces off of the label. + // + + if (OemLabel.Length > 0) { + + USHORT i; + USHORT LastSpaceIndex = MAXUSHORT; + + // + // Check the label for illegal characters + // + + for ( i = 0; i < (ULONG)OemLabel.Length; i += 1 ) { + + if ( FsRtlIsLeadDbcsCharacter( OemLabel.Buffer[i] ) ) { + + LastSpaceIndex = MAXUSHORT; + i += 1; + continue; + } + + if (!FsRtlIsAnsiCharacterLegalFat(OemLabel.Buffer[i], FALSE) || + (OemLabel.Buffer[i] == '.')) { + + return STATUS_INVALID_VOLUME_LABEL; + } + + // + // Watch for the last run of spaces, so we can strip them. + // + + if (OemLabel.Buffer[i] == ' ' && + LastSpaceIndex == MAXUSHORT) { + LastSpaceIndex = i; + } else { + LastSpaceIndex = MAXUSHORT; + } + } + + if (LastSpaceIndex != MAXUSHORT) { + OemLabel.Length = LastSpaceIndex; + } + } + + // + // Get the Unicode upcased string to store in the VPB. + // + + UpcasedLabel.Length = UnicodeString.Length; + UpcasedLabel.MaximumLength = 11*sizeof(WCHAR); + UpcasedLabel.Buffer = &TmpBuffer[0]; + + Status = RtlOemStringToCountedUnicodeString( &UpcasedLabel, + &OemLabel, + FALSE ); + + if (!NT_SUCCESS( Status )) { + + DebugTrace(-1, Dbg, "FatSetFsLabelInfo: Label must be too long. %08lx\n", Status ); + + return STATUS_INVALID_VOLUME_LABEL; + } + + DirentBcb = NULL; + + // + // Make this look like a write through to disk. This is important to + // avoid a unpleasant window where it looks like we have the wrong volume. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); + + _SEH2_TRY { + + // + // Are we setting or removing the label? Note that shaving spaces could + // make this different than wondering if the input buffer is non-zero length. + // + + if (OemLabel.Length > 0) { + + // + // Locate the volume label if there already is one + // + + FatLocateVolumeLabel( IrpContext, + Vcb, + &Dirent, + &DirentBcb, +#ifndef __REACTOS__ + &ByteOffset ); +#else + (PVBO)&ByteOffset ); +#endif + + // + // Check that we really got one, if not then we need to create + // a new one. The procedure we call will raise an appropriate + // status if we are not able to allocate a new dirent + // + + if (Dirent == NULL) { + + ByteOffset = FatCreateNewDirent( IrpContext, + Vcb->RootDcb, + 1 ); + + FatPrepareWriteDirectoryFile( IrpContext, + Vcb->RootDcb, + ByteOffset, + sizeof(DIRENT), + &DirentBcb, +#ifndef __REACTOS__ + &Dirent, +#else + (PVOID *)&Dirent, +#endif + FALSE, + TRUE, + &Status ); + + ASSERT( NT_SUCCESS( Status )); + + } else { + + // + // Just mark this guy dirty now. + // + + FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); + } + + // + // Now reconstruct the volume label dirent. + // + + FatConstructLabelDirent( IrpContext, + Dirent, + &OemLabel ); + + // + // Unpin the Bcb here so that we will get any IO errors + // here before changing the VPB label. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + FatUnpinRepinnedBcbs( IrpContext ); + + // + // Now set the upcased label in the VPB + // + + RtlCopyMemory( &Vcb->Vpb->VolumeLabel[0], + &UpcasedLabel.Buffer[0], + UpcasedLabel.Length ); + + Vcb->Vpb->VolumeLabelLength = UpcasedLabel.Length; + + } else { + + // + // Otherwise we're trying to delete the label + // Locate the current volume label if there already is one + // + + FatLocateVolumeLabel( IrpContext, + Vcb, + &Dirent, + &DirentBcb, +#ifndef __REACTOS__ + &ByteOffset ); +#else + (PVBO)&ByteOffset ); +#endif + + // + // Check that we really got one + // + + if (Dirent == NULL) { + + try_return( Status = STATUS_SUCCESS ); + } + + // + // Now delete the current label. + // + + Dirent->FileName[0] = FAT_DIRENT_DELETED; + + ASSERT( (Vcb->RootDcb->Specific.Dcb.UnusedDirentVbo == 0xffffffff) || + RtlAreBitsSet( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap, + ByteOffset / sizeof(DIRENT), + 1 ) ); + + RtlClearBits( &Vcb->RootDcb->Specific.Dcb.FreeDirentBitmap, + ByteOffset / sizeof(DIRENT), + 1 ); + + FatSetDirtyBcb( IrpContext, DirentBcb, Vcb, TRUE ); + + // + // Unpin the Bcb here so that we will get any IO errors + // here before changing the VPB label. + // + + FatUnpinBcb( IrpContext, DirentBcb ); + FatUnpinRepinnedBcbs( IrpContext ); + + // + // Now set the label in the VPB + // + + Vcb->Vpb->VolumeLabelLength = 0; + } + + Status = STATUS_SUCCESS; + + try_exit: NOTHING; + } _SEH2_FINALLY { + + DebugUnwind( FatSetFsALabelInfo ); + + FatUnpinBcb( IrpContext, DirentBcb ); + + DebugTrace(-1, Dbg, "FatSetFsALabelInfo -> STATUS_SUCCESS\n", 0); + } _SEH2_END; + + return Status; +} + + diff --git a/drivers/filesystems/fastfat_new/workque.c b/drivers/filesystems/fastfat_new/workque.c new file mode 100644 index 00000000000..857957d96de --- /dev/null +++ b/drivers/filesystems/fastfat_new/workque.c @@ -0,0 +1,362 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + WorkQue.c + +Abstract: + + This module implements the Work queue routines for the Fat File + system. + + +--*/ + +#include "fatprocs.h" + +// +// The following constant is the maximum number of ExWorkerThreads that we +// will allow to be servicing a particular target device at any one time. +// + +#define FSP_PER_DEVICE_THRESHOLD (2) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatOplockComplete) +#pragma alloc_text(PAGE, FatPrePostIrp) +#endif + + +VOID +NTAPI +FatOplockComplete ( + IN PVOID Context, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the oplock package when an oplock break has + completed, allowing an Irp to resume execution. If the status in + the Irp is STATUS_SUCCESS, then we queue the Irp to the Fsp queue. + Otherwise we complete the Irp with the status in the Irp. + +Arguments: + + Context - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + // + // Check on the return value in the Irp. + // + + if (Irp->IoStatus.Status == STATUS_SUCCESS) { + + // + // Insert the Irp context in the workqueue. + // + + FatAddToWorkque( (PIRP_CONTEXT) Context, Irp ); + + // + // Otherwise complete the request. + // + + } else { + + FatCompleteRequest( (PIRP_CONTEXT) Context, Irp, Irp->IoStatus.Status ); + } + + return; +} + + +VOID +NTAPI +FatPrePostIrp ( + IN PVOID Context, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs any neccessary work before STATUS_PENDING is + returned with the Fsd thread. This routine is called within the + filesystem and by the oplock package. + +Arguments: + + Context - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp; + PIRP_CONTEXT IrpContext; + + // + // If there is no Irp, we are done. + // + + if (Irp == NULL) { + + return; + } + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + IrpContext = (PIRP_CONTEXT) Context; + + // + // If there is a STACK FatIoContext pointer, clean and NULL it. + // + + if ((IrpContext->FatIoContext != NULL) && + FlagOn(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT)) { + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT); + IrpContext->FatIoContext = NULL; + } + + // + // We need to lock the user's buffer, unless this is an MDL-read, + // in which case there is no user buffer. + // + // **** we need a better test than non-MDL (read or write)! + + if (IrpContext->MajorFunction == IRP_MJ_READ || + IrpContext->MajorFunction == IRP_MJ_WRITE) { + + // + // If not an Mdl request, lock the user's buffer. + // + + if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) { + + FatLockUserBuffer( IrpContext, + Irp, + (IrpContext->MajorFunction == IRP_MJ_READ) ? + IoWriteAccess : IoReadAccess, + (IrpContext->MajorFunction == IRP_MJ_READ) ? + IrpSp->Parameters.Read.Length : IrpSp->Parameters.Write.Length ); + } + + // + // We also need to check whether this is a query file operation. + // + + } else if (IrpContext->MajorFunction == IRP_MJ_DIRECTORY_CONTROL + && IrpContext->MinorFunction == IRP_MN_QUERY_DIRECTORY) { + + FatLockUserBuffer( IrpContext, + Irp, + IoWriteAccess, + IrpSp->Parameters.QueryDirectory.Length ); + + // + // We also need to check whether this is a query ea operation. + // + + } else if (IrpContext->MajorFunction == IRP_MJ_QUERY_EA) { + + FatLockUserBuffer( IrpContext, + Irp, + IoWriteAccess, + IrpSp->Parameters.QueryEa.Length ); + + // + // We also need to check whether this is a set ea operation. + // + + } else if (IrpContext->MajorFunction == IRP_MJ_SET_EA) { + + FatLockUserBuffer( IrpContext, + Irp, + IoReadAccess, + IrpSp->Parameters.SetEa.Length ); + + // + // These two FSCTLs use neither I/O, so check for them. + // + + } else if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && + (IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST) && + ((IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_GET_VOLUME_BITMAP) || + (IrpSp->Parameters.FileSystemControl.FsControlCode == FSCTL_GET_RETRIEVAL_POINTERS))) { + + FatLockUserBuffer( IrpContext, + Irp, + IoWriteAccess, + IrpSp->Parameters.FileSystemControl.OutputBufferLength ); + } + + // + // Mark that we've already returned pending to the user + // + + IoMarkIrpPending( Irp ); + + return; +} + + +NTSTATUS +FatFsdPostRequest( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine enqueues the request packet specified by IrpContext to the + Ex Worker threads. This is a FSD routine. + +Arguments: + + IrpContext - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet, or NULL if it has already been completed. + +Return Value: + + STATUS_PENDING + + +--*/ + +{ + ASSERT( ARGUMENT_PRESENT(Irp) ); + ASSERT( IrpContext->OriginatingIrp == Irp ); + + FatPrePostIrp( IrpContext, Irp ); + + FatAddToWorkque( IrpContext, Irp ); + + // + // And return to our caller + // + + return STATUS_PENDING; +} + + +// +// Local support routine. +// + +VOID +FatAddToWorkque ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called to acually store the posted Irp to the Fsp + workque. + +Arguments: + + IrpContext - Pointer to the IrpContext to be queued to the Fsp + + Irp - I/O Request Packet. + +Return Value: + + None. + +--*/ + +{ + KIRQL SavedIrql; + PIO_STACK_LOCATION IrpSp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Check if this request has an associated file object, and thus volume + // device object. + // + + if ( IrpSp->FileObject != NULL ) { + + PVOLUME_DEVICE_OBJECT Vdo; + + Vdo = CONTAINING_RECORD( IrpSp->DeviceObject, + VOLUME_DEVICE_OBJECT, + DeviceObject ); + + // + // Check to see if this request should be sent to the overflow + // queue. If not, then send it off to an exworker thread. + // + + KeAcquireSpinLock( &Vdo->OverflowQueueSpinLock, &SavedIrql ); + + if ( Vdo->PostedRequestCount > FSP_PER_DEVICE_THRESHOLD) { + + // + // We cannot currently respond to this IRP so we'll just enqueue it + // to the overflow queue on the volume. + // + + InsertTailList( &Vdo->OverflowQueue, + &IrpContext->WorkQueueItem.List ); + + Vdo->OverflowQueueCount += 1; + + KeReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql ); + + return; + + } else { + + // + // We are going to send this Irp to an ex worker thread so up + // the count. + // + + Vdo->PostedRequestCount += 1; + + KeReleaseSpinLock( &Vdo->OverflowQueueSpinLock, SavedIrql ); + } + } + + // + // Send it off..... + // + + ExInitializeWorkItem( &IrpContext->WorkQueueItem, + FatFspDispatch, + IrpContext ); + + ExQueueWorkItem( &IrpContext->WorkQueueItem, CriticalWorkQueue ); + + return; +} + + diff --git a/drivers/filesystems/fastfat_new/write.c b/drivers/filesystems/fastfat_new/write.c new file mode 100644 index 00000000000..59f70076de8 --- /dev/null +++ b/drivers/filesystems/fastfat_new/write.c @@ -0,0 +1,2830 @@ +/*++ + +Copyright (c) 1989-2000 Microsoft Corporation + +Module Name: + + Write.c + +Abstract: + + This module implements the File Write routine for Write called by the + dispatch driver. + + +--*/ + +#include "fatprocs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (FAT_BUG_CHECK_WRITE) + +// +// The local debug trace level +// + +#define Dbg (DEBUG_TRACE_WRITE) + +// +// Macros to increment the appropriate performance counters. +// + +#define CollectWriteStats(VCB,OPEN_TYPE,BYTE_COUNT) { \ + PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()].Common; \ + if (((OPEN_TYPE) == UserFileOpen)) { \ + Stats->UserFileWrites += 1; \ + Stats->UserFileWriteBytes += (ULONG)(BYTE_COUNT); \ + } else if (((OPEN_TYPE) == VirtualVolumeFile || ((OPEN_TYPE) == DirectoryFile))) { \ + Stats->MetaDataWrites += 1; \ + Stats->MetaDataWriteBytes += (ULONG)(BYTE_COUNT); \ + } \ +} + +BOOLEAN FatNoAsync = FALSE; + +// +// Local support routines +// + +VOID +NTAPI +FatDeferredFlushDpc ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +VOID +NTAPI +FatDeferredFlush ( + PVOID Parameter + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, FatDeferredFlush) +#pragma alloc_text(PAGE, FatCommonWrite) +#endif + + +NTSTATUS +NTAPI +FatFsdWrite ( + IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtWriteFile API call + +Arguments: + + VolumeDeviceObject - Supplies the volume device object where the + file being Write exists + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The FSD status for the IRP + +--*/ + +{ + PFCB Fcb; + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + + BOOLEAN ModWriter = FALSE; + BOOLEAN TopLevel; + + DebugTrace(+1, Dbg, "FatFsdWrite\n", 0); + + // + // Call the common Write routine, with blocking allowed if synchronous + // + + FsRtlEnterFileSystem(); + + // + // We are first going to do a quick check for paging file IO. Since this + // is a fast path, we must replicate the check for the fsdo. + // + + if (!FatDeviceIsFatFsdo( IoGetCurrentIrpStackLocation(Irp)->DeviceObject)) { + + Fcb = (PFCB)(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext); + + if ((NodeType(Fcb) == FAT_NTC_FCB) && + FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) { + + // + // Do the usual STATUS_PENDING things. + // + + IoMarkIrpPending( Irp ); + + // + // Perform the actual IO, it will be completed when the io finishes. + // + + FatPagingFileIo( Irp, Fcb ); + + FsRtlExitFileSystem(); + + return STATUS_PENDING; + } + } + + _SEH2_TRY { + + TopLevel = FatIsIrpTopLevel( Irp ); + + IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) ); + + // + // This is a kludge for the mod writer case. The correct state + // of recursion is set in IrpContext, however, we much with the + // actual top level Irp field to get the correct WriteThrough + // behaviour. + // + + if (IoGetTopLevelIrp() == (PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP) { + + ModWriter = TRUE; + + IoSetTopLevelIrp( Irp ); + } + + // + // If this is an Mdl complete request, don't go through + // common write. + // + + if (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) { + + DebugTrace(0, Dbg, "Calling FatCompleteMdl\n", 0 ); + Status = FatCompleteMdl( IrpContext, Irp ); + + } else { + + Status = FatCommonWrite( IrpContext, Irp ); + } + + } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error status that we get back from the + // execption code + // + + Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() ); + } _SEH2_END; + +// ASSERT( !(ModWriter && (Status == STATUS_CANT_WAIT)) ); + + ASSERT( !(ModWriter && TopLevel) ); + + if (ModWriter) { IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP); } + + if (TopLevel) { IoSetTopLevelIrp( NULL ); } + + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "FatFsdWrite -> %08lx\n", Status); + + UNREFERENCED_PARAMETER( VolumeDeviceObject ); + + return Status; +} + + +NTSTATUS +FatCommonWrite ( + IN PIRP_CONTEXT IrpContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This is the common write routine for NtWriteFile, called from both + the Fsd, or from the Fsp if a request could not be completed without + blocking in the Fsd. This routine's actions are + conditionalized by the Wait input parameter, which determines whether + it is allowed to block or not. If a blocking condition is encountered + with Wait == FALSE, however, the request is posted to the Fsp, who + always calls with WAIT == TRUE. + +Arguments: + + Irp - Supplies the Irp to process + +Return Value: + + NTSTATUS - The return status for the operation + +--*/ + +{ + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + + VBO StartingVbo; + ULONG ByteCount; + ULONG FileSize; + ULONG InitialFileSize; + ULONG InitialValidDataLength; + + PIO_STACK_LOCATION IrpSp; + PFILE_OBJECT FileObject; + TYPE_OF_OPEN TypeOfOpen; + + BOOLEAN PostIrp = FALSE; + BOOLEAN OplockPostIrp = FALSE; + BOOLEAN ExtendingFile = FALSE; + BOOLEAN FcbOrDcbAcquired = FALSE; + BOOLEAN SwitchBackToAsync = FALSE; + BOOLEAN CalledByLazyWriter = FALSE; + BOOLEAN ExtendingValidData = FALSE; + BOOLEAN FcbAcquiredExclusive = FALSE; + BOOLEAN FcbCanDemoteToShared = FALSE; + BOOLEAN WriteFileSizeToDirent = FALSE; + BOOLEAN RecursiveWriteThrough = FALSE; + BOOLEAN UnwindOutstandingAsync = FALSE; + BOOLEAN PagingIoResourceAcquired = FALSE; + + BOOLEAN SynchronousIo; + BOOLEAN WriteToEof; + BOOLEAN PagingIo; + BOOLEAN NonCachedIo; + BOOLEAN Wait; + + NTSTATUS Status; + + FAT_IO_CONTEXT StackFatIoContext; + + // + // A system buffer is only used if we have to access the buffer directly + // from the Fsp to clear a portion or to do a synchronous I/O, or a + // cached transfer. It is possible that our caller may have already + // mapped a system buffer, in which case we must remember this so + // we do not unmap it on the way out. + // + + PVOID SystemBuffer = (PVOID) NULL; + + LARGE_INTEGER StartingByte; + + // + // Get current Irp stack location and file object + // + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + FileObject = IrpSp->FileObject; + + + DebugTrace(+1, Dbg, "FatCommonWrite\n", 0); + DebugTrace( 0, Dbg, "Irp = %8lx\n", Irp); + DebugTrace( 0, Dbg, "ByteCount = %8lx\n", IrpSp->Parameters.Write.Length); + DebugTrace( 0, Dbg, "ByteOffset.LowPart = %8lx\n", IrpSp->Parameters.Write.ByteOffset.LowPart); + DebugTrace( 0, Dbg, "ByteOffset.HighPart = %8lx\n", IrpSp->Parameters.Write.ByteOffset.HighPart); + + // + // Initialize the appropriate local variables. + // + + Wait = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO); + NonCachedIo = BooleanFlagOn(Irp->Flags,IRP_NOCACHE); + SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO); + + //ASSERT( PagingIo || FileObject->WriteAccess ); + + // + // Extract the bytecount and do our noop/throttle checking. + // + + ByteCount = IrpSp->Parameters.Write.Length; + + // + // If there is nothing to write, return immediately. + // + + if (ByteCount == 0) { + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + // + // See if we have to defer the write. + // + + if (!NonCachedIo && + !CcCanIWrite(FileObject, + ByteCount, + (BOOLEAN)(Wait && !BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_IN_FSP)), + BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE))) { + + BOOLEAN Retrying = BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE); + + FatPrePostIrp( IrpContext, Irp ); + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED_WRITE ); + + CcDeferWrite( FileObject, + (PCC_POST_DEFERRED_WRITE)FatAddToWorkque, + IrpContext, + Irp, + ByteCount, + Retrying ); + + return STATUS_PENDING; + } + + // + // Determine our starting position and type. If we are writing + // at EOF, then we will need additional synchronization before + // the IO is issued to determine where the data will go. + // + + StartingByte = IrpSp->Parameters.Write.ByteOffset; + StartingVbo = StartingByte.LowPart; + + WriteToEof = ( (StartingByte.LowPart == FILE_WRITE_TO_END_OF_FILE) && + (StartingByte.HighPart == -1) ); + + // + // Extract the nature of the write from the file object, and case on it + // + + TypeOfOpen = FatDecodeFileObject(FileObject, &Vcb, &FcbOrDcb, &Ccb); + + ASSERT( Vcb != NULL ); + + // + // Save callers who try to do cached IO to the raw volume from themselves. + // + + if (TypeOfOpen == UserVolumeOpen) { + + NonCachedIo = TRUE; + } + + ASSERT(!(NonCachedIo == FALSE && TypeOfOpen == VirtualVolumeFile)); + + // + // Collect interesting statistics. The FLAG_USER_IO bit will indicate + // what type of io we're doing in the FatNonCachedIo function. + // + + if (PagingIo) { + CollectWriteStats(Vcb, TypeOfOpen, ByteCount); + + if (TypeOfOpen == UserFileOpen) { + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO); + } else { + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_USER_IO); + } + } + + // + // We must disallow writes to regular objects that would require us + // to maintain an AllocationSize of greater than 32 significant bits. + // + // If this is paging IO, this is simply a case where we need to trim. + // This will occur in due course. + // + + if (!PagingIo && !WriteToEof && (TypeOfOpen != UserVolumeOpen)) { + + if (!FatIsIoRangeValid( Vcb, StartingByte, ByteCount )) { + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_DISK_FULL ); + + return STATUS_DISK_FULL; + } + } + + // + // Allocate if necessary and initialize a FAT_IO_CONTEXT block for + // all non cached Io. For synchronous Io + // we use stack storage, otherwise we allocate pool. + // + + if (NonCachedIo) { + + if (IrpContext->FatIoContext == NULL) { + + if (!Wait) { + + IrpContext->FatIoContext = + FsRtlAllocatePoolWithTag( NonPagedPool, + sizeof(FAT_IO_CONTEXT), + TAG_FAT_IO_CONTEXT ); + + } else { + + IrpContext->FatIoContext = &StackFatIoContext; + + SetFlag( IrpContext->Flags, IRP_CONTEXT_STACK_IO_CONTEXT ); + } + } + + RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) ); + + if (Wait) { + + KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent, + NotificationEvent, + FALSE ); + + } else { + + IrpContext->FatIoContext->Wait.Async.ResourceThreadId = + ExGetCurrentResourceThread(); + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + + IrpContext->FatIoContext->Wait.Async.FileObject = FileObject; + } + } + + // + // Check if this volume has already been shut down. If it has, fail + // this write request. + // + + if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN) ) { + + Irp->IoStatus.Information = 0; + FatCompleteRequest( IrpContext, Irp, STATUS_TOO_LATE ); + return STATUS_TOO_LATE; + } + + // + // This case corresponds to a write of the volume file (only the first + // fat allowed, the other fats are written automatically in parallel). + // + // We use an Mcb keep track of dirty sectors. Actual entries are Vbos + // and Lbos (ie. bytes), though they are all added in sector chunks. + // Since Vbo == Lbo for the volume file, the Mcb entries + // alternate between runs of Vbo == Lbo, and holes (Lbo == 0). We use + // the prior to represent runs of dirty fat sectors, and the latter + // for runs of clean fat. Note that since the first part of the volume + // file (boot sector) is always clean (a hole), and an Mcb never ends in + // a hole, there must always be an even number of runs(entries) in the Mcb. + // + // The strategy is to find the first and last dirty run in the desired + // write range (which will always be a set of pages), and write from the + // former to the later. The may result in writing some clean data, but + // will generally be more efficient than writing each runs seperately. + // + + if (TypeOfOpen == VirtualVolumeFile) { + + LBO DirtyLbo; + LBO CleanLbo; + + VBO DirtyVbo; + VBO StartingDirtyVbo; + + ULONG DirtyByteCount; + ULONG CleanByteCount; + + ULONG WriteLength; + + BOOLEAN MoreDirtyRuns = TRUE; + + IO_STATUS_BLOCK RaiseIosb; + + DebugTrace(0, Dbg, "Type of write is Virtual Volume File\n", 0); + + // + // If we can't wait we have to post this. + // + + if (!Wait) { + + DebugTrace( 0, Dbg, "Passing request to Fsp\n", 0 ); + + Status = FatFsdPostRequest(IrpContext, Irp); + + return Status; + } + + // + // If we weren't called by the Lazy Writer, then this write + // must be the result of a write-through or flush operation. + // Setting the IrpContext flag, will cause DevIoSup.c to + // write-through the data to the disk. + // + + if (!FlagOn((ULONG_PTR)IoGetTopLevelIrp(), FSRTL_CACHE_TOP_LEVEL_IRP)) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); + } + + // + // Assert an even number of entries in the Mcb, an odd number would + // mean that the Mcb is corrupt. + // + + ASSERT( (FsRtlNumberOfRunsInLargeMcb( &Vcb->DirtyFatMcb ) & 1) == 0); + + // + // We need to skip over any clean sectors at the start of the write. + // + // Also check the two cases where there are no dirty fats in the + // desired write range, and complete them with success. + // + // 1) There is no Mcb entry corresponding to StartingVbo, meaning + // we are beyond the end of the Mcb, and thus dirty fats. + // + // 2) The run at StartingVbo is clean and continues beyond the + // desired write range. + // + + if (!FatLookupMcbEntry( Vcb, &Vcb->DirtyFatMcb, + StartingVbo, + &DirtyLbo, + &DirtyByteCount, + NULL ) + + || ( (DirtyLbo == 0) && (DirtyByteCount >= ByteCount) ) ) { + + DebugTrace(0, DEBUG_TRACE_DEBUG_HOOKS, + "No dirty fat sectors in the write range.\n", 0); + + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + DirtyVbo = (VBO)DirtyLbo; + + // + // If the last run was a hole (clean), up DirtyVbo to the next + // run, which must be dirty. + // + + if (DirtyVbo == 0) { + + DirtyVbo = StartingVbo + DirtyByteCount; + } + + // + // This is where the write will start. + // + + StartingDirtyVbo = DirtyVbo; + + // + // + // Now start enumerating the dirty fat sectors spanning the desired + // write range, this first one of which is now DirtyVbo. + // + + while ( MoreDirtyRuns ) { + + // + // Find the next dirty run, if it is not there, the Mcb ended + // in a hole, or there is some other corruption of the Mcb. + // + + if (!FatLookupMcbEntry( Vcb, &Vcb->DirtyFatMcb, + DirtyVbo, + &DirtyLbo, + &DirtyByteCount, + NULL )) { + + DirtyVbo = (VBO)DirtyLbo; + + DebugTrace(0, Dbg, "Last dirty fat Mcb entry was a hole: corrupt.\n", 0); + FatBugCheck( 0, 0, 0 ); + + } else { + + DirtyVbo = (VBO)DirtyLbo; + + // + // This has to correspond to a dirty run, and must start + // within the write range since we check it at entry to, + // and at the bottom of this loop. + // + + ASSERT((DirtyVbo != 0) && (DirtyVbo < StartingVbo + ByteCount)); + + // + // There are three ways we can know that this was the + // last dirty run we want to write. + // + // 1) The current dirty run extends beyond or to the + // desired write range. + // + // 2) On trying to find the following clean run, we + // discover that this is the last run in the Mcb. + // + // 3) The following clean run extend beyond the + // desired write range. + // + // In any of these cases we set MoreDirtyRuns = FALSE. + // + + // + // If the run is larger than we are writing, we also + // must truncate the WriteLength. This is benign in + // the equals case. + // + + if (DirtyVbo + DirtyByteCount >= StartingVbo + ByteCount) { + + DirtyByteCount = StartingVbo + ByteCount - DirtyVbo; + + MoreDirtyRuns = FALSE; + + } else { + + // + // Scan the clean hole after this dirty run. If this + // run was the last, prepare to exit the loop + // + + if (!FatLookupMcbEntry( Vcb, &Vcb->DirtyFatMcb, + DirtyVbo + DirtyByteCount, + &CleanLbo, + &CleanByteCount, + NULL )) { + + MoreDirtyRuns = FALSE; + + } else { + + // + // Assert that we actually found a clean run. + // and compute the start of the next dirty run. + // + + ASSERT (CleanLbo == 0); + + // + // If the next dirty run starts beyond the desired + // write, we have found all the runs we need, so + // prepare to exit. + // + + if (DirtyVbo + DirtyByteCount + CleanByteCount >= + StartingVbo + ByteCount) { + + MoreDirtyRuns = FALSE; + + } else { + + // + // Compute the start of the next dirty run. + // + + DirtyVbo += DirtyByteCount + CleanByteCount; + } + } + } + } + } // while ( MoreDirtyRuns ) + + // + // At this point DirtyVbo and DirtyByteCount correctly reflect the + // final dirty run, constrained to the desired write range. + // + // Now compute the length we finally must write. + // + + WriteLength = (DirtyVbo + DirtyByteCount) - StartingDirtyVbo; + + // + // We must now assume that the write will complete with success, + // and initialize our expected status in RaiseIosb. It will be + // modified below if an error occurs. + // + + RaiseIosb.Status = STATUS_SUCCESS; + RaiseIosb.Information = ByteCount; + + // + // Loop through all the fats, setting up a multiple async to + // write them all. If there are more than FAT_MAX_PARALLEL_IOS + // then we do several muilple asyncs. + // + + { + ULONG Fat; + ULONG BytesPerFat; + IO_RUN StackIoRuns[2]; + PIO_RUN IoRuns; + + BytesPerFat = FatBytesPerFat( &Vcb->Bpb ); + + if ((ULONG)Vcb->Bpb.Fats > 2) { + + IoRuns = FsRtlAllocatePoolWithTag( PagedPool, + (ULONG)Vcb->Bpb.Fats, + TAG_IO_RUNS ); + + } else { + + IoRuns = StackIoRuns; + } + + for (Fat = 0; Fat < (ULONG)Vcb->Bpb.Fats; Fat++) { + + IoRuns[Fat].Vbo = StartingDirtyVbo; + IoRuns[Fat].Lbo = Fat * BytesPerFat + StartingDirtyVbo; + IoRuns[Fat].Offset = StartingDirtyVbo - StartingVbo; + IoRuns[Fat].ByteCount = WriteLength; + } + + // + // Keep track of meta-data disk ios. + // + + Vcb->Statistics[KeGetCurrentProcessorNumber()].Common.MetaDataDiskWrites += Vcb->Bpb.Fats; + + _SEH2_TRY { + + FatMultipleAsync( IrpContext, + Vcb, + Irp, + (ULONG)Vcb->Bpb.Fats, + IoRuns ); + + } _SEH2_FINALLY { + + if (IoRuns != StackIoRuns) { + + ExFreePool( IoRuns ); + } + } _SEH2_END; + + // + // Wait for all the writes to finish + // + + FatWaitSync( IrpContext ); + + // + // If we got an error, or verify required, remember it. + // + + if (!NT_SUCCESS( Irp->IoStatus.Status )) { + + DebugTrace( 0, + Dbg, + "Error %X while writing volume file.\n", + Irp->IoStatus.Status ); + + RaiseIosb = Irp->IoStatus; + } + } + + // + // If the writes were a success, set the sectors clean, else + // raise the error status and mark the volume as needing + // verification. This will automatically reset the volume + // structures. + // + // If not, then mark this volume as needing verification to + // automatically cause everything to get cleaned up. + // + + Irp->IoStatus = RaiseIosb; + + if ( NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + FatRemoveMcbEntry( Vcb, &Vcb->DirtyFatMcb, + StartingDirtyVbo, + WriteLength ); + + } else { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", Status ); + + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // This case corresponds to a general opened volume (DASD), ie. + // open ("a:"). + // + + if (TypeOfOpen == UserVolumeOpen) { + + LBO StartingLbo; + LBO VolumeSize; + + // + // Precalculate the volume size since we're nearly always going + // to be wanting to use it. + // + + VolumeSize = (LBO) Int32x32To64( Vcb->Bpb.BytesPerSector, + (Vcb->Bpb.Sectors != 0 ? Vcb->Bpb.Sectors : + Vcb->Bpb.LargeSectors)); + + StartingLbo = StartingByte.QuadPart; + + DebugTrace(0, Dbg, "Type of write is User Volume.\n", 0); + + // + // Verify that the volume for this handle is still valid, permitting + // operations to proceed on dismounted volumes via the handle which + // performed the dismount. + // + + if (!FlagOn( Ccb->Flags, CCB_FLAG_COMPLETE_DISMOUNT )) { + + FatQuickVerifyVcb( IrpContext, Vcb ); + } + + if (!FlagOn( Ccb->Flags, CCB_FLAG_DASD_PURGE_DONE )) { + + BOOLEAN PreviousWait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + // + // Grab the entire volume so that even the normally unsafe action + // of writing to an unlocked volume won't open us to a race between + // the flush and purge of the FAT below. + // + // I really don't think this is particularly important to worry about, + // but a repro case for another bug happens to dance into this race + // condition pretty easily. Eh. + // + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + FatAcquireExclusiveVolume( IrpContext, Vcb ); + + _SEH2_TRY { + + // + // If the volume isn't locked, flush and purge it. + // + + if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) { + + FatFlushFat( IrpContext, Vcb ); + CcPurgeCacheSection( &Vcb->SectionObjectPointers, + NULL, + 0, + FALSE ); + + FatPurgeReferencedFileObjects( IrpContext, Vcb->RootDcb, Flush ); + } + + } _SEH2_FINALLY { + + FatReleaseVolume( IrpContext, Vcb ); + if (!PreviousWait) { + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + } + } _SEH2_END; + + SetFlag( Ccb->Flags, CCB_FLAG_DASD_PURGE_DONE | + CCB_FLAG_DASD_FLUSH_DONE ); + } + + if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) { + + // + // Make sure we don't try to write past end of volume, + // reducing the requested byte count if necessary. + // + + if (WriteToEof || StartingLbo >= VolumeSize) { + FatCompleteRequest( IrpContext, Irp, STATUS_SUCCESS ); + return STATUS_SUCCESS; + } + + if (ByteCount > VolumeSize - StartingLbo) { + + ByteCount = (ULONG) (VolumeSize - StartingLbo); + + // + // For async writes we had set the byte count in the FatIoContext + // above, so fix that here. + // + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + } + } + } else { + + // + // This has a peculiar interpretation, but just adjust the starting + // byte to the end of the visible volume. + // + + if (WriteToEof) { + + StartingLbo = VolumeSize; + } + } + + // + // For DASD we have to probe and lock the user's buffer + // + + FatLockUserBuffer( IrpContext, Irp, IoReadAccess, ByteCount ); + + // + // Set the FO_MODIFIED flag here to trigger a verify when this + // handle is closed. Note that we can err on the conservative + // side with no problem, i.e. if we accidently do an extra + // verify there is no problem. + // + + SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); + + // + // Write the data and wait for the results + // + + FatSingleAsync( IrpContext, + Vcb, + StartingLbo, + ByteCount, + Irp ); + + if (!Wait) { + + // + // We, nor anybody else, need the IrpContext any more. + // + + IrpContext->FatIoContext = NULL; + + FatDeleteIrpContext( IrpContext ); + + DebugTrace(-1, Dbg, "FatNonCachedIo -> STATUS_PENDING\n", 0); + + return STATUS_PENDING; + } + + FatWaitSync( IrpContext ); + + // + // If the call didn't succeed, raise the error status + // + // Also mark this volume as needing verification to automatically + // cause everything to get cleaned up. + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + // + // Update the current file position. We assume that + // open/create zeros out the CurrentByteOffset field. + // + + if (SynchronousIo && !PagingIo) { + FileObject->CurrentByteOffset.QuadPart = + StartingLbo + Irp->IoStatus.Information; + } + + DebugTrace(-1, Dbg, "FatCommonWrite -> %08lx\n", Status ); + + FatCompleteRequest( IrpContext, Irp, Status ); + return Status; + } + + // + // At this point we know there is an Fcb/Dcb. + // + + ASSERT( FcbOrDcb != NULL ); + + // + // Use a try-finally to free Fcb/Dcb and buffers on the way out. + // + + _SEH2_TRY { + + // + // This case corresponds to a normal user write file. + // + + if ( TypeOfOpen == UserFileOpen ) { + + ULONG ValidDataLength; + ULONG ValidDataToDisk; + ULONG ValidDataToCheck; + + DebugTrace(0, Dbg, "Type of write is user file open\n", 0); + + // + // If this is a noncached transfer and is not a paging I/O, and + // the file has been opened cached, then we will do a flush here + // to avoid stale data problems. Note that we must flush before + // acquiring the Fcb shared since the write may try to acquire + // it exclusive. + // + // The Purge following the flush will garentee cache coherency. + // + + if (NonCachedIo && !PagingIo && + (FileObject->SectionObjectPointer->DataSectionObject != NULL)) { + + // + // We need the Fcb exclsuive to do the CcPurgeCache + // + + if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + FcbOrDcbAcquired = TRUE; + FcbAcquiredExclusive = TRUE; + + // + // Preacquire pagingio for the flush. + // + + ExAcquireSharedStarveExclusive( FcbOrDcb->Header.PagingIoResource, TRUE ); + + CcFlushCache( FileObject->SectionObjectPointer, + WriteToEof ? &FcbOrDcb->Header.FileSize : &StartingByte, + ByteCount, + &Irp->IoStatus ); + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + + if (!NT_SUCCESS( Irp->IoStatus.Status)) { + + try_return( Irp->IoStatus.Status ); + } + + // + // Now pick up and hold pagingIO exclusive. This serializes us with the + // completion of a coincedent lazy writer doing its part of the write of + // this range. + // + // We hold so that we will prevent a pagefault from occuring and seeing + // soon-to-be stale data from the disk. We used to believe this was + // something to be left to the app to synchronize; we now realize that + // noncached IO on a fileserver is doomed without the filesystem forcing + // the coherency issue. By only penalizing noncached coherency when + // needed, this is about the best we can do. + // + + ExAcquireResourceExclusiveLite( FcbOrDcb->Header.PagingIoResource, TRUE); + PagingIoResourceAcquired = TRUE; + + CcPurgeCacheSection( FileObject->SectionObjectPointer, + WriteToEof ? &FcbOrDcb->Header.FileSize : &StartingByte, + ByteCount, + FALSE ); + + // + // Indicate we're OK with the fcb being demoted to shared access + // if that turns out to be possible later on after VDL extension + // is checked for. + // + // PagingIo must be held all the way through. + // + + FcbCanDemoteToShared = TRUE; + } + + // + // We assert that Paging Io writes will never WriteToEof. + // + + ASSERT( WriteToEof ? !PagingIo : TRUE ); + + // + // First let's acquire the Fcb shared. Shared is enough if we + // are not writing beyond EOF. + // + + if ( PagingIo ) { + + (VOID)ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + PagingIoResourceAcquired = TRUE; + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.Resource = + FcbOrDcb->Header.PagingIoResource; + } + + // + // Check to see if we colided with a MoveFile call, and if + // so block until it completes. + // + + if (FcbOrDcb->MoveFileEvent) { + + (VOID)KeWaitForSingleObject( FcbOrDcb->MoveFileEvent, + Executive, + KernelMode, + FALSE, + NULL ); + } + + } else { + + // + // We may already have the Fcb due to noncached coherency + // work done just above; however, we may still have to extend + // valid data length. We can't demote this to shared, matching + // what occured before, until we figure that out a bit later. + // + // We kept ahold of it since our lockorder is main->paging, + // and paging must now held across the noncached write from + // the purge on. + // + + // + // If this is async I/O, we will wait if there is an exclusive + // waiter. + // + + if (!Wait && NonCachedIo) { + + if (!FcbOrDcbAcquired && + !FatAcquireSharedFcbWaitForEx( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb ); + try_return( PostIrp = TRUE ); + } + + // + // Note we will have to release this resource elsewhere. If we came + // out of the noncached coherency path, we will also have to drop + // the paging io resource. + // + + IrpContext->FatIoContext->Wait.Async.Resource = FcbOrDcb->Header.Resource; + + if (FcbCanDemoteToShared) { + + IrpContext->FatIoContext->Wait.Async.Resource2 = FcbOrDcb->Header.PagingIoResource; + } + } else { + + if (!FcbOrDcbAcquired && + !FatAcquireSharedFcb( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb ); + try_return( PostIrp = TRUE ); + } + } + + FcbOrDcbAcquired = TRUE; + } + + // + // Get a first tentative file size and valid data length. + // We must get ValidDataLength first since it is always + // increased second (in case we are unprotected) and + // we don't want to capture ValidDataLength > FileSize. + // + + ValidDataToDisk = FcbOrDcb->ValidDataToDisk; + ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart; + FileSize = FcbOrDcb->Header.FileSize.LowPart; + + ASSERT( ValidDataLength <= FileSize ); + + // + // If are paging io, then we do not want + // to write beyond end of file. If the base is beyond Eof, we will just + // Noop the call. If the transfer starts before Eof, but extends + // beyond, we will truncate the transfer to the last sector + // boundary. + // + + // + // Just in case this is paging io, limit write to file size. + // Otherwise, in case of write through, since Mm rounds up + // to a page, we might try to acquire the resource exclusive + // when our top level guy only acquired it shared. Thus, =><=. + // + + if ( PagingIo ) { + + if (StartingVbo >= FileSize) { + + DebugTrace( 0, Dbg, "PagingIo started beyond EOF.\n", 0 ); + + Irp->IoStatus.Information = 0; + + try_return( Status = STATUS_SUCCESS ); + } + + if (ByteCount > FileSize - StartingVbo) { + + DebugTrace( 0, Dbg, "PagingIo extending beyond EOF.\n", 0 ); + + ByteCount = FileSize - StartingVbo; + } + } + + // + // Determine if we were called by the lazywriter. + // (see resrcsup.c) + // + + if (FcbOrDcb->Specific.Fcb.LazyWriteThread == PsGetCurrentThread()) { + + CalledByLazyWriter = TRUE; + + if (FlagOn( FcbOrDcb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE )) { + + // + // Fail if the start of this request is beyond valid data length. + // Don't worry if this is an unsafe test. MM and CC won't + // throw this page away if it is really dirty. + // + + if ((StartingVbo + ByteCount > ValidDataLength) && + (StartingVbo < FileSize)) { + + // + // It's OK if byte range is within the page containing valid data length, + // since we will use ValidDataToDisk as the start point. + // + + if (StartingVbo + ByteCount > ((ValidDataLength + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { + + // + // Don't flush this now. + // + + try_return( Status = STATUS_FILE_LOCK_CONFLICT ); + } + } + } + } + + // + // This code detects if we are a recursive synchronous page write + // on a write through file object. + // + + if (FlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && + FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)) { + + PIRP TopIrp; + + TopIrp = IoGetTopLevelIrp(); + + // + // This clause determines if the top level request was + // in the FastIo path. Gack. Since we don't have a + // real sharing protocol for the top level IRP field ... + // yet ... if someone put things other than a pure IRP in + // there we best be careful. + // + + if ((ULONG_PTR)TopIrp > FSRTL_MAX_TOP_LEVEL_IRP_FLAG && + NodeType(TopIrp) == IO_TYPE_IRP) { + + PIO_STACK_LOCATION IrpStack; + + IrpStack = IoGetCurrentIrpStackLocation(TopIrp); + + // + // Finally this routine detects if the Top irp was a + // write to this file and thus we are the writethrough. + // + + if ((IrpStack->MajorFunction == IRP_MJ_WRITE) && + (IrpStack->FileObject->FsContext == FileObject->FsContext)) { + + RecursiveWriteThrough = TRUE; + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); + } + } + } + + // + // Here is the deal with ValidDataLength and FileSize: + // + // Rule 1: PagingIo is never allowed to extend file size. + // + // Rule 2: Only the top level requestor may extend Valid + // Data Length. This may be paging IO, as when a + // a user maps a file, but will never be as a result + // of cache lazy writer writes since they are not the + // top level request. + // + // Rule 3: If, using Rules 1 and 2, we decide we must extend + // file size or valid data, we take the Fcb exclusive. + // + + // + // Now see if we are writing beyond valid data length, and thus + // maybe beyond the file size. If so, then we must + // release the Fcb and reacquire it exclusive. Note that it is + // important that when not writing beyond EOF that we check it + // while acquired shared and keep the FCB acquired, in case some + // turkey truncates the file. + // + + // + // Note that the lazy writer must not be allowed to try and + // acquire the resource exclusive. This is not a problem since + // the lazy writer is paging IO and thus not allowed to extend + // file size, and is never the top level guy, thus not able to + // extend valid data length. + // + + if ( !CalledByLazyWriter && + + !RecursiveWriteThrough && + + (WriteToEof || + StartingVbo + ByteCount > ValidDataLength)) { + + // + // If this was an asynchronous write, we are going to make + // the request synchronous at this point, but only kinda. + // At the last moment, before sending the write off to the + // driver, we may shift back to async. + // + // The modified page writer already has the resources + // he requires, so this will complete in small finite + // time. + // + + if (!Wait) { + + Wait = TRUE; + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + if (NonCachedIo) { + + ASSERT( TypeOfOpen == UserFileOpen ); + + SwitchBackToAsync = TRUE; + } + } + + // + // We need Exclusive access to the Fcb/Dcb since we will + // probably have to extend valid data and/or file. + // + + // + // Y'know, the PagingIo case is a mapped page writer, and + // MmFlushSection or the mapped page writer itself already + // snatched up the main exclusive for us via the AcquireForCcFlush + // or AcquireForModWrite logic (the default logic parallels FAT's + // requirements since this order/model came first). Should ASSERT + // this since it'll just go 1->2, and a few more unnecesary DPC + // transitions. + // + // The preacquire is done to avoid inversion over the collided flush + // meta-resource in Mm. The one time this is not true is at final + // system shutdown time, when Mm goes off and flushes all the dirty + // pages. Since the callback is defined as Wait == FALSE he can't + // guarantee acquisition (though with clean process shutdown being + // enforced, it really should be now). Permit this to float. + // + // Note that since we're going to fall back on the acquisition aleady + // done for us, don't confuse things by thinking we did the work + // for it. + // + + if ( PagingIo ) { + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + PagingIoResourceAcquired = FALSE; + + } else { + + // + // The Fcb may already be acquired exclusive due to coherency + // work performed earlier. If so, obviously no work to do. + // + + if (!FcbAcquiredExclusive) { + + FatReleaseFcb( IrpContext, FcbOrDcb ); + FcbOrDcbAcquired = FALSE; + + if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + FcbOrDcbAcquired = TRUE; + FcbAcquiredExclusive = TRUE; + } + } + + // + // Now that we have the Fcb exclusive, see if this write + // qualifies for being made async again. The key point + // here is that we are going to update ValidDataLength in + // the Fcb before returning. We must make sure this will + // not cause a problem. One thing we must do is keep out + // the FastIo path. + // + + if (SwitchBackToAsync) { + + if ((FcbOrDcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) || + (StartingVbo + ByteCount > FcbOrDcb->Header.ValidDataLength.LowPart) || + FatNoAsync) { + + RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) ); + + KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent, + NotificationEvent, + FALSE ); + + SwitchBackToAsync = FALSE; + + } else { + + if (!FcbOrDcb->NonPaged->OutstandingAsyncEvent) { + + FcbOrDcb->NonPaged->OutstandingAsyncEvent = + FsRtlAllocatePoolWithTag( NonPagedPool, + sizeof(KEVENT), + TAG_EVENT ); + + KeInitializeEvent( FcbOrDcb->NonPaged->OutstandingAsyncEvent, + NotificationEvent, + FALSE ); + } + + // + // If we are transitioning from 0 to 1, reset the event. + // + + if (ExInterlockedAddUlong( &FcbOrDcb->NonPaged->OutstandingAsyncWrites, + 1, + &FatData.GeneralSpinLock ) == 0) { + + KeClearEvent( FcbOrDcb->NonPaged->OutstandingAsyncEvent ); + } + + UnwindOutstandingAsync = TRUE; + + IrpContext->FatIoContext->Wait.Async.NonPagedFcb = FcbOrDcb->NonPaged; + } + } + + // + // Now that we have the Fcb exclusive, get a new batch of + // filesize and ValidDataLength. + // + + ValidDataToDisk = FcbOrDcb->ValidDataToDisk; + ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart; + FileSize = FcbOrDcb->Header.FileSize.LowPart; + + // + // If this is PagingIo check again if any pruning is + // required. It is important to start from basic + // princples in case the file was *grown* ... + // + + if ( PagingIo ) { + + if (StartingVbo >= FileSize) { + Irp->IoStatus.Information = 0; + try_return( Status = STATUS_SUCCESS ); + } + + ByteCount = IrpSp->Parameters.Write.Length; + + if (ByteCount > FileSize - StartingVbo) { + ByteCount = FileSize - StartingVbo; + } + } + } + + // + // Remember the final requested byte count + // + + if (NonCachedIo && !Wait) { + + IrpContext->FatIoContext->Wait.Async.RequestedByteCount = + ByteCount; + } + + // + // Remember the initial file size and valid data length, + // just in case ..... + // + + InitialFileSize = FileSize; + + InitialValidDataLength = ValidDataLength; + + // + // Make sure the FcbOrDcb is still good + // + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + // + // Check for writing to end of File. If we are, then we have to + // recalculate a number of fields. + // + + if ( WriteToEof ) { + + StartingVbo = FileSize; + StartingByte = FcbOrDcb->Header.FileSize; + + // + // Since we couldn't know this information until now, perform the + // necessary bounds checking that we ommited at the top because + // this is a WriteToEof operation. + // + + if (!FatIsIoRangeValid( Vcb, StartingByte, ByteCount )) { + + Irp->IoStatus.Information = 0; + try_return( Status = STATUS_DISK_FULL ); + } + } + + // + // If this is a non paging write to a data stream object we have to + // check for access according to the current state op/filelocks. + // + // Note that after this point, operations will be performed on the file. + // No modifying activity can occur prior to this point in the write + // path. + // + + if (!PagingIo && TypeOfOpen == UserFileOpen) { + + Status = FsRtlCheckOplock( &FcbOrDcb->Specific.Fcb.Oplock, + Irp, + IrpContext, + FatOplockComplete, + FatPrePostIrp ); + + if (Status != STATUS_SUCCESS) { + + OplockPostIrp = TRUE; + PostIrp = TRUE; + try_return( NOTHING ); + } + + // + // This oplock call can affect whether fast IO is possible. + // We may have broken an oplock to no oplock held. If the + // current state of the file is FastIoIsNotPossible then + // recheck the fast IO state. + // + + if (FcbOrDcb->Header.IsFastIoPossible == FastIoIsNotPossible) { + + FcbOrDcb->Header.IsFastIoPossible = FatIsFastIoPossible( FcbOrDcb ); + } + + // + // And finally check the regular file locks. + // + + if (!FsRtlCheckLockForWriteAccess( &FcbOrDcb->Specific.Fcb.FileLock, Irp )) { + + try_return( Status = STATUS_FILE_LOCK_CONFLICT ); + } + } + + // + // Determine if we will deal with extending the file. Note that + // this implies extending valid data, and so we already have all + // of the required synchronization done. + // + + if (!PagingIo && (StartingVbo + ByteCount > FileSize)) { + + ExtendingFile = TRUE; + } + + if ( ExtendingFile ) { + + // + // EXTENDING THE FILE + // + // Update our local copy of FileSize + // + + FileSize = StartingVbo + ByteCount; + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + // + // If the write goes beyond the allocation size, add some + // file allocation. + // + + if ( FileSize > FcbOrDcb->Header.AllocationSize.LowPart ) { + + BOOLEAN AllocateMinimumSize = TRUE; + + // + // Only do allocation chuncking on writes if this is + // not the first allocation added to the file. + // + + if (FcbOrDcb->Header.AllocationSize.LowPart != 0 ) { + + ULONG ApproximateClusterCount; + ULONG TargetAllocation; + ULONG Multiplier; + ULONG BytesPerCluster; + ULONG ClusterAlignedFileSize; + + // + // We are going to try and allocate a bigger chunk than + // we actually need in order to maximize FastIo usage. + // + // The multiplier is computed as follows: + // + // + // (FreeDiskSpace ) + // Mult = ( (-------------------------) / 32 ) + 1 + // (FileSize - AllocationSize) + // + // and max out at 32. + // + // With this formula we start winding down chunking + // as we get near the disk space wall. + // + // For instance on an empty 1 MEG floppy doing an 8K + // write, the multiplier is 6, or 48K to allocate. + // When this disk is half full, the multipler is 3, + // and when it is 3/4 full, the mupltiplier is only 1. + // + // On a larger disk, the multiplier for a 8K read will + // reach its maximum of 32 when there is at least ~8 Megs + // available. + // + + // + // Small write performance note, use cluster aligned + // file size in above equation. + // + + // + // We need to carefully consider what happens when we approach + // a 2^32 byte filesize. Overflows will cause problems. + // + + BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster; + + // + // This can overflow if the target filesize is in the last cluster. + // In this case, we can obviously skip over all of this fancy + // logic and just max out the file right now. + // + + ClusterAlignedFileSize = (FileSize + (BytesPerCluster - 1)) & + ~(BytesPerCluster - 1); + + if (ClusterAlignedFileSize != 0) { + + // + // This actually has a chance but the possibility of overflowing + // the numerator is pretty unlikely, made more unlikely by moving + // the divide by 32 up to scale the BytesPerCluster. However, even if it does the + // effect is completely benign. + // + // FAT32 with a 64k cluster and over 2^21 clusters would do it (and + // so forth - 2^(16 - 5 + 21) == 2^32). Since this implies a partition + // of 32gb and a number of clusters (and cluster size) we plan to + // disallow in format for FAT32, the odds of this happening are pretty + // low anyway. + // + + Multiplier = ((Vcb->AllocationSupport.NumberOfFreeClusters * + (BytesPerCluster >> 5)) / + (ClusterAlignedFileSize - + FcbOrDcb->Header.AllocationSize.LowPart)) + 1; + + if (Multiplier > 32) { Multiplier = 32; } + + Multiplier *= (ClusterAlignedFileSize - FcbOrDcb->Header.AllocationSize.LowPart); + + TargetAllocation = FcbOrDcb->Header.AllocationSize.LowPart + Multiplier; + + // + // We know that TargetAllocation is in whole clusters, so simply + // checking if it wrapped is correct. If it did, we fall back + // to allocating up to the maximum legal size. + // + + if (TargetAllocation < FcbOrDcb->Header.AllocationSize.LowPart) { + + TargetAllocation = ~BytesPerCluster + 1; + Multiplier = TargetAllocation - FcbOrDcb->Header.AllocationSize.LowPart; + } + + // + // Now do an unsafe check here to see if we should even + // try to allocate this much. If not, just allocate + // the minimum size we need, if so so try it, but if it + // fails, just allocate the minimum size we need. + // + + ApproximateClusterCount = (Multiplier / BytesPerCluster); + + if (ApproximateClusterCount <= Vcb->AllocationSupport.NumberOfFreeClusters) { + + _SEH2_TRY { + + FatAddFileAllocation( IrpContext, + FcbOrDcb, + FileObject, + TargetAllocation ); + + AllocateMinimumSize = FALSE; + SetFlag( FcbOrDcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE ); + + } _SEH2_EXCEPT( _SEH2_GetExceptionCode() == STATUS_DISK_FULL ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { + + FatResetExceptionState( IrpContext ); + } _SEH2_END; + } + } + } + + if ( AllocateMinimumSize ) { + + FatAddFileAllocation( IrpContext, + FcbOrDcb, + FileObject, + FileSize ); + } + + // + // Assert that the allocation worked + // + + ASSERT( FcbOrDcb->Header.AllocationSize.LowPart >= FileSize ); + } + + // + // Set the new file size in the Fcb + // + + ASSERT( FileSize <= FcbOrDcb->Header.AllocationSize.LowPart ); + + FcbOrDcb->Header.FileSize.LowPart = FileSize; + + // + // Extend the cache map, letting mm knows the new file size. + // We only have to do this if the file is cached. + // + + if (CcIsFileCached(FileObject)) { + CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize ); + } + } + + // + // Determine if we will deal with extending valid data. + // + + if ( !CalledByLazyWriter && + !RecursiveWriteThrough && + (StartingVbo + ByteCount > ValidDataLength) ) { + + ExtendingValidData = TRUE; + + } else { + + // + // If not extending valid data, and we otherwise believe we + // could demote from exclusive to shared, do so. This will + // occur when we synchronize tight for noncached coherency + // but must defer the demotion until after we decide about + // valid data length, which requires it exclusive. Since we + // can't drop/re-pick the resources without letting a pagefault + // squirt through, the resource decision was kept up in the air + // until now. + // + // Note that we've still got PagingIo exclusive in these cases. + // + + if (FcbCanDemoteToShared) { + + ASSERT( FcbAcquiredExclusive && ExIsResourceAcquiredExclusiveLite( FcbOrDcb->Header.Resource )); + ExConvertExclusiveToSharedLite( FcbOrDcb->Header.Resource ); + FcbAcquiredExclusive = FALSE; + } + } + + if (ValidDataToDisk > ValidDataLength) { + + ValidDataToCheck = ValidDataToDisk; + + } else { + + ValidDataToCheck = ValidDataLength; + } + + // + // HANDLE THE NON-CACHED CASE + // + + if ( NonCachedIo ) { + + // + // Declare some local variables for enumeration through the + // runs of the file, and an array to store parameters for + // parallel I/Os + // + + ULONG SectorSize; + + ULONG BytesToWrite; + + DebugTrace(0, Dbg, "Non cached write.\n", 0); + + // + // Round up to sector boundry. The end of the write interval + // must, however, be beyond EOF. + // + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + BytesToWrite = (ByteCount + (SectorSize - 1)) + & ~(SectorSize - 1); + + // + // All requests should be well formed and + // make sure we don't wipe out any data + // + + if (((StartingVbo & (SectorSize - 1)) != 0) || + + ((BytesToWrite != ByteCount) && + (StartingVbo + ByteCount < ValidDataLength))) { + + ASSERT( FALSE ); + + DebugTrace( 0, Dbg, "FatCommonWrite -> STATUS_NOT_IMPLEMENTED\n", 0); + try_return( Status = STATUS_NOT_IMPLEMENTED ); + } + + // + // If this noncached transfer is at least one sector beyond + // the current ValidDataLength in the Fcb, then we have to + // zero the sectors in between. This can happen if the user + // has opened the file noncached, or if the user has mapped + // the file and modified a page beyond ValidDataLength. It + // *cannot* happen if the user opened the file cached, because + // ValidDataLength in the Fcb is updated when he does the cached + // write (we also zero data in the cache at that time), and + // therefore, we will bypass this test when the data + // is ultimately written through (by the Lazy Writer). + // + // For the paging file we don't care about security (ie. + // stale data), do don't bother zeroing. + // + // We can actually get writes wholly beyond valid data length + // from the LazyWriter because of paging Io decoupling. + // + + if (!CalledByLazyWriter && + !RecursiveWriteThrough && + (StartingVbo > ValidDataToCheck)) { + + FatZeroData( IrpContext, + Vcb, + FileObject, + ValidDataToCheck, + StartingVbo - ValidDataToCheck ); + } + + // + // Make sure we write FileSize to the dirent if we + // are extending it and we are successful. (This may or + // may not occur Write Through, but that is fine.) + // + + WriteFileSizeToDirent = TRUE; + + // + // Perform the actual IO + // + + if (SwitchBackToAsync) { + + Wait = FALSE; + ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + } + +#ifdef SYSCACHE_COMPILE + +#define MY_SIZE 0x1000000 +#define LONGMAP_COUNTER + +#ifdef BITMAP + // + // Maintain a bitmap of IO started on this file. + // + + { + PULONG WriteMask = FcbOrDcb->WriteMask; + + if (NULL == WriteMask) { + + WriteMask = FsRtlAllocatePoolWithTag( NonPagedPool, + (MY_SIZE/PAGE_SIZE) / 8, + 'wtaF' ); + + FcbOrDcb->WriteMask = WriteMask; + RtlZeroMemory(WriteMask, (MY_SIZE/PAGE_SIZE) / 8); + } + + if (StartingVbo < MY_SIZE) { + + ULONG Off = StartingVbo; + ULONG Len = BytesToWrite; + + if (Off + Len > MY_SIZE) { + Len = MY_SIZE - Off; + } + + while (Len != 0) { + WriteMask[(Off/PAGE_SIZE) / 32] |= + 1 << (Off/PAGE_SIZE) % 32; + + Off += PAGE_SIZE; + if (Len <= PAGE_SIZE) { + break; + } + Len -= PAGE_SIZE; + } + } + } +#endif + +#ifdef LONGMAP_COUNTER + // + // Maintain a longmap of IO started on this file, each ulong containing + // the value of an ascending counter per write (gives us order information). + // + // Unlike the old bitmask stuff, this is mostly well synchronized. + // + + { + PULONG WriteMask = (PULONG)FcbOrDcb->WriteMask; + + if (NULL == WriteMask) { + + WriteMask = FsRtlAllocatePoolWithTag( NonPagedPool, + (MY_SIZE/PAGE_SIZE) * sizeof(ULONG), + 'wtaF' ); + + FcbOrDcb->WriteMask = WriteMask; + RtlZeroMemory(WriteMask, (MY_SIZE/PAGE_SIZE) * sizeof(ULONG)); + } + + if (StartingVbo < MY_SIZE) { + + ULONG Off = StartingVbo; + ULONG Len = BytesToWrite; + ULONG Tick = InterlockedIncrement( &FcbOrDcb->WriteMaskData ); + + if (Off + Len > MY_SIZE) { + Len = MY_SIZE - Off; + } + + while (Len != 0) { + InterlockedExchange( WriteMask + Off/PAGE_SIZE, Tick ); + + Off += PAGE_SIZE; + if (Len <= PAGE_SIZE) { + break; + } + Len -= PAGE_SIZE; + } + } + } +#endif + +#endif + + if (FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + BytesToWrite, + BytesToWrite ) == STATUS_PENDING) { + + UnwindOutstandingAsync = FALSE; + + Wait = TRUE; + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT ); + + IrpContext->FatIoContext = NULL; + Irp = NULL; + + // + // As a matter of fact, if we hit this we are in deep trouble + // if VDL is being extended. We are no longer attached to the + // IRP, and have thus lost synchronization. Note that we should + // not hit this case anymore since we will not re-async vdl extension. + // + + ASSERT( !ExtendingValidData ); + + try_return( Status = STATUS_PENDING ); + } + + // + // If the call didn't succeed, raise the error status + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + + } else { + + ULONG Temp; + + // + // Else set the context block to reflect the entire write + // Also assert we got how many bytes we asked for. + // + + ASSERT( Irp->IoStatus.Information == BytesToWrite ); + + Irp->IoStatus.Information = ByteCount; + + // + // Take this opportunity to update ValidDataToDisk. + // + + Temp = StartingVbo + BytesToWrite; + + if (FcbOrDcb->ValidDataToDisk < Temp) { + FcbOrDcb->ValidDataToDisk = Temp; + } + } + + // + // The transfer is either complete, or the Iosb contains the + // appropriate status. + // + + try_return( Status ); + + } // if No Intermediate Buffering + + + // + // HANDLE CACHED CASE + // + + else { + + ASSERT( !PagingIo ); + + // + // We delay setting up the file cache until now, in case the + // caller never does any I/O to the file, and thus + // FileObject->PrivateCacheMap == NULL. + // + + if ( FileObject->PrivateCacheMap == NULL ) { + + DebugTrace(0, Dbg, "Initialize cache mapping.\n", 0); + + // + // Get the file allocation size, and if it is less than + // the file size, raise file corrupt error. + // + + if (FcbOrDcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) { + + FatLookupFileAllocationSize( IrpContext, FcbOrDcb ); + } + + if ( FileSize > FcbOrDcb->Header.AllocationSize.LowPart ) { + + FatPopUpFileCorrupt( IrpContext, FcbOrDcb ); + + FatRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Now initialize the cache map. + // + + CcInitializeCacheMap( FileObject, + (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize, + FALSE, + &FatData.CacheManagerCallbacks, + FcbOrDcb ); + + CcSetReadAheadGranularity( FileObject, READ_AHEAD_GRANULARITY ); + + // + // Special case large floppy tranfers, and make the file + // object write through. For small floppy transfers, + // set a timer to go off in a second and flush the file. + // + // + + if (!FlagOn( FileObject->Flags, FO_WRITE_THROUGH ) && + FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH)) { + + if (((StartingByte.LowPart & (PAGE_SIZE-1)) == 0) && + (ByteCount >= PAGE_SIZE)) { + + SetFlag( FileObject->Flags, FO_WRITE_THROUGH ); + + } else { + + LARGE_INTEGER OneSecondFromNow; + PDEFERRED_FLUSH_CONTEXT FlushContext; + + // + // Get pool and initialize the timer and DPC + // + + FlushContext = FsRtlAllocatePoolWithTag( NonPagedPool, + sizeof(DEFERRED_FLUSH_CONTEXT), + TAG_DEFERRED_FLUSH_CONTEXT ); + + KeInitializeTimer( &FlushContext->Timer ); + + KeInitializeDpc( &FlushContext->Dpc, + FatDeferredFlushDpc, + FlushContext ); + + + // + // We have to reference the file object here. + // + + ObReferenceObject( FileObject ); + + FlushContext->File = FileObject; + + // + // Let'er rip! + // + + OneSecondFromNow.QuadPart = (LONG)-1*1000*1000*10; + + KeSetTimer( &FlushContext->Timer, + OneSecondFromNow, + &FlushContext->Dpc ); + } + } + } + + // + // If this write is beyond valid data length, then we + // must zero the data in between. + // + + if ( StartingVbo > ValidDataToCheck ) { + + // + // Call the Cache Manager to zero the data. + // + + if (!FatZeroData( IrpContext, + Vcb, + FileObject, + ValidDataToCheck, + StartingVbo - ValidDataToCheck )) { + + DebugTrace( 0, Dbg, "Cached Write could not wait to zero\n", 0 ); + + try_return( PostIrp = TRUE ); + } + } + + WriteFileSizeToDirent = BooleanFlagOn(IrpContext->Flags, + IRP_CONTEXT_FLAG_WRITE_THROUGH); + + + // + // DO A NORMAL CACHED WRITE, if the MDL bit is not set, + // + + if (!FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) { + + DebugTrace(0, Dbg, "Cached write.\n", 0); + + // + // Get hold of the user's buffer. + // + + SystemBuffer = FatMapUserBuffer( IrpContext, Irp ); + + // + // Do the write, possibly writing through + // + + if (!CcCopyWrite( FileObject, + &StartingByte, + ByteCount, + Wait, + SystemBuffer )) { + + DebugTrace( 0, Dbg, "Cached Write could not wait\n", 0 ); + + try_return( PostIrp = TRUE ); + } + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = ByteCount; + + try_return( Status = STATUS_SUCCESS ); + + } else { + + // + // DO AN MDL WRITE + // + + DebugTrace(0, Dbg, "MDL write.\n", 0); + + ASSERT( Wait ); + + CcPrepareMdlWrite( FileObject, + &StartingByte, + ByteCount, + &Irp->MdlAddress, + &Irp->IoStatus ); + + Status = Irp->IoStatus.Status; + + try_return( Status ); + } + } + } + + // + // These two cases correspond to a system write directory file and + // ea file. + // + + if (( TypeOfOpen == DirectoryFile ) || ( TypeOfOpen == EaFile)) { + + ULONG SectorSize; + + DebugTrace(0, Dbg, "Write Directory or Ea file.\n", 0); + + // + // Make sure the FcbOrDcb is still good + // + + FatVerifyFcb( IrpContext, FcbOrDcb ); + + // + // Synchronize here with people deleting directories and + // mucking with the internals of the EA file. + // + + if (!ExAcquireSharedStarveExclusive( FcbOrDcb->Header.PagingIoResource, + Wait )) { + + DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb ); + + try_return( PostIrp = TRUE ); + } + + PagingIoResourceAcquired = TRUE; + + if (!Wait) { + + IrpContext->FatIoContext->Wait.Async.Resource = + FcbOrDcb->Header.PagingIoResource; + } + + // + // Check to see if we colided with a MoveFile call, and if + // so block until it completes. + // + + if (FcbOrDcb->MoveFileEvent) { + + (VOID)KeWaitForSingleObject( FcbOrDcb->MoveFileEvent, + Executive, + KernelMode, + FALSE, + NULL ); + } + + // + // If we weren't called by the Lazy Writer, then this write + // must be the result of a write-through or flush operation. + // Setting the IrpContext flag, will cause DevIoSup.c to + // write-through the data to the disk. + // + + if (!FlagOn((ULONG_PTR)IoGetTopLevelIrp(), FSRTL_CACHE_TOP_LEVEL_IRP)) { + + SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH ); + } + + // + // For the noncached case, assert that everything is sector + // alligned. + // + + SectorSize = (ULONG)Vcb->Bpb.BytesPerSector; + + // + // We make several assumptions about these two types of files. + // Make sure all of them are true. + // + + ASSERT( NonCachedIo && PagingIo ); + ASSERT( ((StartingVbo | ByteCount) & (SectorSize - 1)) == 0 ); + + // + // These calls must always be within the allocation size, which is + // convienently the same as filesize, which conveniently doesn't + // get reset to a hint value when we verify the volume. + // + + if (StartingVbo >= FcbOrDcb->Header.FileSize.LowPart) { + + DebugTrace( 0, Dbg, "PagingIo dirent started beyond EOF.\n", 0 ); + + Irp->IoStatus.Information = 0; + + try_return( Status = STATUS_SUCCESS ); + } + + if ( StartingVbo + ByteCount > FcbOrDcb->Header.FileSize.LowPart ) { + + DebugTrace( 0, Dbg, "PagingIo dirent extending beyond EOF.\n", 0 ); + ByteCount = FcbOrDcb->Header.FileSize.LowPart - StartingVbo; + } + + // + // Perform the actual IO + // + + if (FatNonCachedIo( IrpContext, + Irp, + FcbOrDcb, + StartingVbo, + ByteCount, + ByteCount ) == STATUS_PENDING) { + + IrpContext->FatIoContext = NULL; + + Irp = NULL; + + try_return( Status = STATUS_PENDING ); + } + + // + // The transfer is either complete, or the Iosb contains the + // appropriate status. + // + // Also, mark the volume as needing verification to automatically + // clean up stuff. + // + + if (!NT_SUCCESS( Status = Irp->IoStatus.Status )) { + + FatNormalizeAndRaiseStatus( IrpContext, Status ); + } + + try_return( Status ); + } + + // + // This is the case of a user who openned a directory. No writing is + // allowed. + // + + if ( TypeOfOpen == UserDirectoryOpen ) { + + DebugTrace( 0, Dbg, "FatCommonWrite -> STATUS_INVALID_PARAMETER\n", 0); + + try_return( Status = STATUS_INVALID_PARAMETER ); + } + + // + // If we get this far, something really serious is wrong. + // + + DebugDump("Illegal TypeOfOpen\n", 0, FcbOrDcb ); + + FatBugCheck( TypeOfOpen, (ULONG_PTR) FcbOrDcb, 0 ); + + try_exit: NOTHING; + + + // + // If the request was not posted and there is still an Irp, + // deal with it. + // + + if (Irp) { + + if ( !PostIrp ) { + + ULONG ActualBytesWrote; + + DebugTrace( 0, Dbg, "Completing request with status = %08lx\n", + Status); + + DebugTrace( 0, Dbg, " Information = %08lx\n", + Irp->IoStatus.Information); + + // + // Record the total number of bytes actually written + // + + ActualBytesWrote = (ULONG)Irp->IoStatus.Information; + + // + // If the file was opened for Synchronous IO, update the current + // file position. + // + + if (SynchronousIo && !PagingIo) { + + FileObject->CurrentByteOffset.LowPart = + StartingVbo + ActualBytesWrote; + } + + // + // The following are things we only do if we were successful + // + + if ( NT_SUCCESS( Status ) ) { + + // + // If this was not PagingIo, mark that the modify + // time on the dirent needs to be updated on close. + // + + if ( !PagingIo ) { + + SetFlag( FileObject->Flags, FO_FILE_MODIFIED ); + } + + // + // If we extended the file size and we are meant to + // immediately update the dirent, do so. (This flag is + // set for either Write Through or noncached, because + // in either case the data and any necessary zeros are + // actually written to the file.) + // + + if ( ExtendingFile && WriteFileSizeToDirent ) { + + ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess ); + + FatSetFileSizeInDirent( IrpContext, FcbOrDcb, NULL ); + + // + // Report that a file size has changed. + // + + FatNotifyReportChange( IrpContext, + Vcb, + FcbOrDcb, + FILE_NOTIFY_CHANGE_SIZE, + FILE_ACTION_MODIFIED ); + } + + if ( ExtendingFile && !WriteFileSizeToDirent ) { + + SetFlag( FileObject->Flags, FO_FILE_SIZE_CHANGED ); + } + + if ( ExtendingValidData ) { + + ULONG EndingVboWritten = StartingVbo + ActualBytesWrote; + + // + // Never set a ValidDataLength greater than FileSize. + // + + if ( FileSize < EndingVboWritten ) { + + FcbOrDcb->Header.ValidDataLength.LowPart = FileSize; + + } else { + + FcbOrDcb->Header.ValidDataLength.LowPart = EndingVboWritten; + } + + // + // Now, if we are noncached and the file is cached, we must + // tell the cache manager about the VDL extension so that + // async cached IO will not be optimized into zero-page faults + // beyond where it believes VDL is. + // + // In the cached case, since Cc did the work, it has updated + // itself already. + // + + if (NonCachedIo && CcIsFileCached(FileObject)) { + CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize ); + } + } + } + + // + // Note that we have to unpin repinned Bcbs here after the above + // work, but if we are going to post the request, we must do this + // before the post (below). + // + + FatUnpinRepinnedBcbs( IrpContext ); + + } else { + + // + // Take action if the Oplock package is not going to post the Irp. + // + + if (!OplockPostIrp) { + + FatUnpinRepinnedBcbs( IrpContext ); + + if ( ExtendingFile ) { + + // + // We need the PagingIo resource exclusive whenever we + // pull back either file size or valid data length. + // + + if ( FcbOrDcb->Header.PagingIoResource != NULL ) { + + (VOID)ExAcquireResourceExclusiveLite(FcbOrDcb->Header.PagingIoResource, TRUE); + } + + FcbOrDcb->Header.FileSize.LowPart = InitialFileSize; + + ASSERT( FcbOrDcb->Header.FileSize.LowPart <= FcbOrDcb->Header.AllocationSize.LowPart ); + + // + // Pull back the cache map as well + // + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { + + *CcGetFileSizePointer(FileObject) = FcbOrDcb->Header.FileSize; + } + + if ( FcbOrDcb->Header.PagingIoResource != NULL ) { + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + } + } + + DebugTrace( 0, Dbg, "Passing request to Fsp\n", 0 ); + + Status = FatFsdPostRequest(IrpContext, Irp); + } + } + } + + } _SEH2_FINALLY { + + DebugUnwind( FatCommonWrite ); + + if (_SEH2_AbnormalTermination()) { + +#ifndef __REACTOS__ + PERESOURCE PagingIoResource = NULL; +#endif + + // + // Restore initial file size and valid data length + // + + if (ExtendingFile || ExtendingValidData) { + + // + // We got an error, pull back the file size if we extended it. + // + // We need the PagingIo resource exclusive whenever we + // pull back either file size or valid data length. + // + + FcbOrDcb->Header.FileSize.LowPart = InitialFileSize; + FcbOrDcb->Header.ValidDataLength.LowPart = InitialValidDataLength; + + ASSERT( FcbOrDcb->Header.FileSize.LowPart <= FcbOrDcb->Header.AllocationSize.LowPart ); + + // + // Pull back the cache map as well + // + + if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) { + + *CcGetFileSizePointer(FileObject) = FcbOrDcb->Header.FileSize; + } + } + } + + // + // Check if this needs to be backed out. + // + + if (UnwindOutstandingAsync) { + + ExInterlockedAddUlong( &FcbOrDcb->NonPaged->OutstandingAsyncWrites, + 0xffffffff, + &FatData.GeneralSpinLock ); + } + + // + // If the FcbOrDcb has been acquired, release it. + // + + if (FcbOrDcbAcquired && Irp) { + + FatReleaseFcb( NULL, FcbOrDcb ); + } + + if (PagingIoResourceAcquired && Irp) { + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + } + + // + // Complete the request if we didn't post it and no exception + // + // Note that FatCompleteRequest does the right thing if either + // IrpContext or Irp are NULL + // + + if ( !PostIrp && !_SEH2_AbnormalTermination() ) { + + FatCompleteRequest( IrpContext, Irp, Status ); + } + + DebugTrace(-1, Dbg, "FatCommonWrite -> %08lx\n", Status ); + } _SEH2_END; + + return Status; +} + + +// +// Local support routine +// + +VOID +NTAPI +FatDeferredFlushDpc ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is dispatched 1 second after a small write to a deferred + write device that initialized the cache map. It exqueues an executive + worker thread to perform the actual task of flushing the file. + +Arguments: + + DeferredContext - Contains the deferred flush context. + +Return Value: + + None. + +--*/ + +{ + PDEFERRED_FLUSH_CONTEXT FlushContext; + + FlushContext = (PDEFERRED_FLUSH_CONTEXT)DeferredContext; + + // + // Send it off + // + + ExInitializeWorkItem( &FlushContext->Item, + FatDeferredFlush, + FlushContext ); + + ExQueueWorkItem( &FlushContext->Item, CriticalWorkQueue ); +} + + +// +// Local support routine +// + +VOID +NTAPI +FatDeferredFlush ( + PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine performs the actual task of flushing the file. + +Arguments: + + DeferredContext - Contains the deferred flush context. + +Return Value: + + None. + +--*/ + +{ + + PFILE_OBJECT File; + PVCB Vcb; + PFCB FcbOrDcb; + PCCB Ccb; + + File = ((PDEFERRED_FLUSH_CONTEXT)Parameter)->File; + + FatDecodeFileObject(File, &Vcb, &FcbOrDcb, &Ccb); + ASSERT( FcbOrDcb != NULL ); + + // + // Make us appear as a top level FSP request so that we will + // receive any errors from the flush. + // + + IoSetTopLevelIrp( (PIRP)FSRTL_FSP_TOP_LEVEL_IRP ); + + ExAcquireResourceSharedLite( FcbOrDcb->Header.Resource, TRUE ); + ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, TRUE ); + + CcFlushCache( File->SectionObjectPointer, NULL, 0, NULL ); + + ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource ); + ExReleaseResourceLite( FcbOrDcb->Header.Resource ); + + IoSetTopLevelIrp( NULL ); + + ObDereferenceObject( File ); + + ExFreePool( Parameter ); + +} + +