xref: /freebsd/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c (revision b9128a37faafede823eb456aa65a11ac69997284)
1 /*-
2  * Copyright (c) 2003-2007 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "archive_platform.h"
27 
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
30 #endif
31 #ifdef HAVE_ERRNO_H
32 #include <errno.h>
33 #endif
34 #ifdef HAVE_GRP_H
35 #include <grp.h>
36 #endif
37 #ifdef HAVE_PWD_H
38 #include <pwd.h>
39 #endif
40 #ifdef HAVE_STDLIB_H
41 #include <stdlib.h>
42 #endif
43 #ifdef HAVE_STRING_H
44 #include <string.h>
45 #endif
46 
47 #include "archive.h"
48 
49 #if defined(_WIN32) && !defined(__CYGWIN__)
50 int
51 archive_read_disk_set_standard_lookup(struct archive *a)
52 {
53 	archive_set_error(a, -1, "Standard lookups not available on Windows");
54 	return (ARCHIVE_FATAL);
55 }
56 #else /* ! (_WIN32 && !__CYGWIN__) */
57 #define	name_cache_size 127
58 
59 static const char * const NO_NAME = "(noname)";
60 
61 struct name_cache {
62 	struct archive *archive;
63 	char   *buff;
64 	size_t  buff_size;
65 	int	probes;
66 	int	hits;
67 	size_t	size;
68 	struct {
69 		id_t id;
70 		const char *name;
71 	} cache[name_cache_size];
72 };
73 
74 static const char *	lookup_gname(void *, int64_t);
75 static const char *	lookup_uname(void *, int64_t);
76 static void	cleanup(void *);
77 static const char *	lookup_gname_helper(struct name_cache *, id_t gid);
78 static const char *	lookup_uname_helper(struct name_cache *, id_t uid);
79 
80 /*
81  * Installs functions that use getpwuid()/getgrgid()---along with
82  * a simple cache to accelerate such lookups---into the archive_read_disk
83  * object.  This is in a separate file because getpwuid()/getgrgid()
84  * can pull in a LOT of library code (including NIS/LDAP functions, which
85  * pull in DNS resolvers, etc).  This can easily top 500kB, which makes
86  * it inappropriate for some space-constrained applications.
87  *
88  * Applications that are size-sensitive may want to just use the
89  * real default functions (defined in archive_read_disk.c) that just
90  * use the uid/gid without the lookup.  Or define your own custom functions
91  * if you prefer.
92  */
93 int
94 archive_read_disk_set_standard_lookup(struct archive *a)
95 {
96 	struct name_cache *ucache = malloc(sizeof(struct name_cache));
97 	struct name_cache *gcache = malloc(sizeof(struct name_cache));
98 
99 	if (ucache == NULL || gcache == NULL) {
100 		archive_set_error(a, ENOMEM,
101 		    "Can't allocate uname/gname lookup cache");
102 		free(ucache);
103 		free(gcache);
104 		return (ARCHIVE_FATAL);
105 	}
106 
107 	memset(ucache, 0, sizeof(*ucache));
108 	ucache->archive = a;
109 	ucache->size = name_cache_size;
110 	memset(gcache, 0, sizeof(*gcache));
111 	gcache->archive = a;
112 	gcache->size = name_cache_size;
113 
114 	archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
115 	archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);
116 
117 	return (ARCHIVE_OK);
118 }
119 
120 static void
121 cleanup(void *data)
122 {
123 	struct name_cache *cache = (struct name_cache *)data;
124 	size_t i;
125 
126 	if (cache != NULL) {
127 		for (i = 0; i < cache->size; i++) {
128 			if (cache->cache[i].name != NULL &&
129 			    cache->cache[i].name != NO_NAME)
130 				free((void *)(uintptr_t)cache->cache[i].name);
131 		}
132 		free(cache->buff);
133 		free(cache);
134 	}
135 }
136 
137 /*
138  * Lookup uid/gid from uname/gname, return NULL if no match.
139  */
140 static const char *
141 lookup_name(struct name_cache *cache,
142     const char * (*lookup_fn)(struct name_cache *, id_t), id_t id)
143 {
144 	const char *name;
145 	int slot;
146 
147 
148 	cache->probes++;
149 
150 	slot = id % cache->size;
151 	if (cache->cache[slot].name != NULL) {
152 		if (cache->cache[slot].id == id) {
153 			cache->hits++;
154 			if (cache->cache[slot].name == NO_NAME)
155 				return (NULL);
156 			return (cache->cache[slot].name);
157 		}
158 		if (cache->cache[slot].name != NO_NAME)
159 			free((void *)(uintptr_t)cache->cache[slot].name);
160 		cache->cache[slot].name = NULL;
161 	}
162 
163 	name = (lookup_fn)(cache, id);
164 	if (name == NULL) {
165 		/* Cache and return the negative response. */
166 		cache->cache[slot].name = NO_NAME;
167 		cache->cache[slot].id = id;
168 		return (NULL);
169 	}
170 
171 	cache->cache[slot].name = name;
172 	cache->cache[slot].id = id;
173 	return (cache->cache[slot].name);
174 }
175 
176 static const char *
177 lookup_uname(void *data, int64_t uid)
178 {
179 	struct name_cache *uname_cache = (struct name_cache *)data;
180 	return (lookup_name(uname_cache,
181 		    &lookup_uname_helper, (id_t)uid));
182 }
183 
184 #if HAVE_GETPWUID_R
185 static const char *
186 lookup_uname_helper(struct name_cache *cache, id_t id)
187 {
188 	struct passwd	pwent, *result;
189 	char * nbuff;
190 	size_t nbuff_size;
191 	int r;
192 
193 	if (cache->buff_size == 0) {
194 		cache->buff_size = 256;
195 		cache->buff = malloc(cache->buff_size);
196 	}
197 	if (cache->buff == NULL)
198 		return (NULL);
199 	for (;;) {
200 		result = &pwent; /* Old getpwuid_r ignores last arg. */
201 		r = getpwuid_r((uid_t)id, &pwent,
202 			       cache->buff, cache->buff_size, &result);
203 		if (r == 0)
204 			break;
205 		if (r != ERANGE)
206 			break;
207 		/* ERANGE means our buffer was too small, but POSIX
208 		 * doesn't tell us how big the buffer should be, so
209 		 * we just double it and try again.  Because the buffer
210 		 * is kept around in the cache object, we shouldn't
211 		 * have to do this very often. */
212 		nbuff_size = cache->buff_size * 2;
213 		nbuff = realloc(cache->buff, nbuff_size);
214 		if (nbuff == NULL)
215 			break;
216 		cache->buff = nbuff;
217 		cache->buff_size = nbuff_size;
218 	}
219 	if (r != 0) {
220 		archive_set_error(cache->archive, errno,
221 		    "Can't lookup user for id %d", (int)id);
222 		return (NULL);
223 	}
224 	if (result == NULL)
225 		return (NULL);
226 
227 	return strdup(result->pw_name);
228 }
229 #else
230 static const char *
231 lookup_uname_helper(struct name_cache *cache, id_t id)
232 {
233 	struct passwd	*result;
234 	(void)cache; /* UNUSED */
235 
236 	result = getpwuid((uid_t)id);
237 
238 	if (result == NULL)
239 		return (NULL);
240 
241 	return strdup(result->pw_name);
242 }
243 #endif
244 
245 static const char *
246 lookup_gname(void *data, int64_t gid)
247 {
248 	struct name_cache *gname_cache = (struct name_cache *)data;
249 	return (lookup_name(gname_cache,
250 		    &lookup_gname_helper, (id_t)gid));
251 }
252 
253 #if HAVE_GETGRGID_R
254 static const char *
255 lookup_gname_helper(struct name_cache *cache, id_t id)
256 {
257 	struct group	grent, *result;
258 	char * nbuff;
259 	size_t nbuff_size;
260 	int r;
261 
262 	if (cache->buff_size == 0) {
263 		cache->buff_size = 256;
264 		cache->buff = malloc(cache->buff_size);
265 	}
266 	if (cache->buff == NULL)
267 		return (NULL);
268 	for (;;) {
269 		result = &grent; /* Old getgrgid_r ignores last arg. */
270 		r = getgrgid_r((gid_t)id, &grent,
271 			       cache->buff, cache->buff_size, &result);
272 		if (r == 0)
273 			break;
274 		if (r != ERANGE)
275 			break;
276 		/* ERANGE means our buffer was too small, but POSIX
277 		 * doesn't tell us how big the buffer should be, so
278 		 * we just double it and try again. */
279 		nbuff_size = cache->buff_size * 2;
280 		nbuff = realloc(cache->buff, nbuff_size);
281 		if (nbuff == NULL)
282 			break;
283 		cache->buff = nbuff;
284 		cache->buff_size = nbuff_size;
285 	}
286 	if (r != 0) {
287 		archive_set_error(cache->archive, errno,
288 		    "Can't lookup group for id %d", (int)id);
289 		return (NULL);
290 	}
291 	if (result == NULL)
292 		return (NULL);
293 
294 	return strdup(result->gr_name);
295 }
296 #else
297 static const char *
298 lookup_gname_helper(struct name_cache *cache, id_t id)
299 {
300 	struct group	*result;
301 	(void)cache; /* UNUSED */
302 
303 	result = getgrgid((gid_t)id);
304 
305 	if (result == NULL)
306 		return (NULL);
307 
308 	return strdup(result->gr_name);
309 }
310 #endif
311 
312 #endif /* ! (_WIN32 && !__CYGWIN__) */
313