Replace lll_private_futex_* (*) with lll_futex_* (*, LLL_PRIVATE).
[kopensolaris-gnu/glibc.git] / nscd / nscd_helper.c
index 65e78a1..50146a0 100644 (file)
@@ -1,4 +1,5 @@
-/* Copyright (C) 1998-2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+/* Copyright (C) 1998-2002,2003,2004,2005,2006,2007
+   Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
@@ -21,6 +22,8 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <stdbool.h>
+#include <string.h>
+#include <time.h>
 #include <unistd.h>
 #include <sys/mman.h>
 #include <sys/poll.h>
@@ -94,16 +97,21 @@ __readvall (int fd, const struct iovec *iov, int iovcnt)
 
 
 static int
-open_socket (void)
+open_socket (request_type type, const char *key, size_t keylen)
 {
   int sock = __socket (PF_UNIX, SOCK_STREAM, 0);
   if (sock < 0)
     return -1;
 
+  struct
+  {
+    request_header req;
+    char key[keylen];
+  } reqdata;
+  size_t real_sizeof_reqdata = sizeof (request_header) + keylen;
+
   /* Make socket non-blocking.  */
-  int fl = __fcntl (sock, F_GETFL);
-  if (fl != -1)
-    __fcntl (sock, F_SETFL, fl | O_NONBLOCK);
+  __fcntl (sock, F_SETFL, O_RDWR | O_NONBLOCK);
 
   struct sockaddr_un sun;
   sun.sun_family = AF_UNIX;
@@ -112,13 +120,56 @@ open_socket (void)
       && errno != EINPROGRESS)
     goto out;
 
-  struct pollfd fds[1];
-  fds[0].fd = sock;
-  fds[0].events = POLLOUT | POLLERR | POLLHUP;
-  if (__poll (fds, 1, 5 * 1000) > 0)
-    /* Success.  We do not check for success of the connect call here.
-       If it failed, the following operations will fail.  */
-    return sock;
+  reqdata.req.version = NSCD_VERSION;
+  reqdata.req.type = type;
+  reqdata.req.key_len = keylen;
+
+  memcpy (reqdata.key, key, keylen);
+
+  bool first_try = true;
+  struct timeval tvend;
+  /* Fake initializing tvend.  */
+  asm ("" : "=m" (tvend));
+  while (1)
+    {
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+#endif
+      ssize_t wres = TEMP_FAILURE_RETRY (__send (sock, &reqdata,
+                                                real_sizeof_reqdata,
+                                                MSG_NOSIGNAL));
+      if (__builtin_expect (wres == (ssize_t) real_sizeof_reqdata, 1))
+       /* We managed to send the request.  */
+       return sock;
+
+      if (wres != -1 || errno != EAGAIN)
+       /* Something is really wrong, no chance to continue.  */
+       break;
+
+      /* The daemon is busy wait for it.  */
+      int to;
+      struct timeval now;
+      (void) __gettimeofday (&now, NULL);
+      if (first_try)
+       {
+         tvend.tv_usec = now.tv_usec;
+         tvend.tv_sec = now.tv_sec + 5;
+         to = 5 * 1000;
+         first_try = false;
+       }
+      else
+       to = ((tvend.tv_sec - now.tv_sec) * 1000
+             + (tvend.tv_usec - now.tv_usec) / 1000);
+
+      struct pollfd fds[1];
+      fds[0].fd = sock;
+      fds[0].events = POLLOUT | POLLERR | POLLHUP;
+      if (__poll (fds, 1, to) <= 0)
+       /* The connection timed out or broke down.  */
+       break;
+
+      /* We try to write again.  */
+    }
 
  out:
   close_not_cancel_no_status (sock);
@@ -175,35 +226,19 @@ get_mapping (request_type type, const char *key,
   struct mapped_database *result = NO_MAPPING;
 #ifdef SCM_RIGHTS
   const size_t keylen = strlen (key) + 1;
-  char resdata[keylen];
   int saved_errno = errno;
 
   int mapfd = -1;
+  char resdata[keylen];
 
-  /* Send the request.  */
-  struct iovec iov[2];
-  request_header req;
-
-  int sock = open_socket ();
+  /* Open a socket and send the request.  */
+  int sock = open_socket (type, key, keylen);
   if (sock < 0)
     goto out;
 
-  req.version = NSCD_VERSION;
-  req.type = type;
-  req.key_len = keylen;
-
-  iov[0].iov_base = &req;
-  iov[0].iov_len = sizeof (req);
-  iov[1].iov_base = (void *) key;
-  iov[1].iov_len = keylen;
-
-  if (__builtin_expect (TEMP_FAILURE_RETRY (__writev (sock, iov, 2))
-                       != iov[0].iov_len + iov[1].iov_len, 0))
-    /* We cannot even write the request.  */
-    goto out_close2;
-
   /* Room for the data sent along with the file descriptor.  We expect
      the key name back.  */
+  struct iovec iov[1];
   iov[0].iov_base = resdata;
   iov[0].iov_len = keylen;
 
@@ -230,19 +265,20 @@ get_mapping (request_type type, const char *key,
   if (wait_on_socket (sock) <= 0)
     goto out_close2;
 
-#ifndef MSG_NOSIGNAL
-# define MSG_NOSIGNAL 0
-#endif
+# ifndef MSG_CMSG_CLOEXEC
+#  define MSG_CMSG_CLOEXEC 0
+# endif
   if (__builtin_expect (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg,
-                                                      MSG_NOSIGNAL))
+                                                      MSG_CMSG_CLOEXEC))
                        != keylen, 0))
     goto out_close2;
 
-  mapfd = *(int *) CMSG_DATA (cmsg);
+  if (__builtin_expect (CMSG_FIRSTHDR (&msg) == NULL
+                       || (CMSG_FIRSTHDR (&msg)->cmsg_len
+                           != CMSG_LEN (sizeof (int))), 0))
+    goto out_close2;
 
-  if (__builtin_expect (CMSG_FIRSTHDR (&msg)->cmsg_len
-                       != CMSG_LEN (sizeof (int)), 0))
-    goto out_close;
+  mapfd = *(int *) CMSG_DATA (cmsg);
 
   struct stat64 st;
   if (__builtin_expect (strcmp (resdata, key) != 0, 0)
@@ -288,6 +324,7 @@ get_mapping (request_type type, const char *key,
       newp->data = ((char *) mapping + head.header_size
                    + roundup (head.module * sizeof (ref_t), ALIGN));
       newp->mapsize = size;
+      newp->datasize = head.data_size;
       /* Set counter to 1 to show it is usable.  */
       newp->counter = 1;
 
@@ -314,17 +351,18 @@ get_mapping (request_type type, const char *key,
 
 struct mapped_database *
 __nscd_get_map_ref (request_type type, const char *name,
-                   struct locked_map_ptr *mapptr, int *gc_cyclep)
+                   volatile struct locked_map_ptr *mapptr, int *gc_cyclep)
 {
   struct mapped_database *cur = mapptr->mapped;
   if (cur == NO_MAPPING)
     return cur;
 
   int cnt = 0;
-  while (atomic_compare_and_exchange_val_acq (&mapptr->lock, 1, 0) != 0)
+  while (__builtin_expect (atomic_compare_and_exchange_val_acq (&mapptr->lock,
+                                                               1, 0) != 0, 0))
     {
       // XXX Best number of rounds?
-      if (++cnt > 5)
+      if (__builtin_expect (++cnt > 5, 0))
        return NO_MAPPING;
 
       atomic_delay ();
@@ -337,8 +375,10 @@ __nscd_get_map_ref (request_type type, const char *name,
       /* If not mapped or timestamp not updated, request new map.  */
       if (cur == NULL
          || (cur->head->nscd_certainly_running == 0
-             && cur->head->timestamp + MAPPING_TIMEOUT < time (NULL)))
-       cur = get_mapping (type, name, &mapptr->mapped);
+             && cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
+         || cur->head->data_size > cur->datasize)
+       cur = get_mapping (type, name,
+                          (struct mapped_database **) &mapptr->mapped);
 
       if (__builtin_expect (cur != NO_MAPPING, 1))
        {
@@ -356,28 +396,50 @@ __nscd_get_map_ref (request_type type, const char *name,
 }
 
 
-const struct datahead *
+/* Don't return const struct datahead *, as eventhough the record
+   is normally constant, it can change arbitrarily during nscd
+   garbage collection.  */
+struct datahead *
 __nscd_cache_search (request_type type, const char *key, size_t keylen,
                     const struct mapped_database *mapped)
 {
   unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module;
+  size_t datasize = mapped->datasize;
 
   ref_t work = mapped->head->array[hash];
-  while (work != ENDREF)
+  while (work != ENDREF && work + sizeof (struct hashentry) <= datasize)
     {
       struct hashentry *here = (struct hashentry *) (mapped->data + work);
 
-      if (type == here->type && keylen == here->len
-         && memcmp (key, mapped->data + here->key, keylen) == 0)
+#ifndef _STRING_ARCH_unaligned
+      /* Although during garbage collection when moving struct hashentry
+        records around we first copy from old to new location and then
+        adjust pointer from previous hashentry to it, there is no barrier
+        between those memory writes.  It is very unlikely to hit it,
+        so check alignment only if a misaligned load can crash the
+        application.  */
+      if ((uintptr_t) here & (__alignof__ (*here) - 1))
+       return NULL;
+#endif
+
+      if (type == here->type
+         && keylen == here->len
+         && here->key + keylen <= datasize
+         && memcmp (key, mapped->data + here->key, keylen) == 0
+         && here->packet + sizeof (struct datahead) <= datasize)
        {
          /* We found the entry.  Increment the appropriate counter.  */
-         const struct datahead *dh
+         struct datahead *dh
            = (struct datahead *) (mapped->data + here->packet);
 
+#ifndef _STRING_ARCH_unaligned
+         if ((uintptr_t) dh & (__alignof__ (*dh) - 1))
+           return NULL;
+#endif
+
          /* See whether we must ignore the entry or whether something
             is wrong because garbage collection is in progress.  */
-         if (dh->usable && ((char *) dh + dh->allocsize
-                            <= (char *) mapped->head + mapped->mapsize))
+         if (dh->usable && here->packet + dh->allocsize <= datasize)
            return dh;
        }
 
@@ -393,28 +455,22 @@ int
 __nscd_open_socket (const char *key, size_t keylen, request_type type,
                    void *response, size_t responselen)
 {
+  /* This should never happen and it is something the nscd daemon
+     enforces, too.  He it helps to limit the amount of stack
+     used.  */
+  if (keylen > MAXKEYLEN)
+    return -1;
+
   int saved_errno = errno;
 
-  int sock = open_socket ();
+  int sock = open_socket (type, key, keylen);
   if (sock >= 0)
     {
-      request_header req;
-      req.version = NSCD_VERSION;
-      req.type = type;
-      req.key_len = keylen;
-
-      struct iovec vec[2];
-      vec[0].iov_base = &req;
-      vec[0].iov_len = sizeof (request_header);
-      vec[1].iov_base = (void *) key;
-      vec[1].iov_len = keylen;
-
-      ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
-      if (nbytes == (ssize_t) (sizeof (request_header) + keylen)
-         /* Wait for data.  */
-         && wait_on_socket (sock) > 0)
+      /* Wait for data.  */
+      if (wait_on_socket (sock) > 0)
        {
-         nbytes = TEMP_FAILURE_RETRY (__read (sock, response, responselen));
+         ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, response,
+                                                      responselen));
          if (nbytes == (ssize_t) responselen)
            return sock;
        }