/*++ Copyright (c) 1999 Microsoft Corporation Module Name: InnerIo.c Abstract: This module implements the read, write, and lockctrl routines for the reflector. Author: Revision History: --*/ #include "precomp.h" #pragma hdrstop #include "webdav.h" typedef struct _DAV_IRPCOMPLETION_CONTEXT { KEVENT Event; } DAV_IRPCOMPLETION_CONTEXT, *PDAV_IRPCOMPLETION_CONTEXT; NTSTATUS DavIrpCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP CalldownIrp, IN PVOID Context ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, MRxDAVBuildAsynchronousRequest) #pragma alloc_text(PAGE, DavXxxInformation) #endif // // Implementation of functions begins here. // NTSTATUS DavIrpCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP CalldownIrp, IN PVOID Context ) /*++ Routine Description: This routine is called when the calldownirp is completed. Arguments: DeviceObject - The DAV Device Object. CalldownIrp - The IRP that was sent down. Context - The callback context. Return Value: RXSTATUS - STATUS_MORE_PROCESSING_REQUIRED --*/ { PDAV_IRPCOMPLETION_CONTEXT IrpCompletionContext = NULL; // // This is not Pageable code. // IrpCompletionContext = (PDAV_IRPCOMPLETION_CONTEXT)Context; if (CalldownIrp->PendingReturned){ KeSetEvent( &IrpCompletionContext->Event, 0, FALSE ); } return(STATUS_MORE_PROCESSING_REQUIRED); } NTSTATUS MRxDAVBuildAsynchronousRequest( IN PRX_CONTEXT RxContext, IN PIO_COMPLETION_ROUTINE CompletionRoutine OPTIONAL ) /*++ 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 irp; PIO_STACK_LOCATION irpSp; ULONG MajorFunction = RxContext->MajorFunction; RxCaptureFcb; PLOWIO_CONTEXT LowIoContext = &(RxContext->LowIoContext); PUMRX_ASYNCENGINE_CONTEXT AsyncEngineContext; PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen; PWEBDAV_SRV_OPEN davSrvOpen = MRxDAVGetSrvOpenExtension(SrvOpen); PDEVICE_OBJECT DeviceObject = davSrvOpen->UnderlyingDeviceObject; PFILE_OBJECT FileObject = davSrvOpen->UnderlyingFileObject; LARGE_INTEGER ZeroAsLI; ULONG MdlLength = 0; PAGED_CODE(); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Entering MRxDAVBuildAsynchronousRequest!!!!\n", PsGetCurrentThreadId())); DavDbgTrace(DAV_TRACE_CONTEXT, ("%ld: MRxDAVBuildAsynchronousRequest: RxContext: %08lx.\n", PsGetCurrentThreadId(), RxContext)); AsyncEngineContext = (PUMRX_ASYNCENGINE_CONTEXT)RxContext->MRxContext[0]; IF_DEBUG { RxCaptureFobx; ASSERT (capFobx != NULL); ASSERT (capFobx->pSrvOpen == RxContext->pRelevantSrvOpen); } ASSERT (davSrvOpen->UnderlyingFileObject); if (DeviceObject->Flags & DO_BUFFERED_IO) { // // I cannot handled buffered_I/O devices. Sigh. // DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVBuildAsynchronousRequest: Buffered IO.\n", PsGetCurrentThreadId())); return STATUS_INVALID_DEVICE_REQUEST; } DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVBuildAsynchronousRequest: Length = %08lx, Offset " "= %08lx.\n", PsGetCurrentThreadId(), LowIoContext->ParamsFor.ReadWrite.ByteCount, (ULONG)LowIoContext->ParamsFor.ReadWrite.ByteOffset)); ZeroAsLI.QuadPart = 0; irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); if (!irp) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVBuildAsynchronousRequest/IoAllocateIrp.\n", PsGetCurrentThreadId())); return STATUS_INSUFFICIENT_RESOURCES; } // // Set current thread for IoSetHardErrorOrVerifyDevice. // irp->Tail.Overlay.Thread = PsGetCurrentThread(); // // 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(irp); irpSp->MajorFunction = (UCHAR) MajorFunction; irpSp->FileObject = FileObject; { BOOLEAN EnableCalls = CompletionRoutine != NULL; IoSetCompletionRoutine(irp, CompletionRoutine, RxContext, EnableCalls, EnableCalls, EnableCalls); } if ( (MajorFunction == IRP_MJ_READ) || (MajorFunction == IRP_MJ_WRITE) ) { BOOLEAN PagingIo; PagingIo = BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_PAGING_IO); if (PagingIo) { DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVBuildAsynchronousRequest: Paging IO.\n", PsGetCurrentThreadId())); } // // For now, never paging I/O. // PagingIo = FALSE; // irp->Flags |= IRP_NOCACHE; // // Set the parameters according to whether this is a read or a write // operation. Notice that these parameters must be set even if the // driver has not specified buffered or direct I/O. // ASSERT (&irpSp->Parameters.Write.Key == &irpSp->Parameters.Read.Key); ASSERT (&irpSp->Parameters.Write.Length == &irpSp->Parameters.Read.Length); ASSERT (&irpSp->Parameters.Write.ByteOffset == &irpSp->Parameters.Read.ByteOffset); irpSp->Parameters.Read.Key = LowIoContext->ParamsFor.ReadWrite.Key; irpSp->Parameters.Read.ByteOffset.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset; irp->RequestorMode = KernelMode; irp->UserBuffer = RxLowIoGetBufferAddress(RxContext); MdlLength = RxContext->CurrentIrp->MdlAddress->ByteCount; if (PagingIo) { DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVBuildAsynchronousRequest: MdlLength = %08lx.\n", PsGetCurrentThreadId(), MdlLength)); irpSp->Parameters.Read.Length = MdlLength; } else { irpSp->Parameters.Read.Length = LowIoContext->ParamsFor.ReadWrite.ByteCount; } } else if (MajorFunction == IRP_MJ_FLUSH_BUFFERS) { // // Nothing else to do!!! // MdlLength = 0; } else { FILE_INFORMATION_CLASS FileInformationClass = RxContext->Info.FileInformationClass; PVOID Buffer = RxContext->Info.Buffer; PULONG pLengthRemaining = &RxContext->Info.LengthRemaining; BOOLEAN Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT); ASSERT( MajorFunction == IRP_MJ_DIRECTORY_CONTROL ); irpSp->MinorFunction = IRP_MN_QUERY_DIRECTORY; irpSp->Parameters.QueryDirectory = RxContext->CurrentIrpSp->Parameters.QueryDirectory; ASSERT ( (irpSp->Parameters.QueryDirectory.FileInformationClass == FileInformationClass) && (irpSp->Parameters.QueryDirectory.Length == *pLengthRemaining) ); irpSp->Flags = RxContext->CurrentIrpSp->Flags; irp->UserBuffer = Buffer; MdlLength = *pLengthRemaining; if (Wait) { irp->Flags |= IRP_SYNCHRONOUS_API; } } // // Build an MDL if necessary. // if (MdlLength != 0) { irp->MdlAddress = IoAllocateMdl(irp->UserBuffer, MdlLength, FALSE, FALSE, NULL); if (!irp->MdlAddress) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVBuildAsynchronousRequest/IoAllocateMdl.\n", PsGetCurrentThreadId())); IoFreeIrp(irp); return(STATUS_INSUFFICIENT_RESOURCES); } MmBuildMdlForNonPagedPool(irp->MdlAddress); } // // Finally, return a pointer to the IRP. // AsyncEngineContext->CalldownIrp = irp; return STATUS_SUCCESS; } NTSTATUS DavXxxInformation( IN const int xMajorFunction, IN PFILE_OBJECT FileObject, IN ULONG InformationClass, IN ULONG Length, OUT PVOID Information, OUT PULONG ReturnedLength ) /*++ Routine Description: This routine returns the requested information about a specified file or volume. The information returned is determined by the class that is specified, and it is placed into the caller's output buffer. Arguments: pFileObject - Supplies a pointer to the file object about which the requested information is returned. FsInformationClass - Specifies the type of information which should be returned about the file/volume. Length - Supplies the length of the buffer in bytes. FsInformation - Supplies a buffer to receive the requested information returned about the file. This buffer must not be pageable and must reside in system space. ReturnedLength - Supplies a variable that is to receive the length of the information written to the buffer. FileInformation - Boolean that indicates whether the information requested is for a file or a volume. Return Value: The status returned is the final completion status of the operation. --*/ { NTSTATUS Status; PIRP irp,TopIrp; PIO_STACK_LOCATION irpSp; PDEVICE_OBJECT DeviceObject; DAV_IRPCOMPLETION_CONTEXT IrpCompletionContext; ULONG DummyReturnedLength; PAGED_CODE(); if (ReturnedLength == NULL) { ReturnedLength = &DummyReturnedLength; } DeviceObject = IoGetRelatedDeviceObject( FileObject ); // // Allocate and initialize the I/O Request Packet (IRP) for this operation. // The allocation is performed with an exception handler in case the // caller does not have enough quota to allocate the packet. // irp = IoAllocateIrp( DeviceObject->StackSize, TRUE ); if (!irp) { return STATUS_INSUFFICIENT_RESOURCES; } irp->Tail.Overlay.OriginalFileObject = FileObject; irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->RequestorMode = KernelMode; // // Get a pointer to the stack location for the first driver. This will be // used to pass the original function codes and parameters. // irpSp = IoGetNextIrpStackLocation( irp ); irpSp->MajorFunction = (UCHAR)xMajorFunction; irpSp->FileObject = FileObject; IoSetCompletionRoutine(irp, DavIrpCompletionRoutine, &IrpCompletionContext, TRUE,TRUE,TRUE); //call no matter what.... irp->AssociatedIrp.SystemBuffer = Information; // // Copy the caller's parameters to the service-specific portion of the // IRP. // IF_DEBUG { ASSERT( (irpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) || (irpSp->MajorFunction == IRP_MJ_SET_INFORMATION) || (irpSp->MajorFunction == IRP_MJ_QUERY_VOLUME_INFORMATION) ); if (irpSp->MajorFunction == IRP_MJ_SET_INFORMATION) { ASSERT( (InformationClass == FileAllocationInformation) || (InformationClass == FileEndOfFileInformation) ); } ASSERT(&irpSp->Parameters.QueryFile.Length == &irpSp->Parameters.SetFile.Length); ASSERT(&irpSp->Parameters.QueryFile.Length == &irpSp->Parameters.QueryVolume.Length); ASSERT(&irpSp->Parameters.QueryFile.FileInformationClass == &irpSp->Parameters.SetFile.FileInformationClass); ASSERT((PVOID)&irpSp->Parameters.QueryFile.FileInformationClass == (PVOID)&irpSp->Parameters.QueryVolume.FsInformationClass); } irpSp->Parameters.QueryFile.Length = Length; irpSp->Parameters.QueryFile.FileInformationClass = InformationClass; // // Now simply invoke the driver at its dispatch entry with the IRP. // KeInitializeEvent(&IrpCompletionContext.Event, NotificationEvent, FALSE ); try { TopIrp = IoGetTopLevelIrp(); IoSetTopLevelIrp(NULL); //tell the underlying guy he's all clear Status = IoCallDriver(DeviceObject,irp); } finally { IoSetTopLevelIrp(TopIrp); //restore my context for unwind } if (Status == (STATUS_PENDING)) { KeWaitForSingleObject( &IrpCompletionContext.Event, Executive, KernelMode, FALSE, NULL ); Status = irp->IoStatus.Status; } if (Status == STATUS_SUCCESS) { *ReturnedLength = (ULONG)irp->IoStatus.Information; } IoFreeIrp(irp); return(Status); }