archive_read_disk_set_standard_lookup.c revision 256281
1697Spaul/*-
21156Sjkh * Copyright (c) 2003-2007 Tim Kientzle
31156Sjkh * All rights reserved.
41156Sjkh *
51156Sjkh * Redistribution and use in source and binary forms, with or without
61156Sjkh * modification, are permitted provided that the following conditions
71156Sjkh * are met:
81156Sjkh * 1. Redistributions of source code must retain the above copyright
91156Sjkh *    notice, this list of conditions and the following disclaimer.
101156Sjkh * 2. Redistributions in binary form must reproduce the above copyright
111156Sjkh *    notice, this list of conditions and the following disclaimer in the
121156Sjkh *    documentation and/or other materials provided with the distribution.
131156Sjkh *
141156Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
151156Sjkh * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
161156Sjkh * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1713771Smpp * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
181156Sjkh * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
191156Sjkh * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
201156Sjkh * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
211156Sjkh * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
221156Sjkh * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
231156Sjkh * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
241156Sjkh */
251156Sjkh
261156Sjkh#include "archive_platform.h"
271156Sjkh__FBSDID("$FreeBSD: stable/10/contrib/libarchive/libarchive/archive_read_disk_set_standard_lookup.c 232153 2012-02-25 10:58:02Z mm $");
281156Sjkh
291156Sjkh#ifdef HAVE_SYS_TYPES_H
3050473Speter#include <sys/types.h>
311156Sjkh#endif
321156Sjkh#ifdef HAVE_ERRNO_H
331156Sjkh#include <errno.h>
34697Spaul#endif
35697Spaul#ifdef HAVE_GRP_H
361156Sjkh#include <grp.h>
371156Sjkh#endif
381156Sjkh#ifdef HAVE_PWD_H
39697Spaul#include <pwd.h>
40697Spaul#endif
41697Spaul#ifdef HAVE_STDLIB_H
42697Spaul#include <stdlib.h>
43697Spaul#endif
4436311Sdfr#ifdef HAVE_STRING_H
4536311Sdfr#include <string.h>
4636311Sdfr#endif
4736311Sdfr
4836311Sdfr#include "archive.h"
4936311Sdfr
5036311Sdfr#if defined(_WIN32) && !defined(__CYGWIN__)
5136311Sdfrint
5236311Sdfrarchive_read_disk_set_standard_lookup(struct archive *a)
5336311Sdfr{
5436311Sdfr	archive_set_error(a, -1, "Standard lookups not available on Windows");
5536311Sdfr	return (ARCHIVE_FATAL);
5636311Sdfr}
5736311Sdfr#else /* ! (_WIN32 && !__CYGWIN__) */
5836311Sdfr#define	name_cache_size 127
5936311Sdfr
6036311Sdfrstatic const char * const NO_NAME = "(noname)";
6136311Sdfr
6236311Sdfrstruct name_cache {
6336311Sdfr	struct archive *archive;
6436311Sdfr	char   *buff;
6536311Sdfr	size_t  buff_size;
6636311Sdfr	int	probes;
6736311Sdfr	int	hits;
6836311Sdfr	size_t	size;
6936311Sdfr	struct {
7036311Sdfr		id_t id;
7133137Sjdp		const char *name;
7233137Sjdp	} cache[name_cache_size];
73697Spaul};
7413771Smpp
75697Spaulstatic const char *	lookup_gname(void *, int64_t);
761156Sjkhstatic const char *	lookup_uname(void *, int64_t);
771156Sjkhstatic void	cleanup(void *);
78697Spaulstatic const char *	lookup_gname_helper(struct name_cache *, id_t gid);
79697Spaulstatic const char *	lookup_uname_helper(struct name_cache *, id_t uid);
801156Sjkh
811156Sjkh/*
821156Sjkh * Installs functions that use getpwuid()/getgrgid()---along with
831156Sjkh * a simple cache to accelerate such lookups---into the archive_read_disk
841156Sjkh * object.  This is in a separate file because getpwuid()/getgrgid()
851156Sjkh * can pull in a LOT of library code (including NIS/LDAP functions, which
861156Sjkh * pull in DNS resolveers, etc).  This can easily top 500kB, which makes
87697Spaul * it inappropriate for some space-constrained applications.
88697Spaul *
89697Spaul * Applications that are size-sensitive may want to just use the
901156Sjkh * real default functions (defined in archive_read_disk.c) that just
911156Sjkh * use the uid/gid without the lookup.  Or define your own custom functions
92697Spaul * if you prefer.
93697Spaul */
94697Spaulint
951156Sjkharchive_read_disk_set_standard_lookup(struct archive *a)
961156Sjkh{
971156Sjkh	struct name_cache *ucache = malloc(sizeof(struct name_cache));
981156Sjkh	struct name_cache *gcache = malloc(sizeof(struct name_cache));
991156Sjkh
1001156Sjkh	if (ucache == NULL || gcache == NULL) {
1011156Sjkh		archive_set_error(a, ENOMEM,
1021156Sjkh		    "Can't allocate uname/gname lookup cache");
1031156Sjkh		free(ucache);
104697Spaul		free(gcache);
105697Spaul		return (ARCHIVE_FATAL);
106697Spaul	}
107697Spaul
108697Spaul	memset(ucache, 0, sizeof(*ucache));
109697Spaul	ucache->archive = a;
110697Spaul	ucache->size = name_cache_size;
1111156Sjkh	memset(gcache, 0, sizeof(*gcache));
112697Spaul	gcache->archive = a;
113697Spaul	gcache->size = name_cache_size;
114697Spaul
115697Spaul	archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
11631584Sjdp	archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);
11731584Sjdp
118697Spaul	return (ARCHIVE_OK);
119697Spaul}
120697Spaul
121697Spaulstatic void
122697Spaulcleanup(void *data)
123697Spaul{
124697Spaul	struct name_cache *cache = (struct name_cache *)data;
125697Spaul	size_t i;
126697Spaul
1271156Sjkh	if (cache != NULL) {
128697Spaul		for (i = 0; i < cache->size; i++) {
129697Spaul			if (cache->cache[i].name != NULL &&
1301156Sjkh			    cache->cache[i].name != NO_NAME)
1311156Sjkh				free((void *)(uintptr_t)cache->cache[i].name);
1321156Sjkh		}
13318591Speter		free(cache->buff);
1341156Sjkh		free(cache);
1351156Sjkh	}
1361156Sjkh}
1371156Sjkh
1381156Sjkh/*
1391156Sjkh * Lookup uid/gid from uname/gname, return NULL if no match.
1401156Sjkh */
1411156Sjkhstatic const char *
1421156Sjkhlookup_name(struct name_cache *cache,
1431156Sjkh    const char * (*lookup_fn)(struct name_cache *, id_t), id_t id)
1441156Sjkh{
145697Spaul	const char *name;
146697Spaul	int slot;
147697Spaul
1481156Sjkh
149697Spaul	cache->probes++;
150697Spaul
1511156Sjkh	slot = id % cache->size;
152697Spaul	if (cache->cache[slot].name != NULL) {
153697Spaul		if (cache->cache[slot].id == id) {
154697Spaul			cache->hits++;
1551156Sjkh			if (cache->cache[slot].name == NO_NAME)
1561156Sjkh				return (NULL);
157697Spaul			return (cache->cache[slot].name);
158697Spaul		}
159697Spaul		if (cache->cache[slot].name != NO_NAME)
160697Spaul			free((void *)(uintptr_t)cache->cache[slot].name);
161697Spaul		cache->cache[slot].name = NULL;
162697Spaul	}
163697Spaul
1641156Sjkh	name = (lookup_fn)(cache, id);
1651156Sjkh	if (name == NULL) {
1661156Sjkh		/* Cache and return the negative response. */
1671156Sjkh		cache->cache[slot].name = NO_NAME;
1681156Sjkh		cache->cache[slot].id = id;
169697Spaul		return (NULL);
170697Spaul	}
171697Spaul
172697Spaul	cache->cache[slot].name = name;
173697Spaul	cache->cache[slot].id = id;
1741156Sjkh	return (cache->cache[slot].name);
1751156Sjkh}
1761156Sjkh
1771156Sjkhstatic const char *
1781156Sjkhlookup_uname(void *data, int64_t uid)
1791156Sjkh{
1801156Sjkh	struct name_cache *uname_cache = (struct name_cache *)data;
1811156Sjkh	return (lookup_name(uname_cache,
182697Spaul		    &lookup_uname_helper, (id_t)uid));
183697Spaul}
184697Spaul
1859335Sdfr#if HAVE_GETPWUID_R
1869335Sdfrstatic const char *
1879335Sdfrlookup_uname_helper(struct name_cache *cache, id_t id)
1889335Sdfr{
18927838Sjdp	struct passwd	pwent, *result;
19033137Sjdp	char * nbuff;
1919335Sdfr	size_t nbuff_size;
1929335Sdfr	int r;
193697Spaul
1949335Sdfr	if (cache->buff_size == 0) {
1959335Sdfr		cache->buff_size = 256;
196697Spaul		cache->buff = malloc(cache->buff_size);
197697Spaul	}
19831342Sbrian	if (cache->buff == NULL)
1999335Sdfr		return (NULL);
20031342Sbrian	for (;;) {
20131342Sbrian		result = &pwent; /* Old getpwuid_r ignores last arg. */
2029335Sdfr		r = getpwuid_r((uid_t)id, &pwent,
20331342Sbrian			       cache->buff, cache->buff_size, &result);
20433137Sjdp		if (r == 0)
20533137Sjdp			break;
206697Spaul		if (r != ERANGE)
207697Spaul			break;
208697Spaul		/* ERANGE means our buffer was too small, but POSIX
209697Spaul		 * doesn't tell us how big the buffer should be, so
210697Spaul		 * we just double it and try again.  Because the buffer
211697Spaul		 * is kept around in the cache object, we shouldn't
212697Spaul		 * have to do this very often. */
2131156Sjkh		nbuff_size = cache->buff_size * 2;
2141156Sjkh		nbuff = realloc(cache->buff, nbuff_size);
215697Spaul		if (nbuff == NULL)
216697Spaul			break;
217697Spaul		cache->buff = nbuff;
218697Spaul		cache->buff_size = nbuff_size;
219697Spaul	}
2201156Sjkh	if (r != 0) {
2211156Sjkh		archive_set_error(cache->archive, errno,
2221156Sjkh		    "Can't lookup user for id %d", (int)id);
223697Spaul		return (NULL);
2241156Sjkh	}
2251156Sjkh	if (result == NULL)
2266887Snate		return (NULL);
227697Spaul
228697Spaul	return strdup(result->pw_name);
229697Spaul}
230697Spaul#else
231697Spaulstatic const char *
232697Spaullookup_uname_helper(struct name_cache *cache, id_t id)
2331156Sjkh{
2341156Sjkh	struct passwd	*result;
2351156Sjkh
2361156Sjkh	result = getpwuid((uid_t)id);
2371156Sjkh
2381156Sjkh	if (result == NULL)
2391156Sjkh		return (NULL);
2401156Sjkh
24118591Speter	return strdup(result->pw_name);
242697Spaul}
2431156Sjkh#endif
2441156Sjkh
2451156Sjkhstatic const char *
2461156Sjkhlookup_gname(void *data, int64_t gid)
2471156Sjkh{
2481156Sjkh	struct name_cache *gname_cache = (struct name_cache *)data;
2491156Sjkh	return (lookup_name(gname_cache,
250697Spaul		    &lookup_gname_helper, (id_t)gid));
251697Spaul}
2521156Sjkh
253697Spaul#if HAVE_GETGRGID_R
254697Spaulstatic const char *
255697Spaullookup_gname_helper(struct name_cache *cache, id_t id)
25613771Smpp{
257697Spaul	struct group	grent, *result;
2581156Sjkh	char * nbuff;
259697Spaul	size_t nbuff_size;
260697Spaul	int r;
2616887Snate
2626887Snate	if (cache->buff_size == 0) {
2636887Snate		cache->buff_size = 256;
26433137Sjdp		cache->buff = malloc(cache->buff_size);
265697Spaul	}
266697Spaul	if (cache->buff == NULL)
267697Spaul		return (NULL);
268697Spaul	for (;;) {
269697Spaul		result = &grent; /* Old getgrgid_r ignores last arg. */
270697Spaul		r = getgrgid_r((gid_t)id, &grent,
2711156Sjkh			       cache->buff, cache->buff_size, &result);
2721156Sjkh		if (r == 0)
2736887Snate			break;
27433137Sjdp		if (r != ERANGE)
275697Spaul			break;
276697Spaul		/* ERANGE means our buffer was too small, but POSIX
277697Spaul		 * doesn't tell us how big the buffer should be, so
278697Spaul		 * we just double it and try again. */
279697Spaul		nbuff_size = cache->buff_size * 2;
280697Spaul		nbuff = realloc(cache->buff, nbuff_size);
281697Spaul		if (nbuff == NULL)
282697Spaul			break;
283697Spaul		cache->buff = nbuff;
284697Spaul		cache->buff_size = nbuff_size;
285697Spaul	}
286697Spaul	if (r != 0) {
287697Spaul		archive_set_error(cache->archive, errno,
288697Spaul		    "Can't lookup group for id %d", (int)id);
28918591Speter		return (NULL);
290697Spaul	}
291697Spaul	if (result == NULL)
292697Spaul		return (NULL);
293697Spaul
294697Spaul	return strdup(result->gr_name);
29518591Speter}
296697Spaul#else
297697Spaulstatic const char *
298697Spaullookup_gname_helper(struct name_cache *cache, id_t id)
299697Spaul{
300697Spaul	struct group	*result;
301697Spaul
302697Spaul	result = getgrgid((gid_t)id);
303697Spaul
304697Spaul	if (result == NULL)
305697Spaul		return (NULL);
306697Spaul
307697Spaul	return strdup(result->gr_name);
308697Spaul}
309697Spaul#endif
310697Spaul
311697Spaul#endif /* ! (_WIN32 && !__CYGWIN__) */
312697Spaul