sh: Reduce unnecessary forks with eval.
[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  * @(#)cache.c  8.1 (Berkeley) 5/31/93
38  * $FreeBSD: src/bin/pax/cache.c,v 1.12.2.1 2001/08/01 05:03:11 obrien Exp $
39  * $DragonFly: src/bin/pax/cache.c,v 1.7 2006/09/27 21:58:08 pavalos Exp $
40  */
41
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <string.h>
45 #include <stdio.h>
46 #include <pwd.h>
47 #include <grp.h>
48 #include <unistd.h>
49 #include <stdlib.h>
50 #include "pax.h"
51 #include "cache.h"
52 #include "extern.h"
53
54 /*
55  * routines that control user, group, uid and gid caches (for the archive
56  * member print routine).
57  * IMPORTANT:
58  * these routines cache BOTH hits and misses, a major performance improvement
59  */
60
61 static  int pwopn = 0;          /* is password file open */
62 static  int gropn = 0;          /* is group file open */
63 static UIDC **uidtb = NULL;     /* uid to name cache */
64 static GIDC **gidtb = NULL;     /* gid to name cache */
65 static UIDC **usrtb = NULL;     /* user name to uid cache */
66 static GIDC **grptb = NULL;     /* group name to gid cache */
67
68 /*
69  * uidtb_start
70  *      creates an empty uidtb
71  * Return:
72  *      0 if ok, -1 otherwise
73  */
74
75 int
76 uidtb_start(void)
77 {
78         static int fail = 0;
79
80         if (uidtb != NULL)
81                 return(0);
82         if (fail)
83                 return(-1);
84         if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
85                 ++fail;
86                 paxwarn(1, "Unable to allocate memory for user id cache table");
87                 return(-1);
88         }
89         return(0);
90 }
91
92 /*
93  * gidtb_start
94  *      creates an empty gidtb
95  * Return:
96  *      0 if ok, -1 otherwise
97  */
98
99 int
100 gidtb_start(void)
101 {
102         static int fail = 0;
103
104         if (gidtb != NULL)
105                 return(0);
106         if (fail)
107                 return(-1);
108         if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
109                 ++fail;
110                 paxwarn(1, "Unable to allocate memory for group id cache table");
111                 return(-1);
112         }
113         return(0);
114 }
115
116 /*
117  * usrtb_start
118  *      creates an empty usrtb
119  * Return:
120  *      0 if ok, -1 otherwise
121  */
122
123 int
124 usrtb_start(void)
125 {
126         static int fail = 0;
127
128         if (usrtb != NULL)
129                 return(0);
130         if (fail)
131                 return(-1);
132         if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
133                 ++fail;
134                 paxwarn(1, "Unable to allocate memory for user name cache table");
135                 return(-1);
136         }
137         return(0);
138 }
139
140 /*
141  * grptb_start
142  *      creates an empty grptb
143  * Return:
144  *      0 if ok, -1 otherwise
145  */
146
147 int
148 grptb_start(void)
149 {
150         static int fail = 0;
151
152         if (grptb != NULL)
153                 return(0);
154         if (fail)
155                 return(-1);
156         if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
157                 ++fail;
158                 paxwarn(1,"Unable to allocate memory for group name cache table");
159                 return(-1);
160         }
161         return(0);
162 }
163
164 /*
165  * name_uid()
166  *      caches the name (if any) for the uid. If frc set, we always return the
167  *      the stored name (if valid or invalid match). We use a simple hash table.
168  * Return
169  *      Pointer to stored name (or a empty string)
170  */
171
172 char *
173 name_uid(uid_t uid, int frc)
174 {
175         struct passwd *pw;
176         UIDC *ptr;
177
178         if ((uidtb == NULL) && (uidtb_start() < 0))
179                 return("");
180
181         /*
182          * see if we have this uid cached
183          */
184         ptr = uidtb[uid % UID_SZ];
185         if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
186                 /*
187                  * have an entry for this uid
188                  */
189                 if (frc || (ptr->valid == VALID))
190                         return(ptr->name);
191                 return("");
192         }
193
194         /*
195          * No entry for this uid, we will add it
196          */
197         if (!pwopn) {
198                 setpassent(1);
199                 ++pwopn;
200         }
201         if (ptr == NULL)
202                 ptr = (UIDC *)malloc(sizeof(UIDC));
203
204         if ((pw = getpwuid(uid)) == NULL) {
205                 /*
206                  * no match for this uid in the local password file
207                  * a string that is the uid in numeric format
208                  */
209                 if (ptr == NULL)
210                         return("");
211                 ptr->uid = uid;
212                 ptr->valid = INVALID;
213                 snprintf(ptr->name, sizeof(ptr->name), "%lu",
214                                (unsigned long)uid);
215                 if (frc == 0)
216                         return("");
217         } else {
218                 /*
219                  * there is an entry for this uid in the password file
220                  */
221                 if (ptr == NULL)
222                         return(pw->pw_name);
223                 ptr->uid = uid;
224                 strncpy(ptr->name, pw->pw_name, UNMLEN - 1);
225                 ptr->name[UNMLEN-1] = '\0';
226                 ptr->valid = VALID;
227         }
228         return(ptr->name);
229 }
230
231 /*
232  * name_gid()
233  *      caches the name (if any) for the gid. If frc set, we always return the
234  *      the stored name (if valid or invalid match). We use a simple hash table.
235  * Return
236  *      Pointer to stored name (or a empty string)
237  */
238
239 char *
240 name_gid(gid_t gid, int frc)
241 {
242         struct group *gr;
243         GIDC *ptr;
244
245         if ((gidtb == NULL) && (gidtb_start() < 0))
246                 return("");
247
248         /*
249          * see if we have this gid cached
250          */
251         ptr = gidtb[gid % GID_SZ];
252         if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
253                 /*
254                  * have an entry for this gid
255                  */
256                 if (frc || (ptr->valid == VALID))
257                         return(ptr->name);
258                 return("");
259         }
260
261         /*
262          * No entry for this gid, we will add it
263          */
264         if (!gropn) {
265                 setgroupent(1);
266                 ++gropn;
267         }
268         if (ptr == NULL)
269                 ptr = (GIDC *)malloc(sizeof(GIDC));
270
271         if ((gr = getgrgid(gid)) == NULL) {
272                 /*
273                  * no match for this gid in the local group file, put in
274                  * a string that is the gid in numeric format
275                  */
276                 if (ptr == NULL)
277                         return("");
278                 ptr->gid = gid;
279                 ptr->valid = INVALID;
280                 snprintf(ptr->name, sizeof(ptr->name), "%lu",
281                                (unsigned long)gid);
282                 if (frc == 0)
283                         return("");
284         } else {
285                 /*
286                  * there is an entry for this group in the group file
287                  */
288                 if (ptr == NULL)
289                         return(gr->gr_name);
290                 ptr->gid = gid;
291                 strncpy(ptr->name, gr->gr_name, GNMLEN - 1);
292                 ptr->name[GNMLEN-1] = '\0';
293                 ptr->valid = VALID;
294         }
295         return(ptr->name);
296 }
297
298 /*
299  * uid_name()
300  *      caches the uid for a given user name. We use a simple hash table.
301  * Return
302  *      the uid (if any) for a user name, or a -1 if no match can be found
303  */
304
305 int
306 uid_name(char *name, uid_t *uid)
307 {
308         struct passwd *pw;
309         UIDC *ptr;
310         int namelen;
311
312         /*
313          * return -1 for mangled names
314          */
315         if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
316                 return(-1);
317         if ((usrtb == NULL) && (usrtb_start() < 0))
318                 return(-1);
319
320         /*
321          * look up in hash table, if found and valid return the uid,
322          * if found and invalid, return a -1
323          */
324         ptr = usrtb[st_hash(name, namelen, UNM_SZ)];
325         if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
326                 if (ptr->valid == INVALID)
327                         return(-1);
328                 *uid = ptr->uid;
329                 return(0);
330         }
331
332         if (!pwopn) {
333                 setpassent(1);
334                 ++pwopn;
335         }
336
337         if (ptr == NULL)
338                 ptr = usrtb[st_hash(name, namelen, UNM_SZ)] =
339                   (UIDC *)malloc(sizeof(UIDC));
340
341         /*
342          * no match, look it up, if no match store it as an invalid entry,
343          * or store the matching uid
344          */
345         if (ptr == NULL) {
346                 if ((pw = getpwnam(name)) == NULL)
347                         return(-1);
348                 *uid = pw->pw_uid;
349                 return(0);
350         }
351         strncpy(ptr->name, name, UNMLEN - 1);
352         ptr->name[UNMLEN-1] = '\0';
353         if ((pw = getpwnam(name)) == NULL) {
354                 ptr->valid = INVALID;
355                 return(-1);
356         }
357         ptr->valid = VALID;
358         *uid = ptr->uid = pw->pw_uid;
359         return(0);
360 }
361
362 /*
363  * gid_name()
364  *      caches the gid for a given group name. We use a simple hash table.
365  * Return
366  *      the gid (if any) for a group name, or a -1 if no match can be found
367  */
368
369 int
370 gid_name(char *name, gid_t *gid)
371 {
372         struct group *gr;
373         GIDC *ptr;
374         int namelen;
375
376         /*
377          * return -1 for mangled names
378          */
379         if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
380                 return(-1);
381         if ((grptb == NULL) && (grptb_start() < 0))
382                 return(-1);
383
384         /*
385          * look up in hash table, if found and valid return the uid,
386          * if found and invalid, return a -1
387          */
388         ptr = grptb[st_hash(name, namelen, GID_SZ)];
389         if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
390                 if (ptr->valid == INVALID)
391                         return(-1);
392                 *gid = ptr->gid;
393                 return(0);
394         }
395
396         if (!gropn) {
397                 setgroupent(1);
398                 ++gropn;
399         }
400         if (ptr == NULL)
401                 ptr = grptb[st_hash(name, namelen, GID_SZ)] =
402                   (GIDC *)malloc(sizeof(GIDC));
403
404         /*
405          * no match, look it up, if no match store it as an invalid entry,
406          * or store the matching gid
407          */
408         if (ptr == NULL) {
409                 if ((gr = getgrnam(name)) == NULL)
410                         return(-1);
411                 *gid = gr->gr_gid;
412                 return(0);
413         }
414
415         strncpy(ptr->name, name, GNMLEN - 1);
416         ptr->name[GNMLEN-1] = '\0';
417         if ((gr = getgrnam(name)) == NULL) {
418                 ptr->valid = INVALID;
419                 return(-1);
420         }
421         ptr->valid = VALID;
422         *gid = ptr->gid = gr->gr_gid;
423         return(0);
424 }