Use "use warnings" rather than -w on the #! line.
[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
WD
22diff --git a/backup.c b/backup.c
23--- a/backup.c
24+++ b/backup.c
a54a2c4d 25@@ -29,10 +29,17 @@ extern int preserve_specials;
fc068916
WD
26 extern int preserve_links;
27 extern int safe_symlinks;
8af83008 28 extern int backup_dir_len;
19a778eb 29+extern int backup_dir_dels_len;
8af83008 30 extern unsigned int backup_dir_remainder;
19a778eb 31+extern unsigned int backup_dir_dels_remainder;
8af83008 32 extern char backup_dir_buf[MAXPATHLEN];
19a778eb 33+extern char backup_dir_dels_buf[MAXPATHLEN];
8af83008 34 extern char *backup_suffix;
19a778eb 35+extern char *backup_suffix_dels;
8af83008 36 extern char *backup_dir;
19a778eb 37+extern char *backup_dir_dels;
8af83008 38+
70891d26
WD
39+static int deleting;
40
8af83008 41 /* make a complete pathname for backup file */
5e3c6c93 42 char *get_backup_name(const char *fname)
a54a2c4d 43@@ -51,11 +58,28 @@ char *get_backup_name(const char *fname)
8af83008
WD
44 return NULL;
45 }
46
5e3c6c93 47+static char *get_delete_name(const char *fname)
8af83008 48+{
19a778eb
WD
49+ if (backup_dir_dels) {
50+ if (stringjoin(backup_dir_dels_buf + backup_dir_dels_len, backup_dir_dels_remainder,
51+ fname, backup_suffix_dels, NULL) < backup_dir_dels_remainder)
52+ return backup_dir_dels_buf;
8af83008 53+ } else {
19a778eb
WD
54+ if (stringjoin(backup_dir_dels_buf, MAXPATHLEN,
55+ fname, backup_suffix_dels, NULL) < MAXPATHLEN)
56+ return backup_dir_dels_buf;
8af83008
WD
57+ }
58+
59+ rprintf(FERROR, "delete filename too long\n");
60+ return NULL;
61+}
62+
63 /* simple backup creates a backup with a suffix in the same directory */
5e3c6c93 64 static int make_simple_backup(const char *fname)
8af83008 65 {
4877ebcc 66 int rename_errno;
5e3c6c93
WD
67- const char *fnamebak = get_backup_name(fname);
68+ const char *fnamebak = deleting ? get_delete_name(fname)
69+ : get_backup_name(fname);
8af83008
WD
70
71 if (!fnamebak)
72 return 0;
cdcd2137 73@@ -96,7 +120,7 @@ int make_bak_dir(const char *fullpath)
8af83008 74 {
cdcd2137 75 char fbuf[MAXPATHLEN], *rel, *end, *p;
ffc18846 76 struct file_struct *file;
cdcd2137
WD
77- int len = backup_dir_len;
78+ int len = deleting ? backup_dir_dels_len : backup_dir_len;
52e09c4e 79 stat_x sx;
8af83008 80
cdcd2137 81 while (*fullpath == '.' && fullpath[1] == '/') {
91270139 82@@ -221,7 +245,8 @@ static int keep_backup(const char *fname)
e0e47893 83 if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
8af83008
WD
84 return 1; /* the file could have disappeared */
85
70891d26 86- if (!(buf = get_backup_name(fname))) {
8af83008 87+ buf = deleting ? get_delete_name(fname) : get_backup_name(fname);
70891d26
WD
88+ if (!buf) {
89 unmake_file(file);
8af83008 90 return 0;
70891d26 91 }
7f0bf1cb 92@@ -356,7 +381,17 @@ static int keep_backup(const char *fname)
c0c7984e
WD
93 /* main backup switch routine */
94 int make_backup(const char *fname)
95 {
96- if (backup_dir)
97+ if (deleting ? backup_dir_dels : backup_dir)
44917741
WD
98 return keep_backup(fname);
99 return make_simple_backup(fname);
100 }
8af83008 101+
79f132a1 102+/* backup switch routine called only when backing-up removed file */
8af83008
WD
103+int safe_delete(char *fname)
104+{
44917741 105+ int ret;
8af83008 106+ deleting = 1;
44917741
WD
107+ ret = make_backup(fname);
108+ deleting = 0;
109+ return ret;
110+}
cc3e685d
WD
111diff --git a/generator.c b/generator.c
112--- a/generator.c
113+++ b/generator.c
4c107044 114@@ -96,6 +96,9 @@ extern uid_t our_uid;
52f25864
WD
115 extern char *backup_dir;
116 extern char *backup_suffix;
117 extern int backup_suffix_len;
19a778eb
WD
118+extern char *backup_dir_dels;
119+extern char *backup_suffix_dels;
120+extern int backup_suffix_dels_len;
fc068916 121 extern struct file_list *cur_flist, *first_flist, *dir_flist;
c0c7984e 122 extern struct filter_list_struct daemon_filter_list;
cc3e685d 123
4c107044 124@@ -142,10 +145,15 @@ static void handle_skipped_hlink(struct file_struct *file, int itemizing,
c0c7984e
WD
125 enum logcode code, int f_out);
126 #endif
52f25864 127
a5e6228a 128+
d608ca23 129+/* Function now compares both backup_suffix and backup_suffix_dels. */
52f25864
WD
130 static int is_backup_file(char *fn)
131 {
132 int k = strlen(fn) - backup_suffix_len;
133- return k > 0 && strcmp(fn+k, backup_suffix) == 0;
134+ if (k > 0 && strcmp(fn+k, backup_suffix) == 0)
135+ return 1;
19a778eb
WD
136+ k += backup_suffix_len - backup_suffix_dels_len;
137+ return k > 0 && strcmp(fn+k, backup_suffix_dels) == 0;
52f25864
WD
138 }
139
87d0091c 140 /* Delete a file or directory. If DEL_RECURSE is set in the flags, this will
4c107044 141@@ -193,9 +201,9 @@ static enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
87d0091c
WD
142 if (S_ISDIR(mode)) {
143 what = "rmdir";
f813befd 144 ok = do_rmdir(fbuf) == 0;
fc068916
WD
145- } else if (make_backups > 0 && (backup_dir || !is_backup_file(fbuf))) {
146+ } else if (make_backups > 0 && (backup_dir_dels || !is_backup_file(fbuf))) {
87d0091c 147 what = "make_backup";
f813befd
WD
148- ok = make_backup(fbuf);
149+ ok = safe_delete(fbuf);
87d0091c
WD
150 } else {
151 what = "unlink";
f813befd 152 ok = robust_unlink(fbuf) == 0;
cc3e685d
WD
153diff --git a/options.c b/options.c
154--- a/options.c
155+++ b/options.c
c0c7984e 156@@ -150,10 +150,14 @@ int no_detach
8af83008
WD
157 int write_batch = 0;
158 int read_batch = 0;
159 int backup_dir_len = 0;
e2e42a01 160+int backup_dir_dels_len = 0;
8af83008 161 int backup_suffix_len;
19a778eb 162+int backup_suffix_dels_len;
8af83008 163 unsigned int backup_dir_remainder;
19a778eb 164+unsigned int backup_dir_dels_remainder;
8af83008
WD
165
166 char *backup_suffix = NULL;
19a778eb 167+char *backup_suffix_dels = NULL;
8af83008
WD
168 char *tmpdir = NULL;
169 char *partial_dir = NULL;
daceaa67 170 char *basis_dir[MAX_BASIS_DIRS+1];
c0c7984e 171@@ -165,7 +169,9 @@ char *stdout_format = NULL;
e0e47893 172 char *password_file = NULL;
8af83008
WD
173 char *rsync_path = RSYNC_PATH;
174 char *backup_dir = NULL;
19a778eb 175+char *backup_dir_dels = NULL;
8af83008 176 char backup_dir_buf[MAXPATHLEN];
19a778eb 177+char backup_dir_dels_buf[MAXPATHLEN];
4a65fe72 178 char *sockopts = NULL;
daceaa67 179 int rsync_port = 0;
c59d6641 180 int compare_dest = 0;
abd3adb8 181@@ -326,6 +332,8 @@ void usage(enum logcode F)
8af83008 182 rprintf(F," -b, --backup make backups (see --suffix & --backup-dir)\n");
79f132a1
WD
183 rprintf(F," --backup-dir=DIR make backups into hierarchy based in DIR\n");
184 rprintf(F," --suffix=SUFFIX set backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
c0c7984e
WD
185+ rprintf(F," --backup-dir-dels=DIR backup removed files into hierarchy based in DIR\n");
186+ rprintf(F," --suffix-dels=SUFFIX set removed-files suffix (def. --suffix w/o b-d-d)\n");
79f132a1 187 rprintf(F," -u, --update skip files that are newer on the receiver\n");
0b2fb126 188 rprintf(F," --inplace update destination files in-place (SEE MAN PAGE)\n");
489b0a72 189 rprintf(F," --append append data onto shorter files\n");
abd3adb8 190@@ -610,7 +618,9 @@ static struct poptOption long_options[] = {
a54a2c4d
WD
191 {"backup", 'b', POPT_ARG_VAL, &make_backups, 1, 0, 0 },
192 {"no-backup", 0, POPT_ARG_VAL, &make_backups, 0, 0, 0 },
8af83008 193 {"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
19a778eb 194+ {"backup-dir-dels", 0, POPT_ARG_STRING, &backup_dir_dels, 0, 0, 0 },
489b0a72
WD
195 {"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
196+ {"suffix-dels", 0, POPT_ARG_STRING, &backup_suffix_dels, 0, 0, 0 },
197 {"list-only", 0, POPT_ARG_VAL, &list_only, 2, 0, 0 },
57e73b72
WD
198 {"read-batch", 0, POPT_ARG_STRING, &batch_name, OPT_READ_BATCH, 0, 0 },
199 {"write-batch", 0, POPT_ARG_STRING, &batch_name, OPT_WRITE_BATCH, 0, 0 },
abd3adb8 200@@ -1454,6 +1464,8 @@ int parse_arguments(int *argc_p, const char ***argv_p)
c0c7984e 201 tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT);
60a8bf36 202 if (backup_dir)
c0c7984e 203 backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, SP_DEFAULT);
60a8bf36 204+ if (backup_dir_dels)
c0c7984e 205+ backup_dir_dels = sanitize_path(NULL, backup_dir_dels, NULL, 0, SP_DEFAULT);
8af83008 206 }
c0c7984e
WD
207 if (daemon_filter_list.head && !am_sender) {
208 struct filter_list_struct *elp = &daemon_filter_list;
abd3adb8 209@@ -1475,6 +1487,14 @@ int parse_arguments(int *argc_p, const char ***argv_p)
c0c7984e 210 if (check_filter(elp, FLOG, dir, 1) < 0)
fb11cdd7 211 goto options_rejected;
8af83008 212 }
19a778eb
WD
213+ /* Clean backup_dir_dels same as for backup_dir */
214+ if (backup_dir_dels) {
def2ace9
WD
215+ if (!*backup_dir_dels)
216+ goto options_rejected;
19a778eb 217+ clean_fname(backup_dir_dels, 1);
bc3fcf1d 218+ if (check_filter(elp, FLOG, backup_dir_dels, 1) < 0)
8af83008
WD
219+ goto options_rejected;
220+ }
221 }
def2ace9
WD
222
223 if (!backup_suffix)
abd3adb8 224@@ -1486,6 +1506,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
8af83008
WD
225 backup_suffix);
226 return 0;
227 }
c0c7984e
WD
228+ /* --suffix-dels defaults to --suffix, or empty for a client given an
229+ * explicit --backup-dir-dels (just as --suffix defaults to empty when
230+ * a --backup-dir is given). The second case does not apply to the
231+ * server for consistency with server_options, which sends --suffix-dels
232+ * to the server iff it differs from --suffix. */
19a778eb 233+ if (!backup_suffix_dels)
a5e6228a 234+ backup_suffix_dels = backup_dir_dels && !am_server ? "" : backup_suffix;
19a778eb
WD
235+ backup_suffix_dels_len = strlen(backup_suffix_dels);
236+ if (strchr(backup_suffix_dels, '/') != NULL) {
8af83008 237+ snprintf(err_buf, sizeof err_buf,
19a778eb 238+ "--suffix-dels cannot contain slashes: %s\n",
e2e42a01 239+ backup_suffix_dels);
8af83008
WD
240+ return 0;
241+ }
242 if (backup_dir) {
243 backup_dir_len = strlcpy(backup_dir_buf, backup_dir, sizeof backup_dir_buf);
244 backup_dir_remainder = sizeof backup_dir_buf - backup_dir_len;
abd3adb8 245@@ -1509,6 +1543,34 @@ int parse_arguments(int *argc_p, const char ***argv_p)
e0e47893 246 "P *%s", backup_suffix);
c8a8b4a7 247 parse_rule(&filter_list, backup_dir_buf, 0, 0);
8af83008 248 }
a5e6228a 249+ if (backup_dir_dels) {
19a778eb
WD
250+ backup_dir_dels_len = strlcpy(backup_dir_dels_buf, backup_dir_dels, sizeof backup_dir_dels_buf);
251+ backup_dir_dels_remainder = sizeof backup_dir_dels_buf - backup_dir_dels_len;
252+ if (backup_dir_dels_remainder < 32) {
8af83008 253+ snprintf(err_buf, sizeof err_buf,
19a778eb 254+ "the --backup-dir-dels path is WAY too long.\n");
8af83008
WD
255+ return 0;
256+ }
19a778eb
WD
257+ if (backup_dir_dels_buf[backup_dir_dels_len - 1] != '/') {
258+ backup_dir_dels_buf[backup_dir_dels_len++] = '/';
259+ backup_dir_dels_buf[backup_dir_dels_len] = '\0';
8af83008
WD
260+ }
261+ if (verbose > 1 && !am_sender)
19a778eb 262+ rprintf(FINFO, "backup_dir_dels is %s\n", backup_dir_dels_buf);
a5e6228a
WD
263+ } else if (backup_dir) {
264+ backup_dir_dels = backup_dir;
265+ backup_dir_dels_len = backup_dir_len;
266+ backup_dir_dels_remainder = backup_dir_remainder;
267+ strlcpy(backup_dir_dels_buf, backup_dir_buf, sizeof backup_dir_buf);
19a778eb 268+ } else if (!backup_suffix_dels_len && (!am_server || !am_sender)) {
8af83008 269+ snprintf(err_buf, sizeof err_buf,
19a778eb 270+ "--suffix-dels cannot be a null string without --backup-dir-dels\n");
8af83008 271+ return 0;
c0c7984e
WD
272+ } else if (make_backups && delete_mode && !delete_excluded && !am_server) {
273+ snprintf(backup_dir_dels_buf, sizeof backup_dir_dels_buf,
274+ "P *%s", backup_suffix_dels);
275+ parse_rule(&filter_list, backup_dir_dels_buf, 0, 0);
8af83008
WD
276+ }
277
a54a2c4d
WD
278 if (make_backups && !backup_dir) {
279 omit_dir_times = 0; /* Implied, so avoid -O to sender. */
abd3adb8 280@@ -1917,6 +1979,10 @@ void server_options(char **args, int *argc_p)
8af83008
WD
281 args[ac++] = "--backup-dir";
282 args[ac++] = backup_dir;
283 }
37d900d6 284+ if (backup_dir_dels && backup_dir_dels != backup_dir) {
19a778eb
WD
285+ args[ac++] = "--backup-dir-dels";
286+ args[ac++] = backup_dir_dels;
8af83008
WD
287+ }
288
289 /* Only send --suffix if it specifies a non-default value. */
290 if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
abd3adb8 291@@ -1925,7 +1991,14 @@ void server_options(char **args, int *argc_p)
8af83008
WD
292 goto oom;
293 args[ac++] = arg;
294 }
295-
a5e6228a
WD
296+ /* Only send --suffix-dels if it specifies a value different from the
297+ * --suffix value, which would normally be used for deletions too. */
298+ if (strcmp(backup_suffix_dels, backup_suffix) != 0) {
8af83008 299+ /* We use the following syntax to avoid weirdness with '~'. */
19a778eb 300+ if (asprintf(&arg, "--suffix-dels=%s", backup_suffix_dels) < 0)
8af83008
WD
301+ goto oom;
302+ args[ac++] = arg;
303+ }
304 if (am_sender) {
a54a2c4d
WD
305 if (max_delete > 0) {
306 if (asprintf(&arg, "--max-delete=%d", max_delete) < 0)