Tweaked to work with latest CVS.
[rsync/rsync-patches.git] / chmod-option.diff
CommitLineData
2ece3e77
WD
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
bd006671
MP
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 \
2ece3e77
WD
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 @@
bd006671
MP
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
2ece3e77
WD
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)
bd006671
MP
34+{
35+ int state = 1;
2ece3e77
WD
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;
bd006671
MP
39+
40+ while (*modestr) {
2ece3e77
WD
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)
bd006671 91+ topoct |= topbits;
2ece3e77 92+ else
bd006671 93+ topoct = 04000;
2ece3e77
WD
94+ break;
95+ case 't':
96+ topoct |= 01000;
97+ break;
98+ default:
99+ state = 4; /* Invalid Mode! */
100+ break;
101+ }
bd006671 102+ }
bd006671 103+
2ece3e77
WD
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) {
bd006671
MP
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;
2ece3e77 130+ }
bd006671 131+
2ece3e77
WD
132+ curr_mode->Xkeep = Xkeep;
133+ state = 3;
134+ where = what = topoct = topbits = Xkeep = 0;
135+ }
bd006671
MP
136+ }
137+
2ece3e77 138+ if (state == 4) {
bd006671 139+ free_chmod_mode(first_mode);
2ece3e77 140+ first_mode = NULL;
bd006671
MP
141+ }
142+ return first_mode;
143+}
144+
145+
2ece3e77
WD
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)
bd006671 149+{
2ece3e77 150+ int IsX = oldmode & 0111;
bd006671
MP
151+ int NonPerm = oldmode - (oldmode & 07777);
152+
153+ while (chmod_modes) {
154+ oldmode &= chmod_modes->ModeAND;
2ece3e77 155+ if (chmod_modes->Xkeep && !IsX)
bd006671 156+ oldmode |= chmod_modes->ModeOR & (07777 - 0111);
2ece3e77 157+ else
bd006671 158+ oldmode |= chmod_modes->ModeOR;
bd006671
MP
159+ chmod_modes = chmod_modes->next;
160+ }
161+
2ece3e77 162+ return oldmode + NonPerm;
bd006671
MP
163+}
164+
2ece3e77
WD
165+/* Free the linked list created by parse_chmod. */
166+int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
bd006671
MP
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+}
2ece3e77
WD
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;
bd006671 180 extern int do_progress;
2ece3e77 181 extern int am_root;
bd006671
MP
182 extern int am_server;
183+extern int am_sender;
184 extern int always_checksum;
2ece3e77
WD
185 extern int module_id;
186 extern int ignore_errors;
187@@ -63,6 +64,8 @@ extern int sanitize_paths;
bd006671
MP
188 extern int read_batch;
189 extern int write_batch;
190
191+extern struct chmod_mode_struct *chmod_modes;
192+
2ece3e77
WD
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;
bd006671 207
2ece3e77
WD
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;
bd006671
MP
211 char *password_file = NULL;
212 char *rsync_path = RSYNC_PATH;
213 char *backup_dir = NULL;
214+char *chmod_mode = NULL;
2ece3e77 215 char backup_dir_buf[MAXPATHLEN];
bd006671 216 int rsync_port = RSYNC_PORT;
2ece3e77
WD
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;
bd006671
MP
221
222+struct chmod_mode_struct *chmod_modes = NULL;
2ece3e77
WD
223+
224 static int daemon_opt; /* sets am_daemon after option error-reporting */
225 static int modify_window_set;
bd006671 226
2ece3e77 227@@ -239,6 +242,7 @@ void usage(enum logcode F)
bd006671
MP
228 rprintf(F," -g, --group preserve group\n");
229 rprintf(F," -D, --devices preserve devices (root only)\n");
2ece3e77 230 rprintf(F," -t, --times preserve times\n");
bd006671
MP
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");
2ece3e77
WD
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 }
bd006671 246
2ece3e77
WD
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;
bd006671 256
2ece3e77 257@@ -932,6 +944,11 @@ void server_options(char **args,int *arg
bd006671 258 */
2ece3e77 259 args[ac++] = link_dest ? "--link-dest" : "--compare-dest";
bd006671
MP
260 args[ac++] = compare_dest;
261+ }
262+
263+ if (chmod_mode && !am_sender) {
264+ args[ac++] = "--chmod";
265+ args[ac++] = chmod_mode;
266 }
267
2ece3e77
WD
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
bd006671 272 void sum_init(void);
2ece3e77 273 void sum_update(char *p, int len);
bd006671 274 void sum_end(char *sum);
2ece3e77
WD
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);
bd006671
MP
279 void _exit_cleanup(int code, const char *file, int line);
280 void cleanup_disable(void);
2ece3e77
WD
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
bd006671
MP
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
2ece3e77
WD
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
bd006671
MP
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.
2ece3e77
WD
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 @@
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"
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
2ece3e77 353+checkit "$RSYNC -avv \"$fromdir/\" \"$checkdir/\"" "$fromdir" "$checkdir"
bd006671
MP
354+
355+# And then manually make the changes which should occur
356+chmod ug-s,a+rX $checkdir/*
357+
2ece3e77 358+checkit "$RSYNC -avv --chmod ug-s,a+rX \"$fromdir/\" \"$todir/\"" "$checkdir" "$todir"
bd006671 359+
2ece3e77 360+# The script would have aborted on error, so getting here means we've won.
bd006671 361+exit 0