]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - nscd/netgroupcache.c
Prefer https to http for gnu.org and fsf.org URLs
[thirdparty/glibc.git] / nscd / netgroupcache.c
index 70cf9c14b3a82755ea6939fb2dd85b64848d8938..d4c769d88887cb73f9920446e48ac02edb5e4e85 100644 (file)
@@ -1,5 +1,5 @@
 /* Cache handling for netgroup lookup.
-   Copyright (C) 2011-2012 Free Software Foundation, Inc.
+   Copyright (C) 2011-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@gmail.com>, 2011.
 
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
 
 #include <alloca.h>
 #include <assert.h>
 #include <errno.h>
 #include <libintl.h>
 #include <stdbool.h>
+#include <stdlib.h>
 #include <unistd.h>
 #include <sys/mman.h>
 
@@ -65,13 +66,57 @@ struct dataset
   char strdata[0];
 };
 
+/* Sends a notfound message and prepares a notfound dataset to write to the
+   cache.  Returns true if there was enough memory to allocate the dataset and
+   returns the dataset in DATASETP, total bytes to write in TOTALP and the
+   timeout in TIMEOUTP.  KEY_COPY is set to point to the copy of the key in the
+   dataset. */
+static bool
+do_notfound (struct database_dyn *db, int fd, request_header *req,
+              const char *key, struct dataset **datasetp, ssize_t *totalp,
+              time_t *timeoutp, char **key_copy)
+{
+  struct dataset *dataset;
+  ssize_t total;
+  time_t timeout;
+  bool cacheable = false;
+
+  total = sizeof (notfound);
+  timeout = time (NULL) + db->negtimeout;
+
+  if (fd != -1)
+    TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
+
+  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1);
+  /* If we cannot permanently store the result, so be it.  */
+  if (dataset != NULL)
+    {
+      timeout = datahead_init_neg (&dataset->head,
+                                  sizeof (struct dataset) + req->key_len,
+                                  total, db->negtimeout);
+
+      /* This is the reply.  */
+      memcpy (&dataset->resp, &notfound, total);
+
+      /* Copy the key data.  */
+      memcpy (dataset->strdata, key, req->key_len);
+      *key_copy = dataset->strdata;
+
+      cacheable = true;
+    }
+  *timeoutp = timeout;
+  *totalp = total;
+  *datasetp = dataset;
+  return cacheable;
+}
 
 static time_t
 addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
                 const char *key, uid_t uid, struct hashentry *he,
-                struct datahead *dh, struct dataset **resultp)
+                struct datahead *dh, struct dataset **resultp,
+                void **tofreep)
 {
-  if (__builtin_expect (debug_level > 0, 0))
+  if (__glibc_unlikely (debug_level > 0))
     {
       if (he == NULL)
        dbg_log (_("Haven't found \"%s\" in netgroup cache!"), key);
@@ -84,6 +129,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
   struct dataset *dataset;
   bool cacheable = false;
   ssize_t total;
+  bool found = false;
 
   char *key_copy = NULL;
   struct __netgrent data;
@@ -91,55 +137,26 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
   size_t buffilled = sizeof (*dataset);
   char *buffer = NULL;
   size_t nentries = 0;
-  bool use_malloc = false;
   size_t group_len = strlen (key) + 1;
-  union
-  {
-    struct name_list elem;
-    char mem[sizeof (struct name_list) + group_len];
-  } first_needed;
+  struct name_list *first_needed
+    = alloca (sizeof (struct name_list) + group_len);
+  *tofreep = NULL;
 
   if (netgroup_database == NULL
-      && __nss_database_lookup ("netgroup", NULL, NULL, &netgroup_database))
+      && __nss_database_lookup2 ("netgroup", NULL, NULL, &netgroup_database))
     {
       /* No such service.  */
-      total = sizeof (notfound);
-      timeout = time (NULL) + db->negtimeout;
-
-      if (fd != -1)
-       TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
-
-      dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len, 1);
-      /* If we cannot permanently store the result, so be it.  */
-      if (dataset != NULL)
-       {
-         dataset->head.allocsize = sizeof (struct dataset) + req->key_len;
-         dataset->head.recsize = total;
-         dataset->head.notfound = true;
-         dataset->head.nreloads = 0;
-         dataset->head.usable = true;
-
-         /* Compute the timeout time.  */
-         timeout = dataset->head.timeout = time (NULL) + db->negtimeout;
-         dataset->head.ttl = db->negtimeout;
-
-         /* This is the reply.  */
-         memcpy (&dataset->resp, &notfound, total);
-
-         /* Copy the key data.  */
-         memcpy (dataset->strdata, key, req->key_len);
-
-         cacheable = true;
-       }
-
+      cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
+                              &key_copy);
       goto writeout;
     }
 
   memset (&data, '\0', sizeof (data));
-  buffer = alloca (buflen);
-  first_needed.elem.next = &first_needed.elem;
-  memcpy (first_needed.elem.name, key, group_len);
-  data.needed_groups = &first_needed.elem;
+  buffer = xmalloc (buflen);
+  *tofreep = buffer;
+  first_needed->next = first_needed;
+  memcpy (first_needed->name, key, group_len);
+  data.needed_groups = first_needed;
 
   while (data.needed_groups != NULL)
     {
@@ -167,6 +184,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
 
          if (status == NSS_STATUS_SUCCESS)
            {
+             found = true;
              union
              {
                enum nss_status (*f) (struct __netgrent *, char *, size_t,
@@ -179,11 +197,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
                  {
                    int e;
                    status = getfct.f (&data, buffer + buffilled,
-                                      buflen - buffilled, &e);
-                   if (status == NSS_STATUS_RETURN)
-                     /* This was the last one for this group.  Look
-                        at next group if available.  */
-                     break;
+                                      buflen - buffilled - req->key_len, &e);
                    if (status == NSS_STATUS_SUCCESS)
                      {
                        if (data.type == triple_val)
@@ -192,51 +206,66 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
                            const char *nuser = data.val.triple.user;
                            const char *ndomain = data.val.triple.domain;
 
-                           if (data.val.triple.host > data.val.triple.user
-                               || data.val.triple.user > data.val.triple.domain)
+                           size_t hostlen = strlen (nhost ?: "") + 1;
+                           size_t userlen = strlen (nuser ?: "") + 1;
+                           size_t domainlen = strlen (ndomain ?: "") + 1;
+
+                           if (nhost == NULL || nuser == NULL || ndomain == NULL
+                               || nhost > nuser || nuser > ndomain)
                              {
-                               const char *last = MAX (nhost,
-                                                       MAX (nuser, ndomain));
-                               size_t bufused = (last + strlen (last) + 1
-                                                 - buffer);
+                               const char *last = nhost;
+                               if (last == NULL
+                                   || (nuser != NULL && nuser > last))
+                                 last = nuser;
+                               if (last == NULL
+                                   || (ndomain != NULL && ndomain > last))
+                                 last = ndomain;
+
+                               size_t bufused
+                                 = (last == NULL
+                                    ? buffilled
+                                    : last + strlen (last) + 1 - buffer);
 
                                /* We have to make temporary copies.  */
-                               size_t hostlen = strlen (nhost) + 1;
-                               size_t userlen = strlen (nuser) + 1;
-                               size_t domainlen = strlen (ndomain) + 1;
                                size_t needed = hostlen + userlen + domainlen;
 
                                if (buflen - req->key_len - bufused < needed)
                                  {
-                                   size_t newsize = MAX (2 * buflen,
-                                                         buflen + 2 * needed);
-                                   if (use_malloc || newsize > 1024 * 1024)
-                                     {
-                                       buflen = newsize;
-                                       char *newbuf = xrealloc (use_malloc
-                                                                ? buffer
-                                                                : NULL,
-                                                                buflen);
-
-                                       buffer = newbuf;
-                                       use_malloc = true;
-                                     }
-                                   else
-                                     extend_alloca (buffer, buflen, newsize);
+                                   buflen += MAX (buflen, 2 * needed);
+                                   /* Save offset in the old buffer.  We don't
+                                      bother with the NULL check here since
+                                      we'll do that later anyway.  */
+                                   size_t nhostdiff = nhost - buffer;
+                                   size_t nuserdiff = nuser - buffer;
+                                   size_t ndomaindiff = ndomain - buffer;
+
+                                   char *newbuf = xrealloc (buffer, buflen);
+                                   /* Fix up the triplet pointers into the new
+                                      buffer.  */
+                                   nhost = (nhost ? newbuf + nhostdiff
+                                            : NULL);
+                                   nuser = (nuser ? newbuf + nuserdiff
+                                            : NULL);
+                                   ndomain = (ndomain ? newbuf + ndomaindiff
+                                              : NULL);
+                                   buffer = newbuf;
                                  }
 
                                nhost = memcpy (buffer + bufused,
-                                               nhost, hostlen);
+                                               nhost ?: "", hostlen);
                                nuser = memcpy ((char *) nhost + hostlen,
-                                               nuser, userlen);
+                                               nuser ?: "", userlen);
                                ndomain = memcpy ((char *) nuser + userlen,
-                                                 ndomain, domainlen);
+                                                 ndomain ?: "", domainlen);
                              }
 
                            char *wp = buffer + buffilled;
-                           wp = stpcpy (wp, nhost) + 1;
-                           wp = stpcpy (wp, nuser) + 1;
-                           wp = stpcpy (wp, ndomain) + 1;
+                           wp = memmove (wp, nhost ?: "", hostlen);
+                           wp += hostlen;
+                           wp = memmove (wp, nuser ?: "", userlen);
+                           wp += userlen;
+                           wp = memmove (wp, ndomain ?: "", domainlen);
+                           wp += domainlen;
                            buffilled = wp - buffer;
                            ++nentries;
                          }
@@ -287,21 +316,18 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
                              }
                          }
                      }
-                   else if (status == NSS_STATUS_UNAVAIL && e == ERANGE)
+                   else if (status == NSS_STATUS_TRYAGAIN && e == ERANGE)
                      {
-                       size_t newsize = 2 * buflen;
-                       if (use_malloc || newsize > 1024 * 1024)
-                         {
-                           buflen = newsize;
-                           char *newbuf = xrealloc (use_malloc
-                                                    ? buffer : NULL, buflen);
-
-                           buffer = newbuf;
-                           use_malloc = true;
-                         }
-                       else
-                         extend_alloca (buffer, buflen, newsize);
+                       buflen *= 2;
+                       buffer = xrealloc (buffer, buflen);
                      }
+                   else if (status == NSS_STATUS_RETURN
+                            || status == NSS_STATUS_NOTFOUND
+                            || status == NSS_STATUS_UNAVAIL)
+                     /* This was either the last one for this group or the
+                        group was empty or the NSS module had an internal
+                        failure.  Look at next group if available.  */
+                     break;
                  }
 
              enum nss_status (*endfct) (struct __netgrent *);
@@ -317,17 +343,23 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
        }
     }
 
+  /* No results.  Return a failure and write out a notfound record in the
+     cache.  */
+  if (!found)
+    {
+      cacheable = do_notfound (db, fd, req, key, &dataset, &total, &timeout,
+                              &key_copy);
+      goto writeout;
+    }
+
   total = buffilled;
 
   /* Fill in the dataset.  */
   dataset = (struct dataset *) buffer;
-  dataset->head.allocsize = total + req->key_len;
-  dataset->head.recsize = total - offsetof (struct dataset, resp);
-  dataset->head.notfound = false;
-  dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
-  dataset->head.usable = true;
-  dataset->head.ttl = db->postimeout;
-  timeout = dataset->head.timeout = time (NULL) + dataset->head.ttl;
+  timeout = datahead_init_pos (&dataset->head, total + req->key_len,
+                              total - offsetof (struct dataset, resp),
+                              he == NULL ? 0 : dh->nreloads + 1,
+                              db->postimeout);
 
   dataset->resp.version = NSCD_VERSION;
   dataset->resp.found = 1;
@@ -364,7 +396,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
   {
     struct dataset *newp
       = (struct dataset *) mempool_alloc (db, total + req->key_len, 1);
-    if (__builtin_expect (newp != NULL, 1))
+    if (__glibc_likely (newp != NULL))
       {
        /* Adjust pointer into the memory block.  */
        key_copy = (char *) newp + (key_copy - buffer);
@@ -384,33 +416,7 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
         since while inserting this thread might block and so would
         unnecessarily let the receiver wait.  */
     writeout:
-#ifdef HAVE_SENDFILE
-      if (__builtin_expect (db->mmap_used, 1) && cacheable)
-       {
-         assert (db->wr_fd != -1);
-         assert ((char *) &dataset->resp > (char *) db->data);
-         assert ((char *) dataset - (char *) db->head + total
-                 <= (sizeof (struct database_pers_head)
-                     + db->head->module * sizeof (ref_t)
-                     + db->head->data_size));
-# ifndef __ASSUME_SENDFILE
-         ssize_t written =
-# endif
-           sendfileall (fd, db->wr_fd, (char *) &dataset->resp
-                        - (char *) db->head, dataset->head.recsize);
-# ifndef __ASSUME_SENDFILE
-         if (written == -1 && errno == ENOSYS)
-           goto use_write;
-# endif
-       }
-      else
-#endif
-       {
-#if defined HAVE_SENDFILE && !defined __ASSUME_SENDFILE
-       use_write:
-#endif
-         writeall (fd, &dataset->resp, dataset->head.recsize);
-       }
+      writeall (fd, &dataset->resp, dataset->head.recsize);
     }
 
   if (cacheable)
@@ -436,9 +442,6 @@ addgetnetgrentX (struct database_dyn *db, int fd, request_header *req,
     }
 
  out:
-  if (use_malloc)
-    free (buffer);
-
   *resultp = dataset;
 
   return timeout;
@@ -452,7 +455,7 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
 {
   const char *group = key;
   key = (char *) rawmemchr (key, '\0') + 1;
-  size_t group_len = key - group - 1;
+  size_t group_len = key - group;
   const char *host = *key++ ? key : NULL;
   if (host != NULL)
     key = (char *) rawmemchr (key, '\0') + 1;
@@ -461,7 +464,7 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
     key = (char *) rawmemchr (key, '\0') + 1;
   const char *domain = *key++ ? key : NULL;
 
-  if (__builtin_expect (debug_level > 0, 0))
+  if (__glibc_unlikely (debug_level > 0))
     {
       if (he == NULL)
        dbg_log (_("Haven't found \"%s (%s,%s,%s)\" in netgroup cache!"),
@@ -475,8 +478,12 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
                                                            group, group_len,
                                                            db, uid);
   time_t timeout;
+  void *tofree;
   if (result != NULL)
-    timeout = result->head.timeout;
+    {
+      timeout = result->head.timeout;
+      tofree = NULL;
+    }
   else
     {
       request_header req_get =
@@ -485,7 +492,7 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
          .key_len = group_len
        };
       timeout = addgetnetgrentX (db, -1, &req_get, group, uid, NULL, NULL,
-                                &result);
+                                &result, &tofree);
     }
 
   struct indataset
@@ -498,18 +505,18 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
                                            1);
   struct indataset dataset_mem;
   bool cacheable = true;
-  if (__builtin_expect (dataset == NULL, 0))
+  if (__glibc_unlikely (dataset == NULL))
     {
       cacheable = false;
       dataset = &dataset_mem;
     }
 
-  dataset->head.allocsize = sizeof (*dataset) + req->key_len;
-  dataset->head.recsize = sizeof (innetgroup_response_header);
+  datahead_init_pos (&dataset->head, sizeof (*dataset) + req->key_len,
+                    sizeof (innetgroup_response_header),
+                    he == NULL ? 0 : dh->nreloads + 1, result->head.ttl);
+  /* Set the notfound status and timeout based on the result from
+     getnetgrent.  */
   dataset->head.notfound = result->head.notfound;
-  dataset->head.nreloads = he == NULL ? 0 : (dh->nreloads + 1);
-  dataset->head.usable = true;
-  dataset->head.ttl = result->head.ttl;
   dataset->head.timeout = timeout;
 
   dataset->resp.version = NSCD_VERSION;
@@ -527,15 +534,19 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
        {
          bool success = true;
 
-         if (host != NULL)
+         /* For the host, user and domain in each triplet, we assume success
+            if the value is blank because that is how the wildcard entry to
+            match anything is stored in the netgroup cache.  */
+         if (host != NULL && *triplets != '\0')
            success = strcmp (host, triplets) == 0;
          triplets = (const char *) rawmemchr (triplets, '\0') + 1;
 
-         if (success && user != NULL)
+         if (success && user != NULL && *triplets != '\0')
            success = strcmp (user, triplets) == 0;
          triplets = (const char *) rawmemchr (triplets, '\0') + 1;
 
-         if (success && (domain == NULL || strcmp (domain, triplets) == 0))
+         if (success && (domain == NULL || *triplets == '\0'
+                         || strcmp (domain, triplets) == 0))
            {
              dataset->resp.result = 1;
              break;
@@ -552,7 +563,9 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
       dh->timeout = timeout;
       dh->ttl = dataset->head.ttl;
       ++dh->nreloads;
-      return timeout;
+      if (cacheable)
+        pthread_rwlock_unlock (&db->lock);
+      goto out;
     }
 
   if (he == NULL)
@@ -560,36 +573,9 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
       /* We write the dataset before inserting it to the database
         since while inserting this thread might block and so would
         unnecessarily let the receiver wait.  */
-       assert (fd != -1);
+      assert (fd != -1);
 
-#ifdef HAVE_SENDFILE
-      if (__builtin_expect (db->mmap_used, 1) && cacheable)
-       {
-         assert (db->wr_fd != -1);
-         assert ((char *) &dataset->resp > (char *) db->data);
-         assert ((char *) dataset - (char *) db->head + sizeof (*dataset)
-                 <= (sizeof (struct database_pers_head)
-                     + db->head->module * sizeof (ref_t)
-                     + db->head->data_size));
-# ifndef __ASSUME_SENDFILE
-         ssize_t written =
-# endif
-           sendfileall (fd, db->wr_fd,
-                        (char *) &dataset->resp - (char *) db->head,
-                        sizeof (innetgroup_response_header));
-# ifndef __ASSUME_SENDFILE
-         if (written == -1 && errno == ENOSYS)
-           goto use_write;
-# endif
-       }
-      else
-       {
-# ifndef __ASSUME_SENDFILE
-       use_write:
-# endif
-#endif
-         writeall (fd, &dataset->resp, sizeof (innetgroup_response_header));
-       }
+      writeall (fd, &dataset->resp, sizeof (innetgroup_response_header));
     }
 
   if (cacheable)
@@ -615,17 +601,30 @@ addinnetgrX (struct database_dyn *db, int fd, request_header *req,
        dh->usable = false;
     }
 
+ out:
+  free (tofree);
   return timeout;
 }
 
 
+static time_t
+addgetnetgrentX_ignore (struct database_dyn *db, int fd, request_header *req,
+                       const char *key, uid_t uid, struct hashentry *he,
+                       struct datahead *dh)
+{
+  struct dataset *ignore;
+  void *tofree;
+  time_t timeout = addgetnetgrentX (db, fd, req, key, uid, he, dh,
+                                   &ignore, &tofree);
+  free (tofree);
+  return timeout;
+}
+
 void
 addgetnetgrent (struct database_dyn *db, int fd, request_header *req,
                void *key, uid_t uid)
 {
-  struct dataset *ignore;
-
-  addgetnetgrentX (db, fd, req, key, uid, NULL, NULL, &ignore);
+  addgetnetgrentX_ignore (db, fd, req, key, uid, NULL, NULL);
 }
 
 
@@ -638,10 +637,8 @@ readdgetnetgrent (struct database_dyn *db, struct hashentry *he,
       .type = GETNETGRENT,
       .key_len = he->len
     };
-  struct dataset *ignore;
-
-  return addgetnetgrentX (db, -1, &req, db->data + he->key, he->owner, he, dh,
-                         &ignore);
+  return addgetnetgrentX_ignore
+    (db, -1, &req, db->data + he->key, he->owner, he, dh);
 }