Merge pull request #6188 from duplicati/feature/make-file-backend-compliant

Make file backend error compliant
This commit is contained in:
Kenneth Skovhede
2025-04-23 17:20:10 +02:00
committed by GitHub
2 changed files with 86 additions and 45 deletions
+85 -45
View File
@@ -155,15 +155,9 @@ namespace Duplicati.Library.Backend
#region IBackendInterface Members
public string DisplayName
{
get { return Strings.FileBackend.DisplayName; }
}
public string DisplayName => Strings.FileBackend.DisplayName;
public string ProtocolKey
{
get { return "file"; }
}
public string ProtocolKey => "file";
/// <inheritdoc />
public async IAsyncEnumerable<IFileEntry> ListAsync([EnumeratorCancellation] CancellationToken cancelToken)
@@ -185,63 +179,107 @@ namespace Duplicati.Library.Backend
private static Random random = new Random();
public async Task Put(string remotename, System.IO.Stream stream, CancellationToken cancelToken)
{
using(System.IO.FileStream writestream = systemIO.FileCreate(GetRemoteName(remotename)))
try
{
if (random.NextDouble() > 0.6666)
throw new Exception("Random upload failure");
await Utility.Utility.CopyStreamAsync(stream, writestream, cancelToken).ConfigureAwait(false);
using(System.IO.FileStream writestream = systemIO.FileCreate(GetRemoteName(remotename)))
{
if (random.NextDouble() > 0.6666)
throw new Exception("Random upload failure");
await Utility.Utility.CopyStreamAsync(stream, writestream, cancelToken).ConfigureAwait(false);
}
}
catch (Exception ex)
{
MapException(ex);
throw;
}
}
#else
public async Task PutAsync(string targetFilename, Stream sourceStream, CancellationToken cancelToken)
{
string targetFilePath = GetRemoteName(targetFilename);
long copiedBytes = 0;
using (var targetStream = systemIO.FileCreate(targetFilePath))
using (var timeoutStream = targetStream.ObserveWriteTimeout(m_timeouts.ReadWriteTimeout))
copiedBytes = await Utility.Utility.CopyStreamAsync(sourceStream, timeoutStream, true, cancelToken).ConfigureAwait(false);
try
{
string targetFilePath = GetRemoteName(targetFilename);
long copiedBytes = 0;
using (var targetStream = systemIO.FileCreate(targetFilePath))
using (var timeoutStream = targetStream.ObserveWriteTimeout(m_timeouts.ReadWriteTimeout))
copiedBytes = await Utility.Utility.CopyStreamAsync(sourceStream, timeoutStream, true, cancelToken).ConfigureAwait(false);
VerifyMatchingSize(targetFilePath, sourceStream, copiedBytes);
}
catch (Exception ex)
{
MapException(ex);
throw;
}
VerifyMatchingSize(targetFilePath, sourceStream, copiedBytes);
}
#endif
public async Task GetAsync(string remotename, Stream stream, CancellationToken cancelToken)
{
// FileOpenRead has flags System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read
using (var readstream = systemIO.FileOpenRead(GetRemoteName(remotename)))
using (var timeoutStream = readstream.ObserveReadTimeout(m_timeouts.ReadWriteTimeout))
await Utility.Utility.CopyStreamAsync(timeoutStream, stream, true, cancelToken).ConfigureAwait(false);
try
{
// FileOpenRead has flags System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read
using (var readstream = systemIO.FileOpenRead(GetRemoteName(remotename)))
using (var timeoutStream = readstream.ObserveReadTimeout(m_timeouts.ReadWriteTimeout))
await Utility.Utility.CopyStreamAsync(timeoutStream, stream, true, cancelToken).ConfigureAwait(false);
}
catch (Exception ex)
{
MapException(ex);
throw;
}
}
public Task PutAsync(string targetFilename, string sourceFilePath, CancellationToken cancelToken)
{
string targetFilePath = GetRemoteName(targetFilename);
if (m_moveFile)
try
{
if (systemIO.FileExists(targetFilePath))
systemIO.FileDelete(targetFilePath);
string targetFilePath = GetRemoteName(targetFilename);
if (m_moveFile)
{
if (systemIO.FileExists(targetFilePath))
systemIO.FileDelete(targetFilePath);
var sourceFileInfo = new FileInfo(sourceFilePath);
var sourceFileLength = sourceFileInfo.Exists ? (long?)sourceFileInfo.Length : null;
var sourceFileInfo = new FileInfo(sourceFilePath);
var sourceFileLength = sourceFileInfo.Exists ? (long?)sourceFileInfo.Length : null;
systemIO.FileMove(sourceFilePath, targetFilePath);
if (m_verifyDestinationLength)
VerifyMatchingSize(targetFilePath, null, sourceFileLength);
systemIO.FileMove(sourceFilePath, targetFilePath);
if (m_verifyDestinationLength)
VerifyMatchingSize(targetFilePath, null, sourceFileLength);
}
else
{
systemIO.FileCopy(sourceFilePath, targetFilePath, true);
if (m_verifyDestinationLength)
VerifyMatchingSize(targetFilePath, sourceFilePath);
}
return Task.CompletedTask;
}
else
catch (Exception ex)
{
systemIO.FileCopy(sourceFilePath, targetFilePath, true);
if (m_verifyDestinationLength)
VerifyMatchingSize(targetFilePath, sourceFilePath);
MapException(ex);
throw;
}
return Task.CompletedTask;
}
public Task GetAsync(string remotename, string filename, CancellationToken cancelToken)
{
systemIO.FileCopy(GetRemoteName(remotename), filename, true);
return Task.CompletedTask;
try
{
systemIO.FileCopy(GetRemoteName(remotename), filename, true);
return Task.CompletedTask;
}
catch (Exception ex)
{
MapException(ex);
throw;
}
}
public async Task DeleteAsync(string remotename, CancellationToken cancelToken)
@@ -274,13 +312,7 @@ namespace Duplicati.Library.Backend
}
}
public string Description
{
get
{
return Strings.FileBackend.Description;
}
}
public string Description => Strings.FileBackend.Description;
public Task TestAsync(CancellationToken cancelToken)
=> this.TestListAsync(cancelToken);
@@ -307,6 +339,14 @@ namespace Duplicati.Library.Backend
#endregion
private static void MapException(Exception ex)
{
if (ex is DirectoryNotFoundException)
throw new FolderMissingException(Strings.FileBackend.FolderMissingError(ex.Message), ex);
if (ex is FileNotFoundException)
throw new FileMissingException(Strings.FileBackend.FileNotFoundError(ex.Message), ex);
}
private DriveInfo? GetDrive()
{
string root;
@@ -37,5 +37,6 @@ namespace Duplicati.Library.Backend.Strings
public static string ForceReauthShort { get { return LC.L(@"Force authentication against remote share"); } }
public static string DisableLengthVerificationLong { get { return LC.L(@"As an extra precaution the uploaded file length will be checked against the local source length."); } }
public static string DisableLengthVerificationShort { get { return LC.L(@"Disable length verification"); } }
public static string FileNotFoundError(string filename) { return LC.L(@"File not found: {0}", filename); }
}
}