mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1326 lines
31 KiB
1326 lines
31 KiB
/*++
|
|
|
|
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);
|
|
|
|
}
|
|
|