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.
725 lines
18 KiB
725 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1996-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
CacheSup.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the cache management routines for the Udfs
|
|
FSD and FSP, by calling the Common Cache Manager.
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
|
|
Author:
|
|
|
|
Dan Lovinger [DanLo] 12-Sep-1996
|
|
|
|
Revision History:
|
|
|
|
Tom Jolly [tomjolly] 21-Jan-2000 CcPurge and append at end of vmcb stream
|
|
|
|
// @@END_DDKSPLIT
|
|
|
|
--*/
|
|
|
|
#include "UdfProcs.h"
|
|
|
|
//
|
|
// The Bug check file id for this module
|
|
//
|
|
|
|
#define BugCheckFileId (UDFS_BUG_CHECK_CACHESUP)
|
|
|
|
//
|
|
// The local debug trace level
|
|
//
|
|
|
|
#define Dbg (UDFS_DEBUG_LEVEL_CACHESUP)
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, UdfCompleteMdl)
|
|
#pragma alloc_text(PAGE, UdfCreateInternalStream)
|
|
#pragma alloc_text(PAGE, UdfDeleteInternalStream)
|
|
#pragma alloc_text(PAGE, UdfMapMetadataView)
|
|
#pragma alloc_text(PAGE, UdfPurgeVolume)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
UdfCreateInternalStream (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN PFCB Fcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates an internal stream file for interaction
|
|
with the cache manager. The Fcb here will be for a directory
|
|
stream.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Vcb for this volume.
|
|
|
|
Fcb - Points to the Fcb for this file. It is an Index Fcb.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFILE_OBJECT StreamFile = NULL;
|
|
BOOLEAN DecrementReference = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check inputs.
|
|
//
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
ASSERT_FCB_INDEX( Fcb );
|
|
|
|
//
|
|
// We may only have the Fcb shared. Lock the Fcb and do a
|
|
// safe test to see if we need to really create the file object.
|
|
//
|
|
|
|
UdfLockFcb( IrpContext, Fcb );
|
|
|
|
if (Fcb->FileObject != NULL) {
|
|
|
|
UdfUnlockFcb( IrpContext, Fcb );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Create the internal stream. The Vpb should be pointing at our volume
|
|
// device object at this point.
|
|
//
|
|
|
|
StreamFile = IoCreateStreamFileObject( NULL, Vcb->Vpb->RealDevice );
|
|
|
|
if (StreamFile == NULL) {
|
|
|
|
UdfRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
//
|
|
// Initialize the fields of the file object.
|
|
//
|
|
|
|
StreamFile->ReadAccess = TRUE;
|
|
StreamFile->WriteAccess = FALSE;
|
|
StreamFile->DeleteAccess = FALSE;
|
|
|
|
StreamFile->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject;
|
|
|
|
//
|
|
// Set the file object type and increment the Vcb counts.
|
|
//
|
|
|
|
UdfSetFileObject( IrpContext,
|
|
StreamFile,
|
|
StreamFileOpen,
|
|
Fcb,
|
|
NULL );
|
|
|
|
//
|
|
// We will reference the current Fcb twice to keep it from going
|
|
// away in the error path. Otherwise if we dereference it
|
|
// below in the finally clause a close could cause the Fcb to
|
|
// be deallocated.
|
|
//
|
|
|
|
UdfLockVcb( IrpContext, Vcb );
|
|
|
|
DebugTrace(( +1, Dbg,
|
|
"UdfCreateInternalStream, Fcb %08x Vcb %d/%d Fcb %d/%d\n",
|
|
Fcb,
|
|
Vcb->VcbReference,
|
|
Vcb->VcbUserReference,
|
|
Fcb->FcbReference,
|
|
Fcb->FcbUserReference ));
|
|
|
|
UdfIncrementReferenceCounts( IrpContext, Fcb, 2, 0 );
|
|
UdfUnlockVcb( IrpContext, Vcb );
|
|
DecrementReference = TRUE;
|
|
|
|
//
|
|
// Initialize the cache map for the file.
|
|
//
|
|
|
|
CcInitializeCacheMap( StreamFile,
|
|
(PCC_FILE_SIZES)&Fcb->AllocationSize,
|
|
TRUE,
|
|
&UdfData.CacheManagerCallbacks,
|
|
Fcb );
|
|
|
|
//
|
|
// Go ahead and store the stream file into the Fcb.
|
|
//
|
|
|
|
Fcb->FileObject = StreamFile;
|
|
StreamFile = NULL;
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( "UdfCreateInternalStream" );
|
|
|
|
//
|
|
// If we raised then we need to dereference the file object.
|
|
//
|
|
|
|
if (StreamFile != NULL) {
|
|
|
|
ObDereferenceObject( StreamFile );
|
|
Fcb->FileObject = NULL;
|
|
}
|
|
|
|
//
|
|
// Dereference and unlock the Fcb.
|
|
//
|
|
|
|
if (DecrementReference) {
|
|
|
|
UdfLockVcb( IrpContext, Vcb );
|
|
UdfDecrementReferenceCounts( IrpContext, Fcb, 1, 0 );
|
|
|
|
DebugTrace(( -1, Dbg,
|
|
"UdfCreateInternalStream, Vcb %d/%d Fcb %d/%d\n",
|
|
Vcb->VcbReference,
|
|
Vcb->VcbUserReference,
|
|
Fcb->FcbReference,
|
|
Fcb->FcbUserReference ));
|
|
|
|
UdfUnlockVcb( IrpContext, Vcb );
|
|
}
|
|
|
|
UdfUnlockFcb( IrpContext, Fcb );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
UdfDeleteInternalStream (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFCB Fcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates an internal stream file for interaction
|
|
with the cache manager. The Fcb here can be for either a
|
|
directory stream or for a metadata stream.
|
|
|
|
Arguments:
|
|
|
|
Fcb - Points to the Fcb for this file. It is either an Index or
|
|
Metadata Fcb.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
ASSERT_FCB( Fcb );
|
|
|
|
//
|
|
// Lock the Fcb.
|
|
//
|
|
|
|
UdfLockFcb( IrpContext, Fcb );
|
|
|
|
//
|
|
// Capture the file object.
|
|
//
|
|
|
|
FileObject = Fcb->FileObject;
|
|
Fcb->FileObject = NULL;
|
|
|
|
//
|
|
// It is now safe to unlock the Fcb.
|
|
//
|
|
|
|
UdfUnlockFcb( IrpContext, Fcb );
|
|
|
|
//
|
|
// Dereference the file object if present.
|
|
//
|
|
|
|
if (FileObject != NULL) {
|
|
|
|
if (FileObject->PrivateCacheMap != NULL) {
|
|
|
|
CcUninitializeCacheMap( FileObject, NULL, NULL );
|
|
}
|
|
|
|
ObDereferenceObject( FileObject );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UdfCompleteMdl (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the function of completing Mdl reads.
|
|
It should be called only from UdfCommonRead.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the originating Irp.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Will always be STATUS_SUCCESS.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Do completion processing.
|
|
//
|
|
|
|
FileObject = IoGetCurrentIrpStackLocation( Irp )->FileObject;
|
|
|
|
CcMdlReadComplete( FileObject, Irp->MdlAddress );
|
|
|
|
//
|
|
// Mdl is now deallocated.
|
|
//
|
|
|
|
Irp->MdlAddress = NULL;
|
|
|
|
//
|
|
// Complete the request and exit right away.
|
|
//
|
|
|
|
UdfCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
UdfMapMetadataView (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PMAPPED_PVIEW View,
|
|
IN PVCB Vcb,
|
|
IN USHORT Partition,
|
|
IN ULONG Lbn,
|
|
IN ULONG Length,
|
|
IN MAPMETAOP Operation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform the common work of mapping an extent of metadata into a mapped view.
|
|
Any existing view in the supplied MAPPED_VIEW is unmapped.
|
|
|
|
Any single thread must only ever have ONE mapping ESTABLISHED through the
|
|
Vmcb stream at any one time. Failure to observe this may result in deadlocks
|
|
when the Vmcb package tries to extend an existing mapping and hence do a
|
|
purge. I.e. no more than one MAPPED_VIEW should be in use (actually mapped)
|
|
by any given thread at any moment.
|
|
|
|
Acquires Vcb->VmcbMappingResource shared (will be held on return, except for
|
|
INIT_ONLY operation). May acquire exclusive before shared if the mapping
|
|
is not present in the vmcb, so calling threads must have no other active
|
|
mappings through the vmcb stream.
|
|
|
|
Arguments:
|
|
|
|
View - View structure to map the bytes into
|
|
|
|
Vcb - Vcb of the volume the extent is on
|
|
|
|
Partition - Partition of the extent
|
|
|
|
Lbn - Lbn of the extent
|
|
|
|
Length - Length of the extent
|
|
|
|
Operation - METAMAPOP_INIT_VIEW_ONLY - Just store the part/lbn/len. Doesn't
|
|
access the vmcb, or do a CcMap.
|
|
|
|
METAMAPOP_REMAP_VIEW - Do the CcMap through the vmcb using
|
|
the partition/lbn/len already in
|
|
the supplied view record
|
|
|
|
METAMAPOP_INIT_AND_MAP - Does both of the above in sequence.
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER Offset;
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
|
|
//
|
|
// Remove any existing mapping & release Vmcb mapping resource
|
|
//
|
|
|
|
UdfUnpinView( IrpContext, View );
|
|
|
|
if ( METAMAPOP_REMAP_VIEW != Operation) {
|
|
|
|
//
|
|
// Update the view information if we're not remapping using the
|
|
// existing values in the view record.
|
|
//
|
|
|
|
View->Partition = Partition;
|
|
View->Lbn = Lbn;
|
|
View->Length = Length;
|
|
View->Vsn = UDF_INVALID_VSN;
|
|
View->Bcb = View->View = NULL;
|
|
}
|
|
|
|
if ( METAMAPOP_INIT_VIEW_ONLY != Operation) {
|
|
|
|
ASSERT_NOT_HELD_VMCB( Vcb);
|
|
|
|
//
|
|
// Find (or add) the mapping for this extent in the vmcb stream. We now
|
|
// store the Vsn in the MAPPED_VIEW, so we don't have to do the lookup
|
|
// again later (simplifies locking, amongst other things).
|
|
//
|
|
|
|
View->Vsn = UdfLookupMetaVsnOfExtent( IrpContext,
|
|
Vcb,
|
|
View->Partition,
|
|
View->Lbn,
|
|
View->Length,
|
|
FALSE );
|
|
|
|
Offset.QuadPart = LlBytesFromSectors( Vcb, View->Vsn );
|
|
|
|
//
|
|
// Map the extent. Acquire the vmcb map resource to synchronise against
|
|
// purges of the vmcb stream. See comments in Vmcb code for more detail.
|
|
//
|
|
|
|
UdfAcquireVmcbForCcMap( IrpContext, Vcb);
|
|
|
|
try {
|
|
|
|
CcMapData( Vcb->MetadataFcb->FileObject,
|
|
&Offset,
|
|
View->Length,
|
|
TRUE,
|
|
&View->Bcb,
|
|
&View->View );
|
|
}
|
|
finally {
|
|
|
|
//
|
|
// If this raised, we should release the mapping lock. Callers will
|
|
// only cleanup and release if a non-NULL BCB is present in the pview.
|
|
//
|
|
|
|
if (AbnormalTermination()) {
|
|
|
|
UdfReleaseVmcb( IrpContext, Vcb);
|
|
|
|
View->View = View->Bcb = NULL;
|
|
}
|
|
else {
|
|
|
|
ASSERT( View->View && View->Bcb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UdfPurgeVolume (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN BOOLEAN DismountUnderway
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to purge the volume. The purpose is to make all the stale file
|
|
objects in the system go away, minimizing the reference counts, so that the volume may
|
|
be locked or deleted.
|
|
|
|
The Vcb is already acquired exclusively. We will lock out all file operations by
|
|
acquiring the global file resource. Then we will walk through all of the Fcb's and
|
|
perform the purge.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Vcb for the volume to purge.
|
|
|
|
DismountUnderway - Indicates that we are trying to delete all of the objects.
|
|
We will purge the Metadata and VolumeDasd and dereference all internal streams.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The first failure of the purge operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PVOID RestartKey = NULL;
|
|
PFCB ThisFcb = NULL;
|
|
PFCB NextFcb;
|
|
|
|
BOOLEAN RemovedFcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT_EXCLUSIVE_VCB( Vcb);
|
|
|
|
//
|
|
// Force any remaining Fcb's in the delayed close queue to be closed.
|
|
//
|
|
|
|
UdfFspClose( Vcb );
|
|
|
|
//
|
|
// Acquire the global file resource.
|
|
//
|
|
|
|
UdfAcquireAllFiles( IrpContext, Vcb );
|
|
|
|
//
|
|
// Loop through each Fcb in the Fcb Table and perform the flush.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Lock the Vcb to lookup the next Fcb.
|
|
//
|
|
|
|
UdfLockVcb( IrpContext, Vcb );
|
|
NextFcb = UdfGetNextFcb( IrpContext, Vcb, &RestartKey );
|
|
|
|
//
|
|
// Reference the NextFcb if present.
|
|
//
|
|
|
|
if (NextFcb != NULL) {
|
|
|
|
NextFcb->FcbReference += 1;
|
|
}
|
|
|
|
//
|
|
// If the last Fcb is present then decrement reference count and call teardown
|
|
// to see if it should be removed.
|
|
//
|
|
|
|
if (ThisFcb != NULL) {
|
|
|
|
ThisFcb->FcbReference -= 1;
|
|
|
|
UdfUnlockVcb( IrpContext, Vcb );
|
|
|
|
UdfTeardownStructures( IrpContext, ThisFcb, FALSE, &RemovedFcb );
|
|
|
|
} else {
|
|
|
|
UdfUnlockVcb( IrpContext, Vcb );
|
|
}
|
|
|
|
//
|
|
// Break out of the loop if no more Fcb's.
|
|
//
|
|
|
|
if (NextFcb == NULL) {
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Move to the next Fcb.
|
|
//
|
|
|
|
ThisFcb = NextFcb;
|
|
|
|
//
|
|
// If there is a image section then see if that can be closed.
|
|
//
|
|
|
|
if (ThisFcb->FcbNonpaged->SegmentObject.ImageSectionObject != NULL) {
|
|
|
|
MmFlushImageSection( &ThisFcb->FcbNonpaged->SegmentObject, MmFlushForWrite );
|
|
}
|
|
|
|
//
|
|
// If there is a data section then purge this. If there is an image
|
|
// section then we won't be able to. Remember this if it is our first
|
|
// error.
|
|
//
|
|
|
|
if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) &&
|
|
!CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject,
|
|
NULL,
|
|
0,
|
|
FALSE ) &&
|
|
(Status == STATUS_SUCCESS)) {
|
|
|
|
Status = STATUS_UNABLE_TO_DELETE_SECTION;
|
|
}
|
|
|
|
//
|
|
// Dereference the internal stream if dismounting.
|
|
//
|
|
|
|
if (DismountUnderway &&
|
|
(SafeNodeType( ThisFcb ) != UDFS_NTC_FCB_DATA) &&
|
|
(ThisFcb->FileObject != NULL)) {
|
|
|
|
UdfDeleteInternalStream( IrpContext, ThisFcb );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now look at the Root Index, Metadata, Volume Dasd and VAT Fcbs.
|
|
// Note that we usually hit the Root Index in the loop above, but
|
|
// it is possible miss it if it didn't get into the Fcb table in the
|
|
// first place!
|
|
//
|
|
|
|
if (DismountUnderway) {
|
|
|
|
if (Vcb->RootIndexFcb != NULL) {
|
|
|
|
ThisFcb = Vcb->RootIndexFcb;
|
|
InterlockedIncrement( &ThisFcb->FcbReference );
|
|
|
|
if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) &&
|
|
!CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject,
|
|
NULL,
|
|
0,
|
|
FALSE ) &&
|
|
(Status == STATUS_SUCCESS)) {
|
|
|
|
Status = STATUS_UNABLE_TO_DELETE_SECTION;
|
|
}
|
|
|
|
UdfDeleteInternalStream( IrpContext, ThisFcb );
|
|
InterlockedDecrement( &ThisFcb->FcbReference );
|
|
UdfTeardownStructures( IrpContext, ThisFcb, FALSE, &RemovedFcb );
|
|
}
|
|
|
|
if (Vcb->MetadataFcb != NULL) {
|
|
|
|
ThisFcb = Vcb->MetadataFcb;
|
|
InterlockedIncrement( &ThisFcb->FcbReference );
|
|
|
|
if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) &&
|
|
!CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject,
|
|
NULL,
|
|
0,
|
|
FALSE ) &&
|
|
(Status == STATUS_SUCCESS)) {
|
|
|
|
Status = STATUS_UNABLE_TO_DELETE_SECTION;
|
|
}
|
|
|
|
UdfDeleteInternalStream( IrpContext, ThisFcb );
|
|
InterlockedDecrement( &ThisFcb->FcbReference );
|
|
UdfTeardownStructures( IrpContext, ThisFcb, FALSE, &RemovedFcb );
|
|
}
|
|
|
|
if (Vcb->VatFcb != NULL) {
|
|
|
|
ThisFcb = Vcb->VatFcb;
|
|
InterlockedIncrement( &ThisFcb->FcbReference );
|
|
|
|
if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) &&
|
|
!CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject,
|
|
NULL,
|
|
0,
|
|
FALSE ) &&
|
|
(Status == STATUS_SUCCESS)) {
|
|
|
|
Status = STATUS_UNABLE_TO_DELETE_SECTION;
|
|
}
|
|
|
|
UdfDeleteInternalStream( IrpContext, ThisFcb );
|
|
InterlockedDecrement( &ThisFcb->FcbReference );
|
|
UdfTeardownStructures( IrpContext, ThisFcb, FALSE, &RemovedFcb );
|
|
}
|
|
|
|
if (Vcb->VolumeDasdFcb != NULL) {
|
|
|
|
ThisFcb = Vcb->VolumeDasdFcb;
|
|
InterlockedIncrement( &ThisFcb->FcbReference );
|
|
|
|
if ((ThisFcb->FcbNonpaged->SegmentObject.DataSectionObject != NULL) &&
|
|
!CcPurgeCacheSection( &ThisFcb->FcbNonpaged->SegmentObject,
|
|
NULL,
|
|
0,
|
|
FALSE ) &&
|
|
(Status == STATUS_SUCCESS)) {
|
|
|
|
Status = STATUS_UNABLE_TO_DELETE_SECTION;
|
|
}
|
|
|
|
InterlockedDecrement( &ThisFcb->FcbReference );
|
|
UdfTeardownStructures( IrpContext, ThisFcb, FALSE, &RemovedFcb );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release all of the files.
|
|
//
|
|
|
|
UdfReleaseAllFiles( IrpContext, Vcb );
|
|
|
|
return Status;
|
|
}
|
|
|