|
|
/*++
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; }
|