(gc): Fix test for stack overuse.
[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);
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) < 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) < 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                                    == 0, 1))
377             {
378               if (req->type == GETGRBYNAME && db->propagate)
379                 (void) cache_add (GETGRBYGID, cp, key_offset, &dataset->head,
380                                   false, db, owner);
381             }
382
383         out:
384           pthread_rwlock_unlock (&db->lock);
385         }
386     }
387
388   if (__builtin_expect (written != total, 0) && debug_level > 0)
389     {
390       char buf[256];
391       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
392                strerror_r (errno, buf, sizeof (buf)));
393     }
394 }
395
396
397 union keytype
398 {
399   void *v;
400   gid_t g;
401 };
402
403
404 static int
405 lookup (int type, union keytype key, struct group *resultbufp, char *buffer,
406         size_t buflen, struct group **grp)
407 {
408   if (type == GETGRBYNAME)
409     return __getgrnam_r (key.v, resultbufp, buffer, buflen, grp);
410   else
411     return __getgrgid_r (key.g, resultbufp, buffer, buflen, grp);
412 }
413
414
415 static void
416 addgrbyX (struct database_dyn *db, int fd, request_header *req,
417           union keytype key, const char *keystr, uid_t uid,
418           struct hashentry *he, struct datahead *dh)
419 {
420   /* Search for the entry matching the key.  Please note that we don't
421      look again in the table whether the dataset is now available.  We
422      simply insert it.  It does not matter if it is in there twice.  The
423      pruning function only will look at the timestamp.  */
424   size_t buflen = 1024;
425   char *buffer = (char *) alloca (buflen);
426   struct group resultbuf;
427   struct group *grp;
428   bool use_malloc = false;
429   int errval = 0;
430
431   if (__builtin_expect (debug_level > 0, 0))
432     {
433       if (he == NULL)
434         dbg_log (_("Haven't found \"%s\" in group cache!"), keystr);
435       else
436         dbg_log (_("Reloading \"%s\" in group cache!"), keystr);
437     }
438
439   while (lookup (req->type, key, &resultbuf, buffer, buflen, &grp) != 0
440          && (errval = errno) == ERANGE)
441     {
442       errno = 0;
443
444       if (__builtin_expect (buflen > 32768, 0))
445         {
446           char *old_buffer = buffer;
447           buflen *= 2;
448           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
449           if (buffer == NULL)
450             {
451               /* We ran out of memory.  We cannot do anything but
452                  sending a negative response.  In reality this should
453                  never happen.  */
454               grp = NULL;
455               buffer = old_buffer;
456
457               /* We set the error to indicate this is (possibly) a
458                  temporary error and that it does not mean the entry
459                  is not available at all.  */
460               errval = EAGAIN;
461               break;
462             }
463           use_malloc = true;
464         }
465       else
466         /* Allocate a new buffer on the stack.  If possible combine it
467            with the previously allocated buffer.  */
468         buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
469     }
470
471   cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval);
472
473   if (use_malloc)
474     free (buffer);
475 }
476
477
478 void
479 addgrbyname (struct database_dyn *db, int fd, request_header *req,
480              void *key, uid_t uid)
481 {
482   union keytype u = { .v = key };
483
484   addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
485 }
486
487
488 void
489 readdgrbyname (struct database_dyn *db, struct hashentry *he,
490                struct datahead *dh)
491 {
492   request_header req =
493     {
494       .type = GETGRBYNAME,
495       .key_len = he->len
496     };
497   union keytype u = { .v = db->data + he->key };
498
499   addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
500 }
501
502
503 void
504 addgrbygid (struct database_dyn *db, int fd, request_header *req,
505             void *key, uid_t uid)
506 {
507   char *ep;
508   gid_t gid = strtoul ((char *) key, &ep, 10);
509
510   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
511     {
512       if (debug_level > 0)
513         dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);
514
515       errno = EINVAL;
516       return;
517     }
518
519   union keytype u = { .g = gid };
520
521   addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
522 }
523
524
525 void
526 readdgrbygid (struct database_dyn *db, struct hashentry *he,
527               struct datahead *dh)
528 {
529   char *ep;
530   gid_t gid = strtoul (db->data + he->key, &ep, 10);
531
532   /* Since the key has been added before it must be OK.  */
533   assert (*(db->data + he->key) != '\0' && *ep == '\0');
534
535   request_header req =
536     {
537       .type = GETGRBYGID,
538       .key_len = he->len
539     };
540   union keytype u = { .g = gid };
541
542   addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
543 }