mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 12:26:32 +00:00
c9864da823
ExAllocatePoolWithTag doesn't raise an exception on failure, only ExAllocatePoolWithQuotaTag does. Use that when quotas are relevant instead of silently continuing with a NULL pointer.
4647 lines
154 KiB
C
4647 lines
154 KiB
C
/*
|
|
* PROJECT: ReactOS Kernel
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: ntoskrnl/io/iomgr/iofunc.c
|
|
* PURPOSE: Generic I/O Functions that build IRPs for various operations
|
|
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
|
|
* Gunnar Dalsnes
|
|
* Filip Navara (navaraf@reactos.org)
|
|
* Pierre Schweitzer (pierre@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#include <ioevent.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
#include "internal/io_i.h"
|
|
|
|
volatile LONG IoPageReadIrpAllocationFailure = 0;
|
|
volatile LONG IoPageReadNonPagefileIrpAllocationFailure = 0;
|
|
|
|
/* PRIVATE FUNCTIONS *********************************************************/
|
|
|
|
VOID
|
|
NTAPI
|
|
IopCleanupAfterException(IN PFILE_OBJECT FileObject,
|
|
IN PIRP Irp OPTIONAL,
|
|
IN PKEVENT Event OPTIONAL,
|
|
IN PKEVENT LocalEvent OPTIONAL)
|
|
{
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "IRP: %p. FO: %p\n", Irp, FileObject);
|
|
|
|
if (Irp)
|
|
{
|
|
/* Check if we had a buffer */
|
|
if (Irp->AssociatedIrp.SystemBuffer)
|
|
{
|
|
/* Free it */
|
|
ExFreePool(Irp->AssociatedIrp.SystemBuffer);
|
|
}
|
|
|
|
/* Free the mdl */
|
|
if (Irp->MdlAddress) IoFreeMdl(Irp->MdlAddress);
|
|
|
|
/* Free the IRP */
|
|
IoFreeIrp(Irp);
|
|
}
|
|
|
|
/* Check if we had a file lock */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Release it */
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
|
|
/* Check if we had an event */
|
|
if (Event) ObDereferenceObject(Event);
|
|
|
|
/* Check if we had a local event */
|
|
if (LocalEvent) ExFreePool(LocalEvent);
|
|
|
|
/* Derefenrce the FO */
|
|
ObDereferenceObject(FileObject);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopFinalizeAsynchronousIo(IN NTSTATUS SynchStatus,
|
|
IN PKEVENT Event,
|
|
IN PIRP Irp,
|
|
IN KPROCESSOR_MODE PreviousMode,
|
|
IN PIO_STATUS_BLOCK KernelIosb,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock)
|
|
{
|
|
NTSTATUS FinalStatus = SynchStatus;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "IRP: %p. Status: %lx\n", Irp, SynchStatus);
|
|
|
|
/* Make sure the IRP was completed, but returned pending */
|
|
if (FinalStatus == STATUS_PENDING)
|
|
{
|
|
/* Wait for the IRP */
|
|
FinalStatus = KeWaitForSingleObject(Event,
|
|
Executive,
|
|
PreviousMode,
|
|
FALSE,
|
|
NULL);
|
|
if (FinalStatus == STATUS_USER_APC)
|
|
{
|
|
/* Abort the request */
|
|
IopAbortInterruptedIrp(Event, Irp);
|
|
}
|
|
|
|
/* Set the final status */
|
|
FinalStatus = KernelIosb->Status;
|
|
}
|
|
|
|
/* Wrap potential user-mode write in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
*IoStatusBlock = *KernelIosb;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
FinalStatus = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Free the event and return status */
|
|
ExFreePool(Event);
|
|
return FinalStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopPerformSynchronousRequest(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PFILE_OBJECT FileObject,
|
|
IN BOOLEAN Deferred,
|
|
IN KPROCESSOR_MODE PreviousMode,
|
|
IN BOOLEAN SynchIo,
|
|
IN IOP_TRANSFER_TYPE TransferType)
|
|
{
|
|
NTSTATUS Status;
|
|
PKNORMAL_ROUTINE NormalRoutine;
|
|
PVOID NormalContext = NULL;
|
|
KIRQL OldIrql;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "IRP: %p. DO: %p. FO: %p\n",
|
|
Irp, DeviceObject, FileObject);
|
|
|
|
/* Queue the IRP */
|
|
IopQueueIrpToThread(Irp);
|
|
|
|
/* Update operation counts */
|
|
IopUpdateOperationCount(TransferType);
|
|
|
|
/* Call the driver */
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
|
|
/* Check if we're optimizing this case */
|
|
if (Deferred)
|
|
{
|
|
/* We are! Check if the IRP wasn't completed */
|
|
if (Status != STATUS_PENDING)
|
|
{
|
|
/* Complete it ourselves */
|
|
NormalRoutine = NULL;
|
|
NormalContext = NULL;
|
|
ASSERT(!Irp->PendingReturned);
|
|
KeRaiseIrql(APC_LEVEL, &OldIrql);
|
|
IopCompleteRequest(&Irp->Tail.Apc,
|
|
&NormalRoutine,
|
|
&NormalContext,
|
|
(PVOID*)&FileObject,
|
|
&NormalContext);
|
|
KeLowerIrql(OldIrql);
|
|
}
|
|
}
|
|
|
|
/* Check if this was synch I/O */
|
|
if (SynchIo)
|
|
{
|
|
/* Make sure the IRP was completed, but returned pending */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
/* Wait for the IRP */
|
|
Status = KeWaitForSingleObject(&FileObject->Event,
|
|
Executive,
|
|
PreviousMode,
|
|
(FileObject->Flags &
|
|
FO_ALERTABLE_IO) != 0,
|
|
NULL);
|
|
if ((Status == STATUS_ALERTED) || (Status == STATUS_USER_APC))
|
|
{
|
|
/* Abort the request */
|
|
IopAbortInterruptedIrp(&FileObject->Event, Irp);
|
|
}
|
|
|
|
/* Set the final status */
|
|
Status = FileObject->FinalStatus;
|
|
}
|
|
|
|
/* Release the file lock */
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopDeviceFsIoControl(IN HANDLE DeviceHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE UserApcRoutine OPTIONAL,
|
|
IN PVOID UserApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG IoControlCode,
|
|
IN PVOID InputBuffer,
|
|
IN ULONG InputBufferLength OPTIONAL,
|
|
OUT PVOID OutputBuffer,
|
|
IN ULONG OutputBufferLength OPTIONAL,
|
|
IN BOOLEAN IsDevIoCtl)
|
|
{
|
|
NTSTATUS Status;
|
|
PFILE_OBJECT FileObject;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
PKEVENT EventObject = NULL;
|
|
BOOLEAN LockedForSynch = FALSE;
|
|
ULONG AccessType;
|
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|
ACCESS_MASK DesiredAccess;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
ULONG BufferLength;
|
|
POOL_TYPE PoolType;
|
|
|
|
PAGED_CODE();
|
|
|
|
IOTRACE(IO_CTL_DEBUG, "Handle: %p. CTL: %lx. Type: %lx\n",
|
|
DeviceHandle, IoControlCode, IsDevIoCtl);
|
|
|
|
/* Get the access type */
|
|
AccessType = IO_METHOD_FROM_CTL_CODE(IoControlCode);
|
|
|
|
/* Check if we came from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the status block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Check if this is buffered I/O */
|
|
if (AccessType == METHOD_BUFFERED)
|
|
{
|
|
/* Check if we have an output buffer */
|
|
if (OutputBuffer)
|
|
{
|
|
/* Probe the output buffer */
|
|
ProbeForWrite(OutputBuffer,
|
|
OutputBufferLength,
|
|
sizeof(CHAR));
|
|
}
|
|
else
|
|
{
|
|
/* Make sure the caller can't fake this as we depend on this */
|
|
OutputBufferLength = 0;
|
|
}
|
|
}
|
|
|
|
/* Check if we we have an input buffer I/O */
|
|
if (AccessType != METHOD_NEITHER)
|
|
{
|
|
/* Check if we have an input buffer */
|
|
if (InputBuffer)
|
|
{
|
|
/* Probe the input buffer */
|
|
ProbeForRead(InputBuffer, InputBufferLength, sizeof(CHAR));
|
|
}
|
|
else
|
|
{
|
|
/* Make sure the caller can't fake this as we depend on this */
|
|
InputBufferLength = 0;
|
|
}
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Don't check for access rights right now, KernelMode can do anything */
|
|
Status = ObReferenceObjectByHandle(DeviceHandle,
|
|
0,
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&FileObject,
|
|
&HandleInformation);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Can't use an I/O completion port and an APC at the same time */
|
|
if ((FileObject->CompletionContext) && (UserApcRoutine))
|
|
{
|
|
/* Fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check if we from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Get the access mask */
|
|
DesiredAccess = (ACCESS_MASK)((IoControlCode >> 14) & 3);
|
|
|
|
/* Check if we can open it */
|
|
if ((DesiredAccess != FILE_ANY_ACCESS) &&
|
|
(HandleInformation.GrantedAccess & DesiredAccess) != DesiredAccess)
|
|
{
|
|
/* Dereference the file object and fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
/* Check for an event */
|
|
if (Event)
|
|
{
|
|
/* Reference it */
|
|
Status = ObReferenceObjectByHandle(Event,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&EventObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Dereference the file object and fail */
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Clear it */
|
|
KeClearEvent(EventObject);
|
|
}
|
|
|
|
/* Check if this is a file that was opened for Synch I/O */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
if (EventObject) ObDereferenceObject(EventObject);
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Remember to unlock later */
|
|
LockedForSynch = TRUE;
|
|
}
|
|
|
|
/* Check if this is a direct open or not */
|
|
if (FileObject->Flags & FO_DIRECT_DEVICE_OPEN)
|
|
{
|
|
/* It's a direct open, get the attached device */
|
|
DeviceObject = IoGetAttachedDevice(FileObject->DeviceObject);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise get the related device */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
}
|
|
|
|
/* If this is a device I/O, try to do it with FastIO path */
|
|
if (IsDevIoCtl)
|
|
{
|
|
PFAST_IO_DISPATCH FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
|
|
/* Check whether FSD is FastIO aware and provide an appropriate routine */
|
|
if (FastIoDispatch != NULL && FastIoDispatch->FastIoDeviceControl != NULL)
|
|
{
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
|
|
/* If we have an output buffer coming from usermode */
|
|
if (PreviousMode != KernelMode && OutputBuffer != NULL)
|
|
{
|
|
/* Probe it according to its usage */
|
|
_SEH2_TRY
|
|
{
|
|
if (AccessType == METHOD_IN_DIRECT)
|
|
{
|
|
ProbeForRead(OutputBuffer, OutputBufferLength, sizeof(CHAR));
|
|
}
|
|
else if (AccessType == METHOD_OUT_DIRECT)
|
|
{
|
|
ProbeForWrite(OutputBuffer, OutputBufferLength, sizeof(CHAR));
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Cleanup after exception and return */
|
|
IopCleanupAfterException(FileObject, NULL, EventObject, NULL);
|
|
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* If we are dismounting a volume, increase the dismount count */
|
|
if (IoControlCode == FSCTL_DISMOUNT_VOLUME)
|
|
{
|
|
InterlockedIncrement((PLONG)&SharedUserData->DismountCount);
|
|
}
|
|
|
|
/* Call the FSD */
|
|
if (FastIoDispatch->FastIoDeviceControl(FileObject,
|
|
TRUE,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
IoControlCode,
|
|
&KernelIosb,
|
|
DeviceObject))
|
|
{
|
|
IO_COMPLETION_CONTEXT CompletionInfo = { NULL, NULL };
|
|
|
|
/* Write the IOSB back */
|
|
_SEH2_TRY
|
|
{
|
|
*IoStatusBlock = KernelIosb;
|
|
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
KernelIosb.Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Backup our complete context in case it exists */
|
|
if (FileObject->CompletionContext)
|
|
{
|
|
CompletionInfo = *(FileObject->CompletionContext);
|
|
}
|
|
|
|
/* If we had an event, signal it */
|
|
if (Event)
|
|
{
|
|
KeSetEvent(EventObject, IO_NO_INCREMENT, FALSE);
|
|
ObDereferenceObject(EventObject);
|
|
}
|
|
|
|
/* If FO was locked, unlock it */
|
|
if (LockedForSynch)
|
|
{
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
|
|
/* Set completion if required */
|
|
if (CompletionInfo.Port != NULL && UserApcContext != NULL)
|
|
{
|
|
if (!NT_SUCCESS(IoSetIoCompletion(CompletionInfo.Port,
|
|
CompletionInfo.Key,
|
|
UserApcContext,
|
|
KernelIosb.Status,
|
|
KernelIosb.Information,
|
|
TRUE)))
|
|
{
|
|
KernelIosb.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
/* We're done with FastIO! */
|
|
ObDereferenceObject(FileObject);
|
|
return KernelIosb.Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clear the event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, EventObject, NULL);
|
|
|
|
/* Setup the IRP */
|
|
Irp->UserIosb = IoStatusBlock;
|
|
Irp->UserEvent = EventObject;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = UserApcRoutine;
|
|
Irp->Overlay.AsynchronousParameters.UserApcContext = UserApcContext;
|
|
Irp->Cancel = FALSE;
|
|
Irp->CancelRoutine = NULL;
|
|
Irp->PendingReturned = FALSE;
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->MdlAddress = NULL;
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
Irp->Flags = 0;
|
|
Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
/* Set stack location settings */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->FileObject = FileObject;
|
|
StackPtr->MajorFunction = IsDevIoCtl ?
|
|
IRP_MJ_DEVICE_CONTROL :
|
|
IRP_MJ_FILE_SYSTEM_CONTROL;
|
|
StackPtr->MinorFunction = 0; /* Minor function 0 is IRP_MN_USER_FS_REQUEST */
|
|
StackPtr->Control = 0;
|
|
StackPtr->Flags = 0;
|
|
StackPtr->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
|
|
/* Set the IOCTL Data */
|
|
StackPtr->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
|
|
StackPtr->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
|
|
StackPtr->Parameters.DeviceIoControl.OutputBufferLength =
|
|
OutputBufferLength;
|
|
|
|
PoolType = IsDevIoCtl ? NonPagedPoolCacheAligned : NonPagedPool;
|
|
|
|
/* Handle the Methods */
|
|
switch (AccessType)
|
|
{
|
|
/* Buffered I/O */
|
|
case METHOD_BUFFERED:
|
|
|
|
/* Enter SEH for allocations */
|
|
_SEH2_TRY
|
|
{
|
|
/* Select the right Buffer Length */
|
|
BufferLength = (InputBufferLength > OutputBufferLength) ?
|
|
InputBufferLength : OutputBufferLength;
|
|
|
|
/* Make sure there is one */
|
|
if (BufferLength)
|
|
{
|
|
/* Allocate the System Buffer */
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuotaTag(PoolType,
|
|
BufferLength,
|
|
TAG_SYS_BUF);
|
|
|
|
/* Check if we got a buffer */
|
|
if (InputBuffer)
|
|
{
|
|
/* Copy into the System Buffer */
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
InputBuffer,
|
|
InputBufferLength);
|
|
}
|
|
|
|
/* Write the flags */
|
|
Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
|
|
if (OutputBuffer) Irp->Flags |= IRP_INPUT_OPERATION;
|
|
|
|
/* Save the Buffer */
|
|
Irp->UserBuffer = OutputBuffer;
|
|
}
|
|
else
|
|
{
|
|
/* Clear the Flags and Buffer */
|
|
Irp->UserBuffer = NULL;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Cleanup after exception and return */
|
|
IopCleanupAfterException(FileObject, Irp, EventObject, NULL);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
break;
|
|
|
|
/* Direct I/O */
|
|
case METHOD_IN_DIRECT:
|
|
case METHOD_OUT_DIRECT:
|
|
|
|
/* Enter SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Check if we got an input buffer */
|
|
if ((InputBufferLength) && (InputBuffer))
|
|
{
|
|
/* Allocate the System Buffer */
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuotaTag(PoolType,
|
|
InputBufferLength,
|
|
TAG_SYS_BUF);
|
|
|
|
/* Copy into the System Buffer */
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
InputBuffer,
|
|
InputBufferLength);
|
|
|
|
/* Write the flags */
|
|
Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
|
|
}
|
|
|
|
/* Check if we got an output buffer */
|
|
if (OutputBufferLength)
|
|
{
|
|
/* Allocate the System Buffer */
|
|
Irp->MdlAddress = IoAllocateMdl(OutputBuffer,
|
|
OutputBufferLength,
|
|
FALSE,
|
|
FALSE,
|
|
Irp);
|
|
if (!Irp->MdlAddress)
|
|
{
|
|
/* Raise exception we'll catch */
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
/* Do the probe */
|
|
MmProbeAndLockPages(Irp->MdlAddress,
|
|
PreviousMode,
|
|
(AccessType == METHOD_IN_DIRECT) ?
|
|
IoReadAccess : IoWriteAccess);
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Cleanup after exception and return */
|
|
IopCleanupAfterException(FileObject, Irp, EventObject, NULL);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
break;
|
|
|
|
case METHOD_NEITHER:
|
|
|
|
/* Just save the Buffer */
|
|
Irp->UserBuffer = OutputBuffer;
|
|
StackPtr->Parameters.DeviceIoControl.Type3InputBuffer = InputBuffer;
|
|
}
|
|
|
|
/* Use deferred completion for FS I/O */
|
|
if (!IsDevIoCtl)
|
|
{
|
|
Irp->Flags |= IRP_DEFER_IO_COMPLETION;
|
|
}
|
|
|
|
/* If we are dismounting a volume, increaase the dismount count */
|
|
if (IoControlCode == FSCTL_DISMOUNT_VOLUME)
|
|
{
|
|
InterlockedIncrement((PLONG)&SharedUserData->DismountCount);
|
|
}
|
|
|
|
/* Perform the call */
|
|
return IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
!IsDevIoCtl,
|
|
PreviousMode,
|
|
LockedForSynch,
|
|
IopOtherTransfer);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopQueryDeviceInformation(IN PFILE_OBJECT FileObject,
|
|
IN ULONG InformationClass,
|
|
IN ULONG Length,
|
|
OUT PVOID Information,
|
|
OUT PULONG ReturnedLength,
|
|
IN BOOLEAN File)
|
|
{
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
BOOLEAN LocalEvent = FALSE;
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "Handle: %p. CTL: %lx. Type: %lx\n",
|
|
FileObject, InformationClass, File);
|
|
|
|
/* Reference the object */
|
|
ObReferenceObject(FileObject);
|
|
|
|
/* Check if this is a file that was opened for Synch I/O */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
(void)IopLockFileObject(FileObject, KernelMode);
|
|
|
|
/* Use File Object event */
|
|
KeClearEvent(&FileObject->Event);
|
|
}
|
|
else
|
|
{
|
|
/* Use local event */
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
LocalEvent = TRUE;
|
|
}
|
|
|
|
/* Get the Device Object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, NULL, NULL);
|
|
|
|
/* Set the IRP */
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
Irp->UserIosb = &IoStatusBlock;
|
|
Irp->UserEvent = (LocalEvent) ? &Event : NULL;
|
|
Irp->Flags = (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;
|
|
Irp->Flags |= IRP_BUFFERED_IO;
|
|
Irp->AssociatedIrp.SystemBuffer = Information;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
/* Set the Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = File ? IRP_MJ_QUERY_INFORMATION:
|
|
IRP_MJ_QUERY_VOLUME_INFORMATION;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Check which type this is */
|
|
if (File)
|
|
{
|
|
/* Set Parameters */
|
|
StackPtr->Parameters.QueryFile.FileInformationClass = InformationClass;
|
|
StackPtr->Parameters.QueryFile.Length = Length;
|
|
}
|
|
else
|
|
{
|
|
/* Set Parameters */
|
|
StackPtr->Parameters.QueryVolume.FsInformationClass = InformationClass;
|
|
StackPtr->Parameters.QueryVolume.Length = Length;
|
|
}
|
|
|
|
/* Queue the IRP */
|
|
IopQueueIrpToThread(Irp);
|
|
|
|
/* Call the Driver */
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
|
|
/* Check if this was synch I/O */
|
|
if (!LocalEvent)
|
|
{
|
|
/* Check if the request is pending */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
/* Wait on the file object */
|
|
Status = KeWaitForSingleObject(&FileObject->Event,
|
|
Executive,
|
|
KernelMode,
|
|
(FileObject->Flags &
|
|
FO_ALERTABLE_IO) != 0,
|
|
NULL);
|
|
if (Status == STATUS_ALERTED)
|
|
{
|
|
/* Abort the operation */
|
|
IopAbortInterruptedIrp(&FileObject->Event, Irp);
|
|
}
|
|
|
|
/* Get the final status */
|
|
Status = FileObject->FinalStatus;
|
|
}
|
|
|
|
/* Release the file lock */
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
else if (Status == STATUS_PENDING)
|
|
{
|
|
/* Wait on the local event and get the final status */
|
|
KeWaitForSingleObject(&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
|
|
/* Return the Length and Status. ReturnedLength is NOT optional */
|
|
*ReturnedLength = (ULONG)IoStatusBlock.Information;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopGetFileInformation(IN PFILE_OBJECT FileObject,
|
|
IN ULONG Length,
|
|
IN FILE_INFORMATION_CLASS FileInfoClass,
|
|
OUT PVOID Buffer,
|
|
OUT PULONG ReturnedLength)
|
|
{
|
|
PIRP Irp;
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION Stack;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Allocate an IRP */
|
|
ObReferenceObject(FileObject);
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (Irp == NULL)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Init event */
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
|
|
/* Setup the IRP */
|
|
Irp->UserIosb = &IoStatusBlock;
|
|
Irp->UserEvent = &Event;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->AssociatedIrp.SystemBuffer = Buffer;
|
|
Irp->Flags = IRP_SYNCHRONOUS_API | IRP_BUFFERED_IO | IRP_OB_QUERY_NAME;
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
Stack = IoGetNextIrpStackLocation(Irp);
|
|
Stack->MajorFunction = IRP_MJ_QUERY_INFORMATION;
|
|
Stack->FileObject = FileObject;
|
|
Stack->Parameters.QueryFile.FileInformationClass = FileInfoClass;
|
|
Stack->Parameters.QueryFile.Length = Length;
|
|
|
|
|
|
/* Queue the IRP */
|
|
IopQueueIrpToThread(Irp);
|
|
|
|
/* Call the driver */
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
|
|
*ReturnedLength = IoStatusBlock.Information;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopGetBasicInformationFile(IN PFILE_OBJECT FileObject,
|
|
OUT PFILE_BASIC_INFORMATION BasicInfo)
|
|
{
|
|
ULONG ReturnedLength;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Try to do it the fast way if possible */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
if (DeviceObject->DriverObject->FastIoDispatch != NULL &&
|
|
DeviceObject->DriverObject->FastIoDispatch->FastIoQueryBasicInfo != NULL &&
|
|
DeviceObject->DriverObject->FastIoDispatch->FastIoQueryBasicInfo(FileObject,
|
|
((FileObject->Flags & FO_SYNCHRONOUS_IO) != 0),
|
|
BasicInfo,
|
|
&IoStatusBlock,
|
|
DeviceObject))
|
|
{
|
|
return IoStatusBlock.Status;
|
|
}
|
|
|
|
/* In case it failed, fall back to IRP-based method */
|
|
return IopGetFileInformation(FileObject, sizeof(FILE_BASIC_INFORMATION), FileBasicInformation, BasicInfo, &ReturnedLength);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
IopOpenLinkOrRenameTarget(OUT PHANDLE Handle,
|
|
IN PIRP Irp,
|
|
IN PFILE_RENAME_INFORMATION RenameInfo,
|
|
IN PFILE_OBJECT FileObject)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE TargetHandle;
|
|
UNICODE_STRING FileName;
|
|
PIO_STACK_LOCATION Stack;
|
|
PFILE_OBJECT TargetFileObject;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_BASIC_INFORMATION BasicInfo;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|
ACCESS_MASK DesiredAccess = FILE_WRITE_DATA;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* First, establish whether our target is a directory */
|
|
if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN))
|
|
{
|
|
Status = IopGetBasicInformationFile(FileObject, &BasicInfo);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
if (BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
DesiredAccess = FILE_ADD_SUBDIRECTORY;
|
|
}
|
|
}
|
|
|
|
/* Setup the string to the target */
|
|
FileName.Buffer = RenameInfo->FileName;
|
|
FileName.Length = RenameInfo->FileNameLength;
|
|
FileName.MaximumLength = RenameInfo->FileNameLength;
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&FileName,
|
|
(FileObject->Flags & FO_OPENED_CASE_SENSITIVE ? 0 : OBJ_CASE_INSENSITIVE) | OBJ_KERNEL_HANDLE,
|
|
RenameInfo->RootDirectory,
|
|
NULL);
|
|
|
|
/* And open its parent directory
|
|
* Use hint if specified
|
|
*/
|
|
if (FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION)
|
|
{
|
|
PFILE_OBJECT_EXTENSION FileObjectExtension;
|
|
|
|
ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));
|
|
|
|
FileObjectExtension = FileObject->FileObjectExtension;
|
|
Status = IoCreateFileSpecifyDeviceObjectHint(&TargetHandle,
|
|
DesiredAccess | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_OPEN_FOR_BACKUP_INTENT,
|
|
NULL,
|
|
0,
|
|
CreateFileTypeNone,
|
|
NULL,
|
|
IO_FORCE_ACCESS_CHECK | IO_OPEN_TARGET_DIRECTORY | IO_NO_PARAMETER_CHECKING,
|
|
FileObjectExtension->TopDeviceObjectHint);
|
|
}
|
|
else
|
|
{
|
|
Status = IoCreateFile(&TargetHandle,
|
|
DesiredAccess | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_OPEN_FOR_BACKUP_INTENT,
|
|
NULL,
|
|
0,
|
|
CreateFileTypeNone,
|
|
NULL,
|
|
IO_FORCE_ACCESS_CHECK | IO_OPEN_TARGET_DIRECTORY | IO_NO_PARAMETER_CHECKING);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Once open, continue only if:
|
|
* Target exists and we're allowed to overwrite it
|
|
*/
|
|
Stack = IoGetNextIrpStackLocation(Irp);
|
|
if (Stack->Parameters.SetFile.FileInformationClass == FileLinkInformation &&
|
|
!RenameInfo->ReplaceIfExists &&
|
|
IoStatusBlock.Information == FILE_EXISTS)
|
|
{
|
|
ObCloseHandle(TargetHandle, KernelMode);
|
|
return STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
|
|
/* Now, we'll get the associated device of the target, to check for same device location
|
|
* So, get the FO first
|
|
*/
|
|
Status = ObReferenceObjectByHandle(TargetHandle,
|
|
FILE_WRITE_DATA,
|
|
IoFileObjectType,
|
|
KernelMode,
|
|
(PVOID *)&TargetFileObject,
|
|
&HandleInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ObCloseHandle(TargetHandle, KernelMode);
|
|
return Status;
|
|
}
|
|
|
|
/* We can dereference, we have the handle */
|
|
ObDereferenceObject(TargetFileObject);
|
|
/* If we're not on the same device, error out **/
|
|
if (IoGetRelatedDeviceObject(TargetFileObject) != IoGetRelatedDeviceObject(FileObject))
|
|
{
|
|
ObCloseHandle(TargetHandle, KernelMode);
|
|
return STATUS_NOT_SAME_DEVICE;
|
|
}
|
|
|
|
/* Return parent directory file object and handle */
|
|
Stack->Parameters.SetFile.FileObject = TargetFileObject;
|
|
*Handle = TargetHandle;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
ULONG
|
|
IopGetFileMode(IN PFILE_OBJECT FileObject)
|
|
{
|
|
ULONG Mode = 0;
|
|
|
|
if (FileObject->Flags & FO_WRITE_THROUGH)
|
|
Mode |= FILE_WRITE_THROUGH;
|
|
|
|
if (FileObject->Flags & FO_SEQUENTIAL_ONLY)
|
|
Mode |= FILE_SEQUENTIAL_ONLY;
|
|
|
|
if (FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)
|
|
Mode |= FILE_NO_INTERMEDIATE_BUFFERING;
|
|
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
if (FileObject->Flags & FO_ALERTABLE_IO)
|
|
Mode |= FILE_SYNCHRONOUS_IO_ALERT;
|
|
else
|
|
Mode |= FILE_SYNCHRONOUS_IO_NONALERT;
|
|
}
|
|
|
|
if (FileObject->Flags & FO_DELETE_ON_CLOSE)
|
|
Mode |= FILE_DELETE_ON_CLOSE;
|
|
|
|
return Mode;
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
IopGetMountFlag(IN PDEVICE_OBJECT DeviceObject)
|
|
{
|
|
KIRQL OldIrql;
|
|
PVPB Vpb;
|
|
BOOLEAN Mounted;
|
|
|
|
/* Assume not mounted */
|
|
Mounted = FALSE;
|
|
|
|
/* Check whether we have the mount flag */
|
|
IoAcquireVpbSpinLock(&OldIrql);
|
|
|
|
Vpb = DeviceObject->Vpb;
|
|
if (Vpb != NULL &&
|
|
BooleanFlagOn(Vpb->Flags, VPB_MOUNTED))
|
|
{
|
|
Mounted = TRUE;
|
|
}
|
|
|
|
IoReleaseVpbSpinLock(OldIrql);
|
|
|
|
return Mounted;
|
|
}
|
|
|
|
static
|
|
BOOLEAN
|
|
IopVerifyDriverObjectOnStack(IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDRIVER_OBJECT DriverObject)
|
|
{
|
|
PDEVICE_OBJECT StackDO;
|
|
|
|
/* Browse our whole device stack, trying to find the appropriate driver */
|
|
StackDO = IopGetDeviceAttachmentBase(DeviceObject);
|
|
while (StackDO != NULL)
|
|
{
|
|
/* We've found the driver, return success */
|
|
if (StackDO->DriverObject == DriverObject)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* Move to the next */
|
|
StackDO = StackDO->AttachedDevice;
|
|
}
|
|
|
|
/* We only reach there if driver was not found */
|
|
return FALSE;
|
|
}
|
|
|
|
static
|
|
NTSTATUS
|
|
IopGetDriverPathInformation(IN PFILE_OBJECT FileObject,
|
|
IN PFILE_FS_DRIVER_PATH_INFORMATION DriverPathInfo,
|
|
IN ULONG Length)
|
|
{
|
|
KIRQL OldIrql;
|
|
NTSTATUS Status;
|
|
UNICODE_STRING DriverName;
|
|
PDRIVER_OBJECT DriverObject;
|
|
|
|
/* Make sure the structure is consistent (ie, driver name fits into the buffer) */
|
|
if (Length - FIELD_OFFSET(FILE_FS_DRIVER_PATH_INFORMATION, DriverName) < DriverPathInfo->DriverNameLength)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Setup the whole driver name */
|
|
DriverName.Length = DriverPathInfo->DriverNameLength;
|
|
DriverName.MaximumLength = DriverPathInfo->DriverNameLength;
|
|
DriverName.Buffer = &DriverPathInfo->DriverName[0];
|
|
|
|
/* Ask Ob for such driver */
|
|
Status = ObReferenceObjectByName(&DriverName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
0,
|
|
IoDriverObjectType,
|
|
KernelMode,
|
|
NULL,
|
|
(PVOID*)&DriverObject);
|
|
/* No such driver, bail out */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Lock the devices database, we'll browse it */
|
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueIoDatabaseLock);
|
|
/* If we have a VPB, browse the stack from the volume */
|
|
if (FileObject->Vpb != NULL && FileObject->Vpb->DeviceObject != NULL)
|
|
{
|
|
DriverPathInfo->DriverInPath = IopVerifyDriverObjectOnStack(FileObject->Vpb->DeviceObject, DriverObject);
|
|
}
|
|
/* Otherwise, do it from the normal device */
|
|
else
|
|
{
|
|
DriverPathInfo->DriverInPath = IopVerifyDriverObjectOnStack(FileObject->DeviceObject, DriverObject);
|
|
}
|
|
KeReleaseQueuedSpinLock(LockQueueIoDatabaseLock, OldIrql);
|
|
|
|
/* No longer needed */
|
|
ObDereferenceObject(DriverObject);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* PUBLIC FUNCTIONS **********************************************************/
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoSynchronousPageWrite(IN PFILE_OBJECT FileObject,
|
|
IN PMDL Mdl,
|
|
IN PLARGE_INTEGER Offset,
|
|
IN PKEVENT Event,
|
|
IN PIO_STATUS_BLOCK StatusBlock)
|
|
{
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
IOTRACE(IO_API_DEBUG, "FileObject: %p. Mdl: %p. Offset: %p\n",
|
|
FileObject, Mdl, Offset);
|
|
|
|
/* Is the write originating from Cc? */
|
|
if (FileObject->SectionObjectPointer != NULL &&
|
|
FileObject->SectionObjectPointer->SharedCacheMap != NULL)
|
|
{
|
|
++CcDataFlushes;
|
|
CcDataPages += BYTES_TO_PAGES(MmGetMdlByteCount(Mdl));
|
|
}
|
|
|
|
/* Get the Device Object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Allocate IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the Stack */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
|
|
/* Create the IRP Settings */
|
|
Irp->MdlAddress = Mdl;
|
|
Irp->UserBuffer = MmGetMdlVirtualAddress(Mdl);
|
|
Irp->UserIosb = StatusBlock;
|
|
Irp->UserEvent = Event;
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->Flags = IRP_PAGING_IO | IRP_NOCACHE | IRP_SYNCHRONOUS_PAGING_IO;
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
/* Set the Stack Settings */
|
|
StackPtr->Parameters.Write.Length = MmGetMdlByteCount(Mdl);
|
|
StackPtr->Parameters.Write.ByteOffset = *Offset;
|
|
StackPtr->MajorFunction = IRP_MJ_WRITE;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Call the Driver */
|
|
return IoCallDriver(DeviceObject, Irp);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoPageRead(IN PFILE_OBJECT FileObject,
|
|
IN PMDL Mdl,
|
|
IN PLARGE_INTEGER Offset,
|
|
IN PKEVENT Event,
|
|
IN PIO_STATUS_BLOCK StatusBlock)
|
|
{
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
IOTRACE(IO_API_DEBUG, "FileObject: %p. Mdl: %p. Offset: %p\n",
|
|
FileObject, Mdl, Offset);
|
|
|
|
/* Get the Device Object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Allocate IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
/* If allocation failed, try to see whether we can use
|
|
* the reserve IRP
|
|
*/
|
|
if (Irp == NULL)
|
|
{
|
|
/* We will use it only for paging file */
|
|
if (MmIsFileObjectAPagingFile(FileObject))
|
|
{
|
|
InterlockedExchangeAdd(&IoPageReadIrpAllocationFailure, 1);
|
|
Irp = IopAllocateReserveIrp(DeviceObject->StackSize);
|
|
}
|
|
else
|
|
{
|
|
InterlockedExchangeAdd(&IoPageReadNonPagefileIrpAllocationFailure, 1);
|
|
}
|
|
|
|
/* If allocation failed (not a paging file or too big stack size)
|
|
* Fail for real
|
|
*/
|
|
if (Irp == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
/* Get the Stack */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
|
|
/* Create the IRP Settings */
|
|
Irp->MdlAddress = Mdl;
|
|
Irp->UserBuffer = MmGetMdlVirtualAddress(Mdl);
|
|
Irp->UserIosb = StatusBlock;
|
|
Irp->UserEvent = Event;
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->Flags = IRP_PAGING_IO |
|
|
IRP_NOCACHE |
|
|
IRP_SYNCHRONOUS_PAGING_IO |
|
|
IRP_INPUT_OPERATION;
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
/* Set the Stack Settings */
|
|
StackPtr->Parameters.Read.Length = MmGetMdlByteCount(Mdl);
|
|
StackPtr->Parameters.Read.ByteOffset = *Offset;
|
|
StackPtr->MajorFunction = IRP_MJ_READ;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Call the Driver */
|
|
return IoCallDriver(DeviceObject, Irp);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoQueryFileInformation(IN PFILE_OBJECT FileObject,
|
|
IN FILE_INFORMATION_CLASS FileInformationClass,
|
|
IN ULONG Length,
|
|
OUT PVOID FileInformation,
|
|
OUT PULONG ReturnedLength)
|
|
{
|
|
/* Call the shared routine */
|
|
return IopQueryDeviceInformation(FileObject,
|
|
FileInformationClass,
|
|
Length,
|
|
FileInformation,
|
|
ReturnedLength,
|
|
TRUE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoQueryVolumeInformation(IN PFILE_OBJECT FileObject,
|
|
IN FS_INFORMATION_CLASS FsInformationClass,
|
|
IN ULONG Length,
|
|
OUT PVOID FsInformation,
|
|
OUT PULONG ReturnedLength)
|
|
{
|
|
/* Call the shared routine */
|
|
return IopQueryDeviceInformation(FileObject,
|
|
FsInformationClass,
|
|
Length,
|
|
FsInformation,
|
|
ReturnedLength,
|
|
FALSE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
IoSetInformation(IN PFILE_OBJECT FileObject,
|
|
IN FILE_INFORMATION_CLASS FileInformationClass,
|
|
IN ULONG Length,
|
|
IN PVOID FileInformation)
|
|
{
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
BOOLEAN LocalEvent = FALSE;
|
|
KEVENT Event;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "FileObject: %p. Class: %lx. Length: %lx\n",
|
|
FileObject, FileInformationClass, Length);
|
|
|
|
/* Reference the object */
|
|
ObReferenceObject(FileObject);
|
|
|
|
/* Check if this is a file that was opened for Synch I/O */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
(void)IopLockFileObject(FileObject, KernelMode);
|
|
|
|
/* Use File Object event */
|
|
KeClearEvent(&FileObject->Event);
|
|
}
|
|
else
|
|
{
|
|
/* Use local event */
|
|
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
|
LocalEvent = TRUE;
|
|
}
|
|
|
|
/* Get the Device Object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, NULL, NULL);
|
|
|
|
/* Set the IRP */
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->RequestorMode = KernelMode;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
Irp->UserIosb = &IoStatusBlock;
|
|
Irp->UserEvent = (LocalEvent) ? &Event : NULL;
|
|
Irp->Flags = (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;
|
|
Irp->Flags |= IRP_BUFFERED_IO;
|
|
Irp->AssociatedIrp.SystemBuffer = FileInformation;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
|
|
/* Set the Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_SET_INFORMATION;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Set Parameters */
|
|
StackPtr->Parameters.SetFile.FileInformationClass = FileInformationClass;
|
|
StackPtr->Parameters.SetFile.Length = Length;
|
|
|
|
/* Queue the IRP */
|
|
IopQueueIrpToThread(Irp);
|
|
|
|
/* Call the Driver */
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
|
|
/* Check if this was synch I/O */
|
|
if (!LocalEvent)
|
|
{
|
|
/* Check if the request is pending */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
/* Wait on the file object */
|
|
Status = KeWaitForSingleObject(&FileObject->Event,
|
|
Executive,
|
|
KernelMode,
|
|
(FileObject->Flags &
|
|
FO_ALERTABLE_IO) != 0,
|
|
NULL);
|
|
if (Status == STATUS_ALERTED)
|
|
{
|
|
/* Abort the operation */
|
|
IopAbortInterruptedIrp(&FileObject->Event, Irp);
|
|
}
|
|
|
|
/* Get the final status */
|
|
Status = FileObject->FinalStatus;
|
|
}
|
|
|
|
/* Release the file lock */
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
else if (Status == STATUS_PENDING)
|
|
{
|
|
/* Wait on the local event and get the final status */
|
|
KeWaitForSingleObject(&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
|
|
/* Return the status */
|
|
return Status;
|
|
}
|
|
|
|
/* NATIVE SERVICES ***********************************************************/
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtDeviceIoControlFile(IN HANDLE DeviceHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE UserApcRoutine OPTIONAL,
|
|
IN PVOID UserApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG IoControlCode,
|
|
IN PVOID InputBuffer,
|
|
IN ULONG InputBufferLength OPTIONAL,
|
|
OUT PVOID OutputBuffer,
|
|
IN ULONG OutputBufferLength OPTIONAL)
|
|
{
|
|
/* Call the Generic Function */
|
|
return IopDeviceFsIoControl(DeviceHandle,
|
|
Event,
|
|
UserApcRoutine,
|
|
UserApcContext,
|
|
IoStatusBlock,
|
|
IoControlCode,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
TRUE);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtFsControlFile(IN HANDLE DeviceHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE UserApcRoutine OPTIONAL,
|
|
IN PVOID UserApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN ULONG IoControlCode,
|
|
IN PVOID InputBuffer,
|
|
IN ULONG InputBufferLength OPTIONAL,
|
|
OUT PVOID OutputBuffer,
|
|
IN ULONG OutputBufferLength OPTIONAL)
|
|
{
|
|
/* Call the Generic Function */
|
|
return IopDeviceFsIoControl(DeviceHandle,
|
|
Event,
|
|
UserApcRoutine,
|
|
UserApcContext,
|
|
IoStatusBlock,
|
|
IoControlCode,
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
FALSE);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtFlushBuffersFile(IN HANDLE FileHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock)
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
NTSTATUS Status;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PKEVENT Event = NULL;
|
|
BOOLEAN LocalEvent = FALSE;
|
|
OBJECT_HANDLE_INFORMATION ObjectHandleInfo;
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Protect probes */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the I/O Status block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Get the File Object */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
0,
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&FileObject,
|
|
&ObjectHandleInfo);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/*
|
|
* Check if the handle has either FILE_WRITE_DATA or FILE_APPEND_DATA was
|
|
* granted. However, if this is a named pipe, make sure we don't ask for
|
|
* FILE_APPEND_DATA as it interferes with the FILE_CREATE_PIPE_INSTANCE
|
|
* access right!
|
|
*/
|
|
if (!(ObjectHandleInfo.GrantedAccess &
|
|
((!(FileObject->Flags & FO_NAMED_PIPE) ? FILE_APPEND_DATA : 0) |
|
|
FILE_WRITE_DATA)))
|
|
{
|
|
/* We failed */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* Check if we should use Sync IO or not */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use local event */
|
|
Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_IO);
|
|
if (!Event)
|
|
{
|
|
/* We failed */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
KeInitializeEvent(Event, SynchronizationEvent, FALSE);
|
|
LocalEvent = TRUE;
|
|
}
|
|
|
|
/* Get the Device Object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Clear the event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, NULL, Event);
|
|
|
|
/* Set up the IRP */
|
|
Irp->Flags = (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;
|
|
Irp->UserIosb = (LocalEvent) ? &KernelIosb : IoStatusBlock;
|
|
Irp->UserEvent = (LocalEvent) ? Event : NULL;
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
|
|
/* Set up Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_FLUSH_BUFFERS;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Call the Driver */
|
|
Status = IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
FALSE,
|
|
PreviousMode,
|
|
!LocalEvent,
|
|
IopOtherTransfer);
|
|
|
|
/* Check if this was async I/O */
|
|
if (LocalEvent)
|
|
{
|
|
/* It was, finalize this request */
|
|
Status = IopFinalizeAsynchronousIo(Status,
|
|
Event,
|
|
Irp,
|
|
PreviousMode,
|
|
&KernelIosb,
|
|
IoStatusBlock);
|
|
}
|
|
|
|
/* Return the Status */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtNotifyChangeDirectoryFile(IN HANDLE FileHandle,
|
|
IN HANDLE EventHandle OPTIONAL,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID Buffer,
|
|
IN ULONG BufferSize,
|
|
IN ULONG CompletionFilter,
|
|
IN BOOLEAN WatchTree)
|
|
{
|
|
PIRP Irp;
|
|
PKEVENT Event = NULL;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PFILE_OBJECT FileObject;
|
|
PIO_STACK_LOCATION IoStack;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
BOOLEAN LockedForSync = FALSE;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Check if we're called from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Enter SEH for probing */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the I/O STatus block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe the buffer */
|
|
if (BufferSize) ProbeForWrite(Buffer, BufferSize, sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Check if CompletionFilter is valid */
|
|
if (!CompletionFilter || (CompletionFilter & ~FILE_NOTIFY_VALID_MASK))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
/* Get File Object */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
FILE_LIST_DIRECTORY,
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&FileObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Can't use an I/O completion port and an APC at the same time */
|
|
if ((FileObject->CompletionContext) && (ApcRoutine))
|
|
{
|
|
/* Fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check if we have an event handle */
|
|
if (EventHandle)
|
|
{
|
|
/* Reference it */
|
|
Status = ObReferenceObjectByHandle(EventHandle,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
PreviousMode,
|
|
(PVOID *)&Event,
|
|
NULL);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
KeClearEvent(Event);
|
|
}
|
|
|
|
/* Check if we should use Sync IO or not */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
if (Event) ObDereferenceObject(Event);
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
LockedForSync = TRUE;
|
|
}
|
|
|
|
/* Clear File Object event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Get the device object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, Event, NULL);
|
|
|
|
/* Set up the IRP */
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->UserIosb = IoStatusBlock;
|
|
Irp->UserEvent = Event;
|
|
Irp->UserBuffer = Buffer;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
|
Irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
|
|
|
/* Set up Stack Data */
|
|
IoStack = IoGetNextIrpStackLocation(Irp);
|
|
IoStack->MajorFunction = IRP_MJ_DIRECTORY_CONTROL;
|
|
IoStack->MinorFunction = IRP_MN_NOTIFY_CHANGE_DIRECTORY;
|
|
IoStack->FileObject = FileObject;
|
|
|
|
/* Set parameters */
|
|
IoStack->Parameters.NotifyDirectory.CompletionFilter = CompletionFilter;
|
|
IoStack->Parameters.NotifyDirectory.Length = BufferSize;
|
|
if (WatchTree) IoStack->Flags = SL_WATCH_TREE;
|
|
|
|
/* Perform the call */
|
|
return IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
FALSE,
|
|
PreviousMode,
|
|
LockedForSync,
|
|
IopOtherTransfer);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtLockFile(IN HANDLE FileHandle,
|
|
IN HANDLE EventHandle OPTIONAL,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PLARGE_INTEGER ByteOffset,
|
|
IN PLARGE_INTEGER Length,
|
|
IN ULONG Key,
|
|
IN BOOLEAN FailImmediately,
|
|
IN BOOLEAN ExclusiveLock)
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PLARGE_INTEGER LocalLength = NULL;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PKEVENT Event = NULL;
|
|
BOOLEAN LockedForSync = FALSE;
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
LARGE_INTEGER CapturedByteOffset, CapturedLength;
|
|
NTSTATUS Status;
|
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PAGED_CODE();
|
|
CapturedByteOffset.QuadPart = 0;
|
|
CapturedLength.QuadPart = 0;
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Get File Object */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
0,
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&FileObject,
|
|
&HandleInformation);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Check if we're called from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Can't use an I/O completion port and an APC at the same time */
|
|
if ((FileObject->CompletionContext) && (ApcRoutine))
|
|
{
|
|
/* Fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Must have either FILE_READ_DATA or FILE_WRITE_DATA access */
|
|
if (!(HandleInformation.GrantedAccess &
|
|
(FILE_WRITE_DATA | FILE_READ_DATA)))
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* Enter SEH for probing */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the I/O STatus block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe and capture the large integers */
|
|
CapturedByteOffset = ProbeForReadLargeInteger(ByteOffset);
|
|
CapturedLength = ProbeForReadLargeInteger(Length);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Dereference the object and return exception code */
|
|
ObDereferenceObject(FileObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, capture them directly */
|
|
CapturedByteOffset = *ByteOffset;
|
|
CapturedLength = *Length;
|
|
}
|
|
|
|
/* Check if we have an event handle */
|
|
if (EventHandle)
|
|
{
|
|
/* Reference it */
|
|
Status = ObReferenceObjectByHandle(EventHandle,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
PreviousMode,
|
|
(PVOID *)&Event,
|
|
NULL);
|
|
if (Status != STATUS_SUCCESS) return Status;
|
|
KeClearEvent(Event);
|
|
}
|
|
|
|
/* Get the device object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Try to do it the FastIO way if possible */
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
if (FastIoDispatch != NULL && FastIoDispatch->FastIoLock != NULL)
|
|
{
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
|
|
if (FastIoDispatch->FastIoLock(FileObject,
|
|
&CapturedByteOffset,
|
|
&CapturedLength,
|
|
PsGetCurrentProcess(),
|
|
Key,
|
|
FailImmediately,
|
|
ExclusiveLock,
|
|
&KernelIosb,
|
|
DeviceObject))
|
|
{
|
|
/* Write the IOSB back */
|
|
_SEH2_TRY
|
|
{
|
|
*IoStatusBlock = KernelIosb;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
KernelIosb.Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* If we had an event, signal it */
|
|
if (EventHandle)
|
|
{
|
|
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
|
|
ObDereferenceObject(Event);
|
|
}
|
|
|
|
/* Set completion if required */
|
|
if (FileObject->CompletionContext != NULL && ApcContext != NULL)
|
|
{
|
|
if (!NT_SUCCESS(IoSetIoCompletion(FileObject->CompletionContext->Port,
|
|
FileObject->CompletionContext->Key,
|
|
ApcContext,
|
|
KernelIosb.Status,
|
|
KernelIosb.Information,
|
|
TRUE)))
|
|
{
|
|
KernelIosb.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
FileObject->LockOperation = TRUE;
|
|
|
|
/* We're done with FastIO! */
|
|
ObDereferenceObject(FileObject);
|
|
return KernelIosb.Status;
|
|
}
|
|
}
|
|
|
|
/* Check if we should use Sync IO or not */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
if (Event) ObDereferenceObject(Event);
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
LockedForSync = TRUE;
|
|
}
|
|
|
|
/* Clear File Object event */
|
|
KeClearEvent(&FileObject->Event);
|
|
FileObject->LockOperation = TRUE;
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, Event, NULL);
|
|
|
|
/* Set up the IRP */
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->UserIosb = IoStatusBlock;
|
|
Irp->UserEvent = Event;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
|
Irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
|
|
|
/* Set up Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_LOCK_CONTROL;
|
|
StackPtr->MinorFunction = IRP_MN_LOCK;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Allocate local buffer */
|
|
LocalLength = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(LARGE_INTEGER),
|
|
TAG_LOCK);
|
|
if (!LocalLength)
|
|
{
|
|
/* Allocating failed, clean up and return failure */
|
|
IopCleanupAfterException(FileObject, Irp, Event, NULL);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
/* Set the length */
|
|
*LocalLength = CapturedLength;
|
|
Irp->Tail.Overlay.AuxiliaryBuffer = (PVOID)LocalLength;
|
|
StackPtr->Parameters.LockControl.Length = LocalLength;
|
|
|
|
/* Set Parameters */
|
|
StackPtr->Parameters.LockControl.ByteOffset = CapturedByteOffset;
|
|
StackPtr->Parameters.LockControl.Key = Key;
|
|
|
|
/* Set Flags */
|
|
if (FailImmediately) StackPtr->Flags = SL_FAIL_IMMEDIATELY;
|
|
if (ExclusiveLock) StackPtr->Flags |= SL_EXCLUSIVE_LOCK;
|
|
|
|
/* Perform the call */
|
|
return IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
FALSE,
|
|
PreviousMode,
|
|
LockedForSync,
|
|
IopOtherTransfer);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryDirectoryFile(IN HANDLE FileHandle,
|
|
IN HANDLE EventHandle OPTIONAL,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID FileInformation,
|
|
IN ULONG Length,
|
|
IN FILE_INFORMATION_CLASS FileInformationClass,
|
|
IN BOOLEAN ReturnSingleEntry,
|
|
IN PUNICODE_STRING FileName OPTIONAL,
|
|
IN BOOLEAN RestartScan)
|
|
{
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PFILE_OBJECT FileObject;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
NTSTATUS Status;
|
|
BOOLEAN LockedForSynch = FALSE;
|
|
PKEVENT Event = NULL;
|
|
volatile PVOID AuxBuffer = NULL;
|
|
PMDL Mdl;
|
|
UNICODE_STRING CapturedFileName;
|
|
PUNICODE_STRING SearchPattern;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Check if we came from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Enter SEH for probing */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the I/O Status Block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe the file information */
|
|
ProbeForWrite(FileInformation, Length, sizeof(ULONG));
|
|
|
|
/* Check if we have a file name */
|
|
if (FileName)
|
|
{
|
|
/* Capture it */
|
|
CapturedFileName = ProbeForReadUnicodeString(FileName);
|
|
if (CapturedFileName.Length)
|
|
{
|
|
/* Probe its buffer */
|
|
ProbeForRead(CapturedFileName.Buffer,
|
|
CapturedFileName.Length,
|
|
1);
|
|
}
|
|
|
|
/* Allocate the auxiliary buffer */
|
|
AuxBuffer = ExAllocatePoolWithTag(NonPagedPool,
|
|
CapturedFileName.Length +
|
|
sizeof(UNICODE_STRING),
|
|
TAG_SYSB);
|
|
RtlCopyMemory((PVOID)((ULONG_PTR)AuxBuffer +
|
|
sizeof(UNICODE_STRING)),
|
|
CapturedFileName.Buffer,
|
|
CapturedFileName.Length);
|
|
|
|
/* Setup the search pattern */
|
|
SearchPattern = (PUNICODE_STRING)AuxBuffer;
|
|
SearchPattern->Buffer = (PWCHAR)((ULONG_PTR)AuxBuffer +
|
|
sizeof(UNICODE_STRING));
|
|
SearchPattern->Length = CapturedFileName.Length;
|
|
SearchPattern->MaximumLength = CapturedFileName.Length;
|
|
}
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Free buffer and return the exception code */
|
|
if (AuxBuffer) ExFreePoolWithTag(AuxBuffer, TAG_SYSB);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Check input parameters */
|
|
|
|
switch (FileInformationClass)
|
|
{
|
|
#define CHECK_LENGTH(class, struct) \
|
|
case class: \
|
|
if (Length < sizeof(struct)) \
|
|
return STATUS_INFO_LENGTH_MISMATCH; \
|
|
break
|
|
CHECK_LENGTH(FileDirectoryInformation, FILE_DIRECTORY_INFORMATION);
|
|
CHECK_LENGTH(FileFullDirectoryInformation, FILE_FULL_DIR_INFORMATION);
|
|
CHECK_LENGTH(FileIdFullDirectoryInformation, FILE_ID_FULL_DIR_INFORMATION);
|
|
CHECK_LENGTH(FileNamesInformation, FILE_NAMES_INFORMATION);
|
|
CHECK_LENGTH(FileBothDirectoryInformation, FILE_BOTH_DIR_INFORMATION);
|
|
CHECK_LENGTH(FileIdBothDirectoryInformation, FILE_ID_BOTH_DIR_INFORMATION);
|
|
default:
|
|
break;
|
|
#undef CHECK_LENGTH
|
|
}
|
|
|
|
/* Get File Object */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
FILE_LIST_DIRECTORY,
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID *)&FileObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
if (AuxBuffer) ExFreePoolWithTag(AuxBuffer, TAG_SYSB);
|
|
return Status;
|
|
}
|
|
|
|
/* Are there two associated completion routines? */
|
|
if (FileObject->CompletionContext != NULL && ApcRoutine != NULL)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
if (AuxBuffer) ExFreePoolWithTag(AuxBuffer, TAG_SYSB);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check if we have an even handle */
|
|
if (EventHandle)
|
|
{
|
|
/* Get its pointer */
|
|
Status = ObReferenceObjectByHandle(EventHandle,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
PreviousMode,
|
|
(PVOID *)&Event,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
if (AuxBuffer) ExFreePoolWithTag(AuxBuffer, TAG_SYSB);
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Clear it */
|
|
KeClearEvent(Event);
|
|
}
|
|
|
|
/* Check if this is a file that was opened for Synch I/O */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
if (Event) ObDereferenceObject(Event);
|
|
ObDereferenceObject(FileObject);
|
|
if (AuxBuffer) ExFreePoolWithTag(AuxBuffer, TAG_SYSB);
|
|
return Status;
|
|
}
|
|
|
|
/* Remember to unlock later */
|
|
LockedForSynch = TRUE;
|
|
}
|
|
|
|
/* Get the device object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Clear the File Object's event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, EventHandle, AuxBuffer);
|
|
|
|
/* Set up the IRP */
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->UserIosb = IoStatusBlock;
|
|
Irp->UserEvent = Event;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
|
Irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
|
Irp->MdlAddress = NULL;
|
|
Irp->Tail.Overlay.AuxiliaryBuffer = AuxBuffer;
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
|
|
/* Check if this is buffered I/O */
|
|
if (DeviceObject->Flags & DO_BUFFERED_IO)
|
|
{
|
|
/* Enter SEH (ExAllocatePoolWithQuotaTag raises on failure!) */
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate a buffer */
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuotaTag(NonPagedPool, Length, TAG_SYSB);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, Event, NULL);
|
|
if (AuxBuffer) ExFreePoolWithTag(AuxBuffer, TAG_SYSB);
|
|
|
|
/* Return the exception code */
|
|
return _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the buffer and flags */
|
|
Irp->UserBuffer = FileInformation;
|
|
Irp->Flags = (IRP_BUFFERED_IO |
|
|
IRP_DEALLOCATE_BUFFER |
|
|
IRP_INPUT_OPERATION);
|
|
}
|
|
else if (DeviceObject->Flags & DO_DIRECT_IO)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate an MDL */
|
|
Mdl = IoAllocateMdl(FileInformation, Length, FALSE, TRUE, Irp);
|
|
if (!Mdl) ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
MmProbeAndLockPages(Mdl, PreviousMode, IoWriteAccess);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, Event, NULL);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* No allocation flags, and use the buffer directly */
|
|
Irp->UserBuffer = FileInformation;
|
|
}
|
|
|
|
/* Set up Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->FileObject = FileObject;
|
|
StackPtr->MajorFunction = IRP_MJ_DIRECTORY_CONTROL;
|
|
StackPtr->MinorFunction = IRP_MN_QUERY_DIRECTORY;
|
|
|
|
/* Set Parameters */
|
|
StackPtr->Parameters.QueryDirectory.FileInformationClass =
|
|
FileInformationClass;
|
|
StackPtr->Parameters.QueryDirectory.FileName = AuxBuffer;
|
|
StackPtr->Parameters.QueryDirectory.FileIndex = 0;
|
|
StackPtr->Parameters.QueryDirectory.Length = Length;
|
|
StackPtr->Flags = 0;
|
|
if (RestartScan) StackPtr->Flags = SL_RESTART_SCAN;
|
|
if (ReturnSingleEntry) StackPtr->Flags |= SL_RETURN_SINGLE_ENTRY;
|
|
|
|
/* Set deferred I/O */
|
|
Irp->Flags |= IRP_DEFER_IO_COMPLETION;
|
|
|
|
/* Perform the call */
|
|
return IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
TRUE,
|
|
PreviousMode,
|
|
LockedForSynch,
|
|
IopOtherTransfer);
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryEaFile(IN HANDLE FileHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN BOOLEAN ReturnSingleEntry,
|
|
IN PVOID EaList OPTIONAL,
|
|
IN ULONG EaListLength,
|
|
IN PULONG EaIndex OPTIONAL,
|
|
IN BOOLEAN RestartScan)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryInformationFile(IN HANDLE FileHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PVOID FileInformation,
|
|
IN ULONG Length,
|
|
IN FILE_INFORMATION_CLASS FileInformationClass)
|
|
{
|
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|
PFILE_OBJECT FileObject;
|
|
NTSTATUS Status;
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PKEVENT Event = NULL;
|
|
BOOLEAN LocalEvent = FALSE;
|
|
PKNORMAL_ROUTINE NormalRoutine;
|
|
PVOID NormalContext;
|
|
KIRQL OldIrql;
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
BOOLEAN CallDriver = TRUE;
|
|
PFILE_ACCESS_INFORMATION AccessBuffer;
|
|
PFILE_MODE_INFORMATION ModeBuffer;
|
|
PFILE_ALIGNMENT_INFORMATION AlignmentBuffer;
|
|
PFILE_ALL_INFORMATION AllBuffer;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Check if we're called from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Validate the information class */
|
|
if ((FileInformationClass < 0) ||
|
|
(FileInformationClass >= FileMaximumInformation) ||
|
|
!(IopQueryOperationLength[FileInformationClass]))
|
|
{
|
|
/* Invalid class */
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* Validate the length */
|
|
if (Length < IopQueryOperationLength[FileInformationClass])
|
|
{
|
|
/* Invalid length */
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
/* Enter SEH for probing */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the I/O Status block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe the information */
|
|
ProbeForWrite(FileInformation, Length, sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
#if DBG
|
|
else
|
|
{
|
|
/* Validate the information class */
|
|
if ((FileInformationClass < 0) ||
|
|
(FileInformationClass >= FileMaximumInformation) ||
|
|
!(IopQueryOperationLength[FileInformationClass]))
|
|
{
|
|
/* Invalid class */
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* Validate the length */
|
|
if (Length < IopQueryOperationLength[FileInformationClass])
|
|
{
|
|
/* Invalid length */
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Reference the Handle */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
IopQueryOperationAccess
|
|
[FileInformationClass],
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID *)&FileObject,
|
|
&HandleInformation);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Check if this is a direct open or not */
|
|
if (FileObject->Flags & FO_DIRECT_DEVICE_OPEN)
|
|
{
|
|
/* Get the device object */
|
|
DeviceObject = IoGetAttachedDevice(FileObject->DeviceObject);
|
|
}
|
|
else
|
|
{
|
|
/* Get the device object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
}
|
|
|
|
/* Check if this is a file that was opened for Synch I/O */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Check if the caller just wants the position */
|
|
if (FileInformationClass == FilePositionInformation)
|
|
{
|
|
/* Protect write in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write the offset */
|
|
((PFILE_POSITION_INFORMATION)FileInformation)->
|
|
CurrentByteOffset = FileObject->CurrentByteOffset;
|
|
|
|
/* Fill out the I/O Status Block */
|
|
IoStatusBlock->Information = sizeof(FILE_POSITION_INFORMATION);
|
|
Status = IoStatusBlock->Status = STATUS_SUCCESS;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Release the file lock, dereference the file and return */
|
|
IopUnlockFileObject(FileObject);
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use local event */
|
|
Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_IO);
|
|
if (!Event)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
KeInitializeEvent(Event, SynchronizationEvent, FALSE);
|
|
LocalEvent = TRUE;
|
|
}
|
|
|
|
/* Check if FastIO is possible for the two available information classes */
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
if (FastIoDispatch != NULL &&
|
|
((FileInformationClass == FileBasicInformation && FastIoDispatch->FastIoQueryBasicInfo != NULL) ||
|
|
(FileInformationClass == FileStandardInformation && FastIoDispatch->FastIoQueryStandardInfo != NULL)))
|
|
{
|
|
BOOLEAN Success = FALSE;
|
|
|
|
if (FileInformationClass == FileBasicInformation)
|
|
{
|
|
Success = FastIoDispatch->FastIoQueryBasicInfo(FileObject, TRUE,
|
|
FileInformation,
|
|
&KernelIosb,
|
|
DeviceObject);
|
|
}
|
|
else
|
|
{
|
|
Success = FastIoDispatch->FastIoQueryStandardInfo(FileObject, TRUE,
|
|
FileInformation,
|
|
&KernelIosb,
|
|
DeviceObject);
|
|
}
|
|
|
|
/* If call succeed */
|
|
if (Success)
|
|
{
|
|
/* Write the IOSB back */
|
|
_SEH2_TRY
|
|
{
|
|
*IoStatusBlock = KernelIosb;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
KernelIosb.Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Free the event if we had one */
|
|
if (LocalEvent)
|
|
{
|
|
ExFreePoolWithTag(Event, TAG_IO);
|
|
}
|
|
|
|
/* If FO was locked, unlock it */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
|
|
/* We're done with FastIO! */
|
|
ObDereferenceObject(FileObject);
|
|
return KernelIosb.Status;
|
|
}
|
|
}
|
|
|
|
/* Clear the File Object event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, NULL, Event);
|
|
|
|
/* Set the IRP */
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
Irp->Flags = (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;
|
|
Irp->UserIosb = (LocalEvent) ? &KernelIosb : IoStatusBlock;
|
|
Irp->UserEvent = (LocalEvent) ? Event : NULL;
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
Irp->MdlAddress = NULL;
|
|
Irp->UserBuffer = FileInformation;
|
|
|
|
/* Set the Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_QUERY_INFORMATION;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Enter SEH (ExAllocatePoolWithQuotaTag raises on failure!) */
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate a buffer */
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuotaTag(NonPagedPool, Length, TAG_SYSB);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, NULL, Event);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the flags */
|
|
Irp->Flags |= (IRP_BUFFERED_IO |
|
|
IRP_DEALLOCATE_BUFFER |
|
|
IRP_INPUT_OPERATION |
|
|
IRP_DEFER_IO_COMPLETION);
|
|
|
|
/* Set the Parameters */
|
|
StackPtr->Parameters.QueryFile.FileInformationClass = FileInformationClass;
|
|
StackPtr->Parameters.QueryFile.Length = Length;
|
|
|
|
/* Queue the IRP */
|
|
IopQueueIrpToThread(Irp);
|
|
|
|
/* Update operation counts */
|
|
IopUpdateOperationCount(IopOtherTransfer);
|
|
|
|
/* Fill in file information before calling the driver.
|
|
See 'File System Internals' page 485.*/
|
|
if (FileInformationClass == FileAccessInformation)
|
|
{
|
|
AccessBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
AccessBuffer->AccessFlags = HandleInformation.GrantedAccess;
|
|
Irp->IoStatus.Information = sizeof(FILE_ACCESS_INFORMATION);
|
|
CallDriver = FALSE;
|
|
}
|
|
else if (FileInformationClass == FileModeInformation)
|
|
{
|
|
ModeBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
ModeBuffer->Mode = IopGetFileMode(FileObject);
|
|
Irp->IoStatus.Information = sizeof(FILE_MODE_INFORMATION);
|
|
CallDriver = FALSE;
|
|
}
|
|
else if (FileInformationClass == FileAlignmentInformation)
|
|
{
|
|
AlignmentBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
AlignmentBuffer->AlignmentRequirement = DeviceObject->AlignmentRequirement;
|
|
Irp->IoStatus.Information = sizeof(FILE_ALIGNMENT_INFORMATION);
|
|
CallDriver = FALSE;
|
|
}
|
|
else if (FileInformationClass == FileAllInformation)
|
|
{
|
|
AllBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
AllBuffer->AccessInformation.AccessFlags = HandleInformation.GrantedAccess;
|
|
AllBuffer->ModeInformation.Mode = IopGetFileMode(FileObject);
|
|
AllBuffer->AlignmentInformation.AlignmentRequirement = DeviceObject->AlignmentRequirement;
|
|
Irp->IoStatus.Information = sizeof(FILE_ACCESS_INFORMATION) +
|
|
sizeof(FILE_MODE_INFORMATION) +
|
|
sizeof(FILE_ALIGNMENT_INFORMATION);
|
|
}
|
|
|
|
/* Call the Driver */
|
|
if (CallDriver)
|
|
{
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
/* Check if this was async I/O */
|
|
if (LocalEvent)
|
|
{
|
|
/* Then to a non-alertable wait */
|
|
Status = KeWaitForSingleObject(Event,
|
|
Executive,
|
|
PreviousMode,
|
|
FALSE,
|
|
NULL);
|
|
if (Status == STATUS_USER_APC)
|
|
{
|
|
/* Abort the request */
|
|
IopAbortInterruptedIrp(Event, Irp);
|
|
}
|
|
|
|
/* Set the final status */
|
|
Status = KernelIosb.Status;
|
|
|
|
/* Enter SEH to write the IOSB back */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write it back to the caller */
|
|
*IoStatusBlock = KernelIosb;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Free the event */
|
|
ExFreePoolWithTag(Event, TAG_IO);
|
|
}
|
|
else
|
|
{
|
|
/* Wait for the IRP */
|
|
Status = KeWaitForSingleObject(&FileObject->Event,
|
|
Executive,
|
|
PreviousMode,
|
|
(FileObject->Flags &
|
|
FO_ALERTABLE_IO) != 0,
|
|
NULL);
|
|
if ((Status == STATUS_USER_APC) || (Status == STATUS_ALERTED))
|
|
{
|
|
/* Abort the request */
|
|
IopAbortInterruptedIrp(&FileObject->Event, Irp);
|
|
}
|
|
|
|
/* Set the final status */
|
|
Status = FileObject->FinalStatus;
|
|
|
|
/* Release the file lock */
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Free the event if we had one */
|
|
if (LocalEvent)
|
|
{
|
|
/* Clear it in the IRP for completion */
|
|
Irp->UserEvent = NULL;
|
|
ExFreePoolWithTag(Event, TAG_IO);
|
|
}
|
|
|
|
/* Set the caller IOSB */
|
|
Irp->UserIosb = IoStatusBlock;
|
|
|
|
/* The IRP wasn't completed, complete it ourselves */
|
|
NormalRoutine = NULL;
|
|
NormalContext = NULL;
|
|
KeRaiseIrql(APC_LEVEL, &OldIrql);
|
|
IopCompleteRequest(&Irp->Tail.Apc,
|
|
&NormalRoutine,
|
|
&NormalContext,
|
|
(PVOID*)&FileObject,
|
|
&NormalContext);
|
|
KeLowerIrql(OldIrql);
|
|
|
|
/* Release the file object if we had locked it*/
|
|
if (!LocalEvent) IopUnlockFileObject(FileObject);
|
|
}
|
|
|
|
/* Return the Status */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryQuotaInformationFile(IN HANDLE FileHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN BOOLEAN ReturnSingleEntry,
|
|
IN PVOID SidList OPTIONAL,
|
|
IN ULONG SidListLength,
|
|
IN PSID StartSid OPTIONAL,
|
|
IN BOOLEAN RestartScan)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtReadFile(IN HANDLE FileHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN PLARGE_INTEGER ByteOffset OPTIONAL,
|
|
IN PULONG Key OPTIONAL)
|
|
{
|
|
NTSTATUS Status;
|
|
PFILE_OBJECT FileObject;
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
PKEVENT EventObject = NULL;
|
|
LARGE_INTEGER CapturedByteOffset;
|
|
ULONG CapturedKey = 0;
|
|
BOOLEAN Synchronous = FALSE;
|
|
PMDL Mdl;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
BOOLEAN Success;
|
|
|
|
PAGED_CODE();
|
|
CapturedByteOffset.QuadPart = 0;
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Get File Object */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
FILE_READ_DATA,
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&FileObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Get the device object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Validate User-Mode Buffers */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the status block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe the read buffer */
|
|
ProbeForWrite(Buffer, Length, 1);
|
|
|
|
/* Check if we got a byte offset */
|
|
if (ByteOffset)
|
|
{
|
|
/* Capture and probe it */
|
|
CapturedByteOffset = ProbeForReadLargeInteger(ByteOffset);
|
|
}
|
|
|
|
/* Can't use an I/O completion port and an APC at the same time */
|
|
if ((FileObject->CompletionContext) && (ApcRoutine))
|
|
{
|
|
/* Fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Perform additional checks for non-cached file access */
|
|
if (FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)
|
|
{
|
|
/* Fail if Length is not sector size aligned
|
|
* Perform a quick check for 2^ sector sizes
|
|
* If it fails, try a more standard way
|
|
*/
|
|
if ((DeviceObject->SectorSize != 0) &&
|
|
((DeviceObject->SectorSize - 1) & Length) != 0)
|
|
{
|
|
if (Length % DeviceObject->SectorSize != 0)
|
|
{
|
|
/* Release the file object and and fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
/* Fail if buffer doesn't match alignment requirements */
|
|
if (((ULONG_PTR)Buffer & DeviceObject->AlignmentRequirement) != 0)
|
|
{
|
|
/* Release the file object and and fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (ByteOffset)
|
|
{
|
|
/* Fail if ByteOffset is not sector size aligned */
|
|
if ((DeviceObject->SectorSize != 0) &&
|
|
(CapturedByteOffset.QuadPart % DeviceObject->SectorSize != 0))
|
|
{
|
|
/* Release the file object and and fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Capture and probe the key */
|
|
if (Key) CapturedKey = ProbeForReadUlong(Key);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Release the file object and return the exception code */
|
|
ObDereferenceObject(FileObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Kernel mode: capture directly */
|
|
if (ByteOffset) CapturedByteOffset = *ByteOffset;
|
|
if (Key) CapturedKey = *Key;
|
|
}
|
|
|
|
/* Check for invalid offset */
|
|
if ((CapturedByteOffset.QuadPart < 0) && (CapturedByteOffset.QuadPart != -2))
|
|
{
|
|
/* -2 is FILE_USE_FILE_POINTER_POSITION */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check for event */
|
|
if (Event)
|
|
{
|
|
/* Reference it */
|
|
Status = ObReferenceObjectByHandle(Event,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&EventObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Otherwise reset the event */
|
|
KeClearEvent(EventObject);
|
|
}
|
|
|
|
/* Check if we should use Sync IO or not */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock the file object */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
if (EventObject) ObDereferenceObject(EventObject);
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Check if we don't have a byte offset available */
|
|
if (!(ByteOffset) ||
|
|
((CapturedByteOffset.u.LowPart == FILE_USE_FILE_POINTER_POSITION) &&
|
|
(CapturedByteOffset.u.HighPart == -1)))
|
|
{
|
|
/* Use the Current Byte Offset instead */
|
|
CapturedByteOffset = FileObject->CurrentByteOffset;
|
|
}
|
|
|
|
/* If the file is cached, try fast I/O */
|
|
if (FileObject->PrivateCacheMap)
|
|
{
|
|
/* Perform fast read */
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
ASSERT(FastIoDispatch != NULL && FastIoDispatch->FastIoRead != NULL);
|
|
|
|
Success = FastIoDispatch->FastIoRead(FileObject,
|
|
&CapturedByteOffset,
|
|
Length,
|
|
TRUE,
|
|
CapturedKey,
|
|
Buffer,
|
|
&KernelIosb,
|
|
DeviceObject);
|
|
|
|
/* Only accept the result if we got a straightforward status */
|
|
if (Success &&
|
|
(KernelIosb.Status == STATUS_SUCCESS ||
|
|
KernelIosb.Status == STATUS_BUFFER_OVERFLOW ||
|
|
KernelIosb.Status == STATUS_END_OF_FILE))
|
|
{
|
|
/* Fast path -- update transfer & operation counts */
|
|
IopUpdateOperationCount(IopReadTransfer);
|
|
IopUpdateTransferCount(IopReadTransfer,
|
|
(ULONG)KernelIosb.Information);
|
|
|
|
/* Enter SEH to write the IOSB back */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write it back to the caller */
|
|
*IoStatusBlock = KernelIosb;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* The caller's IOSB was invalid, so fail */
|
|
if (EventObject) ObDereferenceObject(EventObject);
|
|
IopUnlockFileObject(FileObject);
|
|
ObDereferenceObject(FileObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Signal the completion event */
|
|
if (EventObject)
|
|
{
|
|
KeSetEvent(EventObject, 0, FALSE);
|
|
ObDereferenceObject(EventObject);
|
|
}
|
|
|
|
/* Clean up */
|
|
IopUnlockFileObject(FileObject);
|
|
ObDereferenceObject(FileObject);
|
|
return KernelIosb.Status;
|
|
}
|
|
}
|
|
|
|
/* Remember we are sync */
|
|
Synchronous = TRUE;
|
|
}
|
|
else if (!(ByteOffset) &&
|
|
!(FileObject->Flags & (FO_NAMED_PIPE | FO_MAILSLOT)))
|
|
{
|
|
/* Otherwise, this was async I/O without a byte offset, so fail */
|
|
if (EventObject) ObDereferenceObject(EventObject);
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Clear the File Object's event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, EventObject, NULL);
|
|
|
|
/* Set the IRP */
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
|
Irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
|
Irp->UserIosb = IoStatusBlock;
|
|
Irp->UserEvent = EventObject;
|
|
Irp->PendingReturned = FALSE;
|
|
Irp->Cancel = FALSE;
|
|
Irp->CancelRoutine = NULL;
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
Irp->MdlAddress = NULL;
|
|
|
|
/* Set the Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_READ;
|
|
StackPtr->FileObject = FileObject;
|
|
StackPtr->Parameters.Read.Key = CapturedKey;
|
|
StackPtr->Parameters.Read.Length = Length;
|
|
StackPtr->Parameters.Read.ByteOffset = CapturedByteOffset;
|
|
|
|
/* Check if this is buffered I/O */
|
|
if (DeviceObject->Flags & DO_BUFFERED_IO)
|
|
{
|
|
/* Check if we have a buffer length */
|
|
if (Length)
|
|
{
|
|
/* Enter SEH (ExAllocatePoolWithQuotaTag raises on failure!) */
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate a buffer */
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuotaTag(NonPagedPool, Length, TAG_SYSB);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, EventObject, NULL);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the buffer and flags */
|
|
Irp->UserBuffer = Buffer;
|
|
Irp->Flags = (IRP_BUFFERED_IO |
|
|
IRP_DEALLOCATE_BUFFER |
|
|
IRP_INPUT_OPERATION);
|
|
}
|
|
else
|
|
{
|
|
/* Not reading anything */
|
|
Irp->Flags = IRP_BUFFERED_IO | IRP_INPUT_OPERATION;
|
|
}
|
|
}
|
|
else if (DeviceObject->Flags & DO_DIRECT_IO)
|
|
{
|
|
/* Check if we have a buffer length */
|
|
if (Length)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate an MDL */
|
|
Mdl = IoAllocateMdl(Buffer, Length, FALSE, TRUE, Irp);
|
|
if (!Mdl)
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
MmProbeAndLockPages(Mdl, PreviousMode, IoWriteAccess);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, EventObject, NULL);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
}
|
|
|
|
/* No allocation flags */
|
|
Irp->Flags = 0;
|
|
}
|
|
else
|
|
{
|
|
/* No allocation flags, and use the buffer directly */
|
|
Irp->Flags = 0;
|
|
Irp->UserBuffer = Buffer;
|
|
}
|
|
|
|
/* Now set the deferred read flags */
|
|
Irp->Flags |= (IRP_READ_OPERATION | IRP_DEFER_IO_COMPLETION);
|
|
|
|
if (FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) Irp->Flags |= IRP_NOCACHE;
|
|
|
|
/* Perform the call */
|
|
return IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
TRUE,
|
|
PreviousMode,
|
|
Synchronous,
|
|
IopReadTransfer);
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtReadFileScatter(IN HANDLE FileHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE UserApcRoutine OPTIONAL,
|
|
IN PVOID UserApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK UserIoStatusBlock,
|
|
IN FILE_SEGMENT_ELEMENT BufferDescription [],
|
|
IN ULONG BufferLength,
|
|
IN PLARGE_INTEGER ByteOffset,
|
|
IN PULONG Key OPTIONAL)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetEaFile(IN HANDLE FileHandle,
|
|
IN PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PVOID EaBuffer,
|
|
IN ULONG EaBufferSize)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetInformationFile(IN HANDLE FileHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PVOID FileInformation,
|
|
IN ULONG Length,
|
|
IN FILE_INFORMATION_CLASS FileInformationClass)
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
NTSTATUS Status;
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
|
|
PKEVENT Event = NULL;
|
|
BOOLEAN LocalEvent = FALSE;
|
|
PKNORMAL_ROUTINE NormalRoutine;
|
|
PVOID NormalContext;
|
|
KIRQL OldIrql;
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
PVOID Queue;
|
|
PFILE_COMPLETION_INFORMATION CompletionInfo = FileInformation;
|
|
PIO_COMPLETION_CONTEXT Context;
|
|
PFILE_RENAME_INFORMATION RenameInfo;
|
|
HANDLE TargetHandle = NULL;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Check if we're called from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Validate the information class */
|
|
if ((FileInformationClass < 0) ||
|
|
(FileInformationClass >= FileMaximumInformation) ||
|
|
!(IopSetOperationLength[FileInformationClass]))
|
|
{
|
|
/* Invalid class */
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* Validate the length */
|
|
if (Length < IopSetOperationLength[FileInformationClass])
|
|
{
|
|
/* Invalid length */
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
/* Enter SEH for probing */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the I/O Status block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe the information */
|
|
ProbeForRead(FileInformation,
|
|
Length,
|
|
(Length == sizeof(BOOLEAN)) ?
|
|
sizeof(BOOLEAN) : sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Validate the information class */
|
|
if ((FileInformationClass < 0) ||
|
|
(FileInformationClass >= FileMaximumInformation) ||
|
|
!(IopSetOperationLength[FileInformationClass]))
|
|
{
|
|
/* Invalid class */
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* Validate the length */
|
|
if (Length < IopSetOperationLength[FileInformationClass])
|
|
{
|
|
/* Invalid length */
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
}
|
|
|
|
/* Reference the Handle */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
IopSetOperationAccess
|
|
[FileInformationClass],
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID *)&FileObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Check if this is a direct open or not */
|
|
if (FileObject->Flags & FO_DIRECT_DEVICE_OPEN)
|
|
{
|
|
/* Get the device object */
|
|
DeviceObject = IoGetAttachedDevice(FileObject->DeviceObject);
|
|
}
|
|
else
|
|
{
|
|
/* Get the device object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
}
|
|
|
|
DPRINT("Will call: %p\n", DeviceObject);
|
|
DPRINT("Associated driver: %p (%wZ)\n", DeviceObject->DriverObject, &DeviceObject->DriverObject->DriverName);
|
|
|
|
/* Check if this is a file that was opened for Synch I/O */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Check if the caller just wants the position */
|
|
if (FileInformationClass == FilePositionInformation)
|
|
{
|
|
/* Protect write in SEH */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write the offset */
|
|
FileObject->CurrentByteOffset =
|
|
((PFILE_POSITION_INFORMATION)FileInformation)->
|
|
CurrentByteOffset;
|
|
|
|
/* Fill out the I/O Status Block */
|
|
IoStatusBlock->Information = 0;
|
|
Status = IoStatusBlock->Status = STATUS_SUCCESS;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Update transfer count */
|
|
IopUpdateTransferCount(IopOtherTransfer, Length);
|
|
|
|
/* Release the file lock, dereference the file and return */
|
|
IopUnlockFileObject(FileObject);
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use local event */
|
|
Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_IO);
|
|
if (!Event)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
KeInitializeEvent(Event, SynchronizationEvent, FALSE);
|
|
LocalEvent = TRUE;
|
|
}
|
|
|
|
/* Clear the File Object event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, NULL, Event);
|
|
|
|
/* Set the IRP */
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
Irp->Flags = (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;
|
|
Irp->UserIosb = (LocalEvent) ? &KernelIosb : IoStatusBlock;
|
|
Irp->UserEvent = (LocalEvent) ? Event : NULL;
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
Irp->MdlAddress = NULL;
|
|
Irp->UserBuffer = FileInformation;
|
|
|
|
/* Set the Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_SET_INFORMATION;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Enter SEH (ExAllocatePoolWithQuotaTag raises on failure!) */
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate a buffer */
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuotaTag(NonPagedPool, Length, TAG_SYSB);
|
|
|
|
/* Copy the data into it */
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
FileInformation,
|
|
Length);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, NULL, Event);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the flags */
|
|
Irp->Flags |= (IRP_BUFFERED_IO |
|
|
IRP_DEALLOCATE_BUFFER |
|
|
IRP_DEFER_IO_COMPLETION);
|
|
|
|
/* Set the Parameters */
|
|
StackPtr->Parameters.SetFile.FileInformationClass = FileInformationClass;
|
|
StackPtr->Parameters.SetFile.Length = Length;
|
|
|
|
/* Queue the IRP */
|
|
IopQueueIrpToThread(Irp);
|
|
|
|
/* Update operation counts */
|
|
IopUpdateOperationCount(IopOtherTransfer);
|
|
|
|
/* FIXME: Later, we can implement a lot of stuff here and avoid a driver call */
|
|
/* Handle IO Completion Port quickly */
|
|
if (FileInformationClass == FileCompletionInformation)
|
|
{
|
|
/* Check if the file object already has a completion port */
|
|
if ((FileObject->Flags & FO_SYNCHRONOUS_IO) ||
|
|
(FileObject->CompletionContext))
|
|
{
|
|
/* Fail */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
/* Reference the Port */
|
|
CompletionInfo = Irp->AssociatedIrp.SystemBuffer;
|
|
Status = ObReferenceObjectByHandle(CompletionInfo->Port,
|
|
IO_COMPLETION_MODIFY_STATE,
|
|
IoCompletionType,
|
|
PreviousMode,
|
|
(PVOID*)&Queue,
|
|
NULL);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Allocate the Context */
|
|
Context = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(IO_COMPLETION_CONTEXT),
|
|
IOC_TAG);
|
|
if (Context)
|
|
{
|
|
/* Set the Data */
|
|
Context->Key = CompletionInfo->Key;
|
|
Context->Port = Queue;
|
|
if (InterlockedCompareExchangePointer((PVOID*)&FileObject->
|
|
CompletionContext,
|
|
Context,
|
|
NULL))
|
|
{
|
|
/*
|
|
* Someone else set the completion port in the
|
|
* meanwhile, so dereference the port and fail.
|
|
*/
|
|
ExFreePoolWithTag(Context, IOC_TAG);
|
|
ObDereferenceObject(Queue);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Dereference the Port now */
|
|
ObDereferenceObject(Queue);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set the IRP Status */
|
|
Irp->IoStatus.Status = Status;
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
else if (FileInformationClass == FileRenameInformation ||
|
|
FileInformationClass == FileLinkInformation ||
|
|
FileInformationClass == FileMoveClusterInformation)
|
|
{
|
|
/* Get associated information */
|
|
RenameInfo = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
/* Only rename if:
|
|
* -> We have a name
|
|
* -> In unicode
|
|
* -> sizes are valid
|
|
*/
|
|
if (RenameInfo->FileNameLength != 0 &&
|
|
!(RenameInfo->FileNameLength & 1) &&
|
|
(Length - FIELD_OFFSET(FILE_RENAME_INFORMATION, FileName) >= RenameInfo->FileNameLength))
|
|
{
|
|
/* Properly set information received */
|
|
if (FileInformationClass == FileMoveClusterInformation)
|
|
{
|
|
StackPtr->Parameters.SetFile.ClusterCount = ((PFILE_MOVE_CLUSTER_INFORMATION)RenameInfo)->ClusterCount;
|
|
}
|
|
else
|
|
{
|
|
StackPtr->Parameters.SetFile.ReplaceIfExists = RenameInfo->ReplaceIfExists;
|
|
}
|
|
|
|
/* If we got fully path OR relative target, attempt a parent directory open */
|
|
if (RenameInfo->FileName[0] == OBJ_NAME_PATH_SEPARATOR || RenameInfo->RootDirectory)
|
|
{
|
|
Status = IopOpenLinkOrRenameTarget(&TargetHandle, Irp, RenameInfo, FileObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
Irp->IoStatus.Status = Status;
|
|
}
|
|
else
|
|
{
|
|
/* Call the Driver */
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Call the Driver */
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Status = Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Call the Driver */
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
}
|
|
|
|
/* Check if we're waiting for the IRP to complete */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
/* Check if this was async I/O */
|
|
if (LocalEvent)
|
|
{
|
|
/* Then to a non-alertable wait */
|
|
Status = KeWaitForSingleObject(Event,
|
|
Executive,
|
|
PreviousMode,
|
|
FALSE,
|
|
NULL);
|
|
if (Status == STATUS_USER_APC)
|
|
{
|
|
/* Abort the request */
|
|
IopAbortInterruptedIrp(Event, Irp);
|
|
}
|
|
|
|
/* Set the final status */
|
|
Status = KernelIosb.Status;
|
|
|
|
/* Enter SEH to write the IOSB back */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write it back to the caller */
|
|
*IoStatusBlock = KernelIosb;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Get the exception code */
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Free the event */
|
|
ExFreePoolWithTag(Event, TAG_IO);
|
|
}
|
|
else
|
|
{
|
|
/* Wait for the IRP */
|
|
Status = KeWaitForSingleObject(&FileObject->Event,
|
|
Executive,
|
|
PreviousMode,
|
|
(FileObject->Flags &
|
|
FO_ALERTABLE_IO) != 0,
|
|
NULL);
|
|
if ((Status == STATUS_USER_APC) || (Status == STATUS_ALERTED))
|
|
{
|
|
/* Abort the request */
|
|
IopAbortInterruptedIrp(&FileObject->Event, Irp);
|
|
}
|
|
|
|
/* Set the final status */
|
|
Status = FileObject->FinalStatus;
|
|
|
|
/* Release the file lock */
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Free the event if we had one */
|
|
if (LocalEvent)
|
|
{
|
|
/* Clear it in the IRP for completion */
|
|
Irp->UserEvent = NULL;
|
|
ExFreePoolWithTag(Event, TAG_IO);
|
|
}
|
|
|
|
/* Set the caller IOSB */
|
|
Irp->UserIosb = IoStatusBlock;
|
|
|
|
/* The IRP wasn't completed, complete it ourselves */
|
|
NormalRoutine = NULL;
|
|
NormalContext = NULL;
|
|
KeRaiseIrql(APC_LEVEL, &OldIrql);
|
|
IopCompleteRequest(&Irp->Tail.Apc,
|
|
&NormalRoutine,
|
|
&NormalContext,
|
|
(PVOID*)&FileObject,
|
|
&NormalContext);
|
|
KeLowerIrql(OldIrql);
|
|
|
|
/* Release the file object if we had locked it*/
|
|
if (!LocalEvent) IopUnlockFileObject(FileObject);
|
|
}
|
|
|
|
if (TargetHandle != NULL)
|
|
{
|
|
ObCloseHandle(TargetHandle, KernelMode);
|
|
}
|
|
|
|
/* Return the Status */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetQuotaInformationFile(IN HANDLE FileHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferLength)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtUnlockFile(IN HANDLE FileHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PLARGE_INTEGER ByteOffset,
|
|
IN PLARGE_INTEGER Length,
|
|
IN ULONG Key OPTIONAL)
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PLARGE_INTEGER LocalLength = NULL;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PKEVENT Event = NULL;
|
|
BOOLEAN LocalEvent = FALSE;
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
LARGE_INTEGER CapturedByteOffset, CapturedLength;
|
|
NTSTATUS Status;
|
|
OBJECT_HANDLE_INFORMATION HandleInformation;
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
PAGED_CODE();
|
|
CapturedByteOffset.QuadPart = 0;
|
|
CapturedLength.QuadPart = 0;
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Get File Object */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
0,
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&FileObject,
|
|
&HandleInformation);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Check if we're called from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Must have either FILE_READ_DATA or FILE_WRITE_DATA access */
|
|
if (!(HandleInformation.GrantedAccess &
|
|
(FILE_WRITE_DATA | FILE_READ_DATA)))
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
/* Enter SEH for probing */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the I/O Status block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe and capture the large integers */
|
|
CapturedByteOffset = ProbeForReadLargeInteger(ByteOffset);
|
|
CapturedLength = ProbeForReadLargeInteger(Length);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Dereference the object and return exception code */
|
|
ObDereferenceObject(FileObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise, capture them directly */
|
|
CapturedByteOffset = *ByteOffset;
|
|
CapturedLength = *Length;
|
|
}
|
|
|
|
/* Check if this is a direct open or not */
|
|
if (FileObject->Flags & FO_DIRECT_DEVICE_OPEN)
|
|
{
|
|
DeviceObject = IoGetAttachedDevice(FileObject->DeviceObject);
|
|
}
|
|
else
|
|
{
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
}
|
|
|
|
/* Try to do it the FastIO way if possible */
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
if (FastIoDispatch != NULL && FastIoDispatch->FastIoUnlockSingle != NULL)
|
|
{
|
|
if (FastIoDispatch->FastIoUnlockSingle(FileObject,
|
|
&CapturedByteOffset,
|
|
&CapturedLength,
|
|
PsGetCurrentProcess(),
|
|
Key,
|
|
&KernelIosb,
|
|
DeviceObject))
|
|
{
|
|
/* Write the IOSB back */
|
|
_SEH2_TRY
|
|
{
|
|
*IoStatusBlock = KernelIosb;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
KernelIosb.Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* We're done with FastIO! */
|
|
ObDereferenceObject(FileObject);
|
|
return KernelIosb.Status;
|
|
}
|
|
}
|
|
|
|
/* Check if we should use Sync IO or not */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use local event */
|
|
Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_IO);
|
|
if (!Event)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
KeInitializeEvent(Event, SynchronizationEvent, FALSE);
|
|
LocalEvent = TRUE;
|
|
}
|
|
|
|
/* Clear File Object event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, NULL, Event);
|
|
|
|
/* Set up the IRP */
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->Flags = (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;
|
|
Irp->UserIosb = (LocalEvent) ? &KernelIosb : IoStatusBlock;
|
|
Irp->UserEvent = (LocalEvent) ? Event : NULL;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
|
|
/* Set up Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_LOCK_CONTROL;
|
|
StackPtr->MinorFunction = IRP_MN_UNLOCK_SINGLE;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Enter SEH (ExAllocatePoolWithQuotaTag raises on failure!) */
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate a buffer */
|
|
LocalLength = ExAllocatePoolWithQuotaTag(NonPagedPool,
|
|
sizeof(LARGE_INTEGER),
|
|
TAG_LOCK);
|
|
|
|
/* Set the length */
|
|
*LocalLength = CapturedLength;
|
|
Irp->Tail.Overlay.AuxiliaryBuffer = (PVOID)LocalLength;
|
|
StackPtr->Parameters.LockControl.Length = LocalLength;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, NULL, Event);
|
|
if (LocalLength) ExFreePoolWithTag(LocalLength, TAG_LOCK);
|
|
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set Parameters */
|
|
StackPtr->Parameters.LockControl.ByteOffset = CapturedByteOffset;
|
|
StackPtr->Parameters.LockControl.Key = Key;
|
|
|
|
/* Call the Driver */
|
|
Status = IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
FALSE,
|
|
PreviousMode,
|
|
!LocalEvent,
|
|
IopOtherTransfer);
|
|
|
|
/* Check if this was async I/O */
|
|
if (LocalEvent)
|
|
{
|
|
/* It was, finalize this request */
|
|
Status = IopFinalizeAsynchronousIo(Status,
|
|
Event,
|
|
Irp,
|
|
PreviousMode,
|
|
&KernelIosb,
|
|
IoStatusBlock);
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtWriteFile(IN HANDLE FileHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
|
|
IN PVOID ApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length,
|
|
IN PLARGE_INTEGER ByteOffset OPTIONAL,
|
|
IN PULONG Key OPTIONAL)
|
|
{
|
|
NTSTATUS Status;
|
|
PFILE_OBJECT FileObject;
|
|
PIRP Irp;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
PKEVENT EventObject = NULL;
|
|
LARGE_INTEGER CapturedByteOffset;
|
|
ULONG CapturedKey = 0;
|
|
BOOLEAN Synchronous = FALSE;
|
|
PMDL Mdl;
|
|
OBJECT_HANDLE_INFORMATION ObjectHandleInfo;
|
|
PFAST_IO_DISPATCH FastIoDispatch;
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
BOOLEAN Success;
|
|
|
|
PAGED_CODE();
|
|
CapturedByteOffset.QuadPart = 0;
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Get File Object for write */
|
|
Status = ObReferenceFileObjectForWrite(FileHandle,
|
|
PreviousMode,
|
|
&FileObject,
|
|
&ObjectHandleInfo);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Get the device object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Validate User-Mode Buffers */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the status block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe the read buffer */
|
|
ProbeForRead(Buffer, Length, 1);
|
|
|
|
/* Check if we got a byte offset */
|
|
if (ByteOffset)
|
|
{
|
|
/* Capture and probe it */
|
|
CapturedByteOffset = ProbeForReadLargeInteger(ByteOffset);
|
|
}
|
|
|
|
/* Can't use an I/O completion port and an APC at the same time */
|
|
if ((FileObject->CompletionContext) && (ApcRoutine))
|
|
{
|
|
/* Fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Perform additional checks for non-cached file access */
|
|
if (FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING)
|
|
{
|
|
/* Fail if Length is not sector size aligned
|
|
* Perform a quick check for 2^ sector sizes
|
|
* If it fails, try a more standard way
|
|
*/
|
|
if ((DeviceObject->SectorSize != 0) &&
|
|
((DeviceObject->SectorSize - 1) & Length) != 0)
|
|
{
|
|
if (Length % DeviceObject->SectorSize != 0)
|
|
{
|
|
/* Release the file object and and fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
/* Fail if buffer doesn't match alignment requirements */
|
|
if (((ULONG_PTR)Buffer & DeviceObject->AlignmentRequirement) != 0)
|
|
{
|
|
/* Release the file object and and fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (ByteOffset)
|
|
{
|
|
/* Fail if ByteOffset is not sector size aligned */
|
|
if ((DeviceObject->SectorSize != 0) &&
|
|
(CapturedByteOffset.QuadPart % DeviceObject->SectorSize != 0))
|
|
{
|
|
/* Only if that's not specific values for synchronous IO */
|
|
if ((CapturedByteOffset.QuadPart != FILE_WRITE_TO_END_OF_FILE) &&
|
|
(CapturedByteOffset.QuadPart != FILE_USE_FILE_POINTER_POSITION ||
|
|
!BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO)))
|
|
{
|
|
/* Release the file object and and fail */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Capture and probe the key */
|
|
if (Key) CapturedKey = ProbeForReadUlong(Key);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Release the file object and return the exception code */
|
|
ObDereferenceObject(FileObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
else
|
|
{
|
|
/* Kernel mode: capture directly */
|
|
if (ByteOffset) CapturedByteOffset = *ByteOffset;
|
|
if (Key) CapturedKey = *Key;
|
|
}
|
|
|
|
/* Check for invalid offset */
|
|
if (CapturedByteOffset.QuadPart < -2)
|
|
{
|
|
/* -1 is FILE_WRITE_TO_END_OF_FILE */
|
|
/* -2 is FILE_USE_FILE_POINTER_POSITION */
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Check if this is an append operation */
|
|
if ((ObjectHandleInfo.GrantedAccess &
|
|
(FILE_APPEND_DATA | FILE_WRITE_DATA)) == FILE_APPEND_DATA)
|
|
{
|
|
/* Give the drivers something to understand */
|
|
CapturedByteOffset.u.LowPart = FILE_WRITE_TO_END_OF_FILE;
|
|
CapturedByteOffset.u.HighPart = -1;
|
|
}
|
|
|
|
/* Check for event */
|
|
if (Event)
|
|
{
|
|
/* Reference it */
|
|
Status = ObReferenceObjectByHandle(Event,
|
|
EVENT_MODIFY_STATE,
|
|
ExEventObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&EventObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fail */
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Otherwise reset the event */
|
|
KeClearEvent(EventObject);
|
|
}
|
|
|
|
/* Check if we should use Sync IO or not */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock the file object */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
if (EventObject) ObDereferenceObject(EventObject);
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
|
|
/* Check if we don't have a byte offset available */
|
|
if (!(ByteOffset) ||
|
|
((CapturedByteOffset.u.LowPart == FILE_USE_FILE_POINTER_POSITION) &&
|
|
(CapturedByteOffset.u.HighPart == -1)))
|
|
{
|
|
/* Use the Current Byte Offset instead */
|
|
CapturedByteOffset = FileObject->CurrentByteOffset;
|
|
}
|
|
|
|
/* If the file is cached, try fast I/O */
|
|
if (FileObject->PrivateCacheMap)
|
|
{
|
|
/* Perform fast write */
|
|
FastIoDispatch = DeviceObject->DriverObject->FastIoDispatch;
|
|
ASSERT(FastIoDispatch != NULL && FastIoDispatch->FastIoWrite != NULL);
|
|
|
|
Success = FastIoDispatch->FastIoWrite(FileObject,
|
|
&CapturedByteOffset,
|
|
Length,
|
|
TRUE,
|
|
CapturedKey,
|
|
Buffer,
|
|
&KernelIosb,
|
|
DeviceObject);
|
|
|
|
/* Only accept the result if it was successful */
|
|
if (Success &&
|
|
KernelIosb.Status == STATUS_SUCCESS)
|
|
{
|
|
/* Fast path -- update transfer & operation counts */
|
|
IopUpdateOperationCount(IopWriteTransfer);
|
|
IopUpdateTransferCount(IopWriteTransfer,
|
|
(ULONG)KernelIosb.Information);
|
|
|
|
/* Enter SEH to write the IOSB back */
|
|
_SEH2_TRY
|
|
{
|
|
/* Write it back to the caller */
|
|
*IoStatusBlock = KernelIosb;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* The caller's IOSB was invalid, so fail */
|
|
if (EventObject) ObDereferenceObject(EventObject);
|
|
IopUnlockFileObject(FileObject);
|
|
ObDereferenceObject(FileObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Signal the completion event */
|
|
if (EventObject)
|
|
{
|
|
KeSetEvent(EventObject, 0, FALSE);
|
|
ObDereferenceObject(EventObject);
|
|
}
|
|
|
|
/* Clean up */
|
|
IopUnlockFileObject(FileObject);
|
|
ObDereferenceObject(FileObject);
|
|
return KernelIosb.Status;
|
|
}
|
|
}
|
|
|
|
/* Remember we are sync */
|
|
Synchronous = TRUE;
|
|
}
|
|
else if (!(ByteOffset) &&
|
|
!(FileObject->Flags & (FO_NAMED_PIPE | FO_MAILSLOT)))
|
|
{
|
|
/* Otherwise, this was async I/O without a byte offset, so fail */
|
|
if (EventObject) ObDereferenceObject(EventObject);
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Clear the File Object's event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, EventObject, NULL);
|
|
|
|
/* Set the IRP */
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
|
Irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
|
Irp->UserIosb = IoStatusBlock;
|
|
Irp->UserEvent = EventObject;
|
|
Irp->PendingReturned = FALSE;
|
|
Irp->Cancel = FALSE;
|
|
Irp->CancelRoutine = NULL;
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
Irp->MdlAddress = NULL;
|
|
|
|
/* Set the Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_WRITE;
|
|
StackPtr->FileObject = FileObject;
|
|
StackPtr->Flags = FileObject->Flags & FO_WRITE_THROUGH ?
|
|
SL_WRITE_THROUGH : 0;
|
|
StackPtr->Parameters.Write.Key = CapturedKey;
|
|
StackPtr->Parameters.Write.Length = Length;
|
|
StackPtr->Parameters.Write.ByteOffset = CapturedByteOffset;
|
|
|
|
/* Check if this is buffered I/O */
|
|
if (DeviceObject->Flags & DO_BUFFERED_IO)
|
|
{
|
|
/* Check if we have a buffer length */
|
|
if (Length)
|
|
{
|
|
/* Enter SEH (ExAllocatePoolWithQuotaTag raises on failure!) */
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate a buffer */
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuotaTag(NonPagedPool, Length, TAG_SYSB);
|
|
|
|
/* Copy the data into it */
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, Buffer, Length);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, EventObject, NULL);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the flags */
|
|
Irp->Flags = (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
|
|
}
|
|
else
|
|
{
|
|
/* Not writing anything */
|
|
Irp->Flags = IRP_BUFFERED_IO;
|
|
}
|
|
}
|
|
else if (DeviceObject->Flags & DO_DIRECT_IO)
|
|
{
|
|
/* Check if we have a buffer length */
|
|
if (Length)
|
|
{
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate an MDL */
|
|
Mdl = IoAllocateMdl(Buffer, Length, FALSE, TRUE, Irp);
|
|
if (!Mdl)
|
|
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
|
|
MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, EventObject, NULL);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* No allocation flags */
|
|
Irp->Flags = 0;
|
|
}
|
|
else
|
|
{
|
|
/* No allocation flags, and use the buffer directly */
|
|
Irp->Flags = 0;
|
|
Irp->UserBuffer = Buffer;
|
|
}
|
|
|
|
/* Now set the deferred read flags */
|
|
Irp->Flags |= (IRP_WRITE_OPERATION | IRP_DEFER_IO_COMPLETION);
|
|
|
|
if (FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) Irp->Flags |= IRP_NOCACHE;
|
|
|
|
/* Perform the call */
|
|
return IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
TRUE,
|
|
PreviousMode,
|
|
Synchronous,
|
|
IopWriteTransfer);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtWriteFileGather(IN HANDLE FileHandle,
|
|
IN HANDLE Event OPTIONAL,
|
|
IN PIO_APC_ROUTINE UserApcRoutine OPTIONAL,
|
|
IN PVOID UserApcContext OPTIONAL,
|
|
OUT PIO_STATUS_BLOCK UserIoStatusBlock,
|
|
IN FILE_SEGMENT_ELEMENT BufferDescription [],
|
|
IN ULONG BufferLength,
|
|
IN PLARGE_INTEGER ByteOffset,
|
|
IN PULONG Key OPTIONAL)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtQueryVolumeInformationFile(IN HANDLE FileHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
OUT PVOID FsInformation,
|
|
IN ULONG Length,
|
|
IN FS_INFORMATION_CLASS FsInformationClass)
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PKEVENT Event = NULL;
|
|
BOOLEAN LocalEvent = FALSE;
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Check if we're called from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Validate the information class */
|
|
if ((FsInformationClass < 0) ||
|
|
(FsInformationClass >= FileFsMaximumInformation) ||
|
|
!(IopQueryFsOperationLength[FsInformationClass]))
|
|
{
|
|
/* Invalid class */
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* Validate the length */
|
|
if (Length < IopQueryFsOperationLength[FsInformationClass])
|
|
{
|
|
/* Invalid length */
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
/* Enter SEH for probing */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the I/O Status block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe the information */
|
|
ProbeForWrite(FsInformation, Length, sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Get File Object */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
IopQueryFsOperationAccess
|
|
[FsInformationClass],
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&FileObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Only allow direct device open for FileFsDeviceInformation */
|
|
if (BooleanFlagOn(FileObject->Flags, FO_DIRECT_DEVICE_OPEN) &&
|
|
FsInformationClass != FileFsDeviceInformation)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
/* Check if we should use Sync IO or not */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Quick path for FileFsDeviceInformation - the kernel has enough
|
|
* info to reply instead of the driver, excepted for network file systems
|
|
*/
|
|
if (FsInformationClass == FileFsDeviceInformation &&
|
|
(BooleanFlagOn(FileObject->Flags, FO_DIRECT_DEVICE_OPEN) || FileObject->DeviceObject->DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM))
|
|
{
|
|
PFILE_FS_DEVICE_INFORMATION FsDeviceInfo = FsInformation;
|
|
DeviceObject = FileObject->DeviceObject;
|
|
|
|
_SEH2_TRY
|
|
{
|
|
FsDeviceInfo->DeviceType = DeviceObject->DeviceType;
|
|
|
|
/* Complete characteristcs with mount status if relevant */
|
|
FsDeviceInfo->Characteristics = DeviceObject->Characteristics;
|
|
if (IopGetMountFlag(DeviceObject))
|
|
{
|
|
SetFlag(FsDeviceInfo->Characteristics, FILE_DEVICE_IS_MOUNTED);
|
|
}
|
|
|
|
IoStatusBlock->Information = sizeof(FILE_FS_DEVICE_INFORMATION);
|
|
IoStatusBlock->Status = STATUS_SUCCESS;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Check if we had a file lock */
|
|
if (BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO))
|
|
{
|
|
/* Release it */
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
|
|
/* Dereference the FO */
|
|
ObDereferenceObject(FileObject);
|
|
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Check if we had a file lock */
|
|
if (BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO))
|
|
{
|
|
/* Release it */
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
|
|
/* Dereference the FO */
|
|
ObDereferenceObject(FileObject);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
/* This is to be handled by the kernel, not by FSD */
|
|
else if (FsInformationClass == FileFsDriverPathInformation)
|
|
{
|
|
_SEH2_VOLATILE PFILE_FS_DRIVER_PATH_INFORMATION DriverPathInfo = NULL;
|
|
|
|
/* Enter SEH (ExAllocatePoolWithQuotaTag raises on failure!) */
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate our local structure */
|
|
DriverPathInfo = ExAllocatePoolWithQuotaTag(NonPagedPool, Length, TAG_IO);
|
|
|
|
/* And copy back caller data */
|
|
RtlCopyMemory(DriverPathInfo, FsInformation, Length);
|
|
|
|
/* Is the driver in the IO path? */
|
|
Status = IopGetDriverPathInformation(FileObject,
|
|
(PFILE_FS_DRIVER_PATH_INFORMATION)DriverPathInfo,
|
|
Length);
|
|
/* We failed, don't continue execution */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
RtlRaiseStatus(Status);
|
|
}
|
|
|
|
/* We succeed, copy back info */
|
|
((PFILE_FS_DRIVER_PATH_INFORMATION)FsInformation)->DriverInPath = DriverPathInfo->DriverInPath;
|
|
|
|
/* We're done */
|
|
IoStatusBlock->Information = sizeof(FILE_FS_DRIVER_PATH_INFORMATION);
|
|
IoStatusBlock->Status = STATUS_SUCCESS;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Don't leak */
|
|
if (DriverPathInfo != NULL)
|
|
{
|
|
ExFreePoolWithTag(DriverPathInfo, TAG_IO);
|
|
}
|
|
|
|
/* Check if we had a file lock */
|
|
if (BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO))
|
|
{
|
|
/* Release it */
|
|
IopUnlockFileObject(FileObject);
|
|
}
|
|
|
|
/* Dereference the FO */
|
|
ObDereferenceObject(FileObject);
|
|
|
|
return Status;
|
|
}
|
|
|
|
if (!BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO))
|
|
{
|
|
/* Use local event */
|
|
Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_IO);
|
|
if (!Event)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
KeInitializeEvent(Event, SynchronizationEvent, FALSE);
|
|
LocalEvent = TRUE;
|
|
}
|
|
|
|
/* Get the device object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Clear File Object event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp) return IopCleanupFailedIrp(FileObject, NULL, Event);
|
|
|
|
/* Set up the IRP */
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->Flags = (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;
|
|
Irp->UserIosb = (LocalEvent) ? &KernelIosb : IoStatusBlock;
|
|
Irp->UserEvent = (LocalEvent) ? Event : NULL;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
Irp->UserBuffer = FsInformation;
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
Irp->MdlAddress = NULL;
|
|
|
|
/* Set up Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_QUERY_VOLUME_INFORMATION;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Enter SEH (ExAllocatePoolWithQuotaTag raises on failure!) */
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate a buffer */
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuotaTag(NonPagedPool, Length, TAG_SYSB);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, NULL, Event);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the flags for this buffered + deferred I/O */
|
|
Irp->Flags |= (IRP_BUFFERED_IO |
|
|
IRP_DEALLOCATE_BUFFER |
|
|
IRP_INPUT_OPERATION |
|
|
IRP_DEFER_IO_COMPLETION);
|
|
|
|
/* Set Parameters */
|
|
StackPtr->Parameters.QueryVolume.Length = Length;
|
|
StackPtr->Parameters.QueryVolume.FsInformationClass = FsInformationClass;
|
|
|
|
/* Call the Driver */
|
|
Status = IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
TRUE,
|
|
PreviousMode,
|
|
!LocalEvent,
|
|
IopOtherTransfer);
|
|
|
|
/* Check if this was async I/O */
|
|
if (LocalEvent)
|
|
{
|
|
/* It was, finalize this request */
|
|
Status = IopFinalizeAsynchronousIo(Status,
|
|
Event,
|
|
Irp,
|
|
PreviousMode,
|
|
&KernelIosb,
|
|
IoStatusBlock);
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtSetVolumeInformationFile(IN HANDLE FileHandle,
|
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|
IN PVOID FsInformation,
|
|
IN ULONG Length,
|
|
IN FS_INFORMATION_CLASS FsInformationClass)
|
|
{
|
|
PFILE_OBJECT FileObject;
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION StackPtr;
|
|
PDEVICE_OBJECT DeviceObject, TargetDeviceObject;
|
|
PKEVENT Event = NULL;
|
|
BOOLEAN LocalEvent = FALSE;
|
|
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK KernelIosb;
|
|
TARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure;
|
|
PAGED_CODE();
|
|
IOTRACE(IO_API_DEBUG, "FileHandle: %p\n", FileHandle);
|
|
|
|
/* Check if we're called from user mode */
|
|
if (PreviousMode != KernelMode)
|
|
{
|
|
/* Validate the information class */
|
|
if ((FsInformationClass < 0) ||
|
|
(FsInformationClass >= FileFsMaximumInformation) ||
|
|
!(IopSetFsOperationLength[FsInformationClass]))
|
|
{
|
|
/* Invalid class */
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
/* Validate the length */
|
|
if (Length < IopSetFsOperationLength[FsInformationClass])
|
|
{
|
|
/* Invalid length */
|
|
return STATUS_INFO_LENGTH_MISMATCH;
|
|
}
|
|
|
|
/* Enter SEH for probing */
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the I/O Status block */
|
|
ProbeForWriteIoStatusBlock(IoStatusBlock);
|
|
|
|
/* Probe the information */
|
|
ProbeForRead(FsInformation, Length, sizeof(ULONG));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Return the exception code */
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
}
|
|
|
|
/* Get File Object */
|
|
Status = ObReferenceObjectByHandle(FileHandle,
|
|
IopSetFsOperationAccess
|
|
[FsInformationClass],
|
|
IoFileObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&FileObject,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Get target device for notification */
|
|
Status = IoGetRelatedTargetDevice(FileObject, &TargetDeviceObject);
|
|
if (!NT_SUCCESS(Status)) TargetDeviceObject = NULL;
|
|
|
|
/* Check if we should use Sync IO or not */
|
|
if (FileObject->Flags & FO_SYNCHRONOUS_IO)
|
|
{
|
|
/* Lock it */
|
|
Status = IopLockFileObject(FileObject, PreviousMode);
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
if (TargetDeviceObject) ObDereferenceObject(TargetDeviceObject);
|
|
return Status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use local event */
|
|
Event = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_IO);
|
|
if (!Event)
|
|
{
|
|
ObDereferenceObject(FileObject);
|
|
if (TargetDeviceObject) ObDereferenceObject(TargetDeviceObject);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
KeInitializeEvent(Event, SynchronizationEvent, FALSE);
|
|
LocalEvent = TRUE;
|
|
}
|
|
|
|
/* Get the device object */
|
|
DeviceObject = IoGetRelatedDeviceObject(FileObject);
|
|
|
|
/* Clear File Object event */
|
|
KeClearEvent(&FileObject->Event);
|
|
|
|
/* Allocate the IRP */
|
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
if (!Irp)
|
|
{
|
|
if (TargetDeviceObject) ObDereferenceObject(TargetDeviceObject);
|
|
return IopCleanupFailedIrp(FileObject, NULL, Event);
|
|
}
|
|
|
|
/* Set up the IRP */
|
|
Irp->RequestorMode = PreviousMode;
|
|
Irp->Flags = (LocalEvent) ? IRP_SYNCHRONOUS_API : 0;
|
|
Irp->UserIosb = (LocalEvent) ? &KernelIosb : IoStatusBlock;
|
|
Irp->UserEvent = (LocalEvent) ? Event : NULL;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
|
|
Irp->UserBuffer = FsInformation;
|
|
Irp->AssociatedIrp.SystemBuffer = NULL;
|
|
Irp->MdlAddress = NULL;
|
|
|
|
/* Set up Stack Data */
|
|
StackPtr = IoGetNextIrpStackLocation(Irp);
|
|
StackPtr->MajorFunction = IRP_MJ_SET_VOLUME_INFORMATION;
|
|
StackPtr->FileObject = FileObject;
|
|
|
|
/* Enter SEH (ExAllocatePoolWithQuotaTag raises on failure!) */
|
|
_SEH2_TRY
|
|
{
|
|
/* Allocate a buffer */
|
|
Irp->AssociatedIrp.SystemBuffer =
|
|
ExAllocatePoolWithQuotaTag(NonPagedPool, Length, TAG_SYSB);
|
|
|
|
/* Copy the data into it */
|
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, FsInformation, Length);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
/* Allocating failed, clean up and return the exception code */
|
|
IopCleanupAfterException(FileObject, Irp, NULL, Event);
|
|
if (TargetDeviceObject) ObDereferenceObject(TargetDeviceObject);
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Set the flags for this buffered + deferred I/O */
|
|
Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
|
|
|
|
/* Set Parameters */
|
|
StackPtr->Parameters.SetVolume.Length = Length;
|
|
StackPtr->Parameters.SetVolume.FsInformationClass = FsInformationClass;
|
|
|
|
/* Call the Driver */
|
|
Status = IopPerformSynchronousRequest(DeviceObject,
|
|
Irp,
|
|
FileObject,
|
|
FALSE,
|
|
PreviousMode,
|
|
!LocalEvent,
|
|
IopOtherTransfer);
|
|
|
|
/* Check if this was async I/O */
|
|
if (LocalEvent)
|
|
{
|
|
/* It was, finalize this request */
|
|
Status = IopFinalizeAsynchronousIo(Status,
|
|
Event,
|
|
Irp,
|
|
PreviousMode,
|
|
&KernelIosb,
|
|
IoStatusBlock);
|
|
}
|
|
|
|
if (TargetDeviceObject && NT_SUCCESS(Status))
|
|
{
|
|
/* Time to report change */
|
|
NotificationStructure.Version = 1;
|
|
NotificationStructure.Size = sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION);
|
|
NotificationStructure.Event = GUID_IO_VOLUME_NAME_CHANGE;
|
|
NotificationStructure.FileObject = NULL;
|
|
NotificationStructure.NameBufferOffset = - 1;
|
|
Status = IoReportTargetDeviceChange(TargetDeviceObject, &NotificationStructure);
|
|
}
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtCancelDeviceWakeupRequest(IN HANDLE DeviceHandle)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
NTSTATUS
|
|
NTAPI
|
|
NtRequestDeviceWakeup(IN HANDLE DeviceHandle)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|