xref: /freebsd/bin/pax/cache.c (revision 0957b409)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1992 Keith Muller.
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Keith Muller of the University of California, San Diego.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)cache.c	8.1 (Berkeley) 5/31/93";
39 #endif
40 #endif /* not lint */
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43 
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <string.h>
47 #include <stdio.h>
48 #include <pwd.h>
49 #include <grp.h>
50 #include <stdlib.h>
51 #include "pax.h"
52 #include "cache.h"
53 #include "extern.h"
54 
55 /*
56  * routines that control user, group, uid and gid caches (for the archive
57  * member print routine).
58  * IMPORTANT:
59  * these routines cache BOTH hits and misses, a major performance improvement
60  */
61 
62 static	int pwopn = 0;		/* is password file open */
63 static	int gropn = 0;		/* is group file open */
64 static UIDC **uidtb = NULL;	/* uid to name cache */
65 static GIDC **gidtb = NULL;	/* gid to name cache */
66 static UIDC **usrtb = NULL;	/* user name to uid cache */
67 static GIDC **grptb = NULL;	/* group name to gid cache */
68 
69 /*
70  * uidtb_start
71  *	creates an an empty uidtb
72  * Return:
73  *	0 if ok, -1 otherwise
74  */
75 
76 int
77 uidtb_start(void)
78 {
79 	static int fail = 0;
80 
81 	if (uidtb != NULL)
82 		return(0);
83 	if (fail)
84 		return(-1);
85 	if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
86 		++fail;
87 		paxwarn(1, "Unable to allocate memory for user id cache table");
88 		return(-1);
89 	}
90 	return(0);
91 }
92 
93 /*
94  * gidtb_start
95  *	creates an an empty gidtb
96  * Return:
97  *	0 if ok, -1 otherwise
98  */
99 
100 int
101 gidtb_start(void)
102 {
103 	static int fail = 0;
104 
105 	if (gidtb != NULL)
106 		return(0);
107 	if (fail)
108 		return(-1);
109 	if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
110 		++fail;
111 		paxwarn(1, "Unable to allocate memory for group id cache table");
112 		return(-1);
113 	}
114 	return(0);
115 }
116 
117 /*
118  * usrtb_start
119  *	creates an an empty usrtb
120  * Return:
121  *	0 if ok, -1 otherwise
122  */
123 
124 int
125 usrtb_start(void)
126 {
127 	static int fail = 0;
128 
129 	if (usrtb != NULL)
130 		return(0);
131 	if (fail)
132 		return(-1);
133 	if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
134 		++fail;
135 		paxwarn(1, "Unable to allocate memory for user name cache table");
136 		return(-1);
137 	}
138 	return(0);
139 }
140 
141 /*
142  * grptb_start
143  *	creates an an empty grptb
144  * Return:
145  *	0 if ok, -1 otherwise
146  */
147 
148 int
149 grptb_start(void)
150 {
151 	static int fail = 0;
152 
153 	if (grptb != NULL)
154 		return(0);
155 	if (fail)
156 		return(-1);
157 	if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
158 		++fail;
159 		paxwarn(1,"Unable to allocate memory for group name cache table");
160 		return(-1);
161 	}
162 	return(0);
163 }
164 
165 /*
166  * name_uid()
167  *	caches the name (if any) for the uid. If frc set, we always return the
168  *	the stored name (if valid or invalid match). We use a simple hash table.
169  * Return
170  *	Pointer to stored name (or an empty string).
171  */
172 
173 const char *
174 name_uid(uid_t uid, int frc)
175 {
176 	struct passwd *pw;
177 	UIDC *ptr;
178 
179 	if ((uidtb == NULL) && (uidtb_start() < 0))
180 		return("");
181 
182 	/*
183 	 * see if we have this uid cached
184 	 */
185 	ptr = uidtb[uid % UID_SZ];
186 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
187 		/*
188 		 * have an entry for this uid
189 		 */
190 		if (frc || (ptr->valid == VALID))
191 			return(ptr->name);
192 		return("");
193 	}
194 
195 	/*
196 	 * No entry for this uid, we will add it
197 	 */
198 	if (!pwopn) {
199 		setpassent(1);
200 		++pwopn;
201 	}
202 	if (ptr == NULL)
203 		ptr = uidtb[uid % UID_SZ] = (UIDC *)malloc(sizeof(UIDC));
204 
205 	if ((pw = getpwuid(uid)) == NULL) {
206 		/*
207 		 * no match for this uid in the local password file
208 		 * a string that is the uid in numeric format
209 		 */
210 		if (ptr == NULL)
211 			return("");
212 		ptr->uid = uid;
213 		ptr->valid = INVALID;
214 #		ifdef NET2_STAT
215 		(void)snprintf(ptr->name, sizeof(ptr->name), "%u", uid);
216 #		else
217 		(void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
218 			       (unsigned long)uid);
219 #		endif
220 		if (frc == 0)
221 			return("");
222 	} else {
223 		/*
224 		 * there is an entry for this uid in the password file
225 		 */
226 		if (ptr == NULL)
227 			return(pw->pw_name);
228 		ptr->uid = uid;
229 		(void)strncpy(ptr->name, pw->pw_name, UNMLEN - 1);
230 		ptr->name[UNMLEN-1] = '\0';
231 		ptr->valid = VALID;
232 	}
233 	return(ptr->name);
234 }
235 
236 /*
237  * name_gid()
238  *	caches the name (if any) for the gid. If frc set, we always return the
239  *	the stored name (if valid or invalid match). We use a simple hash table.
240  * Return
241  *	Pointer to stored name (or an empty string).
242  */
243 
244 const char *
245 name_gid(gid_t gid, int frc)
246 {
247 	struct group *gr;
248 	GIDC *ptr;
249 
250 	if ((gidtb == NULL) && (gidtb_start() < 0))
251 		return("");
252 
253 	/*
254 	 * see if we have this gid cached
255 	 */
256 	ptr = gidtb[gid % GID_SZ];
257 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
258 		/*
259 		 * have an entry for this gid
260 		 */
261 		if (frc || (ptr->valid == VALID))
262 			return(ptr->name);
263 		return("");
264 	}
265 
266 	/*
267 	 * No entry for this gid, we will add it
268 	 */
269 	if (!gropn) {
270 		setgroupent(1);
271 		++gropn;
272 	}
273 	if (ptr == NULL)
274 		ptr = gidtb[gid % GID_SZ] = (GIDC *)malloc(sizeof(GIDC));
275 
276 	if ((gr = getgrgid(gid)) == NULL) {
277 		/*
278 		 * no match for this gid in the local group file, put in
279 		 * a string that is the gid in numeric format
280 		 */
281 		if (ptr == NULL)
282 			return("");
283 		ptr->gid = gid;
284 		ptr->valid = INVALID;
285 #		ifdef NET2_STAT
286 		(void)snprintf(ptr->name, sizeof(ptr->name), "%u", gid);
287 #		else
288 		(void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
289 			       (unsigned long)gid);
290 #		endif
291 		if (frc == 0)
292 			return("");
293 	} else {
294 		/*
295 		 * there is an entry for this group in the group file
296 		 */
297 		if (ptr == NULL)
298 			return(gr->gr_name);
299 		ptr->gid = gid;
300 		(void)strncpy(ptr->name, gr->gr_name, GNMLEN - 1);
301 		ptr->name[GNMLEN-1] = '\0';
302 		ptr->valid = VALID;
303 	}
304 	return(ptr->name);
305 }
306 
307 /*
308  * uid_name()
309  *	caches the uid for a given user name. We use a simple hash table.
310  * Return
311  *	the uid (if any) for a user name, or a -1 if no match can be found
312  */
313 
314 int
315 uid_name(char *name, uid_t *uid)
316 {
317 	struct passwd *pw;
318 	UIDC *ptr;
319 	int namelen;
320 
321 	/*
322 	 * return -1 for mangled names
323 	 */
324 	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
325 		return(-1);
326 	if ((usrtb == NULL) && (usrtb_start() < 0))
327 		return(-1);
328 
329 	/*
330 	 * look up in hash table, if found and valid return the uid,
331 	 * if found and invalid, return a -1
332 	 */
333 	ptr = usrtb[st_hash(name, namelen, UNM_SZ)];
334 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
335 		if (ptr->valid == INVALID)
336 			return(-1);
337 		*uid = ptr->uid;
338 		return(0);
339 	}
340 
341 	if (!pwopn) {
342 		setpassent(1);
343 		++pwopn;
344 	}
345 
346 	if (ptr == NULL)
347 		ptr = usrtb[st_hash(name, namelen, UNM_SZ)] =
348 		  (UIDC *)malloc(sizeof(UIDC));
349 
350 	/*
351 	 * no match, look it up, if no match store it as an invalid entry,
352 	 * or store the matching uid
353 	 */
354 	if (ptr == NULL) {
355 		if ((pw = getpwnam(name)) == NULL)
356 			return(-1);
357 		*uid = pw->pw_uid;
358 		return(0);
359 	}
360 	(void)strncpy(ptr->name, name, UNMLEN - 1);
361 	ptr->name[UNMLEN-1] = '\0';
362 	if ((pw = getpwnam(name)) == NULL) {
363 		ptr->valid = INVALID;
364 		return(-1);
365 	}
366 	ptr->valid = VALID;
367 	*uid = ptr->uid = pw->pw_uid;
368 	return(0);
369 }
370 
371 /*
372  * gid_name()
373  *	caches the gid for a given group name. We use a simple hash table.
374  * Return
375  *	the gid (if any) for a group name, or a -1 if no match can be found
376  */
377 
378 int
379 gid_name(char *name, gid_t *gid)
380 {
381 	struct group *gr;
382 	GIDC *ptr;
383 	int namelen;
384 
385 	/*
386 	 * return -1 for mangled names
387 	 */
388 	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
389 		return(-1);
390 	if ((grptb == NULL) && (grptb_start() < 0))
391 		return(-1);
392 
393 	/*
394 	 * look up in hash table, if found and valid return the uid,
395 	 * if found and invalid, return a -1
396 	 */
397 	ptr = grptb[st_hash(name, namelen, GID_SZ)];
398 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
399 		if (ptr->valid == INVALID)
400 			return(-1);
401 		*gid = ptr->gid;
402 		return(0);
403 	}
404 
405 	if (!gropn) {
406 		setgroupent(1);
407 		++gropn;
408 	}
409 	if (ptr == NULL)
410 		ptr = grptb[st_hash(name, namelen, GID_SZ)] =
411 		  (GIDC *)malloc(sizeof(GIDC));
412 
413 	/*
414 	 * no match, look it up, if no match store it as an invalid entry,
415 	 * or store the matching gid
416 	 */
417 	if (ptr == NULL) {
418 		if ((gr = getgrnam(name)) == NULL)
419 			return(-1);
420 		*gid = gr->gr_gid;
421 		return(0);
422 	}
423 
424 	(void)strncpy(ptr->name, name, GNMLEN - 1);
425 	ptr->name[GNMLEN-1] = '\0';
426 	if ((gr = getgrnam(name)) == NULL) {
427 		ptr->valid = INVALID;
428 		return(-1);
429 	}
430 	ptr->valid = VALID;
431 	*gid = ptr->gid = gr->gr_gid;
432 	return(0);
433 }
434