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