Updated to apply cleanly.
[rsync/rsync-patches.git] / chmod-option.diff
1 After applying this patch and running configure, you MUST run this
2 command before "make":
3
4     make proto
5
6
7 --- orig/Makefile.in    2004-08-09 21:07:09
8 +++ Makefile.in 2004-07-03 20:13:41
9 @@ -34,7 +34,7 @@ ZLIBOBJ=zlib/deflate.o zlib/infblock.o z
10  OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \
11         main.o checksum.o match.o syscall.o log.o backup.o
12  OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
13 -       fileio.o batch.o clientname.o
14 +       fileio.o batch.o clientname.o chmod.o
15  OBJS3=progress.o pipe.o
16  DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
17  popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
18 --- orig/chmod.c        2004-06-18 17:22:08
19 +++ chmod.c     2004-06-18 17:22:08
20 @@ -0,0 +1,184 @@
21 +#include "rsync.h"
22 +
23 +#define FLAG_X_KEEP (1<<0)
24 +#define FLAG_DIRS_ONLY (1<<1)
25 +#define FLAG_FILES_ONLY (1<<2)
26 +
27 +struct chmod_mode_struct {
28 +       struct chmod_mode_struct *next;
29 +       int ModeAND, ModeOR;
30 +       char flags;
31 +};
32 +
33 +#define CHMOD_ADD 1
34 +#define CHMOD_SUB 2
35 +#define CHMOD_EQ  3
36 +
37 +#define STATE_ERROR 0
38 +#define STATE_1ST_HALF 1
39 +#define STATE_2ND_HALF 2
40 +
41 +/* Parse a chmod-style argument, and break it down into one or more AND/OR
42 + * pairs in a linked list.  We use a state machine to walk through the
43 + * options. */
44 +struct chmod_mode_struct *parse_chmod(char *modestr)
45 +{
46 +       int state = STATE_1ST_HALF;
47 +       int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
48 +       struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
49 +           *prev_mode = NULL;
50 +
51 +       while (state != STATE_ERROR) {
52 +               if (!*modestr || *modestr == ',') {
53 +                       if (!op) {
54 +                               state = STATE_ERROR;
55 +                               break;
56 +                       }
57 +                       prev_mode = curr_mode;
58 +                       curr_mode = new_array(struct chmod_mode_struct, 1);
59 +                       if (prev_mode)
60 +                               prev_mode->next = curr_mode;
61 +                       else
62 +                               first_mode = curr_mode;
63 +                       curr_mode->next = NULL;
64 +
65 +                       switch (op) {
66 +                       case CHMOD_ADD:
67 +                               curr_mode->ModeAND = 07777;
68 +                               curr_mode->ModeOR  = (where * what) + topoct;
69 +                               break;
70 +                       case CHMOD_SUB:
71 +                               curr_mode->ModeAND = 07777 - (where * what) - topoct;
72 +                               curr_mode->ModeOR  = 0;
73 +                               break;
74 +                       case CHMOD_EQ:
75 +                               curr_mode->ModeAND = 07777 - (where * 7);
76 +                               curr_mode->ModeOR  = where * what - topoct;
77 +                               break;
78 +                       }
79 +
80 +                       curr_mode->flags = flags;
81 +
82 +                       if (!*modestr)
83 +                               break;
84 +                       modestr++;
85 +
86 +                       state = STATE_1ST_HALF;
87 +                       where = what = op = topoct = topbits = flags = 0;
88 +               }
89 +
90 +               if (state != STATE_2ND_HALF) {
91 +                       switch (*modestr) {
92 +                       case 'D':
93 +                               if (flags & FLAG_FILES_ONLY)
94 +                                       state = STATE_ERROR;
95 +                               flags |= FLAG_DIRS_ONLY;
96 +                               break;
97 +                       case 'F':
98 +                               if (flags & FLAG_DIRS_ONLY)
99 +                                       state = STATE_ERROR;
100 +                               flags |= FLAG_FILES_ONLY;
101 +                               break;
102 +                       case 'u':
103 +                               where |= 0100;
104 +                               topbits |= 04000;
105 +                               break;
106 +                       case 'g':
107 +                               where |= 0010;
108 +                               topbits |= 02000;
109 +                               break;
110 +                       case 'o':
111 +                               where |= 0001;
112 +                               break;
113 +                       case 'a':
114 +                               where |= 0111;
115 +                               break;
116 +                       case '+':
117 +                               op = CHMOD_ADD;
118 +                               state = STATE_2ND_HALF;
119 +                               break;
120 +                       case '-':
121 +                               op = CHMOD_SUB;
122 +                               state = STATE_2ND_HALF;
123 +                               break;
124 +                       case '=':
125 +                               op = CHMOD_EQ;
126 +                               state = STATE_2ND_HALF;
127 +                               break;
128 +                       default:
129 +                               state = STATE_ERROR;
130 +                               break;
131 +                       }
132 +               } else {
133 +                       switch (*modestr) {
134 +                       case 'r':
135 +                               what |= 4;
136 +                               break;
137 +                       case 'w':
138 +                               what |= 2;
139 +                               break;
140 +                       case 'X':
141 +                               flags |= FLAG_X_KEEP;
142 +                               /* FALL THROUGH */
143 +                       case 'x':
144 +                               what |= 1;
145 +                               break;
146 +                       case 's':
147 +                               if (topbits)
148 +                                       topoct |= topbits;
149 +                               else
150 +                                       topoct = 04000;
151 +                               break;
152 +                       case 't':
153 +                               topoct |= 01000;
154 +                               break;
155 +                       default:
156 +                               state = STATE_ERROR;
157 +                               break;
158 +                       }
159 +               }
160 +               modestr++;
161 +       }
162 +
163 +       if (state == STATE_ERROR) {
164 +               free_chmod_mode(first_mode);
165 +               first_mode = NULL;
166 +       }
167 +       return first_mode;
168 +}
169 +
170 +
171 +/* Takes an existing file permission and a list of AND/OR changes, and
172 + * create a new permissions. */
173 +int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
174 +{
175 +       int IsX = mode & 0111;
176 +       int NonPerm = mode & ~07777;
177 +
178 +       for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
179 +               if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
180 +                       continue;
181 +               if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
182 +                       continue;
183 +               mode &= chmod_modes->ModeAND;
184 +               if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
185 +                       mode |= chmod_modes->ModeOR & ~0111;
186 +               else
187 +                       mode |= chmod_modes->ModeOR;
188 +       }
189 +
190 +       return mode | NonPerm;
191 +}
192 +
193 +/* Free the linked list created by parse_chmod. */
194 +int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
195 +{
196 +       struct chmod_mode_struct *next;
197 +
198 +       while (chmod_modes) {
199 +               next = chmod_modes->next;
200 +               free(chmod_modes);
201 +               chmod_modes = next;
202 +       }
203 +       return 0;
204 +}
205 --- orig/flist.c        2004-08-11 23:42:23
206 +++ flist.c     2004-07-03 20:13:41
207 @@ -33,6 +33,7 @@ extern int verbose;
208  extern int do_progress;
209  extern int am_root;
210  extern int am_server;
211 +extern int am_sender;
212  extern int am_daemon;
213  extern int always_checksum;
214  extern int module_id;
215 @@ -64,6 +65,8 @@ extern int delete_excluded;
216  extern int orig_umask;
217  extern int list_only;
218  
219 +extern struct chmod_mode_struct *chmod_modes;
220 +
221  extern struct exclude_list_struct exclude_list;
222  extern struct exclude_list_struct server_exclude_list;
223  extern struct exclude_list_struct local_exclude_list;
224 @@ -867,7 +870,10 @@ skip_excludes:
225         file->flags = flags;
226         file->modtime = st.st_mtime;
227         file->length = st.st_size;
228 -       file->mode = st.st_mode;
229 +       if (chmod_modes && am_sender && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)))
230 +               file->mode = tweak_mode(st.st_mode, chmod_modes);
231 +       else
232 +               file->mode = st.st_mode;
233         file->uid = st.st_uid;
234         file->gid = st.st_gid;
235  
236 --- orig/options.c      2004-08-11 23:42:23
237 +++ options.c   2004-07-03 20:13:41
238 @@ -126,6 +126,7 @@ char *log_format = NULL;
239  char *password_file = NULL;
240  char *rsync_path = RSYNC_PATH;
241  char *backup_dir = NULL;
242 +char *chmod_mode = NULL;
243  char backup_dir_buf[MAXPATHLEN];
244  int rsync_port = RSYNC_PORT;
245  int link_dest = 0;
246 @@ -138,6 +139,8 @@ int list_only = 0;
247  #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
248  char *batch_name = NULL;
249  
250 +struct chmod_mode_struct *chmod_modes = NULL;
251 +
252  static int daemon_opt;   /* sets am_daemon after option error-reporting */
253  static int modify_window_set;
254  
255 @@ -253,6 +256,7 @@ void usage(enum logcode F)
256    rprintf(F," -g, --group                 preserve group\n");
257    rprintf(F," -D, --devices               preserve devices (root only)\n");
258    rprintf(F," -t, --times                 preserve times\n");
259 +  rprintf(F,"     --chmod=CHMOD           change destination permissions\n");
260    rprintf(F," -S, --sparse                handle sparse files efficiently\n");
261    rprintf(F," -n, --dry-run               show what would have been transferred\n");
262    rprintf(F," -W, --whole-file            copy whole files, no incremental checks\n");
263 @@ -360,6 +364,7 @@ static struct poptOption long_options[] 
264    {"perms",           'p', POPT_ARG_NONE,   &preserve_perms, 0, 0, 0 },
265    {"owner",           'o', POPT_ARG_NONE,   &preserve_uid, 0, 0, 0 },
266    {"group",           'g', POPT_ARG_NONE,   &preserve_gid, 0, 0, 0 },
267 +  {"chmod",            0,  POPT_ARG_STRING, &chmod_mode, 0, 0, 0 },
268    {"devices",         'D', POPT_ARG_NONE,   &preserve_devices, 0, 0, 0 },
269    {"times",           't', POPT_ARG_NONE,   &preserve_times, 0, 0, 0 },
270    {"checksum",        'c', POPT_ARG_NONE,   &always_checksum, 0, 0, 0 },
271 @@ -808,6 +813,13 @@ int parse_arguments(int *argc, const cha
272                 return 0;
273         }
274  
275 +       if (chmod_mode && !(chmod_modes = parse_chmod(chmod_mode))) {
276 +               snprintf(err_buf, sizeof err_buf,
277 +                   "Invalid argument passed to chmod\n");
278 +               rprintf(FERROR, "ERROR: %s", err_buf);
279 +               return 0;
280 +       }
281 +
282         if (do_progress && !verbose)
283                 verbose = 1;
284  
285 @@ -1078,6 +1090,11 @@ void server_options(char **args,int *arg
286                 args[ac++] = compare_dest;
287         }
288  
289 +       if (chmod_mode && !am_sender) {
290 +               args[ac++] = "--chmod";
291 +               args[ac++] = chmod_mode;
292 +       }
293 +
294         if (files_from && (!am_sender || remote_filesfrom_file)) {
295                 if (remote_filesfrom_file) {
296                         args[ac++] = "--files-from";
297 --- orig/rsync.yo       2004-08-11 17:26:27
298 +++ rsync.yo    2004-07-03 20:13:41
299 @@ -301,6 +301,7 @@ verb(
300   -g, --group                 preserve group
301   -D, --devices               preserve devices (root only)
302   -t, --times                 preserve times
303 +     --chmod=CHMOD           change destination permissions
304   -S, --sparse                handle sparse files efficiently
305   -n, --dry-run               show what would have been transferred
306   -W, --whole-file            copy whole files, no incremental checks
307 @@ -574,6 +575,14 @@ cause the next transfer to behave as if 
308  updated (though the rsync algorithm will make the update fairly efficient
309  if the files haven't actually changed, you're much better off using -t).
310  
311 +dit(bf(--chmod)) This options tells rsync to apply the listed "chmod" pattern
312 +to the permission of the files on the destination.  In addition to the normal
313 +parsing rules specified in the chmod manpage, you can specify an item that
314 +should only apply to a directory by prefixing it with a 'D', or specify an
315 +item that should only apply to a file by prefixing it with a 'F'.  For example:
316 +
317 +quote(--chmod=Dg+s,ug+w,Fo-w,+X)
318 +
319  dit(bf(-n, --dry-run)) This tells rsync to not do any file transfers,
320  instead it will just report the actions it would have taken.
321  
322 --- orig/testsuite/chmod-option.test    2004-06-18 17:22:09
323 +++ testsuite/chmod-option.test 2004-06-18 17:22:09
324 @@ -0,0 +1,43 @@
325 +#! /bin/sh
326 +
327 +# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
328 +
329 +# This program is distributable under the terms of the GNU GPL (see
330 +# COPYING).
331 +
332 +# Test that the --chmod option functions correctly.
333 +
334 +. $srcdir/testsuite/rsync.fns
335 +
336 +set -x
337 +
338 +# Build some files
339 +
340 +fromdir="$scratchdir/from"
341 +todir="$scratchdir/to"
342 +checkdir="$scratchdir/check"
343 +
344 +mkdir "$fromdir"
345 +name1="$fromdir/name1"
346 +name2="$fromdir/name2"
347 +dir1="$fromdir/dir1"
348 +dir2="$fromdir/dir2"
349 +echo "This is the file" > "$name1"
350 +echo "This is the other file" > "$name2"
351 +mkdir "$dir1" "$dir2"
352 +
353 +chmod 4700 "$name1" || test_skipped "Can't chmod"
354 +chmod 700 "$dir1"
355 +chmod 770 "$dir2"
356 +
357 +# Copy the files we've created over to another directory
358 +checkit "$RSYNC -avv \"$fromdir/\" \"$checkdir/\"" "$fromdir" "$checkdir"
359 +
360 +# And then manually make the changes which should occur 
361 +chmod ug-s,a+rX "$checkdir"/*
362 +chmod g+w "$checkdir" "$checkdir"/dir*
363 +
364 +checkit "$RSYNC -avv --chmod ug-s,a+rX,Dg+w \"$fromdir/\" \"$todir/\"" "$checkdir" "$todir"
365 +
366 +# The script would have aborted on error, so getting here means we've won.
367 +exit 0