361 lines
10 KiB
C
361 lines
10 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Shutdown.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the file system shutdown routine for Ntfs
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Gary Kimura [GaryKi] 19-Aug-1991
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "NtfsProc.h"
|
|||
|
|
|||
|
//
|
|||
|
// Interal support routine
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
NtfsCheckpointVolumeUntilDone (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PVCB Vcb
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Local debug trace level
|
|||
|
//
|
|||
|
|
|||
|
#define Dbg (DEBUG_TRACE_SHUTDOWN)
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtfsFsdShutdown (
|
|||
|
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine implements the FSD part of shutdown. Note that Shutdown will
|
|||
|
never be done asynchronously so we will never need the Fsp counterpart
|
|||
|
to shutdown.
|
|||
|
|
|||
|
This is the shutdown routine for the Ntfs file system device driver.
|
|||
|
This routine locks the global file system lock and then syncs all the
|
|||
|
mounted volumes.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
VolumeDeviceObject - Supplies the volume device object where the
|
|||
|
file exists
|
|||
|
|
|||
|
Irp - Supplies the Irp being processed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - Always STATUS_SUCCESS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
TOP_LEVEL_CONTEXT TopLevelContext;
|
|||
|
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
|||
|
|
|||
|
IRP_CONTEXT LocalIrpContext;
|
|||
|
PIRP_CONTEXT IrpContext = &LocalIrpContext;
|
|||
|
|
|||
|
PLIST_ENTRY Links;
|
|||
|
PVCB Vcb;
|
|||
|
PIRP NewIrp;
|
|||
|
KEVENT Event;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
|||
|
|
|||
|
DebugTrace( +1, Dbg, ("NtfsFsdShutdown\n") );
|
|||
|
|
|||
|
FsRtlEnterFileSystem();
|
|||
|
|
|||
|
//
|
|||
|
// Allocate an Irp Context that we can use in our procedure calls
|
|||
|
// and we know that shutdown will always be synchronous
|
|||
|
//
|
|||
|
|
|||
|
ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE );
|
|||
|
|
|||
|
NtfsInitializeIrpContext( Irp, TRUE, &IrpContext );
|
|||
|
|
|||
|
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
|||
|
|
|||
|
//
|
|||
|
// Get everyone else out of the way
|
|||
|
//
|
|||
|
|
|||
|
if (!NtfsAcquireExclusiveGlobal( IrpContext, BooleanFlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ))) {
|
|||
|
NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL );
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
BOOLEAN AcquiredFiles;
|
|||
|
BOOLEAN AcquiredCheckpoint;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize an event for doing calls down to
|
|||
|
// our target device objects
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|||
|
|
|||
|
//
|
|||
|
// For every volume that is mounted we will flush the
|
|||
|
// volume and then shutdown the target device objects.
|
|||
|
//
|
|||
|
|
|||
|
for (Links = NtfsData.VcbQueue.Flink;
|
|||
|
Links != &NtfsData.VcbQueue;
|
|||
|
Links = Links->Flink) {
|
|||
|
|
|||
|
ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
|
|||
|
|
|||
|
//
|
|||
|
// Get the Vcb and put it in the IrpContext.
|
|||
|
//
|
|||
|
|
|||
|
Vcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
|
|||
|
IrpContext->Vcb = Vcb;
|
|||
|
|
|||
|
//
|
|||
|
// If we have already been called before for this volume
|
|||
|
// (and yes this does happen), skip this volume as no writes
|
|||
|
// have been allowed since the first shutdown.
|
|||
|
//
|
|||
|
|
|||
|
if ( FlagOn( Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN ) ) {
|
|||
|
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Clear the Mft defrag flag to stop any actions behind our backs.
|
|||
|
//
|
|||
|
|
|||
|
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
|||
|
ClearFlag( Vcb->MftDefragState, VCB_MFT_DEFRAG_PERMITTED );
|
|||
|
NtfsReleaseCheckpoint( IrpContext, Vcb );
|
|||
|
|
|||
|
AcquiredFiles = FALSE;
|
|||
|
AcquiredCheckpoint = FALSE;
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
|||
|
|
|||
|
//
|
|||
|
// Start by locking out all other checkpoint
|
|||
|
// operations.
|
|||
|
//
|
|||
|
|
|||
|
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
|||
|
|
|||
|
while (FlagOn( Vcb->CheckpointFlags, VCB_CHECKPOINT_SYNC_FLAGS )) {
|
|||
|
|
|||
|
//
|
|||
|
// Release the checkpoint event because we cannot checkpoint now.
|
|||
|
//
|
|||
|
|
|||
|
NtfsReleaseCheckpoint( IrpContext, Vcb );
|
|||
|
|
|||
|
NtfsWaitOnCheckpointNotify( IrpContext, Vcb );
|
|||
|
|
|||
|
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
|||
|
}
|
|||
|
|
|||
|
SetFlag( Vcb->CheckpointFlags, VCB_CHECKPOINT_SYNC_FLAGS );
|
|||
|
NtfsResetCheckpointNotify( IrpContext, Vcb );
|
|||
|
NtfsReleaseCheckpoint( IrpContext, Vcb );
|
|||
|
AcquiredCheckpoint = TRUE;
|
|||
|
|
|||
|
NtfsAcquireAllFiles( IrpContext, Vcb, TRUE, TRUE, FALSE );
|
|||
|
AcquiredFiles = TRUE;
|
|||
|
|
|||
|
SetFlag( Vcb->VcbState, VCB_STATE_VOL_PURGE_IN_PROGRESS );
|
|||
|
|
|||
|
if (!FlagOn( Vcb->VcbState, VCB_STATE_LOCKED)) {
|
|||
|
NtfsCheckpointVolumeUntilDone( IrpContext, Vcb );
|
|||
|
}
|
|||
|
NtfsCommitCurrentTransaction( IrpContext );
|
|||
|
|
|||
|
//
|
|||
|
// Bug 308819. We find that transactions continue to happen at times even after shutdown
|
|||
|
// has been flagged. If we stop the log file, then currently we don't check for
|
|||
|
// NULL LSNs getting returned by NtfsWriteLog. As a result our metadata can get
|
|||
|
// corrupted. Until we rectify this, let's just not stop the log file in shutdown.
|
|||
|
//
|
|||
|
// NtfsStopLogFile( Vcb );
|
|||
|
//
|
|||
|
|
|||
|
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
|||
|
ClearFlag( Vcb->CheckpointFlags,
|
|||
|
VCB_CHECKPOINT_SYNC_FLAGS | VCB_DUMMY_CHECKPOINT_POSTED);
|
|||
|
NtfsSetCheckpointNotify( IrpContext, Vcb );
|
|||
|
NtfsReleaseCheckpoint( IrpContext, Vcb );
|
|||
|
AcquiredCheckpoint = FALSE;
|
|||
|
|
|||
|
NewIrp = IoBuildSynchronousFsdRequest( IRP_MJ_SHUTDOWN,
|
|||
|
Vcb->TargetDeviceObject,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
&Event,
|
|||
|
NULL );
|
|||
|
|
|||
|
if (NewIrp == NULL) {
|
|||
|
|
|||
|
NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(IoCallDriver( Vcb->TargetDeviceObject, NewIrp ))) {
|
|||
|
|
|||
|
(VOID) KeWaitForSingleObject( &Event,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL );
|
|||
|
|
|||
|
KeClearEvent( &Event );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtfsMinimumExceptionProcessing( IrpContext );
|
|||
|
}
|
|||
|
|
|||
|
if (AcquiredCheckpoint) {
|
|||
|
|
|||
|
NtfsAcquireCheckpoint( IrpContext, Vcb );
|
|||
|
ClearFlag( Vcb->CheckpointFlags,
|
|||
|
VCB_CHECKPOINT_SYNC_FLAGS | VCB_DUMMY_CHECKPOINT_POSTED);
|
|||
|
NtfsSetCheckpointNotify( IrpContext, Vcb );
|
|||
|
NtfsReleaseCheckpoint( IrpContext, Vcb );
|
|||
|
}
|
|||
|
|
|||
|
SetFlag( Vcb->VcbState, VCB_STATE_FLAG_SHUTDOWN );
|
|||
|
ClearFlag( Vcb->VcbState, VCB_STATE_VOL_PURGE_IN_PROGRESS );
|
|||
|
|
|||
|
if (AcquiredFiles) {
|
|||
|
|
|||
|
NtfsReleaseAllFiles( IrpContext, Vcb, TRUE );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
NtfsReleaseGlobal( IrpContext );
|
|||
|
|
|||
|
NtfsCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
|
|||
|
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
|||
|
}
|
|||
|
|
|||
|
DebugTrace( -1, Dbg, ("NtfsFsdShutdown -> STATUS_SUCCESS\n") );
|
|||
|
|
|||
|
FsRtlExitFileSystem();
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NtfsCheckpointVolumeUntilDone (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PVCB Vcb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine keeps trying to checkpoint/flush a volume until it
|
|||
|
works. Doing clean checkpoints and looping back to retry on log file full.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vcb - Vcb to checkpoint til done
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
try {
|
|||
|
NtfsCheckpointVolume( IrpContext,
|
|||
|
Vcb,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
0,
|
|||
|
Vcb->LastRestartArea );
|
|||
|
} except( (Status = GetExceptionCode()), EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
NtfsMinimumExceptionProcessing( IrpContext );
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
//
|
|||
|
// To make sure that we can access all of our streams correctly,
|
|||
|
// we first restore all of the higher sizes before aborting the
|
|||
|
// transaction. Then we restore all of the lower sizes after
|
|||
|
// the abort, so that all Scbs are finally restored.
|
|||
|
//
|
|||
|
|
|||
|
NtfsRestoreScbSnapshots( IrpContext, TRUE );
|
|||
|
NtfsAbortTransaction( IrpContext, IrpContext->Vcb, NULL );
|
|||
|
NtfsRestoreScbSnapshots( IrpContext, FALSE );
|
|||
|
|
|||
|
//
|
|||
|
// A clean volume checkpoint should never get log file full
|
|||
|
//
|
|||
|
|
|||
|
if (Status == STATUS_LOG_FILE_FULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure we don't leave the error code in the top-level
|
|||
|
// IrpContext field.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( IrpContext->TransactionId == 0 );
|
|||
|
IrpContext->ExceptionStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
NtfsCheckpointVolume( IrpContext,
|
|||
|
Vcb,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
FALSE,
|
|||
|
0,
|
|||
|
Vcb->LastRestartArea );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} while (Status == STATUS_LOG_FILE_FULL);
|
|||
|
}
|