2997 lines
77 KiB
C
2997 lines
77 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ResrcSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Ntfs Resource acquisition routines
|
||
|
||
Author:
|
||
|
||
Gary Kimura [GaryKi] 21-May-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "NtfsProc.h"
|
||
|
||
#undef _NTFSLOCKORDER_
|
||
#define _NTFS_NTFSDBG_DEFINITIONS_
|
||
#include "lockorder.h"
|
||
|
||
#ifdef NTFSDBG
|
||
ULONG NtfsAssertOnLockProb = TRUE;
|
||
ULONG NtfsPrintOnLockProb = FALSE;
|
||
LONG NtfsBreakOnState = -1;
|
||
PIRP_CONTEXT NtfsBreakOnIrpContext = NULL;
|
||
#endif
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtfsAcquireAllFiles)
|
||
#pragma alloc_text(PAGE, NtfsAcquireCheckpointSynchronization)
|
||
#pragma alloc_text(PAGE, NtfsAcquireIndexCcb)
|
||
#pragma alloc_text(PAGE, NtfsReleaseIndexCcb)
|
||
#pragma alloc_text(PAGE, NtfsAcquireExclusiveFcb)
|
||
#pragma alloc_text(PAGE, NtfsAcquireSharedFcbCheckWait)
|
||
#pragma alloc_text(PAGE, NtfsAcquireExclusiveScb)
|
||
#pragma alloc_text(PAGE, NtfsAcquireSharedScbForTransaction)
|
||
#pragma alloc_text(PAGE, NtfsAcquireExclusiveVcb)
|
||
#pragma alloc_text(PAGE, NtfsAcquireFcbWithPaging)
|
||
#pragma alloc_text(PAGE, NtfsAcquireForCreateSection)
|
||
#pragma alloc_text(PAGE, NtfsAcquireScbForLazyWrite)
|
||
#pragma alloc_text(PAGE, NtfsAcquireFileForCcFlush)
|
||
#pragma alloc_text(PAGE, NtfsAcquireFileForModWrite)
|
||
#pragma alloc_text(PAGE, NtfsAcquireSharedVcb)
|
||
#pragma alloc_text(PAGE, NtfsAcquireVolumeFileForLazyWrite)
|
||
#pragma alloc_text(PAGE, NtfsReleaseAllFiles)
|
||
#pragma alloc_text(PAGE, NtfsReleaseCheckpointSynchronization)
|
||
#pragma alloc_text(PAGE, NtfsReleaseFcbWithPaging)
|
||
#pragma alloc_text(PAGE, NtfsReleaseFileForCcFlush)
|
||
#pragma alloc_text(PAGE, NtfsReleaseForCreateSection)
|
||
#pragma alloc_text(PAGE, NtfsReleaseScbFromLazyWrite)
|
||
#pragma alloc_text(PAGE, NtfsReleaseScbWithPaging)
|
||
#pragma alloc_text(PAGE, NtfsReleaseSharedResources)
|
||
#pragma alloc_text(PAGE, NtfsReleaseVolumeFileFromLazyWrite)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
NtfsAcquireAllFiles (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN ULONG Exclusive,
|
||
IN ULONG AcquirePagingIo,
|
||
IN ULONG AcquireAndDrop
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine non-recursively requires all files on a volume.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the volume
|
||
|
||
Exclusive - Indicates if we should be acquiring all the files exclusively.
|
||
If FALSE then we acquire all the files shared except for files with
|
||
streams which could be part of transactions.
|
||
|
||
AcquirePagingIo - Indicates if we need to acquire the paging io resource
|
||
exclusively. Only needed if a future call will flush the volume
|
||
(i.e. shutdown)
|
||
|
||
AcquireAndDrop - Indicates that we only want to acquire and drop each resource.
|
||
Used in cases where we just want to set some state in the Vcb and then
|
||
know that everyone has seen it before proceeding (i.e. Clearing the journal active
|
||
flag). Should only be TRUE if we want to get the resources exclusive.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PFCB Fcb;
|
||
PSCB *Scb;
|
||
PSCB NextScb;
|
||
PVOID RestartKey;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check for the correct combination of flags.
|
||
//
|
||
|
||
ASSERT( !AcquireAndDrop || Exclusive );
|
||
|
||
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
||
|
||
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
|
||
|
||
RestartKey = NULL;
|
||
while (TRUE) {
|
||
|
||
NtfsAcquireFcbTable( IrpContext, Vcb );
|
||
Fcb = NtfsGetNextFcbTableEntry(Vcb, &RestartKey);
|
||
NtfsReleaseFcbTable( IrpContext, Vcb );
|
||
|
||
if (Fcb == NULL) {
|
||
|
||
break;
|
||
}
|
||
|
||
ASSERT_FCB( Fcb );
|
||
|
||
//
|
||
// We can skip over the Fcb's for any of the Scb's in the Vcb.
|
||
// We delay acquiring those to avoid deadlocks.
|
||
//
|
||
|
||
if (!FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
|
||
|
||
//
|
||
// If there is a paging Io resource then acquire this if required.
|
||
//
|
||
|
||
if (AcquirePagingIo && (Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExAcquireResourceExclusiveLite( Fcb->PagingIoResource, TRUE );
|
||
|
||
if (AcquireAndDrop) {
|
||
|
||
ExReleaseResourceLite( Fcb->PagingIoResource );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Acquire this Fcb whether or not the underlying file has been deleted.
|
||
//
|
||
|
||
if (Exclusive ||
|
||
IsDirectory( &Fcb->Info )) {
|
||
|
||
if (AcquireAndDrop) {
|
||
|
||
NtfsAcquireResourceExclusive( IrpContext, Fcb, TRUE );
|
||
NtfsReleaseResource( IrpContext, Fcb );
|
||
|
||
} else {
|
||
|
||
NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Assume that we only need this file shared. We will then
|
||
// look for Lsn related streams.
|
||
//
|
||
|
||
NtfsAcquireSharedFcb( IrpContext, Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
|
||
|
||
//
|
||
// Walk through all of the Scb's for the file and look for
|
||
// an Lsn protected stream.
|
||
//
|
||
|
||
NtfsLockFcb( IrpContext, Fcb );
|
||
|
||
NextScb = NULL;
|
||
|
||
while (NextScb = NtfsGetNextChildScb( Fcb, NextScb )) {
|
||
|
||
if (NextScb->AttributeTypeCode != $DATA) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
NtfsUnlockFcb( IrpContext, Fcb );
|
||
|
||
//
|
||
// If we found a protected Scb then release and reacquire the Fcb
|
||
// exclusively.
|
||
//
|
||
|
||
if (NextScb != NULL) {
|
||
|
||
NtfsReleaseFcb( IrpContext, Fcb );
|
||
NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now acquire the Fcb's in the Vcb.
|
||
//
|
||
|
||
Scb = &Vcb->MftBitmapScb;
|
||
|
||
//
|
||
// Ordering dependent on the fact we acquired root up above because its not a system file
|
||
//
|
||
|
||
ASSERT( (NULL == Vcb->RootIndexScb) || !FlagOn( Vcb->RootIndexScb->Fcb->FcbState, FCB_STATE_SYSTEM_FILE ) );
|
||
|
||
while (TRUE) {
|
||
|
||
if (Scb == &Vcb->UsnJournal) {
|
||
|
||
break;
|
||
}
|
||
|
||
Scb -= 1;
|
||
|
||
if ((*Scb != NULL)
|
||
&& (*Scb != Vcb->BitmapScb)) {
|
||
|
||
if (AcquireAndDrop) {
|
||
|
||
if (AcquirePagingIo && ((*Scb)->Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExAcquireResourceExclusiveLite( (*Scb)->Fcb->PagingIoResource, TRUE );
|
||
ExReleaseResourceLite( (*Scb)->Fcb->PagingIoResource );
|
||
}
|
||
|
||
NtfsAcquireResourceExclusive( IrpContext, (*Scb), TRUE );
|
||
NtfsReleaseResource( IrpContext, (*Scb) );
|
||
|
||
} else {
|
||
|
||
if (AcquirePagingIo && ((*Scb)->Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExAcquireResourceExclusiveLite( (*Scb)->Fcb->PagingIoResource, TRUE );
|
||
}
|
||
|
||
NtfsAcquireExclusiveFcb( IrpContext, (*Scb)->Fcb, NULL, ACQUIRE_NO_DELETE_CHECK );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Treat the bitmap as an end resource and acquire it last.
|
||
//
|
||
|
||
if (Vcb->BitmapScb != NULL) {
|
||
|
||
ULONG AcquireFlags = ACQUIRE_NO_DELETE_CHECK;
|
||
|
||
if (AcquireAndDrop) {
|
||
|
||
if (AcquirePagingIo && (Vcb->BitmapScb->Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExAcquireResourceExclusiveLite( Vcb->BitmapScb->Fcb->PagingIoResource, TRUE );
|
||
ExReleaseResourceLite( Vcb->BitmapScb->Fcb->PagingIoResource );
|
||
}
|
||
|
||
NtfsAcquireResourceExclusive( IrpContext, Vcb->BitmapScb, TRUE );
|
||
NtfsReleaseResource( IrpContext, Vcb->BitmapScb );
|
||
|
||
} else {
|
||
|
||
if (AcquirePagingIo && (Vcb->BitmapScb->Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExAcquireResourceExclusiveLite( Vcb->BitmapScb->Fcb->PagingIoResource, TRUE );
|
||
}
|
||
|
||
NtfsAcquireExclusiveFcb( IrpContext, Vcb->BitmapScb->Fcb, NULL, AcquireFlags );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we don't have to release the files then don't bump this number.
|
||
//
|
||
|
||
if (!AcquireAndDrop) {
|
||
|
||
Vcb->AcquireFilesCount += 1;
|
||
|
||
} else {
|
||
|
||
NtfsReleaseVcb( IrpContext, Vcb );
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseAllFiles (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN BOOLEAN ReleasePagingIo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine non-recursively requires all files on a volume.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the volume
|
||
|
||
ReleasePagingIo - Indicates whether we should release the paging io resources
|
||
as well.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PFCB Fcb;
|
||
PSCB *Scb;
|
||
PVOID RestartKey;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT( Vcb->AcquireFilesCount != 0 );
|
||
Vcb->AcquireFilesCount -= 1;
|
||
|
||
//
|
||
// Loop to flush all of the prerestart streams, to do the loop
|
||
// we cycle through the Fcb Table and for each fcb we acquire it.
|
||
//
|
||
|
||
RestartKey = NULL;
|
||
while (TRUE) {
|
||
|
||
NtfsAcquireFcbTable( IrpContext, Vcb );
|
||
Fcb = NtfsGetNextFcbTableEntry(Vcb, &RestartKey);
|
||
NtfsReleaseFcbTable( IrpContext, Vcb );
|
||
|
||
if (Fcb == NULL) {
|
||
|
||
break;
|
||
}
|
||
|
||
ASSERT_FCB( Fcb );
|
||
|
||
if (!FlagOn(Fcb->FcbState, FCB_STATE_SYSTEM_FILE)) {
|
||
|
||
//
|
||
// Release the file.
|
||
//
|
||
|
||
if (ReleasePagingIo && (Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExReleaseResourceLite( Fcb->PagingIoResource );
|
||
}
|
||
|
||
NtfsReleaseFcb( IrpContext, Fcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now release the Fcb's in the Vcb.
|
||
//
|
||
|
||
Scb = &Vcb->RootIndexScb;
|
||
|
||
while (TRUE) {
|
||
|
||
if (Scb == &Vcb->ObjectIdTableScb) {
|
||
|
||
break;
|
||
}
|
||
|
||
Scb += 1;
|
||
|
||
if (*Scb != NULL) {
|
||
|
||
if (ReleasePagingIo && ((*Scb)->Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExReleaseResourceLite( (*Scb)->Fcb->PagingIoResource );
|
||
}
|
||
|
||
NtfsReleaseFcb( IrpContext, (*Scb)->Fcb );
|
||
}
|
||
}
|
||
|
||
NtfsReleaseVcb( IrpContext, Vcb );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsAcquireCheckpointSynchronization (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Synchronize with checkpointing - this blocks out all fuzzy / clean checkpoints
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb to synchronize with
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
||
|
||
while (FlagOn( Vcb->CheckpointFlags, VCB_CHECKPOINT_SYNC_FLAGS )) {
|
||
|
||
//
|
||
// Release the checkpoint event because we cannot checkpoint now.
|
||
//
|
||
|
||
NtfsReleaseCheckpoint( IrpContext, Vcb );
|
||
NtfsWaitOnCheckpointNotify( IrpContext, Vcb );
|
||
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
||
}
|
||
|
||
SetFlag( Vcb->CheckpointFlags, VCB_CHECKPOINT_SYNC_FLAGS );
|
||
NtfsResetCheckpointNotify( IrpContext, Vcb );
|
||
NtfsReleaseCheckpoint( IrpContext, Vcb );
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
NtfsReleaseCheckpointSynchronization (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Synchronize with checkpointing - this blocks out all fuzzy / clean checkpoints
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb to synchronize with
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
||
ClearFlag( Vcb->CheckpointFlags, VCB_CHECKPOINT_SYNC_FLAGS );
|
||
NtfsSetCheckpointNotify( IrpContext, Vcb );
|
||
NtfsReleaseCheckpoint( IrpContext, Vcb );
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireExclusiveVcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN BOOLEAN RaiseOnCantWait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires exclusive access to the Vcb.
|
||
|
||
This routine will raise if it cannot acquire the resource and wait
|
||
in the IrpContext is false.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb to acquire
|
||
|
||
RaiseOnCantWait - Indicates if we should raise on an acquisition error
|
||
or simply return a BOOLEAN indicating that we couldn't get the
|
||
resource.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - Indicates if we were able to acquire the resource. This is really
|
||
only meaningful if the RaiseOnCantWait value is FALSE.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_VCB(Vcb);
|
||
|
||
PAGED_CODE();
|
||
|
||
if (ExAcquireResourceExclusiveLite( &Vcb->Resource, (BOOLEAN) FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT))) {
|
||
|
||
#ifdef NTFSDBG
|
||
if (FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
if (1 == ExIsResourceAcquiredSharedLite( &Vcb->Resource )) {
|
||
NtfsChangeResourceOrderState( IrpContext, NtfsResourceExVcb, FALSE, (BOOLEAN) !FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT) );
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (RaiseOnCantWait) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireSharedVcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN BOOLEAN RaiseOnCantWait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires shared access to the Vcb.
|
||
|
||
This routine will raise if it cannot acquire the resource and wait
|
||
in the IrpContext is false.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb to acquire
|
||
|
||
RaiseOnCantWait - Indicates if we should raise on an acquisition error
|
||
or simply return a BOOLEAN indicating that we couldn't get the
|
||
resource.
|
||
|
||
N.B. -- If you pass FALSE for this parameter you ABSOLUTELY MUST
|
||
test the return value. Otherwise you aren't certain that
|
||
you hold the Vcb, and you don't know if it's safe to free it.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_VCB(Vcb);
|
||
|
||
PAGED_CODE();
|
||
|
||
if (ExAcquireResourceSharedLite( &Vcb->Resource, (BOOLEAN) FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT))) {
|
||
|
||
#ifdef NTFSDBG
|
||
if (FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
if (1 == ExIsResourceAcquiredSharedLite( &Vcb->Resource )) {
|
||
NtfsChangeResourceOrderState( IrpContext, NtfsResourceSharedVcb, FALSE, (BOOLEAN) !FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT) );
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (RaiseOnCantWait) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
|
||
} else {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
#ifdef NTFSDBG
|
||
|
||
|
||
VOID
|
||
NtfsReleaseVcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will release the Vcb. Normally its a define for lock_order testing
|
||
we use a function so we can easily change the owernship state
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb to release
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
--*/
|
||
|
||
{
|
||
if (FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
if ((ExIsResourceAcquiredExclusiveLite( &Vcb->Resource)) &&
|
||
(1 == ExIsResourceAcquiredSharedLite( &Vcb->Resource ))) {
|
||
NtfsChangeResourceOrderState( IrpContext, NtfsResourceExVcb, TRUE, FALSE );
|
||
} else if (1 == ExIsResourceAcquiredSharedLite( &Vcb->Resource )) {
|
||
NtfsChangeResourceOrderState( IrpContext, NtfsResourceSharedVcb, TRUE, FALSE );
|
||
}
|
||
} else {
|
||
IrpContext->OwnershipState = None;
|
||
}
|
||
ExReleaseResourceLite( &Vcb->Resource );
|
||
}
|
||
#endif
|
||
|
||
|
||
VOID
|
||
NtfsReleaseVcbCheckDelete (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN UCHAR MajorCode,
|
||
IN PFILE_OBJECT FileObject OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will release the Vcb. We will also test here whether we should
|
||
teardown the Vcb at this point. If this is the last open queued to a dismounted
|
||
volume or the last close from a failed mount or the failed mount then we will
|
||
want to test the Vcb for a teardown.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Supplies the Vcb to release
|
||
|
||
MajorCode - Indicates what type of operation we were called from.
|
||
|
||
FileObject - Optionally supplies the file object whose VPB pointer we need to
|
||
zero out
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN ReleaseVcb = TRUE;
|
||
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_VCB(Vcb);
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT ) &&
|
||
(Vcb->CloseCount == 0)) {
|
||
|
||
ULONG ReferenceCount;
|
||
ULONG ResidualCount;
|
||
|
||
KIRQL SavedIrql;
|
||
BOOLEAN DeleteVcb = FALSE;
|
||
|
||
ASSERT_EXCLUSIVE_RESOURCE( &Vcb->Resource );
|
||
|
||
//
|
||
// The volume has gone through dismount. Now we need to decide if this
|
||
// release of the Vcb is the last reference for this volume. If so we
|
||
// can tear the volume down.
|
||
//
|
||
// We compare the reference count in the Vpb with the state of the volume
|
||
// and the type of operation. We also need to check if there is a
|
||
// referenced log file object.
|
||
//
|
||
// If the temp vpb flag isn't set then we already let the iosubsys
|
||
// delete it during dismount
|
||
//
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_TEMP_VPB )) {
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
ReferenceCount = Vcb->Vpb->ReferenceCount;
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
|
||
} else {
|
||
|
||
ReferenceCount = 0;
|
||
}
|
||
|
||
|
||
ResidualCount = 0;
|
||
|
||
if ((Vcb->LogFileObject != NULL) &&
|
||
!FlagOn( Vcb->CheckpointFlags, VCB_DEREFERENCED_LOG_FILE )) {
|
||
|
||
ResidualCount = 1;
|
||
}
|
||
|
||
if (MajorCode == IRP_MJ_CREATE) {
|
||
|
||
ResidualCount += 1;
|
||
}
|
||
|
||
//
|
||
// If the residual count is the same as the count in the Vpb then we
|
||
// can delete the Vpb.
|
||
//
|
||
|
||
if ((ResidualCount == ReferenceCount) &&
|
||
!FlagOn( Vcb->VcbState, VCB_STATE_DELETE_UNDERWAY )) {
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_DELETE_UNDERWAY );
|
||
|
||
//
|
||
// Release the vcb before we grab the global
|
||
//
|
||
|
||
#ifdef NTFSDBG
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
if (1 == ExIsResourceAcquiredExclusiveLite( &Vcb->Resource) ) {
|
||
NtfsChangeResourceOrderState( IrpContext, NtfsResourceExVcb, TRUE, FALSE );
|
||
}
|
||
}
|
||
#endif
|
||
ExReleaseResourceLite( &Vcb->Resource );
|
||
ReleaseVcb = FALSE;
|
||
|
||
//
|
||
// Never delete the Vcb unless this is the last release of
|
||
// this Vcb.
|
||
//
|
||
|
||
if (ExIsResourceAcquiredSharedLite( &Vcb->Resource ) == 0) {
|
||
|
||
if (ARGUMENT_PRESENT(FileObject)) { FileObject->Vpb = NULL; }
|
||
|
||
//
|
||
// If this is a create then the IO system will handle the
|
||
// Vpb.
|
||
//
|
||
|
||
if (MajorCode == IRP_MJ_CREATE) {
|
||
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_TEMP_VPB );
|
||
}
|
||
|
||
//
|
||
// Use the global resource to synchronize the DeleteVcb process.
|
||
//
|
||
|
||
NtfsAcquireExclusiveGlobal( IrpContext, TRUE );
|
||
RemoveEntryList( &Vcb->VcbLinks );
|
||
NtfsReleaseGlobal( IrpContext );
|
||
|
||
//
|
||
// Try to delete the Vcb, reinsert into the queue if
|
||
// the delete is blocked.
|
||
//
|
||
|
||
if (!NtfsDeleteVcb( IrpContext, &Vcb )) {
|
||
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_DELETE_UNDERWAY );
|
||
|
||
NtfsAcquireExclusiveGlobal( IrpContext, TRUE );
|
||
InsertHeadList( &NtfsData.VcbQueue, &Vcb->VcbLinks );
|
||
NtfsReleaseGlobal( IrpContext );
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// From test above we must still own the vcb so its safe to change the flag
|
||
//
|
||
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_DELETE_UNDERWAY );
|
||
}
|
||
}
|
||
}
|
||
|
||
if (ReleaseVcb) {
|
||
|
||
#ifdef NTFSDBG
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
if (1 == ExIsResourceAcquiredExclusiveLite( &Vcb->Resource) ) {
|
||
NtfsChangeResourceOrderState( IrpContext, NtfsResourceExVcb, TRUE, FALSE );
|
||
}
|
||
}
|
||
#endif
|
||
ExReleaseResourceLite( &Vcb->Resource );
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireFcbWithPaging (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN ULONG AcquireFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used in the create path, fsctl path and close path . It acquires the Fcb
|
||
and also the paging IO resource if it exists but only if the irpcontext flag is set.
|
||
i.e during a create supersede/overwrite operation.
|
||
|
||
This routine will raise if it cannot acquire the resource and wait
|
||
in the IrpContext is false.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the Fcb to acquire
|
||
|
||
AcquireFlags - ACQUIRE_DONT_WAIT overrides the wait value in the IrpContext.
|
||
We won't wait for the resource and return whether the resource
|
||
was acquired.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if acquired. FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Status = FALSE;
|
||
BOOLEAN Wait = FALSE;
|
||
BOOLEAN PagingIoAcquired = FALSE;
|
||
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_FCB(Fcb);
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Sanity check that this is create. The supersede flag is only
|
||
// set in the create path and only tested here.
|
||
//
|
||
|
||
ASSERT( IrpContext->MajorFunction == IRP_MJ_CREATE ||
|
||
IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL ||
|
||
IrpContext->MajorFunction == IRP_MJ_CLOSE ||
|
||
IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION ||
|
||
IrpContext->MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION ||
|
||
IrpContext->MajorFunction == IRP_MJ_SET_EA );
|
||
|
||
if (!FlagOn( AcquireFlags, ACQUIRE_DONT_WAIT ) && FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )) {
|
||
|
||
Wait = TRUE;
|
||
}
|
||
|
||
//
|
||
// Free any exclusive paging I/O resource, we currently have, which
|
||
// must presumably be from a directory with a paging I/O resource.
|
||
//
|
||
// We defer releasing the paging io resource when we have logged
|
||
// changes against a stream. The only transaction that should be
|
||
// underway at this point is the create file case where we allocated
|
||
// a file record. In this case it is OK to release the paging io
|
||
// resource for the parent.
|
||
//
|
||
|
||
if (IrpContext->CleanupStructure != NULL) {
|
||
|
||
ASSERT( IrpContext->CleanupStructure != Fcb );
|
||
|
||
// ASSERT(IrpContext->TransactionId == 0);
|
||
NtfsReleasePagingIo( IrpContext, IrpContext->CleanupStructure );
|
||
}
|
||
|
||
//
|
||
// Loop until we get it right - worst case is twice through loop.
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Acquire Paging I/O first. Testing for the PagingIoResource
|
||
// is not really safe without holding the main resource, so we
|
||
// correct for that below.
|
||
//
|
||
|
||
if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING ) &&
|
||
(Fcb->PagingIoResource != NULL)) {
|
||
if (!ExAcquireResourceExclusiveLite( Fcb->PagingIoResource, Wait )) {
|
||
break;
|
||
}
|
||
IrpContext->CleanupStructure = Fcb;
|
||
PagingIoAcquired = TRUE;
|
||
}
|
||
|
||
//
|
||
// Let's acquire this Fcb exclusively.
|
||
//
|
||
|
||
if (!NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, ACQUIRE_NO_DELETE_CHECK | AcquireFlags )) {
|
||
|
||
if (PagingIoAcquired) {
|
||
ASSERT(IrpContext->TransactionId == 0);
|
||
NtfsReleasePagingIo( IrpContext, Fcb );
|
||
}
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If we now do not see a paging I/O resource we are golden,
|
||
// othewise we can absolutely release and acquire the resources
|
||
// safely in the right order, since a resource in the Fcb is
|
||
// not going to go away.
|
||
//
|
||
|
||
if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ACQUIRE_PAGING ) ||
|
||
PagingIoAcquired ||
|
||
(Fcb->PagingIoResource == NULL)) {
|
||
|
||
Status = TRUE;
|
||
break;
|
||
}
|
||
|
||
NtfsReleaseFcb( IrpContext, Fcb );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseFcbWithPaging (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases access to the Fcb, including its
|
||
paging I/O resource if it exists.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the Fcb to acquire
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_FCB(Fcb);
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We test that we currently hold the paging Io exclusive before releasing
|
||
// it. Checking the ExclusivePagingFcb in the IrpContext tells us if
|
||
// it is ours.
|
||
//
|
||
|
||
if ((IrpContext->TransactionId == 0) &&
|
||
(IrpContext->CleanupStructure == Fcb)) {
|
||
NtfsReleasePagingIo( IrpContext, Fcb );
|
||
}
|
||
|
||
NtfsReleaseFcb( IrpContext, Fcb );
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseScbWithPaging (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases access to the Scb, including its
|
||
paging I/O resource if it exists.
|
||
|
||
Arguments:
|
||
|
||
Scb - Supplies the Fcb to acquire
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFCB Fcb = Scb->Fcb;
|
||
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_SCB(Scb);
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Release the paging Io resource in the Scb under the following
|
||
// conditions.
|
||
//
|
||
// - No transaction underway
|
||
// - This paging Io resource is in the IrpContext
|
||
// (This last test insures there is a paging IO resource
|
||
// and we own it).
|
||
//
|
||
|
||
if ((IrpContext->TransactionId == 0) &&
|
||
(IrpContext->CleanupStructure == Fcb)) {
|
||
NtfsReleasePagingIo( IrpContext, Fcb );
|
||
}
|
||
|
||
NtfsReleaseScb( IrpContext, Scb );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireExclusiveFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PSCB Scb OPTIONAL,
|
||
IN ULONG AcquireFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires exclusive access to the Fcb.
|
||
|
||
This routine will raise if it cannot acquire the resource and wait
|
||
in the IrpContext is false.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the Fcb to acquire
|
||
|
||
Scb - This is the Scb for which we are acquiring the Fcb
|
||
|
||
AcquireFlags - Indicating whether to override the wait value in the IrpContext. Also whether
|
||
to noop the check for a deleted file.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if acquired. FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
BOOLEAN Wait;
|
||
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_FCB(Fcb);
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = STATUS_CANT_WAIT;
|
||
|
||
if (FlagOn( AcquireFlags, ACQUIRE_DONT_WAIT )) {
|
||
Wait = FALSE;
|
||
} else if (FlagOn( AcquireFlags, ACQUIRE_WAIT )) {
|
||
Wait = TRUE;
|
||
} else {
|
||
Wait = BooleanFlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
||
}
|
||
|
||
if (NtfsAcquireResourceExclusive( IrpContext, Fcb, Wait )) {
|
||
|
||
//
|
||
// The link count should be non-zero or the file has been
|
||
// deleted. We allow deleted files to be acquired for close and
|
||
// also allow them to be acquired recursively in case we
|
||
// acquire them a second time after marking them deleted (i.e. rename)
|
||
//
|
||
|
||
if (FlagOn( AcquireFlags, ACQUIRE_NO_DELETE_CHECK )
|
||
|
||
||
|
||
|
||
(!FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )
|
||
&& (!ARGUMENT_PRESENT( Scb )
|
||
|| !FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )))
|
||
|
||
||
|
||
|
||
(IrpContext->MajorFunction == IRP_MJ_CLOSE)
|
||
|
||
||
|
||
|
||
(IrpContext->MajorFunction == IRP_MJ_CREATE)
|
||
|
||
||
|
||
|
||
(IrpContext->MajorFunction == IRP_MJ_CLEANUP)) {
|
||
|
||
//
|
||
// Put Fcb in the exclusive Fcb list for this IrpContext,
|
||
// excluding the bitmap for the volume, since we do not need
|
||
// to modify its file record and do not want unnecessary
|
||
// serialization/deadlock problems.
|
||
//
|
||
// If we are growing the volume bitmap then we do want to put
|
||
// it on the list and maintain the BaseExclusiveCount. Also
|
||
// need to do this in the case where we see the volume bitmap
|
||
// during close (it can happen during restart if we have log
|
||
// records for the volume bitmap).
|
||
//
|
||
|
||
//
|
||
// If Fcb already acquired then bump the count.
|
||
//
|
||
|
||
if (Fcb->ExclusiveFcbLinks.Flink != NULL) {
|
||
|
||
Fcb->BaseExclusiveCount += 1;
|
||
|
||
//
|
||
// The fcb is not currently on an exclusive list.
|
||
// Put it on a list if this is not the volume
|
||
// bitmap or we explicitly want to put the volume
|
||
// bitmap on the list.
|
||
//
|
||
|
||
} else if (FlagOn( AcquireFlags, ACQUIRE_HOLD_BITMAP ) ||
|
||
(Fcb->Vcb->BitmapScb == NULL) ||
|
||
(Fcb->Vcb->BitmapScb->Fcb != Fcb)) {
|
||
|
||
ASSERT( Fcb->BaseExclusiveCount == 0 );
|
||
|
||
InsertHeadList( &IrpContext->ExclusiveFcbList,
|
||
&Fcb->ExclusiveFcbLinks );
|
||
|
||
Fcb->BaseExclusiveCount += 1;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// We need to release the Fcb and remember the status code.
|
||
//
|
||
|
||
NtfsReleaseResource( IrpContext, Fcb );
|
||
Status = STATUS_FILE_DELETED;
|
||
|
||
} else if (FlagOn( AcquireFlags, ACQUIRE_DONT_WAIT )) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsAcquireSharedFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PSCB Scb OPTIONAL,
|
||
IN ULONG AcquireFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires shared access to the Fcb.
|
||
|
||
This routine will raise if it cannot acquire the resource and wait
|
||
in the IrpContext is false.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the Fcb to acquire
|
||
|
||
Scb - This is the Scb for which we are acquiring the Fcb
|
||
|
||
AcquireFlags - Indicates if we should acquire the file even if it has been
|
||
deleted.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_FCB(Fcb);
|
||
|
||
Status = STATUS_CANT_WAIT;
|
||
|
||
if (NtfsAcquireResourceShared( IrpContext, Fcb, (BOOLEAN) FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT))) {
|
||
|
||
//
|
||
// The link count should be non-zero or the file has been
|
||
// deleted.
|
||
//
|
||
|
||
if (FlagOn( AcquireFlags, ACQUIRE_NO_DELETE_CHECK ) ||
|
||
(!FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED ) &&
|
||
(!ARGUMENT_PRESENT( Scb ) ||
|
||
!FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )))) {
|
||
|
||
//
|
||
// It's possible that this is a recursive shared aquisition of an
|
||
// Fcb we own exclusively at the top level. In that case we
|
||
// need to bump the acquisition count.
|
||
//
|
||
|
||
if (Fcb->ExclusiveFcbLinks.Flink != NULL) {
|
||
|
||
Fcb->BaseExclusiveCount += 1;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// We need to release the Fcb and remember the status code.
|
||
//
|
||
|
||
NtfsReleaseResource( IrpContext, Fcb );
|
||
Status = STATUS_FILE_DELETED;
|
||
}
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireSharedFcbCheckWait (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN ULONG AcquireFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires shared access to the Fcb but checks whether to wait.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the Fcb to acquire
|
||
|
||
AcquireFlags - Indicates if we should override the wait value in the IrpContext.
|
||
We won't wait for the resource and return whether the resource
|
||
was acquired.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if acquired. FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Wait;
|
||
PAGED_CODE();
|
||
|
||
if (FlagOn( AcquireFlags, ACQUIRE_DONT_WAIT )) {
|
||
Wait = FALSE;
|
||
} else if (FlagOn( AcquireFlags, ACQUIRE_WAIT )) {
|
||
Wait = TRUE;
|
||
} else {
|
||
Wait = BooleanFlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
||
}
|
||
|
||
if (NtfsAcquireResourceShared( IrpContext, Fcb, Wait )) {
|
||
|
||
//
|
||
// It's possible that this is a recursive shared aquisition of an
|
||
// Fcb we own exclusively at the top level. In that case we
|
||
// need to bump the acquisition count.
|
||
//
|
||
|
||
if (Fcb->ExclusiveFcbLinks.Flink != NULL) {
|
||
|
||
Fcb->BaseExclusiveCount += 1;
|
||
}
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine releases the specified Fcb resource. If the Fcb is acquired
|
||
exclusive, and a transaction is still active, then the release is nooped
|
||
in order to preserve two-phase locking. If there is no longer an active
|
||
transaction, then we remove the Fcb from the Exclusive Fcb List off the
|
||
IrpContext, and clear the Flink as a sign. Fcbs are released when the
|
||
transaction is commited.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Fcb to release
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Check if this resource is owned exclusively and we are at the last
|
||
// release for this transaction.
|
||
//
|
||
|
||
if (Fcb->ExclusiveFcbLinks.Flink != NULL) {
|
||
|
||
if (Fcb->BaseExclusiveCount == 1) {
|
||
|
||
//
|
||
// If there is a transaction then noop this request.
|
||
//
|
||
|
||
if (IrpContext->TransactionId != 0) {
|
||
|
||
return;
|
||
}
|
||
|
||
RemoveEntryList( &Fcb->ExclusiveFcbLinks );
|
||
Fcb->ExclusiveFcbLinks.Flink = NULL;
|
||
|
||
//
|
||
// This is a good time to free any Scb snapshots for this Fcb.
|
||
//
|
||
|
||
NtfsFreeSnapshotsForFcb( IrpContext, Fcb );
|
||
}
|
||
|
||
Fcb->BaseExclusiveCount -= 1;
|
||
}
|
||
|
||
ASSERT((Fcb->ExclusiveFcbLinks.Flink == NULL && Fcb->BaseExclusiveCount == 0) ||
|
||
(Fcb->ExclusiveFcbLinks.Flink != NULL && Fcb->BaseExclusiveCount != 0));
|
||
|
||
NtfsReleaseResource( IrpContext, Fcb );
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsAcquireExclusiveScb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires exclusive access to the Scb.
|
||
|
||
This routine will raise if it cannot acquire the resource and wait
|
||
in the IrpContext is false.
|
||
|
||
Arguments:
|
||
|
||
Scb - Scb to acquire
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
NtfsAcquireExclusiveFcb( IrpContext, Scb->Fcb, Scb, 0 );
|
||
|
||
ASSERT( Scb->Fcb->ExclusiveFcbLinks.Flink != NULL
|
||
|| (Scb->Vcb->BitmapScb != NULL
|
||
&& Scb->Vcb->BitmapScb == Scb) );
|
||
|
||
if (FlagOn(Scb->ScbState, SCB_STATE_FILE_SIZE_LOADED)) {
|
||
|
||
NtfsSnapshotScb( IrpContext, Scb );
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsAcquireSharedScbForTransaction (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PSCB Scb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to acquire an Scb shared in order to perform updates to
|
||
the an Scb stream. This is used if the transaction writes to a range of the
|
||
stream without changing the size or position of the data. The caller must
|
||
already provide synchronization to the data itself.
|
||
|
||
There is no corresponding Scb release. It will be released when the transaction commits.
|
||
We will acquire the Scb exclusive if it is not yet in the open attribute table.
|
||
|
||
Arguments:
|
||
|
||
Scb - Scb to acquire
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSCB *Position;
|
||
PSCB *ScbArray;
|
||
ULONG Count;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Make sure we have a free spot in the Scb array in the IrpContext.
|
||
//
|
||
|
||
if (IrpContext->SharedScb == NULL) {
|
||
|
||
Position = (PSCB *) &IrpContext->SharedScb;
|
||
IrpContext->SharedScbSize = 1;
|
||
|
||
//
|
||
// Too bad the first one is not available. If the current size is one then allocate a
|
||
// new block and copy the existing value to it.
|
||
//
|
||
|
||
} else if (IrpContext->SharedScbSize == 1) {
|
||
|
||
ScbArray = NtfsAllocatePool( PagedPool, sizeof( PSCB ) * 4 );
|
||
RtlZeroMemory( ScbArray, sizeof( PSCB ) * 4 );
|
||
*ScbArray = IrpContext->SharedScb;
|
||
IrpContext->SharedScb = ScbArray;
|
||
IrpContext->SharedScbSize = 4;
|
||
Position = ScbArray + 1;
|
||
|
||
//
|
||
// Otherwise look through the existing array and look for a free spot. Allocate a larger
|
||
// array if we need to grow it.
|
||
//
|
||
|
||
} else {
|
||
|
||
Position = IrpContext->SharedScb;
|
||
Count = IrpContext->SharedScbSize;
|
||
|
||
do {
|
||
|
||
if (*Position == NULL) {
|
||
|
||
break;
|
||
}
|
||
|
||
Count -= 1;
|
||
Position += 1;
|
||
|
||
} while (Count != 0);
|
||
|
||
//
|
||
// If we didn't find one then allocate a new structure.
|
||
//
|
||
|
||
if (Count == 0) {
|
||
|
||
ScbArray = NtfsAllocatePool( PagedPool, sizeof( PSCB ) * IrpContext->SharedScbSize * 2 );
|
||
RtlZeroMemory( ScbArray, sizeof( PSCB ) * IrpContext->SharedScbSize * 2 );
|
||
RtlCopyMemory( ScbArray,
|
||
IrpContext->SharedScb,
|
||
sizeof( PSCB ) * IrpContext->SharedScbSize );
|
||
|
||
NtfsFreePool( IrpContext->SharedScb );
|
||
IrpContext->SharedScb = ScbArray;
|
||
Position = ScbArray + IrpContext->SharedScbSize;
|
||
IrpContext->SharedScbSize *= 2;
|
||
}
|
||
}
|
||
|
||
NtfsAcquireResourceShared( IrpContext, Scb, TRUE );
|
||
|
||
if (Scb->NonpagedScb->OpenAttributeTableIndex == 0) {
|
||
|
||
NtfsReleaseResource( IrpContext, Scb );
|
||
NtfsAcquireResourceExclusive( IrpContext, Scb, TRUE );
|
||
}
|
||
|
||
*Position = Scb;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseSharedResources (
|
||
IN PIRP_CONTEXT IrpContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine releases all of the resources acquired shared for
|
||
transaction. The SharedScb structure is freed if necessary and
|
||
the Irp Context field is cleared.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// If only one then free the Scb main resource.
|
||
//
|
||
|
||
if (IrpContext->SharedScbSize == 1) {
|
||
|
||
if (SafeNodeType(IrpContext->SharedScb) == NTFS_NTC_QUOTA_CONTROL) {
|
||
NtfsReleaseQuotaControl( IrpContext,
|
||
(PQUOTA_CONTROL_BLOCK) IrpContext->SharedScb );
|
||
} else {
|
||
|
||
NtfsReleaseResource( IrpContext, ((PSCB) IrpContext->SharedScb) );
|
||
}
|
||
|
||
//
|
||
// Otherwise traverse the array and look for Scb's to release.
|
||
//
|
||
|
||
} else {
|
||
|
||
PSCB *NextScb;
|
||
ULONG Count;
|
||
|
||
NextScb = IrpContext->SharedScb;
|
||
Count = IrpContext->SharedScbSize;
|
||
|
||
do {
|
||
|
||
if (*NextScb != NULL) {
|
||
|
||
if (SafeNodeType(*NextScb) == NTFS_NTC_QUOTA_CONTROL) {
|
||
|
||
NtfsReleaseQuotaControl( IrpContext,
|
||
(PQUOTA_CONTROL_BLOCK) *NextScb );
|
||
} else {
|
||
|
||
NtfsReleaseResource( IrpContext, (*NextScb) );
|
||
}
|
||
*NextScb = NULL;
|
||
}
|
||
|
||
Count -= 1;
|
||
NextScb += 1;
|
||
|
||
} while (Count != 0);
|
||
|
||
NtfsFreePool( IrpContext->SharedScb );
|
||
}
|
||
|
||
IrpContext->SharedScb = NULL;
|
||
IrpContext->SharedScbSize = 0;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseAllResources (
|
||
IN PIRP_CONTEXT IrpContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine release all resources tracked in the irpcontext including
|
||
exclusive fcb, paging / locked headers in the cleanup structure / cached file records
|
||
shared resources / quota blocks acquired for transactions
|
||
|
||
Does not release the vcb since this is hand-tracked.
|
||
Not paged since called by NtfsCleanupIrpContext which is not paged
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PFCB Fcb;
|
||
|
||
//
|
||
// Release the cached file record map
|
||
//
|
||
|
||
NtfsPurgeFileRecordCache( IrpContext );
|
||
|
||
|
||
#ifdef MAPCOUNT_DBG
|
||
//
|
||
// Check all mapping are gone now that we cleaned out cache
|
||
//
|
||
|
||
ASSERT( IrpContext->MapCount == 0 );
|
||
|
||
#endif
|
||
|
||
//
|
||
// Go through and free any Scb's in the queue of shared Scb's for transactions.
|
||
//
|
||
|
||
if (IrpContext->SharedScb != NULL) {
|
||
|
||
NtfsReleaseSharedResources( IrpContext );
|
||
}
|
||
|
||
//
|
||
// Free any exclusive paging I/O resource, or IoAtEof condition,
|
||
// this field is overlayed, minimally in write.c.
|
||
//
|
||
|
||
Fcb = IrpContext->CleanupStructure;
|
||
if (Fcb != NULL) {
|
||
|
||
if (Fcb->NodeTypeCode == NTFS_NTC_FCB) {
|
||
|
||
NtfsReleasePagingIo( IrpContext, Fcb );
|
||
|
||
} else {
|
||
|
||
FsRtlUnlockFsRtlHeader( (PNTFS_ADVANCED_FCB_HEADER) Fcb );
|
||
IrpContext->CleanupStructure = NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally, now that we have written the forget record, we can free
|
||
// any exclusive Scbs that we have been holding.
|
||
//
|
||
|
||
ASSERT( IrpContext->TransactionId == 0 );
|
||
|
||
while (!IsListEmpty( &IrpContext->ExclusiveFcbList )) {
|
||
|
||
Fcb = (PFCB)CONTAINING_RECORD( IrpContext->ExclusiveFcbList.Flink,
|
||
FCB,
|
||
ExclusiveFcbLinks );
|
||
|
||
NtfsReleaseFcb( IrpContext, Fcb );
|
||
}
|
||
|
||
ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_RELEASE_USN_JRNL |
|
||
IRP_CONTEXT_FLAG_RELEASE_MFT );
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsAcquireIndexCcb (
|
||
IN PSCB Scb,
|
||
IN PCCB Ccb,
|
||
IN PEOF_WAIT_BLOCK EofWaitBlock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to serialize access to a Ccb for a directory.
|
||
We must serialize access to the index context or we will corrupt
|
||
the data structure.
|
||
|
||
Arguments:
|
||
|
||
Scb - Scb for the directory to enumerate.
|
||
|
||
Ccb - Pointer to the Ccb for the handle.
|
||
|
||
EofWaitBlock - Uninitialized structure used only to serialize Eof updates. Our
|
||
caller will put this on the stack.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Acquire the mutex for serialization.
|
||
//
|
||
|
||
NtfsAcquireFsrtlHeader( Scb );
|
||
|
||
//
|
||
// Typical case is that we are the only active handle.
|
||
//
|
||
|
||
if (Ccb->EnumQueue.Flink == NULL) {
|
||
|
||
InitializeListHead( &Ccb->EnumQueue );
|
||
NtfsReleaseFsrtlHeader( Scb );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Initialize our event an put ourselves on the stack.
|
||
//
|
||
|
||
KeInitializeEvent( &EofWaitBlock->Event, NotificationEvent, FALSE );
|
||
InsertTailList( &Ccb->EnumQueue, &EofWaitBlock->EofWaitLinks );
|
||
|
||
//
|
||
// Free the mutex and wait. When the wait is satisfied then we are
|
||
// the active handle.
|
||
//
|
||
|
||
NtfsReleaseFsrtlHeader( Scb );
|
||
|
||
KeWaitForSingleObject( &EofWaitBlock->Event,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
(PLARGE_INTEGER)NULL);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseIndexCcb (
|
||
IN PSCB Scb,
|
||
IN PCCB Ccb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to release a Ccb for other people to access.
|
||
|
||
Arguments:
|
||
|
||
Scb - Scb for the directory to enumerate.
|
||
Ccb - Pointer to the Ccb for the handle.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PEOF_WAIT_BLOCK EofWaitBlock;
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Acquire the header and wake the next waiter or clear the list if it
|
||
// is now empty.
|
||
//
|
||
|
||
NtfsAcquireFsrtlHeader( Scb );
|
||
|
||
ASSERT( Ccb->EnumQueue.Flink != NULL );
|
||
if (IsListEmpty( &Ccb->EnumQueue )) {
|
||
|
||
Ccb->EnumQueue.Flink = NULL;
|
||
|
||
} else {
|
||
|
||
EofWaitBlock = (PEOF_WAIT_BLOCK) RemoveHeadList( &Ccb->EnumQueue );
|
||
KeSetEvent( &EofWaitBlock->Event, 0, FALSE );
|
||
}
|
||
|
||
NtfsReleaseFsrtlHeader( Scb );
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireScbForLazyWrite (
|
||
IN PVOID OpaqueScb,
|
||
IN BOOLEAN Wait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The address of this routine is specified when creating a CacheMap for
|
||
a file. It is subsequently called by the Lazy Writer prior to its
|
||
performing lazy writes to the file. This callback is necessary to
|
||
avoid deadlocks with the Lazy Writer. (Note that normal writes
|
||
acquire the Fcb, and then call the Cache Manager, who must acquire
|
||
some of his internal structures. If the Lazy Writer could not call
|
||
this routine first, and were to issue a write after locking Caching
|
||
data structures, then a deadlock could occur.)
|
||
|
||
Arguments:
|
||
|
||
OpaqueScb - The Scb which was specified as a context parameter for this
|
||
routine.
|
||
|
||
Wait - TRUE if the caller is willing to block.
|
||
|
||
Return Value:
|
||
|
||
FALSE - if Wait was specified as FALSE and blocking would have
|
||
been required. The Fcb is not acquired.
|
||
|
||
TRUE - if the Scb has been acquired
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN AcquiredFile = FALSE;
|
||
|
||
ULONG CompressedStream = (ULONG)((ULONG_PTR)OpaqueScb & 1);
|
||
PSCB Scb = (PSCB)((ULONG_PTR)OpaqueScb & ~1);
|
||
PFCB Fcb = Scb->Fcb;
|
||
|
||
ASSERT_SCB(Scb);
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Acquire the Scb only for those files that the write will
|
||
// acquire it for, i.e., not the first set of system files.
|
||
// Otherwise we can deadlock, for example with someone needing
|
||
// a new Mft record.
|
||
//
|
||
|
||
if (NtfsSegmentNumber( &Fcb->FileReference ) <= MASTER_FILE_TABLE2_NUMBER) {
|
||
|
||
//
|
||
// We need to synchronize the lazy writer with the clean volume
|
||
// checkpoint. We do this by acquiring and immediately releasing this
|
||
// Scb. This is to prevent the lazy writer from flushing the log file
|
||
// when the space may be at a premium.
|
||
//
|
||
|
||
if (NtfsAcquireResourceShared( NULL, Scb, Wait )) {
|
||
|
||
if (ExAcquireResourceSharedLite( &Scb->Vcb->MftFlushResource, Wait )) {
|
||
//
|
||
// The mft bitmap will reacquire the mft resource in LookupAllocation
|
||
// if its not loaded during a write - this would deadlock with allocating
|
||
// a mft record. bcb exclusive - mft main vs mft main - bcb shared
|
||
//
|
||
|
||
ASSERT( (Scb != Scb->Vcb->MftBitmapScb) ||
|
||
|
||
((Scb->Mcb.NtfsMcbArraySizeInUse > 0) &&
|
||
((Scb->Mcb.NtfsMcbArray[ Scb->Mcb.NtfsMcbArraySizeInUse - 1].EndingVcn + 1) ==
|
||
LlClustersFromBytes( Scb->Vcb, Scb->Header.AllocationSize.QuadPart ))) );
|
||
|
||
AcquiredFile = TRUE;
|
||
}
|
||
NtfsReleaseResource( NULL, Scb );
|
||
}
|
||
//
|
||
// Now acquire either the main or paging io resource depending on the
|
||
// state of the file.
|
||
//
|
||
|
||
} else if (Scb->Header.PagingIoResource != NULL) {
|
||
AcquiredFile = ExAcquireResourceSharedLite( Scb->Header.PagingIoResource, Wait );
|
||
} else {
|
||
|
||
if (FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_RESIDENT | SCB_STATE_CONVERT_UNDERWAY )) {
|
||
|
||
AcquiredFile = NtfsAcquireResourceExclusive( NULL, Scb, Wait );
|
||
|
||
} else {
|
||
|
||
AcquiredFile = NtfsAcquireResourceShared( NULL, Scb, Wait );
|
||
}
|
||
}
|
||
|
||
if (AcquiredFile) {
|
||
|
||
//
|
||
// We assume the Lazy Writer only acquires this Scb once. When he
|
||
// has acquired it, then he has eliminated anyone who would extend
|
||
// valid data, since they must take out the resource exclusive.
|
||
// Therefore, it should be guaranteed that this flag is currently
|
||
// clear (the ASSERT), and then we will set this flag, to insure
|
||
// that the Lazy Writer will never try to advance Valid Data, and
|
||
// also not deadlock by trying to get the Fcb exclusive.
|
||
//
|
||
|
||
ASSERT( Scb->LazyWriteThread[CompressedStream] == NULL );
|
||
|
||
Scb->LazyWriteThread[CompressedStream] = PsGetCurrentThread();
|
||
|
||
//
|
||
// Make Cc top level, so that we will not post or retry on errors.
|
||
// (If it is not NULL, it must be one of our internal calls to this
|
||
// routine, such as from Restart or Hot Fix.)
|
||
//
|
||
|
||
if (IoGetTopLevelIrp() == NULL) {
|
||
IoSetTopLevelIrp( (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP );
|
||
}
|
||
}
|
||
|
||
return AcquiredFile;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseScbFromLazyWrite (
|
||
IN PVOID OpaqueScb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The address of this routine is specified when creating a CacheMap for
|
||
a file. It is subsequently called by the Lazy Writer after its
|
||
performing lazy writes to the file.
|
||
|
||
Arguments:
|
||
|
||
Scb - The Scb which was specified as a context parameter for this
|
||
routine.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG CompressedStream = (ULONG)((ULONG_PTR)OpaqueScb & 1);
|
||
PSCB Scb = (PSCB)((ULONG_PTR)OpaqueScb & ~1);
|
||
PFCB Fcb = Scb->Fcb;
|
||
ULONG CleanCheckpoint = 0;
|
||
|
||
ASSERT_SCB(Scb);
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Clear the toplevel at this point, if we set it above.
|
||
//
|
||
|
||
if ((((ULONG_PTR) IoGetTopLevelIrp()) & ~0x80000000) == FSRTL_CACHE_TOP_LEVEL_IRP) {
|
||
|
||
//
|
||
// We use the upper bit of this field to indicate that we need to
|
||
// do a clean checkpoint.
|
||
//
|
||
|
||
CleanCheckpoint = (ULONG)FlagOn( (ULONG_PTR) IoGetTopLevelIrp(), 0x80000000 );
|
||
IoSetTopLevelIrp( NULL );
|
||
}
|
||
|
||
Scb->LazyWriteThread[CompressedStream] = NULL;
|
||
|
||
if (NtfsSegmentNumber( &Fcb->FileReference ) <= MASTER_FILE_TABLE2_NUMBER) {
|
||
|
||
ExReleaseResourceLite( &Scb->Vcb->MftFlushResource );
|
||
|
||
} else if (Scb->Header.PagingIoResource != NULL) {
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
} else {
|
||
NtfsReleaseResource( NULL, Scb );
|
||
}
|
||
|
||
//
|
||
// Do a clean checkpoint if necessary.
|
||
//
|
||
|
||
if (CleanCheckpoint) {
|
||
|
||
NtfsCleanCheckpoint( Scb->Vcb );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsAcquireFileForModWrite (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PLARGE_INTEGER EndingOffset,
|
||
OUT PERESOURCE *ResourceToRelease,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
|
||
{
|
||
BOOLEAN AcquiredFile = FALSE;
|
||
|
||
PSCB Scb = (PSCB) (FileObject->FsContext);
|
||
PFCB Fcb = Scb->Fcb;
|
||
|
||
ASSERT_SCB( Scb );
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// All files should not be mod-no-write and have paging resource
|
||
//
|
||
|
||
ASSERT( NtfsSegmentNumber( &Fcb->FileReference ) >= MASTER_FILE_TABLE2_NUMBER );
|
||
ASSERT( Scb->Header.PagingIoResource != NULL );
|
||
|
||
AcquiredFile = NtfsAcquirePagingResourceSharedWaitForExclusive( NULL, Scb, FALSE );
|
||
|
||
//
|
||
// If we got the resource, check if he is possibly trying to extend
|
||
// ValidDataLength. If so that will cause us to go into useless mode
|
||
// possibly doing actual I/O writing zeros out to the file past actual
|
||
// valid data in the cache. This is so inefficient that it is better
|
||
// to tell MM not to do this write.
|
||
//
|
||
|
||
if (AcquiredFile) {
|
||
*ResourceToRelease = Scb->Fcb->PagingIoResource;
|
||
if (FlagOn( Scb->AttributeFlags, ATTRIBUTE_FLAG_COMPRESSION_MASK )) {
|
||
NtfsAcquireFsrtlHeader( Scb );
|
||
if ((EndingOffset->QuadPart > Scb->ValidDataToDisk) &&
|
||
(EndingOffset->QuadPart < Scb->Header.FileSize.QuadPart) &&
|
||
!FlagOn(Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE)) {
|
||
|
||
ExReleaseResourceLite( *ResourceToRelease );
|
||
AcquiredFile = FALSE;
|
||
*ResourceToRelease = NULL;
|
||
}
|
||
NtfsReleaseFsrtlHeader( Scb );
|
||
}
|
||
} else {
|
||
*ResourceToRelease = NULL;
|
||
}
|
||
|
||
return (AcquiredFile ? STATUS_SUCCESS : STATUS_CANT_WAIT);
|
||
}
|
||
|
||
NTSTATUS
|
||
NtfsAcquireFileForCcFlush (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
{
|
||
PFSRTL_COMMON_FCB_HEADER Header = FileObject->FsContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Header->PagingIoResource != NULL) {
|
||
ExAcquireResourceSharedLite( Header->PagingIoResource, TRUE );
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
}
|
||
|
||
NTSTATUS
|
||
NtfsReleaseFileForCcFlush (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
{
|
||
PSCB Scb = (PSCB) FileObject->FsContext;
|
||
BOOLEAN CleanCheckpoint = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
|
||
//
|
||
// If we are getting repeated log file fulls then we want to process that before retrying
|
||
// this request. This will prevent a section flush from failing and returning
|
||
// STATUS_FILE_LOCK_CONFLICT to the user.
|
||
//
|
||
|
||
if (Scb->Vcb->CleanCheckpointMark + 3 < Scb->Vcb->LogFileFullCount) {
|
||
|
||
CleanCheckpoint = TRUE;
|
||
}
|
||
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
|
||
//
|
||
// We may be be in a recursive acquisition callback in that case even
|
||
// after releasing the resource we may still own it and be unable to
|
||
// checkpoint
|
||
//
|
||
|
||
if (CleanCheckpoint &&
|
||
(IoGetTopLevelIrp() == NULL) &&
|
||
!NtfsIsExclusiveScbPagingIo( Scb )) {
|
||
|
||
NtfsCleanCheckpoint( Scb->Vcb );
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
}
|
||
|
||
VOID
|
||
NtfsAcquireForCreateSection (
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
{
|
||
PSCB Scb = (PSCB)FileObject->FsContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
|
||
//
|
||
// Use an unsafe test to see if a dummy checkpoint has been posted.
|
||
// We can use an unsafe test, since the top level caller must retry
|
||
// if a STATUS_FILE_LOCK_CONFLICT is returned.
|
||
//
|
||
|
||
if (FlagOn( Scb->Vcb->CheckpointFlags, VCB_DUMMY_CHECKPOINT_POSTED )) {
|
||
|
||
NtfsCleanCheckpoint( Scb->Vcb );
|
||
}
|
||
|
||
ExAcquireResourceExclusiveLite( Scb->Header.PagingIoResource, TRUE );
|
||
}
|
||
}
|
||
|
||
VOID
|
||
NtfsReleaseForCreateSection (
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
{
|
||
PSCB Scb = (PSCB)FileObject->FsContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireScbForReadAhead (
|
||
IN PVOID OpaqueScb,
|
||
IN BOOLEAN Wait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The address of this routine is specified when creating a CacheMap for
|
||
a file. It is subsequently called by the Lazy Writer prior to its
|
||
performing read ahead to the file.
|
||
|
||
Arguments:
|
||
|
||
Scb - The Scb which was specified as a context parameter for this
|
||
routine.
|
||
|
||
Wait - TRUE if the caller is willing to block.
|
||
|
||
Return Value:
|
||
|
||
FALSE - if Wait was specified as FALSE and blocking would have
|
||
been required. The Fcb is not acquired.
|
||
|
||
TRUE - if the Scb has been acquired
|
||
|
||
--*/
|
||
|
||
{
|
||
PREAD_AHEAD_THREAD ReadAheadThread;
|
||
PVOID CurrentThread;
|
||
KIRQL OldIrql;
|
||
PSCB Scb = (PSCB)OpaqueScb;
|
||
PFCB Fcb = Scb->Fcb;
|
||
BOOLEAN AcquiredFile = FALSE;
|
||
|
||
ASSERT_SCB(Scb);
|
||
|
||
//
|
||
// Acquire the Scb only for those files that the read wil
|
||
// acquire it for, i.e., not the first set of system files.
|
||
// Otherwise we can deadlock, for example with someone needing
|
||
// a new Mft record.
|
||
//
|
||
|
||
if ((Scb->Header.PagingIoResource == NULL) ||
|
||
ExAcquireResourceSharedLite( Scb->Header.PagingIoResource, Wait )) {
|
||
|
||
AcquiredFile = TRUE;
|
||
|
||
//
|
||
// Add our thread to the read ahead list.
|
||
//
|
||
|
||
OldIrql = KeAcquireQueuedSpinLock( LockQueueNtfsStructLock );
|
||
|
||
CurrentThread = (PVOID)PsGetCurrentThread();
|
||
ReadAheadThread = (PREAD_AHEAD_THREAD)NtfsData.ReadAheadThreads.Flink;
|
||
|
||
while ((ReadAheadThread != (PREAD_AHEAD_THREAD)&NtfsData.ReadAheadThreads) &&
|
||
(ReadAheadThread->Thread != NULL)) {
|
||
|
||
//
|
||
// We better not already see ourselves.
|
||
//
|
||
|
||
ASSERT( ReadAheadThread->Thread != CurrentThread );
|
||
|
||
ReadAheadThread = (PREAD_AHEAD_THREAD)ReadAheadThread->Links.Flink;
|
||
}
|
||
|
||
//
|
||
// If we hit the end of the list, then allocate a new one. Note we
|
||
// should have at most one entry per critical worker thread in the
|
||
// system.
|
||
//
|
||
|
||
if (ReadAheadThread == (PREAD_AHEAD_THREAD)&NtfsData.ReadAheadThreads) {
|
||
|
||
ReadAheadThread = NtfsAllocatePoolWithTagNoRaise( NonPagedPool, sizeof(READ_AHEAD_THREAD), 'RftN' );
|
||
|
||
//
|
||
// If we failed to allocate an entry, clean up and raise.
|
||
//
|
||
|
||
if (ReadAheadThread == NULL) {
|
||
|
||
KeReleaseQueuedSpinLock( LockQueueNtfsStructLock, OldIrql );
|
||
|
||
if (NtfsSegmentNumber( &Fcb->FileReference ) > VOLUME_DASD_NUMBER) {
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
}
|
||
}
|
||
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
InsertTailList( &NtfsData.ReadAheadThreads, &ReadAheadThread->Links );
|
||
}
|
||
|
||
ReadAheadThread->Thread = CurrentThread;
|
||
|
||
KeReleaseQueuedSpinLock( LockQueueNtfsStructLock, OldIrql );
|
||
}
|
||
|
||
return AcquiredFile;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseScbFromReadAhead (
|
||
IN PVOID OpaqueScb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The address of this routine is specified when creating a CacheMap for
|
||
a file. It is subsequently called by the Lazy Writer after its
|
||
read ahead.
|
||
|
||
Arguments:
|
||
|
||
Scb - The Scb which was specified as a context parameter for this
|
||
routine.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PREAD_AHEAD_THREAD ReadAheadThread;
|
||
PVOID CurrentThread;
|
||
KIRQL OldIrql;
|
||
PSCB Scb = (PSCB)OpaqueScb;
|
||
PFCB Fcb = Scb->Fcb;
|
||
|
||
ASSERT_SCB(Scb);
|
||
|
||
//
|
||
// Free our read ahead entry.
|
||
//
|
||
|
||
OldIrql = KeAcquireQueuedSpinLock( LockQueueNtfsStructLock );
|
||
|
||
CurrentThread = (PVOID)PsGetCurrentThread();
|
||
ReadAheadThread = (PREAD_AHEAD_THREAD)NtfsData.ReadAheadThreads.Flink;
|
||
|
||
while ((ReadAheadThread != (PREAD_AHEAD_THREAD)&NtfsData.ReadAheadThreads) &&
|
||
(ReadAheadThread->Thread != CurrentThread)) {
|
||
|
||
ReadAheadThread = (PREAD_AHEAD_THREAD)ReadAheadThread->Links.Flink;
|
||
}
|
||
|
||
ASSERT(ReadAheadThread != (PREAD_AHEAD_THREAD)&NtfsData.ReadAheadThreads);
|
||
|
||
ReadAheadThread->Thread = NULL;
|
||
|
||
//
|
||
// Move him to the end of the list so all the allocated entries are at
|
||
// the front, and we simplify our scans.
|
||
//
|
||
|
||
RemoveEntryList( &ReadAheadThread->Links );
|
||
InsertTailList( &NtfsData.ReadAheadThreads, &ReadAheadThread->Links );
|
||
|
||
KeReleaseQueuedSpinLock( LockQueueNtfsStructLock, OldIrql );
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
ExReleaseResourceLite( Scb->Header.PagingIoResource );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireVolumeFileForLazyWrite (
|
||
IN PVOID Vcb,
|
||
IN BOOLEAN Wait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The address of this routine is specified when creating a CacheMap for
|
||
the volume file. It is subsequently called by the Lazy Writer prior to its
|
||
performing lazy writes to the volume file. This callback may one day be
|
||
necessary to avoid deadlocks with the Lazy Writer, however, now
|
||
NtfsCommonWrite does not need to acquire any resource for the volume file,
|
||
so this routine is simply a noop.
|
||
|
||
Arguments:
|
||
|
||
Vcb - The Vcb which was specified as a context parameter for this
|
||
routine.
|
||
|
||
Wait - TRUE if the caller is willing to block.
|
||
|
||
Return Value:
|
||
|
||
TRUE
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER( Vcb );
|
||
UNREFERENCED_PARAMETER( Wait );
|
||
|
||
PAGED_CODE();
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseVolumeFileFromLazyWrite (
|
||
IN PVOID Vcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The address of this routine is specified when creating a CacheMap for
|
||
a file. It is subsequently called by the Lazy Writer after its
|
||
performing lazy writes to the file.
|
||
|
||
Arguments:
|
||
|
||
Vcb - The Vcb which was specified as a context parameter for this
|
||
routine.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER( Vcb );
|
||
|
||
PAGED_CODE();
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTFS_RESOURCE_NAME
|
||
NtfsIdentifyFcb (
|
||
IN PVCB Vcb,
|
||
IN PFCB Fcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Identifies the resource type of a given FCB. I.e is it the mft. Used for
|
||
lock order identification.
|
||
|
||
Arguments:
|
||
|
||
Vcb - The vcb for the volume
|
||
|
||
Fcb - The fcb to identify
|
||
|
||
Return Value:
|
||
|
||
TRUE
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
if ((NtfsSegmentNumber( &Fcb->FileReference ) == MASTER_FILE_TABLE_NUMBER)) {
|
||
return NtfsResourceMft;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == MASTER_FILE_TABLE2_NUMBER)) {
|
||
return NtfsResourceMft2;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == VOLUME_DASD_NUMBER)) {
|
||
return NtfsResourceVolume;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == LOG_FILE_NUMBER)) {
|
||
return NtfsResourceLogFile;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == BAD_CLUSTER_FILE_NUMBER)) {
|
||
return NtfsResourceBadClust;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == SECURITY_FILE_NUMBER)) {
|
||
return NtfsResourceSecure;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == ROOT_FILE_NAME_INDEX_NUMBER)) {
|
||
return NtfsResourceRootDir;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == BIT_MAP_FILE_NUMBER)) {
|
||
return NtfsResourceBitmap;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == BOOT_FILE_NUMBER)) {
|
||
return NtfsResourceBoot;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == EXTEND_NUMBER)) {
|
||
return NtfsResourceExtendDir;
|
||
} else if ((Vcb->UsnJournal && (Fcb == Vcb->UsnJournal->Fcb)) ||
|
||
FlagOn( Fcb->FcbState, FCB_STATE_USN_JOURNAL)) {
|
||
return NtfsResourceUsnJournal;
|
||
} else if (Vcb->QuotaTableScb && (Fcb == Vcb->QuotaTableScb->Fcb)) {
|
||
return NtfsResourceQuotaTable;
|
||
} else if (Vcb->ObjectIdTableScb && (Fcb == Vcb->ObjectIdTableScb->Fcb)) {
|
||
return NtfsResourceObjectIdTable;
|
||
} else if (Vcb->ReparsePointTableScb && (Fcb == Vcb->ReparsePointTableScb->Fcb)) {
|
||
return NtfsResourceReparseTable;
|
||
} else if ((NtfsSegmentNumber( &Fcb->FileReference ) == UPCASE_TABLE_NUMBER)) {
|
||
return NtfsResourceUpCase;
|
||
} else if (Vcb->AttributeDefTableScb && (Fcb == Vcb->AttributeDefTableScb->Fcb)) {
|
||
return NtfsResourceAttrDefTable;
|
||
} else {
|
||
return NtfsResourceFile;
|
||
}
|
||
}
|
||
|
||
#ifdef NTFSDBG
|
||
|
||
BOOLEAN
|
||
NtfsChangeResourceOrderState(
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN NTFS_RESOURCE_NAME NewResource,
|
||
IN BOOLEAN Release,
|
||
IN ULONG UnsafeTransition
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Update the state table because of the new acquired resource
|
||
|
||
Arguments:
|
||
|
||
IrpContext -- contains the state table
|
||
|
||
NewResource -- The new resource acquired
|
||
|
||
Return Value:
|
||
|
||
TRUE if this is a valid transition
|
||
|
||
--*/
|
||
|
||
{
|
||
PTOP_LEVEL_CONTEXT TopLevelContext;
|
||
PIRP_CONTEXT TopIrpContext = IrpContext;
|
||
ULONG_PTR StackBottom;
|
||
ULONG_PTR StackTop;
|
||
LONG Index;
|
||
LONG NumTransitions = sizeof( OwnershipTransitionTable ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
||
|
||
//
|
||
// Work around the forced top level context of reads to find the real top level
|
||
//
|
||
|
||
IoGetStackLimits( &StackTop, &StackBottom );
|
||
|
||
TopLevelContext = NtfsGetTopLevelContext();
|
||
if ((TopLevelContext != NULL) && (TopLevelContext->SavedTopLevelIrp != NULL)) {
|
||
TopLevelContext = (PTOP_LEVEL_CONTEXT)TopLevelContext->SavedTopLevelIrp;
|
||
|
||
if (((ULONG_PTR) TopLevelContext <= StackBottom - sizeof( TOP_LEVEL_CONTEXT )) &&
|
||
((ULONG_PTR) TopLevelContext >= StackTop) &&
|
||
!FlagOn( (ULONG_PTR) TopLevelContext, 0x3 ) &&
|
||
(TopLevelContext->Ntfs == 0x5346544e)) {
|
||
|
||
TopIrpContext = TopLevelContext->ThreadIrpContext;
|
||
}
|
||
}
|
||
|
||
TopIrpContext = TopIrpContext->TopLevelIrpContext;
|
||
|
||
//
|
||
// Skip verification on mounts
|
||
//
|
||
|
||
if ((TopIrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
|
||
(TopIrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME)) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Keep track of how many normal files we own
|
||
//
|
||
|
||
if (NtfsResourceFile == NewResource) {
|
||
|
||
if (Release) {
|
||
|
||
TopIrpContext->FilesOwnedCount -= 1;
|
||
|
||
//
|
||
// Only change state if back to 0 for files
|
||
//
|
||
|
||
if (TopIrpContext->FilesOwnedCount) {
|
||
return TRUE;
|
||
}
|
||
|
||
} else {
|
||
|
||
TopIrpContext->FilesOwnedCount += 1;
|
||
|
||
//
|
||
// Only change state if fwd to 0 for files
|
||
//
|
||
|
||
if (TopIrpContext->FilesOwnedCount > 1) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
for (Index=0; Index < NumTransitions; Index += 1) {
|
||
|
||
if ((!Release &&
|
||
(OwnershipTransitionTable[Index].Begin == TopIrpContext->OwnershipState) &&
|
||
((OwnershipTransitionTable[Index].Acquired == NewResource) ||
|
||
(OwnershipTransitionTable[Index].Acquired == NtfsResourceAny))) ||
|
||
|
||
((OwnershipTransitionTable[Index].End == TopIrpContext->OwnershipState) &&
|
||
(OwnershipTransitionTable[Index].Acquired == NewResource))) {
|
||
|
||
if (Release) {
|
||
TopIrpContext->OwnershipState = OwnershipTransitionTable[Index].Begin;
|
||
|
||
} else {
|
||
TopIrpContext->OwnershipState = OwnershipTransitionTable[Index].End;
|
||
}
|
||
|
||
#ifdef NTFSDBG
|
||
if (TopIrpContext->OwnershipState == NtfsBreakOnState) {
|
||
if ((NULL == NtfsBreakOnIrpContext) || (TopIrpContext == NtfsBreakOnIrpContext) ) {
|
||
KdPrint(( "NTFS: Breaking for matched state\n" ));
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
if (NtfsPrintOnLockProb) {
|
||
if ((NULL == NtfsBreakOnIrpContext) || (TopIrpContext == NtfsBreakOnIrpContext) ) {
|
||
KdPrint(( "NTFS: change context: 0x%x to 0x%x for 0x%x during 0x%x\n", TopIrpContext, TopIrpContext->OwnershipState, NewResource, Release ));
|
||
}
|
||
}
|
||
#endif
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If an unsafe transition (not blocking) check the extra table
|
||
//
|
||
|
||
if (UnsafeTransition && !Release) {
|
||
NumTransitions = sizeof( OwnershipTransitionTableUnsafe ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
||
|
||
for (Index=0; Index < NumTransitions; Index += 1) {
|
||
|
||
if (((OwnershipTransitionTableUnsafe[Index].Begin == TopIrpContext->OwnershipState) &&
|
||
((OwnershipTransitionTableUnsafe[Index].Acquired == NewResource) ||
|
||
(OwnershipTransitionTableUnsafe[Index].Acquired == NtfsResourceAny)))) {
|
||
|
||
TopIrpContext->OwnershipState = OwnershipTransitionTableUnsafe[Index].End;
|
||
|
||
#ifdef NTFSDBG
|
||
if (TopIrpContext->OwnershipState == NtfsBreakOnState) {
|
||
if ((NULL == NtfsBreakOnIrpContext) || (TopIrpContext == NtfsBreakOnIrpContext) ) {
|
||
KdPrint(( "NTFS: Breaking for matched state\n" ));
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
if (NtfsPrintOnLockProb) {
|
||
if ((NULL == NtfsBreakOnIrpContext) || (TopIrpContext == NtfsBreakOnIrpContext) ) {
|
||
KdPrint(( "NTFS: change context: 0x%x to 0x%x for 0x%x during 0x%x\n", TopIrpContext, TopIrpContext->OwnershipState, NewResource, Release ));
|
||
}
|
||
}
|
||
#endif
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check the one way transtions for release and acquire
|
||
//
|
||
|
||
if (Release) {
|
||
|
||
NumTransitions = sizeof( OwnershipTransitionTableRelease ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
||
|
||
for (Index=0; Index < NumTransitions; Index += 1) {
|
||
|
||
if ((OwnershipTransitionTableRelease[Index].Begin == TopIrpContext->OwnershipState) &&
|
||
((OwnershipTransitionTableRelease[Index].Acquired == NewResource) ||
|
||
(OwnershipTransitionTableRelease[Index].Acquired == NtfsResourceAny))) {
|
||
|
||
TopIrpContext->OwnershipState = OwnershipTransitionTableRelease[Index].End;
|
||
|
||
#ifdef NTFSDBG
|
||
if (TopIrpContext->OwnershipState == NtfsBreakOnState) {
|
||
if ((NULL == NtfsBreakOnIrpContext) || (TopIrpContext == NtfsBreakOnIrpContext) ) {
|
||
KdPrint(( "NTFS: Breaking for matched state\n" ));
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
if (NtfsPrintOnLockProb) {
|
||
if ((NULL == NtfsBreakOnIrpContext) || (TopIrpContext == NtfsBreakOnIrpContext) ) {
|
||
KdPrint(( "NTFS: change context: 0x%x to 0x%x for 0x%x during 0x%x\n", TopIrpContext, TopIrpContext->OwnershipState, NewResource, Release ));
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
} else {
|
||
|
||
NumTransitions = sizeof( OwnershipTransitionTableAcquire ) / sizeof( NTFS_OWNERSHIP_TRANSITION );
|
||
|
||
for (Index=0; Index < NumTransitions; Index += 1) {
|
||
|
||
if ((OwnershipTransitionTableAcquire[Index].Begin == TopIrpContext->OwnershipState) &&
|
||
((OwnershipTransitionTableAcquire[Index].Acquired == NewResource) ||
|
||
(OwnershipTransitionTableAcquire[Index].Acquired == NtfsResourceAny))) {
|
||
|
||
TopIrpContext->OwnershipState = OwnershipTransitionTableAcquire[Index].End;
|
||
|
||
#ifdef NTFSDBG
|
||
if (TopIrpContext->OwnershipState == NtfsBreakOnState) {
|
||
if ((NULL == NtfsBreakOnIrpContext) || (TopIrpContext == NtfsBreakOnIrpContext) ) {
|
||
KdPrint(( "NTFS: Breaking for matched state\n" ));
|
||
DbgBreakPoint();
|
||
}
|
||
}
|
||
|
||
if (NtfsPrintOnLockProb) {
|
||
if ((NULL == NtfsBreakOnIrpContext) || (TopIrpContext == NtfsBreakOnIrpContext) ) {
|
||
KdPrint(( "NTFS: change context: 0x%x to 0x%x for 0x%x during 0x%x\n", TopIrpContext, TopIrpContext->OwnershipState, NewResource, Release ));
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef NTFSDBG
|
||
if (NtfsAssertOnLockProb) {
|
||
KdPrint(( "NTFS: unknown transition from state: 0x%x resource: 0x%x release: %d unsafe: %d\n", TopIrpContext->OwnershipState, NewResource, Release, UnsafeTransition ));
|
||
ASSERT( FALSE );
|
||
}
|
||
#endif
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireResourceExclusive (
|
||
IN PIRP_CONTEXT IrpContext OPTIONAL,
|
||
IN PVOID FcbOrScb,
|
||
IN BOOLEAN Wait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires the main resource of the specified structure useing the specified wait
|
||
flag. It will update the resource state in the IrpContext if present.
|
||
|
||
Arguments:
|
||
|
||
FcbOrScb - Data structure on which we are synchronizing.
|
||
|
||
Wait - Indicates if we can wait for the resource.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the resource was acquired, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTFS_RESOURCE_NAME ResourceName;
|
||
PFCB Fcb;
|
||
BOOLEAN Result;
|
||
|
||
//
|
||
// Find the Fcb for either input structure.
|
||
//
|
||
|
||
if (NTFS_NTC_FCB == ((PFCB)FcbOrScb)->NodeTypeCode) {
|
||
Fcb = (PFCB)FcbOrScb;
|
||
|
||
} else {
|
||
Fcb = ((PSCB)FcbOrScb)->Fcb;
|
||
|
||
ASSERT( Fcb->Resource == ((PSCB)FcbOrScb)->Header.Resource );
|
||
}
|
||
|
||
//
|
||
// For blocking calls check 1st
|
||
//
|
||
|
||
if (Wait &&
|
||
ARGUMENT_PRESENT( IrpContext ) &&
|
||
FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) &&
|
||
(0 == ExIsResourceAcquiredSharedLite( Fcb->Resource ))) {
|
||
|
||
ResourceName = NtfsIdentifyFcb( IrpContext->Vcb, Fcb );
|
||
NtfsChangeResourceOrderState( IrpContext, ResourceName, FALSE, FALSE );
|
||
}
|
||
|
||
Result = ExAcquireResourceExclusiveLite( Fcb->Resource, Wait );
|
||
|
||
//
|
||
// For nonblocking calls afterwards when own the resource
|
||
//
|
||
|
||
if (Result &&
|
||
!Wait &&
|
||
ARGUMENT_PRESENT( IrpContext ) &&
|
||
FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) &&
|
||
(1 == ExIsResourceAcquiredSharedLite( Fcb->Resource))) {
|
||
|
||
ResourceName = NtfsIdentifyFcb( IrpContext->Vcb, Fcb );
|
||
NtfsChangeResourceOrderState( IrpContext, ResourceName, FALSE, TRUE );
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireResourceShared (
|
||
IN PIRP_CONTEXT IrpContext OPTIONAL,
|
||
IN PVOID FcbOrScb,
|
||
IN BOOLEAN Wait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to acquire the main resource of the specified structure shared using the
|
||
specified wait flag. It will also update the resource state in the IrpContext if present.
|
||
|
||
Arguments:
|
||
|
||
FcbOrScb - Data structure on which we are synchronizing.
|
||
|
||
Wait - Indicates if we can wait for the resource.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the resource was acquired, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTFS_RESOURCE_NAME ResourceName;
|
||
BOOLEAN Result;
|
||
PFCB Fcb;
|
||
|
||
//
|
||
// Find the Fcb for either input structure.
|
||
//
|
||
|
||
if (NTFS_NTC_FCB == ((PFCB)FcbOrScb)->NodeTypeCode) {
|
||
Fcb = (PFCB)FcbOrScb;
|
||
|
||
} else {
|
||
Fcb = ((PSCB)FcbOrScb)->Fcb;
|
||
|
||
ASSERT( Fcb->Resource == ((PSCB)FcbOrScb)->Header.Resource );
|
||
}
|
||
|
||
//
|
||
// For blocking calls check 1st
|
||
//
|
||
|
||
if (Wait &&
|
||
ARGUMENT_PRESENT( IrpContext ) &&
|
||
FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) &&
|
||
(0 == ExIsResourceAcquiredSharedLite( Fcb->Resource))) {
|
||
|
||
ResourceName = NtfsIdentifyFcb( IrpContext->Vcb, Fcb );
|
||
NtfsChangeResourceOrderState( IrpContext, ResourceName, FALSE, FALSE );
|
||
}
|
||
|
||
Result = ExAcquireResourceSharedLite( Fcb->Resource, Wait );
|
||
|
||
//
|
||
// For nonblocking calls afterwards when own the resource
|
||
//
|
||
|
||
if (Result &&
|
||
!Wait &&
|
||
ARGUMENT_PRESENT( IrpContext ) &&
|
||
FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED ) &&
|
||
(1 == ExIsResourceAcquiredSharedLite( Fcb->Resource ))) {
|
||
|
||
ResourceName = NtfsIdentifyFcb( IrpContext->Vcb, Fcb );
|
||
NtfsChangeResourceOrderState( IrpContext, ResourceName, FALSE, TRUE );
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseResource (
|
||
IN PIRP_CONTEXT IrpContext OPTIONAL,
|
||
IN PVOID FcbOrScb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to release the main resource of the specified structure and update the
|
||
resource state in the IrpContext if present.
|
||
|
||
Arguments:
|
||
|
||
FcbOrScb - Data structure on which we are synchronizing.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
NTFS_RESOURCE_NAME ResourceName;
|
||
PFCB Fcb;
|
||
|
||
//
|
||
// Find the Fcb for either input structure.
|
||
//
|
||
|
||
if (NTFS_NTC_FCB == ((PFCB)FcbOrScb)->NodeTypeCode) {
|
||
Fcb = (PFCB)FcbOrScb;
|
||
|
||
} else {
|
||
Fcb = ((PSCB)FcbOrScb)->Fcb;
|
||
|
||
ASSERT( Fcb->Resource == ((PSCB)FcbOrScb)->Header.Resource );
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT( IrpContext )) {
|
||
if (FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
||
|
||
ResourceName = NtfsIdentifyFcb( IrpContext->Vcb, Fcb );
|
||
|
||
//
|
||
// Only change ownership state if we are really releasing the resource
|
||
//
|
||
|
||
if (1 == ExIsResourceAcquiredSharedLite( Fcb->Resource )) {
|
||
NtfsChangeResourceOrderState( IrpContext, ResourceName, TRUE, FALSE );
|
||
}
|
||
|
||
} else {
|
||
|
||
IrpContext->OwnershipState = None;
|
||
}
|
||
}
|
||
|
||
ExReleaseResourceLite( Fcb->Resource );
|
||
}
|
||
|
||
#endif // NTFSDBG
|