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