diff --git a/Duplicati/Agent/Program.cs b/Duplicati/Agent/Program.cs index 991c114b6..a2dc73769 100644 --- a/Duplicati/Agent/Program.cs +++ b/Duplicati/Agent/Program.cs @@ -119,7 +119,7 @@ public static class Program new Option("--secret-provider-cache", description: "The secret provider cache level", getDefaultValue: () => SecretProviderHelper.CachingLevel.None), new Option("--secret-provider-pattern", description: "The secret provider pattern", getDefaultValue: () => SecretProviderHelper.DEFAULT_PATTERN), new Option($"--{DataFolderManager.PORTABLE_MODE_OPTION}", description: "Use portable mode for locating the database and storing configuration", getDefaultValue: () => DataFolderManager.PORTABLE_MODE), - new Option($"--{DataFolderManager.SERVER_DATAFOLDER_OPTION}", description: "The datafolder to use for locating the database and storing configuration", getDefaultValue: () => new DirectoryInfo(DataFolderManager.GetDataFolder(true))), + new Option($"--{DataFolderManager.SERVER_DATAFOLDER_OPTION}", description: "The datafolder to use for locating the database and storing configuration", getDefaultValue: () => new DirectoryInfo(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly))), }; runcmd.Handler = CommandHandler.Create(RunAgent); diff --git a/Duplicati/CommandLine/CLI/Program.cs b/Duplicati/CommandLine/CLI/Program.cs index 24901ca03..f381c3ba0 100644 --- a/Duplicati/CommandLine/CLI/Program.cs +++ b/Duplicati/CommandLine/CLI/Program.cs @@ -297,7 +297,7 @@ namespace Duplicati.CommandLine new CommandLineArgument("quiet-console", CommandLineArgument.ArgumentType.Boolean, Strings.Program.QuietConsoleOptionShort, Strings.Program.QuietConsoleOptionLong, "false"), new CommandLineArgument("auto-update", CommandLineArgument.ArgumentType.Boolean, Strings.Program.AutoUpdateOptionShort, Strings.Program.AutoUpdateOptionLong, "false"), new CommandLineArgument(DataFolderManager.PORTABLE_MODE_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.Program.PortableModeOptionShort, Strings.Program.PortableModeOptionLong, DataFolderManager.PORTABLE_MODE.ToString().ToLowerInvariant()), - new CommandLineArgument(DataFolderManager.SERVER_DATAFOLDER_OPTION, CommandLineArgument.ArgumentType.Path, Strings.Program.DataFolderOptionShort, Strings.Program.DataFolderOptionLong, DataFolderManager.GetDataFolder(true)), + new CommandLineArgument(DataFolderManager.SERVER_DATAFOLDER_OPTION, CommandLineArgument.ArgumentType.Path, Strings.Program.DataFolderOptionShort, Strings.Program.DataFolderOptionLong, DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)), ]; private static bool ReadOptionsFromFile(TextWriter outwriter, string filename, ref Library.Utility.IFilter filter, List cargs, Dictionary options) diff --git a/Duplicati/CommandLine/ServerUtil/Connection.cs b/Duplicati/CommandLine/ServerUtil/Connection.cs index 968482e75..809cba5de 100644 --- a/Duplicati/CommandLine/ServerUtil/Connection.cs +++ b/Duplicati/CommandLine/ServerUtil/Connection.cs @@ -198,7 +198,7 @@ public class Connection var opts = new Dictionary(); if (settings.Key != null) opts["settings-encryption-key"] = settings.Key.Key; - if (File.Exists(Path.Combine(DataFolderManager.GetDataFolder(true), DataFolderManager.SERVER_DATABASE_FILENAME))) + if (File.Exists(Path.Combine(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly), DataFolderManager.SERVER_DATABASE_FILENAME))) { string? cfg = null; using (var connection = Server.Program.GetDatabaseConnection(opts, true)) @@ -230,12 +230,12 @@ public class Connection return CreateConnectionWithClient(client, accessToken); } } - else if (!string.IsNullOrWhiteSpace(DataFolderManager.GetDataFolder(true))) + else if (!string.IsNullOrWhiteSpace(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly))) { if (console != null) - console.AppendConsoleMessage($"No database found in {DataFolderManager.GetDataFolder(true)}"); + console.AppendConsoleMessage($"No database found in {DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)}"); else - Console.WriteLine($"No database found in {DataFolderManager.GetDataFolder(true)}"); + Console.WriteLine($"No database found in {DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)}"); } } catch (Exception ex) diff --git a/Duplicati/CommandLine/ServerUtil/Settings.cs b/Duplicati/CommandLine/ServerUtil/Settings.cs index 84912da05..7dffb5a8d 100644 --- a/Duplicati/CommandLine/ServerUtil/Settings.cs +++ b/Duplicati/CommandLine/ServerUtil/Settings.cs @@ -179,7 +179,7 @@ public sealed record Settings( File.WriteAllText(SettingsFile, JsonSerializer.Serialize(LoadSettings(SettingsFile, thisKey) .Where(x => x.HostUrl != HostUrl) - .Append(new PersistedSettings(RefreshToken, HostUrl, DataFolderManager.GetDataFolder(false))) // Explicit false for clarity + .Append(new PersistedSettings(RefreshToken, HostUrl, DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ReadWritePermissionSet))) .Select(x => x with { RefreshToken = string.IsNullOrWhiteSpace(x.RefreshToken) || thisKey == null diff --git a/Duplicati/CommandLine/ServerUtil/SettingsBinder.cs b/Duplicati/CommandLine/ServerUtil/SettingsBinder.cs index c5374e655..e887abd74 100644 --- a/Duplicati/CommandLine/ServerUtil/SettingsBinder.cs +++ b/Duplicati/CommandLine/ServerUtil/SettingsBinder.cs @@ -45,7 +45,7 @@ public class SettingsBinder : BinderBase /// /// The server datafolder option. /// - public static readonly Option serverDatafolderOption = new Option($"--{DataFolderManager.SERVER_DATAFOLDER_OPTION}", description: "The server datafolder to use for locating the database and storing configuration", getDefaultValue: () => new DirectoryInfo(DataFolderManager.GetDataFolder(true))); + public static readonly Option serverDatafolderOption = new Option($"--{DataFolderManager.SERVER_DATAFOLDER_OPTION}", description: "The server datafolder to use for locating the database and storing configuration", getDefaultValue: () => new DirectoryInfo(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly))); /// /// The server portable mode option. /// diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs index 868e84fdd..6fccabd6c 100644 --- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs @@ -179,7 +179,7 @@ namespace Duplicati.GUI.TrayIcon } else if (Utility.ParseBoolOption(options, READCONFIGFROMDB_OPTION)) { - if (File.Exists(Path.Combine(DataFolderManager.GetDataFolder(), DataFolderManager.SERVER_DATABASE_FILENAME))) + if (File.Exists(Path.Combine(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ReadWritePermissionSet), DataFolderManager.SERVER_DATABASE_FILENAME))) { passwordSource = PasswordSource.Database; databaseConnection = Server.Program.GetDatabaseConnection(options, true); diff --git a/Duplicati/Library/AutoUpdater/DataFolderLocator.cs b/Duplicati/Library/AutoUpdater/DataFolderLocator.cs index 2c7a5fb9b..8bea84a81 100644 --- a/Duplicati/Library/AutoUpdater/DataFolderLocator.cs +++ b/Duplicati/Library/AutoUpdater/DataFolderLocator.cs @@ -51,7 +51,7 @@ public static class DataFolderLocator public static string GetDefaultStorageFolder(string targetfilename, bool autoCreate, bool readOnly = false) { var folder = DataFolderManager.OVERRIDEN_DATAFOLDER - ? DataFolderManager.GetDataFolder(true) + ? DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly) : GetDefaultStorageFolderInternal(targetfilename, AutoUpdateSettings.AppName); if (SystemIO.IO_OS.DirectoryExists(folder)) diff --git a/Duplicati/Library/AutoUpdater/DataFolderManager.cs b/Duplicati/Library/AutoUpdater/DataFolderManager.cs index 5ffb36ec5..09ef527f2 100644 --- a/Duplicati/Library/AutoUpdater/DataFolderManager.cs +++ b/Duplicati/Library/AutoUpdater/DataFolderManager.cs @@ -34,6 +34,11 @@ namespace Duplicati.Library.AutoUpdater; /// public static class DataFolderManager { + public enum AccessMode + { + ProbeOnly, + ReadWritePermissionSet + } /// /// The log tag for this class /// @@ -86,7 +91,7 @@ public static class DataFolderManager /// /// The folder where the machine id is placed /// - public static string GetDataFolder(bool probeOnly = false) + public static string GetDataFolder(AccessMode mode) { // Trigger portable mode, if the flag is set PORTABLE_MODE = ParseBoolSlim(ExtractOptionSlim(PORTABLE_MODE_OPTION)); @@ -129,7 +134,7 @@ public static class DataFolderManager if (!File.Exists(Path.Combine(dataFolder, Util.InsecurePermissionsMarkerFile))) try { - if (!probeOnly) + if (mode == AccessMode.ReadWritePermissionSet) SystemIO.IO_OS.DirectorySetPermissionUserRWOnly(dataFolder); } catch (Exception ex) @@ -142,7 +147,7 @@ public static class DataFolderManager Directory.CreateDirectory(dataFolder); try { - if (!probeOnly) + if (mode == AccessMode.ReadWritePermissionSet) SystemIO.IO_OS.DirectorySetPermissionUserRWOnly(dataFolder); } catch (Exception ex) @@ -151,7 +156,7 @@ public static class DataFolderManager } } - if (!probeOnly && !File.Exists(Path.Combine(dataFolder, INSTALL_FILE))) + if (mode == AccessMode.ReadWritePermissionSet && !File.Exists(Path.Combine(dataFolder, INSTALL_FILE))) { // In case there was already a machine id file from 2.0.8.1 or older, copy it to the new location if (File.Exists(Path.Combine(dataFolder, "updates", INSTALL_FILE))) @@ -160,7 +165,7 @@ public static class DataFolderManager File.WriteAllText(Path.Combine(dataFolder, INSTALL_FILE), AutoUpdateSettings.UpdateInstallFileText); } - if (!probeOnly && !File.Exists(Path.Combine(dataFolder, MACHINE_FILE))) + if (mode == AccessMode.ReadWritePermissionSet && !File.Exists(Path.Combine(dataFolder, MACHINE_FILE))) File.WriteAllText(Path.Combine(dataFolder, MACHINE_FILE), AutoUpdateSettings.UpdateMachineFileText(InstallID)); return dataFolder; @@ -233,7 +238,7 @@ public static class DataFolderManager /// private static readonly Lazy _installID = new(() => { - try { return File.ReadAllLines(Path.Combine(GetDataFolder(true), INSTALL_FILE)).FirstOrDefault(x => !string.IsNullOrWhiteSpace(x))?.Trim() ?? ""; } + try { return File.ReadAllLines(Path.Combine(GetDataFolder(DataFolderManager.AccessMode.ProbeOnly), INSTALL_FILE)).FirstOrDefault(x => !string.IsNullOrWhiteSpace(x))?.Trim() ?? ""; } catch { } return string.Empty; @@ -250,7 +255,7 @@ public static class DataFolderManager private static readonly Lazy _machineID = new(() => { string? machinedId = null; - try { machinedId = File.ReadAllLines(Path.Combine(GetDataFolder(true), MACHINE_FILE)).FirstOrDefault(x => !string.IsNullOrWhiteSpace(x))?.Trim() ?? ""; } + try { machinedId = File.ReadAllLines(Path.Combine(GetDataFolder(DataFolderManager.AccessMode.ProbeOnly), MACHINE_FILE)).FirstOrDefault(x => !string.IsNullOrWhiteSpace(x))?.Trim() ?? ""; } catch { } return string.IsNullOrWhiteSpace(machinedId) diff --git a/Duplicati/Library/AutoUpdater/PreloadSettingsLoader.cs b/Duplicati/Library/AutoUpdater/PreloadSettingsLoader.cs index 315573b65..3e3562ab8 100644 --- a/Duplicati/Library/AutoUpdater/PreloadSettingsLoader.cs +++ b/Duplicati/Library/AutoUpdater/PreloadSettingsLoader.cs @@ -109,7 +109,7 @@ public static class PreloadSettingsLoader // User-context path for preload settings, // the same default path as where other data is stored - Path.Combine(DataFolderManager.GetDataFolder(true), FILE_NAME), + Path.Combine(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly), FILE_NAME), } .Concat(PortablePreloadPaths) .Where(x => !string.IsNullOrEmpty(x)) diff --git a/Duplicati/Library/AutoUpdater/Program.cs b/Duplicati/Library/AutoUpdater/Program.cs index f7d37d651..e8b64a083 100644 --- a/Duplicati/Library/AutoUpdater/Program.cs +++ b/Duplicati/Library/AutoUpdater/Program.cs @@ -117,7 +117,7 @@ namespace Duplicati.Library.AutoUpdater Console.WriteLine("{0} - Choose different channel than the default {1}, valid settings: {2}", string.Format(AutoUpdateSettings.UPDATECHANNEL_ENVNAME_TEMPLATE, AutoUpdateSettings.AppName), AutoUpdater.AutoUpdateSettings.DefaultUpdateChannel, string.Join(",", Enum.GetNames(typeof(ReleaseType)).Where(x => x != ReleaseType.Unknown.ToString()))); Console.WriteLine(); Console.WriteLine("Updates are downloaded from: {0}", string.Join(";", AutoUpdateSettings.URLs)); - Console.WriteLine("Settings and configuration files are placed in: {0}", DataFolderManager.GetDataFolder(true)); + Console.WriteLine("Settings and configuration files are placed in: {0}", DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)); Console.WriteLine("This version is \"{0}\" ({1}) and is installed in: {2}", UpdaterManager.SelfVersion.Displayname, UpdaterManager.SelfVersion.Version, UpdaterManager.INSTALLATIONDIR); Console.WriteLine(); } diff --git a/Duplicati/Library/Main/Operation/SystemInfoHandler.cs b/Duplicati/Library/Main/Operation/SystemInfoHandler.cs index f8d3dbca0..7e99c8706 100644 --- a/Duplicati/Library/Main/Operation/SystemInfoHandler.cs +++ b/Duplicati/Library/Main/Operation/SystemInfoHandler.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Linq; using System.Globalization; using System.Threading.Tasks; +using Duplicati.Library.AutoUpdater; namespace Duplicati.Library.Main.Operation { @@ -37,7 +38,7 @@ namespace Duplicati.Library.Main.Operation yield return string.Format("Duplicati: {0} ({1})", Library.Utility.Utility.getEntryAssembly().FullName, System.Reflection.Assembly.GetExecutingAssembly().FullName); yield return string.Format("Autoupdate urls: {0}", string.Join(";", AutoUpdater.AutoUpdateSettings.URLs)); - yield return string.Format("Default data folder: {0}", AutoUpdater.DataFolderManager.GetDataFolder(true)); + yield return string.Format("Default data folder: {0}", AutoUpdater.DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)); yield return string.Format("Install folder: {0}", AutoUpdater.UpdaterManager.INSTALLATIONDIR); yield return string.Format("Version name: \"{0}\" ({1})", AutoUpdater.UpdaterManager.SelfVersion.Displayname, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version); yield return string.Format("Current Version folder {0}", System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)); diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index 73a524bae..4452555a0 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -793,7 +793,7 @@ namespace Duplicati.Server public static Connection GetDatabaseConnection(Dictionary commandlineOptions, bool silentConsole) { - DataFolder = DataFolderManager.GetDataFolder(); + DataFolder = DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ReadWritePermissionSet); // Emit a warning if the database is stored in the Windows folder if (Util.IsPathUnderWindowsFolder(DataFolder)) @@ -1015,7 +1015,7 @@ namespace Duplicati.Server new CommandLineArgument(PING_PONG_KEEPALIVE_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.Program.PingpongkeepaliveShort, Strings.Program.PingpongkeepaliveLong), new CommandLineArgument(DISABLE_UPDATE_CHECK_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.Program.DisableupdatecheckShort, Strings.Program.DisableupdatecheckLong), new CommandLineArgument(LOG_RETENTION_OPTION, CommandLineArgument.ArgumentType.Timespan, Strings.Program.LogretentionShort, Strings.Program.LogretentionLong, DEFAULT_LOG_RETENTION), - new CommandLineArgument(DataFolderManager.SERVER_DATAFOLDER_OPTION, CommandLineArgument.ArgumentType.Path, Strings.Program.ServerdatafolderShort, Strings.Program.ServerdatafolderLong(DataFolderManager.GetDataFolder()), DataFolderManager.GetDataFolder()), + new CommandLineArgument(DataFolderManager.SERVER_DATAFOLDER_OPTION, CommandLineArgument.ArgumentType.Path, Strings.Program.ServerdatafolderShort, Strings.Program.ServerdatafolderLong(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)), DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)), new CommandLineArgument(DISABLE_DB_ENCRYPTION_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.Program.DisabledbencryptionShort, Strings.Program.DisabledbencryptionLong), new CommandLineArgument(REQUIRE_DB_ENCRYPTION_KEY_OPTION, CommandLineArgument.ArgumentType.Boolean, Strings.Program.RequiredbencryptionShort, Strings.Program.RequiredbencryptionLong), new CommandLineArgument(SETTINGS_ENCRYPTION_KEY_OPTION, CommandLineArgument.ArgumentType.Password, Strings.Program.SettingsencryptionkeyShort, Strings.Program.SettingsencryptionkeyLong(EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME)),