about summary refs log tree commit diff
path: root/nscd
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2004-09-08 15:46:42 +0000
committerUlrich Drepper <drepper@redhat.com>2004-09-08 15:46:42 +0000
commitc207f23b0b6b2d215be65e670d453768ad304247 (patch)
tree5d10943ec51bbda849db9ca2fdcc7b43feb2011c /nscd
parent0a3ad40da92c0036bd3bd87949f7d7d9864c31c7 (diff)
downloadglibc-c207f23b0b6b2d215be65e670d453768ad304247.tar.gz
glibc-c207f23b0b6b2d215be65e670d453768ad304247.tar.xz
glibc-c207f23b0b6b2d215be65e670d453768ad304247.zip
Update.
	* nscd/connections.c: Implement r/o sharing of nscd's cache with client
	processes via shared memory.
	* nscd/nscd-client.h: Likewise.
	* nscd/nscd.h: Likewise.
	* nscd/nscd_conf.c: Likewise.
	* nscd/nscd_getgr_r.c: Likewise.
	* nscd/nscd_getpw_r.c: Likewise.
	* nscd/nscd_gethst_r.c: Likewise.
	* nscd/nscd.conf: Add new config parameters.
	* nscd/Makefile (aux): Add nscd_helper.
	* nscd/nscd_helper.c: New file.
	* nscd/mem.c (gc): Indicate beginning and end of the gc cycle.

	* nscd/hstcache.c: Simplify a lot.  We cache only the request itself,
	no derived information.
	* connections.c (nscd_init): Fix bug in testing size of the persistent.

	* nis/Makefile (aux): Add nis_hash.
	* nis/nis_hash.c: New file.  Split out from nis_util.c.
	* nis/nis_util.c: Move __nis_hash code in separate file.

	* csu/tst-atomic.c: Improve atomic_increment_val test which would
	not have found a ppc bug.
Diffstat (limited to 'nscd')
-rw-r--r--nscd/Makefile1
-rw-r--r--nscd/connections.c78
-rw-r--r--nscd/hstcache.c163
-rw-r--r--nscd/mem.c11
-rw-r--r--nscd/nscd-client.h146
-rw-r--r--nscd/nscd.conf4
-rw-r--r--nscd/nscd.h82
-rw-r--r--nscd/nscd_conf.c14
-rw-r--r--nscd/nscd_getgr_r.c154
-rw-r--r--nscd/nscd_gethst_r.c295
-rw-r--r--nscd/nscd_getpw_r.c127
-rw-r--r--nscd/nscd_helper.c335
12 files changed, 986 insertions, 424 deletions
diff --git a/nscd/Makefile b/nscd/Makefile
index cb82a2e06f..5b3529267a 100644
--- a/nscd/Makefile
+++ b/nscd/Makefile
@@ -22,6 +22,7 @@
 subdir	:= nscd
 
 routines := nscd_getpw_r nscd_getgr_r nscd_gethst_r
+aux	:= nscd_helper
 
 include ../Makeconfig
 
diff --git a/nscd/connections.c b/nscd/connections.c
index 53e258b498..552d554295 100644
--- a/nscd/connections.c
+++ b/nscd/connections.c
@@ -80,7 +80,10 @@ const char *serv2str[LASTREQ] =
   [GETHOSTBYADDRv6] = "GETHOSTBYADDRv6",
   [SHUTDOWN] = "SHUTDOWN",
   [GETSTAT] = "GETSTAT",
-  [INVALIDATE] = "INVALIDATE"
+  [INVALIDATE] = "INVALIDATE",
+  [GETFDPW] = "GETFDPW",
+  [GETFDGR] = "GETFDGR",
+  [GETFDHST] = "GETFDHST"
 };
 
 /* The control data structures for the services.  */
@@ -91,6 +94,7 @@ struct database_dyn dbs[lastdb] =
     .enabled = 0,
     .check_file = 1,
     .persistent = 0,
+    .shared = 0,
     .filename = "/etc/passwd",
     .db_filename = _PATH_NSCD_PASSWD_DB,
     .disabled_iov = &pwd_iov_disabled,
@@ -105,6 +109,7 @@ struct database_dyn dbs[lastdb] =
     .enabled = 0,
     .check_file = 1,
     .persistent = 0,
+    .shared = 0,
     .filename = "/etc/group",
     .db_filename = _PATH_NSCD_GROUP_DB,
     .disabled_iov = &grp_iov_disabled,
@@ -119,6 +124,7 @@ struct database_dyn dbs[lastdb] =
     .enabled = 0,
     .check_file = 1,
     .persistent = 0,
+    .shared = 0,
     .filename = "/etc/hosts",
     .db_filename = _PATH_NSCD_HOSTS_DB,
     .disabled_iov = &hst_iov_disabled,
@@ -132,7 +138,7 @@ struct database_dyn dbs[lastdb] =
 
 
 /* Mapping of request type to database.  */
-static struct database_dyn *const serv2db[LASTDBREQ + 1] =
+static struct database_dyn *const serv2db[LASTREQ] =
 {
   [GETPWBYNAME] = &dbs[pwddb],
   [GETPWBYUID] = &dbs[pwddb],
@@ -141,7 +147,10 @@ static struct database_dyn *const serv2db[LASTDBREQ + 1] =
   [GETHOSTBYNAME] = &dbs[hstdb],
   [GETHOSTBYNAMEv6] = &dbs[hstdb],
   [GETHOSTBYADDR] = &dbs[hstdb],
-  [GETHOSTBYADDRv6] = &dbs[hstdb]
+  [GETHOSTBYADDRv6] = &dbs[hstdb],
+  [GETFDPW] = &dbs[pwddb],
+  [GETFDGR] = &dbs[grpdb],
+  [GETFDHST] = &dbs[hstdb],
 };
 
 
@@ -158,9 +167,6 @@ static int sock;
 /* Number of times clients had to wait.  */
 unsigned long int client_queued;
 
-/* Alignment requirement of the beginning of the data region.  */
-#define ALIGN 16
-
 
 /* Initialize database information structures.  */
 void
@@ -224,11 +230,10 @@ nscd_init (void)
 		    dbs[cnt].persistent = 0;
 		  }
 		else if ((total = (sizeof (head)
-				   + roundup (head.module
-					      * sizeof (struct hashentry),
+				   + roundup (head.module * sizeof (ref_t),
 					      ALIGN)
 				   + head.data_size))
-			 < st.st_size)
+			 > st.st_size)
 		  {
 		    dbg_log (_("invalid persistent database file \"%s\": %s"),
 			     dbs[cnt].db_filename,
@@ -253,6 +258,7 @@ nscd_init (void)
 			       dbnames[cnt]);
 
 		    dbs[cnt].wr_fd = fd;
+		    dbs[cnt].ro_fd = open (dbs[cnt].db_filename, O_RDONLY);
 		    fd = -1;
 		    /* We also need a read-only descriptor.  */
 		    dbs[cnt].ro_fd = open (dbs[cnt].db_filename, O_RDONLY);
@@ -439,6 +445,9 @@ cannot create read-only descriptor for \"%s\"; no mmap"),
 					* dbs[cnt].head->module);
 	    dbs[cnt].data = xmalloc (dbs[cnt].head->data_size);
 	    dbs[cnt].head->first_free = 0;
+
+	    dbs[cnt].shared = 0;
+	    assert (dbs[cnt].ro_fd == -1);
 	  }
 
 	if (dbs[cnt].check_file)
@@ -529,6 +538,43 @@ invalidate_cache (char *key)
 }
 
 
+#ifdef SCM_RIGHTS
+static void
+send_ro_fd (struct database_dyn *db, char *key, int fd)
+{
+  /* If we do not have an read-only file descriptor do nothing.  */
+  if (db->ro_fd == -1)
+    return;
+
+  /* We need to send some data along with the descriptor.  */
+  struct iovec iov[1];
+  iov[0].iov_base = key;
+  iov[0].iov_len = strlen (key) + 1;
+
+  /* Prepare the control message to transfer the descriptor.  */
+  char buf[CMSG_SPACE (sizeof (int))];
+  struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
+			.msg_control = buf, .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));
+
+  *(int *) CMSG_DATA (cmsg) = db->ro_fd;
+
+  msg.msg_controllen = cmsg->cmsg_len;
+
+  /* Send the control message.  We repeat when we are interrupted but
+     everything else is ignored.  */
+  (void) TEMP_FAILURE_RETRY (sendmsg (fd, &msg, 0));
+
+  if (__builtin_expect (debug_level > 0, 0))
+    dbg_log (_("provide access to FD %d, for %s"), db->ro_fd, key);
+}
+#endif	/* SCM_RIGHTS */
+
+
 /* Handle new request.  */
 static void
 handle_request (int fd, request_header *req, void *key, uid_t uid)
@@ -614,7 +660,7 @@ cannot handle old request version %d; current version is %d"),
   else if (__builtin_expect (debug_level, 0) > 0)
     {
       if (req->type == INVALIDATE)
-	dbg_log ("\t%s (%s)", serv2str[req->type], (char *)key);
+	dbg_log ("\t%s (%s)", serv2str[req->type], (char *) key);
       else
 	dbg_log ("\t%s", serv2str[req->type]);
     }
@@ -697,6 +743,14 @@ cannot handle old request version %d; current version is %d"),
 	}
       break;
 
+    case GETFDPW:
+    case GETFDGR:
+    case GETFDHST:
+#ifdef SCM_RIGHTS
+      send_ro_fd (serv2db[req->type], key, fd);
+#endif
+      break;
+
     default:
       /* Ignore the command, it's nothing we know.  */
       break;
@@ -733,7 +787,9 @@ nscd_run (void *p)
 	  int timeout = -1;
 	  if (run_prune)
 	    {
-	      now = time (NULL);
+	      /* NB: we do not flush the timestamp update using msync since
+		 this value doesnot matter on disk.  */
+	      dbs[my_number].head->timestamp = now = time (NULL);
 	      timeout = now < next_prune ? 1000 * (next_prune - now) : 0;
 	    }
 
diff --git a/nscd/hstcache.c b/nscd/hstcache.c
index 5a536b3346..94568d0d2b 100644
--- a/nscd/hstcache.c
+++ b/nscd/hstcache.c
@@ -77,7 +77,7 @@ static const hst_response_header notfound =
 
 static void
 cache_addhst (struct database_dyn *db, int fd, request_header *req,
-	      const void *key, struct hostent *hst, uid_t owner, int add_addr,
+	      const void *key, struct hostent *hst, uid_t owner,
 	      struct hashentry *he, struct datahead *dh, int errval)
 {
   ssize_t total;
@@ -208,7 +208,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
 	 the current cache handling cannot handle and it is more than
 	 questionable whether it is worthwhile complicating the cache
 	 handling just for handling such a special case. */
-      if (he == NULL && (add_addr || hst->h_addr_list[1] == NULL))
+      if (he == NULL && hst->h_addr_list[1] == NULL)
 	{
 	  dataset = (struct dataset *) mempool_alloc (db,
 						      total + req->key_len);
@@ -269,10 +269,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
 	 itself.  This is the case if the resolver is used and the name
 	 is extended by the domainnames from /etc/resolv.conf.  Therefore
 	 we explicitly add the name here.  */
-      if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
-	key_copy = memcpy (cp, key, req->key_len);
-      else
-	memset (cp, '\0', req->key_len);
+      key_copy = memcpy (cp, key, req->key_len);
 
       /* Now we can determine whether on refill we have to create a new
 	 record or not.  */
@@ -349,141 +346,21 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
 	     problem is that we always must add the hash table entry
 	     with the FIRST flag set first.  Otherwise we get dangling
 	     pointers in case memory allocation fails.  */
-	  assert (add_addr || hst->h_addr_list[1] == NULL);
-
-	  /* Add the normal addresses.  */
-	  if (add_addr)
-	    {
-	      for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
-		{
-		  if (cache_add (addr_list_type, addresses, hst->h_length,
-				 &dataset->head, cnt == 0, db, owner) < 0)
-		    {
-		      /* Ensure the data can be recovered.  */
-		      if (cnt == 0)
-			dataset->head.usable = false;
-		      goto out;
-		    }
-		  addresses += hst->h_length;
-		}
-
-	      /* If necessary the IPv6 addresses.  */
-	      if (addr_list_type == GETHOSTBYADDR)
-		for (cnt = 0; cnt < h_addr_list_cnt; ++cnt)
-		  {
-		    if (cache_add (GETHOSTBYADDRv6, addresses, IN6ADDRSZ,
-				   &dataset->head, false, db, owner) < 0)
-		      goto out;
+	  assert (hst->h_addr_list[1] == NULL);
 
-		    addresses += IN6ADDRSZ;
-		  }
-	    }
 	  /* Avoid adding names if more than one address is available.  See
 	     above for more info.  */
-	  else
-	    {
-	      assert (req->type == GETHOSTBYNAME
-		      || req->type == GETHOSTBYNAMEv6
-		      || req->type == GETHOSTBYADDR
-		      || req->type == GETHOSTBYADDRv6);
-
-	      /* If necessary add the key for this request.  */
-	      if (req->type == GETHOSTBYNAME)
-		{
-		  bool first = true;
-		  if (addr_list_type == GETHOSTBYADDR)
-		    {
-		      if (cache_add (GETHOSTBYNAME, key_copy, req->key_len,
-				     &dataset->head, true, db, owner) < 0)
-			{
-			  /* Could not allocate memory.  Make sure the
-			     data gets discarded.  */
-			  dataset->head.usable = false;
-			  goto out;
-			}
-
-		      first = false;
-		    }
-		  if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
-				 &dataset->head, first, db, owner) < 0)
-		    {
-		      /* Could not allocate memory.  Make sure the
-			 data gets discarded.  */
-		      if (first)
-			dataset->head.usable = false;
-		      goto out;
-		    }
-		}
-	      else if (req->type == GETHOSTBYNAMEv6)
-		{
-		  if (cache_add (GETHOSTBYNAMEv6, key_copy, req->key_len,
-				 &dataset->head, true, db, owner) < 0)
-		    {
-		      /* Could not allocate memory.  Make sure the
-			 data gets discarded.  */
-		      dataset->head.usable = false;
-		      goto out;
-		    }
-
-		  if (addr_list_type == GETHOSTBYADDR
-		      && cache_add (GETHOSTBYNAME, key_copy, req->key_len,
-				    &dataset->head, false, db, owner) < 0)
-		    goto out;
-		}
-
-	      /* And finally the name.  We mark this as the last entry.  */
-	      if (addr_list_type == GETHOSTBYADDR
-		  && req->type == GETHOSTBYADDR
-		  && cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
-				&dataset->head, true, db, owner) < 0)
-		{
-		  /* Could not allocate memory.  Make sure the
-		     data gets discarded.  */
-		  dataset->head.usable = false;
-		  goto out;
-		}
-
-	      if (cache_add (GETHOSTBYNAMEv6, dataset->strdata,
-			     h_name_len, &dataset->head,
-			     ((req->type == GETHOSTBYADDR
-			       && addr_list_type != GETHOSTBYADDR)
-			      || req->type == GETHOSTBYADDRv6), db,
-			     owner) < 0)
-		{
-		  /* Could not allocate memory.  Make sure the
-		     data gets discarded.  */
-		  if ((req->type == GETHOSTBYADDR
-		       && addr_list_type != GETHOSTBYADDR)
-		      || req->type == GETHOSTBYADDRv6)
-		    dataset->head.usable = false;
-		  goto out;
-		}
+	  assert (req->type == GETHOSTBYNAME
+		  || req->type == GETHOSTBYNAMEv6
+		  || req->type == GETHOSTBYADDR
+		  || req->type == GETHOSTBYADDRv6);
 
-	      if (addr_list_type == GETHOSTBYADDR
-		  && req->type != GETHOSTBYADDR
-		  && cache_add (GETHOSTBYNAME, dataset->strdata, h_name_len,
-				&dataset->head, false, db, owner) < 0)
-		goto out;
+	  if (cache_add (req->type, key_copy, req->key_len,
+			 &dataset->head, true, db, owner) < 0)
+	    /* Could not allocate memory.  Make sure the
+	       data gets discarded.  */
+	    dataset->head.usable = false;
 
-	      /* First add all the aliases.  */
-	      for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
-		{
-		  if (addr_list_type == GETHOSTBYADDR)
-		    if (cache_add (GETHOSTBYNAME, aliases,
-				   h_aliases_len[cnt], &dataset->head,
-				   false, db, owner) < 0)
-		      break;
-
-		  if (cache_add (GETHOSTBYNAMEv6, aliases,
-				 h_aliases_len[cnt], &dataset->head,
-				 false, db, owner) < 0)
-		    break;
-
-		  aliases += h_aliases_len[cnt];
-		}
-	    }
-
-	out:
 	  pthread_rwlock_unlock (&db->lock);
 	}
     }
@@ -534,10 +411,18 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
 
   if (__builtin_expect (debug_level > 0, 0))
     {
+      const char *str;
+      char buf[INET6_ADDRSTRLEN + 1];
+      if (req->type == GETHOSTBYNAME || req->type == GETHOSTBYNAMEv6)
+	str = key;
+      else
+	str = inet_ntop (req->type == GETHOSTBYADDR ? AF_INET : AF_INET6,
+			 key, buf, sizeof (buf));
+
       if (he == NULL)
-	dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) key);
+	dbg_log (_("Haven't found \"%s\" in hosts cache!"), (char *) str);
       else
-	dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) key);
+	dbg_log (_("Reloading \"%s\" in hosts cache!"), (char *) str);
     }
 
   if (db->secure)
@@ -583,7 +468,7 @@ addhstbyX (struct database_dyn *db, int fd, request_header *req,
   if (db->secure)
     seteuid (oldeuid);
 
-  cache_addhst (db, fd, req, key, hst, uid, 0, he, dh,
+  cache_addhst (db, fd, req, key, hst, uid, he, dh,
 		h_errno == TRY_AGAIN ? errval : 0);
 
   if (use_malloc)
diff --git a/nscd/mem.c b/nscd/mem.c
index a4e30535c8..5207efc2de 100644
--- a/nscd/mem.c
+++ b/nscd/mem.c
@@ -230,6 +230,12 @@ gc (struct database_dyn *db)
     ++next_data;
 
 
+  /* Now we start modifying the data.  Make sure all readers of the
+     data are aware of this and temporarily don't use the data.  */
+  ++db->head->gc_cycle;
+  assert ((db->head->gc_cycle & 1) == 1);
+
+
   /* We do not perform the move operations right away since the
      he_data array is not sorted by the address of the data.  */
   struct moveinfo
@@ -445,6 +451,11 @@ gc (struct database_dyn *db)
     msync (db->head, db->data + db->head->first_free - (char *) db->head,
 	   MS_ASYNC);
 
+
+  /* Now we are done modifying the data.  */
+  ++db->head->gc_cycle;
+  assert ((db->head->gc_cycle & 1) == 0);
+
   /* We are done.  */
  out:
   pthread_mutex_unlock (&db->memlock);
diff --git a/nscd/nscd-client.h b/nscd/nscd-client.h
index 470bc6986e..708f62d6aa 100644
--- a/nscd/nscd-client.h
+++ b/nscd/nscd-client.h
@@ -23,6 +23,7 @@
 #ifndef _NSCD_CLIENT_H
 #define _NSCD_CLIENT_H	1
 
+#include <atomic.h>
 #include <nscd-types.h>
 
 /* Version number of the daemon interface */
@@ -53,6 +54,9 @@ typedef enum
   SHUTDOWN,		/* Shut the server down.  */
   GETSTAT,		/* Get the server statistic.  */
   INVALIDATE,           /* Invalidate one special cache.  */
+  GETFDPW,
+  GETFDGR,
+  GETFDHST,
   LASTREQ
 } request_type;
 
@@ -110,9 +114,151 @@ typedef struct
 } hst_response_header;
 
 
+/* Type for offsets in data part of database.  */
+typedef uint32_t ref_t;
+/* Value for invalid/no reference.  */
+#define ENDREF	UINT32_MAX
+
+/* Alignment requirement of the beginning of the data region.  */
+#define ALIGN 16
+
+
+/* Head of record in data part of database.  */
+struct datahead
+{
+  size_t allocsize;	/* Allocated Bytes.  */
+  size_t recsize;	/* Size of the record.  */
+  time_t timeout;	/* Time when this entry becomes invalid.  */
+  bool notfound;	/* Nonzero if data for key has not been found.  */
+  uint8_t nreloads;	/* Reloads without use.  */
+  bool usable;		/* False if the entry must be ignored.  */
+
+  /* We need to have the following element aligned for the response
+     header data types and their use in the 'struct dataset' types
+     defined in the XXXcache.c files.  */
+  union
+  {
+    pw_response_header pwdata;
+    gr_response_header grdata;
+    hst_response_header hstdata;
+    ssize_t align1;
+    time_t align2;
+  } data[0];
+};
+
+
+/* Structure for one hash table entry.  */
+struct hashentry
+{
+  request_type type:8;		/* Which type of dataset.  */
+  bool first;			/* True if this was the original key.  */
+  size_t len;			/* Length of key.  */
+  ref_t key;			/* Pointer to key.  */
+  uid_t owner;			/* If secure table, this is the owner.  */
+  ref_t next;			/* Next entry in this hash bucket list.  */
+  ref_t packet;			/* Records for the result.  */
+  union
+  {
+    struct hashentry *dellist;	/* Next record to be deleted.  This can be a
+				   pointer since only nscd uses this field.  */
+    ref_t *prevp;		/* Pointer to field containing forward
+				   reference.  */
+  };
+};
+
+
+/* Current persistent database version.  */
+#define DB_VERSION	1
+
+/* Maximum time allowed between updates of the timestamp.  */
+#define MAPPING_TIMEOUT (5 * 60)
+
+
+/* Header of persistent database file.  */
+struct database_pers_head
+{
+  int version;
+  int header_size;
+  int gc_cycle;
+  volatile time_t timestamp;
+
+  size_t module;
+  size_t data_size;
+
+  size_t first_free;		/* Offset of first free byte in data area.  */
+
+  size_t nentries;
+  size_t maxnentries;
+  size_t maxnsearched;
+
+  uintmax_t poshit;
+  uintmax_t neghit;
+  uintmax_t posmiss;
+  uintmax_t negmiss;
+
+  uintmax_t rdlockdelayed;
+  uintmax_t wrlockdelayed;
+
+  uintmax_t addfailed;
+
+  ref_t array[0];
+};
+
+
+/* Mapped database record.  */
+struct mapped_database
+{
+  const struct database_pers_head *head;
+  const char *data;
+  size_t mapsize;
+  int counter;		/* > 0 indicates it isusable.  */
+};
+#define NO_MAPPING ((struct mapped_database *) -1l)
+
+struct locked_map_ptr
+{
+  int lock;
+  struct mapped_database *mapped;
+};
+#define libc_locked_map_ptr(name) static struct locked_map_ptr name
+
+
 /* Open socket connection to nscd server.  */
 extern int __nscd_open_socket (const char *key, size_t keylen,
 			       request_type type, void *response,
 			       size_t responselen) attribute_hidden;
 
+/* Get reference of mapping.  */
+extern struct mapped_database *__nscd_get_map_ref (request_type type,
+						   const char *name,
+						   struct locked_map_ptr *mapptr,
+						   int *gc_cyclep);
+
+/* Unmap database.  */
+extern void __nscd_unmap (struct mapped_database *mapped);
+
+/* Drop reference of mapping.  */
+static inline int __nscd_drop_map_ref (struct mapped_database *map,
+				       int gc_cycle)
+{
+  if (map != NO_MAPPING)
+    {
+      if (__builtin_expect (map->head->gc_cycle != gc_cycle, 0))
+	/* We might have read inconsistent data.  */
+	return -1;
+
+      if (atomic_decrement_val (&map->counter) == 0)
+	__nscd_unmap (map);
+    }
+
+  return 0;
+}
+
+
+/* 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);
+
 #endif /* nscd.h */
diff --git a/nscd/nscd.conf b/nscd/nscd.conf
index 11d26b9efe..f972851116 100644
--- a/nscd/nscd.conf
+++ b/nscd/nscd.conf
@@ -19,6 +19,7 @@
 #       suggested-size		<service> <prime number>
 #	check-files		<service> <yes|no>
 #	persistent		<service> <yes|no>
+#	shared			<service> <yes|no>
 #
 # Currently supported cache names (services): passwd, group, hosts
 #
@@ -37,6 +38,7 @@
 	suggested-size		passwd		211
 	check-files		passwd		yes
 	persistent		passwd		yes
+	shared			passwd		yes
 
 	enable-cache		group		yes
 	positive-time-to-live	group		3600
@@ -44,6 +46,7 @@
 	suggested-size		group		211
 	check-files		group		yes
 	persistent		group		yes
+	shared			group		yes
 
 	enable-cache		hosts		yes
 	positive-time-to-live	hosts		3600
@@ -51,3 +54,4 @@
 	suggested-size		hosts		211
 	check-files		hosts		yes
 	persistent		hosts		yes
+	shared			hosts		yes
diff --git a/nscd/nscd.h b/nscd/nscd.h
index 45a93fdf29..889588cd9a 100644
--- a/nscd/nscd.h
+++ b/nscd/nscd.h
@@ -42,30 +42,6 @@ typedef enum
 } dbtype;
 
 
-/* Head of record in data part of database.  */
-struct datahead
-{
-  size_t allocsize;	/* Allocated Bytes.  */
-  size_t recsize;	/* Size of the record.  */
-  time_t timeout;	/* Time when this entry becomes invalid.  */
-  bool notfound;	/* Nonzero if data for key has not been found.  */
-  uint8_t nreloads;	/* Reloads without use.  */
-  bool usable;		/* False if the entry must be ignored.  */
-
-  /* We need to have the following element aligned for the response
-     header data types and their use in the 'struct dataset' types
-     defined in the XXXcache.c files.  */
-  union
-  {
-    pw_response_header pwdata;
-    gr_response_header grdata;
-    hst_response_header hstdata;
-    ssize_t align1;
-    time_t align2;
-  } data[0];
-};
-
-
 /* Default limit on the number of times a value gets reloaded without
    being used in the meantime.  NSCD does not throw a value out as
    soon as it times out.  It tries to reload the value from the
@@ -74,63 +50,6 @@ struct datahead
 #define DEFAULT_RELOAD_LIMIT 5
 
 
-/* Type for offsets in data part of database.  */
-typedef uint32_t ref_t;
-/* Value for invalid/no reference.  */
-#define ENDREF	UINT32_MAX
-
-
-/* Structure for one hash table entry.  */
-struct hashentry
-{
-  request_type type:8;		/* Which type of dataset.  */
-  bool first;			/* True if this was the original key.  */
-  size_t len;			/* Length of key.  */
-  ref_t key;			/* Pointer to key.  */
-  uid_t owner;			/* If secure table, this is the owner.  */
-  ref_t next;			/* Next entry in this hash bucket list.  */
-  ref_t packet;			/* Records for the result.  */
-  union
-  {
-    struct hashentry *dellist;	/* Next record to be deleted.  This can be a
-				   pointer since only nscd uses this field.  */
-    ref_t *prevp;		/* Pointer to field containing forward
-				   reference.  */
-  };
-};
-
-
-/* Current persistent database version.  */
-#define DB_VERSION	1
-
-/* Header of persistent database file.  */
-struct database_pers_head
-{
-  int version;
-  int header_size;
-
-  size_t module;
-  size_t data_size;
-
-  size_t first_free;		/* Offset of first free byte in data area.  */
-
-  size_t nentries;
-  size_t maxnentries;
-  size_t maxnsearched;
-
-  uintmax_t poshit;
-  uintmax_t neghit;
-  uintmax_t posmiss;
-  uintmax_t negmiss;
-
-  uintmax_t rdlockdelayed;
-  uintmax_t wrlockdelayed;
-
-  uintmax_t addfailed;
-
-  ref_t array[0];
-};
-
 /* Structure describing dynamic part of one database.  */
 struct database_dyn
 {
@@ -139,6 +58,7 @@ struct database_dyn
   int enabled;
   int check_file;
   int persistent;
+  int shared;
   const char *filename;
   const char *db_filename;
   time_t file_mtime;
diff --git a/nscd/nscd_conf.c b/nscd/nscd_conf.c
index be7527eafb..2e6f8127a7 100644
--- a/nscd/nscd_conf.c
+++ b/nscd/nscd_conf.c
@@ -216,6 +216,20 @@ nscd_parse_file (const char *fname, struct database_dyn dbs[lastdb])
 	  if (cnt == lastdb)
 	    dbg_log ("database %s is not supported\n", arg1);
 	}
+      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);
+	}
       else if (strcmp (entry, "reload-count") == 0)
 	{
 	  if (strcasecmp (arg1, "unlimited") == 0)
diff --git a/nscd/nscd_getgr_r.c b/nscd/nscd_getgr_r.c
index b9cde73044..3a66bb849b 100644
--- a/nscd/nscd_getgr_r.c
+++ b/nscd/nscd_getgr_r.c
@@ -18,6 +18,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <errno.h>
 #include <grp.h>
 #include <stdint.h>
@@ -25,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
 #include <sys/un.h>
@@ -64,36 +66,82 @@ __nscd_getgrgid_r (gid_t gid, struct group *resultbuf, char *buffer,
 }
 
 
+libc_locked_map_ptr (map_handle);
+/* Note that we only free the structure if necessary.  The memory
+   mapping is not removed since it is not visible to the malloc
+   handling.  */
+libc_freeres_fn (gr_map_free)
+{
+
+  if (map_handle.mapped != NO_MAPPING)
+    free (map_handle.mapped);
+}
+
+
 static int
 internal_function
 nscd_getgr_r (const char *key, size_t keylen, request_type type,
 	      struct group *resultbuf, char *buffer, size_t buflen,
 	      struct group **result)
 {
-  gr_response_header gr_resp;
-  int sock = __nscd_open_socket (key, keylen, type, &gr_resp,
-				 sizeof (gr_resp));
-  if (sock == -1)
+  const gr_response_header *gr_resp = NULL;
+  const uint32_t *len = NULL;
+  const char *gr_name = NULL;
+  size_t gr_name_len = 0;
+  int retval = -1;
+  int gc_cycle;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped = __nscd_get_map_ref (GETFDGR, "group",
+						       &map_handle, &gc_cycle);
+ retry:
+  if (mapped != NO_MAPPING)
     {
-      __nss_not_use_nscd_group = 1;
-      return -1;
+      const 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);
+	  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;
+	  recend = (const char *) found->data + found->recsize;
+	}
+    }
+
+  gr_response_header gr_resp_mem;
+  int sock = -1;
+  if (gr_resp == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &gr_resp_mem,
+				 sizeof (gr_resp_mem));
+      if (sock == -1)
+	{
+	  __nss_not_use_nscd_group = 1;
+	  goto out;
+	}
+
+      gr_resp = &gr_resp_mem;
     }
 
   /* No value found so far.  */
-  int retval = -1;
   *result = NULL;
 
-  if (gr_resp.found == -1)
+  if (__builtin_expect (gr_resp->found == -1, 0))
     {
       /* The daemon does not cache this database.  */
       __nss_not_use_nscd_group = 1;
-      goto out;
+      goto out_close;
     }
 
-  if (gr_resp.found == 1)
+  if (gr_resp->found == 1)
     {
       struct iovec vec[2];
-      uint32_t *len;
       char *p = buffer;
       size_t total_len;
       uintptr_t align;
@@ -103,71 +151,90 @@ 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:
 	  __set_errno (ERANGE);
 	  retval = ERANGE;
-	  goto out;
+	  goto out_close;
 	}
       buflen -= total_len;
 
       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;
 
-      /* Allocate array to store lengths.  */
-      len = (uint32_t *) alloca (gr_resp.gr_mem_cnt * sizeof (uint32_t));
+      /* Read the length information, group name, and password.  */
+      if (len == NULL)
+	{
+	  /* Allocate array to store lengths.  */
+	  len = (uint32_t *) alloca (gr_resp->gr_mem_cnt * sizeof (uint32_t));
 
-      total_len = gr_resp.gr_mem_cnt * sizeof (uint32_t);
-      vec[0].iov_base = len;
-      vec[0].iov_len = total_len;
-      vec[1].iov_base = resultbuf->gr_name;
-      vec[1].iov_len = gr_resp.gr_name_len + gr_resp.gr_passwd_len;
-      total_len += gr_resp.gr_name_len + gr_resp.gr_passwd_len;
+	  vec[0].iov_base = (void *) len;
+	  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;
+	  total_len = vec[0].iov_len + vec[1].iov_len;
 
-      /* Get this data.  */
-      size_t n = TEMP_FAILURE_RETRY (__readv (sock, vec, 2));
-      if (__builtin_expect (n != total_len, 0))
-	goto out;
+	  /* Get this data.  */
+	  size_t n = TEMP_FAILURE_RETRY (__readv (sock, vec, 2));
+	  if (__builtin_expect (n != total_len, 0))
+	    goto out_close;
+	}
+      else
+	/* We already have the data.  Just copy the group name and
+	   password.  */
+	memcpy (resultbuf->gr_name, gr_name, gr_name_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];
 	  p += len[cnt];
 	}
 
+      if (__builtin_expect (gr_name + gr_name_len + total_len > recend, 0))
+	goto out_close;
       if (__builtin_expect (total_len > buflen, 0))
 	goto no_room;
 
       retval = 0;
-      n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0],
-					     total_len));
-      if (__builtin_expect (n != total_len, 0))
+      if (gr_name == NULL)
 	{
-	  /* The `errno' to some value != ERANGE.  */
-	  __set_errno (ENOENT);
-	  retval = ENOENT;
+	  size_t n = TEMP_FAILURE_RETRY (__read (sock, resultbuf->gr_mem[0],
+						 total_len));
+	  if (__builtin_expect (n != total_len, 0))
+	    {
+	      /* The `errno' to some value != ERANGE.  */
+	      __set_errno (ENOENT);
+	      retval = ENOENT;
+	    }
+	  else
+	    *result = resultbuf;
 	}
       else
-	*result = resultbuf;
+	{
+	  /* Copy the group member names.  */
+	  memcpy (resultbuf->gr_mem[0], gr_name + gr_name_len, total_len);
+
+	  *result = resultbuf;
+	}
     }
   else
     {
@@ -177,8 +244,15 @@ nscd_getgr_r (const char *key, size_t keylen, request_type type,
       retval = 0;
     }
 
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
  out:
-  close_not_cancel_no_status (sock);
+  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.  */
+    goto retry;
 
   return retval;
 }
diff --git a/nscd/nscd_gethst_r.c b/nscd/nscd_gethst_r.c
index 7449aade80..42327dd317 100644
--- a/nscd/nscd_gethst_r.c
+++ b/nscd/nscd_gethst_r.c
@@ -17,6 +17,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <errno.h>
 #include <netdb.h>
 #include <resolv.h>
@@ -26,9 +27,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <arpa/nameser.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/un.h>
+#include <sys/mman.h>
 #include <not-cancel.h>
 
 #include "nscd-client.h"
@@ -89,56 +88,15 @@ __nscd_gethostbyaddr_r (const void *addr, socklen_t len, int type,
 }
 
 
-/* Create a socket connected to a name. */
-int
-__nscd_open_socket (const char *key, size_t keylen, request_type type,
-		    void *response, size_t responselen)
+libc_locked_map_ptr (map_handle);
+/* Note that we only free the structure if necessary.  The memory
+   mapping is not removed since it is not visible to the malloc
+   handling.  */
+libc_freeres_fn (gr_map_free)
 {
-  struct sockaddr_un addr;
-  int sock;
-  int saved_errno = errno;
-
-  sock = __socket (PF_UNIX, SOCK_STREAM, 0);
-  if (sock < 0)
-    {
-      __set_errno (saved_errno);
-      return -1;
-    }
-
-  addr.sun_family = AF_UNIX;
-  strcpy (addr.sun_path, _PATH_NSCDSOCKET);
-  if (__connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
-    {
-      __set_errno (saved_errno);
-      goto out;
-    }
-
-  request_header req;
-  req.version = NSCD_VERSION;
-  req.type = type;
-  req.key_len = keylen;
-
-  struct iovec vec[2];
-  vec[0].iov_base = &req;
-  vec[0].iov_len = sizeof (request_header);
-  vec[1].iov_base = (void *) key;
-  vec[1].iov_len = keylen;
 
-  ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
-  if (nbytes != (ssize_t) (sizeof (request_header) + keylen))
-    {
-    out:
-      close_not_cancel_no_status (sock);
-      sock = -1;
-    }
-  else
-    {
-      nbytes = TEMP_FAILURE_RETRY (__read (sock, response, responselen));
-      if (nbytes != (ssize_t) responselen)
-	goto out;
-    }
-
-  return sock;
+  if (map_handle.mapped != NO_MAPPING)
+    free (map_handle.mapped);
 }
 
 
@@ -148,30 +106,89 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
 	       struct hostent *resultbuf, char *buffer, size_t buflen,
 	       struct hostent **result, int *h_errnop)
 {
-  hst_response_header hst_resp;
-  int sock = __nscd_open_socket (key, keylen, type, &hst_resp,
-				 sizeof (hst_resp));
-  if (sock == -1)
+  const hst_response_header *hst_resp = NULL;
+  const char *h_name = NULL;
+  const uint32_t *aliases_len = NULL;
+  const char *addr_list = NULL;
+  size_t addr_list_len = 0;
+  int retval = -1;
+  int gc_cycle;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+  int sock = -1;
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped = __nscd_get_map_ref (GETFDHST, "hosts",
+						       &map_handle, &gc_cycle);
+ retry:
+  if (mapped != MAP_FAILED)
     {
-      __nss_not_use_nscd_hosts = 1;
-      return -1;
+      const 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);
+	  addr_list = ((char *) aliases_len
+		       + hst_resp->h_aliases_cnt * sizeof (uint32_t));
+	  addr_list_len = hst_resp->h_addr_list_cnt * INADDRSZ;
+
+#ifndef _STRING_ARCH_unaligned
+	  /* The aliases_len array in the mapped database might very
+	     well be unaligned.  We will access it word-wise so on
+	     platforms which do not tolerate unaligned accesses we
+	     need to make an aligned copy.  */
+	  if (((uintptr_t) aliases_len & (__alignof__ (*aliases_len) - 1))
+	      != 0)
+	    {
+	      uint32_t *tmp = alloca (hst_resp->h_aliases_cnt
+				      * sizeof (uint32_t));
+	      aliases_len = memcpy (tmp, aliases_len,
+				    hst_resp->h_aliases_cnt
+				    * sizeof (uint32_t));
+	    }
+#endif
+	  if (type != GETHOSTBYADDR && type != GETHOSTBYNAME)
+	    {
+	      if (hst_resp->h_length == INADDRSZ)
+		addr_list += addr_list_len;
+	      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;
+	}
+    }
+
+  hst_response_header hst_resp_mem;
+  if (hst_resp == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &hst_resp_mem,
+				 sizeof (hst_resp_mem));
+      if (sock == -1)
+	{
+	  __nss_not_use_nscd_hosts = 1;
+	  goto out;;
+	}
+
+      hst_resp = &hst_resp_mem;
     }
 
   /* No value found so far.  */
-  int retval = -1;
   *result = NULL;
 
-  if (hst_resp.found == -1)
+  if (__builtin_expect (hst_resp->found == -1, 0))
     {
       /* The daemon does not cache this database.  */
       __nss_not_use_nscd_hosts = 1;
-      goto out;
+      goto out_close;
     }
 
-  if (hst_resp.found == 1)
+  if (hst_resp->found == 1)
     {
       struct iovec vec[4];
-      uint32_t *aliases_len;
       char *cp = buffer;
       uintptr_t align1;
       uintptr_t align2;
@@ -185,96 +202,110 @@ 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 + 2)
+      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:
 	  __set_errno (ERANGE);
 	  retval = ERANGE;
-	  goto out;
+	  goto out_close;
 	}
       cp += align1;
 
       /* 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;
-      vec[0].iov_base = resultbuf->h_name;
-      vec[0].iov_len = hst_resp.h_name_len;
-
-      aliases_len = alloca (hst_resp.h_aliases_cnt * sizeof (uint32_t));
-      vec[1].iov_base = aliases_len;
-      vec[1].iov_len = hst_resp.h_aliases_cnt * sizeof (uint32_t);
+      cp += hst_resp->h_name_len + align2;
 
-      total_len = (hst_resp.h_name_len
-		   + hst_resp.h_aliases_cnt * sizeof (uint32_t));
-
-      n = 2;
       if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
 	{
-	  vec[2].iov_base = cp;
-	  vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
-
-	  for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
-	    {
-	      resultbuf->h_addr_list[cnt] = cp;
-	      cp += INADDRSZ;
-	    }
-
 	  resultbuf->h_addrtype = AF_INET;
 	  resultbuf->h_length = INADDRSZ;
-
-	  total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
-
-	  n = 3;
 	}
       else
 	{
-	  if (hst_resp.h_length == INADDRSZ)
-	    {
-	      ignore = alloca (hst_resp.h_addr_list_cnt * INADDRSZ);
-	      vec[2].iov_base = ignore;
-	      vec[2].iov_len = hst_resp.h_addr_list_cnt * INADDRSZ;
+	  resultbuf->h_addrtype = AF_INET6;
+	  resultbuf->h_length = IN6ADDRSZ;
+	}
+      for (cnt = 0; cnt < hst_resp->h_addr_list_cnt; ++cnt)
+	{
+	  resultbuf->h_addr_list[cnt] = cp;
+	  cp += resultbuf->h_length;
+	}
+      resultbuf->h_addr_list[cnt] = NULL;
 
-	      total_len += hst_resp.h_addr_list_cnt * INADDRSZ;
+      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;
+	  n = 1;
 
-	      n = 3;
-	    }
+	  if (hst_resp->h_aliases_cnt > 0)
+	    {
+	      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_base = cp;
-	  vec[n].iov_len = hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+	      total_len += hst_resp->h_aliases_cnt * sizeof (uint32_t);
+	      ++n;
+	    }
 
-	  for (cnt = 0; cnt < hst_resp.h_addr_list_cnt; ++cnt)
+	  if (type == GETHOSTBYADDR || type == GETHOSTBYNAME)
 	    {
-	      resultbuf->h_addr_list[cnt] = cp;
-	      cp += IN6ADDRSZ;
+	      vec[n].iov_base = resultbuf->h_addr_list[0];
+	      vec[n].iov_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)
+		{
+		  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;
 
-	  resultbuf->h_addrtype = AF_INET6;
-	  resultbuf->h_length = IN6ADDRSZ;
+		  total_len += hst_resp->h_addr_list_cnt * INADDRSZ;
 
-	  total_len += hst_resp.h_addr_list_cnt * IN6ADDRSZ;
+		  ++n;
+		}
 
-	  ++n;
-	}
-      resultbuf->h_addr_list[cnt] = NULL;
+	      vec[n].iov_base = resultbuf->h_addr_list[0];
+	      vec[n].iov_len = hst_resp->h_addr_list_cnt * IN6ADDRSZ;
 
-      if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n)) != total_len)
-	goto out;
+	      total_len += hst_resp->h_addr_list_cnt * IN6ADDRSZ;
+
+	      ++n;
+	    }
+
+	  if ((size_t) TEMP_FAILURE_RETRY (__readv (sock, vec, n))
+	      != total_len)
+	    goto out_close;
+	}
+      else
+	{
+	  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];
@@ -282,14 +313,29 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
 	}
       resultbuf->h_aliases[cnt] = NULL;
 
+      if (__builtin_expect ((const char *) addr_list + addr_list_len
+			    + total_len > recend, 0))
+	goto out_close;
       /* See whether this would exceed the buffer capacity.  */
-      if (cp > buffer + buflen)
+      if (__builtin_expect (cp > buffer + buflen, 0))
 	goto no_room;
 
       /* And finally read the aliases.  */
-      if ((size_t) TEMP_FAILURE_RETRY (__read (sock, resultbuf->h_aliases[0],
-					       total_len)) == total_len)
+      if (addr_list == NULL)
+	{
+	  if ((size_t) TEMP_FAILURE_RETRY (__read (sock,
+						   resultbuf->h_aliases[0],
+						   total_len)) == total_len)
+	    {
+	      retval = 0;
+	      *result = resultbuf;
+	    }
+	}
+      else
 	{
+	  memcpy (resultbuf->h_aliases[0],
+		  (const char *) addr_list + addr_list_len, total_len);
+
 	  retval = 0;
 	  *result = resultbuf;
 	}
@@ -297,7 +343,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);
@@ -305,8 +351,15 @@ nscd_gethst_r (const char *key, size_t keylen, request_type type,
       retval = 0;
     }
 
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
  out:
-  __close (sock);
+  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.  */
+    goto retry;
 
   return retval;
 }
diff --git a/nscd/nscd_getpw_r.c b/nscd/nscd_getpw_r.c
index 21f214d9a0..0d15a0cc3f 100644
--- a/nscd/nscd_getpw_r.c
+++ b/nscd/nscd_getpw_r.c
@@ -17,6 +17,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <errno.h>
 #include <pwd.h>
 #include <stdint.h>
@@ -24,6 +25,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
 #include <sys/un.h>
@@ -64,70 +66,124 @@ __nscd_getpwuid_r (uid_t uid, struct passwd *resultbuf, char *buffer,
 }
 
 
+libc_locked_map_ptr (map_handle);
+/* Note that we only free the structure if necessary.  The memory
+   mapping is not removed since it is not visible to the malloc
+   handling.  */
+libc_freeres_fn (gr_map_free)
+{
+
+  if (map_handle.mapped != NO_MAPPING)
+    free (map_handle.mapped);
+}
+
+
 static int
 internal_function
 nscd_getpw_r (const char *key, size_t keylen, request_type type,
 	      struct passwd *resultbuf, char *buffer, size_t buflen,
 	      struct passwd **result)
 {
-  pw_response_header pw_resp;
-  int sock = __nscd_open_socket (key, keylen, type, &pw_resp,
-				 sizeof (pw_resp));
-  if (sock == -1)
+  const pw_response_header *pw_resp = NULL;
+  const char *pw_name = NULL;
+  int retval = -1;
+  int gc_cycle;
+  const char *recend = (const char *) ~UINTMAX_C (0);
+
+  /* If the mapping is available, try to search there instead of
+     communicating with the nscd.  */
+  struct mapped_database *mapped = __nscd_get_map_ref (GETFDPW, "passwd",
+						       &map_handle, &gc_cycle);
+ retry:
+  if (mapped != NO_MAPPING)
     {
-      __nss_not_use_nscd_passwd = 1;
-      return -1;
+      const 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);
+	  recend = (const char *) found->data + found->recsize;
+	}
+    }
+
+  pw_response_header pw_resp_mem;
+  int sock = -1;
+  if (pw_resp == NULL)
+    {
+      sock = __nscd_open_socket (key, keylen, type, &pw_resp_mem,
+				 sizeof (pw_resp_mem));
+      if (sock == -1)
+	{
+	  __nss_not_use_nscd_passwd = 1;
+	  goto out;
+	}
+
+      pw_resp = &pw_resp_mem;
     }
 
   /* No value found so far.  */
-  int retval = -1;
   *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;
+      goto out_close;
     }
 
-  if (pw_resp.found == 1)
+  if (pw_resp->found == 1)
     {
-      char *p = buffer;
-      size_t total = (pw_resp.pw_name_len + pw_resp.pw_passwd_len
-		      + pw_resp.pw_gecos_len + pw_resp.pw_dir_len
-		      + pw_resp.pw_shell_len);
-
-      if (__builtin_expect (buflen < total, 0))
-	{
-	  __set_errno (ERANGE);
-	  retval = ERANGE;
-	  goto out;
-	}
-
       /* 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;
 
-      ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
+      ssize_t total = p - buffer;
+      if (__builtin_expect (pw_name + total > recend, 0))
+	goto out_close;
+      if (__builtin_expect (buflen < total, 0))
+	{
+	  __set_errno (ERANGE);
+	  retval = ERANGE;
+	  goto out_close;
+	}
 
-      if (nbytes == (ssize_t) total)
+      retval = 0;
+      if (pw_name == NULL)
+	{
+	  ssize_t nbytes = TEMP_FAILURE_RETRY (__read (sock, buffer, total));
+
+	  if (__builtin_expect (nbytes != total, 0))
+	    {
+	      /* The `errno' to some value != ERANGE.  */
+	      __set_errno (ENOENT);
+	      retval = ENOENT;
+	    }
+	  else
+	    *result = resultbuf;
+	}
+      else
 	{
-	  retval = 0;
+	  /* Copy the various strings.  */
+	  memcpy (resultbuf->pw_name, pw_name, total);
+
 	  *result = resultbuf;
 	}
     }
@@ -139,8 +195,15 @@ nscd_getpw_r (const char *key, size_t keylen, request_type type,
       retval = 0;
     }
 
+ out_close:
+  if (sock != -1)
+    close_not_cancel_no_status (sock);
  out:
-  close_not_cancel_no_status (sock);
+  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.  */
+    goto retry;
 
   return retval;
 }
diff --git a/nscd/nscd_helper.c b/nscd/nscd_helper.c
new file mode 100644
index 0000000000..95661b6ff3
--- /dev/null
+++ b/nscd/nscd_helper.c
@@ -0,0 +1,335 @@
+/* Copyright (C) 1998-2002, 2003, 2004 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.
+
+   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.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <not-cancel.h>
+#include <nis/rpcsvc/nis.h>
+
+#include "nscd-client.h"
+
+
+static int
+open_socket (void)
+{
+  int sock = __socket (PF_UNIX, SOCK_STREAM, 0);
+  if (sock < 0)
+    return -1;
+
+  /* Make socket non-blocking.  */
+  int fl = __fcntl (sock, F_GETFL);
+  if (fl != -1)
+    __fcntl (sock, F_SETFL, fl | O_NONBLOCK);
+
+  struct sockaddr_un sun;
+  sun.sun_family = AF_UNIX;
+  strcpy (sun.sun_path, _PATH_NSCDSOCKET);
+  if (__connect (sock, (struct sockaddr *) &sun, sizeof (sun)) < 0
+      && errno != EINPROGRESS)
+    goto out;
+
+  struct pollfd fds[1];
+  fds[0].fd = sock;
+  fds[0].events = POLLOUT | POLLERR | POLLHUP;
+  if (__poll (fds, 1, 5 * 1000) > 0)
+    /* Success.  We do not check for success of the connect call here.
+       If it failed, the following operations will fail.  */
+    return sock;
+
+ out:
+  close_not_cancel_no_status (sock);
+
+  return -1;
+}
+
+
+void
+__nscd_unmap (struct mapped_database *mapped)
+{
+  assert (mapped->counter == 0);
+  munmap ((void *) mapped->head, mapped->mapsize);
+  free (mapped);
+}
+
+
+/* Try to get a file descriptor for the shared meory segment
+   containing the database.  */
+static struct mapped_database *
+get_mapping (request_type type, const char *key,
+	     struct mapped_database **mappedp)
+{
+  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;
+
+  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)
+    /* 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.  */
+  iov[0].iov_base = resdata;
+  iov[0].iov_len = keylen;
+
+  char buf[CMSG_SPACE (sizeof (int))];
+  struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1,
+			.msg_control = buf, .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));
+
+  *(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.  */
+    goto out_close2;
+
+  if (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, 0)) != keylen
+      || msg.msg_controllen != CMSG_LEN (sizeof (int)))
+    goto out_close2;
+
+  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))
+    goto out_close;
+
+  struct database_pers_head head;
+  if (TEMP_FAILURE_RETRY (__pread (mapfd, &head, sizeof (head), 0))
+      != sizeof (head))
+    goto out_close;
+
+  if (head.version != DB_VERSION || head.header_size != sizeof (head)
+      /* This really should not happen but who knows, maybe the update
+	 thread got stuck.  */
+      || head.timestamp + MAPPING_TIMEOUT < time (NULL))
+    goto out_close;
+
+  size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN)
+		 + head.data_size);
+
+  if (st.st_size < size)
+    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)
+    {
+      /* Allocate a record for the mapping.  */
+      struct mapped_database *newp;
+
+      newp = malloc (sizeof (*newp));
+      if (newp == NULL)
+	{
+	  /* Ugh, after all we went through the memory allocation failed.  */
+	  munmap (result, size);
+	  goto out_close;
+	}
+
+      newp->head = mapping;
+      newp->data = ((char *) mapping + head.header_size
+		    + roundup (head.module * sizeof (ref_t), ALIGN));
+      newp->mapsize = size;
+      /* Set counter to 1 to show it is usable.  */
+      newp->counter = 1;
+
+      result = newp;
+    }
+
+ out_close:
+  __close (mapfd);
+ out_close2:
+  __close (sock);
+ out:
+  __set_errno (saved_errno);
+#endif	/* SCM_RIGHTS */
+
+  struct mapped_database *oldval = *mappedp;
+  *mappedp = result;
+
+  if (oldval != NULL && atomic_decrement_val (&oldval->counter) == 0)
+    __nscd_unmap (oldval);
+
+  return result;
+}
+
+
+struct mapped_database *
+__nscd_get_map_ref (request_type type, const char *name,
+		    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)
+    {
+      // XXX Best number of rounds?
+      if (++cnt > 5)
+	return NO_MAPPING;
+
+      atomic_delay ();
+    }
+
+  cur = mapptr->mapped;
+
+  if (__builtin_expect (cur != NO_MAPPING, 1))
+    {
+      /* If not mapped or timestamp not updated, request new map.  */
+      if (cur == NULL
+	  // XXX The following syscalls increases the cost of the entire
+	  // XXX lookup by a factor of 5 but unfortunately there is not
+	  // XXX much we can do except hoping we get a userlevel
+	  // XXX implementation soon.
+	  || cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))
+	cur = get_mapping (type, name, &mapptr->mapped);
+
+      if (__builtin_expect (cur != NO_MAPPING, 1))
+	{
+	  if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0,
+				0))
+	    cur = NO_MAPPING;
+	  else
+	    atomic_increment (&cur->counter);
+	}
+    }
+
+  mapptr->lock = 0;
+
+  return cur;
+}
+
+
+const 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;
+
+  ref_t work = mapped->head->array[hash];
+  while (work != ENDREF)
+    {
+      struct hashentry *here = (struct hashentry *) (mapped->data + work);
+
+      if (type == here->type && keylen == here->len
+	  && memcmp (key, mapped->data + here->key, keylen) == 0)
+	{
+	  /* We found the entry.  Increment the appropriate counter.  */
+	  const struct datahead *dh
+	    = (struct datahead *) (mapped->data + here->packet);
+
+	  /* 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))
+	    return dh;
+	}
+
+      work = here->next;
+    }
+
+  return NULL;
+}
+
+
+/* Create a socket connected to a name. */
+int
+__nscd_open_socket (const char *key, size_t keylen, request_type type,
+		    void *response, size_t responselen)
+{
+  int saved_errno = errno;
+
+  int sock = open_socket ();
+  if (sock >= 0)
+    {
+      request_header req;
+      req.version = NSCD_VERSION;
+      req.type = type;
+      req.key_len = keylen;
+
+      struct iovec vec[2];
+      vec[0].iov_base = &req;
+      vec[0].iov_len = sizeof (request_header);
+      vec[1].iov_base = (void *) key;
+      vec[1].iov_len = keylen;
+
+      ssize_t nbytes = TEMP_FAILURE_RETRY (__writev (sock, vec, 2));
+      if (nbytes == (ssize_t) (sizeof (request_header) + keylen))
+	{
+	  /* Wait for data.  */
+	  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;
+	    }
+	}
+
+      close_not_cancel_no_status (sock);
+    }
+
+  __set_errno (saved_errno);
+
+  return -1;
+}