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