mirror of https://github.com/tongzx/nt5src
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.
2996 lines
74 KiB
2996 lines
74 KiB
/*++
|
|
|
|
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
|