Updated patches to work with the current trunk.
[rsync/rsync-patches.git] / backup-dir-dels.diff
CommitLineData
8af83008 1This patches creates two new command line options as follows:
19a778eb
WD
2 --backup-dir-dels=DIR
3 --suffix-dels=SUFFIX
8af83008 4
19a778eb 5The backup-dir-dels and suffix-dels options give the ability to store
79f132a1
WD
6backup of removed files on the receiver in different directories or with
7different suffix than the backup of files that have been changed but that
8are still on the source drive. Both commands can be combined.
8af83008
WD
9
10The default behaviour if one or both of the options are not specified
11is the previous behaviour, both backups use the same directory or
12suffix.
13
14Marc St-Onge
15
03019e41
WD
16To use this patch, run these commands for a successful build:
17
18 patch -p1 <patches/backup-dir-dels.diff
19 ./configure (optional if already run)
20 make
21
cc3e685d 22diff --git a/backup.c b/backup.c
fc557362 23index 0da6b33..ff77078 100644
cc3e685d
WD
24--- a/backup.c
25+++ b/backup.c
a54a2c4d 26@@ -29,10 +29,17 @@ extern int preserve_specials;
fc068916
WD
27 extern int preserve_links;
28 extern int safe_symlinks;
8af83008 29 extern int backup_dir_len;
19a778eb 30+extern int backup_dir_dels_len;
8af83008 31 extern unsigned int backup_dir_remainder;
19a778eb 32+extern unsigned int backup_dir_dels_remainder;
8af83008 33 extern char backup_dir_buf[MAXPATHLEN];
19a778eb 34+extern char backup_dir_dels_buf[MAXPATHLEN];
8af83008 35 extern char *backup_suffix;
19a778eb 36+extern char *backup_suffix_dels;
8af83008 37 extern char *backup_dir;
19a778eb 38+extern char *backup_dir_dels;
8af83008 39+
fc557362 40+static BOOL deleting;
70891d26 41
8af83008 42 /* make a complete pathname for backup file */
5e3c6c93 43 char *get_backup_name(const char *fname)
fc557362 44@@ -51,6 +58,22 @@ char *get_backup_name(const char *fname)
8af83008
WD
45 return NULL;
46 }
47
5e3c6c93 48+static char *get_delete_name(const char *fname)
8af83008 49+{
19a778eb
WD
50+ if (backup_dir_dels) {
51+ if (stringjoin(backup_dir_dels_buf + backup_dir_dels_len, backup_dir_dels_remainder,
52+ fname, backup_suffix_dels, NULL) < backup_dir_dels_remainder)
53+ return backup_dir_dels_buf;
8af83008 54+ } else {
19a778eb
WD
55+ if (stringjoin(backup_dir_dels_buf, MAXPATHLEN,
56+ fname, backup_suffix_dels, NULL) < MAXPATHLEN)
57+ return backup_dir_dels_buf;
8af83008
WD
58+ }
59+
60+ rprintf(FERROR, "delete filename too long\n");
61+ return NULL;
62+}
63+
fc557362
WD
64 /****************************************************************************
65 Create a directory given an absolute path, perms based upon another directory
66 path
67@@ -59,7 +82,7 @@ int make_bak_dir(const char *fullpath)
8af83008 68 {
cdcd2137 69 char fbuf[MAXPATHLEN], *rel, *end, *p;
ffc18846 70 struct file_struct *file;
cdcd2137
WD
71- int len = backup_dir_len;
72+ int len = deleting ? backup_dir_dels_len : backup_dir_len;
52e09c4e 73 stat_x sx;
8af83008 74
cdcd2137 75 while (*fullpath == '.' && fullpath[1] == '/') {
fc557362
WD
76@@ -179,7 +202,7 @@ int make_backup(const char *fname, BOOL prefer_rename)
77 stat_x sx;
78 struct file_struct *file;
79 int save_preserve_xattrs;
80- char *buf = get_backup_name(fname);
81+ char *buf = deleting ? get_delete_name(fname) : get_backup_name(fname);
82 int ret = 0;
8af83008 83
fc557362
WD
84 if (!buf)
85@@ -341,3 +364,13 @@ int make_backup(const char *fname, BOOL prefer_rename)
70891d26 86 }
fc557362 87 return ret;
44917741 88 }
8af83008 89+
79f132a1 90+/* backup switch routine called only when backing-up removed file */
fc557362 91+int safe_delete(const char *fname)
8af83008 92+{
44917741 93+ int ret;
8af83008 94+ deleting = 1;
fc557362 95+ ret = make_backup(fname, True);
44917741
WD
96+ deleting = 0;
97+ return ret;
98+}
fc557362
WD
99diff --git a/delete.c b/delete.c
100index 33fdd0e..31228d0 100644
101--- a/delete.c
102+++ b/delete.c
103@@ -28,6 +28,9 @@ extern int max_delete;
52f25864
WD
104 extern char *backup_dir;
105 extern char *backup_suffix;
106 extern int backup_suffix_len;
19a778eb
WD
107+extern char *backup_dir_dels;
108+extern char *backup_suffix_dels;
109+extern int backup_suffix_dels_len;
fc557362
WD
110 extern uid_t our_uid;
111 extern struct stats stats;
cc3e685d 112
fc557362
WD
113@@ -35,10 +38,14 @@ int ignore_perishable = 0;
114 int non_perishable_cnt = 0;
115 int skipped_deletes = 0;
52f25864 116
d608ca23 117+/* Function now compares both backup_suffix and backup_suffix_dels. */
fc557362 118 static inline int is_backup_file(char *fn)
52f25864
WD
119 {
120 int k = strlen(fn) - backup_suffix_len;
121- return k > 0 && strcmp(fn+k, backup_suffix) == 0;
122+ if (k > 0 && strcmp(fn+k, backup_suffix) == 0)
123+ return 1;
19a778eb
WD
124+ k += backup_suffix_len - backup_suffix_dels_len;
125+ return k > 0 && strcmp(fn+k, backup_suffix_dels) == 0;
52f25864
WD
126 }
127
fc557362
WD
128 /* The directory is about to be deleted: if DEL_RECURSE is given, delete all
129@@ -170,9 +177,9 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
87d0091c 130 what = "rmdir";
f813befd 131 ok = do_rmdir(fbuf) == 0;
87d0091c 132 } else {
fc557362
WD
133- if (make_backups > 0 && (backup_dir || !is_backup_file(fbuf))) {
134+ if (make_backups > 0 && (backup_dir_dels || !is_backup_file(fbuf))) {
135 what = "make_backup";
136- ok = make_backup(fbuf, True);
137+ ok = safe_delete(fbuf);
138 if (ok == 2) {
139 what = "unlink";
140 ok = robust_unlink(fbuf) == 0;
cc3e685d 141diff --git a/options.c b/options.c
fc557362 142index e7c6c61..37e4086 100644
cc3e685d
WD
143--- a/options.c
144+++ b/options.c
fc557362 145@@ -151,10 +151,14 @@ int no_detach
8af83008
WD
146 int write_batch = 0;
147 int read_batch = 0;
148 int backup_dir_len = 0;
e2e42a01 149+int backup_dir_dels_len = 0;
8af83008 150 int backup_suffix_len;
19a778eb 151+int backup_suffix_dels_len;
8af83008 152 unsigned int backup_dir_remainder;
19a778eb 153+unsigned int backup_dir_dels_remainder;
8af83008
WD
154
155 char *backup_suffix = NULL;
19a778eb 156+char *backup_suffix_dels = NULL;
8af83008
WD
157 char *tmpdir = NULL;
158 char *partial_dir = NULL;
daceaa67 159 char *basis_dir[MAX_BASIS_DIRS+1];
fc557362 160@@ -166,7 +170,9 @@ char *stdout_format = NULL;
e0e47893 161 char *password_file = NULL;
8af83008
WD
162 char *rsync_path = RSYNC_PATH;
163 char *backup_dir = NULL;
19a778eb 164+char *backup_dir_dels = NULL;
8af83008 165 char backup_dir_buf[MAXPATHLEN];
19a778eb 166+char backup_dir_dels_buf[MAXPATHLEN];
4a65fe72 167 char *sockopts = NULL;
fc557362
WD
168 char *usermap = NULL;
169 char *groupmap = NULL;
170@@ -669,6 +675,8 @@ void usage(enum logcode F)
8af83008 171 rprintf(F," -b, --backup make backups (see --suffix & --backup-dir)\n");
79f132a1
WD
172 rprintf(F," --backup-dir=DIR make backups into hierarchy based in DIR\n");
173 rprintf(F," --suffix=SUFFIX set backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
c0c7984e
WD
174+ rprintf(F," --backup-dir-dels=DIR backup removed files into hierarchy based in DIR\n");
175+ rprintf(F," --suffix-dels=SUFFIX set removed-files suffix (def. --suffix w/o b-d-d)\n");
79f132a1 176 rprintf(F," -u, --update skip files that are newer on the receiver\n");
0b2fb126 177 rprintf(F," --inplace update destination files in-place (SEE MAN PAGE)\n");
489b0a72 178 rprintf(F," --append append data onto shorter files\n");
fc557362 179@@ -968,7 +976,9 @@ static struct poptOption long_options[] = {
a54a2c4d
WD
180 {"backup", 'b', POPT_ARG_VAL, &make_backups, 1, 0, 0 },
181 {"no-backup", 0, POPT_ARG_VAL, &make_backups, 0, 0, 0 },
8af83008 182 {"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
19a778eb 183+ {"backup-dir-dels", 0, POPT_ARG_STRING, &backup_dir_dels, 0, 0, 0 },
489b0a72
WD
184 {"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
185+ {"suffix-dels", 0, POPT_ARG_STRING, &backup_suffix_dels, 0, 0, 0 },
186 {"list-only", 0, POPT_ARG_VAL, &list_only, 2, 0, 0 },
57e73b72
WD
187 {"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 },
188 {"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 },
fc557362 189@@ -1964,6 +1974,8 @@ int parse_arguments(int *argc_p, const char ***argv_p)
c0c7984e 190 tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT);
60a8bf36 191 if (backup_dir)
c0c7984e 192 backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, SP_DEFAULT);
60a8bf36 193+ if (backup_dir_dels)
c0c7984e 194+ backup_dir_dels = sanitize_path(NULL, backup_dir_dels, NULL, 0, SP_DEFAULT);
8af83008 195 }
c0c7984e
WD
196 if (daemon_filter_list.head && !am_sender) {
197 struct filter_list_struct *elp = &daemon_filter_list;
fc557362 198@@ -1985,6 +1997,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
c0c7984e 199 if (check_filter(elp, FLOG, dir, 1) < 0)
fb11cdd7 200 goto options_rejected;
8af83008 201 }
19a778eb
WD
202+ /* Clean backup_dir_dels same as for backup_dir */
203+ if (backup_dir_dels) {
def2ace9
WD
204+ if (!*backup_dir_dels)
205+ goto options_rejected;
19a778eb 206+ clean_fname(backup_dir_dels, 1);
bc3fcf1d 207+ if (check_filter(elp, FLOG, backup_dir_dels, 1) < 0)
8af83008
WD
208+ goto options_rejected;
209+ }
210 }
def2ace9
WD
211
212 if (!backup_suffix)
fc557362 213@@ -1996,6 +2016,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
8af83008
WD
214 backup_suffix);
215 return 0;
216 }
c0c7984e
WD
217+ /* --suffix-dels defaults to --suffix, or empty for a client given an
218+ * explicit --backup-dir-dels (just as --suffix defaults to empty when
219+ * a --backup-dir is given). The second case does not apply to the
220+ * server for consistency with server_options, which sends --suffix-dels
221+ * to the server iff it differs from --suffix. */
19a778eb 222+ if (!backup_suffix_dels)
a5e6228a 223+ backup_suffix_dels = backup_dir_dels && !am_server ? "" : backup_suffix;
19a778eb
WD
224+ backup_suffix_dels_len = strlen(backup_suffix_dels);
225+ if (strchr(backup_suffix_dels, '/') != NULL) {
8af83008 226+ snprintf(err_buf, sizeof err_buf,
19a778eb 227+ "--suffix-dels cannot contain slashes: %s\n",
e2e42a01 228+ backup_suffix_dels);
8af83008
WD
229+ return 0;
230+ }
231 if (backup_dir) {
232 backup_dir_len = strlcpy(backup_dir_buf, backup_dir, sizeof backup_dir_buf);
233 backup_dir_remainder = sizeof backup_dir_buf - backup_dir_len;
fc557362 234@@ -2019,6 +2053,34 @@ int parse_arguments(int *argc_p, const char ***argv_p)
e0e47893 235 "P *%s", backup_suffix);
c8a8b4a7 236 parse_rule(&filter_list, backup_dir_buf, 0, 0);
8af83008 237 }
a5e6228a 238+ if (backup_dir_dels) {
19a778eb
WD
239+ backup_dir_dels_len = strlcpy(backup_dir_dels_buf, backup_dir_dels, sizeof backup_dir_dels_buf);
240+ backup_dir_dels_remainder = sizeof backup_dir_dels_buf - backup_dir_dels_len;
241+ if (backup_dir_dels_remainder < 32) {
8af83008 242+ snprintf(err_buf, sizeof err_buf,
19a778eb 243+ "the --backup-dir-dels path is WAY too long.\n");
8af83008
WD
244+ return 0;
245+ }
19a778eb
WD
246+ if (backup_dir_dels_buf[backup_dir_dels_len - 1] != '/') {
247+ backup_dir_dels_buf[backup_dir_dels_len++] = '/';
248+ backup_dir_dels_buf[backup_dir_dels_len] = '\0';
8af83008 249+ }
fc557362 250+ if (INFO_GTE(BACKUP, 1) && !am_sender)
19a778eb 251+ rprintf(FINFO, "backup_dir_dels is %s\n", backup_dir_dels_buf);
a5e6228a
WD
252+ } else if (backup_dir) {
253+ backup_dir_dels = backup_dir;
254+ backup_dir_dels_len = backup_dir_len;
255+ backup_dir_dels_remainder = backup_dir_remainder;
256+ strlcpy(backup_dir_dels_buf, backup_dir_buf, sizeof backup_dir_buf);
19a778eb 257+ } else if (!backup_suffix_dels_len && (!am_server || !am_sender)) {
8af83008 258+ snprintf(err_buf, sizeof err_buf,
19a778eb 259+ "--suffix-dels cannot be a null string without --backup-dir-dels\n");
8af83008 260+ return 0;
c0c7984e
WD
261+ } else if (make_backups && delete_mode && !delete_excluded && !am_server) {
262+ snprintf(backup_dir_dels_buf, sizeof backup_dir_dels_buf,
263+ "P *%s", backup_suffix_dels);
264+ parse_rule(&filter_list, backup_dir_dels_buf, 0, 0);
8af83008
WD
265+ }
266
a54a2c4d
WD
267 if (make_backups && !backup_dir) {
268 omit_dir_times = 0; /* Implied, so avoid -O to sender. */
fc557362 269@@ -2432,6 +2494,10 @@ void server_options(char **args, int *argc_p)
8af83008
WD
270 args[ac++] = "--backup-dir";
271 args[ac++] = backup_dir;
272 }
37d900d6 273+ if (backup_dir_dels && backup_dir_dels != backup_dir) {
19a778eb
WD
274+ args[ac++] = "--backup-dir-dels";
275+ args[ac++] = backup_dir_dels;
8af83008
WD
276+ }
277
278 /* Only send --suffix if it specifies a non-default value. */
279 if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
fc557362 280@@ -2440,7 +2506,14 @@ void server_options(char **args, int *argc_p)
8af83008
WD
281 goto oom;
282 args[ac++] = arg;
283 }
284-
a5e6228a
WD
285+ /* Only send --suffix-dels if it specifies a value different from the
286+ * --suffix value, which would normally be used for deletions too. */
287+ if (strcmp(backup_suffix_dels, backup_suffix) != 0) {
8af83008 288+ /* We use the following syntax to avoid weirdness with '~'. */
19a778eb 289+ if (asprintf(&arg, "--suffix-dels=%s", backup_suffix_dels) < 0)
8af83008
WD
290+ goto oom;
291+ args[ac++] = arg;
292+ }
293 if (am_sender) {
a54a2c4d
WD
294 if (max_delete > 0) {
295 if (asprintf(&arg, "--max-delete=%d", max_delete) < 0)