Commit | Line | Data |
---|---|---|
409bd73e WD |
1 | This adds a --usermap and a --groupmap option. See the man page for |
2 | more details. | |
4122278d | 3 | |
409bd73e WD |
4 | To 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 | |
12 | @@ -61,6 +61,8 @@ extern int copy_links; | |
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]; | |
409bd73e WD |
21 | @@ -741,7 +743,7 @@ static struct file_struct *recv_file_ent |
22 | uid = (uid_t)read_varint(f); | |
23 | if (flags & XMIT_USER_NAME_FOLLOWS) | |
24 | uid = recv_user_name(f, uid); | |
25 | - else if (inc_recurse && am_root && !numeric_ids) | |
26 | + else if (inc_recurse && am_root) | |
27 | uid = match_uid(uid); | |
28 | } | |
29 | } | |
30 | @@ -752,7 +754,7 @@ static struct file_struct *recv_file_ent | |
31 | gid = (gid_t)read_varint(f); | |
32 | if (flags & XMIT_GROUP_NAME_FOLLOWS) | |
33 | gid = recv_group_name(f, gid); | |
34 | - else if (inc_recurse && (!am_root || !numeric_ids)) | |
35 | + else if (inc_recurse) | |
36 | gid = match_gid(gid); | |
37 | } | |
38 | } | |
4122278d WD |
39 | @@ -1882,8 +1884,13 @@ struct file_list *recv_file_list(int f) |
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 | |
56 | @@ -156,6 +156,8 @@ char *rsync_path = RSYNC_PATH; | |
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; | |
65 | @@ -367,6 +369,8 @@ void usage(enum logcode F) | |
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"); | |
74 | @@ -568,6 +572,8 @@ static struct poptOption long_options[] | |
75 | {"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 }, | |
76 | {"from0", '0', POPT_ARG_NONE, &eol_nulls, 0, 0, 0}, | |
77 | {"numeric-ids", 0, POPT_ARG_NONE, &numeric_ids, 0, 0, 0 }, | |
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 }, | |
81 | {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 }, | |
82 | {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 }, | |
83 | @@ -1857,6 +1863,22 @@ void server_options(char **args,int *arg | |
84 | args[ac++] = "--numeric-ids"; | |
85 | ||
86 | if (am_sender) { | |
87 | + if (usermap) { | |
88 | + if (strchr(usermap, '\'') != NULL) | |
89 | + usermap = "INVALID"; | |
90 | + if (asprintf(&arg, "--usermap='%s'", usermap) < 0) | |
91 | + goto oom; | |
92 | + args[ac++] = arg; | |
93 | + } | |
94 | + | |
95 | + if (groupmap) { | |
96 | + if (strchr(groupmap, '\'') != NULL) | |
97 | + groupmap = "INVALID"; | |
98 | + if (asprintf(&arg, "--groupmap='%s'", groupmap) < 0) | |
99 | + goto oom; | |
100 | + args[ac++] = arg; | |
101 | + } | |
102 | + | |
103 | if (ignore_existing) | |
104 | args[ac++] = "--ignore-existing"; | |
105 | ||
106 | --- old/rsync.yo | |
107 | +++ new/rsync.yo | |
108 | @@ -361,6 +361,8 @@ to the detailed description below for a | |
109 | --delay-updates put all updated files into place at end | |
110 | -m, --prune-empty-dirs prune empty directory chains from file-list | |
111 | --numeric-ids don't map uid/gid values by user/group name | |
112 | + --usermap=STRING custom username mapping | |
113 | + --groupmap=STRING custom groupname mapping | |
114 | --timeout=TIME set I/O timeout in seconds | |
115 | -I, --ignore-times don't skip files that match size and time | |
116 | --size-only skip files that match in size | |
409bd73e | 117 | @@ -1445,6 +1447,42 @@ from the source system is used instead. |
4122278d WD |
118 | the chroot setting affects rsync's ability to look up the names of the |
119 | users and groups and what you can do about it. | |
120 | ||
121 | +dit(bf(--usermap=STRING, --groupmap=STRING)) These options allow you to | |
409bd73e WD |
122 | +specify users and groups that should be mapped to other values by the |
123 | +receiving side. The bf(STRING) is one or more bf(FROM):bf(TO) pairs of | |
124 | +values separated by commas. Any matching bf(FROM) value from the sender is | |
125 | +replaced with a bf(TO) value from the receiver. You may specify usernames | |
126 | +or user IDs for the bf(FROM) and bf(TO) values, and the bf(FROM) value may | |
127 | +also be a wild-card string, which will be matched against the sender's | |
128 | +names (it will NOT match IDs). For example: | |
129 | + | |
130 | +verb( --usermap=0:foo,bar:baz,*:nobody --groupmap=usr:1,1:usr) | |
131 | + | |
132 | +The first match in the list is the one that is used. You should not use | |
133 | +multiple options of the same type, but instead include all the user | |
134 | +mappings you need separated by commas to a single bf(--usermap) option, | |
135 | +and likewise for groups with the bf(--groupmap) option. | |
4122278d | 136 | + |
409bd73e WD |
137 | +Note that the sender's name for the 0 uid/gid is not actually transmitted |
138 | +to the receiver, so you should either match these values using a 0, or use | |
139 | +the names in effect on the receiving side. All other bf(FROM) names match | |
140 | +those in use on sending side. All bf(TO) names match those in use on the | |
141 | +receiving side. | |
4122278d | 142 | + |
409bd73e WD |
143 | +If the bf(--numeric-ids) option is used, you must specify numeric bf(FROM) |
144 | +values for them to be effective, since the sender does not send any names | |
145 | +when this option is used, and wild-card rules don't match nameless ID | |
146 | +values. The only exceptions to this are (1) the names for the 0 uid/gid, | |
147 | +which are always supplied by the receiving side, and (2) a "*", which | |
148 | +matches even an empty name. | |
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 | |
152 | +as root (see also the bf(--fake-root) option). For the bf(--groupmap) | |
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 | |
409bd73e | 162 | @@ -38,6 +38,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 WD |
168 | |
169 | struct idlist { | |
170 | struct idlist *next; | |
409bd73e | 171 | @@ -45,8 +47,8 @@ struct idlist { |
4122278d WD |
172 | char *name; |
173 | }; | |
174 | ||
175 | -static struct idlist *uidlist; | |
176 | -static struct idlist *gidlist; | |
177 | +static struct idlist *uidlist, *uidmap; | |
178 | +static struct idlist *gidlist, *gidmap; | |
179 | ||
180 | static struct idlist *add_to_list(struct idlist **root, int id, char *name, | |
181 | int id2) | |
409bd73e | 182 | @@ -158,14 +160,41 @@ static int is_in_group(gid_t gid) |
4122278d WD |
183 | /* Add a uid to the list of uids. Only called on receiving side. */ |
184 | static uid_t recv_add_uid(uid_t id, char *name) | |
185 | { | |
186 | - uid_t id2 = name ? map_uid(id, name) : id; | |
187 | struct idlist *node; | |
188 | + uid_t id2; | |
409bd73e WD |
189 | |
190 | - node = add_to_list(&uidlist, (int)id, name, (int)id2); | |
191 | + if (!name) | |
192 | + name = ""; | |
4122278d | 193 | + |
409bd73e WD |
194 | + for (node = uidmap; node; node = node->next) { |
195 | + switch (node->id) { | |
196 | + case -2: | |
197 | + if (!wildmatch(node->name, name)) | |
198 | + continue; | |
199 | + break; | |
200 | + case -1: | |
201 | + if (strcmp(node->name, name) != 0) | |
202 | + continue; | |
203 | + break; | |
204 | + default: | |
205 | + if (node->id != (int)id) | |
206 | + continue; | |
4122278d WD |
207 | + break; |
208 | + } | |
409bd73e WD |
209 | + break; |
210 | + } | |
211 | + if (node) | |
212 | + id2 = node->id2; | |
213 | + else if (*name && id) | |
214 | + id2 = map_uid(id, name); | |
215 | + else | |
216 | + id2 = id; | |
217 | + | |
218 | + node = add_to_list(&uidlist, (int)id, *name ? name : NULL, (int)id2); | |
4122278d | 219 | |
409bd73e WD |
220 | if (verbose > 3) { |
221 | rprintf(FINFO, "uid %d(%s) maps to %d\n", | |
222 | - (int)id, name ? name : "", (int)id2); | |
223 | + (int)id, name, (int)id2); | |
224 | } | |
4122278d | 225 | |
409bd73e WD |
226 | return id2; |
227 | @@ -174,16 +203,43 @@ static uid_t recv_add_uid(uid_t id, char | |
4122278d WD |
228 | /* Add a gid to the list of gids. Only called on receiving side. */ |
229 | static gid_t recv_add_gid(gid_t id, char *name) | |
230 | { | |
231 | - gid_t id2 = name ? map_gid(id, name) : id; | |
232 | struct idlist *node; | |
233 | + gid_t id2; | |
234 | + | |
409bd73e WD |
235 | + if (!name) |
236 | + name = ""; | |
237 | + | |
238 | + for (node = gidmap; node; node = node->next) { | |
239 | + switch (node->id) { | |
240 | + case -2: | |
241 | + if (!wildmatch(node->name, name)) | |
242 | + continue; | |
243 | + break; | |
244 | + case -1: | |
245 | + if (strcmp(node->name, name) != 0) | |
246 | + continue; | |
247 | + break; | |
248 | + default: | |
249 | + if (node->id != (int)id) | |
250 | + continue; | |
4122278d WD |
251 | + break; |
252 | + } | |
409bd73e WD |
253 | + break; |
254 | + } | |
255 | + if (node) | |
256 | + id2 = node->id2; | |
257 | + else if (*name && id) | |
258 | + id2 = map_gid(id, name); | |
259 | + else | |
260 | + id2 = id; | |
4122278d WD |
261 | |
262 | if (!am_root && !is_in_group(id2)) | |
263 | id2 = GID_NONE; | |
409bd73e WD |
264 | - node = add_to_list(&gidlist, (int)id, name, (int)id2); |
265 | + node = add_to_list(&gidlist, (int)id, name ? name : NULL, (int)id2); | |
266 | ||
267 | if (verbose > 3) { | |
268 | rprintf(FINFO, "gid %d(%s) maps to %d\n", | |
269 | - (int)id, name ? name : "", (int)id2); | |
270 | + (int)id, name, (int)id2); | |
271 | } | |
272 | ||
273 | return id2; | |
274 | @@ -192,12 +248,9 @@ static gid_t recv_add_gid(gid_t id, char | |
275 | /* this function is a definate candidate for a faster algorithm */ | |
276 | uid_t match_uid(uid_t uid) | |
277 | { | |
278 | - static uid_t last_in, last_out; | |
279 | + static uid_t last_in = -1, last_out = -1; | |
4122278d WD |
280 | struct idlist *list; |
281 | ||
282 | - if (uid == 0) | |
283 | - return 0; | |
284 | - | |
285 | if (uid == last_in) | |
286 | return last_out; | |
287 | ||
409bd73e WD |
288 | @@ -208,7 +261,7 @@ uid_t match_uid(uid_t uid) |
289 | return last_out = (uid_t)list->id2; | |
290 | } | |
4122278d | 291 | |
409bd73e WD |
292 | - return last_out = uid; |
293 | + return last_out = recv_add_uid(uid, NULL); | |
294 | } | |
4122278d | 295 | |
409bd73e WD |
296 | gid_t match_gid(gid_t gid) |
297 | @@ -344,15 +397,95 @@ void recv_uid_list(int f, struct file_li | |
4122278d | 298 | |
409bd73e WD |
299 | /* Now convert all the uids/gids from sender values to our values. */ |
300 | #ifdef SUPPORT_ACLS | |
301 | - if (preserve_acls && !numeric_ids) | |
302 | + if (preserve_acls && (!numeric_ids || usermap)) | |
303 | match_acl_ids(); | |
304 | #endif | |
305 | - if (am_root && preserve_uid && !numeric_ids) { | |
306 | + if (am_root && preserve_uid && (!numeric_ids || usermap)) { | |
307 | for (i = 0; i < flist->count; i++) | |
308 | F_OWNER(flist->files[i]) = match_uid(F_UID(flist->files[i])); | |
309 | } | |
310 | - if (preserve_gid && (!am_root || !numeric_ids)) { | |
311 | + if (preserve_gid && (!am_root || !numeric_ids || groupmap)) { | |
312 | for (i = 0; i < flist->count; i++) | |
4122278d WD |
313 | F_GROUP(flist->files[i]) = match_gid(F_GID(flist->files[i])); |
314 | } | |
315 | } | |
316 | + | |
317 | +void parse_name_map(char *map, int usernames) | |
318 | +{ | |
319 | + char *colon, *end, *cp = map + strlen(map); | |
320 | + int id1, id2; | |
321 | + | |
322 | + while (1) { | |
323 | + end = cp; | |
324 | + while (cp > map && cp[-1] != ',') cp--; | |
325 | + if (!(colon = strchr(cp, ':'))) { | |
326 | + rprintf(FERROR, "No colon found in --%smap: %s\n", | |
327 | + usernames ? "user" : "group", cp); | |
328 | + exit_cleanup(RERR_SYNTAX); | |
329 | + } | |
330 | + *colon = '\0'; | |
331 | + | |
332 | + if (isDigit(cp)) { | |
333 | + if (strspn(cp, "0123456789") != (size_t)(colon - cp)) { | |
334 | + bad_number: | |
335 | + rprintf(FERROR, "Invalid number in --%smap: %s\n", | |
336 | + usernames ? "user" : "group", cp); | |
337 | + exit_cleanup(RERR_SYNTAX); | |
338 | + } | |
339 | + id1 = atoi(cp); | |
340 | + } else if (strpbrk(cp, "*[?")) | |
341 | + id1 = -2; | |
342 | + else | |
343 | + id1 = -1; | |
344 | + | |
345 | + if (isDigit(colon+1)) { | |
346 | + if (strspn(colon+1, "0123456789") != (size_t)(end - colon - 1)) { | |
347 | + cp = colon+1; | |
348 | + goto bad_number; | |
349 | + } | |
350 | + id2 = atoi(colon+1); | |
351 | + } else { | |
352 | + if (usernames) { | |
353 | + uid_t uid; | |
354 | + if (name_to_uid(colon+1, &uid)) | |
355 | + id2 = (int)uid; | |
356 | + else | |
357 | + id2 = -1; | |
358 | + } else { | |
359 | + gid_t gid; | |
360 | + if (name_to_gid(colon+1, &gid)) | |
361 | + id2 = (int)gid; | |
362 | + else | |
363 | + id2 = -1; | |
364 | + } | |
365 | + if (id2 < 0) { | |
366 | + rprintf(FERROR, "Invalid name in --%smap: %s\n", | |
367 | + usernames ? "user" : "group", colon+1); | |
368 | + exit_cleanup(RERR_SYNTAX); | |
369 | + } | |
370 | + } | |
371 | + | |
409bd73e | 372 | + if (usernames) { |
4122278d | 373 | + add_to_list(&uidmap, id1, id1 < 0 ? cp : NULL, id2); |
409bd73e WD |
374 | + if (numeric_ids && id2 >= 0) |
375 | + add_to_list(&uidlist, id1, NULL, id2); | |
376 | + } else { | |
4122278d | 377 | + add_to_list(&gidmap, id1, id1 < 0 ? cp : NULL, id2); |
409bd73e WD |
378 | + if (numeric_ids && id2 >= 0) |
379 | + add_to_list(&gidlist, id1, NULL, id2); | |
380 | + } | |
4122278d WD |
381 | + |
382 | + if (cp == map) | |
383 | + break; | |
384 | + | |
385 | + *--cp = '\0'; /* replace comma */ | |
386 | + } | |
409bd73e WD |
387 | + |
388 | + if (usernames) { | |
389 | + char *name = uid_to_name(0); | |
390 | + recv_add_uid(0, name ? name : "root"); | |
391 | + } else { | |
392 | + char *name = gid_to_name(0); | |
393 | + recv_add_gid(0, name ? name : "root"); | |
394 | + } | |
4122278d | 395 | +} |