Commit | Line | Data |
---|---|---|
4122278d WD |
1 | This adds a --usermap and a --groupmap option. |
2 | ||
3 | TODO: make this work when --numeric-ids was specified. | |
4 | ||
5 | --- old/flist.c | |
6 | +++ new/flist.c | |
7 | @@ -61,6 +61,8 @@ extern int copy_links; | |
8 | extern int copy_unsafe_links; | |
9 | extern int protocol_version; | |
10 | extern int sanitize_paths; | |
11 | +extern char *usermap; | |
12 | +extern char *groupmap; | |
13 | extern struct stats stats; | |
14 | ||
15 | extern char curr_dir[MAXPATHLEN]; | |
16 | @@ -1882,8 +1884,13 @@ struct file_list *recv_file_list(int f) | |
17 | int dstart, flags; | |
18 | int64 start_read; | |
19 | ||
20 | - if (!first_flist) | |
21 | + if (!first_flist) { | |
22 | rprintf(FLOG, "receiving file list\n"); | |
23 | + if (usermap) | |
24 | + parse_name_map(usermap, 1); | |
25 | + if (groupmap) | |
26 | + parse_name_map(groupmap, 0); | |
27 | + } | |
28 | if (show_filelist_p()) | |
29 | start_filelist_progress("receiving file list"); | |
30 | else if (inc_recurse && verbose && !am_server && !first_flist) | |
31 | --- old/options.c | |
32 | +++ new/options.c | |
33 | @@ -156,6 +156,8 @@ char *rsync_path = RSYNC_PATH; | |
34 | char *backup_dir = NULL; | |
35 | char backup_dir_buf[MAXPATHLEN]; | |
36 | char *sockopts = NULL; | |
37 | +char *usermap = NULL; | |
38 | +char *groupmap = NULL; | |
39 | int rsync_port = 0; | |
40 | int compare_dest = 0; | |
41 | int copy_dest = 0; | |
42 | @@ -367,6 +369,8 @@ void usage(enum logcode F) | |
43 | rprintf(F," --delay-updates put all updated files into place at transfer's end\n"); | |
44 | rprintf(F," -m, --prune-empty-dirs prune empty directory chains from the file-list\n"); | |
45 | rprintf(F," --numeric-ids don't map uid/gid values by user/group name\n"); | |
46 | + rprintf(F," --usermap=STRING custom username mapping\n"); | |
47 | + rprintf(F," --groupmap=STRING custom groupname mapping\n"); | |
48 | rprintf(F," --timeout=TIME set I/O timeout in seconds\n"); | |
49 | rprintf(F," -I, --ignore-times don't skip files that match in size and mod-time\n"); | |
50 | rprintf(F," --size-only skip files that match in size\n"); | |
51 | @@ -568,6 +572,8 @@ static struct poptOption long_options[] | |
52 | {"files-from", 0, POPT_ARG_STRING, &files_from, 0, 0, 0 }, | |
53 | {"from0", '0', POPT_ARG_NONE, &eol_nulls, 0, 0, 0}, | |
54 | {"numeric-ids", 0, POPT_ARG_NONE, &numeric_ids, 0, 0, 0 }, | |
55 | + {"usermap", 0, POPT_ARG_STRING, &usermap, 0, 0, 0 }, | |
56 | + {"groupmap", 0, POPT_ARG_STRING, &groupmap, 0, 0, 0 }, | |
57 | {"timeout", 0, POPT_ARG_INT, &io_timeout, 0, 0, 0 }, | |
58 | {"rsh", 'e', POPT_ARG_STRING, &shell_cmd, 0, 0, 0 }, | |
59 | {"rsync-path", 0, POPT_ARG_STRING, &rsync_path, 0, 0, 0 }, | |
60 | @@ -1857,6 +1863,22 @@ void server_options(char **args,int *arg | |
61 | args[ac++] = "--numeric-ids"; | |
62 | ||
63 | if (am_sender) { | |
64 | + if (usermap) { | |
65 | + if (strchr(usermap, '\'') != NULL) | |
66 | + usermap = "INVALID"; | |
67 | + if (asprintf(&arg, "--usermap='%s'", usermap) < 0) | |
68 | + goto oom; | |
69 | + args[ac++] = arg; | |
70 | + } | |
71 | + | |
72 | + if (groupmap) { | |
73 | + if (strchr(groupmap, '\'') != NULL) | |
74 | + groupmap = "INVALID"; | |
75 | + if (asprintf(&arg, "--groupmap='%s'", groupmap) < 0) | |
76 | + goto oom; | |
77 | + args[ac++] = arg; | |
78 | + } | |
79 | + | |
80 | if (ignore_existing) | |
81 | args[ac++] = "--ignore-existing"; | |
82 | ||
83 | --- old/rsync.yo | |
84 | +++ new/rsync.yo | |
85 | @@ -361,6 +361,8 @@ to the detailed description below for a | |
86 | --delay-updates put all updated files into place at end | |
87 | -m, --prune-empty-dirs prune empty directory chains from file-list | |
88 | --numeric-ids don't map uid/gid values by user/group name | |
89 | + --usermap=STRING custom username mapping | |
90 | + --groupmap=STRING custom groupname mapping | |
91 | --timeout=TIME set I/O timeout in seconds | |
92 | -I, --ignore-times don't skip files that match size and time | |
93 | --size-only skip files that match in size | |
94 | @@ -1445,6 +1447,25 @@ from the source system is used instead. | |
95 | the chroot setting affects rsync's ability to look up the names of the | |
96 | users and groups and what you can do about it. | |
97 | ||
98 | +dit(bf(--usermap=STRING, --groupmap=STRING)) These options allow you to | |
99 | +specify user/group names and IDs that should be mapped to other values by | |
100 | +the receiving side. The bf(STRING) is one or more FROM:TO pairs of values | |
101 | +separated by commas. Any matching FROM value from the sender is replaced | |
102 | +with a TO value from the receiver. You may specify usernames or user IDs | |
103 | +for the FROM and TO values, and the FROM value may also be a wild-card | |
104 | +string, which will be matched against the sender's names (it will not match | |
105 | +IDs). For example: | |
106 | + | |
107 | + --usermap=0:foo,bar:baz,*:nobody --groupmap=root:1,1:root | |
108 | + | |
109 | +The first match in the list is the one that is used. | |
110 | + | |
111 | +For the bf(--usermap) option to be effective you will need to have specified | |
112 | +the bf(-o) (bf(--owner)) option and the receiver will need to be running as | |
113 | +root (see also the bf(--fake-root) option). For the bf(--groupmap) option | |
114 | +to be effective you will need to have specified the bf(-g) (bf(--groups)) | |
115 | +option, and the receiver will need to have permissions to set that group. | |
116 | + | |
117 | dit(bf(--timeout=TIMEOUT)) This option allows you to set a maximum I/O | |
118 | timeout in seconds. If no data is transferred for the specified time | |
119 | then rsync will exit. The default is 0, which means no timeout. | |
120 | --- old/uidlist.c | |
121 | +++ new/uidlist.c | |
122 | @@ -38,6 +38,7 @@ extern int preserve_uid; | |
123 | extern int preserve_gid; | |
124 | extern int preserve_acls; | |
125 | extern int numeric_ids; | |
126 | +extern int protocol_version; | |
127 | ||
128 | struct idlist { | |
129 | struct idlist *next; | |
130 | @@ -45,8 +46,8 @@ struct idlist { | |
131 | char *name; | |
132 | }; | |
133 | ||
134 | -static struct idlist *uidlist; | |
135 | -static struct idlist *gidlist; | |
136 | +static struct idlist *uidlist, *uidmap; | |
137 | +static struct idlist *gidlist, *gidmap; | |
138 | ||
139 | static struct idlist *add_to_list(struct idlist **root, int id, char *name, | |
140 | int id2) | |
141 | @@ -158,8 +159,33 @@ static int is_in_group(gid_t gid) | |
142 | /* Add a uid to the list of uids. Only called on receiving side. */ | |
143 | static uid_t recv_add_uid(uid_t id, char *name) | |
144 | { | |
145 | - uid_t id2 = name ? map_uid(id, name) : id; | |
146 | struct idlist *node; | |
147 | + uid_t id2; | |
148 | + | |
149 | + if (name) { | |
150 | + struct idlist *list; | |
151 | + for (list = uidmap; list; list = list->next) { | |
152 | + switch (list->id) { | |
153 | + case -2: | |
154 | + if (!wildmatch(list->name, name)) | |
155 | + continue; | |
156 | + break; | |
157 | + case -1: | |
158 | + if (strcmp(list->name, name) != 0) | |
159 | + continue; | |
160 | + break; | |
161 | + default: | |
162 | + if (list->id != (int)id) | |
163 | + continue; | |
164 | + break; | |
165 | + } | |
166 | + id2 = list->id2; | |
167 | + break; | |
168 | + } | |
169 | + if (!list) | |
170 | + id2 = id ? map_uid(id, name) : 0; /* don't map root */ | |
171 | + } else | |
172 | + id2 = id; | |
173 | ||
174 | node = add_to_list(&uidlist, (int)id, name, (int)id2); | |
175 | ||
176 | @@ -174,8 +200,33 @@ static uid_t recv_add_uid(uid_t id, char | |
177 | /* Add a gid to the list of gids. Only called on receiving side. */ | |
178 | static gid_t recv_add_gid(gid_t id, char *name) | |
179 | { | |
180 | - gid_t id2 = name ? map_gid(id, name) : id; | |
181 | struct idlist *node; | |
182 | + gid_t id2; | |
183 | + | |
184 | + if (name) { | |
185 | + struct idlist *list; | |
186 | + for (list = gidmap; list; list = list->next) { | |
187 | + switch (list->id) { | |
188 | + case -2: | |
189 | + if (!wildmatch(list->name, name)) | |
190 | + continue; | |
191 | + break; | |
192 | + case -1: | |
193 | + if (strcmp(list->name, name) != 0) | |
194 | + continue; | |
195 | + break; | |
196 | + default: | |
197 | + if (list->id != (int)id) | |
198 | + continue; | |
199 | + break; | |
200 | + } | |
201 | + id2 = list->id2; | |
202 | + break; | |
203 | + } | |
204 | + if (!list) | |
205 | + id2 = id ? map_gid(id, name) : 0; /* don't map root */ | |
206 | + } else | |
207 | + id2 = id; | |
208 | ||
209 | if (!am_root && !is_in_group(id2)) | |
210 | id2 = GID_NONE; | |
211 | @@ -195,9 +246,6 @@ uid_t match_uid(uid_t uid) | |
212 | static uid_t last_in, last_out; | |
213 | struct idlist *list; | |
214 | ||
215 | - if (uid == 0) | |
216 | - return 0; | |
217 | - | |
218 | if (uid == last_in) | |
219 | return last_out; | |
220 | ||
221 | @@ -238,7 +286,7 @@ char *add_uid(uid_t uid) | |
222 | struct idlist *list; | |
223 | struct idlist *node; | |
224 | ||
225 | - if (uid == 0) /* don't map root */ | |
226 | + if (uid == 0 && protocol_version < 30) | |
227 | return NULL; | |
228 | ||
229 | for (list = uidlist; list; list = list->next) { | |
230 | @@ -256,7 +304,7 @@ char *add_gid(gid_t gid) | |
231 | struct idlist *list; | |
232 | struct idlist *node; | |
233 | ||
234 | - if (gid == 0) /* don't map root */ | |
235 | + if (gid == 0 && protocol_version < 30) | |
236 | return NULL; | |
237 | ||
238 | for (list = gidlist; list; list = list->next) { | |
239 | @@ -356,3 +404,70 @@ void recv_uid_list(int f, struct file_li | |
240 | F_GROUP(flist->files[i]) = match_gid(F_GID(flist->files[i])); | |
241 | } | |
242 | } | |
243 | + | |
244 | +void parse_name_map(char *map, int usernames) | |
245 | +{ | |
246 | + char *colon, *end, *cp = map + strlen(map); | |
247 | + int id1, id2; | |
248 | + | |
249 | + while (1) { | |
250 | + end = cp; | |
251 | + while (cp > map && cp[-1] != ',') cp--; | |
252 | + if (!(colon = strchr(cp, ':'))) { | |
253 | + rprintf(FERROR, "No colon found in --%smap: %s\n", | |
254 | + usernames ? "user" : "group", cp); | |
255 | + exit_cleanup(RERR_SYNTAX); | |
256 | + } | |
257 | + *colon = '\0'; | |
258 | + | |
259 | + if (isDigit(cp)) { | |
260 | + if (strspn(cp, "0123456789") != (size_t)(colon - cp)) { | |
261 | + bad_number: | |
262 | + rprintf(FERROR, "Invalid number in --%smap: %s\n", | |
263 | + usernames ? "user" : "group", cp); | |
264 | + exit_cleanup(RERR_SYNTAX); | |
265 | + } | |
266 | + id1 = atoi(cp); | |
267 | + } else if (strpbrk(cp, "*[?")) | |
268 | + id1 = -2; | |
269 | + else | |
270 | + id1 = -1; | |
271 | + | |
272 | + if (isDigit(colon+1)) { | |
273 | + if (strspn(colon+1, "0123456789") != (size_t)(end - colon - 1)) { | |
274 | + cp = colon+1; | |
275 | + goto bad_number; | |
276 | + } | |
277 | + id2 = atoi(colon+1); | |
278 | + } else { | |
279 | + if (usernames) { | |
280 | + uid_t uid; | |
281 | + if (name_to_uid(colon+1, &uid)) | |
282 | + id2 = (int)uid; | |
283 | + else | |
284 | + id2 = -1; | |
285 | + } else { | |
286 | + gid_t gid; | |
287 | + if (name_to_gid(colon+1, &gid)) | |
288 | + id2 = (int)gid; | |
289 | + else | |
290 | + id2 = -1; | |
291 | + } | |
292 | + if (id2 < 0) { | |
293 | + rprintf(FERROR, "Invalid name in --%smap: %s\n", | |
294 | + usernames ? "user" : "group", colon+1); | |
295 | + exit_cleanup(RERR_SYNTAX); | |
296 | + } | |
297 | + } | |
298 | + | |
299 | + if (usernames) | |
300 | + add_to_list(&uidmap, id1, id1 < 0 ? cp : NULL, id2); | |
301 | + else | |
302 | + add_to_list(&gidmap, id1, id1 < 0 ? cp : NULL, id2); | |
303 | + | |
304 | + if (cp == map) | |
305 | + break; | |
306 | + | |
307 | + *--cp = '\0'; /* replace comma */ | |
308 | + } | |
309 | +} |