1 After applying this patch and running configure, you MUST run this
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
23 +#define FLAG_X_KEEP (1<<0)
24 +#define FLAG_DIRS_ONLY (1<<1)
25 +#define FLAG_FILES_ONLY (1<<2)
27 +struct chmod_mode_struct {
28 + struct chmod_mode_struct *next;
29 + int ModeAND, ModeOR;
37 +#define STATE_ERROR 0
38 +#define STATE_1ST_HALF 1
39 +#define STATE_2ND_HALF 2
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
44 +struct chmod_mode_struct *parse_chmod(char *modestr)
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,
51 + while (state != STATE_ERROR) {
52 + if (!*modestr || *modestr == ',') {
54 + state = STATE_ERROR;
57 + prev_mode = curr_mode;
58 + curr_mode = new_array(struct chmod_mode_struct, 1);
60 + prev_mode->next = curr_mode;
62 + first_mode = curr_mode;
63 + curr_mode->next = NULL;
67 + curr_mode->ModeAND = 07777;
68 + curr_mode->ModeOR = (where * what) + topoct;
71 + curr_mode->ModeAND = 07777 - (where * what) - topoct;
72 + curr_mode->ModeOR = 0;
75 + curr_mode->ModeAND = 07777 - (where * 7);
76 + curr_mode->ModeOR = where * what - topoct;
80 + curr_mode->flags = flags;
86 + state = STATE_1ST_HALF;
87 + where = what = op = topoct = topbits = flags = 0;
90 + if (state != STATE_2ND_HALF) {
93 + if (flags & FLAG_FILES_ONLY)
94 + state = STATE_ERROR;
95 + flags |= FLAG_DIRS_ONLY;
98 + if (flags & FLAG_DIRS_ONLY)
99 + state = STATE_ERROR;
100 + flags |= FLAG_FILES_ONLY;
118 + state = STATE_2ND_HALF;
122 + state = STATE_2ND_HALF;
126 + state = STATE_2ND_HALF;
129 + state = STATE_ERROR;
133 + switch (*modestr) {
141 + flags |= FLAG_X_KEEP;
156 + state = STATE_ERROR;
163 + if (state == STATE_ERROR) {
164 + free_chmod_mode(first_mode);
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)
175 + int IsX = mode & 0111;
176 + int NonPerm = mode & ~07777;
178 + for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
179 + if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
181 + if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
183 + mode &= chmod_modes->ModeAND;
184 + if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
185 + mode |= chmod_modes->ModeOR & ~0111;
187 + mode |= chmod_modes->ModeOR;
190 + return mode | NonPerm;
193 +/* Free the linked list created by parse_chmod. */
194 +int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
196 + struct chmod_mode_struct *next;
198 + while (chmod_modes) {
199 + next = chmod_modes->next;
201 + chmod_modes = next;
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;
211 +extern struct chmod_mode_struct *chmod_modes;
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:
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);
224 + file->mode = st.st_mode;
225 file->uid = st.st_uid;
226 file->gid = st.st_gid;
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;
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;
242 +struct chmod_mode_struct *chmod_modes = NULL;
244 static int daemon_opt; /* sets am_daemon after option error-reporting */
245 static int modify_window_set;
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
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);
274 if (do_progress && !verbose)
277 @@ -1087,6 +1099,11 @@ void server_options(char **args,int *arg
278 args[ac++] = compare_dest;
281 + if (chmod_mode && !am_sender) {
282 + args[ac++] = "--chmod";
283 + args[ac++] = chmod_mode;
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).
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:
309 +quote(--chmod=Dg+s,ug+w,Fo-w,+X)
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.
314 --- orig/testsuite/chmod-option.test 2004-06-18 17:22:09
315 +++ testsuite/chmod-option.test 2004-06-18 17:22:09
319 +# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
321 +# This program is distributable under the terms of the GNU GPL (see
324 +# Test that the --chmod option functions correctly.
326 +. $srcdir/testsuite/rsync.fns
332 +fromdir="$scratchdir/from"
333 +todir="$scratchdir/to"
334 +checkdir="$scratchdir/check"
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"
345 +chmod 4700 "$name1" || test_skipped "Can't chmod"
349 +# Copy the files we've created over to another directory
350 +checkit "$RSYNC -avv \"$fromdir/\" \"$checkdir/\"" "$fromdir" "$checkdir"
352 +# And then manually make the changes which should occur
353 +chmod ug-s,a+rX "$checkdir"/*
354 +chmod g+w "$checkdir" "$checkdir"/dir*
356 +checkit "$RSYNC -avv --chmod ug-s,a+rX,Dg+w \"$fromdir/\" \"$todir/\"" "$checkdir" "$todir"
358 +# The script would have aborted on error, so getting here means we've won.