A parallel-checksumming idea.
[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
91a461d6 7--- orig/Makefile.in 2005-07-07 23:11:07
13bed3dd 8+++ Makefile.in 2004-07-03 20:13:41
91a461d6 9@@ -33,7 +33,7 @@ ZLIBOBJ=zlib/deflate.o zlib/inffast.o zl
2ece3e77
WD
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 \
91a461d6
WD
18--- orig/chmod.c 2005-07-09 16:09:14
19+++ chmod.c 2005-07-09 16:09:14
20@@ -0,0 +1,195 @@
bd006671
MP
21+#include "rsync.h"
22+
91a461d6
WD
23+extern int orig_umask;
24+
548aa120
WD
25+#define FLAG_X_KEEP (1<<0)
26+#define FLAG_DIRS_ONLY (1<<1)
27+#define FLAG_FILES_ONLY (1<<2)
28+
bd006671 29+struct chmod_mode_struct {
bd006671 30+ struct chmod_mode_struct *next;
548aa120
WD
31+ int ModeAND, ModeOR;
32+ char flags;
bd006671
MP
33+};
34+
35+#define CHMOD_ADD 1
36+#define CHMOD_SUB 2
37+#define CHMOD_EQ 3
38+
548aa120
WD
39+#define STATE_ERROR 0
40+#define STATE_1ST_HALF 1
41+#define STATE_2ND_HALF 2
42+
bd006671 43+/* Parse a chmod-style argument, and break it down into one or more AND/OR
548aa120
WD
44+ * pairs in a linked list. We use a state machine to walk through the
45+ * options. */
2ece3e77 46+struct chmod_mode_struct *parse_chmod(char *modestr)
bd006671 47+{
548aa120
WD
48+ int state = STATE_1ST_HALF;
49+ int where = 0, what = 0, op = 0, topbits = 0, topoct = 0, flags = 0;
2ece3e77
WD
50+ struct chmod_mode_struct *first_mode = NULL, *curr_mode = NULL,
51+ *prev_mode = NULL;
bd006671 52+
548aa120
WD
53+ while (state != STATE_ERROR) {
54+ if (!*modestr || *modestr == ',') {
91a461d6
WD
55+ int bits;
56+
548aa120
WD
57+ if (!op) {
58+ state = STATE_ERROR;
59+ break;
60+ }
61+ prev_mode = curr_mode;
62+ curr_mode = new_array(struct chmod_mode_struct, 1);
63+ if (prev_mode)
64+ prev_mode->next = curr_mode;
65+ else
66+ first_mode = curr_mode;
67+ curr_mode->next = NULL;
68+
91a461d6
WD
69+ if (where)
70+ bits = where * what;
71+ else {
72+ where = 0111;
73+ bits = (where * what) & ~orig_umask;
74+ }
75+
548aa120
WD
76+ switch (op) {
77+ case CHMOD_ADD:
78+ curr_mode->ModeAND = 07777;
91a461d6 79+ curr_mode->ModeOR = bits + topoct;
548aa120
WD
80+ break;
81+ case CHMOD_SUB:
91a461d6 82+ curr_mode->ModeAND = 07777 - bits - topoct;
548aa120
WD
83+ curr_mode->ModeOR = 0;
84+ break;
85+ case CHMOD_EQ:
91a461d6
WD
86+ curr_mode->ModeAND = 07777 - (where * 7) - (topoct ? topbits : 0);
87+ curr_mode->ModeOR = bits + topoct;
548aa120
WD
88+ break;
89+ }
90+
91+ curr_mode->flags = flags;
92+
93+ if (!*modestr)
94+ break;
95+ modestr++;
96+
97+ state = STATE_1ST_HALF;
98+ where = what = op = topoct = topbits = flags = 0;
99+ }
100+
101+ if (state != STATE_2ND_HALF) {
2ece3e77 102+ switch (*modestr) {
548aa120
WD
103+ case 'D':
104+ if (flags & FLAG_FILES_ONLY)
105+ state = STATE_ERROR;
106+ flags |= FLAG_DIRS_ONLY;
107+ break;
108+ case 'F':
109+ if (flags & FLAG_DIRS_ONLY)
110+ state = STATE_ERROR;
111+ flags |= FLAG_FILES_ONLY;
112+ break;
2ece3e77
WD
113+ case 'u':
114+ where |= 0100;
115+ topbits |= 04000;
116+ break;
117+ case 'g':
118+ where |= 0010;
119+ topbits |= 02000;
120+ break;
121+ case 'o':
122+ where |= 0001;
123+ break;
124+ case 'a':
125+ where |= 0111;
126+ break;
127+ case '+':
128+ op = CHMOD_ADD;
548aa120 129+ state = STATE_2ND_HALF;
2ece3e77
WD
130+ break;
131+ case '-':
132+ op = CHMOD_SUB;
548aa120 133+ state = STATE_2ND_HALF;
2ece3e77
WD
134+ break;
135+ case '=':
136+ op = CHMOD_EQ;
548aa120 137+ state = STATE_2ND_HALF;
2ece3e77
WD
138+ break;
139+ default:
548aa120 140+ state = STATE_ERROR;
2ece3e77
WD
141+ break;
142+ }
143+ } else {
144+ switch (*modestr) {
145+ case 'r':
146+ what |= 4;
147+ break;
148+ case 'w':
149+ what |= 2;
150+ break;
151+ case 'X':
548aa120 152+ flags |= FLAG_X_KEEP;
2ece3e77
WD
153+ /* FALL THROUGH */
154+ case 'x':
155+ what |= 1;
156+ break;
157+ case 's':
158+ if (topbits)
bd006671 159+ topoct |= topbits;
2ece3e77 160+ else
bd006671 161+ topoct = 04000;
2ece3e77
WD
162+ break;
163+ case 't':
164+ topoct |= 01000;
165+ break;
166+ default:
548aa120 167+ state = STATE_ERROR;
2ece3e77
WD
168+ break;
169+ }
bd006671 170+ }
2ece3e77 171+ modestr++;
bd006671
MP
172+ }
173+
548aa120 174+ if (state == STATE_ERROR) {
bd006671 175+ free_chmod_mode(first_mode);
2ece3e77 176+ first_mode = NULL;
bd006671
MP
177+ }
178+ return first_mode;
179+}
180+
181+
2ece3e77
WD
182+/* Takes an existing file permission and a list of AND/OR changes, and
183+ * create a new permissions. */
548aa120 184+int tweak_mode(int mode, struct chmod_mode_struct *chmod_modes)
bd006671 185+{
548aa120
WD
186+ int IsX = mode & 0111;
187+ int NonPerm = mode & ~07777;
188+
189+ for ( ; chmod_modes; chmod_modes = chmod_modes->next) {
190+ if ((chmod_modes->flags & FLAG_DIRS_ONLY) && !S_ISDIR(NonPerm))
191+ continue;
192+ if ((chmod_modes->flags & FLAG_FILES_ONLY) && S_ISDIR(NonPerm))
193+ continue;
194+ mode &= chmod_modes->ModeAND;
195+ if ((chmod_modes->flags & FLAG_X_KEEP) && !IsX && !S_ISDIR(NonPerm))
196+ mode |= chmod_modes->ModeOR & ~0111;
2ece3e77 197+ else
548aa120 198+ mode |= chmod_modes->ModeOR;
bd006671
MP
199+ }
200+
548aa120 201+ return mode | NonPerm;
bd006671
MP
202+}
203+
2ece3e77
WD
204+/* Free the linked list created by parse_chmod. */
205+int free_chmod_mode(struct chmod_mode_struct *chmod_modes)
bd006671
MP
206+{
207+ struct chmod_mode_struct *next;
208+
209+ while (chmod_modes) {
210+ next = chmod_modes->next;
211+ free(chmod_modes);
212+ chmod_modes = next;
213+ }
214+ return 0;
215+}
489b0a72 216--- orig/flist.c 2005-08-17 06:45:07
a6587818 217+++ flist.c 2004-09-18 01:51:11
36bbf3d1 218@@ -62,6 +62,8 @@ extern struct file_list *the_file_list;
33840e0c
WD
219
220 extern char curr_dir[MAXPATHLEN];
bd006671
MP
221
222+extern struct chmod_mode_struct *chmod_modes;
223+
4f9b6a01
WD
224 extern struct filter_list_struct filter_list;
225 extern struct filter_list_struct server_filter_list;
ac23c334 226
388bf7cc 227@@ -883,7 +885,10 @@ skip_filters:
2ece3e77
WD
228 file->flags = flags;
229 file->modtime = st.st_mtime;
230 file->length = st.st_size;
231- file->mode = st.st_mode;
46b46be7 232+ if (chmod_modes && am_sender && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)))
2ece3e77
WD
233+ file->mode = tweak_mode(st.st_mode, chmod_modes);
234+ else
235+ file->mode = st.st_mode;
236 file->uid = st.st_uid;
237 file->gid = st.st_gid;
bd006671 238
489b0a72
WD
239--- orig/options.c 2005-08-27 21:11:26
240+++ options.c 2005-08-27 21:18:52
388bf7cc 241@@ -141,6 +141,7 @@ char *log_format = NULL;
bd006671
MP
242 char *password_file = NULL;
243 char *rsync_path = RSYNC_PATH;
244 char *backup_dir = NULL;
245+char *chmod_mode = NULL;
2ece3e77 246 char backup_dir_buf[MAXPATHLEN];
4c1f2ca5 247 int rsync_port = 0;
c59d6641 248 int compare_dest = 0;
388bf7cc 249@@ -160,6 +161,8 @@ int list_only = 0;
9be39c35
WD
250 #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
251 char *batch_name = NULL;
bd006671
MP
252
253+struct chmod_mode_struct *chmod_modes = NULL;
2ece3e77
WD
254+
255 static int daemon_opt; /* sets am_daemon after option error-reporting */
ac23c334 256 static int F_option_cnt = 0;
2ece3e77 257 static int modify_window_set;
489b0a72 258@@ -289,6 +292,7 @@ void usage(enum logcode F)
bd006671 259 rprintf(F," -D, --devices preserve devices (root only)\n");
2ece3e77 260 rprintf(F," -t, --times preserve times\n");
9a21ad72 261 rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
548aa120 262+ rprintf(F," --chmod=CHMOD change destination permissions\n");
bd006671
MP
263 rprintf(F," -S, --sparse handle sparse files efficiently\n");
264 rprintf(F," -n, --dry-run show what would have been transferred\n");
45f1b149 265 rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n");
489b0a72
WD
266@@ -411,6 +415,7 @@ static struct poptOption long_options[]
267 {"no-relative", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 },
268 {"no-R", 0, POPT_ARG_VAL, &relative_paths, 0, 0, 0 },
269 {"no-implied-dirs", 0, POPT_ARG_VAL, &implied_dirs, 0, 0, 0 },
2ece3e77 270+ {"chmod", 0, POPT_ARG_STRING, &chmod_mode, 0, 0, 0 },
489b0a72
WD
271 {"ignore-times", 'I', POPT_ARG_NONE, &ignore_times, 0, 0, 0 },
272 {"size-only", 0, POPT_ARG_NONE, &size_only, 0, 0, 0 },
273 {"one-file-system", 'x', POPT_ARG_NONE, &one_file_system, 0, 0, 0 },
274@@ -1122,6 +1127,13 @@ int parse_arguments(int *argc, const cha
55173c4e
WD
275 if (make_backups && !backup_dir)
276 omit_dir_times = 1;
bd006671 277
2ece3e77
WD
278+ if (chmod_mode && !(chmod_modes = parse_chmod(chmod_mode))) {
279+ snprintf(err_buf, sizeof err_buf,
280+ "Invalid argument passed to chmod\n");
281+ rprintf(FERROR, "ERROR: %s", err_buf);
282+ return 0;
283+ }
284+
4e75164f 285 if (log_format) {
18ae7b87 286 if (log_format_has(log_format, 'i'))
33840e0c 287 log_format_has_i = 1;
489b0a72 288@@ -1507,6 +1519,11 @@ void server_options(char **args,int *arg
4c1f2ca5 289 }
7628f156
WD
290 }
291
bd006671
MP
292+ if (chmod_mode && !am_sender) {
293+ args[ac++] = "--chmod";
294+ args[ac++] = chmod_mode;
7628f156
WD
295+ }
296+
def2ace9
WD
297 if (files_from && (!am_sender || filesfrom_host)) {
298 if (filesfrom_host) {
7628f156 299 args[ac++] = "--files-from";
489b0a72 300--- orig/rsync.yo 2005-08-27 21:05:12
9a21ad72 301+++ rsync.yo 2005-01-24 01:48:43
489b0a72 302@@ -323,6 +323,7 @@ to the detailed description below for a
2ece3e77
WD
303 -D, --devices preserve devices (root only)
304 -t, --times preserve times
9a21ad72 305 -O, --omit-dir-times omit directories when preserving times
2ece3e77
WD
306+ --chmod=CHMOD change destination permissions
307 -S, --sparse handle sparse files efficiently
308 -n, --dry-run show what would have been transferred
45f1b149 309 -W, --whole-file copy files whole (without rsync algorithm)
489b0a72 310@@ -695,6 +696,14 @@ it is preserving modification times (see
a7219d20 311 the directories on the receiving side, it is a good idea to use bf(-O).
333b8af4 312 This option is inferred if you use bf(--backup) without bf(--backup-dir).
7628f156 313
bd006671 314+dit(bf(--chmod)) This options tells rsync to apply the listed "chmod" pattern
548aa120
WD
315+to the permission of the files on the destination. In addition to the normal
316+parsing rules specified in the chmod manpage, you can specify an item that
317+should only apply to a directory by prefixing it with a 'D', or specify an
318+item that should only apply to a file by prefixing it with a 'F'. For example:
319+
320+quote(--chmod=Dg+s,ug+w,Fo-w,+X)
7628f156 321+
bd006671
MP
322 dit(bf(-n, --dry-run)) This tells rsync to not do any file transfers,
323 instead it will just report the actions it would have taken.
7628f156 324
91a461d6
WD
325--- orig/testsuite/chmod-option.test 2005-07-09 15:49:59
326+++ testsuite/chmod-option.test 2005-07-09 15:49:59
327@@ -0,0 +1,44 @@
bd006671
MP
328+#! /bin/sh
329+
330+# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
331+
332+# This program is distributable under the terms of the GNU GPL (see
333+# COPYING).
334+
335+# Test that the --chmod option functions correctly.
336+
337+. $srcdir/testsuite/rsync.fns
338+
339+set -x
340+
341+# Build some files
342+
343+fromdir="$scratchdir/from"
344+todir="$scratchdir/to"
345+checkdir="$scratchdir/check"
346+
347+mkdir "$fromdir"
348+name1="$fromdir/name1"
349+name2="$fromdir/name2"
46b46be7
WD
350+dir1="$fromdir/dir1"
351+dir2="$fromdir/dir2"
bd006671
MP
352+echo "This is the file" > "$name1"
353+echo "This is the other file" > "$name2"
46b46be7 354+mkdir "$dir1" "$dir2"
bd006671 355+
46b46be7
WD
356+chmod 4700 "$name1" || test_skipped "Can't chmod"
357+chmod 700 "$dir1"
358+chmod 770 "$dir2"
bd006671
MP
359+
360+# Copy the files we've created over to another directory
2ece3e77 361+checkit "$RSYNC -avv \"$fromdir/\" \"$checkdir/\"" "$fromdir" "$checkdir"
bd006671
MP
362+
363+# And then manually make the changes which should occur
91a461d6 364+umask 002
46b46be7 365+chmod ug-s,a+rX "$checkdir"/*
91a461d6 366+chmod +w "$checkdir" "$checkdir"/dir*
bd006671 367+
91a461d6 368+checkit "$RSYNC -avv --chmod ug-s,a+rX,D+w \"$fromdir/\" \"$todir/\"" "$checkdir" "$todir"
bd006671 369+
2ece3e77 370+# The script would have aborted on error, so getting here means we've won.
bd006671 371+exit 0