2 * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/types.h>
41 * Constants and data structures used to implement the thread-safe
42 * group and password file caches. Cache sizes must be prime.
44 #define UIDTONAME_SZ 317 /* Size of uid -> user name cache */
45 #define NAMETOUID_SZ 317 /* Size of user name -> uid cache */
46 #define GIDTONAME_SZ 317 /* Size of gid -> group name cache */
47 #define NAMETOGID_SZ 317 /* Size of group name -> gid cache */
49 /* Node structures used to cache lookups. */
51 char *name; /* user name */
52 uid_t uid; /* cached uid */
53 int valid; /* is this a valid or a miss entry */
54 struct uidc *next; /* for collisions */
58 char *name; /* group name */
59 gid_t gid; /* cached gid */
60 int valid; /* is this a valid or a miss entry */
61 struct gidc *next; /* for collisions */
64 static struct uidc **uidtoname; /* uid to user name cache */
65 static struct gidc **gidtoname; /* gid to group name cache */
66 static struct uidc **nametouid; /* user name to uid cache */
67 static struct gidc **nametogid; /* group name to gid cache */
69 static pthread_mutex_t uid_mtx;
70 static pthread_mutex_t gid_mtx;
72 static void uid_lock(void);
73 static void uid_unlock(void);
74 static void gid_lock(void);
75 static void gid_unlock(void);
77 static uint32_t hash(const char *);
79 /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
80 as used by ELF for hashing function names. */
82 hash(const char *name)
87 while(*name != '\0') {
88 h = (h << 4) + *name++;
89 if ((g = h & 0xF0000000)) {
102 error = pthread_mutex_lock(&uid_mtx);
111 error = pthread_mutex_unlock(&uid_mtx);
120 error = pthread_mutex_lock(&gid_mtx);
129 error = pthread_mutex_unlock(&gid_mtx);
134 uidc_insert(struct uidc **tbl, struct uidc *uidc, uint32_t key)
137 uidc->next = tbl[key];
142 gidc_insert(struct gidc **tbl, struct gidc *gidc, uint32_t key)
145 gidc->next = tbl[key];
149 /* Return the user name for this uid, or NULL if it's not found. */
151 getuserbyid(uid_t uid)
154 struct uidc *uidc, *uidc2;
157 key = uid % UIDTONAME_SZ;
159 uidc = uidtoname[key];
160 while (uidc != NULL) {
161 if (uidc->uid == uid)
167 /* We didn't find this uid, look it up and add it. */
168 uidc = xmalloc(sizeof(struct uidc));
172 /* This uid is in the password file. */
173 uidc->name = xstrdup(pw->pw_name);
175 /* Also add it to the name -> gid table. */
176 uidc2 = xmalloc(sizeof(struct uidc));
178 uidc2->name = uidc->name; /* We reuse the pointer. */
180 key2 = hash(uidc->name) % NAMETOUID_SZ;
181 uidc_insert(nametouid, uidc2, key2);
183 /* Add a miss entry for this uid. */
187 uidc_insert(uidtoname, uidc, key);
189 /* It is safe to unlock here since the cache structure
190 is not going to get freed or changed. */
195 /* Return the group name for this gid, or NULL if it's not found. */
197 getgroupbyid(gid_t gid)
200 struct gidc *gidc, *gidc2;
203 key = gid % GIDTONAME_SZ;
205 gidc = gidtoname[key];
206 while (gidc != NULL) {
207 if (gidc->gid == gid)
213 /* We didn't find this gid, look it up and add it. */
214 gidc = xmalloc(sizeof(struct gidc));
218 /* This gid is in the group file. */
219 gidc->name = xstrdup(gr->gr_name);
221 /* Also add it to the name -> gid table. */
222 gidc2 = xmalloc(sizeof(struct gidc));
224 gidc2->name = gidc->name; /* We reuse the pointer. */
226 key2 = hash(gidc->name) % NAMETOGID_SZ;
227 gidc_insert(nametogid, gidc2, key2);
229 /* Add a miss entry for this gid. */
233 gidc_insert(gidtoname, gidc, key);
235 /* It is safe to unlock here since the cache structure
236 is not going to get freed or changed. */
241 /* Finds the uid for this user name. If it's found, the gid is stored
242 in *uid and 0 is returned. Otherwise, -1 is returned. */
244 getuidbyname(const char *name, uid_t *uid)
247 struct uidc *uidc, *uidc2;
251 key = hash(name) % NAMETOUID_SZ;
252 uidc = nametouid[key];
253 while (uidc != NULL) {
254 if (strcmp(uidc->name, name) == 0)
260 uidc = xmalloc(sizeof(struct uidc));
261 uidc->name = xstrdup(name);
264 /* This user name is in the password file. */
266 uidc->uid = pw->pw_uid;
267 /* Also add it to the uid -> name table. */
268 uidc2 = xmalloc(sizeof(struct uidc));
269 uidc2->name = uidc->name; /* We reuse the pointer. */
270 uidc2->uid = uidc->uid;
272 key2 = uidc2->uid % UIDTONAME_SZ;
273 uidc_insert(uidtoname, uidc2, key2);
275 /* Add a miss entry for this user name. */
277 uidc->uid = (uid_t)-1; /* Should not be accessed. */
279 uidc_insert(nametouid, uidc, key);
281 /* It is safe to unlock here since the cache structure
282 is not going to get freed or changed. */
290 /* Finds the gid for this group name. If it's found, the gid is stored
291 in *gid and 0 is returned. Otherwise, -1 is returned. */
293 getgidbyname(const char *name, gid_t *gid)
296 struct gidc *gidc, *gidc2;
300 key = hash(name) % NAMETOGID_SZ;
301 gidc = nametogid[key];
302 while (gidc != NULL) {
303 if (strcmp(gidc->name, name) == 0)
309 gidc = xmalloc(sizeof(struct gidc));
310 gidc->name = xstrdup(name);
313 /* This group name is in the group file. */
314 gidc->gid = gr->gr_gid;
316 /* Also add it to the gid -> name table. */
317 gidc2 = xmalloc(sizeof(struct gidc));
318 gidc2->name = gidc->name; /* We reuse the pointer. */
319 gidc2->gid = gidc->gid;
321 key2 = gidc2->gid % GIDTONAME_SZ;
322 gidc_insert(gidtoname, gidc2, key2);
324 /* Add a miss entry for this group name. */
325 gidc->gid = (gid_t)-1; /* Should not be accessed. */
328 gidc_insert(nametogid, gidc, key);
330 /* It is safe to unlock here since the cache structure
331 is not going to get freed or changed. */
339 /* Initialize the cache structures. */
344 pthread_mutex_init(&uid_mtx, NULL);
345 pthread_mutex_init(&gid_mtx, NULL);
346 uidtoname = xmalloc(UIDTONAME_SZ * sizeof(struct uidc *));
347 gidtoname = xmalloc(GIDTONAME_SZ * sizeof(struct gidc *));
348 nametouid = xmalloc(NAMETOUID_SZ * sizeof(struct uidc *));
349 nametogid = xmalloc(NAMETOGID_SZ * sizeof(struct gidc *));
350 memset(uidtoname, 0, UIDTONAME_SZ * sizeof(struct uidc *));
351 memset(gidtoname, 0, GIDTONAME_SZ * sizeof(struct gidc *));
352 memset(nametouid, 0, NAMETOUID_SZ * sizeof(struct uidc *));
353 memset(nametogid, 0, NAMETOGID_SZ * sizeof(struct gidc *));
356 /* Cleanup the cache structures. */
360 struct uidc *uidc, *uidc2;
361 struct gidc *gidc, *gidc2;
364 for (i = 0; i < UIDTONAME_SZ; i++) {
366 while (uidc != NULL) {
367 if (uidc->name != NULL) {
377 for (i = 0; i < NAMETOUID_SZ; i++) {
379 while (uidc != NULL) {
380 assert(uidc->name != NULL);
381 /* If it's a valid entry, it has been added to both the
382 uidtoname and nametouid tables, and the name pointer
383 has been reused for both entries. Thus, the name
384 pointer has already been freed in the loop above. */
393 for (i = 0; i < GIDTONAME_SZ; i++) {
395 while (gidc != NULL) {
396 if (gidc->name != NULL) {
406 for (i = 0; i < NAMETOGID_SZ; i++) {
408 while (gidc != NULL) {
409 assert(gidc->name != NULL);
410 /* See above comment. */
419 pthread_mutex_destroy(&uid_mtx);
420 pthread_mutex_destroy(&gid_mtx);