summaryrefslogtreecommitdiffstats
path: root/usr.bin/csup/idcache.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/csup/idcache.c')
-rw-r--r--usr.bin/csup/idcache.c421
1 files changed, 421 insertions, 0 deletions
diff --git a/usr.bin/csup/idcache.c b/usr.bin/csup/idcache.c
new file mode 100644
index 0000000..47a3e71
--- /dev/null
+++ b/usr.bin/csup/idcache.c
@@ -0,0 +1,421 @@
+/*-
+ * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/types.h>
+
+#include <assert.h>
+#include <grp.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "idcache.h"
+#include "misc.h"
+
+/*
+ * Constants and data structures used to implement the thread-safe
+ * group and password file caches. Cache sizes must be prime.
+ */
+#define UIDTONAME_SZ 317 /* Size of uid -> user name cache */
+#define NAMETOUID_SZ 317 /* Size of user name -> uid cache */
+#define GIDTONAME_SZ 317 /* Size of gid -> group name cache */
+#define NAMETOGID_SZ 317 /* Size of group name -> gid cache */
+
+/* Node structures used to cache lookups. */
+struct uidc {
+ char *name; /* user name */
+ uid_t uid; /* cached uid */
+ int valid; /* is this a valid or a miss entry */
+ struct uidc *next; /* for collisions */
+};
+
+struct gidc {
+ char *name; /* group name */
+ gid_t gid; /* cached gid */
+ int valid; /* is this a valid or a miss entry */
+ struct gidc *next; /* for collisions */
+};
+
+static struct uidc **uidtoname; /* uid to user name cache */
+static struct gidc **gidtoname; /* gid to group name cache */
+static struct uidc **nametouid; /* user name to uid cache */
+static struct gidc **nametogid; /* group name to gid cache */
+
+static pthread_mutex_t uid_mtx;
+static pthread_mutex_t gid_mtx;
+
+static void uid_lock(void);
+static void uid_unlock(void);
+static void gid_lock(void);
+static void gid_unlock(void);
+
+static uint32_t hash(const char *);
+
+/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
+ as used by ELF for hashing function names. */
+static uint32_t
+hash(const char *name)
+{
+ uint32_t g, h;
+
+ h = 0;
+ while(*name != '\0') {
+ h = (h << 4) + *name++;
+ if ((g = h & 0xF0000000)) {
+ h ^= g >> 24;
+ h &= 0x0FFFFFFF;
+ }
+ }
+ return (h);
+}
+
+static void
+uid_lock(void)
+{
+ int error;
+
+ error = pthread_mutex_lock(&uid_mtx);
+ assert(!error);
+}
+
+static void
+uid_unlock(void)
+{
+ int error;
+
+ error = pthread_mutex_unlock(&uid_mtx);
+ assert(!error);
+}
+
+static void
+gid_lock(void)
+{
+ int error;
+
+ error = pthread_mutex_lock(&gid_mtx);
+ assert(!error);
+}
+
+static void
+gid_unlock(void)
+{
+ int error;
+
+ error = pthread_mutex_unlock(&gid_mtx);
+ assert(!error);
+}
+
+static void
+uidc_insert(struct uidc **tbl, struct uidc *uidc, uint32_t key)
+{
+
+ uidc->next = tbl[key];
+ tbl[key] = uidc;
+}
+
+static void
+gidc_insert(struct gidc **tbl, struct gidc *gidc, uint32_t key)
+{
+
+ gidc->next = tbl[key];
+ tbl[key] = gidc;
+}
+
+/* Return the user name for this uid, or NULL if it's not found. */
+char *
+getuserbyid(uid_t uid)
+{
+ struct passwd *pw;
+ struct uidc *uidc, *uidc2;
+ uint32_t key, key2;
+
+ key = uid % UIDTONAME_SZ;
+ uid_lock();
+ uidc = uidtoname[key];
+ while (uidc != NULL) {
+ if (uidc->uid == uid)
+ break;
+ uidc = uidc->next;
+ }
+
+ if (uidc == NULL) {
+ /* We didn't find this uid, look it up and add it. */
+ uidc = xmalloc(sizeof(struct uidc));
+ uidc->uid = uid;
+ pw = getpwuid(uid);
+ if (pw != NULL) {
+ /* This uid is in the password file. */
+ uidc->name = xstrdup(pw->pw_name);
+ uidc->valid = 1;
+ /* Also add it to the name -> gid table. */
+ uidc2 = xmalloc(sizeof(struct uidc));
+ uidc2->uid = uid;
+ uidc2->name = uidc->name; /* We reuse the pointer. */
+ uidc2->valid = 1;
+ key2 = hash(uidc->name) % NAMETOUID_SZ;
+ uidc_insert(nametouid, uidc2, key2);
+ } else {
+ /* Add a miss entry for this uid. */
+ uidc->name = NULL;
+ uidc->valid = 0;
+ }
+ uidc_insert(uidtoname, uidc, key);
+ }
+ /* It is safe to unlock here since the cache structure
+ is not going to get freed or changed. */
+ uid_unlock();
+ return (uidc->name);
+}
+
+/* Return the group name for this gid, or NULL if it's not found. */
+char *
+getgroupbyid(gid_t gid)
+{
+ struct group *gr;
+ struct gidc *gidc, *gidc2;
+ uint32_t key, key2;
+
+ key = gid % GIDTONAME_SZ;
+ gid_lock();
+ gidc = gidtoname[key];
+ while (gidc != NULL) {
+ if (gidc->gid == gid)
+ break;
+ gidc = gidc->next;
+ }
+
+ if (gidc == NULL) {
+ /* We didn't find this gid, look it up and add it. */
+ gidc = xmalloc(sizeof(struct gidc));
+ gidc->gid = gid;
+ gr = getgrgid(gid);
+ if (gr != NULL) {
+ /* This gid is in the group file. */
+ gidc->name = xstrdup(gr->gr_name);
+ gidc->valid = 1;
+ /* Also add it to the name -> gid table. */
+ gidc2 = xmalloc(sizeof(struct gidc));
+ gidc2->gid = gid;
+ gidc2->name = gidc->name; /* We reuse the pointer. */
+ gidc2->valid = 1;
+ key2 = hash(gidc->name) % NAMETOGID_SZ;
+ gidc_insert(nametogid, gidc2, key2);
+ } else {
+ /* Add a miss entry for this gid. */
+ gidc->name = NULL;
+ gidc->valid = 0;
+ }
+ gidc_insert(gidtoname, gidc, key);
+ }
+ /* It is safe to unlock here since the cache structure
+ is not going to get freed or changed. */
+ gid_unlock();
+ return (gidc->name);
+}
+
+/* Finds the uid for this user name. If it's found, the gid is stored
+ in *uid and 0 is returned. Otherwise, -1 is returned. */
+int
+getuidbyname(const char *name, uid_t *uid)
+{
+ struct passwd *pw;
+ struct uidc *uidc, *uidc2;
+ uint32_t key, key2;
+
+ uid_lock();
+ key = hash(name) % NAMETOUID_SZ;
+ uidc = nametouid[key];
+ while (uidc != NULL) {
+ if (strcmp(uidc->name, name) == 0)
+ break;
+ uidc = uidc->next;
+ }
+
+ if (uidc == NULL) {
+ uidc = xmalloc(sizeof(struct uidc));
+ uidc->name = xstrdup(name);
+ pw = getpwnam(name);
+ if (pw != NULL) {
+ /* This user name is in the password file. */
+ uidc->valid = 1;
+ uidc->uid = pw->pw_uid;
+ /* Also add it to the uid -> name table. */
+ uidc2 = xmalloc(sizeof(struct uidc));
+ uidc2->name = uidc->name; /* We reuse the pointer. */
+ uidc2->uid = uidc->uid;
+ uidc2->valid = 1;
+ key2 = uidc2->uid % UIDTONAME_SZ;
+ uidc_insert(uidtoname, uidc2, key2);
+ } else {
+ /* Add a miss entry for this user name. */
+ uidc->valid = 0;
+ uidc->uid = (uid_t)-1; /* Should not be accessed. */
+ }
+ uidc_insert(nametouid, uidc, key);
+ }
+ /* It is safe to unlock here since the cache structure
+ is not going to get freed or changed. */
+ uid_unlock();
+ if (!uidc->valid)
+ return (-1);
+ *uid = uidc->uid;
+ return (0);
+}
+
+/* Finds the gid for this group name. If it's found, the gid is stored
+ in *gid and 0 is returned. Otherwise, -1 is returned. */
+int
+getgidbyname(const char *name, gid_t *gid)
+{
+ struct group *gr;
+ struct gidc *gidc, *gidc2;
+ uint32_t key, key2;
+
+ gid_lock();
+ key = hash(name) % NAMETOGID_SZ;
+ gidc = nametogid[key];
+ while (gidc != NULL) {
+ if (strcmp(gidc->name, name) == 0)
+ break;
+ gidc = gidc->next;
+ }
+
+ if (gidc == NULL) {
+ gidc = xmalloc(sizeof(struct gidc));
+ gidc->name = xstrdup(name);
+ gr = getgrnam(name);
+ if (gr != NULL) {
+ /* This group name is in the group file. */
+ gidc->gid = gr->gr_gid;
+ gidc->valid = 1;
+ /* Also add it to the gid -> name table. */
+ gidc2 = xmalloc(sizeof(struct gidc));
+ gidc2->name = gidc->name; /* We reuse the pointer. */
+ gidc2->gid = gidc->gid;
+ gidc2->valid = 1;
+ key2 = gidc2->gid % GIDTONAME_SZ;
+ gidc_insert(gidtoname, gidc2, key2);
+ } else {
+ /* Add a miss entry for this group name. */
+ gidc->gid = (gid_t)-1; /* Should not be accessed. */
+ gidc->valid = 0;
+ }
+ gidc_insert(nametogid, gidc, key);
+ }
+ /* It is safe to unlock here since the cache structure
+ is not going to get freed or changed. */
+ gid_unlock();
+ if (!gidc->valid)
+ return (-1);
+ *gid = gidc->gid;
+ return (0);
+}
+
+/* Initialize the cache structures. */
+void
+idcache_init(void)
+{
+
+ pthread_mutex_init(&uid_mtx, NULL);
+ pthread_mutex_init(&gid_mtx, NULL);
+ uidtoname = xmalloc(UIDTONAME_SZ * sizeof(struct uidc *));
+ gidtoname = xmalloc(GIDTONAME_SZ * sizeof(struct gidc *));
+ nametouid = xmalloc(NAMETOUID_SZ * sizeof(struct uidc *));
+ nametogid = xmalloc(NAMETOGID_SZ * sizeof(struct gidc *));
+ memset(uidtoname, 0, UIDTONAME_SZ * sizeof(struct uidc *));
+ memset(gidtoname, 0, GIDTONAME_SZ * sizeof(struct gidc *));
+ memset(nametouid, 0, NAMETOUID_SZ * sizeof(struct uidc *));
+ memset(nametogid, 0, NAMETOGID_SZ * sizeof(struct gidc *));
+}
+
+/* Cleanup the cache structures. */
+void
+idcache_fini(void)
+{
+ struct uidc *uidc, *uidc2;
+ struct gidc *gidc, *gidc2;
+ size_t i;
+
+ for (i = 0; i < UIDTONAME_SZ; i++) {
+ uidc = uidtoname[i];
+ while (uidc != NULL) {
+ if (uidc->name != NULL) {
+ assert(uidc->valid);
+ free(uidc->name);
+ }
+ uidc2 = uidc->next;
+ free(uidc);
+ uidc = uidc2;
+ }
+ }
+ free(uidtoname);
+ for (i = 0; i < NAMETOUID_SZ; i++) {
+ uidc = nametouid[i];
+ while (uidc != NULL) {
+ assert(uidc->name != NULL);
+ /* If it's a valid entry, it has been added to both the
+ uidtoname and nametouid tables, and the name pointer
+ has been reused for both entries. Thus, the name
+ pointer has already been freed in the loop above. */
+ if (!uidc->valid)
+ free(uidc->name);
+ uidc2 = uidc->next;
+ free(uidc);
+ uidc = uidc2;
+ }
+ }
+ free(nametouid);
+ for (i = 0; i < GIDTONAME_SZ; i++) {
+ gidc = gidtoname[i];
+ while (gidc != NULL) {
+ if (gidc->name != NULL) {
+ assert(gidc->valid);
+ free(gidc->name);
+ }
+ gidc2 = gidc->next;
+ free(gidc);
+ gidc = gidc2;
+ }
+ }
+ free(gidtoname);
+ for (i = 0; i < NAMETOGID_SZ; i++) {
+ gidc = nametogid[i];
+ while (gidc != NULL) {
+ assert(gidc->name != NULL);
+ /* See above comment. */
+ if (!gidc->valid)
+ free(gidc->name);
+ gidc2 = gidc->next;
+ free(gidc);
+ gidc = gidc2;
+ }
+ }
+ free(nametogid);
+ pthread_mutex_destroy(&uid_mtx);
+ pthread_mutex_destroy(&gid_mtx);
+}
OpenPOWER on IntegriCloud