https://git.reactos.org/?p=reactos.git;a=commitdiff;h=a913501626e599fba9d702...
commit a913501626e599fba9d702149896f36046832deb Author: Pierre Schweitzer pierre@reactos.org AuthorDate: Thu Nov 23 15:04:05 2017 +0100
[FASTFAT_NEW] This is not permitted by WDK license. We should rather import from MS GitHub and backport to NT5.2. --- drivers/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 | 3647 ------------- 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 | 815 --- 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, 67799 deletions(-)
diff --git a/drivers/filesystems/fastfat_new/CMakeLists.txt b/drivers/filesystems/fastfat_new/CMakeLists.txt deleted file mode 100644 index f21d829e63..0000000000 --- a/drivers/filesystems/fastfat_new/CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ - -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 deleted file mode 100644 index b0c78a81a4..0000000000 --- a/drivers/filesystems/fastfat_new/acchksup.c +++ /dev/null @@ -1,374 +0,0 @@ -/*++ - -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 deleted file mode 100644 index 79a9e7865a..0000000000 --- a/drivers/filesystems/fastfat_new/allocsup.c +++ /dev/null @@ -1,5001 +0,0 @@ -/*++ - -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 - ... 63110 lines suppressed ...