Added a variable to t_stub.c so that "make test" works.
[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+}
a6587818
WD
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;
9be39c35
WD
208 extern int orig_umask;
209 extern int list_only;
bd006671
MP
210
211+extern struct chmod_mode_struct *chmod_modes;
212+
54691942
WD
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;
a6587818 216@@ -869,7 +871,10 @@ skip_excludes:
2ece3e77
WD
217 file->flags = flags;
218 file->modtime = st.st_mtime;
219 file->length = st.st_size;
220- file->mode = st.st_mode;
46b46be7 221+ if (chmod_modes && am_sender && (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)))
2ece3e77
WD
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;
bd006671 227
a60267ab 228--- orig/options.c 2004-09-23 17:42:07
13bed3dd 229+++ options.c 2004-07-03 20:13:41
f635ed27 230@@ -126,6 +126,7 @@ char *log_format = NULL;
bd006671
MP
231 char *password_file = NULL;
232 char *rsync_path = RSYNC_PATH;
233 char *backup_dir = NULL;
234+char *chmod_mode = NULL;
2ece3e77 235 char backup_dir_buf[MAXPATHLEN];
bd006671 236 int rsync_port = RSYNC_PORT;
2ece3e77 237 int link_dest = 0;
f635ed27 238@@ -138,6 +139,8 @@ int list_only = 0;
9be39c35
WD
239 #define MAX_BATCH_NAME_LEN 256 /* Must be less than MAXPATHLEN-13 */
240 char *batch_name = NULL;
bd006671
MP
241
242+struct chmod_mode_struct *chmod_modes = NULL;
2ece3e77
WD
243+
244 static int daemon_opt; /* sets am_daemon after option error-reporting */
245 static int modify_window_set;
bd006671 246
f635ed27 247@@ -253,6 +256,7 @@ void usage(enum logcode F)
bd006671
MP
248 rprintf(F," -g, --group preserve group\n");
249 rprintf(F," -D, --devices preserve devices (root only)\n");
2ece3e77 250 rprintf(F," -t, --times preserve times\n");
548aa120 251+ rprintf(F," --chmod=CHMOD change destination permissions\n");
bd006671
MP
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");
f635ed27 255@@ -360,6 +364,7 @@ static struct poptOption long_options[]
2ece3e77
WD
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 },
a6587818 263@@ -804,6 +809,13 @@ int parse_arguments(int *argc, const cha
d5753a22 264 return 0;
2ece3e77 265 }
bd006671 266
2ece3e77
WD
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;
bd006671 276
a60267ab 277@@ -1087,6 +1099,11 @@ void server_options(char **args,int *arg
bd006671 278 args[ac++] = compare_dest;
7628f156
WD
279 }
280
bd006671
MP
281+ if (chmod_mode && !am_sender) {
282+ args[ac++] = "--chmod";
283+ args[ac++] = chmod_mode;
7628f156
WD
284+ }
285+
2ece3e77 286 if (files_from && (!am_sender || remote_filesfrom_file)) {
7628f156
WD
287 if (remote_filesfrom_file) {
288 args[ac++] = "--files-from";
a60267ab 289--- orig/rsync.yo 2004-09-24 16:42:30
13bed3dd 290+++ rsync.yo 2004-07-03 20:13:41
67c08134 291@@ -330,6 +330,7 @@ verb(
2ece3e77
WD
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
a60267ab 299@@ -610,6 +611,14 @@ cause the next transfer to behave as if
f635ed27
WD
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).
7628f156 302
bd006671 303+dit(bf(--chmod)) This options tells rsync to apply the listed "chmod" pattern
548aa120
WD
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)
7628f156 310+
bd006671
MP
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.
7628f156 313
f635ed27
WD
314--- orig/testsuite/chmod-option.test 2004-06-18 17:22:09
315+++ testsuite/chmod-option.test 2004-06-18 17:22:09
46b46be7 316@@ -0,0 +1,43 @@
bd006671
MP
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"
46b46be7
WD
339+dir1="$fromdir/dir1"
340+dir2="$fromdir/dir2"
bd006671
MP
341+echo "This is the file" > "$name1"
342+echo "This is the other file" > "$name2"
46b46be7 343+mkdir "$dir1" "$dir2"
bd006671 344+
46b46be7
WD
345+chmod 4700 "$name1" || test_skipped "Can't chmod"
346+chmod 700 "$dir1"
347+chmod 770 "$dir2"
bd006671
MP
348+
349+# Copy the files we've created over to another directory
2ece3e77 350+checkit "$RSYNC -avv \"$fromdir/\" \"$checkdir/\"" "$fromdir" "$checkdir"
bd006671
MP
351+
352+# And then manually make the changes which should occur
46b46be7
WD
353+chmod ug-s,a+rX "$checkdir"/*
354+chmod g+w "$checkdir" "$checkdir"/dir*
bd006671 355+
46b46be7 356+checkit "$RSYNC -avv --chmod ug-s,a+rX,Dg+w \"$fromdir/\" \"$todir/\"" "$checkdir" "$todir"
bd006671 357+
2ece3e77 358+# The script would have aborted on error, so getting here means we've won.
bd006671 359+exit 0