2115 lines
48 KiB
C
2115 lines
48 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"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, NtfsAcquireAllFiles)
|
||
#pragma alloc_text(PAGE, NtfsAcquireExclusiveFcb)
|
||
#pragma alloc_text(PAGE, NtfsAcquireExclusiveScb)
|
||
#pragma alloc_text(PAGE, NtfsAcquireSharedScbForTransaction)
|
||
#pragma alloc_text(PAGE, NtfsAcquireExclusiveGlobal)
|
||
#pragma alloc_text(PAGE, NtfsAcquireExclusiveVcb)
|
||
#pragma alloc_text(PAGE, NtfsAcquireFcbWithPaging)
|
||
#pragma alloc_text(PAGE, NtfsAcquireForCreateSection)
|
||
#pragma alloc_text(PAGE, NtfsAcquireScbForLazyWrite)
|
||
#pragma alloc_text(PAGE, NtfsAcquireSharedGlobal)
|
||
#pragma alloc_text(PAGE, NtfsAcquireFileForCcFlush)
|
||
#pragma alloc_text(PAGE, NtfsAcquireFileForModWrite)
|
||
#pragma alloc_text(PAGE, NtfsAcquireSharedVcb)
|
||
#pragma alloc_text(PAGE, NtfsAcquireVolumeFileForClose)
|
||
#pragma alloc_text(PAGE, NtfsAcquireVolumeFileForLazyWrite)
|
||
#pragma alloc_text(PAGE, NtfsAcquireVolumeForClose)
|
||
#pragma alloc_text(PAGE, NtfsReleaseAllFiles)
|
||
#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, NtfsReleaseVolumeFileFromClose)
|
||
#pragma alloc_text(PAGE, NtfsReleaseVolumeFileFromLazyWrite)
|
||
#pragma alloc_text(PAGE, NtfsReleaseVolumeFromClose)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
NtfsAcquireExclusiveGlobal (
|
||
IN PIRP_CONTEXT IrpContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires exclusive access to the global resource.
|
||
|
||
This routine will raise if it cannot acquire the resource and wait
|
||
in the IrpContext is false.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!ExAcquireResourceExclusive( &NtfsData.Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsAcquireSharedGlobal (
|
||
IN PIRP_CONTEXT IrpContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine acquires shared access to the global resource.
|
||
|
||
This routine will raise if it cannot acquire the resource and wait
|
||
in the IrpContext is false.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!ExAcquireResourceShared( &NtfsData.Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsAcquireAllFiles (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN BOOLEAN Exclusive,
|
||
IN BOOLEAN AcquirePagingIo
|
||
)
|
||
|
||
/*++
|
||
|
||
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)
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PFCB Fcb;
|
||
PSCB *Scb;
|
||
PSCB NextScb;
|
||
PVOID RestartKey;
|
||
|
||
PAGED_CODE();
|
||
|
||
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_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 (NtfsSegmentNumber( &Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER) {
|
||
|
||
//
|
||
// If there is a paging Io resource then acquire this if required.
|
||
//
|
||
|
||
if (AcquirePagingIo && (Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExAcquireResourceExclusive( Fcb->PagingIoResource, TRUE );
|
||
}
|
||
|
||
//
|
||
// Acquire this Fcb whether or not the underlying file has been deleted.
|
||
//
|
||
|
||
if (Exclusive ||
|
||
IsDirectory( &Fcb->Info )) {
|
||
|
||
NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, TRUE, FALSE );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Assume that we only need this file shared. We will then
|
||
// look for Lsn related streams.
|
||
//
|
||
|
||
NtfsAcquireSharedFcb( IrpContext, Fcb, NULL, TRUE );
|
||
|
||
//
|
||
// 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 ||
|
||
NextScb->AttributeTypeCode == $EA)) {
|
||
|
||
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, TRUE, FALSE );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now acquire the Fcb's in the Vcb.
|
||
//
|
||
|
||
Scb = &Vcb->QuotaTableScb;
|
||
|
||
while (TRUE) {
|
||
|
||
if ((*Scb != NULL)
|
||
&& (*Scb != Vcb->BitmapScb)) {
|
||
|
||
if (AcquirePagingIo && ((*Scb)->Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExAcquireResourceExclusive( (*Scb)->Fcb->PagingIoResource, TRUE );
|
||
}
|
||
|
||
NtfsAcquireExclusiveFcb( IrpContext, (*Scb)->Fcb, NULL, TRUE, FALSE );
|
||
}
|
||
|
||
if (Scb == &Vcb->MftScb) {
|
||
|
||
break;
|
||
}
|
||
|
||
Scb -= 1;
|
||
}
|
||
|
||
//
|
||
// Treat the bitmap as an end resource and acquire it last.
|
||
//
|
||
|
||
if (Vcb->BitmapScb != NULL) {
|
||
|
||
if (AcquirePagingIo && (Vcb->BitmapScb->Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExAcquireResourceExclusive( Vcb->BitmapScb->Fcb->PagingIoResource, TRUE );
|
||
}
|
||
|
||
NtfsAcquireExclusiveFcb( IrpContext, Vcb->BitmapScb->Fcb, NULL, TRUE, FALSE );
|
||
}
|
||
|
||
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();
|
||
|
||
//
|
||
// 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 (NtfsSegmentNumber( &Fcb->FileReference ) >= FIRST_USER_FILE_NUMBER) {
|
||
|
||
//
|
||
// Release the file.
|
||
//
|
||
|
||
if (ReleasePagingIo && (Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExReleaseResource( Fcb->PagingIoResource );
|
||
}
|
||
|
||
NtfsReleaseFcb( IrpContext, Fcb );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now release the Fcb's in the Vcb.
|
||
//
|
||
|
||
Scb = &Vcb->QuotaTableScb;
|
||
|
||
while (TRUE) {
|
||
|
||
if (*Scb != NULL) {
|
||
|
||
if (ReleasePagingIo && ((*Scb)->Fcb->PagingIoResource != NULL)) {
|
||
|
||
ExReleaseResource( (*Scb)->Fcb->PagingIoResource );
|
||
}
|
||
|
||
NtfsReleaseFcb( IrpContext, (*Scb)->Fcb );
|
||
}
|
||
|
||
if (Scb == &Vcb->MftScb) {
|
||
|
||
break;
|
||
}
|
||
|
||
Scb -= 1;
|
||
}
|
||
|
||
NtfsReleaseVcb( IrpContext, Vcb );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
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 (ExAcquireResourceExclusive( &Vcb->Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) {
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_VCB(Vcb);
|
||
|
||
PAGED_CODE();
|
||
|
||
if (ExAcquireResourceShared( &Vcb->Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (RaiseOnCantWait) {
|
||
|
||
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
||
|
||
} else {
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
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 acquire
|
||
|
||
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.
|
||
|
||
--*/
|
||
|
||
{
|
||
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.
|
||
//
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
|
||
ReferenceCount = Vcb->Vpb->ReferenceCount;
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
|
||
ResidualCount = 0;
|
||
|
||
if (Vcb->LogFileObject != NULL) {
|
||
|
||
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) {
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_DELETE_UNDERWAY );
|
||
|
||
ExReleaseResource( &Vcb->Resource );
|
||
|
||
//
|
||
// Never delete the Vcb unless this is the last release of
|
||
// this Vcb.
|
||
//
|
||
|
||
if (!ExIsResourceAcquiredExclusive( &Vcb->Resource ) &&
|
||
(ExIsResourceAcquiredShared( &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.
|
||
//
|
||
|
||
(VOID) ExAcquireResourceExclusive( &NtfsData.Resource, TRUE );
|
||
|
||
RemoveEntryList( &Vcb->VcbLinks );
|
||
|
||
ExReleaseResource( &NtfsData.Resource );
|
||
|
||
NtfsDeleteVcb( IrpContext, &Vcb );
|
||
|
||
} else {
|
||
|
||
ClearFlag( Vcb->VcbState, VCB_STATE_DELETE_UNDERWAY );
|
||
}
|
||
|
||
} else {
|
||
|
||
ExReleaseResource( &Vcb->Resource );
|
||
}
|
||
|
||
} else {
|
||
|
||
ExReleaseResource( &Vcb->Resource );
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireFcbWithPaging (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN BOOLEAN DontWait
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used in the create path only. It acquires the Fcb
|
||
and also the paging IO resource if it exists but only if the create
|
||
operation was doing a 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
|
||
|
||
DontWait - If TRUE this 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 );
|
||
|
||
if (!DontWait && FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_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->FcbWithPagingExclusive != NULL) {
|
||
// ASSERT(IrpContext->TransactionId == 0);
|
||
NtfsReleasePagingIo( IrpContext, IrpContext->FcbWithPagingExclusive );
|
||
}
|
||
|
||
//
|
||
// 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 (!ExAcquireResourceExclusive( Fcb->PagingIoResource, Wait )) {
|
||
break;
|
||
}
|
||
IrpContext->FcbWithPagingExclusive = Fcb;
|
||
PagingIoAcquired = TRUE;
|
||
}
|
||
|
||
//
|
||
// Let's acquire this Fcb exclusively.
|
||
//
|
||
|
||
if (!NtfsAcquireExclusiveFcb( IrpContext, Fcb, NULL, TRUE, DontWait )) {
|
||
|
||
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->FcbWithPagingExclusive == 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->FcbWithPagingExclusive == Fcb)) {
|
||
NtfsReleasePagingIo( IrpContext, Fcb );
|
||
}
|
||
|
||
NtfsReleaseScb( IrpContext, Scb );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireExclusiveFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PSCB Scb OPTIONAL,
|
||
IN BOOLEAN NoDeleteCheck,
|
||
IN BOOLEAN DontWait
|
||
)
|
||
|
||
/*++
|
||
|
||
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
|
||
|
||
NoDeleteCheck - If TRUE, we don't do any check for deleted files but
|
||
always acquire the Fcb.
|
||
|
||
DontWait - If TRUE this 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.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
BOOLEAN Wait;
|
||
|
||
ASSERT_IRP_CONTEXT(IrpContext);
|
||
ASSERT_FCB(Fcb);
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = STATUS_CANT_WAIT;
|
||
|
||
if (DontWait ||
|
||
!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
|
||
|
||
Wait = FALSE;
|
||
|
||
} else {
|
||
|
||
Wait = TRUE;
|
||
}
|
||
|
||
if (ExAcquireResourceExclusive( Fcb->Resource, 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 (NoDeleteCheck
|
||
|
||
||
|
||
|
||
(IrpContext->MajorFunction == IRP_MJ_CLOSE)
|
||
|
||
||
|
||
|
||
(IrpContext->MajorFunction == IRP_MJ_CREATE)
|
||
|
||
||
|
||
|
||
(!FlagOn( Fcb->FcbState, FCB_STATE_FILE_DELETED )
|
||
&& (!ARGUMENT_PRESENT( Scb )
|
||
|| !FlagOn( Scb->ScbState, SCB_STATE_ATTRIBUTE_DELETED )))) {
|
||
|
||
//
|
||
// 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 ((Fcb->Vcb->BitmapScb == NULL) ||
|
||
(Fcb->Vcb->BitmapScb->Fcb != Fcb)) {
|
||
|
||
//
|
||
// We need to check if this Fcb is already in an
|
||
// exclusive list. If it is then we want to attach
|
||
// the current IrpContext to the IrpContext holding
|
||
// this Fcb.
|
||
//
|
||
|
||
if (Fcb->ExclusiveFcbLinks.Flink == NULL) {
|
||
|
||
ASSERT( Fcb->BaseExclusiveCount == 0 );
|
||
|
||
InsertTailList( &IrpContext->ExclusiveFcbList,
|
||
&Fcb->ExclusiveFcbLinks );
|
||
}
|
||
|
||
Fcb->BaseExclusiveCount += 1;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// We need to release the Fcb and remember the status code.
|
||
//
|
||
|
||
ExReleaseResource( Fcb->Resource );
|
||
Status = STATUS_FILE_DELETED;
|
||
|
||
} else if (DontWait) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsAcquireSharedFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PSCB Scb OPTIONAL,
|
||
IN BOOLEAN NoDeleteCheck
|
||
)
|
||
|
||
/*++
|
||
|
||
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
|
||
|
||
NoDeleteCheck - If TRUE then 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 (ExAcquireResourceShared( Fcb->Resource, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT))) {
|
||
|
||
//
|
||
// The link count should be non-zero or the file has been
|
||
// deleted.
|
||
//
|
||
|
||
if (NoDeleteCheck ||
|
||
(!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.
|
||
//
|
||
|
||
ExReleaseResource( Fcb->Resource );
|
||
Status = STATUS_FILE_DELETED;
|
||
}
|
||
|
||
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
||
}
|
||
|
||
|
||
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));
|
||
|
||
ExReleaseResource( Fcb->Resource );
|
||
}
|
||
|
||
|
||
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, FALSE, FALSE );
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
ExAcquireResourceShared( Scb->Header.Resource, TRUE );
|
||
|
||
if (Scb->NonpagedScb->OpenAttributeTableIndex == 0) {
|
||
|
||
ExReleaseResource( Scb->Header.Resource );
|
||
ExAcquireResourceExclusive( Scb->Header.Resource, 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) {
|
||
|
||
#ifdef _CAIRO_
|
||
if (SafeNodeType(IrpContext->SharedScb) == NTFS_NTC_QUOTA_CONTROL) {
|
||
NtfsReleaseQuotaControl( IrpContext,
|
||
(PQUOTA_CONTROL_BLOCK) IrpContext->SharedScb );
|
||
} else {
|
||
ExReleaseResource( ((PSCB) IrpContext->SharedScb)->Header.Resource );
|
||
}
|
||
|
||
#else
|
||
ExReleaseResource( ((PSCB) IrpContext->SharedScb)->Header.Resource );
|
||
#endif // _CAIRO_
|
||
|
||
//
|
||
// 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) {
|
||
|
||
#ifdef _CAIRO_
|
||
if (SafeNodeType(*NextScb) == NTFS_NTC_QUOTA_CONTROL) {
|
||
|
||
NtfsReleaseQuotaControl( IrpContext,
|
||
(PQUOTA_CONTROL_BLOCK) *NextScb );
|
||
} else {
|
||
|
||
ExReleaseResource( (*NextScb)->Header.Resource );
|
||
}
|
||
|
||
#else
|
||
ExReleaseResource( (*NextScb)->Header.Resource );
|
||
#endif // _CAIRO_
|
||
|
||
}
|
||
|
||
Count -= 1;
|
||
NextScb += 1;
|
||
|
||
} while (Count != 0);
|
||
|
||
NtfsFreePool( IrpContext->SharedScb );
|
||
}
|
||
|
||
IrpContext->SharedScb = NULL;
|
||
IrpContext->SharedScbSize = 0;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
NtfsAcquireVolumeForClose (
|
||
IN PVOID OpaqueVcb,
|
||
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 closes to the file. This callback is necessary to
|
||
avoid deadlocks with the Lazy Writer. (Note that normal closes
|
||
acquire the Vcb, 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:
|
||
|
||
Vcb - The Vcb which was specified as a close 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 Vcb has been acquired
|
||
|
||
--*/
|
||
|
||
{
|
||
PVCB Vcb = (PVCB)OpaqueVcb;
|
||
|
||
ASSERT_VCB(Vcb);
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Do the code of acquire exclusive Vcb but without the IrpContext
|
||
//
|
||
|
||
if (ExAcquireResourceExclusive( &Vcb->Resource, Wait )) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseVolumeFromClose (
|
||
IN PVOID OpaqueVcb
|
||
)
|
||
|
||
/*++
|
||
|
||
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 closes on the file.
|
||
|
||
Arguments:
|
||
|
||
Vcb - The Vcb which was specified as a close context parameter for this
|
||
routine.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PVCB Vcb = (PVCB)OpaqueVcb;
|
||
|
||
ASSERT_VCB(Vcb);
|
||
|
||
PAGED_CODE();
|
||
|
||
NtfsReleaseVcb( NULL, Vcb );
|
||
|
||
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 = TRUE;
|
||
|
||
ULONG CompressedStream = (ULONG)OpaqueScb & 1;
|
||
PSCB Scb = (PSCB)((ULONG)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 (ExAcquireResourceShared( Scb->Header.Resource, Wait )) {
|
||
|
||
ExReleaseResource( Scb->Header.Resource );
|
||
AcquiredFile = TRUE;
|
||
}
|
||
|
||
//
|
||
// Now acquire either the main or paging io resource depending on the
|
||
// state of the file.
|
||
//
|
||
|
||
} else if (Scb->Header.PagingIoResource != NULL) {
|
||
AcquiredFile = ExAcquireResourceShared( Scb->Header.PagingIoResource, Wait );
|
||
} else {
|
||
AcquiredFile = ExAcquireResourceShared( Scb->Header.Resource, 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)OpaqueScb & 1;
|
||
PSCB Scb = (PSCB)((ULONG)OpaqueScb & ~1);
|
||
PFCB Fcb = Scb->Fcb;
|
||
|
||
ASSERT_SCB(Scb);
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Clear the toplevel at this point, if we set it above.
|
||
//
|
||
|
||
if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP) {
|
||
IoSetTopLevelIrp( NULL );
|
||
}
|
||
|
||
Scb->LazyWriteThread[CompressedStream] = NULL;
|
||
|
||
if (NtfsSegmentNumber( &Fcb->FileReference ) <= MASTER_FILE_TABLE2_NUMBER) {
|
||
|
||
NOTHING;
|
||
|
||
} else if (Scb->Header.PagingIoResource != NULL) {
|
||
ExReleaseResource( Scb->Header.PagingIoResource );
|
||
} else {
|
||
ExReleaseResource( Scb->Header.Resource );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtfsAcquireFileForModWrite (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PLARGE_INTEGER EndingOffset,
|
||
OUT PERESOURCE *ResourceToRelease,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
|
||
{
|
||
BOOLEAN AcquiredFile;
|
||
|
||
PSCB Scb = (PSCB) (FileObject->FsContext);
|
||
PFCB Fcb = Scb->Fcb;
|
||
|
||
ASSERT_SCB(Scb);
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
|
||
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 (AcquiredFile = ExAcquireResourceShared( Scb->Header.Resource, FALSE )) {
|
||
ExReleaseResource( Scb->Header.Resource );
|
||
}
|
||
*ResourceToRelease = NULL;
|
||
|
||
//
|
||
// Now acquire either the main or paging io resource depending on the
|
||
// state of the file.
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// Figure out which resource to acquire.
|
||
//
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
*ResourceToRelease = Scb->Header.PagingIoResource;
|
||
} else {
|
||
*ResourceToRelease = Scb->Header.Resource;
|
||
}
|
||
|
||
//
|
||
// Try to acquire the resource with Wait FALSE
|
||
//
|
||
|
||
AcquiredFile = ExAcquireResourceShared( *ResourceToRelease, 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) {
|
||
if (Scb->CompressionUnit != 0) {
|
||
ExAcquireFastMutex( Scb->Header.FastMutex );
|
||
if ((EndingOffset->QuadPart > Scb->ValidDataToDisk) &&
|
||
(EndingOffset->QuadPart < (Scb->Header.FileSize.QuadPart + PAGE_SIZE - 1)) &&
|
||
!FlagOn(Scb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE)) {
|
||
|
||
ExReleaseResource(*ResourceToRelease);
|
||
AcquiredFile = FALSE;
|
||
*ResourceToRelease = NULL;
|
||
}
|
||
ExReleaseFastMutex( Scb->Header.FastMutex );
|
||
}
|
||
} 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) {
|
||
ExAcquireResourceShared( Header->PagingIoResource, TRUE );
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
}
|
||
|
||
NTSTATUS
|
||
NtfsReleaseFileForCcFlush (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
{
|
||
PFSRTL_COMMON_FCB_HEADER Header = FileObject->FsContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Header->PagingIoResource != NULL) {
|
||
ExReleaseResource( Header->PagingIoResource );
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
UNREFERENCED_PARAMETER( DeviceObject );
|
||
}
|
||
|
||
VOID
|
||
NtfsAcquireForCreateSection (
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
{
|
||
PSCB Scb = (PSCB)FileObject->FsContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
|
||
ExAcquireResourceExclusive( Scb->Header.PagingIoResource, TRUE );
|
||
}
|
||
}
|
||
|
||
VOID
|
||
NtfsReleaseForCreateSection (
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
|
||
{
|
||
PSCB Scb = (PSCB)FileObject->FsContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
|
||
ExReleaseResource( 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) ||
|
||
ExAcquireResourceShared( Scb->Header.PagingIoResource, Wait )) {
|
||
|
||
AcquiredFile = TRUE;
|
||
|
||
//
|
||
// Add our thread to the read ahead list.
|
||
//
|
||
|
||
KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &OldIrql );
|
||
|
||
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 = ExAllocatePoolWithTag( NonPagedPool, sizeof(READ_AHEAD_THREAD), 'RftN' );
|
||
|
||
//
|
||
// If we failed to allocate an entry, clean up and raise.
|
||
//
|
||
|
||
if (ReadAheadThread == NULL) {
|
||
|
||
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, OldIrql );
|
||
|
||
if (NtfsSegmentNumber( &Fcb->FileReference ) > VOLUME_DASD_NUMBER) {
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
ExReleaseResource( Scb->Header.PagingIoResource );
|
||
}
|
||
}
|
||
|
||
ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
InsertTailList( &NtfsData.ReadAheadThreads, &ReadAheadThread->Links );
|
||
}
|
||
|
||
ReadAheadThread->Thread = CurrentThread;
|
||
|
||
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, 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.
|
||
//
|
||
|
||
KeAcquireSpinLock( &NtfsData.StrucSupSpinLock, &OldIrql );
|
||
|
||
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 );
|
||
|
||
KeReleaseSpinLock( &NtfsData.StrucSupSpinLock, OldIrql );
|
||
|
||
if (Scb->Header.PagingIoResource != NULL) {
|
||
ExReleaseResource( Scb->Header.PagingIoResource );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NtfsAcquireVolumeFileForClose (
|
||
IN PVOID Null,
|
||
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
|
||
we do not need to acquire any resource for the volume file,
|
||
so this routine is simply a noop.
|
||
|
||
Arguments:
|
||
|
||
Null - Not required.
|
||
|
||
Wait - TRUE if the caller is willing to block.
|
||
|
||
Return Value:
|
||
|
||
TRUE
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER( Null );
|
||
UNREFERENCED_PARAMETER( Wait );
|
||
|
||
PAGED_CODE();
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
NtfsReleaseVolumeFileFromClose (
|
||
IN PVOID Null
|
||
)
|
||
|
||
/*++
|
||
|
||
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:
|
||
|
||
Null - Not required.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER( Null );
|
||
|
||
PAGED_CODE();
|
||
|
||
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;
|
||
}
|