StreamCopyCommandHandler, Async rename

This commit is contained in:
2022-02-16 00:12:09 +01:00
parent 89d891918c
commit f809f0a640
41 changed files with 529 additions and 182 deletions

View File

@@ -9,7 +9,7 @@ namespace FileTime.Providers.Smb
{
public class SmbContentProvider : IContentProvider
{
private readonly object _initializationGuard = new object();
private readonly object _initializationGuard = new();
private bool _initialized;
private bool _initializing;
private IContainer? _parent;
@@ -39,6 +39,7 @@ namespace FileTime.Providers.Smb
public bool SupportsDirectoryLevelSoftDelete => false;
public bool IsDestroyed => false;
public bool SupportsContentStreams => true;
public SmbContentProvider(IInputInterface inputInterface, Persistence.PersistenceService persistenceService, ILogger<SmbContentProvider> logger)
{
@@ -50,7 +51,7 @@ namespace FileTime.Providers.Smb
_logger = logger;
}
public async Task<IContainer> CreateContainer(string name)
public async Task<IContainer> CreateContainerAsync(string name)
{
var fullName = "\\\\" + name;
var container = _rootContainers.Find(c => c.Name == name);
@@ -69,7 +70,7 @@ namespace FileTime.Providers.Smb
return container;
}
public Task<IElement> CreateElement(string name)
public Task<IElement> CreateElementAsync(string name)
{
throw new NotSupportedException();
}
@@ -98,9 +99,9 @@ namespace FileTime.Providers.Smb
public IContainer? GetParent() => _parent;
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
public async Task<bool> IsExists(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false;
public async Task<bool> IsExistsAsync(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false;
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
@@ -124,7 +125,7 @@ namespace FileTime.Providers.Smb
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult((IReadOnlyList<IElement>?)_elements);
public Task Rename(string newName) => throw new NotSupportedException();
public Task<bool> CanOpen() => Task.FromResult(true);
public Task<bool> CanOpenAsync() => Task.FromResult(true);
public void Destroy() { }

View File

@@ -0,0 +1,67 @@
using FileTime.Core.Providers;
using SMBLibrary;
using SMBLibrary.Client;
namespace FileTime.Providers.Smb
{
public class SmbContentReader : IContentReader
{
private readonly ISMBFileStore _smbFileStore;
private readonly object _fileHandle;
private readonly ISMBClient _client;
private bool disposed;
private long _bytesRead;
public int PreferredBufferSize => (int)_client.MaxReadSize;
public SmbContentReader(ISMBFileStore smbFileStore, object fileHandle, ISMBClient client)
{
_smbFileStore = smbFileStore;
_fileHandle = fileHandle;
_client = client;
}
public Task<byte[]> ReadBytesAsync(int bufferSize)
{
var max = bufferSize > 0 && bufferSize < (int)_client.MaxReadSize ? bufferSize : (int)_client.MaxReadSize;
var status = _smbFileStore.ReadFile(out byte[] data, _fileHandle, _bytesRead, max);
if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.STATUS_END_OF_FILE)
{
throw new Exception("Failed to read from file");
}
if (status == NTStatus.STATUS_END_OF_FILE || data.Length == 0)
{
return Task.FromResult(Array.Empty<byte>());
}
_bytesRead += data.Length;
return Task.FromResult(data);
}
~SmbContentReader()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
_smbFileStore.CloseFile(_fileHandle);
_smbFileStore.Disconnect();
}
}
disposed = true;
}
}
}

View File

@@ -0,0 +1,24 @@
using FileTime.Core.Providers;
namespace FileTime.Providers.Smb
{
public class SmbContentWriter : IContentWriter
{
public int PreferredBufferSize => throw new NotImplementedException();
public void Dispose()
{
throw new NotImplementedException();
}
public Task FlushAsync()
{
throw new NotImplementedException();
}
public Task WriteBytesAsync(byte[] data)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,10 +1,15 @@
using FileTime.Core.Models;
using FileTime.Core.Providers;
using SMBLibrary;
namespace FileTime.Providers.Smb
{
public class SmbFile : IElement
{
private readonly IContainer _parent;
private readonly SmbClientContext _smbClientContext;
private readonly SmbShare _smbShare;
public bool IsSpecial => false;
public string Name { get; }
@@ -17,11 +22,10 @@ namespace FileTime.Providers.Smb
public bool CanRename => true;
public IContentProvider Provider { get; }
private IContainer _parent;
public bool IsDestroyed { get; private set; }
public SmbFile(string name, SmbContentProvider provider, IContainer parent)
public SmbFile(string name, SmbContentProvider provider, SmbShare smbShare, IContainer parent, SmbClientContext smbClientContext)
{
Name = name;
FullName = parent.FullName + Constants.SeparatorChar + Name;
@@ -29,6 +33,8 @@ namespace FileTime.Providers.Smb
Provider = provider;
_parent = parent;
_smbClientContext = smbClientContext;
_smbShare = smbShare;
}
public Task Delete(bool hardDelete = false)
@@ -50,5 +56,35 @@ namespace FileTime.Providers.Smb
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(-1L);
public void Destroy() => IsDestroyed = true;
public async Task<IContentReader> GetContentReaderAsync()
{
return await _smbClientContext.RunWithSmbClientAsync(client =>
{
NTStatus status = NTStatus.STATUS_DATA_ERROR;
var fileStore = _smbShare.TreeConnect(client, out status);
if (status != NTStatus.STATUS_SUCCESS)
{
throw new Exception($"Could not open file {NativePath} for read.");
}
var path = NativePath!;
path = path[(_parent.NativePath!.Length + 1)..];
status = fileStore.CreateFile(out object fileHandle, out FileStatus fileStatus, path, AccessMask.GENERIC_READ | AccessMask.SYNCHRONIZE, SMBLibrary.FileAttributes.Normal, ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, null);
if (status != NTStatus.STATUS_SUCCESS)
{
throw new Exception($"Could not open file {NativePath} for read.");
}
return new SmbContentReader(fileStore, fileHandle, client);
});
}
public Task<IContentWriter> GetContentWriterAsync()
{
throw new NotImplementedException();
}
}
}

View File

@@ -9,7 +9,6 @@ namespace FileTime.Providers.Smb
private IReadOnlyList<IItem>? _items;
private IReadOnlyList<IContainer>? _containers;
private IReadOnlyList<IElement>? _elements;
private readonly SmbShare _smbShare;
private readonly IContainer? _parent;
public string Name { get; }
@@ -22,6 +21,7 @@ namespace FileTime.Providers.Smb
public SmbContentProvider Provider { get; }
IContentProvider IItem.Provider => Provider;
public SmbShare SmbShare { get; }
public SupportsDelete CanDelete => SupportsDelete.True;
public bool CanRename => true;
@@ -35,7 +35,7 @@ namespace FileTime.Providers.Smb
public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent)
{
_parent = parent;
_smbShare = smbShare;
SmbShare = smbShare;
Name = name;
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
@@ -43,21 +43,21 @@ namespace FileTime.Providers.Smb
Provider = contentProvider;
}
public Task<IContainer> CreateContainer(string name)
public Task<IContainer> CreateContainerAsync(string name)
{
throw new NotImplementedException();
}
public Task<IElement> CreateElement(string name)
public Task<IElement> CreateElementAsync(string name)
{
throw new NotImplementedException();
}
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
public IContainer? GetParent() => _parent;
public Task<bool> IsExists(string name)
public Task<bool> IsExistsAsync(string name)
{
throw new NotImplementedException();
}
@@ -78,8 +78,8 @@ namespace FileTime.Providers.Smb
try
{
var path = FullName![(_smbShare.FullName!.Length + 1)..];
(containers, elements) = await _smbShare.ListFolder(this, _smbShare.Name, path, token);
var path = FullName![(SmbShare.FullName!.Length + 1)..];
(containers, elements) = await SmbShare.ListFolder(this, path, token);
}
catch { }
@@ -113,7 +113,7 @@ namespace FileTime.Providers.Smb
if (_elements == null) await RefreshAsync(token);
return _elements;
}
public Task<bool> CanOpen() => Task.FromResult(true);
public Task<bool> CanOpenAsync() => Task.FromResult(true);
public void Destroy() => IsDestroyed = true;

View File

@@ -69,12 +69,12 @@ namespace FileTime.Providers.Smb
return Task.FromResult(_elements);
}
public Task<IContainer> CreateContainer(string name)
public Task<IContainer> CreateContainerAsync(string name)
{
throw new NotSupportedException();
}
public Task<IElement> CreateElement(string name)
public Task<IElement> CreateElementAsync(string name)
{
throw new NotSupportedException();
}
@@ -106,7 +106,7 @@ namespace FileTime.Providers.Smb
public IContainer? GetParent() => Provider;
public Task<bool> IsExists(string name)
public Task<bool> IsExistsAsync(string name)
{
throw new NotImplementedException();
}
@@ -120,7 +120,7 @@ namespace FileTime.Providers.Smb
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
}
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
private void DisposeSmbClient()
{
@@ -211,7 +211,7 @@ namespace FileTime.Providers.Smb
}
public Task Rename(string newName) => throw new NotSupportedException();
public Task<bool> CanOpen() => Task.FromResult(true);
public Task<bool> CanOpenAsync() => Task.FromResult(true);
public void Destroy() { }

View File

@@ -61,12 +61,12 @@ namespace FileTime.Providers.Smb
return _elements;
}
public Task<IContainer> CreateContainer(string name)
public Task<IContainer> CreateContainerAsync(string name)
{
throw new NotImplementedException();
}
public Task<IElement> CreateElement(string name)
public Task<IElement> CreateElementAsync(string name)
{
throw new NotImplementedException();
}
@@ -78,9 +78,9 @@ namespace FileTime.Providers.Smb
public IContainer? GetParent() => _parent;
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
public Task<bool> IsExists(string name)
public Task<bool> IsExistsAsync(string name)
{
throw new NotImplementedException();
}
@@ -92,7 +92,7 @@ namespace FileTime.Providers.Smb
try
{
(containers, elements) = await ListFolder(this, Name, string.Empty, token);
(containers, elements) = await ListFolder(this, string.Empty, token);
}
catch { }
@@ -111,14 +111,15 @@ namespace FileTime.Providers.Smb
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
}
public async Task<(List<IContainer> containers, List<IElement> elements)> ListFolder(IContainer parent, string shareName, string folderName, CancellationToken token = default)
public async Task<(List<IContainer> containers, List<IElement> elements)> ListFolder(IContainer parent, string folderName, CancellationToken token = default)
{
return await _smbClientContext.RunWithSmbClientAsync(client =>
{
var containers = new List<IContainer>();
var elements = new List<IElement>();
NTStatus status = NTStatus.STATUS_DATA_ERROR;
ISMBFileStore fileStore = client.TreeConnect(shareName, out status);
var status = NTStatus.STATUS_DATA_ERROR;
var fileStore = TreeConnect(client, out status);
if (status == NTStatus.STATUS_SUCCESS)
{
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);
@@ -137,7 +138,7 @@ namespace FileTime.Providers.Smb
}
else
{
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent));
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, this, parent, _smbClientContext));
}
}
}
@@ -151,8 +152,13 @@ namespace FileTime.Providers.Smb
});
}
internal ISMBFileStore TreeConnect(ISMBClient client, out NTStatus status)
{
return client.TreeConnect(Name, out status);
}
public Task Rename(string newName) => throw new NotSupportedException();
public Task<bool> CanOpen() => Task.FromResult(true);
public Task<bool> CanOpenAsync() => Task.FromResult(true);
public void Destroy() { }