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

1294 lines
32 KiB
C

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
pinsup.c
Abstract:
This module implements the pointer-based Pin support routines for the
Cache subsystem.
Author:
Tom Miller [TomM] 4-June-1990
Revision History:
--*/
#include "cc.h"
// Define our debug constant
#define me 0x00000008
#if LIST_DBG
#define SetCallersAddress(BCB) { \
RtlGetCallersAddress( &(BCB)->CallerAddress, \
&(BCB)->CallersCallerAddress ); \
}
#endif
// Internal routines
POBCB
CcAllocateObcb (
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN PBCB FirstBcb
);
BOOLEAN
CcMapData (
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Wait,
OUT PVOID *Bcb,
OUT PVOID *Buffer
)
/*++
Routine Description:
This routine attempts to map the specified file data in the cache.
A pointer is returned to the desired data in the cache.
If the caller does not want to block on this call, then
Wait should be supplied as FALSE. If Wait was supplied as FALSE and
it is currently impossible to supply the requested data without
blocking, then this routine will return FALSE. However, if the
data is immediately accessible in the cache and no blocking is
required, this routine returns TRUE with a pointer to the data.
Note that a call to this routine with Wait supplied as TRUE is
considerably faster than a call with Wait supplies as FALSE, because
in the Wait TRUE case we only have to make sure the data is mapped
in order to return.
It is illegal to modify data that is only mapped, and can in fact lead
to serious problems. It is impossible to check for this in all cases,
however CcSetDirtyPinnedData may implement some Assertions to check for
this. If the caller wishes to modify data that it has only mapped, then
it must *first* call CcPinMappedData.
In any case, the caller MUST subsequently call CcUnpinData.
Naturally if CcPinRead or CcPreparePinWrite were called multiple
times for the same data, CcUnpinData must be called the same number
of times.
The returned Buffer pointer is valid until the data is unpinned, at
which point it is invalid to use the pointer further. This buffer pointer
will remain valid if CcPinMappedData is called.
Note that under some circumstances (like Wait supplied as FALSE or more
than a page is requested), this routine may actually pin the data, however
it is not necessary, and in fact not correct, for the caller to be concerned
about this.
Arguments:
FileObject - Pointer to the file object for a file which was
opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
which CcInitializeCacheMap was called by the file system.
FileOffset - Byte offset in file for desired data.
Length - Length of desired data in bytes.
Wait - FALSE if caller may not block, TRUE otherwise (see description
above)
Bcb - On the first call this returns a pointer to a Bcb
parameter which must be supplied as input on all subsequent
calls, for this buffer
Buffer - Returns pointer to desired data, valid until the buffer is
unpinned or freed. This pointer will remain valid if CcPinMappedData
is called.
Return Value:
FALSE - if Wait was supplied as FALSE and the data was not delivered
TRUE - if the data is being delivered
--*/
{
PSHARED_CACHE_MAP SharedCacheMap;
LARGE_INTEGER BeyondLastByte;
ULONG ReceivedLength;
ULONG SavedState;
volatile UCHAR ch;
ULONG PageCount = COMPUTE_PAGES_SPANNED((ULongToPtr(FileOffset->LowPart)), Length);
PETHREAD Thread = PsGetCurrentThread();
DebugTrace(+1, me, "CcMapData\n", 0 );
MmSavePageFaultReadAhead( Thread, &SavedState );
// Increment performance counters
if (Wait) {
CcMapDataWait += 1;
// Initialize the indirect pointer to our miss counter.
CcMissCounter = &CcMapDataWaitMiss;
} else {
CcMapDataNoWait += 1;
}
// Get pointer to SharedCacheMap.
SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
+ sizeof(PVOID));
// Call local routine to Map or Access the file data. If we cannot map
// the data because of a Wait condition, return FALSE.
if (Wait) {
*Buffer = CcGetVirtualAddress( SharedCacheMap,
*FileOffset,
(PVACB *)Bcb,
&ReceivedLength );
ASSERT( ReceivedLength >= Length );
} else if (!CcPinFileData( FileObject,
FileOffset,
Length,
TRUE,
FALSE,
Wait,
(PBCB *)Bcb,
Buffer,
&BeyondLastByte )) {
DebugTrace(-1, me, "CcMapData -> FALSE\n", 0 );
CcMapDataNoWaitMiss += 1;
return FALSE;
} else {
ASSERT( (BeyondLastByte.QuadPart - FileOffset->QuadPart) >= Length );
#if LIST_DBG
{
KIRQL OldIrql;
PBCB BcbTemp = (PBCB)*Bcb;
ExAcquireSpinLock( &CcBcbSpinLock, &OldIrql );
if (BcbTemp->CcBcbLinks.Flink == NULL) {
InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks );
CcBcbCount += 1;
ExReleaseSpinLock( &CcBcbSpinLock, OldIrql );
SetCallersAddress( BcbTemp );
} else {
ExReleaseSpinLock( &CcBcbSpinLock, OldIrql );
}
}
#endif
}
// Now let's just sit here and take the miss(es) like a man (and count them).
try {
// Loop to touch each page
BeyondLastByte.LowPart = 0;
while (PageCount != 0) {
MmSetPageFaultReadAhead( Thread, PageCount - 1 );
ch = *((volatile UCHAR *)(*Buffer) + BeyondLastByte.LowPart);
BeyondLastByte.LowPart += PAGE_SIZE;
PageCount -= 1;
}
} finally {
MmResetPageFaultReadAhead( Thread, SavedState );
if (AbnormalTermination() && (*Bcb != NULL)) {
CcUnpinFileData( (PBCB)*Bcb, TRUE, UNPIN );
*Bcb = NULL;
}
}
CcMissCounter = &CcThrowAway;
// Increment the pointer as a reminder that it is read only.
*(PCHAR *)Bcb += 1;
DebugTrace(-1, me, "CcMapData -> TRUE\n", 0 );
return TRUE;
}
BOOLEAN
CcPinMappedData (
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG Flags,
IN OUT PVOID *Bcb
)
/*++
Routine Description:
This routine attempts to pin data that was previously only mapped.
If the routine determines that in fact it was necessary to actually
pin the data when CcMapData was called, then this routine does not
have to do anything.
If the caller does not want to block on this call, then
Wait should be supplied as FALSE. If Wait was supplied as FALSE and
it is currently impossible to supply the requested data without
blocking, then this routine will return FALSE. However, if the
data is immediately accessible in the cache and no blocking is
required, this routine returns TRUE with a pointer to the data.
If the data is not returned in the first call, the caller
may request the data later with Wait = TRUE. It is not required
that the caller request the data later.
If the caller subsequently modifies the data, it should call
CcSetDirtyPinnedData.
In any case, the caller MUST subsequently call CcUnpinData.
Naturally if CcPinRead or CcPreparePinWrite were called multiple
times for the same data, CcUnpinData must be called the same number
of times.
Note there are no performance counters in this routine, as the misses
will almost always occur on the map above, and there will seldom be a
miss on this conversion.
Arguments:
FileObject - Pointer to the file object for a file which was
opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
which CcInitializeCacheMap was called by the file system.
FileOffset - Byte offset in file for desired data.
Length - Length of desired data in bytes.
Flags - (PIN_WAIT, PIN_EXCLUSIVE, PIN_NO_READ, etc. as defined in cache.h)
If the caller specifies PIN_NO_READ and PIN_EXCLUSIVE, then he must
guarantee that no one else will be attempting to map the view, if he
wants to guarantee that the Bcb is not mapped (view may be purged).
If the caller specifies PIN_NO_READ without PIN_EXCLUSIVE, the data
may or may not be mapped in the return Bcb.
Bcb - On the first call this returns a pointer to a Bcb
parameter which must be supplied as input on all subsequent
calls, for this buffer
Return Value:
FALSE - if Wait was not set and the data was not delivered
TRUE - if the data is being delivered
--*/
{
PVOID Buffer;
LARGE_INTEGER BeyondLastByte;
PSHARED_CACHE_MAP SharedCacheMap;
LARGE_INTEGER LocalFileOffset = *FileOffset;
POBCB MyBcb = NULL;
PBCB *CurrentBcbPtr = (PBCB *)&MyBcb;
BOOLEAN Result = FALSE;
DebugTrace(+1, me, "CcPinMappedData\n", 0 );
// If the Bcb is no longer ReadOnly, then just return.
if ((*(PULONG)Bcb & 1) == 0) {
return TRUE;
}
// Remove the Read Only flag
*(PCHAR *)Bcb -= 1;
// Get pointer to SharedCacheMap.
SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
+ sizeof(PVOID));
// We only count the calls to this routine, since they are almost guaranteed
// to be hits.
CcPinMappedDataCount += 1;
// Guarantee we will put the flag back if required.
try {
if (((PBCB)*Bcb)->NodeTypeCode != CACHE_NTC_BCB) {
// Form loop to handle occassional overlapped Bcb case.
do {
// If we have already been through the loop, then adjust
// our file offset and length from the last time.
if (MyBcb != NULL) {
// If this is the second time through the loop, then it is time
// to handle the overlap case and allocate an OBCB.
if (CurrentBcbPtr == (PBCB *)&MyBcb) {
MyBcb = CcAllocateObcb( FileOffset, Length, (PBCB)MyBcb );
// Set CurrentBcbPtr to point at the first entry in
// the vector (which is already filled in), before
// advancing it below.
CurrentBcbPtr = &MyBcb->Bcbs[0];
}
Length -= (ULONG)(BeyondLastByte.QuadPart - LocalFileOffset.QuadPart);
LocalFileOffset.QuadPart = BeyondLastByte.QuadPart;
CurrentBcbPtr += 1;
}
// Call local routine to Map or Access the file data. If we cannot map
// the data because of a Wait condition, return FALSE.
if (!CcPinFileData( FileObject,
&LocalFileOffset,
Length,
(BOOLEAN)!FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED),
FALSE,
Flags,
CurrentBcbPtr,
&Buffer,
&BeyondLastByte )) {
try_return( Result = FALSE );
}
// Continue looping if we did not get everything.
} while((BeyondLastByte.QuadPart - LocalFileOffset.QuadPart) < Length);
// Free the Vacb before going on.
CcFreeVirtualAddress( (PVACB)*Bcb );
*Bcb = MyBcb;
// Debug routines used to insert and remove Bcbs from the global list
#if LIST_DBG
{
KIRQL OldIrql;
PBCB BcbTemp = (PBCB)*Bcb;
ExAcquireSpinLock( &CcBcbSpinLock, &OldIrql );
if (BcbTemp->CcBcbLinks.Flink == NULL) {
InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks );
CcBcbCount += 1;
ExReleaseSpinLock( &CcBcbSpinLock, OldIrql );
SetCallersAddress( BcbTemp );
} else {
ExReleaseSpinLock( &CcBcbSpinLock, OldIrql );
}
}
#endif
}
// If he really has a Bcb, all we have to do is acquire it shared since he is
// no longer ReadOnly.
else {
if (!ExAcquireSharedStarveExclusive( &((PBCB)*Bcb)->Resource, BooleanFlagOn(Flags, PIN_WAIT))) {
try_return( Result = FALSE );
}
}
Result = TRUE;
try_exit: NOTHING;
}
finally {
if (!Result) {
// Put the Read Only flag back
*(PCHAR *)Bcb += 1;
// We may have gotten partway through
if (MyBcb != NULL) {
CcUnpinData( MyBcb );
}
}
DebugTrace(-1, me, "CcPinMappedData -> %02lx\n", Result );
}
return Result;
}
BOOLEAN
CcPinRead (
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN ULONG Flags,
OUT PVOID *Bcb,
OUT PVOID *Buffer
)
/*++
Routine Description:
This routine attempts to pin the specified file data in the cache.
A pointer is returned to the desired data in the cache. This routine
is intended for File System support and is not intended to be called
from Dpc level.
If the caller does not want to block on this call, then
Wait should be supplied as FALSE. If Wait was supplied as FALSE and
it is currently impossible to supply the requested data without
blocking, then this routine will return FALSE. However, if the
data is immediately accessible in the cache and no blocking is
required, this routine returns TRUE with a pointer to the data.
If the data is not returned in the first call, the caller
may request the data later with Wait = TRUE. It is not required
that the caller request the data later.
If the caller subsequently modifies the data, it should call
CcSetDirtyPinnedData.
In any case, the caller MUST subsequently call CcUnpinData.
Naturally if CcPinRead or CcPreparePinWrite were called multiple
times for the same data, CcUnpinData must be called the same number
of times.
The returned Buffer pointer is valid until the data is unpinned, at
which point it is invalid to use the pointer further.
Arguments:
FileObject - Pointer to the file object for a file which was
opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
which CcInitializeCacheMap was called by the file system.
FileOffset - Byte offset in file for desired data.
Length - Length of desired data in bytes.
Flags - (PIN_WAIT, PIN_EXCLUSIVE, PIN_NO_READ, etc. as defined in cache.h)
If the caller specifies PIN_NO_READ and PIN_EXCLUSIVE, then he must
guarantee that no one else will be attempting to map the view, if he
wants to guarantee that the Bcb is not mapped (view may be purged).
If the caller specifies PIN_NO_READ without PIN_EXCLUSIVE, the data
may or may not be mapped in the return Bcb.
Bcb - On the first call this returns a pointer to a Bcb
parameter which must be supplied as input on all subsequent
calls, for this buffer
Buffer - Returns pointer to desired data, valid until the buffer is
unpinned or freed.
Return Value:
FALSE - if Wait was not set and the data was not delivered
TRUE - if the data is being delivered
--*/
{
PSHARED_CACHE_MAP SharedCacheMap;
PVOID LocalBuffer;
LARGE_INTEGER BeyondLastByte;
LARGE_INTEGER LocalFileOffset = *FileOffset;
POBCB MyBcb = NULL;
PBCB *CurrentBcbPtr = (PBCB *)&MyBcb;
BOOLEAN Result = FALSE;
DebugTrace(+1, me, "CcPinRead\n", 0 );
// Increment performance counters
if (FlagOn(Flags, PIN_WAIT)) {
CcPinReadWait += 1;
// Initialize the indirect pointer to our miss counter.
CcMissCounter = &CcPinReadWaitMiss;
} else {
CcPinReadNoWait += 1;
}
// Get pointer to SharedCacheMap.
SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
+ sizeof(PVOID));
try {
// Form loop to handle occassional overlapped Bcb case.
do {
// If we have already been through the loop, then adjust
// our file offset and length from the last time.
if (MyBcb != NULL) {
// If this is the second time through the loop, then it is time
// to handle the overlap case and allocate an OBCB.
if (CurrentBcbPtr == (PBCB *)&MyBcb) {
MyBcb = CcAllocateObcb( FileOffset, Length, (PBCB)MyBcb );
// Set CurrentBcbPtr to point at the first entry in
// the vector (which is already filled in), before
// advancing it below.
CurrentBcbPtr = &MyBcb->Bcbs[0];
// Also on second time through, return starting Buffer
*Buffer = LocalBuffer;
}
Length -= (ULONG)(BeyondLastByte.QuadPart - LocalFileOffset.QuadPart);
LocalFileOffset.QuadPart = BeyondLastByte.QuadPart;
CurrentBcbPtr += 1;
}
// Call local routine to Map or Access the file data. If we cannot map
// the data because of a Wait condition, return FALSE.
if (!CcPinFileData( FileObject,
&LocalFileOffset,
Length,
(BOOLEAN)!FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED),
FALSE,
Flags,
CurrentBcbPtr,
&LocalBuffer,
&BeyondLastByte )) {
CcPinReadNoWaitMiss += 1;
try_return( Result = FALSE );
}
// Continue looping if we did not get everything.
} while((BeyondLastByte.QuadPart - LocalFileOffset.QuadPart) < Length);
*Bcb = MyBcb;
// Debug routines used to insert and remove Bcbs from the global list
#if LIST_DBG
{
KIRQL OldIrql;
PBCB BcbTemp = (PBCB)*Bcb;
ExAcquireSpinLock( &CcBcbSpinLock, &OldIrql );
if (BcbTemp->CcBcbLinks.Flink == NULL) {
InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks );
CcBcbCount += 1;
ExReleaseSpinLock( &CcBcbSpinLock, OldIrql );
SetCallersAddress( BcbTemp );
} else {
ExReleaseSpinLock( &CcBcbSpinLock, OldIrql );
}
}
#endif
// In the normal (nonoverlapping) case we return the
// correct buffer address here.
if (CurrentBcbPtr == (PBCB *)&MyBcb) {
*Buffer = LocalBuffer;
}
Result = TRUE;
try_exit: NOTHING;
}
finally {
CcMissCounter = &CcThrowAway;
if (!Result) {
// We may have gotten partway through
if (MyBcb != NULL) {
CcUnpinData( MyBcb );
}
}
DebugTrace(-1, me, "CcPinRead -> %02lx\n", Result );
}
return Result;
}
BOOLEAN
CcPreparePinWrite (
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN BOOLEAN Zero,
IN ULONG Flags,
OUT PVOID *Bcb,
OUT PVOID *Buffer
)
/*++
Routine Description:
This routine attempts to lock the specified file data in the cache
and return a pointer to it along with the correct
I/O status. Pages to be completely overwritten may be satisfied
with emtpy pages.
If not all of the pages can be prepared, and Wait was supplied as
FALSE, then this routine will return FALSE, and its outputs will
be meaningless. The caller may request the data later with
Wait = TRUE. However, it is not required that the caller request
the data later.
If Wait is supplied as TRUE, and all of the pages can be prepared
without blocking, this call will return TRUE immediately. Otherwise,
this call will block until all of the pages can be prepared, and
then return TRUE.
When this call returns with TRUE, the caller may immediately begin
to transfer data into the buffers via the Buffer pointer. The
buffer will already be marked dirty.
The caller MUST subsequently call CcUnpinData.
Naturally if CcPinRead or CcPreparePinWrite were called multiple
times for the same data, CcUnpinData must be called the same number
of times.
The returned Buffer pointer is valid until the data is unpinned, at
which point it is invalid to use the pointer further.
Arguments:
FileObject - Pointer to the file object for a file which was
opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for
which CcInitializeCacheMap was called by the file system.
FileOffset - Byte offset in file for desired data.
Length - Length of desired data in bytes.
Zero - If supplied as TRUE, the buffer will be zeroed on return.
Flags - (PIN_WAIT, PIN_EXCLUSIVE, PIN_NO_READ, etc. as defined in cache.h)
If the caller specifies PIN_NO_READ and PIN_EXCLUSIVE, then he must
guarantee that no one else will be attempting to map the view, if he
wants to guarantee that the Bcb is not mapped (view may be purged).
If the caller specifies PIN_NO_READ without PIN_EXCLUSIVE, the data
may or may not be mapped in the return Bcb.
Bcb - This returns a pointer to a Bcb parameter which must be
supplied as input to CcPinWriteComplete.
Buffer - Returns pointer to desired data, valid until the buffer is
unpinned or freed.
Return Value:
FALSE - if Wait was not set and the data was not delivered
TRUE - if the pages are being delivered
--*/
{
PSHARED_CACHE_MAP SharedCacheMap;
PVOID LocalBuffer;
LARGE_INTEGER BeyondLastByte;
LARGE_INTEGER LocalFileOffset = *FileOffset;
POBCB MyBcb = NULL;
PBCB *CurrentBcbPtr = (PBCB *)&MyBcb;
ULONG OriginalLength = Length;
BOOLEAN Result = FALSE;
DebugTrace(+1, me, "CcPreparePinWrite\n", 0 );
// Get pointer to SharedCacheMap.
SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer
+ sizeof(PVOID));
try {
// Form loop to handle occassional overlapped Bcb case.
do {
// If we have already been through the loop, then adjust
// our file offset and length from the last time.
if (MyBcb != NULL) {
// If this is the second time through the loop, then it is time
// to handle the overlap case and allocate an OBCB.
if (CurrentBcbPtr == (PBCB *)&MyBcb) {
MyBcb = CcAllocateObcb( FileOffset, Length, (PBCB)MyBcb );
// Set CurrentBcbPtr to point at the first entry in
// the vector (which is already filled in), before
// advancing it below.
CurrentBcbPtr = &MyBcb->Bcbs[0];
// Also on second time through, return starting Buffer
*Buffer = LocalBuffer;
}
Length -= (ULONG)(BeyondLastByte.QuadPart - LocalFileOffset.QuadPart);
LocalFileOffset.QuadPart = BeyondLastByte.QuadPart;
CurrentBcbPtr += 1;
}
// Call local routine to Map or Access the file data. If we cannot map
// the data because of a Wait condition, return FALSE.
if (!CcPinFileData( FileObject,
&LocalFileOffset,
Length,
FALSE,
TRUE,
Flags,
CurrentBcbPtr,
&LocalBuffer,
&BeyondLastByte )) {
try_return( Result = FALSE );
}
// Continue looping if we did not get everything.
} while((BeyondLastByte.QuadPart - LocalFileOffset.QuadPart) < Length);
*Bcb = MyBcb;
// Debug routines used to insert and remove Bcbs from the global list
#if LIST_DBG
{
KIRQL OldIrql;
PBCB BcbTemp = (PBCB)*Bcb;
ExAcquireSpinLock( &CcBcbSpinLock, &OldIrql );
if (BcbTemp->CcBcbLinks.Flink == NULL) {
InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks );
CcBcbCount += 1;
ExReleaseSpinLock( &CcBcbSpinLock, OldIrql );
SetCallersAddress( BcbTemp );
} else {
ExReleaseSpinLock( &CcBcbSpinLock, OldIrql );
}
}
#endif
// In the normal (nonoverlapping) case we return the
// correct buffer address here.
if (CurrentBcbPtr == (PBCB *)&MyBcb) {
*Buffer = LocalBuffer;
}
if (Zero) {
RtlZeroMemory( *Buffer, OriginalLength );
}
CcSetDirtyPinnedData( MyBcb, NULL );
Result = TRUE;
try_exit: NOTHING;
}
finally {
CcMissCounter = &CcThrowAway;
if (!Result) {
// We may have gotten partway through
if (MyBcb != NULL) {
CcUnpinData( MyBcb );
}
}
DebugTrace(-1, me, "CcPreparePinWrite -> %02lx\n", Result );
}
return Result;
}
VOID
CcUnpinData (
IN PVOID Bcb
)
/*++
Routine Description:
This routine must be called at IPL0, some time after calling CcPinRead
or CcPreparePinWrite. It performs any cleanup that is necessary.
Arguments:
Bcb - Bcb parameter returned from the last call to CcPinRead.
Return Value:
None.
--*/
{
DebugTrace(+1, me, "CcUnpinData:\n", 0 );
DebugTrace( 0, me, " >Bcb = %08lx\n", Bcb );
// Test for ReadOnly and unpin accordingly.
if (((ULONG_PTR)Bcb & 1) != 0) {
// Remove the Read Only flag
(PCHAR)Bcb -= 1;
CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN );
} else {
// Handle the overlapped Bcb case.
if (((POBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) {
PBCB *BcbPtrPtr = &((POBCB)Bcb)->Bcbs[0];
// Loop to free all Bcbs with recursive calls
// (rather than dealing with RO for this uncommon case).
while (*BcbPtrPtr != NULL) {
CcUnpinData(*(BcbPtrPtr++));
}
// Then free the pool for the Obcb
ExFreePool( Bcb );
// Otherwise, it is a normal Bcb
} else {
CcUnpinFileData( (PBCB)Bcb, FALSE, UNPIN );
}
}
DebugTrace(-1, me, "CcUnPinData -> VOID\n", 0 );
}
VOID
CcSetBcbOwnerPointer (
IN PVOID Bcb,
IN PVOID OwnerPointer
)
/*++
Routine Description:
This routine may be called to set the resource owner for the Bcb resource,
for cases where another thread will do the unpin *and* the current thread
may exit.
Arguments:
Bcb - Bcb parameter returned from the last call to CcPinRead.
OwnerPointer - A valid resource owner pointer, which means a pointer to
an allocated system address, with the low-order two bits
set. The address may not be deallocated until after the
unpin call.
Return Value:
None.
--*/
{
ASSERT(((ULONG_PTR)Bcb & 1) == 0);
// Handle the overlapped Bcb case.
if (((POBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) {
PBCB *BcbPtrPtr = &((POBCB)Bcb)->Bcbs[0];
// Loop to set owner for all Bcbs.
while (*BcbPtrPtr != NULL) {
ExSetResourceOwnerPointer( &(*BcbPtrPtr)->Resource, OwnerPointer );
BcbPtrPtr++;
}
// Otherwise, it is a normal Bcb
} else {
// Handle normal case.
ExSetResourceOwnerPointer( &((PBCB)Bcb)->Resource, OwnerPointer );
}
}
VOID
CcUnpinDataForThread (
IN PVOID Bcb,
IN ERESOURCE_THREAD ResourceThreadId
)
/*++
Routine Description:
This routine must be called at IPL0, some time after calling CcPinRead
or CcPreparePinWrite. It performs any cleanup that is necessary,
releasing the Bcb resource for the given thread.
Arguments:
Bcb - Bcb parameter returned from the last call to CcPinRead.
Return Value:
None.
--*/
{
DebugTrace(+1, me, "CcUnpinDataForThread:\n", 0 );
DebugTrace( 0, me, " >Bcb = %08lx\n", Bcb );
DebugTrace( 0, me, " >ResoureceThreadId = %08lx\n", ResoureceThreadId );
// Test for ReadOnly and unpin accordingly.
if (((ULONG_PTR)Bcb & 1) != 0) {
// Remove the Read Only flag
(PCHAR)Bcb -= 1;
CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN );
} else {
// Handle the overlapped Bcb case.
if (((POBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) {
PBCB *BcbPtrPtr = &((POBCB)Bcb)->Bcbs[0];
// Loop to free all Bcbs with recursive calls
// (rather than dealing with RO for this uncommon case).
while (*BcbPtrPtr != NULL) {
CcUnpinDataForThread( *(BcbPtrPtr++), ResourceThreadId );
}
// Then free the pool for the Obcb
ExFreePool( Bcb );
// Otherwise, it is a normal Bcb
} else {
// If not readonly, we can release the resource for the thread first,
// and then call CcUnpinFileData. Release resource first in case
// Bcb gets deallocated.
ExReleaseResourceForThread( &((PBCB)Bcb)->Resource, ResourceThreadId );
CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN );
}
}
DebugTrace(-1, me, "CcUnpinDataForThread -> VOID\n", 0 );
}
POBCB
CcAllocateObcb (
IN PLARGE_INTEGER FileOffset,
IN ULONG Length,
IN PBCB FirstBcb
)
/*++
Routine Description:
This routine is called by the various pinning routines to allocate and
initialize an overlap Bcb.
Arguments:
FileOffset - Starting file offset for the Obcb (An Obcb starts with a
public structure, which someone could use)
Length - Length of the range covered by the Obcb
FirstBcb - First Bcb already created, which only covers the start of
the desired range (low order bit may be set to indicate ReadOnly)
Return Value:
Pointer to the allocated Obcb
--*/
{
ULONG LengthToAllocate;
POBCB Obcb;
PBCB Bcb = (PBCB)((ULONG_PTR)FirstBcb & ~1);
// Allocate according to the worst case, assuming that we
// will need as many additional Bcbs as there are pages
// remaining. Also throw in one more pointer to guarantee
// users of the OBCB can always terminate on NULL.
// We remove fron consideration the range described by the
// first Bcb (note that the range of the Obcb is not strictly
// starting at the first Bcb) and add in locations for the first
// bcb and the null.
LengthToAllocate = FIELD_OFFSET(OBCB, Bcbs) + (2 * sizeof(PBCB)) +
((Length -
(Bcb->ByteLength -
(FileOffset->HighPart?
(ULONG)(FileOffset->QuadPart - Bcb->FileOffset.QuadPart) :
FileOffset->LowPart - Bcb->FileOffset.LowPart)) +
PAGE_SIZE - 1) / PAGE_SIZE) * sizeof(PBCB);
Obcb = FsRtlAllocatePoolWithTag( NonPagedPool, LengthToAllocate, 'bOcC' );
RtlZeroMemory( Obcb, LengthToAllocate );
Obcb->NodeTypeCode = CACHE_NTC_OBCB;
Obcb->NodeByteSize = (USHORT)LengthToAllocate;
Obcb->ByteLength = Length;
Obcb->FileOffset = *FileOffset;
Obcb->Bcbs[0] = FirstBcb;
return Obcb;
}