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