The patches for 3.0.1pre1.
[rsync/rsync-patches.git] / usermap.diff
CommitLineData
409bd73e
WD
1This adds a --usermap and a --groupmap option. See the man page for
2more details.
4122278d 3
409bd73e
WD
4To use this patch, run these commands for a successful build:
5
6 patch -p1 <patches/usermap.diff
7 ./configure (optional if already run)
8 make
4122278d 9
cc3e685d
WD
10diff --git a/flist.c b/flist.c
11--- a/flist.c
12+++ b/flist.c
f9df736a 13@@ -70,6 +70,7 @@ extern int need_unsorted_flist;
d4dd2dd5 14 extern int unsort_ndx;
4122278d 15 extern struct stats stats;
c8a8b4a7 16 extern char *filesfrom_host;
d4dd2dd5 17+extern char *usermap, *groupmap;
4122278d 18
d4dd2dd5
WD
19 extern char curr_dir[MAXPATHLEN];
20
c0c7984e 21@@ -789,7 +790,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
409bd73e 22 uid = (uid_t)read_varint(f);
6a189d36 23 if (xflags & XMIT_USER_NAME_FOLLOWS)
409bd73e
WD
24 uid = recv_user_name(f, uid);
25- else if (inc_recurse && am_root && !numeric_ids)
f62e6e48 26+ else if (inc_recurse && am_root && (!numeric_ids || usermap))
409bd73e
WD
27 uid = match_uid(uid);
28 }
29 }
c0c7984e 30@@ -801,7 +802,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
761f1b71 31 gid_flags = 0;
6a189d36 32 if (xflags & XMIT_GROUP_NAME_FOLLOWS)
761f1b71 33 gid = recv_group_name(f, gid, &gid_flags);
409bd73e 34- else if (inc_recurse && (!am_root || !numeric_ids))
f62e6e48 35+ else if (inc_recurse && (!am_root || !numeric_ids || groupmap))
761f1b71 36 gid = match_gid(gid, &gid_flags);
409bd73e
WD
37 }
38 }
c0c7984e 39@@ -2142,8 +2143,13 @@ struct file_list *recv_file_list(int f)
4122278d
WD
40 int dstart, flags;
41 int64 start_read;
42
43- if (!first_flist)
44+ if (!first_flist) {
45 rprintf(FLOG, "receiving file list\n");
46+ if (usermap)
47+ parse_name_map(usermap, 1);
48+ if (groupmap)
49+ parse_name_map(groupmap, 0);
50+ }
51 if (show_filelist_p())
52 start_filelist_progress("receiving file list");
53 else if (inc_recurse && verbose && !am_server && !first_flist)
cc3e685d
WD
54diff --git a/options.c b/options.c
55--- a/options.c
56+++ b/options.c
c0c7984e 57@@ -167,6 +167,8 @@ char *rsync_path = RSYNC_PATH;
4122278d
WD
58 char *backup_dir = NULL;
59 char backup_dir_buf[MAXPATHLEN];
60 char *sockopts = NULL;
61+char *usermap = NULL;
62+char *groupmap = NULL;
63 int rsync_port = 0;
64 int compare_dest = 0;
65 int copy_dest = 0;
c0c7984e 66@@ -384,6 +386,8 @@ void usage(enum logcode F)
4122278d
WD
67 rprintf(F," --delay-updates put all updated files into place at transfer's end\n");
68 rprintf(F," -m, --prune-empty-dirs prune empty directory chains from the file-list\n");
69 rprintf(F," --numeric-ids don't map uid/gid values by user/group name\n");
70+ rprintf(F," --usermap=STRING custom username mapping\n");
71+ rprintf(F," --groupmap=STRING custom groupname mapping\n");
cc3e685d
WD
72 rprintf(F," --timeout=SECONDS set I/O timeout in seconds\n");
73 rprintf(F," --contimeout=SECONDS set daemon connection timeout in seconds\n");
4122278d 74 rprintf(F," -I, --ignore-times don't skip files that match in size and mod-time\n");
c0c7984e 75@@ -622,6 +626,8 @@ static struct poptOption long_options[] = {
c8a8b4a7 76 {"no-s", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
6cbbe66d
WD
77 {"numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 1, 0, 0 },
78 {"no-numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 0, 0, 0 },
4122278d
WD
79+ {"usermap", 0, POPT_ARG_STRING, &usermap, 0, 0, 0 },
80+ {"groupmap", 0, POPT_ARG_STRING, &groupmap, 0, 0, 0 },
81 {"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 },
6cbbe66d 82 {"no-timeout", 0, POPT_ARG_VAL, &io_timeout, 0, 0, 0 },
cc3e685d 83 {"contimeout", 0, POPT_ARG_INT, &connect_timeout, 0, 0, 0 },
c0c7984e 84@@ -1991,6 +1997,18 @@ void server_options(char **args, int *argc_p)
d4dd2dd5 85 args[ac++] = "--use-qsort";
4122278d
WD
86
87 if (am_sender) {
88+ if (usermap) {
f62e6e48 89+ if (asprintf(&arg, "--usermap=%s", usermap) < 0)
4122278d
WD
90+ goto oom;
91+ args[ac++] = arg;
92+ }
93+
94+ if (groupmap) {
f62e6e48 95+ if (asprintf(&arg, "--groupmap=%s", groupmap) < 0)
4122278d
WD
96+ goto oom;
97+ args[ac++] = arg;
98+ }
99+
100 if (ignore_existing)
101 args[ac++] = "--ignore-existing";
102
cc3e685d
WD
103diff --git a/rsync.yo b/rsync.yo
104--- a/rsync.yo
105+++ b/rsync.yo
106@@ -378,6 +378,8 @@ to the detailed description below for a complete description. verb(
4122278d
WD
107 --delay-updates put all updated files into place at end
108 -m, --prune-empty-dirs prune empty directory chains from file-list
109 --numeric-ids don't map uid/gid values by user/group name
110+ --usermap=STRING custom username mapping
111+ --groupmap=STRING custom groupname mapping
cc3e685d
WD
112 --timeout=SECONDS set I/O timeout in seconds
113 --contimeout=SECONDS set daemon connection timeout in seconds
4122278d 114 -I, --ignore-times don't skip files that match size and time
c0c7984e 115@@ -1602,6 +1604,46 @@ from the source system is used instead. See also the comments on the
4122278d
WD
116 the chroot setting affects rsync's ability to look up the names of the
117 users and groups and what you can do about it.
118
119+dit(bf(--usermap=STRING, --groupmap=STRING)) These options allow you to
409bd73e
WD
120+specify users and groups that should be mapped to other values by the
121+receiving side. The bf(STRING) is one or more bf(FROM):bf(TO) pairs of
122+values separated by commas. Any matching bf(FROM) value from the sender is
123+replaced with a bf(TO) value from the receiver. You may specify usernames
124+or user IDs for the bf(FROM) and bf(TO) values, and the bf(FROM) value may
125+also be a wild-card string, which will be matched against the sender's
f62e6e48 126+names (wild-cards do NOT match against ID numbers). For example:
409bd73e 127+
f62e6e48 128+verb( --usermap=0:bin,wayne:admin,*:nobody --groupmap=usr:1,1:usr)
409bd73e
WD
129+
130+The first match in the list is the one that is used. You should not use
131+multiple options of the same type, but instead include all the user
132+mappings you need separated by commas to a single bf(--usermap) option,
133+and likewise for groups with the bf(--groupmap) option.
4122278d 134+
9405aad3 135+Note that the sender's name for the 0 user and group are not transmitted
409bd73e
WD
136+to the receiver, so you should either match these values using a 0, or use
137+the names in effect on the receiving side. All other bf(FROM) names match
9405aad3
WD
138+those in use on the sending side. All bf(TO) names match those in use on
139+the receiving side.
4122278d 140+
9405aad3
WD
141+Any IDs that do not have name on the sending side are treaded as having an
142+empty name for the purpose of matching. This allows them to be matched via
143+a "*" as well as an empty name mapping. For instance:
144+
145+verb( --usermap=:nobody --groupmap=*:nobody)
146+
147+When the bf(--numeric-ids) option is used,the sender does not send any
148+names, so all the IDs are treaded as having an empty name. This means that
149+you will need to specify numeric bf(FROM) values if you want to map these
150+nameless IDs to different values.
4122278d 151+
409bd73e
WD
152+For the bf(--usermap) option to have any effect, the bf(-o) (bf(--owner))
153+option must be used (or implied), and the receiver will need to be running
9405aad3 154+as a super-user (see also the bf(--fake-super) option). For the bf(--groupmap)
409bd73e
WD
155+option to have any effect, the bf(-g) (bf(--groups)) option must be used
156+(or implied), and the receiver will need to have permissions to set that
157+group.
4122278d
WD
158+
159 dit(bf(--timeout=TIMEOUT)) This option allows you to set a maximum I/O
160 timeout in seconds. If no data is transferred for the specified time
161 then rsync will exit. The default is 0, which means no timeout.
cc3e685d
WD
162diff --git a/uidlist.c b/uidlist.c
163--- a/uidlist.c
164+++ b/uidlist.c
c82285d5
WD
165@@ -24,6 +24,7 @@
166 * are special. */
167
168 #include "rsync.h"
169+#include "ifuncs.h"
170 #include "io.h"
171
172 extern int verbose;
173@@ -32,6 +33,8 @@ extern int preserve_uid;
4122278d
WD
174 extern int preserve_gid;
175 extern int preserve_acls;
176 extern int numeric_ids;
409bd73e
WD
177+extern char *usermap;
178+extern char *groupmap;
4122278d 179
6a189d36
WD
180 #ifdef HAVE_GETGROUPS
181 # ifndef GETGROUPS_T
c82285d5 182@@ -41,6 +44,9 @@ extern int numeric_ids;
6a189d36
WD
183
184 #define GID_NONE ((gid_t)-1)
185
186+#define NFLAGS_WILD_NAME_MATCH (1<<0)
187+#define NFLAGS_NAME_MATCH (1<<1)
188+
4122278d
WD
189 struct idlist {
190 struct idlist *next;
4c15e800 191 const char *name;
c82285d5 192@@ -48,8 +54,8 @@ struct idlist {
6a189d36 193 uint16 flags;
4122278d
WD
194 };
195
196-static struct idlist *uidlist;
197-static struct idlist *gidlist;
198+static struct idlist *uidlist, *uidmap;
199+static struct idlist *gidlist, *gidmap;
200
4c15e800 201 static struct idlist *add_to_list(struct idlist **root, id_t id, const char *name,
6a189d36 202 id_t id2, uint16 flags)
4c15e800 203@@ -84,22 +90,6 @@ static const char *gid_to_name(gid_t gid)
9405aad3
WD
204 return NULL;
205 }
206
85096e5e 207-static uid_t map_uid(uid_t id, const char *name)
9405aad3
WD
208-{
209- uid_t uid;
210- if (id != 0 && name_to_uid(name, &uid))
211- return uid;
212- return id;
213-}
214-
85096e5e 215-static gid_t map_gid(gid_t id, const char *name)
9405aad3
WD
216-{
217- gid_t gid;
218- if (id != 0 && name_to_gid(name, &gid))
219- return gid;
220- return id;
221-}
222-
223 static int is_in_group(gid_t gid)
224 {
225 #ifdef HAVE_GETGROUPS
c82285d5 226@@ -159,34 +149,49 @@ static int is_in_group(gid_t gid)
9405aad3
WD
227 #endif
228 }
229
230-/* Add a uid to the list of uids. Only called on receiving side. */
85096e5e 231-static struct idlist *recv_add_uid(uid_t id, const char *name)
9405aad3 232+/* Add a uid/gid to its list of ids. Only called on receiving side. */
85096e5e 233+static struct idlist *recv_add_id(struct idlist **idmap_ptr, id_t id, const char *name)
4122278d
WD
234 {
235- uid_t id2 = name ? map_uid(id, name) : id;
236 struct idlist *node;
6a189d36 237+ id_t id2;
409bd73e 238
6a189d36 239- node = add_to_list(&uidlist, id, name, id2, 0);
409bd73e
WD
240+ if (!name)
241+ name = "";
4122278d 242
9405aad3 243- if (verbose > 3) {
6a189d36
WD
244- rprintf(FINFO, "uid %u(%s) maps to %u\n",
245- (unsigned)id, name ? name : "", (unsigned)id2);
9405aad3 246+ for (node = *idmap_ptr; node; node = node->next) {
6a189d36 247+ if (node->flags & NFLAGS_WILD_NAME_MATCH) {
409bd73e
WD
248+ if (!wildmatch(node->name, name))
249+ continue;
6a189d36 250+ } else if (node->flags & NFLAGS_NAME_MATCH) {
409bd73e
WD
251+ if (strcmp(node->name, name) != 0)
252+ continue;
6a189d36 253+ } else {
9405aad3 254+ if (node->id != id)
409bd73e 255+ continue;
4122278d 256+ }
409bd73e 257+ break;
9405aad3 258 }
409bd73e
WD
259+ if (node)
260+ id2 = node->id2;
9405aad3
WD
261+ else if (*name && id) {
262+ if (idmap_ptr == &uidmap) {
263+ uid_t uid;
6a189d36 264+ id2 = name_to_uid(name, &uid) ? uid : id;
9405aad3
WD
265+ } else {
266+ gid_t gid;
6a189d36 267+ id2 = name_to_gid(name, &gid) ? gid : id;
9405aad3
WD
268+ }
269+ } else
409bd73e 270+ id2 = id;
4122278d 271
6a189d36 272- return node;
9405aad3
WD
273-}
274-
275-/* Add a gid to the list of gids. Only called on receiving side. */
85096e5e 276-static struct idlist *recv_add_gid(gid_t id, const char *name)
9405aad3
WD
277-{
278- gid_t id2 = name ? map_gid(id, name) : id;
279- struct idlist *node;
280-
6a189d36
WD
281- node = add_to_list(&gidlist, id, name, id2,
282- !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0);
283+ node = add_to_list(idmap_ptr, id, *name ? name : NULL, id2,
284+ !am_root && idmap_ptr == &gidmap
285+ && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0);
409bd73e
WD
286
287 if (verbose > 3) {
6a189d36
WD
288- rprintf(FINFO, "gid %u(%s) maps to %u\n",
289- (unsigned)id, name ? name : "", (unsigned)id2);
290+ rprintf(FINFO, "%sid %u(%s) maps to %u\n",
291+ idmap_ptr == &uidmap ? "u" : "g",
292+ (unsigned)id, name, (unsigned)id2);
409bd73e
WD
293 }
294
6a189d36 295 return node;
85096e5e 296@@ -195,12 +200,9 @@ static struct idlist *recv_add_gid(gid_t id, const char *name)
409bd73e
WD
297 /* this function is a definate candidate for a faster algorithm */
298 uid_t match_uid(uid_t uid)
299 {
300- static uid_t last_in, last_out;
301+ static uid_t last_in = -1, last_out = -1;
4122278d
WD
302 struct idlist *list;
303
304- if (uid == 0)
305- return 0;
306-
307 if (uid == last_in)
308 return last_out;
309
c82285d5 310@@ -208,10 +210,13 @@ uid_t match_uid(uid_t uid)
6a189d36
WD
311
312 for (list = uidlist; list; list = list->next) {
313 if (list->id == uid)
314- return last_out = list->id2;
315+ break;
409bd73e 316 }
4122278d 317
409bd73e 318- return last_out = uid;
6a189d36
WD
319+ if (!list)
320+ list = recv_add_id(&uidmap, uid, NULL);
321+
322+ return last_out = list->id2;
409bd73e 323 }
4122278d 324
6a189d36 325 gid_t match_gid(gid_t gid, uint16 *flags_ptr)
cc3e685d 326@@ -227,7 +232,7 @@ gid_t match_gid(gid_t gid, uint16 *flags_ptr)
761f1b71
WD
327 break;
328 }
329 if (!list)
8f1c03c8
WD
330- list = recv_add_gid(gid, NULL);
331+ list = recv_add_id(&gidmap, gid, NULL);
761f1b71 332 last = list;
9405aad3
WD
333 }
334
85096e5e
WD
335@@ -320,7 +325,7 @@ uid_t recv_user_name(int f, uid_t uid)
336 free(name);
337 name = NULL;
338 }
6a189d36
WD
339- node = recv_add_uid(uid, name); /* node keeps name's memory */
340+ node = recv_add_id(&uidmap, uid, name); /* node keeps name's memory */
341 return node->id2;
9405aad3
WD
342 }
343
85096e5e
WD
344@@ -336,7 +341,7 @@ gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr)
345 free(name);
346 name = NULL;
347 }
6a189d36
WD
348- node = recv_add_gid(gid, name); /* node keeps name's memory */
349+ node = recv_add_id(&gidmap, gid, name); /* node keeps name's memory */
350 if (flags_ptr && node->flags & FLAG_SKIP_GROUP)
351 *flags_ptr |= FLAG_SKIP_GROUP;
352 return node->id2;
85096e5e 353@@ -363,17 +368,93 @@ void recv_id_list(int f, struct file_list *flist)
4122278d 354
409bd73e
WD
355 /* Now convert all the uids/gids from sender values to our values. */
356 #ifdef SUPPORT_ACLS
357- if (preserve_acls && !numeric_ids)
6a189d36 358+ if (preserve_acls && (!numeric_ids || usermap || groupmap))
409bd73e
WD
359 match_acl_ids();
360 #endif
361- if (am_root && preserve_uid && !numeric_ids) {
362+ if (am_root && preserve_uid && (!numeric_ids || usermap)) {
9c85142a 363 for (i = 0; i < flist->used; i++)
6a189d36 364 F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i]));
409bd73e
WD
365 }
366- if (preserve_gid && (!am_root || !numeric_ids)) {
367+ if (preserve_gid && (!am_root || !numeric_ids || groupmap)) {
9c85142a 368 for (i = 0; i < flist->used; i++) {
6a189d36
WD
369 F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]),
370 &flist->files[i]->flags);
371 }
4122278d
WD
372 }
373 }
374+
375+void parse_name_map(char *map, int usernames)
376+{
9405aad3
WD
377+ struct idlist **idmap_ptr = usernames ? &uidmap : &gidmap;
378+ struct idlist **idlist_ptr = usernames ? &uidlist : &gidlist;
4122278d 379+ char *colon, *end, *cp = map + strlen(map);
6a189d36
WD
380+ id_t id1, id2;
381+ uint16 flags;
4122278d
WD
382+
383+ while (1) {
384+ end = cp;
385+ while (cp > map && cp[-1] != ',') cp--;
386+ if (!(colon = strchr(cp, ':'))) {
387+ rprintf(FERROR, "No colon found in --%smap: %s\n",
388+ usernames ? "user" : "group", cp);
389+ exit_cleanup(RERR_SYNTAX);
390+ }
391+ *colon = '\0';
392+
393+ if (isDigit(cp)) {
394+ if (strspn(cp, "0123456789") != (size_t)(colon - cp)) {
395+ bad_number:
396+ rprintf(FERROR, "Invalid number in --%smap: %s\n",
397+ usernames ? "user" : "group", cp);
398+ exit_cleanup(RERR_SYNTAX);
399+ }
6a189d36 400+ flags = 0;
4122278d 401+ id1 = atoi(cp);
6a189d36
WD
402+ } else if (strpbrk(cp, "*[?")) {
403+ flags = NFLAGS_WILD_NAME_MATCH;
404+ id1 = 0;
405+ } else {
406+ flags = NFLAGS_NAME_MATCH;
407+ id1 = 0;
408+ }
4122278d
WD
409+
410+ if (isDigit(colon+1)) {
411+ if (strspn(colon+1, "0123456789") != (size_t)(end - colon - 1)) {
412+ cp = colon+1;
413+ goto bad_number;
414+ }
415+ id2 = atoi(colon+1);
416+ } else {
417+ if (usernames) {
418+ uid_t uid;
9405aad3
WD
419+ if (!name_to_uid(colon+1, &uid)) {
420+ bad_name:
421+ rprintf(FERROR,
422+ "Invalid name in --%smap: %s\n",
423+ usernames ? "user" : "group",
424+ colon+1);
425+ exit_cleanup(RERR_SYNTAX);
426+ }
6a189d36 427+ id2 = uid;
4122278d
WD
428+ } else {
429+ gid_t gid;
9405aad3
WD
430+ if (!name_to_gid(colon+1, &gid))
431+ goto bad_name;
6a189d36 432+ id2 = gid;
4122278d
WD
433+ }
434+ }
435+
6a189d36
WD
436+ add_to_list(idmap_ptr, id1, flags ? cp : NULL, id2, flags);
437+ if (numeric_ids && !flags)
438+ add_to_list(idlist_ptr, id1, NULL, id2, flags);
4122278d
WD
439+
440+ if (cp == map)
441+ break;
442+
443+ *--cp = '\0'; /* replace comma */
444+ }
409bd73e 445+
85096e5e
WD
446+ /* The 0 user/group doesn't get its name sent, so add it explicitly. */
447+ recv_add_id(idmap_ptr, 0,
448+ numeric_ids ? NULL : usernames ? uid_to_name(0) : gid_to_name(0));
4122278d 449+}