New patch from Marc St-Onge.
[rsync/rsync-patches.git] / backup-dir-dels.diff
1 This patches creates two new command line options as follows:
2         --delete-dir
3         --delete-suffix
4
5 The delete-dir and delete-suffix options give the ability to store 
6 backup of deleted files on the receiver in different directories
7 or with different suffix than the backup of files that have been
8 changed but that are still on the source drive.  Both commands can
9 be combined.
10
11 The default behaviour if one or both of the options are not specified
12 is the previous behaviour, both backups use the same directory or
13 suffix.
14
15 Marc St-Onge
16
17 --- orig/backup.c       2004-09-20 19:50:13
18 +++ backup.c    2004-09-22 02:18:49
19 @@ -22,11 +22,17 @@
20  
21  extern int verbose;
22  extern int backup_suffix_len;
23 +extern int delete_suffix_len;
24  extern int backup_dir_len;
25 +extern int delete_dir_len;
26  extern unsigned int backup_dir_remainder;
27 +extern unsigned int delete_dir_remainder;
28  extern char backup_dir_buf[MAXPATHLEN];
29 +extern char delete_dir_buf[MAXPATHLEN];
30  extern char *backup_suffix;
31 +extern char *delete_suffix;
32  extern char *backup_dir;
33 +extern char *delete_dir;
34  
35  extern int am_root;
36  extern int preserve_devices;
37 @@ -35,6 +41,8 @@ extern int preserve_hard_links;
38  extern int orig_umask;
39  extern int safe_symlinks;
40  
41 +static int deleting;
42 +
43  /* make a complete pathname for backup file */
44  char *get_backup_name(char *fname)
45  {
46 @@ -52,10 +60,27 @@ char *get_backup_name(char *fname)
47         return NULL;
48  }
49  
50 +static char *get_delete_name(char *fname)
51 +{
52 +       if (delete_dir) {
53 +               if (stringjoin(delete_dir_buf + delete_dir_len, delete_dir_remainder,
54 +                              fname, delete_suffix, NULL) < delete_dir_remainder)
55 +                       return delete_dir_buf;
56 +       } else {
57 +               if (stringjoin(delete_dir_buf, MAXPATHLEN,
58 +                              fname, delete_suffix, NULL) < MAXPATHLEN)
59 +                       return delete_dir_buf;
60 +       }
61 +
62 +       rprintf(FERROR, "delete filename too long\n");
63 +       return NULL;
64 +}
65 +
66  /* simple backup creates a backup with a suffix in the same directory */
67  static int make_simple_backup(char *fname)
68  {
69 -       char *fnamebak = get_backup_name(fname);
70 +       char *fnamebak = deleting ? get_delete_name(fname)
71 +                                 : get_backup_name(fname);
72  
73         if (!fnamebak)
74                 return 0;
75 @@ -81,7 +106,8 @@ path
76  static int make_bak_dir(char *fullpath)
77  {
78         STRUCT_STAT st;
79 -       char *rel = fullpath + backup_dir_len;
80 +       int dir_len = deleting ? delete_dir_len : backup_dir_len;
81 +       char *rel = fullpath + dir_len;
82         char *end = rel + strlen(rel);
83         char *p = end;
84  
85 @@ -173,7 +199,8 @@ static int keep_backup(char *fname)
86         if (!(file = make_file(fname, NULL, NO_EXCLUDES)))
87                 return 1; /* the file could have disappeared */
88  
89 -       if (!(buf = get_backup_name(fname)))
90 +       buf = deleting ? get_delete_name(fname) : get_backup_name(fname);
91 +       if (!buf)
92                 return 0;
93  
94  #ifdef HAVE_MKNOD
95 @@ -262,7 +289,18 @@ static int keep_backup(char *fname)
96  /* main backup switch routine */
97  int make_backup(char *fname)
98  {
99 +       int ret;
100         if (backup_dir)
101 -               return keep_backup(fname);
102 -       return make_simple_backup(fname);
103 +               ret = keep_backup(fname);
104 +       else
105 +               ret = make_simple_backup(fname);
106 +       deleting = 0;   /* Always restore the default backup process. */
107 +       return ret;
108 +}
109 +
110 +/* backup switch routine called only when backing-up deleted file */
111 +int safe_delete(char *fname)
112 +{
113 +       deleting = 1;
114 +       return make_backup(fname);
115  }
116 --- orig/options.c      2004-09-20 05:10:48
117 +++ options.c   2004-09-22 02:25:03
118 @@ -113,10 +113,14 @@ int no_detach = 0;
119  int write_batch = 0;
120  int read_batch = 0;
121  int backup_dir_len = 0;
122 +int delete_dir_len = 0;        
123  int backup_suffix_len;
124 +int delete_suffix_len;
125  unsigned int backup_dir_remainder;
126 +unsigned int delete_dir_remainder;
127  
128  char *backup_suffix = NULL;
129 +char *delete_suffix = NULL;
130  char *tmpdir = NULL;
131  char *partial_dir = NULL;
132  char *compare_dest = NULL;
133 @@ -126,7 +130,9 @@ char *log_format = NULL;
134  char *password_file = NULL;
135  char *rsync_path = RSYNC_PATH;
136  char *backup_dir = NULL;
137 +char *delete_dir = NULL;
138  char backup_dir_buf[MAXPATHLEN];
139 +char delete_dir_buf[MAXPATHLEN];
140  int rsync_port = RSYNC_PORT;
141  int link_dest = 0;
142  
143 @@ -239,7 +245,9 @@ void usage(enum logcode F)
144    rprintf(F,"     --no-implied-dirs       don't send implied dirs with -R\n");
145    rprintf(F," -b, --backup                make backups (see --suffix & --backup-dir)\n");
146    rprintf(F,"     --backup-dir            make backups into this directory\n");
147 +  rprintf(F,"     --delete-dir            make backups of deleted files into this directory\n");
148    rprintf(F,"     --suffix=SUFFIX         backup suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
149 +  rprintf(F,"     --delete-suffix=SUFFIX  deleted files suffix (default %s w/o --backup-dir)\n",BACKUP_SUFFIX);
150    rprintf(F," -u, --update                update only (don't overwrite newer files)\n");
151    rprintf(F,"     --inplace               update destination files inplace (SEE MAN PAGE)\n");
152    rprintf(F," -K, --keep-dirlinks         treat symlinked dir on receiver as dir\n");
153 @@ -326,6 +334,7 @@ static struct poptOption long_options[] 
154    /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
155    {"version",          0,  POPT_ARG_NONE,   0,              OPT_VERSION, 0, 0},
156    {"suffix",           0,  POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
157 +  {"delete-suffix",    0,  POPT_ARG_STRING, &delete_suffix, 0, 0, 0 },
158    {"rsync-path",       0,  POPT_ARG_STRING, &rsync_path, 0, 0, 0 },
159    {"password-file",    0,  POPT_ARG_STRING, &password_file, 0, 0, 0 },
160    {"ignore-times",    'I', POPT_ARG_NONE,   &ignore_times, 0, 0, 0 },
161 @@ -396,6 +405,7 @@ static struct poptOption long_options[] 
162    {"bwlimit",          0,  POPT_ARG_INT,    &bwlimit, 0, 0, 0 },
163    {"address",          0,  POPT_ARG_STRING, &bind_address, 0, 0, 0 },
164    {"backup-dir",       0,  POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
165 +  {"delete-dir",       0,  POPT_ARG_STRING, &delete_dir, 0, 0, 0 },
166    {"hard-links",      'H', POPT_ARG_NONE,   &preserve_hard_links, 0, 0, 0 },
167    {"read-batch",       0,  POPT_ARG_STRING, &batch_name,  OPT_READ_BATCH, 0, 0 },
168    {"write-batch",      0,  POPT_ARG_STRING, &batch_name,  OPT_WRITE_BATCH, 0, 0 },
169 @@ -733,6 +743,8 @@ int parse_arguments(int *argc, const cha
170                         compare_dest = sanitize_path(NULL, compare_dest, NULL, 0);
171                 if (backup_dir)
172                         backup_dir = sanitize_path(NULL, backup_dir, NULL, 0);
173 +               if (delete_dir)                                                 
174 +                       delete_dir = sanitize_path(NULL, delete_dir, NULL, 0);
175                 if (files_from)
176                         files_from = sanitize_path(NULL, files_from, NULL, 0);
177         }
178 @@ -758,6 +770,12 @@ int parse_arguments(int *argc, const cha
179                         if (check_exclude(elp, backup_dir, 1) < 0)
180                                 goto options_rejected;
181                 }
182 +               /* Clean delete_dir same as for backup_dir */
183 +               if (delete_dir) {
184 +                       clean_fname(delete_dir, 1);
185 +                       if (check_exclude(elp, delete_dir, 1) < 0)
186 +                               goto options_rejected;
187 +               }
188         }
189         if (server_exclude_list.head && files_from) {
190                 clean_fname(files_from, 1);
191 @@ -784,6 +802,16 @@ int parse_arguments(int *argc, const cha
192                         backup_suffix);
193                 return 0;
194         }
195 +       /* if deleted_suffix not supplied, default to backup_suffix */
196 +       if (!delete_suffix)
197 +               delete_suffix = delete_dir ? "" : backup_suffix;
198 +       delete_suffix_len = strlen(delete_suffix);
199 +       if (strchr(delete_suffix, '/') != NULL) {
200 +               snprintf(err_buf, sizeof err_buf,
201 +                       "--delete-suffix cannot contain slashes: %s\n",
202 +                       delete_suffix); 
203 +               return 0;
204 +       }
205         if (backup_dir) {
206                 backup_dir_len = strlcpy(backup_dir_buf, backup_dir, sizeof backup_dir_buf);
207                 backup_dir_remainder = sizeof backup_dir_buf - backup_dir_len;
208 @@ -803,6 +831,31 @@ int parse_arguments(int *argc, const cha
209                         "--suffix cannot be a null string without --backup-dir\n");
210                 return 0;
211         }
212 +       /* If delete_dir not supplied default to backup_dir if it has been supplied */
213 +       if (backup_dir && !delete_dir) {
214 +               delete_dir = backup_dir;
215 +               delete_dir_len = backup_dir_len;
216 +               delete_dir_remainder = backup_dir_remainder;
217 +               strlcpy(delete_dir_buf, backup_dir_buf, sizeof backup_dir_buf);
218 +       } else if (delete_dir) {
219 +               delete_dir_len = strlcpy(delete_dir_buf, delete_dir, sizeof delete_dir_buf);
220 +               delete_dir_remainder = sizeof delete_dir_buf - delete_dir_len;
221 +               if (delete_dir_remainder < 32) {
222 +                       snprintf(err_buf, sizeof err_buf,
223 +                               "the --delete-dir path is WAY too long.\n");
224 +                       return 0;
225 +               }
226 +               if (delete_dir_buf[delete_dir_len - 1] != '/') {
227 +                       delete_dir_buf[delete_dir_len++] = '/';
228 +                       delete_dir_buf[delete_dir_len] = '\0';
229 +               }
230 +               if (verbose > 1 && !am_sender)
231 +                       rprintf(FINFO, "delete_dir is %s\n", delete_dir_buf);
232 +       } else if (!delete_suffix_len && (!am_server || !am_sender)) {
233 +               snprintf(err_buf, sizeof err_buf,
234 +                       "--delete-suffix cannot be a null string without --delete-dir\n");
235 +               return 0;
236 +       }
237  
238         if (do_progress && !verbose)
239                 verbose = 1;
240 @@ -1005,6 +1058,10 @@ void server_options(char **args,int *arg
241                 args[ac++] = "--backup-dir";
242                 args[ac++] = backup_dir;
243         }
244 +       if (delete_dir) {
245 +               args[ac++] = "--delete-dir";
246 +               args[ac++] = delete_dir;
247 +       }
248  
249         /* Only send --suffix if it specifies a non-default value. */
250         if (strcmp(backup_suffix, backup_dir ? "" : BACKUP_SUFFIX) != 0) {
251 @@ -1013,7 +1070,13 @@ void server_options(char **args,int *arg
252                         goto oom;
253                 args[ac++] = arg;
254         }
255 -
256 +       /* Only send --delete-suffix if it specifies a non-default value. */
257 +       if (strcmp(delete_suffix, delete_dir ? "" : BACKUP_SUFFIX) != 0) {
258 +               /* We use the following syntax to avoid weirdness with '~'. */
259 +               if (asprintf(&arg, "--delete-suffix=%s", delete_suffix) < 0)
260 +                       goto oom;
261 +               args[ac++] = arg;
262 +       }
263         if (am_sender) {
264                 if (delete_excluded)
265                         args[ac++] = "--delete-excluded";
266 --- orig/receiver.c     2004-09-21 09:40:27
267 +++ receiver.c  2004-09-22 02:09:20
268 @@ -42,8 +42,11 @@ extern char *compare_dest;
269  extern int make_backups;
270  extern int do_progress;
271  extern char *backup_dir;
272 +extern char *delete_dir;
273  extern char *backup_suffix;
274 +extern char *delete_suffix;
275  extern int backup_suffix_len;
276 +extern int delete_suffix_len;
277  extern int cleanup_got_literal;
278  extern int module_id;
279  extern int ignore_errors;
280 @@ -77,11 +80,14 @@ static void delete_one(char *fn, int is_
281         }
282  }
283  
284 -
285 +/* Function now checks if file matches backup or delete suffix patterns */
286  static int is_backup_file(char *fn)
287  {
288         int k = strlen(fn) - backup_suffix_len;
289 -       return k > 0 && strcmp(fn+k, backup_suffix) == 0;
290 +       if (k > 0 && strcmp(fn+k, backup_suffix) == 0)
291 +               return 1;
292 +       k = strlen(fn) - delete_suffix_len;
293 +       return k > 0 && strcmp(fn+k, delete_suffix) == 0;
294  }
295  
296  
297 @@ -122,10 +128,11 @@ void delete_files(struct file_list *flis
298                                 continue;
299                         if (flist_find(flist,local_file_list->files[i]) < 0) {
300                                 char *f = f_name(local_file_list->files[i]);
301 -                               if (make_backups && (backup_dir || !is_backup_file(f))) {
302 -                                       make_backup(f);
303 +                               int backup_file = is_backup_file(f);
304 +                               if (make_backups && (delete_dir || !backup_file)) {
305 +                                       safe_delete(f);
306                                         if (verbose) {
307 -                                               rprintf(FINFO, "deleting %s\n",
308 +                                               rprintf(FINFO, "safe-deleting %s\n",
309                                                         safe_fname(f));
310                                         }
311                                 } else {