18029ab02SPeter Avalos /*-
28029ab02SPeter Avalos  * Copyright (c) 2003-2007 Tim Kientzle
38029ab02SPeter Avalos  * All rights reserved.
48029ab02SPeter Avalos  *
58029ab02SPeter Avalos  * Redistribution and use in source and binary forms, with or without
68029ab02SPeter Avalos  * modification, are permitted provided that the following conditions
78029ab02SPeter Avalos  * are met:
88029ab02SPeter Avalos  * 1. Redistributions of source code must retain the above copyright
98029ab02SPeter Avalos  *    notice, this list of conditions and the following disclaimer.
108029ab02SPeter Avalos  * 2. Redistributions in binary form must reproduce the above copyright
118029ab02SPeter Avalos  *    notice, this list of conditions and the following disclaimer in the
128029ab02SPeter Avalos  *    documentation and/or other materials provided with the distribution.
138029ab02SPeter Avalos  *
148029ab02SPeter Avalos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
158029ab02SPeter Avalos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
168029ab02SPeter Avalos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
178029ab02SPeter Avalos  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
188029ab02SPeter Avalos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
198029ab02SPeter Avalos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
208029ab02SPeter Avalos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
218029ab02SPeter Avalos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
228029ab02SPeter Avalos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
238029ab02SPeter Avalos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
248029ab02SPeter Avalos  */
258029ab02SPeter Avalos 
268029ab02SPeter Avalos #include "archive_platform.h"
279c82a63eSPeter Avalos __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_set_standard_lookup.c 201109 2009-12-28 03:30:31Z kientzle $");
288029ab02SPeter Avalos 
298029ab02SPeter Avalos #ifdef HAVE_SYS_TYPES_H
308029ab02SPeter Avalos #include <sys/types.h>
318029ab02SPeter Avalos #endif
328029ab02SPeter Avalos #ifdef HAVE_ERRNO_H
338029ab02SPeter Avalos #include <errno.h>
348029ab02SPeter Avalos #endif
358029ab02SPeter Avalos #ifdef HAVE_GRP_H
368029ab02SPeter Avalos #include <grp.h>
378029ab02SPeter Avalos #endif
388029ab02SPeter Avalos #ifdef HAVE_PWD_H
398029ab02SPeter Avalos #include <pwd.h>
408029ab02SPeter Avalos #endif
418029ab02SPeter Avalos #ifdef HAVE_STDLIB_H
428029ab02SPeter Avalos #include <stdlib.h>
438029ab02SPeter Avalos #endif
448029ab02SPeter Avalos #ifdef HAVE_STRING_H
458029ab02SPeter Avalos #include <string.h>
468029ab02SPeter Avalos #endif
478029ab02SPeter Avalos 
488029ab02SPeter Avalos #include "archive.h"
498029ab02SPeter Avalos 
508029ab02SPeter Avalos #if defined(_WIN32) && !defined(__CYGWIN__)
518029ab02SPeter Avalos int
archive_read_disk_set_standard_lookup(struct archive * a)528029ab02SPeter Avalos archive_read_disk_set_standard_lookup(struct archive *a)
538029ab02SPeter Avalos {
548029ab02SPeter Avalos 	archive_set_error(a, -1, "Standard lookups not available on Windows");
558029ab02SPeter Avalos 	return (ARCHIVE_FATAL);
568029ab02SPeter Avalos }
578029ab02SPeter Avalos #else /* ! (_WIN32 && !__CYGWIN__) */
588029ab02SPeter Avalos #define	name_cache_size 127
598029ab02SPeter Avalos 
608029ab02SPeter Avalos static const char * const NO_NAME = "(noname)";
618029ab02SPeter Avalos 
628029ab02SPeter Avalos struct name_cache {
638029ab02SPeter Avalos 	struct archive *archive;
648029ab02SPeter Avalos 	char   *buff;
658029ab02SPeter Avalos 	size_t  buff_size;
668029ab02SPeter Avalos 	int	probes;
678029ab02SPeter Avalos 	int	hits;
688029ab02SPeter Avalos 	size_t	size;
698029ab02SPeter Avalos 	struct {
708029ab02SPeter Avalos 		id_t id;
718029ab02SPeter Avalos 		const char *name;
728029ab02SPeter Avalos 	} cache[name_cache_size];
738029ab02SPeter Avalos };
748029ab02SPeter Avalos 
75c09f92d2SPeter Avalos static const char *	lookup_gname(void *, int64_t);
76c09f92d2SPeter Avalos static const char *	lookup_uname(void *, int64_t);
778029ab02SPeter Avalos static void	cleanup(void *);
788029ab02SPeter Avalos static const char *	lookup_gname_helper(struct name_cache *, id_t gid);
798029ab02SPeter Avalos static const char *	lookup_uname_helper(struct name_cache *, id_t uid);
808029ab02SPeter Avalos 
818029ab02SPeter Avalos /*
828029ab02SPeter Avalos  * Installs functions that use getpwuid()/getgrgid()---along with
838029ab02SPeter Avalos  * a simple cache to accelerate such lookups---into the archive_read_disk
848029ab02SPeter Avalos  * object.  This is in a separate file because getpwuid()/getgrgid()
858029ab02SPeter Avalos  * can pull in a LOT of library code (including NIS/LDAP functions, which
866b384f39SPeter Avalos  * pull in DNS resolvers, etc).  This can easily top 500kB, which makes
878029ab02SPeter Avalos  * it inappropriate for some space-constrained applications.
888029ab02SPeter Avalos  *
898029ab02SPeter Avalos  * Applications that are size-sensitive may want to just use the
908029ab02SPeter Avalos  * real default functions (defined in archive_read_disk.c) that just
918029ab02SPeter Avalos  * use the uid/gid without the lookup.  Or define your own custom functions
928029ab02SPeter Avalos  * if you prefer.
938029ab02SPeter Avalos  */
948029ab02SPeter Avalos int
archive_read_disk_set_standard_lookup(struct archive * a)958029ab02SPeter Avalos archive_read_disk_set_standard_lookup(struct archive *a)
968029ab02SPeter Avalos {
978029ab02SPeter Avalos 	struct name_cache *ucache = malloc(sizeof(struct name_cache));
988029ab02SPeter Avalos 	struct name_cache *gcache = malloc(sizeof(struct name_cache));
998029ab02SPeter Avalos 
1008029ab02SPeter Avalos 	if (ucache == NULL || gcache == NULL) {
1018029ab02SPeter Avalos 		archive_set_error(a, ENOMEM,
1028029ab02SPeter Avalos 		    "Can't allocate uname/gname lookup cache");
1038029ab02SPeter Avalos 		free(ucache);
1048029ab02SPeter Avalos 		free(gcache);
1058029ab02SPeter Avalos 		return (ARCHIVE_FATAL);
1068029ab02SPeter Avalos 	}
1078029ab02SPeter Avalos 
1088029ab02SPeter Avalos 	memset(ucache, 0, sizeof(*ucache));
1098029ab02SPeter Avalos 	ucache->archive = a;
1108029ab02SPeter Avalos 	ucache->size = name_cache_size;
1118029ab02SPeter Avalos 	memset(gcache, 0, sizeof(*gcache));
1128029ab02SPeter Avalos 	gcache->archive = a;
1138029ab02SPeter Avalos 	gcache->size = name_cache_size;
1148029ab02SPeter Avalos 
1158029ab02SPeter Avalos 	archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
1168029ab02SPeter Avalos 	archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);
1178029ab02SPeter Avalos 
1188029ab02SPeter Avalos 	return (ARCHIVE_OK);
1198029ab02SPeter Avalos }
1208029ab02SPeter Avalos 
1218029ab02SPeter Avalos static void
cleanup(void * data)1228029ab02SPeter Avalos cleanup(void *data)
1238029ab02SPeter Avalos {
1248029ab02SPeter Avalos 	struct name_cache *cache = (struct name_cache *)data;
1258029ab02SPeter Avalos 	size_t i;
1268029ab02SPeter Avalos 
1278029ab02SPeter Avalos 	if (cache != NULL) {
1288029ab02SPeter Avalos 		for (i = 0; i < cache->size; i++) {
1298029ab02SPeter Avalos 			if (cache->cache[i].name != NULL &&
1308029ab02SPeter Avalos 			    cache->cache[i].name != NO_NAME)
1318029ab02SPeter Avalos 				free((void *)(uintptr_t)cache->cache[i].name);
1328029ab02SPeter Avalos 		}
1338029ab02SPeter Avalos 		free(cache->buff);
1348029ab02SPeter Avalos 		free(cache);
1358029ab02SPeter Avalos 	}
1368029ab02SPeter Avalos }
1378029ab02SPeter Avalos 
1388029ab02SPeter Avalos /*
1398029ab02SPeter Avalos  * Lookup uid/gid from uname/gname, return NULL if no match.
1408029ab02SPeter Avalos  */
1418029ab02SPeter Avalos static const char *
lookup_name(struct name_cache * cache,const char * (* lookup_fn)(struct name_cache *,id_t),id_t id)1428029ab02SPeter Avalos lookup_name(struct name_cache *cache,
1438029ab02SPeter Avalos     const char * (*lookup_fn)(struct name_cache *, id_t), id_t id)
1448029ab02SPeter Avalos {
1458029ab02SPeter Avalos 	const char *name;
1468029ab02SPeter Avalos 	int slot;
1478029ab02SPeter Avalos 
1488029ab02SPeter Avalos 
1498029ab02SPeter Avalos 	cache->probes++;
1508029ab02SPeter Avalos 
1518029ab02SPeter Avalos 	slot = id % cache->size;
1528029ab02SPeter Avalos 	if (cache->cache[slot].name != NULL) {
1538029ab02SPeter Avalos 		if (cache->cache[slot].id == id) {
1548029ab02SPeter Avalos 			cache->hits++;
1558029ab02SPeter Avalos 			if (cache->cache[slot].name == NO_NAME)
1568029ab02SPeter Avalos 				return (NULL);
1578029ab02SPeter Avalos 			return (cache->cache[slot].name);
1588029ab02SPeter Avalos 		}
1598029ab02SPeter Avalos 		if (cache->cache[slot].name != NO_NAME)
1608029ab02SPeter Avalos 			free((void *)(uintptr_t)cache->cache[slot].name);
1618029ab02SPeter Avalos 		cache->cache[slot].name = NULL;
1628029ab02SPeter Avalos 	}
1638029ab02SPeter Avalos 
1648029ab02SPeter Avalos 	name = (lookup_fn)(cache, id);
1658029ab02SPeter Avalos 	if (name == NULL) {
1668029ab02SPeter Avalos 		/* Cache and return the negative response. */
1678029ab02SPeter Avalos 		cache->cache[slot].name = NO_NAME;
1688029ab02SPeter Avalos 		cache->cache[slot].id = id;
1698029ab02SPeter Avalos 		return (NULL);
1708029ab02SPeter Avalos 	}
1718029ab02SPeter Avalos 
1728029ab02SPeter Avalos 	cache->cache[slot].name = name;
1738029ab02SPeter Avalos 	cache->cache[slot].id = id;
1748029ab02SPeter Avalos 	return (cache->cache[slot].name);
1758029ab02SPeter Avalos }
1768029ab02SPeter Avalos 
1778029ab02SPeter Avalos static const char *
lookup_uname(void * data,int64_t uid)178c09f92d2SPeter Avalos lookup_uname(void *data, int64_t uid)
1798029ab02SPeter Avalos {
1808029ab02SPeter Avalos 	struct name_cache *uname_cache = (struct name_cache *)data;
1818029ab02SPeter Avalos 	return (lookup_name(uname_cache,
1828029ab02SPeter Avalos 		    &lookup_uname_helper, (id_t)uid));
1838029ab02SPeter Avalos }
1848029ab02SPeter Avalos 
1859c82a63eSPeter Avalos #if HAVE_GETPWUID_R
1868029ab02SPeter Avalos static const char *
lookup_uname_helper(struct name_cache * cache,id_t id)1878029ab02SPeter Avalos lookup_uname_helper(struct name_cache *cache, id_t id)
1888029ab02SPeter Avalos {
1898029ab02SPeter Avalos 	struct passwd	pwent, *result;
190c09f92d2SPeter Avalos 	char * nbuff;
191c09f92d2SPeter Avalos 	size_t nbuff_size;
1928029ab02SPeter Avalos 	int r;
1938029ab02SPeter Avalos 
1948029ab02SPeter Avalos 	if (cache->buff_size == 0) {
1958029ab02SPeter Avalos 		cache->buff_size = 256;
1968029ab02SPeter Avalos 		cache->buff = malloc(cache->buff_size);
1978029ab02SPeter Avalos 	}
1988029ab02SPeter Avalos 	if (cache->buff == NULL)
1998029ab02SPeter Avalos 		return (NULL);
2008029ab02SPeter Avalos 	for (;;) {
2019c82a63eSPeter Avalos 		result = &pwent; /* Old getpwuid_r ignores last arg. */
2028029ab02SPeter Avalos 		r = getpwuid_r((uid_t)id, &pwent,
2038029ab02SPeter Avalos 			       cache->buff, cache->buff_size, &result);
2048029ab02SPeter Avalos 		if (r == 0)
2058029ab02SPeter Avalos 			break;
2068029ab02SPeter Avalos 		if (r != ERANGE)
2078029ab02SPeter Avalos 			break;
2088029ab02SPeter Avalos 		/* ERANGE means our buffer was too small, but POSIX
2098029ab02SPeter Avalos 		 * doesn't tell us how big the buffer should be, so
2108029ab02SPeter Avalos 		 * we just double it and try again.  Because the buffer
2118029ab02SPeter Avalos 		 * is kept around in the cache object, we shouldn't
2128029ab02SPeter Avalos 		 * have to do this very often. */
213c09f92d2SPeter Avalos 		nbuff_size = cache->buff_size * 2;
214c09f92d2SPeter Avalos 		nbuff = realloc(cache->buff, nbuff_size);
215c09f92d2SPeter Avalos 		if (nbuff == NULL)
2168029ab02SPeter Avalos 			break;
217c09f92d2SPeter Avalos 		cache->buff = nbuff;
218c09f92d2SPeter Avalos 		cache->buff_size = nbuff_size;
2198029ab02SPeter Avalos 	}
2208029ab02SPeter Avalos 	if (r != 0) {
2218029ab02SPeter Avalos 		archive_set_error(cache->archive, errno,
2228029ab02SPeter Avalos 		    "Can't lookup user for id %d", (int)id);
2238029ab02SPeter Avalos 		return (NULL);
2248029ab02SPeter Avalos 	}
2258029ab02SPeter Avalos 	if (result == NULL)
2268029ab02SPeter Avalos 		return (NULL);
2278029ab02SPeter Avalos 
2288029ab02SPeter Avalos 	return strdup(result->pw_name);
2298029ab02SPeter Avalos }
2309c82a63eSPeter Avalos #else
2319c82a63eSPeter Avalos static const char *
lookup_uname_helper(struct name_cache * cache,id_t id)2329c82a63eSPeter Avalos lookup_uname_helper(struct name_cache *cache, id_t id)
2339c82a63eSPeter Avalos {
2349c82a63eSPeter Avalos 	struct passwd	*result;
235e95abc47Szrj 	(void)cache; /* UNUSED */
2369c82a63eSPeter Avalos 
2379c82a63eSPeter Avalos 	result = getpwuid((uid_t)id);
2389c82a63eSPeter Avalos 
2399c82a63eSPeter Avalos 	if (result == NULL)
2409c82a63eSPeter Avalos 		return (NULL);
2419c82a63eSPeter Avalos 
2429c82a63eSPeter Avalos 	return strdup(result->pw_name);
2439c82a63eSPeter Avalos }
2449c82a63eSPeter Avalos #endif
2458029ab02SPeter Avalos 
2468029ab02SPeter Avalos static const char *
lookup_gname(void * data,int64_t gid)247c09f92d2SPeter Avalos lookup_gname(void *data, int64_t gid)
2488029ab02SPeter Avalos {
2498029ab02SPeter Avalos 	struct name_cache *gname_cache = (struct name_cache *)data;
2508029ab02SPeter Avalos 	return (lookup_name(gname_cache,
2518029ab02SPeter Avalos 		    &lookup_gname_helper, (id_t)gid));
2528029ab02SPeter Avalos }
2538029ab02SPeter Avalos 
2549c82a63eSPeter Avalos #if HAVE_GETGRGID_R
2558029ab02SPeter Avalos static const char *
lookup_gname_helper(struct name_cache * cache,id_t id)2568029ab02SPeter Avalos lookup_gname_helper(struct name_cache *cache, id_t id)
2578029ab02SPeter Avalos {
2588029ab02SPeter Avalos 	struct group	grent, *result;
259c09f92d2SPeter Avalos 	char * nbuff;
260c09f92d2SPeter Avalos 	size_t nbuff_size;
2618029ab02SPeter Avalos 	int r;
2628029ab02SPeter Avalos 
2638029ab02SPeter Avalos 	if (cache->buff_size == 0) {
2648029ab02SPeter Avalos 		cache->buff_size = 256;
2658029ab02SPeter Avalos 		cache->buff = malloc(cache->buff_size);
2668029ab02SPeter Avalos 	}
2678029ab02SPeter Avalos 	if (cache->buff == NULL)
2688029ab02SPeter Avalos 		return (NULL);
2698029ab02SPeter Avalos 	for (;;) {
2709c82a63eSPeter Avalos 		result = &grent; /* Old getgrgid_r ignores last arg. */
2718029ab02SPeter Avalos 		r = getgrgid_r((gid_t)id, &grent,
2728029ab02SPeter Avalos 			       cache->buff, cache->buff_size, &result);
2738029ab02SPeter Avalos 		if (r == 0)
2748029ab02SPeter Avalos 			break;
2758029ab02SPeter Avalos 		if (r != ERANGE)
2768029ab02SPeter Avalos 			break;
2778029ab02SPeter Avalos 		/* ERANGE means our buffer was too small, but POSIX
2788029ab02SPeter Avalos 		 * doesn't tell us how big the buffer should be, so
2798029ab02SPeter Avalos 		 * we just double it and try again. */
280c09f92d2SPeter Avalos 		nbuff_size = cache->buff_size * 2;
281c09f92d2SPeter Avalos 		nbuff = realloc(cache->buff, nbuff_size);
282c09f92d2SPeter Avalos 		if (nbuff == NULL)
2838029ab02SPeter Avalos 			break;
284c09f92d2SPeter Avalos 		cache->buff = nbuff;
285c09f92d2SPeter Avalos 		cache->buff_size = nbuff_size;
2868029ab02SPeter Avalos 	}
2878029ab02SPeter Avalos 	if (r != 0) {
2888029ab02SPeter Avalos 		archive_set_error(cache->archive, errno,
2898029ab02SPeter Avalos 		    "Can't lookup group for id %d", (int)id);
2908029ab02SPeter Avalos 		return (NULL);
2918029ab02SPeter Avalos 	}
2928029ab02SPeter Avalos 	if (result == NULL)
2938029ab02SPeter Avalos 		return (NULL);
2948029ab02SPeter Avalos 
2958029ab02SPeter Avalos 	return strdup(result->gr_name);
2968029ab02SPeter Avalos }
2979c82a63eSPeter Avalos #else
2989c82a63eSPeter Avalos static const char *
lookup_gname_helper(struct name_cache * cache,id_t id)2999c82a63eSPeter Avalos lookup_gname_helper(struct name_cache *cache, id_t id)
3009c82a63eSPeter Avalos {
3019c82a63eSPeter Avalos 	struct group	*result;
302e95abc47Szrj 	(void)cache; /* UNUSED */
3039c82a63eSPeter Avalos 
3049c82a63eSPeter Avalos 	result = getgrgid((gid_t)id);
3059c82a63eSPeter Avalos 
3069c82a63eSPeter Avalos 	if (result == NULL)
3079c82a63eSPeter Avalos 		return (NULL);
3089c82a63eSPeter Avalos 
3099c82a63eSPeter Avalos 	return strdup(result->gr_name);
3109c82a63eSPeter Avalos }
3119c82a63eSPeter Avalos #endif
3129c82a63eSPeter Avalos 
3138029ab02SPeter Avalos #endif /* ! (_WIN32 && !__CYGWIN__) */
314