1019 lines
26 KiB
C
1019 lines
26 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989-2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
VmcbSup.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Historical note: this package was originally written for HPFS (pinball)
|
|||
|
and is now resurrected for UDFS. Since UDFS is readonly in initial
|
|||
|
versions we will snip by #ifdef the write support, leaving it visible
|
|||
|
for the future - this code has not been changed (nearly) whatsoever and
|
|||
|
is left named as Pb (pinball) code.
|
|||
|
|
|||
|
The VMCB routines provide support for maintaining a mapping between
|
|||
|
LBNs and VBNs for a virtual volume file. The volume file is all
|
|||
|
of the sectors that make up the on-disk structures. A file system
|
|||
|
uses this package to map LBNs for on-disk structure to VBNs in a volume
|
|||
|
file. This when used in conjunction with Memory Management and the
|
|||
|
Cache Manager will treat the volume file as a simple mapped file. A
|
|||
|
variable of type VMCB is used to store the mapping information and one
|
|||
|
is needed for every mounted volume.
|
|||
|
|
|||
|
The main idea behind this package is to allow the user to dynamically
|
|||
|
read in new disk structure sectors (e.g., File Entries). The user assigns
|
|||
|
the new sector a VBN in the Volume file and has memory management fault
|
|||
|
the page containing the sector into memory. To do this Memory management
|
|||
|
will call back into the file system to read the page from the volume file
|
|||
|
passing in the appropriate VBN. Now the file system takes the VBN and
|
|||
|
maps it back to its LBN and does the read.
|
|||
|
|
|||
|
The granularity of mapping is one a per page basis. That is if
|
|||
|
a mapping for LBN 8 is added to the VMCB structure and the page size
|
|||
|
is 8 sectors then the VMCB routines will actually assign a mapping for
|
|||
|
LBNS 8 through 15, and they will be assigned to a page aligned set of
|
|||
|
VBNS. This function is needed to allow us to work efficiently with
|
|||
|
memory management. This means that some sectors in some pages might
|
|||
|
actually contain regular file data and not volume information, and so
|
|||
|
when writing the page out we must only write the sectors that are really
|
|||
|
in use by the volume file. To help with this we provide a set
|
|||
|
of routines to keep track of dirty volume file sectors.
|
|||
|
That way, when the file system is called to write a page to the volume
|
|||
|
file, it will only write the sectors that are dirty.
|
|||
|
|
|||
|
Concurrent access the VMCB structure is control by this package.
|
|||
|
|
|||
|
The functions provided in this package are as follows:
|
|||
|
|
|||
|
o UdfInitializeVmcb - Initialize a new VMCB structure.
|
|||
|
|
|||
|
o UdfUninitializeVmcb - Uninitialize an existing VMCB structure.
|
|||
|
|
|||
|
o UdfSetMaximumLbnVmcb - Sets/Resets the maximum allowed LBN
|
|||
|
for the specified VMCB structure.
|
|||
|
|
|||
|
o UdfAddVmcbMapping - This routine takes an LBN and assigns to it
|
|||
|
a VBN. If the LBN already was assigned to an VBN it simply returns
|
|||
|
the old VBN and does not do a new assignemnt.
|
|||
|
|
|||
|
o UdfRemoveVmcbMapping - This routine takes an LBN and removes its
|
|||
|
mapping from the VMCB structure.
|
|||
|
|
|||
|
o UdfVmcbVbnToLbn - This routine takes a VBN and returns the
|
|||
|
LBN it maps to.
|
|||
|
|
|||
|
o UdfVmcbLbnToVbn - This routine takes an LBN and returns the
|
|||
|
VBN its maps to.
|
|||
|
|
|||
|
Authors:
|
|||
|
|
|||
|
Gary Kimura [GaryKi] 4-Apr-1990
|
|||
|
Dan Lovinger [DanLo] 10-Sep-1996
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
Tom Jolly [tomjolly] 21-Jan-2000 CcPurge and extend at end of stream
|
|||
|
Tom Jolly [TomJolly] 1-March-2000 UDF 2.01 support
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "UdfProcs.h"
|
|||
|
|
|||
|
//
|
|||
|
// The Bug check file id for this module
|
|||
|
//
|
|||
|
|
|||
|
#define BugCheckFileId (UDFS_BUG_CHECK_VMCBSUP)
|
|||
|
|
|||
|
//
|
|||
|
// The local debug trace level
|
|||
|
//
|
|||
|
|
|||
|
#define Dbg (UDFS_DEBUG_LEVEL_VMCBSUP)
|
|||
|
|
|||
|
//
|
|||
|
// The following macro is used to calculate the number of pages (in terms of
|
|||
|
// sectors) needed to contain a given sector count. For example (assuming
|
|||
|
// 1kb sector size, 8kb page size)
|
|||
|
//
|
|||
|
// PadSectorCountToPage( 0 Sectors ) = 0 Pages = 0 Sectors
|
|||
|
// PadSectorCountToPage( 1 Sectors ) = 1 Page = 8 Sectors
|
|||
|
// PadSectorCountToPage( 2 Sectors ) = 1 Page = 8 Sectors
|
|||
|
// PadSectorCountToPage( 8 .. ) = 2 Pages = 16 sectors
|
|||
|
//
|
|||
|
// Evaluates to the number of
|
|||
|
//
|
|||
|
|
|||
|
#define PadSectorCountToPage(V, L) ( ( ((L)+((PAGE_SIZE/(V)->SectorSize)-1)) / (PAGE_SIZE/(V)->SectorSize) ) * (PAGE_SIZE/(V)->SectorSize) )
|
|||
|
|
|||
|
//
|
|||
|
// Evaluates to first page aligned LBN <= Supplied LBN
|
|||
|
//
|
|||
|
|
|||
|
#define AlignToPageBase( V, L) ((L) & ~((PAGE_SIZE / (V)->SectorSize)-1))
|
|||
|
|
|||
|
//
|
|||
|
// Evaluates to TRUE if the LBN is page aligned, FALSE otherwise
|
|||
|
//
|
|||
|
|
|||
|
#define IsPageAligned( V, L) (0 == ((L) & ((PAGE_SIZE / (V)->SectorSize)-1)) )
|
|||
|
|
|||
|
//
|
|||
|
// Macros for VMCB synchronisation
|
|||
|
//
|
|||
|
|
|||
|
#define VmcbLockForRead( V) (VOID)ExAcquireResourceSharedLite( &((V)->Resource), TRUE )
|
|||
|
|
|||
|
#define VmcbLockForModify( V) (VOID)ExAcquireResourceExclusiveLite( &((V)->Resource), TRUE )
|
|||
|
|
|||
|
#define VmcbRelease( V) ExReleaseResourceLite( &((V)->Resource))
|
|||
|
|
|||
|
//
|
|||
|
// Local Routines.
|
|||
|
//
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UdfVmcbLookupMcbEntry (
|
|||
|
IN PMCB Mcb,
|
|||
|
IN VBN Vbn,
|
|||
|
OUT PLBN Lbn,
|
|||
|
OUT PULONG SectorCount OPTIONAL,
|
|||
|
OUT PULONG Index OPTIONAL
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, UdfAddVmcbMapping)
|
|||
|
#pragma alloc_text(PAGE, UdfInitializeVmcb)
|
|||
|
#pragma alloc_text(PAGE, UdfRemoveVmcbMapping)
|
|||
|
#pragma alloc_text(PAGE, UdfResetVmcb)
|
|||
|
#pragma alloc_text(PAGE, UdfSetMaximumLbnVmcb)
|
|||
|
#pragma alloc_text(PAGE, UdfUninitializeVmcb)
|
|||
|
#pragma alloc_text(PAGE, UdfVmcbLbnToVbn)
|
|||
|
#pragma alloc_text(PAGE, UdfVmcbLookupMcbEntry)
|
|||
|
#pragma alloc_text(PAGE, UdfVmcbVbnToLbn)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
UdfInitializeVmcb (
|
|||
|
IN PVMCB Vmcb,
|
|||
|
IN POOL_TYPE PoolType,
|
|||
|
IN ULONG MaximumLbn,
|
|||
|
IN ULONG SectorSize
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine initializes a new Vmcb Structure. The caller must
|
|||
|
supply the memory for the structure. This must precede all other calls
|
|||
|
that set/query the volume file mapping.
|
|||
|
|
|||
|
If pool is not available this routine will raise a status value
|
|||
|
indicating insufficient resources.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vmcb - Supplies a pointer to the volume file structure to initialize.
|
|||
|
|
|||
|
PoolType - Supplies the pool type to use when allocating additional
|
|||
|
internal structures.
|
|||
|
|
|||
|
MaximumLbn - Supplies the maximum Lbn value that is valid for this
|
|||
|
volume.
|
|||
|
|
|||
|
LbSize - Size of a sector on this volume
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN VbnInitialized;
|
|||
|
BOOLEAN LbnInitialized;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace(( +1, Dbg, "UdfInitializeVmcb, Vmcb = %08x\n", Vmcb ));
|
|||
|
|
|||
|
VbnInitialized = FALSE;
|
|||
|
LbnInitialized = FALSE;
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the fields in the vmcb structure
|
|||
|
//
|
|||
|
|
|||
|
FsRtlInitializeMcb( &Vmcb->VbnIndexed, PoolType );
|
|||
|
VbnInitialized = TRUE;
|
|||
|
|
|||
|
FsRtlInitializeMcb( &Vmcb->LbnIndexed, PoolType );
|
|||
|
LbnInitialized = TRUE;
|
|||
|
|
|||
|
Vmcb->MaximumLbn = MaximumLbn;
|
|||
|
|
|||
|
Vmcb->SectorSize = SectorSize;
|
|||
|
|
|||
|
Vmcb->NodeTypeCode = UDFS_NTC_VMCB;
|
|||
|
Vmcb->NodeByteSize = sizeof( VMCB);
|
|||
|
|
|||
|
ExInitializeResourceLite( &Vmcb->Resource );
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
//
|
|||
|
// If this is an abnormal termination then check if we need to
|
|||
|
// uninitialize the mcb structures
|
|||
|
//
|
|||
|
|
|||
|
if (AbnormalTermination()) {
|
|||
|
|
|||
|
if (VbnInitialized) { FsRtlUninitializeMcb( &Vmcb->VbnIndexed ); }
|
|||
|
if (LbnInitialized) { FsRtlUninitializeMcb( &Vmcb->LbnIndexed ); }
|
|||
|
}
|
|||
|
|
|||
|
DebugUnwind("UdfInitializeVmcb");
|
|||
|
DebugTrace(( -1, Dbg, "UdfInitializeVmcb -> VOID\n" ));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
UdfUninitializeVmcb (
|
|||
|
IN PVMCB Vmcb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine uninitializes an existing VMCB structure. After calling
|
|||
|
this routine the input VMCB structure must be re-initialized before
|
|||
|
being used again.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vmcb - Supplies a pointer to the VMCB structure to uninitialize.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace(( +1, Dbg, "UdfUninitializeVmcb, Vmcb = %08x\n", Vmcb ));
|
|||
|
|
|||
|
//
|
|||
|
// Unitialize the fields in the Vmcb structure
|
|||
|
//
|
|||
|
|
|||
|
FsRtlUninitializeMcb( &Vmcb->VbnIndexed );
|
|||
|
FsRtlUninitializeMcb( &Vmcb->LbnIndexed );
|
|||
|
|
|||
|
ExDeleteResourceLite( &Vmcb->Resource);
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(( -1, Dbg, "UdfUninitializeVmcb -> VOID\n" ));
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
UdfResetVmcb (
|
|||
|
IN PVMCB Vmcb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine resets the mappings in an existing VMCB structure.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vmcb - Supplies a pointer to the VMCB structure to reset.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace(( +1, Dbg, "UdfResetVmcb, Vmcb = %08x\n", Vmcb ));
|
|||
|
|
|||
|
//
|
|||
|
// Unitialize the fields in the Vmcb structure
|
|||
|
//
|
|||
|
|
|||
|
FsRtlResetLargeMcb( (PLARGE_MCB) &Vmcb->VbnIndexed, TRUE );
|
|||
|
FsRtlResetLargeMcb( (PLARGE_MCB) &Vmcb->LbnIndexed, TRUE );
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(( -1, Dbg, "UdfResetVmcb -> VOID\n" ));
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
UdfSetMaximumLbnVmcb (
|
|||
|
IN PVMCB Vmcb,
|
|||
|
IN ULONG MaximumLbn
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine sets/resets the maximum allowed LBN for the specified
|
|||
|
Vmcb structure. The Vmcb structure must already have been initialized
|
|||
|
by calling UdfInitializeVmcb.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vmcb - Supplies a pointer to the volume file structure to initialize.
|
|||
|
|
|||
|
MaximumLbn - Supplies the maximum Lbn value that is valid for this
|
|||
|
volume.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace(( +1, Dbg, "UdfSetMaximumLbnVmcb, Vmcb = %08x\n", Vmcb ));
|
|||
|
|
|||
|
//
|
|||
|
// Set the field
|
|||
|
//
|
|||
|
|
|||
|
Vmcb->MaximumLbn = MaximumLbn;
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(( -1, Dbg, "UdfSetMaximumLbnVmcb -> VOID\n" ));
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UdfVmcbVbnToLbn (
|
|||
|
IN PVMCB Vmcb,
|
|||
|
IN VBN Vbn,
|
|||
|
IN PLBN Lbn,
|
|||
|
OUT PULONG SectorCount OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine translates a VBN to an LBN.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vmcb - Supplies the VMCB structure being queried.
|
|||
|
|
|||
|
Vbn - Supplies the VBN to translate from.
|
|||
|
|
|||
|
Lbn - Receives the LBN mapped by the input Vbn. This value is only valid
|
|||
|
if the function result is TRUE.
|
|||
|
|
|||
|
SectorCount - Optionally receives the number of sectors corresponding
|
|||
|
to the run.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if he Vbn has a valid mapping and FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN Result;
|
|||
|
|
|||
|
DebugTrace(( +1, Dbg, "UdfVmcbVbnToLbn, Vbn = %08x\n", Vbn ));
|
|||
|
|
|||
|
//
|
|||
|
// Now grab the resource
|
|||
|
//
|
|||
|
|
|||
|
VmcbLockForRead( Vmcb);
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
Result = UdfVmcbLookupMcbEntry( &Vmcb->VbnIndexed,
|
|||
|
Vbn,
|
|||
|
Lbn,
|
|||
|
SectorCount,
|
|||
|
NULL );
|
|||
|
|
|||
|
DebugTrace(( 0, Dbg, "*Lbn = %08x\n", *Lbn ));
|
|||
|
|
|||
|
//
|
|||
|
// If the returned Lbn is greater than the maximum allowed Lbn
|
|||
|
// then return FALSE
|
|||
|
//
|
|||
|
|
|||
|
if (Result && (*Lbn > Vmcb->MaximumLbn)) {
|
|||
|
|
|||
|
try_leave( Result = FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the last returned Lbn is greater than the maximum allowed Lbn
|
|||
|
// then bring in the sector count
|
|||
|
//
|
|||
|
|
|||
|
if (Result &&
|
|||
|
ARGUMENT_PRESENT(SectorCount) &&
|
|||
|
(*Lbn+*SectorCount-1 > Vmcb->MaximumLbn)) {
|
|||
|
|
|||
|
*SectorCount = (Vmcb->MaximumLbn - *Lbn + 1);
|
|||
|
}
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
VmcbRelease( Vmcb);
|
|||
|
|
|||
|
DebugUnwind("UdfVmcbVbnToLbn");
|
|||
|
DebugTrace(( -1, Dbg, "UdfVmcbVbnToLbn -> Result = %08x\n", Result ));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
return Result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UdfVmcbLbnToVbn (
|
|||
|
IN PVMCB Vmcb,
|
|||
|
IN LBN Lbn,
|
|||
|
OUT PVBN Vbn,
|
|||
|
OUT PULONG SectorCount OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine translates an LBN to a VBN.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vmcb - Supplies the VMCB structure being queried.
|
|||
|
|
|||
|
Lbn - Supplies the LBN to translate from.
|
|||
|
|
|||
|
Vbn - Recieves the VBN mapped by the input LBN. This value is
|
|||
|
only valid if the function result is TRUE.
|
|||
|
|
|||
|
SectorCount - Optionally receives the number of sectors corresponding
|
|||
|
to the run.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if the mapping is valid and FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN Result;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace(( +1, Dbg, "UdfVmcbLbnToVbn, Lbn = %08x\n", Lbn ));
|
|||
|
|
|||
|
//
|
|||
|
// If the requested Lbn is greater than the maximum allowed Lbn
|
|||
|
// then the result is FALSE
|
|||
|
//
|
|||
|
|
|||
|
if (Lbn > Vmcb->MaximumLbn) {
|
|||
|
|
|||
|
DebugTrace(( -1, Dbg, "Lbn too large, UdfVmcbLbnToVbn -> FALSE\n" ));
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now grab the resource
|
|||
|
//
|
|||
|
|
|||
|
VmcbLockForRead( Vmcb);
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
Result = UdfVmcbLookupMcbEntry( &Vmcb->LbnIndexed,
|
|||
|
Lbn,
|
|||
|
Vbn,
|
|||
|
SectorCount,
|
|||
|
NULL );
|
|||
|
|
|||
|
if (Result) {
|
|||
|
|
|||
|
DebugTrace(( 0, Dbg, "*Vbn = %08x\n", *Vbn ));
|
|||
|
}
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
VmcbRelease( Vmcb);
|
|||
|
|
|||
|
DebugUnwind("UdfVmcbLbnToVbn");
|
|||
|
DebugTrace(( -1, Dbg, "UdfVmcbLbnToVbn -> Result = %08x\n", Result ));
|
|||
|
}
|
|||
|
|
|||
|
return Result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UdfAddVmcbMapping (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PVMCB Vmcb,
|
|||
|
IN LBN Lbn,
|
|||
|
IN ULONG SectorCount,
|
|||
|
IN BOOLEAN ExactEnd,
|
|||
|
OUT PVBN Vbn,
|
|||
|
OUT PULONG AlignedSectorCount
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine adds a new LBN to VBN mapping to the VMCB structure. When
|
|||
|
a new LBN is added to the structure it does it only on page aligned
|
|||
|
boundaries.
|
|||
|
|
|||
|
If pool is not available to store the information this routine will
|
|||
|
raise a status value indicating insufficient resources.
|
|||
|
|
|||
|
May acquire Vcb->VmcbMappingResource EXCLUSIVE if an existing mapping can
|
|||
|
be extended (and hence a purge is necessary), released before return.
|
|||
|
|
|||
|
Caller must have NO active mappings through Vmcb stream before calling this
|
|||
|
function.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vmcb - Supplies the VMCB being updated.
|
|||
|
|
|||
|
Lbn - Supplies the starting LBN to add to VMCB.
|
|||
|
|
|||
|
SectorCount - Supplies the number of Sectors in the run. We're only currently expecting
|
|||
|
single sector mappings.
|
|||
|
|
|||
|
ExactEnd - Indicates that instead of aligning to map sectors beyond
|
|||
|
the end of the request, use a hole. Implies trying to look at
|
|||
|
these sectors could be undesireable.
|
|||
|
|
|||
|
Vbn - Receives the assigned VBN
|
|||
|
|
|||
|
AlignedSectorCount - Receives the actual sector count created in the
|
|||
|
Vmcb for page alignment purposes. Vbn+AlignedSectorCount-1 == LastVbn.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if this is a new mapping and FALSE if the mapping
|
|||
|
for the LBN already exists. If it already exists then the
|
|||
|
sector count for this new addition must already be in the
|
|||
|
VMCB structure
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
BOOLEAN Result;
|
|||
|
|
|||
|
BOOLEAN VbnMcbAdded = FALSE;
|
|||
|
BOOLEAN LbnMcbAdded = FALSE;
|
|||
|
BOOLEAN AllowRoundToPage;
|
|||
|
|
|||
|
LBN LocalLbn;
|
|||
|
VBN LocalVbn;
|
|||
|
ULONG LocalCount;
|
|||
|
LARGE_INTEGER Offset;
|
|||
|
PVCB Vcb;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace(( +1, Dbg, "UdfAddVmcbMapping, Lbn = %08x\n", Lbn ));
|
|||
|
DebugTrace(( 0, Dbg, " SectorCount = %08x\n", SectorCount ));
|
|||
|
|
|||
|
ASSERT( SectorCount == 1 );
|
|||
|
ASSERT_IRP_CONTEXT( IrpContext);
|
|||
|
|
|||
|
Vcb = IrpContext->Vcb;
|
|||
|
|
|||
|
//
|
|||
|
// Now grab the resource exclusive
|
|||
|
//
|
|||
|
|
|||
|
VmcbLockForModify( Vmcb);
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Check if the Lbn is already mapped, which means we find an entry
|
|||
|
// with a non zero mapping Vbn value.
|
|||
|
//
|
|||
|
|
|||
|
if (UdfVmcbLookupMcbEntry( &Vmcb->LbnIndexed,
|
|||
|
Lbn,
|
|||
|
Vbn,
|
|||
|
&LocalCount,
|
|||
|
NULL )) {
|
|||
|
|
|||
|
//
|
|||
|
// It is already mapped so now the sector count must not exceed
|
|||
|
// the count already in the run
|
|||
|
//
|
|||
|
|
|||
|
if (SectorCount <= LocalCount) {
|
|||
|
|
|||
|
DebugTrace(( 0, Dbg, "Already mapped (Vbn == 0x%08x)\n", *Vbn));
|
|||
|
|
|||
|
*AlignedSectorCount = LocalCount;
|
|||
|
try_leave( Result = FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Trying to add overlapping extents indicates overlapping structures...
|
|||
|
//
|
|||
|
|
|||
|
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If there is a VAT in use, then we treat the media as CDR style, and don't
|
|||
|
// round/align extents to page boundries, since this could include (unreadable)
|
|||
|
// packet leadin/out sectors.
|
|||
|
//
|
|||
|
|
|||
|
AllowRoundToPage = (NULL == Vcb->VatFcb);
|
|||
|
|
|||
|
//
|
|||
|
// At this point, we did not find a full existing mapping for the
|
|||
|
// Lbn and count. But there might be some overlapping runs that we'll
|
|||
|
// need to now remove from the vmcb structure. So for each Lbn in
|
|||
|
// the range we're after, check to see if it is mapped and remove the
|
|||
|
// mapping. We only need to do this test if the sector count is less
|
|||
|
// than or equal to a page size. Because those are the only
|
|||
|
// structures that we know we'll try an remove/overwrite.
|
|||
|
//
|
|||
|
|
|||
|
if (SectorCount <= PadSectorCountToPage(Vmcb, 1)) {
|
|||
|
|
|||
|
if (UdfVmcbLookupMcbEntry( &Vmcb->LbnIndexed,
|
|||
|
Lbn,
|
|||
|
Vbn,
|
|||
|
&LocalCount,
|
|||
|
NULL )) {
|
|||
|
|
|||
|
UdfRemoveVmcbMapping( Vmcb, *Vbn, PadSectorCountToPage(Vmcb, 1) );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We need to add this new run at the end of the Vbns
|
|||
|
//
|
|||
|
|
|||
|
if (!FsRtlLookupLastMcbEntry( &Vmcb->VbnIndexed, &LocalVbn, &LocalLbn )) {
|
|||
|
|
|||
|
//
|
|||
|
// Vmcb is currently empty.
|
|||
|
//
|
|||
|
|
|||
|
LocalVbn = -1;
|
|||
|
}
|
|||
|
|
|||
|
if (!AllowRoundToPage) {
|
|||
|
|
|||
|
//
|
|||
|
// So this volume may have unreadable sectors on it (eg CDR packet written)
|
|||
|
// and so we extend the vmcb one sector at a time, only including sectors
|
|||
|
// which we're specifically asked for, and hence know that we should be
|
|||
|
// able to read.
|
|||
|
//
|
|||
|
// We simply use the next available VSN, purging the last vmcb page if
|
|||
|
// neccessary (we're adding sectors to it), and don't page align the lbn
|
|||
|
// or sectorcount.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( 1 == SectorCount);
|
|||
|
|
|||
|
LocalVbn += 1;
|
|||
|
LocalLbn = Lbn;
|
|||
|
LocalCount = SectorCount;
|
|||
|
|
|||
|
if (!IsPageAligned( Vmcb, LocalVbn)) {
|
|||
|
|
|||
|
//
|
|||
|
// The next VSN is not at the beginning of a page (ie: the last page
|
|||
|
// in the vmcb has space in it for more sectors), so purge this
|
|||
|
// page in the metadata stream before updating the mapping information.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( Vcb && Vcb->MetadataFcb );
|
|||
|
|
|||
|
Offset.QuadPart = (ULONGLONG) BytesFromSectors( IrpContext->Vcb, AlignToPageBase( Vmcb, LocalVbn) );
|
|||
|
|
|||
|
//
|
|||
|
// Block until all mappings through the vmcb stream have been dropped
|
|||
|
// before attempting the purge
|
|||
|
//
|
|||
|
|
|||
|
UdfAcquireVmcbForCcPurge( IrpContext, IrpContext->Vcb);
|
|||
|
|
|||
|
CcPurgeCacheSection( IrpContext->Vcb->MetadataFcb->FileObject->SectionObjectPointer,
|
|||
|
&Offset,
|
|||
|
PAGE_SIZE,
|
|||
|
FALSE );
|
|||
|
|
|||
|
UdfReleaseVmcb( IrpContext, IrpContext->Vcb);
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
|
|||
|
//
|
|||
|
// All sectors on this volume should be readable, so we always extend the
|
|||
|
// vmcb a page at a time, hoping that metadata will be packed sensibly.
|
|||
|
// Because we always extend in page chunks, LocalVbn will be the last VSN
|
|||
|
// in a page aligned block, so +1 lands on the next page (aligned VSN) in
|
|||
|
// the VMCB stream.
|
|||
|
//
|
|||
|
|
|||
|
LocalVbn += 1;
|
|||
|
LocalLbn = AlignToPageBase( Vmcb, Lbn);
|
|||
|
LocalCount = PadSectorCountToPage( Vmcb, SectorCount + (Lbn - LocalLbn));
|
|||
|
|
|||
|
ASSERT( IsPageAligned( Vmcb, LocalVbn));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Add the double mapping
|
|||
|
//
|
|||
|
|
|||
|
if (!FsRtlAddMcbEntry( &Vmcb->VbnIndexed,
|
|||
|
LocalVbn,
|
|||
|
LocalLbn,
|
|||
|
LocalCount )) {
|
|||
|
|
|||
|
UdfRaiseStatus( IrpContext, STATUS_INTERNAL_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
VbnMcbAdded = TRUE;
|
|||
|
|
|||
|
if (!FsRtlAddMcbEntry( &Vmcb->LbnIndexed,
|
|||
|
LocalLbn,
|
|||
|
LocalVbn,
|
|||
|
LocalCount )) {
|
|||
|
|
|||
|
UdfRaiseStatus( IrpContext, STATUS_INTERNAL_ERROR);
|
|||
|
}
|
|||
|
|
|||
|
LbnMcbAdded = TRUE;
|
|||
|
|
|||
|
*Vbn = LocalVbn + (Lbn - LocalLbn);
|
|||
|
*AlignedSectorCount = LocalCount - (Lbn - LocalLbn);
|
|||
|
|
|||
|
Result = TRUE;
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
//
|
|||
|
// If this is an abnormal termination then clean up any mcb's that we
|
|||
|
// might have modified.
|
|||
|
//
|
|||
|
|
|||
|
if (AbnormalTermination()) {
|
|||
|
|
|||
|
if (VbnMcbAdded) { FsRtlRemoveMcbEntry( &Vmcb->VbnIndexed, LocalVbn, LocalCount ); }
|
|||
|
if (LbnMcbAdded) { FsRtlRemoveMcbEntry( &Vmcb->LbnIndexed, LocalLbn, LocalCount ); }
|
|||
|
}
|
|||
|
|
|||
|
VmcbRelease( Vmcb);
|
|||
|
|
|||
|
DebugUnwind("UdfAddVmcbMapping");
|
|||
|
|
|||
|
if (Result) {
|
|||
|
|
|||
|
DebugTrace(( 0, Dbg, " LocalVbn = %08x\n", LocalVbn ));
|
|||
|
DebugTrace(( 0, Dbg, " LocalLbn = %08x\n", LocalLbn ));
|
|||
|
DebugTrace(( 0, Dbg, " LocalCount = %08x\n", LocalCount ));
|
|||
|
DebugTrace(( 0, Dbg, " *Vbn = %08x\n", *Vbn ));
|
|||
|
DebugTrace(( 0, Dbg, " *AlignedSectorCount = %08x\n", *AlignedSectorCount ));
|
|||
|
}
|
|||
|
|
|||
|
DebugTrace((-1, Dbg, "UdfAddVmcbMapping -> %08x\n", Result ));
|
|||
|
}
|
|||
|
|
|||
|
return Result;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
UdfRemoveVmcbMapping (
|
|||
|
IN PVMCB Vmcb,
|
|||
|
IN VBN Vbn,
|
|||
|
IN ULONG SectorCount
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine removes a Vmcb mapping.
|
|||
|
|
|||
|
If pool is not available to store the information this routine will
|
|||
|
raise a status value indicating insufficient resources.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vmcb - Supplies the Vmcb being updated.
|
|||
|
|
|||
|
Vbn - Supplies the VBN to remove
|
|||
|
|
|||
|
SectorCount - Supplies the number of sectors to remove.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LBN Lbn;
|
|||
|
ULONG LocalCount;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace((+1, Dbg, "UdfRemoveVmcbMapping, Vbn = %08x\n", Vbn ));
|
|||
|
DebugTrace(( 0, Dbg, " SectorCount = %08x\n", SectorCount ));
|
|||
|
|
|||
|
//
|
|||
|
// Now grab the resource exclusive
|
|||
|
//
|
|||
|
|
|||
|
VmcbLockForModify( Vmcb);
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
for (i = 0; i < SectorCount; i += 1) {
|
|||
|
|
|||
|
//
|
|||
|
// Lookup the Vbn so we can get its current Lbn mapping
|
|||
|
//
|
|||
|
|
|||
|
if (!UdfVmcbLookupMcbEntry( &Vmcb->VbnIndexed,
|
|||
|
Vbn + i,
|
|||
|
&Lbn,
|
|||
|
&LocalCount,
|
|||
|
NULL )) {
|
|||
|
|
|||
|
UdfBugCheck( 0, 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
FsRtlRemoveMcbEntry( &Vmcb->VbnIndexed,
|
|||
|
Vbn + i,
|
|||
|
1 );
|
|||
|
|
|||
|
FsRtlRemoveMcbEntry( &Vmcb->LbnIndexed,
|
|||
|
Lbn,
|
|||
|
1 );
|
|||
|
}
|
|||
|
|
|||
|
{
|
|||
|
DebugTrace(( 0, Dbg, "VbnIndex:\n", 0 ));
|
|||
|
DebugTrace(( 0, Dbg, "LbnIndex:\n", 0 ));
|
|||
|
}
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
VmcbRelease( Vmcb);
|
|||
|
|
|||
|
DebugUnwind( "UdfRemoveVmcbMapping" );
|
|||
|
DebugTrace(( -1, Dbg, "UdfRemoveVmcbMapping -> VOID\n" ));
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Local support routine
|
|||
|
//
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
UdfVmcbLookupMcbEntry (
|
|||
|
IN PMCB Mcb,
|
|||
|
IN VBN Vbn,
|
|||
|
OUT PLBN Lbn,
|
|||
|
OUT PULONG SectorCount OPTIONAL,
|
|||
|
OUT PULONG Index OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine retrieves the mapping of a Vbn to an Lbn from an Mcb.
|
|||
|
It indicates if the mapping exists and the size of the run.
|
|||
|
|
|||
|
The only difference betweent this and the regular FsRtlLookupMcbEntry
|
|||
|
is that we undo the behavior of returning TRUE in holes in the allocation.
|
|||
|
This is because we don't want to avoid mapping at Lbn 0, which is how the
|
|||
|
emulated behavior of the small Mcb package tells callers that there is no
|
|||
|
mapping at that location in a hole. We have holes all over our Vbn space
|
|||
|
in the VbnIndexed map.
|
|||
|
|
|||
|
The small Mcb package was able to get away with this because Lbn 0 was the
|
|||
|
boot sector (or similar magic location) on the disc. In our metadata stream,
|
|||
|
we wish to use Vbn 0 (remember this is a double map).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Mcb - Supplies the Mcb being examined.
|
|||
|
|
|||
|
Vbn - Supplies the Vbn to lookup.
|
|||
|
|
|||
|
Lbn - Receives the Lbn corresponding to the Vbn. A value of -1 is
|
|||
|
returned if the Vbn does not have a corresponding Lbn.
|
|||
|
|
|||
|
SectorCount - Receives the number of sectors that map from the Vbn to
|
|||
|
contiguous Lbn values beginning with the input Vbn.
|
|||
|
|
|||
|
Index - Receives the index of the run found.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
BOOLEAN - TRUE if the Vbn is within the range of VBNs mapped by the
|
|||
|
MCB (not if it corresponds to a hole in the mapping), and FALSE
|
|||
|
if the Vbn is beyond the range of the MCB's mapping.
|
|||
|
|
|||
|
For example, if an MCB has a mapping for VBNs 5 and 7 but not for
|
|||
|
6, then a lookup on Vbn 5 or 7 will yield a non zero Lbn and a sector
|
|||
|
count of 1. A lookup for Vbn 6 will return FALSE with an Lbn value of
|
|||
|
0, and lookup for Vbn 8 or above will return FALSE.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN Results;
|
|||
|
LONGLONG LiLbn = 0;
|
|||
|
LONGLONG LiSectorCount = 0;
|
|||
|
|
|||
|
Results = FsRtlLookupLargeMcbEntry( (PLARGE_MCB)Mcb,
|
|||
|
(LONGLONG)(Vbn),
|
|||
|
&LiLbn,
|
|||
|
ARGUMENT_PRESENT(SectorCount) ? &LiSectorCount : NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
Index );
|
|||
|
|
|||
|
if ((ULONG)LiLbn == -1) {
|
|||
|
|
|||
|
*Lbn = 0;
|
|||
|
Results = FALSE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
*Lbn = (ULONG)LiLbn;
|
|||
|
}
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(SectorCount)) { *SectorCount = ((ULONG)LiSectorCount); }
|
|||
|
|
|||
|
return Results;
|
|||
|
}
|
|||
|
|
|||
|
|