xref: /freebsd/bin/pax/cache.c (revision c17d43407fe04133a94055b0dbc7ea8965654a9f)
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 int
80 uidtb_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 
103 int
104 gidtb_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 
127 int
128 usrtb_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 
151 int
152 grptb_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 a empty string)
174  */
175 
176 char *
177 name_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 = (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 a empty string)
245  */
246 
247 char *
248 name_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 = (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 
317 int
318 uid_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 
381 int
382 gid_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