/*++ 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); }