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