From 0bba2fb986ff6a439d43a8b3f795a219800b2269 Mon Sep 17 00:00:00 2001 From: Carl Johnsen Date: Fri, 6 Feb 2026 12:16:23 +0100 Subject: [PATCH] Mountpoint is not supported for disk image backup --- .../Library/Main/Operation/BackupHandler.cs | 18 ++++++++++++------ proprietary/DiskImage/SourceProvider.cs | 8 ++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Duplicati/Library/Main/Operation/BackupHandler.cs b/Duplicati/Library/Main/Operation/BackupHandler.cs index 8d2806c50..fcdf22c6c 100644 --- a/Duplicati/Library/Main/Operation/BackupHandler.cs +++ b/Duplicati/Library/Main/Operation/BackupHandler.cs @@ -156,22 +156,27 @@ namespace Duplicati.Library.Main.Operation foreach (var url in entry) { var sanitizedUrl = Library.Utility.Utility.GetUrlWithoutCredentials(url); - var m = Regex.Match(url, @"^@(?[^|]+)\|(?.+)$", RegexOptions.IgnoreCase); + var m = Regex.Match(url, @"^@(?[^|]+\|)*(?.+)$", RegexOptions.IgnoreCase); if (m.Success) { var mountpoint = m.Groups["mountpoint"].Value; - if (mountpoint.Any(x => Path.GetInvalidPathChars().Contains(x))) - throw new UserInformationException(string.Format("The mountpoint \"{0}\" contains invalid characters", mountpoint), "InvalidMountpoint"); - if (!Path.IsPathRooted(mountpoint)) - throw new UserInformationException(string.Format("The mountpoint \"{0}\" is not a valid rooted mountpoint", mountpoint), "InvalidMountpoint"); + if (!string.IsNullOrEmpty(mountpoint)) + { + if (mountpoint.Any(x => Path.GetInvalidPathChars().Contains(x))) + throw new UserInformationException(string.Format("The mountpoint \"{0}\" contains invalid characters", mountpoint), "InvalidMountpoint"); + if (!Path.IsPathRooted(mountpoint)) + throw new UserInformationException(string.Format("The mountpoint \"{0}\" is not a valid rooted mountpoint", mountpoint), "InvalidMountpoint"); + mountpoint = Path.GetFullPath(mountpoint); + } var backendurl = m.Groups["url"].Value; ISourceProvider provider; try { - provider = await SourceProviderLoader.GetSourceProvider(backendurl, Path.GetFullPath(mountpoint), options.RawOptions, cancellationToken).ConfigureAwait(false); + // TODO double check that all providers that require a mountpoint throws correctly when mountpoint is not set. + provider = await SourceProviderLoader.GetSourceProvider(backendurl, mountpoint, options.RawOptions, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -181,6 +186,7 @@ namespace Duplicati.Library.Main.Operation continue; } + Console.WriteLine(ex.StackTrace); throw new UserInformationException($"Failed to load source provider for \"{sanitizedUrl}\": {ex.Message}", "SourceProviderFailed", ex); } diff --git a/proprietary/DiskImage/SourceProvider.cs b/proprietary/DiskImage/SourceProvider.cs index 1b19a840c..1723b01be 100644 --- a/proprietary/DiskImage/SourceProvider.cs +++ b/proprietary/DiskImage/SourceProvider.cs @@ -16,7 +16,6 @@ namespace Duplicati.Proprietary.DiskImage; public sealed class SourceProvider : ISourceProviderModule, IDisposable { private readonly string _devicePath; - private readonly string _mountPoint; private IRawDisk? _disk; private bool _disposed; @@ -25,17 +24,18 @@ public sealed class SourceProvider : ISourceProviderModule, IDisposable public SourceProvider() { _devicePath = null!; - _mountPoint = null!; } public SourceProvider(string url, string mountPoint, Dictionary options) { - _mountPoint = mountPoint; + if (!string.IsNullOrEmpty(mountPoint)) + throw new UserInformationException("Mount point option is not supported for DiskImage provider. The entire disk will be mounted/treated as root.", "MountPointNotSupported"); + var uri = new Library.Utility.Uri(url); _devicePath = uri.HostAndPath; } - public string MountedPath => _mountPoint; + public string MountedPath => $"root{System.IO.Path.DirectorySeparatorChar}"; public string DisplayName => Strings.ProviderDisplayName;