summaryrefslogtreecommitdiffstats
path: root/private/ntos/cntfs/resrcsup.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/cntfs/resrcsup.c')
-rw-r--r--private/ntos/cntfs/resrcsup.c2114
1 files changed, 2114 insertions, 0 deletions
diff --git a/private/ntos/cntfs/resrcsup.c b/private/ntos/cntfs/resrcsup.c
new file mode 100644
index 000000000..e6d186118
--- /dev/null
+++ b/private/ntos/cntfs/resrcsup.c
@@ -0,0 +1,2114 @@
+/*++
+
+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;
+}