e391dc39840154d4c4113167f51976419da511cc
[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 = (sizeof (struct dataset)
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       /* Now we can determine whether on refill we have to create a new
256          record or not.  */
257       if (he != NULL)
258         {
259           assert (fd == -1);
260
261           if (total + n == dh->allocsize
262               && total - offsetof (struct dataset, resp) == dh->recsize
263               && memcmp (&dataset->resp, dh->data,
264                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
265             {
266               /* The data has not changed.  We will just bump the
267                  timeout value.  Note that the new record has been
268                  allocated on the stack and need not be freed.  */
269               dh->timeout = dataset->head.timeout;
270               ++dh->nreloads;
271             }
272           else
273             {
274               /* We have to create a new record.  Just allocate
275                  appropriate memory and copy it.  */
276               struct dataset *newp
277                 = (struct dataset *) mempool_alloc (db, total + n,
278                                                     IDX_result_data);
279               if (newp != NULL)
280                 {
281                   /* Adjust pointers into the memory block.  */
282                   gr_name = (char *) newp + (gr_name - (char *) dataset);
283                   cp = (char *) newp + (cp - (char *) dataset);
284                   key_copy = (char *) newp + (key_copy - (char *) dataset);
285
286                   dataset = memcpy (newp, dataset, total + n);
287                   alloca_used = false;
288                 }
289               else
290                 ++db->head->addfailed;
291
292               /* Mark the old record as obsolete.  */
293               dh->usable = false;
294             }
295         }
296       else
297         {
298           /* We write the dataset before inserting it to the database
299              since while inserting this thread might block and so would
300              unnecessarily let the receiver wait.  */
301           assert (fd != -1);
302
303 #ifdef HAVE_SENDFILE
304           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
305             {
306               assert (db->wr_fd != -1);
307               assert ((char *) &dataset->resp > (char *) db->data);
308               assert ((char *) &dataset->resp - (char *) db->head
309                       + total
310                       <= (sizeof (struct database_pers_head)
311                           + db->head->module * sizeof (ref_t)
312                           + db->head->data_size));
313               written = sendfileall (fd, db->wr_fd,
314                                      (char *) &dataset->resp
315                                      - (char *) db->head, total);
316 # ifndef __ASSUME_SENDFILE
317               if (written == -1 && errno == ENOSYS)
318                 goto use_write;
319 # endif
320             }
321           else
322 # ifndef __ASSUME_SENDFILE
323           use_write:
324 # endif
325 #endif
326             written = writeall (fd, &dataset->resp, total);
327         }
328
329       /* Add the record to the database.  But only if it has not been
330          stored on the stack.  */
331       if (! alloca_used)
332         {
333           /* If necessary, we also propagate the data to disk.  */
334           if (db->persistent)
335             {
336               // XXX async OK?
337               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
338               msync ((void *) pval,
339                      ((uintptr_t) dataset & pagesize_m1) + total + n,
340                      MS_ASYNC);
341             }
342
343           /* Now get the lock to safely insert the records.  */
344           pthread_rwlock_rdlock (&db->lock);
345
346           /* NB: in the following code we always must add the entry
347              marked with FIRST first.  Otherwise we end up with
348              dangling "pointers" in case a latter hash entry cannot be
349              added.  */
350           bool first = true;
351
352           /* If the request was by GID, add that entry first.  */
353           if (req->type == GETGRBYGID)
354             {
355               if (cache_add (GETGRBYGID, cp, key_offset, &dataset->head, true,
356                              db, owner, he == NULL) < 0)
357                 goto out;
358
359               first = false;
360             }
361           /* If the key is different from the name add a separate entry.  */
362           else if (strcmp (key_copy, gr_name) != 0)
363             {
364               if (cache_add (GETGRBYNAME, key_copy, key_len + 1,
365                              &dataset->head, true, db, owner, he == NULL) < 0)
366                 goto out;
367
368               first = false;
369             }
370
371           /* We have to add the value for both, byname and byuid.  */
372           if ((req->type == GETGRBYNAME || db->propagate)
373               && __builtin_expect (cache_add (GETGRBYNAME, gr_name,
374                                               gr_name_len,
375                                               &dataset->head, first, db, owner,
376                                               he == NULL)
377                                    == 0, 1))
378             {
379               if (req->type == GETGRBYNAME && db->propagate)
380                 (void) cache_add (GETGRBYGID, cp, key_offset, &dataset->head,
381                                   false, db, owner, false);
382             }
383
384         out:
385           pthread_rwlock_unlock (&db->lock);
386         }
387     }
388
389   if (__builtin_expect (written != total, 0) && debug_level > 0)
390     {
391       char buf[256];
392       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
393                strerror_r (errno, buf, sizeof (buf)));
394     }
395 }
396
397
398 union keytype
399 {
400   void *v;
401   gid_t g;
402 };
403
404
405 static int
406 lookup (int type, union keytype key, struct group *resultbufp, char *buffer,
407         size_t buflen, struct group **grp)
408 {
409   if (type == GETGRBYNAME)
410     return __getgrnam_r (key.v, resultbufp, buffer, buflen, grp);
411   else
412     return __getgrgid_r (key.g, resultbufp, buffer, buflen, grp);
413 }
414
415
416 static void
417 addgrbyX (struct database_dyn *db, int fd, request_header *req,
418           union keytype key, const char *keystr, uid_t uid,
419           struct hashentry *he, struct datahead *dh)
420 {
421   /* Search for the entry matching the key.  Please note that we don't
422      look again in the table whether the dataset is now available.  We
423      simply insert it.  It does not matter if it is in there twice.  The
424      pruning function only will look at the timestamp.  */
425   size_t buflen = 1024;
426   char *buffer = (char *) alloca (buflen);
427   struct group resultbuf;
428   struct group *grp;
429   bool use_malloc = false;
430   int errval = 0;
431
432   if (__builtin_expect (debug_level > 0, 0))
433     {
434       if (he == NULL)
435         dbg_log (_("Haven't found \"%s\" in group cache!"), keystr);
436       else
437         dbg_log (_("Reloading \"%s\" in group cache!"), keystr);
438     }
439
440   while (lookup (req->type, key, &resultbuf, buffer, buflen, &grp) != 0
441          && (errval = errno) == ERANGE)
442     {
443       errno = 0;
444
445       if (__builtin_expect (buflen > 32768, 0))
446         {
447           char *old_buffer = buffer;
448           buflen *= 2;
449           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
450           if (buffer == NULL)
451             {
452               /* We ran out of memory.  We cannot do anything but
453                  sending a negative response.  In reality this should
454                  never happen.  */
455               grp = NULL;
456               buffer = old_buffer;
457
458               /* We set the error to indicate this is (possibly) a
459                  temporary error and that it does not mean the entry
460                  is not available at all.  */
461               errval = EAGAIN;
462               break;
463             }
464           use_malloc = true;
465         }
466       else
467         /* Allocate a new buffer on the stack.  If possible combine it
468            with the previously allocated buffer.  */
469         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
470     }
471
472   cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval);
473
474   if (use_malloc)
475     free (buffer);
476 }
477
478
479 void
480 addgrbyname (struct database_dyn *db, int fd, request_header *req,
481              void *key, uid_t uid)
482 {
483   union keytype u = { .v = key };
484
485   addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
486 }
487
488
489 void
490 readdgrbyname (struct database_dyn *db, struct hashentry *he,
491                struct datahead *dh)
492 {
493   request_header req =
494     {
495       .type = GETGRBYNAME,
496       .key_len = he->len
497     };
498   union keytype u = { .v = db->data + he->key };
499
500   addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
501 }
502
503
504 void
505 addgrbygid (struct database_dyn *db, int fd, request_header *req,
506             void *key, uid_t uid)
507 {
508   char *ep;
509   gid_t gid = strtoul ((char *) key, &ep, 10);
510
511   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
512     {
513       if (debug_level > 0)
514         dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);
515
516       errno = EINVAL;
517       return;
518     }
519
520   union keytype u = { .g = gid };
521
522   addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
523 }
524
525
526 void
527 readdgrbygid (struct database_dyn *db, struct hashentry *he,
528               struct datahead *dh)
529 {
530   char *ep;
531   gid_t gid = strtoul (db->data + he->key, &ep, 10);
532
533   /* Since the key has been added before it must be OK.  */
534   assert (*(db->data + he->key) != '\0' && *ep == '\0');
535
536   request_header req =
537     {
538       .type = GETGRBYGID,
539       .key_len = he->len
540     };
541   union keytype u = { .g = gid };
542
543   addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
544 }