Fixed a failing hunk.
[rsync/rsync-patches.git] / chmod-option.diff
... / ...
CommitLineData
1After applying this patch and running configure, you MUST run this
2command before "make":
3
4 make proto
5
6
7--- orig/Makefile.in 2004-11-03 11:56:03
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-11-27 18:27:35
229+++ options.c 2004-07-03 20:13:41
230@@ -128,6 +128,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 = 0;
237 int compare_dest = 0;
238@@ -141,6 +142,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 static char *dest_option = NULL;
247@@ -260,6 +263,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@@ -363,6 +367,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@@ -932,6 +937,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@@ -1225,6 +1237,11 @@ void server_options(char **args,int *arg
278 }
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-11-27 17:53:24
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@@ -619,6 +620,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