mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1779 lines
46 KiB
1779 lines
46 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
VerfySup.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the Ntfs Verify volume and fcb support
|
|
routines
|
|
|
|
Author:
|
|
|
|
Gary Kimura [GaryKi] 30-Jan-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "NtfsProc.h"
|
|
|
|
//
|
|
// The Debug trace level for this module
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_VERFYSUP)
|
|
|
|
//
|
|
// Define a tag for general pool allocations from this module
|
|
//
|
|
|
|
#undef MODULE_POOL_TAG
|
|
#define MODULE_POOL_TAG ('VFtN')
|
|
|
|
extern BOOLEAN NtfsCheckQuota;
|
|
|
|
//
|
|
// Local procedure prototypes
|
|
//
|
|
|
|
VOID
|
|
NtfsPerformVerifyDiskRead (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN PVOID Buffer,
|
|
IN LONGLONG Offset,
|
|
IN ULONG NumberOfBytesToRead
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsVerifyReadCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Contxt
|
|
);
|
|
|
|
NTSTATUS
|
|
NtfsDeviceIoControlAsync (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG IoCtl
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, NtfsCheckpointAllVolumes)
|
|
#pragma alloc_text(PAGE, NtfsMarkVolumeDirty)
|
|
#pragma alloc_text(PAGE, NtfsPerformVerifyOperation)
|
|
#pragma alloc_text(PAGE, NtfsPingVolume)
|
|
#pragma alloc_text(PAGE, NtfsUpdateVersionNumber)
|
|
#pragma alloc_text(PAGE, NtfsVerifyOperationIsLegal)
|
|
#endif
|
|
|
|
|
|
BOOLEAN
|
|
NtfsPerformVerifyOperation (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to force a verification of the volume. It assumes
|
|
that everything might be resource/mutex locked so it cannot take out
|
|
any resources. It will read in the boot sector and the dasd file record
|
|
and from those determine if the volume is okay. This routine is called
|
|
whenever the real device has started rejecting I/O requests with
|
|
VERIFY_REQUIRED.
|
|
|
|
If the volume verifies okay then we will return TRUE otherwise we will
|
|
return FALSE.
|
|
|
|
It does not alter the Vcb state.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb being queried.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the volume verified okay, and FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN Results;
|
|
|
|
PPACKED_BOOT_SECTOR BootSector;
|
|
PFILE_RECORD_SEGMENT_HEADER FileRecord;
|
|
|
|
LONGLONG Offset;
|
|
|
|
PSTANDARD_INFORMATION StandardInformation;
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
ASSERT_VCB( Vcb );
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsPerformVerifyOperation, Vcb = %08lx\n", Vcb) );
|
|
|
|
BootSector = NULL;
|
|
FileRecord = NULL;
|
|
|
|
try {
|
|
|
|
//
|
|
// Allocate a buffer for the boot sector, read it in, and then check if
|
|
// it some of the fields still match. The starting lcn is zero and the
|
|
// size is the size of a disk sector.
|
|
//
|
|
|
|
BootSector = NtfsAllocatePool( NonPagedPool,
|
|
ROUND_TO_PAGES( Vcb->BytesPerSector ));
|
|
|
|
NtfsPerformVerifyDiskRead( IrpContext, Vcb, BootSector, (LONGLONG)0, Vcb->BytesPerSector );
|
|
|
|
//
|
|
// For now we will only check that the serial numbers, mft lcn's and
|
|
// number of sectors match up with what they use to be.
|
|
//
|
|
|
|
if ((BootSector->SerialNumber != Vcb->VolumeSerialNumber) ||
|
|
(BootSector->MftStartLcn != Vcb->MftStartLcn) ||
|
|
(BootSector->Mft2StartLcn != Vcb->Mft2StartLcn) ||
|
|
(BootSector->NumberSectors != Vcb->NumberSectors)) {
|
|
|
|
try_return( Results = FALSE );
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer for the dasd file record, read it in, and then check
|
|
// if some of the fields still match. The size of the record is the number
|
|
// of bytes in a file record segment, and because the dasd file record is
|
|
// known to be contiguous with the start of the mft we can compute the starting
|
|
// lcn as the base of the mft plus the dasd number mulitplied by the clusters
|
|
// per file record segment.
|
|
//
|
|
|
|
FileRecord = NtfsAllocatePool( NonPagedPoolCacheAligned,
|
|
ROUND_TO_PAGES( Vcb->BytesPerFileRecordSegment ));
|
|
|
|
Offset = LlBytesFromClusters(Vcb, Vcb->MftStartLcn) +
|
|
(VOLUME_DASD_NUMBER * Vcb->BytesPerFileRecordSegment);
|
|
|
|
NtfsPerformVerifyDiskRead( IrpContext, Vcb, FileRecord, Offset, Vcb->BytesPerFileRecordSegment );
|
|
|
|
//
|
|
// Given a pointer to a file record we want the value of the first attribute which
|
|
// will be the standard information attribute. Then we will check the
|
|
// times stored in the standard information attribute against the times we
|
|
// have saved in the vcb. Note that last access time will be modified if
|
|
// the disk was moved and mounted on a different system without doing a dismount
|
|
// on this system.
|
|
//
|
|
|
|
StandardInformation = NtfsGetValue(((PATTRIBUTE_RECORD_HEADER)Add2Ptr( FileRecord,
|
|
FileRecord->FirstAttributeOffset )));
|
|
|
|
if ((StandardInformation->CreationTime != Vcb->VolumeCreationTime) ||
|
|
(StandardInformation->LastModificationTime != Vcb->VolumeLastModificationTime) ||
|
|
(StandardInformation->LastChangeTime != Vcb->VolumeLastChangeTime) ||
|
|
(StandardInformation->LastAccessTime != Vcb->VolumeLastAccessTime)) {
|
|
|
|
try_return( Results = FALSE );
|
|
}
|
|
|
|
//
|
|
// If the device is not writable we won't remount it.
|
|
//
|
|
|
|
if (NtfsDeviceIoControlAsync( IrpContext,
|
|
Vcb->TargetDeviceObject,
|
|
IOCTL_DISK_IS_WRITABLE ) == STATUS_MEDIA_WRITE_PROTECTED) {
|
|
|
|
try_return( Results = FALSE );
|
|
}
|
|
|
|
//
|
|
// At this point we believe that the disk has not changed so can return true and
|
|
// let our caller reenable the device
|
|
//
|
|
|
|
Results = TRUE;
|
|
|
|
try_exit: NOTHING;
|
|
} finally {
|
|
|
|
if (BootSector != NULL) { NtfsFreePool( BootSector ); }
|
|
if (FileRecord != NULL) { NtfsFreePool( FileRecord ); }
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsPerformVerifyOperation -> %08lx\n", Results) );
|
|
|
|
return Results;
|
|
}
|
|
|
|
|
|
VOID
|
|
NtfsPerformDismountOnVcb (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN BOOLEAN DoCompleteDismount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to start the dismount process on a vcb.
|
|
It marks the Vcb as not mounted and dereferences all opened stream
|
|
file objects, and gets the Vcb out of the Vpb's mounted volume
|
|
structures.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb being dismounted
|
|
|
|
DoCompleteDismount - Indicates if we are to actually mark the volume
|
|
as dismounted or if we are simply to stop the logfile and close
|
|
the internal attribute streams.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFCB Fcb;
|
|
PSCB Scb;
|
|
PVOID RestartKey;
|
|
|
|
BOOLEAN Restart;
|
|
|
|
PVPB NewVpb;
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsPerformDismountOnVcb, Vcb = %08lx\n", Vcb) );
|
|
|
|
//
|
|
// Blow away our delayed close file object.
|
|
//
|
|
|
|
if (!IsListEmpty( &NtfsData.AsyncCloseList ) ||
|
|
!IsListEmpty( &NtfsData.DelayedCloseList )) {
|
|
|
|
NtfsFspClose( Vcb );
|
|
}
|
|
|
|
//
|
|
// Commit any current transaction before we start tearing down the volume.
|
|
//
|
|
|
|
NtfsCommitCurrentTransaction( IrpContext );
|
|
|
|
//
|
|
// Acquire all files exclusively to make dismount atomic.
|
|
//
|
|
|
|
NtfsAcquireAllFiles( IrpContext, Vcb, TRUE, FALSE );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
try {
|
|
|
|
//
|
|
// Get rid of Cairo Files
|
|
//
|
|
|
|
#ifdef _CAIRO_
|
|
if (Vcb->QuotaTableScb != NULL) {
|
|
NtOfsCloseIndex( IrpContext, Vcb->QuotaTableScb );
|
|
Vcb->QuotaTableScb = NULL;
|
|
}
|
|
#endif _CAIRO_
|
|
|
|
//
|
|
// Stop the log file.
|
|
//
|
|
|
|
NtfsStopLogFile( Vcb );
|
|
|
|
//
|
|
// Now for every file Scb with an opened stream file we will delete
|
|
// the internal attribute stream
|
|
//
|
|
|
|
RestartKey = NULL;
|
|
while (TRUE) {
|
|
|
|
NtfsAcquireFcbTable( IrpContext, Vcb );
|
|
Fcb = NtfsGetNextFcbTableEntry(Vcb, &RestartKey);
|
|
NtfsReleaseFcbTable( IrpContext, Vcb );
|
|
|
|
if (Fcb == NULL) {
|
|
|
|
break;
|
|
}
|
|
|
|
ASSERT_FCB( Fcb );
|
|
|
|
Scb = NULL;
|
|
while ((Fcb != NULL) && ((Scb = NtfsGetNextChildScb(Fcb, Scb)) != NULL)) {
|
|
|
|
ASSERT_SCB( Scb );
|
|
|
|
if (Scb->FileObject != NULL) {
|
|
|
|
//
|
|
// Assume we want to start from the beginning of the Fcb table.
|
|
//
|
|
|
|
Restart = TRUE;
|
|
|
|
//
|
|
// For the VolumeDasdScb and bad cluster file, we simply decrement
|
|
// the counts that we incremented.
|
|
//
|
|
|
|
if ((Scb == Vcb->VolumeDasdScb) ||
|
|
(Scb == Vcb->BadClusterFileScb)) {
|
|
|
|
Scb->FileObject = NULL;
|
|
|
|
NtfsDecrementCloseCounts( IrpContext,
|
|
Scb,
|
|
NULL,
|
|
TRUE,
|
|
FALSE,
|
|
FALSE );
|
|
|
|
//
|
|
// Dereference the file object in the Scb unless it is the one in
|
|
// the Vcb for the Log File. This routine may not be able to
|
|
// dereference file object because of synchronization problems (there
|
|
// can be a lazy writer callback in process which owns the paging
|
|
// io resource). In that case we don't want to go back to the beginning
|
|
// of Fcb table or we will loop indefinitely.
|
|
//
|
|
|
|
} else if (Scb->FileObject != Vcb->LogFileObject) {
|
|
|
|
Restart = NtfsDeleteInternalAttributeStream( Scb, TRUE );
|
|
|
|
//
|
|
// This is the file object for the Log file. Remove our
|
|
// extra reference on the logfile Scb.
|
|
//
|
|
|
|
} else if (Scb->FileObject != NULL) {
|
|
|
|
//
|
|
// Remember the log file object so we can defer the dereference.
|
|
//
|
|
|
|
NtfsDecrementCloseCounts( IrpContext,
|
|
Vcb->LogFileScb,
|
|
NULL,
|
|
TRUE,
|
|
FALSE,
|
|
TRUE );
|
|
|
|
Scb->FileObject = NULL;
|
|
}
|
|
|
|
if (Restart) {
|
|
|
|
if (Scb == Vcb->MftScb) { Vcb->MftScb = NULL; }
|
|
if (Scb == Vcb->Mft2Scb) { Vcb->Mft2Scb = NULL; }
|
|
if (Scb == Vcb->LogFileScb) { Vcb->LogFileScb = NULL; }
|
|
if (Scb == Vcb->VolumeDasdScb) { Vcb->VolumeDasdScb = NULL; }
|
|
if (Scb == Vcb->AttributeDefTableScb) { Vcb->AttributeDefTableScb = NULL; }
|
|
if (Scb == Vcb->UpcaseTableScb) { Vcb->UpcaseTableScb = NULL; }
|
|
if (Scb == Vcb->RootIndexScb) { Vcb->RootIndexScb = NULL; }
|
|
if (Scb == Vcb->BitmapScb) { Vcb->BitmapScb = NULL; }
|
|
if (Scb == Vcb->BadClusterFileScb) { Vcb->BadClusterFileScb = NULL; }
|
|
if (Scb == Vcb->QuotaTableScb) { Vcb->QuotaTableScb = NULL; }
|
|
if (Scb == Vcb->MftBitmapScb) { Vcb->MftBitmapScb = NULL; }
|
|
|
|
#if defined (_CAIRO_)
|
|
if (Scb == Vcb->SecurityIdIndex) { Vcb->SecurityIdIndex = NULL; }
|
|
if (Scb == Vcb->SecurityDescriptorHashIndex)
|
|
{ Vcb->SecurityDescriptorHashIndex = NULL; }
|
|
if (Scb == Vcb->SecurityDescriptorStream)
|
|
{ Vcb->SecurityDescriptorStream = NULL; }
|
|
#endif // defined (_CAIRO_)
|
|
|
|
//
|
|
// Now zero out the enumerations so we will start all over again because
|
|
// our call to Delete Internal Attribute Stream just messed up our
|
|
// enumeration.
|
|
//
|
|
|
|
Scb = NULL;
|
|
Fcb = NULL;
|
|
RestartKey = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mark the volume as not mounted.
|
|
//
|
|
|
|
ClearFlag( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED );
|
|
|
|
//
|
|
// Now only really dismount the volume if that's what our caller wants
|
|
//
|
|
|
|
if (DoCompleteDismount) {
|
|
|
|
PREVENT_MEDIA_REMOVAL Prevent;
|
|
KIRQL SavedIrql;
|
|
|
|
//
|
|
// Attempt to unlock any removable media, ignoring status.
|
|
//
|
|
|
|
Prevent.PreventMediaRemoval = FALSE;
|
|
(PVOID)NtfsDeviceIoControl( IrpContext,
|
|
Vcb->TargetDeviceObject,
|
|
IOCTL_DISK_MEDIA_REMOVAL,
|
|
&Prevent,
|
|
sizeof(PREVENT_MEDIA_REMOVAL),
|
|
NULL,
|
|
0 );
|
|
|
|
//
|
|
// Remove this voldo from the mounted disk structures
|
|
//
|
|
// Assume we will need a new Vpb. Deallocate it later if not needed.
|
|
//
|
|
|
|
NewVpb = NtfsAllocatePoolWithTag( NonPagedPoolMustSucceed, sizeof( VPB ), 'VftN' );
|
|
RtlZeroMemory( NewVpb, sizeof( VPB ) );
|
|
|
|
IoAcquireVpbSpinLock( &SavedIrql );
|
|
|
|
//
|
|
// If there are no file objects and no reference counts in the
|
|
// Vpb then we can use the existing Vpb.
|
|
//
|
|
|
|
if ((Vcb->CloseCount == 0) &&
|
|
(Vcb->Vpb->ReferenceCount == 0)) {
|
|
|
|
Vcb->Vpb->DeviceObject = NULL;
|
|
ClearFlag( Vcb->Vpb->Flags, VPB_MOUNTED );
|
|
|
|
//
|
|
// Otherwise we will swap out the Vpb.
|
|
//
|
|
|
|
} else {
|
|
|
|
NewVpb->Type = IO_TYPE_VPB;
|
|
NewVpb->Size = sizeof( VPB );
|
|
NewVpb->RealDevice = Vcb->Vpb->RealDevice;
|
|
Vcb->Vpb->RealDevice->Vpb = NewVpb;
|
|
|
|
SetFlag( Vcb->VcbState, VCB_STATE_TEMP_VPB );
|
|
|
|
NewVpb = NULL;
|
|
}
|
|
|
|
IoReleaseVpbSpinLock( SavedIrql );
|
|
|
|
SetFlag( Vcb->VcbState, VCB_STATE_PERFORMED_DISMOUNT );
|
|
|
|
//
|
|
// Free the temp Vpb if not used.
|
|
//
|
|
|
|
if (NewVpb) {
|
|
|
|
NtfsFreePool( NewVpb );
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
|
|
NtfsReleaseAllFiles( IrpContext, Vcb, FALSE );
|
|
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsPerformDismountOnVcb -> VOID\n") );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NtfsPingVolume (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will ping the volume to see if the device needs to
|
|
be verified. It is used for create operations to see if the
|
|
create should proceed or if we should complete the create Irp
|
|
with a remount status.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb being pinged
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the volume is fine and the operation should
|
|
proceed and FALSE if the volume needs to be verified
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN Results;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugTrace( +1, Dbg, ("NtfsPingVolume, Vcb = %08lx\n", Vcb) );
|
|
|
|
//
|
|
// If the media is removable and the verify volume flag in the
|
|
// device object is not set then we want to ping the device
|
|
// to see if it needs to be verified.
|
|
//
|
|
// Note that we only force this ping for create operations.
|
|
// For others we take a sporting chance. If in the end we
|
|
// have to physically access the disk, the right thing will happen.
|
|
//
|
|
|
|
if ( FlagOn(Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA) &&
|
|
!FlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME) ) {
|
|
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
PDEVICE_OBJECT TargetDevice;
|
|
IO_STATUS_BLOCK Iosb;
|
|
NTSTATUS Status;
|
|
LONGLONG ThirtySeconds = (-10 * 1000 * 1000) * 30;
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
TargetDevice = Vcb->TargetDeviceObject;
|
|
|
|
Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_CHECK_VERIFY,
|
|
TargetDevice,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
&Event,
|
|
&Iosb );
|
|
|
|
if (Irp == NULL) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
|
|
}
|
|
|
|
Status = IoCallDriver( TargetDevice, Irp );
|
|
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
Status = KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)&ThirtySeconds );
|
|
|
|
ASSERT( Status == STATUS_SUCCESS );
|
|
|
|
if ( !NT_SUCCESS(Iosb.Status) ) {
|
|
|
|
NtfsRaiseStatus( IrpContext, Iosb.Status, NULL, NULL );
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NtfsRaiseStatus( IrpContext, Status, NULL, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FlagOn( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) {
|
|
|
|
Results = FALSE;
|
|
|
|
} else {
|
|
|
|
Results = TRUE;
|
|
}
|
|
|
|
DebugTrace( -1, Dbg, ("NtfsPingVolume -> %08lx\n", Results) );
|
|
|
|
return Results;
|
|
}
|
|
|
|
|
|
VOID
|
|
NtfsVolumeCheckpointDpc (
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is dispatched every 5 seconds when disk structure is being
|
|
modified. It had the ExWorker thread to volume checkpoints.
|
|
|
|
Arguments:
|
|
|
|
DeferredContext - Not Used
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
TIMER_STATUS TimerStatus;
|
|
|
|
UNREFERENCED_PARAMETER( SystemArgument1 );
|
|
UNREFERENCED_PARAMETER( SystemArgument2 );
|
|
UNREFERENCED_PARAMETER( DeferredContext );
|
|
UNREFERENCED_PARAMETER( Dpc );
|
|
|
|
//
|
|
// Atomic reset of status indicating the timer is currently fired. This
|
|
// synchronizes with NtfsSetDirtyBcb. After NtfsSetDirtyBcb dirties
|
|
// a Bcb, it sees if it should enable this timer routine.
|
|
//
|
|
// If the status indicates that a timer is active, it does nothing. In this
|
|
// case it is guaranteed that when the timer fires, it causes a checkpoint (to
|
|
// force out the dirty Bcb data).
|
|
//
|
|
// If there is no timer active, it enables it, thus queueing a checkpoint later.
|
|
//
|
|
// If the timer routine actually fires between the dirtying of the Bcb and the
|
|
// testing of the status then a single extra checkpoint is generated. This
|
|
// extra checkpoint is not considered harmful.
|
|
//
|
|
|
|
//
|
|
// Atomically reset status and get previous value
|
|
//
|
|
|
|
TimerStatus = InterlockedExchange( &NtfsData.TimerStatus, TIMER_NOT_SET );
|
|
|
|
//
|
|
// We have only one instance of the work queue item. It can only be
|
|
// queued once. In a slow system, this checkpoint item may not be processed
|
|
// by the time this timer routine fires again.
|
|
//
|
|
|
|
if (NtfsData.VolumeCheckpointItem.List.Flink == NULL) {
|
|
ExQueueWorkItem( &NtfsData.VolumeCheckpointItem, CriticalWorkQueue );
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
NtfsCheckpointAllVolumes (
|
|
PVOID Parameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine searches all of the vcbs for Ntfs and tries to clean
|
|
them. If the vcb is good and dirty but not almost clean then
|
|
we set it almost clean. If the Vcb is good and dirty and almost clean
|
|
then we clean it.
|
|
|
|
Arguments:
|
|
|
|
Parameter - Not Used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
TOP_LEVEL_CONTEXT TopLevelContext;
|
|
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
|
|
|
IRP_CONTEXT LocalIrpContext;
|
|
IRP LocalIrp;
|
|
|
|
PIRP_CONTEXT IrpContext;
|
|
|
|
PLIST_ENTRY Links;
|
|
PVCB Vcb;
|
|
|
|
BOOLEAN AcquiredGlobal = FALSE;
|
|
BOOLEAN StartTimer = FALSE;
|
|
|
|
TIMER_STATUS TimerStatus;
|
|
|
|
UNREFERENCED_PARAMETER( Parameter );
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlZeroMemory( &LocalIrpContext, sizeof(LocalIrpContext) );
|
|
RtlZeroMemory( &LocalIrp, sizeof(LocalIrp) );
|
|
|
|
IrpContext = &LocalIrpContext;
|
|
IrpContext->NodeTypeCode = NTFS_NTC_IRP_CONTEXT;
|
|
IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
|
|
IrpContext->OriginatingIrp = &LocalIrp;
|
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
|
InitializeListHead( &IrpContext->ExclusiveFcbList );
|
|
|
|
//
|
|
// Make sure we don't get any pop-ups
|
|
//
|
|
|
|
ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, TRUE, FALSE );
|
|
ASSERT( ThreadTopLevelContext == &TopLevelContext );
|
|
|
|
(VOID) ExAcquireResourceShared( &NtfsData.Resource, TRUE );
|
|
AcquiredGlobal = TRUE;
|
|
|
|
try {
|
|
|
|
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
|
|
|
try {
|
|
|
|
for (Links = NtfsData.VcbQueue.Flink;
|
|
Links != &NtfsData.VcbQueue;
|
|
Links = Links->Flink) {
|
|
|
|
Vcb = CONTAINING_RECORD(Links, VCB, VcbLinks);
|
|
|
|
IrpContext->Vcb = Vcb;
|
|
|
|
if (FlagOn( Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED )) {
|
|
|
|
NtfsCheckpointVolume( IrpContext, Vcb, FALSE, FALSE, TRUE, Li0 );
|
|
|
|
//
|
|
// Check to see whether this was not a clean checkpoint.
|
|
//
|
|
|
|
if (!FlagOn( Vcb->CheckpointFlags, VCB_LAST_CHECKPOINT_CLEAN )) {
|
|
|
|
StartTimer = TRUE;
|
|
}
|
|
|
|
NtfsCommitCurrentTransaction( IrpContext );
|
|
#ifdef _CAIRO_
|
|
if (NtfsCheckQuota && Vcb->QuotaTableScb != NULL) {
|
|
NtfsPostRepairQuotaIndex( IrpContext, Vcb );
|
|
}
|
|
#endif // _CAIRO_
|
|
|
|
}
|
|
}
|
|
|
|
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
|
|
|
NOTHING;
|
|
}
|
|
|
|
//
|
|
// Synchronize with the checkpoint timer and other instances of this routine.
|
|
//
|
|
// Perform an interlocked exchange to indicate that a timer is being set.
|
|
//
|
|
// If the previous value indicates that no timer was set, then we
|
|
// enable the volume checkpoint timer. This will guarantee that a checkpoint
|
|
// will occur to flush out the dirty Bcb data.
|
|
//
|
|
// If the timer was set previously, then it is guaranteed that a checkpoint
|
|
// will occur without this routine having to reenable the timer.
|
|
//
|
|
// If the timer and checkpoint occurred between the dirtying of the Bcb and
|
|
// the setting of the timer status, then we will be queueing a single extra
|
|
// checkpoint on a clean volume. This is not considered harmful.
|
|
//
|
|
|
|
//
|
|
// Atomically set the timer status to indicate a timer is being set and
|
|
// retrieve the previous value.
|
|
//
|
|
|
|
if (StartTimer) {
|
|
|
|
TimerStatus = InterlockedExchange( &NtfsData.TimerStatus, TIMER_SET );
|
|
|
|
//
|
|
// If the timer is not currently set then we must start the checkpoint timer
|
|
// to make sure the above dirtying is flushed out.
|
|
//
|
|
|
|
if (TimerStatus == TIMER_NOT_SET) {
|
|
|
|
LONGLONG FiveSecondsFromNow = -5*1000*1000*10;
|
|
|
|
KeSetTimer( &NtfsData.VolumeCheckpointTimer,
|
|
*(PLARGE_INTEGER) &FiveSecondsFromNow,
|
|
&NtfsData.VolumeCheckpointDpc );
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
|
|
if (AcquiredGlobal) {
|
|
ExReleaseResource( &NtfsData.Resource );
|
|
}
|
|
|
|
NtfsRestoreTopLevelIrp( ThreadTopLevelContext );
|
|
}
|
|
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
NtfsVerifyOperationIsLegal (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines is the requested operation should be allowed to
|
|
continue. It either returns to the user if the request is Okay, or
|
|
raises an appropriate status.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp to check
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
PAGED_CODE();
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
FileObject = IrpSp->FileObject;
|
|
|
|
//
|
|
// We may be called during a clean all volumes operation. In this case
|
|
// we have a dummy Irp that we created. If the Irp stack
|
|
// count is zero then there is nothing to do here.
|
|
//
|
|
|
|
if (Irp->StackCount == 0) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If there is not a file object, we cannot continue.
|
|
//
|
|
|
|
if (FileObject == NULL) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If we are trying to do any other operation than close on a file
|
|
// object marked for delete, raise STATUS_DELETE_PENDING.
|
|
//
|
|
|
|
if (FileObject->DeletePending == TRUE
|
|
&& IrpContext->MajorFunction != IRP_MJ_CLEANUP
|
|
&& IrpContext->MajorFunction != IRP_MJ_CLOSE ) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_DELETE_PENDING, NULL, NULL );
|
|
}
|
|
|
|
//
|
|
// If we are doing a create, and there is a related file objects, and
|
|
// it it is marked for delete, raise STATUS_DELETE_PENDING.
|
|
//
|
|
|
|
if (IrpContext->MajorFunction == IRP_MJ_CREATE) {
|
|
|
|
PFILE_OBJECT RelatedFileObject;
|
|
|
|
PVCB Vcb;
|
|
PFCB Fcb;
|
|
PSCB Scb;
|
|
PCCB Ccb;
|
|
|
|
RelatedFileObject = FileObject->RelatedFileObject;
|
|
|
|
NtfsDecodeFileObject( IrpContext,
|
|
RelatedFileObject,
|
|
&Vcb,
|
|
&Fcb,
|
|
&Scb,
|
|
&Ccb,
|
|
TRUE );
|
|
|
|
if (RelatedFileObject != NULL
|
|
&& Fcb->LinkCount == 0) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_DELETE_PENDING, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the file object has already been cleaned up, and
|
|
//
|
|
// A) This request is a paging io read or write, or
|
|
// B) This request is a close operation, or
|
|
// C) This request is a set or query info call (for Lou)
|
|
// D) This is an MDL complete
|
|
//
|
|
// let it pass, otherwise return STATUS_FILE_CLOSED.
|
|
//
|
|
|
|
if ( FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE) ) {
|
|
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) ||
|
|
(IrpSp->MajorFunction == IRP_MJ_CLOSE ) ||
|
|
(IrpSp->MajorFunction == IRP_MJ_SET_INFORMATION) ||
|
|
(IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) ||
|
|
( ( (IrpSp->MajorFunction == IRP_MJ_READ) ||
|
|
(IrpSp->MajorFunction == IRP_MJ_WRITE) ) &&
|
|
FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) {
|
|
|
|
NOTHING;
|
|
|
|
} else {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_FILE_CLOSED, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local Support routine
|
|
//
|
|
|
|
VOID
|
|
NtfsPerformVerifyDiskRead (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN PVOID Buffer,
|
|
IN LONGLONG Offset,
|
|
IN ULONG NumberOfBytesToRead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to read in a range of bytes from the disk. It
|
|
bypasses all of the caching and regular I/O logic, and builds and issues
|
|
the requests itself. It does this operation overriding the verify
|
|
volume flag in the device object.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Supplies the Vcb denoting the device for this operation
|
|
|
|
Buffer - Supplies the buffer that will recieve the results of this operation
|
|
|
|
Offset - Supplies the offset of where to start reading
|
|
|
|
NumberOfBytesToRead - Supplies the number of bytes to read, this must
|
|
be in multiple of bytes units acceptable to the disk driver.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT Event;
|
|
PIRP Irp;
|
|
NTSTATUS Status;
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
ASSERT_VCB( Vcb );
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize the event we're going to use
|
|
//
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
|
|
//
|
|
// Build the irp for the operation and also set the overrride flag
|
|
//
|
|
// Note that we may be at APC level, so do this asyncrhonously and
|
|
// use an event for synchronization normal request completion
|
|
// cannot occur at APC level.
|
|
//
|
|
|
|
Irp = IoBuildAsynchronousFsdRequest( IRP_MJ_READ,
|
|
Vcb->TargetDeviceObject,
|
|
Buffer,
|
|
NumberOfBytesToRead,
|
|
(PLARGE_INTEGER)&Offset,
|
|
NULL );
|
|
|
|
if ( Irp == NULL ) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
|
|
}
|
|
|
|
SetFlag( IoGetNextIrpStackLocation( Irp )->Flags, SL_OVERRIDE_VERIFY_VOLUME );
|
|
|
|
//
|
|
// Set up the completion routine
|
|
//
|
|
|
|
IoSetCompletionRoutine( Irp,
|
|
NtfsVerifyReadCompletionRoutine,
|
|
&Event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
//
|
|
// Call the device to do the write and wait for it to finish.
|
|
//
|
|
|
|
try {
|
|
|
|
(VOID)IoCallDriver( Vcb->TargetDeviceObject, Irp );
|
|
(VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
|
|
|
|
//
|
|
// Grab the Status.
|
|
//
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
|
|
} finally {
|
|
|
|
//
|
|
// If there is an MDL (or MDLs) associated with this I/O
|
|
// request, Free it (them) here. This is accomplished by
|
|
// walking the MDL list hanging off of the IRP and deallocating
|
|
// each MDL encountered.
|
|
//
|
|
|
|
while (Irp->MdlAddress != NULL) {
|
|
|
|
PMDL NextMdl;
|
|
|
|
NextMdl = Irp->MdlAddress->Next;
|
|
|
|
MmUnlockPages( Irp->MdlAddress );
|
|
|
|
IoFreeMdl( Irp->MdlAddress );
|
|
|
|
Irp->MdlAddress = NextMdl;
|
|
}
|
|
|
|
IoFreeIrp( Irp );
|
|
}
|
|
|
|
//
|
|
// If it doesn't succeed then raise the error
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
NtfsNormalizeAndRaiseStatus( IrpContext,
|
|
Status,
|
|
STATUS_UNEXPECTED_IO_ERROR );
|
|
}
|
|
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NtfsIoCallSelf (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN UCHAR MajorFunction
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to call ourselves for a simple function. Note that
|
|
if more use is found for this routine than the few current uses, its interface
|
|
may be easily expanded.
|
|
|
|
Arguments:
|
|
|
|
FileObject - FileObject for request.
|
|
|
|
MajorFunction - function to be performed.
|
|
|
|
Return Value:
|
|
|
|
Status code resulting from the driver call
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT Event;
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
NTSTATUS Status;
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize the event we're going to use
|
|
//
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
DeviceObject = IoGetRelatedDeviceObject( FileObject );
|
|
|
|
//
|
|
// Reference the file object here so that no special checks need be made
|
|
// in I/O completion to determine whether or not to dereference the file
|
|
// object.
|
|
//
|
|
|
|
if (MajorFunction != IRP_MJ_CLOSE) {
|
|
ObReferenceObject( FileObject );
|
|
}
|
|
|
|
//
|
|
// Build the irp for the operation and also set the overrride flag
|
|
//
|
|
// Note that we may be at APC level, so do this asyncrhonously and
|
|
// use an event for synchronization normal request completion
|
|
// cannot occur at APC level.
|
|
//
|
|
|
|
|
|
Irp = IoBuildAsynchronousFsdRequest( IRP_MJ_SHUTDOWN,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
|
|
if (Irp == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Fill in a few remaining items
|
|
//
|
|
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
|
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|
IrpSp->MajorFunction = MajorFunction;
|
|
IrpSp->FileObject = FileObject;
|
|
|
|
//
|
|
// Set up the completion routine
|
|
//
|
|
|
|
IoSetCompletionRoutine( Irp,
|
|
NtfsVerifyReadCompletionRoutine,
|
|
&Event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
//
|
|
// Call the device to do the write and wait for it to finish.
|
|
//
|
|
|
|
try {
|
|
|
|
(VOID)IoCallDriver( DeviceObject, Irp );
|
|
(VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
|
|
|
|
//
|
|
// Grab the Status.
|
|
//
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
|
|
} finally {
|
|
|
|
//
|
|
// There should never be an MDL here.
|
|
//
|
|
|
|
ASSERT(Irp->MdlAddress == NULL);
|
|
|
|
IoFreeIrp( Irp );
|
|
}
|
|
|
|
//
|
|
// If it doesn't succeed then raise the error
|
|
//
|
|
// And return to our caller
|
|
//
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
NtfsLogEvent (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PQUOTA_USER_DATA UserData OPTIONAL,
|
|
IN NTSTATUS LogCode,
|
|
IN NTSTATUS FinalStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine logs an io event. If UserData is supplied then the
|
|
data logged is a FILE_QUOTA_INFORMATION structure; otherwise the
|
|
volume label is included.
|
|
|
|
Arguments:
|
|
|
|
UserData - Supplies the optional quota user data index entry.
|
|
|
|
LogCode - Supplies the Io Log code to use for the ErrorCode field.
|
|
|
|
FinalStauts - Supplies the final status of the operation.
|
|
|
|
Return Value:
|
|
|
|
True - if the event was successfully logged.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_ERROR_LOG_PACKET ErrorLogEntry;
|
|
PFILE_QUOTA_INFORMATION FileQuotaInfo;
|
|
ULONG SidLength;
|
|
ULONG DumpDataLength = 0;
|
|
ULONG LabelLength = 0;
|
|
|
|
if (IrpContext->Vcb == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (UserData == NULL) {
|
|
|
|
LabelLength = IrpContext->Vcb->Vpb->VolumeLabelLength + sizeof(WCHAR);
|
|
|
|
#ifdef _CAIRO_
|
|
|
|
} else {
|
|
|
|
//
|
|
// Calculate the required length of the Sid.
|
|
//
|
|
|
|
SidLength = RtlLengthSid( &UserData->QuotaSid );
|
|
|
|
DumpDataLength = SidLength +
|
|
FIELD_OFFSET( FILE_QUOTA_INFORMATION, Sid );
|
|
|
|
#endif // _CAIRO_
|
|
|
|
}
|
|
|
|
ErrorLogEntry = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry( IrpContext->Vcb->TargetDeviceObject,
|
|
(UCHAR) (DumpDataLength +
|
|
LabelLength +
|
|
sizeof(IO_ERROR_LOG_PACKET)) );
|
|
|
|
if (ErrorLogEntry == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
ErrorLogEntry->ErrorCode = LogCode;
|
|
ErrorLogEntry->FinalStatus = FinalStatus;
|
|
|
|
ErrorLogEntry->SequenceNumber = IrpContext->TransactionId;
|
|
ErrorLogEntry->MajorFunctionCode = IrpContext->MajorFunction;
|
|
ErrorLogEntry->RetryCount = 0;
|
|
ErrorLogEntry->DumpDataSize = (USHORT) DumpDataLength;
|
|
|
|
if (UserData == NULL) {
|
|
|
|
PWCHAR String;
|
|
|
|
//
|
|
// The label string at the end of the error log entry.
|
|
//
|
|
|
|
String = (PWCHAR) (ErrorLogEntry + 1);
|
|
|
|
ErrorLogEntry->NumberOfStrings = 1;
|
|
ErrorLogEntry->StringOffset = sizeof( IO_ERROR_LOG_PACKET );
|
|
RtlCopyMemory( String,
|
|
IrpContext->Vcb->Vpb->VolumeLabel,
|
|
IrpContext->Vcb->Vpb->VolumeLabelLength );
|
|
|
|
//
|
|
// Make sure the string is null terminated.
|
|
//
|
|
|
|
String += IrpContext->Vcb->Vpb->VolumeLabelLength / sizeof( WCHAR );
|
|
|
|
*String = L'\0';
|
|
|
|
#ifdef _CAIRO_
|
|
|
|
} else {
|
|
|
|
FileQuotaInfo = (PFILE_QUOTA_INFORMATION) ErrorLogEntry->DumpData;
|
|
|
|
FileQuotaInfo->NextEntryOffset = 0;
|
|
FileQuotaInfo->SidLength = SidLength;
|
|
FileQuotaInfo->ChangeTime.QuadPart = UserData->QuotaChangeTime;
|
|
FileQuotaInfo->QuotaUsed.QuadPart = UserData->QuotaUsed;
|
|
FileQuotaInfo->QuotaThreshold.QuadPart = UserData->QuotaThreshold;
|
|
FileQuotaInfo->QuotaLimit.QuadPart = UserData->QuotaLimit;
|
|
RtlCopyMemory( &FileQuotaInfo->Sid,
|
|
&UserData->QuotaSid,
|
|
SidLength );
|
|
|
|
#endif // _CAIRO
|
|
|
|
}
|
|
|
|
IoWriteErrorLogEntry( ErrorLogEntry );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
NtfsPostVcbIsCorrupt (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN NTSTATUS Status OPTIONAL,
|
|
IN PFILE_REFERENCE FileReference OPTIONAL,
|
|
IN PFCB Fcb OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to mark the volume dirty and possibly raise a hard error.
|
|
|
|
Arguments:
|
|
|
|
Status - If not zero, then this is the error code for the popup.
|
|
|
|
FileReference - If specified, then this is the file reference for the corrupt file.
|
|
|
|
Fcb - If specified, then this is the Fcb for the corrupt file.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PVCB Vcb = IrpContext->Vcb;
|
|
|
|
#ifdef NTFS_CORRUPT
|
|
{
|
|
KdPrint(("NTFS: Post Vcb is corrupt\n", 0));
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Set this flag to keep the volume from ever getting set clean.
|
|
//
|
|
|
|
if (Vcb != NULL) {
|
|
|
|
NtfsMarkVolumeDirty( IrpContext, Vcb );
|
|
|
|
//
|
|
// This would be the appropriate place to raise a hard error popup,
|
|
// ala the code in FastFat. We should do it after marking the volume
|
|
// dirty so that if anything goes wrong with the popup, the volume is
|
|
// already marked anyway.
|
|
//
|
|
|
|
if (Status != 0) {
|
|
|
|
NtfsRaiseInformationHardError( IrpContext,
|
|
Status,
|
|
FileReference,
|
|
Fcb );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
NtfsMarkVolumeDirty (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine may be called any time the Mft is open to mark the volume
|
|
dirty.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Vcb for volume to mark dirty
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
|
|
PVOLUME_INFORMATION VolumeInformation;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if DBG
|
|
KdPrint(("NTFS: Marking volume dirty, Vcb: %08lx\n", Vcb));
|
|
#endif
|
|
|
|
#ifdef NTFS_CORRUPT
|
|
{
|
|
KdPrint(("NTFS: Marking volume dirty\n", 0));
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
// if (FlagOn(*(PULONG)NtGlobalFlag, 0x00080000)) {
|
|
// KdPrint(("NTFS: marking volume dirty\n", 0));
|
|
// DbgBreakPoint();
|
|
// }
|
|
|
|
//
|
|
// Return if the volume is already marked dirty. This also prevents
|
|
// endless recursion if the volume file itself is corrupt.
|
|
//
|
|
|
|
if (FlagOn(Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED_DIRTY)) {
|
|
return;
|
|
}
|
|
|
|
SetFlag(Vcb->VcbState, VCB_STATE_VOLUME_MOUNTED_DIRTY);
|
|
|
|
NtfsInitializeAttributeContext( &AttributeContext );
|
|
|
|
try {
|
|
|
|
if (NtfsLookupAttributeByCode( IrpContext,
|
|
Vcb->VolumeDasdScb->Fcb,
|
|
&Vcb->VolumeDasdScb->Fcb->FileReference,
|
|
$VOLUME_INFORMATION,
|
|
&AttributeContext )) {
|
|
|
|
VolumeInformation =
|
|
(PVOLUME_INFORMATION)NtfsAttributeValue( NtfsFoundAttribute( &AttributeContext ));
|
|
|
|
NtfsPinMappedAttribute( IrpContext, Vcb, &AttributeContext );
|
|
|
|
SetFlag(VolumeInformation->VolumeFlags, VOLUME_DIRTY);
|
|
|
|
CcSetDirtyPinnedData( NtfsFoundBcb(&AttributeContext), NULL );
|
|
|
|
NtfsCleanupAttributeContext( &AttributeContext );
|
|
}
|
|
|
|
} finally {
|
|
|
|
NtfsCleanupAttributeContext( &AttributeContext );
|
|
}
|
|
|
|
NtfsLogEvent( IrpContext,
|
|
NULL,
|
|
IO_FILE_SYSTEM_CORRUPT,
|
|
STATUS_DISK_CORRUPT_ERROR );
|
|
}
|
|
|
|
|
|
VOID
|
|
NtfsUpdateVersionNumber (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PVCB Vcb,
|
|
IN UCHAR MajorVersion,
|
|
IN UCHAR MinorVersion
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to update the version number on disk, without
|
|
going through the logging package.
|
|
|
|
Arguments:
|
|
|
|
Vcb - Vcb for volume.
|
|
|
|
MajorVersion - This is the Major Version number to write out.
|
|
|
|
MinorVersion - This is the Minor Version number to write out.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ATTRIBUTE_ENUMERATION_CONTEXT AttributeContext;
|
|
PVOLUME_INFORMATION VolumeInformation;
|
|
|
|
PAGED_CODE();
|
|
|
|
NtfsInitializeAttributeContext( &AttributeContext );
|
|
|
|
try {
|
|
|
|
//
|
|
// Lookup the volume information attribute.
|
|
//
|
|
|
|
if (NtfsLookupAttributeByCode( IrpContext,
|
|
Vcb->VolumeDasdScb->Fcb,
|
|
&Vcb->VolumeDasdScb->Fcb->FileReference,
|
|
$VOLUME_INFORMATION,
|
|
&AttributeContext )) {
|
|
|
|
VolumeInformation =
|
|
(PVOLUME_INFORMATION)NtfsAttributeValue( NtfsFoundAttribute( &AttributeContext ));
|
|
|
|
NtfsPinMappedAttribute( IrpContext, Vcb, &AttributeContext );
|
|
|
|
//
|
|
// Now update the version number fields.
|
|
//
|
|
|
|
VolumeInformation->MajorVersion = MajorVersion;
|
|
VolumeInformation->MinorVersion = MinorVersion;
|
|
|
|
CcSetDirtyPinnedData( NtfsFoundBcb(&AttributeContext), NULL );
|
|
|
|
NtfsCleanupAttributeContext( &AttributeContext );
|
|
|
|
//
|
|
// Now flush it out, so the new version numbers will be present on
|
|
// the next mount.
|
|
//
|
|
|
|
CcFlushCache( Vcb->MftScb->FileObject->SectionObjectPointer,
|
|
(PLARGE_INTEGER)&AttributeContext.FoundAttribute.MftFileOffset,
|
|
Vcb->BytesPerFileRecordSegment,
|
|
NULL );
|
|
}
|
|
|
|
} finally {
|
|
|
|
NtfsCleanupAttributeContext( &AttributeContext );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsVerifyReadCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Contxt
|
|
)
|
|
|
|
{
|
|
//
|
|
// Set the event so that our call will wake up.
|
|
//
|
|
|
|
KeSetEvent( (PKEVENT)Contxt, 0, FALSE );
|
|
|
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|
UNREFERENCED_PARAMETER( Irp );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
NtfsDeviceIoControlAsync (
|
|
IN PIRP_CONTEXT IrpContext,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN ULONG IoCtl
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to perform an IoCtl when we may be at the APC level
|
|
and calling NtfsDeviceIoControl could be unsafe.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object to which to send the ioctl.
|
|
|
|
IoCtl - Supplies the I/O control code.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT Event;
|
|
PIRP Irp;
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
ASSERT_IRP_CONTEXT( IrpContext );
|
|
|
|
//
|
|
// We'll be leaving the event on the stack, so let's be sure
|
|
// that we've done an FsRtlEnterFileSystem before we got here.
|
|
// I claim that we're never going to end up here in the fast
|
|
// io path, but let's make certain.
|
|
//
|
|
|
|
ASSERTMSG( "Didn't FsRtlEnterFileSystem", KeGetCurrentThread()->KernelApcDisable != 0 );
|
|
|
|
//
|
|
// Initialize the event we're going to use
|
|
//
|
|
|
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|
|
|
//
|
|
// Build the irp for the operation and also set the overrride flag
|
|
//
|
|
// Note that we may be at APC level, so do this asyncrhonously and
|
|
// use an event for synchronization normal request completion
|
|
// cannot occur at APC level.
|
|
//
|
|
|
|
// **** Change this irp_mj_ back after Jeff or Peter changes io so
|
|
// **** that we can pass in the right irp_mj_ and no buffers.
|
|
|
|
Irp = IoBuildAsynchronousFsdRequest( IRP_MJ_FLUSH_BUFFERS, // **** IRP_MJ_DEVICE_CONTROL,
|
|
DeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( Irp == NULL ) {
|
|
|
|
NtfsRaiseStatus( IrpContext, STATUS_INSUFFICIENT_RESOURCES, NULL, NULL );
|
|
}
|
|
|
|
IrpSp = IoGetNextIrpStackLocation( Irp );
|
|
SetFlag( IrpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME );
|
|
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode = IoCtl;
|
|
|
|
// **** Remove this, too.
|
|
|
|
IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
|
|
//
|
|
// Set up the completion routine.
|
|
//
|
|
|
|
IoSetCompletionRoutine( Irp,
|
|
NtfsVerifyReadCompletionRoutine,
|
|
&Event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE );
|
|
|
|
//
|
|
// Call the device to do the io and wait for it to finish.
|
|
//
|
|
|
|
(VOID)IoCallDriver( DeviceObject, Irp );
|
|
(VOID)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, (PLARGE_INTEGER)NULL );
|
|
|
|
//
|
|
// Grab the Status.
|
|
//
|
|
|
|
Status = Irp->IoStatus.Status;
|
|
|
|
IoFreeIrp( Irp );
|
|
|
|
//
|
|
// And return to our caller.
|
|
//
|
|
|
|
return Status;
|
|
}
|
|
|
|
|