SMB, GUI imput handler, ThreadSafe SMB

This commit is contained in:
2022-02-01 13:03:00 +01:00
parent c2dcb49016
commit 9824184d90
10 changed files with 265 additions and 48 deletions

View File

@@ -0,0 +1,62 @@
using SMBLibrary.Client;
namespace FileTime.Providers.Smb
{
public class SmbClientContext
{
private readonly Func<Task<ISMBClient>> _getSmbClient;
private readonly Action _disposeClient;
private bool _isRunning;
private readonly object _lock = new object();
public SmbClientContext(Func<Task<ISMBClient>> getSmbClient, Action disposeClient)
{
_getSmbClient = getSmbClient;
_disposeClient = disposeClient;
}
public async Task<T> RunWithSmbClientAsync<T>(Func<ISMBClient, T> func)
{
while (true)
{
lock (_lock)
{
if(!_isRunning)
{
_isRunning = true;
break;
}
}
await Task.Delay(1);
}
try
{
ISMBClient client;
while (true)
{
try
{
client = await _getSmbClient();
return func(client);
}
catch (Exception e) when (e.Source == "SMBLibrary")
{
_disposeClient();
}
catch (Exception e)
{
throw new Exception("Exception was thrown while executing method with SmbClient.", e);
}
}
}
finally
{
lock (_lock)
{
_isRunning = false;
}
}
}
}
}

View File

@@ -63,9 +63,21 @@ namespace FileTime.Providers.Smb
throw new NotSupportedException();
}
public Task<IItem?> GetByPath(string path)
public async Task<IItem?> GetByPath(string path)
{
throw new NotImplementedException();
if (path == null) return this;
var pathParts = path.TrimStart(Constants.SeparatorChar).Split(Constants.SeparatorChar);
var rootContainer = _rootContainers.Find(c => c.Name == pathParts[0]);
if (rootContainer == null)
{
return null;
}
var remainingPath = string.Join(Constants.SeparatorChar, pathParts.Skip(1));
return remainingPath.Length == 0 ? rootContainer : await rootContainer.GetByPath(remainingPath);
}
public IContainer? GetParent() => _parent;

View File

@@ -17,7 +17,10 @@ namespace FileTime.Providers.Smb
private IReadOnlyList<IItem>? _items;
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
private ISMBClient? _client;
private readonly object _clientGuard = new object();
private bool _refreshingClient;
private readonly IInputInterface _inputInterface;
private readonly SmbClientContext _smbClientContext;
public string Name { get; }
@@ -37,6 +40,7 @@ namespace FileTime.Providers.Smb
public SmbServer(string path, SmbContentProvider contentProvider, IInputInterface inputInterface)
{
_inputInterface = inputInterface;
_smbClientContext = new SmbClientContext(GetSmbClient, DisposeSmbClient);
Provider = contentProvider;
FullName = Name = path;
@@ -86,26 +90,60 @@ namespace FileTime.Providers.Smb
public async Task Refresh()
{
ISMBClient client = await GetSmbClient();
List<string> shares = await _smbClientContext.RunWithSmbClientAsync((client) => client.ListShares(out var status));
List<string> shares = client.ListShares(out var status);
_shares = shares.ConvertAll(s => new SmbShare(s, Provider, this, GetSmbClient)).AsReadOnly();
_shares = shares.ConvertAll(s => new SmbShare(s, Provider, this, _smbClientContext)).AsReadOnly();
_items = _shares.Cast<IItem>().ToList().AsReadOnly();
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty);
}
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
private void DisposeSmbClient()
{
lock (_clientGuard)
{
_client = null;
}
}
private async Task<ISMBClient> GetSmbClient()
{
if (_client == null)
bool isClientNull;
lock (_clientGuard)
{
isClientNull = _client == null;
}
while (isClientNull)
{
if (!await RefreshSmbClient())
{
await Task.Delay(1);
}
lock (_clientGuard)
{
isClientNull = _client == null;
}
}
return _client!;
}
private async Task<bool> RefreshSmbClient()
{
lock (_clientGuard)
{
if (_refreshingClient) return false;
_refreshingClient = true;
}
try
{
var couldParse = IPAddress.TryParse(Name[2..], out var ipAddress);
_client = new SMB2Client();
var client = new SMB2Client();
var connected = couldParse
? _client.Connect(ipAddress, SMBTransportType.DirectTCPTransport)
: _client.Connect(Name[2..], SMBTransportType.DirectTCPTransport);
? client.Connect(ipAddress, SMBTransportType.DirectTCPTransport)
: client.Connect(Name[2..], SMBTransportType.DirectTCPTransport);
if (connected)
{
@@ -122,14 +160,29 @@ namespace FileTime.Providers.Smb
_password = inputs[1];
}
if (_client.Login(string.Empty, _username, _password) != NTStatus.STATUS_SUCCESS)
if (client.Login(string.Empty, _username, _password) != NTStatus.STATUS_SUCCESS)
{
_username = null;
_password = null;
}
else
{
lock (_clientGuard)
{
_client = client;
}
}
}
}
return _client;
finally
{
lock (_clientGuard)
{
_refreshingClient = false;
}
}
return true;
}
public Task Rename(string newName) => throw new NotSupportedException();

View File

@@ -11,7 +11,7 @@ namespace FileTime.Providers.Smb
private IReadOnlyList<IItem>? _items;
private IReadOnlyList<IContainer>? _containers;
private IReadOnlyList<IElement>? _elements;
private Func<Task<ISMBClient>> _getSmbClient;
private SmbClientContext _smbClientContext;
private readonly IContainer? _parent;
public string Name { get; }
@@ -28,10 +28,10 @@ namespace FileTime.Providers.Smb
public AsyncEventHandler Refreshed { get; } = new();
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, Func<Task<ISMBClient>> getSmbClient)
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, SmbClientContext smbClientContext)
{
_parent = parent;
_getSmbClient = getSmbClient;
_smbClientContext = smbClientContext;
Name = name;
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
@@ -117,40 +117,42 @@ namespace FileTime.Providers.Smb
public async Task<(List<IContainer> containers, List<IElement> elements)> ListFolder(IContainer parent, string shareName, string folderName)
{
var containers = new List<IContainer>();
var elements = new List<IElement>();
var client = await _getSmbClient();
ISMBFileStore fileStore = client.TreeConnect(shareName, out var status);
if (status == NTStatus.STATUS_SUCCESS)
return await _smbClientContext.RunWithSmbClientAsync(client =>
{
status = fileStore.CreateFile(out object directoryHandle, out FileStatus fileStatus, folderName, AccessMask.GENERIC_READ, SMBLibrary.FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
var containers = new List<IContainer>();
var elements = new List<IElement>();
NTStatus status = NTStatus.STATUS_DATA_ERROR;
ISMBFileStore fileStore = client.TreeConnect(shareName, out status);
if (status == NTStatus.STATUS_SUCCESS)
{
status = fileStore.QueryDirectory(out List<QueryDirectoryFileInformation> fileList, directoryHandle, "*", FileInformationClass.FileDirectoryInformation);
status = fileStore.CloseFile(directoryHandle);
foreach (var item in fileList)
status = fileStore.CreateFile(out object directoryHandle, out FileStatus fileStatus, folderName, AccessMask.GENERIC_READ, SMBLibrary.FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
if (status == NTStatus.STATUS_SUCCESS)
{
if (item is FileDirectoryInformation fileDirectoryInformation && fileDirectoryInformation.FileName != "." && fileDirectoryInformation.FileName != "..")
status = fileStore.QueryDirectory(out List<QueryDirectoryFileInformation> fileList, directoryHandle, "*", FileInformationClass.FileDirectoryInformation);
status = fileStore.CloseFile(directoryHandle);
foreach (var item in fileList)
{
if ((fileDirectoryInformation.FileAttributes & SMBLibrary.FileAttributes.Directory) == SMBLibrary.FileAttributes.Directory)
if (item is FileDirectoryInformation fileDirectoryInformation && fileDirectoryInformation.FileName != "." && fileDirectoryInformation.FileName != "..")
{
containers.Add(new SmbFolder(fileDirectoryInformation.FileName, Provider, this, parent));
}
else
{
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent));
if ((fileDirectoryInformation.FileAttributes & SMBLibrary.FileAttributes.Directory) == SMBLibrary.FileAttributes.Directory)
{
containers.Add(new SmbFolder(fileDirectoryInformation.FileName, Provider, this, parent));
}
else
{
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent));
}
}
}
}
}
}
containers = containers.OrderBy(c => c.Name).ToList();
elements = elements.OrderBy(e => e.Name).ToList();
containers = containers.OrderBy(c => c.Name).ToList();
elements = elements.OrderBy(e => e.Name).ToList();
return (containers, elements);
return (containers, elements);
});
}
public Task Rename(string newName) => throw new NotSupportedException();