From cf4ee5017fe136059cda042342270801d29eeff5 Mon Sep 17 00:00:00 2001 From: abraunegg Date: Fri, 30 Oct 2020 08:00:26 +1100 Subject: [PATCH] Configure specific File and Folder Permissions (#1108) * Configure specific File and Folder Permissions, defaults for new items downloaded from OneDrive: Directories: 700 Files: 600 --- config | 2 ++ docs/USAGE.md | 22 +++++++++++++++++ src/config.d | 64 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.d | 28 ++++++++++++++++++++-- src/onedrive.d | 8 ++++++- src/sync.d | 3 +++ 6 files changed, 122 insertions(+), 5 deletions(-) diff --git a/config b/config index bafbd739..3b5f9d15 100644 --- a/config +++ b/config @@ -40,3 +40,5 @@ # azure_ad_endpoint = "" # azure_tenant_id = "common" # sync_business_shared_folders = "false" +# sync_dir_permissions = "700" +# sync_file_permissions = "600" \ No newline at end of file diff --git a/docs/USAGE.md b/docs/USAGE.md index dc07e697..6b85a5ac 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -300,6 +300,8 @@ The default configuration file is listed below: # azure_ad_endpoint = "" # azure_tenant_id = "common" # sync_business_shared_folders = "false" +# sync_dir_permissions = "700" +# sync_file_permissions = "600" ``` @@ -327,6 +329,26 @@ The issue here is around how the client stores the sync_dir path in the database **Important Note:** If your `sync_dir` is pointing to a network mount point (a network share via NFS, Windows Network Share, Samba Network Share) these types of network mount points do not support 'inotify', thus tracking real-time changes via inotify of local files is not possible. Local filesystem changes will be replicated between the local filesystem and OneDrive based on the `monitor_interval` value. This is not something (inotify support for NFS, Samba) that this client can fix. +#### sync_dir directory and file permissions +The following are directory and file default permissions for any new directory or file that is created: +* Directories: 700 - This provides the following permissions: `drwx------` +* Files: 600 - This provides the following permissions: `-rw-------` + +To change the default permissions, update the following 2 configuration options with the required permissions. Utilise [Unix Permissions Calculator](http://permissions-calculator.org/) to assist in determining the required permissions. + +```text +# When changing a config option below, remove the '#' from the start of the line +# For explanations of all config options below see docs/USAGE.md or the man page. +# +... +# sync_business_shared_folders = "false" +sync_dir_permissions = "700" +sync_file_permissions = "600" + +``` + +**Important:** Special permission bits (setuid, setgid, sticky bit) are not supported. Valid permission values are from `000` to `777` only. + #### skip_dir Example: ```text diff --git a/src/config.d b/src/config.d index 63f8184a..678736cc 100644 --- a/src/config.d +++ b/src/config.d @@ -36,11 +36,17 @@ final class Config private long[string] longValues; // Compile time regex - this does not change public auto configRegex = ctRegex!(`^(\w+)\s*=\s*"(.*)"\s*$`); + // Default directory permission mode + public long defaultDirectoryPermissionMode = 700; + public int configuredDirectoryPermissionMode; + // Default file permission mode + public long defaultFilePermissionMode = 600; + public int configuredFilePermissionMode; this(string confdirOption) { // default configuration - entries in config file ~/.config/onedrive/config - // an entry here means it can be set via the config file if there is a coresponding read and set in update_from_args() + // an entry here means it can be set via the config file if there is a coresponding entry, read from config and set via update_from_args() stringValues["sync_dir"] = defaultSyncDir; stringValues["skip_file"] = defaultSkipFile; stringValues["skip_dir"] = defaultSkipDir; @@ -106,6 +112,10 @@ final class Config stringValues["azure_tenant_id"] = "common"; // Allow enable / disable of the syncing of OneDrive Business Shared Folders via configuration file boolValues["sync_business_shared_folders"] = false; + // Configure the default folder permission attributes for newly created folders + longValues["sync_dir_permissions"] = defaultDirectoryPermissionMode; + // Configure the default file permission attributes for newly created file + longValues["sync_file_permissions"] = defaultFilePermissionMode; // DEVELOPER OPTIONS // display_memory = true | false @@ -181,7 +191,13 @@ final class Config } // Config directory options all determined - if (!exists(configDirName)) mkdirRecurse(configDirName); + if (!exists(configDirName)) { + // create the directory + mkdirRecurse(configDirName); + // Configure the applicable permissions for the folder + configDirName.setAttributes(returnRequiredDirectoryPermisions()); + } + // configDirName has a trailing / log.vlog("Using 'user' Config Dir: ", configDirName); log.vlog("Using 'system' Config Dir: ", systemConfigDirName); @@ -611,6 +627,50 @@ final class Config } return true; } + + void configureRequiredDirectoryPermisions() { + // return the directory permission mode required + // - return octal!defaultDirectoryPermissionMode; ... cant be used .. which is odd + // Error: variable defaultDirectoryPermissionMode cannot be read at compile time + if (getValueLong("sync_dir_permissions") != defaultDirectoryPermissionMode) { + // return user configured permissions as octal integer + string valueToConvert = to!string(getValueLong("sync_dir_permissions")); + auto convertedValue = parse!long(valueToConvert, 8); + configuredDirectoryPermissionMode = to!int(convertedValue); + } else { + // return default as octal integer + string valueToConvert = to!string(defaultDirectoryPermissionMode); + auto convertedValue = parse!long(valueToConvert, 8); + configuredDirectoryPermissionMode = to!int(convertedValue); + } + } + + void configureRequiredFilePermisions() { + // return the file permission mode required + // - return octal!defaultFilePermissionMode; ... cant be used .. which is odd + // Error: variable defaultFilePermissionMode cannot be read at compile time + if (getValueLong("sync_file_permissions") != defaultFilePermissionMode) { + // return user configured permissions as octal integer + string valueToConvert = to!string(getValueLong("sync_file_permissions")); + auto convertedValue = parse!long(valueToConvert, 8); + configuredFilePermissionMode = to!int(convertedValue); + } else { + // return default as octal integer + string valueToConvert = to!string(defaultFilePermissionMode); + auto convertedValue = parse!long(valueToConvert, 8); + configuredFilePermissionMode = to!int(convertedValue); + } + } + + int returnRequiredDirectoryPermisions() { + // read the configuredDirectoryPermissionMode and return + return configuredDirectoryPermissionMode; + } + + int returnRequiredFilePermisions() { + // read the configuredFilePermissionMode and return + return configuredFilePermissionMode; + } } void outputLongHelp(Option[] opt) diff --git a/src/main.d b/src/main.d index d8d148e5..fbe815a8 100644 --- a/src/main.d +++ b/src/main.d @@ -607,6 +607,23 @@ int main(string[] args) itemDb = new ItemDatabase(cfg.databaseFilePathDryRun); } + // What are the permission that have been set for the application? + // These are relevant for: + // - The ~/OneDrive parent folder or 'sync_dir' configured item + // - Any new folder created under ~/OneDrive or 'sync_dir' + // - Any new file created under ~/OneDrive or 'sync_dir' + // valid permissions are 000 -> 777 - anything else is invalid + if ((cfg.getValueLong("sync_dir_permissions") < 0) || (cfg.getValueLong("sync_file_permissions") < 0) || (cfg.getValueLong("sync_dir_permissions") > 777) || (cfg.getValueLong("sync_file_permissions") > 777)) { + log.error("ERROR: Invalid 'User|Group|Other' permissions set within config file. Please check."); + return EXIT_FAILURE; + } else { + // debug log output what permissions are being set to + log.vdebug("Configuring default new folder permissions as: ", cfg.getValueLong("sync_dir_permissions")); + cfg.configureRequiredDirectoryPermisions(); + log.vdebug("Configuring default new file permissions as: ", cfg.getValueLong("sync_file_permissions")); + cfg.configureRequiredFilePermisions(); + } + // configure the sync direcory based on syncDir config option log.vlog("All operations will be performed in: ", syncDir); if (!exists(syncDir)) { @@ -614,6 +631,8 @@ int main(string[] args) try { // Attempt to create the sync dir we have been configured with mkdirRecurse(syncDir); + // Configure the applicable permissions for the folder + syncDir.setAttributes(cfg.returnRequiredDirectoryPermisions()); } catch (std.file.FileException e) { // Creating the sync directory failed log.error("ERROR: Unable to create local OneDrive syncDir - ", e.msg); @@ -621,6 +640,8 @@ int main(string[] args) return EXIT_FAILURE; } } + + // Change the working directory to the 'sync_dir' configured item chdir(syncDir); // Configure selective sync by parsing and getting a regex for skip_file config component @@ -858,8 +879,11 @@ int main(string[] args) if (!exists(cfg.getValueString("single_directory"))) { // The requested path to use with --single-directory does not exist locally within the configured 'sync_dir' log.logAndNotify("WARNING: The requested path for --single-directory does not exist locally. Creating requested path within ", syncDir); - // Make the required path locally - mkdirRecurse(cfg.getValueString("single_directory")); + // Make the required --single-directory path locally + string singleDirectoryPath = cfg.getValueString("single_directory"); + mkdirRecurse(singleDirectoryPath); + // Configure the applicable permissions for the folder + singleDirectoryPath.setAttributes(cfg.returnRequiredDirectoryPermisions()); } } // perform a --synchronize sync diff --git a/src/onedrive.d b/src/onedrive.d index 77b50f27..47542748 100644 --- a/src/onedrive.d +++ b/src/onedrive.d @@ -495,7 +495,11 @@ final class OneDriveApi scope(failure) { if (exists(saveToPath)) remove(saveToPath); } - mkdirRecurse(dirName(saveToPath)); + // Create the directory + string newPath = dirName(saveToPath); + mkdirRecurse(newPath); + // Configure the applicable permissions for the folder + newPath.setAttributes(cfg.returnRequiredDirectoryPermisions()); const(char)[] url = driveByIdUrl ~ driveId ~ "/items/" ~ id ~ "/content?AVOverride=1"; download(url, saveToPath, fileSize); } @@ -807,6 +811,8 @@ final class OneDriveApi // close open file file.close(); } + // Configure the applicable permissions for the file + filename.setAttributes(cfg.returnRequiredFilePermisions()); } http.method = HTTP.Method.get; diff --git a/src/sync.d b/src/sync.d index 55f384a6..5cfac9c6 100644 --- a/src/sync.d +++ b/src/sync.d @@ -2413,7 +2413,10 @@ final class SyncEngine } if (!dryRun) { + // Create the new directory mkdirRecurse(path); + // Configure the applicable permissions for the folder + path.setAttributes(cfg.returnRequiredDirectoryPermisions()); } else { // we dont create the directory, but we need to track that we 'faked it' idsFaked ~= [item.driveId, item.id];