854 lines
27 KiB
C
854 lines
27 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
lock.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the code to implement the NtLockFile and the
|
|||
|
NtUnlockFile system services for the NT I/O system.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Darryl E. Havens (darrylh) 29-Nov-1989
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "iomgr.h"
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, NtLockFile)
|
|||
|
#pragma alloc_text(PAGE, NtUnlockFile)
|
|||
|
#endif
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtLockFile(
|
|||
|
IN HANDLE FileHandle,
|
|||
|
IN HANDLE Event 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
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service locks a specified range of bytes on the file specified by
|
|||
|
the FileHandle parameter. The lock may either be an exclusive lock or
|
|||
|
a shared lock. Furthermore, the caller has the option of specifying
|
|||
|
whether or not the service should return immediately if the lock cannot
|
|||
|
be acquired without waiting.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FileHandle - Supplies a handle to an open file.
|
|||
|
|
|||
|
Event - Supplies an optional event to be set to the Signaled state when
|
|||
|
the operation is complete.
|
|||
|
|
|||
|
ApcRoutine - Supplies an optional APC routine to be executed when the
|
|||
|
operation is complete.
|
|||
|
|
|||
|
ApcContext - Supplies a context parameter to be passed to the ApcRoutine,
|
|||
|
if an ApcRoutine was specified.
|
|||
|
|
|||
|
IoStatusBlock - Address of the caller's I/O status block.
|
|||
|
|
|||
|
ByteOffset - Specifies the starting byte offset of the range to lock.
|
|||
|
|
|||
|
Length - Specifies the length of the byte range to be locked.
|
|||
|
|
|||
|
Key - Specifies the key to be associated with the lock.
|
|||
|
|
|||
|
FailImmediately - Specifies that if the lock cannot immediately be
|
|||
|
acquired that the service should return to the caller.
|
|||
|
|
|||
|
ExclusiveLock - Specifies, if TRUE, that the lock should be an exclusive
|
|||
|
lock; otherwise the lock is a shared lock.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The status returned is success if the operation was properly queued to
|
|||
|
the I/O system. Once the operation completes, the status can be
|
|||
|
determined by examining the Status field of the I/O status block.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP irp;
|
|||
|
NTSTATUS status;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PFAST_IO_DISPATCH fastIoDispatch;
|
|||
|
PKEVENT eventObject = (PKEVENT) NULL;
|
|||
|
KPROCESSOR_MODE requestorMode;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
LARGE_INTEGER fileOffset;
|
|||
|
LARGE_INTEGER length;
|
|||
|
ACCESS_MASK grantedAccess;
|
|||
|
OBJECT_HANDLE_INFORMATION handleInformation;
|
|||
|
BOOLEAN synchronousIo;
|
|||
|
PETHREAD CurrentThread;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Get the previous mode; i.e., the mode of the caller.
|
|||
|
//
|
|||
|
|
|||
|
CurrentThread = PsGetCurrentThread ();
|
|||
|
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
|
|||
|
|
|||
|
//
|
|||
|
// Reference the file object so the target device can be found and the
|
|||
|
// access rights mask can be used in the following checks for callers
|
|||
|
// in user mode. Note that if the handle does not refer to a file
|
|||
|
// object, then it will fail.
|
|||
|
//
|
|||
|
|
|||
|
status = ObReferenceObjectByHandle( FileHandle,
|
|||
|
0L,
|
|||
|
IoFileObjectType,
|
|||
|
requestorMode,
|
|||
|
(PVOID *) &fileObject,
|
|||
|
&handleInformation);
|
|||
|
if (!NT_SUCCESS( status )) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
grantedAccess = handleInformation.GrantedAccess;
|
|||
|
|
|||
|
if (requestorMode != KernelMode) {
|
|||
|
|
|||
|
//
|
|||
|
// The caller's access mode is not kernel so probe each of the arguments
|
|||
|
// and capture them as necessary. If any failures occur, the condition
|
|||
|
// handler will be invoked to handle them. It will simply cleanup and
|
|||
|
// return an access violation status code back to the system service
|
|||
|
// dispatcher.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Check to ensure that the caller has either READ or WRITE access to
|
|||
|
// the file. If not, cleanup and return an error.
|
|||
|
//
|
|||
|
|
|||
|
if (!SeComputeGrantedAccesses( grantedAccess, FILE_READ_DATA | FILE_WRITE_DATA )) {
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return STATUS_ACCESS_DENIED;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// The IoStatusBlock parameter must be writeable by the caller.
|
|||
|
//
|
|||
|
|
|||
|
ProbeForWriteIoStatusEx( IoStatusBlock , ApcRoutine);
|
|||
|
|
|||
|
//
|
|||
|
// The ByteOffset parameter must be readable by the caller. Probe
|
|||
|
// and capture it.
|
|||
|
//
|
|||
|
|
|||
|
ProbeForReadSmallStructure( ByteOffset,
|
|||
|
sizeof( LARGE_INTEGER ),
|
|||
|
sizeof( ULONG ) );
|
|||
|
fileOffset = *ByteOffset;
|
|||
|
|
|||
|
//
|
|||
|
// Likewise, the Length parameter must also be readable by the
|
|||
|
// caller. Probe and capture it as well.
|
|||
|
//
|
|||
|
|
|||
|
ProbeForReadSmallStructure( Length,
|
|||
|
sizeof( LARGE_INTEGER ),
|
|||
|
sizeof( ULONG ) );
|
|||
|
length = *Length;
|
|||
|
|
|||
|
//
|
|||
|
// If this file has an I/O completion port associated w/it, then
|
|||
|
// ensure that the caller did not supply an APC routine, as the
|
|||
|
// two are mutually exclusive methods for I/O completion
|
|||
|
// notification.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->CompletionContext && IopApcRoutinePresent( ApcRoutine )) {
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
|
|||
|
//
|
|||
|
// An exception was incurred attempting to probe the caller's
|
|||
|
// parameters. Dereference the file object and return an
|
|||
|
// appropriate error status code.
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return GetExceptionCode();
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The caller's mode was kernel. Get the ByteOffset and Length
|
|||
|
// parameter 's to the expected locations.
|
|||
|
//
|
|||
|
|
|||
|
fileOffset = *ByteOffset;
|
|||
|
length = *Length;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the address of the event object and set the event to the Not-
|
|||
|
// Signaled state, if an event was specified. Note here, too, that if
|
|||
|
// the handle does not refer to an event, or if the event cannot be
|
|||
|
// written, then the reference will fail. Since certain legacy
|
|||
|
// applications rely on an old bug in Win32's LockFileEx, we must
|
|||
|
// tolerate bad event handles.
|
|||
|
//
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT( Event )) {
|
|||
|
status = ObReferenceObjectByHandle( Event,
|
|||
|
EVENT_MODIFY_STATE,
|
|||
|
ExEventObjectType,
|
|||
|
requestorMode,
|
|||
|
(PVOID *) &eventObject,
|
|||
|
NULL );
|
|||
|
if (!NT_SUCCESS( status )) {
|
|||
|
ASSERT( !eventObject );
|
|||
|
} else {
|
|||
|
KeClearEvent( eventObject );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the address of the target device object and the fast Io dispatch
|
|||
|
// structure.
|
|||
|
//
|
|||
|
|
|||
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|||
|
fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;
|
|||
|
|
|||
|
//
|
|||
|
// Turbo lock support. If the fast Io Dispatch specifies a fast lock
|
|||
|
// routine then we'll first try and calling it with the specified lock
|
|||
|
// parameters.
|
|||
|
//
|
|||
|
|
|||
|
if (fastIoDispatch && fastIoDispatch->FastIoLock) {
|
|||
|
|
|||
|
IO_STATUS_BLOCK localIoStatus;
|
|||
|
|
|||
|
if (fastIoDispatch->FastIoLock( fileObject,
|
|||
|
&fileOffset,
|
|||
|
&length,
|
|||
|
PsGetCurrentProcessByThread(CurrentThread),
|
|||
|
Key,
|
|||
|
FailImmediately,
|
|||
|
ExclusiveLock,
|
|||
|
&localIoStatus,
|
|||
|
deviceObject )) {
|
|||
|
|
|||
|
//
|
|||
|
// Carefully return the I/O status.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
#if defined(_WIN64)
|
|||
|
//
|
|||
|
// If this is a32-bit thread, and the IO request is
|
|||
|
// asynchronous, then the IOSB is 32-bit. Wow64 always sends
|
|||
|
// the 32-bit IOSB when the I/O is asynchronous.
|
|||
|
//
|
|||
|
if (IopIsIosb32(ApcRoutine)) {
|
|||
|
PIO_STATUS_BLOCK32 UserIosb32 = (PIO_STATUS_BLOCK32)IoStatusBlock;
|
|||
|
|
|||
|
UserIosb32->Information = (ULONG)localIoStatus.Information;
|
|||
|
UserIosb32->Status = (NTSTATUS)localIoStatus.Status;
|
|||
|
} else {
|
|||
|
*IoStatusBlock = localIoStatus;
|
|||
|
}
|
|||
|
#else
|
|||
|
*IoStatusBlock = localIoStatus;
|
|||
|
#endif
|
|||
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
localIoStatus.Status = GetExceptionCode();
|
|||
|
localIoStatus.Information = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a valid event was specified, set it.
|
|||
|
//
|
|||
|
|
|||
|
if (eventObject) {
|
|||
|
KeSetEvent( eventObject, 0, FALSE );
|
|||
|
ObDereferenceObject( eventObject );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Note that the file object event need not be set to the
|
|||
|
// Signaled state, as it is already set.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// If this file object has a completion port associated with it
|
|||
|
// and this request has a non-NULL APC context then a completion
|
|||
|
// message needs to be queued.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->CompletionContext && ARGUMENT_PRESENT( ApcContext )) {
|
|||
|
if (!NT_SUCCESS(IoSetIoCompletion( fileObject->CompletionContext->Port,
|
|||
|
fileObject->CompletionContext->Key,
|
|||
|
ApcContext,
|
|||
|
localIoStatus.Status,
|
|||
|
localIoStatus.Information,
|
|||
|
TRUE ))) {
|
|||
|
localIoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Cleanup and return.
|
|||
|
//
|
|||
|
|
|||
|
fileObject->LockOperation = TRUE;
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return localIoStatus.Status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make a special check here to determine whether this is a synchronous
|
|||
|
// I/O operation. If it is, then wait here until the file is owned by
|
|||
|
// the current thread.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|||
|
|
|||
|
BOOLEAN interrupted;
|
|||
|
|
|||
|
if (!IopAcquireFastLock( fileObject )) {
|
|||
|
status = IopAcquireFileObjectLock( fileObject,
|
|||
|
requestorMode,
|
|||
|
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
|
|||
|
&interrupted );
|
|||
|
if (interrupted) {
|
|||
|
if (eventObject) {
|
|||
|
ObDereferenceObject( eventObject );
|
|||
|
}
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return status;
|
|||
|
}
|
|||
|
}
|
|||
|
synchronousIo = TRUE;
|
|||
|
} else {
|
|||
|
synchronousIo = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the file object to the Not-Signaled state and mark it as having had
|
|||
|
// a lock operation performed on it.
|
|||
|
//
|
|||
|
|
|||
|
KeClearEvent( &fileObject->Event );
|
|||
|
fileObject->LockOperation = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate and initialize the I/O Request Packet (IRP) for this operation.
|
|||
|
// The allocation is performed with an exception handler in case the
|
|||
|
// caller does not have enough quota to allocate the packet.
|
|||
|
|
|||
|
irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
|
|||
|
if (!irp) {
|
|||
|
|
|||
|
//
|
|||
|
// An IRP could not be allocated. Cleanup and return an appropriate
|
|||
|
// error status code.
|
|||
|
//
|
|||
|
|
|||
|
IopAllocateIrpCleanup( fileObject, eventObject );
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|||
|
irp->Tail.Overlay.Thread = CurrentThread;
|
|||
|
irp->RequestorMode = requestorMode;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the service independent parameters in the IRP.
|
|||
|
//
|
|||
|
|
|||
|
irp->UserEvent = eventObject;
|
|||
|
irp->UserIosb = IoStatusBlock;
|
|||
|
irp->Overlay.AsynchronousParameters.UserApcRoutine = ApcRoutine;
|
|||
|
irp->Overlay.AsynchronousParameters.UserApcContext = ApcContext;
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the stack location for the first driver. This will be
|
|||
|
// used to pass the original function codes and parameters.
|
|||
|
//
|
|||
|
|
|||
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|||
|
irpSp->MajorFunction = IRP_MJ_LOCK_CONTROL;
|
|||
|
irpSp->MinorFunction = IRP_MN_LOCK;
|
|||
|
irpSp->FileObject = fileObject;
|
|||
|
|
|||
|
//
|
|||
|
// Copy the caller's parameters to the service-specific portion of the
|
|||
|
// IRP.
|
|||
|
//
|
|||
|
|
|||
|
irpSp->Flags = 0;
|
|||
|
if (FailImmediately) {
|
|||
|
irpSp->Flags = SL_FAIL_IMMEDIATELY;
|
|||
|
}
|
|||
|
if (ExclusiveLock) {
|
|||
|
irpSp->Flags |= SL_EXCLUSIVE_LOCK;
|
|||
|
}
|
|||
|
irpSp->Parameters.LockControl.Key = Key;
|
|||
|
irpSp->Parameters.LockControl.ByteOffset = fileOffset;
|
|||
|
|
|||
|
try {
|
|||
|
PLARGE_INTEGER lengthBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to allocate an intermediary buffer to hold the length of
|
|||
|
// this lock operation. If it fails, either because there is no
|
|||
|
// more quota, or because there are no more resources, then the
|
|||
|
// exception handler will be invoked to cleanup and exit.
|
|||
|
//
|
|||
|
|
|||
|
lengthBuffer = ExAllocatePoolWithQuota( NonPagedPool,
|
|||
|
sizeof( LARGE_INTEGER ) );
|
|||
|
|
|||
|
*lengthBuffer = length;
|
|||
|
irp->Tail.Overlay.AuxiliaryBuffer = (PCHAR) lengthBuffer;
|
|||
|
irpSp->Parameters.LockControl.Length = lengthBuffer;
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
|
|||
|
//
|
|||
|
// An exception was incurred. Simply clean everything up and
|
|||
|
// return an appropriate error status code.
|
|||
|
//
|
|||
|
|
|||
|
IopExceptionCleanup( fileObject,
|
|||
|
irp,
|
|||
|
eventObject,
|
|||
|
(PKEVENT) NULL );
|
|||
|
|
|||
|
return GetExceptionCode();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Queue the packet, call the driver, and synchronize appopriately with
|
|||
|
// I/O completion.
|
|||
|
//
|
|||
|
|
|||
|
return IopSynchronousServiceTail( deviceObject,
|
|||
|
irp,
|
|||
|
fileObject,
|
|||
|
FALSE,
|
|||
|
requestorMode,
|
|||
|
synchronousIo,
|
|||
|
OtherTransfer );
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
NtUnlockFile(
|
|||
|
IN HANDLE FileHandle,
|
|||
|
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
|||
|
IN PLARGE_INTEGER ByteOffset,
|
|||
|
IN PLARGE_INTEGER Length,
|
|||
|
IN ULONG Key
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This service releases the lock associated with the specified byte range
|
|||
|
for the file specified by the FileHandle parameter.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
FileHandle - Supplies a handle to an open file.
|
|||
|
|
|||
|
IoStatusBlock - Address of the caller's I/O status block.
|
|||
|
|
|||
|
ByteOffset - Specifies the byte offset of the range to unlock.
|
|||
|
|
|||
|
Length - Specifies the length of the byte range to unlock.
|
|||
|
|
|||
|
Key - Specifies the key associated with the locked range.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The status returned is the final completion status of the operation.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP irp;
|
|||
|
NTSTATUS status;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PFAST_IO_DISPATCH fastIoDispatch;
|
|||
|
PKEVENT event;
|
|||
|
KPROCESSOR_MODE requestorMode;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
IO_STATUS_BLOCK localIoStatus;
|
|||
|
LARGE_INTEGER fileOffset;
|
|||
|
LARGE_INTEGER length;
|
|||
|
ACCESS_MASK grantedAccess;
|
|||
|
OBJECT_HANDLE_INFORMATION handleInformation;
|
|||
|
BOOLEAN synchronousIo;
|
|||
|
PETHREAD CurrentThread;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Get the previous mode; i.e., the mode of the caller.
|
|||
|
//
|
|||
|
|
|||
|
CurrentThread = PsGetCurrentThread ();
|
|||
|
requestorMode = KeGetPreviousModeByThread(&CurrentThread->Tcb);
|
|||
|
|
|||
|
//
|
|||
|
// Reference the file object so the target device can be found and the
|
|||
|
// access rights mask can be used in the following checks for callers
|
|||
|
// in user mode. Note that if the handle does not refer to a file
|
|||
|
// object, then it will fail.
|
|||
|
//
|
|||
|
|
|||
|
status = ObReferenceObjectByHandle( FileHandle,
|
|||
|
0L,
|
|||
|
IoFileObjectType,
|
|||
|
requestorMode,
|
|||
|
(PVOID *) &fileObject,
|
|||
|
&handleInformation);
|
|||
|
if (!NT_SUCCESS( status )) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
grantedAccess = handleInformation.GrantedAccess;
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if the requestor mode was user. If so, perform a bunch
|
|||
|
// of extra checks.
|
|||
|
//
|
|||
|
|
|||
|
if (requestorMode != KernelMode) {
|
|||
|
|
|||
|
//
|
|||
|
// The caller's access mode is not kernel so probe each of the arguments
|
|||
|
// and capture them as necessary. If any failures occur, the condition
|
|||
|
// handler will be invoked to handle them. It will simply cleanup and
|
|||
|
// return an access violation status code back to the system service
|
|||
|
// dispatcher.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Check to ensure that the caller has either READ or WRITE access
|
|||
|
// to the file. If not, cleanup and return an error.
|
|||
|
//
|
|||
|
|
|||
|
if (!SeComputeGrantedAccesses( grantedAccess, FILE_READ_DATA | FILE_WRITE_DATA )) {
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return STATUS_ACCESS_DENIED;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// The IoStatusBlock parameter must be writeable by the caller.
|
|||
|
//
|
|||
|
|
|||
|
ProbeForWriteIoStatus( IoStatusBlock );
|
|||
|
|
|||
|
//
|
|||
|
// The ByteOffset parameter must be readable by the caller. Probe
|
|||
|
// and capture it.
|
|||
|
//
|
|||
|
|
|||
|
ProbeForReadSmallStructure( ByteOffset,
|
|||
|
sizeof( LARGE_INTEGER ),
|
|||
|
sizeof( ULONG ) );
|
|||
|
fileOffset = *ByteOffset;
|
|||
|
|
|||
|
//
|
|||
|
// Likewise, the Length parameter must also be readable by the
|
|||
|
// caller. Probe and capture it as well.
|
|||
|
//
|
|||
|
|
|||
|
ProbeForReadSmallStructure( Length,
|
|||
|
sizeof( LARGE_INTEGER ),
|
|||
|
sizeof( ULONG ) );
|
|||
|
length = *Length;
|
|||
|
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
|
|||
|
//
|
|||
|
// An exception was incurred while attempting to probe the
|
|||
|
// caller's parameters. Dereference the file object and return
|
|||
|
// an appropriate error status code.
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return GetExceptionCode();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The caller's mode was kernel. Get the ByteOffset and Length
|
|||
|
// parameter 's to the expected locations.
|
|||
|
//
|
|||
|
|
|||
|
fileOffset = *ByteOffset;
|
|||
|
length = *Length;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get the address of the target device object. If this file represents
|
|||
|
// a device that was opened directly, then simply use the device or its
|
|||
|
// attached device(s) directly. Also get the fast I/O dispatch address.
|
|||
|
//
|
|||
|
|
|||
|
if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
|
|||
|
deviceObject = IoGetRelatedDeviceObject( fileObject );
|
|||
|
} else {
|
|||
|
deviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
|
|||
|
}
|
|||
|
fastIoDispatch = deviceObject->DriverObject->FastIoDispatch;
|
|||
|
|
|||
|
//
|
|||
|
// Turbo lock support. If the fast Io Dispatch specifies a fast lock
|
|||
|
// routine then we'll first try and calling it with the specified lock
|
|||
|
// parameters.
|
|||
|
//
|
|||
|
|
|||
|
if (fastIoDispatch && fastIoDispatch->FastIoUnlockSingle) {
|
|||
|
|
|||
|
IO_STATUS_BLOCK localIoStatus;
|
|||
|
|
|||
|
if (fastIoDispatch->FastIoUnlockSingle( fileObject,
|
|||
|
&fileOffset,
|
|||
|
&length,
|
|||
|
PsGetCurrentProcessByThread(CurrentThread),
|
|||
|
Key,
|
|||
|
&localIoStatus,
|
|||
|
deviceObject )) {
|
|||
|
|
|||
|
//
|
|||
|
// Carefully return the I/O status.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
*IoStatusBlock = localIoStatus;
|
|||
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
localIoStatus.Status = GetExceptionCode();
|
|||
|
localIoStatus.Information = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Cleanup and return.
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return localIoStatus.Status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make a special check here to determine whether this is a synchronous
|
|||
|
// I/O operation. If it is, then wait here until the file is owned by
|
|||
|
// the current thread. If this is not a (serialized) synchronous I/O
|
|||
|
// operation, then allocate and initialize the local event.
|
|||
|
//
|
|||
|
|
|||
|
if (fileObject->Flags & FO_SYNCHRONOUS_IO) {
|
|||
|
|
|||
|
BOOLEAN interrupted;
|
|||
|
|
|||
|
if (!IopAcquireFastLock( fileObject )) {
|
|||
|
status = IopAcquireFileObjectLock( fileObject,
|
|||
|
requestorMode,
|
|||
|
(BOOLEAN) ((fileObject->Flags & FO_ALERTABLE_IO) != 0),
|
|||
|
&interrupted );
|
|||
|
if (interrupted) {
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return status;
|
|||
|
}
|
|||
|
}
|
|||
|
synchronousIo = TRUE;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// This is a synchronous API being invoked for a file that is opened
|
|||
|
// for asynchronous I/O. This means that this system service is
|
|||
|
// to synchronize the completion of the operation before returning
|
|||
|
// to the caller. A local event is used to do this.
|
|||
|
//
|
|||
|
|
|||
|
event = ExAllocatePool( NonPagedPool, sizeof( KEVENT ) );
|
|||
|
if (event == NULL) {
|
|||
|
ObDereferenceObject( fileObject );
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
KeInitializeEvent( event, SynchronizationEvent, FALSE );
|
|||
|
synchronousIo = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the file object to the Not-Signaled state.
|
|||
|
//
|
|||
|
|
|||
|
KeClearEvent( &fileObject->Event );
|
|||
|
|
|||
|
//
|
|||
|
// Allocate and initialize the I/O Request Packet (IRP) for this operation.
|
|||
|
// The allocation is performed with an exception handler in case the
|
|||
|
// caller does not have enough quota to allocate the packet.
|
|||
|
//
|
|||
|
|
|||
|
irp = IoAllocateIrp( deviceObject->StackSize, TRUE );
|
|||
|
if (!irp) {
|
|||
|
|
|||
|
//
|
|||
|
// An IRP could not be allocated. Cleanup and return an appropriate
|
|||
|
// error status code.
|
|||
|
//
|
|||
|
|
|||
|
if (!(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
|
|||
|
ExFreePool( event );
|
|||
|
}
|
|||
|
|
|||
|
IopAllocateIrpCleanup( fileObject, (PKEVENT) NULL );
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
|||
|
irp->Tail.Overlay.Thread = CurrentThread;
|
|||
|
irp->RequestorMode = requestorMode;
|
|||
|
|
|||
|
//
|
|||
|
// Fill in the service independent parameters in the IRP.
|
|||
|
//
|
|||
|
|
|||
|
if (synchronousIo) {
|
|||
|
irp->UserEvent = (PKEVENT) NULL;
|
|||
|
irp->UserIosb = IoStatusBlock;
|
|||
|
} else {
|
|||
|
irp->UserEvent = event;
|
|||
|
irp->UserIosb = &localIoStatus;
|
|||
|
irp->Flags = IRP_SYNCHRONOUS_API;
|
|||
|
}
|
|||
|
irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Get a pointer to the stack location for the first driver. This will
|
|||
|
// be used to pass the original function codes and parameters.
|
|||
|
//
|
|||
|
|
|||
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|||
|
irpSp->MajorFunction = IRP_MJ_LOCK_CONTROL;
|
|||
|
irpSp->MinorFunction = IRP_MN_UNLOCK_SINGLE;
|
|||
|
irpSp->FileObject = fileObject;
|
|||
|
|
|||
|
try {
|
|||
|
PLARGE_INTEGER lengthBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to allocate an intermediary buffer to hold the length of
|
|||
|
// this lock operation. If it fails, either because there is no
|
|||
|
// more quota, or because there are no more resources, then the
|
|||
|
// exception handler will be invoked to cleanup and exit.
|
|||
|
//
|
|||
|
|
|||
|
lengthBuffer = ExAllocatePoolWithQuota( NonPagedPool,
|
|||
|
sizeof( LARGE_INTEGER ) );
|
|||
|
|
|||
|
*lengthBuffer = length;
|
|||
|
irp->Tail.Overlay.AuxiliaryBuffer = (PCHAR) lengthBuffer;
|
|||
|
irpSp->Parameters.LockControl.Length = lengthBuffer;
|
|||
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|||
|
|
|||
|
//
|
|||
|
// An exception was incurred. Simply clean everything up and
|
|||
|
// return an appropriate error status code.
|
|||
|
//
|
|||
|
|
|||
|
if (!(fileObject->Flags & FO_SYNCHRONOUS_IO)) {
|
|||
|
ExFreePool( event );
|
|||
|
}
|
|||
|
|
|||
|
IopExceptionCleanup( fileObject,
|
|||
|
irp,
|
|||
|
NULL,
|
|||
|
(PKEVENT) NULL );
|
|||
|
|
|||
|
return GetExceptionCode();
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Copy the caller's parameters to the service-specific portion of the
|
|||
|
// IRP.
|
|||
|
//
|
|||
|
|
|||
|
irpSp->Parameters.LockControl.Key = Key;
|
|||
|
irpSp->Parameters.LockControl.ByteOffset = fileOffset;
|
|||
|
|
|||
|
//
|
|||
|
// Queue the packet, call the driver, and synchronize appopriately with
|
|||
|
// I/O completion.
|
|||
|
//
|
|||
|
|
|||
|
status = IopSynchronousServiceTail( deviceObject,
|
|||
|
irp,
|
|||
|
fileObject,
|
|||
|
FALSE,
|
|||
|
requestorMode,
|
|||
|
synchronousIo,
|
|||
|
OtherTransfer );
|
|||
|
|
|||
|
//
|
|||
|
// If the file for this operation was not opened for synchronous I/O, then
|
|||
|
// synchronization of completion of the I/O operation has not yet occurred
|
|||
|
// since the allocated event must be used for synchronous APIs on files
|
|||
|
// opened for asynchronous I/O. Synchronize the completion of the I/O
|
|||
|
// operation now.
|
|||
|
//
|
|||
|
|
|||
|
if (!synchronousIo) {
|
|||
|
|
|||
|
status = IopSynchronousApiServiceTail( status,
|
|||
|
event,
|
|||
|
irp,
|
|||
|
requestorMode,
|
|||
|
&localIoStatus,
|
|||
|
IoStatusBlock );
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|