]> Sergey Matveev's repositories - bfs.git/blob - src/pwcache.c
Formatting fixes
[bfs.git] / src / pwcache.c
1 // Copyright © Tavian Barnes <tavianator@tavianator.com>
2 // SPDX-License-Identifier: 0BSD
3
4 #include "pwcache.h"
5 #include "alloc.h"
6 #include "config.h"
7 #include "trie.h"
8 #include <errno.h>
9 #include <grp.h>
10 #include <pwd.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 /** Represents cache hits for negative results. */
16 static void *MISSING = &MISSING;
17
18 /** Callback type for bfs_getent(). */
19 typedef void *bfs_getent_fn(const void *key, void *ptr, size_t bufsize);
20
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) {
23         if (leaf->value) {
24                 errno = 0;
25                 return leaf->value == MISSING ? NULL : leaf->value;
26         }
27
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.
31         size_t bufsize = 128;
32         void *ptr = varena_alloc(varena, bufsize);
33         if (!ptr) {
34                 return NULL;
35         }
36
37         while (true) {
38                 void *ret = fn(key, ptr, bufsize);
39                 if (ret) {
40                         leaf->value = ret;
41                         return ret;
42                 } else if (errno == 0) {
43                         leaf->value = MISSING;
44                         break;
45                 } else if (errno == ERANGE) {
46                         void *next = varena_grow(varena, ptr, &bufsize);
47                         if (!next) {
48                                 break;
49                         }
50                         ptr = next;
51                 } else {
52                         break;
53                 }
54         }
55
56         varena_free(varena, ptr, bufsize);
57         return NULL;
58 }
59
60 /**
61  * An arena-allocated struct passwd.
62  */
63 struct bfs_passwd {
64         struct passwd pwd;
65         char buf[];
66 };
67
68 struct bfs_users {
69         /** bfs_passwd arena. */
70         struct varena varena;
71         /** A map from usernames to entries. */
72         struct trie by_name;
73         /** A map from UIDs to entries. */
74         struct trie by_uid;
75 };
76
77 struct bfs_users *bfs_users_new(void) {
78         struct bfs_users *users = ALLOC(struct bfs_users);
79         if (!users) {
80                 return NULL;
81         }
82
83         VARENA_INIT(&users->varena, struct bfs_passwd, buf);
84         trie_init(&users->by_name);
85         trie_init(&users->by_uid);
86         return users;
87 }
88
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;
92
93         struct passwd *ret;
94         errno = getpwnam_r(key, &storage->pwd, storage->buf, bufsize, &ret);
95         return ret;
96 }
97
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);
100         if (!leaf) {
101                 return NULL;
102         }
103
104         return bfs_getent(bfs_getpwnam_impl, name, leaf, &users->varena);
105 }
106
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;
111
112         struct passwd *ret;
113         errno = getpwuid_r(*uid, &storage->pwd, storage->buf, bufsize, &ret);
114         return ret;
115 }
116
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));
119         if (!leaf) {
120                 return NULL;
121         }
122
123         return bfs_getent(bfs_getpwuid_impl, &uid, leaf, &users->varena);
124 }
125
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);
130 }
131
132 void bfs_users_free(struct bfs_users *users) {
133         if (users) {
134                 trie_destroy(&users->by_uid);
135                 trie_destroy(&users->by_name);
136                 varena_destroy(&users->varena);
137                 free(users);
138         }
139 }
140
141 /**
142  * An arena-allocated struct group.
143  */
144 struct bfs_group {
145         struct group grp;
146         char buf[];
147 };
148
149 struct bfs_groups {
150         /** bfs_group arena. */
151         struct varena varena;
152         /** A map from group names to entries. */
153         struct trie by_name;
154         /** A map from GIDs to entries. */
155         struct trie by_gid;
156 };
157
158 struct bfs_groups *bfs_groups_new(void) {
159         struct bfs_groups *groups = ALLOC(struct bfs_groups);
160         if (!groups) {
161                 return NULL;
162         }
163
164         VARENA_INIT(&groups->varena, struct bfs_group, buf);
165         trie_init(&groups->by_name);
166         trie_init(&groups->by_gid);
167         return groups;
168 }
169
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;
173
174         struct group *ret;
175         errno = getgrnam_r(key, &storage->grp, storage->buf, bufsize, &ret);
176         return ret;
177 }
178
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);
181         if (!leaf) {
182                 return NULL;
183         }
184
185         return bfs_getent(bfs_getgrnam_impl, name, leaf, &groups->varena);
186 }
187
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;
192
193         struct group *ret;
194         errno = getgrgid_r(*gid, &storage->grp, storage->buf, bufsize, &ret);
195         return ret;
196 }
197
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));
200         if (!leaf) {
201                 return NULL;
202         }
203
204         return bfs_getent(bfs_getgrgid_impl, &gid, leaf, &groups->varena);
205 }
206
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);
211 }
212
213 void bfs_groups_free(struct bfs_groups *groups) {
214         if (groups) {
215                 trie_destroy(&groups->by_gid);
216                 trie_destroy(&groups->by_name);
217                 varena_destroy(&groups->varena);
218                 free(groups);
219         }
220 }