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;
|
||
}
|