--- /dev/null
+This patches creates two new command line options as follows:
+ --delete-dir
+ --delete-suffix
+
+The delete-dir and delete-suffix options give the ability to store
+backup of deleted files on the receiver in different directories
+or with different suffix than the backup of files that have been
+changed but that are still on the source drive. Both commands can
+be combined.
+
+The default behaviour if one or both of the options are not specified
+is the previous behaviour, both backups use the same directory or
+suffix.
+
+Marc St-Onge
+
+--- orig/backup.c 2004-09-20 19:50:13
++++ backup.c 2004-09-22 02:18:49
+@@ -22,11 +22,17 @@
+
+ extern int verbose;
+ extern int backup_suffix_len;
++extern int delete_suffix_len;
+ extern int backup_dir_len;
++extern int delete_dir_len;
+ extern unsigned int backup_dir_remainder;
++extern unsigned int delete_dir_remainder;
+ extern char backup_dir_buf[MAXPATHLEN];
++extern char delete_dir_buf[MAXPATHLEN];
+ extern char *backup_suffix;
++extern char *delete_suffix;
+ extern char *backup_dir;
++extern char *delete_dir;
+
+ extern int am_root;
+ extern int preserve_devices;
+@@ -35,6 +41,8 @@ extern int preserve_hard_links;
+ extern int orig_umask;
+ extern int safe_symlinks;
+
++static int deleting;
++
+ /* make a complete pathname for backup file */
+ char *get_backup_name(char *fname)
+ {
+@@ -52,10 +60,27 @@ char *get_backup_name(char *fname)
+ return NULL;
+ }
+
++static char *get_delete_name(char *fname)
++{
++ if (delete_dir) {
++ if (stringjoin(delete_dir_buf + delete_dir_len, delete_dir_remainder,
++ fname, delete_suffix, NULL) < delete_dir_remainder)
++ return delete_dir_buf;
++ } else {
++ if (stringjoin(delete_dir_buf, MAXPATHLEN,
++ fname, delete_suffix, NULL) < MAXPATHLEN)
++ return delete_dir_buf;
++ }
++
++ rprintf(FERROR, "delete filename too long\n");
++ return NULL;
++}
++
+ /* simple backup creates a backup with a suffix in the same directory */
+ static int make_simple_backup(char *fname)
+ {
+- char *fnamebak = get_backup_name(fname);
++ char *fnamebak = deleting ? get_delete_name(fname)
++ : get_backup_name(fname);
+
+ if (!fnamebak)
+ return 0;
+@@ -81,7 +106,8 @@ path
+ static int make_bak_dir(char *fullpath)
+ {
+ STRUCT_STAT st;
+- char *rel = fullpath + backup_dir_len;
++ int dir_len = deleting ? delete_dir_len : backup_dir_len;
++ char *rel = fullpath + dir_len;
+ char *end = rel + strlen(rel);
+ char *p = end;
+
+@@ -173,7 +199,8 @@ static int keep_backup(char *fname)
+ if (!(file = make_file(fname, NULL, NO_EXCLUDES)))
+ return 1; /* the file could have disappeared */
+
+- if (!(buf = get_backup_name(fname)))
++ buf = deleting ? get_delete_name(fname) : get_backup_name(fname);
++ if (!buf)
+ return 0;
+
+ #ifdef HAVE_MKNOD
+@@ -262,7 +289,18 @@ static int keep_backup(char *fname)
+ /* main backup switch routine */
+ int make_backup(char *fname)
+ {
++ int ret;
+ if (backup_dir)
+- return keep_backup(fname);
+- return make_simple_backup(fname);
++ ret = keep_backup(fname);
++ else
++ ret = make_simple_backup(fname);
++ deleting = 0; /* Always restore the default backup process. */
++ return ret;
++}
++
++/* backup switch routine called only when backing-up deleted file */
++int safe_delete(char *fname)
++{
++ deleting = 1;
++ return make_backup(fname);
+ }
+--- orig/options.c 2004-09-20 05:10:48
++++ options.c 2004-09-22 02:25:03
+@@ -113,10 +113,14 @@ int no_detach = 0;
+ int write_batch = 0;
+ int read_batch = 0;
+ int backup_dir_len = 0;
++int delete_dir_len = 0;
+ int backup_suffix_len;
++int delete_suffix_len;
+ unsigned int backup_dir_remainder;
++unsigned int delete_dir_remainder;
+
+ char *backup_suffix = NULL;
++char *delete_suffix = NULL;
+ char *tmpdir = NULL;
+ char *partial_dir = NULL;
+ char *compare_dest = NULL;
+@@ -126,7 +130,9 @@ char *log_format = NULL;
+ char *password_file = NULL;
+ char *rsync_path = RSYNC_PATH;
+ char *backup_dir = NULL;
++char *delete_dir = NULL;
+ char backup_dir_buf[MAXPATHLEN];
++char delete_dir_buf[MAXPATHLEN];
+ int rsync_port = RSYNC_PORT;
+ int link_dest = 0;
+
+@@ -239,7 +245,9 @@ void usage(enum logcode F)
+ rprintf(F," --no-implied-dirs don't send implied dirs with -R\n");
+ rprintf(F," -b, --backup make backups (see --suffix & --backup-dir)\n");
+ rprintf(F," --backup-dir make backups into this directory\n");
++ rprintf(F," --delete-dir make backups of deleted files into this directory\n");
+ rprintf(F," --suffix=SUFFIX backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
++ rprintf(F," --delete-suffix=SUFFIX deleted files suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
+ rprintf(F," -u, --update update only (don't overwrite newer files)\n");
+ rprintf(F," --inplace update destination files inplace (SEE MAN PAGE)\n");
+ rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n");
+@@ -326,6 +334,7 @@ static struct poptOption long_options[]
+ /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+ {"version", 0, POPT_ARG_NONE, 0, OPT_VERSION, 0, 0},
+ {"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
++ {"delete-suffix", 0, POPT_ARG_STRING, &delete_suffix, 0, 0, 0 },
+ {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 },
+ {"password-file", 0, POPT_ARG_STRING, &password_file, 0, 0, 0 },
+ {"ignore-times", 'I', POPT_ARG_NONE, &ignore_times, 0, 0, 0 },
+@@ -396,6 +405,7 @@ static struct poptOption long_options[]
+ {"bwlimit", 0, POPT_ARG_INT, &bwlimit, 0, 0, 0 },
+ {"address", 0, POPT_ARG_STRING, &bind_address, 0, 0, 0 },
+ {"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
++ {"delete-dir", 0, POPT_ARG_STRING, &delete_dir, 0, 0, 0 },
+ {"hard-links", 'H', POPT_ARG_NONE, &preserve_hard_links, 0, 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 },
+@@ -733,6 +743,8 @@ int parse_arguments(int *argc, const cha
+ compare_dest = sanitize_path(NULL, compare_dest, NULL, 0);
+ if (backup_dir)
+ backup_dir = sanitize_path(NULL, backup_dir, NULL, 0);
++ if (delete_dir)
++ delete_dir = sanitize_path(NULL, delete_dir, NULL, 0);
+ if (files_from)
+ files_from = sanitize_path(NULL, files_from, NULL, 0);
+ }
+@@ -758,6 +770,12 @@ int parse_arguments(int *argc, const cha
+ if (check_exclude(elp, backup_dir, 1) < 0)
+ goto options_rejected;
+ }
++ /* Clean delete_dir same as for backup_dir */
++ if (delete_dir) {
++ clean_fname(delete_dir, 1);
++ if (check_exclude(elp, delete_dir, 1) < 0)
++ goto options_rejected;
++ }
+ }
+ if (server_exclude_list.head && files_from) {
+ clean_fname(files_from, 1);
+@@ -784,6 +802,16 @@ int parse_arguments(int *argc, const cha
+ backup_suffix);
+ return 0;
+ }
++ /* if deleted_suffix not supplied, default to backup_suffix */
++ if (!delete_suffix)
++ delete_suffix = delete_dir ? "" : backup_suffix;
++ delete_suffix_len = strlen(delete_suffix);
++ if (strchr(delete_suffix, '/') != NULL) {
++ snprintf(err_buf, sizeof err_buf,
++ "--delete-suffix cannot contain slashes: %s\n",
++ delete_suffix);
++ return 0;
++ }
+ if (backup_dir) {
+ backup_dir_len = strlcpy(backup_dir_buf, backup_dir, sizeof backup_dir_buf);
+ backup_dir_remainder = sizeof backup_dir_buf - backup_dir_len;
+@@ -803,6 +831,31 @@ int parse_arguments(int *argc, const cha
+ "--suffix cannot be a null string without --backup-dir\n");
+ return 0;
+ }
++ /* If delete_dir not supplied default to backup_dir if it has been supplied */
++ if (backup_dir && !delete_dir) {
++ delete_dir = backup_dir;
++ delete_dir_len = backup_dir_len;
++ delete_dir_remainder = backup_dir_remainder;
++ strlcpy(delete_dir_buf, backup_dir_buf, sizeof backup_dir_buf);
++ } else if (delete_dir) {
++ delete_dir_len = strlcpy(delete_dir_buf, delete_dir, sizeof delete_dir_buf);
++ delete_dir_remainder = sizeof delete_dir_buf - delete_dir_len;
++ if (delete_dir_remainder < 32) {
++ snprintf(err_buf, sizeof err_buf,
++ "the --delete-dir path is WAY too long.\n");
++ return 0;
++ }
++ if (delete_dir_buf[delete_dir_len - 1] != '/') {
++ delete_dir_buf[delete_dir_len++] = '/';
++ delete_dir_buf[delete_dir_len] = '\0';
++ }
++ if (verbose > 1 && !am_sender)
++ rprintf(FINFO, "delete_dir is %s\n", delete_dir_buf);
++ } else if (!delete_suffix_len && (!am_server || !am_sender)) {
++ snprintf(err_buf, sizeof err_buf,
++ "--delete-suffix cannot be a null string without --delete-dir\n");
++ return 0;
++ }
+
+ if (do_progress && !verbose)
+ verbose = 1;
+@@ -1005,6 +1058,10 @@ void server_options(char **args,int *arg
+ args[ac++] = "--backup-dir";
+ args[ac++] = backup_dir;
+ }
++ if (delete_dir) {
++ args[ac++] = "--delete-dir";
++ args[ac++] = delete_dir;
++ }
+
+ /* Only send --suffix if it specifies a non-default value. */
+ if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
+@@ -1013,7 +1070,13 @@ void server_options(char **args,int *arg
+ goto oom;
+ args[ac++] = arg;
+ }
+-
++ /* Only send --delete-suffix if it specifies a non-default value. */
++ if (strcmp(delete_suffix, delete_dir ? "" : BACKUP_SUFFIX) != 0) {
++ /* We use the following syntax to avoid weirdness with '~'. */
++ if (asprintf(&arg, "--delete-suffix=%s", delete_suffix) < 0)
++ goto oom;
++ args[ac++] = arg;
++ }
+ if (am_sender) {
+ if (delete_excluded)
+ args[ac++] = "--delete-excluded";
+--- orig/receiver.c 2004-09-21 09:40:27
++++ receiver.c 2004-09-22 02:09:20
+@@ -42,8 +42,11 @@ extern char *compare_dest;
+ extern int make_backups;
+ extern int do_progress;
+ extern char *backup_dir;
++extern char *delete_dir;
+ extern char *backup_suffix;
++extern char *delete_suffix;
+ extern int backup_suffix_len;
++extern int delete_suffix_len;
+ extern int cleanup_got_literal;
+ extern int module_id;
+ extern int ignore_errors;
+@@ -77,11 +80,14 @@ static void delete_one(char *fn, int is_
+ }
+ }
+
+-
++/* Function now checks if file matches backup or delete suffix patterns */
+ static int is_backup_file(char *fn)
+ {
+ int k = strlen(fn) - backup_suffix_len;
+- return k > 0 && strcmp(fn+k, backup_suffix) == 0;
++ if (k > 0 && strcmp(fn+k, backup_suffix) == 0)
++ return 1;
++ k = strlen(fn) - delete_suffix_len;
++ return k > 0 && strcmp(fn+k, delete_suffix) == 0;
+ }
+
+
+@@ -122,10 +128,11 @@ void delete_files(struct file_list *flis
+ continue;
+ if (flist_find(flist,local_file_list->files[i]) < 0) {
+ char *f = f_name(local_file_list->files[i]);
+- if (make_backups && (backup_dir || !is_backup_file(f))) {
+- make_backup(f);
++ int backup_file = is_backup_file(f);
++ if (make_backups && (delete_dir || !backup_file)) {
++ safe_delete(f);
+ if (verbose) {
+- rprintf(FINFO, "deleting %s\n",
++ rprintf(FINFO, "safe-deleting %s\n",
+ safe_fname(f));
+ }
+ } else {