* Implement POSIX (XSI)'s header file re_comp.h
[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.3 2003/09/28 14:39:14 hmp 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 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 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 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 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         register struct passwd *pw;
176         register 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 #               ifdef NET2_STAT
214                 (void)snprintf(ptr->name, sizeof(ptr->name), "%u", uid);
215 #               else
216                 (void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
217                                (unsigned long)uid);
218 #               endif
219                 if (frc == 0)
220                         return("");
221         } else {
222                 /*
223                  * there is an entry for this uid in the password file
224                  */
225                 if (ptr == NULL)
226                         return(pw->pw_name);
227                 ptr->uid = uid;
228                 (void)strncpy(ptr->name, pw->pw_name, UNMLEN - 1);
229                 ptr->name[UNMLEN-1] = '\0';
230                 ptr->valid = VALID;
231         }
232         return(ptr->name);
233 }
234
235 /*
236  * name_gid()
237  *      caches the name (if any) for the gid. If frc set, we always return the
238  *      the stored name (if valid or invalid match). We use a simple hash table.
239  * Return
240  *      Pointer to stored name (or a empty string)
241  */
242
243 char *
244 name_gid(gid_t gid, int frc)
245 {
246         register struct group *gr;
247         register GIDC *ptr;
248
249         if ((gidtb == NULL) && (gidtb_start() < 0))
250                 return("");
251
252         /*
253          * see if we have this gid cached
254          */
255         ptr = gidtb[gid % GID_SZ];
256         if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
257                 /*
258                  * have an entry for this gid
259                  */
260                 if (frc || (ptr->valid == VALID))
261                         return(ptr->name);
262                 return("");
263         }
264
265         /*
266          * No entry for this gid, we will add it
267          */
268         if (!gropn) {
269                 setgroupent(1);
270                 ++gropn;
271         }
272         if (ptr == NULL)
273                 ptr = (GIDC *)malloc(sizeof(GIDC));
274
275         if ((gr = getgrgid(gid)) == NULL) {
276                 /*
277                  * no match for this gid in the local group file, put in
278                  * a string that is the gid in numeric format
279                  */
280                 if (ptr == NULL)
281                         return("");
282                 ptr->gid = gid;
283                 ptr->valid = INVALID;
284 #               ifdef NET2_STAT
285                 (void)snprintf(ptr->name, sizeof(ptr->name), "%u", gid);
286 #               else
287                 (void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
288                                (unsigned long)gid);
289 #               endif
290                 if (frc == 0)
291                         return("");
292         } else {
293                 /*
294                  * there is an entry for this group in the group file
295                  */
296                 if (ptr == NULL)
297                         return(gr->gr_name);
298                 ptr->gid = gid;
299                 (void)strncpy(ptr->name, gr->gr_name, GNMLEN - 1);
300                 ptr->name[GNMLEN-1] = '\0';
301                 ptr->valid = VALID;
302         }
303         return(ptr->name);
304 }
305
306 /*
307  * uid_name()
308  *      caches the uid for a given user name. We use a simple hash table.
309  * Return
310  *      the uid (if any) for a user name, or a -1 if no match can be found
311  */
312
313 int
314 uid_name(char *name, uid_t *uid)
315 {
316         register struct passwd *pw;
317         register UIDC *ptr;
318         register int namelen;
319
320         /*
321          * return -1 for mangled names
322          */
323         if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
324                 return(-1);
325         if ((usrtb == NULL) && (usrtb_start() < 0))
326                 return(-1);
327
328         /*
329          * look up in hash table, if found and valid return the uid,
330          * if found and invalid, return a -1
331          */
332         ptr = usrtb[st_hash(name, namelen, UNM_SZ)];
333         if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
334                 if (ptr->valid == INVALID)
335                         return(-1);
336                 *uid = ptr->uid;
337                 return(0);
338         }
339
340         if (!pwopn) {
341                 setpassent(1);
342                 ++pwopn;
343         }
344
345         if (ptr == NULL)
346                 ptr = usrtb[st_hash(name, namelen, UNM_SZ)] =
347                   (UIDC *)malloc(sizeof(UIDC));
348
349         /*
350          * no match, look it up, if no match store it as an invalid entry,
351          * or store the matching uid
352          */
353         if (ptr == NULL) {
354                 if ((pw = getpwnam(name)) == NULL)
355                         return(-1);
356                 *uid = pw->pw_uid;
357                 return(0);
358         }
359         (void)strncpy(ptr->name, name, UNMLEN - 1);
360         ptr->name[UNMLEN-1] = '\0';
361         if ((pw = getpwnam(name)) == NULL) {
362                 ptr->valid = INVALID;
363                 return(-1);
364         }
365         ptr->valid = VALID;
366         *uid = ptr->uid = pw->pw_uid;
367         return(0);
368 }
369
370 /*
371  * gid_name()
372  *      caches the gid for a given group name. We use a simple hash table.
373  * Return
374  *      the gid (if any) for a group name, or a -1 if no match can be found
375  */
376
377 int
378 gid_name(char *name, gid_t *gid)
379 {
380         register struct group *gr;
381         register GIDC *ptr;
382         register int namelen;
383
384         /*
385          * return -1 for mangled names
386          */
387         if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
388                 return(-1);
389         if ((grptb == NULL) && (grptb_start() < 0))
390                 return(-1);
391
392         /*
393          * look up in hash table, if found and valid return the uid,
394          * if found and invalid, return a -1
395          */
396         ptr = grptb[st_hash(name, namelen, GID_SZ)];
397         if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
398                 if (ptr->valid == INVALID)
399                         return(-1);
400                 *gid = ptr->gid;
401                 return(0);
402         }
403
404         if (!gropn) {
405                 setgroupent(1);
406                 ++gropn;
407         }
408         if (ptr == NULL)
409                 ptr = grptb[st_hash(name, namelen, GID_SZ)] =
410                   (GIDC *)malloc(sizeof(GIDC));
411
412         /*
413          * no match, look it up, if no match store it as an invalid entry,
414          * or store the matching gid
415          */
416         if (ptr == NULL) {
417                 if ((gr = getgrnam(name)) == NULL)
418                         return(-1);
419                 *gid = gr->gr_gid;
420                 return(0);
421         }
422
423         (void)strncpy(ptr->name, name, GNMLEN - 1);
424         ptr->name[GNMLEN-1] = '\0';
425         if ((gr = getgrnam(name)) == NULL) {
426                 ptr->valid = INVALID;
427                 return(-1);
428         }
429         ptr->valid = VALID;
430         *gid = ptr->gid = gr->gr_gid;
431         return(0);
432 }