1015 lines
26 KiB
C
1015 lines
26 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
VerfySup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Udfs Verification routines.
|
||
|
||
// @@BEGIN_DDKSPLIT
|
||
|
||
Author:
|
||
|
||
Dan Lovinger [DanLo] 18-July-1996
|
||
|
||
Revision History:
|
||
|
||
// @@END_DDKSPLIT
|
||
|
||
--*/
|
||
|
||
#include "UdfProcs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (UDFS_BUG_CHECK_VERFYSUP)
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (UDFS_DEBUG_LEVEL_VERFYSUP)
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, UdfVerifyFcbOperation)
|
||
#pragma alloc_text(PAGE, UdfVerifyVcb)
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
UdfPerformVerify (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PIRP Irp,
|
||
IN PDEVICE_OBJECT DeviceToVerify
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines performs an IoVerifyVolume operation and takes the
|
||
appropriate action. If the verify is successful then we send the originating
|
||
Irp off to an Ex Worker Thread. This routine is called from the exception handler.
|
||
|
||
No file system resources are held when this routine is called.
|
||
|
||
Arguments:
|
||
|
||
Irp - The irp to send off after all is well and done.
|
||
|
||
Device - The real device needing verification.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVCB Vcb;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
BOOLEAN AllowRawMount = FALSE;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_IRP( Irp );
|
||
|
||
//
|
||
// Check if this Irp has a status of Verify required and if it does
|
||
// then call the I/O system to do a verify.
|
||
//
|
||
// Skip the IoVerifyVolume if this is a mount or verify request
|
||
// itself. Trying a recursive mount will cause a deadlock with
|
||
// the DeviceObject->DeviceLock.
|
||
//
|
||
|
||
if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
|
||
((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) ||
|
||
(IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME))) {
|
||
|
||
return UdfFsdPostRequest( IrpContext, Irp );
|
||
}
|
||
|
||
//
|
||
// Extract a pointer to the Vcb from the VolumeDeviceObject.
|
||
// Note that since we have specifically excluded mount,
|
||
// requests, we know that IrpSp->DeviceObject is indeed a
|
||
// volume device object.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
Vcb = &CONTAINING_RECORD( IrpSp->DeviceObject,
|
||
VOLUME_DEVICE_OBJECT,
|
||
DeviceObject )->Vcb;
|
||
|
||
//
|
||
// Check if the volume still thinks it needs to be verified,
|
||
// if it doesn't then we can skip doing a verify because someone
|
||
// else beat us to it. If this is a 'forced' verify then we
|
||
// always go through the motions for mount sync. purposes.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// We will allow Raw to mount this volume if we were doing a
|
||
// an absolute DASD open.
|
||
//
|
||
|
||
if ((IrpContext->MajorFunction == IRP_MJ_CREATE) &&
|
||
(IrpSp->FileObject->FileName.Length == 0) &&
|
||
(IrpSp->FileObject->RelatedFileObject == NULL)) {
|
||
|
||
AllowRawMount = TRUE;
|
||
}
|
||
|
||
//
|
||
// Send down the verify FSCTL. Note that this is sent to the
|
||
// currently mounted volume, which may not be this one.
|
||
//
|
||
|
||
Status = IoVerifyVolume( DeviceToVerify, AllowRawMount );
|
||
|
||
//
|
||
// Acquire the Vcb so we're working with a stable VcbCondition.
|
||
//
|
||
|
||
UdfAcquireVcbShared( IrpContext, Vcb, FALSE);
|
||
|
||
//
|
||
// If the verify operation completed it will return
|
||
// either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly.
|
||
//
|
||
// If UdfVerifyVolume encountered an error during
|
||
// processing, it will return that error. If we got
|
||
// STATUS_WRONG_VOLUME from the verify, and our volume
|
||
// is now mounted, commute the status to STATUS_SUCCESS.
|
||
//
|
||
|
||
if ((Status == STATUS_WRONG_VOLUME) &&
|
||
(Vcb->VcbCondition == VcbMounted)) {
|
||
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
else if ((STATUS_SUCCESS == Status) && (Vcb->VcbCondition != VcbMounted)) {
|
||
|
||
//
|
||
// If the verify succeeded, but our volume is not mounted,
|
||
// then some other volume is on the device.
|
||
//
|
||
|
||
Status = STATUS_WRONG_VOLUME;
|
||
}
|
||
|
||
//
|
||
// Do a quick unprotected check here. The routine will do
|
||
// a safe check. After here we can release the resource.
|
||
// Note that if the volume really went away, we will be taking
|
||
// the Reparse path.
|
||
//
|
||
|
||
//
|
||
// If the device might need to go away then call our dismount routine.
|
||
//
|
||
|
||
if (((Vcb->VcbCondition == VcbNotMounted) ||
|
||
(Vcb->VcbCondition == VcbInvalid) ||
|
||
(Vcb->VcbCondition == VcbDismountInProgress)) &&
|
||
(Vcb->VcbReference <= Vcb->VcbResidualReference)) {
|
||
|
||
UdfReleaseVcb( IrpContext, Vcb);
|
||
|
||
UdfAcquireUdfData( IrpContext );
|
||
UdfCheckForDismount( IrpContext, Vcb, FALSE );
|
||
UdfReleaseUdfData( IrpContext );
|
||
}
|
||
else {
|
||
|
||
UdfReleaseVcb( IrpContext, Vcb);
|
||
}
|
||
|
||
//
|
||
// If this is a create and the verify succeeded then complete the
|
||
// request with a REPARSE status.
|
||
//
|
||
|
||
if ((IrpContext->MajorFunction == IRP_MJ_CREATE) &&
|
||
(IrpSp->FileObject->RelatedFileObject == NULL) &&
|
||
((Status == STATUS_SUCCESS) || (Status == STATUS_WRONG_VOLUME))) {
|
||
|
||
Irp->IoStatus.Information = IO_REMOUNT;
|
||
|
||
UdfCompleteRequest( IrpContext, Irp, STATUS_REPARSE );
|
||
Status = STATUS_REPARSE;
|
||
Irp = NULL;
|
||
IrpContext = NULL;
|
||
|
||
DebugTrace(( 0, Dbg, "UdfPerformVerify, Reparsing create irp.\n"));
|
||
|
||
//
|
||
// If there is still an error to process then call the Io system
|
||
// for a popup.
|
||
//
|
||
|
||
} else if ((Irp != NULL) && !NT_SUCCESS( Status )) {
|
||
|
||
DebugTrace(( 0, Dbg, "UdfPerformVerify, Raising error %x (Op %x)\n", Status,
|
||
IrpContext->MajorFunction));
|
||
//
|
||
// Fill in the device object if required.
|
||
//
|
||
|
||
if (IoIsErrorUserInduced( Status ) ) {
|
||
|
||
IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify );
|
||
}
|
||
|
||
//
|
||
// We should not be receiving this status from verify
|
||
// volume - we'll end up recursing out of stack.
|
||
//
|
||
|
||
ASSERT( STATUS_VERIFY_REQUIRED != Status);
|
||
|
||
UdfNormalizeAndRaiseStatus( IrpContext, Status );
|
||
}
|
||
|
||
//
|
||
// If there is still an Irp, send it off to an Ex Worker thread.
|
||
//
|
||
|
||
if (IrpContext != NULL) {
|
||
|
||
DebugTrace(( 0, Dbg, "UdfPerformVerify, Posting IRP (Op %x)\n", IrpContext->MajorFunction));
|
||
|
||
Status = UdfFsdPostRequest( IrpContext, Irp );
|
||
}
|
||
|
||
} except(UdfExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
||
|
||
//
|
||
// We had some trouble trying to perform the verify or raised
|
||
// an error ourselves. So we'll abort the I/O request with
|
||
// the error status that we get back from the execption code.
|
||
//
|
||
|
||
Status = UdfProcessException( IrpContext, Irp, GetExceptionCode() );
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
UdfCheckForDismount (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb,
|
||
IN BOOLEAN Force
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to check if a volume is ready for dismount. This
|
||
occurs when only file system references are left on the volume.
|
||
|
||
If the dismount is not currently underway and the user reference count
|
||
has gone to zero then we can begin the dismount.
|
||
|
||
If the dismount is in progress and there are no references left on the
|
||
volume (we check the Vpb for outstanding references as well to catch
|
||
any create calls dispatched to the file system) then we can delete
|
||
the Vcb.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Vcb for the volume to try to dismount.
|
||
|
||
Force - Whether we will force this volume to be dismounted.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - True if the Vcb was not gone by the time this function finished,
|
||
False if it was deleted.
|
||
|
||
This is only a trustworthy indication to the caller if it had the vcb
|
||
exclusive itself.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN UnlockVcb = TRUE;
|
||
BOOLEAN VcbPresent = TRUE;
|
||
KIRQL SavedIrql;
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_VCB( Vcb );
|
||
|
||
ASSERT_EXCLUSIVE_UDFDATA;
|
||
|
||
//
|
||
// Acquire and lock this Vcb to check the dismount state.
|
||
//
|
||
|
||
UdfAcquireVcbExclusive( IrpContext, Vcb, FALSE );
|
||
|
||
//
|
||
// Lets get rid of any pending closes for this volume.
|
||
//
|
||
|
||
UdfFspClose( Vcb );
|
||
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// If the dismount is not already underway then check if the
|
||
// user reference count has gone to zero or we are being forced
|
||
// to disconnect. If so start the teardown on the Vcb.
|
||
//
|
||
|
||
if (Vcb->VcbCondition != VcbDismountInProgress) {
|
||
|
||
if (Vcb->VcbUserReference <= Vcb->VcbResidualUserReference || Force) {
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
VcbPresent = UdfDismountVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
//
|
||
// If the teardown is underway and there are absolutely no references
|
||
// remaining then delete the Vcb. References here include the
|
||
// references in the Vcb and Vpb.
|
||
//
|
||
|
||
} else if (Vcb->VcbReference == 0) {
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
|
||
//
|
||
// If there are no file objects and no reference counts in the
|
||
// Vpb we can delete the Vcb. Don't forget that we have the
|
||
// last reference in the Vpb.
|
||
//
|
||
|
||
if (Vcb->Vpb->ReferenceCount == 1) {
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UnlockVcb = FALSE;
|
||
UdfDeleteVcb( IrpContext, Vcb );
|
||
VcbPresent = FALSE;
|
||
|
||
} else {
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Unlock the Vcb if still held.
|
||
//
|
||
|
||
if (UnlockVcb) {
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
//
|
||
// Release any resources still acquired.
|
||
//
|
||
|
||
if (VcbPresent) {
|
||
|
||
UdfReleaseVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
return VcbPresent;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
UdfDismountVcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when all of the user references to a volume are
|
||
gone. We will initiate all of the teardown any system resources.
|
||
|
||
If all of the references to this volume are gone at the end of this routine
|
||
then we will complete the teardown of this Vcb and mark the current Vpb
|
||
as not mounted. Otherwise we will allocated a new Vpb for this device
|
||
and keep the current Vpb attached to the Vcb.
|
||
|
||
Arguments:
|
||
|
||
Vcb - Vcb for the volume to dismount.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if we didn't delete the Vcb, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PVPB OldVpb;
|
||
BOOLEAN VcbPresent = TRUE;
|
||
KIRQL SavedIrql;
|
||
|
||
BOOLEAN FinalReference;
|
||
|
||
ASSERT_EXCLUSIVE_UDFDATA;
|
||
ASSERT_EXCLUSIVE_VCB( Vcb );
|
||
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// We should only take this path once.
|
||
//
|
||
|
||
ASSERT( Vcb->VcbCondition != VcbDismountInProgress );
|
||
|
||
//
|
||
// Mark the Vcb as DismountInProgress.
|
||
//
|
||
|
||
UdfSetVcbCondition( Vcb, VcbDismountInProgress);
|
||
|
||
//
|
||
// Remove our reference to the internal Fcb's. The Fcb's will then
|
||
// be removed in the purge path below.
|
||
//
|
||
|
||
if (Vcb->RootIndexFcb != NULL) {
|
||
|
||
Vcb->RootIndexFcb->FcbReference -= 1;
|
||
Vcb->RootIndexFcb->FcbUserReference -= 1;
|
||
}
|
||
|
||
if (Vcb->MetadataFcb != NULL) {
|
||
|
||
Vcb->MetadataFcb->FcbReference -= 1;
|
||
Vcb->MetadataFcb->FcbUserReference -= 1;
|
||
}
|
||
|
||
if (Vcb->VatFcb != NULL) {
|
||
|
||
Vcb->VatFcb->FcbReference -= 1;
|
||
Vcb->VatFcb->FcbUserReference -= 1;
|
||
}
|
||
|
||
if (Vcb->VolumeDasdFcb != NULL) {
|
||
|
||
Vcb->VolumeDasdFcb->FcbReference -= 1;
|
||
Vcb->VolumeDasdFcb->FcbUserReference -= 1;
|
||
}
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// Purge the volume.
|
||
//
|
||
|
||
UdfPurgeVolume( IrpContext, Vcb, TRUE );
|
||
|
||
//
|
||
// Empty the delayed and async close queues.
|
||
//
|
||
|
||
UdfFspClose( Vcb );
|
||
|
||
OldVpb = Vcb->Vpb;
|
||
|
||
//
|
||
// Remove the mount volume reference.
|
||
//
|
||
|
||
UdfLockVcb( IrpContext, Vcb );
|
||
Vcb->VcbReference -= 1;
|
||
|
||
//
|
||
// Acquire the Vpb spinlock to check for Vpb references.
|
||
//
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
|
||
//
|
||
// Remember if this is the last reference on this Vcb. We incremented
|
||
// the count on the Vpb earlier so we get one last crack it. If our
|
||
// reference has gone to zero but the vpb reference count is greater
|
||
// than zero then the Io system will be responsible for deleting the
|
||
// Vpb.
|
||
//
|
||
|
||
FinalReference = (BOOLEAN) ((Vcb->VcbReference == 0) &&
|
||
(OldVpb->ReferenceCount == 1));
|
||
|
||
//
|
||
// There is a reference count in the Vpb and in the Vcb. We have
|
||
// incremented the reference count in the Vpb to make sure that
|
||
// we have last crack at it. If this is a failed mount then we
|
||
// want to return the Vpb to the IO system to use for the next
|
||
// mount request.
|
||
//
|
||
|
||
if (OldVpb->RealDevice->Vpb == OldVpb) {
|
||
|
||
//
|
||
// If not the final reference then swap out the Vpb. We must
|
||
// preserve the REMOVE_PENDING flag so that the device is
|
||
// not remounted in the middle of a PnP remove operation.
|
||
//
|
||
|
||
if (!FinalReference) {
|
||
|
||
ASSERT( Vcb->SwapVpb != NULL );
|
||
|
||
Vcb->SwapVpb->Type = IO_TYPE_VPB;
|
||
Vcb->SwapVpb->Size = sizeof( VPB );
|
||
Vcb->SwapVpb->RealDevice = OldVpb->RealDevice;
|
||
|
||
Vcb->SwapVpb->RealDevice->Vpb = Vcb->SwapVpb;
|
||
|
||
Vcb->SwapVpb->Flags = FlagOn( OldVpb->Flags, VPB_REMOVE_PENDING );
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
|
||
//
|
||
// Indicate we used up the swap.
|
||
//
|
||
|
||
Vcb->SwapVpb = NULL;
|
||
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
|
||
//
|
||
// We want to leave the Vpb for the IO system. Mark it
|
||
// as being not mounted. Go ahead and delete the Vcb as
|
||
// well.
|
||
//
|
||
|
||
} else {
|
||
|
||
//
|
||
// Make sure to remove the last reference on the Vpb.
|
||
//
|
||
|
||
OldVpb->ReferenceCount -= 1;
|
||
|
||
OldVpb->DeviceObject = NULL;
|
||
ClearFlag( Vcb->Vpb->Flags, VPB_MOUNTED );
|
||
ClearFlag( Vcb->Vpb->Flags, VPB_LOCKED );
|
||
|
||
//
|
||
// Clear the Vpb flag so we know not to delete it.
|
||
//
|
||
|
||
Vcb->Vpb = NULL;
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UdfDeleteVcb( IrpContext, Vcb );
|
||
VcbPresent = FALSE;
|
||
}
|
||
|
||
//
|
||
// Someone has already swapped in a new Vpb. If this is the final reference
|
||
// then the file system is responsible for deleting the Vpb.
|
||
//
|
||
|
||
} else if (FinalReference) {
|
||
|
||
//
|
||
// Make sure to remove the last reference on the Vpb.
|
||
//
|
||
|
||
OldVpb->ReferenceCount -= 1;
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
UdfDeleteVcb( IrpContext, Vcb );
|
||
VcbPresent = FALSE;
|
||
|
||
//
|
||
// The current Vpb is no longer the Vpb for the device (the IO system
|
||
// has already allocated a new one). We leave our reference in the
|
||
// Vpb and will be responsible for deleting it at a later time.
|
||
//
|
||
|
||
} else {
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
UdfUnlockVcb( IrpContext, Vcb );
|
||
}
|
||
|
||
//
|
||
// Let our caller know whether the Vcb is still present.
|
||
//
|
||
|
||
return VcbPresent;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
UdfMarkDevForVerifyIfVcbMounted(
|
||
IN PVCB Vcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks to see if the specified Vcb is currently mounted on
|
||
the device or not. If it is, it sets the verify flag on the device, if
|
||
not then the state is noted in the Vcb.
|
||
|
||
Arguments:
|
||
|
||
Vcb - This is the volume to check.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the device has been marked for verify here, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN Marked = FALSE;
|
||
KIRQL SavedIrql;
|
||
|
||
IoAcquireVpbSpinLock( &SavedIrql );
|
||
|
||
if (Vcb->Vpb->RealDevice->Vpb == Vcb->Vpb) {
|
||
|
||
UdfMarkRealDevForVerify( Vcb->Vpb->RealDevice);
|
||
Marked = TRUE;
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Flag this to avoid the VPB spinlock in future passes.
|
||
//
|
||
|
||
SetFlag( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE);
|
||
}
|
||
|
||
IoReleaseVpbSpinLock( SavedIrql );
|
||
|
||
return Marked;
|
||
}
|
||
|
||
|
||
VOID
|
||
UdfVerifyVcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PVCB Vcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks that the current Vcb is valid and currently mounted
|
||
on the device. It will raise on an error condition.
|
||
|
||
We check whether the volume needs verification and the current state
|
||
of the Vcb.
|
||
|
||
Arguments:
|
||
|
||
Vcb - This is the volume to verify.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
IO_STATUS_BLOCK Iosb;
|
||
ULONG MediaChangeCount = 0;
|
||
BOOLEAN ForceVerify = FALSE;
|
||
BOOLEAN DevMarkedForVerify;
|
||
KIRQL SavedIrql;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace((0, Dbg, "UdfVerifyVcb %x (condition %d)\n", IrpContext->MajorFunction,
|
||
Vcb->VcbCondition));
|
||
|
||
//
|
||
// Fail immediately if the volume is in the progress of being dismounted
|
||
// or has been marked invalid.
|
||
//
|
||
|
||
if ((Vcb->VcbCondition == VcbInvalid) ||
|
||
((Vcb->VcbCondition == VcbDismountInProgress) &&
|
||
(IrpContext->MajorFunction != IRP_MJ_CREATE))) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_INVALID );
|
||
}
|
||
|
||
if (FlagOn( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA )) {
|
||
|
||
//
|
||
// Capture the real device verify state.
|
||
//
|
||
|
||
DevMarkedForVerify = UdfRealDevNeedsVerify( Vcb->Vpb->RealDevice);
|
||
|
||
//
|
||
// If 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.
|
||
//
|
||
|
||
if (Vcb->VcbCondition != VcbMountInProgress) {
|
||
|
||
Status = UdfPerformDevIoCtrl( IrpContext,
|
||
( Vcb->Vpb->RealDevice->DeviceType == FILE_DEVICE_CD_ROM ?
|
||
IOCTL_CDROM_CHECK_VERIFY :
|
||
IOCTL_DISK_CHECK_VERIFY ),
|
||
Vcb->TargetDeviceObject,
|
||
NULL,
|
||
0,
|
||
&MediaChangeCount,
|
||
sizeof(ULONG),
|
||
FALSE,
|
||
FALSE,
|
||
&Iosb );
|
||
|
||
if (Iosb.Information != sizeof(ULONG)) {
|
||
|
||
//
|
||
// Be safe about the count in case the driver didn't fill it in
|
||
//
|
||
|
||
MediaChangeCount = 0;
|
||
}
|
||
|
||
//
|
||
// There are four cases when we want to do a verify. These are the
|
||
// first three.
|
||
//
|
||
// 1. We are mounted, and the device has become empty
|
||
// 2. The device has returned verify required (=> DO_VERIFY_VOL flag is
|
||
// set, but could be due to hardware condition)
|
||
// 3. Media change count doesn't match the one in the Vcb
|
||
//
|
||
|
||
if (((Vcb->VcbCondition == VcbMounted) &&
|
||
UdfIsRawDevice( IrpContext, Status ))
|
||
||
|
||
(Status == STATUS_VERIFY_REQUIRED)
|
||
||
|
||
(NT_SUCCESS(Status) &&
|
||
(Vcb->MediaChangeCount != MediaChangeCount))) {
|
||
|
||
//
|
||
// If we are currently the volume on the device then it is our
|
||
// responsibility to set the verify flag. If we're not on the device,
|
||
// then we shouldn't touch the flag.
|
||
//
|
||
|
||
if (!FlagOn( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE) &&
|
||
!DevMarkedForVerify) {
|
||
|
||
DevMarkedForVerify = UdfMarkDevForVerifyIfVcbMounted( Vcb);
|
||
}
|
||
|
||
ForceVerify = TRUE;
|
||
|
||
DebugTrace((0, Dbg, "Force verify due to dev state. CV %x Vcb->Mc %d Device->Mc %d\n",
|
||
Status, Vcb->MediaChangeCount, MediaChangeCount));
|
||
//
|
||
// Note that we no longer update the media change count here. We
|
||
// do so only when we've actually completed a verify at a
|
||
// particular change count value.
|
||
//
|
||
}
|
||
}
|
||
|
||
//
|
||
// This is the 4th verify case.
|
||
//
|
||
// We ALWAYS force CREATE requests on unmounted volumes through the
|
||
// verify path. These requests could have been in limbo between
|
||
// IoCheckMountedVpb and us when a verify/mount took place and caused
|
||
// a completely different fs/volume to be mounted. In this case the
|
||
// checks above may not have caught the condition, since we may already
|
||
// have verified (wrong volume) and decided that we have nothing to do.
|
||
// We want the requests to be re routed to the currently mounted volume,
|
||
// since they were directed at the 'drive', not our volume.
|
||
//
|
||
|
||
if (NT_SUCCESS( Status) && !ForceVerify &&
|
||
(IrpContext->MajorFunction == IRP_MJ_CREATE)) {
|
||
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->Irp);
|
||
|
||
ForceVerify = (IrpSp->FileObject->RelatedFileObject == NULL) &&
|
||
((Vcb->VcbCondition == VcbDismountInProgress) ||
|
||
(Vcb->VcbCondition == VcbNotMounted));
|
||
|
||
//
|
||
// Note that we don't touch the device verify flag here. It required
|
||
// it would have been caught and set by the first set of checks.
|
||
//
|
||
|
||
if (ForceVerify) {
|
||
|
||
DebugTrace((0, Dbg, "Forcing verify on Create request\n"));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Raise any verify / error if neccessary.
|
||
//
|
||
|
||
if (ForceVerify || !NT_SUCCESS( Status)) {
|
||
|
||
DebugTrace((0, Dbg, "Raising verify / status %x\n", Status));
|
||
|
||
IoSetHardErrorOrVerifyDevice( IrpContext->Irp,
|
||
Vcb->Vpb->RealDevice );
|
||
|
||
UdfRaiseStatus( IrpContext, ForceVerify ? STATUS_VERIFY_REQUIRED : Status);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Based on the condition of the Vcb we'll either return to our
|
||
// caller or raise an error condition
|
||
//
|
||
|
||
switch (Vcb->VcbCondition) {
|
||
|
||
case VcbNotMounted:
|
||
|
||
DebugTrace(( 0, Dbg, "Raising WRONG VOLUME\n"));
|
||
|
||
IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice );
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
|
||
break;
|
||
|
||
case VcbInvalid:
|
||
case VcbDismountInProgress :
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_INVALID );
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
UdfVerifyFcbOperation (
|
||
IN PIRP_CONTEXT IrpContext OPTIONAL,
|
||
IN PFCB Fcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to verify that the state of the Fcb is valid
|
||
to allow the current operation to continue. We use the state of the
|
||
Vcb, target device and type of operation to determine this.
|
||
|
||
Arguments:
|
||
|
||
IrpContext - IrpContext for the request. If not present then we
|
||
were called from the fast IO path.
|
||
|
||
Fcb - Fcb to perform the request on.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the request can continue, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PVCB Vcb = Fcb->Vcb;
|
||
PDEVICE_OBJECT RealDevice = Vcb->Vpb->RealDevice;
|
||
PIRP Irp;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check that the fileobject has not been cleaned up.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT( IrpContext )) {
|
||
|
||
PFILE_OBJECT FileObject;
|
||
|
||
Irp = IrpContext->Irp;
|
||
FileObject = IoGetCurrentIrpStackLocation( Irp)->FileObject;
|
||
|
||
if ( FileObject && FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE)) {
|
||
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
//
|
||
// Following FAT, we allow certain operations even on cleaned up
|
||
// file objects. Everything else, we fail.
|
||
//
|
||
|
||
if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) ||
|
||
(IrpSp->MajorFunction == IRP_MJ_CLOSE ) ||
|
||
(IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) ||
|
||
( (IrpSp->MajorFunction == IRP_MJ_READ) &&
|
||
FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) {
|
||
|
||
NOTHING;
|
||
|
||
} else {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CLOSED );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Fail immediately if the volume is in the progress of being dismounted
|
||
// or has been marked invalid.
|
||
//
|
||
|
||
if ((Vcb->VcbCondition == VcbInvalid) ||
|
||
(Vcb->VcbCondition == VcbDismountInProgress)) {
|
||
|
||
if (ARGUMENT_PRESENT( IrpContext )) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_INVALID );
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Always fail if the volume needs to be verified.
|
||
//
|
||
|
||
if (UdfRealDevNeedsVerify( RealDevice)) {
|
||
|
||
if (ARGUMENT_PRESENT( IrpContext )) {
|
||
|
||
IoSetHardErrorOrVerifyDevice( IrpContext->Irp,
|
||
RealDevice );
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED );
|
||
}
|
||
|
||
return FALSE;
|
||
|
||
//
|
||
// All operations are allowed on mounted volumes.
|
||
//
|
||
|
||
} else if ((Vcb->VcbCondition == VcbMounted) ||
|
||
(Vcb->VcbCondition == VcbMountInProgress)) {
|
||
|
||
return TRUE;
|
||
|
||
//
|
||
// Fail all requests for fast Io on other Vcb conditions.
|
||
//
|
||
|
||
} else if (!ARGUMENT_PRESENT( IrpContext )) {
|
||
|
||
return FALSE;
|
||
|
||
//
|
||
// The remaining case is VcbNotMounted - raise WRONG_VOLUME.
|
||
//
|
||
|
||
} else if (Vcb->VcbCondition == VcbNotMounted) {
|
||
|
||
if (ARGUMENT_PRESENT( IrpContext )) {
|
||
|
||
IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice );
|
||
UdfRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|