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: head/lib/libarchive/archive_read_disk_set_standard_lookup.c 201109 2009-12-28 03:30:31Z kientzle $"); 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__) 51 int 52 archive_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 60 static const char * const NO_NAME = "(noname)"; 61 62 struct 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 75 static const char * lookup_gname(void *, int64_t); 76 static const char * lookup_uname(void *, int64_t); 77 static void cleanup(void *); 78 static const char * lookup_gname_helper(struct name_cache *, id_t gid); 79 static 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 resolvers, 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 */ 94 int 95 archive_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 121 static void 122 cleanup(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 */ 141 static const char * 142 lookup_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 177 static const char * 178 lookup_uname(void *data, int64_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 186 static const char * 187 lookup_uname_helper(struct name_cache *cache, id_t id) 188 { 189 struct passwd pwent, *result; 190 char * nbuff; 191 size_t nbuff_size; 192 int r; 193 194 if (cache->buff_size == 0) { 195 cache->buff_size = 256; 196 cache->buff = malloc(cache->buff_size); 197 } 198 if (cache->buff == NULL) 199 return (NULL); 200 for (;;) { 201 result = &pwent; /* Old getpwuid_r ignores last arg. */ 202 r = getpwuid_r((uid_t)id, &pwent, 203 cache->buff, cache->buff_size, &result); 204 if (r == 0) 205 break; 206 if (r != ERANGE) 207 break; 208 /* ERANGE means our buffer was too small, but POSIX 209 * doesn't tell us how big the buffer should be, so 210 * we just double it and try again. Because the buffer 211 * is kept around in the cache object, we shouldn't 212 * have to do this very often. */ 213 nbuff_size = cache->buff_size * 2; 214 nbuff = realloc(cache->buff, nbuff_size); 215 if (nbuff == NULL) 216 break; 217 cache->buff = nbuff; 218 cache->buff_size = nbuff_size; 219 } 220 if (r != 0) { 221 archive_set_error(cache->archive, errno, 222 "Can't lookup user for id %d", (int)id); 223 return (NULL); 224 } 225 if (result == NULL) 226 return (NULL); 227 228 return strdup(result->pw_name); 229 } 230 #else 231 static const char * 232 lookup_uname_helper(struct name_cache *cache, id_t id) 233 { 234 struct passwd *result; 235 (void)cache; /* UNUSED */ 236 237 result = getpwuid((uid_t)id); 238 239 if (result == NULL) 240 return (NULL); 241 242 return strdup(result->pw_name); 243 } 244 #endif 245 246 static const char * 247 lookup_gname(void *data, int64_t gid) 248 { 249 struct name_cache *gname_cache = (struct name_cache *)data; 250 return (lookup_name(gname_cache, 251 &lookup_gname_helper, (id_t)gid)); 252 } 253 254 #if HAVE_GETGRGID_R 255 static const char * 256 lookup_gname_helper(struct name_cache *cache, id_t id) 257 { 258 struct group grent, *result; 259 char * nbuff; 260 size_t nbuff_size; 261 int r; 262 263 if (cache->buff_size == 0) { 264 cache->buff_size = 256; 265 cache->buff = malloc(cache->buff_size); 266 } 267 if (cache->buff == NULL) 268 return (NULL); 269 for (;;) { 270 result = &grent; /* Old getgrgid_r ignores last arg. */ 271 r = getgrgid_r((gid_t)id, &grent, 272 cache->buff, cache->buff_size, &result); 273 if (r == 0) 274 break; 275 if (r != ERANGE) 276 break; 277 /* ERANGE means our buffer was too small, but POSIX 278 * doesn't tell us how big the buffer should be, so 279 * we just double it and try again. */ 280 nbuff_size = cache->buff_size * 2; 281 nbuff = realloc(cache->buff, nbuff_size); 282 if (nbuff == NULL) 283 break; 284 cache->buff = nbuff; 285 cache->buff_size = nbuff_size; 286 } 287 if (r != 0) { 288 archive_set_error(cache->archive, errno, 289 "Can't lookup group for id %d", (int)id); 290 return (NULL); 291 } 292 if (result == NULL) 293 return (NULL); 294 295 return strdup(result->gr_name); 296 } 297 #else 298 static const char * 299 lookup_gname_helper(struct name_cache *cache, id_t id) 300 { 301 struct group *result; 302 (void)cache; /* UNUSED */ 303 304 result = getgrgid((gid_t)id); 305 306 if (result == NULL) 307 return (NULL); 308 309 return strdup(result->gr_name); 310 } 311 #endif 312 313 #endif /* ! (_WIN32 && !__CYGWIN__) */ 314