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__)
50int
51archive_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
59static const char * const NO_NAME = "(noname)";
60
61struct 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
74static const char *	lookup_gname(void *, int64_t);
75static const char *	lookup_uname(void *, int64_t);
76static void	cleanup(void *);
77static const char *	lookup_gname_helper(struct name_cache *, id_t gid);
78static 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 */
93int
94archive_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
120static void
121cleanup(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 */
140static const char *
141lookup_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
176static const char *
177lookup_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
185static const char *
186lookup_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
230static const char *
231lookup_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
245static const char *
246lookup_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
254static const char *
255lookup_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
297static const char *
298lookup_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