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__FBSDID("$FreeBSD$"); 28 29#ifdef HAVE_SYS_TYPES_H 30#include <sys/types.h> 31#endif 32#ifdef HAVE_ERRNO_H 33#include <errno.h> 34#endif 35#ifdef HAVE_GRP_H 36#include <grp.h> 37#endif 38#ifdef HAVE_PWD_H 39#include <pwd.h> 40#endif 41#ifdef HAVE_STDLIB_H 42#include <stdlib.h> 43#endif 44#ifdef HAVE_STRING_H 45#include <string.h> 46#endif 47 48#include "archive.h" 49 50#if defined(_WIN32) && !defined(__CYGWIN__) 51int 52archive_read_disk_set_standard_lookup(struct archive *a) 53{ 54 archive_set_error(a, -1, "Standard lookups not available on Windows"); 55 return (ARCHIVE_FATAL); 56} 57#else /* ! (_WIN32 && !__CYGWIN__) */ 58#define name_cache_size 127 59 60static const char * const NO_NAME = "(noname)"; 61 62struct name_cache { 63 struct archive *archive; 64 char *buff; 65 size_t buff_size; 66 int probes; 67 int hits; 68 size_t size; 69 struct { 70 id_t id; 71 const char *name; 72 } cache[name_cache_size]; 73}; 74 75static const char * lookup_gname(void *, gid_t); 76static const char * lookup_uname(void *, uid_t); 77static void cleanup(void *); 78static const char * lookup_gname_helper(struct name_cache *, id_t gid); 79static const char * lookup_uname_helper(struct name_cache *, id_t uid); 80 81/* 82 * Installs functions that use getpwuid()/getgrgid()---along with 83 * a simple cache to accelerate such lookups---into the archive_read_disk 84 * object. This is in a separate file because getpwuid()/getgrgid() 85 * can pull in a LOT of library code (including NIS/LDAP functions, which 86 * pull in DNS resolveers, etc). This can easily top 500kB, which makes 87 * it inappropriate for some space-constrained applications. 88 * 89 * Applications that are size-sensitive may want to just use the 90 * real default functions (defined in archive_read_disk.c) that just 91 * use the uid/gid without the lookup. Or define your own custom functions 92 * if you prefer. 93 */ 94int 95archive_read_disk_set_standard_lookup(struct archive *a) 96{ 97 struct name_cache *ucache = malloc(sizeof(struct name_cache)); 98 struct name_cache *gcache = malloc(sizeof(struct name_cache)); 99 100 if (ucache == NULL || gcache == NULL) { 101 archive_set_error(a, ENOMEM, 102 "Can't allocate uname/gname lookup cache"); 103 free(ucache); 104 free(gcache); 105 return (ARCHIVE_FATAL); 106 } 107 108 memset(ucache, 0, sizeof(*ucache)); 109 ucache->archive = a; 110 ucache->size = name_cache_size; 111 memset(gcache, 0, sizeof(*gcache)); 112 gcache->archive = a; 113 gcache->size = name_cache_size; 114 115 archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup); 116 archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup); 117 118 return (ARCHIVE_OK); 119} 120 121static void 122cleanup(void *data) 123{ 124 struct name_cache *cache = (struct name_cache *)data; 125 size_t i; 126 127 if (cache != NULL) { 128 for (i = 0; i < cache->size; i++) { 129 if (cache->cache[i].name != NULL && 130 cache->cache[i].name != NO_NAME) 131 free((void *)(uintptr_t)cache->cache[i].name); 132 } 133 free(cache->buff); 134 free(cache); 135 } 136} 137 138/* 139 * Lookup uid/gid from uname/gname, return NULL if no match. 140 */ 141static const char * 142lookup_name(struct name_cache *cache, 143 const char * (*lookup_fn)(struct name_cache *, id_t), id_t id) 144{ 145 const char *name; 146 int slot; 147 148 149 cache->probes++; 150 151 slot = id % cache->size; 152 if (cache->cache[slot].name != NULL) { 153 if (cache->cache[slot].id == id) { 154 cache->hits++; 155 if (cache->cache[slot].name == NO_NAME) 156 return (NULL); 157 return (cache->cache[slot].name); 158 } 159 if (cache->cache[slot].name != NO_NAME) 160 free((void *)(uintptr_t)cache->cache[slot].name); 161 cache->cache[slot].name = NULL; 162 } 163 164 name = (lookup_fn)(cache, id); 165 if (name == NULL) { 166 /* Cache and return the negative response. */ 167 cache->cache[slot].name = NO_NAME; 168 cache->cache[slot].id = id; 169 return (NULL); 170 } 171 172 cache->cache[slot].name = name; 173 cache->cache[slot].id = id; 174 return (cache->cache[slot].name); 175} 176 177static const char * 178lookup_uname(void *data, uid_t uid) 179{ 180 struct name_cache *uname_cache = (struct name_cache *)data; 181 return (lookup_name(uname_cache, 182 &lookup_uname_helper, (id_t)uid)); 183} 184 185#if HAVE_GETPWUID_R 186static const char * 187lookup_uname_helper(struct name_cache *cache, id_t id) 188{ 189 struct passwd pwent, *result; 190 int r; 191 192 if (cache->buff_size == 0) { 193 cache->buff_size = 256; 194 cache->buff = malloc(cache->buff_size); 195 } 196 if (cache->buff == NULL) 197 return (NULL); 198 for (;;) { 199 result = &pwent; /* Old getpwuid_r ignores last arg. */ 200 r = getpwuid_r((uid_t)id, &pwent, 201 cache->buff, cache->buff_size, &result); 202 if (r == 0) 203 break; 204 if (r != ERANGE) 205 break; 206 /* ERANGE means our buffer was too small, but POSIX 207 * doesn't tell us how big the buffer should be, so 208 * we just double it and try again. Because the buffer 209 * is kept around in the cache object, we shouldn't 210 * have to do this very often. */ 211 cache->buff_size *= 2; 212 cache->buff = realloc(cache->buff, cache->buff_size); 213 if (cache->buff == NULL) 214 break; 215 } 216 if (r != 0) { 217 archive_set_error(cache->archive, errno, 218 "Can't lookup user for id %d", (int)id); 219 return (NULL); 220 } 221 if (result == NULL) 222 return (NULL); 223 224 return strdup(result->pw_name); 225} 226#else 227static const char * 228lookup_uname_helper(struct name_cache *cache, id_t id) 229{ 230 struct passwd *result; 231 232 result = getpwuid((uid_t)id); 233 234 if (result == NULL) 235 return (NULL); 236 237 return strdup(result->pw_name); 238} 239#endif 240 241static const char * 242lookup_gname(void *data, gid_t gid) 243{ 244 struct name_cache *gname_cache = (struct name_cache *)data; 245 return (lookup_name(gname_cache, 246 &lookup_gname_helper, (id_t)gid)); 247} 248 249#if HAVE_GETGRGID_R 250static const char * 251lookup_gname_helper(struct name_cache *cache, id_t id) 252{ 253 struct group grent, *result; 254 int r; 255 256 if (cache->buff_size == 0) { 257 cache->buff_size = 256; 258 cache->buff = malloc(cache->buff_size); 259 } 260 if (cache->buff == NULL) 261 return (NULL); 262 for (;;) { 263 result = &grent; /* Old getgrgid_r ignores last arg. */ 264 r = getgrgid_r((gid_t)id, &grent, 265 cache->buff, cache->buff_size, &result); 266 if (r == 0) 267 break; 268 if (r != ERANGE) 269 break; 270 /* ERANGE means our buffer was too small, but POSIX 271 * doesn't tell us how big the buffer should be, so 272 * we just double it and try again. */ 273 cache->buff_size *= 2; 274 cache->buff = realloc(cache->buff, cache->buff_size); 275 if (cache->buff == NULL) 276 break; 277 } 278 if (r != 0) { 279 archive_set_error(cache->archive, errno, 280 "Can't lookup group for id %d", (int)id); 281 return (NULL); 282 } 283 if (result == NULL) 284 return (NULL); 285 286 return strdup(result->gr_name); 287} 288#else 289static const char * 290lookup_gname_helper(struct name_cache *cache, id_t id) 291{ 292 struct group *result; 293 294 result = getgrgid((gid_t)id); 295 296 if (result == NULL) 297 return (NULL); 298 299 return strdup(result->gr_name); 300} 301#endif 302 303#endif /* ! (_WIN32 && !__CYGWIN__) */ 304