]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/grpcache.c
* sysdeps/unix/sysv/linux/sparc/bits/shm.h: Define SHM_NORESERVE.
[thirdparty/glibc.git] / nscd / grpcache.c
CommitLineData
67479a70 1/* Cache handling for group lookup.
4379b403 2 Copyright (C) 1998-2002, 2003, 2004, 2005 Free Software Foundation, Inc.
d67281a7 3 This file is part of the GNU C Library.
67479a70 4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
d67281a7 5
a334319f
UD
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
d67281a7 10
a334319f 11 The GNU C Library is distributed in the hope that it will be useful,
d67281a7 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
a334319f
UD
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
d67281a7 15
a334319f
UD
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
d67281a7 20
9caf4f1c 21#include <alloca.h>
a95a08b4 22#include <assert.h>
d67281a7 23#include <errno.h>
67479a70 24#include <error.h>
d67281a7 25#include <grp.h>
a95a08b4 26#include <libintl.h>
0472723a 27#include <stdbool.h>
67479a70
UD
28#include <stddef.h>
29#include <stdio.h>
3107c0c5 30#include <stdint.h>
da2d1bc5 31#include <stdlib.h>
d67281a7 32#include <string.h>
ba9234d9 33#include <unistd.h>
a95a08b4 34#include <sys/mman.h>
0472723a 35#include <stackinfo.h>
d67281a7 36
d67281a7 37#include "nscd.h"
67479a70 38#include "dbg_log.h"
d67281a7 39
67479a70
UD
40/* This is the standard reply in case the service is disabled. */
41static const gr_response_header disabled =
d67281a7 42{
c2e13112
RM
43 .version = NSCD_VERSION,
44 .found = -1,
45 .gr_name_len = 0,
46 .gr_passwd_len = 0,
47 .gr_gid = -1,
48 .gr_mem_cnt = 0,
d67281a7 49};
d67281a7 50
67479a70
UD
51/* This is the struct describing how to write this record. */
52const struct iovec grp_iov_disabled =
d67281a7 53{
c2e13112
RM
54 .iov_base = (void *) &disabled,
55 .iov_len = sizeof (disabled)
d67281a7 56};
d67281a7 57
67479a70
UD
58
59/* This is the standard reply in case we haven't found the dataset. */
60static const gr_response_header notfound =
d67281a7 61{
c2e13112
RM
62 .version = NSCD_VERSION,
63 .found = 0,
64 .gr_name_len = 0,
65 .gr_passwd_len = 0,
66 .gr_gid = -1,
67 .gr_mem_cnt = 0,
d67281a7 68};
d67281a7 69
d67281a7 70
67479a70 71static void
a95a08b4
UD
72cache_addgr (struct database_dyn *db, int fd, request_header *req,
73 const void *key, struct group *grp, uid_t owner,
74 struct hashentry *he, struct datahead *dh, int errval)
d67281a7 75{
67479a70
UD
76 ssize_t total;
77 ssize_t written;
78 time_t t = time (NULL);
d67281a7 79
a95a08b4
UD
80 /* We allocate all data in one memory block: the iov vector,
81 the response header and the dataset itself. */
82 struct dataset
83 {
84 struct datahead head;
85 gr_response_header resp;
86 char strdata[0];
87 } *dataset;
88
89 assert (offsetof (struct dataset, resp) == offsetof (struct datahead, data));
90
67479a70 91 if (grp == NULL)
d67281a7 92 {
a95a08b4
UD
93 if (he != NULL && errval == EAGAIN)
94 {
95 /* If we have an old record available but cannot find one
96 now because the service is not available we keep the old
97 record and make sure it does not get removed. */
98 if (reload_count != UINT_MAX)
99 /* Do not reset the value if we never not reload the record. */
100 dh->nreloads = reload_count - 1;
101
102 written = total = 0;
103 }
104 else
105 {
106 /* We have no data. This means we send the standard reply for this
107 case. */
108 total = sizeof (notfound);
d67281a7 109
2c210d1e
UD
110 written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
111 MSG_NOSIGNAL));
d67281a7 112
a95a08b4
UD
113 dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
114 /* If we cannot permanently store the result, so be it. */
115 if (dataset != NULL)
116 {
117 dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
118 dataset->head.recsize = total;
119 dataset->head.notfound = true;
120 dataset->head.nreloads = 0;
121 dataset->head.usable = true;
d67281a7 122
a95a08b4
UD
123 /* Compute the timeout time. */
124 dataset->head.timeout = t + db->negtimeout;
14e9dd67 125
a95a08b4
UD
126 /* This is the reply. */
127 memcpy (&dataset->resp, &notfound, total);
d67281a7 128
a95a08b4
UD
129 /* Copy the key data. */
130 memcpy (dataset->strdata, key, req->key_len);
d67281a7 131
cf244b74
UD
132 /* If necessary, we also propagate the data to disk. */
133 if (db->persistent)
134 {
135 // XXX async OK?
136 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
137 msync ((void *) pval,
138 ((uintptr_t) dataset & pagesize_m1)
139 + sizeof (struct dataset) + req->key_len, MS_ASYNC);
140 }
141
a95a08b4
UD
142 /* Now get the lock to safely insert the records. */
143 pthread_rwlock_rdlock (&db->lock);
144
145 if (cache_add (req->type, &dataset->strdata, req->key_len,
146 &dataset->head, true, db, owner) < 0)
147 /* Ensure the data can be recovered. */
148 dataset->head.usable = false;
149
150 pthread_rwlock_unlock (&db->lock);
151
152 /* Mark the old entry as obsolete. */
153 if (dh != NULL)
154 dh->usable = false;
155 }
156 else
157 ++db->head->addfailed;
99bb9f42 158 }
d67281a7 159 }
ba9234d9
UD
160 else
161 {
67479a70 162 /* Determine the I/O structure. */
67479a70
UD
163 size_t gr_name_len = strlen (grp->gr_name) + 1;
164 size_t gr_passwd_len = strlen (grp->gr_passwd) + 1;
165 size_t gr_mem_cnt = 0;
3107c0c5 166 uint32_t *gr_mem_len;
67479a70
UD
167 size_t gr_mem_len_total = 0;
168 char *gr_name;
169 char *cp;
a95a08b4 170 const size_t key_len = strlen (key);
9f3731cf 171 const size_t buf_len = 3 * sizeof (grp->gr_gid) + key_len + 1;
a95a08b4 172 char *buf = alloca (buf_len);
67479a70
UD
173 ssize_t n;
174 size_t cnt;
175
176 /* We need this to insert the `bygid' entry. */
a95a08b4
UD
177 int key_offset;
178 n = snprintf (buf, buf_len, "%d%c%n%s", grp->gr_gid, '\0',
179 &key_offset, (char *) key) + 1;
67479a70
UD
180
181 /* Determine the length of all members. */
182 while (grp->gr_mem[gr_mem_cnt])
183 ++gr_mem_cnt;
3107c0c5 184 gr_mem_len = (uint32_t *) alloca (gr_mem_cnt * sizeof (uint32_t));
67479a70
UD
185 for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
186 {
187 gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
188 gr_mem_len_total += gr_mem_len[gr_mem_cnt];
189 }
ba9234d9 190
a95a08b4
UD
191 written = total = (sizeof (struct dataset)
192 + gr_mem_cnt * sizeof (uint32_t)
193 + gr_name_len + gr_passwd_len + gr_mem_len_total);
194
195 /* If we refill the cache, first assume the reconrd did not
196 change. Allocate memory on the cache since it is likely
197 discarded anyway. If it turns out to be necessary to have a
198 new record we can still allocate real memory. */
199 bool alloca_used = false;
200 dataset = NULL;
201
202 if (he == NULL)
203 {
204 dataset = (struct dataset *) mempool_alloc (db, total + n);
205 if (dataset == NULL)
206 ++db->head->addfailed;
207 }
208
209 if (dataset == NULL)
210 {
211 /* We cannot permanently add the result in the moment. But
212 we can provide the result as is. Store the data in some
213 temporary memory. */
214 dataset = (struct dataset *) alloca (total + n);
215
216 /* We cannot add this record to the permanent database. */
217 alloca_used = true;
218 }
219
220 dataset->head.allocsize = total + n;
221 dataset->head.recsize = total - offsetof (struct dataset, resp);
222 dataset->head.notfound = false;
223 dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
224 dataset->head.usable = true;
225
226 /* Compute the timeout time. */
227 dataset->head.timeout = t + db->postimeout;
228
229 dataset->resp.version = NSCD_VERSION;
230 dataset->resp.found = 1;
231 dataset->resp.gr_name_len = gr_name_len;
232 dataset->resp.gr_passwd_len = gr_passwd_len;
233 dataset->resp.gr_gid = grp->gr_gid;
234 dataset->resp.gr_mem_cnt = gr_mem_cnt;
235
236 cp = dataset->strdata;
d67281a7 237
67479a70 238 /* This is the member string length array. */
3107c0c5 239 cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (uint32_t));
0a55a284
UD
240 gr_name = cp;
241 cp = mempcpy (cp, grp->gr_name, gr_name_len);
67479a70 242 cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
d67281a7 243
67479a70
UD
244 for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
245 cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
d67281a7 246
a95a08b4 247 /* Finally the stringified GID value. */
67479a70 248 memcpy (cp, buf, n);
a95a08b4
UD
249 char *key_copy = cp + key_offset;
250 assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
d67281a7 251
a95a08b4
UD
252 /* Now we can determine whether on refill we have to create a new
253 record or not. */
254 if (he != NULL)
255 {
256 assert (fd == -1);
d6db0975 257
a95a08b4
UD
258 if (total + n == dh->allocsize
259 && total - offsetof (struct dataset, resp) == dh->recsize
260 && memcmp (&dataset->resp, dh->data,
261 dh->allocsize - offsetof (struct dataset, resp)) == 0)
262 {
263 /* The data has not changed. We will just bump the
264 timeout value. Note that the new record has been
265 allocated on the stack and need not be freed. */
266 dh->timeout = dataset->head.timeout;
267 ++dh->nreloads;
268 }
269 else
270 {
271 /* We have to create a new record. Just allocate
272 appropriate memory and copy it. */
273 struct dataset *newp
274 = (struct dataset *) mempool_alloc (db, total + n);
275 if (newp != NULL)
276 {
277 /* Adjust pointers into the memory block. */
278 gr_name = (char *) newp + (gr_name - (char *) dataset);
279 cp = (char *) newp + (cp - (char *) dataset);
280
281 dataset = memcpy (newp, dataset, total + n);
282 alloca_used = false;
283 }
284
285 /* Mark the old record as obsolete. */
286 dh->usable = false;
287 }
288 }
289 else
290 {
291 /* We write the dataset before inserting it to the database
292 since while inserting this thread might block and so would
293 unnecessarily let the receiver wait. */
294 assert (fd != -1);
d67281a7 295
d2dc7d84 296 written = writeall (fd, &dataset->resp, total);
a95a08b4 297 }
d67281a7 298
a95a08b4
UD
299 /* Add the record to the database. But only if it has not been
300 stored on the stack. */
301 if (! alloca_used)
302 {
303 /* If necessary, we also propagate the data to disk. */
304 if (db->persistent)
3418007e
UD
305 {
306 // XXX async OK?
307 uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
308 msync ((void *) pval,
309 ((uintptr_t) dataset & pagesize_m1) + total + n,
310 MS_ASYNC);
311 }
14e9dd67 312
a95a08b4
UD
313 /* Now get the lock to safely insert the records. */
314 pthread_rwlock_rdlock (&db->lock);
d67281a7 315
a95a08b4
UD
316 /* NB: in the following code we always must add the entry
317 marked with FIRST first. Otherwise we end up with
318 dangling "pointers" in case a latter hash entry cannot be
319 added. */
a334319f 320 bool first = req->type == GETGRBYNAME;
dc630ccc 321
a95a08b4 322 /* If the request was by GID, add that entry first. */
a334319f 323 if (req->type != GETGRBYNAME)
a95a08b4 324 {
3d73c4ba
UD
325 if (cache_add (GETGRBYGID, cp, key_offset, &dataset->head, true,
326 db, owner) < 0)
a95a08b4
UD
327 {
328 /* Could not allocate memory. Make sure the data gets
329 discarded. */
330 dataset->head.usable = false;
331 goto out;
332 }
333 }
334 /* If the key is different from the name add a separate entry. */
335 else if (strcmp (key_copy, gr_name) != 0)
336 {
337 if (cache_add (GETGRBYNAME, key_copy, key_len + 1,
a334319f 338 &dataset->head, first, db, owner) < 0)
a95a08b4
UD
339 {
340 /* Could not allocate memory. Make sure the data gets
341 discarded. */
342 dataset->head.usable = false;
343 goto out;
344 }
345
346 first = false;
347 }
348
349 /* We have to add the value for both, byname and byuid. */
a334319f
UD
350 if (__builtin_expect (cache_add (GETGRBYNAME, gr_name, gr_name_len,
351 &dataset->head, first, db, owner)
352 == 0, 1))
a95a08b4 353 {
a334319f 354 if (req->type == GETGRBYNAME)
3d73c4ba 355 (void) cache_add (GETGRBYGID, cp, key_offset, &dataset->head,
a95a08b4
UD
356 req->type != GETGRBYNAME, db, owner);
357 }
358 else if (first)
359 /* Could not allocate memory. Make sure the data gets
360 discarded. */
361 dataset->head.usable = false;
d67281a7 362
a95a08b4
UD
363 out:
364 pthread_rwlock_unlock (&db->lock);
365 }
d67281a7
UD
366 }
367
23700036 368 if (__builtin_expect (written != total, 0) && debug_level > 0)
d67281a7 369 {
67479a70
UD
370 char buf[256];
371 dbg_log (_("short write in %s: %s"), __FUNCTION__,
372 strerror_r (errno, buf, sizeof (buf)));
d67281a7 373 }
d67281a7
UD
374}
375
d67281a7 376
a95a08b4
UD
377union keytype
378{
379 void *v;
380 gid_t g;
381};
382
383
384static int
385lookup (int type, union keytype key, struct group *resultbufp, char *buffer,
386 size_t buflen, struct group **grp)
387{
388 if (type == GETGRBYNAME)
389 return __getgrnam_r (key.v, resultbufp, buffer, buflen, grp);
390 else
391 return __getgrgid_r (key.g, resultbufp, buffer, buflen, grp);
392}
393
394
395static void
396addgrbyX (struct database_dyn *db, int fd, request_header *req,
397 union keytype key, const char *keystr, uid_t uid,
398 struct hashentry *he, struct datahead *dh)
67479a70
UD
399{
400 /* Search for the entry matching the key. Please note that we don't
401 look again in the table whether the dataset is now available. We
402 simply insert it. It does not matter if it is in there twice. The
403 pruning function only will look at the timestamp. */
a95a08b4 404 size_t buflen = 1024;
c7a9b6e2 405 char *buffer = (char *) alloca (buflen);
67479a70
UD
406 struct group resultbuf;
407 struct group *grp;
0472723a 408 bool use_malloc = false;
a95a08b4 409 int errval = 0;
d67281a7 410
c7a9b6e2 411 if (__builtin_expect (debug_level > 0, 0))
a95a08b4
UD
412 {
413 if (he == NULL)
414 dbg_log (_("Haven't found \"%s\" in group cache!"), keystr);
415 else
416 dbg_log (_("Reloading \"%s\" in group cache!"), keystr);
417 }
d67281a7 418
3c12b91a
UD
419#if 0
420 uid_t oldeuid = 0;
a95a08b4 421 if (db->secure)
a1c542bf
UD
422 {
423 oldeuid = geteuid ();
2edb61e3 424 pthread_seteuid_np (uid);
a1c542bf 425 }
3c12b91a 426#endif
a1c542bf 427
a95a08b4
UD
428 while (lookup (req->type, key, &resultbuf, buffer, buflen, &grp) != 0
429 && (errval = errno) == ERANGE)
d67281a7 430 {
0472723a 431 char *old_buffer = buffer;
67479a70 432 errno = 0;
0472723a
UD
433
434 if (__builtin_expect (buflen > 32768, 0))
435 {
4379b403 436 buflen *= 2;
0472723a
UD
437 buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
438 if (buffer == NULL)
439 {
440 /* We ran out of memory. We cannot do anything but
441 sending a negative response. In reality this should
442 never happen. */
443 grp = NULL;
444 buffer = old_buffer;
a95a08b4
UD
445
446 /* We set the error to indicate this is (possibly) a
447 temporary error and that it does not mean the entry
448 is not available at all. */
449 errval = EAGAIN;
0472723a
UD
450 break;
451 }
452 use_malloc = true;
453 }
454 else
9caf4f1c
UD
455 /* Allocate a new buffer on the stack. If possible combine it
456 with the previously allocated buffer. */
4379b403 457 buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
d67281a7 458 }
d67281a7 459
3c12b91a 460#if 0
a95a08b4 461 if (db->secure)
2edb61e3 462 pthread_seteuid_np (oldeuid);
3c12b91a 463#endif
a1c542bf 464
a95a08b4 465 cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval);
0472723a
UD
466
467 if (use_malloc)
468 free (buffer);
d67281a7
UD
469}
470
d67281a7 471
67479a70 472void
a95a08b4
UD
473addgrbyname (struct database_dyn *db, int fd, request_header *req,
474 void *key, uid_t uid)
475{
476 union keytype u = { .v = key };
477
478 addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
479}
480
481
482void
483readdgrbyname (struct database_dyn *db, struct hashentry *he,
484 struct datahead *dh)
485{
486 request_header req =
487 {
488 .type = GETGRBYNAME,
489 .key_len = he->len
490 };
491 union keytype u = { .v = db->data + he->key };
492
493 addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
494}
495
496
497void
498addgrbygid (struct database_dyn *db, int fd, request_header *req,
a1c542bf 499 void *key, uid_t uid)
67479a70 500{
8e9b2075 501 char *ep;
a95a08b4 502 gid_t gid = strtoul ((char *) key, &ep, 10);
0472723a 503
a95a08b4 504 if (*(char *) key == '\0' || *ep != '\0') /* invalid numeric uid */
8e9b2075
UD
505 {
506 if (debug_level > 0)
c7a9b6e2 507 dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);
8e9b2075
UD
508
509 errno = EINVAL;
510 return;
511 }
d67281a7 512
a95a08b4 513 union keytype u = { .g = gid };
d67281a7 514
a95a08b4
UD
515 addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
516}
a1c542bf 517
0472723a 518
a95a08b4
UD
519void
520readdgrbygid (struct database_dyn *db, struct hashentry *he,
521 struct datahead *dh)
522{
523 char *ep;
524 gid_t gid = strtoul (db->data + he->key, &ep, 10);
d67281a7 525
a95a08b4
UD
526 /* Since the key has been added before it must be OK. */
527 assert (*(db->data + he->key) != '\0' && *ep == '\0');
a1c542bf 528
a95a08b4
UD
529 request_header req =
530 {
531 .type = GETGRBYGID,
532 .key_len = he->len
533 };
534 union keytype u = { .g = gid };
0472723a 535
a95a08b4 536 addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
d67281a7 537}