diff --git a/options.c b/options.c index 578507c6e..a74d2b0e1 100644 --- a/options.c +++ b/options.c @@ -224,6 +224,7 @@ char *iconv_opt = #endif struct chmod_mode_struct *chmod_modes = NULL; +struct chmod_mode_struct *dest_chmod_modes = NULL; static const char *debug_verbosity[] = { /*0*/ NULL, @@ -584,7 +585,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE, OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS, - OPT_STOP_AFTER, OPT_STOP_AT, + OPT_STOP_AFTER, OPT_STOP_AT, OPT_CHMOD_DEST, OPT_REFUSED_BASE = 9000}; static struct poptOption long_options[] = { @@ -687,6 +688,7 @@ static struct poptOption long_options[] = { {"i-d", 0, POPT_ARG_VAL, &implied_dirs, 1, 0, 0 }, {"no-i-d", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 }, {"chmod", 0, POPT_ARG_STRING, 0, OPT_CHMOD, 0, 0 }, + {"chmod-dest", 0, POPT_ARG_STRING, 0, OPT_CHMOD_DEST, 0, 0 }, {"ignore-times", 'I', POPT_ARG_NONE, &ignore_times, 0, 0, 0 }, {"size-only", 0, POPT_ARG_NONE, &size_only, 0, 0, 0 }, {"one-file-system", 'x', POPT_ARG_NONE, 0, 'x', 0, 0 }, @@ -1751,6 +1753,16 @@ int parse_arguments(int *argc_p, const char ***argv_p) } break; + case OPT_CHMOD_DEST: + arg = poptGetOptArg(pc); + if (!parse_chmod(arg, &dest_chmod_modes)) { + snprintf(err_buf, sizeof err_buf, + "Invalid argument passed to --chmod-dest (%s)\n", + arg); + goto cleanup; + } + break; + case OPT_INFO: arg = poptGetOptArg(pc); parse_output_words(info_words, info_levels, arg, USER_PRIORITY); diff --git a/rsync.1.md b/rsync.1.md index 7e40e3617..1fb75d85e 100644 --- a/rsync.1.md +++ b/rsync.1.md @@ -448,6 +448,7 @@ has its own detailed description later in this manpage. --perms, -p preserve permissions --executability, -E preserve executability --chmod=CHMOD affect file and/or directory permissions +--chmod-dest=CHMOD override permissions with --fake-super --acls, -A preserve ACLs (implies --perms) --xattrs, -X preserve extended attributes --owner, -o preserve owner (super-user only) @@ -1518,6 +1519,20 @@ expand it. See the [`--perms`](#opt) and [`--executability`](#opt) options for how the resulting permission value can be applied to the files in the transfer. +0. `--chmod-dest=CHMOD` + + This option tells rsync to apply one or more comma-separated "chmod" modes + to the permissions of the destination file on disk. The syntax is identical + to the [`--chmod`](#opt) option. + + This option is used with the [`--fake-super`](#opt) option to apply the + original source permissions to the fake super attributes, while applying + different permissions to the destination file. + + When the destination file is being used as a backup, this allows permissions + to be applied to the backup independent of the permissions of the source + file being backed up. + 0. `--owner`, `-o` This option causes rsync to set the owner of the destination file to be the diff --git a/rsync.c b/rsync.c index b130aba56..418f56a85 100644 --- a/rsync.c +++ b/rsync.c @@ -55,6 +55,7 @@ extern int make_backups; extern int sanitize_paths; extern struct file_list *cur_flist, *first_flist, *dir_flist; extern struct chmod_mode_struct *daemon_chmod_modes; +extern struct chmod_mode_struct *dest_chmod_modes; #ifdef ICONV_OPTION extern char *iconv_opt; #endif @@ -655,9 +656,12 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, } #endif + if (dest_chmod_modes) + new_mode = tweak_mode(new_mode, dest_chmod_modes); + #ifdef HAVE_CHMOD if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) { - int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode); + int ret = am_root < 0 && !dest_chmod_modes ? 0 : do_chmod(fname, new_mode); if (ret < 0) { rsyserr(FERROR_XFER, errno, "failed to set permissions on %s",