/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: shadow.c Abstract: This module contains the code that implements local read/write operations for shadow FCB Author: Ahmed Mohamed (ahmedm) 15-Dec-2001 Environment: Kernel mode Revision History: --*/ #include "precomp.h" #pragma hdrstop // // define this so that rdbsstrace flag works, just use the lowio one since shadow is part of it // #define Dbg (DEBUG_TRACE_LOWIO) #define RxGetShadowSrvOpenContext(SrvOpen) ((PMRXSHADOW_SRV_OPEN) (SrvOpen)->ShadowContext) #define RxShadowLockKeyLock(LowIoContext, ShadowCtx) (LowIoContext->ParamsFor.Locks.Key) #define RxShadowLockKey(LowIoContext, ShadowCtx) (LowIoContext->ParamsFor.ReadWrite.Key) typedef struct { PIRP Irp; BOOLEAN Cancelable; LONG Refcnt; } RX_SHADOW_CONTEXT, *PRX_SHADOW_CONTEXT; extern PETHREAD RxSpinUpRequestsThread; NTSTATUS RxShadowVerifyIoParameters( PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, PVOID Buffer, ULONG Length, PLARGE_INTEGER FileOffset ) { if (!FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) { return STATUS_SUCCESS; } // // The file was opened without intermediate buffering enabled. // Check that the Buffer is properly aligned, and that the // length is an integral number of the block size. // if ((DeviceObject->SectorSize && (Length & (DeviceObject->SectorSize - 1))) || ((ULONG_PTR)Buffer & DeviceObject->AlignmentRequirement)) { // // Check for sector sizes that are not a power of two. // if ((DeviceObject->SectorSize && (Length % DeviceObject->SectorSize)) || ((ULONG_PTR)Buffer & DeviceObject->AlignmentRequirement)) { return STATUS_INVALID_PARAMETER; } } // // If a ByteOffset parameter was specified, ensure that it is // is of the proper type. // if ((FileOffset->LowPart == FILE_WRITE_TO_END_OF_FILE) && (FileOffset->HighPart == -1)) { NOTHING; } else if ((FileOffset->LowPart == FILE_USE_FILE_POINTER_POSITION) && (FileOffset->HighPart == -1) && FlagOn( FileObject->Flags, FO_SYNCHRONOUS_IO )) { NOTHING; } else if (DeviceObject->SectorSize && (FileOffset->LowPart & (DeviceObject->SectorSize - 1))) { return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } NTSTATUS RxShadowBuildAsynchronousRequest ( IN PIRP OriginalIrp, IN PDEVICE_OBJECT DeviceObject, IN PFILE_OBJECT FileObject, IN PFCB Fcb, IN PRX_CONTEXT RxContext, IN PMRXSHADOW_SRV_OPEN LocalSrvOpen, IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL, IN PVOID Arg, OUT PIRP *Irp ) /*++ Routine Description: This routine builds an I/O Request Packet (IRP) suitable for a File System Driver (FSD) to use in requesting an I/O operation from a device driver. The request (RxContext->MajorFunction) must be one of the following request codes: IRP_MJ_READ IRP_MJ_WRITE IRP_MJ_DIRECTORY_CONTROL IRP_MJ_FLUSH_BUFFERS IRP_MJ_SHUTDOWN (not yet implemented) Arguments: RxContext - The RDBSS context. CompletionRoutine - The Irp CompletionRoutine. Return Value: The return status of the operation. --*/ { PIRP NewIrp; PIO_STACK_LOCATION IrpSp; ULONG MajorFunction = RxContext->MajorFunction; PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext; LONG Length; *Irp = NULL; if ((MajorFunction != IRP_MJ_READ) && (MajorFunction != IRP_MJ_WRITE) && (MajorFunction != IRP_MJ_LOCK_CONTROL)) { return STATUS_NOT_SUPPORTED; } IF_DEBUG { PFOBX Fobx = (PFOBX)RxContext->pFobx; ASSERT( Fobx != NULL ); ASSERT( Fobx->pSrvOpen == RxContext->pRelevantSrvOpen ); } NewIrp = IoAllocateIrp( DeviceObject->StackSize, FALSE ); if (!NewIrp) { return STATUS_INSUFFICIENT_RESOURCES; } // // Set current thread for IoSetHardErrorOrVerifyDevice. // NewIrp->Tail.Overlay.Thread = RxSpinUpRequestsThread; //PsGetCurrentThread(); NewIrp->Tail.Overlay.OriginalFileObject = FileObject; NewIrp->RequestorMode = KernelMode; NewIrp->AssociatedIrp.SystemBuffer = (PVOID)NULL; // // Get a pointer to the stack location of the first driver which will be // invoked. This is where the function codes and the parameters are set. // IrpSp = IoGetNextIrpStackLocation( NewIrp ); IrpSp->MajorFunction = (UCHAR) MajorFunction; IrpSp->MinorFunction = 0; IrpSp->FileObject = FileObject; IrpSp->DeviceObject = DeviceObject; if (CompletionRoutine != NULL) { IoSetCompletionRoutine( NewIrp, CompletionRoutine, Arg, TRUE, TRUE, TRUE ); } NewIrp->Flags = 0; SetFlag( NewIrp->Flags, FlagOn( OriginalIrp->Flags, IRP_SYNCHRONOUS_API | IRP_NOCACHE ) ); if (MajorFunction == IRP_MJ_LOCK_CONTROL) { // // We need to tag the lock flag // FileObject->LockOperation = TRUE; IrpSp->MinorFunction = RxContext->MinorFunction; IrpSp->Flags = (UCHAR)LowIoContext->ParamsFor.Locks.Flags; IrpSp->Parameters.LockControl.Length = (PLARGE_INTEGER)&LowIoContext->ParamsFor.Locks.Length; IrpSp->Parameters.LockControl.Key = RxShadowLockKeyLock( LowIoContext, LocalSrvOpen ); IrpSp->Parameters.LockControl.ByteOffset.QuadPart = LowIoContext->ParamsFor.Locks.ByteOffset; NewIrp->Tail.Overlay.AuxiliaryBuffer = OriginalIrp->Tail.Overlay.AuxiliaryBuffer; *Irp = NewIrp; return STATUS_SUCCESS; } // // if file is opened with no intermediate bufffering then set no cache flag // if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) { SetFlag( NewIrp->Flags, IRP_NOCACHE ); } Length = LowIoContext->ParamsFor.ReadWrite.ByteCount; if (MajorFunction == IRP_MJ_WRITE) { if (FlagOn( FileObject->Flags, FO_WRITE_THROUGH )) { IrpSp->Flags = SL_WRITE_THROUGH; } IrpSp->Parameters.Write.ByteOffset.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset; IrpSp->Parameters.Write.Length = Length; IrpSp->Parameters.Write.Key = RxShadowLockKey( LowIoContext, LocalSrvOpen ); } else { IrpSp->Parameters.Read.ByteOffset.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset; IrpSp->Parameters.Read.Length = Length; IrpSp->Parameters.Read.Key = RxShadowLockKey( LowIoContext, LocalSrvOpen ); } NewIrp->UserBuffer = OriginalIrp->UserBuffer; NewIrp->MdlAddress = RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer; if (NewIrp->MdlAddress != NULL) { NewIrp->UserBuffer = MmGetMdlVirtualAddress( NewIrp->MdlAddress ); if (FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP )) { // // we must map the mdl into system address space and use the system address instead // NewIrp->UserBuffer = MmGetSystemAddressForMdlSafe( NewIrp->MdlAddress, NormalPagePriority ); // // we need to zap out the mdl address, otherwise the filesystem complains that the // userbuffer and the mdl startva are not the same // NewIrp->MdlAddress = NULL; } } // // Finally, return a pointer to the IRP. // *Irp = NewIrp; return STATUS_SUCCESS; } NTSTATUS RxShadowCommonCompletion ( PRX_CONTEXT RxContext, PIRP Irp, NTSTATUS Status, ULONG_PTR Information ) { PRX_SHADOW_CONTEXT Context; BOOLEAN SynchronousIo = !BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ); // // Clear the MDL address from the IRP if it is a re-use of our own. Do this before completion // so we can successfully read the Buffer MDL from the LOWIO_CONTEXT // if ( (Irp->MdlAddress == RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer) && ( (RxContext->MajorFunction == IRP_MJ_READ) || (RxContext->MajorFunction == IRP_MJ_WRITE) ) ) { Irp->MdlAddress = NULL; } // // we need to synch with cancel // Context = (PRX_SHADOW_CONTEXT)RxContext->MRxContext; if (Context->Cancelable) { KIRQL SavedIrql; KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql ); Irp = Context->Irp; if (!FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_CANCELLED )) { RxContext->MRxCancelRoutine = NULL; Context->Irp = NULL; } else { LONG x; // // cancel thread must have a reference on the Irp so we don't free it now but // on the actual cancel call // x = InterlockedDecrement( &Context->Refcnt ); if (x > 0) { Irp = NULL; } else { // // we could have already got cancelled and we need to let the others we are done // Context->Irp = NULL; } } KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql ); } RxContext->StoredStatus = Status; RxContext->InformationToReturn += Information; if (SynchronousIo) { // // Signal the thread that is waiting after queuing the workitem on the // KQueue. // RxSignalSynchronousWaiter( RxContext ); } else { RxLowIoCompletion( RxContext ); } if (Irp != NULL) { if (Irp->MdlAddress) { PMDL mdl,nextMdl; for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) { nextMdl = mdl->Next; MmUnlockPages( mdl ); } for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) { nextMdl = mdl->Next; IoFreeMdl( mdl ); } Irp->MdlAddress = NULL; } // // We are done with this Irp, so free it. // IoFreeIrp( Irp ); } return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS RxShadowIrpCompletion ( IN PDEVICE_OBJECT DeviceObject, IN PIRP CalldownIrp OPTIONAL, IN PVOID Context ) /*++ Routine Description: This routine is called when the calldownIrp is completed. Arguments: DeviceObject - The device object in play. CalldownIrp - Context - Return Value: RXSTATUS - STATUS_MORE_PROCESSING_REQUIRED --*/ { PRX_CONTEXT RxContext = (PRX_CONTEXT)Context; RxShadowCommonCompletion( RxContext, CalldownIrp, CalldownIrp->IoStatus.Status, CalldownIrp->IoStatus.Information ); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS RxShadowCancelRoutine( PRX_CONTEXT RxContext ) { KIRQL SavedIrql; PIRP Irp; LONG x; PRX_SHADOW_CONTEXT Context; Irp = NULL; KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql ); Context = (RX_SHADOW_CONTEXT *) RxContext->MRxContext; Irp = Context->Irp; if (Irp != NULL) { // // io hasn't completed yet // InterlockedIncrement( &Context->Refcnt ); // // need to clear the Irp field // Context->Irp = NULL; } KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql ); if (Irp != NULL) { IoCancelIrp(Irp); x = InterlockedDecrement( &Context->Refcnt ); if (x == 0) { if (Irp->MdlAddress) { PMDL mdl,nextMdl; for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) { nextMdl = mdl->Next; MmUnlockPages( mdl ); } for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) { nextMdl = mdl->Next; IoFreeMdl( mdl ); } Irp->MdlAddress = NULL; } IoFreeIrp( Irp ); } } return STATUS_SUCCESS; } NTSTATUS RxShadowIoHandler ( IN PRX_CONTEXT RxContext, IN PIRP Irp, IN PFCB Fcb, IN BOOLEAN Cancelable ) /*++ Routine Description: This routine is common to guys who use the async context engine. It has the responsibility for getting a context, initing, starting and finalizing it, but the internal guts of the procesing is via the continuation routine that is passed in. Arguments: RxContext - The RDBSS context. Irp - The original irp Fcb - The fcb io is being done on Cancelable - Can the irp be cancelled Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status = STATUS_SUCCESS; PMRXSHADOW_SRV_OPEN LocalSrvOpen; PLOWIO_CONTEXT LowIoContext = &(RxContext->LowIoContext); PIRP TopIrp = NULL; BOOLEAN SynchronousIo; PIRP ShadowIrp = NULL; LocalSrvOpen = RxGetShadowSrvOpenContext( RxContext->pRelevantSrvOpen ); // // We are in, issue the I/O // SynchronousIo = !BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ); if (LocalSrvOpen->UnderlyingFileObject != NULL) { PDEVICE_OBJECT DeviceObject; PFILE_OBJECT FileObject; DeviceObject = LocalSrvOpen->UnderlyingDeviceObject; FileObject = LocalSrvOpen->UnderlyingFileObject; if (SynchronousIo) { KeInitializeEvent( &RxContext->SyncEvent, NotificationEvent, FALSE ); } Status = RxShadowBuildAsynchronousRequest( Irp, DeviceObject, FileObject, Fcb, RxContext, LocalSrvOpen, RxShadowIrpCompletion, (PVOID) RxContext, &ShadowIrp ); if (Status == STATUS_SUCCESS) { PRX_SHADOW_CONTEXT Context; Context = (PRX_SHADOW_CONTEXT)RxContext->MRxContext; ASSERT( sizeof( *Context ) <= sizeof( RxContext->MRxContext )); // // save new Irp if we want to resume later // Context->Irp = ShadowIrp; Context->Cancelable = Cancelable; Context->Refcnt = 1; try { // // Save the TopLevel Irp. // TopIrp = IoGetTopLevelIrp(); // // Tell the underlying guy he's all clear. // IoSetTopLevelIrp( NULL ); Status = IoCallDriver( DeviceObject, ShadowIrp ); } finally { // // Restore my context for unwind. // IoSetTopLevelIrp( TopIrp ); } if (Cancelable == TRUE) { KIRQL SavedIrql; TopIrp = NULL; KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql ); if ((Context->Irp != NULL) && !FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_CANCELLED )) { // // io is still pending and hasn't been cancelled // RxContext->MRxCancelRoutine = RxShadowCancelRoutine; } else if (Context->Irp != NULL) { // // io is already cancelled // TopIrp = Context->Irp; // // we need to clear the Irp field // Context->Irp = NULL; // // we need to take an extra reference // InterlockedIncrement( &Context->Refcnt ); } KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql ); if (TopIrp != NULL) { LONG x; IoCancelIrp( TopIrp ); x = InterlockedDecrement( &Context->Refcnt ); if (x == 0) { if (TopIrp->MdlAddress) { TopIrp->MdlAddress = NULL; } IoFreeIrp( TopIrp ); } } } if (SynchronousIo) { RxWaitSync( RxContext ); Status = RxContext->StoredStatus; } else { Status = STATUS_PENDING; } } } else { Status = STATUS_VOLUME_DISMOUNTED; } return Status; } NTSTATUS RxShadowFastLowIo ( IN PRX_CONTEXT RxContext, IN PIRP Irp ) /*++ Routine Description: This routine handles network read requests. Arguments: RxContext - the RDBSS context Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED; PMRXSHADOW_SRV_OPEN MrxShadowSrvOpen; PLOWIO_CONTEXT LowIoContext = &(RxContext->LowIoContext); PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); LARGE_INTEGER Offset; BOOLEAN Wait, PagingIo; IO_STATUS_BLOCK Ios; PVOID Buffer; PIRP TopIrp; // // we only support read and write // if ((LowIoContext->Operation != LOWIO_OP_READ) && (LowIoContext->Operation != LOWIO_OP_WRITE)) { return Status; } // // don't deal with name pipes // if (FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_OPERATION )) { return Status; } // // check for locks and take default path // if (IrpSp->FileObject && IrpSp->FileObject->LockOperation) { return Status; } PagingIo = BooleanFlagOn( LowIoContext->ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_PAGING_IO ); MrxShadowSrvOpen = RxGetShadowSrvOpenContext( RxContext->pRelevantSrvOpen ); // // The only time we can get PagingIo write on a loopback file is if the // file has been memory mapped. // // // We don't handle PagingIo read and no-buffering handles through fast // path. PagingIo write is tried through the fast path and if it does // not succeed then we return STATUS_MORE_PROCESSING_REQUIRED. // if ((PagingIo && LowIoContext->Operation == LOWIO_OP_READ) || (MrxShadowSrvOpen == NULL) || (MrxShadowSrvOpen->UnderlyingFileObject == NULL ) || (IrpSp->FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)) { return Status; } Offset.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset; // // get user buffer // if (!FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP )) { Buffer = Irp->UserBuffer; } else { ASSERT( LowIoContext->ParamsFor.ReadWrite.Buffer != NULL ); Buffer = RxLowIoGetBufferAddress( RxContext ); } // // Check shadow state and io params. // if (RxShadowVerifyIoParameters( MrxShadowSrvOpen->UnderlyingDeviceObject, IrpSp->FileObject, Buffer, LowIoContext->ParamsFor.ReadWrite.ByteCount, &Offset) != STATUS_SUCCESS) { return Status; } Wait = !BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION ); if (PagingIo) { ASSERT(LowIoContext->Operation == LOWIO_OP_WRITE); Wait = FALSE; } // // Save the TopLevel Irp. // TopIrp = IoGetTopLevelIrp(); // // Tell the underlying guy he's all clear. // IoSetTopLevelIrp( NULL ); try { if ((LowIoContext->Operation == LOWIO_OP_READ) && (MrxShadowSrvOpen->FastIoRead != NULL) && MrxShadowSrvOpen->FastIoRead(MrxShadowSrvOpen->UnderlyingFileObject, &Offset, LowIoContext->ParamsFor.ReadWrite.ByteCount, Wait, RxShadowLockKey( LowIoContext, MrxShadowSrvOpen ), Buffer, &Ios, MrxShadowSrvOpen->UnderlyingDeviceObject )) { // // the fast io path worked // Irp->IoStatus = Ios; RxContext->StoredStatus = Ios.Status; RxContext->InformationToReturn += Ios.Information; Status = Ios.Status; } else if ((LowIoContext->Operation == LOWIO_OP_WRITE) && (MrxShadowSrvOpen->FastIoWrite != NULL) && MrxShadowSrvOpen->FastIoWrite(MrxShadowSrvOpen->UnderlyingFileObject, &Offset, LowIoContext->ParamsFor.ReadWrite.ByteCount, Wait, RxShadowLockKey(LowIoContext, MrxShadowSrvOpen), Buffer, &Ios, MrxShadowSrvOpen->UnderlyingDeviceObject )) { // // The fast io path worked. // Irp->IoStatus = Ios; RxContext->StoredStatus = Ios.Status; RxContext->InformationToReturn += Ios.Information; Status = Ios.Status; } } except( EXCEPTION_EXECUTE_HANDLER ) { // // TODO: Should we fall through to the slow path on an exception? // Status = GetExceptionCode(); } if (Status != STATUS_SUCCESS && PagingIo) { ASSERT(LowIoContext->Operation == LOWIO_OP_WRITE); Status = STATUS_MORE_PROCESSING_REQUIRED; } // // Restore my context for unwind. // IoSetTopLevelIrp( TopIrp ); return Status; } NTSTATUS RxShadowLowIo ( IN PRX_CONTEXT RxContext, IN PIRP Irp, IN PFCB Fcb ) /*++ Routine Description: This routine handles network read requests. Arguments: RxContext - the RDBSS context Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED; PMRXSHADOW_SRV_OPEN MrxShadowSrvOpen; PLOWIO_CONTEXT LowIoContext = &(RxContext->LowIoContext); PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp ); LARGE_INTEGER Offset; PVOID Buffer; // // we only support read and write and lock // if ((LowIoContext->Operation != LOWIO_OP_READ) && (LowIoContext->Operation != LOWIO_OP_WRITE) && (RxContext->MajorFunction != IRP_MJ_LOCK_CONTROL)) { return Status; } // // don't deal with name pipes // if (FlagOn( RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_OPERATION )) { return Status; } MrxShadowSrvOpen = RxGetShadowSrvOpenContext( RxContext->pRelevantSrvOpen ); if ((MrxShadowSrvOpen == NULL) || (MrxShadowSrvOpen->UnderlyingFileObject == NULL)) { return Status; } // // if min-rdr wants to handle shadow io then pass call down // if (MrxShadowSrvOpen->DispatchRoutine) { return MrxShadowSrvOpen->DispatchRoutine( RxContext ); } if ((LowIoContext->Operation == LOWIO_OP_READ) || (LowIoContext->Operation == LOWIO_OP_WRITE)) { Offset.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset; // // if we have no mdl then use the user buffer directly // if (!LowIoContext->ParamsFor.ReadWrite.Buffer) { Buffer = Irp->UserBuffer; } else { Buffer = RxLowIoGetBufferAddress( RxContext ); } // // check shadow state and io params // Status = RxShadowVerifyIoParameters( MrxShadowSrvOpen->UnderlyingDeviceObject, IrpSp->FileObject, Buffer, LowIoContext->ParamsFor.ReadWrite.ByteCount, &Offset ); if (Status != STATUS_SUCCESS) { // // don't return status more processing required here in order to enforce proper // alignment. Note, if the user has the file locked the server can't help it anyway. // return Status; } } Status = RxShadowIoHandler( RxContext, Irp, Fcb, RxContext->MajorFunction == IRP_MJ_LOCK_CONTROL ? TRUE : FALSE ); return Status; }