about summary refs log tree commit diff
path: root/nscd
diff options
context:
space:
mode:
Diffstat (limited to 'nscd')
-rw-r--r--nscd/Makefile45
-rw-r--r--nscd/aicache.c60
-rw-r--r--nscd/cache.c97
-rw-r--r--nscd/connections.c587
-rw-r--r--nscd/dbg_log.c24
-rw-r--r--nscd/gai.c21
-rw-r--r--nscd/getgrgid_r.c22
-rw-r--r--nscd/getgrnam_r.c22
-rw-r--r--nscd/gethstbyad_r.c22
-rw-r--r--nscd/gethstbynm2_r.c22
-rw-r--r--nscd/getpwnam_r.c22
-rw-r--r--nscd/getpwuid_r.c22
-rw-r--r--nscd/grpcache.c80
-rw-r--r--nscd/hstcache.c58
-rw-r--r--nscd/initgrcache.c63
-rw-r--r--nscd/mem.c52
-rw-r--r--nscd/nscd-client.h26
-rw-r--r--nscd/nscd.c102
-rw-r--r--nscd/nscd.conf11
-rw-r--r--nscd/nscd.h34
-rw-r--r--nscd/nscd.init43
-rw-r--r--nscd/nscd_conf.c194
-rw-r--r--nscd/nscd_getai.c83
-rw-r--r--nscd/nscd_getgr_r.c114
-rw-r--r--nscd/nscd_gethst_r.c159
-rw-r--r--nscd/nscd_getpw_r.c74
-rw-r--r--nscd/nscd_helper.c253
-rw-r--r--nscd/nscd_initgroups.c77
-rw-r--r--nscd/nscd_nischeck.c96
-rw-r--r--nscd/nscd_setup_thread.c26
-rw-r--r--nscd/nscd_stat.c26
-rw-r--r--nscd/pwdcache.c77
-rw-r--r--nscd/selinux.c154
-rw-r--r--nscd/selinux.h12
34 files changed, 1873 insertions, 907 deletions
diff --git a/nscd/Makefile b/nscd/Makefile
index 70a35198c2..9c98018217 100644
--- a/nscd/Makefile
+++ b/nscd/Makefile
@@ -1,4 +1,5 @@
-# Copyright (C) 1998, 2000, 2002, 2003, 2004 Free Software Foundation, Inc.
+# Copyright (C) 1998,2000,2002,2003,2004,2005,2006
+#	Free Software Foundation, Inc.
 # This file is part of the GNU C Library.
 
 # The GNU C Library is free software; you can redistribute it and/or
@@ -36,13 +37,12 @@ nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \
 
 ifeq ($(have-thread-library),yes)
 
-others := nscd_nischeck
 ifneq (yesyes,$(have-fpie)$(build-shared))
 others += nscd
 endif
-install-sbin := nscd nscd_nischeck
+install-sbin := nscd
 
-extra-objs := $(nscd-modules:=.o) nscd_nischeck.o
+extra-objs := $(nscd-modules:=.o)
 
 endif
 
@@ -51,15 +51,32 @@ otherlibs += $(nssobjdir)/libnss_files.a $(resolvobjdir)/libnss_dns.a \
 	     $(resolvobjdir)/libresolv.a
 endif
 
+all-nscd-modules := $(nscd-modules) selinux
 ifeq (yes,$(have-selinux))
+ifeq (yes,$(have-libaudit))
+libaudit = -laudit
+ifeq (yes,$(have-libcap))
+libcap = -lcap
+endif
+endif
+
 nscd-modules += selinux
-selinux-LIBS := -lselinux
+selinux-LIBS := -lselinux $(libaudit) $(libcap)
+
+# The configure.in check for libselinux and its headers did not use
+# $SYSINCLUDES.  The directory specified by --with-headers usually
+# contains only the basic kernel interface headers, not something like
+# libselinux.  So the simplest thing is to presume that the standard
+# system headers will be ok for this file.
+$(objpfx)nscd_stat.o: sysincludes = # nothing
+$(objpfx)selinux.o: sysincludes = # nothing
 endif
 
+LDLIBS-nscd = $(selinux-LIBS)
+
 distribute := nscd.h nscd-client.h dbg_log.h \
-	      $(addsuffix .c, $(filter-out xmalloc, $(nscd-modules))) \
-	      nscd_nischeck.c nscd.conf nscd.init nscd_proto.h \
-	      nscd-types.h
+	      $(addsuffix .c, $(filter-out xmalloc,$(all-nscd-modules))) \
+	      nscd.conf nscd.init nscd_proto.h nscd-types.h
 
 include ../Rules
 
@@ -69,10 +86,13 @@ CFLAGS-nscd_gethst_r.c = -fexceptions
 CFLAGS-nscd_getai.c = -fexceptions
 CFLAGS-nscd_initgroups.c = -fexceptions
 
-nscd-cflags = -DIS_IN_nscd=1
+nscd-cflags = -DIS_IN_nscd=1 -D_FORTIFY_SOURCE=2
 ifeq (yesyes,$(have-fpie)$(build-shared))
 nscd-cflags += -fpie
 endif
+ifeq (yes,$(have-ssp))
+nscd-cflags += -fstack-protector
+endif
 
 CFLAGS-nscd.c += $(nscd-cflags)
 CFLAGS-connections.c += $(nscd-cflags)
@@ -104,13 +124,13 @@ relro-LDFLAGS += -Wl,-z,now
 endif
 
 $(objpfx)nscd: $(addprefix $(objpfx),$(nscd-modules:=.o))
-	$(LINK.o) -pie -Wl,-O1 \
+	$(LINK.o) -pie -Wl,-O1 $(nscd-cflags) \
 	  $(sysdep-LDFLAGS) $(config-LDFLAGS) $(relro-LDFLAGS) \
 	  $(extra-B-$(@F:lib%.so=%).so) -B$(csu-objpfx) \
 	  $(extra-B-$(@F:lib%.so=%).so) $(load-map-file) \
 	  $(LDFLAGS) $(LDFLAGS-$(@F)) \
 	  -L$(subst :, -L,$(rpath-link)) -Wl,-rpath-link=$(rpath-link) \
-	  -o $@ $^ $(selinux-LIBS) $(common-objpfx)libc_nonshared.a
+	  -o $@ $^ $(LDLIBS-nscd) $(common-objpfx)libc_nonshared.a
 endif
 
 # This makes sure -DNOT_IN_libc is passed for all these modules.
@@ -119,14 +139,11 @@ lib := nonlib
 include $(patsubst %,$(..)cppflags-iterator.mk,$(cpp-srcs-left))
 
 $(objpfx)nscd: $(nscd-modules:%=$(objpfx)%.o)
-$(objpfx)nscd_nischeck: $(objpfx)nscd_nischeck.o
 
 ifeq ($(build-shared),yes)
 $(objpfx)nscd: $(common-objpfx)rt/librt.so $(shared-thread-library) \
 	       $(common-objpfx)nis/libnsl.so
-$(objpfx)nscd_nischeck: $(common-objpfx)nis/libnsl.so
 else
 $(objpfx)nscd: $(common-objpfx)rt/librt.a $(static-thread-library) \
 	       $(common-objpfx)nis/libnsl.a
-$(objpfx)nscd_nischeck: $(common-objpfx)nis/libnsl.a
 endif
diff --git a/nscd/aicache.c b/nscd/aicache.c
index 4e0496ff44..4640b4df94 100644
--- a/nscd/aicache.c
+++ b/nscd/aicache.c
@@ -1,22 +1,20 @@
 /* Cache handling for host lookup.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <assert.h>
 #include <errno.h>
@@ -26,8 +24,12 @@
 #include <time.h>
 #include <unistd.h>
 #include <sys/mman.h>
-#include <dbg_log.h>
-#include <nscd.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
 
 
 typedef enum nss_status (*nss_gethostbyname3_r)
@@ -310,7 +312,7 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
 		      *family++ = th[j].h_addrtype;
 		    }
 
-	      char *cp = family;
+	      void *cp = family;
 	      if (canon != NULL)
 		cp = mempcpy (cp, canon, canonlen);
 
@@ -365,7 +367,31 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
 		     wait.  */
 		  assert (fd != -1);
 
-		  TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total));
+#ifdef HAVE_SENDFILE
+		  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+		    {
+		      assert (db->wr_fd != -1);
+		      assert ((char *) &dataset->resp > (char *) db->data);
+		      assert ((char *) &dataset->resp - (char *) db->head
+			      + total
+			      <= (sizeof (struct database_pers_head)
+				  + db->head->module * sizeof (ref_t)
+				  + db->head->data_size));
+		      ssize_t written;
+		      written = sendfileall (fd, db->wr_fd,
+					     (char *) &dataset->resp
+					     - (char *) db->head, total);
+# ifndef __ASSUME_SENDFILE
+		      if (written == -1 && errno == ENOSYS)
+			goto use_write;
+# endif
+		    }
+		  else
+# ifndef __ASSUME_SENDFILE
+		  use_write:
+# endif
+#endif
+		    writeall (fd, &dataset->resp, total);
 		}
 
 	      goto out;
@@ -399,7 +425,7 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
       total = sizeof (notfound);
 
       if (fd != -1)
-	TEMP_FAILURE_RETRY (write (fd, &notfound, total));
+	TEMP_FAILURE_RETRY (send (fd, &notfound, total, MSG_NOSIGNAL));
 
       dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
       /* If we cannot permanently store the result, so be it.  */
diff --git a/nscd/cache.c b/nscd/cache.c
index efac4b3bcc..be9be2aa4f 100644
--- a/nscd/cache.c
+++ b/nscd/cache.c
@@ -1,26 +1,25 @@
-/* Copyright (c) 1998, 1999, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (c) 1998, 1999, 2003-2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <assert.h>
 #include <atomic.h>
 #include <errno.h>
 #include <error.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <stdlib.h>
 #include <string.h>
@@ -169,6 +168,12 @@ cache_add (int type, const void *key, size_t len, struct datahead *packet,
   if (nentries > table->head->maxnentries)
     table->head->maxnentries = nentries;
 
+  if (table->persistent)
+    // XXX async OK?
+    msync ((void *) table->head,
+	   (char *) &table->head->array[hash] - (char *) table->head
+	   + sizeof (ref_t), MS_ASYNC);
+
   return 0;
 }
 
@@ -185,21 +190,42 @@ cache_add (int type, const void *key, size_t len, struct datahead *packet,
    free the data structures since some hash table entries share the same
    data.  */
 void
-prune_cache (struct database_dyn *table, time_t now)
+prune_cache (struct database_dyn *table, time_t now, int fd)
 {
   size_t cnt = table->head->module;
 
   /* If this table is not actually used don't do anything.  */
   if (cnt == 0)
-    return;
+    {
+      if (fd != -1)
+	{
+	  /* Reply to the INVALIDATE initiator.  */
+	  int32_t resp = 0;
+	  writeall (fd, &resp, sizeof (resp));
+	}
+      return;
+    }
+
+  /* This function can be called from the cleanup thread but also in
+     response to an invalidate command.  Make sure only one thread is
+     running.  When not serving INVALIDATE request, no need for the
+     second to wait around.  */
+  if (fd == -1)
+    {
+      if (pthread_mutex_trylock (&table->prunelock) != 0)
+	/* The work is already being done.  */
+	return;
+    }
+  else
+    pthread_mutex_lock (&table->prunelock);
 
   /* If we check for the modification of the underlying file we invalidate
      the entries also in this case.  */
   if (table->check_file)
     {
-      struct stat st;
+      struct stat64 st;
 
-      if (stat (table->filename, &st) < 0)
+      if (stat64 (table->filename, &st) < 0)
 	{
 	  char buf[128];
 	  /* We cannot stat() the file, disable file checking if the
@@ -232,6 +258,10 @@ prune_cache (struct database_dyn *table, time_t now)
   char *const data = table->data;
   bool any = false;
 
+  if (__builtin_expect (debug_level > 2, 0))
+    dbg_log (_("pruning %s cache; time %ld"),
+	     dbnames[table - dbs], (long int) now);
+
   do
     {
       ref_t run = table->head->array[--cnt];
@@ -241,6 +271,25 @@ prune_cache (struct database_dyn *table, time_t now)
 	  struct hashentry *runp = (struct hashentry *) (data + run);
 	  struct datahead *dh = (struct datahead *) (data + runp->packet);
 
+	  /* Some debug support.  */
+	  if (__builtin_expect (debug_level > 2, 0))
+	    {
+	      char buf[INET6_ADDRSTRLEN];
+	      const char *str;
+
+	      if (runp->type == GETHOSTBYADDR || runp->type == GETHOSTBYADDRv6)
+		{
+		  inet_ntop (runp->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
+			     data + runp->key, buf, sizeof (buf));
+		  str = buf;
+		}
+	      else
+		str = data + runp->key;
+
+	      dbg_log (_("considering %s entry \"%s\", timeout %" PRIu64),
+		       serv2str[runp->type], str, dh->timeout);
+	    }
+
 	  /* Check whether the entry timed out.  */
 	  if (dh->timeout < now)
 	    {
@@ -339,6 +388,14 @@ prune_cache (struct database_dyn *table, time_t now)
     }
   while (cnt > 0);
 
+  if (fd != -1)
+    {
+      /* Reply to the INVALIDATE initiator that the cache has been
+	 invalidated.  */
+      int32_t resp = 0;
+      writeall (fd, &resp, sizeof (resp));
+    }
+
   if (first <= last)
     {
       struct hashentry *head = NULL;
@@ -395,7 +452,7 @@ prune_cache (struct database_dyn *table, time_t now)
       /* Make sure the data is saved to disk.  */
       if (table->persistent)
 	msync (table->head,
-	       table->data + table->head->first_free - (char *) table->head,
+	       data + table->head->first_free - (char *) table->head,
 	       MS_ASYNC);
 
       /* One extra pass if we do debugging.  */
@@ -411,11 +468,11 @@ prune_cache (struct database_dyn *table, time_t now)
 	      if (runp->type == GETHOSTBYADDR || runp->type == GETHOSTBYADDRv6)
 		{
 		  inet_ntop (runp->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
-			     table->data + runp->key, buf, sizeof (buf));
+			     data + runp->key, buf, sizeof (buf));
 		  str = buf;
 		}
 	      else
-		str = table->data + runp->key;
+		str = data + runp->key;
 
 	      dbg_log ("remove %s entry \"%s\"", serv2str[runp->type], str);
 
@@ -427,4 +484,6 @@ prune_cache (struct database_dyn *table, time_t now)
   /* Run garbage collection if any entry has been removed or replaced.  */
   if (any)
     gc (table);
+
+  pthread_mutex_unlock (&table->prunelock);
 }
diff --git a/nscd/connections.c b/nscd/connections.c
index f22d72e265..8f11421431 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -1,22 +1,20 @@
 /* Inner loops of cache daemon.
-   Copyright (C) 1998-2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 1998-2003, 2004, 2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <alloca.h>
 #include <assert.h>
@@ -39,6 +37,9 @@
 #include <sys/mman.h>
 #include <sys/param.h>
 #include <sys/poll.h>
+#ifdef HAVE_SENDFILE
+# include <sys/sendfile.h>
+#endif
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/un.h>
@@ -46,10 +47,9 @@
 #include "nscd.h"
 #include "dbg_log.h"
 #include "selinux.h"
-
-
-/* Number of bytes of data we initially reserve for each hash table bucket.  */
-#define DEFAULT_DATASIZE_PER_BUCKET 1024
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
 
 
 /* Wrapper functions with error checking for standard functions.  */
@@ -68,6 +68,7 @@ static gid_t *server_groups;
 # define NGROUPS 32
 #endif
 static int server_ngroups;
+static volatile int sighup_pending;
 
 static pthread_attr_t attr;
 
@@ -100,10 +101,13 @@ struct database_dyn dbs[lastdb] =
 {
   [pwddb] = {
     .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    .prunelock = PTHREAD_MUTEX_INITIALIZER,
     .enabled = 0,
     .check_file = 1,
     .persistent = 0,
+    .propagate = 1,
     .shared = 0,
+    .max_db_size = DEFAULT_MAX_DB_SIZE,
     .filename = "/etc/passwd",
     .db_filename = _PATH_NSCD_PASSWD_DB,
     .disabled_iov = &pwd_iov_disabled,
@@ -115,10 +119,13 @@ struct database_dyn dbs[lastdb] =
   },
   [grpdb] = {
     .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    .prunelock = PTHREAD_MUTEX_INITIALIZER,
     .enabled = 0,
     .check_file = 1,
     .persistent = 0,
+    .propagate = 1,
     .shared = 0,
+    .max_db_size = DEFAULT_MAX_DB_SIZE,
     .filename = "/etc/group",
     .db_filename = _PATH_NSCD_GROUP_DB,
     .disabled_iov = &grp_iov_disabled,
@@ -130,10 +137,13 @@ struct database_dyn dbs[lastdb] =
   },
   [hstdb] = {
     .lock = PTHREAD_RWLOCK_WRITER_NONRECURSIVE_INITIALIZER_NP,
+    .prunelock = PTHREAD_MUTEX_INITIALIZER,
     .enabled = 0,
     .check_file = 1,
     .persistent = 0,
+    .propagate = 0,		/* Not used.  */
     .shared = 0,
+    .max_db_size = DEFAULT_MAX_DB_SIZE,
     .filename = "/etc/hosts",
     .db_filename = _PATH_NSCD_HOSTS_DB,
     .disabled_iov = &hst_iov_disabled,
@@ -181,20 +191,252 @@ static int sock;
 unsigned long int client_queued;
 
 
-/* Initialize database information structures.  */
-void
-nscd_init (void)
+ssize_t
+writeall (int fd, const void *buf, size_t len)
 {
-  struct sockaddr_un sock_addr;
-  size_t cnt;
+  size_t n = len;
+  ssize_t ret;
+  do
+    {
+      ret = TEMP_FAILURE_RETRY (send (fd, buf, n, MSG_NOSIGNAL));
+      if (ret <= 0)
+	break;
+      buf = (const char *) buf + ret;
+      n -= ret;
+    }
+  while (n > 0);
+  return ret < 0 ? ret : len - n;
+}
+
+
+#ifdef HAVE_SENDFILE
+ssize_t
+sendfileall (int tofd, int fromfd, off_t off, size_t len)
+{
+  ssize_t n = len;
+  ssize_t ret;
 
-  /* Secure mode and unprivileged mode are incompatible */
-  if (server_user != NULL && secure_in_use)
+  do
     {
-      dbg_log (_("Cannot run nscd in secure mode as unprivileged user"));
-      exit (1);
+      ret = TEMP_FAILURE_RETRY (sendfile (tofd, fromfd, &off, n));
+      if (ret <= 0)
+	break;
+      n -= ret;
+    }
+  while (n > 0);
+  return ret < 0 ? ret : len - n;
+}
+#endif
+
+
+enum usekey
+  {
+    use_not = 0,
+    /* The following three are not really used, they are symbolic constants.  */
+    use_first = 16,
+    use_begin = 32,
+    use_end = 64,
+
+    use_he = 1,
+    use_he_begin = use_he | use_begin,
+    use_he_end = use_he | use_end,
+#if SEPARATE_KEY
+    use_key = 2,
+    use_key_begin = use_key | use_begin,
+    use_key_end = use_key | use_end,
+    use_key_first = use_key_begin | use_first,
+#endif
+    use_data = 3,
+    use_data_begin = use_data | use_begin,
+    use_data_end = use_data | use_end,
+    use_data_first = use_data_begin | use_first
+  };
+
+
+static int
+check_use (const char *data, nscd_ssize_t first_free, uint8_t *usemap,
+	   enum usekey use, ref_t start, size_t len)
+{
+  assert (len >= 2);
+
+  if (start > first_free || start + len > first_free
+      || (start & BLOCK_ALIGN_M1))
+    return 0;
+
+  if (usemap[start] == use_not)
+    {
+      /* Add the start marker.  */
+      usemap[start] = use | use_begin;
+      use &= ~use_first;
+
+      while (--len > 0)
+	if (usemap[++start] != use_not)
+	  return 0;
+	else
+	  usemap[start] = use;
+
+      /* Add the end marker.  */
+      usemap[start] = use | use_end;
+    }
+  else if ((usemap[start] & ~use_first) == ((use | use_begin) & ~use_first))
+    {
+      /* Hash entries can't be shared.  */
+      if (use == use_he)
+	return 0;
+
+      usemap[start] |= (use & use_first);
+      use &= ~use_first;
+
+      while (--len > 1)
+	if (usemap[++start] != use)
+	  return 0;
+
+      if (usemap[++start] != (use | use_end))
+	return 0;
+    }
+  else
+    /* Points to a wrong object or somewhere in the middle.  */
+    return 0;
+
+  return 1;
+}
+
+
+/* Verify data in persistent database.  */
+static int
+verify_persistent_db (void *mem, struct database_pers_head *readhead, int dbnr)
+{
+  assert (dbnr == pwddb || dbnr == grpdb || dbnr == hstdb);
+
+  time_t now = time (NULL);
+
+  struct database_pers_head *head = mem;
+  struct database_pers_head head_copy = *head;
+
+  /* Check that the header that was read matches the head in the database.  */
+  if (readhead != NULL && memcmp (head, readhead, sizeof (*head)) != 0)
+    return 0;
+
+  /* First some easy tests: make sure the database header is sane.  */
+  if (head->version != DB_VERSION
+      || head->header_size != sizeof (*head)
+      /* We allow a timestamp to be one hour ahead of the current time.
+	 This should cover daylight saving time changes.  */
+      || head->timestamp > now + 60 * 60 + 60
+      || (head->gc_cycle & 1)
+      || (size_t) head->module > INT32_MAX / sizeof (ref_t)
+      || (size_t) head->data_size > INT32_MAX - head->module * sizeof (ref_t)
+      || head->first_free < 0
+      || head->first_free > head->data_size
+      || (head->first_free & BLOCK_ALIGN_M1) != 0
+      || head->maxnentries < 0
+      || head->maxnsearched < 0)
+    return 0;
+
+  uint8_t *usemap = calloc (head->first_free, 1);
+  if (usemap == NULL)
+    return 0;
+
+  const char *data = (char *) &head->array[roundup (head->module,
+						    ALIGN / sizeof (ref_t))];
+
+  nscd_ssize_t he_cnt = 0;
+  for (nscd_ssize_t cnt = 0; cnt < head->module; ++cnt)
+    {
+      ref_t work = head->array[cnt];
+
+      while (work != ENDREF)
+	{
+	  if (! check_use (data, head->first_free, usemap, use_he, work,
+			   sizeof (struct hashentry)))
+	    goto fail;
+
+	  /* Now we know we can dereference the record.  */
+	  struct hashentry *here = (struct hashentry *) (data + work);
+
+	  ++he_cnt;
+
+	  /* Make sure the record is for this type of service.  */
+	  if (here->type >= LASTREQ
+	      || serv2db[here->type] != &dbs[dbnr])
+	    goto fail;
+
+	  /* Validate boolean field value.  */
+	  if (here->first != false && here->first != true)
+	    goto fail;
+
+	  if (here->len < 0)
+	    goto fail;
+
+	  /* Now the data.  */
+	  if (here->packet < 0
+	      || here->packet > head->first_free
+	      || here->packet + sizeof (struct datahead) > head->first_free)
+	    goto fail;
+
+	  struct datahead *dh = (struct datahead *) (data + here->packet);
+
+	  if (! check_use (data, head->first_free, usemap,
+			   use_data | (here->first ? use_first : 0),
+			   here->packet, dh->allocsize))
+	    goto fail;
+
+	  if (dh->allocsize < sizeof (struct datahead)
+	      || dh->recsize > dh->allocsize
+	      || (dh->notfound != false && dh->notfound != true)
+	      || (dh->usable != false && dh->usable != true))
+	    goto fail;
+
+	  if (here->key < here->packet + sizeof (struct datahead)
+	      || here->key > here->packet + dh->allocsize
+	      || here->key + here->len > here->packet + dh->allocsize)
+	    {
+#if SEPARATE_KEY
+	      /* If keys can appear outside of data, this should be done
+		 instead.  But gc doesn't mark the data in that case.  */
+	      if (! check_use (data, head->first_free, usemap,
+			       use_key | (here->first ? use_first : 0),
+			       here->key, here->len))
+#endif
+		goto fail;
+	    }
+
+	  work = here->next;
+	}
+    }
+
+  if (he_cnt != head->nentries)
+    goto fail;
+
+  /* See if all data and keys had at least one reference from
+     he->first == true hashentry.  */
+  for (ref_t idx = 0; idx < head->first_free; ++idx)
+    {
+#if SEPARATE_KEY
+      if (usemap[idx] == use_key_begin)
+	goto fail;
+#endif
+      if (usemap[idx] == use_data_begin)
+	goto fail;
     }
 
+  /* Finally, make sure the database hasn't changed since the first test.  */
+  if (memcmp (mem, &head_copy, sizeof (*head)) != 0)
+    goto fail;
+
+  free (usemap);
+  return 1;
+
+fail:
+  free (usemap);
+  return 0;
+}
+
+
+/* Initialize database information structures.  */
+void
+nscd_init (void)
+{
   /* Look up unprivileged uid/gid/groups before we start listening on the
      socket  */
   if (server_user != NULL)
@@ -204,7 +446,7 @@ nscd_init (void)
     /* No configuration for this value, assume a default.  */
     nthreads = 2 * lastdb;
 
-  for (cnt = 0; cnt < lastdb; ++cnt)
+  for (size_t cnt = 0; cnt < lastdb; ++cnt)
     if (dbs[cnt].enabled)
       {
 	pthread_rwlock_init (&dbs[cnt].lock, NULL);
@@ -227,7 +469,7 @@ nscd_init (void)
 		  fail_db:
 		    dbg_log (_("invalid persistent database file \"%s\": %s"),
 			     dbs[cnt].db_filename, strerror (errno));
-		    dbs[cnt].persistent = 0;
+		    unlink (dbs[cnt].db_filename);
 		  }
 		else if (head.module == 0 && head.data_size == 0)
 		  {
@@ -240,22 +482,39 @@ nscd_init (void)
 		    dbg_log (_("invalid persistent database file \"%s\": %s"),
 			     dbs[cnt].db_filename,
 			     _("header size does not match"));
-		    dbs[cnt].persistent = 0;
+		    unlink (dbs[cnt].db_filename);
 		  }
 		else if ((total = (sizeof (head)
 				   + roundup (head.module * sizeof (ref_t),
 					      ALIGN)
 				   + head.data_size))
-			 > st.st_size)
+			 > st.st_size
+			 || total < sizeof (head))
 		  {
 		    dbg_log (_("invalid persistent database file \"%s\": %s"),
 			     dbs[cnt].db_filename,
 			     _("file size does not match"));
-		    dbs[cnt].persistent = 0;
+		    unlink (dbs[cnt].db_filename);
 		  }
-		else if ((mem = mmap (NULL, total, PROT_READ | PROT_WRITE,
-				      MAP_SHARED, fd, 0)) == MAP_FAILED)
+		/* Note we map with the maximum size allowed for the
+		   database.  This is likely much larger than the
+		   actual file size.  This is OK on most OSes since
+		   extensions of the underlying file will
+		   automatically translate more pages available for
+		   memory access.  */
+		else if ((mem = mmap (NULL, dbs[cnt].max_db_size,
+				      PROT_READ | PROT_WRITE,
+				      MAP_SHARED, fd, 0))
+			 == MAP_FAILED)
 		  goto fail_db;
+		else if (!verify_persistent_db (mem, &head, cnt))
+		  {
+		    munmap (mem, total);
+		    dbg_log (_("invalid persistent database file \"%s\": %s"),
+			     dbs[cnt].db_filename,
+			     _("verification failed"));
+		    unlink (dbs[cnt].db_filename);
+		  }
 		else
 		  {
 		    /* Success.  We have the database.  */
@@ -378,20 +637,23 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
 		if (offset % ps != 0)
 		  {
 		    towrite = MIN (remaining, ps - (offset % ps));
-		    pwrite (fd, tmpbuf, towrite, offset);
+		    if (pwrite (fd, tmpbuf, towrite, offset) != towrite)
+		      goto write_fail;
 		    offset += towrite;
 		    remaining -= towrite;
 		  }
 
 		while (remaining > ps)
 		  {
-		    pwrite (fd, tmpbuf, ps, offset);
+		    if (pwrite (fd, tmpbuf, ps, offset) == -1)
+		      goto write_fail;
 		    offset += ps;
 		    remaining -= ps;
 		  }
 
-		if (remaining > 0)
-		  pwrite (fd, tmpbuf, remaining, offset);
+		if (remaining > 0
+		    && pwrite (fd, tmpbuf, remaining, offset) != remaining)
+		  goto write_fail;
 
 		/* Create the header of the file.  */
 		struct database_pers_head head =
@@ -407,10 +669,13 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
 
 		if ((TEMP_FAILURE_RETRY (write (fd, &head, sizeof (head)))
 		     != sizeof (head))
-		    || ftruncate (fd, total) != 0
-		    || (mem = mmap (NULL, total, PROT_READ | PROT_WRITE,
+		    || (TEMP_FAILURE_RETRY_VAL (posix_fallocate (fd, 0, total))
+			!= 0)
+		    || (mem = mmap (NULL, dbs[cnt].max_db_size,
+				    PROT_READ | PROT_WRITE,
 				    MAP_SHARED, fd, 0)) == MAP_FAILED)
 		  {
+		  write_fail:
 		    unlink (dbs[cnt].db_filename);
 		    dbg_log (_("cannot write to database file %s: %s"),
 			     dbs[cnt].db_filename, strerror (errno));
@@ -461,7 +726,7 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
 	    dbs[cnt].head = xmalloc (sizeof (struct database_pers_head)
 				     + (dbs[cnt].suggested_module
 					* sizeof (ref_t)));
-	    memset (dbs[cnt].head, '\0', sizeof (dbs[cnt].head));
+	    memset (dbs[cnt].head, '\0', sizeof (struct database_pers_head));
 	    assert (~ENDREF == 0);
 	    memset (dbs[cnt].head->array, '\xff',
 		    dbs[cnt].suggested_module * sizeof (ref_t));
@@ -478,9 +743,9 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
 	if (dbs[cnt].check_file)
 	  {
 	    /* We need the modification date of the file.  */
-	    struct stat st;
+	    struct stat64 st;
 
-	    if (stat (dbs[cnt].filename, &st) < 0)
+	    if (stat64 (dbs[cnt].filename, &st) < 0)
 	      {
 		/* We cannot stat() the file, disable file checking.  */
 		dbg_log (_("cannot stat() file `%s': %s"),
@@ -497,15 +762,16 @@ cannot set socket to close on exec: %s; disabling paranoia mode"),
   if (sock < 0)
     {
       dbg_log (_("cannot open socket: %s"), strerror (errno));
-      exit (1);
+      exit (errno == EACCES ? 4 : 1);
     }
   /* Bind a name to the socket.  */
+  struct sockaddr_un sock_addr;
   sock_addr.sun_family = AF_UNIX;
   strcpy (sock_addr.sun_path, _PATH_NSCDSOCKET);
   if (bind (sock, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0)
     {
       dbg_log ("%s: %s", _PATH_NSCDSOCKET, strerror (errno));
-      exit (1);
+      exit (errno == EACCES ? 4 : 1);
     }
 
   /* We don't want to get stuck on accept.  */
@@ -551,9 +817,10 @@ close_sockets (void)
 
 
 static void
-invalidate_cache (char *key)
+invalidate_cache (char *key, int fd)
 {
   dbtype number;
+  int32_t resp;
 
   if (strcmp (key, "passwd") == 0)
     number = pwddb;
@@ -567,10 +834,19 @@ invalidate_cache (char *key)
       res_init ();
     }
   else
-    return;
+    {
+      resp = EINVAL;
+      writeall (fd, &resp, sizeof (resp));
+      return;
+    }
 
   if (dbs[number].enabled)
-    prune_cache (&dbs[number], LONG_MAX);
+    prune_cache (&dbs[number], LONG_MAX, fd);
+  else
+    {
+      resp = 0;
+      writeall (fd, &resp, sizeof (resp));
+    }
 }
 
 
@@ -588,9 +864,14 @@ send_ro_fd (struct database_dyn *db, char *key, int fd)
   iov[0].iov_len = strlen (key) + 1;
 
   /* Prepare the control message to transfer the descriptor.  */
-  char buf[CMSG_SPACE (sizeof (int))];
+  union
+  {
+    struct cmsghdr hdr;
+    char bytes[CMSG_SPACE (sizeof (int))];
+  } buf;
   struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
-			.msg_control = buf, .msg_controllen = sizeof (buf) };
+			.msg_control = buf.bytes,
+			.msg_controllen = sizeof (buf) };
   struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
 
   cmsg->cmsg_level = SOL_SOCKET;
@@ -603,7 +884,10 @@ send_ro_fd (struct database_dyn *db, char *key, int fd)
 
   /* Send the control message.  We repeat when we are interrupted but
      everything else is ignored.  */
-  (void) TEMP_FAILURE_RETRY (sendmsg (fd, &msg, 0));
+#ifndef MSG_NOSIGNAL
+# define MSG_NOSIGNAL 0
+#endif
+  (void) TEMP_FAILURE_RETRY (sendmsg (fd, &msg, MSG_NOSIGNAL));
 
   if (__builtin_expect (debug_level > 0, 0))
     dbg_log (_("provide access to FD %d, for %s"), db->ro_fd, key);
@@ -660,8 +944,9 @@ cannot handle old request version %d; current version is %d"),
       if (!db->enabled)
 	{
 	  /* No, sent the prepared record.  */
-	  if (TEMP_FAILURE_RETRY (write (fd, db->disabled_iov->iov_base,
-					 db->disabled_iov->iov_len))
+	  if (TEMP_FAILURE_RETRY (send (fd, db->disabled_iov->iov_base,
+					db->disabled_iov->iov_len,
+					MSG_NOSIGNAL))
 	      != (ssize_t) db->disabled_iov->iov_len
 	      && __builtin_expect (debug_level, 0) > 0)
 	    {
@@ -688,8 +973,34 @@ cannot handle old request version %d; current version is %d"),
       if (cached != NULL)
 	{
 	  /* Hurray it's in the cache.  */
-	  if (TEMP_FAILURE_RETRY (write (fd, cached->data, cached->recsize))
-	      != cached->recsize
+	  ssize_t nwritten;
+
+#ifdef HAVE_SENDFILE
+	  if (db->mmap_used || !cached->notfound)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) cached->data > (char *) db->data);
+	      assert ((char *) cached->data - (char *) db->head
+		      + cached->recsize
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      nwritten = sendfileall (fd, db->wr_fd,
+				      (char *) cached->data
+				      - (char *) db->head, cached->recsize);
+# ifndef __ASSUME_SENDFILE
+	      if (nwritten == -1 && errno == ENOSYS)
+		goto use_write;
+# endif
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    nwritten = writeall (fd, cached->data, cached->recsize);
+
+	  if (nwritten != cached->recsize
 	      && __builtin_expect (debug_level, 0) > 0)
 	    {
 	      /* We have problems sending the result.  */
@@ -759,29 +1070,28 @@ cannot handle old request version %d; current version is %d"),
     case GETSTAT:
     case SHUTDOWN:
     case INVALIDATE:
-      if (! secure_in_use)
-	{
-	  /* Get the callers credentials.  */
+      {
+	/* Get the callers credentials.  */
 #ifdef SO_PEERCRED
-	  struct ucred caller;
-	  socklen_t optlen = sizeof (caller);
+	struct ucred caller;
+	socklen_t optlen = sizeof (caller);
 
-	  if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0)
-	    {
-	      char buf[256];
+	if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0)
+	  {
+	    char buf[256];
 
-	      dbg_log (_("error getting callers id: %s"),
-		       strerror_r (errno, buf, sizeof (buf)));
-	      break;
-	    }
+	    dbg_log (_("error getting caller's id: %s"),
+		     strerror_r (errno, buf, sizeof (buf)));
+	    break;
+	  }
 
-	  uid = caller.uid;
+	uid = caller.uid;
 #else
-	  /* Some systems have no SO_PEERCRED implementation.  They don't
-	     care about security so we don't as well.  */
-	  uid = 0;
+	/* Some systems have no SO_PEERCRED implementation.  They don't
+	   care about security so we don't as well.  */
+	uid = 0;
 #endif
-	}
+      }
 
       /* Accept shutdown, getstat and invalidate only from root.  For
 	 the stat call also allow the user specified in the config file.  */
@@ -793,7 +1103,7 @@ cannot handle old request version %d; current version is %d"),
       else if (uid == 0)
 	{
 	  if (req->type == INVALIDATE)
-	    invalidate_cache (key);
+	    invalidate_cache (key, fd);
 	  else
 	    termination_handler (0);
 	}
@@ -880,7 +1190,7 @@ cannot open /proc/self/cmdline: %s; disabling paranoia mode"),
   /* Second, change back to the old user if we changed it.  */
   if (server_user != NULL)
     {
-      if (setuid (old_uid) != 0)
+      if (setresuid (old_uid, old_uid, old_uid) != 0)
 	{
 	  dbg_log (_("\
 cannot change to old UID: %s; disabling paranoia mode"),
@@ -890,7 +1200,7 @@ cannot change to old UID: %s; disabling paranoia mode"),
 	  return;
 	}
 
-      if (setgid (old_gid) != 0)
+      if (setresgid (old_gid, old_gid, old_gid) != 0)
 	{
 	  dbg_log (_("\
 cannot change to old GID: %s; disabling paranoia mode"),
@@ -941,7 +1251,9 @@ cannot change to old working directory: %s; disabling paranoia mode"),
       setuid (server_uid);
       setgid (server_gid);
     }
-  chdir ("/");
+  if (chdir ("/") != 0)
+    dbg_log (_("cannot change current working directory to \"/\": %s"),
+	     strerror (errno));
   paranoia = 0;
 }
 
@@ -1018,6 +1330,10 @@ nscd_run (void *p)
 	      if (readylist == NULL && to == ETIMEDOUT)
 		{
 		  --nready;
+
+		  if (sighup_pending)
+		    goto sighup_prune;
+
 		  pthread_mutex_unlock (&readylist_lock);
 		  goto only_prune;
 		}
@@ -1027,6 +1343,34 @@ nscd_run (void *p)
 	    pthread_cond_wait (&readylist_cond, &readylist_lock);
 	}
 
+      if (sighup_pending)
+	{
+	  --nready;
+	  pthread_cond_signal (&readylist_cond);
+	sighup_prune:
+	  sighup_pending = 0;
+	  pthread_mutex_unlock (&readylist_lock);
+
+	  /* Prune the password database.  */
+	  if (dbs[pwddb].enabled)
+	    prune_cache (&dbs[pwddb], LONG_MAX, -1);
+
+	  /* Prune the group database.  */
+	  if (dbs[grpdb].enabled)
+	    prune_cache (&dbs[grpdb], LONG_MAX, -1);
+
+	  /* Prune the host database.  */
+	  if (dbs[hstdb].enabled)
+	    prune_cache (&dbs[hstdb], LONG_MAX, -1);
+
+	  /* Re-locking.  */
+	  pthread_mutex_lock (&readylist_lock);
+
+	  /* One more thread available.  */
+	  ++nready;
+	  continue;
+	}
+
       struct fdlist *it = readylist->next;
       if (readylist->next == readylist)
 	/* Just one entry on the list.  */
@@ -1073,25 +1417,7 @@ nscd_run (void *p)
 #ifdef SO_PEERCRED
       pid_t pid = 0;
 
-      if (secure_in_use)
-	{
-	  struct ucred caller;
-	  socklen_t optlen = sizeof (caller);
-
-	  if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &caller, &optlen) < 0)
-	    {
-	      dbg_log (_("error getting callers id: %s"),
-		       strerror_r (errno, buf, sizeof (buf)));
-	      goto close_and_out;
-	    }
-
-	  if (req.type < GETPWBYNAME || req.type > LASTDBREQ
-	      || serv2db[req.type]->secure)
-	    uid = caller.uid;
-
-	  pid = caller.pid;
-	}
-      else if (__builtin_expect (debug_level > 0, 0))
+      if (__builtin_expect (debug_level > 0, 0))
 	{
 	  struct ucred caller;
 	  socklen_t optlen = sizeof (caller);
@@ -1155,8 +1481,7 @@ handle_request: request received (Version = %d)"), req.version);
 	  /* The pthread_cond_timedwait() call timed out.  It is time
 		 to clean up the cache.  */
 	  assert (my_number < lastdb);
-	  prune_cache (&dbs[my_number],
-		       prune_ts.tv_sec + (prune_ts.tv_nsec >= 500000000));
+	  prune_cache (&dbs[my_number], time (NULL), -1);
 
 	  if (clock_gettime (timeout_clock, &prune_ts) == -1)
 	    /* Should never happen.  */
@@ -1217,7 +1542,7 @@ fd_ready (int fd)
 	{
 	  /* We got another thread.  */
 	  ++nthreads;
-	  /* The new thread might new a kick.  */
+	  /* The new thread might need a kick.  */
 	  do_signal = true;
 	}
 
@@ -1280,18 +1605,24 @@ main_loop_poll (void)
 	      /* We have a new incoming connection.  Accept the connection.  */
 	      int fd = TEMP_FAILURE_RETRY (accept (sock, NULL, NULL));
 
-	      /* use the descriptor if we have not reached the limit.  */
-	      if (fd >= 0 && firstfree < nconns)
+	      /* Use the descriptor if we have not reached the limit.  */
+	      if (fd >= 0)
 		{
-		  conns[firstfree].fd = fd;
-		  conns[firstfree].events = POLLRDNORM;
-		  starttime[firstfree] = now;
-		  if (firstfree >= nused)
-		    nused = firstfree + 1;
-
-		  do
-		    ++firstfree;
-		  while (firstfree < nused && conns[firstfree].fd != -1);
+		  if (firstfree < nconns)
+		    {
+		      conns[firstfree].fd = fd;
+		      conns[firstfree].events = POLLRDNORM;
+		      starttime[firstfree] = now;
+		      if (firstfree >= nused)
+			nused = firstfree + 1;
+
+		      do
+			++firstfree;
+		      while (firstfree < nused && conns[firstfree].fd != -1);
+		    }
+		  else
+		    /* We cannot use the connection so close it.  */
+		    close (fd);
 		}
 
 	      --n;
@@ -1402,10 +1733,9 @@ main_loop_epoll (int efd)
 	else
 	  {
 	    /* Remove the descriptor from the epoll descriptor.  */
-	    struct epoll_event ev = { 0, };
-	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, revs[cnt].data.fd, &ev);
+	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, revs[cnt].data.fd, NULL);
 
-	    /* Get a worked to handle the request.  */
+	    /* Get a worker to handle the request.  */
 	    fd_ready (revs[cnt].data.fd);
 
 	    /* Reset the time.  */
@@ -1425,8 +1755,7 @@ main_loop_epoll (int efd)
 	if (cnt != sock && starttime[cnt] != 0 && starttime[cnt] < laststart)
 	  {
 	    /* We are waiting for this one for too long.  Close it.  */
-	    struct epoll_event ev = {0, };
-	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, cnt, &ev);
+	    (void) epoll_ctl (efd, EPOLL_CTL_DEL, cnt, NULL);
 
 	    (void) close (cnt);
 
@@ -1579,23 +1908,49 @@ begin_drop_privileges (void)
 static void
 finish_drop_privileges (void)
 {
+#if defined HAVE_LIBAUDIT && defined HAVE_LIBCAP
+  /* We need to preserve the capabilities to connect to the audit daemon.  */
+  cap_t new_caps = preserve_capabilities ();
+#endif
+
   if (setgroups (server_ngroups, server_groups) == -1)
     {
       dbg_log (_("Failed to run nscd as user '%s'"), server_user);
       error (EXIT_FAILURE, errno, _("setgroups failed"));
     }
 
-  if (setgid (server_gid) == -1)
+  int res;
+  if (paranoia)
+    res = setresgid (server_gid, server_gid, old_gid);
+  else
+    res = setgid (server_gid);
+  if (res == -1)
     {
       dbg_log (_("Failed to run nscd as user '%s'"), server_user);
       perror ("setgid");
-      exit (1);
+      exit (4);
     }
 
-  if (setuid (server_uid) == -1)
+  if (paranoia)
+    res = setresuid (server_uid, server_uid, old_uid);
+  else
+    res = setuid (server_uid);
+  if (res == -1)
     {
       dbg_log (_("Failed to run nscd as user '%s'"), server_user);
       perror ("setuid");
-      exit (1);
+      exit (4);
     }
+
+#if defined HAVE_LIBAUDIT && defined HAVE_LIBCAP
+  /* Remove the temporary capabilities.  */
+  install_real_capabilities (new_caps);
+#endif
+}
+
+/* Handle the HUP signal which will force a dump of the cache */
+void
+sighup_handler (int signum)
+{
+  sighup_pending = 1;
 }
diff --git a/nscd/dbg_log.c b/nscd/dbg_log.c
index afa06dcbe9..d64afc7e8d 100644
--- a/nscd/dbg_log.c
+++ b/nscd/dbg_log.c
@@ -1,21 +1,19 @@
-/* Copyright (c) 1998, 2000, 2004 Free Software Foundation, Inc.
+/* Copyright (c) 1998, 2000, 2004, 2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <stdarg.h>
 #include <stdio.h>
@@ -44,7 +42,7 @@ init_logfile (void)
 {
   if (logfilename)
     {
-      dbgout = fopen (logfilename, "a");
+      dbgout = fopen64 (logfilename, "a");
       return dbgout == NULL ? 0 : 1;
     }
   return 1;
diff --git a/nscd/gai.c b/nscd/gai.c
index 722c7e415d..68719d876a 100644
--- a/nscd/gai.c
+++ b/nscd/gai.c
@@ -1,3 +1,21 @@
+/* Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 2004.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include <alloca.h>
 /* This file uses the getaddrinfo code but it compiles it without NSCD
    support.  We just need a few symbol renames.  */
 #define __getservbyname_r getservbyname_r
@@ -8,6 +26,9 @@
 #define __bind bind
 #define __sendto sendto
 #define __strchrnul strchrnul
+#define __getline getline
+/* nscd uses 1MB or 2MB thread stacks.  */
+#define __libc_use_alloca(size) (size <= __MAX_ALLOCA_CUTOFF)
 
 #include <getaddrinfo.c>
 
diff --git a/nscd/getgrgid_r.c b/nscd/getgrgid_r.c
index d46fb0fcac..037509d8aa 100644
--- a/nscd/getgrgid_r.c
+++ b/nscd/getgrgid_r.c
@@ -1,21 +1,19 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <grp.h>
 
diff --git a/nscd/getgrnam_r.c b/nscd/getgrnam_r.c
index 42daa16177..8fc74dcbaf 100644
--- a/nscd/getgrnam_r.c
+++ b/nscd/getgrnam_r.c
@@ -1,21 +1,19 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <grp.h>
 
diff --git a/nscd/gethstbyad_r.c b/nscd/gethstbyad_r.c
index 47ed3e22e7..4c02492101 100644
--- a/nscd/gethstbyad_r.c
+++ b/nscd/gethstbyad_r.c
@@ -1,21 +1,19 @@
-/* Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
+/* Copyright (C) 1996,1997,1998,1999,2000,2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <netdb.h>
 
diff --git a/nscd/gethstbynm2_r.c b/nscd/gethstbynm2_r.c
index b0cc713a84..416b5ceafa 100644
--- a/nscd/gethstbynm2_r.c
+++ b/nscd/gethstbynm2_r.c
@@ -1,21 +1,19 @@
-/* Copyright (C) 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 2000, 2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <ctype.h>
 #include <errno.h>
diff --git a/nscd/getpwnam_r.c b/nscd/getpwnam_r.c
index df73b99e4a..c92209a0cf 100644
--- a/nscd/getpwnam_r.c
+++ b/nscd/getpwnam_r.c
@@ -1,21 +1,19 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <pwd.h>
 
diff --git a/nscd/getpwuid_r.c b/nscd/getpwuid_r.c
index 015a521bbe..f68951511e 100644
--- a/nscd/getpwuid_r.c
+++ b/nscd/getpwuid_r.c
@@ -1,21 +1,19 @@
-/* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+/* Copyright (C) 1996, 1997, 1998, 2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <pwd.h>
 
diff --git a/nscd/grpcache.c b/nscd/grpcache.c
index c565f5a682..c207492cc0 100644
--- a/nscd/grpcache.c
+++ b/nscd/grpcache.c
@@ -1,22 +1,20 @@
 /* Cache handling for group lookup.
-   Copyright (C) 1998-2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 1998-2005, 2006, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <alloca.h>
 #include <assert.h>
@@ -32,10 +30,14 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/mman.h>
+#include <sys/socket.h>
 #include <stackinfo.h>
 
 #include "nscd.h"
 #include "dbg_log.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
 
 /* This is the standard reply in case the service is disabled.  */
 static const gr_response_header disabled =
@@ -107,7 +109,8 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
 	     case.  */
 	  total = sizeof (notfound);
 
-	  written = TEMP_FAILURE_RETRY (write (fd, &notfound, total));
+	  written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
+					      MSG_NOSIGNAL));
 
 	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
 	  /* If we cannot permanently store the result, so be it.  */
@@ -167,7 +170,7 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
       char *gr_name;
       char *cp;
       const size_t key_len = strlen (key);
-      const size_t buf_len = 3 + sizeof (grp->gr_gid) + key_len + 1;
+      const size_t buf_len = 3 * sizeof (grp->gr_gid) + key_len + 1;
       char *buf = alloca (buf_len);
       ssize_t n;
       size_t cnt;
@@ -276,6 +279,7 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
 		  /* Adjust pointers into the memory block.  */
 		  gr_name = (char *) newp + (gr_name - (char *) dataset);
 		  cp = (char *) newp + (cp - (char *) dataset);
+		  key_copy = (char *) newp + (key_copy - (char *) dataset);
 
 		  dataset = memcpy (newp, dataset, total + n);
 		  alloca_used = false;
@@ -292,7 +296,30 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
 	     unnecessarily let the receiver wait.  */
 	  assert (fd != -1);
 
-	  written = TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total));
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) &dataset->resp - (char *) db->head
+		      + total
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      written = sendfileall (fd, db->wr_fd,
+				     (char *) &dataset->resp
+				     - (char *) db->head, total);
+# ifndef __ASSUME_SENDFILE
+	      if (written == -1 && errno == ENOSYS)
+		goto use_write;
+# endif
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    written = writeall (fd, &dataset->resp, total);
 	}
 
       /* Add the record to the database.  But only if it has not been
@@ -316,10 +343,10 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
 	     marked with FIRST first.  Otherwise we end up with
 	     dangling "pointers" in case a latter hash entry cannot be
 	     added.  */
-	  bool first = req->type == GETGRBYNAME;
+	  bool first = true;
 
 	  /* If the request was by GID, add that entry first.  */
-	  if (req->type != GETGRBYNAME)
+	  if (req->type == GETGRBYGID)
 	    {
 	      if (cache_add (GETGRBYGID, cp, key_offset, &dataset->head, true,
 			     db, owner) < 0)
@@ -329,12 +356,14 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
 		  dataset->head.usable = false;
 		  goto out;
 		}
+
+	      first = false;
 	    }
 	  /* If the key is different from the name add a separate entry.  */
 	  else if (strcmp (key_copy, gr_name) != 0)
 	    {
 	      if (cache_add (GETGRBYNAME, key_copy, key_len + 1,
-			     &dataset->head, first, db, owner) < 0)
+			     &dataset->head, true, db, owner) < 0)
 		{
 		  /* Could not allocate memory.  Make sure the data gets
 		     discarded.  */
@@ -346,11 +375,13 @@ cache_addgr (struct database_dyn *db, int fd, request_header *req,
 	    }
 
 	  /* We have to add the value for both, byname and byuid.  */
-	  if (__builtin_expect (cache_add (GETGRBYNAME, gr_name, gr_name_len,
-					   &dataset->head, first, db, owner)
-				== 0, 1))
+	  if ((req->type == GETGRBYNAME || db->propagate)
+	      && __builtin_expect (cache_add (GETGRBYNAME, gr_name,
+					      gr_name_len,
+					      &dataset->head, first, db, owner)
+				   == 0, 1))
 	    {
-	      if (req->type == GETGRBYNAME)
+	      if (req->type == GETGRBYNAME && db->propagate)
 		(void) cache_add (GETGRBYGID, cp, key_offset, &dataset->head,
 				  req->type != GETGRBYNAME, db, owner);
 	    }
@@ -429,11 +460,10 @@ addgrbyX (struct database_dyn *db, int fd, request_header *req,
     {
       char *old_buffer = buffer;
       errno = 0;
-#define INCR 1024
 
       if (__builtin_expect (buflen > 32768, 0))
 	{
-	  buflen += INCR;
+	  buflen *= 2;
 	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
 	  if (buffer == NULL)
 	    {
@@ -454,7 +484,7 @@ addgrbyX (struct database_dyn *db, int fd, request_header *req,
       else
 	/* Allocate a new buffer on the stack.  If possible combine it
 	   with the previously allocated buffer.  */
-	buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
+	buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
     }
 
 #if 0
diff --git a/nscd/hstcache.c b/nscd/hstcache.c
index 99d2998f49..e27ece5bc6 100644
--- a/nscd/hstcache.c
+++ b/nscd/hstcache.c
@@ -1,22 +1,20 @@
 /* Cache handling for host lookup.
-   Copyright (C) 1998-2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 1998-2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <alloca.h>
 #include <assert.h>
@@ -38,6 +36,9 @@
 
 #include "nscd.h"
 #include "dbg_log.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
 
 
 /* This is the standard reply in case the service is disabled.  */
@@ -115,7 +116,8 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
 	  written = total = sizeof (notfound);
 
 	  if (fd != -1)
-	    written = TEMP_FAILURE_RETRY (write (fd, &notfound, total));
+	    written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
+						MSG_NOSIGNAL));
 
 	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
 	  /* If we cannot permanently store the result, so be it.  */
@@ -327,7 +329,30 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
 	     unnecessarily keep the receiver waiting.  */
 	  assert (fd != -1);
 
-	  written = TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total));
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) &dataset->resp - (char *) db->head
+		      + total
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      written = sendfileall (fd, db->wr_fd,
+				     (char *) &dataset->resp
+				     - (char *) db->head, total);
+# ifndef __ASSUME_SENDFILE
+	      if (written == -1 && errno == ENOSYS)
+		goto use_write;
+# endif
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    written = writeall (fd, &dataset->resp, total);
 	}
 
       /* Add the record to the database.  But only if it has not been
@@ -453,11 +478,10 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
     {
       char *old_buffer = buffer;
       errno = 0;
-#define INCR 1024
 
       if (__builtin_expect (buflen > 32768, 0))
 	{
-	  buflen += INCR;
+	  buflen *= 2;
 	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
 	  if (buffer == NULL)
 	    {
@@ -478,7 +502,7 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
       else
 	/* Allocate a new buffer on the stack.  If possible combine it
 	   with the previously allocated buffer.  */
-	buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
+	buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
     }
 
 #if 0
diff --git a/nscd/initgrcache.c b/nscd/initgrcache.c
index b46433716b..f1f859c552 100644
--- a/nscd/initgrcache.c
+++ b/nscd/initgrcache.c
@@ -1,22 +1,20 @@
 /* Cache handling for host lookup.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <assert.h>
 #include <errno.h>
@@ -26,8 +24,12 @@
 #include <time.h>
 #include <unistd.h>
 #include <sys/mman.h>
-#include <dbg_log.h>
-#include <nscd.h>
+
+#include "dbg_log.h"
+#include "nscd.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
 
 #include "../nss/nsswitch.h"
 
@@ -105,6 +107,7 @@ addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
 
   long int start = 0;
   bool all_tryagain = true;
+  bool any_success = false;
 
   /* This is temporary memory, we need not (ad must not) call
      mempool_alloc.  */
@@ -156,6 +159,8 @@ addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
       if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
 	__libc_fatal ("illegal status in internal_getgrouplist");
 
+      any_success |= status == NSS_STATUS_SUCCESS;
+
       if (status != NSS_STATUS_SUCCESS
 	  && nss_next_action (nip, status) == NSS_ACTION_RETURN)
 	 break;
@@ -169,7 +174,7 @@ addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
   ssize_t total;
   ssize_t written;
  out:
-  if (start == 0)
+  if (!any_success)
     {
       /* Nothing found.  Create a negative result record.  */
       written = total = sizeof (notfound);
@@ -188,7 +193,8 @@ addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
 	  /* We have no data.  This means we send the standard reply for this
 	     case.  */
 	  if (fd != -1)
-	    written = TEMP_FAILURE_RETRY (write (fd, &notfound, total));
+	    written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
+						MSG_NOSIGNAL));
 
 	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
 	  /* If we cannot permanently store the result, so be it.  */
@@ -343,7 +349,30 @@ addinitgroupsX (struct database_dyn *db, int fd, request_header *req,
 	     unnecessarily let the receiver wait.  */
 	  assert (fd != -1);
 
-	  written = TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total));
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) &dataset->resp - (char *) db->head
+		      + total
+		      <= (sizeof (struct database_pers_head)
+			  + db->head->module * sizeof (ref_t)
+			  + db->head->data_size));
+	      written = sendfileall (fd, db->wr_fd,
+				     (char *) &dataset->resp
+				     - (char *) db->head, total);
+# ifndef __ASSUME_SENDFILE
+	      if (written == -1 && errno == ENOSYS)
+		goto use_write;
+# endif
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    written = writeall (fd, &dataset->resp, total);
 	}
 
 
diff --git a/nscd/mem.c b/nscd/mem.c
index c3a0f96702..5206c5af38 100644
--- a/nscd/mem.c
+++ b/nscd/mem.c
@@ -1,26 +1,25 @@
 /* Cache memory handling.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <assert.h>
 #include <errno.h>
 #include <error.h>
+#include <fcntl.h>
 #include <inttypes.h>
 #include <libintl.h>
 #include <limits.h>
@@ -34,12 +33,6 @@
 #include "nscd.h"
 
 
-/* Maximum alignment requirement we will encounter.  */
-#define BLOCK_ALIGN_LOG 3
-#define BLOCK_ALIGN (1 << BLOCK_ALIGN_LOG)
-#define BLOCK_ALIGN_M1 (BLOCK_ALIGN - 1)
-
-
 static int
 sort_he (const void *p1, const void *p2)
 {
@@ -194,7 +187,7 @@ gc (struct database_dyn *db)
       highref -= BLOCK_ALIGN;
     }
 
-  /* No we can iterate over the MARK array and find bits which are not
+  /* Now we can iterate over the MARK array and find bits which are not
      set.  These represent memory which can be recovered.  */
   size_t byte = 0;
   /* Find the first gap.  */
@@ -486,17 +479,26 @@ mempool_alloc (struct database_dyn *db, size_t len)
       if (! tried_resize)
 	{
 	  /* Try to resize the database.  Grow size of 1/8th.  */
-	  size_t new_data_size = db->head->data_size + db->head->data_size / 8;
 	  size_t oldtotal = (sizeof (struct database_pers_head)
-			     + db->head->module * sizeof (ref_t)
+			     + roundup (db->head->module * sizeof (ref_t), ALIGN)
 			     + db->head->data_size);
+	  size_t new_data_size = (db->head->data_size
+				  + MAX (2 * len, db->head->data_size / 8));
 	  size_t newtotal = (sizeof (struct database_pers_head)
-			     + db->head->module * sizeof (ref_t)
+			     + roundup (db->head->module * sizeof (ref_t), ALIGN)
 			     + new_data_size);
+	  if (newtotal > db->max_db_size)
+	    {
+	      new_data_size -= newtotal - db->max_db_size;
+	      newtotal = db->max_db_size;
+	    }
 
-	  if ((!db->mmap_used || ftruncate (db->wr_fd, newtotal) != 0)
-	      /* Try to resize the mapping.  Note: no MREMAP_MAYMOVE.  */
-	      && mremap (db->head, oldtotal, newtotal, 0) == 0)
+	  if (db->mmap_used && newtotal > oldtotal
+	      /* We only have to adjust the file size.  The new pages
+		 become magically available.  */
+	      && TEMP_FAILURE_RETRY_VAL (posix_fallocate (db->wr_fd, oldtotal,
+							  newtotal
+							  - oldtotal)) == 0)
 	    {
 	      db->head->data_size = new_data_size;
 	      tried_resize = true;
diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h
index d49cb8136c..8946b6315b 100644
--- a/nscd/nscd-client.h
+++ b/nscd/nscd-client.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 1998, 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (c) 1998, 1999, 2000, 2003, 2004, 2005, 2006, 2007
+   Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
 
@@ -28,6 +29,7 @@
 #include <sys/types.h>
 #include <atomic.h>
 #include <nscd-types.h>
+#include <sys/uio.h>
 
 
 /* Version number of the daemon interface */
@@ -256,6 +258,7 @@ struct mapped_database
   const char *data;
   size_t mapsize;
   int counter;		/* > 0 indicates it is usable.  */
+  size_t datasize;
 };
 #define NO_MAPPING ((struct mapped_database *) -1l)
 
@@ -275,7 +278,7 @@ extern int __nscd_open_socket (const char *key, size_t keylen,
 /* Get reference of mapping.  */
 extern struct mapped_database *__nscd_get_map_ref (request_type type,
 						   const char *name,
-						   struct locked_map_ptr *mapptr,
+						   volatile struct locked_map_ptr *mapptr,
 						   int *gc_cyclep);
 
 /* Unmap database.  */
@@ -304,9 +307,20 @@ static inline int __nscd_drop_map_ref (struct mapped_database *map,
 
 
 /* Search the mapped database.  */
-extern const struct datahead *__nscd_cache_search (request_type type,
-						   const char *key,
-						   size_t keylen,
-						   const struct mapped_database *mapped);
+extern struct datahead *__nscd_cache_search (request_type type,
+					     const char *key,
+					     size_t keylen,
+					     const struct mapped_database *mapped);
+
+/* Wrappers around read, readv and write that only read/write less than LEN
+   bytes on error or EOF.  */
+extern ssize_t __readall (int fd, void *buf, size_t len)
+  attribute_hidden;
+extern ssize_t __readvall (int fd, const struct iovec *iov, int iovcnt)
+  attribute_hidden;
+extern ssize_t writeall (int fd, const void *buf, size_t len)
+  attribute_hidden;
+extern ssize_t sendfileall (int tofd, int fromfd, off_t off, size_t len)
+  attribute_hidden;
 
 #endif /* nscd.h */
diff --git a/nscd/nscd.c b/nscd/nscd.c
index 0cc1818d9d..588b09d4fb 100644
--- a/nscd/nscd.c
+++ b/nscd/nscd.c
@@ -1,21 +1,19 @@
-/* Copyright (c) 1998-2003, 2004 Free Software Foundation, Inc.
+/* Copyright (c) 1998-2003, 2004, 2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 /* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts.  */
 
@@ -72,7 +70,6 @@ int disabled_passwd;
 int disabled_group;
 int go_background = 1;
 
-int secure_in_use;
 static const char *conffile = _PATH_NSCDCONF;
 
 time_t start_time;
@@ -122,6 +119,9 @@ static struct argp argp =
   options, parse_opt, NULL, doc,
 };
 
+/* The SIGHUP handler is extern to this file */
+extern void sighup_handler(int signum);
+
 /* True if only statistics are requested.  */
 static bool get_stats;
 
@@ -145,17 +145,15 @@ main (int argc, char **argv)
     {
       error (0, 0, gettext ("wrong number of arguments"));
       argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
-      exit (EXIT_FAILURE);
+      exit (1);
     }
 
   /* Read the configuration file.  */
   if (nscd_parse_file (conffile, dbs) != 0)
-    {
-      /* We couldn't read the configuration file.  We don't start the
-	 server.  */
-      dbg_log (_("cannot read configuration file; this is fatal"));
-      exit (1);
-    }
+    /* We couldn't read the configuration file.  We don't start the
+       server.  */
+    error (EXIT_FAILURE, 0,
+	   _("failure while reading configuration file; this is fatal"));
 
   /* Do we only get statistics?  */
   if (get_stats)
@@ -240,7 +238,9 @@ main (int argc, char **argv)
 
       setsid ();
 
-      chdir ("/");
+      if (chdir ("/") != 0)
+	error (EXIT_FAILURE, errno,
+	       _("cannot change current working directory to \"/\""));
 
       openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
 
@@ -266,6 +266,7 @@ main (int argc, char **argv)
   signal (SIGINT, termination_handler);
   signal (SIGQUIT, termination_handler);
   signal (SIGTERM, termination_handler);
+  signal (SIGHUP, sighup_handler);
   signal (SIGPIPE, SIG_IGN);
 
   /* Cleanup files created by a previous 'bind'.  */
@@ -301,7 +302,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
     case 'K':
       if (getuid () != 0)
-	error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
+	error (4, 0, _("Only root is allowed to use this option!"));
       {
 	int sock = nscd_open_socket ();
 	request_header req;
@@ -313,8 +314,9 @@ parse_opt (int key, char *arg, struct argp_state *state)
 	req.version = NSCD_VERSION;
 	req.type = SHUTDOWN;
 	req.key_len = 0;
-	nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
-					    sizeof (request_header)));
+	nbytes = TEMP_FAILURE_RETRY (send (sock, &req,
+					   sizeof (request_header),
+					   MSG_NOSIGNAL));
 	close (sock);
 	exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
       }
@@ -325,7 +327,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
 
     case 'i':
       if (getuid () != 0)
-	error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
+	error (4, 0, _("Only root is allowed to use this option!"));
       else
 	{
 	  int sock = nscd_open_socket ();
@@ -334,9 +336,6 @@ parse_opt (int key, char *arg, struct argp_state *state)
 	    exit (EXIT_FAILURE);
 
 	  request_header req;
-	  ssize_t nbytes;
-	  struct iovec iov[2];
-
 	  if (strcmp (arg, "passwd") == 0)
 	    req.key_len = sizeof "passwd";
 	  else if (strcmp (arg, "group") == 0)
@@ -349,17 +348,38 @@ parse_opt (int key, char *arg, struct argp_state *state)
 	  req.version = NSCD_VERSION;
 	  req.type = INVALIDATE;
 
+	  struct iovec iov[2];
 	  iov[0].iov_base = &req;
 	  iov[0].iov_len = sizeof (req);
 	  iov[1].iov_base = arg;
 	  iov[1].iov_len = req.key_len;
 
-	  nbytes = TEMP_FAILURE_RETRY (writev (sock, iov, 2));
+	  ssize_t nbytes = TEMP_FAILURE_RETRY (writev (sock, iov, 2));
+
+	  if (nbytes != iov[0].iov_len + iov[1].iov_len)
+	    {
+	      int err = errno;
+	      close (sock);
+	      error (EXIT_FAILURE, err, _("write incomplete"));
+	    }
+
+	  /* Wait for ack.  Older nscd just closed the socket when
+	     prune_cache finished, silently ignore that.  */
+	  int32_t resp = 0;
+	  nbytes = TEMP_FAILURE_RETRY (read (sock, &resp, sizeof (resp)));
+	  if (nbytes != 0 && nbytes != sizeof (resp))
+	    {
+	      int err = errno;
+	      close (sock);
+	      error (EXIT_FAILURE, err, _("cannot read invalidate ACK"));
+	    }
 
 	  close (sock);
 
-	  exit (nbytes != iov[0].iov_len + iov[1].iov_len
-		? EXIT_FAILURE : EXIT_SUCCESS);
+	  if (resp != 0)
+	    error (EXIT_FAILURE, resp, _("invalidation failed"));
+
+	  exit (0);
 	}
 
     case 't':
@@ -367,16 +387,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
       break;
 
     case 'S':
-#if 0
-      if (strcmp (arg, "passwd,yes") == 0)
-	secure_in_use = dbs[pwddb].secure = 1;
-      else if (strcmp (arg, "group,yes") == 0)
-	secure_in_use = dbs[grpdb].secure = 1;
-      else if (strcmp (arg, "hosts,yes") == 0)
-	secure_in_use = dbs[hstdb].secure = 1;
-#else
       error (0, 0, _("secure services not implemented anymore"));
-#endif
       break;
 
     default:
@@ -395,7 +406,7 @@ print_version (FILE *stream, struct argp_state *state)
 Copyright (C) %s Free Software Foundation, Inc.\n\
 This is free software; see the source for copying conditions.  There is NO\n\
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "2004");
+"), "2006");
   fprintf (stream, gettext ("Written by %s.\n"),
 	   "Thorsten Kukuk and Ulrich Drepper");
 }
@@ -442,6 +453,9 @@ termination_handler (int signum)
   /* Synchronize memory.  */
   for (int cnt = 0; cnt < lastdb; ++cnt)
     {
+      if (!dbs[cnt].enabled)
+	continue;
+
       /* Make sure nobody keeps using the database.  */
       dbs[cnt].head->timestamp = 0;
 
@@ -495,10 +509,10 @@ write_pid (const char *file)
     return -1;
 
   fprintf (fp, "%d\n", getpid ());
-  if (fflush (fp) || ferror (fp))
-    return -1;
+
+  int result = fflush (fp) || ferror (fp) ? -1 : 0;
 
   fclose (fp);
 
-  return 0;
+  return result;
 }
diff --git a/nscd/nscd.conf b/nscd/nscd.conf
index 87e7a84487..954eafd554 100644
--- a/nscd/nscd.conf
+++ b/nscd/nscd.conf
@@ -23,6 +23,8 @@
 #	check-files		<service> <yes|no>
 #	persistent		<service> <yes|no>
 #	shared			<service> <yes|no>
+#	max-db-size		<service> <number bytes>
+#	auto-propagate		<service> <yes|no>
 #
 # Currently supported cache names (services): passwd, group, hosts
 #
@@ -31,8 +33,8 @@
 #	logfile			/var/log/nscd.log
 #	threads			6
 #	max-threads		128
-#	server-user		nobody
-#	stat-user		somebody
+	server-user		nscd
+#	stat-user		nocpulse
 	debug-level		0
 #	reload-count		5
 	paranoia		no
@@ -45,6 +47,8 @@
 	check-files		passwd		yes
 	persistent		passwd		yes
 	shared			passwd		yes
+	max-db-size		passwd		33554432
+	auto-propagate		passwd		yes
 
 	enable-cache		group		yes
 	positive-time-to-live	group		3600
@@ -53,6 +57,8 @@
 	check-files		group		yes
 	persistent		group		yes
 	shared			group		yes
+	max-db-size		group		33554432
+	auto-propagate		group		yes
 
 	enable-cache		hosts		yes
 	positive-time-to-live	hosts		3600
@@ -61,3 +67,4 @@
 	check-files		hosts		yes
 	persistent		hosts		yes
 	shared			hosts		yes
+	max-db-size		hosts		33554432
diff --git a/nscd/nscd.h b/nscd/nscd.h
index d5dc613d22..5c2ff3a95b 100644
--- a/nscd/nscd.h
+++ b/nscd/nscd.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 1998, 1999, 2000, 2001, 2003, 2004
+/* Copyright (c) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
@@ -58,16 +58,18 @@ typedef enum
 struct database_dyn
 {
   pthread_rwlock_t lock;
+  pthread_mutex_t prunelock;
 
   int enabled;
   int check_file;
   int persistent;
   int shared;
-  const char *filename;
+  int propagate;
+  const char filename[12];
   const char *db_filename;
   time_t file_mtime;
   size_t suggested_module;
-  int secure;
+  size_t max_db_size;
 
   unsigned long int postimeout;	/* In seconds.  */
   unsigned long int negtimeout;	/* In seconds.  */
@@ -94,6 +96,17 @@ struct database_dyn
 /* Path used when not using persistent storage.  */
 #define _PATH_NSCD_XYZ_DB_TMP	"/var/run/nscd/dbXXXXXX"
 
+/* Maximum alignment requirement we will encounter.  */
+#define BLOCK_ALIGN_LOG 3
+#define BLOCK_ALIGN (1 << BLOCK_ALIGN_LOG)
+#define BLOCK_ALIGN_M1 (BLOCK_ALIGN - 1)
+
+/* Default value for the maximum size of the database files.  */
+#define DEFAULT_MAX_DB_SIZE	(32 * 1024 * 1024)
+
+/* Number of bytes of data we initially reserve for each hash table bucket.  */
+#define DEFAULT_DATASIZE_PER_BUCKET 1024
+
 
 /* Global variables.  */
 extern struct database_dyn dbs[lastdb];
@@ -110,9 +123,6 @@ extern int nthreads;
 /* Maximum number of threads to use.  */
 extern int max_nthreads;
 
-/* Tables for which we cache data with uid.  */
-extern int secure_in_use; /* Is one of the above 1?  */
-
 /* User name to run server processes as.  */
 extern const char *server_user;
 
@@ -175,7 +185,7 @@ extern struct datahead *cache_search (request_type, void *key, size_t len,
 extern int cache_add (int type, const void *key, size_t len,
 		      struct datahead *packet, bool first,
 		      struct database_dyn *table, uid_t owner);
-extern void prune_cache (struct database_dyn *table, time_t now);
+extern void prune_cache (struct database_dyn *table, time_t now, int fd);
 
 /* pwdcache.c */
 extern void addpwbyname (struct database_dyn *db, int fd, request_header *req,
@@ -236,4 +246,14 @@ extern void gc (struct database_dyn *db);
 /* nscd_setup_thread.c */
 extern void setup_thread (struct database_dyn *db);
 
+
+/* Special version of TEMP_FAILURE_RETRY for functions returning error
+   values.  */
+#define TEMP_FAILURE_RETRY_VAL(expression) \
+  (__extension__							      \
+    ({ long int __result;						      \
+       do __result = (long int) (expression);				      \
+       while (__result == EINTR);					      \
+       __result; }))
+
 #endif /* nscd.h */
diff --git a/nscd/nscd.init b/nscd/nscd.init
index d5c1cb9ae3..1fba72f5c3 100644
--- a/nscd/nscd.init
+++ b/nscd/nscd.init
@@ -9,7 +9,18 @@
 #		slow naming services like NIS, NIS+, LDAP, or hesiod.
 # processname: /usr/sbin/nscd
 # config: /etc/nscd.conf
+# config: /etc/sysconfig/nscd
 #
+### BEGIN INIT INFO
+# Provides: nscd
+# Required-Start: $syslog
+# Default-Stop: 0 1 6
+# Short-Description: Starts the Name Switch Cache Daemon
+# Description:  This is a daemon which handles passwd and group lookups \
+#		for running programs and cache the results for the next \
+#		query.  You should start this daemon if you use \
+#		slow naming services like NIS, NIS+, LDAP, or hesiod.
+### END INIT INFO
 
 # Sanity checks.
 [ -f /etc/nscd.conf ] || exit 0
@@ -18,20 +29,8 @@
 # Source function library.
 . /etc/init.d/functions
 
-# nscd does not run on any kernel lower than 2.2.0 because of threading
-# problems, so we require that in first place.
-case $(uname -r) in
-    2.[2-9].*)
-	# this is okay
-	;;
-    [3-9]*)
-	# these are of course also okay
-	;;
-    *)
-	#this is not
-	exit 0
-	;;
-esac
+# Source an auxiliary options file if we have one, and pick up NSCD_OPTIONS.
+[ -r /etc/sysconfig/nscd ] && . /etc/sysconfig/nscd
 
 RETVAL=0
 prog=nscd
@@ -47,7 +46,7 @@ start () {
 #   	fi
 #   done
     echo -n $"Starting $prog: "
-    daemon /usr/sbin/nscd $secure
+    daemon /usr/sbin/nscd $secure $NSCD_OPTIONS
     RETVAL=$?
     echo
     [ $RETVAL -eq 0 ] && touch /var/lock/subsys/nscd
@@ -88,21 +87,23 @@ case "$1" in
 	RETVAL=$?
 	;;
     status)
-        status nscd
+	status nscd
 	RETVAL=$?
-        ;;
+	;;
     restart)
 	restart
 	RETVAL=$?
 	;;
-    condrestart)
+    try-restart | condrestart)
 	[ -e /var/lock/subsys/nscd ] && restart
 	RETVAL=$?
 	;;
-    reload)
-  	killproc /usr/sbin/nscd -HUP
+    force-reload | reload)
+    	echo -n $"Reloading $prog: "
+	killproc /usr/sbin/nscd -HUP
 	RETVAL=$?
-        ;;
+	echo
+	;;
     *)
 	echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
 	RETVAL=1
diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c
index d21f2fc501..2048eca886 100644
--- a/nscd/nscd_conf.c
+++ b/nscd/nscd_conf.c
@@ -1,24 +1,23 @@
-/* Copyright (c) 1998, 2000, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (c) 1998,2000,2003,2004,2005,2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <ctype.h>
 #include <errno.h>
+#include <error.h>
 #include <libintl.h>
 #include <malloc.h>
 #include <pwd.h>
@@ -45,6 +44,18 @@ const char *dbnames[lastdb] =
   [hstdb] = "hosts"
 };
 
+
+static int
+find_db (const char *name)
+{
+  for (int cnt = 0; cnt < lastdb; ++cnt)
+    if (strcmp (name, dbnames[cnt]) == 0)
+      return cnt;
+
+  error (0, 0, _("database %s is not supported\n"), name);
+  return -1;
+}
+
 int
 nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
 {
@@ -52,6 +63,7 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
   char *line, *cp, *entry, *arg1, *arg2;
   size_t len;
   int cnt;
+  const unsigned int initial_error_message_count = error_message_count;
 
   /* Open the configuration file.  */
   fp = fopen (fname, "r");
@@ -91,7 +103,7 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
       ++arg1;
       *cp = '\0';
       if (strlen (entry) == 0)
-	dbg_log (_("Parse error: %s"), line);
+	error (0, 0, _("Parse error: %s"), line);
       while (isspace (*arg1) && *arg1 != '\0')
 	++arg1;
       cp = arg1;
@@ -112,64 +124,49 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
 
       if (strcmp (entry, "positive-time-to-live") == 0)
 	{
-	  for (cnt = 0; cnt < lastdb; ++cnt)
-	    if (strcmp (arg1, dbnames[cnt]) == 0)
-	      {
-		dbs[cnt].postimeout = atol (arg2);
-		break;
-	      }
-	  if (cnt == lastdb)
-	    dbg_log ("database %s is not supported\n", arg1);
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    dbs[idx].postimeout = atol (arg2);
 	}
       else if (strcmp (entry, "negative-time-to-live") == 0)
 	{
-	  for (cnt = 0; cnt < lastdb; ++cnt)
-	    if (strcmp (arg1, dbnames[cnt]) == 0)
-	      {
-		dbs[cnt].negtimeout = atol (arg2);
-		break;
-	      }
-	  if (cnt == lastdb)
-	    dbg_log ("database %s is not supported\n", arg1);
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    dbs[idx].negtimeout = atol (arg2);
 	}
       else if (strcmp (entry, "suggested-size") == 0)
 	{
-	  for (cnt = 0; cnt < lastdb; ++cnt)
-	    if (strcmp (arg1, dbnames[cnt]) == 0)
-	      {
-		dbs[cnt].suggested_module = atol (arg2);
-		break;
-	      }
-	  if (cnt == lastdb)
-	    dbg_log ("database %s is not supported\n", arg1);
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    dbs[idx].suggested_module = atol (arg2);
 	}
       else if (strcmp (entry, "enable-cache") == 0)
 	{
-	  for (cnt = 0; cnt < lastdb; ++cnt)
-	    if (strcmp (arg1, dbnames[cnt]) == 0)
-	      {
-		if (strcmp (arg2, "no") == 0)
-		  dbs[cnt].enabled = 0;
-		else if (strcmp (arg2, "yes") == 0)
-		  dbs[cnt].enabled = 1;
-		break;
-	      }
-	  if (cnt == lastdb)
-	    dbg_log ("database %s is not supported\n", arg1);
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].enabled = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].enabled = 1;
+	    }
 	}
       else if (strcmp (entry, "check-files") == 0)
 	{
-	  for (cnt = 0; cnt < lastdb; ++cnt)
-	    if (strcmp (arg1, dbnames[cnt]) == 0)
-	      {
-		if (strcmp (arg2, "no") == 0)
-		  dbs[cnt].check_file = 0;
-		else if (strcmp (arg2, "yes") == 0)
-		  dbs[cnt].check_file = 1;
-		break;
-	      }
-	  if (cnt == lastdb)
-	    dbg_log ("database %s is not supported\n", arg1);
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].check_file = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].check_file = 1;
+	    }
+	}
+      else if (strcmp (entry, "max-db-size") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    dbs[idx].max_db_size = atol (arg2);
 	}
       else if (strcmp (entry, "logfile") == 0)
 	set_logfile (arg1);
@@ -191,14 +188,14 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
       else if (strcmp (entry, "server-user") == 0)
         {
           if (!arg1)
-            dbg_log (_("Must specify user name for server-user option"));
+            error (0, 0, _("Must specify user name for server-user option"));
           else
             server_user = xstrdup (arg1);
         }
       else if (strcmp (entry, "stat-user") == 0)
         {
           if (arg1 == NULL)
-            dbg_log (_("Must specify user name for stat-user option"));
+            error (0, 0, _("Must specify user name for stat-user option"));
           else
 	    {
 	      stat_user = xstrdup (arg1);
@@ -210,31 +207,25 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
         }
       else if (strcmp (entry, "persistent") == 0)
 	{
-	  for (cnt = 0; cnt < lastdb; ++cnt)
-	    if (strcmp (arg1, dbnames[cnt]) == 0)
-	      {
-		if (strcmp (arg2, "no") == 0)
-		  dbs[cnt].persistent = 0;
-		else if (strcmp (arg2, "yes") == 0)
-		  dbs[cnt].persistent = 1;
-		break;
-	      }
-	  if (cnt == lastdb)
-	    dbg_log ("database %s is not supported\n", arg1);
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].persistent = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].persistent = 1;
+	    }
 	}
       else if (strcmp (entry, "shared") == 0)
 	{
-	  for (cnt = 0; cnt < lastdb; ++cnt)
-	    if (strcmp (arg1, dbnames[cnt]) == 0)
-	      {
-		if (strcmp (arg2, "no") == 0)
-		  dbs[cnt].shared = 0;
-		else if (strcmp (arg2, "yes") == 0)
-		  dbs[cnt].shared = 1;
-		break;
-	      }
-	  if (cnt == lastdb)
-	    dbg_log ("database %s is not supported\n", arg1);
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].shared = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].shared = 1;
+	    }
 	}
       else if (strcmp (entry, "reload-count") == 0)
 	{
@@ -248,7 +239,7 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
 	      else if (count >= 0)
 	    reload_count = count;
 	      else
-		dbg_log (_("invalid value for 'reload-count': %u"), count);
+		error (0, 0, _("invalid value for 'reload-count': %u"), count);
 	    }
 	}
       else if (strcmp (entry, "paranoia") == 0)
@@ -263,10 +254,21 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
 	  if (arg1 != NULL)
 	    restart_interval = atol (arg1);
 	  else
-            dbg_log (_("Must specify value for restart-interval option"));
+            error (0, 0, _("Must specify value for restart-interval option"));
+	}
+      else if (strcmp (entry, "auto-propagate") == 0)
+	{
+	  int idx = find_db (arg1);
+	  if (idx >= 0)
+	    {
+	      if (strcmp (arg2, "no") == 0)
+		dbs[idx].propagate = 0;
+	      else if (strcmp (arg2, "yes") == 0)
+		dbs[idx].propagate = 1;
+	    }
 	}
       else
-	dbg_log (_("Unknown option: %s %s %s"), entry, arg1, arg2);
+	error (0, 0, _("Unknown option: %s %s %s"), entry, arg1, arg2);
     }
   while (!feof_unlocked (fp));
 
@@ -279,7 +281,7 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
       oldcwd = get_current_dir_name ();
       if (oldcwd == NULL)
 	{
-	  dbg_log (_("\
+	  error (0, 0, _("\
 cannot get current working directory: %s; disabling paranoia mode"),
 		   strerror (errno));
 	  paranoia = 0;
@@ -290,10 +292,26 @@ cannot get current working directory: %s; disabling paranoia mode"),
   if (max_nthreads < nthreads)
     max_nthreads = nthreads;
 
+  for (cnt = 0; cnt < lastdb; ++cnt)
+    {
+      size_t datasize = (sizeof (struct database_pers_head)
+			 + roundup (dbs[cnt].suggested_module
+				    * sizeof (ref_t), ALIGN)
+			 + (dbs[cnt].suggested_module
+			    * DEFAULT_DATASIZE_PER_BUCKET));
+      if (datasize > dbs[cnt].max_db_size)
+	{
+	  error (0, 0, _("maximum file size for %s database too small"),
+		   dbnames[cnt]);
+	  dbs[cnt].max_db_size = datasize;
+	}
+
+    }
+
   /* Free the buffer.  */
   free (line);
   /* Close configuration file.  */
   fclose (fp);
 
-  return 0;
+  return error_message_count != initial_error_message_count;
 }
diff --git a/nscd/nscd_getai.c b/nscd/nscd_getai.c
index 24b374b0dc..5df32dc6dc 100644
--- a/nscd/nscd_getai.c
+++ b/nscd/nscd_getai.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2004 Free Software Foundation, Inc.
+/* Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
 
@@ -34,7 +34,7 @@ extern int __nss_not_use_nscd_hosts;
 
 
 /* We use the mapping from nscd_gethst.  */
-libc_locked_map_ptr (extern, __hst_map_handle);
+libc_locked_map_ptr (extern, __hst_map_handle) attribute_hidden;
 
 
 int
@@ -42,6 +42,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
 {
   size_t keylen = strlen (key) + 1;
   int gc_cycle;
+  int nretries = 0;
 
   /* If the mapping is available, try to search there instead of
      communicating with the nscd.  */
@@ -50,49 +51,53 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
 			       &gc_cycle);
 
  retry:;
-  const ai_response_header *ai_resp = NULL;
   struct nscd_ai_result *resultbuf = NULL;
   const char *recend = (const char *) ~UINTMAX_C (0);
   char *respdata = NULL;
   int retval = -1;
   int sock = -1;
+  ai_response_header ai_resp;
 
   if (mapped != NO_MAPPING)
     {
-      const struct datahead *found = __nscd_cache_search (GETAI, key, keylen,
-							  mapped);
+      struct datahead *found = __nscd_cache_search (GETAI, key, keylen,
+						    mapped);
       if (found != NULL)
 	{
-	  ai_resp = &found->data[0].aidata;
-	  respdata = (char *) (ai_resp + 1);
+	  respdata = (char *) (&found->data[0].aidata + 1);
+	  ai_resp = found->data[0].aidata;
 	  recend = (const char *) found->data + found->recsize;
+	  /* Now check if we can trust ai_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
 	}
     }
 
   /* If we do not have the cache mapped, try to get the data over the
      socket.  */
-  ai_response_header ai_resp_mem;
-  if (ai_resp == NULL)
+  if (respdata == NULL)
     {
-      sock = __nscd_open_socket (key, keylen, GETAI, &ai_resp_mem,
-				 sizeof (ai_resp_mem));
+      sock = __nscd_open_socket (key, keylen, GETAI, &ai_resp,
+				 sizeof (ai_resp));
       if (sock == -1)
 	{
-	  /* nscd not running or wrong version or hosts caching disabled.  */
+	  /* nscd not running or wrong version.  */
 	  __nss_not_use_nscd_hosts = 1;
 	  goto out;
 	}
-
-      ai_resp = &ai_resp_mem;
     }
 
-  if (ai_resp->found == 1)
+  if (ai_resp.found == 1)
     {
-      size_t datalen = ai_resp->naddrs + ai_resp->addrslen + ai_resp->canonlen;
+      size_t datalen = ai_resp.naddrs + ai_resp.addrslen + ai_resp.canonlen;
 
-      /* This check is really only affects the case where the data
+      /* This check really only affects the case where the data
 	 comes from the mapped cache.  */
-      if ((char *) (ai_resp + 1) + datalen > recend)
+      if (respdata + datalen > recend)
 	{
 	  assert (sock == -1);
 	  goto out;
@@ -108,10 +113,10 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
 	}
 
       /* Set up the data structure, including pointers.  */
-      resultbuf->naddrs = ai_resp->naddrs;
+      resultbuf->naddrs = ai_resp.naddrs;
       resultbuf->addrs = (char *) (resultbuf + 1);
-      resultbuf->family = (uint8_t *) (resultbuf->addrs + ai_resp->addrslen);
-      if (ai_resp->canonlen != 0)
+      resultbuf->family = (uint8_t *) (resultbuf->addrs + ai_resp.addrslen);
+      if (ai_resp.canonlen != 0)
 	resultbuf->canon = (char *) (resultbuf->family + resultbuf->naddrs);
       else
 	resultbuf->canon = NULL;
@@ -119,8 +124,7 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
       if (respdata == NULL)
 	{
 	  /* Read the data from the socket.  */
-	  if ((size_t) TEMP_FAILURE_RETRY (__read (sock, resultbuf + 1,
-						   datalen)) == datalen)
+	  if ((size_t) __readall (sock, resultbuf + 1, datalen) == datalen)
 	    {
 	      retval = 0;
 	      *result = resultbuf;
@@ -138,10 +142,13 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
 
 	  /* Try to detect corrupt databases.  */
 	  if (resultbuf->canon != NULL
-	      && resultbuf->canon[ai_resp->canonlen - 1] != '\0')
+	      && resultbuf->canon[ai_resp.canonlen - 1] != '\0')
 	    /* We cannot use the database.  */
 	    {
-	      free (resultbuf);
+	      if (mapped->head->gc_cycle != gc_cycle)
+		retval = -2;
+	      else
+		free (resultbuf);
 	      goto out_close;
 	    }
 
@@ -151,8 +158,15 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
     }
   else
     {
+      if (__builtin_expect (ai_resp.found == -1, 0))
+	{
+	  /* The daemon does not cache this database.  */
+	  __nss_not_use_nscd_hosts = 1;
+	  goto out_close;
+	}
+
       /* Store the error number.  */
-      *h_errnop = ai_resp->error;
+      *h_errnop = ai_resp.error;
 
       /* The `errno' to some value != ERANGE.  */
       __set_errno (ENOENT);
@@ -164,22 +178,25 @@ __nscd_getai (const char *key, struct nscd_ai_result **result, int *h_errnop)
   if (sock != -1)
     close_not_cancel_no_status (sock);
  out:
-  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0 && retval != -1)
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
     {
       /* When we come here this means there has been a GC cycle while we
 	 were looking for the data.  This means the data might have been
 	 inconsistent.  Retry if possible.  */
-      if ((gc_cycle & 1) != 0)
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
 	{
 	  /* nscd is just running gc now.  Disable using the mapping.  */
-	  __nscd_unmap (mapped);
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
 	  mapped = NO_MAPPING;
 	}
 
-      *result = NULL;
-      free (resultbuf);
-
-      goto retry;
+      if (retval != -1)
+	{
+	  *result = NULL;
+	  free (resultbuf);
+	  goto retry;
+	}
     }
 
   return retval;
diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c
index 282912db3e..fc036f2888 100644
--- a/nscd/nscd_getgr_r.c
+++ b/nscd/nscd_getgr_r.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004
+/* Copyright (C) 1998-2000, 2002-2005, 2006, 2007
    Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998.
@@ -67,7 +67,7 @@ __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
 }
 
 
-libc_locked_map_ptr (,__gr_map_handle);
+libc_locked_map_ptr (,__gr_map_handle) attribute_hidden;
 /* Note that we only free the structure if necessary.  The memory
    mapping is not removed since it is not visible to the malloc
    handling.  */
@@ -89,6 +89,7 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
 	      struct group **result)
 {
   int gc_cycle;
+  int nretries = 0;
   const uint32_t *len = NULL;
   size_t lensize = 0;
 
@@ -98,55 +99,59 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
 						       &__gr_map_handle,
 						       &gc_cycle);
  retry:;
-  const gr_response_header *gr_resp = NULL;
   const char *gr_name = NULL;
   size_t gr_name_len = 0;
   int retval = -1;
   const char *recend = (const char *) ~UINTMAX_C (0);
+  gr_response_header gr_resp;
 
   if (mapped != NO_MAPPING)
     {
-      const struct datahead *found = __nscd_cache_search (type, key, keylen,
-							  mapped);
+      struct datahead *found = __nscd_cache_search (type, key, keylen, mapped);
       if (found != NULL)
 	{
-	  gr_resp = &found->data[0].grdata;
-	  len = (const uint32_t *) (gr_resp + 1);
-	  /* The alignment is always sufficient.  */
-	  assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
+	  len = (const uint32_t *) (&found->data[0].grdata + 1);
+	  gr_resp = found->data[0].grdata;
 	  gr_name = ((const char *) len
-		     + gr_resp->gr_mem_cnt * sizeof (uint32_t));
-	  gr_name_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len;
+		     + gr_resp.gr_mem_cnt * sizeof (uint32_t));
+	  gr_name_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
 	  recend = (const char *) found->data + found->recsize;
+	  /* Now check if we can trust gr_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+
+	  /* The alignment is always sufficient, unless GC is in progress.  */
+	  assert (((uintptr_t) len & (__alignof__ (*len) - 1)) == 0);
 	}
     }
 
-  gr_response_header gr_resp_mem;
   int sock = -1;
-  if (gr_resp == NULL)
+  if (gr_name == NULL)
     {
-      sock = __nscd_open_socket (key, keylen, type, &gr_resp_mem,
-				 sizeof (gr_resp_mem));
+      sock = __nscd_open_socket (key, keylen, type, &gr_resp,
+				 sizeof (gr_resp));
       if (sock == -1)
 	{
 	  __nss_not_use_nscd_group = 1;
 	  goto out;
 	}
-
-      gr_resp = &gr_resp_mem;
     }
 
   /* No value found so far.  */
   *result = NULL;
 
-  if (__builtin_expect (gr_resp->found == -1, 0))
+  if (__builtin_expect (gr_resp.found == -1, 0))
     {
       /* The daemon does not cache this database.  */
       __nss_not_use_nscd_group = 1;
       goto out_close;
     }
 
-  if (gr_resp->found == 1)
+  if (gr_resp.found == 1)
     {
       struct iovec vec[2];
       char *p = buffer;
@@ -158,8 +163,8 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
 	 align the pointer.  */
       align = ((__alignof__ (char *) - (p - ((char *) 0)))
 	       & (__alignof__ (char *) - 1));
-      total_len = (align + (1 + gr_resp->gr_mem_cnt) * sizeof (char *)
-		   + gr_resp->gr_name_len + gr_resp->gr_passwd_len);
+      total_len = (align + (1 + gr_resp.gr_mem_cnt) * sizeof (char *)
+		   + gr_resp.gr_name_len + gr_resp.gr_passwd_len);
       if (__builtin_expect (buflen < total_len, 0))
 	{
 	no_room:
@@ -171,16 +176,16 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
 
       p += align;
       resultbuf->gr_mem = (char **) p;
-      p += (1 + gr_resp->gr_mem_cnt) * sizeof (char *);
+      p += (1 + gr_resp.gr_mem_cnt) * sizeof (char *);
 
       /* Set pointers for strings.  */
       resultbuf->gr_name = p;
-      p += gr_resp->gr_name_len;
+      p += gr_resp.gr_name_len;
       resultbuf->gr_passwd = p;
-      p += gr_resp->gr_passwd_len;
+      p += gr_resp.gr_passwd_len;
 
       /* Fill in what we know now.  */
-      resultbuf->gr_gid = gr_resp->gr_gid;
+      resultbuf->gr_gid = gr_resp.gr_gid;
 
       /* Read the length information, group name, and password.  */
       if (gr_name == NULL)
@@ -188,21 +193,21 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
 	  /* Allocate array to store lengths.  */
 	  if (lensize == 0)
 	    {
-	      lensize = gr_resp->gr_mem_cnt * sizeof (uint32_t);
+	      lensize = gr_resp.gr_mem_cnt * sizeof (uint32_t);
 	      len = (uint32_t *) alloca (lensize);
 	    }
-	  else if (gr_resp->gr_mem_cnt * sizeof (uint32_t) > lensize)
+	  else if (gr_resp.gr_mem_cnt * sizeof (uint32_t) > lensize)
 	    len = extend_alloca (len, lensize,
-				 gr_resp->gr_mem_cnt * sizeof (uint32_t));
+				 gr_resp.gr_mem_cnt * sizeof (uint32_t));
 
 	  vec[0].iov_base = (void *) len;
-	  vec[0].iov_len = gr_resp->gr_mem_cnt * sizeof (uint32_t);
+	  vec[0].iov_len = gr_resp.gr_mem_cnt * sizeof (uint32_t);
 	  vec[1].iov_base = resultbuf->gr_name;
-	  vec[1].iov_len = gr_resp->gr_name_len + gr_resp->gr_passwd_len;
+	  vec[1].iov_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
 	  total_len = vec[0].iov_len + vec[1].iov_len;
 
 	  /* Get this data.  */
-	  size_t n = TEMP_FAILURE_RETRY (__readv (sock, vec, 2));
+	  size_t n = __readvall (sock, vec, 2);
 	  if (__builtin_expect (n != total_len, 0))
 	    goto out_close;
 	}
@@ -210,14 +215,14 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
 	/* We already have the data.  Just copy the group name and
 	   password.  */
 	memcpy (resultbuf->gr_name, gr_name,
-		gr_resp->gr_name_len + gr_resp->gr_passwd_len);
+		gr_resp.gr_name_len + gr_resp.gr_passwd_len);
 
       /* Clear the terminating entry.  */
-      resultbuf->gr_mem[gr_resp->gr_mem_cnt] = NULL;
+      resultbuf->gr_mem[gr_resp.gr_mem_cnt] = NULL;
 
       /* Prepare reading the group members.  */
       total_len = 0;
-      for (cnt = 0; cnt < gr_resp->gr_mem_cnt; ++cnt)
+      for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
 	{
 	  resultbuf->gr_mem[cnt] = p;
 	  total_len += len[cnt];
@@ -225,15 +230,30 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
 	}
 
       if (__builtin_expect (gr_name + gr_name_len + total_len > recend, 0))
-	goto out_close;
+	{
+	  /* len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
+	    retval = -2;
+	  goto out_close;
+	}
       if (__builtin_expect (total_len > buflen, 0))
-	goto no_room;
+	{
+	  /* len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (gr_name != NULL && mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out_close;
+	    }
+	  else
+	    goto no_room;
+	}
 
       retval = 0;
       if (gr_name == NULL)
 	{
-	  size_t n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0],
-						 total_len));
+	  size_t n = __readall (sock, resultbuf->gr_mem[0], total_len);
 	  if (__builtin_expect (n != total_len, 0))
 	    {
 	      /* The `errno' to some value != ERANGE.  */
@@ -250,14 +270,14 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
 
 	  /* Try to detect corrupt databases.  */
 	  if (resultbuf->gr_name[gr_name_len - 1] != '\0'
-	      || resultbuf->gr_passwd[gr_resp->gr_passwd_len - 1] != '\0'
-	      || ({for (cnt = 0; cnt < gr_resp->gr_mem_cnt; ++cnt)
+	      || resultbuf->gr_passwd[gr_resp.gr_passwd_len - 1] != '\0'
+	      || ({for (cnt = 0; cnt < gr_resp.gr_mem_cnt; ++cnt)
 		     if (resultbuf->gr_mem[cnt][len[cnt] - 1] != '\0')
 		       break;
-	  	   cnt < gr_resp->gr_mem_cnt; }))
+	  	   cnt < gr_resp.gr_mem_cnt; }))
 	    {
 	      /* We cannot use the database.  */
-	      retval = -1;
+	      retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
 	      goto out_close;
 	    }
 
@@ -276,19 +296,21 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
   if (sock != -1)
     close_not_cancel_no_status (sock);
  out:
-  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0 && retval != -1)
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
     {
       /* When we come here this means there has been a GC cycle while we
 	 were looking for the data.  This means the data might have been
 	 inconsistent.  Retry if possible.  */
-      if ((gc_cycle & 1) != 0)
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
 	{
 	  /* nscd is just running gc now.  Disable using the mapping.  */
-	  __nscd_unmap (mapped);
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
 	  mapped = NO_MAPPING;
 	}
 
-      goto retry;
+      if (retval != -1)
+	goto retry;
     }
 
   return retval;
diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c
index 5d9d569107..90e1815bdd 100644
--- a/nscd/nscd_gethst_r.c
+++ b/nscd/nscd_gethst_r.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1998-2002, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (C) 1998-2005, 2006, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
@@ -87,7 +87,7 @@ __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
 }
 
 
-libc_locked_map_ptr (, __hst_map_handle);
+libc_locked_map_ptr (, __hst_map_handle) attribute_hidden;
 /* Note that we only free the structure if necessary.  The memory
    mapping is not removed since it is not visible to the malloc
    handling.  */
@@ -118,7 +118,6 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
 			       &gc_cycle);
 
  retry:;
-  const hst_response_header *hst_resp = NULL;
   const char *h_name = NULL;
   const uint32_t *aliases_len = NULL;
   const char *addr_list = NULL;
@@ -126,18 +125,27 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
   int retval = -1;
   const char *recend = (const char *) ~UINTMAX_C (0);
   int sock = -1;
+  hst_response_header hst_resp;
   if (mapped != NO_MAPPING)
     {
-      const struct datahead *found = __nscd_cache_search (type, key, keylen,
-							  mapped);
+      /* No const qualifier, as it can change during garbage collection.  */
+      struct datahead *found = __nscd_cache_search (type, key, keylen, mapped);
       if (found != NULL)
 	{
-	  hst_resp = &found->data[0].hstdata;
-	  h_name = (char *) (hst_resp + 1);
-	  aliases_len = (uint32_t *) (h_name + hst_resp->h_name_len);
+	  h_name = (char *) (&found->data[0].hstdata + 1);
+	  hst_resp = found->data[0].hstdata;
+	  aliases_len = (uint32_t *) (h_name + hst_resp.h_name_len);
 	  addr_list = ((char *) aliases_len
-		       + hst_resp->h_aliases_cnt * sizeof (uint32_t));
-	  addr_list_len = hst_resp->h_addr_list_cnt * INADDRSZ;
+		       + hst_resp.h_aliases_cnt * sizeof (uint32_t));
+	  addr_list_len = hst_resp.h_addr_list_cnt * INADDRSZ;
+	  recend = (const char *) found->data + found->recsize;
+	  /* Now check if we can trust hst_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
 
 #ifndef _STRING_ARCH_unaligned
 	  /* The aliases_len array in the mapped database might very
@@ -147,51 +155,47 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
 	  if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
 	      != 0)
 	    {
-	      uint32_t *tmp = alloca (hst_resp->h_aliases_cnt
+	      uint32_t *tmp = alloca (hst_resp.h_aliases_cnt
 				      * sizeof (uint32_t));
 	      aliases_len = memcpy (tmp, aliases_len,
-				    hst_resp->h_aliases_cnt
+				    hst_resp.h_aliases_cnt
 				    * sizeof (uint32_t));
 	    }
 #endif
 	  if (type != GETHOSTBYADDR && type != GETHOSTBYNAME)
 	    {
-	      if (hst_resp->h_length == INADDRSZ)
+	      if (hst_resp.h_length == INADDRSZ)
 		addr_list += addr_list_len;
-	      addr_list_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+	      addr_list_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
 	    }
-	  recend = (const char *) found->data + found->recsize;
 	  if (__builtin_expect ((const char *) addr_list + addr_list_len
 				> recend, 0))
-	    goto out_close;
+	    goto out;
 	}
     }
 
-  hst_response_header hst_resp_mem;
-  if (hst_resp == NULL)
+  if (h_name == NULL)
     {
-      sock = __nscd_open_socket (key, keylen, type, &hst_resp_mem,
-				 sizeof (hst_resp_mem));
+      sock = __nscd_open_socket (key, keylen, type, &hst_resp,
+				 sizeof (hst_resp));
       if (sock == -1)
 	{
 	  __nss_not_use_nscd_hosts = 1;
-	  goto out;;
+	  goto out;
 	}
-
-      hst_resp = &hst_resp_mem;
     }
 
   /* No value found so far.  */
   *result = NULL;
 
-  if (__builtin_expect (hst_resp->found == -1, 0))
+  if (__builtin_expect (hst_resp.found == -1, 0))
     {
       /* The daemon does not cache this database.  */
       __nss_not_use_nscd_hosts = 1;
       goto out_close;
     }
 
-  if (hst_resp->found == 1)
+  if (hst_resp.found == 1)
     {
       struct iovec vec[4];
       char *cp = buffer;
@@ -207,17 +211,18 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
 	 align the pointer and the base of the h_addr_list pointers.  */
       align1 = ((__alignof__ (char *) - (cp - ((char *) 0)))
 		& (__alignof__ (char *) - 1));
-      align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp->h_name_len)
+      align2 = ((__alignof__ (char *) - ((cp + align1 + hst_resp.h_name_len)
 					 - ((char *) 0)))
 		& (__alignof__ (char *) - 1));
-      if (buflen < (align1 + hst_resp->h_name_len + align2
-		    + ((hst_resp->h_aliases_cnt + hst_resp->h_addr_list_cnt
+      if (buflen < (align1 + hst_resp.h_name_len + align2
+		    + ((hst_resp.h_aliases_cnt + hst_resp.h_addr_list_cnt
 			+ 2)
 		       * sizeof (char *))
-		    + hst_resp->h_addr_list_cnt * (type == AF_INET
-						   ? INADDRSZ : IN6ADDRSZ)))
+		    + hst_resp.h_addr_list_cnt * (type == AF_INET
+						  ? INADDRSZ : IN6ADDRSZ)))
 	{
 	no_room:
+	  *h_errnop = NETDB_INTERNAL;
 	  __set_errno (ERANGE);
 	  retval = ERANGE;
 	  goto out_close;
@@ -226,12 +231,12 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
 
       /* Prepare the result as far as we can.  */
       resultbuf->h_aliases = (char **) cp;
-      cp += (hst_resp->h_aliases_cnt + 1) * sizeof (char *);
+      cp += (hst_resp.h_aliases_cnt + 1) * sizeof (char *);
       resultbuf->h_addr_list = (char **) cp;
-      cp += (hst_resp->h_addr_list_cnt + 1) * sizeof (char *);
+      cp += (hst_resp.h_addr_list_cnt + 1) * sizeof (char *);
 
       resultbuf->h_name = cp;
-      cp += hst_resp->h_name_len + align2;
+      cp += hst_resp.h_name_len + align2;
 
       if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
 	{
@@ -243,7 +248,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
 	  resultbuf->h_addrtype = AF_INET6;
 	  resultbuf->h_length = IN6ADDRSZ;
 	}
-      for (cnt = 0; cnt < hst_resp->h_addr_list_cnt; ++cnt)
+      for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
 	{
 	  resultbuf->h_addr_list[cnt] = cp;
 	  cp += resultbuf->h_length;
@@ -253,64 +258,63 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
       if (h_name == NULL)
 	{
 	  vec[0].iov_base = resultbuf->h_name;
-	  vec[0].iov_len = hst_resp->h_name_len;
-	  total_len = hst_resp->h_name_len;
+	  vec[0].iov_len = hst_resp.h_name_len;
+	  total_len = hst_resp.h_name_len;
 	  n = 1;
 
-	  if (hst_resp->h_aliases_cnt > 0)
+	  if (hst_resp.h_aliases_cnt > 0)
 	    {
-	      aliases_len = alloca (hst_resp->h_aliases_cnt
+	      aliases_len = alloca (hst_resp.h_aliases_cnt
 				    * sizeof (uint32_t));
 	      vec[n].iov_base = (void *) aliases_len;
-	      vec[n].iov_len = hst_resp->h_aliases_cnt * sizeof (uint32_t);
+	      vec[n].iov_len = hst_resp.h_aliases_cnt * sizeof (uint32_t);
 
-	      total_len += hst_resp->h_aliases_cnt * sizeof (uint32_t);
+	      total_len += hst_resp.h_aliases_cnt * sizeof (uint32_t);
 	      ++n;
 	    }
 
 	  if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
 	    {
 	      vec[n].iov_base = resultbuf->h_addr_list[0];
-	      vec[n].iov_len = hst_resp->h_addr_list_cnt * INADDRSZ;
+	      vec[n].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
 
-	      total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
+	      total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
 
 	      ++n;
 	    }
 	  else
 	    {
-	      if (hst_resp->h_length == INADDRSZ)
+	      if (hst_resp.h_length == INADDRSZ)
 		{
-		  ignore = alloca (hst_resp->h_addr_list_cnt * INADDRSZ);
+		  ignore = alloca (hst_resp.h_addr_list_cnt * INADDRSZ);
 		  vec[n].iov_base = ignore;
-		  vec[n].iov_len = hst_resp->h_addr_list_cnt * INADDRSZ;
+		  vec[n].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
 
-		  total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
+		  total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
 
 		  ++n;
 		}
 
 	      vec[n].iov_base = resultbuf->h_addr_list[0];
-	      vec[n].iov_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+	      vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
 
-	      total_len += hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+	      total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
 
 	      ++n;
 	    }
 
-	  if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n))
-	      != total_len)
+	  if ((size_t) __readvall (sock, vec, n) != total_len)
 	    goto out_close;
 	}
       else
 	{
-	  memcpy (resultbuf->h_name, h_name, hst_resp->h_name_len);
+	  memcpy (resultbuf->h_name, h_name, hst_resp.h_name_len);
 	  memcpy (resultbuf->h_addr_list[0], addr_list, addr_list_len);
 	}
 
       /*  Now we also can read the aliases.  */
       total_len = 0;
-      for (cnt = 0; cnt < hst_resp->h_aliases_cnt; ++cnt)
+      for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
 	{
 	  resultbuf->h_aliases[cnt] = cp;
 	  cp += aliases_len[cnt];
@@ -320,17 +324,32 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
 
       if (__builtin_expect ((const char *) addr_list + addr_list_len
 			    + total_len > recend, 0))
-	goto out_close;
+	{
+	  /* aliases_len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
+	    retval = -2;
+	  goto out_close;
+	}
       /* See whether this would exceed the buffer capacity.  */
       if (__builtin_expect (cp > buffer + buflen, 0))
-	goto no_room;
+	{
+	  /* aliases_len array might contain garbage during nscd GC cycle,
+	     retry rather than fail in that case.  */
+	  if (addr_list != NULL && mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out_close;
+	    }
+	  goto no_room;
+	}
 
       /* And finally read the aliases.  */
       if (addr_list == NULL)
 	{
-	  if ((size_t) TEMP_FAILURE_RETRY (__read (sock,
-						   resultbuf->h_aliases[0],
-						   total_len)) == total_len)
+	  if (total_len == 0
+	      || ((size_t) __readall (sock, resultbuf->h_aliases[0], total_len)
+		  == total_len))
 	    {
 	      retval = 0;
 	      *result = resultbuf;
@@ -342,14 +361,18 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
 		  (const char *) addr_list + addr_list_len, total_len);
 
 	  /* Try to detect corrupt databases.  */
-	  if (resultbuf->h_name[hst_resp->h_name_len - 1] != '\0'
-	      || ({for (cnt = 0; cnt < hst_resp->h_aliases_cnt; ++cnt)
+	  if (resultbuf->h_name[hst_resp.h_name_len - 1] != '\0'
+	      || ({for (cnt = 0; cnt < hst_resp.h_aliases_cnt; ++cnt)
 		     if (resultbuf->h_aliases[cnt][aliases_len[cnt] - 1]
 			 != '\0')
 		       break;
-		   cnt < hst_resp->h_aliases_cnt; }))
-	    /* We cannot use the database.  */
-	    goto out_close;
+		   cnt < hst_resp.h_aliases_cnt; }))
+	    {
+	      /* We cannot use the database.  */
+	      if (mapped->head->gc_cycle != gc_cycle)
+		retval = -2;
+	      goto out_close;
+	    }
 
 	  retval = 0;
 	  *result = resultbuf;
@@ -358,7 +381,7 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
   else
     {
       /* Store the error number.  */
-      *h_errnop = hst_resp->error;
+      *h_errnop = hst_resp.error;
 
       /* The `errno' to some value != ERANGE.  */
       __set_errno (ENOENT);
@@ -370,19 +393,21 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
   if (sock != -1)
     close_not_cancel_no_status (sock);
  out:
-  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0 && retval != -1)
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
     {
       /* When we come here this means there has been a GC cycle while we
 	 were looking for the data.  This means the data might have been
 	 inconsistent.  Retry if possible.  */
-      if ((gc_cycle & 1) != 0 || ++nretries == 5)
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
 	{
 	  /* nscd is just running gc now.  Disable using the mapping.  */
-	  __nscd_unmap (mapped);
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
 	  mapped = NO_MAPPING;
 	}
 
-      goto retry;
+      if (retval != -1)
+	goto retry;
     }
 
   return retval;
diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c
index fe5fb43ca1..b84baa1a66 100644
--- a/nscd/nscd_getpw_r.c
+++ b/nscd/nscd_getpw_r.c
@@ -1,4 +1,5 @@
-/* Copyright (C) 1998, 1999, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (C) 1998, 1999, 2003, 2004, 2005, 2007
+   Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@uni-paderborn.de>, 1998.
 
@@ -88,76 +89,81 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
 	      struct passwd **result)
 {
   int gc_cycle;
+  int nretries = 0;
+
   /* If the mapping is available, try to search there instead of
      communicating with the nscd.  */
   struct mapped_database *mapped;
   mapped = __nscd_get_map_ref (GETFDPW, "passwd", &map_handle, &gc_cycle);
 
  retry:;
-  const pw_response_header *pw_resp = NULL;
   const char *pw_name = NULL;
   int retval = -1;
   const char *recend = (const char *) ~UINTMAX_C (0);
+  pw_response_header pw_resp;
 
   if (mapped != NO_MAPPING)
     {
-      const struct datahead *found = __nscd_cache_search (type, key, keylen,
-							  mapped);
+      struct datahead *found = __nscd_cache_search (type, key, keylen, mapped);
       if (found != NULL)
 	{
-	  pw_resp = &found->data[0].pwdata;
-	  pw_name = (const char *) (pw_resp + 1);
+	  pw_name = (const char *) (&found->data[0].pwdata + 1);
+	  pw_resp = found->data[0].pwdata;
 	  recend = (const char *) found->data + found->recsize;
+	  /* Now check if we can trust pw_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
 	}
     }
 
-  pw_response_header pw_resp_mem;
   int sock = -1;
-  if (pw_resp == NULL)
+  if (pw_name == NULL)
     {
-      sock = __nscd_open_socket (key, keylen, type, &pw_resp_mem,
-				 sizeof (pw_resp_mem));
+      sock = __nscd_open_socket (key, keylen, type, &pw_resp,
+				 sizeof (pw_resp));
       if (sock == -1)
 	{
 	  __nss_not_use_nscd_passwd = 1;
 	  goto out;
 	}
-
-      pw_resp = &pw_resp_mem;
     }
 
   /* No value found so far.  */
   *result = NULL;
 
-  if (__builtin_expect (pw_resp->found == -1, 0))
+  if (__builtin_expect (pw_resp.found == -1, 0))
     {
       /* The daemon does not cache this database.  */
       __nss_not_use_nscd_passwd = 1;
       goto out_close;
     }
 
-  if (pw_resp->found == 1)
+  if (pw_resp.found == 1)
     {
       /* Set the information we already have.  */
-      resultbuf->pw_uid = pw_resp->pw_uid;
-      resultbuf->pw_gid = pw_resp->pw_gid;
+      resultbuf->pw_uid = pw_resp.pw_uid;
+      resultbuf->pw_gid = pw_resp.pw_gid;
 
       char *p = buffer;
       /* get pw_name */
       resultbuf->pw_name = p;
-      p += pw_resp->pw_name_len;
+      p += pw_resp.pw_name_len;
       /* get pw_passwd */
       resultbuf->pw_passwd = p;
-      p += pw_resp->pw_passwd_len;
+      p += pw_resp.pw_passwd_len;
       /* get pw_gecos */
       resultbuf->pw_gecos = p;
-      p += pw_resp->pw_gecos_len;
+      p += pw_resp.pw_gecos_len;
       /* get pw_dir */
       resultbuf->pw_dir = p;
-      p += pw_resp->pw_dir_len;
+      p += pw_resp.pw_dir_len;
       /* get pw_pshell */
       resultbuf->pw_shell = p;
-      p += pw_resp->pw_shell_len;
+      p += pw_resp.pw_shell_len;
 
       ssize_t total = p - buffer;
       if (__builtin_expect (pw_name + total > recend, 0))
@@ -172,7 +178,7 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
       retval = 0;
       if (pw_name == NULL)
 	{
-	  ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
+	  ssize_t nbytes = __readall (sock, buffer, total);
 
 	  if (__builtin_expect (nbytes != total, 0))
 	    {
@@ -189,14 +195,14 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
 	  memcpy (resultbuf->pw_name, pw_name, total);
 
 	  /* Try to detect corrupt databases.  */
-	  if (resultbuf->pw_name[pw_resp->pw_name_len - 1] != '\0'
-	      || resultbuf->pw_passwd[pw_resp->pw_passwd_len - 1] != '\0'
-	      || resultbuf->pw_gecos[pw_resp->pw_gecos_len - 1] != '\0'
-	      || resultbuf->pw_dir[pw_resp->pw_dir_len - 1] != '\0'
-	      || resultbuf->pw_shell[pw_resp->pw_shell_len - 1] != '\0')
+	  if (resultbuf->pw_name[pw_resp.pw_name_len - 1] != '\0'
+	      || resultbuf->pw_passwd[pw_resp.pw_passwd_len - 1] != '\0'
+	      || resultbuf->pw_gecos[pw_resp.pw_gecos_len - 1] != '\0'
+	      || resultbuf->pw_dir[pw_resp.pw_dir_len - 1] != '\0'
+	      || resultbuf->pw_shell[pw_resp.pw_shell_len - 1] != '\0')
 	    {
 	      /* We cannot use the database.  */
-	      retval = -1;
+	      retval = mapped->head->gc_cycle != gc_cycle ? -2 : -1;
 	      goto out_close;
 	    }
 
@@ -215,21 +221,21 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
   if (sock != -1)
     close_not_cancel_no_status (sock);
  out:
-  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0 && retval != -1)
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
     {
       /* When we come here this means there has been a GC cycle while we
 	 were looking for the data.  This means the data might have been
 	 inconsistent.  Retry if possible.  */
-      if ((gc_cycle & 1) != 0)
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
 	{
 	  /* nscd is just running gc now.  Disable using the mapping.  */
-	  __nscd_unmap (mapped);
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
 	  mapped = NO_MAPPING;
 	}
 
-      free (resultbuf);
-
-      goto retry;
+      if (retval != -1)
+	goto retry;
     }
 
   return retval;
diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
index 0e16cb8aeb..71ea53e19d 100644
--- a/nscd/nscd_helper.c
+++ b/nscd/nscd_helper.c
@@ -1,4 +1,5 @@
-/* Copyright (C) 1998-2002, 2003, 2004 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,11 +22,14 @@
 #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>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <sys/uio.h>
 #include <sys/un.h>
 #include <not-cancel.h>
@@ -34,6 +38,64 @@
 #include "nscd-client.h"
 
 
+ssize_t
+__readall (int fd, void *buf, size_t len)
+{
+  size_t n = len;
+  ssize_t ret;
+  do
+    {
+      ret = TEMP_FAILURE_RETRY (__read (fd, buf, n));
+      if (ret <= 0)
+	break;
+      buf = (char *) buf + ret;
+      n -= ret;
+    }
+  while (n > 0);
+  return ret < 0 ? ret : len - n;
+}
+
+
+ssize_t
+__readvall (int fd, const struct iovec *iov, int iovcnt)
+{
+  ssize_t ret = TEMP_FAILURE_RETRY (__readv (fd, iov, iovcnt));
+  if (ret <= 0)
+    return ret;
+
+  size_t total = 0;
+  for (int i = 0; i < iovcnt; ++i)
+    total += iov[i].iov_len;
+
+  if (ret < total)
+    {
+      struct iovec iov_buf[iovcnt];
+      ssize_t r = ret;
+
+      struct iovec *iovp = memcpy (iov_buf, iov, iovcnt * sizeof (*iov));
+      do
+	{
+	  while (iovp->iov_len <= r)
+	    {
+	      r -= iovp->iov_len;
+	      --iovcnt;
+	      ++iovp;
+	    }
+	  iovp->iov_base = (char *) iovp->iov_base + r;
+	  iovp->iov_len -= r;
+	  r = TEMP_FAILURE_RETRY (__readv (fd, iovp, iovcnt));
+	  if (r <= 0)
+	    break;
+	  ret += r;
+	}
+      while (ret < total);
+      if (r < 0)
+	ret = r;
+    }
+  return ret;
+}
+
+
 static int
 open_socket (void)
 {
@@ -77,6 +139,36 @@ __nscd_unmap (struct mapped_database *mapped)
 }
 
 
+static int
+wait_on_socket (int sock)
+{
+  struct pollfd fds[1];
+  fds[0].fd = sock;
+  fds[0].events = POLLIN | POLLERR | POLLHUP;
+  int n = __poll (fds, 1, 5 * 1000);
+  if (n == -1 && __builtin_expect (errno == EINTR, 0))
+    {
+      /* Handle the case where the poll() call is interrupted by a
+	 signal.  We cannot just use TEMP_FAILURE_RETRY since it might
+	 lead to infinite loops.  */
+      struct timeval now;
+      (void) __gettimeofday (&now, NULL);
+      long int end = (now.tv_sec + 5) * 1000 + (now.tv_usec + 500) / 1000;
+      while (1)
+	{
+	  long int timeout = end - (now.tv_sec * 1000
+				    + (now.tv_usec + 500) / 1000);
+	  n = __poll (fds, 1, timeout);
+	  if (n != -1 || errno != EINTR)
+	    break;
+	  (void) __gettimeofday (&now, NULL);
+	}
+    }
+
+  return n;
+}
+
+
 /* Try to get a file descriptor for the shared meory segment
    containing the database.  */
 static struct mapped_database *
@@ -86,102 +178,115 @@ 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;
 
   /* Send the request.  */
-  struct iovec iov[2];
-  request_header req;
+  struct
+  {
+    request_header req;
+    char key[keylen];
+  } reqdata;
+  size_t real_sizeof_reqdata = sizeof (request_header) + keylen;
 
   int sock = open_socket ();
   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 (TEMP_FAILURE_RETRY (__writev (sock, iov, 2))
-      != iov[0].iov_len + iov[1].iov_len)
+  reqdata.req.version = NSCD_VERSION;
+  reqdata.req.type = type;
+  reqdata.req.key_len = keylen;
+  memcpy (reqdata.key, key, keylen);
+
+# ifndef MSG_NOSIGNAL
+#  define MSG_NOSIGNAL 0
+# endif
+  if (__builtin_expect (TEMP_FAILURE_RETRY (__send (sock, &reqdata,
+						    real_sizeof_reqdata,
+						    MSG_NOSIGNAL))
+			!= real_sizeof_reqdata, 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.  */
+# define resdata reqdata.key
+  struct iovec iov[1];
   iov[0].iov_base = resdata;
   iov[0].iov_len = keylen;
 
-  char buf[CMSG_SPACE (sizeof (int))];
+  union
+  {
+    struct cmsghdr hdr;
+    char bytes[CMSG_SPACE (sizeof (int))];
+  } buf;
   struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
-			.msg_control = buf, .msg_controllen = sizeof (buf) };
+			.msg_control = buf.bytes,
+			.msg_controllen = sizeof (buf) };
   struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
 
   cmsg->cmsg_level = SOL_SOCKET;
   cmsg->cmsg_type = SCM_RIGHTS;
   cmsg->cmsg_len = CMSG_LEN (sizeof (int));
 
+  /* This access is well-aligned since BUF is correctly aligned for an
+     int and CMSG_DATA preserves this alignment.  */
   *(int *) CMSG_DATA (cmsg) = -1;
 
   msg.msg_controllen = cmsg->cmsg_len;
 
-  struct pollfd fds[1];
-  fds[0].fd = sock;
-  fds[0].events = POLLIN | POLLERR | POLLHUP;
-  if (__poll (fds, 1, 5 * 1000) <= 0)
-    /* Failure or timeout.  */
+  if (wait_on_socket (sock) <= 0)
     goto out_close2;
 
-  if (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0)) != keylen)
+  if (__builtin_expect (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0))
+			!= 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 (CMSG_FIRSTHDR (&msg)->cmsg_len != CMSG_LEN (sizeof (int)))
-    goto out_close;
+  mapfd = *(int *) CMSG_DATA (cmsg);
 
   struct stat64 st;
-  if (strcmp (resdata, key) != 0
-      || fstat64 (mapfd, &st) != 0
-      || st.st_size < sizeof (struct database_pers_head))
+  if (__builtin_expect (strcmp (resdata, key) != 0, 0)
+      || __builtin_expect (fstat64 (mapfd, &st) != 0, 0)
+      || __builtin_expect (st.st_size < sizeof (struct database_pers_head), 0))
     goto out_close;
 
   struct database_pers_head head;
-  if (TEMP_FAILURE_RETRY (__pread (mapfd, &head, sizeof (head), 0))
-      != sizeof (head))
+  if (__builtin_expect (TEMP_FAILURE_RETRY (__pread (mapfd, &head,
+						     sizeof (head), 0))
+			!= sizeof (head), 0))
     goto out_close;
 
-  if (head.version != DB_VERSION || head.header_size != sizeof (head)
+  if (__builtin_expect (head.version != DB_VERSION, 0)
+      || __builtin_expect (head.header_size != sizeof (head), 0)
       /* This really should not happen but who knows, maybe the update
 	 thread got stuck.  */
-      || (! head.nscd_certainly_running
-	  && head.timestamp + MAPPING_TIMEOUT < time (NULL)))
+      || __builtin_expect (! head.nscd_certainly_running
+			   && head.timestamp + MAPPING_TIMEOUT < time (NULL),
+			   0))
     goto out_close;
 
   size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
 		 + head.data_size);
 
-  if (st.st_size < size)
+  if (__builtin_expect (st.st_size < size, 0))
     goto out_close;
 
   /* The file is large enough, map it now.  */
   void *mapping = __mmap (NULL, size, PROT_READ, MAP_SHARED, mapfd, 0);
-  if (mapping != MAP_FAILED)
+  if (__builtin_expect (mapping != MAP_FAILED, 1))
     {
       /* Allocate a record for the mapping.  */
-      struct mapped_database *newp;
-
-      newp = malloc (sizeof (*newp));
+      struct mapped_database *newp = malloc (sizeof (*newp));
       if (newp == NULL)
 	{
 	  /* Ugh, after all we went through the memory allocation failed.  */
-	  __munmap (result, size);
+	  __munmap (mapping, size);
 	  goto out_close;
 	}
 
@@ -189,6 +294,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;
 
@@ -215,17 +321,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 ();
@@ -238,8 +345,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))
 	{
@@ -257,28 +366,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;
 	}
 
@@ -311,19 +442,13 @@ __nscd_open_socket (const char *key, size_t keylen, request_type type,
       vec[1].iov_len = keylen;
 
       ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
-      if (nbytes == (ssize_t) (sizeof (request_header) + keylen))
-	{
+      if (nbytes == (ssize_t) (sizeof (request_header) + keylen)
 	  /* Wait for data.  */
-	  struct pollfd fds[1];
-	  fds[0].fd = sock;
-	  fds[0].events = POLLIN | POLLERR | POLLHUP;
-	  if (__poll (fds, 1, 5 * 1000) > 0)
-	    {
-	      nbytes = TEMP_FAILURE_RETRY (__read (sock, response,
-						   responselen));
-	      if (nbytes == (ssize_t) responselen)
-		return sock;
-	    }
+	  && wait_on_socket (sock) > 0)
+	{
+	  nbytes = TEMP_FAILURE_RETRY (__read (sock, response, responselen));
+	  if (nbytes == (ssize_t) responselen)
+	    return sock;
 	}
 
       close_not_cancel_no_status (sock);
diff --git a/nscd/nscd_initgroups.c b/nscd/nscd_initgroups.c
index 2ea9e7f862..866455a96c 100644
--- a/nscd/nscd_initgroups.c
+++ b/nscd/nscd_initgroups.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2004 Free Software Foundation, Inc.
+/* Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
 
@@ -30,7 +30,7 @@
 
 
 /* We use the same mapping as in nscd_getgr.   */
-libc_locked_map_ptr (extern, __gr_map_handle);
+libc_locked_map_ptr (extern, __gr_map_handle) attribute_hidden;
 
 
 int
@@ -39,6 +39,7 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
 {
   size_t userlen = strlen (user) + 1;
   int gc_cycle;
+  int nretries = 0;
 
   /* If the mapping is available, try to search there instead of
      communicating with the nscd.  */
@@ -46,44 +47,49 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
   mapped = __nscd_get_map_ref (GETFDGR, "group", &__gr_map_handle, &gc_cycle);
 
  retry:;
-  const initgr_response_header *initgr_resp = NULL;
   char *respdata = NULL;
   int retval = -1;
   int sock = -1;
+  initgr_response_header initgr_resp;
 
   if (mapped != NO_MAPPING)
     {
-      const struct datahead *found = __nscd_cache_search (INITGROUPS, user,
-							  userlen, mapped);
+      struct datahead *found = __nscd_cache_search (INITGROUPS, user,
+						    userlen, mapped);
       if (found != NULL)
 	{
-	  initgr_resp = &found->data[0].initgrdata;
-	  respdata = (char *) (initgr_resp + 1);
+	  respdata = (char *) (&found->data[0].initgrdata + 1);
+	  initgr_resp = found->data[0].initgrdata;
 	  char *recend = (char *) found->data + found->recsize;
 
-	  if (respdata + initgr_resp->ngrps * sizeof (int32_t) > recend)
+	  /* Now check if we can trust initgr_resp fields.  If GC is
+	     in progress, it can contain anything.  */
+	  if (mapped->head->gc_cycle != gc_cycle)
+	    {
+	      retval = -2;
+	      goto out;
+	    }
+
+	  if (respdata + initgr_resp.ngrps * sizeof (int32_t) > recend)
 	    goto out;
 	}
     }
 
   /* If we do not have the cache mapped, try to get the data over the
      socket.  */
-  initgr_response_header initgr_resp_mem;
-  if (initgr_resp == NULL)
+  if (respdata == NULL)
     {
-      sock = __nscd_open_socket (user, userlen, INITGROUPS, &initgr_resp_mem,
-				 sizeof (initgr_resp_mem));
+      sock = __nscd_open_socket (user, userlen, INITGROUPS, &initgr_resp,
+				 sizeof (initgr_resp));
       if (sock == -1)
 	{
-	  /* nscd not running or wrong version or hosts caching disabled.  */
+	  /* nscd not running or wrong version.  */
 	  __nss_not_use_nscd_group = 1;
 	  goto out;
 	}
-
-      initgr_resp = &initgr_resp_mem;
     }
 
-  if (initgr_resp->found == 1)
+  if (initgr_resp.found == 1)
     {
       /* The following code assumes that gid_t and int32_t are the
 	 same size.  This is the case for al existing implementation.
@@ -91,40 +97,46 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
 	 doesn't use memcpy but instead copies each array element one
 	 by one.  */
       assert (sizeof (int32_t) == sizeof (gid_t));
-      assert (initgr_resp->ngrps > 0);
+      assert (initgr_resp.ngrps >= 0);
 
       /* Make sure we have enough room.  We always count GROUP in even
 	 though we might not end up adding it.  */
-      if (*size < initgr_resp->ngrps + 1)
+      if (*size < initgr_resp.ngrps + 1)
 	{
 	  gid_t *newp = realloc (*groupsp,
-				 (initgr_resp->ngrps + 1) * sizeof (gid_t));
+				 (initgr_resp.ngrps + 1) * sizeof (gid_t));
 	  if (newp == NULL)
 	    /* We cannot increase the buffer size.  */
-	    goto out;
+	    goto out_close;
 
 	  *groupsp = newp;
-	  *size = initgr_resp->ngrps + 1;
+	  *size = initgr_resp.ngrps + 1;
 	}
 
       if (respdata == NULL)
 	{
 	  /* Read the data from the socket.  */
-	  if ((size_t) TEMP_FAILURE_RETRY (__read (sock, *groupsp,
-						   initgr_resp->ngrps
-						   * sizeof (gid_t)))
-	      == initgr_resp->ngrps * sizeof (gid_t))
-	    retval = initgr_resp->ngrps;
+	  if ((size_t) __readall (sock, *groupsp, initgr_resp.ngrps
+						  * sizeof (gid_t))
+	      == initgr_resp.ngrps * sizeof (gid_t))
+	    retval = initgr_resp.ngrps;
 	}
       else
 	{
 	  /* Just copy the data.  */
-	  retval = initgr_resp->ngrps;
+	  retval = initgr_resp.ngrps;
 	  memcpy (*groupsp, respdata, retval * sizeof (gid_t));
 	}
     }
   else
     {
+      if (__builtin_expect (initgr_resp.found == -1, 0))
+	{
+	  /* The daemon does not cache this database.  */
+	  __nss_not_use_nscd_group = 1;
+	  goto out_close;
+	}
+
       /* No group found yet.   */
       retval = 0;
 
@@ -143,22 +155,25 @@ __nscd_getgrouplist (const char *user, gid_t group, long int *size,
 	(*groupsp)[retval++] = group;
     }
 
+ out_close:
   if (sock != -1)
     close_not_cancel_no_status (sock);
  out:
-  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0 && retval != -1)
+  if (__nscd_drop_map_ref (mapped, &gc_cycle) != 0)
     {
       /* When we come here this means there has been a GC cycle while we
 	 were looking for the data.  This means the data might have been
 	 inconsistent.  Retry if possible.  */
-      if ((gc_cycle & 1) != 0)
+      if ((gc_cycle & 1) != 0 || ++nretries == 5 || retval == -1)
 	{
 	  /* nscd is just running gc now.  Disable using the mapping.  */
-	  __nscd_unmap (mapped);
+	  if (atomic_decrement_val (&mapped->counter) == 0)
+	    __nscd_unmap (mapped);
 	  mapped = NO_MAPPING;
 	}
 
-      goto retry;
+      if (retval != -1)
+	goto retry;
     }
 
   return retval;
diff --git a/nscd/nscd_nischeck.c b/nscd/nscd_nischeck.c
deleted file mode 100644
index a6817cf79e..0000000000
--- a/nscd/nscd_nischeck.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/* Copyright (c) 1999, 2002, 2003, 2004 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1999.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
-
-/* nscd_nischeck: Check, if everybody has read permissions for NIS+ table.
-   Return value:
-    0: Everybody can read the NIS+ table
-    1: Only authenticated users could read the NIS+ table */
-
-#include <argp.h>
-#include <error.h>
-#include <stdlib.h>
-#include <libintl.h>
-#include <locale.h>
-#include <rpcsvc/nis.h>
-
-/* Get libc version number.  */
-#include <version.h>
-
-#define PACKAGE _libc_intl_domainname
-
-/* Name and version of program.  */
-static void print_version (FILE *stream, struct argp_state *state);
-void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
-
-/* Data structure to communicate with argp functions.  */
-static struct argp argp =
-{
-  NULL, NULL, NULL, NULL,
-};
-
-int
-main (int argc, char **argv)
-{
-  int remaining;
-  nis_result *res;
-  char *tablename, *cp;
-
-  /* Set locale via LC_ALL.  */
-  setlocale (LC_ALL, "");
-  /* Set the text message domain.  */
-  textdomain (PACKAGE);
-
-  /* Parse and process arguments.  */
-  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
-
-  if (remaining + 1 != argc)
-    {
-      error (0, 0, gettext ("wrong number of arguments"));
-      argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
-      exit (EXIT_FAILURE);
-    }
-
-  tablename = alloca (strlen (argv[1]) + 10);
-  cp = stpcpy (tablename, argv[1]);
-  strcpy (cp, ".org_dir");
-
-  res = nis_lookup (tablename, EXPAND_NAME|FOLLOW_LINKS);
-
-  if (res == NULL ||
-      (res->status != NIS_SUCCESS && res->status != NIS_S_SUCCESS))
-    return 0;
-
-  if (NIS_NOBODY(NIS_RES_OBJECT(res)->zo_access, NIS_READ_ACC))
-    return 0;
-  else
-    return 1;
-}
-
-/* Print the version information.  */
-static void
-print_version (FILE *stream, struct argp_state *state)
-{
-  fprintf (stream, "nscd_nischeck (GNU %s) %s\n", PACKAGE, VERSION);
-  fprintf (stream, gettext ("\
-Copyright (C) %s Free Software Foundation, Inc.\n\
-This is free software; see the source for copying conditions.  There is NO\n\
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "2004");
-  fprintf (stream, gettext ("Written by %s.\n"), "Thorsten Kukuk");
-}
diff --git a/nscd/nscd_setup_thread.c b/nscd/nscd_setup_thread.c
new file mode 100644
index 0000000000..32bfe07000
--- /dev/null
+++ b/nscd/nscd_setup_thread.c
@@ -0,0 +1,26 @@
+/* Setup of nscd worker threads.  Stub verison.
+   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#include <nscd.h>
+
+
+void
+setup_thread (struct database_dyn *db)
+{
+  /* Nothing.  */
+}
diff --git a/nscd/nscd_stat.c b/nscd/nscd_stat.c
index 9231642278..7f6bd1c83e 100644
--- a/nscd/nscd_stat.c
+++ b/nscd/nscd_stat.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 1998, 2003, 2004 Free Software Foundation, Inc.
+/* Copyright (c) 1998, 2003, 2004, 2005 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1998.
 
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/socket.h>
 #include <unistd.h>
 #include <libintl.h>
 
@@ -75,6 +76,10 @@ struct statdata
   int debug_level;
   time_t runtime;
   unsigned long int client_queued;
+  int nthreads;
+  int max_nthreads;
+  int paranoia;
+  time_t restart_interval;
   int ndbs;
   struct dbstat dbs[lastdb];
 #ifdef HAVE_SELINUX
@@ -93,6 +98,10 @@ send_stats (int fd, struct database_dyn dbs[lastdb])
   data.debug_level = debug_level;
   data.runtime = time (NULL) - start_time;
   data.client_queued = client_queued;
+  data.nthreads = nthreads;
+  data.max_nthreads = max_nthreads;
+  data.paranoia = paranoia;
+  data.restart_interval = restart_interval;
   data.ndbs = lastdb;
 
   for (cnt = 0; cnt < lastdb; ++cnt)
@@ -125,7 +134,8 @@ send_stats (int fd, struct database_dyn dbs[lastdb])
   if (selinux_enabled)
     nscd_avc_cache_stats (&data.cstats);
 
-  if (TEMP_FAILURE_RETRY (write (fd, &data, sizeof (data))) != sizeof (data))
+  if (TEMP_FAILURE_RETRY (send (fd, &data, sizeof (data), MSG_NOSIGNAL))
+      != sizeof (data))
     {
       char buf[256];
       dbg_log (_("cannot write statistics: %s"),
@@ -143,8 +153,8 @@ receive_print_stats (void)
   int fd;
   int i;
   uid_t uid = getuid ();
-  const char *yesstr = nl_langinfo (YESSTR);
-  const char *nostr = nl_langinfo (NOSTR);
+  const char *yesstr = _("yes");
+  const char *nostr = _("no");
 
   /* Find out whether there is another user but root allowed to
      request statistics.  */
@@ -172,7 +182,8 @@ receive_print_stats (void)
   req.version = NSCD_VERSION;
   req.type = GETSTAT;
   req.key_len = 0;
-  nbytes = TEMP_FAILURE_RETRY (write (fd, &req, sizeof (request_header)));
+  nbytes = TEMP_FAILURE_RETRY (send (fd, &req, sizeof (request_header),
+				     MSG_NOSIGNAL));
   if (nbytes != sizeof (request_header))
     {
       int err = errno;
@@ -230,8 +241,9 @@ receive_print_stats (void)
 	    "%15lu  number of times clients had to wait\n"
 	    "%15s  paranoia mode enabled\n"
 	    "%15lu  restart internal\n"),
-	  nthreads, max_nthreads, data.client_queued,
-	  paranoia ? yesstr : nostr, (unsigned long int) restart_interval);
+	  data.nthreads, data.max_nthreads, data.client_queued,
+	  data.paranoia ? yesstr : nostr,
+	  (unsigned long int) data.restart_interval);
 
   for (i = 0; i < lastdb; ++i)
     {
diff --git a/nscd/pwdcache.c b/nscd/pwdcache.c
index e8b9578778..ae579df510 100644
--- a/nscd/pwdcache.c
+++ b/nscd/pwdcache.c
@@ -1,22 +1,20 @@
 /* Cache handling for passwd lookup.
-   Copyright (C) 1998-2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 1998-2005, 2006, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
 
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation.
 
-   The GNU C Library is distributed in the hope that it will be useful,
+   This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include <alloca.h>
 #include <assert.h>
@@ -32,10 +30,14 @@
 #include <time.h>
 #include <unistd.h>
 #include <sys/mman.h>
+#include <sys/socket.h>
 #include <stackinfo.h>
 
 #include "nscd.h"
 #include "dbg_log.h"
+#ifdef HAVE_SENDFILE
+# include <kernel-features.h>
+#endif
 
 /* This is the standard reply in case the service is disabled.  */
 static const pw_response_header disabled =
@@ -114,7 +116,8 @@ cache_addpw (struct database_dyn *db, int fd, request_header *req,
 	  written = total = sizeof (notfound);
 
 	  if (fd != -1)
-	    written = TEMP_FAILURE_RETRY (write (fd, &notfound, total));
+	    written = TEMP_FAILURE_RETRY (send (fd, &notfound, total,
+						MSG_NOSIGNAL));
 
 	  dataset = mempool_alloc (db, sizeof (struct dataset) + req->key_len);
 	  /* If we cannot permanently store the result, so be it.  */
@@ -271,6 +274,7 @@ cache_addpw (struct database_dyn *db, int fd, request_header *req,
 		{
 		  /* Adjust pointer into the memory block.  */
 		  cp = (char *) newp + (cp - (char *) dataset);
+		  key_copy = (char *) newp + (key_copy - (char *) dataset);
 
 		  dataset = memcpy (newp, dataset, total + n);
 		  alloca_used = false;
@@ -287,7 +291,30 @@ cache_addpw (struct database_dyn *db, int fd, request_header *req,
 	     unnecessarily let the receiver wait.  */
 	  assert (fd != -1);
 
-	  written = TEMP_FAILURE_RETRY (write (fd, &dataset->resp, total));
+#ifdef HAVE_SENDFILE
+	  if (__builtin_expect (db->mmap_used, 1) && !alloca_used)
+	    {
+	      assert (db->wr_fd != -1);
+	      assert ((char *) &dataset->resp > (char *) db->data);
+	      assert ((char *) &dataset->resp - (char *) db->head
+		      + total
+		      <= (sizeof (struct database_pers_head)
+                          + db->head->module * sizeof (ref_t)
+                          + db->head->data_size));
+	      written = sendfileall (fd, db->wr_fd,
+				     (char *) &dataset->resp
+				     - (char *) db->head, total);
+# ifndef __ASSUME_SENDFILE
+	      if (written == -1 && errno == ENOSYS)
+		goto use_write;
+# endif
+	    }
+	  else
+# ifndef __ASSUME_SENDFILE
+	  use_write:
+# endif
+#endif
+	    written = writeall (fd, &dataset->resp, total);
 	}
 
 
@@ -312,10 +339,10 @@ cache_addpw (struct database_dyn *db, int fd, request_header *req,
 	     marked with FIRST first.  Otherwise we end up with
 	     dangling "pointers" in case a latter hash entry cannot be
 	     added.  */
-	  bool first = req->type == GETPWBYNAME;
+	  bool first = true;
 
 	  /* If the request was by UID, add that entry first.  */
-	  if (req->type != GETPWBYNAME)
+	  if (req->type == GETPWBYUID)
 	    {
 	      if (cache_add (GETPWBYUID, cp, key_offset, &dataset->head, true,
 			     db, owner) < 0)
@@ -325,12 +352,14 @@ cache_addpw (struct database_dyn *db, int fd, request_header *req,
 		  dataset->head.usable = false;
 		  goto out;
 		}
+
+	      first = false;
 	    }
 	  /* If the key is different from the name add a separate entry.  */
 	  else if (strcmp (key_copy, dataset->strdata) != 0)
 	    {
 	      if (cache_add (GETPWBYNAME, key_copy, key_len + 1,
-			     &dataset->head, first, db, owner) < 0)
+			     &dataset->head, true, db, owner) < 0)
 		{
 		  /* Could not allocate memory.  Make sure the data gets
 		     discarded.  */
@@ -342,11 +371,12 @@ cache_addpw (struct database_dyn *db, int fd, request_header *req,
 	    }
 
 	  /* We have to add the value for both, byname and byuid.  */
-	  if (__builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
-					   pw_name_len, &dataset->head, first,
-					   db, owner) == 0, 1))
+	  if ((req->type == GETPWBYNAME || db->propagate)
+	      && __builtin_expect (cache_add (GETPWBYNAME, dataset->strdata,
+					      pw_name_len, &dataset->head,
+					      first, db, owner) == 0, 1))
 	    {
-	      if (req->type == GETPWBYNAME)
+	      if (req->type == GETPWBYNAME && db->propagate)
 		(void) cache_add (GETPWBYUID, cp, key_offset, &dataset->head,
 				  req->type != GETPWBYNAME, db, owner);
 	    }
@@ -425,11 +455,10 @@ addpwbyX (struct database_dyn *db, int fd, request_header *req,
     {
       char *old_buffer = buffer;
       errno = 0;
-#define INCR 1024
 
       if (__builtin_expect (buflen > 32768, 0))
 	{
-	  buflen += INCR;
+	  buflen *= 2;
 	  buffer = (char *) realloc (use_malloc ? buffer : NULL, buflen);
 	  if (buffer == NULL)
 	    {
@@ -450,7 +479,7 @@ addpwbyX (struct database_dyn *db, int fd, request_header *req,
       else
 	/* Allocate a new buffer on the stack.  If possible combine it
 	   with the previously allocated buffer.  */
-	buffer = (char *) extend_alloca (buffer, buflen, buflen + INCR);
+	buffer = (char *) extend_alloca (buffer, buflen, 2 * buflen);
     }
 
 #if 0
diff --git a/nscd/selinux.c b/nscd/selinux.c
index f57f0920ae..b826031150 100644
--- a/nscd/selinux.c
+++ b/nscd/selinux.c
@@ -1,5 +1,5 @@
 /* SELinux access controls for nscd.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Matthew Rickard <mjricka@epoch.ncsc.mil>, 2004.
 
@@ -18,6 +18,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include "config.h"
 #include <error.h>
 #include <errno.h>
 #include <libintl.h>
@@ -26,10 +27,15 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <syslog.h>
+#include <unistd.h>
+#include <sys/prctl.h>
 #include <selinux/av_permissions.h>
 #include <selinux/avc.h>
 #include <selinux/flask.h>
 #include <selinux/selinux.h>
+#ifdef HAVE_LIBAUDIT
+# include <libaudit.h>
+#endif
 
 #include "dbg_log.h"
 #include "selinux.h"
@@ -66,6 +72,11 @@ static struct avc_entry_ref aeref;
 /* Thread to listen for SELinux status changes via netlink.  */
 static pthread_t avc_notify_thread;
 
+#ifdef HAVE_LIBAUDIT
+/* Prototype for supporting the audit daemon */
+static void log_callback (const char *fmt, ...);
+#endif
+
 /* Prototypes for AVC callback functions.  */
 static void *avc_create_thread (void (*run) (void));
 static void avc_stop_thread (void *thread);
@@ -77,7 +88,11 @@ static void avc_free_lock (void *lock);
 /* AVC callback structures for use in avc_init.  */
 static const struct avc_log_callback log_cb =
 {
+#ifdef HAVE_LIBAUDIT
+  .func_log = log_callback,
+#else
   .func_log = dbg_log,
+#endif
   .func_audit = NULL
 };
 static const struct avc_thread_callback thread_cb =
@@ -93,6 +108,137 @@ static const struct avc_lock_callback lock_cb =
   .func_free_lock = avc_free_lock
 };
 
+#ifdef HAVE_LIBAUDIT
+/* The audit system's netlink socket descriptor */
+static int audit_fd = -1;
+
+/* When an avc denial occurs, log it to audit system */
+static void
+log_callback (const char *fmt, ...)
+{
+  if (audit_fd >= 0)
+    {
+      va_list ap;
+      va_start (ap, fmt);
+
+      char *buf;
+      int e = vasprintf (&buf, fmt, ap);
+      if (e < 0)
+	{
+	  buf = alloca (BUFSIZ);
+	  vsnprintf (buf, BUFSIZ, fmt, ap);
+	}
+
+      /* FIXME: need to attribute this to real user, using getuid for now */
+      audit_log_user_avc_message (audit_fd, AUDIT_USER_AVC, buf, NULL, NULL,
+				  NULL, getuid ());
+
+      if (e >= 0)
+	free (buf);
+
+      va_end (ap);
+    }
+}
+
+/* Initialize the connection to the audit system */
+static void
+audit_init (void)
+{
+  audit_fd = audit_open ();
+  if (audit_fd < 0
+      /* If kernel doesn't support audit, bail out */
+      && errno != EINVAL && errno != EPROTONOSUPPORT && errno != EAFNOSUPPORT)
+    dbg_log (_("Failed opening connection to the audit subsystem: %m"));
+}
+
+
+# ifdef HAVE_LIBCAP
+static const cap_value_t new_cap_list[] =
+  { CAP_AUDIT_WRITE };
+#  define nnew_cap_list (sizeof (new_cap_list) / sizeof (new_cap_list[0]))
+static const cap_value_t tmp_cap_list[] =
+  { CAP_AUDIT_WRITE, CAP_SETUID, CAP_SETGID };
+#  define ntmp_cap_list (sizeof (tmp_cap_list) / sizeof (tmp_cap_list[0]))
+
+cap_t
+preserve_capabilities (void)
+{
+  if (getuid () != 0)
+    /* Not root, then we cannot preserve anything.  */
+    return NULL;
+
+  if (prctl (PR_SET_KEEPCAPS, 1) == -1)
+    {
+      dbg_log (_("Failed to set keep-capabilities"));
+      error (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
+      /* NOTREACHED */
+    }
+
+  cap_t tmp_caps = cap_init ();
+  cap_t new_caps;
+  if (tmp_caps != NULL)
+    new_caps = cap_init ();
+
+  if (tmp_caps == NULL || new_caps == NULL)
+    {
+      if (tmp_caps != NULL)
+	cap_free (tmp_caps);
+
+      dbg_log (_("Failed to initialize drop of capabilities"));
+      error (EXIT_FAILURE, 0, _("cap_init failed"));
+    }
+
+  /* There is no reason why these should not work.  */
+  cap_set_flag (new_caps, CAP_PERMITTED, nnew_cap_list,
+		(cap_value_t *) new_cap_list, CAP_SET);
+  cap_set_flag (new_caps, CAP_EFFECTIVE, nnew_cap_list,
+		(cap_value_t *) new_cap_list, CAP_SET);
+
+  cap_set_flag (tmp_caps, CAP_PERMITTED, ntmp_cap_list,
+		(cap_value_t *) tmp_cap_list, CAP_SET);
+  cap_set_flag (tmp_caps, CAP_EFFECTIVE, ntmp_cap_list,
+		(cap_value_t *) tmp_cap_list, CAP_SET);
+
+  int res = cap_set_proc (tmp_caps);
+
+  cap_free (tmp_caps);
+
+  if (__builtin_expect (res != 0, 0))
+    {
+      cap_free (new_caps);
+      dbg_log (_("Failed to drop capabilities\n"));
+      error (EXIT_FAILURE, 0, _("cap_set_proc failed"));
+    }
+
+  return new_caps;
+}
+
+void
+install_real_capabilities (cap_t new_caps)
+{
+  /* If we have no capabilities there is nothing to do here.  */
+  if (new_caps == NULL)
+    return;
+
+  if (cap_set_proc (new_caps))
+    {
+      cap_free (new_caps);
+      dbg_log (_("Failed to drop capabilities"));
+      error (EXIT_FAILURE, 0, _("cap_set_proc failed"));
+      /* NOTREACHED */
+    }
+
+  cap_free (new_caps);
+
+  if (prctl (PR_SET_KEEPCAPS, 0) == -1)
+    {
+      dbg_log (_("Failed to unset keep-capabilities"));
+      error (EXIT_FAILURE, errno, _("prctl(KEEPCAPS) failed"));
+      /* NOTREACHED */
+    }
+}
+# endif /* HAVE_LIBCAP */
+#endif /* HAVE_LIBAUDIT */
 
 /* Determine if we are running on an SELinux kernel. Set selinux_enabled
    to the result.  */
@@ -182,6 +328,9 @@ nscd_avc_init (void)
     error (EXIT_FAILURE, errno, _("Failed to start AVC"));
   else
     dbg_log (_("Access Vector Cache (AVC) started"));
+#ifdef HAVE_LIBAUDIT
+  audit_init ();
+#endif
 }
 
 
@@ -262,6 +411,9 @@ void
 nscd_avc_destroy (void)
 {
   avc_destroy ();
+#ifdef HAVE_LIBAUDIT
+  audit_close (audit_fd);
+#endif
 }
 
 #endif /* HAVE_SELINUX */
diff --git a/nscd/selinux.h b/nscd/selinux.h
index b9eb053aa0..27afcd6e86 100644
--- a/nscd/selinux.h
+++ b/nscd/selinux.h
@@ -1,5 +1,5 @@
 /* Header for nscd SELinux access controls.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2006, 2007 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Matthew Rickard <mjricka@epoch.ncsc.mil>, 2004.
 
@@ -22,6 +22,9 @@
 #define _SELINUX_H 1
 
 #include "nscd.h"
+#ifdef HAVE_LIBCAP
+# include <sys/capability.h>
+#endif
 
 #ifdef HAVE_SELINUX
 /* Global variable to tell if the kernel has SELinux support.  */
@@ -42,6 +45,13 @@ extern int nscd_request_avc_has_perm (int fd, request_type req);
 extern void nscd_avc_cache_stats (struct avc_cache_stats *cstats);
 /* Display statistics on AVC usage.  */
 extern void nscd_avc_print_stats (struct avc_cache_stats *cstats);
+
+# ifdef HAVE_LIBCAP
+/* Preserve capabilities to connect to connnect to the audit daemon.  */
+extern cap_t preserve_capabilities (void);
+/* Install final capabilities.  */
+extern void install_real_capabilities (cap_t new_caps);
+# endif
 #else
 # define selinux_enabled 0
 # define nscd_avc_init() (void) 0