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