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