ldbl-128ibm-compat: Provide nexttoward functions
[glibc.git] / nscd / initgrcache.c
bloba9f230bfb027ed1a0d8f1e408c23f54ef2bab7d5
1 /* Cache handling for host lookup.
2 Copyright (C) 2004-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
6 This program is free software; you can redistribute it and/or modify
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.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <https://www.gnu.org/licenses/>. */
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>
27 #include <scratch_buffer.h>
28 #include <config.h>
30 #include "dbg_log.h"
31 #include "nscd.h"
33 #include "../nss/nsswitch.h"
35 #ifdef LINK_OBSOLETE_NSL
36 # define DEFAULT_CONFIG "compat [NOTFOUND=return] files"
37 #else
38 # define DEFAULT_CONFIG "files"
39 #endif
41 /* Type of the lookup function. */
42 typedef enum nss_status (*initgroups_dyn_function) (const char *, gid_t,
43 long int *, long int *,
44 gid_t **, long int, int *);
47 static const initgr_response_header notfound =
49 .version = NSCD_VERSION,
50 .found = 0,
51 .ngrps = 0
55 #include "../grp/compat-initgroups.c"
58 static time_t
59 addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
60 void *key, uid_t uid, struct hashentry *const he,
61 struct datahead *dh)
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. */
69 /* We allocate all data in one memory block: the iov vector,
70 the response header and the dataset itself. */
71 struct dataset
73 struct datahead head;
74 initgr_response_header resp;
75 char strdata[0];
76 } *dataset = NULL;
78 if (__glibc_unlikely (debug_level > 0))
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);
86 static service_user *group_database;
87 service_user *nip;
88 int no_more;
90 if (group_database == NULL)
91 no_more = __nss_database_lookup2 ("group", NULL, DEFAULT_CONFIG,
92 &group_database);
93 else
94 no_more = 0;
95 nip = group_database;
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);
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;
110 long int start = 0;
111 bool all_tryagain = true;
112 bool any_success = false;
114 /* This is temporary memory, we need not (and must not) call
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));
118 if (__glibc_unlikely (groups == NULL))
119 /* No more memory. */
120 goto out;
122 /* Nothing added yet. */
123 while (! no_more)
125 long int prev_start = start;
126 enum nss_status status;
127 initgroups_dyn_function fct;
128 fct = __nss_lookup_function (nip, "initgroups_dyn");
130 if (fct == NULL)
132 status = compat_call (nip, key, -1, &start, &size, &groups,
133 limit, &errno);
135 if (nss_next_action (nip, NSS_STATUS_UNAVAIL) != NSS_ACTION_CONTINUE)
136 break;
138 else
139 status = DL_CALL_FCT (fct, (key, -1, &start, &size, &groups,
140 limit, &errno));
142 /* Remove duplicates. */
143 long int cnt = prev_start;
144 while (cnt < start)
146 long int inner;
147 for (inner = 0; inner < prev_start; ++inner)
148 if (groups[inner] == groups[cnt])
149 break;
151 if (inner < prev_start)
152 groups[cnt] = groups[--start];
153 else
154 ++cnt;
157 if (status != NSS_STATUS_TRYAGAIN)
158 all_tryagain = false;
160 /* This is really only for debugging. */
161 if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
162 __libc_fatal ("Illegal status in internal_getgrouplist.\n");
164 any_success |= status == NSS_STATUS_SUCCESS;
166 if (status != NSS_STATUS_SUCCESS
167 && nss_next_action (nip, status) == NSS_ACTION_RETURN)
168 break;
170 if (nip->next == NULL)
171 no_more = -1;
172 else
173 nip = nip->next;
176 bool all_written;
177 ssize_t total;
178 time_t timeout;
179 out:
180 all_written = true;
181 timeout = MAX_TIMEOUT_VALUE;
182 if (!any_success)
184 /* Nothing found. Create a negative result record. */
185 total = sizeof (notfound);
187 if (he != NULL && all_tryagain)
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;
196 /* Reload with the same time-to-live value. */
197 timeout = dh->timeout = time (NULL) + db->postimeout;
199 else
201 /* We have no data. This means we send the standard reply for this
202 case. */
203 if (fd != -1
204 && TEMP_FAILURE_RETRY (send (fd, &notfound, total,
205 MSG_NOSIGNAL)) != total)
206 all_written = false;
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))
212 /* Mark the old entry as obsolete. */
213 if (dh != NULL)
214 dh->usable = false;
216 else if ((dataset = mempool_alloc (db, (sizeof (struct dataset)
217 + req->key_len), 1)) != NULL)
219 timeout = datahead_init_neg (&dataset->head,
220 (sizeof (struct dataset)
221 + req->key_len), total,
222 db->negtimeout);
224 /* This is the reply. */
225 memcpy (&dataset->resp, &notfound, total);
227 /* Copy the key data. */
228 char *key_copy = memcpy (dataset->strdata, key, req->key_len);
230 /* If necessary, we also propagate the data to disk. */
231 if (db->persistent)
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);
240 (void) cache_add (req->type, key_copy, req->key_len,
241 &dataset->head, true, db, uid, he == NULL);
243 pthread_rwlock_unlock (&db->lock);
245 /* Mark the old entry as obsolete. */
246 if (dh != NULL)
247 dh->usable = false;
251 else
254 total = offsetof (struct dataset, strdata) + start * sizeof (int32_t);
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;
263 if (he == NULL)
264 dataset = (struct dataset *) mempool_alloc (db, total + req->key_len,
267 if (dataset == NULL)
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);
274 /* We cannot add this record to the permanent database. */
275 alloca_used = true;
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);
283 dataset->resp.version = NSCD_VERSION;
284 dataset->resp.found = 1;
285 dataset->resp.ngrps = start;
287 char *cp = dataset->strdata;
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
295 gid_t *gcp = (gid_t *) cp;
297 for (int i = 0; i < start; ++i)
298 *gcp++ = groups[i];
300 cp = (char *) gcp;
303 /* Finally the user name. */
304 memcpy (cp, key, req->key_len);
306 assert (cp == dataset->strdata + total - offsetof (struct dataset,
307 strdata));
309 /* Now we can determine whether on refill we have to create a new
310 record or not. */
311 if (he != NULL)
313 assert (fd == -1);
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)
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;
326 else
328 /* We have to create a new record. Just allocate
329 appropriate memory and copy it. */
330 struct dataset *newp
331 = (struct dataset *) mempool_alloc (db, total + req->key_len,
333 if (newp != NULL)
335 /* Adjust pointer into the memory block. */
336 cp = (char *) newp + (cp - (char *) dataset);
338 dataset = memcpy (newp, dataset, total + req->key_len);
339 alloca_used = false;
342 /* Mark the old record as obsolete. */
343 dh->usable = false;
346 else
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);
353 if (writeall (fd, &dataset->resp, dataset->head.recsize)
354 != dataset->head.recsize)
355 all_written = false;
359 /* Add the record to the database. But only if it has not been
360 stored on the stack. */
361 if (! alloca_used)
363 /* If necessary, we also propagate the data to disk. */
364 if (db->persistent)
366 // XXX async OK?
367 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
368 msync ((void *) pval,
369 ((uintptr_t) dataset & pagesize_m1) + total
370 + req->key_len, MS_ASYNC);
373 (void) cache_add (INITGROUPS, cp, req->key_len, &dataset->head, true,
374 db, uid, he == NULL);
376 pthread_rwlock_unlock (&db->lock);
380 free (groups);
382 if (__builtin_expect (!all_written, 0) && debug_level > 0)
384 char buf[256];
385 dbg_log (_("short write in %s: %s"), __FUNCTION__,
386 strerror_r (errno, buf, sizeof (buf)));
389 return timeout;
393 void
394 addinitgroups (struct database_dyn *db, int fd, request_header *req, void *key,
395 uid_t uid)
397 addinitgroupsX (db, fd, req, key, uid, NULL, NULL);
401 time_t
402 readdinitgroups (struct database_dyn *db, struct hashentry *he,
403 struct datahead *dh)
405 request_header req =
407 .type = INITGROUPS,
408 .key_len = he->len
411 return addinitgroupsX (db, -1, &req, db->data + he->key, he->owner, he, dh);