From ca5159adf79ebcd5696e784cf91543125516ac86 Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Tue, 22 Apr 2025 22:56:47 +0200 Subject: [PATCH] Make file backend error compliant This PR makes the file backend throw the correct exception types for missing files and folders, so it works with the backend tester without errors. --- Duplicati/Library/Backend/File/FileBackend.cs | 130 ++++++++++++------ Duplicati/Library/Backend/File/Strings.cs | 1 + 2 files changed, 86 insertions(+), 45 deletions(-) diff --git a/Duplicati/Library/Backend/File/FileBackend.cs b/Duplicati/Library/Backend/File/FileBackend.cs index 2d3471d1f..d63f56941 100644 --- a/Duplicati/Library/Backend/File/FileBackend.cs +++ b/Duplicati/Library/Backend/File/FileBackend.cs @@ -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"; /// public async IAsyncEnumerable 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; diff --git a/Duplicati/Library/Backend/File/Strings.cs b/Duplicati/Library/Backend/File/Strings.cs index 47f367db2..27a5213b2 100644 --- a/Duplicati/Library/Backend/File/Strings.cs +++ b/Duplicati/Library/Backend/File/Strings.cs @@ -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); } } }