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
5214a41b 22based-on: 24079e988fc31af4eba56cd2701fdc5a4154980d
cc3e685d
WD
23diff --git a/backup.c b/backup.c
24--- a/backup.c
25+++ b/backup.c
7170ca8d 26@@ -29,29 +29,36 @@ 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
7170ca8d
WD
42 /* Create a backup path from the given fname, putting the result into
43 * backup_dir_buf. Any new directories (compared to the prior backup
44 * path) are ensured to exist as directories, replacing anything else
45 * that may be in the way (e.g. a symlink). */
46-static BOOL copy_valid_path(const char *fname)
47+static BOOL copy_valid_path(const char *fname, char *buf, int prefix_len, unsigned int remainder, const char *suffix)
8af83008 48 {
7170ca8d
WD
49 const char *f;
50 int flags;
51 BOOL ret = True;
52e09c4e 52 stat_x sx;
7170ca8d
WD
53- char *b, *rel = backup_dir_buf + backup_dir_len, *name = rel;
54+ char *b, *rel = buf + prefix_len, *name = rel;
8af83008 55
7170ca8d
WD
56 for (f = fname, b = rel; *f && *f == *b; f++, b++) {
57 if (*b == '/')
58 name = b + 1;
59 }
8af83008 60
7170ca8d
WD
61- if (stringjoin(rel, backup_dir_remainder, fname, backup_suffix, NULL) >= backup_dir_remainder) {
62+ if (stringjoin(rel, remainder, fname, suffix, NULL) >= remainder) {
63 rprintf(FERROR, "backup filename too long\n");
64 *name = '\0';
65 return False;
66@@ -62,16 +69,16 @@ static BOOL copy_valid_path(const char *fname)
67 return True;
68 *b = '\0';
69
70- if (do_lstat(backup_dir_buf, &sx.st) < 0) {
71+ if (do_lstat(buf, &sx.st) < 0) {
72 if (errno == ENOENT)
73 break;
74- rsyserr(FERROR, errno, "backup lstat %s failed", backup_dir_buf);
75+ rsyserr(FERROR, errno, "backup lstat %s failed", buf);
76 *name = '\0';
77 return False;
78 }
79 if (!S_ISDIR(sx.st.st_mode)) {
80 flags = get_del_for_flag(sx.st.st_mode) | DEL_FOR_BACKUP | DEL_RECURSE;
81- if (delete_item(backup_dir_buf, sx.st.st_mode, flags) == 0)
82+ if (delete_item(buf, sx.st.st_mode, flags) == 0)
83 break;
84 *name = '\0';
85 return False;
86@@ -85,8 +92,8 @@ static BOOL copy_valid_path(const char *fname)
87 for ( ; b; name = b + 1, b = strchr(name, '/')) {
88 *b = '\0';
89
90- if (mkdir_defmode(backup_dir_buf) < 0) {
91- rsyserr(FERROR, errno, "backup mkdir %s failed", backup_dir_buf);
92+ if (mkdir_defmode(buf) < 0) {
93+ rsyserr(FERROR, errno, "backup mkdir %s failed", buf);
94 *name = '\0';
95 ret = False;
96 break;
97@@ -114,7 +121,7 @@ static BOOL copy_valid_path(const char *fname)
98 free_xattr(&sx);
99 }
100 #endif
101- set_file_attrs(backup_dir_buf, file, NULL, NULL, 0);
102+ set_file_attrs(buf, file, NULL, NULL, 0);
103 unmake_file(file);
104 }
105
106@@ -134,15 +141,20 @@ static BOOL copy_valid_path(const char *fname)
107 /* Make a complete pathname for backup file and verify any new path elements. */
108 char *get_backup_name(const char *fname)
109 {
110+ char *buf = deleting ? backup_dir_dels_buf : backup_dir_buf;
111+ char *suffix = deleting ? backup_suffix_dels : backup_suffix;
112+
113 if (backup_dir) {
114+ int prefix_len = deleting ? backup_dir_dels_len : backup_dir_len;
115+ unsigned int remainder = deleting ? backup_dir_dels_remainder : backup_dir_remainder;
116 /* copy fname into backup_dir_buf while validating the dirs. */
117- if (copy_valid_path(fname))
118- return backup_dir_buf;
119+ if (copy_valid_path(fname, buf, prefix_len, remainder, suffix))
120+ return buf;
121 return NULL;
122 } else {
123 if (stringjoin(backup_dir_buf, MAXPATHLEN,
124- fname, backup_suffix, NULL) < MAXPATHLEN)
125- return backup_dir_buf;
126+ fname, suffix, NULL) < MAXPATHLEN)
127+ return buf;
70891d26 128 }
7170ca8d
WD
129
130 rprintf(FERROR, "backup filename too long\n");
131@@ -317,3 +329,13 @@ int make_backup(const char *fname, BOOL prefer_rename)
132 rprintf(FINFO, "backed up %s to %s\n", fname, buf);
fc557362 133 return ret;
44917741 134 }
8af83008 135+
79f132a1 136+/* backup switch routine called only when backing-up removed file */
fc557362 137+int safe_delete(const char *fname)
8af83008 138+{
44917741 139+ int ret;
8af83008 140+ deleting = 1;
fc557362 141+ ret = make_backup(fname, True);
44917741
WD
142+ deleting = 0;
143+ return ret;
144+}
fc557362 145diff --git a/delete.c b/delete.c
fc557362
WD
146--- a/delete.c
147+++ b/delete.c
148@@ -28,6 +28,9 @@ extern int max_delete;
52f25864
WD
149 extern char *backup_dir;
150 extern char *backup_suffix;
151 extern int backup_suffix_len;
19a778eb
WD
152+extern char *backup_dir_dels;
153+extern char *backup_suffix_dels;
154+extern int backup_suffix_dels_len;
fc557362
WD
155 extern uid_t our_uid;
156 extern struct stats stats;
cc3e685d 157
fc557362
WD
158@@ -35,10 +38,14 @@ int ignore_perishable = 0;
159 int non_perishable_cnt = 0;
160 int skipped_deletes = 0;
52f25864 161
d608ca23 162+/* Function now compares both backup_suffix and backup_suffix_dels. */
fc557362 163 static inline int is_backup_file(char *fn)
52f25864
WD
164 {
165 int k = strlen(fn) - backup_suffix_len;
166- return k > 0 && strcmp(fn+k, backup_suffix) == 0;
167+ if (k > 0 && strcmp(fn+k, backup_suffix) == 0)
168+ return 1;
19a778eb
WD
169+ k += backup_suffix_len - backup_suffix_dels_len;
170+ return k > 0 && strcmp(fn+k, backup_suffix_dels) == 0;
52f25864
WD
171 }
172
fc557362
WD
173 /* The directory is about to be deleted: if DEL_RECURSE is given, delete all
174@@ -170,9 +177,9 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
87d0091c 175 what = "rmdir";
f813befd 176 ok = do_rmdir(fbuf) == 0;
87d0091c 177 } else {
7170ca8d
WD
178- if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir || !is_backup_file(fbuf))) {
179+ if (make_backups > 0 && !(flags & DEL_FOR_BACKUP) && (backup_dir_dels || !is_backup_file(fbuf))) {
fc557362
WD
180 what = "make_backup";
181- ok = make_backup(fbuf, True);
182+ ok = safe_delete(fbuf);
183 if (ok == 2) {
184 what = "unlink";
185 ok = robust_unlink(fbuf) == 0;
cc3e685d
WD
186diff --git a/options.c b/options.c
187--- a/options.c
188+++ b/options.c
fc557362 189@@ -151,10 +151,14 @@ int no_detach
8af83008
WD
190 int write_batch = 0;
191 int read_batch = 0;
192 int backup_dir_len = 0;
e2e42a01 193+int backup_dir_dels_len = 0;
8af83008 194 int backup_suffix_len;
19a778eb 195+int backup_suffix_dels_len;
8af83008 196 unsigned int backup_dir_remainder;
19a778eb 197+unsigned int backup_dir_dels_remainder;
8af83008
WD
198
199 char *backup_suffix = NULL;
19a778eb 200+char *backup_suffix_dels = NULL;
8af83008
WD
201 char *tmpdir = NULL;
202 char *partial_dir = NULL;
daceaa67 203 char *basis_dir[MAX_BASIS_DIRS+1];
fc557362 204@@ -166,7 +170,9 @@ char *stdout_format = NULL;
e0e47893 205 char *password_file = NULL;
8af83008
WD
206 char *rsync_path = RSYNC_PATH;
207 char *backup_dir = NULL;
19a778eb 208+char *backup_dir_dels = NULL;
8af83008 209 char backup_dir_buf[MAXPATHLEN];
19a778eb 210+char backup_dir_dels_buf[MAXPATHLEN];
4a65fe72 211 char *sockopts = NULL;
fc557362
WD
212 char *usermap = NULL;
213 char *groupmap = NULL;
72e5645e 214@@ -670,6 +676,8 @@ void usage(enum logcode F)
8af83008 215 rprintf(F," -b, --backup make backups (see --suffix & --backup-dir)\n");
79f132a1
WD
216 rprintf(F," --backup-dir=DIR make backups into hierarchy based in DIR\n");
217 rprintf(F," --suffix=SUFFIX set backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
c0c7984e
WD
218+ rprintf(F," --backup-dir-dels=DIR backup removed files into hierarchy based in DIR\n");
219+ rprintf(F," --suffix-dels=SUFFIX set removed-files suffix (def. --suffix w/o b-d-d)\n");
79f132a1 220 rprintf(F," -u, --update skip files that are newer on the receiver\n");
0b2fb126 221 rprintf(F," --inplace update destination files in-place (SEE MAN PAGE)\n");
489b0a72 222 rprintf(F," --append append data onto shorter files\n");
72e5645e 223@@ -969,7 +977,9 @@ static struct poptOption long_options[] = {
a54a2c4d
WD
224 {"backup", 'b', POPT_ARG_VAL, &make_backups, 1, 0, 0 },
225 {"no-backup", 0, POPT_ARG_VAL, &make_backups, 0, 0, 0 },
8af83008 226 {"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
19a778eb 227+ {"backup-dir-dels", 0, POPT_ARG_STRING, &backup_dir_dels, 0, 0, 0 },
489b0a72
WD
228 {"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
229+ {"suffix-dels", 0, POPT_ARG_STRING, &backup_suffix_dels, 0, 0, 0 },
230 {"list-only", 0, POPT_ARG_VAL, &list_only, 2, 0, 0 },
57e73b72
WD
231 {"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 },
232 {"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 },
5214a41b 233@@ -1991,6 +2001,8 @@ int parse_arguments(int *argc_p, const char ***argv_p)
c0c7984e 234 tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT);
60a8bf36 235 if (backup_dir)
c0c7984e 236 backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, SP_DEFAULT);
60a8bf36 237+ if (backup_dir_dels)
c0c7984e 238+ backup_dir_dels = sanitize_path(NULL, backup_dir_dels, NULL, 0, SP_DEFAULT);
8af83008 239 }
c0c7984e 240 if (daemon_filter_list.head && !am_sender) {
7170ca8d 241 filter_rule_list *elp = &daemon_filter_list;
5214a41b 242@@ -2012,6 +2024,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
c0c7984e 243 if (check_filter(elp, FLOG, dir, 1) < 0)
fb11cdd7 244 goto options_rejected;
8af83008 245 }
19a778eb
WD
246+ /* Clean backup_dir_dels same as for backup_dir */
247+ if (backup_dir_dels) {
def2ace9
WD
248+ if (!*backup_dir_dels)
249+ goto options_rejected;
19a778eb 250+ clean_fname(backup_dir_dels, 1);
bc3fcf1d 251+ if (check_filter(elp, FLOG, backup_dir_dels, 1) < 0)
8af83008
WD
252+ goto options_rejected;
253+ }
254 }
def2ace9
WD
255
256 if (!backup_suffix)
5214a41b 257@@ -2023,6 +2043,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
8af83008
WD
258 backup_suffix);
259 return 0;
260 }
c0c7984e
WD
261+ /* --suffix-dels defaults to --suffix, or empty for a client given an
262+ * explicit --backup-dir-dels (just as --suffix defaults to empty when
263+ * a --backup-dir is given). The second case does not apply to the
264+ * server for consistency with server_options, which sends --suffix-dels
265+ * to the server iff it differs from --suffix. */
19a778eb 266+ if (!backup_suffix_dels)
a5e6228a 267+ backup_suffix_dels = backup_dir_dels && !am_server ? "" : backup_suffix;
19a778eb
WD
268+ backup_suffix_dels_len = strlen(backup_suffix_dels);
269+ if (strchr(backup_suffix_dels, '/') != NULL) {
8af83008 270+ snprintf(err_buf, sizeof err_buf,
19a778eb 271+ "--suffix-dels cannot contain slashes: %s\n",
e2e42a01 272+ backup_suffix_dels);
8af83008
WD
273+ return 0;
274+ }
275 if (backup_dir) {
7170ca8d
WD
276 while (*backup_dir == '.' && backup_dir[1] == '/')
277 backup_dir += 2;
5214a41b 278@@ -2056,6 +2090,34 @@ int parse_arguments(int *argc_p, const char ***argv_p)
e0e47893 279 "P *%s", backup_suffix);
7170ca8d 280 parse_filter_str(&filter_list, backup_dir_buf, rule_template(0), 0);
8af83008 281 }
a5e6228a 282+ if (backup_dir_dels) {
19a778eb
WD
283+ backup_dir_dels_len = strlcpy(backup_dir_dels_buf, backup_dir_dels, sizeof backup_dir_dels_buf);
284+ backup_dir_dels_remainder = sizeof backup_dir_dels_buf - backup_dir_dels_len;
285+ if (backup_dir_dels_remainder < 32) {
8af83008 286+ snprintf(err_buf, sizeof err_buf,
19a778eb 287+ "the --backup-dir-dels path is WAY too long.\n");
8af83008
WD
288+ return 0;
289+ }
19a778eb
WD
290+ if (backup_dir_dels_buf[backup_dir_dels_len - 1] != '/') {
291+ backup_dir_dels_buf[backup_dir_dels_len++] = '/';
292+ backup_dir_dels_buf[backup_dir_dels_len] = '\0';
8af83008 293+ }
fc557362 294+ if (INFO_GTE(BACKUP, 1) && !am_sender)
19a778eb 295+ rprintf(FINFO, "backup_dir_dels is %s\n", backup_dir_dels_buf);
a5e6228a
WD
296+ } else if (backup_dir) {
297+ backup_dir_dels = backup_dir;
298+ backup_dir_dels_len = backup_dir_len;
299+ backup_dir_dels_remainder = backup_dir_remainder;
300+ strlcpy(backup_dir_dels_buf, backup_dir_buf, sizeof backup_dir_buf);
19a778eb 301+ } else if (!backup_suffix_dels_len && (!am_server || !am_sender)) {
8af83008 302+ snprintf(err_buf, sizeof err_buf,
19a778eb 303+ "--suffix-dels cannot be a null string without --backup-dir-dels\n");
8af83008 304+ return 0;
c0c7984e
WD
305+ } else if (make_backups && delete_mode && !delete_excluded && !am_server) {
306+ snprintf(backup_dir_dels_buf, sizeof backup_dir_dels_buf,
307+ "P *%s", backup_suffix_dels);
308+ parse_rule(&filter_list, backup_dir_dels_buf, 0, 0);
8af83008
WD
309+ }
310
a54a2c4d
WD
311 if (make_backups && !backup_dir) {
312 omit_dir_times = 0; /* Implied, so avoid -O to sender. */
5214a41b 313@@ -2469,6 +2531,10 @@ void server_options(char **args, int *argc_p)
8af83008
WD
314 args[ac++] = "--backup-dir";
315 args[ac++] = backup_dir;
316 }
37d900d6 317+ if (backup_dir_dels && backup_dir_dels != backup_dir) {
19a778eb
WD
318+ args[ac++] = "--backup-dir-dels";
319+ args[ac++] = backup_dir_dels;
8af83008
WD
320+ }
321
322 /* Only send --suffix if it specifies a non-default value. */
323 if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
5214a41b 324@@ -2477,7 +2543,14 @@ void server_options(char **args, int *argc_p)
8af83008
WD
325 goto oom;
326 args[ac++] = arg;
327 }
328-
a5e6228a
WD
329+ /* Only send --suffix-dels if it specifies a value different from the
330+ * --suffix value, which would normally be used for deletions too. */
331+ if (strcmp(backup_suffix_dels, backup_suffix) != 0) {
8af83008 332+ /* We use the following syntax to avoid weirdness with '~'. */
19a778eb 333+ if (asprintf(&arg, "--suffix-dels=%s", backup_suffix_dels) < 0)
8af83008
WD
334+ goto oom;
335+ args[ac++] = arg;
336+ }
337 if (am_sender) {
a54a2c4d
WD
338 if (max_delete > 0) {
339 if (asprintf(&arg, "--max-delete=%d", max_delete) < 0)