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

glibc/2.7/nscd/initgrcache.c

    1: /* Cache handling for host lookup.
    2:    Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
    3:    This file is part of the GNU C Library.
    4:    Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
    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 <assert.h>
   21: #include <errno.h>
   22: #include <grp.h>
   23: #include <libintl.h>
   24: #include <string.h>
   25: #include <time.h>
   26: #include <unistd.h>
   27: #include <sys/mman.h>
   28: 
   29: #include "dbg_log.h"
   30: #include "nscd.h"
   31: #ifdef HAVE_SENDFILE
   32: # include <kernel-features.h>
   33: #endif
   34: 
   35: #include "../nss/nsswitch.h"
   36: 
   37: 
   38: /* Type of the lookup function.  */
   39: typedef enum nss_status (*initgroups_dyn_function) (const char *, gid_t,
   40:                                                     long int *, long int *,
   41:                                                     gid_t **, long int, int *);
   42: 
   43: 
   44: static const initgr_response_header notfound =
   45: {
   46:   .version = NSCD_VERSION,
   47:   .found = 0,
   48:   .ngrps = 0
   49: };
   50: 
   51: 
   52: #include "../grp/compat-initgroups.c"
   53: 
   54: 
   55: static void
   56: addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
   57:                 void *key, uid_t uid, struct hashentry *he,
   58:                 struct datahead *dh)
   59: {
   60:   /* Search for the entry matching the key.  Please note that we don't
   61:      look again in the table whether the dataset is now available.  We
   62:      simply insert it.  It does not matter if it is in there twice.  The
   63:      pruning function only will look at the timestamp.  */
   64: 
   65: 
   66:   /* We allocate all data in one memory block: the iov vector,
   67:      the response header and the dataset itself.  */
   68:   struct dataset
   69:   {
   70:     struct datahead head;
   71:     initgr_response_header resp;
   72:     char strdata[0];
   73:   } *dataset = NULL;
   74: 
   75:   if (__builtin_expect (debug_level > 0, 0))
   76:     {
   77:       if (he == NULL)
   78:         dbg_log (_("Haven't found \"%s\" in group cache!"), (char *) key);
   79:       else
   80:         dbg_log (_("Reloading \"%s\" in group cache!"), (char *) key);
   81:     }
   82: 
   83:   static service_user *group_database;
   84:   service_user *nip = NULL;
   85:   int no_more;
   86: 
   87:   if (group_database != NULL)
   88:     {
   89:       nip = group_database;
   90:       no_more = 0;
   91:     }
   92:   else
   93:     no_more = __nss_database_lookup ("group", NULL,
   94:                                      "compat [NOTFOUND=return] files", &nip);
   95: 
   96:  /* We always use sysconf even if NGROUPS_MAX is defined.  That way, the
   97:      limit can be raised in the kernel configuration without having to
   98:      recompile libc.  */
   99:   long int limit = __sysconf (_SC_NGROUPS_MAX);
  100: 
  101:   long int size;
  102:   if (limit > 0)
  103:     /* We limit the size of the intially allocated array.  */
  104:     size = MIN (limit, 64);
  105:   else
  106:     /* No fixed limit on groups.  Pick a starting buffer size.  */
  107:     size = 16;
  108: 
  109:   long int start = 0;
  110:   bool all_tryagain = true;
  111:   bool any_success = false;
  112: 
  113:   /* This is temporary memory, we need not (ad must not) call
  114:      mempool_alloc.  */
  115:   // XXX This really should use alloca.  need to change the backends.
  116:   gid_t *groups = (gid_t *) malloc (size * sizeof (gid_t));
  117:   if (__builtin_expect (groups == NULL, 0))
  118:     /* No more memory.  */
  119:     goto out;
  120: 
  121:   /* Nothing added yet.  */
  122:   while (! no_more)
  123:     {
  124:       long int prev_start = start;
  125:       enum nss_status status;
  126:       initgroups_dyn_function fct;
  127:       fct = __nss_lookup_function (nip, "initgroups_dyn");
  128: 
  129:       if (fct == NULL)
  130:         {
  131:           status = compat_call (nip, key, -1, &start, &size, &groups,
  132:                                 limit, &errno);
  133: 
  134:           if (nss_next_action (nip, NSS_STATUS_UNAVAIL) != NSS_ACTION_CONTINUE)
  135:             break;
  136:         }
  137:       else
  138:         status = DL_CALL_FCT (fct, (key, -1, &start, &size, &groups,
  139:                                     limit, &errno));
  140: 
  141:       /* Remove duplicates.  */
  142:       long int cnt = prev_start;
  143:       while (cnt < start)
  144:         {
  145:           long int inner;
  146:           for (inner = 0; inner < prev_start; ++inner)
  147:             if (groups[inner] == groups[cnt])
  148:               break;
  149: 
  150:           if (inner < prev_start)
  151:             groups[cnt] = groups[--start];
  152:           else
  153:             ++cnt;
  154:         }
  155: 
  156:       if (status != NSS_STATUS_TRYAGAIN)
  157:         all_tryagain = false;
  158: 
  159:       /* This is really only for debugging.  */
  160:       if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
  161:         __libc_fatal ("illegal status in internal_getgrouplist");
  162: 
  163:       any_success |= status == NSS_STATUS_SUCCESS;
  164: 
  165:       if (status != NSS_STATUS_SUCCESS
  166:           && nss_next_action (nip, status) == NSS_ACTION_RETURN)
  167:          break;
  168: 
  169:       if (nip->next == NULL)
  170:         no_more = -1;
  171:       else
  172:         nip = nip->next;
  173:     }
  174: 
  175:   ssize_t total;
  176:   ssize_t written;
  177:  out:
  178:   if (!any_success)
  179:     {
  180:       /* Nothing found.  Create a negative result record.  */
  181:       written = total = sizeof (notfound);
  182: 
  183:       if (he != NULL && all_tryagain)
  184:         {
  185:           /* If we have an old record available but cannot find one now
  186:              because the service is not available we keep the old record
  187:              and make sure it does not get removed.  */
  188:           if (reload_count != UINT_MAX && dh->nreloads == reload_count)
  189:             /* Do not reset the value if we never not reload the record.  */
  190:             dh->nreloads = reload_count - 1;
  191:         }
  192:       else
  193:         {
  194:           /* We have no data.  This means we send the standard reply for this
  195:              case.  */
  196:           if (fd != -1)
  197:             written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
  198:                                                 MSG_NOSIGNAL));
  199: 
  200:           dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
  201:           /* If we cannot permanently store the result, so be it.  */
  202:           if (dataset != NULL)
  203:             {
  204:               dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
  205:               dataset->head.recsize = total;
  206:               dataset->head.notfound = true;
  207:               dataset->head.nreloads = 0;
  208:               dataset->head.usable = true;
  209: 
  210:               /* Compute the timeout time.  */
  211:               dataset->head.timeout = time (NULL) + db->negtimeout;
  212: 
  213:               /* This is the reply.  */
  214:               memcpy (&dataset->resp, &notfound, total);
  215: 
  216:               /* Copy the key data.  */
  217:               char *key_copy = memcpy (dataset->strdata, key, req->key_len);
  218: 
  219:               /* If necessary, we also propagate the data to disk.  */
  220:               if (db->persistent)
  221:                 {
  222:                   // XXX async OK?
  223:                   uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
  224:                   msync ((void *) pval,
  225:                          ((uintptr_t) dataset & pagesize_m1)
  226:                          + sizeof (struct dataset) + req->key_len, MS_ASYNC);
  227:                 }
  228: 
  229:               /* Now get the lock to safely insert the records.  */
  230:               pthread_rwlock_rdlock (&db->lock);
  231: 
  232:               if (cache_add (req->type, key_copy, req->key_len,
  233:                              &dataset->head, true, db, uid) < 0)
  234:                 /* Ensure the data can be recovered.  */
  235:                 dataset->head.usable = false;
  236: 
  237:               pthread_rwlock_unlock (&db->lock);
  238: 
  239:               /* Mark the old entry as obsolete.  */
  240:               if (dh != NULL)
  241:                 dh->usable = false;
  242:             }
  243:           else
  244:             ++db->head->addfailed;
  245:         }
  246:     }
  247:   else
  248:     {
  249: 
  250:       written = total = sizeof (struct dataset) + start * sizeof (int32_t);
  251: 
  252:       /* If we refill the cache, first assume the reconrd did not
  253:          change.  Allocate memory on the cache since it is likely
  254:          discarded anyway.  If it turns out to be necessary to have a
  255:          new record we can still allocate real memory.  */
  256:       bool alloca_used = false;
  257:       dataset = NULL;
  258: 
  259:       if (he == NULL)
  260:         {
  261:           dataset = (struct dataset *) mempool_alloc (db,
  262:                                                       total + req->key_len);
  263:           if (dataset == NULL)
  264:             ++db->head->addfailed;
  265:         }
  266: 
  267:       if (dataset == NULL)
  268:         {
  269:           /* We cannot permanently add the result in the moment.  But
  270:              we can provide the result as is.  Store the data in some
  271:              temporary memory.  */
  272:           dataset = (struct dataset *) alloca (total + req->key_len);
  273: 
  274:           /* We cannot add this record to the permanent database.  */
  275:           alloca_used = true;
  276:         }
  277: 
  278:       dataset->head.allocsize = total + req->key_len;
  279:       dataset->head.recsize = total - offsetof (struct dataset, resp);
  280:       dataset->head.notfound = false;
  281:       dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
  282:       dataset->head.usable = true;
  283: 
  284:       /* Compute the timeout time.  */
  285:       dataset->head.timeout = time (NULL) + db->postimeout;
  286: 
  287:       dataset->resp.version = NSCD_VERSION;
  288:       dataset->resp.found = 1;
  289:       dataset->resp.ngrps = start;
  290: 
  291:       char *cp = dataset->strdata;
  292: 
  293:       /* Copy the GID values.  If the size of the types match this is
  294:          very simple.  */
  295:       if (sizeof (gid_t) == sizeof (int32_t))
  296:         cp = mempcpy (cp, groups, start * sizeof (gid_t));
  297:       else
  298:         {
  299:           gid_t *gcp = (gid_t *) cp;
  300: 
  301:           for (int i = 0; i < start; ++i)
  302:             *gcp++ = groups[i];
  303: 
  304:           cp = (char *) gcp;
  305:         }
  306: 
  307:       /* Finally the user name.  */
  308:       memcpy (cp, key, req->key_len);
  309: 
  310:       /* Now we can determine whether on refill we have to create a new
  311:          record or not.  */
  312:       if (he != NULL)
  313:         {
  314:           assert (fd == -1);
  315: 
  316:           if (total + req->key_len == dh->allocsize
  317:               && total - offsetof (struct dataset, resp) == dh->recsize
  318:               && memcmp (&dataset->resp, dh->data,
  319:                          dh->allocsize - offsetof (struct dataset, resp)) == 0)
  320:             {
  321:               /* The data has not changed.  We will just bump the
  322:                  timeout value.  Note that the new record has been
  323:                  allocated on the stack and need not be freed.  */
  324:               dh->timeout = dataset->head.timeout;
  325:               ++dh->nreloads;
  326:             }
  327:           else
  328:             {
  329:               /* We have to create a new record.  Just allocate
  330:                  appropriate memory and copy it.  */
  331:               struct dataset *newp
  332:                 = (struct dataset *) mempool_alloc (db, total + req->key_len);
  333:               if (newp != NULL)
  334:                 {
  335:                   /* Adjust pointer into the memory block.  */
  336:                   cp = (char *) newp + (cp - (char *) dataset);
  337: 
  338:                   dataset = memcpy (newp, dataset, total + req->key_len);
  339:                   alloca_used = false;
  340:                 }
  341: 
  342:               /* Mark the old record as obsolete.  */
  343:               dh->usable = false;
  344:             }
  345:         }
  346:       else
  347:         {
  348:           /* We write the dataset before inserting it to the database
  349:              since while inserting this thread might block and so would
  350:              unnecessarily let the receiver wait.  */
  351:           assert (fd != -1);
  352: 
  353: #ifdef HAVE_SENDFILE
  354:           if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
  355:             {
  356:               assert (db->wr_fd != -1);
  357:               assert ((char *) &dataset->resp > (char *) db->data);
  358:               assert ((char *) &dataset->resp - (char *) db->head
  359:                       + total
  360:                       <= (sizeof (struct database_pers_head)
  361:                           + db->head->module * sizeof (ref_t)
  362:                           + db->head->data_size));
  363:               written = sendfileall (fd, db->wr_fd,
  364:                                      (char *) &dataset->resp
  365:                                      - (char *) db->head, total);
  366: # ifndef __ASSUME_SENDFILE
  367:               if (written == -1 && errno == ENOSYS)
  368:                 goto use_write;
  369: # endif
  370:             }
  371:           else
  372: # ifndef __ASSUME_SENDFILE
  373:           use_write:
  374: # endif
  375: #endif
  376:             written = writeall (fd, &dataset->resp, total);
  377:         }
  378: 
  379: 
  380:       /* Add the record to the database.  But only if it has not been
  381:          stored on the stack.  */
  382:       if (! alloca_used)
  383:         {
  384:           /* If necessary, we also propagate the data to disk.  */
  385:           if (db->persistent)
  386:             {
  387:               // XXX async OK?
  388:               uintptr_t pval = (uintptr_t) dataset & ~pagesize_m1;
  389:               msync ((void *) pval,
  390:                      ((uintptr_t) dataset & pagesize_m1) + total +
  391:                      req->key_len, MS_ASYNC);
  392:             }
  393: 
  394:           /* Now get the lock to safely insert the records.  */
  395:           pthread_rwlock_rdlock (&db->lock);
  396: 
  397:           if (cache_add (INITGROUPS, cp, req->key_len, &dataset->head, true,
  398:                          db, uid) < 0)
  399:             /* Could not allocate memory.  Make sure the data gets
  400:                discarded.  */
  401:             dataset->head.usable = false;
  402: 
  403:           pthread_rwlock_unlock (&db->lock);
  404:         }
  405:     }
  406: 
  407:   free (groups);
  408: 
  409:   if (__builtin_expect (written != total, 0) && debug_level > 0)
  410:     {
  411:       char buf[256];
  412:       dbg_log (_("short write in %s: %s"), __FUNCTION__,
  413:                strerror_r (errno, buf, sizeof (buf)));
  414:     }
  415: }
  416: 
  417: 
  418: void
  419: addinitgroups (struct database_dyn *db, int fd, request_header *req, void *key,
  420:                uid_t uid)
  421: {
  422:   addinitgroupsX (db, fd, req, key, uid, NULL, NULL);
  423: }
  424: 
  425: 
  426: void
  427: readdinitgroups (struct database_dyn *db, struct hashentry *he,
  428:                  struct datahead *dh)
  429: {
  430:   request_header req =
  431:     {
  432:       .type = INITGROUPS,
  433:       .key_len = he->len
  434:     };
  435: 
  436:   addinitgroupsX (db, -1, &req, db->data + he->key, he->owner, he, dh);
  437: }
Syntax (Markdown)