Moved a function.
[rsync/rsync.git] / uidlist.c
1 /*
2    Copyright (C) Andrew Tridgell 1996
3    Copyright (C) Paul Mackerras 1996
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /* handle the mapping of uid/gid and user/group names between systems.
21    If the source username/group does not exist on the target then use
22    the numeric ids. Never do any mapping for uid=0 or gid=0 as these
23    are special.
24 */
25
26 #include "rsync.h"
27
28 #ifdef HAVE_GETGROUPS
29 # if !defined(GETGROUPS_T)
30 #  define GETGROUPS_T gid_t
31 # endif
32 # ifndef NGROUPS_MAX
33 /* It ought to be defined, but just in case. */
34 #  define NGROUPS_MAX 32
35 # endif
36 #endif
37
38 extern int verbose;
39 extern int preserve_uid;
40 extern int preserve_gid;
41 extern int numeric_ids;
42 extern int am_root;
43
44 struct idlist {
45         struct idlist *next;
46         int id, id2;
47         char *name;
48 };
49
50 static struct idlist *uidlist;
51 static struct idlist *gidlist;
52
53 static struct idlist *add_list(int id, char *name)
54 {
55         struct idlist *list = new(struct idlist);
56         if (!list) out_of_memory("add_list");
57         list->next = NULL;
58         list->name = strdup(name);
59         if (!list->name) out_of_memory("add_list");
60         list->id = (int)id;
61         return list;
62 }
63
64
65
66 /* turn a uid into a user name */
67 static char *uid_to_name(uid_t uid)
68 {
69         struct passwd *pass = getpwuid(uid);
70         if (pass) return(pass->pw_name);
71         return NULL;
72 }
73
74 /* turn a gid into a group name */
75 static char *gid_to_name(gid_t gid)
76 {
77         struct group *grp = getgrgid(gid);
78         if (grp) return(grp->gr_name);
79         return NULL;
80 }
81
82 static int map_uid(int id, char *name)
83 {
84         uid_t uid;
85         if (uid != 0 && name_to_uid(name, &uid))
86                 return uid;
87         return id;
88 }
89
90 static int map_gid(int id, char *name)
91 {
92         gid_t gid;
93         if (gid != 0 && name_to_gid(name, &gid))
94                 return gid;
95         return id;
96 }
97
98 static int is_in_group(gid_t gid)
99 {
100 #ifdef HAVE_GETGROUPS
101         static gid_t last_in = GID_NONE, last_out;
102         static int ngroups = -2;
103         static GETGROUPS_T *gidset;
104         int n;
105
106         if (gid == last_in)
107                 return last_out;
108         if (ngroups < -1) {
109                 gid_t mygid = MY_GID();
110                 if ((ngroups = getgroups(0, 0)) < 0)
111                         ngroups = 0;
112                 gidset = new_array(GETGROUPS_T, ngroups+1);
113                 if (ngroups > 0)
114                         ngroups = getgroups(ngroups, gidset);
115                 /* The default gid might not be in the list on some systems. */
116                 for (n = 0; n < ngroups; n++) {
117                         if (gidset[n] == mygid)
118                                 break;
119                 }
120                 if (n == ngroups)
121                         gidset[ngroups++] = mygid;
122                 if (verbose > 3) {
123                         char gidbuf[NGROUPS_MAX*16+32];
124                         int pos;
125                         sprintf(gidbuf, "process has %d gid%s: ",
126                             ngroups, ngroups == 1? "" : "s");
127                         pos = strlen(gidbuf);
128                         for (n = 0; n < ngroups; n++) {
129                                 sprintf(gidbuf+pos, " %ld", (long)gidset[n]);
130                                 pos += strlen(gidbuf+pos);
131                         }
132                         rprintf(FINFO, "%s\n", gidbuf);
133                 }
134         }
135
136         last_in = gid;
137         for (n = 0; n < ngroups; n++) {
138                 if (gidset[n] == gid)
139                         return last_out = 1;
140         }
141         return last_out = 0;
142
143 #else
144         static gid_t mygid = GID_NONE;
145         if (mygid == GID_NONE) {
146                 mygid = MY_GID();
147                 if (verbose > 3)
148                         rprintf(FINFO, "process has gid %ld\n", (long)mygid);
149         }
150         return gid == mygid;
151 #endif
152 }
153
154 /* this function is a definate candidate for a faster algorithm */
155 static uid_t match_uid(uid_t uid)
156 {
157         static uid_t last_in, last_out;
158         struct idlist *list = uidlist;
159
160         if (uid == last_in)
161                 return last_out;
162
163         last_in = uid;
164
165         while (list) {
166                 if (list->id == (int)uid) {
167                         last_out = (uid_t)list->id2;
168                         return last_out;
169                 }
170                 list = list->next;
171         }
172
173         last_out = uid;
174         return last_out;
175 }
176
177 static gid_t match_gid(gid_t gid)
178 {
179         static gid_t last_in = GID_NONE, last_out = GID_NONE;
180         struct idlist *list = gidlist;
181
182         if (gid == last_in)
183                 return last_out;
184
185         last_in = gid;
186
187         while (list) {
188                 if (list->id == (int)gid) {
189                         last_out = (gid_t)list->id2;
190                         return last_out;
191                 }
192                 list = list->next;
193         }
194         
195         if (am_root || is_in_group(gid))
196                 last_out = gid;
197         else
198                 last_out = GID_NONE;
199         return last_out;
200 }
201
202 /* add a uid to the list of uids */
203 void add_uid(uid_t uid)
204 {
205         struct idlist *list = uidlist;
206         char *name;
207
208         if (numeric_ids) return;
209
210         /* don't map root */
211         if (uid==0) return;
212
213         if (!list) {
214                 if (!(name = uid_to_name(uid))) return;
215                 uidlist = add_list((int)uid, name);
216                 return;
217         }
218
219         while (list->next) {
220                 if (list->id == (int)uid) return;
221                 list = list->next;
222         }
223
224         if (list->id == (int)uid) return;
225
226         if (!(name = uid_to_name(uid))) return;
227
228         list->next = add_list((int)uid, name);
229 }
230
231 /* add a gid to the list of gids */
232 void add_gid(gid_t gid)
233 {
234         struct idlist *list = gidlist;
235         char *name;
236
237         if (numeric_ids) return;
238
239         /* don't map root */
240         if (gid==0) return;
241
242         if (!list) {
243                 if (!(name = gid_to_name(gid))) return;
244                 gidlist = add_list((int)gid, name);
245                 return;
246         }
247
248         while (list->next) {
249                 if (list->id == (int)gid) return;
250                 list = list->next;
251         }
252
253         if (list->id == (int)gid) return;
254
255         if (!(name = gid_to_name(gid))) return;
256
257         list->next = add_list((int)gid, name);
258 }
259
260
261 /* send a complete uid/gid mapping to the peer */
262 void send_uid_list(int f)
263 {
264         struct idlist *list;
265
266         if (numeric_ids) return;
267
268         if (preserve_uid) {
269                 /* we send sequences of uid/byte-length/name */
270                 list = uidlist;
271                 while (list) {
272                         int len = strlen(list->name);
273                         write_int(f, list->id);
274                         write_byte(f, len);
275                         write_buf(f, list->name, len);
276                         list = list->next;
277                 }
278
279                 /* terminate the uid list with a 0 uid. We explicitly exclude
280                  * 0 from the list */
281                 write_int(f, 0);
282         }
283
284         if (preserve_gid) {
285                 list = gidlist;
286                 while (list) {
287                         int len = strlen(list->name);
288                         write_int(f, list->id);
289                         write_byte(f, len);
290                         write_buf(f, list->name, len);
291                         list = list->next;
292                 }
293                 write_int(f, 0);
294         }
295 }
296
297 /* recv a complete uid/gid mapping from the peer and map the uid/gid
298  * in the file list to local names */
299 void recv_uid_list(int f, struct file_list *flist)
300 {
301         int id, i;
302         char *name;
303         struct idlist *list;
304
305         if (numeric_ids) return;
306
307         if (preserve_uid) {
308                 /* read the uid list */
309                 list = uidlist;
310                 while ((id = read_int(f)) != 0) {
311                         int len = read_byte(f);
312                         name = new_array(char, len+1);
313                         if (!name) out_of_memory("recv_uid_list");
314                         read_sbuf(f, name, len);
315                         if (!list) {
316                                 uidlist = add_list(id, name);
317                                 list = uidlist;
318                         } else {
319                                 list->next = add_list(id, name);
320                                 list = list->next;
321                         }
322                         list->id2 = map_uid(id, name);
323                         free(name);
324                 }
325                 if (verbose > 3) {
326                         for (list = uidlist; list; list = list->next) {
327                                 rprintf(FINFO, "uid %ld (%s) maps to %ld\n",
328                                     (long)list->id, list->name,
329                                     (long)list->id2);
330                         }
331                 }
332         }
333
334
335         if (preserve_gid) {
336                 /* and the gid list */
337                 list = gidlist;
338                 while ((id = read_int(f)) != 0) {
339                         int len = read_byte(f);
340                         name = new_array(char, len+1);
341                         if (!name) out_of_memory("recv_uid_list");
342                         read_sbuf(f, name, len);
343                         if (!list) {
344                                 gidlist = add_list(id, name);
345                                 list = gidlist;
346                         } else {
347                                 list->next = add_list(id, name);
348                                 list = list->next;
349                         }
350                         list->id2 = map_gid(id, name);
351                         if (!am_root && !is_in_group(list->id2))
352                                 list->id2 = GID_NONE;
353                         free(name);
354                 }
355                 if (verbose > 3) {
356                         for (list = gidlist; list; list = list->next) {
357                                 rprintf(FINFO, "gid %ld (%s) maps to %ld\n",
358                                     (long)list->id, list->name,
359                                     (long)list->id2);
360                         }
361                 }
362         }
363
364         if (!(am_root && preserve_uid) && !preserve_gid) return;
365
366         /* now convert the uid/gid of all files in the list to the mapped
367          * uid/gid */
368         for (i = 0; i < flist->count; i++) {
369                 if (am_root && preserve_uid && flist->files[i]->uid != 0)
370                         flist->files[i]->uid = match_uid(flist->files[i]->uid);
371                 if (preserve_gid && (!am_root || flist->files[i]->gid != 0))
372                         flist->files[i]->gid = match_gid(flist->files[i]->gid);
373         }
374 }