summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pwcache.c134
1 files changed, 80 insertions, 54 deletions
diff --git a/src/pwcache.c b/src/pwcache.c
index 9f32eb0..a9acfc0 100644
--- a/src/pwcache.c
+++ b/src/pwcache.c
@@ -17,54 +17,56 @@
static void *MISSING = &MISSING;
/** Callback type for bfs_getent(). */
-typedef void *bfs_getent_fn(const void *key, void *ent, void *buf, size_t bufsize);
+typedef void *bfs_getent_fn(const void *key, void *ptr, size_t bufsize);
/** Shared scaffolding for get{pw,gr}{nam,?id}_r(). */
-static void *bfs_getent(struct trie_leaf *leaf, bfs_getent_fn *fn, const void *key, size_t entsize, size_t bufsize) {
+static void *bfs_getent(bfs_getent_fn *fn, const void *key, struct trie_leaf *leaf, struct varena *varena, size_t bufsize) {
if (leaf->value) {
errno = 0;
return leaf->value == MISSING ? NULL : leaf->value;
}
- void *buf = NULL;
- while (true) {
- void *result = buf;
- buf = realloc(buf, entsize + bufsize);
- if (!buf) {
- free(result);
- return NULL;
- }
+ void *ptr = varena_alloc(varena, bufsize);
+ if (!ptr) {
+ return NULL;
+ }
- result = fn(key, buf, (char *)buf + entsize, bufsize);
- if (result) {
- leaf->value = result;
- return result;
+ while (true) {
+ void *ret = fn(key, ptr, bufsize);
+ if (ret) {
+ leaf->value = ret;
+ return ret;
} else if (errno == 0) {
- free(buf);
leaf->value = MISSING;
- return NULL;
+ break;
} else if (errno == ERANGE) {
- bufsize *= 2;
+ void *next = varena_grow(varena, ptr, &bufsize);
+ if (!next) {
+ break;
+ }
+ ptr = next;
} else {
- free(buf);
- return NULL;
+ break;
}
}
-}
-/** Flush a single cache. */
-static void bfs_pwcache_flush(struct trie *trie) {
- TRIE_FOR_EACH(trie, leaf) {
- if (leaf->value != MISSING) {
- free(leaf->value);
- }
- trie_remove(trie, leaf);
- }
+ varena_free(varena, ptr, bufsize);
+ return NULL;
}
+/**
+ * An arena-allocated struct passwd.
+ */
+struct bfs_passwd {
+ struct passwd pwd;
+ char buf[];
+};
+
struct bfs_users {
/** Initial buffer size for getpw*_r(). */
size_t bufsize;
+ /** bfs_passwd arena. */
+ struct varena varena;
/** A map from usernames to entries. */
struct trie by_name;
/** A map from UIDs to entries. */
@@ -84,16 +86,19 @@ struct bfs_users *bfs_users_new(void) {
users->bufsize = 1024;
}
+ VARENA_INIT(&users->varena, struct bfs_passwd, buf);
trie_init(&users->by_name);
trie_init(&users->by_uid);
return users;
}
/** bfs_getent() callback for getpwnam_r(). */
-static void *bfs_getpwnam_impl(const void *key, void *ent, void *buf, size_t bufsize) {
- struct passwd *result;
- errno = getpwnam_r(key, ent, buf, bufsize, &result);
- return result;
+static void *bfs_getpwnam_impl(const void *key, void *ptr, size_t bufsize) {
+ struct bfs_passwd *storage = ptr;
+
+ struct passwd *ret;
+ errno = getpwnam_r(key, &storage->pwd, storage->buf, bufsize, &ret);
+ return ret;
}
const struct passwd *bfs_getpwnam(struct bfs_users *users, const char *name) {
@@ -102,14 +107,17 @@ const struct passwd *bfs_getpwnam(struct bfs_users *users, const char *name) {
return NULL;
}
- return bfs_getent(leaf, bfs_getpwnam_impl, name, sizeof(struct passwd), users->bufsize);
+ return bfs_getent(bfs_getpwnam_impl, name, leaf, &users->varena, users->bufsize);
}
/** bfs_getent() callback for getpwuid_r(). */
-static void *bfs_getpwuid_impl(const void *key, void *ent, void *buf, size_t bufsize) {
- struct passwd *result;
- errno = getpwuid_r(*(const uid_t *)key, ent, buf, bufsize, &result);
- return result;
+static void *bfs_getpwuid_impl(const void *key, void *ptr, size_t bufsize) {
+ const uid_t *uid = key;
+ struct bfs_passwd *storage = ptr;
+
+ struct passwd *ret;
+ errno = getpwuid_r(*uid, &storage->pwd, storage->buf, bufsize, &ret);
+ return ret;
}
const struct passwd *bfs_getpwuid(struct bfs_users *users, uid_t uid) {
@@ -118,26 +126,37 @@ const struct passwd *bfs_getpwuid(struct bfs_users *users, uid_t uid) {
return NULL;
}
- return bfs_getent(leaf, bfs_getpwuid_impl, &uid, sizeof(struct passwd), users->bufsize);
+ return bfs_getent(bfs_getpwuid_impl, &uid, leaf, &users->varena, users->bufsize);
}
void bfs_users_flush(struct bfs_users *users) {
- bfs_pwcache_flush(&users->by_name);
- bfs_pwcache_flush(&users->by_uid);
+ trie_clear(&users->by_uid);
+ trie_clear(&users->by_name);
+ varena_clear(&users->varena);
}
void bfs_users_free(struct bfs_users *users) {
if (users) {
- bfs_users_flush(users);
trie_destroy(&users->by_uid);
trie_destroy(&users->by_name);
+ varena_destroy(&users->varena);
free(users);
}
}
+/**
+ * An arena-allocated struct group.
+ */
+struct bfs_group {
+ struct group grp;
+ char buf[];
+};
+
struct bfs_groups {
/** Initial buffer size for getgr*_r(). */
size_t bufsize;
+ /** bfs_group arena. */
+ struct varena varena;
/** A map from group names to entries. */
struct trie by_name;
/** A map from GIDs to entries. */
@@ -157,16 +176,19 @@ struct bfs_groups *bfs_groups_new(void) {
groups->bufsize = 1024;
}
+ VARENA_INIT(&groups->varena, struct bfs_group, buf);
trie_init(&groups->by_name);
trie_init(&groups->by_gid);
return groups;
}
/** bfs_getent() callback for getgrnam_r(). */
-static void *bfs_getgrnam_impl(const void *key, void *ent, void *buf, size_t bufsize) {
- struct group *result;
- errno = getgrnam_r(key, ent, buf, bufsize, &result);
- return result;
+static void *bfs_getgrnam_impl(const void *key, void *ptr, size_t bufsize) {
+ struct bfs_group *storage = ptr;
+
+ struct group *ret;
+ errno = getgrnam_r(key, &storage->grp, storage->buf, bufsize, &ret);
+ return ret;
}
const struct group *bfs_getgrnam(struct bfs_groups *groups, const char *name) {
@@ -175,14 +197,17 @@ const struct group *bfs_getgrnam(struct bfs_groups *groups, const char *name) {
return NULL;
}
- return bfs_getent(leaf, bfs_getgrnam_impl, name, sizeof(struct group), groups->bufsize);
+ return bfs_getent(bfs_getgrnam_impl, name, leaf, &groups->varena, groups->bufsize);
}
/** bfs_getent() callback for getgrgid_r(). */
-static void *bfs_getgrgid_impl(const void *key, void *ent, void *buf, size_t bufsize) {
- struct group *result;
- errno = getgrgid_r(*(const gid_t *)key, ent, buf, bufsize, &result);
- return result;
+static void *bfs_getgrgid_impl(const void *key, void *ptr, size_t bufsize) {
+ const gid_t *gid = key;
+ struct bfs_group *storage = ptr;
+
+ struct group *ret;
+ errno = getgrgid_r(*gid, &storage->grp, storage->buf, bufsize, &ret);
+ return ret;
}
const struct group *bfs_getgrgid(struct bfs_groups *groups, gid_t gid) {
@@ -191,19 +216,20 @@ const struct group *bfs_getgrgid(struct bfs_groups *groups, gid_t gid) {
return NULL;
}
- return bfs_getent(leaf, bfs_getgrgid_impl, &gid, sizeof(struct group), groups->bufsize);
+ return bfs_getent(bfs_getgrgid_impl, &gid, leaf, &groups->varena, groups->bufsize);
}
void bfs_groups_flush(struct bfs_groups *groups) {
- bfs_pwcache_flush(&groups->by_name);
- bfs_pwcache_flush(&groups->by_gid);
+ trie_clear(&groups->by_gid);
+ trie_clear(&groups->by_name);
+ varena_clear(&groups->varena);
}
void bfs_groups_free(struct bfs_groups *groups) {
if (groups) {
- bfs_groups_flush(groups);
trie_destroy(&groups->by_gid);
trie_destroy(&groups->by_name);
+ varena_destroy(&groups->varena);
free(groups);
}
}