1327 lines
31 KiB
C
1327 lines
31 KiB
C
/*++
|
||
|
||
Copyright (c) 1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Lock.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Lock routine for the NetWare redirector.
|
||
|
||
Notes on the implementation of locks.
|
||
|
||
o Netware servers handle lock conflicts differently than a LAN Man
|
||
server, or NT file system would. In particular:
|
||
|
||
- A lock conflict on a single file handle (i.e. the same app owns
|
||
the lock, and is trying to obtain a conflicting lock): The
|
||
netware server will fail the request only if the lock range is
|
||
identical to a held lock. Also, the lock fails immediately, even
|
||
if the app requested a blocking lock.
|
||
|
||
- A lock conflict generated by 2 app from the same workstation:
|
||
The server will fail the request if the request lock overlaps an
|
||
existing lock by even a single byte, but the server will fail the
|
||
request immediately, even if the app requested a blocking lock.
|
||
|
||
- A lock conflict generated by 2 different workstations: This works
|
||
as expected. The lock fails if it overlaps an existing lock, and
|
||
the request blocks if requested by the app.
|
||
|
||
o The NT workstation needs to impose NT file system behaviour when dealing
|
||
with a netware server. There are 2 key elements (complications)
|
||
added to the redirector to handle this.
|
||
|
||
- A locally maintained lock database. This is used to test for
|
||
lock conflicts locally. If a conflict is detected and the
|
||
requestor asks for a blocking lock, the lock request is queued
|
||
to a local lock conflict list. This list is processed when real
|
||
locks are released.
|
||
|
||
- A pending lock list. This is used to poll the netware server
|
||
about remote lock conflicts. We could not let our lock request
|
||
block indefinitely as this would tie up our one channel of
|
||
communication to the server.
|
||
|
||
o The data structures
|
||
|
||
- NonPagedFcb
|
||
-> FileLockList - The list of existing locks.
|
||
-> PendingLockList - The list of locks pending due to a
|
||
local conflict.
|
||
|
||
- NwPendingLockList
|
||
The list of locks pending due to a remote conflict. The
|
||
locks are retried indefinitely using a polling mechanism.
|
||
|
||
A request can be removed from the pending list via (1) a
|
||
cleanup for the correct ICB (2) the IRP can be cancelled.
|
||
(3) The server actually grants the lock.
|
||
|
||
o Other notes:
|
||
|
||
We play some games to allow us to use the FCB resource as the
|
||
synchronization mechanism, even though much processing happens
|
||
at raised IRQL. Be careful not to break this.
|
||
|
||
Author:
|
||
|
||
Colin Watson [ColinW] 13-May-1993
|
||
Manny Weiser [MannyW] 16-May-1993
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "Procs.h"
|
||
|
||
|
||
//
|
||
// The debug trace level
|
||
//
|
||
|
||
#define Dbg (DEBUG_TRACE_LOCKCTRL)
|
||
|
||
NTSTATUS
|
||
NwCommonLock(
|
||
PIRP_CONTEXT pIrpContext
|
||
);
|
||
|
||
NTSTATUS
|
||
LockNcp(
|
||
PIRP_CONTEXT IrpContext,
|
||
PICB Icb
|
||
);
|
||
|
||
NTSTATUS
|
||
LockNcpCallback (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN ULONG BytesAvailable,
|
||
IN PUCHAR Response
|
||
);
|
||
|
||
NTSTATUS
|
||
UnlockNcpCallback (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN ULONG BytesAvailable,
|
||
IN PUCHAR Response
|
||
);
|
||
|
||
BOOLEAN
|
||
LockIsOverlapping(
|
||
PNONPAGED_FCB pNpFcb,
|
||
LONG StartFileOffset,
|
||
ULONG Length
|
||
);
|
||
|
||
VOID
|
||
AddLockToFcb(
|
||
PNONPAGED_FCB pNpFcb,
|
||
PNW_FILE_LOCK FileLock
|
||
);
|
||
|
||
VOID
|
||
RemoveLockFromFcb(
|
||
PNONPAGED_FCB pNpFcb,
|
||
PNW_FILE_LOCK FileLock
|
||
);
|
||
|
||
VOID
|
||
ReattemptPendingLocks(
|
||
PNONPAGED_FCB pNpFcb
|
||
);
|
||
|
||
BOOLEAN
|
||
LockExists(
|
||
PNONPAGED_FCB pNpFcb,
|
||
LONG StartOffset,
|
||
ULONG Length,
|
||
PNW_FILE_LOCK *FileLock
|
||
);
|
||
|
||
NTSTATUS
|
||
UnlockIcbLocks(
|
||
PIRP_CONTEXT pIrpContext
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, NwFsdLockControl )
|
||
#pragma alloc_text( PAGE, NwCommonLock )
|
||
#pragma alloc_text( PAGE, LockNcp )
|
||
#pragma alloc_text( PAGE, LockIsOverlapping )
|
||
#pragma alloc_text( PAGE, NwFreeLocksForIcb )
|
||
#pragma alloc_text( PAGE, UnlockIcbLocks )
|
||
|
||
#ifndef QFE_BUILD
|
||
#pragma alloc_text( PAGE1, LockNcpCallback )
|
||
#pragma alloc_text( PAGE1, UnlockNcpCallback )
|
||
#pragma alloc_text( PAGE1, AddLockToFcb )
|
||
#pragma alloc_text( PAGE1, RemoveLockFromFcb )
|
||
#pragma alloc_text( PAGE1, ReattemptPendingLocks )
|
||
#pragma alloc_text( PAGE1, LockExists )
|
||
#endif
|
||
|
||
#endif
|
||
|
||
#if 0 // Not pageable
|
||
|
||
// see ifndef QFE_BUILD above
|
||
|
||
#endif
|
||
|
||
|
||
|
||
NTSTATUS
|
||
NwFsdLockControl (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the FSD part of the NtCreateFile and NtOpenFile
|
||
API calls.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies the device object for the redirector.
|
||
|
||
Irp - Supplies the Irp being processed
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The Fsd status for the Irp
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
PIRP_CONTEXT IrpContext = NULL;
|
||
BOOLEAN TopLevel;
|
||
|
||
PAGED_CODE();
|
||
|
||
TimerStart(Dbg);
|
||
DebugTrace(+1, Dbg, "NwFsdLockControl\n", 0);
|
||
|
||
//
|
||
// Call the common lock routine, with block allowed if the operation
|
||
// is synchronous.
|
||
//
|
||
|
||
FsRtlEnterFileSystem();
|
||
TopLevel = NwIsIrpTopLevel( Irp );
|
||
|
||
try {
|
||
|
||
IrpContext = AllocateIrpContext( Irp );
|
||
Status = NwCommonLock( IrpContext );
|
||
|
||
} except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
|
||
|
||
if ( IrpContext == NULL ) {
|
||
|
||
//
|
||
// If we couldn't allocate an irp context, just complete
|
||
// irp without any fanfare.
|
||
//
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
Irp->IoStatus.Status = Status;
|
||
Irp->IoStatus.Information = 0;
|
||
IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
|
||
|
||
} else {
|
||
|
||
//
|
||
// We had some trouble trying to perform the requested
|
||
// operation, so we'll abort the I/O request with
|
||
// the error Status that we get back from the
|
||
// execption code
|
||
//
|
||
|
||
Status = NwProcessException( IrpContext, GetExceptionCode() );
|
||
}
|
||
|
||
}
|
||
|
||
if ( IrpContext ) {
|
||
NwCompleteRequest( IrpContext, Status );
|
||
}
|
||
|
||
if ( TopLevel ) {
|
||
NwSetTopLevelIrp( NULL );
|
||
}
|
||
FsRtlExitFileSystem();
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "NwFsdLock -> %08lx\n", Status );
|
||
|
||
TimerStop(Dbg,"NwFsdLockControl");
|
||
|
||
return Status;
|
||
|
||
UNREFERENCED_PARAMETER(DeviceObject);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NwCommonLock (
|
||
IN PIRP_CONTEXT IrpContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the common code for NtLockFile/NtUnlockFile.
|
||
|
||
Arguments:
|
||
|
||
IrpContext - Supplies the request being processed.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
PIRP Irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
|
||
NODE_TYPE_CODE nodeTypeCode;
|
||
PICB icb;
|
||
PFCB fcb;
|
||
PVOID fsContext;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the current stack location
|
||
//
|
||
|
||
Irp = IrpContext->pOriginalIrp;
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
DebugTrace(+1, Dbg, "CommonLock...\n", 0);
|
||
DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG_PTR)Irp);
|
||
|
||
//
|
||
// Decode the file object to figure out who we are. If the result
|
||
// is not the root DCB then its an illegal parameter.
|
||
//
|
||
|
||
nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
|
||
&fsContext,
|
||
(PVOID *)&icb );
|
||
|
||
if (nodeTypeCode != NW_NTC_ICB) {
|
||
|
||
DebugTrace(0, Dbg, "Not a file\n", 0);
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
|
||
DebugTrace(-1, Dbg, "CommonLock -> %08lx\n", status );
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Make sure that this ICB is still active.
|
||
//
|
||
|
||
NwVerifyIcb( icb );
|
||
|
||
fcb = (PFCB)icb->SuperType.Fcb;
|
||
nodeTypeCode = fcb->NodeTypeCode;
|
||
|
||
if (nodeTypeCode == NW_NTC_FCB ) {
|
||
|
||
IrpContext->pScb = fcb->Scb;
|
||
IrpContext->pNpScb = IrpContext->pScb->pNpScb;
|
||
IrpContext->Icb = icb;
|
||
|
||
} else {
|
||
|
||
DebugTrace(0, Dbg, "Not a file\n", 0);
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
|
||
DebugTrace(-1, Dbg, "CommonLock -> %08lx\n", status );
|
||
return status;
|
||
}
|
||
|
||
switch (irpSp->MinorFunction) {
|
||
|
||
case IRP_MN_LOCK:
|
||
case IRP_MN_UNLOCK_SINGLE:
|
||
case IRP_MN_UNLOCK_ALL:
|
||
case IRP_MN_UNLOCK_ALL_BY_KEY:
|
||
status = LockNcp( IrpContext, icb );
|
||
break;
|
||
|
||
default:
|
||
//
|
||
// Minor function added to I/O system that this driver does
|
||
// not understand.
|
||
//
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "CommonLock -> %08lx\n", status);
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
LockNcp(
|
||
PIRP_CONTEXT IrpContext,
|
||
PICB Icb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine exchanges a series of Lock NCPs with the server.
|
||
|
||
Arguments:
|
||
|
||
IrpContext - A pointer to IRP context information for this request.
|
||
|
||
Icb - Supplies the file specific information.
|
||
|
||
Return Value:
|
||
|
||
Status of transfer.
|
||
|
||
--*/
|
||
{
|
||
PIRP irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
LARGE_INTEGER ByteOffset;
|
||
LARGE_INTEGER Length;
|
||
ULONG Key;
|
||
|
||
PSCB pScb;
|
||
PNONPAGED_FCB pNpFcb;
|
||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||
|
||
PNW_FILE_LOCK FileLock = NULL;
|
||
USHORT LockFlags = 3;
|
||
|
||
PAGED_CODE();
|
||
|
||
irp = IrpContext->pOriginalIrp;
|
||
irpSp = IoGetCurrentIrpStackLocation( irp );
|
||
|
||
ByteOffset = irpSp->Parameters.LockControl.ByteOffset;
|
||
|
||
if ( irpSp->Parameters.LockControl.Length != NULL ) {
|
||
Length = *irpSp->Parameters.LockControl.Length;
|
||
} else {
|
||
Length.HighPart = 0;
|
||
Length.LowPart = 0;
|
||
}
|
||
|
||
Key = irpSp->Parameters.LockControl.Key;
|
||
|
||
DebugTrace(+1, Dbg, "LockNcp...\n", 0);
|
||
DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG_PTR)irp);
|
||
DebugTrace( 0, Dbg, "MinorFun= %08lx\n", (ULONG)irpSp->MinorFunction);
|
||
DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
|
||
DebugTrace( 0, Dbg, "HOffset = %lx\n", ByteOffset.HighPart);
|
||
DebugTrace( 0, Dbg, "LOffset = %lx\n", ByteOffset.LowPart);
|
||
DebugTrace( 0, Dbg, "HLength = %lx\n", Length.HighPart);
|
||
DebugTrace( 0, Dbg, "LLength = %lx\n", Length.LowPart);
|
||
DebugTrace( 0, Dbg, "Key = %lx\n", Key);
|
||
|
||
pScb = Icb->SuperType.Fcb->Scb;
|
||
|
||
ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
|
||
|
||
pNpFcb = Icb->SuperType.Fcb->NonPagedFcb;
|
||
|
||
//
|
||
// Get to the front of the ScbQueue to protect access to the lock list.
|
||
//
|
||
|
||
NwAppendToQueueAndWait( IrpContext );
|
||
|
||
try {
|
||
|
||
switch ( irpSp->MinorFunction ) {
|
||
|
||
case IRP_MN_LOCK:
|
||
|
||
//
|
||
// Since we are doing a lock we will need to send an End Of Job
|
||
// for this PID.
|
||
//
|
||
|
||
NwSetEndOfJobRequired( pScb->pNpScb, Icb->Pid );
|
||
|
||
//
|
||
// Try to allocate a lock structure before we ask the
|
||
// server to perform the lock.
|
||
//
|
||
|
||
FileLock = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NW_FILE_LOCK ) );
|
||
IrpContext->Specific.Lock.FileLock = FileLock;
|
||
|
||
FileLock->NodeTypeCode = NW_NTC_FILE_LOCK;
|
||
FileLock->NodeByteSize = sizeof( NW_FILE_LOCK );
|
||
|
||
FileLock->StartFileOffset = ByteOffset.LowPart;
|
||
FileLock->Length = Length.LowPart;
|
||
FileLock->EndFileOffset = ByteOffset.LowPart + Length.LowPart - 1;
|
||
FileLock->Key = Key;
|
||
FileLock->Icb = Icb;
|
||
FileLock->IrpContext = IrpContext;
|
||
|
||
//
|
||
// Remember the originating process because NT locks
|
||
// have PROCESS level granularity!!
|
||
//
|
||
|
||
FileLock->pOwnerProc = PsGetCurrentProcessId();
|
||
|
||
if ( irpSp->Flags & SL_EXCLUSIVE_LOCK ) {
|
||
LockFlags = 0x00;
|
||
} else {
|
||
LockFlags = 0x02;
|
||
}
|
||
|
||
FileLock->Flags = LockFlags;
|
||
|
||
//
|
||
// Is this is an overlapping lock
|
||
//
|
||
|
||
if ( irpSp->Flags & SL_FAIL_IMMEDIATELY ) {
|
||
IrpContext->Specific.Lock.Wait = FALSE;
|
||
} else {
|
||
IrpContext->Specific.Lock.Wait = TRUE;
|
||
}
|
||
|
||
if ( LockIsOverlapping( pNpFcb, ByteOffset.LowPart, Length.LowPart ) ) {
|
||
|
||
if ( IrpContext->Specific.Lock.Wait ) {
|
||
|
||
//
|
||
// Queue this IRP context to the FCB. We'll process it
|
||
// when the local conflict is removed.
|
||
//
|
||
|
||
InsertTailList( &pNpFcb->PendingLockList, &FileLock->ListEntry );
|
||
status = STATUS_PENDING;
|
||
NwDequeueIrpContext( IrpContext, FALSE );
|
||
|
||
} else {
|
||
status = STATUS_FILE_LOCK_CONFLICT;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Send the lock request.
|
||
//
|
||
|
||
status = Exchange (
|
||
IrpContext,
|
||
LockNcpCallback,
|
||
"Fbrddw",
|
||
NCP_LOCK_RANGE,
|
||
LockFlags | 0x01,
|
||
Icb->Handle, sizeof( Icb->Handle ),
|
||
ByteOffset.LowPart,
|
||
Length.LowPart,
|
||
LockTimeoutThreshold );
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case IRP_MN_UNLOCK_SINGLE:
|
||
|
||
if ( !LockExists( pNpFcb, ByteOffset.LowPart, Length.LowPart, &FileLock ) ) {
|
||
|
||
status = STATUS_RANGE_NOT_LOCKED;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Verify that this lock belongs to this process.
|
||
//
|
||
|
||
if ( FileLock->pOwnerProc != PsGetCurrentProcessId() ) {
|
||
|
||
DebugTrace( 0, Dbg, "Unlock process not owner!\n", 0 );
|
||
status = STATUS_ACCESS_DENIED;
|
||
|
||
//
|
||
// Ensure that we don't free the filelock
|
||
//
|
||
|
||
FileLock = NULL;
|
||
|
||
} else {
|
||
|
||
IrpContext->Specific.Lock.FileLock = FileLock;
|
||
|
||
status = Exchange (
|
||
IrpContext,
|
||
UnlockNcpCallback,
|
||
"F-rddw",
|
||
NCP_UNLOCK_RANGE,
|
||
Icb->Handle, sizeof( Icb->Handle ),
|
||
ByteOffset.LowPart,
|
||
Length.LowPart,
|
||
1 );
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
case IRP_MN_UNLOCK_ALL:
|
||
IrpContext->Icb = Icb;
|
||
IrpContext->Specific.Lock.ByKey = FALSE ;
|
||
|
||
status = UnlockIcbLocks( IrpContext );
|
||
break;
|
||
|
||
case IRP_MN_UNLOCK_ALL_BY_KEY:
|
||
IrpContext->Icb = Icb;
|
||
IrpContext->Specific.Lock.Key = Key ;
|
||
IrpContext->Specific.Lock.ByKey = TRUE ;
|
||
|
||
status = UnlockIcbLocks( IrpContext );
|
||
break;
|
||
}
|
||
|
||
} finally {
|
||
if ( AbnormalTermination() || !NT_SUCCESS( status ) ) {
|
||
if ( FileLock != NULL ) {
|
||
FREE_POOL( FileLock );
|
||
}
|
||
|
||
NwDequeueIrpContext( IrpContext, FALSE );
|
||
}
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "LockNcb -> %08lx\n", status );
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
LockNcpCallback (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN ULONG BytesAvailable,
|
||
IN PUCHAR Response
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine receives the response from a user NCP.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PIRP Irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
|
||
DebugTrace(+1, Dbg, "LockNcpCallback...\n", 0);
|
||
|
||
if ( BytesAvailable == 0) {
|
||
|
||
//
|
||
// No response from server. Status is in pIrpContext->
|
||
// ResponseParameters.Error
|
||
//
|
||
|
||
FREE_POOL( IrpContext->Specific.Lock.FileLock );
|
||
|
||
NwDequeueIrpContext( IrpContext, FALSE );
|
||
NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
|
||
|
||
DebugTrace(-1, Dbg, "LockNcpCallback -> %08lx\n", STATUS_REMOTE_NOT_LISTENING);
|
||
return STATUS_REMOTE_NOT_LISTENING;
|
||
}
|
||
|
||
Irp = IrpContext->pOriginalIrp;
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
Status = ParseResponse( IrpContext, Response, BytesAvailable, "N" );
|
||
|
||
if (NT_SUCCESS(Status) ) {
|
||
|
||
DebugTrace(0, Dbg, "Lock successfully applied\n", 0);
|
||
|
||
//
|
||
// Record this lock in the Icb lock chain
|
||
//
|
||
|
||
AddLockToFcb(
|
||
IrpContext->Icb->NpFcb,
|
||
IrpContext->Specific.Lock.FileLock );
|
||
|
||
} else if ( Status == STATUS_FILE_LOCK_CONFLICT &&
|
||
IrpContext->Specific.Lock.Wait ) {
|
||
|
||
DebugTrace(0, Dbg, "Lock conflict, adding %08lx to Pending Lock list\n", IrpContext );
|
||
|
||
//
|
||
// The lock conflicts with an existing lock, but the app wants
|
||
// to wait. Queue the request to the pending lock list and
|
||
// return, pending.
|
||
//
|
||
|
||
NwDequeueIrpContext( IrpContext, FALSE );
|
||
IrpContext->Specific.Lock.Key = 5;
|
||
|
||
ExInterlockedInsertTailList(
|
||
&NwPendingLockList,
|
||
&IrpContext->NextRequest,
|
||
&NwPendingLockSpinLock );
|
||
|
||
Status = STATUS_PENDING;
|
||
|
||
DebugTrace(-1, Dbg, "LockNcpCallback -> %08lx\n", Status);
|
||
return( Status );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Status unsuccesful is returned when trying to lock 0 bytes.
|
||
// Map the error.
|
||
//
|
||
|
||
if ( Status == STATUS_UNSUCCESSFUL ) {
|
||
|
||
DebugTrace(-1, Dbg, "Locklength -> %08lx\n",IrpContext->Specific.Lock.FileLock->Length );
|
||
|
||
if (IrpContext->Specific.Lock.FileLock->Length == 0) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
}
|
||
|
||
FREE_POOL( IrpContext->Specific.Lock.FileLock );
|
||
}
|
||
|
||
//
|
||
// If any locks were pending due to a local lock conflict, try
|
||
// them now.
|
||
//
|
||
|
||
ReattemptPendingLocks(IrpContext->Icb->NpFcb);
|
||
|
||
//
|
||
// We're done with this request. Dequeue the IRP context from
|
||
// SCB and complete the request.
|
||
//
|
||
|
||
NwDequeueIrpContext( IrpContext, FALSE );
|
||
NwCompleteRequest( IrpContext, Status );
|
||
|
||
DebugTrace(-1, Dbg, "LockNcpCallback -> %08lx\n", Status);
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
UnlockNcpCallback (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN ULONG BytesAvailable,
|
||
IN PUCHAR Response
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine receives the response from a user NCP.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PIRP Irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
|
||
DebugTrace(0, Dbg, "UnlockNcpCallback...\n", 0);
|
||
|
||
//
|
||
// Remove this lock in the Fcb lock chain, regardlesss of the status
|
||
// of the IO.
|
||
//
|
||
|
||
RemoveLockFromFcb(
|
||
IrpContext->Icb->NpFcb,
|
||
IrpContext->Specific.Lock.FileLock );
|
||
|
||
FREE_POOL( IrpContext->Specific.Lock.FileLock );
|
||
|
||
//
|
||
// If any locks were pending due to a local lock conflict, try
|
||
// them now.
|
||
//
|
||
|
||
ReattemptPendingLocks(IrpContext->Icb->NpFcb);
|
||
|
||
if ( BytesAvailable == 0) {
|
||
|
||
//
|
||
// No response from server. Status is in pIrpContext->
|
||
// ResponseParameters.Error
|
||
//
|
||
|
||
NwDequeueIrpContext( IrpContext, FALSE );
|
||
NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
|
||
|
||
return STATUS_REMOTE_NOT_LISTENING;
|
||
}
|
||
|
||
Irp = IrpContext->pOriginalIrp;
|
||
irpSp = IoGetCurrentIrpStackLocation( Irp );
|
||
|
||
Status = ParseResponse( IrpContext, Response, BytesAvailable, "N" );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
Error(
|
||
EVENT_NWRDR_FAILED_UNLOCK,
|
||
Status,
|
||
NULL,
|
||
0,
|
||
1,
|
||
IrpContext->pNpScb->ServerName.Buffer );
|
||
}
|
||
|
||
//
|
||
// We're done with this request. Dequeue the IRP context from
|
||
// SCB and complete the request.
|
||
//
|
||
|
||
NwDequeueIrpContext( IrpContext, FALSE );
|
||
NwCompleteRequest( IrpContext, Status );
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
LockIsOverlapping(
|
||
PNONPAGED_FCB pNpFcb,
|
||
LONG StartFileOffset,
|
||
ULONG Length
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine tests to see if the requested lock would overlap an
|
||
existing lock.
|
||
|
||
*** This routine must be called at the front of the queue.
|
||
|
||
Arguments:
|
||
|
||
pNpFcb - The FCB of the file being locked.
|
||
|
||
StartFileOffset - The first byte in the range to lock.
|
||
|
||
Length - The number of bytes to lock.
|
||
|
||
Return Value:
|
||
|
||
TRUE - This lock overlaps an existing lock.
|
||
FALSE - This lock does not overlap an existing lock.
|
||
|
||
--*/
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PNW_FILE_LOCK pFileLock;
|
||
LONG EndFileOffset = StartFileOffset + Length - 1;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( Length == 0 ) {
|
||
return( FALSE );
|
||
}
|
||
|
||
for ( ListEntry = pNpFcb->FileLockList.Flink;
|
||
ListEntry != &pNpFcb->FileLockList;
|
||
ListEntry = ListEntry->Flink ) {
|
||
|
||
pFileLock = CONTAINING_RECORD( ListEntry, NW_FILE_LOCK, ListEntry );
|
||
|
||
//
|
||
// Stop the search if the current lock starts before the potential
|
||
// new lock ends.
|
||
//
|
||
|
||
if ( pFileLock->StartFileOffset > EndFileOffset ) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// The new lock overlaps if it starts of ends in the middle of
|
||
// an existing lock.
|
||
//
|
||
|
||
if (( StartFileOffset >= pFileLock->StartFileOffset &&
|
||
StartFileOffset <= pFileLock->EndFileOffset )
|
||
||
|
||
( EndFileOffset >= pFileLock->StartFileOffset &&
|
||
EndFileOffset <= pFileLock->EndFileOffset ) ) {
|
||
|
||
|
||
DebugTrace(0, Dbg, "Lock is overlapping\n", 0);
|
||
return( TRUE );
|
||
}
|
||
}
|
||
|
||
DebugTrace(0, Dbg, "Lock is NOT overlapping\n", 0);
|
||
return( FALSE );
|
||
}
|
||
|
||
VOID
|
||
AddLockToFcb(
|
||
PNONPAGED_FCB pNpFcb,
|
||
PNW_FILE_LOCK FileLock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine inserts a lock structure into the ordered list of locks
|
||
for this ICB.
|
||
|
||
*** This routine must be called when at the front of the ScbQueue.
|
||
|
||
Arguments:
|
||
|
||
NpFcb - The non paged FCB of file that is being locked.
|
||
|
||
FileLock - The file lock structure to insert.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PNW_FILE_LOCK pFileLock;
|
||
|
||
LONG StartFileOffset = FileLock->StartFileOffset;
|
||
LONG EndFileOffset = FileLock->EndFileOffset;
|
||
|
||
DebugTrace(0, Dbg, "Adding Lock to FCB %08lx\n", pNpFcb);
|
||
DebugTrace(0, Dbg, "Lock is %08lx\n", FileLock );
|
||
|
||
if ( IsListEmpty( &pNpFcb->FileLockList ) ) {
|
||
InsertHeadList( &pNpFcb->FileLockList, &FileLock->ListEntry );
|
||
return;
|
||
}
|
||
|
||
for ( ListEntry = pNpFcb->FileLockList.Flink;
|
||
ListEntry != &pNpFcb->FileLockList;
|
||
ListEntry = ListEntry->Flink ) {
|
||
|
||
pFileLock = CONTAINING_RECORD( ListEntry, NW_FILE_LOCK, ListEntry );
|
||
|
||
//
|
||
// Stop the search if the current lock starts after the
|
||
// new lock ends.
|
||
//
|
||
|
||
if ( pFileLock->StartFileOffset > EndFileOffset ) {
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Insert the file lock into the ordered list.
|
||
//
|
||
|
||
InsertTailList( ListEntry, &FileLock->ListEntry );
|
||
}
|
||
|
||
|
||
VOID
|
||
RemoveLockFromFcb(
|
||
PNONPAGED_FCB pNpFcb,
|
||
PNW_FILE_LOCK FileLock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes a lock structure from the ordered list of locks
|
||
for this FCB.
|
||
|
||
*** This routine must be called when at the front of the ScbQueue.
|
||
|
||
Arguments:
|
||
|
||
pNpFcb - The non paged FCB of file that is being unlocked.
|
||
|
||
FileLock - The file lock structure to remove.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
#if DBG
|
||
PNW_FILE_LOCK foundFileLock;
|
||
#endif
|
||
|
||
DebugTrace(0, Dbg, "Removing Lock from FCB %08lx\n", pNpFcb);
|
||
DebugTrace(0, Dbg, "Lock is %08lx\n", FileLock );
|
||
|
||
ASSERT( LockExists( pNpFcb, FileLock->StartFileOffset, FileLock->Length, &foundFileLock ) );
|
||
ASSERT( foundFileLock == FileLock );
|
||
|
||
RemoveEntryList( &FileLock->ListEntry );
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
ReattemptPendingLocks(
|
||
PNONPAGED_FCB pNpFcb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine reattempts locks that are pending due to a local lock
|
||
conflict.
|
||
|
||
*** This routine must be called when at the front of the ScbQueue.
|
||
|
||
Arguments:
|
||
|
||
pNpFcb - The non paged FCB of file that is being processed.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PLIST_ENTRY listEntry, nextListEntry;
|
||
PNW_FILE_LOCK fileLock;
|
||
NTSTATUS status;
|
||
|
||
DebugTrace(+1, Dbg, "ReattemptPendingLocks...\n", 0);
|
||
|
||
//
|
||
// Run the list of pending locks.
|
||
//
|
||
|
||
for ( listEntry = pNpFcb->PendingLockList.Flink;
|
||
listEntry != &pNpFcb->PendingLockList;
|
||
listEntry = nextListEntry ) {
|
||
|
||
nextListEntry = listEntry->Flink;
|
||
|
||
fileLock = CONTAINING_RECORD( listEntry, NW_FILE_LOCK, ListEntry );
|
||
|
||
if ( !LockIsOverlapping( pNpFcb, fileLock->StartFileOffset, fileLock->Length ) ) {
|
||
|
||
//
|
||
// It is now safe to try this lock.
|
||
//
|
||
|
||
RemoveEntryList( listEntry );
|
||
|
||
DebugTrace(0, Dbg, "Reattempt lock %08lx\n", fileLock->IrpContext);
|
||
|
||
status = Exchange (
|
||
fileLock->IrpContext,
|
||
LockNcpCallback,
|
||
"Fbrddw",
|
||
NCP_LOCK_RANGE,
|
||
fileLock->Flags | 0x01,
|
||
fileLock->Icb->Handle, sizeof( fileLock->Icb->Handle ),
|
||
fileLock->StartFileOffset,
|
||
fileLock->Length,
|
||
LockTimeoutThreshold );
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
|
||
NwDequeueIrpContext( fileLock->IrpContext, FALSE );
|
||
NwCompleteRequest( fileLock->IrpContext, status );
|
||
|
||
FREE_POOL( fileLock );
|
||
|
||
} else if ( status == STATUS_PENDING ) {
|
||
DebugTrace(-1, Dbg, "ReattemptPendingLocks\n", 0);
|
||
return;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "ReattemptPendingLocks\n", 0);
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
LockExists(
|
||
PNONPAGED_FCB pNpFcb,
|
||
LONG StartOffset,
|
||
ULONG Length,
|
||
PNW_FILE_LOCK *FileLock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine test whether or not a lock is owned for this ICB.
|
||
|
||
*** This routine must be called when at the front of the ScbQueue.
|
||
|
||
Arguments:
|
||
|
||
pNpFcb - The non paged FCB of file that is being locked.
|
||
|
||
StartOffset - The starting file offset of the lock.
|
||
|
||
Length - The number of bytes to lock.
|
||
|
||
FileLock - Returns a pointer to the FileLock structure if it was found.
|
||
|
||
Return Value:
|
||
|
||
TRUE - This lock is being held for this ICB.
|
||
FALSE - This lock is NOT being held for this ICB.
|
||
|
||
--*/
|
||
{
|
||
PLIST_ENTRY ListEntry;
|
||
PNW_FILE_LOCK pFileLock;
|
||
LONG EndOffset = StartOffset + Length - 1;
|
||
|
||
for ( ListEntry = pNpFcb->FileLockList.Flink;
|
||
ListEntry != &pNpFcb->FileLockList;
|
||
ListEntry = ListEntry->Flink ) {
|
||
|
||
pFileLock = CONTAINING_RECORD( ListEntry, NW_FILE_LOCK, ListEntry );
|
||
|
||
//
|
||
// Search for the lock that exactly matches this one.
|
||
//
|
||
|
||
if ( pFileLock->StartFileOffset == StartOffset &&
|
||
pFileLock->EndFileOffset == EndOffset ) {
|
||
|
||
*FileLock = pFileLock;
|
||
DebugTrace(0, Dbg, "Found lock\n", 0);
|
||
return( TRUE );
|
||
}
|
||
|
||
}
|
||
|
||
*FileLock = NULL;
|
||
|
||
DebugTrace(0, Dbg, "Could not find lock\n", 0);
|
||
return( FALSE );
|
||
}
|
||
|
||
NTSTATUS
|
||
UnlockIcbLocks(
|
||
PIRP_CONTEXT pIrpContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine unlocks the first lock for an ICB.
|
||
|
||
*** This routine must be called when at the front of the ScbQueue.
|
||
|
||
Arguments:
|
||
|
||
IrpContext - A pointer to the IRP context pointers for this request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PICB pIcb;
|
||
PNW_FILE_LOCK pFileLock;
|
||
PLIST_ENTRY pLockEntry;
|
||
NTSTATUS Status;
|
||
PNONPAGED_FCB pNpFcb;
|
||
PVOID pLockProc;
|
||
|
||
DebugTrace(+1, Dbg, "UnlockIcbLocks...\n", 0);
|
||
|
||
pIcb = pIrpContext->Icb;
|
||
pNpFcb = pIcb->NpFcb;
|
||
|
||
pLockEntry = &pNpFcb->FileLockList;
|
||
pLockProc = PsGetCurrentProcessId();
|
||
|
||
DebugTrace(0, Dbg, "UnlockIcbLocks: current process is %08lx.\n", pLockProc);
|
||
|
||
while ( pLockEntry->Flink != &pNpFcb->FileLockList ) {
|
||
|
||
pFileLock = CONTAINING_RECORD( pLockEntry->Flink, NW_FILE_LOCK, ListEntry );
|
||
|
||
//
|
||
// If this lock isn't for this process, skip it.
|
||
//
|
||
|
||
if ( pFileLock->pOwnerProc != pLockProc ) {
|
||
|
||
DebugTrace(0, Dbg, "Skipping lock %08lx, not for this process.\n", pLockEntry);
|
||
pLockEntry = pLockEntry->Flink;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// If we're unlocking by key and the key doesn't match, skip it.
|
||
//
|
||
|
||
if ( ( pIrpContext->Specific.Lock.ByKey ) &&
|
||
( pFileLock->Key != pIrpContext->Specific.Lock.Key ) ) {
|
||
|
||
DebugTrace(0, Dbg, "Skipping lock %08lx, bad key.\n", pLockEntry);
|
||
pLockEntry = pLockEntry->Flink;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Otherwise, Do our best to free the lock.
|
||
//
|
||
|
||
DebugTrace(0, Dbg, "Freeing lock %08lx.\n", pLockEntry);
|
||
RemoveEntryList( &pFileLock->ListEntry );
|
||
|
||
Status = ExchangeWithWait (
|
||
pIrpContext,
|
||
SynchronousResponseCallback,
|
||
"F-rddw",
|
||
NCP_UNLOCK_RANGE,
|
||
pIcb->Handle, sizeof( pIcb->Handle ),
|
||
pFileLock->StartFileOffset,
|
||
pFileLock->Length,
|
||
1 );
|
||
|
||
FREE_POOL( pFileLock );
|
||
pLockEntry = pLockEntry->Flink;
|
||
|
||
}
|
||
|
||
//
|
||
// We are finished.
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", 0);
|
||
NwDequeueIrpContext( pIrpContext, FALSE );
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
NwFreeLocksForIcb(
|
||
IN PIRP_CONTEXT pIrpContext,
|
||
PICB Icb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine unlocks all locks held for a specific ICB.
|
||
|
||
Because its only called from Cleanup prior to a close we can
|
||
simply free the internal structures. The server will clear the
|
||
locks on the handle when it gets the close.
|
||
|
||
Arguments:
|
||
|
||
ICB - The ICB to free the locks for.
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY listEntry, nextListEntry;
|
||
PNW_FILE_LOCK pFileLock;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "NwFreeLockForIcb...\n", 0);
|
||
|
||
NwAppendToQueueAndWait( pIrpContext );
|
||
|
||
for ( listEntry = Icb->NpFcb->FileLockList.Flink;
|
||
listEntry != &Icb->NpFcb->FileLockList;
|
||
listEntry = nextListEntry ) {
|
||
|
||
nextListEntry = listEntry->Flink;
|
||
|
||
pFileLock = CONTAINING_RECORD(
|
||
listEntry,
|
||
NW_FILE_LOCK,
|
||
ListEntry );
|
||
|
||
if ( pFileLock->Icb == Icb ) {
|
||
|
||
RemoveEntryList( listEntry );
|
||
FREE_POOL( pFileLock );
|
||
|
||
DebugTrace( 0, Dbg, "Freed lock %08lx\n", pFileLock );
|
||
}
|
||
|
||
}
|
||
|
||
ReattemptPendingLocks( Icb->NpFcb );
|
||
|
||
DebugTrace(-1, Dbg, "NwFreeLockForIcb -> VOID\n", 0);
|
||
|
||
}
|
||
|