From 094579b352b68bdd268a1bbdbc6f5cddd5df625b Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Sun, 12 Jul 2026 11:47:26 +0200 Subject: [PATCH] Nit: Explicitly pass flag indicating if parity repair is allowed --- .../Library/Main/Backend/BackendManager.cs | 23 +++++++++++-------- Duplicati/Library/Main/IBackendManager.cs | 20 ++++++++-------- .../Library/Main/Operation/CompactHandler.cs | 2 +- .../Main/Operation/ListChangesHandler.cs | 4 ++-- .../Main/Operation/ListControlFilesHandler.cs | 2 +- .../Main/Operation/ListFilesHandler.cs | 6 ++--- .../Main/Operation/RecreateDatabaseHandler.cs | 8 +++---- .../Library/Main/Operation/RepairHandler.cs | 10 ++++---- .../Operation/Restore/VolumeDownloader.cs | 2 +- .../Operation/RestoreControlFilesHandler.cs | 2 +- .../Library/Main/Operation/RestoreHandler.cs | 2 +- .../Library/Main/Operation/TestHandler.cs | 6 ++--- .../UnitTest/LockingDeleteAndCompactTests.cs | 10 ++++---- Duplicati/UnitTest/SetLocksHandlerTests.cs | 10 ++++---- 14 files changed, 56 insertions(+), 51 deletions(-) diff --git a/Duplicati/Library/Main/Backend/BackendManager.cs b/Duplicati/Library/Main/Backend/BackendManager.cs index f8706dd33..034d8d18b 100644 --- a/Duplicati/Library/Main/Backend/BackendManager.cs +++ b/Duplicati/Library/Main/Backend/BackendManager.cs @@ -309,9 +309,10 @@ internal partial class BackendManager : IBackendManager /// The name of the remote file /// The hash of the remote file, for verification /// The size of the remote file, for verification + /// Whether a failed download may be repaired using parity data /// The cancellation token /// A temporary file with the contents of the remote file - public async Task GetAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true) + public async Task GetAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken) { var op = new GetOperation(remotename, size, context, cancelToken) { @@ -331,9 +332,10 @@ internal partial class BackendManager : IBackendManager /// The name of the remote file /// The hash of the remote file, for verification /// The size of the remote file, for verification + /// Whether a failed download may be repaired using parity data /// The cancellation token /// A temporary file with the contents of the remote file - public async Task GetDirectAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true) + public async Task GetDirectAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken) { var op = new GetOperation(remotename, size, context, cancelToken) { @@ -365,9 +367,10 @@ internal partial class BackendManager : IBackendManager /// The name of the remote file /// The hash of the remote file, or null if not known /// The size of the remote file, or -1 if not known + /// Whether a failed download may be repaired using parity data /// The cancellation token /// A tuple containing the temporary file, the hash of the file, and the size of the file - public async Task<(TempFile File, string Hash, long Size)> GetWithInfoAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true) + public async Task<(TempFile File, string Hash, long Size)> GetWithInfoAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken) { var op = new GetOperation(remotename, size, context, cancelToken) { @@ -557,22 +560,23 @@ internal partial class BackendManager : IBackendManager /// Performs a download of the files specified, with pre-fetch to overlap the download and processing /// /// The volumes to download + /// Whether a failed download may be repaired using parity data /// The cancellation token /// The downloaded files and the volume they came from - public async IAsyncEnumerable<(TempFile File, string Hash, long Size, string Name)> GetFilesOverlappedAsync(IEnumerable volumes, [EnumeratorCancellation] CancellationToken cancelToken, bool allowParityRepair = true) + public async IAsyncEnumerable<(TempFile File, string Hash, long Size, string Name)> GetFilesOverlappedAsync(IEnumerable volumes, bool allowParityRepair, [EnumeratorCancellation] CancellationToken cancelToken) { var prevVolume = volumes.FirstOrDefault(); if (prevVolume == null) yield break; // Get the first volume, so we do not have pending parallel transfers - var prevResult = await GetWithInfoAsync(prevVolume.Name, prevVolume.Hash, prevVolume.Size, cancelToken, allowParityRepair) + var prevResult = await GetWithInfoAsync(prevVolume.Name, prevVolume.Hash, prevVolume.Size, allowParityRepair, cancelToken) .ConfigureAwait(false); foreach (var volume in volumes.Skip(1)) { // Prepare the next volume, while processing the previous one - var nextTask = GetWithInfoAsync(volume.Name, volume.Hash, volume.Size, cancelToken, allowParityRepair); + var nextTask = GetWithInfoAsync(volume.Name, volume.Hash, volume.Size, allowParityRepair, cancelToken); // Assuming we do not throw while yielding, otherwise we would need to dispose nextTask yield return (prevResult.File, prevResult.Hash, prevResult.Size, prevVolume.Name); @@ -592,22 +596,23 @@ internal partial class BackendManager : IBackendManager /// Performs a direct download of the files specified, with pre-fetch to overlap the download and processing /// /// The volumes to download + /// Whether a failed download may be repaired using parity data /// The cancellation token /// The downloaded files and the volume they came from - public async IAsyncEnumerable<(TempFile File, string Name)> GetFilesOverlappedDirectAsync(IEnumerable volumes, [EnumeratorCancellation] CancellationToken cancelToken, bool allowParityRepair = true) + public async IAsyncEnumerable<(TempFile File, string Name)> GetFilesOverlappedDirectAsync(IEnumerable volumes, bool allowParityRepair, [EnumeratorCancellation] CancellationToken cancelToken) { var prevVolume = volumes.FirstOrDefault(); if (prevVolume == null) yield break; // Get the first volume, so we do not have pending parallel transfers - var prevResult = await GetDirectAsync(prevVolume.Name, prevVolume.Hash, prevVolume.Size, cancelToken, allowParityRepair) + var prevResult = await GetDirectAsync(prevVolume.Name, prevVolume.Hash, prevVolume.Size, allowParityRepair, cancelToken) .ConfigureAwait(false); foreach (var volume in volumes.Skip(1)) { // Prepare the next volume, while processing the previous one - var nextTask = GetDirectAsync(volume.Name, volume.Hash, volume.Size, cancelToken, allowParityRepair); + var nextTask = GetDirectAsync(volume.Name, volume.Hash, volume.Size, allowParityRepair, cancelToken); // Assuming we do not throw while yielding, otherwise we would need to dispose nextTask yield return (prevResult, prevVolume.Name); diff --git a/Duplicati/Library/Main/IBackendManager.cs b/Duplicati/Library/Main/IBackendManager.cs index 36cc55f21..4d0417b83 100644 --- a/Duplicati/Library/Main/IBackendManager.cs +++ b/Duplicati/Library/Main/IBackendManager.cs @@ -144,10 +144,10 @@ internal interface IBackendManager : IDisposable /// The name of the file to get /// The hash of the file to get, or null if not known /// The size of the file to get, or -1 if not known - /// The cancellation token /// Whether a failed download may be repaired using parity data. Disable for the test/verification flow so damaged files are reported. + /// The cancellation token /// The file, hash, and size - Task<(TempFile File, string Hash, long Size)> GetWithInfoAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true); + Task<(TempFile File, string Hash, long Size)> GetWithInfoAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken); /// /// Gets a file from the backend @@ -155,10 +155,10 @@ internal interface IBackendManager : IDisposable /// The name of the file to get /// The hash of the file to get, or null if not known /// The size of the file to get, or -1 if not known - /// The cancellation token /// Whether a failed download may be repaired using parity data. Disable for the test/verification flow so damaged files are reported. + /// The cancellation token /// The downloaded file - Task GetAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true); + Task GetAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken); /// /// Gets a file from the backend without decrypting it @@ -166,28 +166,28 @@ internal interface IBackendManager : IDisposable /// The name of the remote volume /// The hash of the volume /// The size of the volume - /// The cancellation token /// Whether a failed download may be repaired using parity data. Disable for the test/verification flow so damaged files are reported. + /// The cancellation token /// The downloaded file - Task GetDirectAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true); + Task GetDirectAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken); /// /// Performs a download of the files specified, with pre-fetch to overlap the download and processing /// /// The volumes to download - /// The cancellation token /// Whether a failed download may be repaired using parity data. Disable for the test/verification flow so damaged files are reported. + /// The cancellation token /// The downloaded files, hash, size, and name - IAsyncEnumerable<(TempFile File, string Hash, long Size, string Name)> GetFilesOverlappedAsync(IEnumerable volumes, CancellationToken cancelToken, bool allowParityRepair = true); + IAsyncEnumerable<(TempFile File, string Hash, long Size, string Name)> GetFilesOverlappedAsync(IEnumerable volumes, bool allowParityRepair, CancellationToken cancelToken); /// /// Performs a direct download of the files specified, with pre-fetch to overlap the download and processing /// /// The volumes to download - /// The cancellation token /// Whether a failed download may be repaired using parity data. Disable for the test/verification flow so damaged files are reported. + /// The cancellation token /// The downloaded files and the volume they came from - IAsyncEnumerable<(TempFile File, string Name)> GetFilesOverlappedDirectAsync(IEnumerable volumes, CancellationToken cancelToken, bool allowParityRepair = true); + IAsyncEnumerable<(TempFile File, string Name)> GetFilesOverlappedDirectAsync(IEnumerable volumes, bool allowParityRepair, CancellationToken cancelToken); /// /// Flushes the database messages to the database diff --git a/Duplicati/Library/Main/Operation/CompactHandler.cs b/Duplicati/Library/Main/Operation/CompactHandler.cs index 42c4f0459..ef9d9b41d 100644 --- a/Duplicati/Library/Main/Operation/CompactHandler.cs +++ b/Duplicati/Library/Main/Operation/CompactHandler.cs @@ -194,7 +194,7 @@ namespace Duplicati.Library.Main.Operation using (var q = await db.CreateBlockQueryHelperAsync(m_result.TaskControl.ProgressToken).ConfigureAwait(false)) { - await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(volumesToDownload, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) + await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(volumesToDownload, allowParityRepair: true, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) { using (tmpfile) { diff --git a/Duplicati/Library/Main/Operation/ListChangesHandler.cs b/Duplicati/Library/Main/Operation/ListChangesHandler.cs index 4e996d98b..c51477e18 100644 --- a/Duplicati/Library/Main/Operation/ListChangesHandler.cs +++ b/Duplicati/Library/Main/Operation/ListChangesHandler.cs @@ -146,7 +146,7 @@ namespace Duplicati.Library.Main.Operation return; using (new Logging.Timer(LOGTAG, "InsertBaseFiles", "Inserting base files into database")) - using (var tmpfile = await backendManager.GetAsync(baseFile.File.Name, null, baseFile.File.Size, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) + using (var tmpfile = await backendManager.GetAsync(baseFile.File.Name, null, baseFile.File.Size, allowParityRepair: true, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) using (var rd = new Volumes.FilesetVolumeReader(RestoreHandler.GetCompressionModule(baseFile.File.Name), tmpfile, m_options)) foreach (var f in rd.Files) if (FilterExpression.Matches(filter, f.Path)) @@ -158,7 +158,7 @@ namespace Duplicati.Library.Main.Operation return; using (new Logging.Timer(LOGTAG, "InsertCompareFiles", "Inserting compare files into database")) - using (var tmpfile = await backendManager.GetAsync(compareFile.File.Name, null, compareFile.File.Size, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) + using (var tmpfile = await backendManager.GetAsync(compareFile.File.Name, null, compareFile.File.Size, allowParityRepair: true, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) using (var rd = new Volumes.FilesetVolumeReader(RestoreHandler.GetCompressionModule(compareFile.File.Name), tmpfile, m_options)) foreach (var f in rd.Files) if (FilterExpression.Matches(filter, f.Path)) diff --git a/Duplicati/Library/Main/Operation/ListControlFilesHandler.cs b/Duplicati/Library/Main/Operation/ListControlFilesHandler.cs index f2efbfee2..0edfcd7e8 100644 --- a/Duplicati/Library/Main/Operation/ListControlFilesHandler.cs +++ b/Duplicati/Library/Main/Operation/ListControlFilesHandler.cs @@ -66,7 +66,7 @@ namespace Duplicati.Library.Main.Operation .ConfigureAwait(false); var files = new List(); - using (var tmpfile = await backendManager.GetAsync(file.Name, entry.Hash, entry.Size < 0 ? file.Size : entry.Size, cancellationToken).ConfigureAwait(false)) + using (var tmpfile = await backendManager.GetAsync(file.Name, entry.Hash, entry.Size < 0 ? file.Size : entry.Size, allowParityRepair: true, cancellationToken).ConfigureAwait(false)) using (var tmp = new Volumes.FilesetVolumeReader(RestoreHandler.GetCompressionModule(file.Name), tmpfile, m_options)) foreach (var cf in tmp.ControlFiles) if (Library.Utility.FilterExpression.Matches(filter, cf.Key)) diff --git a/Duplicati/Library/Main/Operation/ListFilesHandler.cs b/Duplicati/Library/Main/Operation/ListFilesHandler.cs index 921c0183d..da1ced578 100644 --- a/Duplicati/Library/Main/Operation/ListFilesHandler.cs +++ b/Duplicati/Library/Main/Operation/ListFilesHandler.cs @@ -160,7 +160,7 @@ namespace Duplicati.Library.Main.Operation if (!await m_result.TaskControl.ProgressRendevouzAsync().ConfigureAwait(false)) return; - using (var tmpfile = await backendManager.GetAsync(firstEntry.File.Name, null, firstEntry.File.Size, cancellationToken).ConfigureAwait(false)) + using (var tmpfile = await backendManager.GetAsync(firstEntry.File.Name, null, firstEntry.File.Size, allowParityRepair: true, cancellationToken).ConfigureAwait(false)) { VolumeReaderBase.UpdateOptionsFromManifest(RestoreHandler.GetCompressionModule(firstEntry.File.Name), tmpfile, m_options); using (var rd = new FilesetVolumeReader(RestoreHandler.GetCompressionModule(firstEntry.File.Name), tmpfile, m_options)) @@ -196,7 +196,7 @@ namespace Duplicati.Library.Main.Operation long flindex = 1; var filteredListMap = filteredList.ToDictionary(x => x.Value.File.Name, x => x.Value); - await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(filteredList.Select(x => new RemoteVolumeMapper(x.Value)), cancellationToken).ConfigureAwait(false)) + await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(filteredList.Select(x => new RemoteVolumeMapper(x.Value)), allowParityRepair: true, cancellationToken).ConfigureAwait(false)) { var flentry = filteredListMap[name]; using (tmpfile) @@ -256,7 +256,7 @@ namespace Duplicati.Library.Main.Operation { var list = new List(); var map = filteredList.ToDictionary(x => x.Value.File.Name, x => x); - await foreach (var (file, hash, size, name) in backendManager.GetFilesOverlappedAsync(filteredList.Select(x => new RemoteVolumeMapper(x.Value)), cancelToken).ConfigureAwait(false)) + await foreach (var (file, hash, size, name) in backendManager.GetFilesOverlappedAsync(filteredList.Select(x => new RemoteVolumeMapper(x.Value)), allowParityRepair: true, cancelToken).ConfigureAwait(false)) { // We must obtain the partial/full status from the fileset file in the dlist files. // Without this, the restore dialog will show all versions as full, or all versions diff --git a/Duplicati/Library/Main/Operation/RecreateDatabaseHandler.cs b/Duplicati/Library/Main/Operation/RecreateDatabaseHandler.cs index 6df32dd8f..189991a29 100644 --- a/Duplicati/Library/Main/Operation/RecreateDatabaseHandler.cs +++ b/Duplicati/Library/Main/Operation/RecreateDatabaseHandler.cs @@ -237,7 +237,7 @@ namespace Duplicati.Library.Main.Operation var isFirstFilelist = true; // At this point, we do not know the hashing used to verify the files, so we need to use direct download, and manually decrypt the files // so we can calculate the hash AFTER we have read the manifest content and updated the options - await foreach (var (tmpencfile, name) in backendManager.GetFilesOverlappedDirectAsync(filelistWork, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) + await foreach (var (tmpencfile, name) in backendManager.GetFilesOverlappedDirectAsync(filelistWork, allowParityRepair: true, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) { try { @@ -364,7 +364,7 @@ namespace Duplicati.Library.Main.Operation progress = 0; - await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(indexfiles, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) + await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(indexfiles, allowParityRepair: true, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) { try { @@ -546,7 +546,7 @@ namespace Duplicati.Library.Main.Operation } progress = 0; - await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(lst, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) + await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(lst, allowParityRepair: true, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) { try { @@ -776,7 +776,7 @@ namespace Duplicati.Library.Main.Operation // Download all required block volumes and extract metadata blocks. var volumesToDownload = volumeInfo.Values.Cast().ToList(); - await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(volumesToDownload, cancellationToken).ConfigureAwait(false)) + await foreach (var (tmpfile, hash, size, name) in backendManager.GetFilesOverlappedAsync(volumesToDownload, allowParityRepair: true, cancellationToken).ConfigureAwait(false)) { using (tmpfile) { diff --git a/Duplicati/Library/Main/Operation/RepairHandler.cs b/Duplicati/Library/Main/Operation/RepairHandler.cs index 701b05fac..0685de3ce 100644 --- a/Duplicati/Library/Main/Operation/RepairHandler.cs +++ b/Duplicati/Library/Main/Operation/RepairHandler.cs @@ -265,7 +265,7 @@ namespace Duplicati.Library.Main.Operation m_result.OperationProgressUpdater.UpdateProgress((float)progress / targetProgess); KeyValuePair>> res; - var (tf, hash, size) = await backendManager.GetWithInfoAsync(n.Name, n.Hash, n.Size, cancellationToken).ConfigureAwait(false); + var (tf, hash, size) = await backendManager.GetWithInfoAsync(n.Name, n.Hash, n.Size, allowParityRepair: true, cancellationToken).ConfigureAwait(false); using (tf) res = await TestHandler.TestVolumeInternalsAsync(testdb, n, tf, m_options, 1, cancellationToken) @@ -322,7 +322,7 @@ namespace Duplicati.Library.Main.Operation { try { - (var tf, var hash, var size) = await backendManager.GetWithInfoAsync(n.File.Name, null, n.File.Size, cancellationToken).ConfigureAwait(false); + (var tf, var hash, var size) = await backendManager.GetWithInfoAsync(n.File.Name, null, n.File.Size, allowParityRepair: true, cancellationToken).ConfigureAwait(false); using (tf) using (var ifr = new IndexVolumeReader(n.CompressionModule, tf, m_options, m_options.BlockhashSize)) { @@ -479,7 +479,7 @@ namespace Duplicati.Library.Main.Operation var remoteVolume = await db .GetRemoteVolumeAsync(volumename, cancellationToken) .ConfigureAwait(false); - using var tmpfile = await backendManager.GetAsync(remoteVolume.Name, remoteVolume.Hash, remoteVolume.Size, cancellationToken).ConfigureAwait(false); + using var tmpfile = await backendManager.GetAsync(remoteVolume.Name, remoteVolume.Hash, remoteVolume.Size, allowParityRepair: true, cancellationToken).ConfigureAwait(false); var parsed = VolumeBase.ParseFilename(remoteVolume.Name); using var stream = new FileStream(tmpfile, FileMode.Open, FileAccess.Read, FileShare.Read); using var compressor = DynamicLoader.CompressionLoader.GetModule(parsed.CompressionModule, stream, ArchiveMode.Read, m_options.RawOptions); @@ -1245,7 +1245,7 @@ namespace Duplicati.Library.Main.Operation await EmitBlockListBlock().ConfigureAwait(false); //Then we grab all remote volumes that have the missing blocks - await foreach (var (tmpfile, _, _, name) in backendManager.GetFilesOverlappedAsync(await mbl.GetMissingBlockSourcesAsync(cancellationToken).ToListAsync(cancellationToken: cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false)) + await foreach (var (tmpfile, _, _, name) in backendManager.GetFilesOverlappedAsync(await mbl.GetMissingBlockSourcesAsync(cancellationToken).ToListAsync(cancellationToken: cancellationToken).ConfigureAwait(false), allowParityRepair: true, cancellationToken).ConfigureAwait(false)) { try { @@ -1357,7 +1357,7 @@ namespace Duplicati.Library.Main.Operation .GetRemoteVolumeFromFilesetIDAsync(entry.Key, m_result.TaskControl.ProgressToken) .ConfigureAwait(false); var parsed = VolumeBase.ParseFilename(volume.Name); - using var tmpfile = await backendManager.GetAsync(volume.Name, volume.Hash, volume.Size, CancellationToken.None).ConfigureAwait(false); + using var tmpfile = await backendManager.GetAsync(volume.Name, volume.Hash, volume.Size, allowParityRepair: true, CancellationToken.None).ConfigureAwait(false); using var stream = new FileStream(tmpfile, FileMode.Open, FileAccess.Read, FileShare.Read); using var compressor = DynamicLoader.CompressionLoader.GetModule(parsed.CompressionModule, stream, ArchiveMode.Read, m_options.RawOptions); if (compressor == null) diff --git a/Duplicati/Library/Main/Operation/Restore/VolumeDownloader.cs b/Duplicati/Library/Main/Operation/Restore/VolumeDownloader.cs index 44129bf7d..e03c28262 100644 --- a/Duplicati/Library/Main/Operation/Restore/VolumeDownloader.cs +++ b/Duplicati/Library/Main/Operation/Restore/VolumeDownloader.cs @@ -83,7 +83,7 @@ namespace Duplicati.Library.Main.Operation.Restore .ConfigureAwait(false); try { - f = await backend.GetDirectAsync(volume_name, hash, size, results.TaskControl.TransferToken).ConfigureAwait(false); + f = await backend.GetDirectAsync(volume_name, hash, size, allowParityRepair: true, results.TaskControl.TransferToken).ConfigureAwait(false); } catch (Exception) { diff --git a/Duplicati/Library/Main/Operation/RestoreControlFilesHandler.cs b/Duplicati/Library/Main/Operation/RestoreControlFilesHandler.cs index eee092c2e..9b3079547 100644 --- a/Duplicati/Library/Main/Operation/RestoreControlFilesHandler.cs +++ b/Duplicati/Library/Main/Operation/RestoreControlFilesHandler.cs @@ -77,7 +77,7 @@ namespace Duplicati.Library.Main.Operation .ConfigureAwait(false); var res = new List(); - using (var tmpfile = await backendManager.GetAsync(file.Name, entry.Hash, entry.Size < 0 ? file.Size : entry.Size, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) + using (var tmpfile = await backendManager.GetAsync(file.Name, entry.Hash, entry.Size < 0 ? file.Size : entry.Size, allowParityRepair: true, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) using (var tmp = new Volumes.FilesetVolumeReader(RestoreHandler.GetCompressionModule(file.Name), tmpfile, m_options)) foreach (var cf in tmp.ControlFiles) if (FilterExpression.Matches(filter, cf.Key)) diff --git a/Duplicati/Library/Main/Operation/RestoreHandler.cs b/Duplicati/Library/Main/Operation/RestoreHandler.cs index 8d2645760..d23ab278e 100644 --- a/Duplicati/Library/Main/Operation/RestoreHandler.cs +++ b/Duplicati/Library/Main/Operation/RestoreHandler.cs @@ -1174,7 +1174,7 @@ namespace Duplicati.Library.Main.Operation var brokenFiles = new List(); using (new Logging.Timer(LOGTAG, "PatchWithBlocklist", "PatchWithBlocklist")) - await foreach (var (tmpfile, _, _, name) in backendManager.GetFilesOverlappedAsync(volumes, cancellationToken).ConfigureAwait(false)) + await foreach (var (tmpfile, _, _, name) in backendManager.GetFilesOverlappedAsync(volumes, allowParityRepair: true, cancellationToken).ConfigureAwait(false)) { try { diff --git a/Duplicati/Library/Main/Operation/TestHandler.cs b/Duplicati/Library/Main/Operation/TestHandler.cs index d1694294e..13e4de1d8 100644 --- a/Duplicati/Library/Main/Operation/TestHandler.cs +++ b/Duplicati/Library/Main/Operation/TestHandler.cs @@ -87,7 +87,7 @@ namespace Duplicati.Library.Main.Operation var faultyIndexFiles = new List(); // Do not repair with parity during verification: a repairable download would // otherwise mask that the remote copy is actually damaged. - await foreach (var (tf, hash, size, name) in backend.GetFilesOverlappedAsync(files, m_result.TaskControl.ProgressToken, allowParityRepair: false).ConfigureAwait(false)) + await foreach (var (tf, hash, size, name) in backend.GetFilesOverlappedAsync(files, allowParityRepair: false, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) { var vol = new RemoteVolume(name, hash, size); try @@ -200,7 +200,7 @@ namespace Duplicati.Library.Main.Operation Logging.Log.WriteInformationMessage(LOGTAG, "MissingRemoteHash", LC.L("No hash or size recorded for {0}, performing full verification", f.Name)); KeyValuePair>> res; - (var tf, var hash, var size) = await backend.GetWithInfoAsync(f.Name, f.Hash, f.Size, m_result.TaskControl.ProgressToken, allowParityRepair: false).ConfigureAwait(false); + (var tf, var hash, var size) = await backend.GetWithInfoAsync(f.Name, f.Hash, f.Size, allowParityRepair: false, m_result.TaskControl.ProgressToken).ConfigureAwait(false); using (tf) res = await TestVolumeInternalsAsync(db, f, tf, m_options, 1, m_result.TaskControl.ProgressToken) @@ -227,7 +227,7 @@ namespace Duplicati.Library.Main.Operation } else { - using (var tf = await backend.GetAsync(f.Name, f.Hash, f.Size, m_result.TaskControl.ProgressToken, allowParityRepair: false).ConfigureAwait(false)) + using (var tf = await backend.GetAsync(f.Name, f.Hash, f.Size, allowParityRepair: false, m_result.TaskControl.ProgressToken).ConfigureAwait(false)) { } } diff --git a/Duplicati/UnitTest/LockingDeleteAndCompactTests.cs b/Duplicati/UnitTest/LockingDeleteAndCompactTests.cs index 01dc426d0..b941e4b94 100644 --- a/Duplicati/UnitTest/LockingDeleteAndCompactTests.cs +++ b/Duplicati/UnitTest/LockingDeleteAndCompactTests.cs @@ -64,11 +64,11 @@ namespace Duplicati.UnitTest public TempFile DecryptFile(TempFile volume, string volume_name, Options options, bool dispose) => throw new NotImplementedException(); public Task DeleteAsync(string remotename, long size, bool waitForComplete, CancellationToken cancelToken) => throw new NotImplementedException(); public Task GetQuotaInfoAsync(CancellationToken cancelToken) => throw new NotImplementedException(); - public Task<(TempFile File, string Hash, long Size)> GetWithInfoAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); - public Task GetAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); - public Task GetDirectAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); - public System.Collections.Generic.IAsyncEnumerable<(TempFile File, string Hash, long Size, string Name)> GetFilesOverlappedAsync(System.Collections.Generic.IEnumerable volumes, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); - public System.Collections.Generic.IAsyncEnumerable<(TempFile File, string Name)> GetFilesOverlappedDirectAsync(System.Collections.Generic.IEnumerable volumes, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); + public Task<(TempFile File, string Hash, long Size)> GetWithInfoAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); + public Task GetAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); + public Task GetDirectAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); + public System.Collections.Generic.IAsyncEnumerable<(TempFile File, string Hash, long Size, string Name)> GetFilesOverlappedAsync(System.Collections.Generic.IEnumerable volumes, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); + public System.Collections.Generic.IAsyncEnumerable<(TempFile File, string Name)> GetFilesOverlappedDirectAsync(System.Collections.Generic.IEnumerable volumes, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); public Task FlushPendingMessagesAsync(IBackendManagerDatabase database, CancellationToken cancellationToken) => Task.CompletedTask; public void UpdateThrottleValues(long maxUploadPrSecond, long maxDownloadPrSecond) => throw new NotImplementedException(); #endregion diff --git a/Duplicati/UnitTest/SetLocksHandlerTests.cs b/Duplicati/UnitTest/SetLocksHandlerTests.cs index 61bf09a77..a73b274b8 100644 --- a/Duplicati/UnitTest/SetLocksHandlerTests.cs +++ b/Duplicati/UnitTest/SetLocksHandlerTests.cs @@ -96,11 +96,11 @@ namespace Duplicati.UnitTest public TempFile DecryptFile(TempFile volume, string volume_name, Options options, bool dispose) => throw new NotImplementedException(); public Task DeleteAsync(string remotename, long size, bool waitForComplete, CancellationToken cancelToken) => throw new NotImplementedException(); public Task GetQuotaInfoAsync(CancellationToken cancelToken) => throw new NotImplementedException(); - public Task<(TempFile File, string Hash, long Size)> GetWithInfoAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); - public Task GetAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); - public Task GetDirectAsync(string remotename, string hash, long size, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); - public IAsyncEnumerable<(TempFile File, string Hash, long Size, string Name)> GetFilesOverlappedAsync(IEnumerable volumes, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); - public IAsyncEnumerable<(TempFile File, string Name)> GetFilesOverlappedDirectAsync(IEnumerable volumes, CancellationToken cancelToken, bool allowParityRepair = true) => throw new NotImplementedException(); + public Task<(TempFile File, string Hash, long Size)> GetWithInfoAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); + public Task GetAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); + public Task GetDirectAsync(string remotename, string hash, long size, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); + public IAsyncEnumerable<(TempFile File, string Hash, long Size, string Name)> GetFilesOverlappedAsync(IEnumerable volumes, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); + public IAsyncEnumerable<(TempFile File, string Name)> GetFilesOverlappedDirectAsync(IEnumerable volumes, bool allowParityRepair, CancellationToken cancelToken) => throw new NotImplementedException(); public Task FlushPendingMessagesAsync(IBackendManagerDatabase database, CancellationToken cancellationToken) => Task.CompletedTask; public void UpdateThrottleValues(long maxUploadPrSecond, long maxDownloadPrSecond) => throw new NotImplementedException(); #endregion