From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/nw/rdr/lock.c | 1357 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1357 insertions(+) create mode 100644 private/nw/rdr/lock.c (limited to 'private/nw/rdr/lock.c') 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); + +} + -- cgit v1.2.3