mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3402 lines
95 KiB
3402 lines
95 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
AllocSup.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the Allocation support routines for Rx.
|
|
|
|
Author:
|
|
|
|
DavidGoebel [DavidGoe] 31-Oct-90
|
|
|
|
Revision History:
|
|
|
|
DavidGoebel [DavidGoe] 31-Oct-90
|
|
|
|
Add unwinding support. Some steps had to be reordered, and whether
|
|
operations cpuld fail carefully considered. In particular, attention
|
|
was paid to to the order of Mcb operations (see note below).
|
|
|
|
|
|
##### ## # # #### ###### #####
|
|
# # # # ## # # # # # #
|
|
# # # # # # # # ##### # #
|
|
# # ###### # # # # ### # #####
|
|
# # # # # ## # # # # #
|
|
##### # # # # #### ###### # #
|
|
______________________________________________
|
|
|
|
|
|
++++++++++++++++++++++++++++++++++++++++++++++++++|
|
|
| |
|
|
| The unwinding aspects of this module depend on |
|
|
| operational details of the Mcb package. Do not |
|
|
| attempt to modify unwind procedures without |
|
|
| thoughoughly understanding the innerworkings of |
|
|
| the Mcb package. |
|
|
| |
|
|
++++++++++++++++++++++++++++++++++++++++++++++++++|
|
|
|
|
|
|
# # ## ##### # # # # # ####
|
|
# # # # # # ## # # ## # # #
|
|
# # # # # # # # # # # # # #
|
|
# ## # ###### ##### # # # # # # # # ###
|
|
## ## # # # # # ## # # ## # #
|
|
# # # # # # # # # # # ####
|
|
______________________________________________________
|
|
--*/
|
|
|
|
// ----------------------joejoe-----------found-------------#include "RxProcs.h"
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// The Bug check file id for this module
|
|
//
|
|
|
|
#define BugCheckFileId (RDBSS_BUG_CHECK_ALLOCSUP)
|
|
|
|
//
|
|
// Local debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_ALLOCSUP)
|
|
|
|
//
|
|
// Cluster/Index routines implemented in AllocSup.c
|
|
//
|
|
|
|
typedef enum _CLUSTER_TYPE {
|
|
RxClusterAvailable,
|
|
RxClusterReserved,
|
|
RxClusterBad,
|
|
RxClusterLast,
|
|
RxClusterNext
|
|
} CLUSTER_TYPE;
|
|
|
|
//
|
|
// This strucure is used by RxLookupRxEntry to remember a pinned page
|
|
// of rx.
|
|
//
|
|
|
|
typedef struct _RDBSS_ENUMERATION_CONTEXT {
|
|
|
|
VBO VboOfPinnedPage;
|
|
PBCB Bcb;
|
|
PVOID PinnedPage;
|
|
|
|
} RDBSS_ENUMERATION_CONTEXT, *PRDBSS_ENUMERATION_CONTEXT;
|
|
|
|
//
|
|
// Local support routine prototypes
|
|
//
|
|
|
|
CLUSTER_TYPE
|
|
RxInterpretClusterType (
|
|
IN PVCB Vcb,
|
|
IN RDBSS_ENTRY Entry
|
|
);
|
|
|
|
VOID
|
|
RxLookupRxEntry(
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN ULONG RxIndex,
|
|
IN OUT PRDBSS_ENTRY RxEntry,
|
|
IN OUT PRDBSS_ENUMERATION_CONTEXT Context
|
|
);
|
|
|
|
VOID
|
|
RxSetRxEntry(
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN ULONG RxIndex,
|
|
IN RDBSS_ENTRY RxEntry
|
|
);
|
|
|
|
VOID
|
|
RxSetRxRun(
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN ULONG StartingRxIndex,
|
|
IN ULONG ClusterCount,
|
|
IN BOOLEAN ChainTogether
|
|
);
|
|
|
|
UCHAR
|
|
RxLogOf(
|
|
IN ULONG Value
|
|
);
|
|
|
|
|
|
//
|
|
// The following macros provide a convenient way of hiding the details
|
|
// of bitmap allocation schemes.
|
|
//
|
|
|
|
|
|
//
|
|
// VOID
|
|
// RxLockFreeClusterBitMap (
|
|
// IN PVCB Vcb
|
|
// );
|
|
//
|
|
|
|
#define RxLockFreeClusterBitMap(VCB) { \
|
|
RXSTATUS Status; \
|
|
Status = KeWaitForSingleObject( &(VCB)->FreeClusterBitMapEvent, \
|
|
Executive, \
|
|
KernelMode, \
|
|
FALSE, \
|
|
(PLARGE_INTEGER) NULL ); \
|
|
ASSERT( NT_SUCCESS( Status ) ); \
|
|
}
|
|
|
|
//
|
|
// VOID
|
|
// RxUnlockFreeClusterBitMap (
|
|
// IN PVCB Vcb
|
|
// );
|
|
//
|
|
|
|
#define RxUnlockFreeClusterBitMap(VCB) { \
|
|
ULONG PreviousState; \
|
|
PreviousState = KeSetEvent( &(VCB)->FreeClusterBitMapEvent, 0, FALSE ); \
|
|
ASSERT( PreviousState == 0 ); \
|
|
}
|
|
|
|
//
|
|
// BOOLEAN
|
|
// RxIsClusterFree (
|
|
// IN PRX_CONTEXT RxContext,
|
|
// IN PVCB Vcb,
|
|
// IN ULONG RxIndex
|
|
// );
|
|
//
|
|
|
|
#define RxIsClusterFree(RXCONTEXT,VCB,RDBSS_INDEX) \
|
|
\
|
|
(RtlCheckBit(&(VCB)->FreeClusterBitMap,(RDBSS_INDEX)) == 0)
|
|
|
|
//
|
|
// BOOLEAN
|
|
// RxIsClusterAllocated (
|
|
// IN PRX_CONTEXT RxContext,
|
|
// IN PVCB Vcb,
|
|
// IN ULONG RxIndex
|
|
// );
|
|
//
|
|
|
|
#define RxIsClusterAllocated(RXCONTEXT,VCB,RDBSS_INDEX) \
|
|
\
|
|
(RtlCheckBit(&(VCB)->FreeClusterBitMap,(RDBSS_INDEX)) != 0)
|
|
|
|
//
|
|
// VOID
|
|
// RxFreeClusters (
|
|
// IN PRX_CONTEXT RxContext,
|
|
// IN PVCB Vcb,
|
|
// IN ULONG RxIndex,
|
|
// IN ULONG ClusterCount
|
|
// );
|
|
//
|
|
|
|
#ifdef DOUBLE_SPACE_WRITE
|
|
|
|
#define RxFreeClusters(RXCONTEXT,VCB,RDBSS_INDEX,CLUSTER_COUNT) { \
|
|
\
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 0 ) == 1 ); \
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 1 ) == 1 ); \
|
|
\
|
|
DebugTrace( 0, Dbg, "Free clusters (Index<<16 | Count) (%8lx)\n", \
|
|
(RDBSS_INDEX)<<16 | (CLUSTER_COUNT)); \
|
|
if ((CLUSTER_COUNT) == 1) { \
|
|
RxSetRxEntry((RXCONTEXT),(VCB),(RDBSS_INDEX),RDBSS_CLUSTER_AVAILABLE); \
|
|
} else { \
|
|
RxSetRxRun((RXCONTEXT),(VCB),(RDBSS_INDEX),(CLUSTER_COUNT),FALSE); \
|
|
} \
|
|
if ((VCB)->Dscb != NULL) { \
|
|
RxDblsDeallocateClusters((RXCONTEXT),(VCB)->Dscb,(RDBSS_INDEX),(CLUSTER_COUNT)); \
|
|
} \
|
|
}
|
|
|
|
#else
|
|
|
|
#define RxFreeClusters(RXCONTEXT,VCB,RDBSS_INDEX,CLUSTER_COUNT) { \
|
|
\
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 0 ) == 1 ); \
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 1 ) == 1 ); \
|
|
\
|
|
DebugTrace( 0, Dbg, "Free clusters (Index<<16 | Count) (%8lx)\n", \
|
|
(RDBSS_INDEX)<<16 | (CLUSTER_COUNT)); \
|
|
if ((CLUSTER_COUNT) == 1) { \
|
|
RxSetRxEntry((RXCONTEXT),(VCB),(RDBSS_INDEX),RDBSS_CLUSTER_AVAILABLE); \
|
|
} else { \
|
|
RxSetRxRun((RXCONTEXT),(VCB),(RDBSS_INDEX),(CLUSTER_COUNT),FALSE); \
|
|
} \
|
|
}
|
|
|
|
#endif // DOUBLE_SPACE_WRITE
|
|
|
|
//
|
|
// VOID
|
|
// RxAllocateClusters (
|
|
// IN PRX_CONTEXT RxContext,
|
|
// IN PVCB Vcb,
|
|
// IN ULONG RxIndex,
|
|
// IN ULONG ClusterCount
|
|
// );
|
|
//
|
|
|
|
#define RxAllocateClusters(RXCONTEXT,VCB,RDBSS_INDEX,CLUSTER_COUNT) { \
|
|
\
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 0 ) == 1 ); \
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 1 ) == 1 ); \
|
|
\
|
|
DebugTrace( 0, Dbg, "Allocate clusters (Index<<16 | Count) (%8lx)\n", \
|
|
(RDBSS_INDEX)<<16 | (CLUSTER_COUNT)); \
|
|
if ((CLUSTER_COUNT) == 1) { \
|
|
RxSetRxEntry((RXCONTEXT),(VCB),(RDBSS_INDEX),RDBSS_CLUSTER_LAST); \
|
|
} else { \
|
|
RxSetRxRun((RXCONTEXT),(VCB),(RDBSS_INDEX),(CLUSTER_COUNT),TRUE); \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// VOID
|
|
// RxUnreserveClusters (
|
|
// IN PRX_CONTEXT RxContext,
|
|
// IN PVCB Vcb,
|
|
// IN ULONG RxIndex,
|
|
// IN ULONG ClusterCount
|
|
// );
|
|
//
|
|
|
|
#define RxUnreserveClusters(RXCONTEXT,VCB,RDBSS_INDEX,CLUSTER_COUNT) { \
|
|
\
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 0 ) == 1 ); \
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 1 ) == 1 ); \
|
|
\
|
|
RtlClearBits(&(VCB)->FreeClusterBitMap,(RDBSS_INDEX),(CLUSTER_COUNT)); \
|
|
}
|
|
|
|
//
|
|
// VOID
|
|
// RxReserveClusters (
|
|
// IN PRX_CONTEXT RxContext,
|
|
// IN PVCB Vcb,
|
|
// IN ULONG RxIndex,
|
|
// IN ULONG ClusterCount
|
|
// );
|
|
//
|
|
|
|
#define RxReserveClusters(RXCONTEXT,VCB,RDBSS_INDEX,CLUSTER_COUNT) { \
|
|
\
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 0 ) == 1 ); \
|
|
ASSERTMSG("RxFreeClusters ", RtlCheckBit( &(VCB)->FreeClusterBitMap, 1 ) == 1 ); \
|
|
\
|
|
RtlSetBits(&(VCB)->FreeClusterBitMap,(RDBSS_INDEX),(CLUSTER_COUNT)); \
|
|
}
|
|
|
|
//
|
|
// ULONG
|
|
// RxFindFreeClusterRun (
|
|
// IN PRX_CONTEXT RxContext,
|
|
// IN PVCB Vcb,
|
|
// IN ULONG ClusterCount,
|
|
// IN PULONG AlternateClusterHint
|
|
// );
|
|
//
|
|
|
|
#define RxFindFreeClusterRun(RXCONTEXT,VCB,CLUSTER_COUNT,CLUSTER_HINT) \
|
|
\
|
|
RtlFindClearBits( &(VCB)->FreeClusterBitMap, \
|
|
(CLUSTER_COUNT), \
|
|
((CLUSTER_HINT) != 0)?(CLUSTER_HINT):(VCB)->ClusterHint )
|
|
|
|
//
|
|
// ULONG
|
|
// RxLongestFreeClusterRun (
|
|
// IN PRX_CONTEXT RxContext,
|
|
// IN PVCB Vcb,
|
|
// IN PULONG RxIndex,
|
|
// );
|
|
//
|
|
|
|
#define RxLongestFreeClusterRun(RXCONTEXT,VCB,RDBSS_INDEX) \
|
|
\
|
|
RtlFindLongestRunClear(&(VCB)->FreeClusterBitMap,(RDBSS_INDEX))
|
|
|
|
#if DBG
|
|
extern KSPIN_LOCK VWRSpinLock;
|
|
#endif
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, RxLookupFileAllocation)
|
|
#pragma alloc_text(PAGE, RxAddFileAllocation)
|
|
#pragma alloc_text(PAGE, RxAllocateDiskSpace)
|
|
#pragma alloc_text(PAGE, RxDeallocateDiskSpace)
|
|
#pragma alloc_text(PAGE, RxInterpretClusterType)
|
|
#pragma alloc_text(PAGE, RxLogOf)
|
|
#pragma alloc_text(PAGE, RxLookupRxEntry)
|
|
#pragma alloc_text(PAGE, RxLookupFileAllocationSize)
|
|
#pragma alloc_text(PAGE, RxMergeAllocation)
|
|
#pragma alloc_text(PAGE, RxSetRxEntry)
|
|
#pragma alloc_text(PAGE, RxSetRxRun)
|
|
#pragma alloc_text(PAGE, RxSetupAllocationSupport)
|
|
#pragma alloc_text(PAGE, RxSplitAllocation)
|
|
#pragma alloc_text(PAGE, RxTearDownAllocationSupport)
|
|
#pragma alloc_text(PAGE, RxTruncateFileAllocation)
|
|
#endif
|
|
|
|
|
|
|
|
VOID
|
|
RxSetupAllocationSupport (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in the Allocation Support structure in the Vcb.
|
|
Most entries are computed using rx.h macros supplied with data from
|
|
the Bios Parameter Block. The free cluster count, however, requires
|
|
going to the Rx and actually counting free sectors. At the same time
|
|
the free cluster bit map is initalized.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb to fill in.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG BitMapSize;
|
|
PVOID BitMapBuffer;
|
|
|
|
PBCB (*SavedBcbs)[2] = NULL;
|
|
|
|
PBCB Bcbs[2][2];
|
|
|
|
DebugTrace(+1, Dbg, "RxSetupAllocationSupport\n", 0);
|
|
DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb);
|
|
|
|
//
|
|
// Compute a number of fields for Vcb.AllocationSupport
|
|
//
|
|
|
|
Vcb->AllocationSupport.RootDirectoryLbo = RxRootDirectoryLbo( &Vcb->Bpb );
|
|
Vcb->AllocationSupport.RootDirectorySize = RxRootDirectorySize( &Vcb->Bpb );
|
|
|
|
Vcb->AllocationSupport.FileAreaLbo = RxFileAreaLbo( &Vcb->Bpb );
|
|
|
|
Vcb->AllocationSupport.NumberOfClusters = RxNumberOfClusters( &Vcb->Bpb );
|
|
|
|
Vcb->AllocationSupport.RxIndexBitSize = RxIndexBitSize( &Vcb->Bpb );
|
|
|
|
Vcb->AllocationSupport.LogOfBytesPerSector = RxLogOf(Vcb->Bpb.BytesPerSector);
|
|
Vcb->AllocationSupport.LogOfBytesPerCluster = RxLogOf(
|
|
RxBytesPerCluster( &Vcb->Bpb ));
|
|
Vcb->AllocationSupport.NumberOfFreeClusters = 0;
|
|
|
|
//
|
|
// Deal with a bug in DOS 5 format, if the Rx is not big enough to
|
|
// describe all the clusters on the disk, reduce this number.
|
|
//
|
|
|
|
{
|
|
ULONG ClustersDescribableByRx;
|
|
|
|
ClustersDescribableByRx = ( (Vcb->Bpb.SectorsPerRx *
|
|
Vcb->Bpb.BytesPerSector * 8)
|
|
/ RxIndexBitSize(&Vcb->Bpb) ) - 2;
|
|
|
|
if (Vcb->AllocationSupport.NumberOfClusters > ClustersDescribableByRx) {
|
|
|
|
KdPrint(("FASTRDBSS: Mounting wierd volume!\n"));
|
|
|
|
Vcb->AllocationSupport.NumberOfClusters = ClustersDescribableByRx;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Extend the virtual volume file to include the Rx
|
|
//
|
|
|
|
{
|
|
CC_FILE_SIZES FileSizes;
|
|
|
|
FileSizes.AllocationSize.QuadPart =
|
|
FileSizes.FileSize.QuadPart =
|
|
RxReservedBytes( &Vcb->Bpb ) + RxBytesPerRx( &Vcb->Bpb );
|
|
FileSizes.ValidDataLength = RxMaxLarge;
|
|
|
|
if ( Vcb->VirtualVolumeFile->PrivateCacheMap == NULL ) {
|
|
|
|
CcInitializeCacheMap( Vcb->VirtualVolumeFile,
|
|
&FileSizes,
|
|
TRUE,
|
|
&RxData.CacheManagerNoOpCallbacks,
|
|
Vcb );
|
|
|
|
} else {
|
|
|
|
CcSetFileSizes( Vcb->VirtualVolumeFile, &FileSizes );
|
|
}
|
|
}
|
|
|
|
try {
|
|
|
|
//
|
|
// Initialize the free cluster BitMap. The number of bits is the
|
|
// number of clusters plus the two reserved entries. Note that
|
|
// FsRtlAllocatePool will always allocate me something longword alligned.
|
|
//
|
|
|
|
BitMapSize = Vcb->AllocationSupport.NumberOfClusters + 2;
|
|
|
|
BitMapBuffer = FsRtlAllocatePool( PagedPool, (BitMapSize + 7) / 8 );
|
|
|
|
RtlInitializeBitMap( &Vcb->FreeClusterBitMap,
|
|
(PULONG)BitMapBuffer,
|
|
BitMapSize );
|
|
|
|
//
|
|
// Read the rx and count up free clusters.
|
|
//
|
|
// Rather than just reading rx entries one at a time, a faster
|
|
// approach is used. The entire Rx is read in and and we read
|
|
// through it, keeping track of runs of free and runs of allocated
|
|
// clusters. When we switch from free to aloocated or visa versa,
|
|
// the previous run is marked in the bit map.
|
|
//
|
|
|
|
{
|
|
ULONG Page;
|
|
ULONG RxIndex;
|
|
RDBSS_ENTRY RxEntry;
|
|
PRDBSS_ENTRY RxBuffer;
|
|
|
|
ULONG ClustersThisRun;
|
|
ULONG RxIndexBitSize;
|
|
ULONG StartIndexOfThisRun;
|
|
PULONG FreeClusterCount;
|
|
|
|
enum RunType {
|
|
FreeClusters,
|
|
AllocatedClusters
|
|
} CurrentRun;
|
|
|
|
//
|
|
// Keep local copies of these variables around for speed.
|
|
//
|
|
|
|
FreeClusterCount = &Vcb->AllocationSupport.NumberOfFreeClusters;
|
|
RxIndexBitSize = Vcb->AllocationSupport.RxIndexBitSize;
|
|
|
|
//
|
|
// Read in one page of rx at a time. We cannot read in the
|
|
// all of the rx we need because of cache manager limitations.
|
|
//
|
|
// SavedBcb was initialized to be able to hold the largest
|
|
// possible number of pages in a rx 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.
|
|
//
|
|
|
|
if ( RxIndexBitSize == 16 ) {
|
|
|
|
ULONG NumberOfPages;
|
|
ULONG Offset;
|
|
|
|
NumberOfPages = ( RxReservedBytes(&Vcb->Bpb) +
|
|
RxBytesPerRx(&Vcb->Bpb) +
|
|
(PAGE_SIZE - 1) ) / PAGE_SIZE;
|
|
|
|
//
|
|
// Figure out how much memory we will need for the Bcb
|
|
// buffer and fill it in.
|
|
//
|
|
|
|
SavedBcbs = FsRtlAllocatePool( PagedPool,
|
|
(NumberOfPages + 1) * sizeof(PBCB) * 2 );
|
|
|
|
RtlZeroMemory( &SavedBcbs[0][0], (NumberOfPages + 1) * sizeof(PBCB) * 2 );
|
|
|
|
for ( Page = 0, Offset = 0;
|
|
Page < NumberOfPages;
|
|
Page++, Offset += PAGE_SIZE ) {
|
|
|
|
RxReadVolumeFile( RxContext,
|
|
Vcb,
|
|
Offset,
|
|
PAGE_SIZE,
|
|
&SavedBcbs[Page][0],
|
|
(PVOID *)&SavedBcbs[Page][1] );
|
|
}
|
|
|
|
Page = RxReservedBytes(&Vcb->Bpb) / PAGE_SIZE;
|
|
|
|
RxBuffer = (PRDBSS_ENTRY)((PUCHAR)SavedBcbs[Page][1] +
|
|
RxReservedBytes(&Vcb->Bpb) % PAGE_SIZE) + 2;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We read in the entire rx in the 12 bit case.
|
|
//
|
|
|
|
SavedBcbs = Bcbs;
|
|
|
|
RtlZeroMemory( &SavedBcbs[0][0], 2 * sizeof(PBCB) * 2);
|
|
|
|
RxReadVolumeFile( RxContext,
|
|
Vcb,
|
|
RxReservedBytes( &Vcb->Bpb ),
|
|
RxBytesPerRx( &Vcb->Bpb ),
|
|
&SavedBcbs[0][0],
|
|
(PVOID *)&RxBuffer );
|
|
}
|
|
|
|
//
|
|
// For a rx, we know the first two clusters are allways
|
|
// reserved. So start an allocated run.
|
|
//
|
|
|
|
CurrentRun = AllocatedClusters;
|
|
StartIndexOfThisRun = 0;
|
|
|
|
for (RxIndex = 2; RxIndex < BitMapSize; RxIndex++) {
|
|
|
|
if (RxIndexBitSize == 12) {
|
|
|
|
RxLookup12BitEntry(RxBuffer, RxIndex, &RxEntry);
|
|
|
|
} else {
|
|
|
|
//
|
|
// If we just stepped onto a new page, grab a new pointer.
|
|
//
|
|
|
|
if (((ULONG)RxBuffer & (PAGE_SIZE - 1)) == 0) {
|
|
|
|
Page++;
|
|
|
|
RxBuffer = (PRDBSS_ENTRY)SavedBcbs[Page][1];
|
|
}
|
|
|
|
RxEntry = *RxBuffer;
|
|
|
|
RxBuffer += 1;
|
|
}
|
|
|
|
//
|
|
// Are we switching from a free run to an allocated run?
|
|
//
|
|
|
|
if ((CurrentRun == FreeClusters) &&
|
|
(RxEntry != RDBSS_CLUSTER_AVAILABLE)) {
|
|
|
|
ClustersThisRun = RxIndex - StartIndexOfThisRun;
|
|
|
|
*FreeClusterCount += ClustersThisRun;
|
|
|
|
RtlClearBits( &Vcb->FreeClusterBitMap,
|
|
StartIndexOfThisRun,
|
|
ClustersThisRun );
|
|
|
|
CurrentRun = AllocatedClusters;
|
|
StartIndexOfThisRun = RxIndex;
|
|
}
|
|
|
|
//
|
|
// Are we switching from an allocated run to a free run?
|
|
//
|
|
|
|
if ((CurrentRun == AllocatedClusters) &&
|
|
(RxEntry == RDBSS_CLUSTER_AVAILABLE)) {
|
|
|
|
ClustersThisRun = RxIndex - StartIndexOfThisRun;
|
|
|
|
RtlSetBits( &Vcb->FreeClusterBitMap,
|
|
StartIndexOfThisRun,
|
|
ClustersThisRun );
|
|
|
|
CurrentRun = FreeClusters;
|
|
StartIndexOfThisRun = RxIndex;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we have to record the final run we encoutered
|
|
//
|
|
|
|
ClustersThisRun = RxIndex - StartIndexOfThisRun;
|
|
|
|
if ( CurrentRun == FreeClusters ) {
|
|
|
|
*FreeClusterCount += ClustersThisRun;
|
|
|
|
RtlClearBits( &Vcb->FreeClusterBitMap,
|
|
StartIndexOfThisRun,
|
|
ClustersThisRun );
|
|
|
|
} else {
|
|
|
|
RtlSetBits( &Vcb->FreeClusterBitMap,
|
|
StartIndexOfThisRun,
|
|
ClustersThisRun );
|
|
}
|
|
}
|
|
|
|
ASSERT( RtlCheckBit( &Vcb->FreeClusterBitMap, 0 ) == 1 );
|
|
ASSERT( RtlCheckBit( &Vcb->FreeClusterBitMap, 1 ) == 1 );
|
|
|
|
} finally {
|
|
|
|
ULONG i = 0;
|
|
|
|
DebugUnwind( RxSetupAllocationSupport );
|
|
|
|
//
|
|
// If we hit an exception, back out.
|
|
//
|
|
|
|
if (AbnormalTermination()) {
|
|
|
|
RxTearDownAllocationSupport( RxContext, Vcb );
|
|
}
|
|
|
|
//
|
|
// We are done reading the Rx, so unpin the Bcbs.
|
|
//
|
|
|
|
if (SavedBcbs != NULL) {
|
|
|
|
while ( SavedBcbs[i][0] != NULL ) {
|
|
|
|
RxUnpinBcb( RxContext, SavedBcbs[i][0] );
|
|
|
|
i += 1;
|
|
}
|
|
|
|
if (SavedBcbs != Bcbs) {
|
|
|
|
ExFreePool( SavedBcbs );
|
|
}
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "RxSetupAllocationSupport -> (VOID)\n", 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RxTearDownAllocationSupport (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prepares the volume for closing. Specifically, we must
|
|
release the free rx bit map buffer, and uninitialize the dirty rx
|
|
Mcb.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb to fill in.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
DebugTrace(+1, Dbg, "RxTearDownAllocationSupport\n", 0);
|
|
DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb);
|
|
|
|
//
|
|
// 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 rx Mcb
|
|
//
|
|
|
|
FsRtlRemoveMcbEntry( &Vcb->DirtyRxMcb, 0, 0xFFFFFFFF );
|
|
|
|
DebugTrace(-1, Dbg, "RxTearDownAllocationSupport -> (VOID)\n", 0);
|
|
|
|
UNREFERENCED_PARAMETER( RxContext );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RxLookupFileAllocation (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PFCB FcbOrDcb,
|
|
IN VBO Vbo,
|
|
OUT PLBO Lbo,
|
|
OUT PULONG ByteCount,
|
|
OUT PBOOLEAN Allocated,
|
|
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 rx 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.
|
|
|
|
Index - Receives the Index of the run
|
|
|
|
--*/
|
|
|
|
{
|
|
VBO CurrentVbo;
|
|
LBO CurrentLbo;
|
|
LBO PriorLbo;
|
|
|
|
VBO FirstVboOfCurrentRun;
|
|
LBO FirstLboOfCurrentRun;
|
|
|
|
BOOLEAN LastCluster;
|
|
ULONG Runs;
|
|
|
|
PVCB Vcb;
|
|
RDBSS_ENTRY RxEntry;
|
|
ULONG BytesPerCluster;
|
|
ULONG BytesOnVolume;
|
|
|
|
RDBSS_ENUMERATION_CONTEXT Context;
|
|
|
|
DebugTrace(+1, Dbg, "RxLookupFileAllocation\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;
|
|
|
|
//
|
|
// Check the trivial case that the mapping is already in our
|
|
// Mcb.
|
|
//
|
|
|
|
if ( FsRtlLookupMcbEntry(&FcbOrDcb->Mcb, Vbo, Lbo, ByteCount, Index) ) {
|
|
|
|
*Allocated = TRUE;
|
|
|
|
DebugTrace( 0, Dbg, "Found run in Mcb.\n", 0);
|
|
DebugTrace(-1, Dbg, "RxLookupFileAllocation -> (VOID)\n", 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialize the Vcb, the cluster size, LastCluster, and
|
|
// FirstLboOfCurrentRun (to be used as an indication of the first
|
|
// itteration through the following while loop).
|
|
//
|
|
|
|
Vcb = FcbOrDcb->Vcb;
|
|
BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
|
BytesOnVolume = 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, "RxLookupFileAllocation -> (VOID)\n", 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// The Vbo is beyond the last Mcb entry. So we adjust Current Vbo/Lbo
|
|
// and RxEntry 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 (FsRtlLookupLastMcbEntry(&FcbOrDcb->Mcb, &CurrentVbo, &CurrentLbo)) {
|
|
|
|
DebugTrace( 0, Dbg, "Current Mcb size = %8lx.\n", CurrentVbo + 1);
|
|
|
|
CurrentVbo -= (BytesPerCluster - 1);
|
|
CurrentLbo -= (BytesPerCluster - 1);
|
|
|
|
Runs = FsRtlNumberOfRunsInMcb( &FcbOrDcb->Mcb );
|
|
|
|
} 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, "RxLookupFileAllocation -> (VOID)\n", 0);
|
|
return;
|
|
|
|
} else {
|
|
|
|
CurrentVbo = 0;
|
|
CurrentLbo = RxGetLboFromIndex( 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 Rx, and update
|
|
// the Mcb as we go. We use a try-finally to unpin the page
|
|
// of rx hanging around. Also we mark *Allocated = FALSE, so that
|
|
// the caller wont try to use the data if we hit an exception.
|
|
//
|
|
|
|
*Allocated = FALSE;
|
|
|
|
try {
|
|
|
|
RxEntry = (RDBSS_ENTRY)RxGetIndexFromLbo( 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 Rx looking for our Vbo. We continue through the Rx until we
|
|
// hit a noncontiguity beyond the desired Vbo, or the last cluster.
|
|
//
|
|
|
|
while ( !LastCluster ) {
|
|
|
|
//
|
|
// Get the next rx entry, and update our Current variables.
|
|
//
|
|
|
|
RxLookupRxEntry( RxContext, Vcb, RxEntry, &RxEntry, &Context );
|
|
|
|
PriorLbo = CurrentLbo;
|
|
CurrentLbo = RxGetLboFromIndex( Vcb, RxEntry );
|
|
CurrentVbo += BytesPerCluster;
|
|
|
|
switch ( RxInterpretClusterType( Vcb, RxEntry )) {
|
|
|
|
//
|
|
// Check for a break in the Rx allocation chain.
|
|
//
|
|
|
|
case RxClusterAvailable:
|
|
case RxClusterReserved:
|
|
case RxClusterBad:
|
|
|
|
DebugTrace( 0, Dbg, "Break in allocation chain, entry = %d\n", RxEntry);
|
|
DebugTrace(-1, Dbg, "RxLookupFileAllocation -> Rx Corrupt. Raise Status.\n", 0);
|
|
|
|
RxPopUpFileCorrupt( RxContext, FcbOrDcb );
|
|
|
|
RxRaiseStatus( RxContext, RxStatus(FILE_CORRUPT_ERROR) );
|
|
break;
|
|
|
|
//
|
|
// If this is the last cluster, we must update the Mcb and
|
|
// exit the loop.
|
|
//
|
|
|
|
case RxClusterLast:
|
|
|
|
//
|
|
// 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
|
|
// itteration through the loop. Thus if FirstLboOfCurrentRun
|
|
// is 0, then there was an Mcb entry and we are on our first
|
|
// itteration, 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);
|
|
|
|
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)FsRtlAddMcbEntry( &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 RxLookupFileAllocationSize
|
|
// Vbo is set to MAXULONG - 1, and AllocationSize to MAXULONG.
|
|
// 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 >= CurrentVbo ) {
|
|
|
|
FcbOrDcb->Header.AllocationSize.QuadPart = CurrentVbo;
|
|
*Allocated = FALSE;
|
|
|
|
DebugTrace( 0, Dbg, "New file allocation size = %08lx.\n", CurrentVbo);
|
|
|
|
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 RxClusterNext:
|
|
|
|
//
|
|
// Do a quick check here for cycles in that Rx that can
|
|
// infinite loops here.
|
|
//
|
|
|
|
if ( CurrentVbo > BytesOnVolume ) {
|
|
|
|
RxPopUpFileCorrupt( RxContext, FcbOrDcb );
|
|
|
|
RxRaiseStatus( RxContext, RxStatus(FILE_CORRUPT_ERROR) );
|
|
break;
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
FsRtlAddMcbEntry( &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", RxEntry);
|
|
|
|
RxBugCheck( 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)) {
|
|
|
|
*Index = Runs - 1;
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxLookupFileAllocation );
|
|
|
|
//
|
|
// We are done reading the Rx, so unpin the last page of rx
|
|
// that is hanging around
|
|
//
|
|
|
|
RxUnpinBcb( RxContext, Context.Bcb );
|
|
|
|
DebugTrace(-1, Dbg, "RxLookupFileAllocation -> (VOID)\n", 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RxAddFileAllocation (
|
|
IN PRX_CONTEXT RxContext,
|
|
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;
|
|
|
|
DebugTrace(+1, Dbg, "RxAddFileAllocation\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.LowPart == 0xffffffff) {
|
|
|
|
RxLookupFileAllocationSize( RxContext, 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, "RxAddFileAllocation -> (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;
|
|
|
|
if (FcbOrDcb->Header.AllocationSize.LowPart == 0) {
|
|
|
|
PBCB Bcb = NULL;
|
|
PDIRENT Dirent;
|
|
LBO FirstLboOfFile;
|
|
BOOLEAN UnwindWeAllocatedDiskSpace = FALSE;
|
|
|
|
try {
|
|
|
|
RxGetDirentFromFcbOrDcb( RxContext,
|
|
FcbOrDcb,
|
|
&Dirent,
|
|
&Bcb );
|
|
|
|
//
|
|
// Set this dirty right now since this call can fail.
|
|
//
|
|
|
|
RxSetDirtyBcb( RxContext, Bcb, Vcb );
|
|
|
|
|
|
RxAllocateDiskSpace( RxContext,
|
|
Vcb,
|
|
0,
|
|
&DesiredAllocationSize,
|
|
&FcbOrDcb->Mcb );
|
|
|
|
UnwindWeAllocatedDiskSpace = TRUE;
|
|
|
|
//
|
|
// We have to update the dirent and FcbOrDcb copies of
|
|
// FirstClusterOfFile since before it was 0
|
|
//
|
|
|
|
FsRtlLookupMcbEntry( &FcbOrDcb->Mcb,
|
|
0,
|
|
&FirstLboOfFile,
|
|
(PULONG)NULL,
|
|
NULL );
|
|
|
|
DebugTrace( 0, Dbg, "First Lbo of file will be %08lx.\n", FirstLboOfFile );
|
|
|
|
FcbOrDcb->FirstClusterOfFile = RxGetIndexFromLbo( Vcb, FirstLboOfFile );
|
|
|
|
FcbOrDcb->Header.AllocationSize.QuadPart = DesiredAllocationSize;
|
|
|
|
Dirent->FirstClusterOfFile = (RDBSS_ENTRY)FcbOrDcb->FirstClusterOfFile;
|
|
|
|
//
|
|
// Inform the cache manager to increase the section size
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT(FileObject) && CcIsFileCached(FileObject) ) {
|
|
|
|
CcSetFileSizes( FileObject,
|
|
(PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize );
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxAddFileAllocation );
|
|
|
|
if ( AbnormalTermination() && UnwindWeAllocatedDiskSpace ) {
|
|
|
|
RxDeallocateDiskSpace( RxContext, Vcb, &FcbOrDcb->Mcb );
|
|
}
|
|
|
|
RxUnpinBcb( RxContext, Bcb );
|
|
|
|
DebugTrace(-1, Dbg, "RxAddFileAllocation -> (VOID)\n", 0);
|
|
}
|
|
|
|
} else {
|
|
|
|
MCB NewMcb;
|
|
LBO LastAllocatedLbo;
|
|
VBO DontCare;
|
|
ULONG NewAllocation;
|
|
BOOLEAN UnwindWeInitializedMcb = FALSE;
|
|
BOOLEAN UnwindWeAllocatedDiskSpace = FALSE;
|
|
|
|
try {
|
|
|
|
//
|
|
// Get the first cluster following the current allocation
|
|
//
|
|
|
|
FsRtlLookupLastMcbEntry( &FcbOrDcb->Mcb, &DontCare, &LastAllocatedLbo);
|
|
|
|
//
|
|
// Try to get some disk space starting from there
|
|
//
|
|
|
|
NewAllocation = DesiredAllocationSize - FcbOrDcb->Header.AllocationSize.LowPart;
|
|
|
|
FsRtlInitializeMcb( &NewMcb, PagedPool );
|
|
UnwindWeInitializedMcb = TRUE;
|
|
|
|
RxAllocateDiskSpace( RxContext,
|
|
Vcb,
|
|
RxGetIndexFromLbo(Vcb, LastAllocatedLbo + 1),
|
|
&NewAllocation,
|
|
&NewMcb );
|
|
|
|
UnwindWeAllocatedDiskSpace = TRUE;
|
|
|
|
//
|
|
// Tack the new Mcb onto the end of the FcbOrDcb one.
|
|
//
|
|
|
|
RxMergeAllocation( RxContext,
|
|
Vcb,
|
|
&FcbOrDcb->Mcb,
|
|
&NewMcb );
|
|
|
|
//
|
|
// Now that we increased the allocation of the file, mark it in the
|
|
// FcbOrDcb.
|
|
//
|
|
|
|
FcbOrDcb->Header.AllocationSize.LowPart += NewAllocation;
|
|
|
|
//
|
|
// Inform the cache manager to increase the section size
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT(FileObject) && CcIsFileCached(FileObject) ) {
|
|
|
|
CcSetFileSizes( FileObject,
|
|
(PCC_FILE_SIZES)&FcbOrDcb->Header.AllocationSize );
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxAddFileAllocation );
|
|
|
|
//
|
|
// Detect the case where RxMergeAllocation failed, and
|
|
// Deallocate the disk space
|
|
//
|
|
|
|
if ( (UnwindWeAllocatedDiskSpace == TRUE) &&
|
|
(FcbOrDcb->Header.AllocationSize.LowPart < DesiredAllocationSize) ) {
|
|
|
|
RxDeallocateDiskSpace( RxContext, Vcb, &NewMcb );
|
|
}
|
|
|
|
if (UnwindWeInitializedMcb == TRUE) {
|
|
|
|
FsRtlUninitializeMcb( &NewMcb );
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "RxAddFileAllocation -> (VOID)\n", 0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Give FlushFileBuffer a clue here.
|
|
//
|
|
|
|
SetFlag(FcbOrDcb->FcbState, FCB_STATE_FLUSH_RDBSS);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RxTruncateFileAllocation (
|
|
IN PRX_CONTEXT RxContext,
|
|
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;
|
|
MCB RemainingMcb;
|
|
ULONG BytesPerCluster;
|
|
PDIRENT Dirent = NULL;
|
|
|
|
ULONG UnwindInitialAllocationSize;
|
|
ULONG UnwindInitialFirstClusterOfFile;
|
|
BOOLEAN UnwindWeAllocatedMcb = FALSE;
|
|
|
|
DebugTrace(+1, Dbg, "RxTruncateFileAllocation\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.LowPart == 0xffffffff) {
|
|
|
|
RxLookupFileAllocationSize( RxContext, FcbOrDcb );
|
|
}
|
|
|
|
//
|
|
// Round up the Desired Allocation Size to the next cluster size
|
|
//
|
|
|
|
Vcb = FcbOrDcb->Vcb;
|
|
|
|
BytesPerCluster = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
|
|
|
|
DesiredAllocationSize = (DesiredAllocationSize + (BytesPerCluster - 1)) &
|
|
~(BytesPerCluster - 1);
|
|
//
|
|
// Check for the benign case that the file is already smaller than
|
|
// the desired truncation.
|
|
//
|
|
|
|
if (DesiredAllocationSize >= FcbOrDcb->Header.AllocationSize.LowPart) {
|
|
|
|
DebugTrace(0, Dbg, "Desired size within current allocation.\n", 0);
|
|
|
|
DebugTrace(-1, Dbg, "RxTruncateFileAllocation -> (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 RxDeallocateDiskSpace must be the final dangerous operation.
|
|
//
|
|
|
|
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
|
|
//
|
|
|
|
RxGetDirentFromFcbOrDcb( RxContext, FcbOrDcb, &Dirent, &Bcb );
|
|
|
|
Dirent->FirstClusterOfFile = 0;
|
|
FcbOrDcb->FirstClusterOfFile = 0;
|
|
|
|
RxSetDirtyBcb( RxContext, Bcb, Vcb );
|
|
|
|
RxDeallocateDiskSpace( RxContext, Vcb, &FcbOrDcb->Mcb );
|
|
|
|
FsRtlRemoveMcbEntry( &FcbOrDcb->Mcb, 0, 0xFFFFFFFF );
|
|
|
|
} else {
|
|
|
|
//
|
|
// Split the existing allocation into two parts, one we will keep, and
|
|
// one we will deallocate.
|
|
//
|
|
|
|
FsRtlInitializeMcb( &RemainingMcb, PagedPool );
|
|
UnwindWeAllocatedMcb = TRUE;
|
|
|
|
RxSplitAllocation( RxContext,
|
|
Vcb,
|
|
&FcbOrDcb->Mcb,
|
|
DesiredAllocationSize,
|
|
&RemainingMcb );
|
|
|
|
RxDeallocateDiskSpace( RxContext, Vcb, &RemainingMcb );
|
|
|
|
FsRtlUninitializeMcb( &RemainingMcb );
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxTruncateFileAllocation );
|
|
|
|
if ( AbnormalTermination() ) {
|
|
|
|
FcbOrDcb->Header.AllocationSize.LowPart = UnwindInitialAllocationSize;
|
|
|
|
if ( (DesiredAllocationSize == 0) && (Dirent != NULL)) {
|
|
|
|
Dirent->FirstClusterOfFile = (RDBSS_ENTRY)UnwindInitialFirstClusterOfFile;
|
|
FcbOrDcb->FirstClusterOfFile = UnwindInitialFirstClusterOfFile;
|
|
}
|
|
|
|
if ( UnwindWeAllocatedMcb ) {
|
|
|
|
FsRtlUninitializeMcb( &RemainingMcb );
|
|
}
|
|
|
|
//
|
|
// God knows what state we left the disk allocation in.
|
|
// Clear the Mcb.
|
|
//
|
|
|
|
FsRtlRemoveMcbEntry( &FcbOrDcb->Mcb, 0, 0xFFFFFFFF );
|
|
}
|
|
|
|
RxUnpinBcb( RxContext, Bcb );
|
|
|
|
DebugTrace(-1, Dbg, "RxTruncateFileAllocation -> (VOID)\n", 0);
|
|
}
|
|
|
|
//
|
|
// Give FlushFileBuffer a clue here.
|
|
//
|
|
|
|
SetFlag(FcbOrDcb->FcbState, FCB_STATE_FLUSH_RDBSS);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RxLookupFileAllocationSize (
|
|
IN PRX_CONTEXT RxContext,
|
|
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 Allocated;
|
|
|
|
DebugTrace(+1, Dbg, "RxLookupAllocationSize\n", 0);
|
|
DebugTrace( 0, Dbg, " FcbOrDcb = %8lx\n", FcbOrDcb);
|
|
|
|
//
|
|
// We call RxLookupFileAllocation with Vbo of 0xffffffff - 1.
|
|
//
|
|
|
|
RxLookupFileAllocation( RxContext,
|
|
FcbOrDcb,
|
|
0xffffffff - 1,
|
|
&Lbo,
|
|
&ByteCount,
|
|
&Allocated,
|
|
NULL );
|
|
//
|
|
// Assert that we found no allocation.
|
|
//
|
|
|
|
ASSERT( Allocated == FALSE );
|
|
|
|
DebugTrace(-1, Dbg, "RxLookupFileAllocationSize -> (VOID)\n", 0);
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RxAllocateDiskSpace (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN ULONG AlternativeClusterHint,
|
|
IN PULONG ByteCount,
|
|
OUT PMCB 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 AlternativeClusterHint. 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 rx chain (i.e., RDBSS_CLUSTER_LAST)):
|
|
|
|
|
|
Mcb (empty)
|
|
|
|
becomes
|
|
|
|
Mcb |--a--|--b--|--c--!
|
|
|
|
^
|
|
ByteCount ----------+
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the VCB being modified
|
|
|
|
AlternativeClusterHint - Supplies an alternative 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.
|
|
|
|
Mcb - Receives the MCB describing the newly allocated disk space. The
|
|
caller passes in an initialized Mcb that is fill in by this procedure.
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR LogOfBytesPerCluster;
|
|
ULONG BytesPerCluster;
|
|
ULONG StartingCluster;
|
|
ULONG ClusterCount;
|
|
#if DBG
|
|
ULONG i;
|
|
ULONG PreviousClear;
|
|
#endif
|
|
|
|
DebugTrace(+1, Dbg, "RxAllocateDiskSpace\n", 0);
|
|
DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb);
|
|
DebugTrace( 0, Dbg, " *ByteCount = %8lx\n", *ByteCount);
|
|
DebugTrace( 0, Dbg, " Mcb = %8lx\n", Mcb);
|
|
|
|
//
|
|
// Make sure byte count is not zero
|
|
//
|
|
|
|
if (*ByteCount == 0) {
|
|
|
|
DebugTrace(0, Dbg, "Nothing to allocate.\n", 0);
|
|
|
|
DebugTrace(-1, Dbg, "RxAllocateDiskSpace -> (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 rolled over and there is
|
|
// no way we can satisfy the request.
|
|
//
|
|
|
|
if (*ByteCount == 0) {
|
|
|
|
DebugTrace(0, Dbg, "Disk Full. Raise Status.\n", 0);
|
|
RxRaiseStatus( RxContext, RxStatus(DISK_FULL) );
|
|
}
|
|
|
|
ClusterCount = (*ByteCount >> LogOfBytesPerCluster);
|
|
|
|
//
|
|
// Make sure there are enough free clusters to start with, and
|
|
// take them so that nobody else does later. Bah Humbug!
|
|
//
|
|
|
|
RxLockFreeClusterBitMap( Vcb );
|
|
|
|
if (ClusterCount <= Vcb->AllocationSupport.NumberOfFreeClusters) {
|
|
|
|
Vcb->AllocationSupport.NumberOfFreeClusters -= ClusterCount;
|
|
|
|
} else {
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
|
|
DebugTrace(0, Dbg, "Disk Full. Raise Status.\n", 0);
|
|
RxRaiseStatus( RxContext, RxStatus(DISK_FULL) );
|
|
}
|
|
|
|
//
|
|
// Try to find a run of free clusters large enough for us.
|
|
//
|
|
|
|
StartingCluster = RxFindFreeClusterRun( RxContext,
|
|
Vcb,
|
|
ClusterCount,
|
|
AlternativeClusterHint );
|
|
|
|
//
|
|
// If the above call was successful, we can just update the rx
|
|
// and Mcb and exit. Otherwise we have to look for smaller free
|
|
// runs.
|
|
//
|
|
|
|
if (StartingCluster != 0xffffffff) {
|
|
|
|
try {
|
|
|
|
#if DBG
|
|
//
|
|
// Verify that the Bits are all really zero.
|
|
//
|
|
|
|
for (i=0; i<ClusterCount; i++) {
|
|
ASSERT( RtlCheckBit(&Vcb->FreeClusterBitMap,
|
|
StartingCluster + i) == 0 );
|
|
}
|
|
|
|
PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap );
|
|
#endif // DBG
|
|
|
|
//
|
|
// Take the clusters we found, and unlock the bit map.
|
|
//
|
|
|
|
RxReserveClusters(RxContext, Vcb, StartingCluster, ClusterCount);
|
|
|
|
ASSERT( RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ) ==
|
|
PreviousClear - ClusterCount );
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
|
|
//
|
|
// Note that this call will never fail since there is always
|
|
// room for one entry in an empty Mcb.
|
|
//
|
|
|
|
FsRtlAddMcbEntry( Mcb,
|
|
0,
|
|
RxGetLboFromIndex( Vcb, StartingCluster ),
|
|
*ByteCount);
|
|
|
|
//
|
|
// Update the rx.
|
|
//
|
|
|
|
RxAllocateClusters(RxContext, Vcb, StartingCluster, ClusterCount);
|
|
|
|
//
|
|
// If we used the Vcb hint index, update it.
|
|
//
|
|
|
|
if (AlternativeClusterHint == 0) {
|
|
|
|
Vcb->ClusterHint = StartingCluster + ClusterCount;
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxAllocateDiskSpace );
|
|
|
|
//
|
|
// If the allocate clusters failed, remove the run from the Mcb,
|
|
// unreserve the clusters, and reset the free cluster count.
|
|
//
|
|
|
|
if ( AbnormalTermination() ) {
|
|
|
|
FsRtlRemoveMcbEntry( Mcb, 0, *ByteCount );
|
|
|
|
RxLockFreeClusterBitMap( Vcb );
|
|
#if DBG
|
|
PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap );
|
|
#endif
|
|
RxUnreserveClusters( RxContext, Vcb, StartingCluster, ClusterCount );
|
|
|
|
ASSERT( RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ) ==
|
|
PreviousClear + ClusterCount );
|
|
|
|
Vcb->AllocationSupport.NumberOfFreeClusters += ClusterCount;
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
ULONG Index;
|
|
ULONG CurrentVbo;
|
|
ULONG PriorLastIndex;
|
|
ULONG BytesFound;
|
|
|
|
ULONG ClustersFound;
|
|
ULONG ClustersRemaining;
|
|
|
|
try {
|
|
|
|
//
|
|
// While the request is still incomplete, look for the largest
|
|
// run of free clusters, mark them taken, allocate the run in
|
|
// the Mcb and Rx, and if this isn't the first time through
|
|
// the loop link it to prior run on the rx. The Mcb will
|
|
// coalesce automatically.
|
|
//
|
|
|
|
ClustersRemaining = ClusterCount;
|
|
CurrentVbo = 0;
|
|
PriorLastIndex = 0;
|
|
|
|
while (ClustersRemaining != 0) {
|
|
|
|
//
|
|
// If we just entered the loop, the bit map is already locked
|
|
//
|
|
|
|
if ( CurrentVbo != 0 ) {
|
|
|
|
RxLockFreeClusterBitMap( Vcb );
|
|
}
|
|
|
|
//
|
|
// 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() itteration.
|
|
//
|
|
|
|
ClustersFound = RxLongestFreeClusterRun( RxContext, Vcb, &Index );
|
|
|
|
#if DBG
|
|
//
|
|
// Verify that the Bits are all really zero.
|
|
//
|
|
|
|
for (i=0; i<ClustersFound; i++) {
|
|
ASSERT( RtlCheckBit(&Vcb->FreeClusterBitMap,
|
|
Index + i) == 0 );
|
|
}
|
|
|
|
PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap );
|
|
#endif // DBG
|
|
|
|
if (ClustersFound > ClustersRemaining) {
|
|
|
|
ClustersFound = ClustersRemaining;
|
|
}
|
|
|
|
//
|
|
// If we found no free cluster, then our Vcb free cluster
|
|
// count is messed up, or our bit map is corrupted, or both.
|
|
//
|
|
|
|
if (ClustersFound == 0) {
|
|
|
|
RxBugCheck( 0, 0, 0 );
|
|
}
|
|
|
|
//
|
|
// Take the clusters we found, and unlock the bit map.
|
|
//
|
|
|
|
RxReserveClusters( RxContext, Vcb, Index, ClustersFound );
|
|
|
|
ASSERT( RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ) ==
|
|
PreviousClear - ClustersFound );
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
|
|
//
|
|
// Add the newly alloced run to the Mcb.
|
|
//
|
|
|
|
BytesFound = ClustersFound << LogOfBytesPerCluster;
|
|
|
|
FsRtlAddMcbEntry( Mcb,
|
|
CurrentVbo,
|
|
RxGetLboFromIndex( Vcb, Index ),
|
|
BytesFound );
|
|
|
|
//
|
|
// Connect the last allocated run with this one, and allocate
|
|
// this run on the Rx.
|
|
//
|
|
|
|
if (PriorLastIndex != 0) {
|
|
|
|
RxSetRxEntry( RxContext,
|
|
Vcb,
|
|
PriorLastIndex,
|
|
(RDBSS_ENTRY)Index );
|
|
}
|
|
|
|
//
|
|
// Update the rx
|
|
//
|
|
|
|
RxAllocateClusters( RxContext, Vcb, Index, ClustersFound );
|
|
|
|
//
|
|
// Prepare for the next itteration.
|
|
//
|
|
|
|
CurrentVbo += BytesFound;
|
|
|
|
ClustersRemaining -= ClustersFound;
|
|
|
|
PriorLastIndex = Index + ClustersFound - 1;
|
|
}
|
|
|
|
//
|
|
// Now all the requested clusters have been allocgated.
|
|
// If we were using the Vcb hint index, update it.
|
|
//
|
|
|
|
if (AlternativeClusterHint == 0) {
|
|
|
|
Vcb->ClusterHint = Index + ClustersFound;
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxAllocateDiskSpace );
|
|
|
|
//
|
|
// Is there any unwinding to do?
|
|
//
|
|
|
|
if ( AbnormalTermination() ) {
|
|
|
|
//
|
|
// We must have failed during either the add mcb entry or
|
|
// allocate clusters. Thus we always have to unreserve
|
|
// the current run. If the allocate sectors failed, we
|
|
// must also remove the mcb run. We just unconditionally
|
|
// remove the entry since, if it is not there, the effect
|
|
// is benign.
|
|
//
|
|
|
|
RxLockFreeClusterBitMap( Vcb );
|
|
#if DBG
|
|
PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap );
|
|
#endif
|
|
RxUnreserveClusters( RxContext, Vcb, Index, ClustersFound );
|
|
Vcb->AllocationSupport.NumberOfFreeClusters += ClustersFound;
|
|
|
|
ASSERT( RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ) ==
|
|
PreviousClear + ClustersFound );
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
|
|
FsRtlRemoveMcbEntry( Mcb, CurrentVbo, BytesFound );
|
|
|
|
//
|
|
// Now we have tidyed up, we are ready to just send it
|
|
// off to deallocate disk space
|
|
//
|
|
|
|
RxDeallocateDiskSpace( RxContext, Vcb, Mcb );
|
|
|
|
//
|
|
// Now finally, remove all the entries from the mcb
|
|
//
|
|
|
|
FsRtlRemoveMcbEntry( Mcb, 0, 0xFFFFFFFF );
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "RxAllocateDiskSpace -> (VOID)\n", 0);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RxDeallocateDiskSpace (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN PMCB 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 RDBSS_CLUSTER_LAST entry.
|
|
|
|
Pictorially what is done is the following
|
|
|
|
Rx |--a--|--b--|--c--|
|
|
Mcb |--a--|--b--|--c--|
|
|
|
|
becomes
|
|
|
|
Rx |--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:
|
|
|
|
VOID - TRUE if the operation completed and FALSE if it had to
|
|
block but could not.
|
|
|
|
--*/
|
|
|
|
{
|
|
LBO Lbo;
|
|
VBO Vbo;
|
|
|
|
ULONG RunsInMcb;
|
|
ULONG ByteCount;
|
|
ULONG ClusterCount;
|
|
ULONG ClusterIndex;
|
|
ULONG McbIndex;
|
|
|
|
UCHAR LogOfBytesPerCluster;
|
|
|
|
DebugTrace(+1, Dbg, "RxDeallocateDiskSpace\n", 0);
|
|
DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb);
|
|
DebugTrace( 0, Dbg, " Mcb = %8lx\n", Mcb);
|
|
|
|
LogOfBytesPerCluster = Vcb->AllocationSupport.LogOfBytesPerCluster;
|
|
|
|
RunsInMcb = FsRtlNumberOfRunsInMcb( Mcb );
|
|
|
|
if ( RunsInMcb == 0 ) {
|
|
|
|
DebugTrace(-1, Dbg, "RxDeallocateDiskSpace -> (VOID)\n", 0);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
|
|
//
|
|
// Run though the Mcb, freeing all the runs in the rx.
|
|
//
|
|
// We do this in two steps (first update the rx, 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.
|
|
//
|
|
|
|
RunsInMcb = FsRtlNumberOfRunsInMcb( Mcb );
|
|
|
|
for ( McbIndex = 0; McbIndex < RunsInMcb; McbIndex++ ) {
|
|
|
|
FsRtlGetNextMcbEntry( Mcb, McbIndex, &Vbo, &Lbo, &ByteCount );
|
|
|
|
//
|
|
// Assert that Rx files have no holes.
|
|
//
|
|
|
|
ASSERT( Lbo != 0 );
|
|
|
|
//
|
|
// Write RDBSS_CLUSTER_AVAILABLE to each cluster in the run.
|
|
//
|
|
|
|
ClusterCount = ByteCount >> LogOfBytesPerCluster;
|
|
ClusterIndex = RxGetIndexFromLbo( Vcb, Lbo );
|
|
|
|
RxFreeClusters( RxContext, Vcb, ClusterIndex, ClusterCount );
|
|
}
|
|
|
|
//
|
|
// From now on, nothing can go wrong .... (as in raise)
|
|
//
|
|
|
|
RxLockFreeClusterBitMap( Vcb );
|
|
|
|
for ( McbIndex = 0; McbIndex < RunsInMcb; McbIndex++ ) {
|
|
#if DBG
|
|
ULONG PreviousClear;
|
|
#endif
|
|
|
|
FsRtlGetNextMcbEntry( Mcb, McbIndex, &Vbo, &Lbo, &ByteCount );
|
|
|
|
//
|
|
// Write RDBSS_CLUSTER_AVAILABLE to each cluster in the run, and
|
|
// mark the bits clear in the FreeClusterBitMap.
|
|
//
|
|
|
|
ClusterCount = ByteCount >> LogOfBytesPerCluster;
|
|
ClusterIndex = RxGetIndexFromLbo( Vcb, Lbo );
|
|
#if DBG
|
|
PreviousClear = RtlNumberOfClearBits( &Vcb->FreeClusterBitMap );
|
|
#endif
|
|
RxUnreserveClusters( RxContext, Vcb, ClusterIndex, ClusterCount );
|
|
|
|
ASSERT( RtlNumberOfClearBits( &Vcb->FreeClusterBitMap ) ==
|
|
PreviousClear + ClusterCount );
|
|
|
|
//
|
|
// Deallocation is now complete. Adjust the free cluster count.
|
|
//
|
|
|
|
Vcb->AllocationSupport.NumberOfFreeClusters += ClusterCount;
|
|
}
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxDeallocateDiskSpace );
|
|
|
|
//
|
|
// Is there any unwinding to do?
|
|
//
|
|
|
|
if ( AbnormalTermination() ) {
|
|
|
|
LBO Lbo;
|
|
VBO Vbo;
|
|
|
|
ULONG Index;
|
|
ULONG Clusters;
|
|
ULONG RxIndex;
|
|
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" itteration even though
|
|
// the SetRxRun could not have been successful. This
|
|
// allows us a convienent way to re-link the final successful
|
|
// SetRxRun.
|
|
//
|
|
|
|
PriorLastIndex = 0;
|
|
|
|
for (Index = 0; Index <= McbIndex; Index++) {
|
|
|
|
FsRtlGetNextMcbEntry(Mcb, Index, &Vbo, &Lbo, &ByteCount);
|
|
|
|
RxIndex = RxGetIndexFromLbo( Vcb, Lbo );
|
|
Clusters = ByteCount >> LogOfBytesPerCluster;
|
|
|
|
//
|
|
// We must always restore the prior itteration's last
|
|
// entry, pointing it to the first cluster of this run.
|
|
//
|
|
|
|
if (PriorLastIndex != 0) {
|
|
|
|
RxSetRxEntry( RxContext,
|
|
Vcb,
|
|
PriorLastIndex,
|
|
(RDBSS_ENTRY)RxIndex );
|
|
}
|
|
|
|
//
|
|
// If this is not the last entry (the one that failed)
|
|
// then reallocate the disk space on the rx.
|
|
//
|
|
|
|
if ( Index < McbIndex ) {
|
|
|
|
RxAllocateClusters(RxContext, Vcb, RxIndex, Clusters);
|
|
|
|
PriorLastIndex = RxIndex + Clusters - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "RxDeallocateDiskSpace -> (VOID)\n", 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RxSplitAllocation (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN OUT PMCB Mcb,
|
|
IN VBO SplitAtVbo,
|
|
OUT PMCB 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 RDBSS_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 rx chain (i.e., RDBSS_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;
|
|
|
|
DebugTrace(+1, Dbg, "RxSplitAllocation\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 );
|
|
|
|
//
|
|
// 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 );
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Move the runs after SplitAtVbo from the souce to the target
|
|
//
|
|
|
|
SourceVbo = SplitAtVbo;
|
|
TargetVbo = 0;
|
|
|
|
while (FsRtlLookupMcbEntry(Mcb, SourceVbo, &Lbo, &ByteCount, NULL)) {
|
|
|
|
FsRtlAddMcbEntry( RemainingMcb, TargetVbo, Lbo, ByteCount );
|
|
|
|
FsRtlRemoveMcbEntry( Mcb, SourceVbo, ByteCount );
|
|
|
|
TargetVbo += ByteCount;
|
|
SourceVbo += ByteCount;
|
|
}
|
|
|
|
//
|
|
// Mark the last pre-split cluster as a RDBSS_LAST_CLUSTER
|
|
//
|
|
|
|
if ( SplitAtVbo != 0 ) {
|
|
|
|
FsRtlLookupLastMcbEntry( Mcb, &DontCare, &Lbo );
|
|
|
|
RxSetRxEntry( RxContext,
|
|
Vcb,
|
|
RxGetIndexFromLbo( Vcb, Lbo ),
|
|
RDBSS_CLUSTER_LAST );
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxSplitAllocation );
|
|
|
|
//
|
|
// If we got an exception, we must glue back together the Mcbs
|
|
//
|
|
|
|
if ( AbnormalTermination() ) {
|
|
|
|
TargetVbo = SplitAtVbo;
|
|
SourceVbo = 0;
|
|
|
|
while (FsRtlLookupMcbEntry(RemainingMcb, SourceVbo, &Lbo, &ByteCount, NULL)) {
|
|
|
|
FsRtlAddMcbEntry( Mcb, TargetVbo, Lbo, ByteCount );
|
|
|
|
FsRtlRemoveMcbEntry( RemainingMcb, SourceVbo, ByteCount );
|
|
|
|
TargetVbo += ByteCount;
|
|
SourceVbo += ByteCount;
|
|
}
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "RxSplitAllocation -> (VOID)\n", 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
RxMergeAllocation (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN OUT PMCB Mcb,
|
|
IN PMCB 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 rx chain (i.e., RDBSS_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;
|
|
|
|
DebugTrace(+1, Dbg, "RxMergeAllocation\n", 0);
|
|
DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb);
|
|
DebugTrace( 0, Dbg, " Mcb = %8lx\n", Mcb);
|
|
DebugTrace( 0, Dbg, " SecondMcb = %8lx\n", SecondMcb);
|
|
|
|
try {
|
|
|
|
//
|
|
// Append the runs from SecondMcb to Mcb
|
|
//
|
|
|
|
FsRtlLookupLastMcbEntry( Mcb, &SpliceVbo, &SpliceLbo );
|
|
|
|
SourceVbo = 0;
|
|
TargetVbo = SpliceVbo + 1;
|
|
|
|
while (FsRtlLookupMcbEntry(SecondMcb, SourceVbo, &Lbo, &ByteCount, NULL)) {
|
|
|
|
FsRtlAddMcbEntry( Mcb, TargetVbo, Lbo, ByteCount );
|
|
|
|
SourceVbo += ByteCount;
|
|
TargetVbo += ByteCount;
|
|
}
|
|
|
|
//
|
|
// Link the last pre-merge cluster to the first cluster of SecondMcb
|
|
//
|
|
|
|
FsRtlLookupMcbEntry( SecondMcb, 0, &Lbo, (PULONG)NULL, NULL );
|
|
|
|
RxSetRxEntry( RxContext,
|
|
Vcb,
|
|
RxGetIndexFromLbo( Vcb, SpliceLbo ),
|
|
(RDBSS_ENTRY)RxGetIndexFromLbo( Vcb, Lbo ) );
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxMergeAllocation );
|
|
|
|
//
|
|
// If we got an exception, we must remove the runs added to Mcb
|
|
//
|
|
|
|
if ( AbnormalTermination() ) {
|
|
|
|
ULONG CutLength;
|
|
|
|
if ((CutLength = TargetVbo - (SpliceVbo + 1)) != 0) {
|
|
|
|
FsRtlRemoveMcbEntry( Mcb, SpliceVbo + 1, CutLength);
|
|
}
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "RxMergeAllocation -> (VOID)\n", 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
CLUSTER_TYPE
|
|
RxInterpretClusterType (
|
|
IN PVCB Vcb,
|
|
IN RDBSS_ENTRY Entry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure tells the caller how to interpret the input rx table
|
|
entry. It will indicate if the rx cluster is available, resereved,
|
|
bad, the last one, or the another rx index. This procedure can deal
|
|
with both 12 and 16 bit rx.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb to examine, yields 12/16 bit info
|
|
|
|
Entry - Supplies the rx entry to examine
|
|
|
|
Return Value:
|
|
|
|
CLUSTER_TYPE - Is the type of the input Rx entry
|
|
|
|
--*/
|
|
|
|
{
|
|
DebugTrace(+1, Dbg, "InterpretClusterType\n", 0);
|
|
DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb);
|
|
DebugTrace( 0, Dbg, " Entry = %8lx\n", Entry);
|
|
|
|
//
|
|
// check for 12 or 16 bit rx
|
|
//
|
|
|
|
if (Vcb->AllocationSupport.RxIndexBitSize == 12) {
|
|
|
|
//
|
|
// for 12 bit rx check for one of the cluster types, but first
|
|
// make sure we only looking at 12 bits of the entry
|
|
//
|
|
|
|
ASSERT( Entry <= 0xfff );
|
|
|
|
if (Entry == 0x000) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterAvailable\n", 0);
|
|
|
|
return RxClusterAvailable;
|
|
|
|
} else if (Entry < 0xff0) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterNext\n", 0);
|
|
|
|
return RxClusterNext;
|
|
|
|
} else if (Entry >= 0xff8) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterLast\n", 0);
|
|
|
|
return RxClusterLast;
|
|
|
|
} else if (Entry <= 0xff6) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterReserved\n", 0);
|
|
|
|
return RxClusterReserved;
|
|
|
|
} else if (Entry == 0xff7) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterBad\n", 0);
|
|
|
|
return RxClusterBad;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// for 16 bit rx check for one of the cluster types, but first
|
|
// make sure we are only looking at 16 bits of the entry
|
|
//
|
|
|
|
ASSERT( Entry <= 0xffff );
|
|
|
|
if (Entry == 0x0000) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterAvailable\n", 0);
|
|
|
|
return RxClusterAvailable;
|
|
|
|
} else if (Entry < 0xfff0) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterNext\n", 0);
|
|
|
|
return RxClusterNext;
|
|
|
|
} else if (Entry >= 0xfff8) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterLast\n", 0);
|
|
|
|
return RxClusterLast;
|
|
|
|
} else if (Entry <= 0xfff6) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterReserved\n", 0);
|
|
|
|
return RxClusterReserved;
|
|
|
|
} else if (Entry == 0xfff7) {
|
|
|
|
DebugTrace(-1, Dbg, "RxInterpretClusterType -> RxClusterBad\n", 0);
|
|
|
|
return RxClusterBad;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
VOID
|
|
RxLookupRxEntry (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN ULONG RxIndex,
|
|
IN OUT PRDBSS_ENTRY RxEntry,
|
|
IN OUT PRDBSS_ENUMERATION_CONTEXT Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes an index into the rx and gives back the value
|
|
in the Rx at this index. At any given time, for a 16 bit rx, this
|
|
routine allows only one page per volume of the rx to be pinned in
|
|
memory. For a 12 bit bit rx, the entire rx (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,
|
|
rx access context, etc.
|
|
|
|
RxIndex - Supplies the rx index to examine.
|
|
|
|
RxEntry - Receives the rx entry pointed to by RxIndex. Note that
|
|
it must point to non-paged pool.
|
|
|
|
Context - This structure keeps track of a page of pinned rx between calls.
|
|
|
|
--*/
|
|
|
|
{
|
|
DebugTrace(+1, Dbg, "RxLookupRxEntry\n", 0);
|
|
DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb);
|
|
DebugTrace( 0, Dbg, " RxIndex = %4x\n", RxIndex);
|
|
DebugTrace( 0, Dbg, " RxEntry = %8lx\n", RxEntry);
|
|
|
|
//
|
|
// Make sure they gave us a valid rx index.
|
|
//
|
|
|
|
RxVerifyIndexIsValid(RxContext, Vcb, RxIndex);
|
|
|
|
//
|
|
// Case on 12 or 16 bit rxs.
|
|
//
|
|
// In the 12 bit case (mostly floppies) we always have the whole rx
|
|
// (max 6k bytes) pinned during allocation operations. This is possibly
|
|
// a wee bit slower, but saves headaches over rx 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.RxIndexBitSize == 12) {
|
|
|
|
//
|
|
// Check to see if the rx is already pinned, otherwise pin it.
|
|
//
|
|
|
|
if (Context->Bcb == NULL) {
|
|
|
|
RxReadVolumeFile( RxContext,
|
|
Vcb,
|
|
RxReservedBytes( &Vcb->Bpb ),
|
|
RxBytesPerRx( &Vcb->Bpb ),
|
|
&Context->Bcb,
|
|
&Context->PinnedPage );
|
|
}
|
|
|
|
//
|
|
// Load the return value.
|
|
//
|
|
|
|
RxLookup12BitEntry( Context->PinnedPage, RxIndex, RxEntry );
|
|
|
|
} else {
|
|
|
|
//
|
|
// DEAL WITH 16 BIT CASE
|
|
//
|
|
|
|
ULONG PageEntryOffset;
|
|
ULONG OffsetIntoVolumeFile;
|
|
|
|
//
|
|
// Initialize two local variables that help us.
|
|
//
|
|
|
|
OffsetIntoVolumeFile = RxReservedBytes(&Vcb->Bpb) + RxIndex * sizeof(RDBSS_ENTRY);
|
|
PageEntryOffset = (OffsetIntoVolumeFile % PAGE_SIZE) / sizeof(RDBSS_ENTRY);
|
|
|
|
//
|
|
// Check to see if we need to read in a new page of rx
|
|
//
|
|
|
|
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.
|
|
//
|
|
|
|
RxUnpinBcb( RxContext, Context->Bcb );
|
|
|
|
RxReadVolumeFile( RxContext,
|
|
Vcb,
|
|
OffsetIntoVolumeFile & ~(PAGE_SIZE - 1),
|
|
PAGE_SIZE,
|
|
&Context->Bcb,
|
|
&Context->PinnedPage );
|
|
|
|
Context->VboOfPinnedPage = OffsetIntoVolumeFile & ~(PAGE_SIZE - 1);
|
|
}
|
|
|
|
//
|
|
// Grab the rx entry from the pinned page, and return
|
|
//
|
|
|
|
*RxEntry = ((PRDBSS_ENTRY)(Context->PinnedPage))[PageEntryOffset];
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "RxLookupRxEntry -> (VOID)\n", 0);
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
VOID
|
|
RxSetRxEntry (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN ULONG RxIndex,
|
|
IN RDBSS_ENTRY RxEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes an index into the rx and puts a value in the Rx
|
|
at this index. The routine special cases 12 and 16 bit rxs. In both
|
|
cases we go to the cache manager for a piece of the rx.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb to examine, yields 12/16 bit info, etc.
|
|
|
|
RxIndex - Supplies the destination rx index.
|
|
|
|
RxEntry - Supplies the source rx entry.
|
|
|
|
--*/
|
|
|
|
{
|
|
LBO Lbo;
|
|
PBCB Bcb = NULL;
|
|
ULONG SectorSize;
|
|
ULONG OffsetIntoVolumeFile;
|
|
BOOLEAN ReleaseMutex = FALSE;
|
|
|
|
DebugTrace(+1, Dbg, "RxSetRxEntry\n", 0);
|
|
DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb);
|
|
DebugTrace( 0, Dbg, " RxIndex = %4x\n", RxIndex);
|
|
DebugTrace( 0, Dbg, " RxEntry = %4x\n", RxEntry);
|
|
|
|
//
|
|
// Make sure they gave us a valid rx index.
|
|
//
|
|
|
|
RxVerifyIndexIsValid(RxContext, Vcb, RxIndex);
|
|
|
|
//
|
|
// Set Sector Size
|
|
//
|
|
|
|
SectorSize = 1 << Vcb->AllocationSupport.LogOfBytesPerSector;
|
|
|
|
//
|
|
// Case on 12 or 16 bit rxs.
|
|
//
|
|
// In the 12 bit case (mostly floppies) we always have the whole rx
|
|
// (max 6k bytes) pinned during allocation operations. This is possibly
|
|
// a wee bit slower, but saves headaches over rx 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 rx
|
|
// entry.
|
|
//
|
|
|
|
//
|
|
// DEAL WITH 12 BIT CASE
|
|
//
|
|
|
|
try {
|
|
|
|
if (Vcb->AllocationSupport.RxIndexBitSize == 12) {
|
|
|
|
PVOID PinnedRx;
|
|
|
|
//
|
|
// Make sure we have a valid entry
|
|
//
|
|
|
|
RxEntry &= 0xfff;
|
|
|
|
//
|
|
// We read in the entire rx. Note that using prepare write marks
|
|
// the bcb pre-dirty, so we don't have to do it explicitly.
|
|
//
|
|
|
|
OffsetIntoVolumeFile = RxReservedBytes( &Vcb->Bpb ) + RxIndex * 3 / 2;
|
|
|
|
RxPrepareWriteVolumeFile( RxContext,
|
|
Vcb,
|
|
RxReservedBytes( &Vcb->Bpb ),
|
|
RxBytesPerRx( &Vcb->Bpb ),
|
|
&Bcb,
|
|
&PinnedRx,
|
|
FALSE );
|
|
|
|
//
|
|
// Mark the sector(s) dirty in the DirtyRxMcb. 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 rx Mcb.
|
|
//
|
|
|
|
Lbo = OffsetIntoVolumeFile & ~(SectorSize - 1);
|
|
|
|
FsRtlAddMcbEntry( &Vcb->DirtyRxMcb, 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;
|
|
|
|
FsRtlAddMcbEntry( &Vcb->DirtyRxMcb, Lbo, Lbo, SectorSize );
|
|
}
|
|
|
|
//
|
|
// Store the entry into the rx; we need a little synchonization
|
|
// here and can't use a spinlock since the bytes might not be
|
|
// resident.
|
|
//
|
|
|
|
RxLockFreeClusterBitMap( Vcb );
|
|
|
|
ReleaseMutex = TRUE;
|
|
|
|
RxSet12BitEntry( PinnedRx, RxIndex, RxEntry );
|
|
|
|
ReleaseMutex = FALSE;
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
|
|
} else {
|
|
|
|
//
|
|
// DEAL WITH 16 BIT CASE
|
|
//
|
|
|
|
PRDBSS_ENTRY PinnedRxEntry;
|
|
|
|
//
|
|
// Read in a new page of rx
|
|
//
|
|
|
|
OffsetIntoVolumeFile = RxReservedBytes( &Vcb->Bpb ) +
|
|
RxIndex * sizeof( RDBSS_ENTRY );
|
|
|
|
RxPrepareWriteVolumeFile( RxContext,
|
|
Vcb,
|
|
OffsetIntoVolumeFile,
|
|
sizeof(RDBSS_ENTRY),
|
|
&Bcb,
|
|
(PVOID *)&PinnedRxEntry,
|
|
FALSE );
|
|
|
|
//
|
|
// Mark the sector dirty in the DirtyRxMcb
|
|
//
|
|
|
|
Lbo = OffsetIntoVolumeFile & ~(SectorSize - 1);
|
|
|
|
FsRtlAddMcbEntry( &Vcb->DirtyRxMcb, Lbo, Lbo, SectorSize);
|
|
|
|
//
|
|
// Store the RxEntry 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
|
|
RxLockFreeClusterBitMap( Vcb );
|
|
ReleaseMutex = TRUE;
|
|
*PinnedRxEntry = RxEntry;
|
|
ReleaseMutex = FALSE;
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
#else
|
|
*PinnedRxEntry = RxEntry;
|
|
#endif // ALPHA
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxSetRxEntry );
|
|
|
|
//
|
|
// If we still somehow have the Mutex, release it.
|
|
//
|
|
|
|
if (ReleaseMutex) {
|
|
|
|
ASSERT( AbnormalTermination() );
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
}
|
|
|
|
//
|
|
// Unpin the Bcb
|
|
//
|
|
|
|
RxUnpinBcb(RxContext, Bcb);
|
|
|
|
DebugTrace(-1, Dbg, "RxSetRxEntry -> (VOID)\n", 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
VOID
|
|
RxSetRxRun (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PVCB Vcb,
|
|
IN ULONG StartingRxIndex,
|
|
IN ULONG ClusterCount,
|
|
IN BOOLEAN ChainTogether
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets a continues run of clusters in the rx. If ChainTogether
|
|
is TRUE, then the clusters are linked together as in normal Rx fasion,
|
|
with the last cluster receiving RDBSS_CLUSTER_LAST. If ChainTogether is
|
|
FALSE, all the entries are set to RDBSS_CLUSTER_AVAILABLE, effectively
|
|
freeing all the clusters in the run.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb to examine, yields 12/16 bit info, etc.
|
|
|
|
StartingRxIndex - Supplies the destination rx index.
|
|
|
|
ClusterCount - Supplies the number of contiguous clusters to work on.
|
|
|
|
ChainTogether - Tells us whether to fill the entries with links, or
|
|
RDBSS_CLUSTER_AVAILABLE
|
|
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
PBCB SavedBcbs[(0x10000 * sizeof(RDBSS_ENTRY) / PAGE_SIZE) + 2][2];
|
|
|
|
ULONG SectorSize;
|
|
ULONG Cluster;
|
|
|
|
LBO StartSectorLbo;
|
|
LBO FinalSectorLbo;
|
|
LBO Lbo;
|
|
|
|
PVOID PinnedRx;
|
|
|
|
ULONG StartingPage;
|
|
|
|
BOOLEAN ReleaseMutex = FALSE;
|
|
|
|
DebugTrace(+1, Dbg, "RxSetRxRun\n", 0);
|
|
DebugTrace( 0, Dbg, " Vcb = %8lx\n", Vcb);
|
|
DebugTrace( 0, Dbg, " StartingRxIndex = %8x\n", StartingRxIndex);
|
|
DebugTrace( 0, Dbg, " ClusterCount = %8lx\n", ClusterCount);
|
|
DebugTrace( 0, Dbg, " ChainTogether = %s\n", ChainTogether ? "TRUE":"FALSE");
|
|
|
|
//
|
|
// Make sure they gave us a valid rx run.
|
|
//
|
|
|
|
RxVerifyIndexIsValid(RxContext, Vcb, StartingRxIndex);
|
|
RxVerifyIndexIsValid(RxContext, Vcb, StartingRxIndex + ClusterCount - 1);
|
|
|
|
//
|
|
// Check special case
|
|
//
|
|
|
|
if (ClusterCount == 0) {
|
|
|
|
DebugTrace(-1, Dbg, "RxSetRxRun -> (VOID)\n", 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Set Sector Size
|
|
//
|
|
|
|
SectorSize = 1 << Vcb->AllocationSupport.LogOfBytesPerSector;
|
|
|
|
//
|
|
// Case on 12 or 16 bit rxs.
|
|
//
|
|
// In the 12 bit case (mostly floppies) we always have the whole rx
|
|
// (max 6k bytes) pinned during allocation operations. This is possibly
|
|
// a wee bit slower, but saves headaches over rx 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
|
|
//
|
|
|
|
try {
|
|
|
|
if (Vcb->AllocationSupport.RxIndexBitSize == 12) {
|
|
|
|
StartingPage = 0;
|
|
|
|
//
|
|
// We read in the entire rx. 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);
|
|
|
|
RxPrepareWriteVolumeFile( RxContext,
|
|
Vcb,
|
|
RxReservedBytes( &Vcb->Bpb ),
|
|
RxBytesPerRx( &Vcb->Bpb ),
|
|
&SavedBcbs[0][0],
|
|
&PinnedRx,
|
|
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 rx entry stragling a sector boundry.
|
|
//
|
|
// Note that if the first AddMcbEntry succeeds, all following ones
|
|
// will simply coalese, and thus also succeed.
|
|
//
|
|
|
|
StartSectorLbo = (RxReservedBytes( &Vcb->Bpb ) + StartingRxIndex * 3 / 2)
|
|
& ~(SectorSize - 1);
|
|
|
|
FinalSectorLbo = (RxReservedBytes( &Vcb->Bpb ) + ((StartingRxIndex +
|
|
ClusterCount) * 3 + 2) / 2) & ~(SectorSize - 1);
|
|
|
|
for (Lbo = StartSectorLbo; Lbo <= FinalSectorLbo; Lbo += SectorSize) {
|
|
|
|
FsRtlAddMcbEntry( &Vcb->DirtyRxMcb, Lbo, Lbo, SectorSize );
|
|
}
|
|
|
|
//
|
|
// Store the entries into the rx; we need a little
|
|
// synchonization here and can't use a spinlock since the bytes
|
|
// might not be resident.
|
|
//
|
|
|
|
RxLockFreeClusterBitMap( Vcb );
|
|
|
|
ReleaseMutex = TRUE;
|
|
|
|
for (Cluster = StartingRxIndex;
|
|
Cluster < StartingRxIndex + ClusterCount - 1;
|
|
Cluster++) {
|
|
|
|
RxSet12BitEntry( PinnedRx,
|
|
Cluster,
|
|
ChainTogether ? Cluster + 1 : RDBSS_CLUSTER_AVAILABLE );
|
|
}
|
|
|
|
//
|
|
// Save the last entry
|
|
//
|
|
|
|
RxSet12BitEntry( PinnedRx,
|
|
Cluster,
|
|
ChainTogether ?
|
|
RDBSS_CLUSTER_LAST & 0xfff : RDBSS_CLUSTER_AVAILABLE );
|
|
|
|
ReleaseMutex = FALSE;
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
|
|
} else {
|
|
|
|
//
|
|
// DEAL WITH 16 BIT CASE
|
|
//
|
|
|
|
VBO StartOffsetInVolume;
|
|
VBO FinalOffsetInVolume;
|
|
|
|
ULONG Page;
|
|
ULONG FinalCluster;
|
|
PRDBSS_ENTRY RxEntry;
|
|
|
|
StartOffsetInVolume = RxReservedBytes(&Vcb->Bpb) +
|
|
StartingRxIndex * sizeof(RDBSS_ENTRY);
|
|
|
|
FinalOffsetInVolume = StartOffsetInVolume +
|
|
(ClusterCount - 1) * sizeof(RDBSS_ENTRY);
|
|
|
|
StartingPage = StartOffsetInVolume / PAGE_SIZE;
|
|
|
|
//
|
|
// Read in one page of rx at a time. We cannot read in the
|
|
// all of the rx we need because of cache manager limitations.
|
|
//
|
|
// SavedBcb was initialized to be able to hold the largest
|
|
// possible number of pages in a rx 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 ) {
|
|
|
|
RxPrepareWriteVolumeFile( RxContext,
|
|
Vcb,
|
|
Offset,
|
|
PAGE_SIZE,
|
|
&SavedBcbs[Page][0],
|
|
(PVOID *)&SavedBcbs[Page][1],
|
|
FALSE );
|
|
|
|
if (Page == 0) {
|
|
|
|
RxEntry = (PRDBSS_ENTRY)((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) {
|
|
|
|
FsRtlAddMcbEntry( &Vcb->DirtyRxMcb, 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
|
|
RxLockFreeClusterBitMap( Vcb );
|
|
ReleaseMutex = TRUE;
|
|
#endif // ALPHA
|
|
|
|
FinalCluster = StartingRxIndex + ClusterCount - 1;
|
|
Page = 0;
|
|
|
|
for (Cluster = StartingRxIndex;
|
|
Cluster <= FinalCluster;
|
|
Cluster++, RxEntry++) {
|
|
|
|
//
|
|
// If we just crossed a page boundry (as apposed to starting
|
|
// on one), update out idea of RxEntry.
|
|
|
|
if ( (((ULONG)RxEntry & (PAGE_SIZE-1)) == 0) &&
|
|
(Cluster != StartingRxIndex) ) {
|
|
|
|
Page += 1;
|
|
RxEntry = (PRDBSS_ENTRY)SavedBcbs[Page][1];
|
|
}
|
|
|
|
*RxEntry = ChainTogether ? (RDBSS_ENTRY)(Cluster + 1) :
|
|
RDBSS_CLUSTER_AVAILABLE;
|
|
}
|
|
|
|
//
|
|
// Fix up the last entry if we were chaining together
|
|
//
|
|
|
|
if ( ChainTogether ) {
|
|
|
|
*(RxEntry-1) = RDBSS_CLUSTER_LAST;
|
|
}
|
|
#ifdef ALPHA
|
|
ReleaseMutex = FALSE;
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
#endif // ALPHA
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
ULONG i = 0;
|
|
|
|
DebugUnwind( RxSetRxRun );
|
|
|
|
//
|
|
// If we still somehow have the Mutex, release it.
|
|
//
|
|
|
|
if (ReleaseMutex) {
|
|
|
|
ASSERT( AbnormalTermination() );
|
|
|
|
RxUnlockFreeClusterBitMap( Vcb );
|
|
}
|
|
|
|
//
|
|
// Unpin the Bcbs
|
|
//
|
|
|
|
while ( SavedBcbs[i][0] != NULL ) {
|
|
|
|
RxUnpinBcb( RxContext, SavedBcbs[i][0] );
|
|
|
|
i += 1;
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "RxSetRxRun -> (VOID)\n", 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
UCHAR
|
|
RxLogOf (
|
|
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;
|
|
|
|
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);
|
|
|
|
RxBugCheck( Value, Log, 0 );
|
|
}
|
|
|
|
DebugTrace(-1, Dbg, "LogOf -> %8lx\n", Log);
|
|
|
|
return Log;
|
|
}
|