]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libarchive/archive_read_disk_set_standard_lookup.c
Merge libpcap 1.0.0.
[FreeBSD/FreeBSD.git] / lib / libarchive / archive_read_disk_set_standard_lookup.c
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 #ifdef _WIN32
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
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         int     probes;
65         int     hits;
66         size_t  size;
67         struct {
68                 id_t id;
69                 const char *name;
70         } cache[name_cache_size];
71 };
72
73 static const char *     lookup_gname(void *, gid_t);
74 static const char *     lookup_uname(void *, uid_t);
75 static void     cleanup(void *);
76 static const char *     lookup_gname_helper(struct archive *, id_t gid);
77 static const char *     lookup_uname_helper(struct archive *, id_t uid);
78
79 /*
80  * Installs functions that use getpwuid()/getgrgid()---along with
81  * a simple cache to accelerate such lookups---into the archive_read_disk
82  * object.  This is in a separate file because getpwuid()/getgrgid()
83  * can pull in a LOT of library code (including NIS/LDAP functions, which
84  * pull in DNS resolveers, etc).  This can easily top 500kB, which makes
85  * it inappropriate for some space-constrained applications.
86  *
87  * Applications that are size-sensitive may want to just use the
88  * real default functions (defined in archive_read_disk.c) that just
89  * use the uid/gid without the lookup.  Or define your own custom functions
90  * if you prefer.
91  */
92 int
93 archive_read_disk_set_standard_lookup(struct archive *a)
94 {
95         struct name_cache *ucache = malloc(sizeof(struct name_cache));
96         struct name_cache *gcache = malloc(sizeof(struct name_cache));
97
98         if (ucache == NULL || gcache == NULL) {
99                 archive_set_error(a, ENOMEM,
100                     "Can't allocate uname/gname lookup cache");
101                 free(ucache);
102                 free(gcache);
103                 return (ARCHIVE_FATAL);
104         }
105
106         memset(ucache, 0, sizeof(*ucache));
107         ucache->archive = a;
108         ucache->size = name_cache_size;
109         memset(gcache, 0, sizeof(*gcache));
110         gcache->archive = a;
111         gcache->size = name_cache_size;
112
113         archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
114         archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);
115
116         return (ARCHIVE_OK);
117 }
118
119 static void
120 cleanup(void *data)
121 {
122         struct name_cache *cache = (struct name_cache *)data;
123         size_t i;
124
125         if (cache != NULL) {
126                 for (i = 0; i < cache->size; i++) {
127                         if (cache->cache[i].name != NULL &&
128                             cache->cache[i].name != NO_NAME)
129                                 free((void *)(uintptr_t)cache->cache[i].name);
130                 }
131                 free(cache);
132         }
133 }
134
135 /*
136  * Lookup uid/gid from uname/gname, return NULL if no match.
137  */
138 static const char *
139 lookup_name(struct name_cache *cache,
140     const char * (*lookup_fn)(struct archive *, id_t), id_t id)
141 {
142         const char *name;
143         int slot;
144
145
146         cache->probes++;
147
148         slot = id % cache->size;
149         if (cache->cache[slot].name != NULL) {
150                 if (cache->cache[slot].id == id) {
151                         cache->hits++;
152                         if (cache->cache[slot].name == NO_NAME)
153                                 return (NULL);
154                         return (cache->cache[slot].name);
155                 }
156                 if (cache->cache[slot].name != NO_NAME)
157                         free((void *)(uintptr_t)cache->cache[slot].name);
158                 cache->cache[slot].name = NULL;
159         }
160
161         name = (lookup_fn)(cache->archive, id);
162         if (name == NULL) {
163                 /* Cache and return the negative response. */
164                 cache->cache[slot].name = NO_NAME;
165                 cache->cache[slot].id = id;
166                 return (NULL);
167         }
168
169         cache->cache[slot].name = name;
170         cache->cache[slot].id = id;
171         return (cache->cache[slot].name);
172 }
173
174 static const char *
175 lookup_uname(void *data, uid_t uid)
176 {
177         struct name_cache *uname_cache = (struct name_cache *)data;
178         return (lookup_name(uname_cache,
179                     &lookup_uname_helper, (id_t)uid));
180 }
181
182 static const char *
183 lookup_uname_helper(struct archive *a, id_t id)
184 {
185         char buffer[64];
186         struct passwd   pwent, *result;
187         int r;
188
189         errno = 0;
190         r = getpwuid_r((uid_t)id, &pwent, buffer, sizeof(buffer), &result);
191         if (r != 0) {
192                 archive_set_error(a, errno,
193                     "Can't lookup user for id %d", (int)id);
194                 return (NULL);
195         }
196         if (result == NULL)
197                 return (NULL);
198
199         return strdup(pwent.pw_name);
200 }
201
202 static const char *
203 lookup_gname(void *data, gid_t gid)
204 {
205         struct name_cache *gname_cache = (struct name_cache *)data;
206         return (lookup_name(gname_cache,
207                     &lookup_gname_helper, (id_t)gid));
208 }
209
210 static const char *
211 lookup_gname_helper(struct archive *a, id_t id)
212 {
213         char buffer[64];
214         struct group    grent, *result;
215         int r;
216
217         errno = 0;
218         r = getgrgid_r((gid_t)id, &grent, buffer, sizeof(buffer), &result);
219         if (r != 0) {
220                 archive_set_error(a, errno,
221                     "Can't lookup group for id %d", (int)id);
222                 return (NULL);
223         }
224         if (result == NULL)
225                 return (NULL);
226
227         return strdup(grent.gr_name);
228 }
229 #endif /* _WIN32 */