diff --git a/Duplicati/Library/Localization/LocalizationService.cs b/Duplicati/Library/Localization/LocalizationService.cs
index ad65f3051..1e2750bd0 100644
--- a/Duplicati/Library/Localization/LocalizationService.cs
+++ b/Duplicati/Library/Localization/LocalizationService.cs
@@ -42,7 +42,7 @@ namespace Duplicati.Library.Localization
///
/// Regular expression to match a locale
///
- public static readonly Regex CI_MATCHER = new Regex(@"[A-z]{2}(-[A-z]{4})?(-[A-z]{2})?");
+ public static readonly Regex CI_MATCHER = new Regex(@"[A-z]{2}([-_][A-z]{4})?([-_][A-z]{2})?");
///
/// Returns a temporary disposable localization context
@@ -77,6 +77,7 @@ namespace Duplicati.Library.Localization
public static CultureInfo ParseCulture(string culture, bool returninvariant = false)
{
var ci = returninvariant ? CultureInfo.InvariantCulture : null;
+ culture = culture.Replace("_", "-");
if (CI_MATCHER.Match(culture).Success)
try { ci = new CultureInfo(culture); }
diff --git a/Duplicati/Library/Main/Operation/BackupHandler.cs b/Duplicati/Library/Main/Operation/BackupHandler.cs
index e4a90b082..90e5df11a 100644
--- a/Duplicati/Library/Main/Operation/BackupHandler.cs
+++ b/Duplicati/Library/Main/Operation/BackupHandler.cs
@@ -835,7 +835,7 @@ namespace Duplicati.Library.Main.Operation
if (m_options.StoreMetadata)
{
- metadata = snapshot.GetMetadata(path);
+ metadata = snapshot.GetMetadata(path, attributes.HasFlag(System.IO.FileAttributes.ReparsePoint), m_symlinkPolicy == Options.SymlinkStrategy.Follow);
if (metadata == null)
metadata = new Dictionary();
diff --git a/Duplicati/Library/Snapshots/ISnapshotService.cs b/Duplicati/Library/Snapshots/ISnapshotService.cs
index 2060a466e..314e8b66e 100644
--- a/Duplicati/Library/Snapshots/ISnapshotService.cs
+++ b/Duplicati/Library/Snapshots/ISnapshotService.cs
@@ -81,7 +81,9 @@ namespace Duplicati.Library.Snapshots
///
/// The metadata for the given file or folder
/// The file or folder to examine
- Dictionary GetMetadata(string file);
+ /// A flag indicating if the target is a symlink
+ /// A flag indicating if a symlink should be followed
+ Dictionary GetMetadata(string file, bool isSymlink, bool followSymlink);
///
/// Gets a value indicating if the path points to a block device
diff --git a/Duplicati/Library/Snapshots/ISystemIO.cs b/Duplicati/Library/Snapshots/ISystemIO.cs
index 417f0698b..cf3fef902 100644
--- a/Duplicati/Library/Snapshots/ISystemIO.cs
+++ b/Duplicati/Library/Snapshots/ISystemIO.cs
@@ -57,7 +57,7 @@ namespace Duplicati.Library.Snapshots
IEnumerable EnumerateFileSystemEntries(string path);
void SetMetadata(string path, Dictionary metdata, bool restorePermissions);
- Dictionary GetMetadata(string path);
+ Dictionary GetMetadata(string path, bool isSymlink, bool followSymlink);
}
}
diff --git a/Duplicati/Library/Snapshots/LinuxSnapshot.cs b/Duplicati/Library/Snapshots/LinuxSnapshot.cs
index 8978cfa26..e2a5d6b08 100644
--- a/Duplicati/Library/Snapshots/LinuxSnapshot.cs
+++ b/Duplicati/Library/Snapshots/LinuxSnapshot.cs
@@ -463,10 +463,12 @@ namespace Duplicati.Library.Snapshots
///
/// The metadata for the given file or folder
/// The file or folder to examine
- public Dictionary GetMetadata(string file)
+ /// A flag indicating if the target is a symlink
+ /// A flag indicating if a symlink should be followed
+ public Dictionary GetMetadata(string file, bool isSymlink, bool followSymlink)
{
var local = ConvertToSnapshotPath(FindSnapShotByLocalPath(file), file);
- return _sysIO.GetMetadata(local);
+ return _sysIO.GetMetadata(local, isSymlink, followSymlink);
}
///
diff --git a/Duplicati/Library/Snapshots/NoSnapshot.cs b/Duplicati/Library/Snapshots/NoSnapshot.cs
index bd228a3e0..edfca74ba 100644
--- a/Duplicati/Library/Snapshots/NoSnapshot.cs
+++ b/Duplicati/Library/Snapshots/NoSnapshot.cs
@@ -176,7 +176,9 @@ namespace Duplicati.Library.Snapshots
///
/// The metadata for the given file or folder
/// The file or folder to examine
- public abstract Dictionary GetMetadata(string file);
+ /// A flag indicating if the target is a symlink
+ /// A flag indicating if a symlink should be followed
+ public abstract Dictionary GetMetadata(string file, bool isSymlink, bool followSymlink);
///
/// Gets a value indicating if the path points to a block device
@@ -189,7 +191,7 @@ namespace Duplicati.Library.Snapshots
/// Gets a unique hardlink target ID
///
/// The hardlink ID
- /// The file or folder to examine
+ /// The file or folder to examine
public abstract string HardlinkTargetID(string path);
#endregion
}
diff --git a/Duplicati/Library/Snapshots/NoSnapshotLinux.cs b/Duplicati/Library/Snapshots/NoSnapshotLinux.cs
index cd586349e..a6d18c3b7 100644
--- a/Duplicati/Library/Snapshots/NoSnapshotLinux.cs
+++ b/Duplicati/Library/Snapshots/NoSnapshotLinux.cs
@@ -53,9 +53,11 @@ namespace Duplicati.Library.Snapshots
///
/// The metadata for the given file or folder
/// The file or folder to examine
- public override Dictionary GetMetadata(string file)
+ /// A flag indicating if the target is a symlink
+ /// A flag indicating if a symlink should be followed
+ public override Dictionary GetMetadata(string file, bool isSymlink, bool followSymlink)
{
- return _sysIO.GetMetadata(file);
+ return _sysIO.GetMetadata(file, isSymlink, followSymlink);
}
///
diff --git a/Duplicati/Library/Snapshots/NoSnapshotWindows.cs b/Duplicati/Library/Snapshots/NoSnapshotWindows.cs
index 00cb23c86..e708042ac 100644
--- a/Duplicati/Library/Snapshots/NoSnapshotWindows.cs
+++ b/Duplicati/Library/Snapshots/NoSnapshotWindows.cs
@@ -146,9 +146,11 @@ namespace Duplicati.Library.Snapshots
///
/// The metadata for the given file or folder
/// The file or folder to examine
- public override Dictionary GetMetadata(string file)
+ /// A flag indicating if the target is a symlink
+ /// A flag indicating if a symlink should be followed
+ public override Dictionary GetMetadata(string file, bool isSymlink, bool followSymlink)
{
- return m_sysIO.GetMetadata(file);
+ return m_sysIO.GetMetadata(file, isSymlink, followSymlink);
}
///
diff --git a/Duplicati/Library/Snapshots/SystemIOLinux.cs b/Duplicati/Library/Snapshots/SystemIOLinux.cs
index d6e8e1a72..2e05cabc9 100644
--- a/Duplicati/Library/Snapshots/SystemIOLinux.cs
+++ b/Duplicati/Library/Snapshots/SystemIOLinux.cs
@@ -165,12 +165,12 @@ namespace Duplicati.Library.Snapshots
Directory.Delete(NoSnapshot.NormalizePath(path), recursive);
}
- public Dictionary GetMetadata(string file)
+ public Dictionary GetMetadata(string file, bool isSymlink, bool followSymlink)
{
var f = NoSnapshot.NormalizePath(file);
var dict = new Dictionary();
- var n = UnixSupport.File.GetExtendedAttributes(f);
+ var n = UnixSupport.File.GetExtendedAttributes(f, isSymlink, followSymlink);
if (n != null)
foreach(var x in n)
dict["unix-ext:" + x.Key] = Convert.ToBase64String(x.Value);
diff --git a/Duplicati/Library/Snapshots/SystemIOWindows.cs b/Duplicati/Library/Snapshots/SystemIOWindows.cs
index cba47b071..c4b3cc44c 100644
--- a/Duplicati/Library/Snapshots/SystemIOWindows.cs
+++ b/Duplicati/Library/Snapshots/SystemIOWindows.cs
@@ -500,7 +500,7 @@ namespace Duplicati.Library.Snapshots
Alphaleonis.Win32.Filesystem.Directory.SetAccessControl(PrefixWithUNC(path), rules, AccessControlSections.All);
}
- public Dictionary GetMetadata(string path)
+ public Dictionary GetMetadata(string path, bool isSymlink, bool followSymlink)
{
var isDirTarget = path.EndsWith(DIRSEP);
var targetpath = isDirTarget ? path.Substring(0, path.Length - 1) : path;
diff --git a/Duplicati/Library/Snapshots/WindowsSnapshot.cs b/Duplicati/Library/Snapshots/WindowsSnapshot.cs
index 78fd4b343..fd3f4d3ee 100644
--- a/Duplicati/Library/Snapshots/WindowsSnapshot.cs
+++ b/Duplicati/Library/Snapshots/WindowsSnapshot.cs
@@ -394,9 +394,11 @@ namespace Duplicati.Library.Snapshots
///
/// The metadata for the given file or folder
/// The file or folder to examine
- public Dictionary GetMetadata(string file)
+ /// A flag indicating if the target is a symlink
+ /// A flag indicating if a symlink should be followed
+ public Dictionary GetMetadata(string file, bool isSymlink, bool followSymlink)
{
- return _ioWin.GetMetadata(GetSnapshotPath(file));
+ return _ioWin.GetMetadata(GetSnapshotPath(file), isSymlink, followSymlink);
}
///
diff --git a/Duplicati/Library/Snapshots/lvm-scripts/create-lvm-snapshot.sh b/Duplicati/Library/Snapshots/lvm-scripts/create-lvm-snapshot.sh
index a83c0b605..32b830158 100644
--- a/Duplicati/Library/Snapshots/lvm-scripts/create-lvm-snapshot.sh
+++ b/Duplicati/Library/Snapshots/lvm-scripts/create-lvm-snapshot.sh
@@ -116,14 +116,27 @@ then
exit $EXIT_CODE
fi
+#
+# Find filesystem used on $DEVICE.
+# XFS filesystems need to be mounted with option -o nouuid.
+# Other filesystems do not support that option.
+#
+
+FILESYSTEM=`df -PT /dev/"$DEVICE" | tail -1 | awk '{print $2}'`
+if [ "$FILESYSTEM" == "xfs" ]; then
+ MOUNT_OPTIONS="ro,nouuid"
+else
+ MOUNT_OPTIONS="ro"
+fi
+
#
# Mount the snapshot on the mount point
#
-mount -o ro "$LV_SNAPSHOT" "$TMPDIR"
+mount -o "$MOUNT_OPTIONS" "$LV_SNAPSHOT" "$TMPDIR"
if [ "$?" -ne 0 ]
then
EXIT_CODE=$?
- echo "Error: mount -o ro \"$LV_SNAPSHOT\" \"$TMPDIR\" failed!"
+ echo "Error: mount -o \"$MOUNTOPTIONS\" \"$LV_SNAPSHOT\" \"$TMPDIR\" failed!"
#We have created the volume, so remove it before exit
lvremove --force "$LV_GROUP/$NAME"
@@ -134,4 +147,4 @@ fi
# Report back to the caller what the name of the snapshot volume is
#
echo "tmpdir=\"$TMPDIR\""
-exit 0
\ No newline at end of file
+exit 0
diff --git a/Duplicati/Server/webroot/ngax/index.html b/Duplicati/Server/webroot/ngax/index.html
index 8d349bbd7..728c3a310 100755
--- a/Duplicati/Server/webroot/ngax/index.html
+++ b/Duplicati/Server/webroot/ngax/index.html
@@ -151,7 +151,7 @@
Restore backup
-
+