1 // Copyright © Tavian Barnes <tavianator@tavianator.com>
2 // SPDX-License-Identifier: 0BSD
15 /** Represents cache hits for negative results. */
16 static void *MISSING = &MISSING;
18 /** Callback type for bfs_getent(). */
19 typedef void *bfs_getent_fn(const void *key, void *ptr, size_t bufsize);
21 /** Shared scaffolding for get{pw,gr}{nam,?id}_r(). */
22 static void *bfs_getent(bfs_getent_fn *fn, const void *key, struct trie_leaf *leaf, struct varena *varena) {
25 return leaf->value == MISSING ? NULL : leaf->value;
28 // _SC_GET{PW,GR}_R_SIZE_MAX tend to be fairly large (~1K). That's okay
29 // for temporary allocations, but for these long-lived ones, let's start
30 // with a smaller buffer.
32 void *ptr = varena_alloc(varena, bufsize);
38 void *ret = fn(key, ptr, bufsize);
42 } else if (errno == 0) {
43 leaf->value = MISSING;
45 } else if (errno == ERANGE) {
46 void *next = varena_grow(varena, ptr, &bufsize);
56 varena_free(varena, ptr, bufsize);
61 * An arena-allocated struct passwd.
69 /** bfs_passwd arena. */
71 /** A map from usernames to entries. */
73 /** A map from UIDs to entries. */
77 struct bfs_users *bfs_users_new(void) {
78 struct bfs_users *users = ALLOC(struct bfs_users);
83 VARENA_INIT(&users->varena, struct bfs_passwd, buf);
84 trie_init(&users->by_name);
85 trie_init(&users->by_uid);
89 /** bfs_getent() callback for getpwnam_r(). */
90 static void *bfs_getpwnam_impl(const void *key, void *ptr, size_t bufsize) {
91 struct bfs_passwd *storage = ptr;
94 errno = getpwnam_r(key, &storage->pwd, storage->buf, bufsize, &ret);
98 const struct passwd *bfs_getpwnam(struct bfs_users *users, const char *name) {
99 struct trie_leaf *leaf = trie_insert_str(&users->by_name, name);
104 return bfs_getent(bfs_getpwnam_impl, name, leaf, &users->varena);
107 /** bfs_getent() callback for getpwuid_r(). */
108 static void *bfs_getpwuid_impl(const void *key, void *ptr, size_t bufsize) {
109 const uid_t *uid = key;
110 struct bfs_passwd *storage = ptr;
113 errno = getpwuid_r(*uid, &storage->pwd, storage->buf, bufsize, &ret);
117 const struct passwd *bfs_getpwuid(struct bfs_users *users, uid_t uid) {
118 struct trie_leaf *leaf = trie_insert_mem(&users->by_uid, &uid, sizeof(uid));
123 return bfs_getent(bfs_getpwuid_impl, &uid, leaf, &users->varena);
126 void bfs_users_flush(struct bfs_users *users) {
127 trie_clear(&users->by_uid);
128 trie_clear(&users->by_name);
129 varena_clear(&users->varena);
132 void bfs_users_free(struct bfs_users *users) {
134 trie_destroy(&users->by_uid);
135 trie_destroy(&users->by_name);
136 varena_destroy(&users->varena);
142 * An arena-allocated struct group.
150 /** bfs_group arena. */
151 struct varena varena;
152 /** A map from group names to entries. */
154 /** A map from GIDs to entries. */
158 struct bfs_groups *bfs_groups_new(void) {
159 struct bfs_groups *groups = ALLOC(struct bfs_groups);
164 VARENA_INIT(&groups->varena, struct bfs_group, buf);
165 trie_init(&groups->by_name);
166 trie_init(&groups->by_gid);
170 /** bfs_getent() callback for getgrnam_r(). */
171 static void *bfs_getgrnam_impl(const void *key, void *ptr, size_t bufsize) {
172 struct bfs_group *storage = ptr;
175 errno = getgrnam_r(key, &storage->grp, storage->buf, bufsize, &ret);
179 const struct group *bfs_getgrnam(struct bfs_groups *groups, const char *name) {
180 struct trie_leaf *leaf = trie_insert_str(&groups->by_name, name);
185 return bfs_getent(bfs_getgrnam_impl, name, leaf, &groups->varena);
188 /** bfs_getent() callback for getgrgid_r(). */
189 static void *bfs_getgrgid_impl(const void *key, void *ptr, size_t bufsize) {
190 const gid_t *gid = key;
191 struct bfs_group *storage = ptr;
194 errno = getgrgid_r(*gid, &storage->grp, storage->buf, bufsize, &ret);
198 const struct group *bfs_getgrgid(struct bfs_groups *groups, gid_t gid) {
199 struct trie_leaf *leaf = trie_insert_mem(&groups->by_gid, &gid, sizeof(gid));
204 return bfs_getent(bfs_getgrgid_impl, &gid, leaf, &groups->varena);
207 void bfs_groups_flush(struct bfs_groups *groups) {
208 trie_clear(&groups->by_gid);
209 trie_clear(&groups->by_name);
210 varena_clear(&groups->varena);
213 void bfs_groups_free(struct bfs_groups *groups) {
215 trie_destroy(&groups->by_gid);
216 trie_destroy(&groups->by_name);
217 varena_destroy(&groups->varena);