Pass lock / unlock requests to the underlying object store

This commit is contained in:
Tal Aloni 2017-08-31 15:22:26 +03:00
parent c08b425205
commit e7da671877
5 changed files with 187 additions and 2 deletions

View file

@ -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" />

View 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();
}
}
}

View 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();
}
}
}

View file

@ -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)
{

View file

@ -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);