summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/lock.c')
-rw-r--r--private/nw/rdr/lock.c1357
1 files changed, 1357 insertions, 0 deletions
diff --git a/private/nw/rdr/lock.c b/private/nw/rdr/lock.c
new file mode 100644
index 000000000..42d069f5e
--- /dev/null
+++ b/private/nw/rdr/lock.c
@@ -0,0 +1,1357 @@
+/*++
+
+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);
+
+}
+