(linenum→info "unix/slp.c:2238")

glibc/2.7/nscd/grpcache.c

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