/****************************************************************************/ // io.c // // Kernel file I/O utility functions. // // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include #include // // External references // VOID IoQueueThreadIrp(IN PIRP); /*============================================================================= == Internal Functions Defined =============================================================================*/ NTSTATUS _CtxDoFileIo( IN ULONG MajorFunction, IN PFILE_OBJECT fileObject, IN PVOID Buffer, IN ULONG Length, IN PKEVENT pEvent, OUT PIO_STATUS_BLOCK pIosb, OUT PIRP *ppIrp ); NTSTATUS _CtxDeviceControlComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); /******************************************************************************* * CtxReadFile * * Kernel read file routine. * * ENTRY: * fileObject (input) * pointer to file object for I/O * Buffer (input) * pointer to read buffer * Length (input) * length of read buffer * pEvent (input) * pointer to I/O event (optional) * pIosb (output) * pointer to IoStatus block (optional) ******************************************************************************/ NTSTATUS CtxReadFile( IN PFILE_OBJECT fileObject, IN PVOID Buffer, IN ULONG Length, IN PKEVENT pEvent OPTIONAL, OUT PIO_STATUS_BLOCK pIosb OPTIONAL, OUT PIRP *ppIrp OPTIONAL) { return _CtxDoFileIo(IRP_MJ_READ, fileObject, Buffer, Length, pEvent, pIosb, ppIrp); } /******************************************************************************* * CtxWriteFile * * Kernel write file routine. * * ENTRY: * fileObject (input) * pointer to file object for I/O * Buffer (input) * pointer to write buffer * Length (input) * length of write buffer * pEvent (input) * pointer to I/O event (optional) * pIosb (output) * pointer to IoStatus block (optional) ******************************************************************************/ NTSTATUS CtxWriteFile( IN PFILE_OBJECT fileObject, IN PVOID Buffer, IN ULONG Length, IN PKEVENT pEvent OPTIONAL, OUT PIO_STATUS_BLOCK pIosb OPTIONAL, OUT PIRP *ppIrp OPTIONAL) { return _CtxDoFileIo(IRP_MJ_WRITE, fileObject, Buffer, Length, pEvent, pIosb, ppIrp); } NTSTATUS _CtxDoFileIo( IN ULONG MajorFunction, IN PFILE_OBJECT fileObject, IN PVOID Buffer, IN ULONG Length, IN PKEVENT pEvent, OUT PIO_STATUS_BLOCK pIosb, OUT PIRP *ppIrp) { PDEVICE_OBJECT deviceObject; LARGE_INTEGER Offset; PIRP irp; PIO_STACK_LOCATION irpSp; NTSTATUS status; KIRQL irql; static IO_STATUS_BLOCK Iosb; /* * We don't support synchronous (i.e. locked) file I/O. */ ASSERT( !(fileObject->Flags & FO_SYNCHRONOUS_IO) ); if ((fileObject->Flags & FO_SYNCHRONOUS_IO)) { return STATUS_INVALID_PARAMETER_MIX; } /* * If caller specified an event, clear it before we begin. */ if (pEvent) { KeClearEvent(pEvent); } /* * If the caller does not supply an IOSB, supply * a static one to avoid the overhead of the exception * handler in the IO completion APC. Since the caller(s) * do not care about the result, we can point all such * callers to the same one. */ if (pIosb == NULL) { pIosb = &Iosb; } /* * Get the DeviceObject for this file */ deviceObject = IoGetRelatedDeviceObject(fileObject); /* * Build the IRP for this request */ Offset.LowPart = FILE_WRITE_TO_END_OF_FILE; Offset.HighPart = -1; irp = IoBuildAsynchronousFsdRequest(MajorFunction, deviceObject, Buffer, Length, &Offset, pIosb); if (irp == NULL) return STATUS_INSUFFICIENT_RESOURCES; /* * Save callers event pointer. * Also, we must set IRP_SYNCHRONOUS_API in the IRP flags so that * the I/O completion code will NOT attempt to dereference the * event object, since it is not a real object manager object. */ irp->UserEvent = pEvent; irp->Flags |= IRP_SYNCHRONOUS_API; /* * Reference the file object since it will be dereferenced in the * I/O completion code, and save a pointer to it in the IRP. */ ObReferenceObject(fileObject); irp->Tail.Overlay.OriginalFileObject = fileObject; irpSp = IoGetNextIrpStackLocation(irp); irpSp->FileObject = fileObject; /* * Set the address of the current thread in the packet so the * completion code will have a context to execute in. */ irp->Tail.Overlay.Thread = PsGetCurrentThread(); // // Queue the IRP to the current thread // IoQueueThreadIrp(irp); // // Call driver // status = IoCallDriver( deviceObject, irp ); // // If irp->UserEvent == NULL, IO completion will set the file // object event and status. // if (pEvent == NULL) { if (status == STATUS_PENDING) { status = KeWaitForSingleObject(&fileObject->Event, Executive, KernelMode, // Prevent KSTACK from paging FALSE, // Non-alertable (PLARGE_INTEGER)NULL); ASSERT(status != STATUS_ALERTED); ASSERT(status != STATUS_USER_APC); status = fileObject->FinalStatus; } } // This crappy function interface is broken -- returning the IRP // pointer could corrupt the system, since it could be completed // and deallocated before the return completes and the caller // attempts to use the pointer. To get the IRP back the caller // would have had to set a completion routine, but we use // IoBuildAsynchronousFsdRequest() which does not allow that. // Want to change the interface to get rid of the OPTIONAL // junk -- TermDD only uses the write() interface and always // passes NULL for everything -- but who knows whether Citrix // uses this internally? // Set the return pointer to NULL to cause the caller to fault. if (pEvent != NULL && ppIrp != NULL) *ppIrp = NULL; return status; } /******************************************************************************* * CtxDeviceIoControlFile * * Kernel DeviceIoControl routine * * ENTRY: * fileObject (input) * pointer to file object for I/O * IoControlCode (input) * Io control code * InputBuffer (input) * pointer to input buffer (optional) * InputBufferLength (input) * length of input buffer * OutputBuffer (input) * pointer to output buffer (optional) * OutputBufferLength (input) * length of output buffer * InternalDeviceIoControl (input) * if TRUE, use IOCTL_INTERNAL_DEVICE_IO_CONTROL * pEvent (input) * pointer to I/O event (optional) * pIosb (output) * pointer to IoStatus block (optional) ******************************************************************************/ NTSTATUS CtxDeviceIoControlFile( IN PFILE_OBJECT fileObject, IN ULONG IoControlCode, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer OPTIONAL, IN ULONG OutputBufferLength, IN BOOLEAN InternalDeviceIoControl, IN PKEVENT pEvent OPTIONAL, OUT PIO_STATUS_BLOCK pIosb, OUT PIRP *ppIrp OPTIONAL) { PDEVICE_OBJECT deviceObject; PIRP irp; PIO_STACK_LOCATION irpSp; NTSTATUS status; /* * We don't support synchronous (i.e. locked) file I/O. */ ASSERT( !(fileObject->Flags & FO_SYNCHRONOUS_IO) ); if ( (fileObject->Flags & FO_SYNCHRONOUS_IO) ) { return( STATUS_INVALID_PARAMETER_MIX ); } /* * If caller specified an event, clear it before we begin. */ if (pEvent) { KeClearEvent(pEvent); } /* * Get the DeviceObject for this file */ deviceObject = IoGetRelatedDeviceObject( fileObject ); /* * Build the IRP for this request */ irp = IoBuildDeviceIoControlRequest( IoControlCode, deviceObject, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength, InternalDeviceIoControl, pEvent, pIosb ); if ( irp == NULL ) return( STATUS_INSUFFICIENT_RESOURCES ); /* * Reference the file object since it will be dereferenced in the * I/O completion code, and save a pointer to it in the IRP. * Also, we must set IRP_SYNCHRONOUS_API in the IRP flags so that * the I/O completion code will NOT attempt to dereference the * event object, since it is not a real object manager object. */ ObReferenceObject( fileObject ); irp->Tail.Overlay.OriginalFileObject = fileObject; irpSp = IoGetNextIrpStackLocation( irp ); irpSp->FileObject = fileObject; irp->Flags |= IRP_SYNCHRONOUS_API; /* * Call the driver */ status = IoCallDriver(deviceObject, irp); /* * If the caller did not specify a wait event and the I/O is pending, * then we must wait for the I/O to complete before we return. */ if (pEvent == NULL) { if (status == STATUS_PENDING) { status = KeWaitForSingleObject(&fileObject->Event, UserRequest, KernelMode, FALSE, NULL); if (status == STATUS_SUCCESS) status = fileObject->FinalStatus; } /* * Caller specified a wait event. * Return the Irp pointer if the caller specified a return pointer. */ } else { if (ppIrp) *ppIrp = irp; } return status; } /******************************************************************************* * CtxInternalDeviceIoControlFile * * Kernel DeviceIoControl routine * * ENTRY: * fileObject (input) * pointer to file object for I/O * IrpParameters (input) * information to write to the parameters section of the * stack location of the IRP. * IrpParametersLength (input) * length of the parameter information. Cannot be greater than 16. * MdlBuffer (input) * if non-NULL, a buffer of nonpaged pool to be mapped * into an MDL and placed in the MdlAddress field of the IRP. * MdlBufferLength (input) * the size of the buffer pointed to by MdlBuffer. * MinorFunction (input) * the minor function code for the request. * pEvent (input) * pointer to I/O event (optional) * pIosb (output) * pointer to IoStatus block (optional) ******************************************************************************/ NTSTATUS CtxInternalDeviceIoControlFile( IN PFILE_OBJECT fileObject, IN PVOID IrpParameters, IN ULONG IrpParametersLength, IN PVOID MdlBuffer OPTIONAL, IN ULONG MdlBufferLength, IN UCHAR MinorFunction, IN PKEVENT pEvent OPTIONAL, OUT PIO_STATUS_BLOCK pIosb OPTIONAL, OUT PIRP *ppIrp OPTIONAL) { PDEVICE_OBJECT deviceObject; PIRP irp; PIO_STACK_LOCATION irpSp; PMDL mdl; NTSTATUS status; /* * We don't support synchronous (i.e. locked) file I/O. */ ASSERT( !(fileObject->Flags & FO_SYNCHRONOUS_IO) ); if ( (fileObject->Flags & FO_SYNCHRONOUS_IO) ) { return( STATUS_INVALID_PARAMETER_MIX ); } /* * If caller specified an event, clear it before we begin. */ if ( pEvent ) { KeClearEvent( pEvent ); } /* * Get the DeviceObject for this file */ deviceObject = IoGetRelatedDeviceObject( fileObject ); /* * Build the IRP for this request */ irp = IoBuildDeviceIoControlRequest( 0, deviceObject, NULL, 0, NULL, 0, TRUE, pEvent, pIosb ); if ( irp == NULL ) return( STATUS_INSUFFICIENT_RESOURCES ); /* * If an MDL buffer was specified, get an MDL, map the buffer, * and place the MDL pointer in the IRP. */ if ( MdlBuffer != NULL ) { mdl = IoAllocateMdl( MdlBuffer, MdlBufferLength, FALSE, FALSE, irp ); if ( mdl == NULL ) { IoFreeIrp( irp ); ObDereferenceObject( fileObject ); return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool( mdl ); } else { irp->MdlAddress = NULL; } /* * Reference the file object since it will be dereferenced in the * I/O completion code, and save a pointer to it in the IRP. * Also, we must set IRP_SYNCHRONOUS_API in the IRP flags so that * the I/O completion code will NOT attempt to dereference the * event object, since it is not a real object manager object. */ ObReferenceObject( fileObject ); irp->Tail.Overlay.OriginalFileObject = fileObject; irpSp = IoGetNextIrpStackLocation( irp ); irpSp->FileObject = fileObject; irp->Flags |= IRP_SYNCHRONOUS_API; /* * Fill in the service-dependent parameters for the request. */ irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; irpSp->MinorFunction = MinorFunction; ASSERT( IrpParametersLength <= sizeof(irpSp->Parameters) ); RtlCopyMemory( &irpSp->Parameters, IrpParameters, IrpParametersLength ); /* * Set up a completion routine which we'll use to free the MDL * allocated previously. */ IoSetCompletionRoutine( irp, _CtxDeviceControlComplete, NULL, TRUE, TRUE, TRUE ); /* * Call the driver */ status = IoCallDriver( deviceObject, irp ); /* * If the caller did not specify a wait event and the I/O is pending, * then we must wait for the I/O to complete before we return. */ if ( pEvent == NULL ) { if ( status == STATUS_PENDING ) { status = KeWaitForSingleObject( &fileObject->Event, UserRequest, KernelMode, FALSE, NULL ); if ( status == STATUS_SUCCESS ) status = fileObject->FinalStatus; } /* * Caller specified a wait event. * Return the Irp pointer if the caller specified a return pointer. */ } else { if ( ppIrp ) *ppIrp = irp; } return( status ); } NTSTATUS _CtxDeviceControlComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { // // If there was an MDL in the IRP, free it and reset the pointer to // NULL. The IO system can't handle a nonpaged pool MDL being freed // in an IRP, which is why we do it here. // if ( Irp->MdlAddress != NULL ) { IoFreeMdl( Irp->MdlAddress ); Irp->MdlAddress = NULL; } return( STATUS_SUCCESS ); }