]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/grpcache.c
Update.
[thirdparty/glibc.git] / nscd / grpcache.c
CommitLineData
67479a70 1/* Cache handling for group lookup.
99bb9f42 2 Copyright (C) 1998-2002, 2003, 2004 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
UD
5
6 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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
UD
10
11 The GNU C Library 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 GNU
41bdb6e2 14 Lesser General Public License for more details.
d67281a7 15
41bdb6e2
AJ
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
a95a08b4 110 written = TEMP_FAILURE_RETRY (write (fd, &notfound, total));
d67281a7 111
a95a08b4
UD
112 dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
113 /* If we cannot permanently store the result, so be it. */
114 if (dataset != NULL)
115 {
116 dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
117 dataset->head.recsize = total;
118 dataset->head.notfound = true;
119 dataset->head.nreloads = 0;
120 dataset->head.usable = true;
d67281a7 121
a95a08b4
UD
122 /* Compute the timeout time. */
123 dataset->head.timeout = t + db->negtimeout;
14e9dd67 124
a95a08b4
UD
125 /* This is the reply. */
126 memcpy (&dataset->resp, &notfound, total);
d67281a7 127
a95a08b4
UD
128 /* Copy the key data. */
129 memcpy (dataset->strdata, key, req->key_len);
d67281a7 130
a95a08b4
UD
131 /* Now get the lock to safely insert the records. */
132 pthread_rwlock_rdlock (&db->lock);
133
134 if (cache_add (req->type, &dataset->strdata, req->key_len,
135 &dataset->head, true, db, owner) < 0)
136 /* Ensure the data can be recovered. */
137 dataset->head.usable = false;
138
139 pthread_rwlock_unlock (&db->lock);
140
141 /* Mark the old entry as obsolete. */
142 if (dh != NULL)
143 dh->usable = false;
144 }
145 else
146 ++db->head->addfailed;
99bb9f42 147 }
d67281a7 148 }
ba9234d9
UD
149 else
150 {
67479a70 151 /* Determine the I/O structure. */
67479a70
UD
152 size_t gr_name_len = strlen (grp->gr_name) + 1;
153 size_t gr_passwd_len = strlen (grp->gr_passwd) + 1;
154 size_t gr_mem_cnt = 0;
3107c0c5 155 uint32_t *gr_mem_len;
67479a70
UD
156 size_t gr_mem_len_total = 0;
157 char *gr_name;
158 char *cp;
a95a08b4
UD
159 const size_t key_len = strlen (key);
160 const size_t buf_len = 3 + sizeof (grp->gr_gid) + key_len + 1;
161 char *buf = alloca (buf_len);
67479a70
UD
162 ssize_t n;
163 size_t cnt;
164
165 /* We need this to insert the `bygid' entry. */
a95a08b4
UD
166 int key_offset;
167 n = snprintf (buf, buf_len, "%d%c%n%s", grp->gr_gid, '\0',
168 &key_offset, (char *) key) + 1;
67479a70
UD
169
170 /* Determine the length of all members. */
171 while (grp->gr_mem[gr_mem_cnt])
172 ++gr_mem_cnt;
3107c0c5 173 gr_mem_len = (uint32_t *) alloca (gr_mem_cnt * sizeof (uint32_t));
67479a70
UD
174 for (gr_mem_cnt = 0; grp->gr_mem[gr_mem_cnt]; ++gr_mem_cnt)
175 {
176 gr_mem_len[gr_mem_cnt] = strlen (grp->gr_mem[gr_mem_cnt]) + 1;
177 gr_mem_len_total += gr_mem_len[gr_mem_cnt];
178 }
ba9234d9 179
a95a08b4
UD
180 written = total = (sizeof (struct dataset)
181 + gr_mem_cnt * sizeof (uint32_t)
182 + gr_name_len + gr_passwd_len + gr_mem_len_total);
183
184 /* If we refill the cache, first assume the reconrd did not
185 change. Allocate memory on the cache since it is likely
186 discarded anyway. If it turns out to be necessary to have a
187 new record we can still allocate real memory. */
188 bool alloca_used = false;
189 dataset = NULL;
190
191 if (he == NULL)
192 {
193 dataset = (struct dataset *) mempool_alloc (db, total + n);
194 if (dataset == NULL)
195 ++db->head->addfailed;
196 }
197
198 if (dataset == NULL)
199 {
200 /* We cannot permanently add the result in the moment. But
201 we can provide the result as is. Store the data in some
202 temporary memory. */
203 dataset = (struct dataset *) alloca (total + n);
204
205 /* We cannot add this record to the permanent database. */
206 alloca_used = true;
207 }
208
209 dataset->head.allocsize = total + n;
210 dataset->head.recsize = total - offsetof (struct dataset, resp);
211 dataset->head.notfound = false;
212 dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
213 dataset->head.usable = true;
214
215 /* Compute the timeout time. */
216 dataset->head.timeout = t + db->postimeout;
217
218 dataset->resp.version = NSCD_VERSION;
219 dataset->resp.found = 1;
220 dataset->resp.gr_name_len = gr_name_len;
221 dataset->resp.gr_passwd_len = gr_passwd_len;
222 dataset->resp.gr_gid = grp->gr_gid;
223 dataset->resp.gr_mem_cnt = gr_mem_cnt;
224
225 cp = dataset->strdata;
d67281a7 226
67479a70 227 /* This is the member string length array. */
3107c0c5 228 cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (uint32_t));
0a55a284
UD
229 gr_name = cp;
230 cp = mempcpy (cp, grp->gr_name, gr_name_len);
67479a70 231 cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
d67281a7 232
67479a70
UD
233 for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
234 cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
d67281a7 235
a95a08b4 236 /* Finally the stringified GID value. */
67479a70 237 memcpy (cp, buf, n);
a95a08b4
UD
238 char *key_copy = cp + key_offset;
239 assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
d67281a7 240
a95a08b4
UD
241 /* Now we can determine whether on refill we have to create a new
242 record or not. */
243 if (he != NULL)
244 {
245 assert (fd == -1);
d6db0975 246
a95a08b4
UD
247 if (total + n == dh->allocsize
248 && total - offsetof (struct dataset, resp) == dh->recsize
249 && memcmp (&dataset->resp, dh->data,
250 dh->allocsize - offsetof (struct dataset, resp)) == 0)
251 {
252 /* The data has not changed. We will just bump the
253 timeout value. Note that the new record has been
254 allocated on the stack and need not be freed. */
255 dh->timeout = dataset->head.timeout;
256 ++dh->nreloads;
257 }
258 else
259 {
260 /* We have to create a new record. Just allocate
261 appropriate memory and copy it. */
262 struct dataset *newp
263 = (struct dataset *) mempool_alloc (db, total + n);
264 if (newp != NULL)
265 {
266 /* Adjust pointers into the memory block. */
267 gr_name = (char *) newp + (gr_name - (char *) dataset);
268 cp = (char *) newp + (cp - (char *) dataset);
269
270 dataset = memcpy (newp, dataset, total + n);
271 alloca_used = false;
272 }
273
274 /* Mark the old record as obsolete. */
275 dh->usable = false;
276 }
277 }
278 else
279 {
280 /* We write the dataset before inserting it to the database
281 since while inserting this thread might block and so would
282 unnecessarily let the receiver wait. */
283 assert (fd != -1);
d67281a7 284
a95a08b4
UD
285 written = TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total));
286 }
d67281a7 287
a95a08b4
UD
288 /* Add the record to the database. But only if it has not been
289 stored on the stack. */
290 if (! alloca_used)
291 {
292 /* If necessary, we also propagate the data to disk. */
293 if (db->persistent)
294 // XXX async OK?
295 msync (dataset, total + n, MS_ASYNC);
14e9dd67 296
a95a08b4
UD
297 /* Now get the lock to safely insert the records. */
298 pthread_rwlock_rdlock (&db->lock);
d67281a7 299
a95a08b4
UD
300 /* NB: in the following code we always must add the entry
301 marked with FIRST first. Otherwise we end up with
302 dangling "pointers" in case a latter hash entry cannot be
303 added. */
304 bool first = req->type == GETGRBYNAME;
dc630ccc 305
a95a08b4
UD
306 /* If the request was by GID, add that entry first. */
307 if (req->type != GETGRBYNAME)
308 {
3d73c4ba
UD
309 if (cache_add (GETGRBYGID, cp, key_offset, &dataset->head, true,
310 db, owner) < 0)
a95a08b4
UD
311 {
312 /* Could not allocate memory. Make sure the data gets
313 discarded. */
314 dataset->head.usable = false;
315 goto out;
316 }
317 }
318 /* If the key is different from the name add a separate entry. */
319 else if (strcmp (key_copy, gr_name) != 0)
320 {
321 if (cache_add (GETGRBYNAME, key_copy, key_len + 1,
322 &dataset->head, first, db, owner) < 0)
323 {
324 /* Could not allocate memory. Make sure the data gets
325 discarded. */
326 dataset->head.usable = false;
327 goto out;
328 }
329
330 first = false;
331 }
332
333 /* We have to add the value for both, byname and byuid. */
334 if (__builtin_expect (cache_add (GETGRBYNAME, gr_name, gr_name_len,
335 &dataset->head, first, db, owner)
336 == 0, 1))
337 {
338 if (req->type == GETGRBYNAME)
3d73c4ba 339 (void) cache_add (GETGRBYGID, cp, key_offset, &dataset->head,
a95a08b4
UD
340 req->type != GETGRBYNAME, db, owner);
341 }
342 else if (first)
343 /* Could not allocate memory. Make sure the data gets
344 discarded. */
345 dataset->head.usable = false;
d67281a7 346
a95a08b4
UD
347 out:
348 pthread_rwlock_unlock (&db->lock);
349 }
d67281a7
UD
350 }
351
23700036 352 if (__builtin_expect (written != total, 0) && debug_level > 0)
d67281a7 353 {
67479a70
UD
354 char buf[256];
355 dbg_log (_("short write in %s: %s"), __FUNCTION__,
356 strerror_r (errno, buf, sizeof (buf)));
d67281a7 357 }
d67281a7
UD
358}
359
d67281a7 360
a95a08b4
UD
361union keytype
362{
363 void *v;
364 gid_t g;
365};
366
367
368static int
369lookup (int type, union keytype key, struct group *resultbufp, char *buffer,
370 size_t buflen, struct group **grp)
371{
372 if (type == GETGRBYNAME)
373 return __getgrnam_r (key.v, resultbufp, buffer, buflen, grp);
374 else
375 return __getgrgid_r (key.g, resultbufp, buffer, buflen, grp);
376}
377
378
379static void
380addgrbyX (struct database_dyn *db, int fd, request_header *req,
381 union keytype key, const char *keystr, uid_t uid,
382 struct hashentry *he, struct datahead *dh)
67479a70
UD
383{
384 /* Search for the entry matching the key. Please note that we don't
385 look again in the table whether the dataset is now available. We
386 simply insert it. It does not matter if it is in there twice. The
387 pruning function only will look at the timestamp. */
a95a08b4 388 size_t buflen = 1024;
c7a9b6e2 389 char *buffer = (char *) alloca (buflen);
67479a70
UD
390 struct group resultbuf;
391 struct group *grp;
a1c542bf 392 uid_t oldeuid = 0;
0472723a 393 bool use_malloc = false;
a95a08b4 394 int errval = 0;
d67281a7 395
c7a9b6e2 396 if (__builtin_expect (debug_level > 0, 0))
a95a08b4
UD
397 {
398 if (he == NULL)
399 dbg_log (_("Haven't found \"%s\" in group cache!"), keystr);
400 else
401 dbg_log (_("Reloading \"%s\" in group cache!"), keystr);
402 }
d67281a7 403
a95a08b4 404 if (db->secure)
a1c542bf
UD
405 {
406 oldeuid = geteuid ();
407 seteuid (uid);
408 }
409
a95a08b4
UD
410 while (lookup (req->type, key, &resultbuf, buffer, buflen, &grp) != 0
411 && (errval = errno) == ERANGE)
d67281a7 412 {
0472723a 413 char *old_buffer = buffer;
67479a70 414 errno = 0;
9caf4f1c 415#define INCR 1024
0472723a
UD
416
417 if (__builtin_expect (buflen > 32768, 0))
418 {
9caf4f1c 419 buflen += INCR;
0472723a
UD
420 buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
421 if (buffer == NULL)
422 {
423 /* We ran out of memory. We cannot do anything but
424 sending a negative response. In reality this should
425 never happen. */
426 grp = NULL;
427 buffer = old_buffer;
a95a08b4
UD
428
429 /* We set the error to indicate this is (possibly) a
430 temporary error and that it does not mean the entry
431 is not available at all. */
432 errval = EAGAIN;
0472723a
UD
433 break;
434 }
435 use_malloc = true;
436 }
437 else
9caf4f1c
UD
438 /* Allocate a new buffer on the stack. If possible combine it
439 with the previously allocated buffer. */
440 buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
d67281a7 441 }
d67281a7 442
a95a08b4 443 if (db->secure)
a1c542bf
UD
444 seteuid (oldeuid);
445
a95a08b4 446 cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval);
0472723a
UD
447
448 if (use_malloc)
449 free (buffer);
d67281a7
UD
450}
451
d67281a7 452
67479a70 453void
a95a08b4
UD
454addgrbyname (struct database_dyn *db, int fd, request_header *req,
455 void *key, uid_t uid)
456{
457 union keytype u = { .v = key };
458
459 addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
460}
461
462
463void
464readdgrbyname (struct database_dyn *db, struct hashentry *he,
465 struct datahead *dh)
466{
467 request_header req =
468 {
469 .type = GETGRBYNAME,
470 .key_len = he->len
471 };
472 union keytype u = { .v = db->data + he->key };
473
474 addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
475}
476
477
478void
479addgrbygid (struct database_dyn *db, int fd, request_header *req,
a1c542bf 480 void *key, uid_t uid)
67479a70 481{
8e9b2075 482 char *ep;
a95a08b4 483 gid_t gid = strtoul ((char *) key, &ep, 10);
0472723a 484
a95a08b4 485 if (*(char *) key == '\0' || *ep != '\0') /* invalid numeric uid */
8e9b2075
UD
486 {
487 if (debug_level > 0)
c7a9b6e2 488 dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);
8e9b2075
UD
489
490 errno = EINVAL;
491 return;
492 }
d67281a7 493
a95a08b4 494 union keytype u = { .g = gid };
d67281a7 495
a95a08b4
UD
496 addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
497}
a1c542bf 498
0472723a 499
a95a08b4
UD
500void
501readdgrbygid (struct database_dyn *db, struct hashentry *he,
502 struct datahead *dh)
503{
504 char *ep;
505 gid_t gid = strtoul (db->data + he->key, &ep, 10);
d67281a7 506
a95a08b4
UD
507 /* Since the key has been added before it must be OK. */
508 assert (*(db->data + he->key) != '\0' && *ep == '\0');
a1c542bf 509
a95a08b4
UD
510 request_header req =
511 {
512 .type = GETGRBYGID,
513 .key_len = he->len
514 };
515 union keytype u = { .g = gid };
0472723a 516
a95a08b4 517 addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
d67281a7 518}