Added a variable to t_stub.c so that "make test" works.
[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-13 07:18:58
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-09-21 09:40:27
206 +++ flist.c     2004-09-18 01:51:11
207 @@ -65,6 +65,8 @@ extern int delete_excluded;
208  extern int orig_umask;
209  extern int list_only;
210  
211 +extern struct chmod_mode_struct *chmod_modes;
212 +
213  extern struct exclude_list_struct exclude_list;
214  extern struct exclude_list_struct server_exclude_list;
215  extern struct exclude_list_struct local_exclude_list;
216 @@ -869,7 +871,10 @@ skip_excludes:
217         file->flags = flags;
218         file->modtime = st.st_mtime;
219         file->length = st.st_size;
220 -       file->mode = st.st_mode;
221 +       if (chmod_modes && am_sender && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)))
222 +               file->mode = tweak_mode(st.st_mode, chmod_modes);
223 +       else
224 +               file->mode = st.st_mode;
225         file->uid = st.st_uid;
226         file->gid = st.st_gid;
227  
228 --- orig/options.c      2004-09-23 17:42:07
229 +++ options.c   2004-07-03 20:13:41
230 @@ -126,6 +126,7 @@ char *log_format = NULL;
231  char *password_file = NULL;
232  char *rsync_path = RSYNC_PATH;
233  char *backup_dir = NULL;
234 +char *chmod_mode = NULL;
235  char backup_dir_buf[MAXPATHLEN];
236  int rsync_port = RSYNC_PORT;
237  int link_dest = 0;
238 @@ -138,6 +139,8 @@ int list_only = 0;
239  #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
240  char *batch_name = NULL;
241  
242 +struct chmod_mode_struct *chmod_modes = NULL;
243 +
244  static int daemon_opt;   /* sets am_daemon after option error-reporting */
245  static int modify_window_set;
246  
247 @@ -253,6 +256,7 @@ void usage(enum logcode F)
248    rprintf(F," -g, --group                 preserve group\n");
249    rprintf(F," -D, --devices               preserve devices (root only)\n");
250    rprintf(F," -t, --times                 preserve times\n");
251 +  rprintf(F,"     --chmod=CHMOD           change destination permissions\n");
252    rprintf(F," -S, --sparse                handle sparse files efficiently\n");
253    rprintf(F," -n, --dry-run               show what would have been transferred\n");
254    rprintf(F," -W, --whole-file            copy whole files, no incremental checks\n");
255 @@ -360,6 +364,7 @@ static struct poptOption long_options[] 
256    {"perms",           'p', POPT_ARG_NONE,   &preserve_perms, 0, 0, 0 },
257    {"owner",           'o', POPT_ARG_NONE,   &preserve_uid, 0, 0, 0 },
258    {"group",           'g', POPT_ARG_NONE,   &preserve_gid, 0, 0, 0 },
259 +  {"chmod",            0,  POPT_ARG_STRING, &chmod_mode, 0, 0, 0 },
260    {"devices",         'D', POPT_ARG_NONE,   &preserve_devices, 0, 0, 0 },
261    {"times",           't', POPT_ARG_NONE,   &preserve_times, 0, 0, 0 },
262    {"checksum",        'c', POPT_ARG_NONE,   &always_checksum, 0, 0, 0 },
263 @@ -804,6 +809,13 @@ int parse_arguments(int *argc, const cha
264                 return 0;
265         }
266  
267 +       if (chmod_mode && !(chmod_modes = parse_chmod(chmod_mode))) {
268 +               snprintf(err_buf, sizeof err_buf,
269 +                   "Invalid argument passed to chmod\n");
270 +               rprintf(FERROR, "ERROR: %s", err_buf);
271 +               return 0;
272 +       }
273 +
274         if (do_progress && !verbose)
275                 verbose = 1;
276  
277 @@ -1087,6 +1099,11 @@ void server_options(char **args,int *arg
278                 args[ac++] = compare_dest;
279         }
280  
281 +       if (chmod_mode && !am_sender) {
282 +               args[ac++] = "--chmod";
283 +               args[ac++] = chmod_mode;
284 +       }
285 +
286         if (files_from && (!am_sender || remote_filesfrom_file)) {
287                 if (remote_filesfrom_file) {
288                         args[ac++] = "--files-from";
289 --- orig/rsync.yo       2004-09-24 16:42:30
290 +++ rsync.yo    2004-07-03 20:13:41
291 @@ -330,6 +330,7 @@ verb(
292   -g, --group                 preserve group
293   -D, --devices               preserve devices (root only)
294   -t, --times                 preserve times
295 +     --chmod=CHMOD           change destination permissions
296   -S, --sparse                handle sparse files efficiently
297   -n, --dry-run               show what would have been transferred
298   -W, --whole-file            copy whole files, no incremental checks
299 @@ -610,6 +611,14 @@ cause the next transfer to behave as if 
300  updated (though the rsync algorithm will make the update fairly efficient
301  if the files haven't actually changed, you're much better off using -t).
302  
303 +dit(bf(--chmod)) This options tells rsync to apply the listed "chmod" pattern
304 +to the permission of the files on the destination.  In addition to the normal
305 +parsing rules specified in the chmod manpage, you can specify an item that
306 +should only apply to a directory by prefixing it with a 'D', or specify an
307 +item that should only apply to a file by prefixing it with a 'F'.  For example:
308 +
309 +quote(--chmod=Dg+s,ug+w,Fo-w,+X)
310 +
311  dit(bf(-n, --dry-run)) This tells rsync to not do any file transfers,
312  instead it will just report the actions it would have taken.
313  
314 --- orig/testsuite/chmod-option.test    2004-06-18 17:22:09
315 +++ testsuite/chmod-option.test 2004-06-18 17:22:09
316 @@ -0,0 +1,43 @@
317 +#! /bin/sh
318 +
319 +# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
320 +
321 +# This program is distributable under the terms of the GNU GPL (see
322 +# COPYING).
323 +
324 +# Test that the --chmod option functions correctly.
325 +
326 +. $srcdir/testsuite/rsync.fns
327 +
328 +set -x
329 +
330 +# Build some files
331 +
332 +fromdir="$scratchdir/from"
333 +todir="$scratchdir/to"
334 +checkdir="$scratchdir/check"
335 +
336 +mkdir "$fromdir"
337 +name1="$fromdir/name1"
338 +name2="$fromdir/name2"
339 +dir1="$fromdir/dir1"
340 +dir2="$fromdir/dir2"
341 +echo "This is the file" > "$name1"
342 +echo "This is the other file" > "$name2"
343 +mkdir "$dir1" "$dir2"
344 +
345 +chmod 4700 "$name1" || test_skipped "Can't chmod"
346 +chmod 700 "$dir1"
347 +chmod 770 "$dir2"
348 +
349 +# Copy the files we've created over to another directory
350 +checkit "$RSYNC -avv \"$fromdir/\" \"$checkdir/\"" "$fromdir" "$checkdir"
351 +
352 +# And then manually make the changes which should occur 
353 +chmod ug-s,a+rX "$checkdir"/*
354 +chmod g+w "$checkdir" "$checkdir"/dir*
355 +
356 +checkit "$RSYNC -avv --chmod ug-s,a+rX,Dg+w \"$fromdir/\" \"$todir/\"" "$checkdir" "$todir"
357 +
358 +# The script would have aborted on error, so getting here means we've won.
359 +exit 0