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