diff --git a/Duplicati/Library/Main/Operation/BackupHandler.cs b/Duplicati/Library/Main/Operation/BackupHandler.cs
index fd20bcbce..33d093bf7 100644
--- a/Duplicati/Library/Main/Operation/BackupHandler.cs
+++ b/Duplicati/Library/Main/Operation/BackupHandler.cs
@@ -90,7 +90,7 @@ namespace Duplicati.Library.Main.Operation
if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
{
- return new NoSnapshotLinux();
+ return new NoSnapshotLinux(options.IgnoreAdvisoryLocking);
}
else if (OperatingSystem.IsWindows())
{
diff --git a/Duplicati/Library/Main/Options.cs b/Duplicati/Library/Main/Options.cs
index eb7eadc81..09f39a3c0 100644
--- a/Duplicati/Library/Main/Options.cs
+++ b/Duplicati/Library/Main/Options.cs
@@ -303,6 +303,7 @@ namespace Duplicati.Library.Main
new CommandLineArgument("vss-exclude-writers", CommandLineArgument.ArgumentType.String, Strings.Options.VssexcludewritersShort, Strings.Options.VssexcludewritersLong, "{e8132975-6f93-4464-a53e-1050253ae220}"),
new CommandLineArgument("vss-use-mapping", CommandLineArgument.ArgumentType.Boolean, Strings.Options.VssusemappingShort, Strings.Options.VssusemappingLong, "false"),
new CommandLineArgument("usn-policy", CommandLineArgument.ArgumentType.Enumeration, Strings.Options.UsnpolicyShort, Strings.Options.UsnpolicyLong, "off", null, Enum.GetNames(typeof(OptimizationStrategy))),
+ new CommandLineArgument("ignore-advisory-locking", CommandLineArgument.ArgumentType.Boolean, Strings.Options.IgnoreadvisorylockingShort, Strings.Options.IgnoreadvisorylockingLong, "false"),
new CommandLineArgument("encryption-module", CommandLineArgument.ArgumentType.String, Strings.Options.EncryptionmoduleShort, Strings.Options.EncryptionmoduleLong, "aes"),
new CommandLineArgument("compression-module", CommandLineArgument.ArgumentType.String, Strings.Options.CompressionmoduleShort, Strings.Options.CompressionmoduleLong, "zip"),
@@ -956,6 +957,11 @@ namespace Duplicati.Library.Main
}
}
+ ///
+ /// Gets a flag indicating if advisory locking should be ignored
+ ///
+ public bool IgnoreAdvisoryLocking => GetBool("ignore-advisory-locking");
+
///
/// Gets the symlink strategy to use
///
diff --git a/Duplicati/Library/Main/Strings.cs b/Duplicati/Library/Main/Strings.cs
index a4682c0f6..5a53ba771 100644
--- a/Duplicati/Library/Main/Strings.cs
+++ b/Duplicati/Library/Main/Strings.cs
@@ -132,6 +132,8 @@ namespace Duplicati.Library.Main.Strings
public static string VssexcludewritersShort { get { return LC.L(@"A semicolon separated list of guids of VSS writers to exclude (Windows only)"); } }
public static string UsnpolicyLong { get { return LC.L(@"This setting controls the usage of NTFS USN numbers, which allows Duplicati to obtain a list of files and folders much faster. If this is set to ""off"", Duplicati will not attempt to use USN. Setting this to ""auto"" makes Duplicati attempt to use USN, and fail silently if that was not allowed or supported. A setting of ""on"" will also make Duplicati attempt to use USN, but will produce a warning message in the log if it fails. Setting it to ""required"" will make Duplicati abort the backup if the USN usage fails. This feature is only supported on Windows and requires administrative privileges."); } }
public static string UsnpolicyShort { get { return LC.L(@"Control the use of NTFS Update Sequence Numbers"); } }
+ public static string IgnoreadvisorylockingShort { get { return LC.L(@"Ignore advisory locking"); } }
+ public static string IgnoreadvisorylockingLong { get { return LC.L(@"When reading files Duplicati can skip files that are marked locked by another application to ensure consistency. This flag can disable the check and perform optimistic reads of locked files."); } }
public static string DisabletimetoleranceLong { get { return LC.L(@"When matching timestamps, Duplicati will adjust the times by a small fraction to ensure that minor time differences do not cause unexpected updates. If the option --{0} is set to keep a week of backups, and the backup is made the same time each week, it is possible that the clock drifts slightly, such that full week has just passed, causing Duplicati to delete the older backup earlier than expected. To avoid this, Duplicati inserts a 1% tolerance (max 1 hour). Use this option to disable the tolerance, and use strict time checking.", "keep-time"); } }
public static string DisabletimetoleranceShort { get { return LC.L(@"Deactivate tolerance when comparing times"); } }
public static string ListverifyuploadsLong { get { return LC.L(@"Use this option to verify uploads by listing contents."); } }
diff --git a/Duplicati/Library/Snapshots/NoSnapshotLinux.cs b/Duplicati/Library/Snapshots/NoSnapshotLinux.cs
index b9f0532f2..2408dea40 100644
--- a/Duplicati/Library/Snapshots/NoSnapshotLinux.cs
+++ b/Duplicati/Library/Snapshots/NoSnapshotLinux.cs
@@ -24,7 +24,6 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
-using System.Threading.Tasks;
using Duplicati.Library.Common.IO;
using Microsoft.Win32.SafeHandles;
@@ -37,25 +36,45 @@ namespace Duplicati.Library.Snapshots
[SupportedOSPlatform("macOS")]
public sealed class NoSnapshotLinux : SnapshotBase
{
-
- [DllImport("libc", EntryPoint = "fopen", SetLastError = true)]
- private static extern IntPtr fopen(string path, string mode);
-
- [DllImport("libc", EntryPoint = "fclose", SetLastError = true)]
- private static extern int fclose(IntPtr handle);
-
- [DllImport("libc", EntryPoint = "fileno", SetLastError = true)]
- private static extern int fileno(IntPtr stream);
-
- [DllImport("libc", EntryPoint = "strerror", SetLastError = true)]
- private static extern IntPtr strerror(int errnum);
-
- private static string GetErrorMessage(int errno)
+ ///
+ /// PInvoke methods
+ ///
+ private static class PInvoke
{
- IntPtr strPtr = strerror(errno);
- return Marshal.PtrToStringAnsi(strPtr) ?? $"Unknown error: {errno}";
+ [DllImport("libc", EntryPoint = "fopen", SetLastError = true)]
+ public static extern IntPtr fopen(string path, string mode);
+
+ [DllImport("libc", EntryPoint = "fclose", SetLastError = true)]
+ public static extern int fclose(IntPtr handle);
+
+ [DllImport("libc", EntryPoint = "fileno", SetLastError = true)]
+ public static extern int fileno(IntPtr stream);
+
+ [DllImport("libc", EntryPoint = "strerror", SetLastError = true)]
+ public static extern IntPtr strerror(int errnum);
+
+ public static string GetErrorMessage(int errno)
+ {
+ IntPtr strPtr = PInvoke.strerror(errno);
+ return Marshal.PtrToStringAnsi(strPtr) ?? $"Unknown error: {errno}";
+ }
}
+ ///
+ /// Flag indicating if advisory locks should be ignored
+ ///
+ private readonly bool m_ignoreAdvisoryLocks;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A flag indicating if advisory locks should be ignored
+ public NoSnapshotLinux(bool ignoreAdvisoryLocks)
+ {
+ m_ignoreAdvisoryLocks = ignoreAdvisoryLocks;
+ }
+
+
///
/// Returns the symlink target if the entry is a symlink, and null otherwise
///
@@ -133,51 +152,60 @@ namespace Duplicati.Library.Snapshots
///
public override Stream OpenRead(string localPath)
{
- IntPtr filePtr = fopen(localPath, "r");
- var errorNo = Marshal.GetLastWin32Error(); // Surprisingly, this is to be used on linux/macos
- if (filePtr == IntPtr.Zero)
+ if (m_ignoreAdvisoryLocks)
{
- throw new IOException($"Unable to open file: {localPath}. Error: {errorNo} - {GetErrorMessage(errorNo)}");
+ var filePtr = PInvoke.fopen(localPath, "r");
+ var errorNo = Marshal.GetLastWin32Error(); // Surprisingly, this is to be used on linux/macos
+ if (filePtr == IntPtr.Zero)
+ {
+ throw new IOException($"Unable to open file: {localPath}. Error: {errorNo} - {PInvoke.GetErrorMessage(errorNo)}");
+ }
+
+ try
+ {
+
+ SafeFileHandle safeFileHandle = new(PInvoke.fileno(filePtr), false);
+ return new UnixFileStream(safeFileHandle, filePtr, FileAccess.Read);
+ }
+ catch // Catch all exceptions and rethrow
+ {
+ if (filePtr != IntPtr.Zero)
+ PInvoke.fclose(filePtr);
+
+ throw;
+ }
}
-
- try
+ else
{
-
- SafeFileHandle safeFileHandle = new(fileno(filePtr), false);
-
- return new UnixFileStream(safeFileHandle, filePtr, FileAccess.Read);
- }
- catch // Catch all exceptions and rethrow
- {
- if (filePtr!=IntPtr.Zero)
- fclose(filePtr);
-
- throw;
+ return base.OpenRead(localPath);
}
}
- }
- public class UnixFileStream : FileStream
- {
- [DllImport("libc", EntryPoint = "fclose", SetLastError = true)]
- private static extern int fclose(IntPtr handle);
- IntPtr _fileHandle;
- SafeFileHandle _handle;
- public UnixFileStream(SafeFileHandle handle, IntPtr fileHandle, FileAccess access) : base(handle, access)
+
+ ///
+ /// Stream wrapping a file handle
+ ///
+ private class UnixFileStream : FileStream
{
- _fileHandle = fileHandle;
- _handle = handle;
- }
- protected override void Dispose(bool disposing)
- {
- if (_fileHandle != IntPtr.Zero)
+ IntPtr _fileHandle;
+ SafeFileHandle _handle;
+ public UnixFileStream(SafeFileHandle handle, IntPtr fileHandle, FileAccess access) : base(handle, access)
{
- fclose(_fileHandle);
- _fileHandle = IntPtr.Zero;
+ _fileHandle = fileHandle;
+ _handle = handle;
}
- _handle?.Dispose();
- base.Dispose(disposing);
- }
+ protected override void Dispose(bool disposing)
+ {
+ if (_fileHandle != IntPtr.Zero)
+ {
+ PInvoke.fclose(_fileHandle);
+ _fileHandle = IntPtr.Zero;
+ }
+ _handle?.Dispose();
+ base.Dispose(disposing);
+ }
+
+ }
}
}