Avoid using .NET File operations during restore as they normalize paths.

This commit is contained in:
Kenneth Skovhede
2026-06-15 22:51:13 +02:00
parent aea61969e4
commit 4aa61b2352
@@ -811,7 +811,7 @@ namespace Duplicati.Library.Main.Operation.Restore
List<BlockRequest> verified_blocks = [];
// Check if the file exists
if (File.Exists(file.TargetPath))
if (await restoreDestination.FileExists(file.TargetPath, cancellationToken).ConfigureAwait(false))
{
filehasher.Initialize();
try
@@ -867,17 +867,19 @@ namespace Duplicati.Library.Main.Operation.Restore
if (filehasher.Hash != null && Convert.ToBase64String(filehasher.Hash) == file.Hash)
{
// Truncate the file if it is larger than the expected size.
FileInfo fi = new(file.TargetPath);
if (file.Length < fi.Length)
var currentLength = await restoreDestination.GetFileLength(file.TargetPath, cancellationToken).ConfigureAwait(false);
if (file.Length < currentLength)
{
if (options.Dryrun)
{
Logging.Log.WriteDryrunMessage(LOGTAG, "DryrunRestore", @$"Would have truncated ""{file.TargetPath}"" from {fi.Length} to {file.Length}");
Logging.Log.WriteDryrunMessage(LOGTAG, "DryrunRestore", @$"Would have truncated ""{file.TargetPath}"" from {currentLength} to {file.Length}");
}
else
{
// Reopen file with write permission
fi.IsReadOnly = false; // The metadata handler will revert this back later.
// The metadata handler will revert this back later.
if (await restoreDestination.HasReadOnlyAttribute(file.TargetPath, cancellationToken).ConfigureAwait(false))
await restoreDestination.ClearReadOnlyAttribute(file.TargetPath, cancellationToken).ConfigureAwait(false);
try
{
using var f = await restoreDestination.OpenWrite(file.TargetPath, cancellationToken).ConfigureAwait(false);
@@ -931,15 +933,14 @@ namespace Duplicati.Library.Main.Operation.Restore
List<BlockRequest> missing_blocks = [];
List<BlockRequest> verified_blocks = [];
// Check if the file exists
if (File.Exists(file.OriginalPath))
// Check if the file exists (ignore if this was a remote source)
if (SystemIO.IO_OS.FileExists(file.OriginalPath))
{
filehasher.Initialize();
// Open both files, as the target file is still being read to produce the overall file hash, if all the blocks are present across both the target and original files.
// Note: The original file is the source file on the local system (not within the restore destination),
// so we use File.OpenRead directly instead of restoreDestination.OpenRead
using var f_original = File.OpenRead(file.OriginalPath);
using var f_original = SystemIO.IO_OS.FileOpenRead(file.OriginalPath);
using var f_target = options.Dryrun ?
(await restoreDestination.FileExists(file.TargetPath, cancellationToken).ConfigureAwait(false) ?
await restoreDestination.OpenRead(file.TargetPath, cancellationToken).ConfigureAwait(false) :