Added commandline option to toggle locks

This commit is contained in:
Kenneth Skovhede
2024-10-08 09:35:00 +02:00
parent 7d6964b5b2
commit 91ea81f537
4 changed files with 90 additions and 54 deletions
@@ -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())
{
+6
View File
@@ -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
}
}
/// <summary>
/// Gets a flag indicating if advisory locking should be ignored
/// </summary>
public bool IgnoreAdvisoryLocking => GetBool("ignore-advisory-locking");
/// <summary>
/// Gets the symlink strategy to use
/// </summary>
+2
View File
@@ -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."); } }
+81 -53
View File
@@ -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)
/// <summary>
/// PInvoke methods
/// </summary>
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}";
}
}
/// <summary>
/// Flag indicating if advisory locks should be ignored
/// </summary>
private readonly bool m_ignoreAdvisoryLocks;
/// <summary>
/// Initializes a new instance of the <see cref="NoSnapshotLinux"/> class.
/// </summary>
/// <param name="ignoreAdvisoryLocks">A flag indicating if advisory locks should be ignored</param>
public NoSnapshotLinux(bool ignoreAdvisoryLocks)
{
m_ignoreAdvisoryLocks = ignoreAdvisoryLocks;
}
/// <summary>
/// Returns the symlink target if the entry is a symlink, and null otherwise
/// </summary>
@@ -133,51 +152,60 @@ namespace Duplicati.Library.Snapshots
/// <returns></returns>
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)
/// <summary>
/// Stream wrapping a file handle
/// </summary>
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);
}
}
}
}