Windows2000/private/ntos/udfs/vmcbsup.c
2020-09-30 17:12:32 +02:00

1071 lines
36 KiB
C

/*++
Copyright (c) 1989 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.
#if VMCB_WRITE_SUPPORT
o PbSetDirtyVmcb - This routine is used to mark sectors dirty in the volume file.
o PbSetCleanVmcb - This routine is used to mark sectors clean in the volume file.
o PbGetDirtySectorsVmcb - This routine is used to retrieve the dirty sectors for a page in the volume file.
o PbGetAndCleanDirtyVmcb - This routine is used to retrieve the
dirty sectors for a page in the volume file and atomically clear the dirty sectors.
#endif // VMCB_WRITE_SUPPORT
Authors:
Gary Kimura [GaryKi] 4-Apr-1990
Dan Lovinger [DanLo] 10-Sep-1996
--*/
#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)
// Turn off write/dirty/clean sector support
#define VMCB_WRITE_SUPPORT 0
// The following macro is used to calculate the number of pages (in terms of
// sectors) needed to contain a given sector count. For example,
// PageAlign( 0 Sectors ) = 0 Pages = 0 Sectors
// PageAlign( 1 Sectors ) = 1 Page = 8 Sectors
// PageAlign( 2 Sectors ) = 1 Page = 8 Sectors
#define PageAlign(V, L) ((((L)+((PAGE_SIZE/(V)->SectorSize)-1))/(PAGE_SIZE/(V)->SectorSize))*(PAGE_SIZE/(V)->SectorSize))
#if VMCB_WRITE_SUPPORT
// The following constant is a bit mask, with one bit set for each sector
// that'll fit in a page (4K page, 8 bits; 8K page, 16 bits, etc)
#define SECTOR_MASK ((1 << (PAGE_SIZE / sizeof (SECTOR))) - 1)
// The Dirty Page structure are elements in the dirty table generic table.
// This is followed by the procedure prototypes for the local generic table routines
typedef struct _DIRTY_PAGE
{
ULONG LbnPageNumber;
ULONG DirtyMask;
} DIRTY_PAGE;
typedef DIRTY_PAGE *PDIRTY_PAGE;
RTL_GENERIC_COMPARE_RESULTS PbCompareDirtyVmcb(IN PRTL_GENERIC_TABLE DirtyTable, IN PVOID FirstStruct, IN PVOID SecondStruct);
PVOID PbAllocateDirtyVmcb(IN PRTL_GENERIC_TABLE DirtyTable, IN CLONG ByteSize);
VOID PbDeallocateDirtyVmcb(IN PRTL_GENERIC_TABLE DirtyTable, IN PVOID Buffer);
ULONG PbDumpDirtyVmcb(IN PVMCB Vmcb);
#endif // VMCB_WRITE_SUPPORT
// 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)
#if VMCB_WRITE_SUPPORT
#pragma alloc_text(PAGE, PbAllocateDirtyVmcb)
#pragma alloc_text(PAGE, PbDeallocateDirtyVmcb)
#pragma alloc_text(PAGE, PbDumpDirtyVmcb)
#pragma alloc_text(PAGE, PbGetAndCleanDirtyVmcb)
#endif // VMCB_WRITE_SUPPORT
#pragma alloc_text(PAGE, UdfInitializeVmcb)
#pragma alloc_text(PAGE, UdfRemoveVmcbMapping)
#pragma alloc_text(PAGE, UdfResetVmcb)
#if VMCB_WRITE_SUPPORT
#pragma alloc_text(PAGE, PbSetCleanVmcb)
#pragma alloc_text(PAGE, PbSetDirtyVmcb)
#endif // VMCB_WRITE_SUPPORT
#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
KeInitializeMutex(&Vmcb->Mutex, 0);
FsRtlInitializeMcb(&Vmcb->VbnIndexed, PoolType);
VbnInitialized = TRUE;
FsRtlInitializeMcb(&Vmcb->LbnIndexed, PoolType);
LbnInitialized = TRUE;
Vmcb->MaximumLbn = MaximumLbn;
Vmcb->SectorSize = SectorSize;
#if VMCB_WRITE_SUPPORT
// For the dirty table we store in the table context field the pool
// type to use for allocating additional structures
RtlInitializeGenericTable(&Vmcb->DirtyTable, PbCompareDirtyVmcb, PbAllocateDirtyVmcb, PbDeallocateDirtyVmcb, (PVOID)PoolType);
#endif // VMCB_WRITE_SUPPORT
} 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"));
}
// And return to our caller
return;
}
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);
// And return to our caller
DebugTrace((-1, Dbg, "UdfUninitializeVmcb -> VOID\n"));
}
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"));
}
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"));
}
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 mutex for the vmcb
(VOID)KeWaitForSingleObject(&Vmcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
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{
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
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 mutex for the vmcb
(VOID)KeWaitForSingleObject(&Vmcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
try {
Result = UdfVmcbLookupMcbEntry(&Vmcb->LbnIndexed, Lbn, Vbn, SectorCount, NULL);
DebugTrace((0, Dbg, "*Vbn = %08x\n", *Vbn));
} finally{
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
DebugUnwind("UdfVmcbLbnToVbn");
DebugTrace((-1, Dbg, "UdfVmcbLbnToVbn -> Result = %08x\n", Result));
}
return Result;
}
BOOLEAN UdfAddVmcbMapping(
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.
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
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;
BOOLEAN LbnMcbAdded;
LBN LocalLbn;
VBN LocalVbn;
ULONG LocalCount;
PAGED_CODE();
DebugTrace((+1, Dbg, "UdfAddVmcbMapping, Lbn = %08x\n", Lbn));
DebugTrace((0, Dbg, " SectorCount = %08x\n", SectorCount));
ASSERT(SectorCount != 0);
VbnMcbAdded = FALSE;
LbnMcbAdded = FALSE;
// Now grab the mutex for the vmcb
(VOID)KeWaitForSingleObject(&Vmcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
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) {
try_leave(Result = FALSE);
}
}
// 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 <= PageAlign(Vmcb, 1)) {
if (UdfVmcbLookupMcbEntry(&Vmcb->LbnIndexed, Lbn, Vbn, &LocalCount, NULL)) {
UdfRemoveVmcbMapping(Vmcb, *Vbn, PageAlign(Vmcb, 1));
}
}
// We need to add this new run at the end of the Vbns. To do this we
// need to look up the last mcb entry or use a vbn for the second
// page, if the mcb is empty. We'll also special case the situation
// where the last lbn of the mapping and the mapping we're adding
// simply flow into each other in which case we'll not bother bumping
// the vbn to a page alignment
if (FsRtlLookupLastMcbEntry(&Vmcb->VbnIndexed, &LocalVbn, &LocalLbn)) {
if (LocalLbn + 1 == Lbn) {
LocalVbn = LocalVbn + 1;
LocalLbn = LocalLbn + 1;
} else {
// Get the next available Vbn Page, and calculate the Lbn for the page containing the Lbn
LocalVbn = PageAlign(Vmcb, LocalVbn + 1);
LocalLbn = PageAlign(Vmcb, Lbn + 1) - PageAlign(Vmcb, 1);
}
} else {
// Get the first available Vbn page, and calculate the Lbn for the page containing the Lbn.
LocalVbn = 0;
LocalLbn = PageAlign(Vmcb, Lbn + 1) - PageAlign(Vmcb, 1);
}
// Calculate the number of sectors that we need to map to keep
// everything on a page granularity.
LocalCount = PageAlign(Vmcb, SectorCount + (Lbn - LocalLbn));
// See if we should use a hole to map the alignment at the end of the request.
if (ExactEnd && Lbn + SectorCount < LocalLbn + LocalCount) {
LocalCount = SectorCount + (Lbn - LocalLbn);
}
// Add the double mapping
FsRtlAddMcbEntry(&Vmcb->VbnIndexed, LocalVbn, LocalLbn, LocalCount);
VbnMcbAdded = TRUE;
FsRtlAddMcbEntry(&Vmcb->LbnIndexed, LocalLbn, LocalVbn, LocalCount);
LbnMcbAdded = TRUE;
*Vbn = LocalVbn + (Lbn - LocalLbn);
*AlignedSectorCount = LocalCount - (Lbn - LocalLbn);
try_leave(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);
}
}
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
DebugUnwind("UdfAddVmcbMapping");
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 mutex for the vmcb
(VOID)KeWaitForSingleObject(&Vmcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
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{
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
DebugUnwind("UdfRemoveVmcbMapping");
DebugTrace((-1, Dbg, "UdfRemoveVmcbMapping -> VOID\n"));
}
}
// 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;
LONGLONG LiSectorCount;
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;
}
#if VMCB_WRITE_SUPPORT
VOID PbSetDirtyVmcb(IN PVMCB Vmcb, IN ULONG LbnPageNumber, IN ULONG Mask)
/*++
Routine Description:
This routine sets the sectors within a page as dirty based on the input mask.
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 manipulated.
LbnPageNumber - Supplies the Page Number (LBN based) of the page being
modified. For example, with a page size of 8 a page number of 0
corresponds to LBN values 0 through 7, a page number of 1 corresponds
to 8 through 15, and so on.
Mask - Supplies the mask of dirty sectors to set for the Page (a 1 bit
means to set it dirty). For example to set LBN 9 dirty on a system
with a page size of 8 the LbnPageNumber will be 1, and the mask will be 0x00000002.
Return Value:
None.
--*/
{
DIRTY_PAGE Key;
PDIRTY_PAGE Entry;
PAGED_CODE();
DebugTrace(+1, Dbg, ("UdfSetDirtyVmcb\n", 0));
DebugTrace(0, Dbg, (" LbnPageNumber = %08x\n", LbnPageNumber));
DebugTrace(0, Dbg, (" Mask = %08x\n", Mask));
Key.LbnPageNumber = LbnPageNumber;
Key.DirtyMask = 0;
// Now grab the mutex for the vmcb
(VOID)KeWaitForSingleObject(&Vmcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
try {
Entry = RtlInsertElementGenericTable(&Vmcb->DirtyTable, &Key, sizeof(DIRTY_PAGE), NULL);
Entry->DirtyMask = (Entry->DirtyMask | Mask) & (SECTOR_MASK); //**** change to manifest constant
DebugTrace(0, Dbg, ("DirtyMask = %08x\n", Entry->DirtyMask));
{
DebugTrace(0, Dbg, ("", PbDumpDirtyVmcb(Vmcb)));
}
} finally{
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
DebugUnwind("UdfSetDirtyVmcb");
DebugTrace(-1, Dbg, ("UdfSetDirtyVmcb -> VOID\n", 0));
}
return;
}
#endif // VMCB_WRITE_SUPPORT
#if VMCB_WRITE_SUPPORT
VOID PbSetCleanVmcb(IN PVMCB Vmcb, IN ULONG LbnPageNumber, IN ULONG Mask)
/*++
Routine Description:
This routine sets all of the sectors within a page as clean. All
of the sectors in a page whether they are dirty or not are set clean by this procedure.
Arguments:
Vmcb - Supplies the Vmcb being manipulated.
LbnPageNumber - Supplies the Page Number (Lbn based) of page being
modified. For example, with a page size of 8 a page number of 0
corresponds to LBN values 0 through 7, a page number of 1 corresponds to 8 through 15, and so on.
Mask - Supplies the mask of clean sectors to set for the Page (a 1 bit
means to set it clean). For example to set LBN 9 clean on a system
with a page size of 8 the LbnPageNumber will be 1, and the mask will be 0x00000002.
Return Value:
None.
--*/
{
DIRTY_PAGE Key;
PDIRTY_PAGE Entry;
PAGED_CODE();
DebugTrace(+1, Dbg, ("UdfSetCleanVmcb\n", 0));
DebugTrace(0, Dbg, (" LbnPageNumber = %08x\n", LbnPageNumber));
DebugTrace(0, Dbg, (" Mask = %08x\n", Mask));
Key.LbnPageNumber = LbnPageNumber;
// Now grab the mutex for the vmcb
(VOID)KeWaitForSingleObject(&Vmcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
try {
// If the page is not in the table, it is already all clean
if (Entry = RtlLookupElementGenericTable(&Vmcb->DirtyTable, &Key)) {
Entry->DirtyMask &= ~Mask;
DebugTrace(0, Dbg, ("DirtyMask = %08x\n", Entry->DirtyMask));
// If the mask is all clean now, delete the entry
if (Entry->DirtyMask == 0) {
(VOID)RtlDeleteElementGenericTable(&Vmcb->DirtyTable, &Key);
}
}
{
DebugTrace(0, Dbg, ("", PbDumpDirtyVmcb(Vmcb)));
}
} finally{
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
DebugTrace(-1, Dbg, ("UdfSetCleanVcmb -> VOID\n", 0));
}
return;
}
#endif // VMCB_WRITE_SUPPORT
#if VMCB_WRITE_SUPPORT
ULONG PbGetDirtySectorsVmcb(IN PVMCB Vmcb, IN ULONG LbnPageNumber)
/*++
Routine Description:
This routine returns to its caller a mask of dirty sectors within a page.
Arguments:
Vmcb - Supplies the Vmcb being manipulated
LbnPageNumber - Supplies the Page Number (Lbn based) of page being
modified. For example, with a page size of 8 a page number of 0
corresponds to LBN values 0 through 7, a page number of 1 corresponds to 8 through 15, and so on.
Return Value:
ULONG - Receives a mask of dirty sectors within the specified page.
(a 1 bit indicates that the sector is dirty).
--*/
{
DIRTY_PAGE Key;
PDIRTY_PAGE Entry;
ULONG Mask;
DebugTrace(+1, Dbg, ("UdfGetDirtySectorsVmcb\n", 0));
DebugTrace(0, Dbg, (" LbnPageNumber = %08x\n", LbnPageNumber));
Key.LbnPageNumber = LbnPageNumber;
// Now grab the mutex for the vmcb
(VOID)KeWaitForSingleObject(&Vmcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
try {
if ((Entry = RtlLookupElementGenericTable(&Vmcb->DirtyTable, &Key)) == NULL) {
DebugTrace(0, Dbg, ("Entry not found\n", 0));
try_leave(Mask = 0);
}
Mask = Entry->DirtyMask & (SECTOR_MASK); //**** change to manifest constant
} finally{
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
DebugTrace(-1, Dbg, ("UdfGetDirtySectorsVmcb -> %08x\n", Mask));
}
return Mask;
}
#endif // VMCB_WRITE_SUPPORT
#if VMCB_WRITE_SUPPORT
ULONG PbGetAndCleanDirtyVmcb(IN PVMCB Vmcb, IN ULONG LbnPageNumber)
/*++
Routine Description:
This routine returns to its caller a mask of dirty sectors within a page, and atomically clear the bits.
Arguments:
Vmcb - Supplies the Vmcb being manipulated
LbnPageNumber - Supplies the Page Number (Lbn based) of page being
modified. For example, with a page size of 8 a page number of 0
corresponds to LBN values 0 through 7, a page number of 1 corresponds to 8 through 15, and so on.
Return Value:
ULONG - Receives a mask of dirty sectors within the specified page.
(a 1 bit indicates that the sector is dirty).
--*/
{
DIRTY_PAGE Key;
PDIRTY_PAGE Entry;
ULONG Mask;
PAGED_CODE();
DebugTrace(+1, Dbg, ("UdfGetAndCleanDirtyVmcb\n", 0));
DebugTrace(0, Dbg, (" LbnPageNumber = %08x\n", LbnPageNumber));
Key.LbnPageNumber = LbnPageNumber;
// Now grab the mutex for the vmcb
(VOID)KeWaitForSingleObject(&Vmcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
try {
// Locate the dirty page within the dirty table
if ((Entry = RtlLookupElementGenericTable(&Vmcb->DirtyTable, &Key)) == NULL) {
DebugTrace(0, Dbg, ("Entry not found\n", 0));
try_leave(Mask = 0);
}
// We found a page so generate a proper mask and then delete the dirty page
Mask = Entry->DirtyMask & (SECTOR_MASK); //**** change to manifest constant
(VOID)RtlDeleteElementGenericTable(&Vmcb->DirtyTable, &Key);
} finally{
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
DebugTrace(-1, Dbg, ("UdfGetAndCleanDirtyVmcb -> %08x\n", Mask));
}
return Mask;
}
#endif // VMCB_WRITE_SUPPORT
// Local support routines
#if VMCB_WRITE_SUPPORT
RTL_GENERIC_COMPARE_RESULTS PbCompareDirtyVmcb(IN PRTL_GENERIC_TABLE DirtyTable,
IN PVOID FirstStruct,
IN PVOID SecondStruct)
/*++
Routine Description:
This generic table support routine compares two dirty page structures
Arguments:
DirtyTable - Supplies the generic table being queried
FirstStruct - Really supplies the first structure to compare
SecondStruct - Really supplies the second structure to compare
Return Value:
RTL_GENERIDC_COMPARE_RESULTS - The results of comparing the two input structures
--*/
{
PDIRTY_PAGE DirtyPage1 = FirstStruct;
PDIRTY_PAGE DirtyPage2 = SecondStruct;
UNREFERENCED_PARAMETER(DirtyTable);
PAGED_CODE();
if (DirtyPage1->LbnPageNumber < DirtyPage2->LbnPageNumber) {
return GenericLessThan;
} else if (DirtyPage1->LbnPageNumber > DirtyPage2->LbnPageNumber) {
return GenericGreaterThan;
} else {
return GenericEqual;
}
}
#endif // VMCB_WRITE_SUPPORT
// Local support routines
#if VMCB_WRITE_SUPPORT
PVOID PbAllocateDirtyVmcb(IN PRTL_GENERIC_TABLE DirtyTable, IN CLONG ByteSize)
/*++
Routine Description:
This generic table support routine allocates memory
Arguments:
DirtyTable - Supplies the generic table being modified
ByteSize - Supplies the size, in bytes, to allocate
Return Value:
PVOID - Returns a pointer to the allocated data
--*/
{
PAGED_CODE();
return FsRtlAllocatePoolWithTag((POOL_TYPE)DirtyTable->TableContext, ByteSize, 'bcmV');
}
#endif // VMCB_WRITE_SUPPORT
// Local support routines
#if VMCB_WRITE_SUPPORT
VOID PbDeallocateDirtyVmcb(IN PRTL_GENERIC_TABLE DirtyTable, IN PVOID Buffer)
/*++
Routine Description:
This generic table support routine deallocates memory
Arguments:
DirtyTable - Supplies the generic table being modified
Buffer - Supplies the buffer being deallocated
Return Value:
None.
--*/
{
UNREFERENCED_PARAMETER(DirtyTable);
PAGED_CODE();
ExFreePool(Buffer);
return;
}
#endif // VMCB_WRITE_SUPPORT
// Local support routines
#if VMCB_WRITE_SUPPORT
ULONG PbDumpDirtyVmcb(IN PVMCB Vmcb)
{
PDIRTY_PAGE Ptr;
PAGED_CODE();
KdPrint((" Dump Dirty Vmcb\n"));
for (Ptr = RtlEnumerateGenericTable(&Vmcb->DirtyTable, TRUE);
Ptr != NULL;
Ptr = RtlEnumerateGenericTable(&Vmcb->DirtyTable, FALSE)) {
KdPrint((" LbnPageNumber = %08x, ", Ptr->LbnPageNumber));
KdPrint(("DirtyMask = %08x\n", Ptr->DirtyMask));
}
return 0;
}
#endif // VMCB_WRITE_SUPPORT
#if VMCB_WRITE_SUPPORT
NTSTATUS PbFlushVolumeFile(IN PIRP_CONTEXT IrpContext, IN PVCB Vcb)
/*++
Routine Description:
The function carefully flushes the entire volume file. It is nessecary
to dance around a bit because of complicated synchronization reasons.
Arguments:
Vcb - Supplies the Vcb being flushed
Return Value:
NTSTATUS - The status of the flush operation
--*/
{
ULONG ElementNumber;
ULONG NumberOfDirtyPages;
PULONG VbnsToFlush;
LBN Lbn;
PDIRTY_PAGE Ptr;
NTSTATUS ReturnStatus = STATUS_SUCCESS;
PVMCB Vmcb = (PNONOPAQUE_VMCB)&Vcb->Vmcb;
// The only way we have to correctly synchronize things is to
// repin stuff, and then unpin repin it with WriteThrough as TRUE.
// Grab the mutex for the vmcb
(VOID)KeWaitForSingleObject(&Vmcb->Mutex, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL);
NumberOfDirtyPages = RtlNumberGenericTableElements(&Vmcb->DirtyTable);
// If there are no dirty sectors, no need to flush.
if (NumberOfDirtyPages == 0) {
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
return STATUS_SUCCESS;
}
try {
VbnsToFlush = FsRtlAllocatePoolWithTag(PagedPool, NumberOfDirtyPages * sizeof(ULONG), 'bcmV');
} finally{
if (AbnormalTermination()) {
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
}
}
for (Ptr = RtlEnumerateGenericTable(&Vmcb->DirtyTable, TRUE), ElementNumber = 0;
Ptr != NULL;
Ptr = RtlEnumerateGenericTable(&Vmcb->DirtyTable, FALSE), ElementNumber += 1) {
VBN Vbn;
BOOLEAN Result;
// Lbn pages always map to Vbn pages. Thus any sector in an Lbn
// page will map to the same Vbn page. So it suffices to map the
// first Lbn in the page to a Vbn and flush that page.
Lbn = Ptr->LbnPageNumber * (PAGE_SIZE / 512);
ASSERT(Ptr->DirtyMask != 0);
Result = PbVmcbLbnToVbn(&Vcb->Vmcb, Lbn, &Vbn, NULL);
// This lookup must work as the LBN page was dirty.
if (!Result) {
PbBugCheck(0, 0, 0);
}
// Bring store this Vbn away for flushing later.
ASSERT(ElementNumber < NumberOfDirtyPages);
ASSERT((Vbn & (PAGE_SIZE / 512 - 1)) == 0);
VbnsToFlush[ElementNumber] = Vbn;
}
ASSERT(ElementNumber == NumberOfDirtyPages);
// Now drop the mutex and walk through the dirty Vbn list generated
// above. We cannot hold the mutex while doing IO as this will cause
// a deadlock with the cache manager.
(VOID)KeReleaseMutex(&Vmcb->Mutex, FALSE);
for (ElementNumber = 0; ElementNumber < NumberOfDirtyPages; ElementNumber += 1) {
PBCB Bcb;
PVOID DontCare;
LARGE_INTEGER Offset;
IO_STATUS_BLOCK Iosb;
// This page is dirty. Flush it by writing it though.
Offset.QuadPart = VbnsToFlush[ElementNumber] << 9;
try {
(VOID)CcPinRead(Vcb->VirtualVolumeFile, &Offset, PAGE_SIZE, TRUE, &Bcb, &DontCare);
CcSetDirtyPinnedData(Bcb, NULL);
CcRepinBcb(Bcb);
CcUnpinData(Bcb);
CcUnpinRepinnedBcb(Bcb, TRUE, &Iosb);
if (!NT_SUCCESS(Iosb.Status)) {
ReturnStatus = Iosb.Status;
}
} except(PbExceptionFilter(IrpContext, GetExceptionInformation())) {
ReturnStatus = IrpContext->ExceptionStatus;
}
}
ExFreePool(VbnsToFlush);
return ReturnStatus;
}
#endif // VMCB_WRITE_SUPPORT