Use "use warnings" rather than -w on the #! line.
[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
ae306a29 13@@ -71,6 +71,7 @@ extern int sender_symlink_iconv;
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
ae306a29 21@@ -767,7 +768,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 }
ae306a29 30@@ -779,7 +780,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 }
bf1bd9d4 39@@ -2248,8 +2249,13 @@ struct file_list *recv_file_list(int f)
4122278d 40 int64 start_read;
bf1bd9d4 41 int save_verbose = verbose;
4122278d
WD
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;
abd3adb8 66@@ -385,6 +387,9 @@ 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");
6ac9ce9a 72+ rprintf(F," --chown=USER:GROUP simple username/groupname mapping\n");
cc3e685d
WD
73 rprintf(F," --timeout=SECONDS set I/O timeout in seconds\n");
74 rprintf(F," --contimeout=SECONDS set daemon connection timeout in seconds\n");
4122278d 75 rprintf(F," -I, --ignore-times don't skip files that match in size and mod-time\n");
abd3adb8 76@@ -447,7 +452,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
ab5ffac0
WD
77 OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP,
78 OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
79 OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
80- OPT_NO_D, OPT_APPEND, OPT_NO_ICONV,
6ac9ce9a 81+ OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN,
ab5ffac0
WD
82 OPT_SERVER, OPT_REFUSED_BASE = 9000};
83
84 static struct poptOption long_options[] = {
abd3adb8 85@@ -623,6 +628,9 @@ static struct poptOption long_options[] = {
c8a8b4a7 86 {"no-s", 0, POPT_ARG_VAL, &protect_args, 0, 0, 0},
6cbbe66d
WD
87 {"numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 1, 0, 0 },
88 {"no-numeric-ids", 0, POPT_ARG_VAL, &numeric_ids, 0, 0, 0 },
ab5ffac0
WD
89+ {"usermap", 0, POPT_ARG_STRING, 0, OPT_USERMAP, 0, 0 },
90+ {"groupmap", 0, POPT_ARG_STRING, 0, OPT_GROUPMAP, 0, 0 },
6ac9ce9a 91+ {"chown", 0, POPT_ARG_STRING, 0, OPT_CHOWN, 0, 0 },
4122278d 92 {"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 },
6cbbe66d 93 {"no-timeout", 0, POPT_ARG_VAL, &io_timeout, 0, 0, 0 },
cc3e685d 94 {"contimeout", 0, POPT_ARG_INT, &connect_timeout, 0, 0, 0 },
abd3adb8 95@@ -1229,6 +1237,43 @@ int parse_arguments(int *argc_p, const char ***argv_p)
ab5ffac0
WD
96 }
97 break;
98
99+ case OPT_USERMAP:
100+ if (usermap) {
101+ snprintf(err_buf, sizeof err_buf,
102+ "You can only specify --usermap once.\n");
103+ return 0;
104+ }
105+ usermap = (char *)poptGetOptArg(pc);
106+ break;
107+
108+ case OPT_GROUPMAP:
109+ if (groupmap) {
110+ snprintf(err_buf, sizeof err_buf,
111+ "You can only specify --groupmap once.\n");
112+ return 0;
113+ }
114+ groupmap = (char *)poptGetOptArg(pc);
115+ break;
6ac9ce9a
WD
116+
117+ case OPT_CHOWN:
118+ if (usermap || groupmap) {
119+ snprintf(err_buf, sizeof err_buf,
120+ "You can only specify --chown once.\n");
121+ return 0;
122+ } else {
123+ const char *chown = poptGetOptArg(pc);
124+ int len;
125+ if ((arg = strchr(chown, ':')) != NULL) {
126+ if (arg[1] && asprintf(&groupmap, "*:%s", arg+1) < 0)
127+ out_of_memory("parse_arguments");
128+ len = arg - chown;
129+ } else
130+ len = strlen(chown);
131+ if (len && asprintf(&usermap, "*:%.*s", len, chown) < 0)
132+ out_of_memory("parse_arguments");
133+ }
134+ break;
ab5ffac0
WD
135+
136 case OPT_HELP:
137 usage(FINFO);
138 exit_cleanup(0);
abd3adb8 139@@ -2007,6 +2052,18 @@ void server_options(char **args, int *argc_p)
d4dd2dd5 140 args[ac++] = "--use-qsort";
4122278d
WD
141
142 if (am_sender) {
143+ if (usermap) {
f62e6e48 144+ if (asprintf(&arg, "--usermap=%s", usermap) < 0)
4122278d
WD
145+ goto oom;
146+ args[ac++] = arg;
147+ }
148+
149+ if (groupmap) {
f62e6e48 150+ if (asprintf(&arg, "--groupmap=%s", groupmap) < 0)
4122278d
WD
151+ goto oom;
152+ args[ac++] = arg;
153+ }
154+
155 if (ignore_existing)
156 args[ac++] = "--ignore-existing";
157
cc3e685d
WD
158diff --git a/rsync.yo b/rsync.yo
159--- a/rsync.yo
160+++ b/rsync.yo
abd3adb8 161@@ -382,6 +382,9 @@ to the detailed description below for a complete description. verb(
4122278d
WD
162 --delay-updates put all updated files into place at end
163 -m, --prune-empty-dirs prune empty directory chains from file-list
164 --numeric-ids don't map uid/gid values by user/group name
165+ --usermap=STRING custom username mapping
166+ --groupmap=STRING custom groupname mapping
6ac9ce9a 167+ --chown=USER:GROUP simple username/groupname mapping
cc3e685d
WD
168 --timeout=SECONDS set I/O timeout in seconds
169 --contimeout=SECONDS set daemon connection timeout in seconds
4122278d 170 -I, --ignore-times don't skip files that match size and time
abd3adb8 171@@ -1624,6 +1627,57 @@ from the source system is used instead. See also the comments on the
4122278d
WD
172 the chroot setting affects rsync's ability to look up the names of the
173 users and groups and what you can do about it.
174
175+dit(bf(--usermap=STRING, --groupmap=STRING)) These options allow you to
409bd73e
WD
176+specify users and groups that should be mapped to other values by the
177+receiving side. The bf(STRING) is one or more bf(FROM):bf(TO) pairs of
178+values separated by commas. Any matching bf(FROM) value from the sender is
179+replaced with a bf(TO) value from the receiver. You may specify usernames
180+or user IDs for the bf(FROM) and bf(TO) values, and the bf(FROM) value may
181+also be a wild-card string, which will be matched against the sender's
ab5ffac0
WD
182+names (wild-cards do NOT match against ID numbers, though see below for
183+why a '*' matches everything). You may instead specify a range of ID
184+numbers via an inclusive range: LOW-HIGH. For example:
409bd73e 185+
ab5ffac0 186+verb( --usermap=0-99:nobody,wayne:admin,*:normal --groupmap=usr:1,1:usr)
409bd73e 187+
ab5ffac0
WD
188+The first match in the list is the one that is used. You should specify
189+all your user mappings using a single bf(--usermap) option, and/or all
190+your group mappings using a single bf(--groupmap) option.
4122278d 191+
9405aad3 192+Note that the sender's name for the 0 user and group are not transmitted
409bd73e 193+to the receiver, so you should either match these values using a 0, or use
ab5ffac0
WD
194+the names in effect on the receiving side (typically "root"). All other
195+bf(FROM) names match those in use on the sending side. All bf(TO) names
196+match those in use on the receiving side.
4122278d 197+
ab5ffac0 198+Any IDs that do not have a name on the sending side are treated as having an
9405aad3 199+empty name for the purpose of matching. This allows them to be matched via
ab5ffac0 200+a "*" or using an empty name. For instance:
9405aad3
WD
201+
202+verb( --usermap=:nobody --groupmap=*:nobody)
203+
204+When the bf(--numeric-ids) option is used,the sender does not send any
ab5ffac0 205+names, so all the IDs are treated as having an empty name. This means that
9405aad3
WD
206+you will need to specify numeric bf(FROM) values if you want to map these
207+nameless IDs to different values.
4122278d 208+
409bd73e
WD
209+For the bf(--usermap) option to have any effect, the bf(-o) (bf(--owner))
210+option must be used (or implied), and the receiver will need to be running
9405aad3 211+as a super-user (see also the bf(--fake-super) option). For the bf(--groupmap)
409bd73e
WD
212+option to have any effect, the bf(-g) (bf(--groups)) option must be used
213+(or implied), and the receiver will need to have permissions to set that
214+group.
6ac9ce9a
WD
215+
216+dit(bf(--chown=USER:GROUP)) This option forces all files to be owned by USER
217+with group GROUP. This is a simpler interface than using bf(--usermap) and
218+bf(--groupmap) directly, but it is implemented using those options internally,
219+so you cannot mix them. If either the USER or GROUP is empty, no mapping for
220+the omitted user/group will occur. If GROUP is empty, the trailing colon may
221+be omitted, but if USER is empty, a leading colon must be supplied.
222+
223+If you specify "--chown=foo:bar, this is exactly the same as specifying
224+"--usermap=*:foo --groupmap=*:bar", only easier.
4122278d
WD
225+
226 dit(bf(--timeout=TIMEOUT)) This option allows you to set a maximum I/O
227 timeout in seconds. If no data is transferred for the specified time
228 then rsync will exit. The default is 0, which means no timeout.
ae306a29
WD
229diff --git a/support/mapfrom b/support/mapfrom
230new file mode 100755
231--- /dev/null
232+++ b/support/mapfrom
233@@ -0,0 +1,5 @@
234+#!/usr/bin/perl
235+while (<>) {
236+ push @_, "$2:$1" if /^(\w+):[^:]+:(\d+)/;
237+}
238+print join(',', @_), "\n";
239diff --git a/support/mapto b/support/mapto
240new file mode 100755
241--- /dev/null
242+++ b/support/mapto
243@@ -0,0 +1,5 @@
244+#!/usr/bin/perl
245+while (<>) {
246+ push @_, "$1:$2" if /^(\w+):[^:]+:(\d+)/;
247+}
248+print join(',', @_), "\n";
cc3e685d
WD
249diff --git a/uidlist.c b/uidlist.c
250--- a/uidlist.c
251+++ b/uidlist.c
c82285d5
WD
252@@ -24,6 +24,7 @@
253 * are special. */
254
255 #include "rsync.h"
256+#include "ifuncs.h"
257 #include "io.h"
258
259 extern int verbose;
260@@ -32,6 +33,8 @@ extern int preserve_uid;
4122278d
WD
261 extern int preserve_gid;
262 extern int preserve_acls;
263 extern int numeric_ids;
409bd73e
WD
264+extern char *usermap;
265+extern char *groupmap;
4122278d 266
6a189d36
WD
267 #ifdef HAVE_GETGROUPS
268 # ifndef GETGROUPS_T
c82285d5 269@@ -41,6 +44,9 @@ extern int numeric_ids;
6a189d36
WD
270
271 #define GID_NONE ((gid_t)-1)
272
273+#define NFLAGS_WILD_NAME_MATCH (1<<0)
274+#define NFLAGS_NAME_MATCH (1<<1)
275+
4122278d
WD
276 struct idlist {
277 struct idlist *next;
4c15e800 278 const char *name;
c82285d5 279@@ -48,8 +54,8 @@ struct idlist {
6a189d36 280 uint16 flags;
4122278d
WD
281 };
282
283-static struct idlist *uidlist;
284-static struct idlist *gidlist;
285+static struct idlist *uidlist, *uidmap;
286+static struct idlist *gidlist, *gidmap;
287
4c15e800 288 static struct idlist *add_to_list(struct idlist **root, id_t id, const char *name,
6a189d36 289 id_t id2, uint16 flags)
4c15e800 290@@ -84,22 +90,6 @@ static const char *gid_to_name(gid_t gid)
9405aad3
WD
291 return NULL;
292 }
293
85096e5e 294-static uid_t map_uid(uid_t id, const char *name)
9405aad3
WD
295-{
296- uid_t uid;
297- if (id != 0 && name_to_uid(name, &uid))
298- return uid;
299- return id;
300-}
301-
85096e5e 302-static gid_t map_gid(gid_t id, const char *name)
9405aad3
WD
303-{
304- gid_t gid;
305- if (id != 0 && name_to_gid(name, &gid))
306- return gid;
307- return id;
308-}
309-
310 static int is_in_group(gid_t gid)
311 {
312 #ifdef HAVE_GETGROUPS
ab5ffac0 313@@ -159,34 +149,53 @@ static int is_in_group(gid_t gid)
9405aad3
WD
314 #endif
315 }
316
317-/* Add a uid to the list of uids. Only called on receiving side. */
85096e5e 318-static struct idlist *recv_add_uid(uid_t id, const char *name)
9405aad3 319+/* Add a uid/gid to its list of ids. Only called on receiving side. */
ab5ffac0
WD
320+static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idmap,
321+ id_t id, const char *name)
4122278d
WD
322 {
323- uid_t id2 = name ? map_uid(id, name) : id;
324 struct idlist *node;
ab5ffac0 325+ int flag;
6a189d36 326+ id_t id2;
409bd73e 327
6a189d36 328- node = add_to_list(&uidlist, id, name, id2, 0);
409bd73e
WD
329+ if (!name)
330+ name = "";
4122278d 331
9405aad3 332- if (verbose > 3) {
6a189d36
WD
333- rprintf(FINFO, "uid %u(%s) maps to %u\n",
334- (unsigned)id, name ? name : "", (unsigned)id2);
ab5ffac0 335+ for (node = idmap; node; node = node->next) {
6a189d36 336+ if (node->flags & NFLAGS_WILD_NAME_MATCH) {
409bd73e
WD
337+ if (!wildmatch(node->name, name))
338+ continue;
6a189d36 339+ } else if (node->flags & NFLAGS_NAME_MATCH) {
409bd73e
WD
340+ if (strcmp(node->name, name) != 0)
341+ continue;
ab5ffac0
WD
342+ } else if (node->name) {
343+ if (id < node->id || id > (unsigned long)node->name)
344+ continue;
6a189d36 345+ } else {
9405aad3 346+ if (node->id != id)
409bd73e 347+ continue;
4122278d 348+ }
409bd73e 349+ break;
9405aad3 350 }
409bd73e
WD
351+ if (node)
352+ id2 = node->id2;
9405aad3 353+ else if (*name && id) {
41b674b3 354+ if (idlist_ptr == &uidlist) {
9405aad3 355+ uid_t uid;
6a189d36 356+ id2 = name_to_uid(name, &uid) ? uid : id;
9405aad3
WD
357+ } else {
358+ gid_t gid;
6a189d36 359+ id2 = name_to_gid(name, &gid) ? gid : id;
9405aad3
WD
360+ }
361+ } else
409bd73e 362+ id2 = id;
4122278d 363
6a189d36 364- return node;
9405aad3
WD
365-}
366-
367-/* Add a gid to the list of gids. Only called on receiving side. */
85096e5e 368-static struct idlist *recv_add_gid(gid_t id, const char *name)
9405aad3
WD
369-{
370- gid_t id2 = name ? map_gid(id, name) : id;
371- struct idlist *node;
372-
6a189d36
WD
373- node = add_to_list(&gidlist, id, name, id2,
374- !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0);
41b674b3 375+ flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0;
ab5ffac0 376+ node = add_to_list(idlist_ptr, id, *name ? name : NULL, id2, flag);
409bd73e
WD
377
378 if (verbose > 3) {
6a189d36
WD
379- rprintf(FINFO, "gid %u(%s) maps to %u\n",
380- (unsigned)id, name ? name : "", (unsigned)id2);
381+ rprintf(FINFO, "%sid %u(%s) maps to %u\n",
41b674b3 382+ idlist_ptr == &uidlist ? "u" : "g",
6a189d36 383+ (unsigned)id, name, (unsigned)id2);
409bd73e
WD
384 }
385
6a189d36 386 return node;
ab5ffac0 387@@ -195,12 +204,9 @@ static struct idlist *recv_add_gid(gid_t id, const char *name)
409bd73e
WD
388 /* this function is a definate candidate for a faster algorithm */
389 uid_t match_uid(uid_t uid)
390 {
391- static uid_t last_in, last_out;
392+ static uid_t last_in = -1, last_out = -1;
4122278d
WD
393 struct idlist *list;
394
395- if (uid == 0)
396- return 0;
397-
398 if (uid == last_in)
399 return last_out;
400
ab5ffac0 401@@ -208,10 +214,13 @@ uid_t match_uid(uid_t uid)
6a189d36
WD
402
403 for (list = uidlist; list; list = list->next) {
404 if (list->id == uid)
405- return last_out = list->id2;
406+ break;
409bd73e 407 }
4122278d 408
409bd73e 409- return last_out = uid;
6a189d36 410+ if (!list)
ab5ffac0 411+ list = recv_add_id(&uidlist, uidmap, uid, NULL);
6a189d36
WD
412+
413+ return last_out = list->id2;
409bd73e 414 }
4122278d 415
6a189d36 416 gid_t match_gid(gid_t gid, uint16 *flags_ptr)
ab5ffac0 417@@ -227,7 +236,7 @@ gid_t match_gid(gid_t gid, uint16 *flags_ptr)
761f1b71
WD
418 break;
419 }
420 if (!list)
8f1c03c8 421- list = recv_add_gid(gid, NULL);
ab5ffac0 422+ list = recv_add_id(&gidlist, gidmap, gid, NULL);
761f1b71 423 last = list;
9405aad3
WD
424 }
425
ab5ffac0 426@@ -320,7 +329,7 @@ uid_t recv_user_name(int f, uid_t uid)
85096e5e
WD
427 free(name);
428 name = NULL;
429 }
6a189d36 430- node = recv_add_uid(uid, name); /* node keeps name's memory */
ab5ffac0 431+ node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */
6a189d36 432 return node->id2;
9405aad3
WD
433 }
434
ab5ffac0 435@@ -336,7 +345,7 @@ gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr)
85096e5e
WD
436 free(name);
437 name = NULL;
438 }
6a189d36 439- node = recv_add_gid(gid, name); /* node keeps name's memory */
ab5ffac0 440+ node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */
6a189d36
WD
441 if (flags_ptr && node->flags & FLAG_SKIP_GROUP)
442 *flags_ptr |= FLAG_SKIP_GROUP;
443 return node->id2;
ab5ffac0 444@@ -363,17 +372,103 @@ void recv_id_list(int f, struct file_list *flist)
4122278d 445
409bd73e
WD
446 /* Now convert all the uids/gids from sender values to our values. */
447 #ifdef SUPPORT_ACLS
448- if (preserve_acls && !numeric_ids)
6a189d36 449+ if (preserve_acls && (!numeric_ids || usermap || groupmap))
409bd73e
WD
450 match_acl_ids();
451 #endif
452- if (am_root && preserve_uid && !numeric_ids) {
453+ if (am_root && preserve_uid && (!numeric_ids || usermap)) {
9c85142a 454 for (i = 0; i < flist->used; i++)
6a189d36 455 F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i]));
409bd73e
WD
456 }
457- if (preserve_gid && (!am_root || !numeric_ids)) {
458+ if (preserve_gid && (!am_root || !numeric_ids || groupmap)) {
9c85142a 459 for (i = 0; i < flist->used; i++) {
6a189d36
WD
460 F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]),
461 &flist->files[i]->flags);
462 }
4122278d
WD
463 }
464 }
465+
466+void parse_name_map(char *map, int usernames)
467+{
9405aad3
WD
468+ struct idlist **idmap_ptr = usernames ? &uidmap : &gidmap;
469+ struct idlist **idlist_ptr = usernames ? &uidlist : &gidlist;
ab5ffac0
WD
470+ char *colon, *end, *name, *cp = map + strlen(map);
471+ id_t id1;
6a189d36 472+ uint16 flags;
4122278d 473+
ab5ffac0 474+ /* Parse the list in reverse, so the order in the struct is right. */
4122278d
WD
475+ while (1) {
476+ end = cp;
477+ while (cp > map && cp[-1] != ',') cp--;
478+ if (!(colon = strchr(cp, ':'))) {
479+ rprintf(FERROR, "No colon found in --%smap: %s\n",
480+ usernames ? "user" : "group", cp);
481+ exit_cleanup(RERR_SYNTAX);
482+ }
ab5ffac0
WD
483+ if (!colon[1]) {
484+ rprintf(FERROR, "No name found after colon --%smap: %s\n",
485+ usernames ? "user" : "group", cp);
486+ exit_cleanup(RERR_SYNTAX);
487+ }
4122278d
WD
488+ *colon = '\0';
489+
490+ if (isDigit(cp)) {
ab5ffac0
WD
491+ char *dash = strchr(cp, '-');
492+ if (strspn(cp, "0123456789-") != (size_t)(colon - cp)
493+ || (dash && (!dash[1] || strchr(dash+1, '-')))) {
4122278d
WD
494+ bad_number:
495+ rprintf(FERROR, "Invalid number in --%smap: %s\n",
496+ usernames ? "user" : "group", cp);
497+ exit_cleanup(RERR_SYNTAX);
498+ }
ab5ffac0
WD
499+ if (dash)
500+ name = (char *)atol(dash+1);
501+ else
502+ name = (char *)0;
6a189d36 503+ flags = 0;
ab5ffac0 504+ id1 = atol(cp);
6a189d36
WD
505+ } else if (strpbrk(cp, "*[?")) {
506+ flags = NFLAGS_WILD_NAME_MATCH;
ab5ffac0 507+ name = cp;
6a189d36
WD
508+ id1 = 0;
509+ } else {
510+ flags = NFLAGS_NAME_MATCH;
ab5ffac0 511+ name = cp;
6a189d36
WD
512+ id1 = 0;
513+ }
4122278d
WD
514+
515+ if (isDigit(colon+1)) {
516+ if (strspn(colon+1, "0123456789") != (size_t)(end - colon - 1)) {
517+ cp = colon+1;
518+ goto bad_number;
519+ }
ab5ffac0
WD
520+ add_to_list(idmap_ptr, id1, name, atol(colon+1), flags);
521+ } else if (usernames) {
522+ uid_t uid;
523+ if (name_to_uid(colon+1, &uid))
524+ add_to_list(idmap_ptr, id1, name, uid, flags);
525+ else {
526+ rprintf(FERROR,
527+ "Unknown --usermap name on receiver: %s\n",
528+ colon+1);
529+ }
4122278d 530+ } else {
ab5ffac0
WD
531+ gid_t gid;
532+ if (name_to_gid(colon+1, &gid))
533+ add_to_list(idmap_ptr, id1, name, gid, flags);
534+ else {
535+ rprintf(FERROR,
536+ "Unknown --groupmap name on receiver: %s\n",
537+ colon+1);
4122278d
WD
538+ }
539+ }
540+
4122278d
WD
541+ if (cp == map)
542+ break;
543+
544+ *--cp = '\0'; /* replace comma */
545+ }
409bd73e 546+
85096e5e 547+ /* The 0 user/group doesn't get its name sent, so add it explicitly. */
ab5ffac0 548+ recv_add_id(idlist_ptr, *idmap_ptr, 0,
85096e5e 549+ numeric_ids ? NULL : usernames ? uid_to_name(0) : gid_to_name(0));
4122278d 550+}