xref: /freebsd/bin/pax/cache.c (revision 7f3dea244c40159a41ab22da77a434d7c5b5e85a)
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$";
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 #if __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 		pax_warn(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 #if __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 		pax_warn(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 #if __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 		pax_warn(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 #if __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 		pax_warn(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 #if __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)sprintf(ptr->name, "%u", uid);
246 #		else
247 		(void)sprintf(ptr->name, "%lu", (u_long)uid);
248 #		endif
249 		if (frc == 0)
250 			return("");
251 	} else {
252 		/*
253 		 * there is an entry for this uid in the password file
254 		 */
255 		if (ptr == NULL)
256 			return(pw->pw_name);
257 		ptr->uid = uid;
258 		(void)strncpy(ptr->name, pw->pw_name, UNMLEN - 1);
259 		ptr->name[UNMLEN-1] = '\0';
260 		ptr->valid = VALID;
261 	}
262 	return(ptr->name);
263 }
264 
265 /*
266  * name_gid()
267  *	caches the name (if any) for the gid. If frc set, we always return the
268  *	the stored name (if valid or invalid match). We use a simple hash table.
269  * Return
270  *	Pointer to stored name (or a empty string)
271  */
272 
273 #if __STDC__
274 char *
275 name_gid(gid_t gid, int frc)
276 #else
277 char *
278 name_gid(gid, frc)
279 	gid_t gid;
280 	int frc;
281 #endif
282 {
283 	register struct group *gr;
284 	register GIDC *ptr;
285 
286 	if ((gidtb == NULL) && (gidtb_start() < 0))
287 		return("");
288 
289 	/*
290 	 * see if we have this gid cached
291 	 */
292 	ptr = gidtb[gid % GID_SZ];
293 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
294 		/*
295 		 * have an entry for this gid
296 		 */
297 		if (frc || (ptr->valid == VALID))
298 			return(ptr->name);
299 		return("");
300 	}
301 
302 	/*
303 	 * No entry for this gid, we will add it
304 	 */
305 	if (!gropn) {
306 		setgroupent(1);
307 		++gropn;
308 	}
309 	if (ptr == NULL)
310 		ptr = (GIDC *)malloc(sizeof(GIDC));
311 
312 	if ((gr = getgrgid(gid)) == NULL) {
313 		/*
314 		 * no match for this gid in the local group file, put in
315 		 * a string that is the gid in numeric format
316 		 */
317 		if (ptr == NULL)
318 			return("");
319 		ptr->gid = gid;
320 		ptr->valid = INVALID;
321 #		ifdef NET2_STAT
322 		(void)sprintf(ptr->name, "%u", gid);
323 #		else
324 		(void)sprintf(ptr->name, "%lu", (u_long)gid);
325 #		endif
326 		if (frc == 0)
327 			return("");
328 	} else {
329 		/*
330 		 * there is an entry for this group in the group file
331 		 */
332 		if (ptr == NULL)
333 			return(gr->gr_name);
334 		ptr->gid = gid;
335 		(void)strncpy(ptr->name, gr->gr_name, GNMLEN - 1);
336 		ptr->name[GNMLEN-1] = '\0';
337 		ptr->valid = VALID;
338 	}
339 	return(ptr->name);
340 }
341 
342 /*
343  * uid_name()
344  *	caches the uid for a given user name. We use a simple hash table.
345  * Return
346  *	the uid (if any) for a user name, or a -1 if no match can be found
347  */
348 
349 #if __STDC__
350 int
351 uid_name(char *name, uid_t *uid)
352 #else
353 int
354 uid_name(name, uid)
355 	char *name;
356 	uid_t *uid;
357 #endif
358 {
359 	register struct passwd *pw;
360 	register UIDC *ptr;
361 	register int namelen;
362 
363 	/*
364 	 * return -1 for mangled names
365 	 */
366 	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
367 		return(-1);
368 	if ((usrtb == NULL) && (usrtb_start() < 0))
369 		return(-1);
370 
371 	/*
372 	 * look up in hash table, if found and valid return the uid,
373 	 * if found and invalid, return a -1
374 	 */
375 	ptr = usrtb[st_hash(name, namelen, UNM_SZ)];
376 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
377 		if (ptr->valid == INVALID)
378 			return(-1);
379 		*uid = ptr->uid;
380 		return(0);
381 	}
382 
383 	if (!pwopn) {
384 		setpassent(1);
385 		++pwopn;
386 	}
387 
388 	if (ptr == NULL)
389 		ptr = (UIDC *)malloc(sizeof(UIDC));
390 
391 	/*
392 	 * no match, look it up, if no match store it as an invalid entry,
393 	 * or store the matching uid
394 	 */
395 	if (ptr == NULL) {
396 		if ((pw = getpwnam(name)) == NULL)
397 			return(-1);
398 		*uid = pw->pw_uid;
399 		return(0);
400 	}
401 	(void)strncpy(ptr->name, name, UNMLEN - 1);
402 	ptr->name[UNMLEN-1] = '\0';
403 	if ((pw = getpwnam(name)) == NULL) {
404 		ptr->valid = INVALID;
405 		return(-1);
406 	}
407 	ptr->valid = VALID;
408 	*uid = ptr->uid = pw->pw_uid;
409 	return(0);
410 }
411 
412 /*
413  * gid_name()
414  *	caches the gid for a given group name. We use a simple hash table.
415  * Return
416  *	the gid (if any) for a group name, or a -1 if no match can be found
417  */
418 
419 #if __STDC__
420 int
421 gid_name(char *name, gid_t *gid)
422 #else
423 int
424 gid_name(name, gid)
425 	char *name;
426 	gid_t *gid;
427 #endif
428 {
429 	register struct group *gr;
430 	register GIDC *ptr;
431 	register int namelen;
432 
433 	/*
434 	 * return -1 for mangled names
435 	 */
436 	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
437 		return(-1);
438 	if ((grptb == NULL) && (grptb_start() < 0))
439 		return(-1);
440 
441 	/*
442 	 * look up in hash table, if found and valid return the uid,
443 	 * if found and invalid, return a -1
444 	 */
445 	ptr = grptb[st_hash(name, namelen, GID_SZ)];
446 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
447 		if (ptr->valid == INVALID)
448 			return(-1);
449 		*gid = ptr->gid;
450 		return(0);
451 	}
452 
453 	if (!gropn) {
454 		setgroupent(1);
455 		++gropn;
456 	}
457 	if (ptr == NULL)
458 		ptr = (GIDC *)malloc(sizeof(GIDC));
459 
460 	/*
461 	 * no match, look it up, if no match store it as an invalid entry,
462 	 * or store the matching gid
463 	 */
464 	if (ptr == NULL) {
465 		if ((gr = getgrnam(name)) == NULL)
466 			return(-1);
467 		*gid = gr->gr_gid;
468 		return(0);
469 	}
470 
471 	(void)strncpy(ptr->name, name, GNMLEN - 1);
472 	ptr->name[GNMLEN-1] = '\0';
473 	if ((gr = getgrnam(name)) == NULL) {
474 		ptr->valid = INVALID;
475 		return(-1);
476 	}
477 	ptr->valid = VALID;
478 	*gid = ptr->gid = gr->gr_gid;
479 	return(0);
480 }
481