mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-08-02 14:01:50 +02:00
Pass lock / unlock requests to the underlying object store
This commit is contained in:
parent
c08b425205
commit
e7da671877
5 changed files with 187 additions and 2 deletions
|
@ -212,6 +212,7 @@
|
|||
<Compile Include="Server\SMB1\CloseHelper.cs" />
|
||||
<Compile Include="Server\SMB1\EchoHelper.cs" />
|
||||
<Compile Include="Server\SMB1\FileStoreResponseHelper.cs" />
|
||||
<Compile Include="Server\SMB1\LockingHelper.cs" />
|
||||
<Compile Include="Server\SMB1\NegotiateHelper.cs" />
|
||||
<Compile Include="Server\SMB1\NotifyChangeHelper.cs" />
|
||||
<Compile Include="Server\SMB1\NTCreateHelper.cs" />
|
||||
|
@ -233,6 +234,7 @@
|
|||
<Compile Include="Server\SMB2\CloseHelper.cs" />
|
||||
<Compile Include="Server\SMB2\CreateHelper.cs" />
|
||||
<Compile Include="Server\SMB2\IOCtlHelper.cs" />
|
||||
<Compile Include="Server\SMB2\LockHelper.cs" />
|
||||
<Compile Include="Server\SMB2\NegotiateHelper.cs" />
|
||||
<Compile Include="Server\SMB2\QueryDirectoryHelper.cs" />
|
||||
<Compile Include="Server\SMB2\QueryInfoHelper.cs" />
|
||||
|
|
76
SMBLibrary/Server/SMB1/LockingHelper.cs
Normal file
76
SMBLibrary/Server/SMB1/LockingHelper.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
|
||||
*
|
||||
* You can redistribute this program and/or modify it under the terms of
|
||||
* the GNU Lesser Public License as published by the Free Software Foundation,
|
||||
* either version 3 of the License, or (at your option) any later version.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SMBLibrary.SMB1;
|
||||
using Utilities;
|
||||
|
||||
namespace SMBLibrary.Server.SMB1
|
||||
{
|
||||
internal class LockingHelper
|
||||
{
|
||||
internal static List<SMB1Command> GetLockingAndXResponse(SMB1Header header, LockingAndXRequest request, ISMBShare share, SMB1ConnectionState state)
|
||||
{
|
||||
SMB1Session session = state.GetSession(header.UID);
|
||||
OpenFileObject openFile = session.GetOpenFileObject(request.FID);
|
||||
if (openFile == null)
|
||||
{
|
||||
state.LogToServer(Severity.Verbose, "Locking failed. Invalid FID.");
|
||||
header.Status = NTStatus.STATUS_INVALID_HANDLE;
|
||||
return new ErrorResponse(request.CommandName);
|
||||
}
|
||||
|
||||
if ((request.TypeOfLock & LockType.CHANGE_LOCKTYPE) > 0)
|
||||
{
|
||||
// [MS-CIFS] Windows NT Server does not support the CHANGE_LOCKTYPE flag of TypeOfLock.
|
||||
state.LogToServer(Severity.Verbose, "Locking failed. CHANGE_LOCKTYPE is not supported.");
|
||||
header.Status = NTStatus.STATUS_NOT_IMPLEMENTED;
|
||||
return new ErrorResponse(request.CommandName);
|
||||
}
|
||||
|
||||
if (request.Unlocks.Count == 0 && request.Locks.Count == 0)
|
||||
{
|
||||
// [MS-CIFS] If NumberOfRequestedUnlocks and NumberOfRequestedLocks are both zero [..] the server MUST NOT send an SMB_COM_LOCKING_ANDX Response.
|
||||
return new List<SMB1Command>();
|
||||
}
|
||||
|
||||
// [MS-CIFS] If the CANCEL_LOCK bit is set, Windows NT servers cancel only the first lock request range listed in the lock array.
|
||||
for(int lockIndex = 0; lockIndex < request.Unlocks.Count; lockIndex++)
|
||||
{
|
||||
LockingRange lockingRange = request.Unlocks[lockIndex];
|
||||
header.Status = share.FileStore.UnlockFile(openFile.Handle, (long)lockingRange.ByteOffset, (long)lockingRange.LengthInBytes);
|
||||
if (header.Status != NTStatus.STATUS_SUCCESS)
|
||||
{
|
||||
state.LogToServer(Severity.Verbose, "Locking: Unlocking '{0}{1}' failed. Offset: {2}, Length: {3}. NTStatus: {4}.", share.Name, openFile.Path, lockingRange.ByteOffset, lockingRange.LengthInBytes, header.Status);
|
||||
return new ErrorResponse(request.CommandName);
|
||||
}
|
||||
state.LogToServer(Severity.Verbose, "Locking: Unlocking '{0}{1}' succeeded. Offset: {2}, Length: {3}.", share.Name, openFile.Path, lockingRange.ByteOffset, lockingRange.LengthInBytes);
|
||||
}
|
||||
|
||||
for (int lockIndex = 0; lockIndex < request.Locks.Count; lockIndex++)
|
||||
{
|
||||
LockingRange lockingRange = request.Locks[lockIndex];
|
||||
bool exclusiveLock = (request.TypeOfLock & LockType.SHARED_LOCK) == 0;
|
||||
header.Status = share.FileStore.LockFile(openFile.Handle, (long)lockingRange.ByteOffset, (long)lockingRange.LengthInBytes, exclusiveLock);
|
||||
if (header.Status != NTStatus.STATUS_SUCCESS)
|
||||
{
|
||||
state.LogToServer(Severity.Verbose, "Locking: Locking '{0}{1}' failed. Offset: {2}, Length: {3}. NTStatus: {4}.", share.Name, openFile.Path, lockingRange.ByteOffset, lockingRange.LengthInBytes, header.Status);
|
||||
// [MS-CIFS] This client request is atomic. If the area to be locked is already locked or the
|
||||
// lock request otherwise fails, no other ranges specified in the client request are locked.
|
||||
for (int index = 0; index < lockIndex; index++)
|
||||
{
|
||||
share.FileStore.UnlockFile(openFile.Handle, (long)request.Locks[index].ByteOffset, (long)request.Locks[index].LengthInBytes);
|
||||
}
|
||||
return new ErrorResponse(request.CommandName);
|
||||
}
|
||||
state.LogToServer(Severity.Verbose, "Locking: Locking '{0}{1}' succeeded. Offset: {2}, Length: {3}.", share.Name, openFile.Path, lockingRange.ByteOffset, lockingRange.LengthInBytes);
|
||||
}
|
||||
|
||||
return new LockingAndXResponse();
|
||||
}
|
||||
}
|
||||
}
|
104
SMBLibrary/Server/SMB2/LockHelper.cs
Normal file
104
SMBLibrary/Server/SMB2/LockHelper.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
|
||||
*
|
||||
* You can redistribute this program and/or modify it under the terms of
|
||||
* the GNU Lesser Public License as published by the Free Software Foundation,
|
||||
* either version 3 of the License, or (at your option) any later version.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SMBLibrary.SMB2;
|
||||
using Utilities;
|
||||
|
||||
namespace SMBLibrary.Server.SMB2
|
||||
{
|
||||
internal class LockHelper
|
||||
{
|
||||
internal static SMB2Command GetLockResponse(LockRequest request, ISMBShare share, SMB2ConnectionState state)
|
||||
{
|
||||
SMB2Session session = state.GetSession(request.Header.SessionID);
|
||||
OpenFileObject openFile = session.GetOpenFileObject(request.FileId);
|
||||
if (openFile == null)
|
||||
{
|
||||
state.LogToServer(Severity.Verbose, "Lock failed. Invalid FileId.");
|
||||
return new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED);
|
||||
}
|
||||
|
||||
if (request.Locks.Count == 0)
|
||||
{
|
||||
// [MS-SMB2] The lock count MUST be greater than or equal to 1
|
||||
state.LogToServer(Severity.Verbose, "Lock: Invalid number of locks, must be greater than 0.");
|
||||
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
// [MS-SMB2] If the flags of the initial SMB2_LOCK_ELEMENT in the Locks array of the request has
|
||||
// SMB2_LOCKFLAG_UNLOCK set, the server MUST process the lock array as a series of unlocks.
|
||||
// Otherwise, it MUST process the lock array as a series of lock requests.
|
||||
bool unlock = request.Locks[0].Unlock;
|
||||
foreach(LockElement lockElement in request.Locks)
|
||||
{
|
||||
if (unlock)
|
||||
{
|
||||
if (lockElement.SharedLock || lockElement.ExclusiveLock)
|
||||
{
|
||||
state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: Lock in a series of unlocks.");
|
||||
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lockElement.Unlock)
|
||||
{
|
||||
state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: Unlock in a series of locks.");
|
||||
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
if (lockElement.SharedLock && lockElement.ExclusiveLock)
|
||||
{
|
||||
state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: SMB2_LOCKFLAG_SHARED_LOCK and SMB2_LOCKFLAG_EXCLUSIVE_LOCK are mutually exclusive.");
|
||||
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
if (request.Locks.Count > 1 && !lockElement.FailImmediately)
|
||||
{
|
||||
state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: SMB2_LOCKFLAG_FAIL_IMMEDIATELY not set in a series of locks.");
|
||||
return new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int lockIndex = 0; lockIndex < request.Locks.Count; lockIndex++)
|
||||
{
|
||||
LockElement lockElement = request.Locks[lockIndex];
|
||||
if (unlock)
|
||||
{
|
||||
NTStatus status = share.FileStore.UnlockFile(openFile.Handle, (long)lockElement.Offset, (long)lockElement.Length);
|
||||
if (status != NTStatus.STATUS_SUCCESS)
|
||||
{
|
||||
// [MS-SMB2] If the unlock operation fails, the server MUST fail the operation with the error code received from the object store and stop processing further entries in the Locks array.
|
||||
state.LogToServer(Severity.Information, "Lock: Unlocking '{0}{1}' failed. Offset: {2}, Length: {3}. NTStatus: {4}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length, status);
|
||||
return new ErrorResponse(request.CommandName, status);
|
||||
}
|
||||
state.LogToServer(Severity.Information, "Lock: Unlocking '{0}{1}' succeeded. Offset: {2}, Length: {3}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
NTStatus status = share.FileStore.LockFile(openFile.Handle, (long)lockElement.Offset, (long)lockElement.Length, lockElement.ExclusiveLock);
|
||||
if (status != NTStatus.STATUS_SUCCESS)
|
||||
{
|
||||
// [MS-SMB2] If the lock operation fails, the server MUST unlock any ranges locked as part of processing the previous entries in the Locks array of this request.
|
||||
state.LogToServer(Severity.Information, "Lock: Locking '{0}{1}' failed. Offset: {2}, Length: {3}. NTStatus: {4}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length, status);
|
||||
for (int index = 0; index < lockIndex; index++)
|
||||
{
|
||||
share.FileStore.UnlockFile(openFile.Handle, (long)request.Locks[index].Offset, (long)request.Locks[index].Length);
|
||||
}
|
||||
return new ErrorResponse(request.CommandName, status);
|
||||
}
|
||||
state.LogToServer(Severity.Information, "Lock: Locking '{0}{1}' succeeded. Offset: {2}, Length: {3}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length);
|
||||
}
|
||||
}
|
||||
|
||||
return new LockResponse();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -221,8 +221,7 @@ namespace SMBLibrary.Server
|
|||
}
|
||||
else if (command is LockingAndXRequest)
|
||||
{
|
||||
header.Status = NTStatus.STATUS_ACCESS_DENIED;
|
||||
return new ErrorResponse(command.CommandName);
|
||||
return LockingHelper.GetLockingAndXResponse(header, (LockingAndXRequest)command, share, state);
|
||||
}
|
||||
else if (command is OpenAndXRequest)
|
||||
{
|
||||
|
|
|
@ -157,6 +157,10 @@ namespace SMBLibrary.Server
|
|||
{
|
||||
return ReadWriteResponseHelper.GetWriteResponse((WriteRequest)command, share, state);
|
||||
}
|
||||
else if (command is LockRequest)
|
||||
{
|
||||
return LockHelper.GetLockResponse((LockRequest)command, share, state);
|
||||
}
|
||||
else if (command is FlushRequest)
|
||||
{
|
||||
return ReadWriteResponseHelper.GetFlushResponse((FlushRequest)command, share, state);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue