/************************************************************************* * * io.c * * Functions to perform kernel level file I/O. * * Copyright (c) 1997-1999 Microsoft Corporation * * *************************************************************************/ #include #include #if !defined(_GDIPLUS_) /*============================================================================= == 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) * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ 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) * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ 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; extern ULONG IoReadOperationCount, IoWriteOperationCount; 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; } } if ( pEvent != NULL && ppIrp != NULL ) { // Trap whatever driver uses this inheritly broken interface *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) * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ 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 OPTIONAL, 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) * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ 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 ); } #endif // !defined(_GDIPLUS_)