unmake_file(file);
}
-@@ -156,28 +163,34 @@ static BOOL copy_valid_path(const char *fname)
+@@ -156,7 +163,12 @@ static BOOL copy_valid_path(const char *fname)
/* Make a complete pathname for backup file and verify any new path elements. */
char *get_backup_name(const char *fname)
{
+ char *suffix = deleting ? backup_suffix_dels : backup_suffix;
+
if (backup_dir) {
- static int initialized = 0;
-- if (!initialized) {
+ int prefix_len = deleting ? backup_dir_dels_len : backup_dir_len;
+ unsigned int remainder = deleting ? backup_dir_dels_remainder : backup_dir_remainder;
-+ int init_bits = deleting ? 2 : 1;
-+ if (!(initialized & init_bits)) {
+ static int initialized = 0;
+ if (!initialized) {
int ret;
-- if (backup_dir_len > 1)
-- backup_dir_buf[backup_dir_len-1] = '\0';
-- ret = make_path(backup_dir_buf, 0);
-- if (backup_dir_len > 1)
-- backup_dir_buf[backup_dir_len-1] = '/';
-+ if (prefix_len > 1)
-+ buf[prefix_len-1] = '\0';
-+ ret = make_path(buf, 0);
-+ if (prefix_len > 1)
-+ buf[prefix_len-1] = '/';
- if (ret < 0)
- return NULL;
-- initialized = 1;
-+ initialized |= init_bits;
+@@ -170,14 +182,14 @@ char *get_backup_name(const char *fname)
+ initialized = 1;
}
/* copy fname into backup_dir_buf while validating the dirs. */
- if (copy_valid_path(fname))
- if (stringjoin(backup_dir_buf, MAXPATHLEN, fname, backup_suffix, NULL) < MAXPATHLEN)
- return backup_dir_buf;
-+ if (stringjoin(buf, MAXPATHLEN, fname, suffix, NULL) < MAXPATHLEN)
++ if (stringjoin(backup_dir_buf, MAXPATHLEN, fname, suffix, NULL) < MAXPATHLEN)
+ return buf;
rprintf(FERROR, "backup filename too long\n");
return NULL;
-@@ -352,3 +365,13 @@ int make_backup(const char *fname, BOOL prefer_rename)
+@@ -353,3 +365,13 @@ int make_backup(const char *fname, BOOL prefer_rename)
rprintf(FINFO, "backed up %s to %s\n", fname, buf);
return ret;
}
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
-@@ -153,10 +153,14 @@ int no_detach
+@@ -166,10 +166,14 @@ int no_detach
int write_batch = 0;
int read_batch = 0;
int backup_dir_len = 0;
char *tmpdir = NULL;
char *partial_dir = NULL;
char *basis_dir[MAX_BASIS_DIRS+1];
-@@ -168,7 +172,9 @@ char *stdout_format = NULL;
- char *password_file = NULL;
+@@ -182,7 +186,9 @@ char *password_file = NULL;
+ char *early_input_file = NULL;
char *rsync_path = RSYNC_PATH;
char *backup_dir = NULL;
+char *backup_dir_dels = NULL;
char *sockopts = NULL;
char *usermap = NULL;
char *groupmap = NULL;
-@@ -681,6 +687,8 @@ void usage(enum logcode F)
- rprintf(F," --backup-deleted make backups only of deleted files\n");
- rprintf(F," --backup-dir=DIR make backups into hierarchy based in DIR\n");
- rprintf(F," --suffix=SUFFIX set backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
-+ rprintf(F," --backup-dir-dels=DIR backup removed files into hierarchy based in DIR\n");
-+ rprintf(F," --suffix-dels=SUFFIX set removed-files suffix (def. --suffix w/o b-d-d)\n");
- rprintf(F," -u, --update skip files that are newer on the receiver\n");
- rprintf(F," --inplace update destination files in-place (SEE MAN PAGE)\n");
- rprintf(F," --append append data onto shorter files\n");
-@@ -999,7 +1007,9 @@ static struct poptOption long_options[] = {
+@@ -780,7 +786,9 @@ static struct poptOption long_options[] = {
{"backup-deleted", 0, POPT_ARG_VAL, &make_backups, 1, 0, 0 },
{"no-backup", 0, POPT_ARG_VAL, &make_backups, 0, 0, 0 },
{"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
{"list-only", 0, POPT_ARG_VAL, &list_only, 2, 0, 0 },
{"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 },
{"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 },
-@@ -2101,6 +2111,8 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+@@ -2253,6 +2261,8 @@ int parse_arguments(int *argc_p, const char ***argv_p)
tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT);
if (backup_dir)
backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, SP_DEFAULT);
}
if (daemon_filter_list.head && !am_sender) {
filter_rule_list *elp = &daemon_filter_list;
-@@ -2122,6 +2134,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+@@ -2274,6 +2284,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (check_filter(elp, FLOG, dir, 1) < 0)
goto options_rejected;
}
}
if (!backup_suffix)
-@@ -2133,6 +2153,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+@@ -2285,6 +2303,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
backup_suffix);
- return 0;
+ goto cleanup;
}
+ /* --suffix-dels defaults to --suffix, or empty for a client given an
+ * explicit --backup-dir-dels (just as --suffix defaults to empty when
+ }
if (backup_dir) {
size_t len;
- while (*backup_dir == '.' && backup_dir[1] == '/')
-@@ -2168,6 +2202,34 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+ make_backups = 1; /* --backup-dir implies --backup */
+@@ -2321,6 +2353,34 @@ int parse_arguments(int *argc_p, const char ***argv_p)
"P *%s", backup_suffix);
parse_filter_str(&filter_list, backup_dir_buf, rule_template(0), 0);
}
+ parse_filter_str(&filter_list, backup_dir_dels_buf, rule_template(0), 0);
+ }
- if (preserve_times) {
- preserve_times = PRESERVE_FILE_TIMES;
-@@ -2594,6 +2656,10 @@ void server_options(char **args, int *argc_p)
+ if (make_backups && !backup_dir)
+ omit_dir_times = -1; /* Implied, so avoid -O to sender. */
+@@ -2790,11 +2850,20 @@ void server_options(char **args, int *argc_p)
args[ac++] = "--backup-dir";
- args[ac++] = backup_dir;
+ args[ac++] = safe_arg("", backup_dir);
}
+ if (backup_dir_dels && backup_dir_dels != backup_dir) {
+ args[ac++] = "--backup-dir-dels";
+ }
/* Only send --suffix if it specifies a non-default value. */
- if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
-@@ -2602,6 +2668,14 @@ void server_options(char **args, int *argc_p)
- goto oom;
- args[ac++] = arg;
- }
+ if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0)
+ args[ac++] = safe_arg("--suffix", backup_suffix);
+
+ /* Only send --suffix-dels if it specifies a value different from the
+ * --suffix value, which would normally be used for deletions too. */
-+ if (strcmp(backup_suffix_dels, backup_suffix) != 0) {
-+ /* We use the following syntax to avoid weirdness with '~'. */
-+ if (asprintf(&arg, "--suffix-dels=%s", backup_suffix_dels) < 0)
-+ goto oom;
-+ args[ac++] = arg;
-+ }
++ if (strcmp(backup_suffix_dels, backup_suffix) != 0)
++ args[ac++] = safe_arg("--suffix-dels", backup_suffix_dels);
++
+ if (checksum_choice)
+ args[ac++] = safe_arg("--checksum-choice", checksum_choice);
+
+diff --git a/rsync.1.md b/rsync.1.md
+--- a/rsync.1.md
++++ b/rsync.1.md
+@@ -430,7 +430,9 @@ has its own detailed description later in this manpage.
+ --backup, -b make backups (see --suffix & --backup-dir)
+ --backup-deleted make backups only of deleted files
+ --backup-dir=DIR make backups into hierarchy based in DIR
++--backup-dir-dels=DIR backup removed files into hierarchy based in DIR
+ --suffix=SUFFIX backup suffix (default ~ w/o --backup-dir)
++--suffix-dels=SUFFIX set removed-files suffix (def. --suffix w/o b-d-d)
+ --update, -u skip files that are newer on the receiver
+ --inplace update destination files in-place
+ --append append data onto shorter files
+@@ -1028,6 +1030,11 @@ expand it.
+ daemon is the receiver, the backup dir cannot go outside the module's path
+ hierarchy, so take extra care not to delete it or copy into it.
+
++0. `--backup-dir-dels=DIR`
++
++ Works like [`--backup-dir`](#opt) except for deleted files in conjunction
++ with the [`--backup-deleted`](#opt) option.
++
+ 0. `--suffix=SUFFIX`
- if (checksum_choice) {
- if (asprintf(&arg, "--checksum-choice=%s", checksum_choice) < 0)
+ This option allows you to override the default backup suffix used with the