using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using FubarDev.FtpServer.FileSystem;
namespace ShadowEditor.Server.Remote.FileSystem
{
///
/// A implementation that uses the
/// standard .NET functionality to access the file system.
///
public class RemoteFileSystem : IUnixFileSystem
{
private bool _disposedValue;
///
/// Initializes a new instance of the class.
///
/// The path to use as root
/// Allow deletion of non-empty directories?
public RemoteFileSystem(string rootPath, bool allowNonEmptyDirectoryDelete)
{
FileSystemEntryComparer = StringComparer.OrdinalIgnoreCase;
Root = new RemoteDirectoryEntry(this, Directory.CreateDirectory(rootPath), true);
SupportsNonEmptyDirectoryDelete = allowNonEmptyDirectoryDelete;
}
///
public bool SupportsNonEmptyDirectoryDelete { get; }
///
public StringComparer FileSystemEntryComparer { get; }
///
public IUnixDirectoryEntry Root { get; }
///
public bool SupportsAppend => true;
///
public Task> GetEntriesAsync(IUnixDirectoryEntry directoryEntry, CancellationToken cancellationToken)
{
var result = new List();
var searchDirInfo = ((RemoteDirectoryEntry)directoryEntry).Info;
foreach (var info in searchDirInfo.EnumerateFileSystemInfos())
{
var dirInfo = info as DirectoryInfo;
if (dirInfo != null)
{
result.Add(new RemoteDirectoryEntry(this, dirInfo, false));
}
else
{
var fileInfo = info as FileInfo;
if (fileInfo != null)
{
result.Add(new RemoteFileEntry(this, fileInfo));
}
}
}
return Task.FromResult>(result);
}
///
public Task GetEntryByNameAsync(IUnixDirectoryEntry directoryEntry, string name, CancellationToken cancellationToken)
{
var searchDirInfo = ((RemoteDirectoryEntry)directoryEntry).Info;
var fullPath = Path.Combine(searchDirInfo.FullName, name);
IUnixFileSystemEntry result;
if (File.Exists(fullPath))
result = new RemoteFileEntry(this, new FileInfo(fullPath));
else if (Directory.Exists(fullPath))
result = new RemoteDirectoryEntry(this, new DirectoryInfo(fullPath), false);
else
result = null;
return Task.FromResult(result);
}
///
public Task MoveAsync(IUnixDirectoryEntry parent, IUnixFileSystemEntry source, IUnixDirectoryEntry target, string fileName, CancellationToken cancellationToken)
{
var targetEntry = (RemoteDirectoryEntry)target;
var targetName = Path.Combine(targetEntry.Info.FullName, fileName);
var sourceFileEntry = source as RemoteFileEntry;
if (sourceFileEntry != null)
{
sourceFileEntry.Info.MoveTo(targetName);
return Task.FromResult(new RemoteFileEntry(this, new FileInfo(targetName)));
}
var sourceDirEntry = (RemoteDirectoryEntry)source;
sourceDirEntry.Info.MoveTo(targetName);
return Task.FromResult(new RemoteDirectoryEntry(this, new DirectoryInfo(targetName), false));
}
///
public Task UnlinkAsync(IUnixFileSystemEntry entry, CancellationToken cancellationToken)
{
var dirEntry = entry as RemoteDirectoryEntry;
if (dirEntry != null)
{
dirEntry.Info.Delete(SupportsNonEmptyDirectoryDelete);
}
else
{
var fileEntry = (RemoteFileEntry)entry;
fileEntry.Info.Delete();
}
return Task.FromResult(0);
}
///
public Task CreateDirectoryAsync(IUnixDirectoryEntry targetDirectory, string directoryName, CancellationToken cancellationToken)
{
var targetEntry = (RemoteDirectoryEntry)targetDirectory;
var newDirInfo = targetEntry.Info.CreateSubdirectory(directoryName);
return Task.FromResult(new RemoteDirectoryEntry(this, newDirInfo, false));
}
///
public Task OpenReadAsync(IUnixFileEntry fileEntry, long startPosition, CancellationToken cancellationToken)
{
var fileInfo = ((RemoteFileEntry)fileEntry).Info;
var input = fileInfo.OpenRead();
if (startPosition != 0)
input.Seek(startPosition, SeekOrigin.Begin);
return Task.FromResult(input);
}
///
public async Task AppendAsync(IUnixFileEntry fileEntry, long? startPosition, Stream data, CancellationToken cancellationToken)
{
var fileInfo = ((RemoteFileEntry)fileEntry).Info;
using (var output = fileInfo.OpenWrite())
{
if (startPosition == null)
startPosition = fileInfo.Length;
output.Seek(startPosition.Value, SeekOrigin.Begin);
await data.CopyToAsync(output, 4096, cancellationToken);
}
return null;
}
///
public async Task CreateAsync(IUnixDirectoryEntry targetDirectory, string fileName, Stream data, CancellationToken cancellationToken)
{
var targetEntry = (RemoteDirectoryEntry)targetDirectory;
var fileInfo = new FileInfo(Path.Combine(targetEntry.Info.FullName, fileName));
using (var output = fileInfo.Create())
{
await data.CopyToAsync(output, 4096, cancellationToken);
}
return null;
}
///
public async Task ReplaceAsync(IUnixFileEntry fileEntry, Stream data, CancellationToken cancellationToken)
{
var fileInfo = ((RemoteFileEntry)fileEntry).Info;
using (var output = fileInfo.OpenWrite())
{
await data.CopyToAsync(output, 4096, cancellationToken);
output.SetLength(output.Position);
}
return null;
}
///
/// Sets the modify/access/create timestamp of a file system item
///
/// The to change the timestamp for
/// The modification timestamp
/// The access timestamp
/// The creation timestamp
/// The cancellation token
/// The modified
public Task SetMacTimeAsync(IUnixFileSystemEntry entry, DateTimeOffset? modify, DateTimeOffset? access, DateTimeOffset? create, CancellationToken cancellationToken)
{
var dirEntry = entry as RemoteDirectoryEntry;
var fileEntry = entry as RemoteFileEntry;
var item = dirEntry == null ? (FileSystemInfo)fileEntry.Info : dirEntry.Info;
if (access != null)
item.LastAccessTimeUtc = access.Value.UtcDateTime;
if (modify != null)
item.LastWriteTimeUtc = modify.Value.UtcDateTime;
if (create != null)
item.CreationTimeUtc = create.Value.UtcDateTime;
if (dirEntry != null)
return Task.FromResult(new RemoteDirectoryEntry(this, (DirectoryInfo)item, dirEntry.IsRoot));
return Task.FromResult(new RemoteFileEntry(this, (FileInfo)item));
}
///
public void Dispose()
{
Dispose(true);
}
///
/// Dispose the object
///
/// true when called from
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
// Nothing to dispose
}
_disposedValue = true;
}
}
}
}