configuration parsing and loading code for rsyncd. This is based
[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 extern int preserve_uid;
29 extern int preserve_gid;
30 extern int numeric_ids;
31
32 struct idlist {
33         struct idlist *next;
34         int id, id2;
35         char *name;
36 };
37
38 static struct idlist *uidlist;
39 static struct idlist *gidlist;
40
41 static struct idlist *add_list(int id, char *name)
42 {
43         struct idlist *list = (struct idlist *)malloc(sizeof(list[0]));
44         if (!list) out_of_memory("add_list");
45         list->next = NULL;
46         list->name = strdup(name);
47         if (!list->name) out_of_memory("add_list");
48         list->id = (int)id;
49         return list;
50 }
51
52
53
54 /* turn a uid into a user name */
55 static char *uid_to_name(uid_t uid)
56 {
57         struct passwd *pass = getpwuid(uid);
58         if (pass) return(pass->pw_name);
59         return NULL;
60 }
61
62 /* turn a gid into a group name */
63 static char *gid_to_name(gid_t gid)
64 {
65         struct group *grp = getgrgid(gid);
66         if (grp) return(grp->gr_name);
67         return NULL;
68 }
69
70
71 /* turn a user name into a uid */
72 static uid_t name_to_uid(char *name)
73 {
74         struct passwd *pass;
75         if (!name || !*name) return 0;
76         pass = getpwnam(name);
77         if (pass) return(pass->pw_uid);
78         return 0;
79 }
80
81 /* turn a group name into a gid */
82 static gid_t name_to_gid(char *name)
83 {
84         struct group *grp;
85         if (!name || !*name) return 0;
86         grp = getgrnam(name);
87         if (grp) return(grp->gr_gid);
88         return 0;
89 }
90
91 static int map_uid(int id, char *name)
92 {
93         uid_t uid = name_to_uid(name);
94         if (uid != 0) return uid;
95         return id;
96 }
97
98 static int map_gid(int id, char *name)
99 {
100         gid_t gid = name_to_gid(name);
101         if (gid != 0) return gid;
102         return id;
103 }
104
105 /* this function is a definate candidate for a faster algorithm */
106 static uid_t match_uid(uid_t uid)
107 {
108         static uid_t last_in, last_out;
109         struct idlist *list = uidlist;
110
111         if (uid == last_in) return last_out;
112
113         last_in = uid;
114
115         while (list) {
116                 if (list->id == (int)uid) {
117                         last_out = (uid_t)list->id2;
118                         return last_out;
119                 }
120                 list = list->next;
121         }
122         
123         last_out = uid;
124         return last_out;
125 }
126
127 static gid_t match_gid(gid_t gid)
128 {
129         static gid_t last_in, last_out;
130         struct idlist *list = gidlist;
131
132         if (gid == last_in) return last_out;
133
134         last_in = gid;
135
136         while (list) {
137                 if (list->id == (int)gid) {
138                         last_out = (gid_t)list->id2;
139                         return last_out;
140                 }
141                 list = list->next;
142         }
143         
144         last_out = gid;
145         return last_out;
146 }
147
148 /* add a uid to the list of uids */
149 void add_uid(uid_t uid)
150 {
151         struct idlist *list = uidlist;
152         char *name;
153
154         if (numeric_ids) return;
155
156         /* don't map root */
157         if (uid==0) return;
158
159         if (!list) {
160                 if (!(name = uid_to_name(uid))) return;
161                 uidlist = add_list((int)uid, name);
162                 return;
163         }
164
165         while (list->next) {
166                 if (list->id == (int)uid) return;
167                 list = list->next;
168         }
169
170         if (list->id == (int)uid) return;
171
172         if (!(name = uid_to_name(uid))) return;
173
174         list->next = add_list((int)uid, name);
175 }
176
177 /* add a gid to the list of gids */
178 void add_gid(gid_t gid)
179 {
180         struct idlist *list = gidlist;
181         char *name;
182
183         if (numeric_ids) return;
184
185         /* don't map root */
186         if (gid==0) return;
187
188         if (!list) {
189                 if (!(name = gid_to_name(gid))) return;
190                 gidlist = add_list((int)gid, name);
191                 return;
192         }
193
194         while (list->next) {
195                 if (list->id == (int)gid) return;
196                 list = list->next;
197         }
198
199         if (list->id == (int)gid) return;
200
201         if (!(name = gid_to_name(gid))) return;
202
203         list->next = add_list((int)gid, name);
204 }
205
206
207 /* send a complete uid/gid mapping to the peer */
208 void send_uid_list(int f)
209 {
210         struct idlist *list;
211
212         if (numeric_ids) return;
213
214         if (preserve_uid) {
215                 /* we send sequences of uid/byte-length/name */
216                 list = uidlist;
217                 while (list) {
218                         int len = strlen(list->name);
219                         write_int(f, list->id);
220                         write_byte(f, len);
221                         write_buf(f, list->name, len);
222                         list = list->next;
223                 }
224
225                 /* terminate the uid list with a 0 uid. We explicitly exclude
226                    0 from the list */
227                 write_int(f, 0);
228         }
229
230         if (preserve_gid) {
231                 list = gidlist;
232                 while (list) {
233                         int len = strlen(list->name);
234                         write_int(f, list->id);
235                         write_byte(f, len);
236                         write_buf(f, list->name, len);
237                         list = list->next;
238                 }
239                 write_int(f, 0);
240         }
241 }
242
243 /* recv a complete uid/gid mapping from the peer and map the uid/gid
244    in the file list to local names */
245 void recv_uid_list(int f, struct file_list *flist)
246 {
247         int id, i;
248         char *name;
249         struct idlist *list;
250
251         if (numeric_ids) return;
252
253         if (preserve_uid) {
254                 /* read the uid list */
255                 list = uidlist;
256                 id = read_int(f);
257                 while (id != 0) {
258                         int len = read_byte(f);
259                         name = (char *)malloc(len+1);
260                         if (!name) out_of_memory("recv_uid_list");
261                         read_sbuf(f, name, len);
262                         if (!list) {
263                                 uidlist = add_list(id, name);
264                                 list = uidlist;
265                         } else {
266                                 list->next = add_list(id, name);
267                                 list = list->next;
268                         }
269                         list->id2 = map_uid(id, name);
270                         free(name);
271                         id = read_int(f);
272                 }
273         }
274
275
276         if (preserve_gid) {
277                 /* and the gid list */
278                 list = gidlist;
279                 id = read_int(f);
280                 while (id != 0) {
281                         int len = read_byte(f);
282                         name = (char *)malloc(len+1);
283                         if (!name) out_of_memory("recv_uid_list");
284                         read_sbuf(f, name, len);
285                         if (!list) {
286                                 gidlist = add_list(id, name);
287                                 list = gidlist;
288                         } else {
289                                 list->next = add_list(id, name);
290                                 list = list->next;
291                         }
292                         list->id2 = map_gid(id, name);
293                         free(name);
294                         id = read_int(f);
295                 }
296         }
297
298         if (!uidlist && !gidlist) return;
299
300         /* now convert the uid/gid of all files in the list to the mapped
301            uid/gid */
302         for (i=0;i<flist->count;i++) {
303                 if (preserve_uid && flist->files[i]->uid != 0) {
304                         flist->files[i]->uid = match_uid(flist->files[i]->uid);
305                 }
306                 if (preserve_gid && flist->files[i]->gid != 0) {
307                         flist->files[i]->gid = match_gid(flist->files[i]->gid);
308                 }
309         }
310 }