Пример #1
0
NTSTATUS
CdPerformVerify (
    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;

    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 CdFsdPostRequest( 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;
    try {

        //
        //  Send down the verify FSCTL.  Note that this is sent to the
        //  currently mounted volume,  which may not be this one.
        //
        //  We will allow Raw to mount this volume if we were doing a
        //  an absolute DASD open.
        //

        Status = IoVerifyVolume( DeviceToVerify, CdOperationIsDasdOpen( IrpContext));

        //
        //  Acquire the Vcb so we're working with a stable VcbCondition.
        //

        CdAcquireVcbShared( IrpContext, Vcb, FALSE);

        //
        //  If the verify operation completed it will return
        //  either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly.
        //
        //  If CdVerifyVolume 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 <= CDFS_RESIDUAL_REFERENCE)) {

            CdReleaseVcb( IrpContext, Vcb);

            CdAcquireCdData( IrpContext );
            CdCheckForDismount( IrpContext, Vcb, FALSE );
            CdReleaseCdData( IrpContext );
        }
        else {

            CdReleaseVcb( 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;

            CdCompleteRequest( IrpContext, Irp, STATUS_REPARSE );
            Status = STATUS_REPARSE;
            Irp = NULL;
            IrpContext = NULL;

            //
            //  If there is still an error to process then call the Io system
            //  for a popup.
            //

        } else if ((Irp != NULL) && !NT_SUCCESS( Status )) {

            //
            //  Fill in the device object if required.
            //

            if (IoIsErrorUserInduced( Status ) ) {

                IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify );
            }

            CdNormalizeAndRaiseStatus( IrpContext, Status );
        }

        //
        //  If there is still an Irp, send it off to an Ex Worker thread.
        //

        if (IrpContext != NULL) {

            Status = CdFsdPostRequest( IrpContext, Irp );
        }

    }
    except(CdExceptionFilter( 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 = CdProcessException( IrpContext, Irp, GetExceptionCode() );
    }

    return Status;
}
Пример #2
0
NTSTATUS
CdProcessException (
    _In_opt_ PIRP_CONTEXT IrpContext,
    _Inout_ PIRP Irp,
    _In_ NTSTATUS ExceptionCode
    )

/*++

Routine Description:

    This routine processes an exception.  It either completes the request
    with the exception status in the IrpContext, sends this off to the Fsp
    workque or causes it to be retried in the current thread if a verification
    is needed.

    If the volume needs to be verified (STATUS_VERIFY_REQUIRED) and we can
    do the work in the current thread we will translate the status code
    to STATUS_CANT_WAIT to indicate that we need to retry the request.

Arguments:

    Irp - Supplies the Irp being processed

    ExceptionCode - Supplies the normalized exception status being handled

Return Value:

    NTSTATUS - Returns the results of either posting the Irp or the
        saved completion status.

--*/

{
    PDEVICE_OBJECT Device = NULL;
    PVPB Vpb;
    PETHREAD Thread;

    ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext );
    ASSERT_IRP( Irp );
    
    //
    //  If there is not an irp context, then complete the request with the
    //  current status code.
    //

    if (!ARGUMENT_PRESENT( IrpContext )) {

        CdCompleteRequest( NULL, Irp, ExceptionCode );
        return ExceptionCode;
    }

    //
    //  Get the real exception status from the IrpContext.
    //

    ExceptionCode = IrpContext->ExceptionStatus;

    //
    //  Check if we are posting this request.  One of the following must be true
    //  if we are to post a request.
    //
    //      - Status code is STATUS_CANT_WAIT and the request is asynchronous
    //          or we are forcing this to be posted.
    //
    //      - Status code is STATUS_VERIFY_REQUIRED and we are at APC level
    //          or higher, or within a guarded region.  Can't wait for IO in 
    //          the verify path in this case.
    //
    //  Set the MORE_PROCESSING flag in the IrpContext to keep if from being
    //  deleted if this is a retryable condition.
    //
    //
    //  Note that (children of) CdFsdPostRequest can raise (Mdl allocation).
    //

    try {

        if (ExceptionCode == STATUS_CANT_WAIT) {

            if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST )) {

                ExceptionCode = CdFsdPostRequest( IrpContext, Irp );
            }
        } 
        else if ((ExceptionCode == STATUS_VERIFY_REQUIRED) &&
                 FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL ) &&
                 KeAreAllApcsDisabled()) {
                 
            ExceptionCode = CdFsdPostRequest( IrpContext, Irp );
        }
    }
    except( CdExceptionFilter( IrpContext, GetExceptionInformation() ))  {
    
        ExceptionCode = GetExceptionCode();        
    }
    
    //
    //  If we posted the request or our caller will retry then just return here.
    //

    if ((ExceptionCode == STATUS_PENDING) ||
        (ExceptionCode == STATUS_CANT_WAIT)) {

        return ExceptionCode;
    }

    ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING );

    //
    //  If we are not a top level request then we just complete the request
    //  with the current status code.
    //

    if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL )) {

        CdCompleteRequest( IrpContext, Irp, ExceptionCode );
        return ExceptionCode;
    }

    //
    //  Store this error into the Irp for posting back to the Io system.
    //

    Irp->IoStatus.Status = ExceptionCode;

    if (IoIsErrorUserInduced( ExceptionCode )) {

        //
        //  Check for the various error conditions that can be caused by,
        //  and possibly resolved my the user.
        //

        if (ExceptionCode == STATUS_VERIFY_REQUIRED) {

            //
            //  Now we are at the top level file system entry point.
            //
            //  If we have already posted this request then the device to
            //  verify is in the original thread.  Find this via the Irp.
            //

            Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread );
            IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL );
            
            //
            //  If there is no device in that location then check in the
            //  current thread.
            //

            if (Device == NULL) {

                Device = IoGetDeviceToVerify( PsGetCurrentThread() );
                IoSetDeviceToVerify( PsGetCurrentThread(), NULL );

                NT_ASSERT( Device != NULL );

            }

            //
            //  It turns out some storage drivers really do set invalid non-NULL device 
            //  objects to verify.
            //
            //  To work around this, completely ignore the device to verify in the thread, 
            //  and just use our real device object instead.
            //

            if (IrpContext->Vcb) {

                Device = IrpContext->Vcb->Vpb->RealDevice;
            }

            //
            //  Let's not BugCheck just because the device to verify is somehow still NULL.
            //

            if (Device == NULL) {

                ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR;

                CdCompleteRequest( IrpContext, Irp, ExceptionCode );

                return ExceptionCode;
            }

            //
            //  CdPerformVerify() will do the right thing with the Irp.
            //  If we return STATUS_CANT_WAIT then the current thread
            //  can retry the request.
            //

            return CdPerformVerify( IrpContext, Irp, Device );
        }

        //
        //  The other user induced conditions generate an error unless
        //  they have been disabled for this request.
        //

        if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS )) {

            CdCompleteRequest( IrpContext, Irp, ExceptionCode );

            return ExceptionCode;

        } 
        //
        //  Generate a pop-up.
        //
        else {

            if (IoGetCurrentIrpStackLocation( Irp )->FileObject != NULL) {

                Vpb = IoGetCurrentIrpStackLocation( Irp )->FileObject->Vpb;

            } else {

                Vpb = NULL;
            }


            //
            //  The device to verify is either in my thread local storage
            //  or that of the thread that owns the Irp.
            //

            Thread = Irp->Tail.Overlay.Thread;
            Device = IoGetDeviceToVerify( Thread );

            if (Device == NULL) {

                Thread = PsGetCurrentThread();
                Device = IoGetDeviceToVerify( Thread );

                NT_ASSERT( Device != NULL );
            }

            //
            //  It turns out some storage drivers really do set invalid non-NULL device 
            //  objects to verify.
            //
            //  To work around this, completely ignore the device to verify in the thread, 
            //  and just use our real device object instead.
            //

            if (IrpContext->Vcb) {

                Device = IrpContext->Vcb->Vpb->RealDevice;
            }

            //
            //  Let's not BugCheck just because the device to verify is somehow still NULL.
            //

            if (Device == NULL) {

                CdCompleteRequest( IrpContext, Irp, ExceptionCode );

                return ExceptionCode;
            }

            //
            //  This routine actually causes the pop-up.  It usually
            //  does this by queuing an APC to the callers thread,
            //  but in some cases it will complete the request immediately,
            //  so it is very important to IoMarkIrpPending() first.
            //

            IoMarkIrpPending( Irp );
            IoRaiseHardError( Irp, Vpb, Device );

            //
            //  We will be handing control back to the caller here, so
            //  reset the saved device object.
            //

            IoSetDeviceToVerify( Thread, NULL );

            //
            //  The Irp will be completed by Io or resubmitted.  In either
            //  case we must clean up the IrpContext here.
            //

            CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
            return STATUS_PENDING;
        }
    }

    //
    //  This is just a run of the mill error.
    //

    CdCompleteRequest( IrpContext, Irp, ExceptionCode );

    return ExceptionCode;
}
Пример #3
0
NTSTATUS
CdCommonWrite (
    _Inout_ PIRP_CONTEXT IrpContext,
    _Inout_ PIRP Irp
    )

/*++

Routine Description:

    This is the common entry point for NtWriteFile calls.  For synchronous requests,
    CommonWrite will complete the request in the current thread.  If not
    synchronous the request will be passed to the Fsp if there is a need to
    block.

Arguments:

    Irp - Supplies the Irp to process

Return Value:

    NTSTATUS - The result of this operation.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );

    TYPE_OF_OPEN TypeOfOpen;
    PFCB Fcb;
    PCCB Ccb;

    BOOLEAN Wait;
    ULONG SynchronousIo;
    PVOID UserBuffer;

    LONGLONG StartingOffset;
    LONGLONG ByteRange;
    ULONG ByteCount;
    ULONG WriteByteCount;
    ULONG OriginalByteCount;

    BOOLEAN ReleaseFile = TRUE;

    CD_IO_CONTEXT LocalIoContext;

    PAGED_CODE();

    //
    //  If this is a zero length write then return SUCCESS immediately.
    //

    if (IrpSp->Parameters.Write.Length == 0) {

        CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
        return STATUS_SUCCESS;
    }

    //
    //  Decode the file object and verify we support write on this.  It
    //  must be a volume file.
    //

    TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb );

    // Internal lock object is acquired if return status is STATUS_PENDING
    _Analysis_suppress_lock_checking_(Fcb->Resource);

    if (TypeOfOpen != UserVolumeOpen) {

        CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
        return STATUS_INVALID_DEVICE_REQUEST;
    }

    //
    //  Examine our input parameters to determine if this is noncached and/or
    //  a paging io operation.
    //

    Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
    SynchronousIo = FlagOn( IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO );


    //
    //  Extract the range of the Io.
    //

    StartingOffset = IrpSp->Parameters.Write.ByteOffset.QuadPart;
    OriginalByteCount = ByteCount = IrpSp->Parameters.Write.Length;

    ByteRange = StartingOffset + ByteCount;

    //
    //  Acquire the file shared to perform the write.
    //

    CdAcquireFileShared( IrpContext, Fcb );

    //
    //  Use a try-finally to facilitate cleanup.
    //

    try {

        //
        //  Verify the Fcb.  Allow writes if this is a DASD handle that is 
        //  dismounting the volume.
        //

        if (!FlagOn( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE ))  {

            CdVerifyFcbOperation( IrpContext, Fcb );
        }

        if (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO )) {

            //
            //  Complete the request if it begins beyond the end of file.
            //

            if (StartingOffset >= Fcb->FileSize.QuadPart) {

                try_return( Status = STATUS_END_OF_FILE );
            }

            //
            //  Truncate the write if it extends beyond the end of the file.
            //

            if (ByteRange > Fcb->FileSize.QuadPart) {

                ByteCount = (ULONG) (Fcb->FileSize.QuadPart - StartingOffset);
                ByteRange = Fcb->FileSize.QuadPart;
            }
        }

        //
        //  If we have an unaligned transfer then post this request if
        //  we can't wait.  Unaligned means that the starting offset
        //  is not on a sector boundary or the write is not integral
        //  sectors.
        //

        WriteByteCount = BlockAlign( Fcb->Vcb, ByteCount );

        if (SectorOffset( StartingOffset ) ||
            SectorOffset( WriteByteCount ) ||
            (WriteByteCount > OriginalByteCount)) {

            if (!Wait) {

                CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
            }

            //
            //  Make sure we don't overwrite the buffer.
            //

            WriteByteCount = ByteCount;
        }

        //
        //  Initialize the IoContext for the write.
        //  If there is a context pointer, we need to make sure it was
        //  allocated and not a stale stack pointer.
        //

        if (IrpContext->IoContext == NULL ||
            !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) {

            //
            //  If we can wait, use the context on the stack.  Otherwise
            //  we need to allocate one.
            //

            if (Wait) {

                IrpContext->IoContext = &LocalIoContext;
                ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO );

            } else {

                IrpContext->IoContext = CdAllocateIoContext();
                SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO );
            }
        }

        RtlZeroMemory( IrpContext->IoContext, sizeof( CD_IO_CONTEXT ) );

        //
        //  Store whether we allocated this context structure in the structure
        //  itself.
        //

        IrpContext->IoContext->AllocatedContext =
            BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO );

        if (Wait) {

            KeInitializeEvent( &IrpContext->IoContext->SyncEvent,
                               NotificationEvent,
                               FALSE );

        } else {

            IrpContext->IoContext->ResourceThreadId = ExGetCurrentResourceThread();
            IrpContext->IoContext->Resource = Fcb->Resource;
            IrpContext->IoContext->RequestedByteCount = ByteCount;
        }

        Irp->IoStatus.Information = WriteByteCount;

        //
        //  Set the FO_MODIFIED flag here to trigger a verify when this
        //  handle is closed.  Note that we can err on the conservative
        //  side with no problem, i.e. if we accidently do an extra
        //  verify there is no problem.
        //

        SetFlag( IrpSp->FileObject->Flags, FO_FILE_MODIFIED );

        //
        //  Dasd access is always non-cached. Call the Dasd write routine to
        //  perform the actual write.
        //

        Status = CdVolumeDasdWrite( IrpContext, Fcb, StartingOffset, WriteByteCount );

        //
        //  Don't complete this request now if STATUS_PENDING was returned.
        //

        if (Status == STATUS_PENDING) {

            Irp = NULL;
            ReleaseFile = FALSE;

        //
        //  Test is we should zero part of the buffer or update the
        //  synchronous file position.
        //

        } else {

            //
            //  Convert any unknown error code to IO_ERROR.
            //

            if (!NT_SUCCESS( Status )) {

                //
                //  Set the information field to zero.
                //

                Irp->IoStatus.Information = 0;

                //
                //  Raise if this is a user induced error.
                //

                if (IoIsErrorUserInduced( Status )) {

                    CdRaiseStatus( IrpContext, Status );
                }

                Status = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR );

            //
            //  Check if there is any portion of the user's buffer to zero.
            //

            } else if (WriteByteCount != ByteCount) {

                CdMapUserBuffer( IrpContext, &UserBuffer );
                
                SafeZeroMemory( IrpContext,
                                Add2Ptr( UserBuffer,
                                         ByteCount,
                                         PVOID ),
                                WriteByteCount - ByteCount );

                Irp->IoStatus.Information = ByteCount;
            }

            //
            //  Update the file position if this is a synchronous request.
            //

            if (SynchronousIo && NT_SUCCESS( Status )) {

                IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange;
            }
        }

    try_exit:  NOTHING;
    } finally {

        //
        //  Release the Fcb.
        //

        if (ReleaseFile) {

            CdReleaseFile( IrpContext, Fcb );
        }
    }

    //
    //  Post the request if we got CANT_WAIT.
    //

    if (Status == STATUS_CANT_WAIT) {

        Status = CdFsdPostRequest( IrpContext, Irp );

    //
    //  Otherwise complete the request.
    //

    } else {

        CdCompleteRequest( IrpContext, Irp, Status );
    }

    return Status;
}