cache.c revision 330897
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 * 4. 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
38static char sccsid[] = "@(#)cache.c	8.1 (Berkeley) 5/31/93";
39#endif
40#endif /* not lint */
41#include <sys/cdefs.h>
42__FBSDID("$FreeBSD: stable/11/bin/pax/cache.c 330897 2018-03-14 03:19:51Z eadler $");
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
62static	int pwopn = 0;		/* is password file open */
63static	int gropn = 0;		/* is group file open */
64static UIDC **uidtb = NULL;	/* uid to name cache */
65static GIDC **gidtb = NULL;	/* gid to name cache */
66static UIDC **usrtb = NULL;	/* user name to uid cache */
67static 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
76int
77uidtb_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
100int
101gidtb_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
124int
125usrtb_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
148int
149grptb_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
173const char *
174name_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
244const char *
245name_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
314int
315uid_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
378int
379gid_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