Update.
[platform/upstream/glibc.git] / nscd / grpcache.c
1 /* Cache handling for group lookup.
2    Copyright (C) 1998-2002, 2003, 2004 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               /* 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;
147         }
148     }
149   else
150     {
151       /* Determine the I/O structure.  */
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;
155       uint32_t *gr_mem_len;
156       size_t gr_mem_len_total = 0;
157       char *gr_name;
158       char *cp;
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);
162       ssize_t n;
163       size_t cnt;
164
165       /* We need this to insert the `bygid' entry.  */
166       int key_offset;
167       n = snprintf (buf, buf_len, "%d%c%n%s", grp->gr_gid, '\0',
168                     &key_offset, (char *) key) + 1;
169
170       /* Determine the length of all members.  */
171       while (grp->gr_mem[gr_mem_cnt])
172         ++gr_mem_cnt;
173       gr_mem_len = (uint32_t *) alloca (gr_mem_cnt * sizeof (uint32_t));
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         }
179
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;
226
227       /* This is the member string length array.  */
228       cp = mempcpy (cp, gr_mem_len, gr_mem_cnt * sizeof (uint32_t));
229       gr_name = cp;
230       cp = mempcpy (cp, grp->gr_name, gr_name_len);
231       cp = mempcpy (cp, grp->gr_passwd, gr_passwd_len);
232
233       for (cnt = 0; cnt < gr_mem_cnt; ++cnt)
234         cp = mempcpy (cp, grp->gr_mem[cnt], gr_mem_len[cnt]);
235
236       /* Finally the stringified GID value.  */
237       memcpy (cp, buf, n);
238       char *key_copy = cp + key_offset;
239       assert (key_copy == (char *) rawmemchr (cp, '\0') + 1);
240
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);
246
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);
284
285           written = TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total));
286         }
287
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             {
295               // XXX async OK?
296               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
297               msync ((void *) pval,
298                      ((uintptr_t) dataset & pagesize_m1) + total + n,
299                      MS_ASYNC);
300             }
301
302           /* Now get the lock to safely insert the records.  */
303           pthread_rwlock_rdlock (&db->lock);
304
305           /* NB: in the following code we always must add the entry
306              marked with FIRST first.  Otherwise we end up with
307              dangling "pointers" in case a latter hash entry cannot be
308              added.  */
309           bool first = req->type == GETGRBYNAME;
310
311           /* If the request was by GID, add that entry first.  */
312           if (req->type != GETGRBYNAME)
313             {
314               if (cache_add (GETGRBYGID, cp, key_offset, &dataset->head, true,
315                              db, owner) < 0)
316                 {
317                   /* Could not allocate memory.  Make sure the data gets
318                      discarded.  */
319                   dataset->head.usable = false;
320                   goto out;
321                 }
322             }
323           /* If the key is different from the name add a separate entry.  */
324           else if (strcmp (key_copy, gr_name) != 0)
325             {
326               if (cache_add (GETGRBYNAME, key_copy, key_len + 1,
327                              &dataset->head, first, db, owner) < 0)
328                 {
329                   /* Could not allocate memory.  Make sure the data gets
330                      discarded.  */
331                   dataset->head.usable = false;
332                   goto out;
333                 }
334
335               first = false;
336             }
337
338           /* We have to add the value for both, byname and byuid.  */
339           if (__builtin_expect (cache_add (GETGRBYNAME, gr_name, gr_name_len,
340                                            &dataset->head, first, db, owner)
341                                 == 0, 1))
342             {
343               if (req->type == GETGRBYNAME)
344                 (void) cache_add (GETGRBYGID, cp, key_offset, &dataset->head,
345                                   req->type != GETGRBYNAME, db, owner);
346             }
347           else if (first)
348             /* Could not allocate memory.  Make sure the data gets
349                discarded.  */
350             dataset->head.usable = false;
351
352         out:
353           pthread_rwlock_unlock (&db->lock);
354         }
355     }
356
357   if (__builtin_expect (written != total, 0) && debug_level > 0)
358     {
359       char buf[256];
360       dbg_log (_("short write in %s: %s"),  __FUNCTION__,
361                strerror_r (errno, buf, sizeof (buf)));
362     }
363 }
364
365
366 union keytype
367 {
368   void *v;
369   gid_t g;
370 };
371
372
373 static int
374 lookup (int type, union keytype key, struct group *resultbufp, char *buffer,
375         size_t buflen, struct group **grp)
376 {
377   if (type == GETGRBYNAME)
378     return __getgrnam_r (key.v, resultbufp, buffer, buflen, grp);
379   else
380     return __getgrgid_r (key.g, resultbufp, buffer, buflen, grp);
381 }
382
383
384 static void
385 addgrbyX (struct database_dyn *db, int fd, request_header *req,
386           union keytype key, const char *keystr, uid_t uid,
387           struct hashentry *he, struct datahead *dh)
388 {
389   /* Search for the entry matching the key.  Please note that we don't
390      look again in the table whether the dataset is now available.  We
391      simply insert it.  It does not matter if it is in there twice.  The
392      pruning function only will look at the timestamp.  */
393   size_t buflen = 1024;
394   char *buffer = (char *) alloca (buflen);
395   struct group resultbuf;
396   struct group *grp;
397   uid_t oldeuid = 0;
398   bool use_malloc = false;
399   int errval = 0;
400
401   if (__builtin_expect (debug_level > 0, 0))
402     {
403       if (he == NULL)
404         dbg_log (_("Haven't found \"%s\" in group cache!"), keystr);
405       else
406         dbg_log (_("Reloading \"%s\" in group cache!"), keystr);
407     }
408
409   if (db->secure)
410     {
411       oldeuid = geteuid ();
412       seteuid (uid);
413     }
414
415   while (lookup (req->type, key, &resultbuf, buffer, buflen, &grp) != 0
416          && (errval = errno) == ERANGE)
417     {
418       char *old_buffer = buffer;
419       errno = 0;
420 #define INCR 1024
421
422       if (__builtin_expect (buflen > 32768, 0))
423         {
424           buflen += INCR;
425           buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
426           if (buffer == NULL)
427             {
428               /* We ran out of memory.  We cannot do anything but
429                  sending a negative response.  In reality this should
430                  never happen.  */
431               grp = NULL;
432               buffer = old_buffer;
433
434               /* We set the error to indicate this is (possibly) a
435                  temporary error and that it does not mean the entry
436                  is not available at all.  */
437               errval = EAGAIN;
438               break;
439             }
440           use_malloc = true;
441         }
442       else
443         /* Allocate a new buffer on the stack.  If possible combine it
444            with the previously allocated buffer.  */
445         buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
446     }
447
448   if (db->secure)
449     seteuid (oldeuid);
450
451   cache_addgr (db, fd, req, keystr, grp, uid, he, dh, errval);
452
453   if (use_malloc)
454     free (buffer);
455 }
456
457
458 void
459 addgrbyname (struct database_dyn *db, int fd, request_header *req,
460              void *key, uid_t uid)
461 {
462   union keytype u = { .v = key };
463
464   addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
465 }
466
467
468 void
469 readdgrbyname (struct database_dyn *db, struct hashentry *he,
470                struct datahead *dh)
471 {
472   request_header req =
473     {
474       .type = GETGRBYNAME,
475       .key_len = he->len
476     };
477   union keytype u = { .v = db->data + he->key };
478
479   addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
480 }
481
482
483 void
484 addgrbygid (struct database_dyn *db, int fd, request_header *req,
485             void *key, uid_t uid)
486 {
487   char *ep;
488   gid_t gid = strtoul ((char *) key, &ep, 10);
489
490   if (*(char *) key == '\0' || *ep != '\0')  /* invalid numeric uid */
491     {
492       if (debug_level > 0)
493         dbg_log (_("Invalid numeric gid \"%s\"!"), (char *) key);
494
495       errno = EINVAL;
496       return;
497     }
498
499   union keytype u = { .g = gid };
500
501   addgrbyX (db, fd, req, u, key, uid, NULL, NULL);
502 }
503
504
505 void
506 readdgrbygid (struct database_dyn *db, struct hashentry *he,
507               struct datahead *dh)
508 {
509   char *ep;
510   gid_t gid = strtoul (db->data + he->key, &ep, 10);
511
512   /* Since the key has been added before it must be OK.  */
513   assert (*(db->data + he->key) != '\0' && *ep == '\0');
514
515   request_header req =
516     {
517       .type = GETGRBYGID,
518       .key_len = he->len
519     };
520   union keytype u = { .g = gid };
521
522   addgrbyX (db, -1, &req, u, db->data + he->key, he->owner, he, dh);
523 }