SMB, GUI imput handler, ThreadSafe SMB
This commit is contained in:
@@ -49,8 +49,8 @@ namespace FileTime.Providers.Local
|
||||
public async Task<IItem?> GetByPath(string path)
|
||||
{
|
||||
var pathParts = (IsCaseInsensitive ? path.ToLower() : path).TrimStart(Constants.SeparatorChar).Split(Constants.SeparatorChar);
|
||||
|
||||
if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && pathParts.Length == 1 && pathParts[0] == "") return this;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && pathParts.Length == 1 && pathParts[0] == "") return this;
|
||||
|
||||
var rootContainer = _rootContainers.FirstOrDefault(c => NormalizePath(c.Name) == NormalizePath(pathParts[0]));
|
||||
|
||||
|
||||
62
src/Providers/FileTime.Providers.Smb/SmbClientContext.cs
Normal file
62
src/Providers/FileTime.Providers.Smb/SmbClientContext.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user