NT4/private/ntos/io/iosubs.c
2020-09-30 17:12:29 +02:00

10337 lines
287 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
iosubs.c
Abstract:
This module contains the subroutines for the I/O system.
Author:
Darryl E. Havens (darrylh) 16-Apr-1989
Environment:
Kernel mode
Revision History:
--*/
#include "iop.h"
//
// This is the overall system device configuration record.
//
static CONFIGURATION_INFORMATION ConfigurationInformation = {
0, // DiskCount
0, // FloppyCount
0, // CdRomCount
0, // TapeCount
0, // ScsiPortCount
0, // SerialCount
0, // ParallelCount
FALSE, // Primary ATDISK IO address claimed
FALSE // Secondary ATDISK IO address claimed
};
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, IoAttachDevice)
#pragma alloc_text(PAGE, IoCancelThreadIo)
#pragma alloc_text(PAGE, IoCheckDesiredAccess)
#pragma alloc_text(PAGE, IoCheckEaBufferValidity)
#pragma alloc_text(PAGE, IoCheckFunctionAccess)
#pragma alloc_text(PAGE, IoCheckShareAccess)
#pragma alloc_text(PAGE, IoConnectInterrupt)
#pragma alloc_text(PAGE, IoCreateController)
#pragma alloc_text(PAGE, IoCreateDevice)
#pragma alloc_text(PAGE, IoCreateFile)
#pragma alloc_text(PAGE, IoCreateNotificationEvent)
#pragma alloc_text(PAGE, IoCreateStreamFileObject)
#pragma alloc_text(PAGE, IoCreateSymbolicLink)
#pragma alloc_text(PAGE, IoCreateSynchronizationEvent)
#pragma alloc_text(PAGE, IoCreateUnprotectedSymbolicLink)
#pragma alloc_text(PAGE, IoDeleteController)
#pragma alloc_text(PAGE, IoDeleteSymbolicLink)
#pragma alloc_text(PAGE, IoDisconnectInterrupt)
#pragma alloc_text(PAGE, IoEnqueueIrp)
#pragma alloc_text(PAGE, IoFastQueryNetworkAttributes)
#pragma alloc_text(PAGE, IoGetConfigurationInformation)
#pragma alloc_text(PAGE, IoGetDeviceObjectPointer)
#pragma alloc_text(PAGE, IoInitializeTimer)
#pragma alloc_text(PAGE, IoQueryFileInformation)
#pragma alloc_text(PAGE, IoQueryVolumeInformation)
#pragma alloc_text(PAGE, IoPageFileCreated)
#pragma alloc_text(PAGE, IoRegisterDriverReinitialization)
#pragma alloc_text(PAGE, IoRegisterFileSystem)
#pragma alloc_text(PAGE, IoRegisterFsRegistrationChange)
#pragma alloc_text(PAGE, IoRegisterShutdownNotification)
#pragma alloc_text(PAGE, IoRemoveShareAccess)
#pragma alloc_text(PAGE, IoSetInformation)
#pragma alloc_text(PAGE, IoSetShareAccess)
#pragma alloc_text(PAGE, IoUnregisterFileSystem)
#pragma alloc_text(PAGE, IoUnregisterFsRegistrationChange)
#pragma alloc_text(PAGE, IoUpdateShareAccess)
#pragma alloc_text(PAGELK, IoShutdownSystem)
#pragma alloc_text(PAGELK, IoUnregisterShutdownNotification)
#endif
VOID
IoAcquireCancelSpinLock(
OUT PKIRQL Irql
)
/*++
Routine Description:
This routine is invoked to acquire the cancel spin lock. This spin lock
must be acquired before setting the address of a cancel routine in an
IRP.
Arguments:
Irql - Address of a variable to receive the old IRQL.
Return Value:
None.
--*/
{
KIRQL oldIrql;
//
// Simply acquire the cancel spin lock and return.
//
ExAcquireSpinLock( &IopCancelSpinLock, &oldIrql );
*Irql = oldIrql;
}
VOID
IoAcquireVpbSpinLock(
OUT PKIRQL Irql
)
/*++
Routine Description:
This routine is invoked to acquire the Volume Parameter Block (VPB) spin
lock. This spin lock must be acquired before accessing the mount flag,
reference count, and device object fields of a VPB.
Arguments:
Irql - Address of a variable to receive the old IRQL.
Return Value:
None.
--*/
{
KIRQL oldIrql;
//
// Simply acquire the VPB spin lock and return.
//
ExAcquireSpinLock( &IopVpbSpinLock, &oldIrql );
*Irql = oldIrql;
}
NTSTATUS
IoAllocateAdapterChannel(
IN PADAPTER_OBJECT AdapterObject,
IN PDEVICE_OBJECT DeviceObject,
IN ULONG NumberOfMapRegisters,
IN PDRIVER_CONTROL ExecutionRoutine,
IN PVOID Context
)
/*++
Routine Description:
This routine allocates the adapter channel specified by the adapter object.
This is accomplished by calling HalAllocateAdapterChannel which does all of
the work.
Arguments:
AdapterObject - Pointer to the adapter control object to allocate to the
driver.
DeviceObject - Pointer to the driver's device object that represents the
device allocating the adapter.
NumberOfMapRegisters - The number of map registers that are to be allocated
from the channel, if any.
ExecutionRoutine - The address of the driver's execution routine that is
invoked once the adapter channel (and possibly map registers) have been
allocated.
Context - An untyped longword context parameter passed to the driver's
execution routine.
Return Value:
Returns STATUS_SUCESS unless too many map registers are requested.
Notes:
Note that this routine MUST be invoked at DISPATCH_LEVEL or above.
--*/
{
PWAIT_CONTEXT_BLOCK wcb;
wcb = &DeviceObject->Queue.Wcb;
wcb->DeviceObject = DeviceObject;
wcb->CurrentIrp = DeviceObject->CurrentIrp;
wcb->DeviceContext = Context;
return( HalAllocateAdapterChannel( AdapterObject,
wcb,
NumberOfMapRegisters,
ExecutionRoutine ) );
}
VOID
IoAllocateController(
IN PCONTROLLER_OBJECT ControllerObject,
IN PDEVICE_OBJECT DeviceObject,
IN PDRIVER_CONTROL ExecutionRoutine,
IN PVOID Context
)
/*++
Routine Description:
This routine allocates the controller specified by the controller object.
This is accomplished by placing the device object of the driver that wants
to allocate the controller on the controller's queue. If the queue is
already "busy", then the controller has already been allocated, so the
device object is simply placed onto the queue and waits until the controller
becomes free.
Once the controller becomes free (or if it already is), then the driver's
execution routine is invoked.
Arguments:
ControllerObject - Pointer to the controller object to allocate to the
driver.
DeviceObject - Pointer to the driver's device object that represents the
device allocating the controller.
ExecutionRoutine - The address of the driver's execution routine that is
invoked once the controller has been allocated.
Context - An untyped longword context parameter passed to the driver's
execution routine.
Return Value:
None.
Notes:
Note that this routine MUST be invoked at DISPATCH_LEVEL or above.
--*/
{
IO_ALLOCATION_ACTION action;
//
// Initialize the device object's wait context block in case this device
// must wait before being able to allocate the controller.
//
DeviceObject->Queue.Wcb.DeviceRoutine = ExecutionRoutine;
DeviceObject->Queue.Wcb.DeviceContext = Context;
//
// Allocate the controller object for this particular device. If the
// controller cannot be allocated because it has already been allocated
// to another device, then return to the caller now; otherwise,
// continue.
//
if (!KeInsertDeviceQueue( &ControllerObject->DeviceWaitQueue,
&DeviceObject->Queue.Wcb.WaitQueueEntry )) {
//
// The controller was not busy so it has been allocated. Simply
// invoke the driver's execution routine now.
//
action = ExecutionRoutine( DeviceObject,
DeviceObject->CurrentIrp,
0,
Context );
//
// If the driver would like to have the controller deallocated,
// then deallocate it now.
//
if (action == DeallocateObject) {
IoFreeController( ControllerObject );
}
}
}
PVOID
IoAllocateErrorLogEntry(
IN PVOID IoObject,
IN UCHAR EntrySize
)
/*++
Routine Description:
This routine allocates and initializes an error log entry buffer and returns
a pointer to the data entry portion of the buffer.
Arguments:
IoObject - Pointer to driver's device object or driver object.
EntrySize - Size of entry to be allocated, in bytes. The maximum size is
specified by ERROR_LOG_MAXIMUM_SIZE.
Return Value:
Pointer to the body of the allocated error log entry, or NULL, if there are
no free entries in the system.
Note:
This routine assumes that the caller wants an error log entry within the
bounds of the maximum size.
--*/
{
PERROR_LOG_ENTRY elEntry;
PDEVICE_OBJECT deviceObject;
PDRIVER_OBJECT driverObject;
PVOID returnValue;
NTSTATUS status;
KIRQL oldIrql;
ULONG size;
//
// Make sure that a I/O object pointer was passed in.
//
if (IoObject == NULL) {
return(NULL);
}
//
// Assume for a moment this is a device object.
//
deviceObject = IoObject;
//
// Determine if this is a driver object or device object. Either maybe
// passed in.
//
if (deviceObject->Type == IO_TYPE_DEVICE) {
driverObject = deviceObject->DriverObject;
} else if (deviceObject->Type == IO_TYPE_DRIVER) {
driverObject = (PDRIVER_OBJECT) IoObject;
deviceObject = NULL;
} else {
return(NULL);
}
//
// Make sure the packet is large enough but not too large.
//
if (EntrySize < sizeof(IO_ERROR_LOG_PACKET) ||
EntrySize > ERROR_LOG_MAXIMUM_SIZE) {
return(NULL);
}
//
// Round entry size to a PVOID size boundary.
//
EntrySize = (UCHAR) ((EntrySize + sizeof(PVOID) - 1) & ~(sizeof(PVOID) - 1));
//
// Calculate the size of the entry needed.
//
size = sizeof(ERROR_LOG_ENTRY) + EntrySize;
//
// Make sure that there is not too many outstanding packets.
//
ExAcquireSpinLock(&IopErrorLogAllocationLock, &oldIrql);
try{
if (IopErrorLogAllocation > IOP_MAXIMUM_LOG_ALLOCATION) {
//
// Fail the request.
//
return(NULL);
}
//
// Increase the outstanding allocation.
//
IopErrorLogAllocation += size;
//
// Allocate the packet.
//
elEntry = ExAllocatePoolWithTag( NonPagedPool, size, 'rEoI' );
if (elEntry == NULL) {
//
// Drop the allocation and return.
//
IopErrorLogAllocation -= size;
return(NULL);
}
//
// Reference the device object and driver object. So they don't
// go away before the name get pulled out.
//
if (deviceObject != NULL) {
ObReferenceObject( deviceObject );
}
ObReferenceObject( driverObject );
//
// Initialize the fields.
//
RtlZeroMemory(elEntry, size);
elEntry->Type = IO_TYPE_ERROR_LOG;
elEntry->Size = (USHORT) size;
elEntry->DeviceObject = deviceObject;
elEntry->DriverObject = driverObject;
returnValue = elEntry+1;
} finally {
ExReleaseSpinLock(&IopErrorLogAllocationLock, oldIrql);
}
return returnValue;
}
PIRP
IoAllocateIrp(
IN CCHAR StackSize,
IN BOOLEAN ChargeQuota
)
/*++
Routine Description:
This routine allocates an I/O Request Packet from the system nonpaged pool.
The packet will be allocated to contain StackSize stack locations. The IRP
will also be initialized.
Arguments:
StackSize - Specifies the maximum number of stack locations required.
ChargeQuota - Specifies whether quota should be charged against thread.
Return Value:
The function value is the address of the allocated/initialized IRP,
or NULL if one could not be allocated.
--*/
{
USHORT allocateSize;
UCHAR fixedSize;
PIRP irp;
PNPAGED_LOOKASIDE_LIST lookasideList;
UCHAR mustSucceed;
USHORT packetSize;
//
// If the size of the packet required is less than or equal to those on
// the lookaside lists, then attempt to allocate the packet from the
// lookaside lists.
//
irp = NULL;
fixedSize = 0;
mustSucceed = 0;
packetSize = IoSizeOfIrp(StackSize);
allocateSize = packetSize;
if (StackSize <= (CCHAR)IopLargeIrpStackLocations) {
fixedSize = IRP_ALLOCATED_FIXED_SIZE;
lookasideList = &IopSmallIrpLookasideList;
if (StackSize != 1) {
allocateSize = IoSizeOfIrp((CCHAR)IopLargeIrpStackLocations);
lookasideList = &IopLargeIrpLookasideList;
}
lookasideList->L.TotalAllocates += 1;
irp = (PIRP)ExInterlockedPopEntrySList(&lookasideList->L.ListHead,
&lookasideList->Lock);
}
//
// If an IRP was not allocated from the lookaside list, then allocate
// the packet from nonpaged pool and charge quota if requested.
//
if (!irp) {
if (fixedSize != 0) {
lookasideList->L.AllocateMisses += 1;
}
//
// There are no free packets on the lookaside list, or the packet is
// too large to be allocated from one of the lists, so it must be
// allocated from nonpaged pool. If quota is to be charged, charge it
// against the current process. Otherwise, allocate the pool normally.
//
if (ChargeQuota) {
try {
irp = ExAllocatePoolWithQuotaTag(NonPagedPool, allocateSize,' prI');
} except(EXCEPTION_EXECUTE_HANDLER) {
NOTHING;
}
} else {
//
// Attempt to allocate the pool from non-paged pool. If this
// fails, and the caller's previous mode was kernel then allocate
// the pool as must succeed.
//
irp = ExAllocatePoolWithTag(NonPagedPool, allocateSize, ' prI');
if (!irp) {
mustSucceed = IRP_ALLOCATED_MUST_SUCCEED;
if (KeGetPreviousMode() == KernelMode ) {
irp = ExAllocatePoolWithTag(NonPagedPoolMustSucceed,
allocateSize,
' prI');
}
}
}
if (!irp) {
return NULL;
}
} else {
ChargeQuota = FALSE;
}
//
// Initialize the packet.
//
IopInitializeIrp(irp, packetSize, StackSize);
irp->AllocationFlags = (fixedSize | mustSucceed);
if (ChargeQuota) {
irp->AllocationFlags |= IRP_QUOTA_CHARGED;
}
return irp;
}
PMDL
IoAllocateMdl(
IN PVOID VirtualAddress,
IN ULONG Length,
IN BOOLEAN SecondaryBuffer,
IN BOOLEAN ChargeQuota,
IN OUT PIRP Irp OPTIONAL
)
/*++
Routine Description:
This routine allocates a Memory Descriptor List (MDL) large enough to map
the buffer specified by the VirtualAddress and Length parameters. If the
routine is given a pointer to an Irp, then it will chain the MDL to the
IRP in the appropriate way.
If this routine is not given a pointer to an Irp it is up to the caller to
set the MDL address in the IRP that the MDL is being allocated for.
Note that the header information of the MDL will also be initialized.
Arguments:
VirtualAddress - Starting virtual address of the buffer to be mapped.
Length - Length, in bytes, of the buffer to be mapped.
SecondaryBuffer - Indicates whether this is a chained buffer.
ChargeQuota - Indicates whether quota should be charged if MDL allocated.
N.B. This parameter is ignored.
Irp - Optional pointer to IRP that MDL is being allocated for.
Return Value:
A pointer to the allocated MDL, or NULL if one could not be allocated.
Note that if no MDL could be allocated because there was not enough quota,
then it is up to the caller to catch the raised exception.
--*/
{
ULONG allocateSize;
USHORT fixedSize;
PMDL mdl;
USHORT mustSucceed;
ULONG size;
PMDL tmpMdlPtr;
ASSERT(Length);
//
// If the requested length is greater than 2Gb, then we're not going
// to be able to map the memory, so fail the request.
//
if (Length & 0x80000000) {
return NULL;
}
//
// Allocate an MDL from the lookaside list or pool as appropriate.
//
mdl = NULL;
fixedSize = 0;
mustSucceed = 0;
size = COMPUTE_PAGES_SPANNED(VirtualAddress, Length);
if (size > IOP_FIXED_SIZE_MDL_PFNS) {
allocateSize = sizeof(MDL) + (sizeof(ULONG) * size);
if (allocateSize > MAXUSHORT) {
return NULL;
}
} else {
fixedSize = MDL_ALLOCATED_FIXED_SIZE;
allocateSize = sizeof(MDL) + (sizeof(ULONG) * IOP_FIXED_SIZE_MDL_PFNS);
mdl = (PMDL)ExAllocateFromNPagedLookasideList(&IopMdlLookasideList);
}
if (!mdl) {
mdl = ExAllocatePoolWithTag(NonPagedPool, allocateSize, ' ldM');
if (!mdl) {
if (KeGetPreviousMode() == KernelMode) {
mustSucceed = MDL_ALLOCATED_MUST_SUCCEED;
mdl = ExAllocatePoolWithTag(NonPagedPoolMustSucceed,
allocateSize,
' ldM' );
}
if (!mdl) {
return NULL;
}
}
}
//
// Now fill in the header of the MDL.
//
MmInitializeMdl(mdl, VirtualAddress, Length);
mdl->MdlFlags |= (fixedSize | mustSucceed);
//
// Finally, if an IRP was specified, store the address of the MDL
// based on whether or not this is a secondary buffer.
//
if (Irp) {
if (!SecondaryBuffer) {
Irp->MdlAddress = mdl;
} else {
tmpMdlPtr = Irp->MdlAddress;
while (tmpMdlPtr->Next != NULL) {
tmpMdlPtr = tmpMdlPtr->Next;
}
tmpMdlPtr->Next = mdl;
}
}
return mdl;
}
NTSTATUS
IoAsynchronousPageWrite(
IN PFILE_OBJECT FileObject,
IN PMDL MemoryDescriptorList,
IN PLARGE_INTEGER StartingOffset,
IN PIO_APC_ROUTINE ApcRoutine,
IN PVOID ApcContext,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PIRP *Irp
)
/*++
Routine Description:
This routine provides a special, fast interface for the Modified Page Writer
(MPW) to write pages to the disk quickly and with very little overhead. All
of the special handling for this request is recognized by setting the
IRP_PAGING_IO flag in the IRP flags word.
Arguments:
FileObject - A pointer to a referenced file object describing which file
the write should be performed on.
MemoryDescriptorList - An MDL which describes the physical pages that the
pages should be written to the disk. All of the pages have been locked
in memory. The MDL also describes the length of the write operation.
StartingOffset - Pointer to he offset in the file from which the write
should take place.
ApcRoutine - The address of a kernel APC routine which should be executed
after the write operation has completed.
ApcContext - A context parameter which should be supplied to the kernel APC
routine when it executes.
IoStatusBlock - A pointer to the I/O status block in which the final status
and information should be stored.
Irp - If specified, allows the caller to squirel away a pointer to the Irp.
Return Value:
The function value is the final status of the queue request to the I/O
system subcomponents.
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
//
// Begin by getting a pointer to the device object that the file resides
// on.
//
deviceObject = IoGetRelatedDeviceObject( FileObject );
//
// Allocate an I/O Request Packet (IRP) for this out-page operation.
//
irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
if (!irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// If specified, let the caller know what Irp is responsible for this
// transfer. While this is mainly for debuggering purposes, it is
// absolutely essential to debug certain types of problems, and is
// very cheap, thus is included in the FREE build as well.
//
if (ARGUMENT_PRESENT(Irp)) {
*Irp = irp;
}
//
// Get a pointer to the first stack location in the packet. This location
// will be used to pass the function codes and parameters to the first
// driver.
//
irpSp = IoGetNextIrpStackLocation( irp );
//
// Fill in the IRP according to this request.
//
irp->MdlAddress = MemoryDescriptorList;
irp->Flags = IRP_PAGING_IO | IRP_NOCACHE;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->Tail.Overlay.OriginalFileObject = FileObject;
irp->UserBuffer = (PVOID) ((PCHAR) MemoryDescriptorList->StartVa + MemoryDescriptorList->ByteOffset);
irp->RequestorMode = KernelMode;
irp->UserIosb = IoStatusBlock;
irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
//
// Fill in the normal write parameters.
//
irpSp->MajorFunction = IRP_MJ_WRITE;
irpSp->Parameters.Write.Length = MemoryDescriptorList->ByteCount;
irpSp->Parameters.Write.ByteOffset = *StartingOffset;
irpSp->FileObject = FileObject;
//
// Queue the packet to the appropriate driver based on whether or not there
// is a VPB associated with the device.
//
status = IoCallDriver( deviceObject, irp );
if (NT_ERROR( status )) {
IoStatusBlock->Status = status;
IoStatusBlock->Information = 0;
ApcRoutine( ApcContext, IoStatusBlock, 0 );
status = STATUS_PENDING;
}
return status;
}
VOID
IopSetAttachedDevicePointer(
IN PDEVICE_OBJECT AttachedDevice,
IN PDEVICE_OBJECT SourceDevice
)
{
KIRQL irql;
ExAcquireFastLock( &IopDatabaseLock, &irql );
AttachedDevice->AttachedDevice = SourceDevice;
AttachedDevice->Spare1++;
SourceDevice->StackSize = (UCHAR) (AttachedDevice->StackSize + 1);
SourceDevice->AlignmentRequirement = AttachedDevice->AlignmentRequirement;
SourceDevice->SectorSize = AttachedDevice->SectorSize;
ExReleaseFastLock( &IopDatabaseLock, irql );
}
NTSTATUS
IoAttachDevice(
IN PDEVICE_OBJECT SourceDevice,
IN PUNICODE_STRING TargetDevice,
OUT PDEVICE_OBJECT *AttachedDevice
)
/*++
Routine Description:
This routine "attaches" a device to another device. That is, it associates
the source device to a target device which enables the I/O system to ensure
that the target device a) exists, and b) cannot be unloaded until the source
device has detached. Also, requests bound for the target device are given
to the source device first, where applicable.
Arguments:
SourceDevice - Pointer to device object to be attached to target.
TargetDevice - Supplies the name of the target device to which the attach
is to occur.
AttachedDevice - Returns a pointer to the device to which the attach
occurred. This is the device object that the source driver should
use to communicate with the target driver.
Return Value:
The function value is the final status of the operation.
--*/
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
PFILE_OBJECT fileObject;
PDEVICE_OBJECT attachedDevice;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE fileHandle;
IO_STATUS_BLOCK ioStatus;
PAGED_CODE();
//
// Attempt to open the target device for attach access. This ensures that
// the device itself will be opened, with all of the special considerations
// thereof.
//
InitializeObjectAttributes( &objectAttributes,
TargetDevice,
0,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
status = ZwOpenFile( &fileHandle,
FILE_READ_ATTRIBUTES,
&objectAttributes,
&ioStatus,
0,
FILE_NON_DIRECTORY_FILE | IO_ATTACH_DEVICE_API );
if (NT_SUCCESS( status )) {
//
// The open operation was successful. Dereference the file handle
// and obtain a pointer to the device object for the handle.
//
status = ObReferenceObjectByHandle( fileHandle,
0,
IoFileObjectType,
KernelMode,
(PVOID *) &fileObject,
NULL );
if (NT_SUCCESS( status )) {
//
// Get a pointer to the device object for this file, and close
// the handle.
//
deviceObject = IoGetRelatedDeviceObject( fileObject );
(VOID) ZwClose( fileHandle );
} else {
return status;
}
} else {
return status;
}
//
// Scan the list of drivers either attached to this device, or a file
// system servicing this device, or drivers that are attached to a file
// system servicing this device, and attach to it. Also, return a pointer
// to the attached device.
//
attachedDevice = IoGetRelatedDeviceObject( fileObject );
*AttachedDevice = attachedDevice;
//
// Set the attached device pointer so that the driver being attached to
// cannot unload until the detach occurs, and so that attempts to open the
// device object go through the attached driver. Note that the reference
// count is not incremented since exclusive drivers can only be opened once
// and this would count as an open. At that point, both device objects
// would become useless.
//
IopSetAttachedDevicePointer( attachedDevice, SourceDevice );
//
// Finally, dereference the file object. This decrements the reference
// count for the target device so that when the detach occurs the device
// can go away if necessary.
//
ObDereferenceObject( fileObject );
//
// Return the final status of the operation.
//
return status;
}
NTSTATUS
IoAttachDeviceByPointer(
IN PDEVICE_OBJECT SourceDevice,
IN PDEVICE_OBJECT TargetDevice
)
/*++
Routine Description:
This routine attaches the source device object to the target device
object.
Arguments:
SourceDevice - Specifies the device object that is to be attached to
the target device.
TargetDevice - Specifies the device object to which the attachment is
to take place.
Return Value:
The function value is the final status of the attach operation.
Note:
THIS FUNCTION IS OBSOLETE!!! see IoAttachDeviceToDeviceStack
--*/
{
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
KIRQL irql;
//
// Get a pointer to the topmost device object in the stack of devices,
// beginning with the TargetDevice.
//
ExAcquireFastLock( &IopDatabaseLock, &irql );
deviceObject = IoGetAttachedDevice( TargetDevice );
//
// Now attach to the device, provided that it is not being unloaded,
// deleted or initializing.
//
if (deviceObject->Flags & DO_DEVICE_INITIALIZING ||
deviceObject->DeviceObjectExtension->ExtensionFlags &
(DOE_UNLOAD_PENDING | DOE_DELETE_PENDING)) {
status = STATUS_NO_SUCH_DEVICE;
} else {
status = STATUS_SUCCESS;
deviceObject->AttachedDevice = SourceDevice;
SourceDevice->StackSize = (UCHAR) (deviceObject->StackSize + 1);
SourceDevice->AlignmentRequirement = deviceObject->AlignmentRequirement;
SourceDevice->SectorSize = deviceObject->SectorSize;
}
ExReleaseFastLock( &IopDatabaseLock, irql );
return status;
}
PDEVICE_OBJECT
IoAttachDeviceToDeviceStack(
IN PDEVICE_OBJECT SourceDevice,
IN PDEVICE_OBJECT TargetDevice
)
/*++
Routine Description:
This routine attaches the source device object to the target device
object and returns a pointer to the actual device attached to, if
successful.
Arguments:
SourceDevice - Specifies the device object that is to be attached to
the target device.
TargetDevice - Specifies the device object to which the attachment is
to occur.
Return Value:
The function value is a pointer to the device object to which the
attachment actually occurred.
--*/
{
PDEVICE_OBJECT deviceObject;
KIRQL irql;
//
// Get a pointer to the topmost device object in the stack of devices,
// beginning with the TargetDevice.
//
ExAcquireFastLock( &IopDatabaseLock, &irql );
deviceObject = IoGetAttachedDevice( TargetDevice );
//
// Now attach to the device, provided that it is not being unloaded,
// deleted or initializing.
//
if (deviceObject->Flags & DO_DEVICE_INITIALIZING ||
deviceObject->DeviceObjectExtension->ExtensionFlags &
(DOE_UNLOAD_PENDING | DOE_DELETE_PENDING)) {
deviceObject = (PDEVICE_OBJECT) NULL;
} else {
deviceObject->AttachedDevice = SourceDevice;
SourceDevice->StackSize = (UCHAR) (deviceObject->StackSize + 1);
SourceDevice->AlignmentRequirement = deviceObject->AlignmentRequirement;
SourceDevice->SectorSize = deviceObject->SectorSize;
}
ExReleaseFastLock( &IopDatabaseLock, irql );
return deviceObject;
}
PIRP
IoBuildAsynchronousFsdRequest(
IN ULONG MajorFunction,
IN PDEVICE_OBJECT DeviceObject,
IN OUT PVOID Buffer OPTIONAL,
IN ULONG Length OPTIONAL,
IN PLARGE_INTEGER StartingOffset OPTIONAL,
IN PIO_STATUS_BLOCK IoStatusBlock 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 must be one of the following request codes:
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_FLUSH_BUFFERS
IRP_MJ_SHUTDOWN
This routine provides a simple, fast interface to the device driver w/o
having to put the knowledge of how to build an IRP into all of the FSDs
(and device drivers) in the system.
Arguments:
MajorFunction - Function to be performed; see previous list.
DeviceObject - Pointer to device object on which the I/O will be performed.
Buffer - Pointer to buffer to get data from or write data into. This
parameter is required for read/write, but not for flush or shutdown
functions.
Length - Length of buffer in bytes. This parameter is required for
read/write, but not for flush or shutdown functions.
StartingOffset - Pointer to the offset on the disk to read/write from/to.
This parameter is required for read/write, but not for flush or
shutdown functions.
IoStatusBlock - Pointer to I/O status block for completion status
information. This parameter is optional since most asynchronous FSD
requests will be synchronized by using completion routines, and so the
I/O status block will not be written.
Return Value:
The function value is a pointer to the IRP representing the specified
request.
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
//
// Begin by allocating the IRP for this request. Do not charge quota to
// the current process for this IRP.
//
irp = IoAllocateIrp( DeviceObject->StackSize, FALSE );
if (!irp) {
return irp;
}
//
// 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 );
//
// Set the major function code.
//
irpSp->MajorFunction = (UCHAR) MajorFunction;
if (MajorFunction != IRP_MJ_FLUSH_BUFFERS && MajorFunction != IRP_MJ_SHUTDOWN) {
//
// Now allocate a buffer or lock the pages of the caller's buffer into
// memory based on whether the target device performs direct or buffered
// I/O operations.
//
if (DeviceObject->Flags & DO_BUFFERED_IO) {
//
// The target device supports buffered I/O operations. Allocate a
// system buffer and, if this is a write, fill it in. Otherwise,
// the copy will be done into the caller's buffer in the completion
// code. Also note that the system buffer should be deallocated on
// completion. Also, set the parameters based on whether this is a
// read or a write operation.
//
irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag( NonPagedPoolCacheAligned,
Length,
' oI' );
if (irp->AssociatedIrp.SystemBuffer == NULL) {
IoFreeIrp( irp );
return (PIRP) NULL;
}
if (MajorFunction == IRP_MJ_WRITE) {
RtlCopyMemory( irp->AssociatedIrp.SystemBuffer, Buffer, Length );
irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
} else {
irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION;
irp->UserBuffer = Buffer;
}
} else if (DeviceObject->Flags & DO_DIRECT_IO) {
//
// The target device supports direct I/O operations. Allocate
// an MDL large enough to map the buffer and lock the pages into
// memory.
//
irp->MdlAddress = IoAllocateMdl( Buffer,
Length,
FALSE,
FALSE,
(PIRP) NULL );
if (irp->MdlAddress == NULL) {
IoFreeIrp( irp );
return (PIRP) NULL;
}
MmProbeAndLockPages( irp->MdlAddress,
KernelMode,
(LOCK_OPERATION) (MajorFunction == IRP_MJ_READ ? IoWriteAccess : IoReadAccess) );
} else {
//
// The operation is neither buffered nor direct. Simply pass the
// address of the buffer in the packet to the driver.
//
irp->UserBuffer = Buffer;
}
//
// 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.
//
if (MajorFunction == IRP_MJ_WRITE) {
irpSp->Parameters.Write.Length = Length;
irpSp->Parameters.Write.ByteOffset = *StartingOffset;
} else {
irpSp->Parameters.Read.Length = Length;
irpSp->Parameters.Read.ByteOffset = *StartingOffset;
}
}
//
// Finally, set the address of the I/O status block and return a pointer
// to the IRP.
//
irp->UserIosb = IoStatusBlock;
return irp;
}
PIRP
IoBuildDeviceIoControlRequest(
IN ULONG IoControlCode,
IN PDEVICE_OBJECT DeviceObject,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength,
IN BOOLEAN InternalDeviceIoControl,
IN PKEVENT Event,
OUT PIO_STATUS_BLOCK IoStatusBlock
)
/*++
Routine Description:
This routine builds an I/O Request Packet (IRP) that can be used to
perform a synchronous internal or normal device I/O control function.
Arguments:
IoControlCode - Specifies the device I/O control code that is to be
performed by the target device driver.
DeviceObject - Specifies the target device on which the I/O control
function is to be performed.
InputBuffer - Optional pointer to an input buffer that is to be passed
to the device driver.
InputBufferLength - Length of the InputBuffer in bytes. If the Input-
Buffer parameter is not passed, this parameter must be zero.
OutputBuffer - Optional pointer to an output buffer that is to be passed
to the device driver.
OutputBufferLength - Length of the OutputBuffer in bytes. If the
OutputBuffer parameter is not passed, this parameter must be zero.
InternalDeviceIoControl - A BOOLEAN parameter that specifies whether
the packet that gets generated should have a major function code
of IRP_MJ_INTERNAL_DEVICE_CONTROL (the parameter is TRUE), or
IRP_MJ_DEVICE_CONTROL (the parameter is FALSE).
Event - Supplies a pointer to a kernel event that is to be set to the
Signaled state when the I/O operation is complete. Note that the
Event must already be set to the Not-Signaled state.
IoStatusBlock - Supplies a pointer to an I/O status block that is to
be filled in with the final status of the operation once it
completes.
Return Value:
The function value is a pointer to the generated IRP suitable for calling
the target device driver.
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
ULONG method;
//
// Begin by allocating the IRP for this request. Do not charge quota to
// the current process for this IRP.
//
irp = IoAllocateIrp( DeviceObject->StackSize, FALSE );
if (!irp) {
return irp;
}
//
// 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 );
//
// Set the major function code based on the type of device I/O control
// function the caller has specified.
//
if (InternalDeviceIoControl) {
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
} else {
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
}
//
// Copy the caller's parameters to the service-specific portion of the
// IRP for those parameters that are the same for all four methods.
//
irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
//
// Get the method bits from the I/O control code to determine how the
// buffers are to be passed to the driver.
//
method = IoControlCode & 3;
//
// Based on the method that the buffers are being passed, either allocate
// buffers or build MDLs or do nothing.
//
switch ( method ) {
case 0:
//
// For this case, allocate a buffer that is large enough to contain
// both the input and the output buffers. Copy the input buffer
// to the allocated buffer and set the appropriate IRP fields.
//
if (InputBufferLength != 0 || OutputBufferLength != 0) {
irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag( NonPagedPoolCacheAligned,
InputBufferLength > OutputBufferLength ? InputBufferLength : OutputBufferLength,
' oI' );
if (irp->AssociatedIrp.SystemBuffer == NULL) {
IoFreeIrp( irp );
return (PIRP) NULL;
}
if (ARGUMENT_PRESENT( InputBuffer )) {
RtlCopyMemory( irp->AssociatedIrp.SystemBuffer,
InputBuffer,
InputBufferLength );
}
irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
irp->UserBuffer = OutputBuffer;
if (ARGUMENT_PRESENT( OutputBuffer )) {
irp->Flags |= IRP_INPUT_OPERATION;
}
} else {
irp->Flags = 0;
irp->UserBuffer = (PVOID) NULL;
}
break;
case 1:
case 2:
//
// For these two cases, allocate a buffer that is large enough to
// contain the input buffer, if any, and copy the information to
// the allocated buffer. Then build an MDL for either read or write
// access, depending on the method, for the output buffer. Note
// that an output buffer must have been specified.
//
if (ARGUMENT_PRESENT( InputBuffer )) {
irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag( NonPagedPoolCacheAligned,
InputBufferLength,
' oI' );
if (irp->AssociatedIrp.SystemBuffer == NULL) {
IoFreeIrp( irp );
return (PIRP) NULL;
}
RtlCopyMemory( irp->AssociatedIrp.SystemBuffer,
InputBuffer,
InputBufferLength );
irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
} else {
irp->Flags = 0;
}
if (ARGUMENT_PRESENT( OutputBuffer )) {
irp->MdlAddress = IoAllocateMdl( OutputBuffer,
OutputBufferLength,
FALSE,
FALSE,
(PIRP) NULL );
if (irp->MdlAddress == NULL) {
if (ARGUMENT_PRESENT( InputBuffer )) {
ExFreePool( irp->AssociatedIrp.SystemBuffer );
}
IoFreeIrp( irp );
return (PIRP) NULL;
}
MmProbeAndLockPages( irp->MdlAddress,
KernelMode,
(LOCK_OPERATION) ((method == 1) ? IoReadAccess : IoWriteAccess) );
}
break;
case 3:
//
// For this case, do nothing. Everything is up to the driver.
// Simply give the driver a copy of the caller's parameters and
// let the driver do everything itself.
//
irp->UserBuffer = OutputBuffer;
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = InputBuffer;
}
//
// Finally, set the address of the I/O status block and the address of
// the kernel event object. Note that I/O completion will not attempt
// to dereference the event since there is no file object associated
// with this operation.
//
irp->UserIosb = IoStatusBlock;
irp->UserEvent = Event;
//
// Also set the address of the current thread in the packet so the
// completion code will have a context to execute in. The IRP also
// needs to be queued to the thread since the caller is going to set
// the file object pointer.
//
irp->Tail.Overlay.Thread = PsGetCurrentThread();
IopQueueThreadIrp( irp );
//
// Simply return a pointer to the packet.
//
return irp;
}
VOID
IoBuildPartialMdl(
IN PMDL SourceMdl,
IN OUT PMDL TargetMdl,
IN PVOID VirtualAddress,
IN ULONG Length
)
/*++
Routine Description:
This routine maps a portion of a buffer as described by an MDL. The
portion of the buffer to be mapped is specified via a virtual address
and an optional length. If the length is not supplied, then the
remainder of the buffer is mapped.
Arguments:
SourceMdl - MDL for the current buffer.
TargetMdl - MDL to map the specified portion of the buffer.
VirtualAddress - Base of the buffer to begin mapping.
Length - Length of buffer to be mapped; if zero, remainder.
Return Value:
None.
Notes:
This routine assumes that the target MDL is large enough to map the
desired portion of the buffer. If the target is not large enough
then an exception will be raised.
It is also assumed that the remaining length of the buffer to be mapped
is non-zero.
--*/
{
ULONG baseVa;
ULONG offset;
ULONG newLength;
PULONG basePointer;
PULONG copyPointer;
//
// Calculate the base address of the buffer that the source Mdl maps.
// Then, determine the length of the buffer to be mapped, if not
// specified.
//
baseVa = (ULONG) MmGetMdlVirtualAddress( SourceMdl );
offset = (ULONG) VirtualAddress - baseVa;
if (Length == 0) {
newLength = MmGetMdlByteCount( SourceMdl ) - offset;
} else {
newLength = Length;
}
//
// Initialize the target MDL header. Note that the original size of
// the MDL structure itself is left unchanged.
//
//ASSERT ((SourceMdl->MdlFlags & MDL_PARTIAL) == 0);
TargetMdl->Process = SourceMdl->Process;
TargetMdl->StartVa = (PVOID) PAGE_ALIGN( VirtualAddress );
TargetMdl->ByteCount = newLength;
TargetMdl->ByteOffset = BYTE_OFFSET( VirtualAddress );
newLength = COMPUTE_PAGES_SPANNED( VirtualAddress, newLength );
if (((TargetMdl->Size - sizeof( MDL )) >> 2L) < newLength ) {
KeBugCheck( TARGET_MDL_TOO_SMALL );
}
//
// Set the MdlFlags in the target MDL. Clear all flags but
// carry across the allocation information, page read and the
// system mapped info.
//
TargetMdl->MdlFlags &= (MDL_ALLOCATED_FIXED_SIZE | MDL_ALLOCATED_MUST_SUCCEED);
TargetMdl->MdlFlags |= SourceMdl->MdlFlags & (MDL_SOURCE_IS_NONPAGED_POOL |
MDL_MAPPED_TO_SYSTEM_VA |
MDL_IO_PAGE_READ);
TargetMdl->MdlFlags |= MDL_PARTIAL;
#if DBG
if (TargetMdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
TargetMdl->MdlFlags |= MDL_PARENT_MAPPED_SYSTEM_VA;
}
#endif //DBG
//
// Preserved the mapped system address.
//
TargetMdl->MappedSystemVa = (PUCHAR)SourceMdl->MappedSystemVa + offset;
//
// Determine the base address of the first PFN in the source MDL that
// needs to be copied to the target. Then, copy as many PFNs as are
// needed.
//
basePointer = (PULONG) (SourceMdl + 1);
basePointer += ((ULONG) TargetMdl->StartVa - (ULONG) SourceMdl->StartVa) >> PAGE_SHIFT;
copyPointer = (PULONG) (TargetMdl + 1);
while (newLength > 0) {
*copyPointer = *basePointer;
copyPointer++;
basePointer++;
newLength--;
}
}
PIRP
IoBuildSynchronousFsdRequest(
IN ULONG MajorFunction,
IN PDEVICE_OBJECT DeviceObject,
IN OUT PVOID Buffer OPTIONAL,
IN ULONG Length OPTIONAL,
IN PLARGE_INTEGER StartingOffset OPTIONAL,
IN PKEVENT Event,
OUT PIO_STATUS_BLOCK IoStatusBlock
)
/*++
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 must be one of the following request codes:
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_FLUSH_BUFFERS
IRP_MJ_SHUTDOWN
This routine provides a simple, fast interface to the device driver w/o
having to put the knowledge of how to build an IRP into all of the FSDs
(and device drivers) in the system.
The IRP created by this function causes the I/O system to complete the
request by setting the specified event to the Signaled state.
Arguments:
MajorFunction - Function to be performed; see previous list.
DeviceObject - Pointer to device object on which the I/O will be performed.
Buffer - Pointer to buffer to get data from or write data into. This
parameter is required for read/write, but not for flush or shutdown
functions.
Length - Length of buffer in bytes. This parameter is required for
read/write, but not for flush or shutdown functions.
StartingOffset - Pointer to the offset on the disk to read/write from/to.
This parameter is required for read/write, but not for flush or
shutdown functions.
Event - Pointer to a kernel event structure for synchronization. The event
will be set to the Signaled state when the I/O has completed.
IoStatusBlock - Pointer to I/O status block for completion status info.
Return Value:
The function value is a pointer to the IRP representing the specified
request.
--*/
{
PIRP irp;
//
// Do all of the real work in real IRP build routine.
//
irp = IoBuildAsynchronousFsdRequest( MajorFunction,
DeviceObject,
Buffer,
Length,
StartingOffset,
IoStatusBlock );
if (irp == NULL) {
return irp;
}
//
// Now fill in the event to the completion code will do the right thing.
// Notice that because there is no FileObject, the I/O completion code
// will not attempt to dereference the event.
//
irp->UserEvent = Event;
//
// Also set the address of the current thread in the packet so the
// completion code will have a context to execute in. The IRP also
// needs to be queued to the thread since the caller is going to set
// the file object pointer.
//
irp->Tail.Overlay.Thread = PsGetCurrentThread();
IopQueueThreadIrp( irp );
return irp;
}
NTSTATUS
FASTCALL
IofCallDriver(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
/*++
Routine Description:
This routine is invoked to pass an I/O Request Packet (IRP) to another
driver at its dispatch routine.
Arguments:
DeviceObject - Pointer to device object to which the IRP should be passed.
Irp - Pointer to IRP for request.
Return Value:
Return status from driver's dispatch routine.
--*/
{
PIO_STACK_LOCATION irpSp;
PDRIVER_OBJECT driverObject;
#if DBG
KIRQL saveIrql;
NTSTATUS status;
#endif
//
// Ensure that this is really an I/O Request Packet.
//
ASSERT( Irp->Type == IO_TYPE_IRP );
//
// Update the IRP stack to point to the next location.
//
Irp->CurrentLocation--;
if (Irp->CurrentLocation <= 0) {
KeBugCheckEx( NO_MORE_IRP_STACK_LOCATIONS, (ULONG) Irp, 0, 0, 0 );
}
irpSp = IoGetNextIrpStackLocation( Irp );
Irp->Tail.Overlay.CurrentStackLocation = irpSp;
//
// Save a pointer to the device object for this request so that it can
// be used later in completion.
//
irpSp->DeviceObject = DeviceObject;
//
// Invoke the driver at its dispatch routine entry point.
//
driverObject = DeviceObject->DriverObject;
#if DBG
saveIrql = KeGetCurrentIrql();
status = driverObject->MajorFunction[irpSp->MajorFunction]( DeviceObject,
Irp );
if (saveIrql != KeGetCurrentIrql()) {
DbgPrint( "IO: IoCallDriver( Driver object: %x Device object: %x Irp: %x )\n",
driverObject,
DeviceObject,
Irp
);
DbgPrint( " Irql before: %x != After: %x\n", saveIrql, KeGetCurrentIrql() );
DbgBreakPoint();
}
return status;
#else
return driverObject->MajorFunction[irpSp->MajorFunction]( DeviceObject,
Irp );
#endif
}
BOOLEAN
IoCancelIrp(
IN PIRP Irp
)
/*++
Routine Description:
This routine is invoked to cancel an individual I/O Request Packet.
It acquires the cancel spin lock, sets the cancel flag in the IRP, and
then invokes the cancel routine specified by the appropriate field in
the IRP, if a routine was specified. It is expected that the cancel
routine will relaease the cancel spinlock. If there is no cancel routine,
then the cancel spin lock is released.
Arguments:
Irp - Supplies a pointer to the IRP to be cancelled.
Return Value:
The function value is TRUE if the IRP was in a cancellable state (it
had a cancel routine), else FALSE is returned.
Notes:
It is assumed that the caller has taken the necessary action to ensure
that the packet cannot be fully completed before invoking this routine.
--*/
{
PDRIVER_CANCEL cancelRoutine;
KIRQL irql;
//
// Acquire the cancel spin lock.
//
IoAcquireCancelSpinLock( &irql );
//
// Set the cancel flag in the IRP.
//
Irp->Cancel = TRUE;
//
// Obtain the address of the cancel routine, and if one was specified,
// invoke it.
//
cancelRoutine = (PDRIVER_CANCEL) InterlockedExchange( (PLONG) &Irp->CancelRoutine,
(LONG) NULL );
if (cancelRoutine) {
if (Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1)) {
KeBugCheckEx( CANCEL_STATE_IN_COMPLETED_IRP, (ULONG) Irp, 0, 0, 0 );
}
Irp->CancelIrql = irql;
cancelRoutine( Irp->Tail.Overlay.CurrentStackLocation->DeviceObject,
Irp );
//
// The cancel spinlock should have been released by the cancel routine.
//
return(TRUE);
} else {
//
// There was no cancel routine, so release the cancel spinlock and
// return indicating the Irp was not currently cancelable.
//
IoReleaseCancelSpinLock( irql );
return(FALSE);
}
}
VOID
IoCancelThreadIo(
IN PETHREAD Thread
)
/*++
Routine Description:
This routine cancels all of the I/O operations for the specified thread.
This is accomplished by walking the list of IRPs in the thread IRP list
and canceling each one individually. No other I/O operations can be
started for the thread since this routine has control of the thread itself.
Arguments:
Tcb - Pointer to the Thread Control Block for the thread.
Return Value:
None.
--*/
{
PLIST_ENTRY header;
PLIST_ENTRY entry;
KIRQL irql;
PETHREAD thread;
PIRP irp;
ULONG count;
LARGE_INTEGER interval;
PAGED_CODE();
DBG_UNREFERENCED_PARAMETER( Thread );
thread = PsGetCurrentThread();
//
// Raise the IRQL so that the IrpList cannot be modified by a completion
// APC.
//
KeRaiseIrql( APC_LEVEL, &irql );
header = &thread->IrpList;
entry = thread->IrpList.Flink;
//
// Walk the list of pending IRPs, canceling each of them.
//
while (header != entry) {
irp = CONTAINING_RECORD( entry, IRP, ThreadListEntry );
IoCancelIrp( irp );
entry = entry->Flink;
}
//
// Wait for the requests to complete. Note that waiting may eventually
// timeout, in which case more work must be done.
//
count = 0;
interval.QuadPart = -10 * 1000 * 100;
while (!IsListEmpty( &Thread->IrpList )) {
//
// Lower the IRQL so that the thread APC can fire which will complete
// the requests. Delay execution for a time and let the request
// finish. The delay time is 100ms.
//
KeLowerIrql( irql );
KeDelayExecutionThread( KernelMode, FALSE, &interval );
if (count++ > 3000) {
//
// This I/O request has timed out, as it has not been completed
// for a full 5 minutes. Attempt to remove the packet's association
// with this thread. Note that by not resetting the count, the
// next time through the loop the next packet, if there is one,
// which has also timed out, will be dealt with, although it
// will be given another 100ms to complete.
//
IopDisassociateThreadIrp();
}
KeRaiseIrql( APC_LEVEL, &irql );
}
KeLowerIrql( irql );
}
NTSTATUS
IoCheckDesiredAccess(
IN OUT PACCESS_MASK DesiredAccess,
IN ACCESS_MASK GrantedAccess
)
/*++
Routine Description:
This routine is invoked to determine whether or not the granted access
to a file allows the access specified by a desired access.
Arguments:
DesiredAccess - Pointer to a variable containing the access desired to
the file.
GrantedAccess - Access currently granted to the file.
Return Value:
The final status of the access check is the function value. If the
accessor has the access to the file, then STATUS_SUCCESS is returned;
otherwise, STATUS_ACCESS_DENIED is returned.
Also, the DesiredAccess is returned with no generic mapping.
--*/
{
PAGED_CODE();
//
// Convert the desired access to a non-generic access mask.
//
RtlMapGenericMask( DesiredAccess,
&IoFileObjectType->TypeInfo.GenericMapping );
//
// Determine whether the desired access to the file is allowed, given
// the current granted access.
//
if (!SeComputeDeniedAccesses( GrantedAccess, *DesiredAccess )) {
return STATUS_SUCCESS;
} else {
return STATUS_ACCESS_DENIED;
}
}
NTSTATUS
IoCheckEaBufferValidity(
IN PFILE_FULL_EA_INFORMATION EaBuffer,
IN ULONG EaLength,
OUT PULONG ErrorOffset
)
/*++
Routine Description:
This routine checks the validity of the specified EA buffer to guarantee
that its format is proper, no fields hang over, that it is not recursive,
etc.
Arguments:
EaBuffer - Pointer to the buffer containing the EAs to be checked.
EaLength - Specifies the length of EaBuffer.
ErrorOffset - A variable to receive the offset of the offending entry
in the EA buffer if an error is incurred. This variable is only
valid if an error occurs.
Return Value:
The function value is STATUS_SUCCESS if the EA buffer contains a valid,
properly formed list, otherwise STATUS_EA_LIST_INCONSISTENT.
--*/
#define ALIGN_LONG( Address ) ( (ULONG) ((Address + 3) & ~3) )
#define GET_OFFSET_LENGTH( CurrentEa, EaBase ) ( \
(ULONG) ((PCHAR) CurrentEa - (PCHAR) EaBase) )
{
LONG tempLength;
ULONG entrySize;
PFILE_FULL_EA_INFORMATION eas;
PAGED_CODE();
//
// Walk the buffer and ensure that its format is valid. That is, ensure
// that it does not walk off the end of the buffer, is not recursive,
// etc.
//
eas = EaBuffer;
tempLength = EaLength;
for (;;) {
//
// Get the size of the current entry in the buffer. The minimum
// size of the entry is the fixed size part of the structure plus
// the length of the name, a single termination character byte which
// must be present (a 0), plus the length of the value. If this
// is not the last entry, then there will also be pad bytes to get
// to the next longword boundary.
//
//
// Start by checking that the fixed size lies within the stated length.
//
if (tempLength < FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0])) {
*ErrorOffset = GET_OFFSET_LENGTH( eas, EaBuffer );
return STATUS_EA_LIST_INCONSISTENT;
}
entrySize = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
eas->EaNameLength + 1 + eas->EaValueLength;
//
// Confirm that the full length lies within the stated buffer length.
//
if ((ULONG) tempLength < entrySize) {
*ErrorOffset = GET_OFFSET_LENGTH( eas, EaBuffer );
return STATUS_EA_LIST_INCONSISTENT;
}
//
// Confirm that there is a NULL terminator after the name.
//
if (eas->EaName[eas->EaNameLength] != '\0') {
*ErrorOffset = GET_OFFSET_LENGTH( eas, EaBuffer );
return STATUS_EA_LIST_INCONSISTENT;
}
if (eas->NextEntryOffset) {
//
// There is another entry in the buffer and it must be longword
// aligned. Ensure that the offset indicates that it is. If it
// isn't, return invalid parameter.
//
if (ALIGN_LONG( entrySize ) != eas->NextEntryOffset ||
(LONG) eas->NextEntryOffset < 0) {
*ErrorOffset = GET_OFFSET_LENGTH( eas, EaBuffer );
return STATUS_EA_LIST_INCONSISTENT;
} else {
//
// There is another entry in the buffer, so account for the
// size of the current entry in the length and get a pointer
// to the next entry.
//
tempLength -= eas->NextEntryOffset;
if (tempLength < 0) {
*ErrorOffset = GET_OFFSET_LENGTH( eas, EaBuffer );
return STATUS_EA_LIST_INCONSISTENT;
}
eas = (PFILE_FULL_EA_INFORMATION) ((PCHAR) eas + eas->NextEntryOffset);
}
} else {
//
// There are no other entries in the buffer. Simply account for
// the overall buffer length according to the size of the current
// entry and exit the loop.
//
tempLength -= entrySize;
break;
}
}
//
// All of the entries in the buffer have been processed. Check to see
// whether the overall buffer length went negative. If so, return an
// error.
//
if (tempLength < 0) {
*ErrorOffset = GET_OFFSET_LENGTH( eas, EaBuffer );
return STATUS_EA_LIST_INCONSISTENT;
}
return STATUS_SUCCESS;
}
NTSTATUS
IoCheckFunctionAccess(
IN ACCESS_MASK GrantedAccess,
IN UCHAR MajorFunction,
IN UCHAR MinorFunction,
IN ULONG IoControlCode,
IN PFILE_INFORMATION_CLASS FileInformationClass OPTIONAL,
IN PFS_INFORMATION_CLASS FsInformationClass OPTIONAL
)
/*++
Routine Description:
This routine checks the parameters and access for the function and
parameters specified by the input parameters against the current access
to the file as described by the GrantedAccess mask parameter. If the
caller has the access to the file, then a successful status code is
returned. Otherwise, an error status code is returned as the function
value.
Arguments:
GrantedAccess - Access granted to the file for the caller.
MajorFunction - Major function code for the operation being performed.
MinorFunction - Minor function code for the operation being performed.
IoControlCode - I/O function control code for a device or file system I/O
code. Used only for those two function types.
FileInformationClass - Optional file information class value of the
operation being performed. This parameter MUST be supplied if the
major function code indicates a query or set file information function
is being performed.
FsInformationClass - Optional FS information class value of the operation
being performed. This parameter MUST be supplied if the major function
code indicates that a query or set file system information function is
being performed.
Return Value:
The final status of the access check is the function value. If the
accessor has the access to the file, then STATUS_SUCCESS is returned;
otherwise, STATUS_ACCESS_DENIED is returned.
Note:
The GrantedAccess mask may not contain any generic mappings. That is,
the IoCheckDesiredAccess function must have been previously invoked to
return a full mask.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER( MinorFunction );
PAGED_CODE();
//
// Determine the major function being performed. If the function code
// is invalid, then return an error.
//
switch( MajorFunction ) {
case IRP_MJ_CREATE:
case IRP_MJ_CLOSE:
break;
case IRP_MJ_READ:
if (SeComputeDeniedAccesses( GrantedAccess, FILE_READ_DATA )) {
status = STATUS_ACCESS_DENIED;
}
break;
case IRP_MJ_WRITE:
if (!SeComputeGrantedAccesses( GrantedAccess, FILE_WRITE_DATA | FILE_APPEND_DATA )) {
status = STATUS_ACCESS_DENIED;
}
break;
case IRP_MJ_QUERY_INFORMATION:
if (IopQueryOperationAccess[*FileInformationClass] != 0) {
if (SeComputeDeniedAccesses( GrantedAccess, IopQueryOperationAccess[*FileInformationClass] )) {
status = STATUS_ACCESS_DENIED;
}
}
break;
case IRP_MJ_SET_INFORMATION:
if (IopSetOperationAccess[*FileInformationClass] != 0) {
if (SeComputeDeniedAccesses( GrantedAccess, IopSetOperationAccess[*FileInformationClass] )) {
status = STATUS_ACCESS_DENIED;
}
}
break;
case IRP_MJ_QUERY_EA:
if (SeComputeDeniedAccesses( GrantedAccess, FILE_READ_EA )) {
status = STATUS_ACCESS_DENIED;
}
break;
case IRP_MJ_SET_EA:
if (SeComputeDeniedAccesses( GrantedAccess, FILE_WRITE_EA )) {
status = STATUS_ACCESS_DENIED;
}
break;
case IRP_MJ_FLUSH_BUFFERS:
if (SeComputeDeniedAccesses( GrantedAccess, FILE_WRITE_DATA )) {
status = STATUS_ACCESS_DENIED;
}
break;
case IRP_MJ_QUERY_VOLUME_INFORMATION:
if (SeComputeDeniedAccesses( GrantedAccess, IopQueryFsOperationAccess[*FsInformationClass] )) {
status = STATUS_ACCESS_DENIED;
}
break;
case IRP_MJ_SET_VOLUME_INFORMATION:
if (SeComputeDeniedAccesses( GrantedAccess, IopSetFsOperationAccess[*FsInformationClass] )) {
status = STATUS_ACCESS_DENIED;
}
break;
case IRP_MJ_DIRECTORY_CONTROL:
if (SeComputeDeniedAccesses( GrantedAccess, FILE_LIST_DIRECTORY )) {
status = STATUS_ACCESS_DENIED;
}
break;
case IRP_MJ_FILE_SYSTEM_CONTROL:
case IRP_MJ_DEVICE_CONTROL:
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
{
ULONG accessMode = (IoControlCode >> 14) & 3;
if (accessMode != FILE_ANY_ACCESS) {
//
// This I/O control requires that the caller have read, write,
// or read/write access to the object. If this is not the case,
// then cleanup and return an appropriate error status code.
//
if (!(SeComputeGrantedAccesses( GrantedAccess, accessMode ))) {
status = STATUS_ACCESS_DENIED;
}
}
}
break;
case IRP_MJ_LOCK_CONTROL:
if (!SeComputeGrantedAccesses( GrantedAccess, FILE_READ_DATA | FILE_WRITE_DATA )) {
status = STATUS_ACCESS_DENIED;
}
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
}
return status;
}
NTSTATUS
IoCheckShareAccess(
IN ACCESS_MASK DesiredAccess,
IN ULONG DesiredShareAccess,
IN OUT PFILE_OBJECT FileObject,
IN OUT PSHARE_ACCESS ShareAccess,
IN BOOLEAN Update
)
/*++
Routine Description:
This routine is invoked to determine whether or not a new accessor to
a file actually has shared access to it. The check is made according
to:
1) How the file is currently opened.
2) What types of shared accesses are currently specified.
3) The desired and shared accesses that the new open is requesting.
If the open should succeed, then the access information about how the
file is currently opened is updated, according to the Update parameter.
Arguments:
DesiredAccess - Desired access of current open request.
DesiredShareAccess - Shared access requested by current open request.
FileObject - Pointer to the file object of the current open request.
ShareAccess - Pointer to the share access structure that describes how
the file is currently being accessed.
Update - Specifies whether or not the share access information for the
file is to be updated.
Return Value:
The final status of the access check is the function value. If the
accessor has access to the file, STATUS_SUCCESS is returned. Otherwise,
STATUS_SHARING_VIOLATION is returned.
Note:
Note that the ShareAccess parameter must be locked against other accesses
from other threads while this routine is executing. Otherwise the counts
will be out-of-synch.
--*/
{
ULONG ocount;
PAGED_CODE();
//
// Set the access type in the file object for the current accessor.
// Note that reading and writing attributes are not included in the
// access check.
//
FileObject->ReadAccess = (BOOLEAN) ((DesiredAccess & (FILE_EXECUTE
| FILE_READ_DATA)) != 0);
FileObject->WriteAccess = (BOOLEAN) ((DesiredAccess & (FILE_WRITE_DATA
| FILE_APPEND_DATA)) != 0);
FileObject->DeleteAccess = (BOOLEAN) ((DesiredAccess & DELETE) != 0);
//
// There is no more work to do unless the user specified one of the
// sharing modes above.
//
if (FileObject->ReadAccess ||
FileObject->WriteAccess ||
FileObject->DeleteAccess) {
FileObject->SharedRead = (BOOLEAN) ((DesiredShareAccess & FILE_SHARE_READ) != 0);
FileObject->SharedWrite = (BOOLEAN) ((DesiredShareAccess & FILE_SHARE_WRITE) != 0);
FileObject->SharedDelete = (BOOLEAN) ((DesiredShareAccess & FILE_SHARE_DELETE) != 0);
//
// Now check to see whether or not the desired accesses are compatible
// with the way that the file is currently open.
//
ocount = ShareAccess->OpenCount;
if ( (FileObject->ReadAccess && (ShareAccess->SharedRead < ocount))
||
(FileObject->WriteAccess && (ShareAccess->SharedWrite < ocount))
||
(FileObject->DeleteAccess && (ShareAccess->SharedDelete < ocount))
||
((ShareAccess->Readers != 0) && !FileObject->SharedRead)
||
((ShareAccess->Writers != 0) && !FileObject->SharedWrite)
||
((ShareAccess->Deleters != 0) && !FileObject->SharedDelete)
) {
//
// The check failed. Simply return to the caller indicating that the
// current open cannot access the file.
//
return STATUS_SHARING_VIOLATION;
//
// The check was successful. Update the counter information in the
// shared access structure for this open request if the caller
// specified that it should be updated.
//
} else if (Update) {
ShareAccess->OpenCount++;
ShareAccess->Readers += FileObject->ReadAccess;
ShareAccess->Writers += FileObject->WriteAccess;
ShareAccess->Deleters += FileObject->DeleteAccess;
ShareAccess->SharedRead += FileObject->SharedRead;
ShareAccess->SharedWrite += FileObject->SharedWrite;
ShareAccess->SharedDelete += FileObject->SharedDelete;
}
}
return STATUS_SUCCESS;
}
VOID
FASTCALL
IofCompleteRequest(
IN PIRP Irp,
IN CCHAR PriorityBoost
)
/*++
Routine Description:
This routine is invoked to complete an I/O request. It is invoked by the
driver in its DPC routine to perform the final completion of the IRP. The
functions performed by this routine are as follows.
1. A check is made to determine whether the packet's stack locations
have been exhausted. If not, then the stack location pointer is set
to the next location and if there is a routine to be invoked, then
it will be invoked. This continues until there are either no more
routines which are interested or the packet runs out of stack.
If a routine is invoked to complete the packet for a specific driver
which needs to perform work a lot of work or the work needs to be
performed in the context of another process, then the routine will
return an alternate success code of STATUS_MORE_PROCESSING_REQUIRED.
This indicates that this completion routine should simply return to
its caller because the operation will be "completed" by this routine
again sometime in the future.
2. A check is made to determine whether this IRP is an associated IRP.
If it is, then the count on the master IRP is decremented. If the
count for the master becomes zero, then the master IRP will be
completed according to the steps below taken for a normal IRP being
completed. If the count is still non-zero, then this IRP (the one
being completed) will simply be deallocated.
3. If this is paging I/O or a close operation, then simply write the
I/O status block and set the event to the signaled state, and
dereference the event. If this is paging I/O, deallocate the IRP
as well.
4. Unlock the pages, if any, specified by the MDL by calling
MmUnlockPages.
5. A check is made to determine whether or not completion of the
request can be deferred until later. If it can be, then this
routine simply exits and leaves it up to the originator of the
request to fully complete the IRP. By not initializing and queueing
the special kernel APC to the calling thread (which is the current
thread by definition), a lot of interrupt and queueing processing
can be avoided.
6. The final rundown routine is invoked to queue the request packet to
the target (requesting) thread as a special kernel mode APC.
Arguments:
Irp - Pointer to the I/O Request Packet to complete.
PriorityBoost - Supplies the amount of priority boost that is to be given
to the target thread when the special kernel APC is queued.
Return Value:
None.
--*/
#define ZeroIrpStackLocation( IrpSp ) { \
(IrpSp)->MinorFunction = 0; \
(IrpSp)->Flags = 0; \
(IrpSp)->Control = 0; \
(IrpSp)->Parameters.Others.Argument1 = 0; \
(IrpSp)->Parameters.Others.Argument2 = 0; \
(IrpSp)->Parameters.Others.Argument3 = 0; \
(IrpSp)->Parameters.Others.Argument4 = 0; \
(IrpSp)->FileObject = (PFILE_OBJECT) NULL; }
{
PIRP masterIrp;
NTSTATUS status;
PIO_STACK_LOCATION stackPointer;
PMDL mdl;
PETHREAD thread;
PFILE_OBJECT fileObject;
KIRQL irql;
//
// Begin by ensuring that this packet has not already been completed
// by someone.
//
if (Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1)) {
KeBugCheckEx( MULTIPLE_IRP_COMPLETE_REQUESTS, (ULONG) Irp, 0, 0, 0 );
}
//
// Ensure that the packet being completed really is still an IRP.
//
ASSERT( Irp->Type == IO_TYPE_IRP );
//
// Ensure that no one believes that this request is still in a cancellable
// state.
//
ASSERT( !Irp->CancelRoutine );
//
// Ensure that the packet is not being completed with a thoroughly
// confusing status code. Actually completing a packet with a pending
// status probably means that someone forgot to set the real status in
// the packet.
//
ASSERT( Irp->IoStatus.Status != STATUS_PENDING );
//
// Ensure that the packet is not being completed with a minus one. This
// is apparently a common problem in some drivers, and has no meaning
// as a status code.
//
ASSERT( Irp->IoStatus.Status != 0xffffffff );
//
// Ensure that if this is a paging I/O operation, and it failed, that the
// reason for the failure isn't because quota was exceeded.
//
ASSERT( !(Irp->Flags & IRP_PAGING_IO && Irp->IoStatus.Status == STATUS_QUOTA_EXCEEDED ) );
//
// Now check to see whether this is the last driver that needs to be
// invoked for this packet. If not, then bump the stack and check to
// see whether the driver wishes to see the completion. As each stack
// location is examined, invoke any routine which needs to be invoked.
// If the routine returns STATUS_MORE_PROCESSING_REQUIRED, then stop the
// processing of this packet.
//
for (stackPointer = IoGetCurrentIrpStackLocation( Irp ),
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++;
Irp->CurrentLocation <= (CCHAR) (Irp->StackCount + 1);
stackPointer++,
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++) {
//
// A stack location was located. Check to see whether or not it
// has a completion routine and if so, whether or not it should be
// invoked.
//
// Begin by saving the pending returned flag in the current stack
// location in the fixed part of the IRP.
//
Irp->PendingReturned = stackPointer->Control & SL_PENDING_RETURNED;
if ( (NT_SUCCESS( Irp->IoStatus.Status ) &&
stackPointer->Control & SL_INVOKE_ON_SUCCESS) ||
(!NT_SUCCESS( Irp->IoStatus.Status ) &&
stackPointer->Control & SL_INVOKE_ON_ERROR) ||
(Irp->Cancel &&
stackPointer->Control & SL_INVOKE_ON_CANCEL)
) {
//
// This driver has specified a completion routine. Invoke the
// routine passing it a pointer to its device object and the
// IRP that is being completed.
//
ZeroIrpStackLocation( stackPointer );
status = stackPointer->CompletionRoutine( (PDEVICE_OBJECT) (Irp->CurrentLocation == (CCHAR) (Irp->StackCount + 1) ?
(PDEVICE_OBJECT) NULL :
IoGetCurrentIrpStackLocation( Irp )->DeviceObject),
Irp,
stackPointer->Context );
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
//
// Note: Notice that if the driver has returned the above
// status value, it may have already DEALLOCATED the
// packet! Therefore, do NOT touch any part of the
// IRP in the following code.
//
return;
}
} else {
if (Irp->PendingReturned && Irp->CurrentLocation <= Irp->StackCount) {
IoMarkIrpPending( Irp );
}
ZeroIrpStackLocation( stackPointer );
}
}
//
// Check to see whether this is an associated IRP. If so, then decrement
// the count in the master IRP. If the count is decremented to zero,
// then complete the master packet as well.
//
if (Irp->Flags & IRP_ASSOCIATED_IRP) {
ULONG count;
masterIrp = Irp->AssociatedIrp.MasterIrp;
count = ExInterlockedAddUlong( (PULONG) &masterIrp->AssociatedIrp.IrpCount,
0xffffffff,
&IopDatabaseLock );
//
// Deallocate this packet and any MDLs that are associated with it
// by either doing direct deallocations if they were allocated from
// a zone or by queueing the packet to a thread to perform the
// deallocation.
//
// Also, check the count of the master IRP to determine whether or not
// the count has gone to zero. If not, then simply get out of here.
// Otherwise, complete the master packet.
//
Irp->Tail.Overlay.Thread = masterIrp->Tail.Overlay.Thread;
IopFreeIrpAndMdls( Irp );
if (count == 1) {
IoCompleteRequest( masterIrp, PriorityBoost );
}
return;
}
//
// Check the auxiliary buffer pointer in the packet and if a buffer was
// allocated, deallocate it now. Note that this buffer must be freed
// here since the pointer is overlayed with the APC that will be used
// to get to the requesting thread's context.
//
if (Irp->Tail.Overlay.AuxiliaryBuffer) {
ExFreePool( Irp->Tail.Overlay.AuxiliaryBuffer );
}
//
// Check to see if this is paging I/O or a close operation. If either,
// then special processing must be performed. The reasons that special
// processing must be performed is different based on the type of
// operation being performed. The biggest reasons for special processing
// on paging operations are that using a special kernel APC for an in-
// page operation cannot work since the special kernel APC can incur
// another pagefault. Likewise, all paging I/O uses MDLs that belong
// to the memory manager, not the I/O system.
//
// Close operations are special because the close may have been invoked
// because of a special kernel APC (some IRP was completed which caused
// the reference count on the object to become zero while in the I/O
// system's special kernel APC routine). Therefore, a special kernel APC
// cannot be used since it cannot execute until the close APC finishes.
//
// The special steps are as follows for a synchronous paging operation
// and close are:
//
// 1. Copy the I/O status block (it is in SVAS, nonpaged).
// 2. Signal the event
// 3. If paging I/O, deallocate the IRP
//
// The special steps taken for asynchronous paging operations (out-pages)
// are as follows:
//
// 1. Initialize a special kernel APC just for page writes.
// 1. Queue the special kernel APC.
//
// It should also be noted that the logic for completing a Mount request
// operation is exactly the same as a Page Read. No assumptions should be
// made here about this being a Page Read operation w/o carefully checking
// to ensure that they are also true for a Mount. That is:
//
// IRP_PAGING_IO and IRP_MOUNT_COMPLETION
//
// are the same flag in the IRP.
//
// Also note that the last time the IRP is touched for a close operation
// must be just before the event is set to the signaled state. Once this
// occurs, the IRP can be deallocated by the thread waiting for the event.
//
if (Irp->Flags & (IRP_PAGING_IO | IRP_CLOSE_OPERATION)) {
if (Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_CLOSE_OPERATION)) {
ULONG flags;
flags = Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO;
*Irp->UserIosb = Irp->IoStatus;
(VOID) KeSetEvent( Irp->UserEvent, PriorityBoost, FALSE );
if (flags) {
IoFreeIrp( Irp );
}
} else {
thread = Irp->Tail.Overlay.Thread;
KeInitializeApc( &Irp->Tail.Apc,
&thread->Tcb,
Irp->ApcEnvironment,
IopCompletePageWrite,
(PKRUNDOWN_ROUTINE) NULL,
(PKNORMAL_ROUTINE) NULL,
KernelMode,
(PVOID) NULL );
(VOID) KeInsertQueueApc( &Irp->Tail.Apc,
(PVOID) NULL,
(PVOID) NULL,
PriorityBoost );
}
return;
}
//
// Check to see whether any pages need to be unlocked.
//
if (Irp->MdlAddress != NULL) {
//
// Unlock any pages that may be described by MDLs.
//
mdl = Irp->MdlAddress;
while (mdl != NULL) {
MmUnlockPages( mdl );
mdl = mdl->Next;
}
}
//
// Make a final check here to determine whether or not this is a
// synchronous I/O operation that is being completed in the context
// of the original requestor. If so, then an optimal path through
// I/O completion can be taken.
//
if (Irp->Flags & IRP_DEFER_IO_COMPLETION && !Irp->PendingReturned) {
return;
}
//
// Finally, initialize the IRP as an APC structure and queue the special
// kernel APC to the target thread.
//
if (!Irp->Cancel) {
thread = Irp->Tail.Overlay.Thread;
fileObject = Irp->Tail.Overlay.OriginalFileObject;
KeInitializeApc( &Irp->Tail.Apc,
&thread->Tcb,
Irp->ApcEnvironment,
IopCompleteRequest,
IopAbortRequest,
(PKNORMAL_ROUTINE) NULL,
KernelMode,
(PVOID) NULL );
(VOID) KeInsertQueueApc( &Irp->Tail.Apc,
fileObject,
(PVOID) NULL,
PriorityBoost );
} else {
//
// This request has been cancelled. Ensure that access to the thread
// is synchronized, otherwise it may go away while attempting to get
// through the remainder of completion for this request. This happens
// when the thread times out waiting for the request to be completed
// once it has been cancelled.
//
// Note that it is safe to capture the thread pointer above, w/o having
// the lock because the cancel flag was not set at that point, and
// the code that disassociates IRPs must set the flag before looking to
// see whether or not the packet has been completed, and this packet
// will appear to be completed because it no longer belongs to a driver.
//
ExAcquireSpinLock( &IopCompletionLock, &irql );
thread = Irp->Tail.Overlay.Thread;
fileObject = Irp->Tail.Overlay.OriginalFileObject;
if (thread) {
KeInitializeApc( &Irp->Tail.Apc,
&thread->Tcb,
Irp->ApcEnvironment,
IopCompleteRequest,
IopAbortRequest,
(PKNORMAL_ROUTINE) NULL,
KernelMode,
(PVOID) NULL );
(VOID) KeInsertQueueApc( &Irp->Tail.Apc,
fileObject,
(PVOID) NULL,
PriorityBoost );
ExReleaseSpinLock( &IopCompletionLock, irql );
} else {
//
// This request has been aborted from completing in the caller's
// thread. This can only occur if the packet was cancelled, and
// the driver did not complete the request, so it was timed out.
// Attempt to drop things on the floor, since the originating thread
// has probably exited at this point.
//
ExReleaseSpinLock( &IopCompletionLock, irql );
ASSERT( Irp->Cancel );
//
// Drop the IRP on the floor.
//
IopDropIrp( Irp, fileObject );
}
}
}
NTSTATUS
IoConnectInterrupt(
OUT PKINTERRUPT *InterruptObject,
IN PKSERVICE_ROUTINE ServiceRoutine,
IN PVOID ServiceContext,
IN PKSPIN_LOCK SpinLock OPTIONAL,
IN ULONG Vector,
IN KIRQL Irql,
IN KIRQL SynchronizeIrql,
IN KINTERRUPT_MODE InterruptMode,
IN BOOLEAN ShareVector,
IN KAFFINITY ProcessorEnableMask,
IN BOOLEAN FloatingSave
)
/*++
Routine Description:
This routine allocates, initializes, and connects interrupt objects for
all of the processors specified in the processor enable mask.
Arguments:
InterruptObject - Address of a variable to receive a pointer to the first
interrupt object allocated and initialized.
ServiceRoutine - Address of the interrupt service routine (ISR) that should
be executed when the interrupt occurs.
ServiceContext - Supplies a pointer to the context information required
by the ISR.
SpinLock - Supplies a pointer to a spin lock to be used when synchronizing
with the ISR.
Vector - Supplies the vector upon which the interrupt occurs.
Irql - Supplies the IRQL upon which the interrupt occurs.
SynchronizeIrql - The request priority that the interrupt should be
synchronized with.
InterruptMode - Specifies the interrupt mode of the device.
ShareVector - Supplies a boolean value that specifies whether the
vector can be shared with other interrupt objects or not. If FALSE
then the vector may not be shared, if TRUE it may be.
Latched.
ProcessorEnableMask - Specifies a bit-vector for each processor on which
the interrupt is to be connected. A value of one in the bit position
cooresponding to the processor number indicates that the interrupt
should be allowed on that processor. At least one bit must be set.
FloatingSave - A BOOLEAN that specifies whether or not the machine's
floating point state should be saved before invoking the ISR.
Return Value:
The function value is the final function status. The three status values
that this routine can itself return are:
STATUS_SUCCESS - Everything worked successfully.
STATUS_INVALID_PARAMETER - No processors were specified.
STATUS_INSUFFICIENT_RESOURCES - There was not enough nonpaged pool.
--*/
{
CCHAR count;
BOOLEAN builtinUsed;
PKINTERRUPT interruptObject;
ULONG processorMask;
NTSTATUS status;
PIO_INTERRUPT_STRUCTURE interruptStructure;
PKSPIN_LOCK spinLock;
PAGED_CODE();
//
// Initialize the return pointer and assume success.
//
*InterruptObject = (PKINTERRUPT) NULL;
status = STATUS_SUCCESS;
//
// Determine how much memory is to be allocated based on how many
// processors this system may have and how many bits are set in the
// processor enable mask.
//
processorMask = ProcessorEnableMask & KeActiveProcessors;
count = 0;
while (processorMask) {
if (processorMask & 1) {
count++;
}
processorMask >>= 1;
}
//
// If any interrupt objects are to be allocated and initialized, allocate
// the memory now.
//
if (count) {
interruptStructure = ExAllocatePoolWithTag( NonPagedPool,
((count - 1) * sizeof( KINTERRUPT )) +
sizeof( IO_INTERRUPT_STRUCTURE ),
'nioI' );
if (interruptStructure == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
status = STATUS_INVALID_PARAMETER;
}
//
// If the caller specified a spin lock to be used for the interrupt object,
// use it. Otherwise, provide one by using the one in the structure that
// was just allocated.
//
if (ARGUMENT_PRESENT( SpinLock )) {
spinLock = SpinLock;
} else {
spinLock = &interruptStructure->SpinLock;
}
//
// If the pool allocation was successful, initialize and connect the
// interrupt objects to the appropriate processors.
//
if (NT_SUCCESS( status )) {
//
// Return the address of the first interrupt object in case an
// interrupt is pending for the device when it is initially connected
// and the driver must synchronize its execution with the ISR.
//
*InterruptObject = &interruptStructure->InterruptObject;
//
// Begin by getting a pointer to the start of the memory to be used
// for interrupt objects other than the builtin object.
//
interruptObject = (PKINTERRUPT) (interruptStructure + 1);
builtinUsed = FALSE;
processorMask = ProcessorEnableMask & KeActiveProcessors;
//
// Now zero the interrupt structure itself so that if something goes
// wrong it can be backed out.
//
RtlZeroMemory( interruptStructure, sizeof( IO_INTERRUPT_STRUCTURE ) );
//
// For each entry in the processor enable mask that is set, connect
// and initialize an interrupt object. The first bit that is set
// uses the builtin interrupt object, and all others use the pointers
// that follow it.
//
for (count = 0; processorMask; count++, processorMask >>= 1) {
if (processorMask & 1) {
KeInitializeInterrupt( builtinUsed ?
interruptObject :
&interruptStructure->InterruptObject,
ServiceRoutine,
ServiceContext,
spinLock,
Vector,
Irql,
SynchronizeIrql,
InterruptMode,
ShareVector,
count,
FloatingSave );
if (!KeConnectInterrupt( builtinUsed ?
interruptObject :
&interruptStructure->InterruptObject )) {
//
// An error occurred while attempting to connect the
// interrupt. This means that the driver either specified
// the wrong type of interrupt mode, or attempted to connect
// to some processor that didn't exist, or whatever. In
// any case, the problem turns out to be an invalid
// parameter was specified. Simply back everything out
// and return an error status.
//
// Note that if the builtin entry was used, then the entire
// structure needs to be walked as there are entries that
// were successfully connected. Otherwise, the first
// attempt to connect failed, so simply free everything
// in-line.
//
if (builtinUsed) {
IoDisconnectInterrupt( &interruptStructure->InterruptObject );
} else {
ExFreePool( interruptStructure );
}
status = STATUS_INVALID_PARAMETER;
break;
}
//
// If the builtin entry has been used, then the interrupt
// object just connected was one of the pointers, so fill
// it in with the address of the memory actually used.
//
if (builtinUsed) {
interruptStructure->InterruptArray[count] = interruptObject++;
} else {
//
// Otherwise, the builtin entry was used, so indicate
// that it is no longer valid to use and start using
// the pointers instead.
//
builtinUsed = TRUE;
}
}
}
}
//
// Finally, reset the address of the interrupt object if the function
// failed and return the final status of the operation.
//
if (!NT_SUCCESS( status )) {
*InterruptObject = (PKINTERRUPT) NULL;
}
return status;
}
PCONTROLLER_OBJECT
IoCreateController(
IN ULONG Size
)
/*++
Routine Description:
This routine creates a controller object that can be used to synchronize
access to a physical device controller from two or more devices.
Arguments:
Size - Size of the adapter extension in bytes.
Return Value:
A pointer to the controller object that was created or a NULL pointer.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
PCONTROLLER_OBJECT controllerObject;
HANDLE handle;
NTSTATUS status;
PAGED_CODE();
//
// Initialize the object attributes structure in preparation for creating
// the controller object.
//
InitializeObjectAttributes( &objectAttributes,
(PUNICODE_STRING) NULL,
0,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
//
// Create the controller object itself.
//
status = ObCreateObject( KernelMode,
IoControllerObjectType,
&objectAttributes,
KernelMode,
(PVOID) NULL,
(ULONG) sizeof( CONTROLLER_OBJECT ) + Size,
0,
0,
(PVOID *) &controllerObject );
if (NT_SUCCESS( status )) {
//
// Insert the controller object into the table.
//
status = ObInsertObject( controllerObject,
NULL,
FILE_READ_DATA | FILE_WRITE_DATA,
1,
(PVOID *) &controllerObject,
&handle );
//
// If the insert operation fails, set return value to NULL.
//
if (!NT_SUCCESS( status )) {
controllerObject = (PCONTROLLER_OBJECT) NULL;
} else {
//
// The insert completed successfully. Close the handle so that if
// the driver is unloaded, the controller object can go away.
//
(VOID) NtClose( handle );
//
// Zero the memory for the controller object.
//
RtlZeroMemory( controllerObject, sizeof( CONTROLLER_OBJECT ) + Size );
//
// Set the type and size of this controller object.
//
controllerObject->Type = IO_TYPE_CONTROLLER;
controllerObject->Size = (USHORT) (sizeof( CONTROLLER_OBJECT ) + Size);
controllerObject->ControllerExtension = (PVOID) (controllerObject + 1);
//
// Finally, initialize the controller's device queue.
//
KeInitializeDeviceQueue( &controllerObject->DeviceWaitQueue );
}
} else {
controllerObject = (PCONTROLLER_OBJECT) NULL;
}
return controllerObject;
}
VOID
IopInsertRemoveDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Insert
)
{
KIRQL irql;
ExAcquireSpinLock( &IopDatabaseLock, &irql );
if (Insert) {
DeviceObject->NextDevice = DriverObject->DeviceObject;
DriverObject->DeviceObject = DeviceObject;
}
else {
PDEVICE_OBJECT *prevPoint;
prevPoint = &DeviceObject->DriverObject->DeviceObject;
while (*prevPoint != DeviceObject) {
prevPoint = &(*prevPoint)->NextDevice;
}
*prevPoint = DeviceObject->NextDevice;
}
ExReleaseSpinLock( &IopDatabaseLock, irql );
}
NTSTATUS
IoCreateDevice(
IN PDRIVER_OBJECT DriverObject,
IN ULONG DeviceExtensionSize,
IN PUNICODE_STRING DeviceName OPTIONAL,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Exclusive,
OUT PDEVICE_OBJECT *DeviceObject
)
/*++
Routine Description:
This routine creates a device object and links it into the I/O database.
Arguments:
DriverObject - A pointer to the driver object for this device.
DeviceExtensionSize - Size, in bytes, of extension to device object;
i.e., the size of the driver-specific data for this device object.
DeviceName - Optional name that should be associated with this device.
DeviceType - The type of device that the device object should represent.
DeviceCharacteristics - The characteristics for the device.
Exclusive - Indicates that the device object should be created with using
the exclusive object attribute.
DeviceObject - Pointer to the device object pointer we will return.
Return Value:
The function value is the final status of the operation.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
PDEVICE_OBJECT deviceObject;
PDEVOBJ_EXTENSION deviceObjectExt;
PVPB vpb = (PVPB) NULL;
HANDLE handle;
BOOLEAN deviceHasName;
CHAR localSecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH];
PSECURITY_DESCRIPTOR securityDescriptor;
PACL acl;
PACE_HEADER ace;
PSID sid;
PACCESS_MASK mask;
ULONG i;
ULONG RoundedSize;
NTSTATUS status;
USHORT sectorSize = 0;
#if DBG
BOOLEAN aceFound;
#endif
PAGED_CODE();
acl = NULL;
//
// Remember whether or not this device was created with a name so that
// it can be deallocated later.
//
deviceHasName = (BOOLEAN) (ARGUMENT_PRESENT( DeviceName ) ? TRUE : FALSE);
//
// Detmermine whether or not this device needs to have a security descriptor
// placed on it that allows read/write access, or whether the system default
// should be used. Disks, virtual disks, and file systems simply use the
// system default descriptor. All others allow read/write access.
//
// NOTE: This routine assumes that it is in the system's security context.
// In particular, it assumes that the Default DACL is the system's
// Default DACL. If this assumption changes in future releases,
// then use of the Default DACL below should be reviewed for
// appropriateness.
//
switch ( DeviceType ) {
case FILE_DEVICE_DISK_FILE_SYSTEM:
sectorSize = 512;
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
if (!sectorSize) {
sectorSize = 2048;
}
case FILE_DEVICE_FILE_SYSTEM:
case FILE_DEVICE_TAPE_FILE_SYSTEM:
//
// Use the standard public default protection for these types of devices.
//
(VOID) RtlCreateSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION );
(VOID) RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor,
TRUE,
SePublicDefaultDacl,
FALSE );
securityDescriptor = (PSECURITY_DESCRIPTOR) &localSecurityDescriptor;
break;
case FILE_DEVICE_DISK:
case FILE_DEVICE_VIRTUAL_DISK:
sectorSize = 512;
case FILE_DEVICE_NETWORK_FILE_SYSTEM:
case FILE_DEVICE_NETWORK:
if (deviceHasName) {
if (DeviceCharacteristics & FILE_FLOPPY_DISKETTE) {
goto put_acl_on_floppy;
}
}
//
// Protect the device so that an administrator can run chkdsk on it.
// This is done by making a copy of the default public ACL and
// changing the accesses granted to the administrators alias.
//
// The logic here is:
//
// - Copy the public default dacl into another buffer
//
// - Find the ACE granting ADMINISTRATORS access
//
// - Change the granted access mask of that ACE to give
// administrators write access.
//
//
acl = ExAllocatePoolWithTag( PagedPool,
SePublicDefaultDacl->AclSize,
'eSoI' );
if (!acl) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory( acl, SePublicDefaultDacl, SePublicDefaultDacl->AclSize );
//
// Find the Administrators ACE
//
#if DBG
aceFound = FALSE;
#endif
for ( i = 0, ace = (PACE_HEADER) ((PVOID) ((PUCHAR) (acl) + sizeof( ACL )));
i < acl->AceCount ;
i++,
ace = ((PVOID) ((PUCHAR) (ace) +
((PACE_HEADER) (ace))->AceSize))
) {
sid = &((PACCESS_ALLOWED_ACE) ace)->SidStart;
if (RtlEqualSid( SeAliasAdminsSid, sid )) {
mask = &((PACCESS_ALLOWED_ACE) ace)->Mask;
(*mask) |= (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
#if DBG
aceFound = TRUE;
#endif
}
}
//
// If the ACE wasn't found, then the public default ACL has been
// changed. For this case, this code needs to be updated to match
// the new public default DACL.
//
#if DBG
ASSERT(aceFound == TRUE);
#endif
//
// Finally, build a full security descriptor from the above DACL.
//
(VOID) RtlCreateSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION );
(VOID) RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor,
TRUE,
acl,
FALSE );
securityDescriptor = (PSECURITY_DESCRIPTOR) &localSecurityDescriptor;
break;
default:
status = RtlCreateSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION );
ASSERT( NT_SUCCESS( status ) );
status = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor,
TRUE,
SePublicOpenDacl,
FALSE );
securityDescriptor = (PSECURITY_DESCRIPTOR) &localSecurityDescriptor;
break;
put_acl_on_floppy:
status = RtlCreateSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION );
ASSERT( NT_SUCCESS( status ) );
status = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor,
TRUE,
(PACL) NULL, //Grants WORLD:ALL
FALSE );
securityDescriptor = (PSECURITY_DESCRIPTOR) &localSecurityDescriptor;
}
//
// Initialize the object attributes structure in preparation for creating
// device object. Note that the device may be created as an exclusive
// device so that only one open can be done to it at a time. This saves
// single user devices from having drivers that implement special code to
// make sure that only one connection is ever valid at any given time.
//
InitializeObjectAttributes( &objectAttributes,
DeviceName,
0,
(HANDLE) NULL,
securityDescriptor );
if (Exclusive) {
objectAttributes.Attributes |= OBJ_EXCLUSIVE;
} else {
objectAttributes.Attributes |= 0;
}
if (deviceHasName) {
objectAttributes.Attributes |= OBJ_PERMANENT;
}
//
// Create the device object itself.
//
RoundedSize = (sizeof( DEVICE_OBJECT ) + DeviceExtensionSize)
% sizeof (LONGLONG);
if (RoundedSize) {
RoundedSize = sizeof (LONGLONG) - RoundedSize;
}
RoundedSize += DeviceExtensionSize;
status = ObCreateObject( KernelMode,
IoDeviceObjectType,
&objectAttributes,
KernelMode,
(PVOID) NULL,
(ULONG) sizeof( DEVICE_OBJECT ) + sizeof ( DEVOBJ_EXTENSION ) +
RoundedSize,
0,
0,
(PVOID *) &deviceObject );
if (!NT_SUCCESS( status )) {
//
// Creating the device object was not successful. Clean everything
// up and indicate that the object was not created.
//
deviceObject = (PDEVICE_OBJECT) NULL;
} else {
//
// The device was successfully created. Initialize the object so
// that it can be inserted into the object table. Begin by zeroing
// the memory for the device object.
//
RtlZeroMemory( deviceObject,
sizeof( DEVICE_OBJECT ) + sizeof ( DEVOBJ_EXTENSION ) +
RoundedSize );
//
// Fill in deviceObject & deviceObjectExtension cross pointers
//
deviceObjectExt = (PDEVOBJ_EXTENSION) (((PCHAR) deviceObject) +
sizeof (DEVICE_OBJECT) + RoundedSize);
deviceObjectExt->DeviceObject = deviceObject;
deviceObject->DeviceObjectExtension = deviceObjectExt;
//
// Initialize deviceObjectExt
// Note: the size of a Device Object Extention is initialized specifically
// to ZERO so no driver will depend on it.
//
deviceObjectExt->Type = IO_TYPE_DEVICE_OBJECT_EXTENSION;
deviceObjectExt->Size = 0;
//
// Set the type and size of this device object.
//
deviceObject->Type = IO_TYPE_DEVICE;
deviceObject->Size = (USHORT) (sizeof( DEVICE_OBJECT ) + DeviceExtensionSize);
//
// Set the device type field in the object so that later code can
// check the type. Likewise, set the device characteristics.
//
deviceObject->DeviceType = DeviceType;
deviceObject->Characteristics = DeviceCharacteristics;
//
// If this device is either a tape or a disk, allocate a Volume
// Parameter Block (VPB) which indicates that the volume has
// never been mounted, and set the device object's VPB pointer to
// it.
//
if ((DeviceType == FILE_DEVICE_DISK) ||
(DeviceType == FILE_DEVICE_TAPE) ||
(DeviceType == FILE_DEVICE_CD_ROM) ||
(DeviceType == FILE_DEVICE_VIRTUAL_DISK)) {
vpb = ExAllocatePoolWithTag( NonPagedPoolMustSucceed,
sizeof( VPB ),
' bpV' );
ASSERT( vpb != NULL );
RtlZeroMemory( vpb, sizeof( VPB ) );
vpb->Type = IO_TYPE_VPB;
vpb->Size = sizeof( VPB );
vpb->RealDevice = deviceObject;
deviceObject->Vpb = vpb;
KeInitializeEvent( &deviceObject->DeviceLock,
SynchronizationEvent,
TRUE );
}
#ifdef _PNP_POWER_
PoInitializeDeviceObject (deviceObject);
#endif // _PNP_POWER_
//
// Initialize the remainder of the device object.
//
deviceObject->AlignmentRequirement = HalGetDmaAlignmentRequirement() - 1;
deviceObject->SectorSize = sectorSize;
deviceObject->Flags = DO_DEVICE_INITIALIZING;
if (Exclusive) {
deviceObject->Flags |= DO_EXCLUSIVE;
}
if (deviceHasName) {
deviceObject->Flags |= DO_DEVICE_HAS_NAME;
}
deviceObject->DeviceExtension = deviceObject + 1;
deviceObject->StackSize = 1;
switch ( DeviceType ) {
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
case FILE_DEVICE_DISK_FILE_SYSTEM:
case FILE_DEVICE_FILE_SYSTEM:
case FILE_DEVICE_NETWORK_FILE_SYSTEM:
case FILE_DEVICE_TAPE_FILE_SYSTEM:
//
// This device represents a file system of some sort. Simply
// initialize the queue list head in the device object.
//
InitializeListHead( &deviceObject->Queue.ListEntry );
break;
default:
//
// This is a real device of some sort. Allocate a spin lock
// and initialize the device queue object in the device object.
//
KeInitializeDeviceQueue( &deviceObject->DeviceQueue );
break;
}
//
// Insert the device object into the table.
//
status = ObInsertObject( deviceObject,
NULL,
FILE_READ_DATA | FILE_WRITE_DATA,
1,
(PVOID *) &deviceObject,
&handle );
if (NT_SUCCESS( status )) {
//
// The insert completed successfully. Link the device object
// and driver objects together. Close the handle so that if
// the driver is unloaded, the device object can go away.
//
deviceObject->DriverObject = DriverObject;
IopInsertRemoveDevice( DriverObject, deviceObject, TRUE );
(VOID) NtClose( handle );
#if _PNP_POWER_
//
// If the newly created device is named we will write the
// name to the NtDevicePaths under its ServiceKey
//
if (deviceObject->Flags & DO_DEVICE_HAS_NAME) {
HANDLE enumHandle;
if (DriverObject->DriverExtension->ServiceKeyName.Length != 0 &&
NT_SUCCESS(IopOpenServiceEnumKeys(
&DriverObject->DriverExtension->ServiceKeyName,
KEY_ALL_ACCESS,
NULL,
&enumHandle,
FALSE
))) {
//
// Append the DeviceObjectName to the NtDevicePaths
// value entry of its ServiceKey. First remove the
// string from NtDevicePaths in case there is a duplicate.
//
IopRemoveStringFromValueKey(enumHandle,
L"NtDevicePaths",
DeviceName
);
IopAppendStringToValueKey (enumHandle,
L"NtDevicePaths",
DeviceName,
TRUE
);
NtClose(enumHandle);
}
//
// If the append operation fails, we don't undo the CreateDevice
// operation because we still have one more chance when the device
// driver call IoRegisterDevicePath(). If the driver does not register
// the device path, it probably is not important enough to fail the
// CreateDevice.
//
}
#endif // PlugAndPlay
} else {
//
// The insert operation failed so everything must be undone.
// If a VPB was associated with the device object, deallocate
// it now.
//
if (vpb != NULL) {
ExFreePool( vpb );
}
//
// Finally, indicate that no device object was created.
//
deviceObject = (PDEVICE_OBJECT) NULL;
}
}
//
// Free the DACL if we allocated it...
//
if (acl != NULL) {
ExFreePool( acl );
}
*DeviceObject = deviceObject;
return status;
}
NTSTATUS
IoCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG Disposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength,
IN CREATE_FILE_TYPE CreateFileType,
IN PVOID ExtraCreateParameters OPTIONAL,
IN ULONG Options
)
/*++
Routine Description:
This is the common routine for both NtCreateFile and NtOpenFile to allow
a user to create or open a file. This procedure is also used internally
by kernel mode components, such as the network server, to perform the
same type of operation, but allows kernel mode code to force checking
arguments and access to the file, rather than bypassing these checks
because the code is running in kernel mode.
Arguments:
FileHandle - A pointer to a variable to receive the handle to the open
file.
DesiredAccess - Supplies the types of access that the caller would like
to the file.
ObjectAttributes - Supplies the attributes to be used for the file object
(name, SECURITY_DESCRIPTOR, etc.)
IoStatusBlock - Specifies the address of the caller's I/O status block.
AllocationSize - Initial size that should be allocated to the file.
This parameter only has an affect if the file is created. Further,
if not specified, then it is taken to mean zero.
FileAttr ibutes - Specifies the attributes that should be set on the file,
if it is created.
ShareAccess - Supplies the types of share access that the caller would
like to the file.
Disposition - Supplies the method for handling the create/open.
CreateOptions - Caller options for how to perform the create/open.
EaBuffer - Optionally specifies a set of EAs to be applied to the file
if it is created.
EaLength - Supplies the length of the EaBuffer.
CreateFileType - The type of file to create.
ExtraCreateParameters - Optionally specifies a pointer to extra create
parameters. The format of the parameters depends on the value of
CreateFileType.
Options - Specifies the options that are to be used during generation
of the create IRP.
Return Value:
The function value is the final status of the create/open operation.
Warning:
If a pointer to ExtraCreateParameters is passed the data must be
readable from kernel mode.
--*/
{
KPROCESSOR_MODE requestorMode;
NTSTATUS status;
HANDLE handle;
OPEN_PACKET openPacket;
BOOLEAN SuccessfulIoParse;
LARGE_INTEGER initialAllocationSize;
PAGED_CODE();
//
// Get the previous mode; i.e., the mode of the caller.
//
requestorMode = KeGetPreviousMode();
if (Options & IO_NO_PARAMETER_CHECKING) {
requestorMode = KernelMode;
}
if (requestorMode != KernelMode || Options & IO_CHECK_CREATE_PARAMETERS) {
//
// Check for any invalid parameters.
//
if (
//
// Check that no invalid file attributes flags were specified.
//
// (FileAttributes & ~FILE_ATTRIBUTE_VALID_SET_FLAGS)
(FileAttributes & ~FILE_ATTRIBUTE_VALID_FLAGS)
||
//
// Check that no invalid share access flags were specified.
//
(ShareAccess & ~FILE_SHARE_VALID_FLAGS)
||
//
// Ensure that the disposition value is in range.
//
(Disposition > FILE_MAXIMUM_DISPOSITION)
||
//
// Check that no invalid create options were specified.
//
(CreateOptions & ~FILE_VALID_OPTION_FLAGS)
||
//
// If the caller specified synchronous I/O, then ensure that
// (s)he also asked for synchronize desired access to the
// file.
//
(CreateOptions & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT) &&
(!(DesiredAccess & SYNCHRONIZE)))
||
//
// Also, if the caller specified that the file is to be deleted
// on close, then ensure that delete is specified as one of the
// desired accesses requested.
//
(CreateOptions & FILE_DELETE_ON_CLOSE &&
(!(DesiredAccess & DELETE)))
||
//
// Likewise, ensure that if one of the synchronous I/O modes
// is specified that the other one is not specified as well.
//
((CreateOptions & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)) ==
(FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT))
||
//
// If a storage type is specfied, check to make sure that it is within range
//
((CreateOptions & FILE_STORAGE_TYPE_SPECIFIED) == FILE_STORAGE_TYPE_SPECIFIED &&
((CreateOptions & FILE_STORAGE_TYPE_MASK) < FILE_MINIMUM_STORAGE_TYPE ||
(CreateOptions & FILE_STORAGE_TYPE_MASK) > FILE_MAXIMUM_STORAGE_TYPE))
||
//
// If this create or open is for a directory operation, check
// that all of the other flags, dispositions, and desired
// access parameters were also specified correctly.
//
// These are as follows:
//
// o No other flags other than the synchronous I/O flags,
// write-through, or open by file ID are set.
//
// o The disposition value is one of create, open, or
// open-if.
//
// o No non-directory accesses have been specified.
//
// Be careful since FILE_STORAGE_TYPE_SPECIFIED encompasses FILE_DIRECTORY_FILE as
// well
//
#if (FILE_STORAGE_TYPE_SPECIFIED != (FILE_DIRECTORY_FILE | FILE_NON_DIRECTORY_FILE))
#pragma message ("Error: FILE_STORAGE_TYPE inconsistent with")
#pragma message (" FILE_DIRECTORY_FILE and FILE_NON_DIRECTORY_FILE")
#endif
((CreateOptions & FILE_DIRECTORY_FILE)
&& !(CreateOptions & FILE_NON_DIRECTORY_FILE)
&& ((CreateOptions & ~(FILE_DIRECTORY_FILE |
FILE_SYNCHRONOUS_IO_ALERT |
FILE_SYNCHRONOUS_IO_NONALERT |
FILE_WRITE_THROUGH |
FILE_COMPLETE_IF_OPLOCKED |
FILE_OPEN_FOR_BACKUP_INTENT |
FILE_TRANSACTED_MODE |
FILE_DELETE_ON_CLOSE |
FILE_OPEN_BY_FILE_ID))
|| ((Disposition != FILE_CREATE)
&& (Disposition != FILE_OPEN)
&& (Disposition != FILE_OPEN_IF))
|| (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA))
)
)
||
//
// FILE_COMPLETE_IF_OPLOCK and FILE_RESERVE_OPFILTER are
// incompatible options.
//
((CreateOptions & FILE_COMPLETE_IF_OPLOCKED) &&
(CreateOptions & FILE_RESERVE_OPFILTER))
||
//
// Finally, if the no intermediate buffering option was
// specified, ensure that the caller did not also request
// append access to the file.
//
(CreateOptions & FILE_NO_INTERMEDIATE_BUFFERING &&
(DesiredAccess & FILE_APPEND_DATA)) ) {
return STATUS_INVALID_PARAMETER;
}
//
// Check the file type specific creation parameters.
//
if (CreateFileType == CreateFileTypeNone) {
NOTHING;
} else if (CreateFileType == CreateFileTypeNamedPipe) {
if (!ARGUMENT_PRESENT( ExtraCreateParameters ) ) {
return STATUS_INVALID_PARAMETER;
} else {
PNAMED_PIPE_CREATE_PARAMETERS NamedPipeCreateParameters;
NamedPipeCreateParameters = ExtraCreateParameters;
//
// Check the parameters for creating a named pipe to
// ensure that no invalid parameters were passed.
//
if (
//
// Check the NamedPipeType field to ensure that it
// is within range.
//
(NamedPipeCreateParameters->NamedPipeType >
FILE_PIPE_MESSAGE_TYPE)
||
//
// Check the ReadMode field to ensure that it is
// within range.
//
(NamedPipeCreateParameters->ReadMode >
FILE_PIPE_MESSAGE_MODE)
||
//
// Check the CompletionMode field to ensure that
// it is within range.
//
(NamedPipeCreateParameters->CompletionMode >
FILE_PIPE_COMPLETE_OPERATION)
||
//
// Check the ShareAccess parameter to ensure that
// it does not specify shared delete access. The
// Named Pipe File System itself will need to ensure
// that at least one of SHARE_READ or SHARE_WRITE
// is specified if the first instance of the pipe
// is being created.
//
(ShareAccess & FILE_SHARE_DELETE)
||
//
// Check the Disposition parameter to ensure that
// is does not specify anything other than create,
// open, or open if.
//
(Disposition < FILE_OPEN || Disposition > FILE_OPEN_IF)
||
//
// Finally, check the CreateOptions parameter to
// ensure that it does not contain any invalid
// options for named pipes.
//
(CreateOptions & ~FILE_VALID_PIPE_OPTION_FLAGS)) {
return STATUS_INVALID_PARAMETER;
}
}
} else if (CreateFileType == CreateFileTypeMailslot) {
if (!ARGUMENT_PRESENT( ExtraCreateParameters ) ) {
return STATUS_INVALID_PARAMETER;
} else {
PMAILSLOT_CREATE_PARAMETERS mailslotCreateParameters;
mailslotCreateParameters = ExtraCreateParameters;
//
// Check the parameters for creating a mailslot to ensure
// that no invalid parameters were passed.
//
if (
//
// Check the ShareAccess parameter to ensure that
// it does not specify shared delete access.
//
(ShareAccess & FILE_SHARE_DELETE)
||
//
// Check the ShareAccess parameter to ensure that
// shared write access is specified.
//
!(ShareAccess & ~FILE_SHARE_WRITE)
||
//
// Check the Disposition parameter to ensure that
// it specifies that the file is to be created.
//
(Disposition != FILE_CREATE)
||
//
// Check the CreateOptions parameter to ensure that
// it does not contain any options that are invalid
// for mailslots.
//
(CreateOptions & ~FILE_VALID_MAILSLOT_OPTION_FLAGS)) {
return STATUS_INVALID_PARAMETER;
}
}
}
}
if (requestorMode != KernelMode) {
//
// The caller's access mode is not kernel so probe each of the
// arguments and capture them as necessary. If any failures occur,
// the condition handler will be invoked to handle them. It will
// simply cleanup and return an access violation status code back
// to the system service dispatcher.
//
openPacket.EaBuffer = (PFILE_FULL_EA_INFORMATION) NULL;
try {
//
// The FileHandle parameter must be writeable by the caller.
// Probe it for a write operation.
//
ProbeAndWriteHandle( FileHandle, 0L );
//
// The IoStatusBlock parameter must be writeable by the caller.
//
ProbeForWriteIoStatus( IoStatusBlock );
//
// The AllocationSize parameter must be readable by the caller
// if it is present. If so, probe and capture it.
//
if (ARGUMENT_PRESENT( AllocationSize )) {
ProbeForRead( AllocationSize,
sizeof( LARGE_INTEGER ),
sizeof( ULONG ) );
initialAllocationSize = *AllocationSize;
} else {
initialAllocationSize.QuadPart = 0;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred while attempting to access the
// caller's parameters. Simply return the reason for the
// exception as the service status.
//
return GetExceptionCode();
}
//
// Finally, if an EaBuffer was specified, ensure that it is readable
// from the caller's mode and capture it.
//
if (ARGUMENT_PRESENT( EaBuffer ) && EaLength) {
ULONG errorOffset;
try {
ProbeForRead( EaBuffer, EaLength, sizeof( ULONG ) );
openPacket.EaBuffer = ExAllocatePoolWithQuotaTag( NonPagedPool,
EaLength,
'aEoI' );
openPacket.EaLength = EaLength;
RtlCopyMemory( openPacket.EaBuffer, EaBuffer, EaLength );
//
// Walk the buffer and ensure that its format is valid. Note
// that has been probed.
//
status = IoCheckEaBufferValidity( openPacket.EaBuffer,
EaLength,
&errorOffset );
if (!NT_SUCCESS( status )) {
IoStatusBlock->Status = status;
IoStatusBlock->Information = errorOffset;
ExRaiseStatus( status );
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// An exception was incurred while attempting to access the
// caller's parameters. Check to see whether or not an EA
// buffer was allocated and deallocate if so.
//
if (openPacket.EaBuffer != NULL) {
ExFreePool( openPacket.EaBuffer );
}
return GetExceptionCode();
}
} else {
//
// No EAs were specified.
//
openPacket.EaBuffer = (PVOID) NULL;
openPacket.EaLength = 0L;
}
} else {
//
// The caller's mode is kernel. Copy the input parameters to their
// expected locations for later use. Also, put move attach device
// flag where it belongs.
//
if (requestorMode == KernelMode) {
if (CreateOptions & IO_ATTACH_DEVICE_API) {
Options |= IO_ATTACH_DEVICE;
CreateOptions &= ~IO_ATTACH_DEVICE_API;
}
}
if (ARGUMENT_PRESENT( AllocationSize )) {
initialAllocationSize = *AllocationSize;
} else {
initialAllocationSize.QuadPart = 0;
}
if (ARGUMENT_PRESENT( EaBuffer ) && EaLength) {
openPacket.EaBuffer = ExAllocatePoolWithTag( NonPagedPool,
EaLength,
'aEoI' );
if (!openPacket.EaBuffer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
openPacket.EaLength = EaLength;
RtlCopyMemory( openPacket.EaBuffer, EaBuffer, EaLength );
} else {
openPacket.EaBuffer = (PVOID) NULL;
openPacket.EaLength = 0L;
}
}
//
// Now fill in an Open Packet (OP) to be used in calling the device object
// parse routine. This packet will allow information to be passed between
// this routine and the parse routine so that a common context may be kept.
// For most services this would be done with an I/O Request Packet (IRP),
// but this cannot be done here because the number of stack entries which
// need to be allocated in the IRP is not yet known.
//
openPacket.Type = IO_TYPE_OPEN_PACKET;
openPacket.Size = sizeof( OPEN_PACKET );
openPacket.ParseCheck = 0L;
openPacket.AllocationSize = initialAllocationSize;
openPacket.CreateOptions = CreateOptions;
openPacket.FileAttributes = (USHORT) FileAttributes;
openPacket.ShareAccess = (USHORT) ShareAccess;
openPacket.Disposition = Disposition;
openPacket.Override = FALSE;
openPacket.QueryOnly = FALSE;
openPacket.DeleteOnly = FALSE;
openPacket.Options = Options;
openPacket.RelatedFileObject = (PFILE_OBJECT) NULL;
openPacket.CreateFileType = CreateFileType;
openPacket.ExtraCreateParameters = ExtraCreateParameters;
//
// Assume that the operation is going to be successful.
//
openPacket.FinalStatus = STATUS_SUCCESS;
//
// Zero the file object field in the OP so the parse routine knows that
// this is the first time through. For reparse operations it will continue
// to use the same file object that it allocated the first time.
//
openPacket.FileObject = (PFILE_OBJECT) NULL;
//
// Update the open count for this process.
//
IopUpdateOtherOperationCount();
//
// Attempt to open the file object by name. This will yield the handle
// that the user is to use as his handle to the file in all subsequent
// calls, if it works.
//
// This call performs a whole lot of the work for actually getting every-
// thing set up for the I/O system. The object manager will take the name
// of the file and will translate it until it reaches a device object (or
// it fails). If the former, then it will invoke the parse routine set up
// by the I/O system for device objects. This routine will actually end
// up creating the file object, allocating an IRP, filling it in, and then
// invoking the driver's dispatch routine with the packet.
//
status = ObOpenObjectByName( ObjectAttributes,
(POBJECT_TYPE) NULL,
requestorMode,
NULL,
DesiredAccess,
&openPacket,
&handle );
//
// If an EA buffer was allocated, deallocate it now before attempting to
// determine whether or not the operation was successful so that it can be
// done in one place rather than in two places.
//
if (openPacket.EaBuffer) {
ExFreePool( openPacket.EaBuffer );
}
//
// Check the status of the open. If it was not successful, cleanup and
// get out. Notice that it is also possible, because this code does not
// explicitly request that a particular type of object (because the Object
// Manager does not check when a parse routine is present and because the
// name first refers to a device object and then a file object), a check
// must be made here to ensure that what was returned was really a file
// object. The check is to see whether the device object parse routine
// believes that it successfully returned a pointer to a file object. If
// it does, then OK; otherwise, something went wrong somewhere.
//
SuccessfulIoParse = (BOOLEAN) (openPacket.ParseCheck == OPEN_PACKET_PATTERN);
if (!NT_SUCCESS( status ) || !SuccessfulIoParse) {
if (NT_SUCCESS( status )) {
//
// The operation was successful as far as the object system is
// concerned, but the I/O system device parse routine was never
// successfully completed so this operation has actually completed
// with an error because of an object mismatch. Therefore, this is
// the wrong type of object so dereference whatever was actually
// referenced by closing the handle that was created for it.
//
NtClose( handle );
status = STATUS_OBJECT_TYPE_MISMATCH;
}
//
// If the final status according to the device parse routine
// indicates that the operation was not successful, then use that
// routine's final status because it is more descriptive than the
// status which was returned by the object manager.
//
if (!NT_SUCCESS( openPacket.FinalStatus )) {
status = openPacket.FinalStatus;
if (NT_WARNING( status )) {
try {
IoStatusBlock->Status = openPacket.FinalStatus;
IoStatusBlock->Information = openPacket.Information;
} except(EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
}
} else if (openPacket.FileObject != NULL && !SuccessfulIoParse) {
//
// Otherwise, one of two things occurred:
//
// 1) The parse routine was invoked at least once and a
// reparse was performed but the parse routine did not
// actually complete.
//
// 2) The parse routine was successful so everything worked
// but the object manager incurred an error after the
// parse routine completed.
//
// For case #1, there is an outstanding file object that still
// exists. This must be cleaned up.
//
// For case #2, nothing must be done as the object manager has
// already dereferenced the file object. Note that this code is
// not invoked if the parse routine completed with a successful
// status return (SuccessfulIoParse is TRUE).
//
if (openPacket.FileObject->FileName.Length != 0) {
ExFreePool( openPacket.FileObject->FileName.Buffer );
}
openPacket.FileObject->DeviceObject = (PDEVICE_OBJECT) NULL;
ObDereferenceObject( openPacket.FileObject );
}
} else {
//
// At this point, the open/create operation has been successfully
// completed. There is a handle to the file object, which has been
// created, and the file object has been signaled.
//
// The remaining work to be done is to complete the operation. This is
// performed as follows:
//
// 1. The file object has been signaled, so no work needs to be done
// for it.
//
// 2. The file handle is returned to the user.
//
// 3. The I/O status block is written with the final status.
//
openPacket.FileObject->Flags |= FO_HANDLE_CREATED;
ASSERT( openPacket.FileObject->Type == IO_TYPE_FILE );
try {
//
// Return the file handle.
//
*FileHandle = handle;
//
// Write the I/O status into the caller's buffer.
//
IoStatusBlock->Information = openPacket.Information;
IoStatusBlock->Status = openPacket.FinalStatus;
status = openPacket.FinalStatus;
} except(EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
#if DBG
if (RtlAreLogging( RTL_EVENT_CLASS_IO )) {
RtlLogEvent( IopCreateFileEventId,
RTL_EVENT_CLASS_IO,
ObjectAttributes->ObjectName,
DesiredAccess,
ShareAccess,
Disposition,
CreateOptions,
CreateFileType,
handle,
status,
openPacket.Information
);
}
#endif // DBG
}
return status;
}
PKEVENT
IoCreateNotificationEvent(
IN PUNICODE_STRING EventName,
OUT PHANDLE EventHandle
)
/*++
Routine Description:
This routine creates a named notification event for use in notifying
different system components or drivers that an event occurred.
Arguments:
EventName - Supplies the full name of the event.
EventHandle - Supplies a location to return a handle to the event.
Return Value:
The function value is a pointer to the created/opened event, or NULL if
the event could not be created/opened.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
NTSTATUS status;
HANDLE eventHandle;
PKEVENT eventObject;
PAGED_CODE();
//
// Begin by initializing the object attributes.
//
InitializeObjectAttributes( &objectAttributes,
EventName,
OBJ_OPENIF,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
//
// Now create or open the event.
//
status = ZwCreateEvent( &eventHandle,
EVENT_ALL_ACCESS,
&objectAttributes,
NotificationEvent,
TRUE );
if (!NT_SUCCESS( status )) {
return (PKEVENT) NULL;
}
//
// Reference the object by its handle to get a pointer that can be returned
// to the caller.
//
(VOID) ObReferenceObjectByHandle( eventHandle,
0,
ExEventObjectType,
KernelMode,
(PVOID *) &eventObject,
NULL );
ObDereferenceObject( eventObject );
//
// Return the handle and the pointer to the event.
//
*EventHandle = eventHandle;
return eventObject;
}
PFILE_OBJECT
IoCreateStreamFileObject(
IN PFILE_OBJECT FileObject OPTIONAL,
IN PDEVICE_OBJECT DeviceObject OPTIONAL
)
/*++
Routine Description:
This routine is invoked to create a new file object that represents an
alternate data stream for an existing file object. The input file object
represents the file object that already exists for a file, and the newly
created stream file object is used to access other parts of the file
other than the data. Some uses of stream file objects are the EAs or
the SECURITY_DESCRIPTORs on the file. The stream file object allows
the file system to cache these parts of the file just as if they were
an entire to themselves.
It is also possible to use stream file objects to represent virtual
volume files. This allows various parts of the on-disk structure to
be viewed as a virtual file and therefore be cached using the same logic
in the file system. For this case, the device object pointer is used
to create the file object.
Arguments:
FileObject - Pointer to the file object to which the new stream file
is related. This pointer is optional.
DeviceObject - Pointer to the device object on which the stream file
is to be opened. This pointer is not optional if the FileObject
pointer is not specified.
Return Value:
The function value is a pointer to the newly created stream file object.
--*/
{
PFILE_OBJECT newFileObject;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE handle;
NTSTATUS status;
PAGED_CODE();
//
// Begin by getting the device object from either the file object or
// the device object parameter.
//
if (ARGUMENT_PRESENT( FileObject )) {
DeviceObject = FileObject->DeviceObject;
}
//
// Increment the reference count for the target device object. Note
// that no check is made to determine whether or not the device driver
// is attempting to unload since this is an implicit open of a pseudo-
// file that is being made, not a real file open request. In essence,
// no new file is really being opened.
//
ExInterlockedAddUlong( &DeviceObject->ReferenceCount, 1, &IopDatabaseLock );
//
// Initialize the object attributes that will be used to create the file
// object.
//
InitializeObjectAttributes( &objectAttributes,
(PUNICODE_STRING) NULL,
0,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
//
// Create the new file object.
//
status = ObCreateObject( KernelMode,
IoFileObjectType,
&objectAttributes,
0,
(PVOID) NULL,
(ULONG) sizeof( FILE_OBJECT ),
(ULONG) sizeof( FILE_OBJECT ),
0,
(PVOID *) &newFileObject );
if (!NT_SUCCESS( status )) {
ExRaiseStatus( status );
}
//
// Initialize the common fields of the file object.
//
RtlZeroMemory( newFileObject, sizeof( FILE_OBJECT ) );
newFileObject->Type = IO_TYPE_FILE;
newFileObject->Size = sizeof( FILE_OBJECT );
newFileObject->DeviceObject = DeviceObject;
newFileObject->Flags = FO_STREAM_FILE;
KeInitializeEvent( &newFileObject->Event, SynchronizationEvent, FALSE );
//
// Insert the device object into the table. Note that this is done w/a
// pointer bias so that the object cannot go away if some random user
// application closes the handle before this code is finished w/it.
//
status = ObInsertObject( newFileObject,
NULL,
FILE_READ_DATA,
1,
(PVOID *) &newFileObject,
&handle );
if (!NT_SUCCESS( status )) {
ExRaiseStatus( status );
}
//
// The insert completed successfully. Update the bookkeeping so that the
// fact that there is a handle is reflected.
//
newFileObject->Flags |= FO_HANDLE_CREATED;
ASSERT( newFileObject->Type == IO_TYPE_FILE );
//
// Synchronize here with the file system to make sure that
// volumes don't go away while en route to the FS.
//
if (DeviceObject->Vpb) {
ExInterlockedAddUlong( &DeviceObject->Vpb->ReferenceCount,
1,
&IopVpbSpinLock );
}
//
// Finally, close the handle to the file. and clear the forward cluster
//
status = NtClose( handle );
ASSERT( NT_SUCCESS( status ) );
return newFileObject;
}
NTSTATUS
IoCreateSymbolicLink(
IN PUNICODE_STRING SymbolicLinkName,
IN PUNICODE_STRING DeviceName
)
/*++
Routine Description:
This routine is invoked to assign a symbolic link name to a device.
Arguments:
SymbolicLinkName - Supplies the symbolic link name as a Unicode string.
DeviceName - Supplies the name to which the symbolic link name refers.
Return Value:
The function value is the final status of the operation.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
HANDLE linkHandle;
NTSTATUS status;
PAGED_CODE();
//
// Begin by initializing the object attributes for the symbolic link.
//
InitializeObjectAttributes( &objectAttributes,
SymbolicLinkName,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
SePublicDefaultSd );
//
// Note that the following assignment can fail (because it is not system
// initialization time and therefore the \ARCname directory does not
// exist - if this is really a call to IoAssignArcName), but that is fine.
//
status = ZwCreateSymbolicLinkObject( &linkHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&objectAttributes,
DeviceName );
if (NT_SUCCESS( status )) {
ZwClose( linkHandle );
}
return status;
}
PKEVENT
IoCreateSynchronizationEvent(
IN PUNICODE_STRING EventName,
OUT PHANDLE EventHandle
)
/*++
Routine Description:
This routine creates a named synchronization event for use in serialization
of access to hardware between two otherwise non-related drivers. The event
is created if it does not already exist, otherwise it is simply opened.
Arguments:
EventName - Supplies the full name of the event.
EventHandle - Supplies a location to return a handle to the event.
Return Value:
The function value is a pointer to the created/opened event, or NULL if
the event could not be created/opened.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
NTSTATUS status;
HANDLE eventHandle;
PKEVENT eventObject;
PAGED_CODE();
//
// Begin by initializing the object attributes.
//
InitializeObjectAttributes( &objectAttributes,
EventName,
OBJ_OPENIF,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
//
// Now create or open the event.
//
status = ZwCreateEvent( &eventHandle,
EVENT_ALL_ACCESS,
&objectAttributes,
SynchronizationEvent,
TRUE );
if (!NT_SUCCESS( status )) {
return (PKEVENT) NULL;
}
//
// Reference the object by its handle to get a pointer that can be returned
// to the caller.
//
(VOID) ObReferenceObjectByHandle( eventHandle,
0,
ExEventObjectType,
KernelMode,
(PVOID *) &eventObject,
NULL );
ObDereferenceObject( eventObject );
//
// Return the handle and the pointer to the event.
//
*EventHandle = eventHandle;
return eventObject;
}
NTSTATUS
IoCreateUnprotectedSymbolicLink(
IN PUNICODE_STRING SymbolicLinkName,
IN PUNICODE_STRING DeviceName
)
/*++
Routine Description:
This routine is invoked to assign an unprotected symbolic link name to
a device. That is, a symbolic link that can be dynamically reassigned
without any special authorization.
NOTE: This routine will NOT over-ride inheritable protection that
the symbolic link might pick up. It simply prevents the caller's
default token protection from being assigned.
Arguments:
SymbolicLinkName - Supplies the symbolic link name as a Unicode string.
DeviceName - Supplies the name to which the symbolic link name refers.
Return Value:
The function value is the final status of the operation.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
HANDLE linkHandle;
NTSTATUS status;
SECURITY_DESCRIPTOR securityDescriptor;
PAGED_CODE();
//
// Create a security descriptor that has all access.
//
status = RtlCreateSecurityDescriptor( &securityDescriptor,
SECURITY_DESCRIPTOR_REVISION1 );
if (!NT_SUCCESS( status )) {
return status;
}
status = RtlSetDaclSecurityDescriptor ( &securityDescriptor,
TRUE,
NULL,
TRUE ); //does not over-ride inheritable protection
if (!NT_SUCCESS( status )) {
return status;
}
//
// Initialize the object attributes for the symbolic link.
//
InitializeObjectAttributes( &objectAttributes,
SymbolicLinkName,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
&securityDescriptor );
//
// Note that the following assignment can fail (because it is not system
// initialization time and therefore the \ARCname directory does not
// exist - if this is really a call to IoAssignArcName), but that is fine.
//
status = ZwCreateSymbolicLinkObject( &linkHandle,
SYMBOLIC_LINK_ALL_ACCESS,
&objectAttributes,
DeviceName );
if (NT_SUCCESS( status )) {
ZwClose( linkHandle );
}
return status;
}
VOID
IoDeleteController(
IN PCONTROLLER_OBJECT ControllerObject
)
/*++
Routine Description:
This routine deletes the specified controller object from the system
so that it may no longer be referenced from a driver. It is invoked
when either the driver is being unloaded from the system, or the driver's
initialization routine failed to properly initialize the device or a
fatal driver initialization error was encountered.
Arguments:
ControllerObject - Pointer to the controller object that is to be
deleted.
Return Value:
None.
--*/
{
PAGED_CODE();
//
// The controller was created as a temporary object, and all of the
// handles for the object have already been closed. At this point,
// simply dereferencing the object will cause it to be deleted.
//
ObDereferenceObject( ControllerObject );
}
VOID
IopRemoveTimerFromTimerList(
IN PIO_TIMER timer
)
{
KIRQL irql;
ExAcquireFastLock( &IopTimerLock, &irql );
RemoveEntryList( &timer->TimerList );
if (timer->TimerFlag) {
IopTimerCount--;
}
ExReleaseFastLock( &IopTimerLock, irql );
}
VOID
IoDeleteDevice(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine deletes the specified device object from the system so that
it may no longer be referenced. It is invoked when either the device
driver is being unloaded from the system, or the driver's initialization
routine failed to properly initialize the device or a fatal driver
initialization error was encountered, or when the device is being removed
from the system.
Arguments:
DeviceObject - Pointer to the device object that is to be deleted.
Return Value:
None.
--*/
{
KIRQL irql;
#if _PNP_POWER_
NTSTATUS status;
HANDLE handle;
UCHAR buffer[512];
POBJECT_NAME_INFORMATION deviceNameInfo;
ULONG returnLength;
#endif
//
// Check to see whether or not the device has registered a shutdown
// handler if necessary, and if so, unregister it.
//
if (DeviceObject->Flags & DO_SHUTDOWN_REGISTERED) {
IoUnregisterShutdownNotification( DeviceObject );
}
//
// Release the pool that was allocated to contain the timer dispatch
// routine and its associated context if there was one.
//
if (DeviceObject->Timer) {
PIO_TIMER timer;
timer = DeviceObject->Timer;
IopRemoveTimerFromTimerList(timer);
ExFreePool( timer );
}
//
// If this device has a name, then mark the
// object as temporary so that when it is dereferenced it will be
// deleted.
//
if (DeviceObject->Flags & DO_DEVICE_HAS_NAME) {
ObMakeTemporaryObject( DeviceObject );
#if _PNP_POWER_
//
// If this device is also registered with PnP manager, we
// need to deregister it.
//
status = IopOpenServiceEnumKeys(
&DeviceObject->DriverObject->DriverExtension->ServiceKeyName,
KEY_ALL_ACCESS,
&handle,
NULL,
FALSE
);
if (NT_SUCCESS( status )) {
deviceNameInfo = (POBJECT_NAME_INFORMATION)buffer;
deviceNameInfo->Name.Length = 0;
if (ObQueryNameString(DeviceObject,
deviceNameInfo,
sizeof(buffer),
&returnLength
)) {
IopRemoveStringFromValueKey (handle,
L"NtDevicePaths",
&deviceNameInfo->Name);
}
NtClose(handle);
}
#endif
}
//
// Mark the device object as deleted.
//
ExAcquireSpinLock( &IopDatabaseLock, &irql );
DeviceObject->DeviceObjectExtension->ExtensionFlags |= DOE_DELETE_PENDING;
if (!DeviceObject->ReferenceCount) {
IopCompleteUnloadOrDelete( DeviceObject, irql );
} else {
ExReleaseSpinLock( &IopDatabaseLock, irql );
}
}
NTSTATUS
IoDeleteSymbolicLink(
IN PUNICODE_STRING SymbolicLinkName
)
/*++
Routine Description:
This routine is invoked to remove a symbolic link from the system. This
generally occurs whenever a driver that has assigned a symbolic link needs
to exit. It can also be used when a driver no longer needs to redirect
a name.
Arguments:
SymbolicLinkName - Provides the Unicode name string to be deassigned.
Return Values:
None.
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
HANDLE linkHandle;
NTSTATUS status;
PAGED_CODE();
//
// Begin by initializing the object attributes for the symbolic link.
//
InitializeObjectAttributes( &objectAttributes,
SymbolicLinkName,
OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
//
// Open the symbolic link itself so that it can be marked temporary and
// closed.
//
status = ZwOpenSymbolicLinkObject( &linkHandle,
DELETE,
&objectAttributes );
if (NT_SUCCESS( status )) {
//
// The symbolic link was successfully opened. Attempt to make it a
// temporary object, and then close the handle. This will cause the
// object to go away.
//
status = ZwMakeTemporaryObject( linkHandle );
if (NT_SUCCESS( status )) {
ZwClose( linkHandle );
}
}
return status;
}
VOID
IoDetachDevice(
IN OUT PDEVICE_OBJECT TargetDevice
)
/*++
Routine Description:
This routine detaches the device object which is currently attached to the
target device.
Arguments:
TargetDevice - Pointer to device object to be detached from.
Return Value:
None.
--*/
{
KIRQL irql;
//
// Detach the device object attached to the target device. This also
// includes decrementing the reference count for the device. Note that
// if the driver has been marked for an unload operation, and the
// reference count goes to zero, then the driver may need to be unloaded
// at this point.
//
ExAcquireSpinLock( &IopDatabaseLock, &irql );
TargetDevice->AttachedDevice = (PDEVICE_OBJECT) NULL;
if (TargetDevice->DeviceObjectExtension->ExtensionFlags &
(DOE_UNLOAD_PENDING | DOE_DELETE_PENDING) &&
!TargetDevice->ReferenceCount) {
IopCompleteUnloadOrDelete( TargetDevice, irql );
} else {
ExReleaseSpinLock( &IopDatabaseLock, irql );
}
}
VOID
IoDisconnectInterrupt(
IN PKINTERRUPT InterruptObject
)
/*++
Routine Description:
This routine disconnects all of the interrupt objects that were
initialized and connected by the IoConnectInterrupt routine. Note
that no interrupt objects directly connected using the kernel
services may be input to this routine.
Arguments:
InterruptObject - Supplies a pointer to the interrupt object allocated
by the IoConnectInterrupt routine.
Return Value:
None.
--*/
{
PIO_INTERRUPT_STRUCTURE interruptStructure;
ULONG i;
PAGED_CODE();
//
// Obtain a pointer to the builtin interrupt object in the I/O interrupt
// structure.
//
interruptStructure = CONTAINING_RECORD( InterruptObject,
IO_INTERRUPT_STRUCTURE,
InterruptObject );
//
// The builtin interrupt object is always used, so simply disconnect
// it.
//
KeDisconnectInterrupt( &interruptStructure->InterruptObject );
//
// Now loop through each of the interrupt objects pointed to by the
// structure and disconnect each.
//
for (i = 0; i < MAXIMUM_PROCESSORS; i++) {
if (interruptStructure->InterruptArray[i] != NULL) {
KeDisconnectInterrupt( interruptStructure->InterruptArray[i] );
}
}
//
// Finally, deallocate the memory associated with the entire structure.
//
ExFreePool( interruptStructure );
}
VOID
IoEnqueueIrp(
IN PIRP Irp
)
/*++
Routine Description:
This routine enqueues the specified I/O Request Packet (IRP) to the thread's
IRP pending queue. The thread that the IRP is queued to is specified by
the IRP's Thread field.
Arguments:
Irp - Supplies a pointer to the IRP to be enqueued.
Return Value:
None.
--*/
{
PAGED_CODE();
//
// Simply enqueue the IRP to the thread's IRP queue.
//
IopQueueThreadIrp( Irp );
return;
}
BOOLEAN
IoFastQueryNetworkAttributes(
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ACCESS_MASK DesiredAccess,
IN ULONG OpenOptions,
OUT PIO_STATUS_BLOCK IoStatus,
OUT PFILE_NETWORK_OPEN_INFORMATION Buffer
)
/*++
Routine Description:
This routine attempts to perform a fast I/O call to obtain the network
attributes for a file. This involves a specialized interface between
this function and the I/O system's device parse method. This allows the
parse method to have the file system pseudo-open the file, obtain the
appropriate attributes for the file, and return them to the caller w/as
little overhead as possbile from either the Object Manager or the I/O
system itself.
Arguments:
ObjectAttributes - Supplies the attributes to be used for opening the
file (e.g., the file's name, etc).
DesiredAccess - Supplies the type(s) of access that the caller would like
to the file.
OpenOptions - Supplies standard NtOpenFile open options.
IoStatus - Supplies a pointer to a variable to receive the final status
of the operation.
Buffer - Supplies an output buffer to receive the network attributes for
the specified file.
Return Value:
The final function value indicates whether or not the fast path could
be taken successfully.
--*/
{
HANDLE handle;
NTSTATUS status;
OPEN_PACKET openPacket;
DUMMY_FILE_OBJECT localFileObject;
//
// Build a parse open packet that tells the parse method to open the
// file and query its network attributes using the fast path, if it
// exists for this file.
//
RtlZeroMemory( &openPacket, sizeof( OPEN_PACKET ) );
openPacket.Type = IO_TYPE_OPEN_PACKET;
openPacket.Size = sizeof( OPEN_PACKET );
openPacket.ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
openPacket.Disposition = FILE_OPEN;
openPacket.CreateOptions = OpenOptions;
openPacket.Options = IO_FORCE_ACCESS_CHECK;
openPacket.NetworkInformation = Buffer;
openPacket.QueryOnly = TRUE;
openPacket.FullAttributes = TRUE;
openPacket.LocalFileObject = &localFileObject;
//
// Open the object by its name. Because of the special QueryOnly flag set
// in the open packet, the parse routine will open the file using the fast
// path open and perform the query, effectively closing it as well.
//
status = ObOpenObjectByName( ObjectAttributes,
(POBJECT_TYPE) NULL,
KernelMode,
NULL,
DesiredAccess,
&openPacket,
&handle );
//
// The opeation is successful if the parse check field of the open packet
// indicates that the parse routine was actually invoked, and the final
// status field of the packet is set to success. The QueryOnly field is
// set to whether or not the fast path was invoked.
//
if (openPacket.ParseCheck != OPEN_PACKET_PATTERN) {
//
// The parse routine was not invoked properly so the operation did
// not work at all.
//
IoStatus->Status = status;
} else {
//
// The fast path routine was successfully invoked so return the
// final status of the operation.
//
IoStatus->Status = openPacket.FinalStatus;
IoStatus->Information = openPacket.Information;
}
return TRUE;
}
VOID
IoFreeController(
IN PCONTROLLER_OBJECT ControllerObject
)
/*++
Routine Description:
This routine is invoked to deallocate the specified controller object.
No checks are made to ensure that the controller is really allocated
to a device object. However, if it is not, then kernel will bugcheck.
If another device is waiting in the queue to allocate the controller
object it will be pulled from the queue and its execution routine will
be invoked.
Arguments:
ControllerObject - Pointer to the controller object to be deallocated.
Return Value:
None.
--*/
{
PKDEVICE_QUEUE_ENTRY packet;
PDEVICE_OBJECT deviceObject;
IO_ALLOCATION_ACTION action;
//
// Simply remove the next entry from the controller's device wait queue.
// If one was successfully removed, invoke its execution routine.
//
packet = KeRemoveDeviceQueue( &ControllerObject->DeviceWaitQueue );
if (packet != NULL) {
deviceObject = CONTAINING_RECORD( packet,
DEVICE_OBJECT,
Queue.Wcb.WaitQueueEntry );
action = deviceObject->Queue.Wcb.DeviceRoutine( deviceObject,
deviceObject->CurrentIrp,
0,
deviceObject->Queue.Wcb.DeviceContext );
//
// If the execution routine wants the controller to be deallocate
// now, deallocate it.
//
if (action == DeallocateObject) {
IoFreeController( ControllerObject );
}
}
}
VOID
IoFreeIrp(
IN PIRP Irp
)
/*++
Routine Description:
This routine deallocates the specified I/O Request Packet.
Arguments:
Irp - I/O Request Packet to deallocate.
Return Value:
None.
--*/
{
PNPAGED_LOOKASIDE_LIST lookasideList;
//
// Ensure that the data structure being freed is really an IRP.
//
ASSERT(Irp->Type == IO_TYPE_IRP);
//
// Ensure that all of the owners of the IRP have at least been notified
// that the request is going away.
//
ASSERT(Irp->CurrentLocation >= Irp->StackCount);
//
// Deallocate the IRP.
//
if (((Irp->AllocationFlags & IRP_ALLOCATED_FIXED_SIZE) == 0) ||
((Irp->AllocationFlags & IRP_ALLOCATED_MUST_SUCCEED) != 0)) {
ExFreePool(Irp);
} else {
lookasideList = &IopSmallIrpLookasideList;
if (Irp->StackCount != 1) {
lookasideList = &IopLargeIrpLookasideList;
}
lookasideList->L.TotalFrees += 1;
if (ExQueryDepthSList(&lookasideList->L.ListHead) >= lookasideList->L.Depth) {
lookasideList->L.FreeMisses += 1;
ExFreePool(Irp);
} else {
if ((Irp->AllocationFlags & IRP_QUOTA_CHARGED) != 0) {
ExReturnPoolQuota(Irp);
}
ExInterlockedPushEntrySList(&lookasideList->L.ListHead,
(PSINGLE_LIST_ENTRY)Irp,
&lookasideList->Lock);
}
}
return;
}
VOID
IoFreeMdl(
IN PMDL Mdl
)
/*++
Routine Description:
This routine frees a Memory Descriptor List (MDL). It only frees the
specified MDL; any chained MDLs must be freed explicitly through another
call to this routine.
Arguments:
Mdl - Pointer to the Memory Descriptor List to be freed.
Return Value:
None.
--*/
{
PNPAGED_LOOKASIDE_LIST lookasideList;
//
// Tell memory management that this MDL will be re-used. This will
// cause MM to unmap any pages that have been mapped for this MDL if
// it is a partial MDL.
//
MmPrepareMdlForReuse(Mdl);
if (((Mdl->MdlFlags & MDL_ALLOCATED_FIXED_SIZE) == 0) ||
((Mdl->MdlFlags & MDL_ALLOCATED_MUST_SUCCEED) != 0)) {
ExFreePool(Mdl);
} else {
ExFreeToNPagedLookasideList(&IopMdlLookasideList, Mdl);
}
}
PDEVICE_OBJECT
IoGetAttachedDevice(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine returns the highest level device object associated with
the specified device.
Arguments:
DeviceObject - Supplies a pointer to the device for which the attached
device is to be returned.
Return Value:
The function value is the highest level device attached to the specified
device. If no devices are attached, then the pointer to the device
object itself is returned.
--*/
{
//
// Loop through all of the device object's attached to the specified
// device. When the last device object is found that is not attached
// to, return it.
//
while (DeviceObject->AttachedDevice) {
DeviceObject = DeviceObject->AttachedDevice;
}
return DeviceObject;
}
PDEVICE_OBJECT
IoGetBaseFileSystemDeviceObject(
IN PFILE_OBJECT FileObject
)
/*++
Routine Description:
This routine returns the base (lowest-level) file system volume device
object associated with a file. I.e., it locates the file system w/o
walking the attached device object list.
Arguments:
FileObject - Supplies a pointer to the file object for which the base
file system device object is to be returned.
Return Value:
The function value is the lowest level volume device object associated
w/the file.
--*/
{
PDEVICE_OBJECT deviceObject;
//
// If the file object has a mounted Vpb, use its DeviceObject.
//
if (FileObject->Vpb != NULL && FileObject->Vpb->DeviceObject != NULL) {
deviceObject = FileObject->Vpb->DeviceObject;
//
// Otherwise, if the real device has a VPB that indicates that it is
// mounted, then use the file system device object associated with the
// VPB.
//
} else if (FileObject->DeviceObject->Vpb != NULL &&
FileObject->DeviceObject->Vpb->DeviceObject != NULL) {
deviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
//
// Otherwise, just return the real device object.
//
} else {
deviceObject = FileObject->DeviceObject;
}
ASSERT( deviceObject != NULL );
//
// Simply return the resultant file object.
//
return deviceObject;
}
PCONFIGURATION_INFORMATION
IoGetConfigurationInformation( VOID )
/*++
Routine Description:
This routine returns a pointer to the system's device configuration
information structure so that drivers and the system can determine how
many different types of devices exist in the system.
Arguments:
None.
Return Value:
The function value is a pointer to the configuration information
structure.
--*/
{
PAGED_CODE();
//
// Simply return a pointer to the built-in configuration information
// structure.
//
return (&ConfigurationInformation);
}
PEPROCESS
IoGetCurrentProcess( VOID )
/*++
Routine Description:
This routine returns a pointer to the current process. It is actually
a jacket routine for the PS version of the same function since device
drivers using the ntddk header file cannot see into a thread object.
Arguments:
None.
Return Value:
The function value is a pointer to the current process.
Note:
Note that this function cannot be paged because it is invoked at raised
IRQL in debug builds, which is the only time that the PAGED_CODE macro
actually does anything. Therefore, it is impossible to find code that
invokes this function at raised IRQL in a normal system w/o simply running
into the "proper conditions". This is too risky to actually page this
routine, so it is left nonpaged.
--*/
{
//
// Simply return a pointer to the current process.
//
return PsGetCurrentProcess();
}
NTSTATUS
IoGetDeviceObjectPointer(
IN PUNICODE_STRING ObjectName,
IN ACCESS_MASK DesiredAccess,
OUT PFILE_OBJECT *FileObject,
OUT PDEVICE_OBJECT *DeviceObject
)
/*++
Routine Description:
This routine returns a pointer to the device object specified by the
object name. It also returns a pointer to the referenced file object
that has been opened to the device that ensures that the device cannot
go away.
To close access to the device, the caller should dereference the file
object pointer.
Arguments:
ObjectName - Name of the device object for which a pointer is to be
returned.
DesiredAccess - Access desired to the target device object.
FileObject - Supplies the address of a variable to receive a pointer
to the file object for the device.
DeviceObject - Supplies the address of a variable to receive a pointer
to the device object for the specified device.
Return Value:
The function value is a referenced pointer to the specified device
object, if the device exists. Otherwise, NULL is returned.
--*/
{
PFILE_OBJECT fileObject;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE fileHandle;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
PAGED_CODE();
//
// Initialize the object attributes to open the device.
//
InitializeObjectAttributes( &objectAttributes,
ObjectName,
0,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL );
status = ZwOpenFile( &fileHandle,
DesiredAccess,
&objectAttributes,
&ioStatus,
0,
FILE_NON_DIRECTORY_FILE );
if (NT_SUCCESS( status )) {
//
// The open operation was successful. Dereference the file handle
// and obtain a pointer to the device object for the handle.
//
status = ObReferenceObjectByHandle( fileHandle,
0,
IoFileObjectType,
KernelMode,
(PVOID *) &fileObject,
NULL );
if (NT_SUCCESS( status )) {
*FileObject = fileObject;
//
// Get a pointer to the device object for this file.
//
*DeviceObject = IoGetRelatedDeviceObject( fileObject );
}
(VOID) ZwClose( fileHandle );
}
return status;
}
PDEVICE_OBJECT
IoGetDeviceToVerify(
IN PETHREAD Thread
)
/*++
Routine Description:
This routine returns a pointer to the device object that is to be verified.
The pointer is set in the thread object by a device driver when the disk
or CD-ROM media appears to have changed since the last access to the device.
Arguments:
Thread - Pointer to the thread whose field is to be queried.
Return Value:
The function value is a pointer to the device to be verified, or NULL.
Note:
This function cannot be made a macro, since fields in the thread object
move from release to release, so this must remain a full function.
--*/
{
//
// Simply return the device to be verified.
//
return Thread->DeviceToVerify;
}
PGENERIC_MAPPING
IoGetFileObjectGenericMapping(
VOID
)
/*++
Routine Description:
This routine returns a pointer to the generic mapping for a file object.
Arguments:
None.
Return Value:
A pointer to the generic mapping for a file object.
--*/
{
//
// Simply return a pointer to the generic mapping for a file object.
//
return &IopFileMapping;
}
PVOID
IoGetInitialStack(
VOID
)
/*++
Routine Description:
This routine returns the base initial address of the current thread's
stack.
Arguments:
None.
Return Value:
The base initial address of the current thread's stack.
Note:
This function cannot be made a macro, since fields in the thread object
move from release to release, so this must remain a full function.
--*/
{
//
// Simply return the initial stack for this thread.
//
return PsGetCurrentThread()->Tcb.InitialStack;
}
PDEVICE_OBJECT
IoGetRelatedDeviceObject(
IN PFILE_OBJECT FileObject
)
/*++
Routine Description:
This routine returns a pointer to the actual device object than an I/O
Request Packet (IRP) should be given to based on the specified file
object.
Arguments:
FileObject - Pointer to the file object representing the open file.
Return Value:
The return value is a pointer to the device object for the driver to
whom the request is to be given.
--*/
{
PDEVICE_OBJECT deviceObject;
//
// If the file object has a mounted Vpb, use its DeviceObject.
//
if (FileObject->Vpb != NULL && FileObject->Vpb->DeviceObject != NULL) {
deviceObject = FileObject->Vpb->DeviceObject;
//
// Otherwise, if the real device has a VPB that indicates that it is
// mounted, then use the file system device object associated with the
// VPB.
//
} else if (FileObject->DeviceObject->Vpb != NULL &&
FileObject->DeviceObject->Vpb->DeviceObject != NULL) {
deviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
//
// Otherwise, just return the real device object.
//
} else {
deviceObject = FileObject->DeviceObject;
}
ASSERT( deviceObject != NULL );
//
// Check to see whether or not the device has any associated devices.
// If so, return the highest level device; otherwise, return a pointer
// to the device object itself.
//
if (deviceObject->AttachedDevice != NULL) {
deviceObject = IoGetAttachedDevice( deviceObject );
}
return deviceObject;
}
PEPROCESS
IoGetRequestorProcess(
IN PIRP Irp
)
/*++
Routine Description:
This routine returns a pointer to the process that originally
requested the specified I/O operation.
Arguments:
Irp - Pointer to the I/O Request Packet.
Return Value:
The function value is a pointer to the original requesting process.
--*/
{
//
// Return the address of the process that requested the I/O operation.
//
if (Irp->Tail.Overlay.Thread) {
return THREAD_TO_PROCESS( Irp->Tail.Overlay.Thread );
} else {
return NULL;
}
}
PIRP
IoGetTopLevelIrp(
VOID
)
/*++
Routine Description:
This routine returns the contents of the TopLevelIrp field of the current
thread.
Arguments:
None.
Return Value:
The final function value is the contents of the TopLevelIrp field.
Note:
This function cannot be made a macro, since fields in the thread object
move from release to release, so this must remain a full function.
--*/
{
//
// Simply return the TopLevelIrp field of the thread.
//
return (PIRP) (PsGetCurrentThread()->TopLevelIrp);
}
VOID
IoInitializeIrp(
IN OUT PIRP Irp,
IN USHORT PacketSize,
IN CCHAR StackSize
)
/*++
Routine Description:
Initializes an IRP.
Arguments:
Irp - a pointer to the IRP to initialize.
PacketSize - length, in bytes, of the IRP.
StackSize - Number of stack locations in the IRP.
Return Value:
None.
--*/
{
//
// Begin by zeroing the entire packet.
//
RtlZeroMemory( Irp, PacketSize );
//
// Initialize the remainder of the packet by setting the appropriate fields
// and setting up the I/O stack locations in the packet.
//
Irp->Type = (CSHORT) IO_TYPE_IRP;
Irp->Size = (USHORT) PacketSize;
Irp->StackCount = (CCHAR) StackSize;
Irp->CurrentLocation = (CCHAR) (StackSize + 1);
Irp->ApcEnvironment = KeGetCurrentApcEnvironment();
Irp->Tail.Overlay.CurrentStackLocation =
((PIO_STACK_LOCATION) ((UCHAR *) (Irp) +
sizeof( IRP ) +
( (StackSize) * sizeof( IO_STACK_LOCATION ))));
}
NTSTATUS
IoInitializeTimer(
IN PDEVICE_OBJECT DeviceObject,
IN PIO_TIMER_ROUTINE TimerRoutine,
IN PVOID Context
)
/*++
Routine Description:
This routine is used by drivers to initialize a timer entry for a device
object.
Arguments:
DeviceObject - Pointer to device object to be used.
TimerRoutine - Driver routine to be executed when timer expires.
Context - Context parameter that is passed to the driver routine.
Return Value:
The function value indicates whether or not the timer was initialized.
--*/
{
PIO_TIMER timer;
PAGED_CODE();
//
// Begin by getting the address of the timer to be used. If no timer has
// been allocated, allocate one and initialize it.
//
timer = DeviceObject->Timer;
if (!timer) {
timer = ExAllocatePoolWithTag( NonPagedPool, sizeof( IO_TIMER ), 'iToI' );
if (!timer) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the timer entry so that it is suitable for being placed
// into the I/O system's timer queue.
//
RtlZeroMemory( timer, sizeof( IO_TIMER ) );
timer->Type = IO_TYPE_TIMER;
timer->DeviceObject = DeviceObject;
DeviceObject->Timer = timer;
}
//
// Set the address of the driver's timer routine and the context parameter
// passed to it and insert it onto the timer queue. Note that the timer
// enable flag is not set, so this routine will not actually be invoked
// yet.
//
timer->TimerRoutine = TimerRoutine;
timer->Context = Context;
(VOID) ExInterlockedInsertTailList( &IopTimerQueueHead,
&timer->TimerList,
&IopTimerLock );
return STATUS_SUCCESS;
}
BOOLEAN
IoIsOperationSynchronous(
IN PIRP Irp
)
/*++
Routine Description:
This routine determines whether an I/O operation is to be considered
synchronous or an asynchronous, from the implementors point-of-view.
Synchronous I/O is defined by how the file was opened, or the API being
used to perform the operation, or by the type of paging I/O being
performed, if the operation is paging I/O.
It is possible for asynchronous paging I/O to occur to a file that was
opened for synchronous I/O. This occurs when the Modified Page Writer
is doing I/O to a file that is mapped, when too many modified pages exist
in the system.
Arguments:
Irp - Pointer to the I/O Request Packet (IRP) representing the operation
to be performed.
Return Value:
A value of TRUE is returned if the operation is synchronous, otherwise
FALSE is returned.
--*/
{
//
// Determine whether this is a synchronous I/O operation. Synchronous I/O
// is defined as an operation that is:
//
// A file opened for synchronous I/O
// OR
// A synchronous API operation
// OR
// A synchronous paging I/O operation
//
// AND this is NOT an asynchronous paging I/O operation occurring to some
// file that was opened for either synchronous or asynchronous I/O.
//
if ((IoGetCurrentIrpStackLocation( Irp )->FileObject->Flags & FO_SYNCHRONOUS_IO ||
Irp->Flags & IRP_SYNCHRONOUS_API ||
Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) &&
!(Irp->Flags & IRP_PAGING_IO &&
!(Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO))) {
return TRUE;
} else {
return FALSE;
}
}
BOOLEAN
IoIsSystemThread(
IN PETHREAD Thread
)
/*++
Routine Description:
This routine returns a BOOLEAN indicating whether or not the specified
thread is a system thread.
Arguments:
Thread - Pointer to the thread to be checked.
Return Value:
A value of TRUE is returned if the indicated thread is a system thread,
else FALSE.
Note:
This function cannot be made a macro, since fields in the thread object
move from release to release, so this must remain a full function.
--*/
{
//
// Simply return whether or not the thread is a system thread by checking
// its TEB address. If it is NULL, or it is in the system part of the
// virtual address space, then it is a system thread.
//
return (BOOLEAN) (!Thread->Tcb.Teb || MM_IS_SYSTEM_VIRTUAL_ADDRESS( Thread->Tcb.Teb ));
}
PIRP
IoMakeAssociatedIrp(
IN PIRP Irp,
IN CCHAR StackSize
)
/*++
Routine Description:
This routine allocates an I/O Request Packet from the system nonpaged pool
and makes it an associated IRP to the specified IRP. The packet will be
allocated to contain StackSize stack locations. The IRP iwll also be
initialized.
Note that it is up to the caller to have set the number of associated IRPs
in the master packet before calling this routine for the first time. The
count should be set in the master packet in: AssociatedIrp.IrpCount.
Arguments:
Irp - Pointer to master IRP to be associated with.
StackSize - Specifies the maximum number of stack locations required.
Return Value:
The function value is the address of the associated IRP or NULL, if the
IRP could be allocated.
--*/
{
USHORT allocateSize;
UCHAR fixedSize;
PIRP associatedIrp;
PNPAGED_LOOKASIDE_LIST lookasideList;
UCHAR mustSucceed;
USHORT packetSize;
//
// If the size of the packet required is less than or equal to those on
// the lookaside lists, then attempt to allocate the packet from the
// lookaside lists.
//
associatedIrp = NULL;
fixedSize = 0;
mustSucceed = 0;
packetSize = IoSizeOfIrp(StackSize);
allocateSize = packetSize;
if (StackSize <= (CCHAR)IopLargeIrpStackLocations) {
fixedSize = IRP_ALLOCATED_FIXED_SIZE;
lookasideList = &IopSmallIrpLookasideList;
if (StackSize != 1) {
allocateSize = IoSizeOfIrp((CCHAR)IopLargeIrpStackLocations);
lookasideList = &IopLargeIrpLookasideList;
}
lookasideList->L.TotalAllocates += 1;
associatedIrp = (PIRP)ExInterlockedPopEntrySList(&lookasideList->L.ListHead,
&lookasideList->Lock);
}
//
// If an IRP was not allocated from the lookaside list, then allocate
// the packet from nonpaged pool.
//
if (!associatedIrp) {
if (fixedSize != 0) {
lookasideList->L.AllocateMisses += 1;
}
//
// There are no free packets on the lookaside list, or the packet is
// too large to be allocated from one of the lists, so it must be
// allocated from general non-paged pool.
//
associatedIrp = ExAllocatePoolWithTag(NonPagedPool, allocateSize, ' prI');
if (!associatedIrp) {
if (KeGetPreviousMode() == KernelMode) {
mustSucceed = IRP_ALLOCATED_MUST_SUCCEED;
associatedIrp = ExAllocatePoolWithTag(NonPagedPoolMustSucceed,
allocateSize,
' prI' );
}
if (!associatedIrp) {
return NULL;
}
}
}
//
// Initialize the packet.
//
IopInitializeIrp(associatedIrp, packetSize, StackSize);
associatedIrp->Flags |= IRP_ASSOCIATED_IRP;
associatedIrp->AllocationFlags |= (fixedSize | mustSucceed);
//
// Set the thread ID to be that of the master.
//
associatedIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
//
// Now make the association between this packet and the master.
//
associatedIrp->AssociatedIrp.MasterIrp = Irp;
return associatedIrp;
}
BOOLEAN
IoPageFileCreated(
IN HANDLE FileHandle
)
/*++
Routine Description:
This routine is invoked by the Memory Manager when a new page file is
created. The purpose is to lookup the paging file to determine whether
or not it was created on the boot device. If so, and taking crash dumps
is enabled, then the retrieval pointers are queried from the file system
for the file so that they can then be used later in writing the crash
dump to the paging file.
The Memory Manager will continue to invoke this function for each paging
file created in the system until the one for the boot device is encountered.
Arguments:
FileHandle - Handle to the paging file just created.
Return Value:
A value of TRUE is returned if this paging file resides on the system's
boot device, otherwise FALSE.
--*/
{
PFILE_OBJECT fileObject;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
BOOLEAN result;
PAGED_CODE();
//
// If crash dumps are not enabled, bail immediately.
//
if (!IopDumpControlBlock ||
!(IopDumpControlBlock->Flags & (DCB_DUMP_ENABLED | DCB_SUMMARY_ENABLED))) {
return FALSE;
}
//
// Crash dumps are enabled, so figure out whether or not this paging
// file is on the boot device.
//
result = FALSE;
status = ObReferenceObjectByHandle( FileHandle,
0,
IoFileObjectType,
KernelMode,
(PVOID *) &fileObject,
NULL );
if (NT_SUCCESS( status )) {
//
// Get a pointer to the device object for this file. Note that it
// cannot go away, since there is an open handle to it, so it is
// OK to dereference it and then use it.
//
deviceObject = fileObject->DeviceObject;
ObDereferenceObject( fileObject );
//
// If this device object represents the boot partition, then query
// the retrieval pointers for the file.
//
if (deviceObject->Flags & DO_SYSTEM_BOOT_PARTITION) {
IO_STATUS_BLOCK ioStatus;
PLARGE_INTEGER mcb;
LARGE_INTEGER requestedMapSize;
//
// Indicate that the size of the file for which to return retrieval
// pointers is the total size of physical memory, plus one page for
// the header.
//
requestedMapSize.QuadPart = IopDumpControlBlock->MemoryDescriptor->NumberOfPages + 1;
requestedMapSize.QuadPart = requestedMapSize.QuadPart << PAGE_SHIFT;
status = ZwFsControlFile( FileHandle,
(HANDLE) NULL,
(PIO_APC_ROUTINE) NULL,
(PVOID) NULL,
&ioStatus,
FSCTL_QUERY_RETRIEVAL_POINTERS,
&requestedMapSize,
sizeof( LARGE_INTEGER ),
&mcb,
sizeof( PVOID ) );
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject( &fileObject->Event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
status = ioStatus.Status;
}
if (NT_SUCCESS( status )) {
ULONG i;
//
// This paging file is on the system's boot partition, and
// the retrieval pointers for the file were just successfully
// queried. Walk the MCB to size it, and then checksum it.
//
IopDumpControlBlock->FileDescriptorArray = mcb;
for (i = 0; mcb->QuadPart; i++, mcb++) {
NOTHING;
}
i = ++i * sizeof( LARGE_INTEGER );
IopDumpControlBlock->FileDescriptorSize = i;
IopDumpControlBlock->FileDescriptorChecksum = IopChecksum( IopDumpControlBlock->FileDescriptorArray,
i );
IopDumpControlBlockChecksum = IopChecksum( IopDumpControlBlock,
IopDumpControlBlock->Size );
}
}
}
return result;
}
NTSTATUS
IoPageRead(
IN PFILE_OBJECT FileObject,
IN PMDL MemoryDescriptorList,
IN PLARGE_INTEGER StartingOffset,
IN PKEVENT Event,
OUT PIO_STATUS_BLOCK IoStatusBlock
)
/*++
Routine Description:
This routine provides a special, fast interface for the Pager to read pages
in from the disk quickly and with very little overhead. All of the special
handling for this request is recognized by setting the IRP_PAGING_IO flag
in the IRP flags word. In-page operations are detected by using the IRP
flag IRP_INPUT_OPERATION.
Arguments:
FileObject - A pointer to a referenced file object describing which file
the read should be performed from.
MemoryDescriptorList - An MDL which describes the physical pages that the
pages should be read into from the disk. All of the pages have been
locked in memory. The MDL also describes the length of the read
operation.
StartingOffset - Pointer to the offset in the file from which the read
should take place.
Event - A pointer to a kernel event structure to be used for synchronization
purposes. The event will be set to the Signaled state once the in-page
operation completes.
IoStatusBlock - A pointer to the I/O status block in which the final status
and information should be stored.
Return Value:
The function value is the final status of the queue request to the I/O
system subcomponents.
Notes:
This routine is invoked at APC_LEVEL; this level is honored throughout the
execution of the entire I/O request, including completion.
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
PDEVICE_OBJECT deviceObject;
//
// Increment performance counter. The Cache Manager I/Os always are
// "recursive".
//
if (MmIsRecursiveIoFault()) {
*CcMissCounter += (MemoryDescriptorList->ByteCount + PAGE_SIZE - 1) >> PAGE_SHIFT;
}
//
// Begin by getting a pointer to the device object that the file resides
// on.
//
deviceObject = IoGetRelatedDeviceObject( FileObject );
//
// Allocate an I/O Request Packet (IRP) for this in-page operation.
//
irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
if (!irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Get a pointer to the first stack location in the packet. This location
// will be used to pass the function codes and parameters to the first
// driver.
//
irpSp = IoGetNextIrpStackLocation( irp );
//
// Fill in the IRP according to this request.
//
irp->MdlAddress = MemoryDescriptorList;
irp->Flags = IRP_PAGING_IO | IRP_NOCACHE | IRP_SYNCHRONOUS_PAGING_IO | IRP_INPUT_OPERATION;
irp->RequestorMode = KernelMode;
irp->UserIosb = IoStatusBlock;
irp->UserEvent = Event;
irp->UserBuffer = (PVOID) ((PCHAR) MemoryDescriptorList->StartVa + MemoryDescriptorList->ByteOffset);
irp->Tail.Overlay.OriginalFileObject = FileObject;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
//
// Fill in the normal read parameters.
//
irpSp->MajorFunction = IRP_MJ_READ;
irpSp->FileObject = FileObject;
irpSp->Parameters.Read.Length = MemoryDescriptorList->ByteCount;
irpSp->Parameters.Read.ByteOffset = *StartingOffset;
//
// Queue the packet to the appropriate driver based on whether or not there
// is a VPB associated with the device.
//
return IoCallDriver( deviceObject, irp );
}
NTSTATUS
IoQueryFileInformation(
IN PFILE_OBJECT FileObject,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN ULONG Length,
OUT PVOID FileInformation,
OUT PULONG ReturnedLength
)
/*++
Routine Description:
This routine returns the requested information about a specified file.
The information returned is determined by the FileInformationClass that
is specified, and it is placed into the caller's FileInformation buffer.
Arguments:
FileObject - Supplies a pointer to the file object about which the requested
information is returned.
FileInformationClass - Specifies the type of information which should be
returned about the file.
Length - Supplies the length, in bytes, of the FileInformation buffer.
FileInformation - 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 FileInformation buffer.
Return Value:
The status returned is the final completion status of the operation.
--*/
{
PAGED_CODE();
//
// Simply invoke the common routine to perform the query operation.
//
return IopQueryXxxInformation( FileObject,
FileInformationClass,
Length,
FileInformation,
ReturnedLength,
TRUE );
}
NTSTATUS
IoQueryVolumeInformation(
IN PFILE_OBJECT FileObject,
IN FS_INFORMATION_CLASS FsInformationClass,
IN ULONG Length,
OUT PVOID FsInformation,
OUT PULONG ReturnedLength
)
/*++
Routine Description:
This routine returns the requested information about a specified volume.
The information returned is determined by the FsInformationClass that
is specified, and it is placed into the caller's FsInformation buffer.
Arguments:
FileObject - 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 volume.
Length - Supplies the length of the FsInformation 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 FsInformation buffer.
Return Value:
The status returned is the final completion status of the operation.
--*/
{
PAGED_CODE();
//
// Simply invoke the common routine to perform the query operation.
//
return IopQueryXxxInformation( FileObject,
FsInformationClass,
Length,
FsInformation,
ReturnedLength,
FALSE );
}
VOID
IoQueueThreadIrp(
IN PIRP Irp
)
/*++
Routine Description:
This routine queues the specified I/O Request Packet (IRP) to the current
thread's IRP pending queue. This queue locates all of the outstanding
I/O requests for the thread.
Arguments:
Irp - Pointer to the I/O Request Packet (IRP) to be queued.
Return Value:
None.
--*/
{
//
// Simply queue the packet using the internal queueing routine.
//
IopQueueThreadIrp( Irp );
}
VOID
IoRaiseHardError(
IN PIRP Irp,
IN PVPB Vpb OPTIONAL,
IN PDEVICE_OBJECT RealDeviceObject
)
/*++
Routine Description:
This routine pops up a hard error in the context of the thread that
originally requested the I/O operation specified by the input IRP. This
is done by queueing a kernel APC to the original thread, passing it a
pointer to the device objects and the IRP. Once the pop up is performed,
the routine either completes the I/O request then, or it calls the driver
back with the same IRP.
If the original request was an IoPageRead, then it was at APC level and
we have to create a thread to "hold" this pop-up. Note that we have to
queue to an ExWorker thread to create the thread since this can only be
done from the system process.
Arguments:
Irp - A pointer to the I/O Request Packet (IRP) for the request that
failed.
Vpb - This is the volume parameter block of the offending media. If the
media not yet mounted, this parameter should be absent.
RealDeviceObject - A pointer to the device object that represents the
device that the file system believes it has mounted. This is
generally the "real" device object in the VPB, but may, in fact,
be a device object attached to the physical device.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION IrpSp;
//
// If pop-ups are disabled for the requesting thread, just complete the
// request.
//
if (Irp->Tail.Overlay.Thread->HardErrorsAreDisabled) {
//
// An error was incurred, so zero out the information field before
// completing the request if this was an input operation. Otherwise,
// IopCompleteRequest will try to copy to the user's buffer.
//
if (Irp->Flags & IRP_INPUT_OPERATION) {
Irp->IoStatus.Information = 0;
}
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
return;
}
//
// If this Irp resulted from a call to IoPageRead(), the caller must
// have been at APC level, so don't try enqueing an APC.
//
// Also if this is a cleanup Irp, force this pop-up to go to the new
// thread so that it cannot be disabled.
//
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if ((Irp->Flags == (IRP_PAGING_IO |
IRP_NOCACHE |
IRP_SYNCHRONOUS_PAGING_IO |
IRP_INPUT_OPERATION)) ||
(IrpSp->MajorFunction == IRP_MJ_CLEANUP)) {
PIOP_APC_HARD_ERROR_PACKET packet;
packet = ExAllocatePoolWithTag( NonPagedPool,
sizeof( IOP_APC_HARD_ERROR_PACKET ),
'rEoI' );
if ( packet == NULL ) {
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
return;
}
ExInitializeWorkItem( &packet->Item, IopStartApcHardError, packet );
packet->Irp = Irp;
packet->Vpb = Vpb;
packet->RealDeviceObject = RealDeviceObject;
ExQueueWorkItem( &packet->Item, CriticalWorkQueue );
} else {
PKAPC apc;
//
// Begin by allocating and initializing an APC that can be sent to the
// target thread.
//
apc = ExAllocatePoolWithTag( NonPagedPool, sizeof( KAPC ), 'CPAK' );
//
// If we could not get the pool, we have no choice but to just complete
// the Irp, thereby passing the error onto the caller.
//
if ( apc == NULL ) {
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
return;
}
KeInitializeApc( apc,
&Irp->Tail.Overlay.Thread->Tcb,
Irp->ApcEnvironment,
IopDeallocateApc,
IopAbortRequest,
IopRaiseHardError,
KernelMode,
Irp );
(VOID) KeInsertQueueApc( apc,
Vpb,
RealDeviceObject,
0 );
}
}
BOOLEAN
IoRaiseInformationalHardError(
IN NTSTATUS ErrorStatus,
IN PUNICODE_STRING String OPTIONAL,
IN PKTHREAD Thread OPTIONAL
)
/*++
Routine Description:
This routine pops up a hard error in the hard error popup thread. The
routine returns immediately, enqueuing the actual pop-up to a worker
thread. The hard error that is raised is informational in the sense that
only the OK button is displayed.
Arguments:
ErrorStatus - The error condition.
String - Depending on the error, a string may have to be enqueued.
Thread - If present, enqueue an APC to this thread rather than using
the hard error thread.
Return Value:
BOOLEAN - TRUE if we decided to dispatch a pop-up. FALSE if we decided
not to because:
- pop-ups are disabled in the requested thread, or
- a pool allocation failed, or
- an equivalent pop-up is currently pending a user response (i.e.
waiting for the user to press <OK>) or in the queue, or
- too many pop-ups have already been queued.
--*/
//
// This macro compares two pop-ups to see if they are content equivalent.
//
#define ArePacketsEquivalent(P1,P2) ( \
(P1->ErrorStatus == P2->ErrorStatus) && \
((!P1->String.Buffer && !P2->String.Buffer) || \
((P1->String.Length == P2->String.Length) && \
(RtlEqualMemory(P1->String.Buffer, \
P2->String.Buffer, \
P1->String.Length)))) \
)
{
KIRQL oldIrql;
PVOID stringBuffer;
PLIST_ENTRY links;
PIOP_HARD_ERROR_PACKET hardErrorPacket;
//
// If pop-ups are disabled for the requesting thread, just return.
//
if (ARGUMENT_PRESENT(Thread) ?
CONTAINING_RECORD(Thread, ETHREAD, Tcb)->HardErrorsAreDisabled :
PsGetCurrentThread()->HardErrorsAreDisabled) {
return FALSE;
}
//
// If this request is going to be sent to the hard error thread, and
// there are more than 25 entries already in the queue, don't
// add any more. We'll do another safe check later on.
//
if ( !ARGUMENT_PRESENT( Thread ) &&
(KeReadStateSemaphore( &IopHardError.WorkQueueSemaphore ) >=
IOP_MAXIMUM_OUTSTANDING_HARD_ERRORS) ) {
return FALSE;
}
//
// Allocate the packet, and a buffer for the string if present.
//
hardErrorPacket = ExAllocatePoolWithTag( NonPagedPool,
sizeof(IOP_HARD_ERROR_PACKET),
'rEoI');
if (!hardErrorPacket) { return FALSE; }
//
// Zero out the packet and fill the NT_STATUS we will pop up.
//
RtlZeroMemory( hardErrorPacket, sizeof(IOP_HARD_ERROR_PACKET) );
hardErrorPacket->ErrorStatus = ErrorStatus;
//
// If there is a string, copy it.
//
if ( ARGUMENT_PRESENT( String ) && String->Length ) {
stringBuffer = ExAllocatePoolWithTag( NonPagedPool,
String->Length,
'rEoI' );
if (!stringBuffer) {
ExFreePool( hardErrorPacket );
return FALSE;
}
hardErrorPacket->String.Length = String->Length;
hardErrorPacket->String.MaximumLength = String->Length;
hardErrorPacket->String.Buffer = stringBuffer;
RtlCopyMemory( stringBuffer, String->Buffer, String->Length );
}
//
// If there is an Thread, enqueue an APC for ourself, otherwise send
// it off to the to the hard error thread.
//
if ( ARGUMENT_PRESENT( Thread ) ) {
PKAPC apc;
//
// Begin by allocating and initializing an APC that can be sent to the
// target thread.
//
apc = ExAllocatePoolWithTag( NonPagedPool, sizeof( KAPC ), 'CPAK' );
//
// If we could not get the pool, we have no choice but to just
// free the packet and return.
//
if ( apc == NULL ) {
if ( hardErrorPacket->String.Buffer ) {
ExFreePool( hardErrorPacket->String.Buffer );
}
ExFreePool( hardErrorPacket );
return FALSE;
}
KeInitializeApc( apc,
Thread,
CurrentApcEnvironment,
IopDeallocateApc,
NULL,
IopRaiseInformationalHardError,
KernelMode,
hardErrorPacket );
(VOID) KeInsertQueueApc( apc, NULL, NULL, 0 );
} else {
//
// Get exclusive access to the work queue.
//
ExAcquireSpinLock( &IopHardError.WorkQueueSpinLock, &oldIrql );
//
// Check the Signal state again, if OK, go ahead and enqueue.
//
if ( KeReadStateSemaphore( &IopHardError.WorkQueueSemaphore ) >=
IOP_MAXIMUM_OUTSTANDING_HARD_ERRORS ) {
ExReleaseSpinLock( &IopHardError.WorkQueueSpinLock, oldIrql );
if ( hardErrorPacket->String.Buffer ) {
ExFreePool( hardErrorPacket->String.Buffer );
}
ExFreePool( hardErrorPacket );
return FALSE;
}
//
// If there is a pop-up currently up, check for a match
//
if (IopCurrentHardError &&
ArePacketsEquivalent( hardErrorPacket, IopCurrentHardError )) {
ExReleaseSpinLock( &IopHardError.WorkQueueSpinLock, oldIrql );
if ( hardErrorPacket->String.Buffer ) {
ExFreePool( hardErrorPacket->String.Buffer );
}
ExFreePool( hardErrorPacket );
return FALSE;
}
//
// Run down the list of queued pop-ups looking for a match.
//
links = IopHardError.WorkQueue.Flink;
while (links != &IopHardError.WorkQueue) {
PIOP_HARD_ERROR_PACKET queueHardErrorPacket;
queueHardErrorPacket = CONTAINING_RECORD( links,
IOP_HARD_ERROR_PACKET,
WorkQueueLinks );
if (ArePacketsEquivalent( hardErrorPacket,
queueHardErrorPacket )) {
ExReleaseSpinLock( &IopHardError.WorkQueueSpinLock, oldIrql );
if ( hardErrorPacket->String.Buffer ) {
ExFreePool( hardErrorPacket->String.Buffer );
}
ExFreePool( hardErrorPacket );
return FALSE;
}
links = links->Flink;
}
//
// Enqueue this packet.
//
InsertTailList( &IopHardError.WorkQueue,
&hardErrorPacket->WorkQueueLinks );
//
// Bump the count on the semaphore so that the hard error thread
// will know that an entry has been placed in the queue.
//
(VOID) KeReleaseSemaphore( &IopHardError.WorkQueueSemaphore,
0,
1L,
FALSE );
//
// If we are not currently running in an ExWorkerThread, queue
// a work item.
//
if ( !IopHardError.ThreadStarted ) {
IopHardError.ThreadStarted = TRUE;
ExQueueWorkItem( &IopHardError.ExWorkItem, DelayedWorkQueue );
}
//
// Finally, release the spinlockevent, allowing access to the work queue again.
// The combination of releasing both the event and the semaphore will
// enable the thread to wake up and obtain the entry.
//
ExReleaseSpinLock( &IopHardError.WorkQueueSpinLock, oldIrql );
}
return TRUE;
}
VOID
IoRegisterDriverReinitialization(
IN PDRIVER_OBJECT DriverObject,
IN PDRIVER_REINITIALIZE DriverReinitializationRoutine,
IN PVOID Context
)
/*++
Routine Description:
This routine is invoked by drivers during their initialization or during
their reinitialization to register with the I/O system to be called again
before I/O system initialization is complete. Note that it is possible
for this to occur during a normally running system, if the driver is
loaded dynamically, so all references to the reinitialization queue must
be synchronized.
Arguments:
DriverObject - Pointer to the driver's driver object.
DriverReinitializationRoutine - The address of the reinitialization
routine that is to be invoked.
Context - Pointer to the context that is passed to the driver's
reinitialization routine.
Return Value:
None.
--*/
{
PREINIT_PACKET reinitEntry;
PAGED_CODE();
//
// Allocate a reinitialization entry to be inserted onto the list. Note
// that if the entry cannot be allocated, then the request is simply
// dropped on the floor.
//
reinitEntry = ExAllocatePoolWithTag( NonPagedPool,
sizeof( REINIT_PACKET ),
'iRoI' );
if (!reinitEntry) {
return;
}
reinitEntry->DriverObject = DriverObject;
reinitEntry->DriverReinitializationRoutine = DriverReinitializationRoutine;
reinitEntry->Context = Context;
(VOID) ExInterlockedInsertTailList( &IopDriverReinitializeQueueHead,
&reinitEntry->ListEntry,
&IopDatabaseLock );
}
VOID
IoRegisterFileSystem(
IN OUT PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine inserts the device object for the file system which the device
object represents into the list of file systems in the system.
Arguments:
DeviceObject - Pointer to device object for the file system.
Return Value:
None.
--*/
{
PNOTIFICATION_PACKET nPacket;
PLIST_ENTRY entry;
PAGED_CODE();
//
// Allocate the I/O database resource for a write operation.
//
(VOID) ExAcquireResourceExclusive( &IopDatabaseResource, TRUE );
//
// Insert the device object into the appropriate file system queue based on
// the driver type in the device object. Notice that if the device type is
// unrecognized, the file system is simply not registered.
//
if (DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) {
InsertHeadList( &IopNetworkFileSystemQueueHead,
&DeviceObject->Queue.ListEntry );
} else if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) {
InsertHeadList( &IopCdRomFileSystemQueueHead,
&DeviceObject->Queue.ListEntry );
} else if (DeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) {
InsertHeadList( &IopDiskFileSystemQueueHead,
&DeviceObject->Queue.ListEntry );
} else if (DeviceObject->DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM) {
InsertHeadList( &IopTapeFileSystemQueueHead,
&DeviceObject->Queue.ListEntry );
}
//
// Ensure that this file system's device is operable.
//
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
//
// Notify all of the registered drivers that this file system has been
// registered as an active file system of some type.
//
entry = IopFsNotifyChangeQueueHead.Flink;
while (entry != &IopFsNotifyChangeQueueHead) {
nPacket = CONTAINING_RECORD( entry, NOTIFICATION_PACKET, ListEntry );
entry = entry->Flink;
nPacket->NotificationRoutine( DeviceObject, TRUE );
}
//
// Release the I/O database resource.
//
ExReleaseResource( &IopDatabaseResource );
//
// Increment the number of reasons that this driver cannot be unloaded.
//
ExInterlockedAddUlong( &DeviceObject->ReferenceCount, 1, &IopDatabaseLock );
}
NTSTATUS
IoRegisterFsRegistrationChange(
IN PDRIVER_OBJECT DriverObject,
IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine
)
/*++
Routine Description:
This routine registers the specified driver's notification routine to be
invoked whenever a file system registers or unregisters itself as an active
file system in the system.
Arguments:
DriverObject - Pointer to the driver object for the driver.
DriverNotificationRoutine - Address of routine to invoke when a file system
registers or unregisters itself.
Return Value:
The return status is the final value of the function.
--*/
{
PNOTIFICATION_PACKET nPacket;
PAGED_CODE();
ASSERT( DriverObject->DeviceObject );
//
// Begin by attempting to allocate storage for the shutdown packet. If
// one cannot be allocated, simply return an appropriate error.
//
nPacket = ExAllocatePoolWithTag( PagedPool,
sizeof( NOTIFICATION_PACKET ),
'sFoI' );
if (!nPacket) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the notification packet and insert it onto the tail of the
// list.
//
nPacket->DriverObject = DriverObject;
nPacket->NotificationRoutine = DriverNotificationRoutine;
ExAcquireResourceExclusive( &IopDatabaseResource, TRUE );
InsertTailList( &IopFsNotifyChangeQueueHead, &nPacket->ListEntry );
ExReleaseResource( &IopDatabaseResource );
//
// Increment the number of reasons that this driver cannot be unloaded.
//
ExInterlockedAddUlong( &DriverObject->DeviceObject->ReferenceCount,
1,
&IopDatabaseLock );
return STATUS_SUCCESS;
}
NTSTATUS
IoRegisterShutdownNotification(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine allows a driver to register that it would like to have its
shutdown routine invoked when the system is being shutdown. This gives
the driver an opportunity to get control just before the system is fully
shutdown.
Arguments:
DeviceObject - Pointer to the driver's device object.
Return Value:
None.
--*/
{
PSHUTDOWN_PACKET shutdown;
PAGED_CODE();
//
// Begin by attempting to allocate storage for the shutdown packet. If
// one cannot be allocated, simply return an appropriate error.
//
shutdown = ExAllocatePoolWithTag( NonPagedPool,
sizeof( SHUTDOWN_PACKET ),
'hSoI' );
if (!shutdown) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Initialize the shutdown packet and insert it onto the head of the list.
// Note that this is done because some drivers have dependencies on LIFO
// notification ordering.
//
shutdown->DeviceObject = DeviceObject;
ExInterlockedInsertHeadList( &IopNotifyShutdownQueueHead,
&shutdown->ListEntry,
&IopDatabaseLock );
//
// Do the bookkeeping to indicate that this driver has successfully
// registered a shutdown notification routine.
//
DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
return STATUS_SUCCESS;
}
VOID
IoReleaseCancelSpinLock(
IN KIRQL Irql
)
/*++
Routine Description:
This routine is invoked to release the cancel spin lock. This spin lock
must be acquired before setting the address of a cancel routine in an
IRP and released after the cancel routine has been set.
Arguments:
Irql - Supplies the IRQL value returned from acquiring the spin lock.
Return Value:
None.
--*/
{
//
// Simply release the cancel spin lock.
//
ExReleaseSpinLock( &IopCancelSpinLock, Irql );
}
VOID
IoReleaseVpbSpinLock(
IN KIRQL Irql
)
/*++
Routine Description:
This routine is invoked to release the Volume Parameter Block (VPB) spin
lock. This spin lock must be acquired before accessing the mount flag,
reference count, and device object fields of a VPB.
Arguments:
Irql - Supplies the IRQL value returned from acquiring the spin lock.
Return Value:
None.
--*/
{
//
// Simply release the VPB spin lock.
//
ExReleaseSpinLock( &IopVpbSpinLock, Irql );
}
VOID
IoRemoveShareAccess(
IN PFILE_OBJECT FileObject,
IN OUT PSHARE_ACCESS ShareAccess
)
/*++
Routine Description:
This routine is invoked to remove the access and share access information
in a file system Share Access structure for a given open instance.
Arguments:
FileObject - Pointer to the file object of the current access being closed.
ShareAccess - Pointer to the share access structure that describes
how the file is currently being accessed.
Return Value:
None.
--*/
{
PAGED_CODE();
//
// If this accessor wanted some type of access other than READ_ or
// WRITE_ATTRIBUTES, then account for the fact that he has closed the
// file. Otherwise, he hasn't been accounted for in the first place
// so don't do anything.
//
if (FileObject->ReadAccess ||
FileObject->WriteAccess ||
FileObject->DeleteAccess) {
//
// Decrement the number of opens in the Share Access structure.
//
ShareAccess->OpenCount--;
//
// For each access type, decrement the appropriate count in the Share
// Access structure.
//
if (FileObject->ReadAccess) {
ShareAccess->Readers--;
}
if (FileObject->WriteAccess) {
ShareAccess->Writers--;
}
if (FileObject->DeleteAccess) {
ShareAccess->Deleters--;
}
//
// For each shared access type, decrement the appropriate count in the
// Share Access structure.
//
if (FileObject->SharedRead) {
ShareAccess->SharedRead--;
}
if (FileObject->SharedWrite) {
ShareAccess->SharedWrite--;
}
if (FileObject->SharedDelete) {
ShareAccess->SharedDelete--;
}
}
}
VOID
IoSetDeviceToVerify(
IN PETHREAD Thread,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine sets the device to verify field in the thread object. This
function is invoked by file systems to NULL this field, or to set it to
predefined values.
Arguments:
Thread - Pointer to the thread whose field is to be set.
DeviceObject - Pointer to the device to be verified, or NULL, or ...
Return Value:
None.
Note:
This function cannot be made a macro, since fields in the thread object
move from release to release, so this must remain a full function.
--*/
{
//
// Simply set the device to be verified in the specified thread.
//
Thread->DeviceToVerify = DeviceObject;
}
VOID
IoSetHardErrorOrVerifyDevice(
IN PIRP Irp,
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine is invoked when a driver realizes that the media
has possibly changed on a device, and it must be verified before
continuing, or a hard error has occured. The device is stored
in the thread local storage of the Irp's originating thread.
Arguments:
Irp - Pointer to an I/O Request Packet to get the thread.
DeviceObject - This is the device that needs to be verified.
Return Value:
None.
--*/
{
//
// Store the address of the device object that needs verification in
// the appropriate field of the thread pointed to by the specified I/O
// Request Packet.
//
ASSERT( Irp->Tail.Overlay.Thread != NULL );
Irp->Tail.Overlay.Thread->DeviceToVerify = DeviceObject;
}
NTSTATUS
IoSetInformation(
IN PFILE_OBJECT FileObject,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN ULONG Length,
IN PVOID FileInformation
)
/*++
Routine Description:
This routine sets the requested information for the specified file.
The information that is set is determined by the FileInformationClass
paramter, and the information itself is passed in the FileInformation
buffer.
Arguments:
FileObject - Supplies a pointer to the file object for the file that
is to be changed.
FileInformationClass - Specifies the type of information that should
be set on the file.
Length - Supplies the length of the FileInformation buffer in bytes.
FileInformation - A buffer containing the file information to set. This
buffer must not be pageable and must reside in system space.
Return Value:
The status returned is the final completion status of the operation.
--*/
{
PIRP irp;
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
KEVENT event;
PIO_STACK_LOCATION irpSp;
IO_STATUS_BLOCK localIoStatus;
HANDLE targetHandle = NULL;
BOOLEAN synchronousIo;
PAGED_CODE();
//
// Reference the file object here so that no special checks need be made
// in I/O completion to determine whether or not to dereference the file
// object.
//
ObReferenceObject( FileObject );
//
// Make a special check here to determine whether this is a synchronous
// I/O operation. If it is, then wait here until the file is owned by
// the current thread. If this is not a (serialized) synchronous I/O
// operation, then initialize the local event.
//
if (FileObject->Flags & FO_SYNCHRONOUS_IO) {
BOOLEAN interrupted;
if (!IopAcquireFastLock( FileObject )) {
status = IopAcquireFileObjectLock( FileObject,
KernelMode,
(BOOLEAN) ((FileObject->Flags & FO_ALERTABLE_IO) != 0),
&interrupted );
if (interrupted) {
ObDereferenceObject( FileObject );
return status;
}
}
KeClearEvent( &FileObject->Event );
synchronousIo = TRUE;
} else {
KeInitializeEvent( &event, SynchronizationEvent, FALSE );
synchronousIo = FALSE;
}
//
// Get the address of the target device object.
//
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 there is
// not enough memory to satisfy the request.
//
irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
if (!irp) {
//
// An IRP could not be allocated. Cleanup and return an appropriate
// error status code.
//
IopAllocateIrpCleanup( FileObject, (PKEVENT) NULL );
return STATUS_INSUFFICIENT_RESOURCES;
}
irp->Tail.Overlay.OriginalFileObject = FileObject;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->RequestorMode = KernelMode;
//
// Fill in the service independent parameters in the IRP.
//
if (synchronousIo) {
irp->UserEvent = (PKEVENT) NULL;
} else {
irp->UserEvent = &event;
irp->Flags = IRP_SYNCHRONOUS_API;
}
irp->UserIosb = &localIoStatus;
//
// 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 = IRP_MJ_SET_INFORMATION;
irpSp->FileObject = FileObject;
//
// Set the system buffer address to the address of the caller's buffer and
// set the flags so that the buffer is not deallocated.
//
irp->AssociatedIrp.SystemBuffer = FileInformation;
irp->Flags |= IRP_BUFFERED_IO;
//
// Copy the caller's parameters to the service-specific portion of the IRP.
//
irpSp->Parameters.SetFile.Length = Length;
irpSp->Parameters.SetFile.FileInformationClass = FileInformationClass;
//
// Insert the packet at the head of the IRP list for the thread.
//
IopQueueThreadIrp( irp );
//
// Everything is now set to invoke the device driver with this request.
// However, it is possible that the information that the caller wants to
// set is device independent (I/O system dependent). If this is the case,
// then the request can be satisfied here without having to have all of
// the drivers implement the same code. Note that having the IRP is still
// necessary since the I/O completion code requires it.
//
if (FileInformationClass == FileModeInformation) {
PFILE_MODE_INFORMATION modeBuffer = FileInformation;
//
// Set or clear the appropriate flags in the file object.
//
if (!(FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)) {
if (modeBuffer->Mode & FILE_WRITE_THROUGH) {
FileObject->Flags |= FO_WRITE_THROUGH;
} else {
FileObject->Flags &= ~FO_WRITE_THROUGH;
}
}
if (modeBuffer->Mode & FILE_SEQUENTIAL_ONLY) {
FileObject->Flags |= FO_SEQUENTIAL_ONLY;
} else {
FileObject->Flags &= ~FO_SEQUENTIAL_ONLY;
}
if (modeBuffer->Mode & FO_SYNCHRONOUS_IO) {
if (modeBuffer->Mode & FILE_SYNCHRONOUS_IO_ALERT) {
FileObject->Flags |= FO_ALERTABLE_IO;
} else {
FileObject->Flags &= ~FO_ALERTABLE_IO;
}
}
status = STATUS_SUCCESS;
//
// Complete the I/O operation.
//
irp->IoStatus.Status = status;
irp->IoStatus.Information = 0;
IoSetNextIrpStackLocation( irp );
IoCompleteRequest( irp, 0 );
} else if (FileInformationClass == FileRenameInformation ||
FileInformationClass == FileLinkInformation ||
FileInformationClass == FileCopyOnWriteInformation ||
FileInformationClass == FileMoveClusterInformation) {
//
// Note that the following code assumes that a rename information
// and a set link information structure look exactly the same.
//
PFILE_RENAME_INFORMATION renameBuffer = FileInformation;
//
// Copy the value of the replace BOOLEAN (or the ClusterCount field)
// from the caller's buffer to the I/O stack location parameter
// field where it is expected by file systems.
//
if (FileInformationClass == FileMoveClusterInformation) {
irpSp->Parameters.SetFile.ClusterCount =
((FILE_MOVE_CLUSTER_INFORMATION *) renameBuffer)->ClusterCount;
} else {
irpSp->Parameters.SetFile.ReplaceIfExists = renameBuffer->ReplaceIfExists;
}
//
// Check to see whether or not a fully qualified pathname was supplied.
// If so, then more processing is required.
//
if (renameBuffer->FileName[0] == (UCHAR) OBJ_NAME_PATH_SEPARATOR ||
renameBuffer->RootDirectory != NULL) {
//
// A fully qualified file name was specified as the target of the
// rename operation. Attempt to open the target file and ensure
// that the replacement policy for the file is consistent with the
// caller's request, and ensure that the file is on the same volume.
//
status = IopOpenLinkOrRenameTarget( &targetHandle,
irp,
renameBuffer,
FileObject );
if (!NT_SUCCESS( status )) {
IoSetNextIrpStackLocation( irp );
IoCompleteRequest( irp, 2 );
} else {
//
// The fully qualified file name specifies a file on the same
// volume and if it exists, then the caller specified that it
// should be replaced.
//
status = IoCallDriver( deviceObject, irp );
}
} else {
//
// This is a simple rename operation, so call the driver and let
// it perform the rename operation within the same directory as
// the source file.
//
status = IoCallDriver( deviceObject, irp );
}
} else {
//
// This is not a request that can be performed here, so invoke the
// driver at its appropriate dispatch entry with the IRP.
//
status = IoCallDriver( deviceObject, irp );
}
//
// If this operation was a synchronous I/O operation, check the return
// status to determine whether or not to wait on the file object. If
// the file object is to be waited on, wait for the operation to complete
// and obtain the final status from the file object itself.
//
if (synchronousIo) {
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject( &FileObject->Event,
Executive,
KernelMode,
(BOOLEAN) ((FileObject->Flags & FO_ALERTABLE_IO) != 0),
(PLARGE_INTEGER) NULL );
if (status == STATUS_ALERTED) {
IopCancelAlertedRequest( &FileObject->Event, irp );
}
status = localIoStatus.Status;
}
IopReleaseFileObjectLock( FileObject );
} else {
//
// This is a normal synchronous I/O operation, as opposed to a
// serialized synchronous I/O operation. For this case, wait for
// the local event and copy the final status information back to
// the caller.
//
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
status = localIoStatus.Status;
}
}
//
// If a target handle was created because of a rename operation, close
// the handle now.
//
if (targetHandle != (HANDLE) NULL) {
NtClose( targetHandle );
}
return status;
}
VOID
IoSetShareAccess(
IN ACCESS_MASK DesiredAccess,
IN ULONG DesiredShareAccess,
IN OUT PFILE_OBJECT FileObject,
OUT PSHARE_ACCESS ShareAccess
)
/*++
Routine Description:
This routine is invoked to set the access and share access information
in a file system Share Access structure for the first open.
Arguments:
DesiredAccess - Desired access of current open request.
DesiredShareAccess - Shared access requested by current open request.
FileObject - Pointer to the file object of the current open request.
ShareAccess - Pointer to the share access structure that describes
how the file is currently being accessed.
Return Value:
None.
--*/
{
PAGED_CODE();
//
// Set the access type in the file object for the current accessor.
//
FileObject->ReadAccess = (BOOLEAN) ((DesiredAccess & (FILE_EXECUTE
| FILE_READ_DATA)) != 0);
FileObject->WriteAccess = (BOOLEAN) ((DesiredAccess & (FILE_WRITE_DATA
| FILE_APPEND_DATA)) != 0);
FileObject->DeleteAccess = (BOOLEAN) ((DesiredAccess & DELETE) != 0);
//
// Check to see whether the current file opener would like to read,
// write, or delete the file. If so, account for it in the share access
// structure; otherwise, skip it.
//
if (FileObject->ReadAccess ||
FileObject->WriteAccess ||
FileObject->DeleteAccess) {
//
// Only update the share modes if the user wants to read, write or
// delete the file.
//
FileObject->SharedRead = (BOOLEAN) ((DesiredShareAccess & FILE_SHARE_READ) != 0);
FileObject->SharedWrite = (BOOLEAN) ((DesiredShareAccess & FILE_SHARE_WRITE) != 0);
FileObject->SharedDelete = (BOOLEAN) ((DesiredShareAccess & FILE_SHARE_DELETE) != 0);
//
// Set the Share Access structure open count.
//
ShareAccess->OpenCount = 1;
//
// Set the number of readers, writers, and deleters in the Share Access
// structure.
//
ShareAccess->Readers = FileObject->ReadAccess;
ShareAccess->Writers = FileObject->WriteAccess;
ShareAccess->Deleters = FileObject->DeleteAccess;
//
// Set the number of shared readers, writers, and deleters in the Share
// Access structure.
//
ShareAccess->SharedRead = FileObject->SharedRead;
ShareAccess->SharedWrite = FileObject->SharedWrite;
ShareAccess->SharedDelete = FileObject->SharedDelete;
} else {
//
// No read, write, or delete access has been requested. Simply zero
// the appropriate fields in the structure so that the next accessor
// sees a consistent state.
//
ShareAccess->OpenCount = 0;
ShareAccess->Readers = 0;
ShareAccess->Writers = 0;
ShareAccess->Deleters = 0;
ShareAccess->SharedRead = 0;
ShareAccess->SharedWrite = 0;
ShareAccess->SharedDelete = 0;
}
}
BOOLEAN
IoSetThreadHardErrorMode(
IN BOOLEAN EnableHardErrors
)
/*++
Routine Description:
This routine either enables or disables hard errors for the current
thread and returns the old state of the flag.
Arguments:
EnableHardErrors - Supplies a BOOLEAN value indicating whether or not
hard errors are to be enabled for the current thread.
Return Value:
The final function value is the previous state of whether or not hard
errors were enabled.
--*/
{
PETHREAD thread;
BOOLEAN oldFlag;
//
// Get a pointer to the current thread, capture the current state of
// hard errors, and set the new state.
//
thread = PsGetCurrentThread();
oldFlag = !thread->HardErrorsAreDisabled;
thread->HardErrorsAreDisabled = !EnableHardErrors;
return oldFlag;
}
VOID
IoSetTopLevelIrp(
IN PIRP Irp
)
/*++
Routine Description:
This routine sets the top level IRP field in the current thread's thread
object. This function is invoked by file systems to either set this field
to the address of an I/O Request Packet (IRP) or to null it.
Arguments:
Irp - Pointer to the IRP to be stored in the top level IRP field.
Return Value:
None.
Note:
This function cannot be made a macro, since fields in the thread object
move from release to release, so this must remain a full function.
--*/
{
//
// Simply set the top level IRP field in the current thread's thread
// object.
//
(PIRP) (PsGetCurrentThread())->TopLevelIrp = Irp;
return;
}
BOOLEAN
IoShutdownSystem(
IN BOOLEAN RebootPending,
IN ULONG Phase
)
/*++
Routine Description:
This routine shuts down the I/O portion of the system in preparation
for a power-off or reboot.
Arguments:
RebootPending - Indicates whether a reboot is imminently pending.
Phase - Indicates which phase of shutdown is being performed.
Return Value:
The return value is the final status of the operation. For the I/O system, this is
always TRUE.
--*/
{
PLIST_ENTRY entry;
PSHUTDOWN_PACKET shutdown;
PDEVICE_OBJECT deviceObject;
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PVOID unlockHandle;
PAGED_CODE();
//
//
//
//
// Initialize the event used to synchronize the complete of all of the
// shutdown routines.
//
KeInitializeEvent( &event, NotificationEvent, FALSE );
if (Phase == 0) {
//
// Walk the list of the drivers in the system that have registered
// themselves as wanting to know when the system is about to be
// shutdown and invoke each.
//
while ((entry = ExInterlockedRemoveHeadList( &IopNotifyShutdownQueueHead, &IopDatabaseLock )) != NULL) {
shutdown = CONTAINING_RECORD( entry, SHUTDOWN_PACKET, ListEntry );
//
// Another driver has been found that has indicated that it requires
// shutdown notification. Invoke the driver's shutdown entry point.
//
deviceObject = IoGetAttachedDevice( shutdown->DeviceObject );
irp = IoBuildSynchronousFsdRequest( IRP_MJ_SHUTDOWN,
deviceObject,
(PVOID) NULL,
0,
(PLARGE_INTEGER) NULL,
&event,
&ioStatus );
if (IoCallDriver( deviceObject, irp ) == STATUS_PENDING) {
#if DBG
PUNICODE_STRING DeviceName = ObGetObjectName( shutdown->DeviceObject );
DbgPrint( "IO: Waiting for shutdown of device object (%x) - %wZ\n",
shutdown->DeviceObject,
DeviceName
);
#endif // DBG
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
}
ExFreePool( shutdown );
KeClearEvent( &event );
}
} else if (Phase == 1) {
//
// Lock this code into memory. Note that it has to be locked, since
// it cannot be faulted normally, as the file systems are being shutdown
// here, and they will no longer accept any requests.
//
unlockHandle = MmLockPagableCodeSection( IoShutdownSystem );
//
// Gain access to the file system header queues by acquiring the
// database resource for shared access.
//
(VOID) ExAcquireResourceShared( &IopDatabaseResource, TRUE );
//
// Loop through each of the disk file systems, invoking each to shutdown
// each of their mounted volumes.
//
for (entry = IopDiskFileSystemQueueHead.Flink;
entry != &IopDiskFileSystemQueueHead;
entry = entry->Flink) {
deviceObject = CONTAINING_RECORD( entry, DEVICE_OBJECT, Queue.ListEntry );
if (deviceObject->AttachedDevice) {
deviceObject = IoGetAttachedDevice( deviceObject );
}
//
// Another file system has been found. Invoke this file system at
// its shutdown entry point.
//
irp = IoBuildSynchronousFsdRequest( IRP_MJ_SHUTDOWN,
deviceObject,
(PVOID) NULL,
0,
(PLARGE_INTEGER) NULL,
&event,
&ioStatus );
if (IoCallDriver( deviceObject, irp ) == STATUS_PENDING) {
#if DBG
PUNICODE_STRING DeviceName = ObGetObjectName( deviceObject );
DbgPrint( "IO: Waiting for shutdown of device object (%x) - %wZ\n",
deviceObject,
DeviceName
);
#endif // DBG
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
}
KeClearEvent( &event );
}
//
// If a reboot is immenent, do not unlock the queue so that no other
// mount operations can take place. Otherwise, release the queue.
//
if (!RebootPending) {
ExReleaseResource( &IopDatabaseResource );
}
MmUnlockPagableImageSection( unlockHandle );
}
return TRUE;
}
#ifndef _PNP_POWER_
//
// Original non-power management copy of
// IoStartNextPacket, IoStartNextPacketByKey, IoStartPacket
//
VOID
IoStartNextPacket(
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Cancelable
)
/*++
Routine Description:
This routine is invoked to dequeue the next packet (IRP) from the
specified device work queue and invoke the device driver's start I/O
routine for it. If the Cancelable paramter is TRUE, then the update of
current IRP is synchronized using the cancel spinlock.
Arguments:
DeviceObject - Pointer to device object itself.
Cancelable - Indicates that IRPs in the device queue may be cancelable.
Return Value:
None.
--*/
{
KIRQL cancelIrql;
PIRP irp;
PKDEVICE_QUEUE_ENTRY packet;
//
// Begin by checking to see whether or not this driver's requests are
// to be considered cancelable. If so, then acquire the cancel spinlock.
//
if (Cancelable) {
IoAcquireCancelSpinLock( &cancelIrql );
}
//
// Clear the current IRP field before starting another request.
//
DeviceObject->CurrentIrp = (PIRP) NULL;
//
// Remove the next packet from the head of the queue. If a packet was
// found, then process it.
//
packet = KeRemoveDeviceQueue( &DeviceObject->DeviceQueue );
if (packet) {
irp = CONTAINING_RECORD( packet, IRP, Tail.Overlay.DeviceQueueEntry );
//
// A packet was located so make it the current packet for this
// device.
//
DeviceObject->CurrentIrp = irp;
if (Cancelable) {
IoReleaseCancelSpinLock( cancelIrql );
}
//
// Invoke the driver's start I/O routine for this packet.
//
DeviceObject->DriverObject->DriverStartIo( DeviceObject, irp );
} else {
//
// No packet was found, so simply release the cancel spinlock if
// it was acquired.
//
if (Cancelable) {
IoReleaseCancelSpinLock( cancelIrql );
}
}
}
VOID
IoStartNextPacketByKey(
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Cancelable,
IN ULONG Key
)
/*++
Routine Description:
This routine is invoked to dequeue the next packet (IRP) from the
specified device work queue by key and invoke the device driver's start
I/O routine for it. If the Cancelable paramter is TRUE, then the
update of current IRP is synchronized using the cancel spinlock.
Arguments:
DeviceObject - Pointer to device object itself.
Cancelable - Indicates that IRPs in the device queue may be cancelable.
Key - Specifics the Key used to remove the entry from the queue.
Return Value:
None.
--*/
{
KIRQL cancelIrql;
PIRP irp;
PKDEVICE_QUEUE_ENTRY packet;
//
// Begin by determining whether or not requests for this device are to
// be considered cancelable. If so, then acquire the cancel spinlock.
//
if (Cancelable) {
IoAcquireCancelSpinLock( &cancelIrql );
}
//
// Clear the current IRP field before starting another request.
//
DeviceObject->CurrentIrp = (PIRP) NULL;
//
// Attempt to remove the indicated packet according to the key from the
// device queue. If one is found, then process it.
//
packet = KeRemoveByKeyDeviceQueue( &DeviceObject->DeviceQueue, Key );
if (packet) {
irp = CONTAINING_RECORD( packet, IRP, Tail.Overlay.DeviceQueueEntry );
//
// A packet was successfully located. Make it the current packet
// and invoke the driver's start I/O routine for it.
//
DeviceObject->CurrentIrp = irp;
if (Cancelable) {
IoReleaseCancelSpinLock( cancelIrql );
}
DeviceObject->DriverObject->DriverStartIo( DeviceObject, irp );
} else {
//
// No packet was found, so release the cancel spinlock if it was
// acquired.
//
if (Cancelable) {
IoReleaseCancelSpinLock( cancelIrql );
}
}
}
VOID
IoStartPacket(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PULONG Key OPTIONAL,
IN PDRIVER_CANCEL CancelFunction OPTIONAL
)
/*++
Routine Description:
This routine attempts to start the specified packet request (IRP) on the
specified device. If the device is already busy, then the packet is
simply queued to the device queue. If a non-NULL CancelFunction is
supplied, it will be put in the IRP. If the IRP has been canceled, the
CancelFunction will be called after the IRP has been inserted into the
queue or made the current packet.
Arguments:
DeviceObject - Pointer to device object itself.
Irp - I/O Request Packet which should be started on the device.
Key - Key to be used in inserting packet into device queue; optional
(if not specified, then packet is inserted at the tail).
CancelFunction - Pointer to an optional cancel routine.
Return Value:
None.
--*/
{
KIRQL oldIrql;
KIRQL cancelIrql;
BOOLEAN i;
//
// Raise the IRQL of the processor to dispatch level for synchronization.
//
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
//
// If the driver has indicated that packets are cancelable, then acquire
// the cancel spinlock and set the address of the cancel function to
// indicate that the packet is not only cancelable, but indicates what
// routine to invoke should it be cancelled.
//
if (CancelFunction) {
IoAcquireCancelSpinLock( &cancelIrql );
Irp->CancelRoutine = CancelFunction;
}
//
// If a key parameter was specified, then insert the request into the
// work queue according to the key; otherwise, simply insert it at the
// tail.
//
if (Key) {
i = KeInsertByKeyDeviceQueue( &DeviceObject->DeviceQueue,
&Irp->Tail.Overlay.DeviceQueueEntry,
*Key );
} else {
i = KeInsertDeviceQueue( &DeviceObject->DeviceQueue,
&Irp->Tail.Overlay.DeviceQueueEntry );
}
//
// If the packet was not inserted into the queue, then this request is
// now the current packet for this device. Indicate so by storing its
// address in the current IRP field, and begin processing the request.
//
if (!i) {
DeviceObject->CurrentIrp = Irp;
if (CancelFunction) {
//
// This request is cancelable. If it has been cancelled already,
// then invoke the driver's cancel routine.
//
if (Irp->Cancel) {
Irp->CancelIrql = cancelIrql;
Irp->CancelRoutine = (PDRIVER_CANCEL) NULL;
CancelFunction( DeviceObject, Irp );
} else {
//
// This packet has not been canceled, so simply start the
// request on the device.
//
IoReleaseCancelSpinLock( cancelIrql );
DeviceObject->DriverObject->DriverStartIo( DeviceObject, Irp );
}
} else {
//
// This packet is not cancelable, so simply invoke the driver's
// start I/O routine to get the request going on the device.
//
DeviceObject->DriverObject->DriverStartIo( DeviceObject, Irp );
}
} else {
//
// The packet was successfully inserted into the device's work queue.
// Make one last check to determine whether or not the packet has
// already been marked cancelled. If it has, then invoke the
// driver's cancel routine now. Note that because the cancel
// spinlock is currently being held, an attempt to cancel the request
// from another processor at this point will simply wait until this
// routine is finished, and then get it cancelled.
//
if (CancelFunction) {
if (Irp->Cancel) {
Irp->CancelIrql = cancelIrql;
Irp->CancelRoutine = (PDRIVER_CANCEL) NULL;
CancelFunction( DeviceObject, Irp );
} else {
IoReleaseCancelSpinLock( cancelIrql );
}
}
}
//
// Restore the IRQL back to its value upon entry to this function before
// returning to the caller.
//
KeLowerIrql( oldIrql );
}
#else
//
// Power management versions os:
// IoStartNextPacket, IoStartNextPacketByKey, IoStartPacket
//
VOID
IoStartNextPacket(
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Cancelable
)
/*++
Routine Description:
This routine is invoked to dequeue the next packet (IRP) from the
specified device work queue and invoke the device driver's start I/O
routine for it. If the Cancelable paramter is TRUE, then the update of
current IRP is synchronized using the cancel spinlock.
Arguments:
DeviceObject - Pointer to device object itself.
Cancelable - Indicates that IRPs in the device queue may be cancelable.
Return Value:
None.
--*/
{
PIRP irp;
PLIST_ENTRY NextEntry;
PKDEVICE_QUEUE_ENTRY packet;
//
// Begin by checking to see whether or not this driver's requests are
// to be considered cancelable. If so, then acquire the cancel spinlock.
//
if (Cancelable) {
IopAcquireCancelSpinLockAtDpcLevel ();
}
//
// Synchronize with DeviceObject DeviceQueue & DeviceHoldingQueue
//
ExAcquireSpinLockAtDpcLevel (&DeviceObject->DeviceQueue.Lock);
ASSERT (DeviceObject->DeviceQueue.Busy == TRUE);
if (IsListEmpty (&DeviceObject->DeviceQueue.DeviceListHead)) {
//
// No packet was found in the queue, clear the Busy flag and
// release any obtained locks
//
DeviceObject->DeviceQueue.Busy = FALSE;
//
// Clear the current IRP field before starting another request.
//
DeviceObject->CurrentIrp = (PIRP) NULL;
ExReleaseSpinLockFromDpcLevel (&DeviceObject->DeviceQueue.Lock);
if (Cancelable) {
IopReleaseCancelSpinLockFromDpcLevel ();
}
} else {
//
// Remove next packet from queue
//
NextEntry = RemoveHeadList(&DeviceObject->DeviceQueue.DeviceListHead);
packet = CONTAINING_RECORD(NextEntry, KDEVICE_QUEUE_ENTRY, DeviceListEntry);
packet->Inserted = FALSE;
//
// Make it the current packet for this device.
//
irp = CONTAINING_RECORD (packet, IRP, Tail.Overlay.DeviceQueueEntry);
DeviceObject->CurrentIrp = irp;
#if 0
//
// If this is a SetPowerIrp, then the queue must be empty now
//
ASSERT (irp != DeviceObject->DeviceObjectExtension->CurrentSetPowerIrp ||
IsListEmpty(&DeviceObject->DeviceQueue.DeviceListHead) == TRUE);
#endif
//
// Release acquired locks and invoke the driver's start I/O routine
//
ExReleaseSpinLockFromDpcLevel (&DeviceObject->DeviceQueue.Lock);
if (Cancelable) {
IopReleaseCancelSpinLockFromDpcLevel ();
}
DeviceObject->DriverObject->DriverStartIo( DeviceObject, irp );
}
}
VOID
IoStartNextPacketByKey(
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Cancelable,
IN ULONG Key
)
/*++
Routine Description:
This routine is invoked to dequeue the next packet (IRP) from the
specified device work queue by key and invoke the device driver's start
I/O routine for it. If the Cancelable paramter is TRUE, then the
update of current IRP is synchronized using the cancel spinlock.
Arguments:
DeviceObject - Pointer to device object itself.
Cancelable - Indicates that IRPs in the device queue may be cancelable.
Key - Specifics the Key used to remove the entry from the queue.
Return Value:
None.
--*/
{
PIRP irp;
PLIST_ENTRY NextEntry;
PKDEVICE_QUEUE_ENTRY packet;
//
// Begin by determining whether or not requests for this device are to
// be considered cancelable. If so, then acquire the cancel spinlock.
//
if (Cancelable) {
IopAcquireCancelSpinLockAtDpcLevel ();
}
//
// Synchronize with DeviceObject DeviceQueue & DeviceHoldingQueue
//
ExAcquireSpinLockAtDpcLevel (&DeviceObject->DeviceQueue.Lock);
ASSERT (DeviceObject->DeviceQueue.Busy == TRUE);
if (IsListEmpty (&DeviceObject->DeviceQueue.DeviceListHead)) {
//
// No packet was found in the queue, clear the Busy flag and
// release any obtained locks
//
DeviceObject->DeviceQueue.Busy = FALSE;
//
// Clear the current IRP field before starting another request.
//
DeviceObject->CurrentIrp = (PIRP) NULL;
ExReleaseSpinLockFromDpcLevel (&DeviceObject->DeviceQueue.Lock);
if (Cancelable) {
IopReleaseCancelSpinLockFromDpcLevel ();
}
} else {
//
// Remove the requested packet from the queue
//
NextEntry = DeviceObject->DeviceQueue.DeviceListHead.Flink;
while (NextEntry != &DeviceObject->DeviceQueue.DeviceListHead) {
packet = CONTAINING_RECORD(NextEntry, KDEVICE_QUEUE_ENTRY,
DeviceListEntry);
if (Key <= packet->SortKey) {
break;
}
NextEntry = NextEntry->Flink;
}
if (NextEntry != &DeviceObject->DeviceQueue.DeviceListHead) {
RemoveEntryList(&packet->DeviceListEntry);
} else {
NextEntry = RemoveHeadList(&DeviceObject->DeviceQueue.DeviceListHead);
packet = CONTAINING_RECORD(NextEntry, KDEVICE_QUEUE_ENTRY,
DeviceListEntry);
}
packet->Inserted = FALSE;
//
// Make it the current packet for this device.
//
irp = CONTAINING_RECORD (packet, IRP, Tail.Overlay.DeviceQueueEntry);
DeviceObject->CurrentIrp = irp;
#if 0
//
// If this is a SetPowerIrp, then the queue must be empty now
//
ASSERT (irp != DeviceObject->DeviceObjectExtension->CurrentSetPowerIrp ||
IsListEmpty(&DeviceObject->DeviceQueue.DeviceListHead) == TRUE);
#endif
//
// Release acquired locks and invoke the driver's start I/O routine
//
ExReleaseSpinLockFromDpcLevel (&DeviceObject->DeviceQueue.Lock);
if (Cancelable) {
IopReleaseCancelSpinLockFromDpcLevel ();
}
DeviceObject->DriverObject->DriverStartIo( DeviceObject, irp );
}
}
//
// VOID
// IopInsertToDeviceQueue(DevQueue,NewEntry,Key)
// IN KDEVICE_QUEUE DevQueue,
// IN KDEVICE_QUEUE_ENTRY NewEntry,
// IN PULONG Key OPTIONAL
//
// Routine Description:
//
// Inserts NewEntry onto the given DevQueue. If Key is specified the
// entry is inserted sorted; otherwise it's inserted FIFO.
//
// IoStartNextPacket or IoStartNextPacketByKey remove entries
// inserted with this function.
//
// Note: when used on the DeviceObject->DeviceQueue the queue is
// maintained in a compatible format with the ke device queue API.
// (I don't know if that's a requirement, but it will interoperate
// with those api)
//
#define IopInsertToDeviceQueue(DevQueue,NewEntry,Key) \
{ \
ULONG SortKey; \
PLIST_ENTRY NextEntry; \
PKDEVICE_QUEUE_ENTRY Entry; \
\
if (!ARGUMENT_PRESENT(Key)) { \
InsertTailList(&DevQueue.DeviceListHead, \
&NewEntry.DeviceListEntry); \
} else { \
SortKey = *Key; \
NextEntry = DevQueue.DeviceListHead.Flink; \
while (NextEntry != &DevQueue.DeviceListHead) { \
Entry = CONTAINING_RECORD(NextEntry, KDEVICE_QUEUE_ENTRY, \
DeviceListEntry); \
if (SortKey < Entry->SortKey) { \
break; \
} \
NextEntry = NextEntry->Flink; \
} \
NextEntry = NextEntry->Blink; \
InsertHeadList(NextEntry, &NewEntry.DeviceListEntry); \
} \
NewEntry.Inserted = TRUE; \
}
VOID
IoStartPacket(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PULONG Key OPTIONAL,
IN PDRIVER_CANCEL CancelFunction OPTIONAL
)
/*++
Routine Description:
This routine attempts to start the specified packet request (IRP) on the
specified device. If the device is already busy, then the packet is
simply queued to the device queue. If a non-NULL CancelFunction is
supplied, it will be put in the IRP. If the IRP has been canceled, the
CancelFunction will be called after the IRP has been inserted into the
queue or made the current packet.
Arguments:
DeviceObject - Pointer to device object itself.
Irp - I/O Request Packet which should be started on the device.
Key - Key to be used in inserting packet into device queue; optional
(if not specified, then packet is inserted at the tail).
CancelFunction - Pointer to an optional cancel routine.
Return Value:
None.
--*/
{
KIRQL oldIrql, cancelIrql;
BOOLEAN flag;
PDEVOBJ_EXTENSION DeviceObjectExt;
//
// BUGBUG: for now some of these fields are in a different structure
//
DeviceObjectExt = DeviceObject->DeviceObjectExtension;
//
// Driver StartIO assumes it's called at dispatch level
//
KeRaiseIrql (DISPATCH_LEVEL, &oldIrql);
//
// If the driver has indicated that packets are cancelable, then acquire
// the cancel spinlock and set the address of the cancel function to
// indicate that the packet is not only cancelable, but indicates what
// routine to invoke should it be cancelled.
//
if (CancelFunction) {
IopAcquireCancelSpinLockAtDpcLevel ();
//
// If IRP is already in the canceled state, cancel it now
//
if (Irp->Cancel) {
// trade AcquireAtDpcLevel acquired lock for normal Acquire
IopReleaseCancelSpinLockFromDpcLevel ();
IoAcquireCancelSpinLock (&cancelIrql);
Irp->CancelIrql = cancelIrql;
Irp->CancelRoutine = (PDRIVER_CANCEL) NULL;
CancelFunction( DeviceObject, Irp );
return ;
}
Irp->CancelRoutine = CancelFunction;
}
//
// Synchronize with DeviceObject DeviceQueue & DeviceHoldingQueue
//
ExAcquireSpinLockAtDpcLevel (&DeviceObject->DeviceQueue.Lock);
//
// If StartIoQueue is not holding, or if this is a SetPowerIrp,
// let this IRP go to the device's StartIO routine
//
if (!DeviceObjectExt->StartIoQueueHolding ||
DeviceObjectExt->CurrentSetPowerIrp == Irp ) {
DeviceObjectExt->StartIoQueueHolding = (BOOLEAN) Irp->Flags & IRP_HOLD_DEVICE_QUEUE;
//
// If StartIO not busy, send it this irp otherwise queue it
//
if (DeviceObject->DeviceQueue.Busy == FALSE) {
//
// StartIO is now busy, queue any new IRPs
//
DeviceObject->DeviceQueue.Busy = TRUE;
//
// Release queue lock & cancel lock
//
ExReleaseSpinLockFromDpcLevel (&DeviceObject->DeviceQueue.Lock);
if (CancelFunction) {
IopReleaseCancelSpinLockFromDpcLevel ();
}
//
// Call driver with irp
//
DeviceObject->CurrentIrp = Irp ;
DeviceObject->DriverObject->DriverStartIo( DeviceObject, Irp );
} else {
//
// Add irp to Device Queue
//
IopInsertToDeviceQueue (
DeviceObject->DeviceQueue,
Irp->Tail.Overlay.DeviceQueueEntry,
Key
);
//
// Release queue lock & cancel lock
//
ExReleaseSpinLockFromDpcLevel (&DeviceObject->DeviceQueue.Lock);
if (CancelFunction) {
IopReleaseCancelSpinLockFromDpcLevel ();
}
}
} else {
//
// The StartIo queue is holding - add the IRP to the DeviceHoldingQueue
//
IopInsertToDeviceQueue (
DeviceObjectExt->DeviceHoldingQueue,
Irp->Tail.Overlay.DeviceQueueEntry,
Key
);
//
// Release queue lock & cancel lock
//
ExReleaseSpinLockFromDpcLevel (&DeviceObject->DeviceQueue.Lock);
if (CancelFunction) {
IopReleaseCancelSpinLockFromDpcLevel ();
}
//
// Request a PowerUp for this device which will release the
// DeviceQueueHolding state
//
PoRequestPowerChange( DeviceObject, PowerUp, PowerUnspecified );
}
KeLowerIrql (oldIrql);
}
VOID
IoHoldDeviceQueue (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine holds the device/irp queue of a device object for the specified
IRP. This routine is invoked by a higher level device driver to temporarily
hold a lower level device's irp queue before sending device removal irp to
the lower level device.
Arguments:
DeviceObject - Supplies a pointer to the device object whose irp queue is to
be temporarily held.
Irp - Supplies a pointer to the irp which caused the irp queue to
be held.
Return Value:
None.
--*/
{
PDEVOBJ_EXTENSION objectExtension;
KIRQL oldIrql;
objectExtension = DeviceObject->DeviceObjectExtension;
Irp->Flags |= IRP_HOLD_DEVICE_QUEUE;
}
VOID
IoReleaseStartIoHoldingQueue (
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine is invoked when a queue holding irp is complete.
All the Irps before and including the first irp with IRP_HOLD_QUEUE flag
set will be released to the device object's device queue. This routine is
intended to be called from driver's completion routine and must be called at
DPC level.
Arguments:
DeviceObject - Pointer to device object itself.
Return Value:
None.
--*/
{
PDEVOBJ_EXTENSION DeviceObjectExt;
PIRP Irp;
PLIST_ENTRY ListEntry;
PLIST_ENTRY ListHead;
PKDEVICE_QUEUE_ENTRY Packet;
BOOLEAN StartPacket = FALSE;
DeviceObjectExt = DeviceObject->DeviceObjectExtension;
ExAcquireSpinLockAtDpcLevel (&DeviceObject->DeviceQueue.Lock);
DeviceObjectExt->StartIoQueueHolding = FALSE;
//
// We're assuming that the DeviceQueue drained before the
// StartIoQueueHolding was reset
//
ListHead = &DeviceObjectExt->DeviceHoldingQueue.DeviceListHead;
ListEntry = ListHead->Flink;
while (ListEntry != ListHead) {
StartPacket = TRUE;
Packet = CONTAINING_RECORD(ListEntry, KDEVICE_QUEUE_ENTRY, DeviceListEntry);
Irp = CONTAINING_RECORD (Packet, IRP, Tail.Overlay.DeviceQueueEntry);
RemoveEntryList(ListEntry);
InsertTailList(&DeviceObject->DeviceQueue.DeviceListHead, ListEntry);
if (Irp->Flags & IRP_HOLD_DEVICE_QUEUE) {
DeviceObjectExt->StartIoQueueHolding = TRUE;
break;
} else {
ListEntry = ListHead->Flink;
}
}
ExReleaseSpinLockFromDpcLevel (&DeviceObject->DeviceQueue.Lock);
if (StartPacket) {
IoStartNextPacket(DeviceObject, (BOOLEAN) (Irp->CancelRoutine != NULL) );
}
}
#endif
VOID
IoStartTimer(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine starts the timer associated with the specified device object.
Arguments:
DeviceObject - Device object associated with the timer to be started.
Return Value:
None.
--*/
{
PIO_TIMER timer;
KIRQL irql;
//
// Get the address of the timer.
//
timer = DeviceObject->Timer;
//
// If the driver is not being unloaded, then it is okay to start timers.
//
if (!(DeviceObject->DeviceObjectExtension->ExtensionFlags &
(DOE_UNLOAD_PENDING | DOE_DELETE_PENDING))) {
//
// Likewise, check to see whether or not the timer is already
// enabled. If so, then simply exit. Otherwise, enable the timer
// by placing it into the I/O system timer queue.
//
ExAcquireFastLock( &IopTimerLock, &irql );
if (!timer->TimerFlag) {
timer->TimerFlag = TRUE;
IopTimerCount++;
}
ExReleaseFastLock( &IopTimerLock, irql );
}
}
VOID
IoStopTimer(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routines stops the timer associated with the specified device object
from invoking being invoked.
Arguments:
DeviceObject - Device object associated with the timer to be stopped.
Return Value:
None.
--*/
{
KIRQL irql;
PIO_TIMER timer;
//
// Obtain the I/O system timer queue lock, and disable the specified
// timer.
//
timer = DeviceObject->Timer;
ExAcquireFastLock( &IopTimerLock, &irql );
if (timer->TimerFlag) {
timer->TimerFlag = FALSE;
IopTimerCount--;
}
ExReleaseFastLock( &IopTimerLock, irql );
}
NTSTATUS
IoSynchronousPageWrite(
IN PFILE_OBJECT FileObject,
IN PMDL MemoryDescriptorList,
IN PLARGE_INTEGER StartingOffset,
IN PKEVENT Event,
OUT PIO_STATUS_BLOCK IoStatusBlock
)
/*++
Routine Description:
This routine provides a special, fast interface for the Modified Page Writer
(MPW) to write pages to the disk quickly and with very little overhead. All
of the special handling for this request is recognized by setting the
IRP_PAGING_IO flag in the IRP flags word.
Arguments:
FileObject - A pointer to a referenced file object describing which file
the write should be performed on.
MemoryDescriptorList - An MDL which describes the physical pages that the
pages should be written to the disk. All of the pages have been locked
in memory. The MDL also describes the length of the write operation.
StartingOffset - Pointer to the offset in the file from which the write
should take place.
Event - A pointer to a kernel event structure to be used for synchronization
purposes. The event will be set to the Signlaged state once the pages
have been written.
IoStatusBlock - A pointer to the I/O status block in which the final status
and information should be stored.
Return Value:
The function value is the final status of the queue request to the I/O
system subcomponents.
--*/
{
PIRP irp;
PIO_STACK_LOCATION irpSp;
PDEVICE_OBJECT deviceObject;
//
// Increment performance counters
//
if (CcIsFileCached(FileObject)) {
CcDataFlushes += 1;
CcDataPages += (MemoryDescriptorList->ByteCount + PAGE_SIZE - 1) >> PAGE_SHIFT;
}
//
// Begin by getting a pointer to the device object that the file resides
// on.
//
deviceObject = IoGetRelatedDeviceObject( FileObject );
//
// Allocate an I/O Request Packet (IRP) for this out-page operation.
//
irp = IoAllocateIrp( deviceObject->StackSize, FALSE );
if (!irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Get a pointer to the first stack location in the packet. This location
// will be used to pass the function codes and parameters to the first
// driver.
//
irpSp = IoGetNextIrpStackLocation( irp );
//
// Fill in the IRP according to this request.
//
irp->MdlAddress = MemoryDescriptorList;
irp->Flags = IRP_PAGING_IO | IRP_NOCACHE | IRP_SYNCHRONOUS_PAGING_IO;
irp->RequestorMode = KernelMode;
irp->UserIosb = IoStatusBlock;
irp->UserEvent = Event;
irp->UserBuffer = (PVOID) ((PCHAR) MemoryDescriptorList->StartVa + MemoryDescriptorList->ByteOffset);
irp->Tail.Overlay.OriginalFileObject = FileObject;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
//
// Fill in the normal write parameters.
//
irpSp->MajorFunction = IRP_MJ_WRITE;
irpSp->Parameters.Write.Length = MemoryDescriptorList->ByteCount;
irpSp->Parameters.Write.ByteOffset = *StartingOffset;
irpSp->FileObject = FileObject;
//
// Queue the packet to the appropriate driver based on whether or not there
// is a VPB associated with the device.
//
return IoCallDriver( deviceObject, irp );
}
PEPROCESS
IoThreadToProcess(
IN PETHREAD Thread
)
/*++
Routine Description:
This routine returns a pointer to the process for the specified thread.
Arguments:
Thread - Thread whose process is to be returned.
Return Value:
A pointer to the thread's process.
Note:
This function cannot be made a macro, since fields in the thread object
move from release to release, so this must remain a full function.
--*/
{
//
// Simply return the thread's process.
//
return THREAD_TO_PROCESS( Thread );
}
VOID
IoUnregisterFileSystem(
IN OUT PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine removes the device object for the file system from the active
list of file systems in the system.
Arguments:
DeviceObject - Pointer to device object for the file system.
Return Value:
None.
--*/
{
PNOTIFICATION_PACKET nPacket;
PLIST_ENTRY entry;
PAGED_CODE();
//
// Allocate the I/O database resource for a write operation.
//
(VOID)ExAcquireResourceExclusive( &IopDatabaseResource, TRUE );
//
// Remove the device object from whatever queue it happens to be in at the
// moment. There is no need to check here to determine if the device queue
// is in a queue since it is assumed that the caller registered it as a
// valid file system.
//
RemoveEntryList( &DeviceObject->Queue.ListEntry );
//
// Notify all of the registered drivers that this file system has been
// unregistered as an active file system of some type.
//
entry = IopFsNotifyChangeQueueHead.Flink;
while (entry != &IopFsNotifyChangeQueueHead) {
nPacket = CONTAINING_RECORD( entry, NOTIFICATION_PACKET, ListEntry );
entry = entry->Flink;
nPacket->NotificationRoutine( DeviceObject, FALSE );
}
//
// Release the I/O database resource.
//
ExReleaseResource( &IopDatabaseResource );
//
// Decrement the number of reasons that this driver cannot be unloaded.
//
ExInterlockedAddUlong( &DeviceObject->ReferenceCount,
0xffffffff,
&IopDatabaseLock );
}
VOID
IoUnregisterFsRegistrationChange(
IN PDRIVER_OBJECT DriverObject,
IN PDRIVER_FS_NOTIFICATION DriverNotificationRoutine
)
/*++
Routine Description:
This routine unregisters the specified driver's notification routine from
begin invoked whenever a file system registers or unregisters itself as an
active file system in the system.
Arguments:
DriverObject - Pointer to the driver object for the driver.
DriverNotificationRoutine - Address of routine to unregister.
Return Value:
None.
--*/
{
PNOTIFICATION_PACKET nPacket;
PLIST_ENTRY entry;
PAGED_CODE();
ASSERT( DriverObject->DeviceObject );
//
// Begin by acquiring the database resource exclusively.
//
ExAcquireResourceExclusive( &IopDatabaseResource, TRUE );
//
// Walk the list of registered notification routines and unregister the
// specified routine.
//
for (entry = IopFsNotifyChangeQueueHead.Flink;
entry != &IopFsNotifyChangeQueueHead;
entry = entry->Flink) {
nPacket = CONTAINING_RECORD( entry, NOTIFICATION_PACKET, ListEntry );
if (nPacket->DriverObject == DriverObject &&
nPacket->NotificationRoutine == DriverNotificationRoutine) {
RemoveEntryList( entry );
ExFreePool( nPacket );
break;
}
}
ExReleaseResource( &IopDatabaseResource );
//
// Decrement the number of reasons that this driver cannot be unloaded.
//
ExInterlockedAddUlong( &DriverObject->DeviceObject->ReferenceCount,
0xffffffff,
&IopDatabaseLock );
}
VOID
IoUnregisterShutdownNotification(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
This routine removes a registered driver from the shutdown notification
queue. Henceforth, the driver will not be notified when the system is
being shutdown.
Arguments:
DeviceObject - Pointer to the driver's device object.
Return Value:
None.
--*/
{
PLIST_ENTRY entry;
PSHUTDOWN_PACKET shutdown;
KIRQL irql;
PVOID unlockHandle;
PAGED_CODE();
//
// Lock this code into memory for the duration of this function's execution.
//
unlockHandle = MmLockPagableCodeSection( IoUnregisterShutdownNotification );
//
// Acquire the spinlock that protects the shutdown notification queue, and
// walk the queue looking for the caller's entry. Once found, remove it
// from the queue. It is an error to not find it, but it is ignored here.
//
ExAcquireSpinLock( &IopDatabaseLock, &irql );
for (entry = IopNotifyShutdownQueueHead.Flink;
entry != &IopNotifyShutdownQueueHead;
entry = entry->Flink) {
//
// An entry has been located. If it is the one that is being searched
// for, simply remove it from the list and deallocate it.
//
shutdown = CONTAINING_RECORD( entry, SHUTDOWN_PACKET, ListEntry );
if (shutdown->DeviceObject == DeviceObject) {
RemoveEntryList( entry );
break;
}
}
//
// Release the spinlock.
//
ExReleaseSpinLock( &IopDatabaseLock, irql );
MmUnlockPagableImageSection( unlockHandle );
if (entry != &IopNotifyShutdownQueueHead) {
//
// The entry was found and removed from the queue, so simply free it
// and account for it in the device object.
//
DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
ExFreePool( entry );
}
}
VOID
IoUpdateShareAccess(
IN OUT PFILE_OBJECT FileObject,
IN OUT PSHARE_ACCESS ShareAccess
)
/*++
Routine Description:
This routine updates the share access context for a file according to
the desired access and share access by the current open requestor. The
IoCheckShareAccess routine must already have been invoked and succeeded
in order to invoke this routine. Note that when the former routine was
invoked the Update parameter must have been FALSE.
Arguments:
FileObject - Pointer to the file object of the current open request.
ShareAccess - Pointer to the share access structure that describes how
the file is currently being accessed.
Return Value:
None.
--*/
{
PAGED_CODE();
//
// Check to see whether or not the desired accesses need read, write,
// or delete access to the file.
//
if (FileObject->ReadAccess ||
FileObject->WriteAccess ||
FileObject->DeleteAccess) {
//
// The open request requires read, write, or delete access so update
// the share access context for the file.
//
ShareAccess->OpenCount++;
ShareAccess->Readers += FileObject->ReadAccess;
ShareAccess->Writers += FileObject->WriteAccess;
ShareAccess->Deleters += FileObject->DeleteAccess;
ShareAccess->SharedRead += FileObject->SharedRead;
ShareAccess->SharedWrite += FileObject->SharedWrite;
ShareAccess->SharedDelete += FileObject->SharedDelete;
}
}
NTSTATUS
IoVerifyVolume(
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN AllowRawMount
)
/*++
Routine Description:
This routine is invoked to check a mounted volume on the specified device
when it appears as if the media may have changed since it was last
accessed. If the volume is not the same volume, and a new mount is not
to be attempted, return the error.
If the verify fails, this routine is used to perform a new mount operation
on the device. In this case, a "clean" VPB is allocated and a new mount
operation is attempted. If no mount operation succeeds, then again the
error handling described above occurs.
Arguments:
DeviceObject - Pointer to device object on which the volume is to be
mounted.
AllowRawMount - Indicates that this verify is on behalf of a DASD open
request, thus we want to allow a raw mount if the verify fails.
Return Value:
The function value is a successful status code if a volume was successfully
mounted on the device. Otherwise, an error code is returned.
--*/
{
NTSTATUS status;
KEVENT event;
PIRP irp;
IO_STATUS_BLOCK ioStatus;
PIO_STACK_LOCATION irpSp;
PVPB newVpb;
BOOLEAN verifySkipped = FALSE;
PDEVICE_OBJECT fsDeviceObject;
//
// Acquire the DeviceObject lock. Nothing in this routine can raise
// so no try {} finally {} is required.
//
status = KeWaitForSingleObject( &DeviceObject->DeviceLock,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
ASSERT( status == STATUS_SUCCESS );
//
// If this volume is not mounted by anyone, skip the verify operation,
// but do the mount.
//
if (!(DeviceObject->Vpb->Flags & VPB_MOUNTED)) {
verifySkipped = TRUE;
status = STATUS_SUCCESS;
} else {
//
// This volume needs to be verified. Initialize the event to be
// used while waiting for the operation to complete.
//
KeInitializeEvent( &event, NotificationEvent, FALSE );
status = STATUS_UNSUCCESSFUL;
//
// Allocate and initialize an IRP for this verify operation. Notice
// that the flags for this operation appear the same as a page read
// operation. This is because the completion code for both of the
// operations is exactly the same logic.
//
fsDeviceObject = DeviceObject->Vpb->DeviceObject;
while (fsDeviceObject->AttachedDevice) {
fsDeviceObject = fsDeviceObject->AttachedDevice;
}
irp = IoAllocateIrp( fsDeviceObject->StackSize, FALSE );
if (!irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO;
irp->RequestorMode = KernelMode;
irp->UserEvent = &event;
irp->UserIosb = &ioStatus;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irpSp = IoGetNextIrpStackLocation( irp );
irpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
irpSp->MinorFunction = IRP_MN_VERIFY_VOLUME;
irpSp->Flags = AllowRawMount ? SL_ALLOW_RAW_MOUNT : 0;
irpSp->Parameters.VerifyVolume.Vpb = DeviceObject->Vpb;
irpSp->Parameters.VerifyVolume.DeviceObject = DeviceObject->Vpb->DeviceObject;
status = IoCallDriver( fsDeviceObject, irp );
//
// Wait for the I/O operation to complete.
//
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL );
status = ioStatus.Status;
}
}
//
// If the verify operation was skipped or unsuccessful perform a mount
// operation.
//
if ((status == STATUS_WRONG_VOLUME) || verifySkipped) {
//
// A mount operation is to be attempted. Allocate a new VPB
// for this device and attempt to mount it. Note that at this
// point, allowing allocation of the VPB to fail is simply too
// difficult to deal with, so if one cannot be allocated normally,
// allocate one specifying that it must succeed.
//
newVpb = ExAllocatePoolWithTag( NonPagedPool, sizeof( VPB ), ' bpV' );
if (newVpb == NULL) {
newVpb = ExAllocatePoolWithTag( NonPagedPoolMustSucceed,
sizeof( VPB ),
' bpV' );
}
RtlZeroMemory( newVpb, sizeof( VPB ) );
newVpb->Type = IO_TYPE_VPB;
newVpb->Size = sizeof( VPB );
newVpb->RealDevice = DeviceObject;
DeviceObject->Vpb = newVpb;
//
// Now mount the volume.
//
if (!NT_SUCCESS( IopMountVolume( DeviceObject, AllowRawMount, TRUE ) )) {
DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
}
}
//
// Release the device lock.
//
KeSetEvent( &DeviceObject->DeviceLock, 0, FALSE );
//
// Return the status from the verify operation as the final status of
// this function.
//
return status;
}
VOID
IoWriteErrorLogEntry(
IN OUT PVOID ElEntry
)
/*++
Routine Description:
This routine places the error log entry specified by the input argument
onto the queue of buffers to be written to the error log process's port.
The error log thread will then actually send it.
Arguments:
Return Value:
--*/
{
PERROR_LOG_ENTRY entry;
KIRQL oldIrql;
//
// Get the address of the error log entry header, acquire the spin lock,
// insert the entry onto the queue, if there are no pending requests
// then queue a worker thread request and release the spin lock.
//
entry = ((PERROR_LOG_ENTRY) ElEntry) - 1;
//
// Set the time that the entry was logged.
//
KeQuerySystemTime( (PVOID) &entry->TimeStamp );
ExAcquireSpinLock( &IopErrorLogLock, &oldIrql );
//
// Queue the request to the error log queue.
//
InsertTailList( &IopErrorLogListHead, &entry->ListEntry );
//
// If there is no pending work, then queue a request to a worker thread.
//
if (!IopErrorLogPortPending) {
IopErrorLogPortPending = TRUE;
ExInitializeWorkItem( &IopErrorLogWorkItem, IopErrorLogThread, NULL );
ExQueueWorkItem( &IopErrorLogWorkItem, DelayedWorkQueue );
}
ExReleaseSpinLock(&IopErrorLogLock, oldIrql);
}
//
// Thunks to support standard call callers
//
#ifdef IoCallDriver
#undef IoCallDriver
#endif
NTSTATUS
IoCallDriver(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
{
return IofCallDriver (DeviceObject, Irp);
}
#ifdef IoCompleteRequest
#undef IoCompleteRequest
#endif
VOID
IoCompleteRequest(
IN PIRP Irp,
IN CCHAR PriorityBoost
)
{
IofCompleteRequest (Irp, PriorityBoost);
}