From aeadcaf515a0d2cf6252dc0a6fd454ae86e793ac Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Thu, 23 Nov 2017 12:34:22 +0100 Subject: [PATCH] [FASTFAT] Import the MS FastFAT sample from WXP. Modified it so that it builds in trunk (with GCC, though). Not to be switched for now, as it doesn't work in ReactOS (yet?). --- .../filesystems/fastfat_new/CMakeLists.txt | 44 + drivers/filesystems/fastfat_new/acchksup.c | 374 + drivers/filesystems/fastfat_new/allocsup.c | 5001 ++++++++++++ drivers/filesystems/fastfat_new/cachesup.c | 1814 +++++ drivers/filesystems/fastfat_new/cleanup.c | 1027 +++ drivers/filesystems/fastfat_new/close.c | 1224 +++ drivers/filesystems/fastfat_new/create.c | 5684 ++++++++++++++ drivers/filesystems/fastfat_new/devctrl.c | 308 + drivers/filesystems/fastfat_new/deviosup.c | 3284 ++++++++ drivers/filesystems/fastfat_new/dirctrl.c | 1526 ++++ drivers/filesystems/fastfat_new/dirsup.c | 3643 +++++++++ drivers/filesystems/fastfat_new/dumpsup.c | 381 + drivers/filesystems/fastfat_new/ea.c | 2013 +++++ drivers/filesystems/fastfat_new/easup.c | 3811 ++++++++++ drivers/filesystems/fastfat_new/fastfat.rc | 5 + drivers/filesystems/fastfat_new/fat.h | 729 ++ drivers/filesystems/fastfat_new/fatdata.c | 1424 ++++ drivers/filesystems/fastfat_new/fatdata.h | 328 + drivers/filesystems/fastfat_new/fatinit.c | 675 ++ drivers/filesystems/fastfat_new/fatprocs.h | 2817 +++++++ drivers/filesystems/fastfat_new/fatprocssrc.c | 1 + drivers/filesystems/fastfat_new/fatstruc.h | 1614 ++++ drivers/filesystems/fastfat_new/fileinfo.c | 4635 ++++++++++++ drivers/filesystems/fastfat_new/filobsup.c | 547 ++ drivers/filesystems/fastfat_new/flush.c | 1261 +++ drivers/filesystems/fastfat_new/fsctrl.c | 6726 +++++++++++++++++ drivers/filesystems/fastfat_new/fspdisp.c | 472 ++ drivers/filesystems/fastfat_new/lfn.h | 56 + drivers/filesystems/fastfat_new/lockctrl.c | 726 ++ drivers/filesystems/fastfat_new/namesup.c | 1021 +++ drivers/filesystems/fastfat_new/nodetype.h | 193 + drivers/filesystems/fastfat_new/pnp.c | 811 ++ drivers/filesystems/fastfat_new/read.c | 1649 ++++ drivers/filesystems/fastfat_new/resrcsup.c | 807 ++ drivers/filesystems/fastfat_new/shutdown.c | 304 + drivers/filesystems/fastfat_new/sources | 45 + drivers/filesystems/fastfat_new/splaysup.c | 504 ++ drivers/filesystems/fastfat_new/strucsup.c | 3626 +++++++++ drivers/filesystems/fastfat_new/timesup.c | 364 + drivers/filesystems/fastfat_new/verfysup.c | 1853 +++++ drivers/filesystems/fastfat_new/volinfo.c | 1272 ++++ drivers/filesystems/fastfat_new/workque.c | 362 + drivers/filesystems/fastfat_new/write.c | 2830 +++++++ 43 files changed, 67791 insertions(+) create mode 100644 drivers/filesystems/fastfat_new/CMakeLists.txt create mode 100644 drivers/filesystems/fastfat_new/acchksup.c create mode 100644 drivers/filesystems/fastfat_new/allocsup.c create mode 100644 drivers/filesystems/fastfat_new/cachesup.c create mode 100644 drivers/filesystems/fastfat_new/cleanup.c create mode 100644 drivers/filesystems/fastfat_new/close.c create mode 100644 drivers/filesystems/fastfat_new/create.c create mode 100644 drivers/filesystems/fastfat_new/devctrl.c create mode 100644 drivers/filesystems/fastfat_new/deviosup.c create mode 100644 drivers/filesystems/fastfat_new/dirctrl.c create mode 100644 drivers/filesystems/fastfat_new/dirsup.c create mode 100644 drivers/filesystems/fastfat_new/dumpsup.c create mode 100644 drivers/filesystems/fastfat_new/ea.c create mode 100644 drivers/filesystems/fastfat_new/easup.c create mode 100644 drivers/filesystems/fastfat_new/fastfat.rc create mode 100644 drivers/filesystems/fastfat_new/fat.h create mode 100644 drivers/filesystems/fastfat_new/fatdata.c create mode 100644 drivers/filesystems/fastfat_new/fatdata.h create mode 100644 drivers/filesystems/fastfat_new/fatinit.c create mode 100644 drivers/filesystems/fastfat_new/fatprocs.h create mode 100644 drivers/filesystems/fastfat_new/fatprocssrc.c create mode 100644 drivers/filesystems/fastfat_new/fatstruc.h create mode 100644 drivers/filesystems/fastfat_new/fileinfo.c create mode 100644 drivers/filesystems/fastfat_new/filobsup.c create mode 100644 drivers/filesystems/fastfat_new/flush.c create mode 100644 drivers/filesystems/fastfat_new/fsctrl.c create mode 100644 drivers/filesystems/fastfat_new/fspdisp.c create mode 100644 drivers/filesystems/fastfat_new/lfn.h create mode 100644 drivers/filesystems/fastfat_new/lockctrl.c create mode 100644 drivers/filesystems/fastfat_new/namesup.c create mode 100644 drivers/filesystems/fastfat_new/nodetype.h create mode 100644 drivers/filesystems/fastfat_new/pnp.c create mode 100644 drivers/filesystems/fastfat_new/read.c create mode 100644 drivers/filesystems/fastfat_new/resrcsup.c create mode 100644 drivers/filesystems/fastfat_new/shutdown.c create mode 100644 drivers/filesystems/fastfat_new/sources create mode 100644 drivers/filesystems/fastfat_new/splaysup.c create mode 100644 drivers/filesystems/fastfat_new/strucsup.c create mode 100644 drivers/filesystems/fastfat_new/timesup.c create mode 100644 drivers/filesystems/fastfat_new/verfysup.c create mode 100644 drivers/filesystems/fastfat_new/volinfo.c create mode 100644 drivers/filesystems/fastfat_new/workque.c create mode 100644 drivers/filesystems/fastfat_new/write.c 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 ); + +} + +