]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/initgrcache.c
ldbl-opt: Add error and error_at_line (bug 23984)
[thirdparty/glibc.git] / nscd / initgrcache.c
CommitLineData
f7e7a396 1/* Cache handling for host lookup.
04277e02 2 Copyright (C) 2004-2019 Free Software Foundation, Inc.
f7e7a396
UD
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
5
43bc8ac6 6 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
7 it under the terms of the GNU General Public License as published
8 by the Free Software Foundation; version 2 of the License, or
9 (at your option) any later version.
f7e7a396 10
43bc8ac6 11 This program is distributed in the hope that it will be useful,
f7e7a396 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
43bc8ac6
UD
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
f7e7a396 15
43bc8ac6 16 You should have received a copy of the GNU General Public License
59ba27a6 17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
f7e7a396
UD
18
19#include <assert.h>
20#include <errno.h>
21#include <grp.h>
22#include <libintl.h>
23#include <string.h>
24#include <time.h>
25#include <unistd.h>
26#include <sys/mman.h>
866ba63b 27#include <scratch_buffer.h>
a1a78204 28#include <config.h>
eac10791
UD
29
30#include "dbg_log.h"
31#include "nscd.h"
f7e7a396
UD
32
33#include "../nss/nsswitch.h"
34
a1a78204
SE
35#ifdef LINK_OBSOLETE_NSL
36# define DEFAULT_CONFIG "compat [NOTFOUND=return] files"
37#else
38# define DEFAULT_CONFIG "files"
39#endif
f7e7a396
UD
40
41/* Type of the lookup function. */
42typedef enum nss_status (*initgroups_dyn_function) (const char *, gid_t,
43 long int *, long int *,
44 gid_t **, long int, int *);
45
46
47static const initgr_response_header notfound =
48{
49 .version = NSCD_VERSION,
50 .found = 0,
51 .ngrps = 0
52};
53
54
55#include "../grp/compat-initgroups.c"
56
57
a4c7ea7b 58static time_t
f7e7a396 59addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
20e498bd 60 void *key, uid_t uid, struct hashentry *const he,
f7e7a396
UD
61 struct datahead *dh)
62{
63 /* Search for the entry matching the key. Please note that we don't
64 look again in the table whether the dataset is now available. We
65 simply insert it. It does not matter if it is in there twice. The
66 pruning function only will look at the timestamp. */
67
68
69 /* We allocate all data in one memory block: the iov vector,
70 the response header and the dataset itself. */
71 struct dataset
72 {
73 struct datahead head;
74 initgr_response_header resp;
75 char strdata[0];
76 } *dataset = NULL;
77
a1ffb40e 78 if (__glibc_unlikely (debug_level > 0))
f7e7a396
UD
79 {
80 if (he == NULL)
81 dbg_log (_("Haven't found \"%s\" in group cache!"), (char *) key);
82 else
83 dbg_log (_("Reloading \"%s\" in group cache!"), (char *) key);
84 }
85
86 static service_user *group_database;
b2179107 87 service_user *nip;
f7e7a396
UD
88 int no_more;
89
b2179107 90 if (group_database == NULL)
a1a78204 91 no_more = __nss_database_lookup ("group", NULL, DEFAULT_CONFIG,
b2179107
AS
92 &group_database);
93 else
94 no_more = 0;
95 nip = group_database;
f7e7a396
UD
96
97 /* We always use sysconf even if NGROUPS_MAX is defined. That way, the
98 limit can be raised in the kernel configuration without having to
99 recompile libc. */
100 long int limit = __sysconf (_SC_NGROUPS_MAX);
101
102 long int size;
103 if (limit > 0)
104 /* We limit the size of the intially allocated array. */
105 size = MIN (limit, 64);
106 else
107 /* No fixed limit on groups. Pick a starting buffer size. */
108 size = 16;
109
110 long int start = 0;
111 bool all_tryagain = true;
cd248c3f 112 bool any_success = false;
f7e7a396 113
e8667ddc 114 /* This is temporary memory, we need not (and must not) call
f7e7a396
UD
115 mempool_alloc. */
116 // XXX This really should use alloca. need to change the backends.
117 gid_t *groups = (gid_t *) malloc (size * sizeof (gid_t));
a1ffb40e 118 if (__glibc_unlikely (groups == NULL))
f7e7a396
UD
119 /* No more memory. */
120 goto out;
121
122 /* Nothing added yet. */
123 while (! no_more)
124 {
695c4370 125 long int prev_start = start;
f7e7a396
UD
126 enum nss_status status;
127 initgroups_dyn_function fct;
128 fct = __nss_lookup_function (nip, "initgroups_dyn");
129
130 if (fct == NULL)
131 {
132 status = compat_call (nip, key, -1, &start, &size, &groups,
133 limit, &errno);
134
135 if (nss_next_action (nip, NSS_STATUS_UNAVAIL) != NSS_ACTION_CONTINUE)
136 break;
137 }
138 else
139 status = DL_CALL_FCT (fct, (key, -1, &start, &size, &groups,
140 limit, &errno));
141
695c4370
UD
142 /* Remove duplicates. */
143 long int cnt = prev_start;
144 while (cnt < start)
145 {
146 long int inner;
147 for (inner = 0; inner < prev_start; ++inner)
148 if (groups[inner] == groups[cnt])
149 break;
150
151 if (inner < prev_start)
152 groups[cnt] = groups[--start];
153 else
154 ++cnt;
155 }
156
f7e7a396
UD
157 if (status != NSS_STATUS_TRYAGAIN)
158 all_tryagain = false;
159
160 /* This is really only for debugging. */
161 if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
a6e8926f 162 __libc_fatal ("Illegal status in internal_getgrouplist.\n");
f7e7a396 163
fbbc73b3
UD
164 any_success |= status == NSS_STATUS_SUCCESS;
165
f7e7a396
UD
166 if (status != NSS_STATUS_SUCCESS
167 && nss_next_action (nip, status) == NSS_ACTION_RETURN)
168 break;
169
170 if (nip->next == NULL)
171 no_more = -1;
172 else
173 nip = nip->next;
174 }
175
306dfba9 176 bool all_written;
f7e7a396 177 ssize_t total;
a4c7ea7b 178 time_t timeout;
f7e7a396 179 out:
306dfba9 180 all_written = true;
a4c7ea7b 181 timeout = MAX_TIMEOUT_VALUE;
fbbc73b3 182 if (!any_success)
f7e7a396
UD
183 {
184 /* Nothing found. Create a negative result record. */
306dfba9 185 total = sizeof (notfound);
f7e7a396
UD
186
187 if (he != NULL && all_tryagain)
188 {
189 /* If we have an old record available but cannot find one now
190 because the service is not available we keep the old record
191 and make sure it does not get removed. */
192 if (reload_count != UINT_MAX && dh->nreloads == reload_count)
193 /* Do not reset the value if we never not reload the record. */
194 dh->nreloads = reload_count - 1;
a4c7ea7b
UD
195
196 /* Reload with the same time-to-live value. */
197 timeout = dh->timeout = time (NULL) + db->postimeout;
f7e7a396
UD
198 }
199 else
200 {
201 /* We have no data. This means we send the standard reply for this
202 case. */
306dfba9
AS
203 if (fd != -1
204 && TEMP_FAILURE_RETRY (send (fd, &notfound, total,
205 MSG_NOSIGNAL)) != total)
206 all_written = false;
f7e7a396 207
3e1aa84e
UD
208 /* If we have a transient error or cannot permanently store
209 the result, so be it. */
210 if (all_tryagain || __builtin_expect (db->negtimeout == 0, 0))
445b4a53
TK
211 {
212 /* Mark the old entry as obsolete. */
213 if (dh != NULL)
214 dh->usable = false;
215 }
99231d9a
UD
216 else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
217 + req->key_len), 1)) != NULL)
f7e7a396 218 {
1cdeb237
SP
219 timeout = datahead_init_neg (&dataset->head,
220 (sizeof (struct dataset)
221 + req->key_len), total,
222 db->negtimeout);
f7e7a396
UD
223
224 /* This is the reply. */
225 memcpy (&dataset->resp, &notfound, total);
226
227 /* Copy the key data. */
228 char *key_copy = memcpy (dataset->strdata, key, req->key_len);
229
230 /* If necessary, we also propagate the data to disk. */
231 if (db->persistent)
232 {
233 // XXX async OK?
234 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
235 msync ((void *) pval,
236 ((uintptr_t) dataset & pagesize_m1)
237 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
238 }
239
7e71e55f 240 (void) cache_add (req->type, key_copy, req->key_len,
528741cb 241 &dataset->head, true, db, uid, he == NULL);
f7e7a396 242
00ebd7ed
UD
243 pthread_rwlock_unlock (&db->lock);
244
f7e7a396
UD
245 /* Mark the old entry as obsolete. */
246 if (dh != NULL)
247 dh->usable = false;
248 }
f7e7a396
UD
249 }
250 }
251 else
252 {
253
306dfba9 254 total = offsetof (struct dataset, strdata) + start * sizeof (int32_t);
f7e7a396
UD
255
256 /* If we refill the cache, first assume the reconrd did not
257 change. Allocate memory on the cache since it is likely
258 discarded anyway. If it turns out to be necessary to have a
259 new record we can still allocate real memory. */
260 bool alloca_used = false;
261 dataset = NULL;
262
263 if (he == NULL)
20e498bd
UD
264 dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
265 1);
f7e7a396
UD
266
267 if (dataset == NULL)
268 {
269 /* We cannot permanently add the result in the moment. But
270 we can provide the result as is. Store the data in some
271 temporary memory. */
272 dataset = (struct dataset *) alloca (total + req->key_len);
273
274 /* We cannot add this record to the permanent database. */
275 alloca_used = true;
276 }
277
1cdeb237
SP
278 timeout = datahead_init_pos (&dataset->head, total + req->key_len,
279 total - offsetof (struct dataset, resp),
280 he == NULL ? 0 : dh->nreloads + 1,
281 db->postimeout);
f7e7a396
UD
282
283 dataset->resp.version = NSCD_VERSION;
284 dataset->resp.found = 1;
285 dataset->resp.ngrps = start;
286
287 char *cp = dataset->strdata;
288
289 /* Copy the GID values. If the size of the types match this is
290 very simple. */
291 if (sizeof (gid_t) == sizeof (int32_t))
292 cp = mempcpy (cp, groups, start * sizeof (gid_t));
293 else
294 {
295 gid_t *gcp = (gid_t *) cp;
296
297 for (int i = 0; i < start; ++i)
298 *gcp++ = groups[i];
299
300 cp = (char *) gcp;
301 }
302
303 /* Finally the user name. */
304 memcpy (cp, key, req->key_len);
305
5a337776
UD
306 assert (cp == dataset->strdata + total - offsetof (struct dataset,
307 strdata));
308
f7e7a396
UD
309 /* Now we can determine whether on refill we have to create a new
310 record or not. */
311 if (he != NULL)
312 {
313 assert (fd == -1);
314
315 if (total + req->key_len == dh->allocsize
316 && total - offsetof (struct dataset, resp) == dh->recsize
317 && memcmp (&dataset->resp, dh->data,
318 dh->allocsize - offsetof (struct dataset, resp)) == 0)
319 {
320 /* The data has not changed. We will just bump the
321 timeout value. Note that the new record has been
322 allocated on the stack and need not be freed. */
323 dh->timeout = dataset->head.timeout;
324 ++dh->nreloads;
325 }
326 else
327 {
328 /* We have to create a new record. Just allocate
329 appropriate memory and copy it. */
330 struct dataset *newp
c52137d3 331 = (struct dataset *) mempool_alloc (db, total + req->key_len,
20e498bd 332 1);
f7e7a396
UD
333 if (newp != NULL)
334 {
335 /* Adjust pointer into the memory block. */
336 cp = (char *) newp + (cp - (char *) dataset);
337
338 dataset = memcpy (newp, dataset, total + req->key_len);
339 alloca_used = false;
340 }
341
342 /* Mark the old record as obsolete. */
343 dh->usable = false;
344 }
345 }
346 else
347 {
348 /* We write the dataset before inserting it to the database
349 since while inserting this thread might block and so would
350 unnecessarily let the receiver wait. */
351 assert (fd != -1);
352
8c78faa9
AZ
353 if (writeall (fd, &dataset->resp, dataset->head.recsize)
354 != dataset->head.recsize)
355 all_written = false;
f7e7a396
UD
356 }
357
358
359 /* Add the record to the database. But only if it has not been
360 stored on the stack. */
361 if (! alloca_used)
362 {
363 /* If necessary, we also propagate the data to disk. */
364 if (db->persistent)
365 {
366 // XXX async OK?
367 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
368 msync ((void *) pval,
34a5a146
JM
369 ((uintptr_t) dataset & pagesize_m1) + total
370 + req->key_len, MS_ASYNC);
f7e7a396
UD
371 }
372
7e71e55f 373 (void) cache_add (INITGROUPS, cp, req->key_len, &dataset->head, true,
528741cb 374 db, uid, he == NULL);
00ebd7ed
UD
375
376 pthread_rwlock_unlock (&db->lock);
f7e7a396
UD
377 }
378 }
379
380 free (groups);
381
306dfba9 382 if (__builtin_expect (!all_written, 0) && debug_level > 0)
f7e7a396
UD
383 {
384 char buf[256];
385 dbg_log (_("short write in %s: %s"), __FUNCTION__,
386 strerror_r (errno, buf, sizeof (buf)));
387 }
a4c7ea7b
UD
388
389 return timeout;
f7e7a396
UD
390}
391
392
393void
394addinitgroups (struct database_dyn *db, int fd, request_header *req, void *key,
395 uid_t uid)
396{
397 addinitgroupsX (db, fd, req, key, uid, NULL, NULL);
398}
399
400
a4c7ea7b 401time_t
f7e7a396
UD
402readdinitgroups (struct database_dyn *db, struct hashentry *he,
403 struct datahead *dh)
404{
405 request_header req =
406 {
407 .type = INITGROUPS,
408 .key_len = he->len
409 };
410
a4c7ea7b 411 return addinitgroupsX (db, -1, &req, db->data + he->key, he->owner, he, dh);
f7e7a396 412}