Fixed failing hunk.
[rsync/rsync-patches.git] / chmod-option.diff
CommitLineData
8a529471
WD
1After applying this patch and running configure, you MUST run this
2command before "make":
3
4 make proto
5
6
67c08134 7--- orig/Makefile.in 2004-08-13 07:18:58
13bed3dd 8+++ Makefile.in 2004-07-03 20:13:41
2ece3e77
WD
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
bd006671
MP
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 \
13bed3dd
WD
18--- orig/chmod.c 2004-06-18 17:22:08
19+++ chmod.c 2004-06-18 17:22:08
548aa120 20@@ -0,0 +1,184 @@
bd006671
MP
21+#include "rsync.h"
22+
548aa120
WD
23+#define FLAG_X_KEEP (1<<0)
24+#define FLAG_DIRS_ONLY (1<<1)
25+#define FLAG_FILES_ONLY (1<<2)
26+
bd006671 27+struct chmod_mode_struct {
bd006671 28+ struct chmod_mode_struct *next;
548aa120
WD
29+ int ModeAND, ModeOR;
30+ char flags;
bd006671
MP
31+};
32+
33+#define CHMOD_ADD 1
34+#define CHMOD_SUB 2
35+#define CHMOD_EQ 3
36+
548aa120
WD
37+#define STATE_ERROR 0
38+#define STATE_1ST_HALF 1
39+#define STATE_2ND_HALF 2
40+
bd006671 41+/* Parse a chmod-style argument, and break it down into one or more AND/OR
548aa120
WD
42+ * pairs in a linked list. We use a state machine to walk through the
43+ * options. */
2ece3e77 44+struct chmod_mode_struct *parse_chmod(char *modestr)
bd006671 45+{
548aa120
WD
46+ int state = STATE_1ST_HALF;
47+ int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
2ece3e77
WD
48+ struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
49+ *prev_mode = NULL;
bd006671 50+
548aa120
WD
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) {
2ece3e77 91+ switch (*modestr) {
548aa120
WD
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;
2ece3e77
WD
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;
548aa120 118+ state = STATE_2ND_HALF;
2ece3e77
WD
119+ break;
120+ case '-':
121+ op = CHMOD_SUB;
548aa120 122+ state = STATE_2ND_HALF;
2ece3e77
WD
123+ break;
124+ case '=':
125+ op = CHMOD_EQ;
548aa120 126+ state = STATE_2ND_HALF;
2ece3e77
WD
127+ break;
128+ default:
548aa120 129+ state = STATE_ERROR;
2ece3e77
WD
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':
548aa120 141+ flags |= FLAG_X_KEEP;
2ece3e77
WD
142+ /* FALL THROUGH */
143+ case 'x':
144+ what |= 1;
145+ break;
146+ case 's':
147+ if (topbits)
bd006671 148+ topoct |= topbits;
2ece3e77 149+ else
bd006671 150+ topoct = 04000;
2ece3e77
WD
151+ break;
152+ case 't':
153+ topoct |= 01000;
154+ break;
155+ default:
548aa120 156+ state = STATE_ERROR;
2ece3e77
WD
157+ break;
158+ }
bd006671 159+ }
2ece3e77 160+ modestr++;
bd006671
MP
161+ }
162+
548aa120 163+ if (state == STATE_ERROR) {
bd006671 164+ free_chmod_mode(first_mode);
2ece3e77 165+ first_mode = NULL;
bd006671
MP
166+ }
167+ return first_mode;
168+}
169+
170+
2ece3e77
WD
171+/* Takes an existing file permission and a list of AND/OR changes, and
172+ * create a new permissions. */
548aa120 173+int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
bd006671 174+{
548aa120
WD
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;
2ece3e77 186+ else
548aa120 187+ mode |= chmod_modes->ModeOR;
bd006671
MP
188+ }
189+
548aa120 190+ return mode | NonPerm;
bd006671
MP
191+}
192+
2ece3e77
WD
193+/* Free the linked list created by parse_chmod. */
194+int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
bd006671
MP
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+}
67c08134 205--- orig/flist.c 2004-08-12 18:34:38
13bed3dd 206+++ flist.c 2004-07-03 20:13:41
2ece3e77 207@@ -33,6 +33,7 @@ extern int verbose;
bd006671 208 extern int do_progress;
2ece3e77 209 extern int am_root;
bd006671
MP
210 extern int am_server;
211+extern int am_sender;
54691942 212 extern int am_daemon;
bd006671 213 extern int always_checksum;
2ece3e77 214 extern int module_id;
9be39c35
WD
215@@ -64,6 +65,8 @@ extern int delete_excluded;
216 extern int orig_umask;
217 extern int list_only;
bd006671
MP
218
219+extern struct chmod_mode_struct *chmod_modes;
220+
54691942
WD
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;
f635ed27 224@@ -867,7 +870,10 @@ skip_excludes:
2ece3e77
WD
225 file->flags = flags;
226 file->modtime = st.st_mtime;
227 file->length = st.st_size;
228- file->mode = st.st_mode;
46b46be7 229+ if (chmod_modes && am_sender && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)))
2ece3e77
WD
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;
bd006671 235
67c08134 236--- orig/options.c 2004-08-12 18:34:38
13bed3dd 237+++ options.c 2004-07-03 20:13:41
f635ed27 238@@ -126,6 +126,7 @@ char *log_format = NULL;
bd006671
MP
239 char *password_file = NULL;
240 char *rsync_path = RSYNC_PATH;
241 char *backup_dir = NULL;
242+char *chmod_mode = NULL;
2ece3e77 243 char backup_dir_buf[MAXPATHLEN];
bd006671 244 int rsync_port = RSYNC_PORT;
2ece3e77 245 int link_dest = 0;
f635ed27 246@@ -138,6 +139,8 @@ int list_only = 0;
9be39c35
WD
247 #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
248 char *batch_name = NULL;
bd006671
MP
249
250+struct chmod_mode_struct *chmod_modes = NULL;
2ece3e77
WD
251+
252 static int daemon_opt; /* sets am_daemon after option error-reporting */
253 static int modify_window_set;
bd006671 254
f635ed27 255@@ -253,6 +256,7 @@ void usage(enum logcode F)
bd006671
MP
256 rprintf(F," -g, --group preserve group\n");
257 rprintf(F," -D, --devices preserve devices (root only)\n");
2ece3e77 258 rprintf(F," -t, --times preserve times\n");
548aa120 259+ rprintf(F," --chmod=CHMOD change destination permissions\n");
bd006671
MP
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");
f635ed27 263@@ -360,6 +364,7 @@ static struct poptOption long_options[]
2ece3e77
WD
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 },
f635ed27 271@@ -808,6 +813,13 @@ int parse_arguments(int *argc, const cha
d5753a22 272 return 0;
2ece3e77 273 }
bd006671 274
2ece3e77
WD
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;
bd006671 284
f635ed27 285@@ -1078,6 +1090,11 @@ void server_options(char **args,int *arg
bd006671 286 args[ac++] = compare_dest;
7628f156
WD
287 }
288
bd006671
MP
289+ if (chmod_mode && !am_sender) {
290+ args[ac++] = "--chmod";
291+ args[ac++] = chmod_mode;
7628f156
WD
292+ }
293+
2ece3e77 294 if (files_from && (!am_sender || remote_filesfrom_file)) {
7628f156
WD
295 if (remote_filesfrom_file) {
296 args[ac++] = "--files-from";
67c08134 297--- orig/rsync.yo 2004-08-13 07:18:59
13bed3dd 298+++ rsync.yo 2004-07-03 20:13:41
67c08134 299@@ -330,6 +330,7 @@ verb(
2ece3e77
WD
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
67c08134 307@@ -603,6 +604,14 @@ cause the next transfer to behave as if
f635ed27
WD
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).
7628f156 310
bd006671 311+dit(bf(--chmod)) This options tells rsync to apply the listed "chmod" pattern
548aa120
WD
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)
7628f156 318+
bd006671
MP
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.
7628f156 321
f635ed27
WD
322--- orig/testsuite/chmod-option.test 2004-06-18 17:22:09
323+++ testsuite/chmod-option.test 2004-06-18 17:22:09
46b46be7 324@@ -0,0 +1,43 @@
bd006671
MP
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"
46b46be7
WD
347+dir1="$fromdir/dir1"
348+dir2="$fromdir/dir2"
bd006671
MP
349+echo "This is the file" > "$name1"
350+echo "This is the other file" > "$name2"
46b46be7 351+mkdir "$dir1" "$dir2"
bd006671 352+
46b46be7
WD
353+chmod 4700 "$name1" || test_skipped "Can't chmod"
354+chmod 700 "$dir1"
355+chmod 770 "$dir2"
bd006671
MP
356+
357+# Copy the files we've created over to another directory
2ece3e77 358+checkit "$RSYNC -avv \"$fromdir/\" \"$checkdir/\"" "$fromdir" "$checkdir"
bd006671
MP
359+
360+# And then manually make the changes which should occur
46b46be7
WD
361+chmod ug-s,a+rX "$checkdir"/*
362+chmod g+w "$checkdir" "$checkdir"/dir*
bd006671 363+
46b46be7 364+checkit "$RSYNC -avv --chmod ug-s,a+rX,Dg+w \"$fromdir/\" \"$todir/\"" "$checkdir" "$todir"
bd006671 365+
2ece3e77 366+# The script would have aborted on error, so getting here means we've won.
bd006671 367+exit 0