/*++
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
);
NTSTATUS
UnlockIcbLocksCallback (
IN PIRP_CONTEXT IrpContext,
IN ULONG BytesAvailable,
IN PUCHAR Response
);
#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 )
#pragma alloc_text( PAGE1, UnlockIcbLocksCallback )
#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)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; // BUGBUG
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)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( 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;
if ( irpSp->Flags & SL_EXCLUSIVE_LOCK ) {
LockFlags = 0x00; // BUGBUG
} else {
LockFlags = 0x02; // BUGBUG
}
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, // BUGBUG
Icb->Handle, sizeof( Icb->Handle ),
ByteOffset.LowPart,
Length.LowPart,
LockTimeoutThreshold );
if ( !NT_SUCCESS( status ) ) {
FREE_POOL( FileLock );
}
}
break;
case IRP_MN_UNLOCK_SINGLE:
if ( !LockExists( pNpFcb, ByteOffset.LowPart, Length.LowPart, &FileLock ) ) {
status = STATUS_RANGE_NOT_LOCKED;
} 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 ;
IrpContext->Specific.Lock.LastLock = &pNpFcb->FileLockList;
status = UnlockIcbLocks( IrpContext );
break;
case IRP_MN_UNLOCK_ALL_BY_KEY:
IrpContext->Icb = Icb;
IrpContext->Specific.Lock.Key = Key ;
IrpContext->Specific.Lock.ByKey = TRUE ;
IrpContext->Specific.Lock.LastLock = &pNpFcb->FileLockList;
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; // BUGBUG Configurable
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 ) {
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, // BUGBUG
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 LastLockEntry;
NTSTATUS Status;
PNONPAGED_FCB pNpFcb;
DebugTrace(+1, Dbg, "UnlockIcbLocks...\n", 0);
pIcb = pIrpContext->Icb;
pNpFcb = pIcb->NpFcb;
LastLockEntry = pIrpContext->Specific.Lock.LastLock;
if ( LastLockEntry->Flink == &pNpFcb->FileLockList ) {
NwDequeueIrpContext( pIrpContext, FALSE );
NwCompleteRequest( pIrpContext, STATUS_SUCCESS );
DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", 0);
return STATUS_PENDING;
}
pFileLock = CONTAINING_RECORD( LastLockEntry->Flink, NW_FILE_LOCK, ListEntry );
if ( pIrpContext->Specific.Lock.ByKey ) {
//
// Doing an unlock by key, skip locks that don't have a matching key.
//
while ( pFileLock->Key != pIrpContext->Specific.Lock.Key ) {
if ( pFileLock->ListEntry.Flink == &pNpFcb->FileLockList ) {
//
// FIXFIX should we return STATUS_RANGE_NOT_LOCKED if there were no matches
// at all?
//
DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", STATUS_SUCCESS);
return( STATUS_SUCCESS );
}
pIrpContext->Specific.Lock.LastLock = &pFileLock->ListEntry;
pFileLock = CONTAINING_RECORD( &pFileLock->ListEntry, NW_FILE_LOCK, ListEntry );
}
// We have a locked range. Proceed to unlock this one and any others.
}
RemoveEntryList( &pFileLock->ListEntry );
Status = Exchange (
pIrpContext,
UnlockIcbLocksCallback,
"F-rddw",
NCP_UNLOCK_RANGE,
pIcb->Handle, sizeof( pIcb->Handle ),
pFileLock->StartFileOffset,
pFileLock->Length,
1 );
FREE_POOL( pFileLock );
DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", Status);
return Status;
}
NTSTATUS
UnlockIcbLocksCallback (
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, "LockNcpCallback...\n", 0);
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 );
//
// Ignore the response, plod ahead.
//
Status = UnlockIcbLocks( IrpContext );
return Status;
}
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);
}