Updated to work with latest CVS.
[rsync/rsync-patches.git] / chmod-option.diff
1 --- Makefile.in 10 Feb 2004 17:06:11 -0000      1.98
2 +++ Makefile.in 10 Mar 2004 08:00:58 -0000
3 @@ -34,7 +34,7 @@ ZLIBOBJ=zlib/deflate.o zlib/infblock.o z
4  OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \
5         main.o checksum.o match.o syscall.o log.o backup.o
6  OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
7 -       fileio.o batch.o clientname.o
8 +       fileio.o batch.o clientname.o chmod.o
9  OBJS3=progress.o pipe.o
10  DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
11  popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
12 --- chmod.c     2004-02-13 17:08:33.000000000 -0800
13 +++ chmod.c     2004-03-09 23:59:27.000000000 -0800
14 @@ -0,0 +1,162 @@
15 +#include "rsync.h"
16 +
17 +struct chmod_mode_struct {
18 +       int ModeAND;
19 +       int ModeOR;
20 +       char Xkeep;
21 +       struct chmod_mode_struct *next;
22 +};
23 +
24 +#define CHMOD_ADD 1
25 +#define CHMOD_SUB 2
26 +#define CHMOD_EQ  3
27 +
28 +/* Parse a chmod-style argument, and break it down into one or more AND/OR
29 + * pairs in a linked list.
30 + * We use a state machine to walk through the options - states 1 and 3
31 + * before/including the operation (+, - or =), state 2 after the operation and
32 + * state 4 if we hit an error. */
33 +struct chmod_mode_struct *parse_chmod(char *modestr)
34 +{
35 +       int state = 1;
36 +       int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, Xkeep = 0;
37 +       struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
38 +           *prev_mode = NULL;
39 +
40 +       while (*modestr) {
41 +               if (state != 2) {
42 +                       switch (*modestr) {
43 +                       case 'u':
44 +                               where |= 0100;
45 +                               topbits |= 04000;
46 +                               break;
47 +                       case 'g':
48 +                               where |= 0010;
49 +                               topbits |= 02000;
50 +                               break;
51 +                       case 'o':
52 +                               where |= 0001;
53 +                               break;
54 +                       case 'a':
55 +                               where |= 0111;
56 +                               break;
57 +                       case '+':
58 +                               op = CHMOD_ADD;
59 +                               state = 2;
60 +                               break;
61 +                       case '-':
62 +                               op = CHMOD_SUB;
63 +                               state = 2;
64 +                               break;
65 +                       case '=':
66 +                               op = CHMOD_EQ;
67 +                               state = 2;
68 +                               break;
69 +                       case ',':
70 +                               break;
71 +                       default:
72 +                               state = 4;      /* Invalid Mode! */
73 +                               break;
74 +                       }
75 +               } else {
76 +                       switch (*modestr) {
77 +                       case 'r':
78 +                               what |= 4;
79 +                               break;
80 +                       case 'w':
81 +                               what |= 2;
82 +                               break;
83 +                       case 'X':
84 +                               Xkeep = 1;
85 +                               /* FALL THROUGH */
86 +                       case 'x':
87 +                               what |= 1;
88 +                               break;
89 +                       case 's':
90 +                               if (topbits)
91 +                                       topoct |= topbits;
92 +                               else
93 +                                       topoct = 04000;
94 +                               break;
95 +                       case 't':
96 +                               topoct |= 01000;
97 +                               break;
98 +                       default:
99 +                               state = 4;      /* Invalid Mode! */
100 +                               break;
101 +                       }
102 +               }
103 +
104 +               if (state == 4)
105 +                       break;
106 +
107 +               modestr++;
108 +               if (!*modestr || *modestr == ',') {
109 +                       prev_mode = curr_mode;
110 +                       curr_mode = new_array(struct chmod_mode_struct, 1);
111 +                       if (prev_mode)
112 +                               prev_mode->next = curr_mode;
113 +                       else
114 +                               first_mode = curr_mode;
115 +                       curr_mode->next = NULL;
116 +
117 +                       switch (op) {
118 +                       case CHMOD_ADD:
119 +                               curr_mode->ModeAND = 07777;
120 +                               curr_mode->ModeOR  = (where * what) + topoct;
121 +                               break;
122 +                       case CHMOD_SUB:
123 +                               curr_mode->ModeAND = 07777 - (where * what) - topoct;
124 +                               curr_mode->ModeOR  = 0;
125 +                               break;
126 +                       case CHMOD_EQ:
127 +                               curr_mode->ModeAND = 07777 - (where * 7);
128 +                               curr_mode->ModeOR  = where * what - topoct;
129 +                               break;
130 +                       }
131 +
132 +                       curr_mode->Xkeep = Xkeep;
133 +                       state = 3;
134 +                       where = what = topoct = topbits = Xkeep = 0;
135 +               }
136 +       }
137 +
138 +       if (state == 4) {
139 +               free_chmod_mode(first_mode);
140 +               first_mode = NULL;
141 +       }
142 +       return first_mode;
143 +}
144 +
145 +
146 +/* Takes an existing file permission and a list of AND/OR changes, and
147 + * create a new permissions. */
148 +int tweak_mode(int oldmode, struct chmod_mode_struct *chmod_modes)
149 +{
150 +       int IsX = oldmode & 0111;
151 +       int NonPerm = oldmode - (oldmode & 07777);
152 +
153 +       while (chmod_modes) {
154 +               oldmode &= chmod_modes->ModeAND;
155 +               if (chmod_modes->Xkeep && !IsX)
156 +                       oldmode |= chmod_modes->ModeOR & (07777 - 0111);
157 +               else
158 +                       oldmode |= chmod_modes->ModeOR;
159 +               chmod_modes = chmod_modes->next;
160 +       }
161 +
162 +       return oldmode + NonPerm;
163 +}
164 +
165 +/* Free the linked list created by parse_chmod. */
166 +int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
167 +{
168 +       struct chmod_mode_struct *next;
169 +
170 +       while (chmod_modes) {
171 +               next = chmod_modes->next;
172 +               free(chmod_modes);
173 +               chmod_modes = next;
174 +       }
175 +       return 0;
176 +}
177 --- flist.c     11 Feb 2004 02:48:58 -0000      1.205
178 +++ flist.c     10 Mar 2004 08:00:59 -0000
179 @@ -33,6 +33,7 @@ extern int verbose;
180  extern int do_progress;
181  extern int am_root;
182  extern int am_server;
183 +extern int am_sender;
184  extern int always_checksum;
185  extern int module_id;
186  extern int ignore_errors;
187 @@ -63,6 +64,8 @@ extern int sanitize_paths;
188  extern int read_batch;
189  extern int write_batch;
190  
191 +extern struct chmod_mode_struct *chmod_modes;
192 +
193  extern struct exclude_struct **exclude_list;
194  extern struct exclude_struct **server_exclude_list;
195  extern struct exclude_struct **local_exclude_list;
196 @@ -831,7 +834,10 @@ skip_excludes:
197         file->flags = flags;
198         file->modtime = st.st_mtime;
199         file->length = st.st_size;
200 -       file->mode = st.st_mode;
201 +       if (chmod_modes && am_sender && S_ISREG(st.st_mode))
202 +               file->mode = tweak_mode(st.st_mode, chmod_modes);
203 +       else
204 +               file->mode = st.st_mode;
205         file->uid = st.st_uid;
206         file->gid = st.st_gid;
207  
208 --- options.c   22 Feb 2004 08:56:43 -0000      1.139
209 +++ options.c   10 Mar 2004 08:00:59 -0000
210 @@ -119,6 +119,7 @@ char *log_format = NULL;
211  char *password_file = NULL;
212  char *rsync_path = RSYNC_PATH;
213  char *backup_dir = NULL;
214 +char *chmod_mode = NULL;
215  char backup_dir_buf[MAXPATHLEN];
216  int rsync_port = RSYNC_PORT;
217  int link_dest = 0;
218 @@ -132,6 +133,8 @@ int list_only = 0;
219  #define MAX_BATCH_PREFIX_LEN 256       /* Must be less than MAXPATHLEN-13 */
220  char *batch_prefix = NULL;
221  
222 +struct chmod_mode_struct *chmod_modes = NULL;
223 +
224  static int daemon_opt;   /* sets am_daemon after option error-reporting */
225  static int modify_window_set;
226  
227 @@ -239,6 +242,7 @@ void usage(enum logcode F)
228    rprintf(F," -g, --group                 preserve group\n");
229    rprintf(F," -D, --devices               preserve devices (root only)\n");
230    rprintf(F," -t, --times                 preserve times\n");
231 +  rprintf(F,"     --chmod=CHMOD           change destination permissions\n");  
232    rprintf(F," -S, --sparse                handle sparse files efficiently\n");
233    rprintf(F," -n, --dry-run               show what would have been transferred\n");
234    rprintf(F," -W, --whole-file            copy whole files, no incremental checks\n");
235 @@ -342,6 +346,7 @@ static struct poptOption long_options[] 
236    {"perms",           'p', POPT_ARG_NONE,   &preserve_perms, 0, 0, 0 },
237    {"owner",           'o', POPT_ARG_NONE,   &preserve_uid, 0, 0, 0 },
238    {"group",           'g', POPT_ARG_NONE,   &preserve_gid, 0, 0, 0 },
239 +  {"chmod",            0,  POPT_ARG_STRING, &chmod_mode, 0, 0, 0 },
240    {"devices",         'D', POPT_ARG_NONE,   &preserve_devices, 0, 0, 0 },
241    {"times",           't', POPT_ARG_NONE,   &preserve_times, 0, 0, 0 },
242    {"checksum",        'c', POPT_ARG_NONE,   &always_checksum, 0, 0, 0 },
243 @@ -687,6 +692,13 @@ int parse_arguments(int *argc, const cha
244                 exit_cleanup(RERR_SYNTAX);
245         }
246  
247 +       if (chmod_mode && !(chmod_modes = parse_chmod(chmod_mode))) {
248 +               snprintf(err_buf, sizeof err_buf,
249 +                   "Invalid argument passed to chmod\n");
250 +               rprintf(FERROR, "ERROR: %s", err_buf);
251 +               return 0;
252 +       }
253 +
254         if (do_progress && !verbose)
255                 verbose = 1;
256  
257 @@ -932,6 +944,11 @@ void server_options(char **args,int *arg
258                  */
259                 args[ac++] = link_dest ? "--link-dest" : "--compare-dest";
260                 args[ac++] = compare_dest;
261 +       }
262 +
263 +       if (chmod_mode && !am_sender) {
264 +               args[ac++] = "--chmod";
265 +               args[ac++] = chmod_mode;
266         }
267  
268         if (files_from && (!am_sender || remote_filesfrom_file)) {
269 --- proto.h     17 Feb 2004 23:13:06 -0000      1.184
270 +++ proto.h     10 Mar 2004 08:00:59 -0000
271 @@ -25,6 +25,9 @@ void file_checksum(char *fname,char *sum
272  void sum_init(void);
273  void sum_update(char *p, int len);
274  void sum_end(char *sum);
275 +struct chmod_mode_struct *parse_chmod(char *modestr);
276 +int tweak_mode(int oldmode, struct chmod_mode_struct *chmod_modes);
277 +int free_chmod_mode(struct chmod_mode_struct *chmod_modes);
278  void close_all(void);
279  void _exit_cleanup(int code, const char *file, int line);
280  void cleanup_disable(void);
281 --- rsync.1     2 Feb 2004 18:23:09 -0000       1.163
282 +++ rsync.1     10 Mar 2004 08:01:00 -0000
283 @@ -336,6 +336,7 @@ to the detailed description below for a 
284   -g, --group                 preserve group
285   -D, --devices               preserve devices (root only)
286   -t, --times                 preserve times
287 +     --chmod=CHMOD           change destination permissions
288   -S, --sparse                handle sparse files efficiently
289   -n, --dry-run               show what would have been transferred
290   -W, --whole-file            copy whole files, no incremental checks
291 @@ -614,6 +615,10 @@ modified cannot be effective; in other w
292  cause the next transfer to behave as if it used -I, and all files will have
293  their checksums compared and show up in log messages even if they haven\&'t
294  changed\&.
295 +.IP 
296 +.IP "\fB--chmod\fP" 
297 +This options tells rsync to apply the listed "chmod" pattern
298 +to the permission of the files on the destination\&.
299  .IP 
300  .IP "\fB-n, --dry-run\fP" 
301  This tells rsync to not do any file transfers,
302 --- rsync.yo    2 Feb 2004 18:23:09 -0000       1.147
303 +++ rsync.yo    10 Mar 2004 08:01:00 -0000
304 @@ -299,6 +299,7 @@ verb(
305   -g, --group                 preserve group
306   -D, --devices               preserve devices (root only)
307   -t, --times                 preserve times
308 +     --chmod=CHMOD           change destination permissions
309   -S, --sparse                handle sparse files efficiently
310   -n, --dry-run               show what would have been transferred
311   -W, --whole-file            copy whole files, no incremental checks
312 @@ -534,6 +535,9 @@ modified cannot be effective; in other w
313  cause the next transfer to behave as if it used -I, and all files will have
314  their checksums compared and show up in log messages even if they haven't
315  changed.
316 +
317 +dit(bf(--chmod)) This options tells rsync to apply the listed "chmod" pattern
318 +to the permission of the files on the destination.
319  
320  dit(bf(-n, --dry-run)) This tells rsync to not do any file transfers,
321  instead it will just report the actions it would have taken.
322 --- testsuite/chmod.test        2004-02-13 17:08:33.000000000 -0800
323 +++ testsuite/chmod.test        2004-03-10 00:05:23.000000000 -0800
324 @@ -0,0 +1,37 @@
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 +echo "This is the file" > "$name1"
348 +echo "This is the other file" > "$name2"
349 +
350 +chmod 4700 "$name1" || test_skipped "Can't chown"
351 +
352 +# Copy the files we've created over to another directory
353 +checkit "$RSYNC -avv \"$fromdir/\" \"$checkdir/\"" "$fromdir" "$checkdir"
354 +
355 +# And then manually make the changes which should occur 
356 +chmod ug-s,a+rX $checkdir/*
357 +
358 +checkit "$RSYNC -avv --chmod ug-s,a+rX \"$fromdir/\" \"$todir/\"" "$checkdir" "$todir"
359 +
360 +# The script would have aborted on error, so getting here means we've won.
361 +exit 0