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