Initial import from FreeBSD RELENG_4:
[dragonfly.git] / bin / pax / cache.c
1 /*-
2  * Copyright (c) 1992 Keith Muller.
3  * Copyright (c) 1992, 1993
4  *      The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Keith Muller of the University of California, San Diego.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by the University of
20  *      California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)cache.c     8.1 (Berkeley) 5/31/93";
41 #endif
42 static const char rcsid[] =
43   "$FreeBSD: src/bin/pax/cache.c,v 1.12.2.1 2001/08/01 05:03:11 obrien Exp $";
44 #endif /* not lint */
45
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <string.h>
49 #include <stdio.h>
50 #include <pwd.h>
51 #include <grp.h>
52 #include <unistd.h>
53 #include <stdlib.h>
54 #include "pax.h"
55 #include "cache.h"
56 #include "extern.h"
57
58 /*
59  * routines that control user, group, uid and gid caches (for the archive
60  * member print routine).
61  * IMPORTANT:
62  * these routines cache BOTH hits and misses, a major performance improvement
63  */
64
65 static  int pwopn = 0;          /* is password file open */
66 static  int gropn = 0;          /* is group file open */
67 static UIDC **uidtb = NULL;     /* uid to name cache */
68 static GIDC **gidtb = NULL;     /* gid to name cache */
69 static UIDC **usrtb = NULL;     /* user name to uid cache */
70 static GIDC **grptb = NULL;     /* group name to gid cache */
71
72 /*
73  * uidtb_start
74  *      creates an an empty uidtb
75  * Return:
76  *      0 if ok, -1 otherwise
77  */
78
79 #ifdef __STDC__
80 int
81 uidtb_start(void)
82 #else
83 int
84 uidtb_start()
85 #endif
86 {
87         static int fail = 0;
88
89         if (uidtb != NULL)
90                 return(0);
91         if (fail)
92                 return(-1);
93         if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
94                 ++fail;
95                 paxwarn(1, "Unable to allocate memory for user id cache table");
96                 return(-1);
97         }
98         return(0);
99 }
100
101 /*
102  * gidtb_start
103  *      creates an an empty gidtb
104  * Return:
105  *      0 if ok, -1 otherwise
106  */
107
108 #ifdef __STDC__
109 int
110 gidtb_start(void)
111 #else
112 int
113 gidtb_start()
114 #endif
115 {
116         static int fail = 0;
117
118         if (gidtb != NULL)
119                 return(0);
120         if (fail)
121                 return(-1);
122         if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
123                 ++fail;
124                 paxwarn(1, "Unable to allocate memory for group id cache table");
125                 return(-1);
126         }
127         return(0);
128 }
129
130 /*
131  * usrtb_start
132  *      creates an an empty usrtb
133  * Return:
134  *      0 if ok, -1 otherwise
135  */
136
137 #ifdef __STDC__
138 int
139 usrtb_start(void)
140 #else
141 int
142 usrtb_start()
143 #endif
144 {
145         static int fail = 0;
146
147         if (usrtb != NULL)
148                 return(0);
149         if (fail)
150                 return(-1);
151         if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
152                 ++fail;
153                 paxwarn(1, "Unable to allocate memory for user name cache table");
154                 return(-1);
155         }
156         return(0);
157 }
158
159 /*
160  * grptb_start
161  *      creates an an empty grptb
162  * Return:
163  *      0 if ok, -1 otherwise
164  */
165
166 #ifdef __STDC__
167 int
168 grptb_start(void)
169 #else
170 int
171 grptb_start()
172 #endif
173 {
174         static int fail = 0;
175
176         if (grptb != NULL)
177                 return(0);
178         if (fail)
179                 return(-1);
180         if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
181                 ++fail;
182                 paxwarn(1,"Unable to allocate memory for group name cache table");
183                 return(-1);
184         }
185         return(0);
186 }
187
188 /*
189  * name_uid()
190  *      caches the name (if any) for the uid. If frc set, we always return the
191  *      the stored name (if valid or invalid match). We use a simple hash table.
192  * Return
193  *      Pointer to stored name (or a empty string)
194  */
195
196 #ifdef __STDC__
197 char *
198 name_uid(uid_t uid, int frc)
199 #else
200 char *
201 name_uid(uid, frc)
202         uid_t uid;
203         int frc;
204 #endif
205 {
206         register struct passwd *pw;
207         register UIDC *ptr;
208
209         if ((uidtb == NULL) && (uidtb_start() < 0))
210                 return("");
211
212         /*
213          * see if we have this uid cached
214          */
215         ptr = uidtb[uid % UID_SZ];
216         if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
217                 /*
218                  * have an entry for this uid
219                  */
220                 if (frc || (ptr->valid == VALID))
221                         return(ptr->name);
222                 return("");
223         }
224
225         /*
226          * No entry for this uid, we will add it
227          */
228         if (!pwopn) {
229                 setpassent(1);
230                 ++pwopn;
231         }
232         if (ptr == NULL)
233                 ptr = (UIDC *)malloc(sizeof(UIDC));
234
235         if ((pw = getpwuid(uid)) == NULL) {
236                 /*
237                  * no match for this uid in the local password file
238                  * a string that is the uid in numeric format
239                  */
240                 if (ptr == NULL)
241                         return("");
242                 ptr->uid = uid;
243                 ptr->valid = INVALID;
244 #               ifdef NET2_STAT
245                 (void)snprintf(ptr->name, sizeof(ptr->name), "%u", uid);
246 #               else
247                 (void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
248                                (unsigned long)uid);
249 #               endif
250                 if (frc == 0)
251                         return("");
252         } else {
253                 /*
254                  * there is an entry for this uid in the password file
255                  */
256                 if (ptr == NULL)
257                         return(pw->pw_name);
258                 ptr->uid = uid;
259                 (void)strncpy(ptr->name, pw->pw_name, UNMLEN - 1);
260                 ptr->name[UNMLEN-1] = '\0';
261                 ptr->valid = VALID;
262         }
263         return(ptr->name);
264 }
265
266 /*
267  * name_gid()
268  *      caches the name (if any) for the gid. If frc set, we always return the
269  *      the stored name (if valid or invalid match). We use a simple hash table.
270  * Return
271  *      Pointer to stored name (or a empty string)
272  */
273
274 #ifdef __STDC__
275 char *
276 name_gid(gid_t gid, int frc)
277 #else
278 char *
279 name_gid(gid, frc)
280         gid_t gid;
281         int frc;
282 #endif
283 {
284         register struct group *gr;
285         register GIDC *ptr;
286
287         if ((gidtb == NULL) && (gidtb_start() < 0))
288                 return("");
289
290         /*
291          * see if we have this gid cached
292          */
293         ptr = gidtb[gid % GID_SZ];
294         if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
295                 /*
296                  * have an entry for this gid
297                  */
298                 if (frc || (ptr->valid == VALID))
299                         return(ptr->name);
300                 return("");
301         }
302
303         /*
304          * No entry for this gid, we will add it
305          */
306         if (!gropn) {
307                 setgroupent(1);
308                 ++gropn;
309         }
310         if (ptr == NULL)
311                 ptr = (GIDC *)malloc(sizeof(GIDC));
312
313         if ((gr = getgrgid(gid)) == NULL) {
314                 /*
315                  * no match for this gid in the local group file, put in
316                  * a string that is the gid in numeric format
317                  */
318                 if (ptr == NULL)
319                         return("");
320                 ptr->gid = gid;
321                 ptr->valid = INVALID;
322 #               ifdef NET2_STAT
323                 (void)snprintf(ptr->name, sizeof(ptr->name), "%u", gid);
324 #               else
325                 (void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
326                                (unsigned long)gid);
327 #               endif
328                 if (frc == 0)
329                         return("");
330         } else {
331                 /*
332                  * there is an entry for this group in the group file
333                  */
334                 if (ptr == NULL)
335                         return(gr->gr_name);
336                 ptr->gid = gid;
337                 (void)strncpy(ptr->name, gr->gr_name, GNMLEN - 1);
338                 ptr->name[GNMLEN-1] = '\0';
339                 ptr->valid = VALID;
340         }
341         return(ptr->name);
342 }
343
344 /*
345  * uid_name()
346  *      caches the uid for a given user name. We use a simple hash table.
347  * Return
348  *      the uid (if any) for a user name, or a -1 if no match can be found
349  */
350
351 #ifdef __STDC__
352 int
353 uid_name(char *name, uid_t *uid)
354 #else
355 int
356 uid_name(name, uid)
357         char *name;
358         uid_t *uid;
359 #endif
360 {
361         register struct passwd *pw;
362         register UIDC *ptr;
363         register int namelen;
364
365         /*
366          * return -1 for mangled names
367          */
368         if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
369                 return(-1);
370         if ((usrtb == NULL) && (usrtb_start() < 0))
371                 return(-1);
372
373         /*
374          * look up in hash table, if found and valid return the uid,
375          * if found and invalid, return a -1
376          */
377         ptr = usrtb[st_hash(name, namelen, UNM_SZ)];
378         if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
379                 if (ptr->valid == INVALID)
380                         return(-1);
381                 *uid = ptr->uid;
382                 return(0);
383         }
384
385         if (!pwopn) {
386                 setpassent(1);
387                 ++pwopn;
388         }
389
390         if (ptr == NULL)
391                 ptr = usrtb[st_hash(name, namelen, UNM_SZ)] =
392                   (UIDC *)malloc(sizeof(UIDC));
393
394         /*
395          * no match, look it up, if no match store it as an invalid entry,
396          * or store the matching uid
397          */
398         if (ptr == NULL) {
399                 if ((pw = getpwnam(name)) == NULL)
400                         return(-1);
401                 *uid = pw->pw_uid;
402                 return(0);
403         }
404         (void)strncpy(ptr->name, name, UNMLEN - 1);
405         ptr->name[UNMLEN-1] = '\0';
406         if ((pw = getpwnam(name)) == NULL) {
407                 ptr->valid = INVALID;
408                 return(-1);
409         }
410         ptr->valid = VALID;
411         *uid = ptr->uid = pw->pw_uid;
412         return(0);
413 }
414
415 /*
416  * gid_name()
417  *      caches the gid for a given group name. We use a simple hash table.
418  * Return
419  *      the gid (if any) for a group name, or a -1 if no match can be found
420  */
421
422 #ifdef __STDC__
423 int
424 gid_name(char *name, gid_t *gid)
425 #else
426 int
427 gid_name(name, gid)
428         char *name;
429         gid_t *gid;
430 #endif
431 {
432         register struct group *gr;
433         register GIDC *ptr;
434         register int namelen;
435
436         /*
437          * return -1 for mangled names
438          */
439         if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
440                 return(-1);
441         if ((grptb == NULL) && (grptb_start() < 0))
442                 return(-1);
443
444         /*
445          * look up in hash table, if found and valid return the uid,
446          * if found and invalid, return a -1
447          */
448         ptr = grptb[st_hash(name, namelen, GID_SZ)];
449         if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
450                 if (ptr->valid == INVALID)
451                         return(-1);
452                 *gid = ptr->gid;
453                 return(0);
454         }
455
456         if (!gropn) {
457                 setgroupent(1);
458                 ++gropn;
459         }
460         if (ptr == NULL)
461                 ptr = grptb[st_hash(name, namelen, GID_SZ)] =
462                   (GIDC *)malloc(sizeof(GIDC));
463
464         /*
465          * no match, look it up, if no match store it as an invalid entry,
466          * or store the matching gid
467          */
468         if (ptr == NULL) {
469                 if ((gr = getgrnam(name)) == NULL)
470                         return(-1);
471                 *gid = gr->gr_gid;
472                 return(0);
473         }
474
475         (void)strncpy(ptr->name, name, GNMLEN - 1);
476         ptr->name[GNMLEN-1] = '\0';
477         if ((gr = getgrnam(name)) == NULL) {
478                 ptr->valid = INVALID;
479                 return(-1);
480         }
481         ptr->valid = VALID;
482         *gid = ptr->gid = gr->gr_gid;
483         return(0);
484 }