This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GNU C Library master sources branch google/grte/v5-2.27/master updated. glibc-2.27-63-g5c0823e


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, google/grte/v5-2.27/master has been updated
       via  5c0823e1b96883a60114e8dd93ece4ec61c7fd86 (commit)
       via  cc94c6b7c88491afbcb1709f3ef1e1bc69d24cf5 (commit)
       via  0086b05823243307aac3e88e54603ac9ab97d001 (commit)
       via  3cc85ebe758ad7ca0fbece5e0a12aa4b0eb83363 (commit)
       via  63d64a8dd42a65ca3fa9f8822c5fda2ab0dc2452 (commit)
       via  9be560e59e09c083947cdd3e7c3cf9fd3140f9b8 (commit)
       via  e4c46226339729ed9f72ce0c8c3f938bb216553c (commit)
       via  be2e30101dc7d4e5fcde03d9520509b506d50875 (commit)
       via  b6ae0cdf4009cd2175c16abf029a5a204efd9bc9 (commit)
       via  f649403e8a5d028e0e36735306189be3aa0ed3f5 (commit)
       via  cfa5a3225d0705d1fe9617e7ca6ceb6f52ad21c8 (commit)
      from  7d75dcf2280cb7234a9d3288447bfeea3afe6305 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=5c0823e1b96883a60114e8dd93ece4ec61c7fd86

commit 5c0823e1b96883a60114e8dd93ece4ec61c7fd86
Author: Stan Shebs <stanshebs@google.com>
Date:   Thu Nov 3 16:31:52 2016 -0700

    Handle a not-found case in borg passwd lookup

diff --git a/nss/nss_borg/borg-pwd.c b/nss/nss_borg/borg-pwd.c
index 9b68b9b..3bbe6ef 100644
--- a/nss/nss_borg/borg-pwd.c
+++ b/nss/nss_borg/borg-pwd.c
@@ -86,13 +86,16 @@ static enum nss_status _nss_borg_getpwent_r_locked(struct passwd *result,
                                                    int *errnop) {
 
   enum nss_status ret;
-
+  // Save a copy of the buffer address, in case first call errors
+  // and sets it to 0.
+  struct passwd *sparecopy = result;
   if (
       f != NULL && (fgetpwent_r(f, result, buffer, buflen, &result) == 0)) {
     DEBUG("Returning borg user %d:%s\n", result->pw_uid, result->pw_name);
     ret = NSS_STATUS_SUCCESS;
   } else if (
-      fb != NULL && (fgetpwent_r(fb, result, buffer, buflen, &result) == 0)) {
+	     // Yes, this is one of those cases where an assign makes sense.
+	     fb != NULL && (result = sparecopy) && (fgetpwent_r(fb, result, buffer, buflen, &result) == 0)) {
     DEBUG("Returning base user %d:%s\n", result->pw_uid, result->pw_name);
     ret = NSS_STATUS_SUCCESS;
   } else {

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=cc94c6b7c88491afbcb1709f3ef1e1bc69d24cf5

commit cc94c6b7c88491afbcb1709f3ef1e1bc69d24cf5
Author: Michael Rothwell <mrothwell@google.com>
Date:   Fri Sep 30 09:46:48 2016 -0700

    Call the correct function.

diff --git a/nss/nss_borg/borg-pwd.c b/nss/nss_borg/borg-pwd.c
index 4f6c544..9b68b9b 100644
--- a/nss/nss_borg/borg-pwd.c
+++ b/nss/nss_borg/borg-pwd.c
@@ -87,12 +87,12 @@ static enum nss_status _nss_borg_getpwent_r_locked(struct passwd *result,
 
   enum nss_status ret;
 
-  if (f != NULL
-      && fgetpwent_r(f, result, buffer, buflen, &result) == 0) {
+  if (
+      f != NULL && (fgetpwent_r(f, result, buffer, buflen, &result) == 0)) {
     DEBUG("Returning borg user %d:%s\n", result->pw_uid, result->pw_name);
     ret = NSS_STATUS_SUCCESS;
-  } else if (fb != NULL
-             && getpwent_r(fb, result, buffer, buflen, &result) == 0) {
+  } else if (
+      fb != NULL && (fgetpwent_r(fb, result, buffer, buflen, &result) == 0)) {
     DEBUG("Returning base user %d:%s\n", result->pw_uid, result->pw_name);
     ret = NSS_STATUS_SUCCESS;
   } else {

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=0086b05823243307aac3e88e54603ac9ab97d001

commit 0086b05823243307aac3e88e54603ac9ab97d001
Author: Michael Rothwell <mrothwell@google.com>
Date:   Fri Sep 30 06:09:04 2016 -0700

    Augment borg-pwd to also search through passwd.borg.base, if it exists.

diff --git a/nss/nss_borg/borg-pwd.c b/nss/nss_borg/borg-pwd.c
index c4ff035..4f6c544 100644
--- a/nss/nss_borg/borg-pwd.c
+++ b/nss/nss_borg/borg-pwd.c
@@ -1,9 +1,6 @@
-// Copyright 2004 Google Inc.
-// Author: Paul Menage
-
-// An NSS module that extends local user account lookup to the file /etc/passwd.borg
-// (Despite the suggestive name, passwd.borg is just a second file in the standard
-// passwd format, separated for various reasons. -sts 2015)
+// An NSS module that extends local user account lookup to the files
+// /etc/passwd.borg and /etc/passwd.borg.base
+// passwd.borg.base is a subset of passwd.borg that is used as a fallback.
 
 #include <stdio.h>
 #include <pwd.h>
@@ -18,6 +15,7 @@ __libc_lock_define_initialized (static, lock)
 #define NSSBORG_UNLOCK  __libc_lock_unlock (lock);
 
 static FILE *f;
+static FILE *fb;
 
 #define DEBUG(fmt, ...)
 
@@ -29,7 +27,10 @@ static enum nss_status _nss_borg_setpwent_locked(void) {
   DEBUG("Opening passwd.borg\n");
   f = fopen("/etc/passwd.borg", "r");
 
-  if (f) {
+  DEBUG("Opening passwd.borg.base\n");
+  fb = fopen("/etc/passwd.borg.base", "r");
+
+  if (f||fb) {
     return NSS_STATUS_SUCCESS;
   } else {
     return NSS_STATUS_UNAVAIL;
@@ -58,6 +59,11 @@ static enum nss_status _nss_borg_endpwent_locked(void) {
     fclose(f);
     f = NULL;
   }
+  DEBUG("Closing passwd.borg.base\n");
+  if (fb) {
+    fclose(fb);
+    fb = NULL;
+  }
   return NSS_STATUS_SUCCESS;
 }
 
@@ -81,8 +87,13 @@ static enum nss_status _nss_borg_getpwent_r_locked(struct passwd *result,
 
   enum nss_status ret;
 
-  if (fgetpwent_r(f, result, buffer, buflen, &result) == 0) {
-    DEBUG("Returning user %d:%s\n", result->pw_uid, result->pw_name);
+  if (f != NULL
+      && fgetpwent_r(f, result, buffer, buflen, &result) == 0) {
+    DEBUG("Returning borg user %d:%s\n", result->pw_uid, result->pw_name);
+    ret = NSS_STATUS_SUCCESS;
+  } else if (fb != NULL
+             && getpwent_r(fb, result, buffer, buflen, &result) == 0) {
+    DEBUG("Returning base user %d:%s\n", result->pw_uid, result->pw_name);
     ret = NSS_STATUS_SUCCESS;
   } else {
     *errnop = errno;

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=3cc85ebe758ad7ca0fbece5e0a12aa4b0eb83363

commit 3cc85ebe758ad7ca0fbece5e0a12aa4b0eb83363
Author: Stan Shebs <stanshebs@google.com>
Date:   Fri Aug 21 14:50:53 2015 -0700

    Describe borg-pwd better, remove dead code.

diff --git a/nss/nss_borg/borg-pwd.c b/nss/nss_borg/borg-pwd.c
index afa696c..c4ff035 100644
--- a/nss/nss_borg/borg-pwd.c
+++ b/nss/nss_borg/borg-pwd.c
@@ -2,6 +2,8 @@
 // Author: Paul Menage
 
 // An NSS module that extends local user account lookup to the file /etc/passwd.borg
+// (Despite the suggestive name, passwd.borg is just a second file in the standard
+// passwd format, separated for various reasons. -sts 2015)
 
 #include <stdio.h>
 #include <pwd.h>
@@ -10,17 +12,10 @@
 #include <errno.h>
 #include <string.h>
 
-#ifdef NSSBORG_STANDALONE
-#include <pthread.h>
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-#define NSSBORG_LOCK  pthread_mutex_lock(&mutex)
-#define NSSBORG_UNLOCK pthread_mutex_unlock(&mutex)
-#else
 #include <libc-lock.h>
 __libc_lock_define_initialized (static, lock)
 #define NSSBORG_LOCK  __libc_lock_lock (lock)
 #define NSSBORG_UNLOCK  __libc_lock_unlock (lock);
-#endif
 
 static FILE *f;
 

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=63d64a8dd42a65ca3fa9f8822c5fda2ab0dc2452

commit 63d64a8dd42a65ca3fa9f8822c5fda2ab0dc2452
Author: Max Kanat-Alexander <mkanat@google.com>
Date:   Wed Jul 6 14:47:51 2016 -0700

    Update nss_cache.c and nss_cache.h to current upstream version. This makes getgrgid_r and similar calls over 18x faster on corp machines.

diff --git a/nss/nss_cache/nss_cache.c b/nss/nss_cache/nss_cache.c
index 6613dbb..53fa9de 100644
--- a/nss/nss_cache/nss_cache.c
+++ b/nss/nss_cache/nss_cache.c
@@ -22,6 +22,8 @@
 
 #include "nss_cache.h"
 
+#include <sys/mman.h>
+
 // Locking implementation: use pthreads.
 #include <pthread.h>
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -38,10 +40,15 @@ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static FILE *p_file = NULL;
 static FILE *g_file = NULL;
-static FILE *s_file = NULL;
 static char p_filename[NSS_CACHE_PATH_LENGTH] = "/etc/passwd.cache";
 static char g_filename[NSS_CACHE_PATH_LENGTH] = "/etc/group.cache";
+#ifndef BSD
+static FILE *s_file = NULL;
 static char s_filename[NSS_CACHE_PATH_LENGTH] = "/etc/shadow.cache";
+#else
+extern int fgetpwent_r(FILE *, struct passwd *, char *, size_t, struct passwd **);
+extern int fgetgrent_r(FILE *, struct group *, char *, size_t, struct group **);
+#endif // ifndef BSD
 
 /* Common return code routine for all *ent_r_locked functions.
  * We need to return TRYAGAIN if the underlying files guy raises ERANGE,
@@ -52,14 +59,14 @@ static inline enum nss_status _nss_cache_ent_bad_return_code(int errnoval) {
   enum nss_status ret;
 
   switch (errnoval) {
-    case ERANGE:
-      DEBUG("ERANGE: Try again with a bigger buffer\n");
-      ret = NSS_STATUS_TRYAGAIN;
-      break;
-    case ENOENT:
-    default:
-      DEBUG("ENOENT or default case: Not found\n");
-      ret = NSS_STATUS_NOTFOUND;
+  case ERANGE:
+    DEBUG("ERANGE: Try again with a bigger buffer\n");
+    ret = NSS_STATUS_TRYAGAIN;
+    break;
+  case ENOENT:
+  default:
+    DEBUG("ENOENT or default case: Not found\n");
+    ret = NSS_STATUS_NOTFOUND;
   };
   return ret;
 }
@@ -68,111 +75,27 @@ static inline enum nss_status _nss_cache_ent_bad_return_code(int errnoval) {
 // Binary search routines below here
 //
 
-// _nss_cache_bsearch_lookup()
-// Binary search through a sorted nss file for a single record.
-
-enum nss_status _nss_cache_bsearch_lookup(FILE *file,
-                                         struct nss_cache_args *args,
-                                         int *errnop) {
-  enum nss_cache_match (*lookup)(
-                                  FILE *,
-                                  struct nss_cache_args *
-                                 ) = args->lookup_function;
-  long min = 0;
-  long max;
-  long pos;
-
-  // get the size of the file
-  if (fseek(file, 0, SEEK_END) != 0) {
-    DEBUG("fseek fail\n");
-    return NSS_STATUS_UNAVAIL;
-  }
-  max = ftell(file);
-
-  // binary search until we are within 100 chars of the right line
-  while (min + 100 < max) {
-    pos = (min + max) / 2;
-
-    if (fseek(file, pos, SEEK_SET) != 0) {
-      DEBUG("fseek fail\n");
-      return NSS_STATUS_UNAVAIL;
-    }
-
-    // scan forward to the start of the next line.
-    for (;;) {
-      int c = getc_unlocked(file);
-      if (c == EOF) break;
-      ++pos;
-      if (c == '\n') break;
-    }
-
-    // break if we've stopped making progress in this loop (long lines)
-    if (pos <= min || pos >= max) {
-      break;
-    }
-
-    // see if this line matches
-    switch (lookup(file, args)) {
-      case NSS_CACHE_EXACT:
-        return NSS_STATUS_SUCCESS;  // done!
-      case NSS_CACHE_HIGH:
-        max = pos;
-        continue;  // search again
-      case NSS_CACHE_LOW:
-        min = pos;
-        continue;  // search again
-      case NSS_CACHE_ERROR:
-        if (errno == ERANGE) {
-          // let the caller retry
-          *errnop = errno;
-          return _nss_cache_ent_bad_return_code(*errnop);
-        }
-        DEBUG("expected error %s [errno=%d] from lookup function\n",
-              strerror(errno), errno);
-        return NSS_STATUS_UNAVAIL;
-    }
-  }
+int _nss_cache_bsearch2_compare(const void *key, const void *value) {
+  struct nss_cache_args *args = (struct nss_cache_args *)key;
+  const char *value_text = (const char *)value;
 
-  // fall back on a linear search in the remaining space
-  DEBUG("Switching to linear scan\n");
-  pos = min - 100;  // back 100, might be in the middle of the right line
-  if (fseek(file, pos, SEEK_SET) != 0) {
-    DEBUG("fseek fail\n");
-    return NSS_STATUS_UNAVAIL;
-  }
-
-  while (pos < max) {
-    switch (lookup(file, args)) {
-      case NSS_CACHE_EXACT:
-        return NSS_STATUS_SUCCESS;
-      case NSS_CACHE_HIGH:
-      case NSS_CACHE_LOW:
-        pos = ftell(file);
-        continue;
-      case NSS_CACHE_ERROR:
-        if (errno == ERANGE) {
-          // let the caller retry
-          *errnop = errno;
-          return _nss_cache_ent_bad_return_code(*errnop);
-        }
-        break;
-    }
-    break;
-  }
-
-  return NSS_STATUS_NOTFOUND;
+  // Using strcmp as the generation of the index sorts without
+  // locale awareness.
+  return strcmp(args->lookup_key, value_text);
 }
 
-// _nss_cache_bsearch()
-// If a sorted nss file is present, attempt a binary search on it.
-
-enum nss_status _nss_cache_bsearch(struct nss_cache_args *args, int *errnop) {
+enum nss_status _nss_cache_bsearch2(struct nss_cache_args *args, int *errnop) {
+  enum nss_cache_match (*lookup)(FILE *, struct nss_cache_args *) =
+      args->lookup_function;
   FILE *file = NULL;
+  FILE *system_file_stream = NULL;
   struct stat system_file;
   struct stat sorted_file;
-  enum nss_status ret;
+  enum nss_status ret = 100;
+  long offset = 0;
+  void *mapped_data = NULL;
 
-  file =  fopen(args->sorted_filename, "r");
+  file = fopen(args->sorted_filename, "r");
   if (file == NULL) {
     DEBUG("error opening %s\n", args->sorted_filename);
     return NSS_STATUS_UNAVAIL;
@@ -197,11 +120,66 @@ enum nss_status _nss_cache_bsearch(struct nss_cache_args *args, int *errnop) {
     return NSS_STATUS_UNAVAIL;
   }
 
-  ret = _nss_cache_bsearch_lookup(file, args, errnop);
+  mapped_data =
+      mmap(NULL, sorted_file.st_size, PROT_READ, MAP_PRIVATE, fileno(file), 0);
+  if (mapped_data == MAP_FAILED) {
+    DEBUG("mmap failed\n");
+    fclose(file);
+    return NSS_STATUS_UNAVAIL;
+  }
 
+  const char *data = (const char *)mapped_data;
+  while (*data != '\n') {
+    ++data;
+  }
+  long entry_size = data - (const char *)mapped_data + 1;
+  long entry_count = sorted_file.st_size / entry_size;
+
+  void *entry = bsearch(args, mapped_data, entry_count, entry_size,
+                        &_nss_cache_bsearch2_compare);
+  if (entry != NULL) {
+    const char *entry_text = entry;
+    sscanf(entry_text + strlen(entry_text) + 1, "%ld", &offset);
+  }
+
+  if (munmap(mapped_data, sorted_file.st_size) == -1) {
+    DEBUG("munmap failed\n");
+  }
   fclose(file);
-  return ret;
 
+  if (entry == NULL) {
+    return NSS_STATUS_NOTFOUND;
+  }
+
+  system_file_stream = fopen(args->system_filename, "r");
+  if (system_file_stream == NULL) {
+    DEBUG("error opening %s\n", args->system_filename);
+    return NSS_STATUS_UNAVAIL;
+  }
+
+  if (fseek(system_file_stream, offset, SEEK_SET) != 0) {
+    DEBUG("fseek fail\n");
+    return NSS_STATUS_UNAVAIL;
+  }
+
+  switch (lookup(system_file_stream, args)) {
+  case NSS_CACHE_EXACT:
+    ret = NSS_STATUS_SUCCESS;
+    break;
+  case NSS_CACHE_ERROR:
+    if (errno == ERANGE) {
+      // let the caller retry
+      *errnop = errno;
+      ret = _nss_cache_ent_bad_return_code(*errnop);
+    }
+    break;
+  default:
+    ret = NSS_STATUS_UNAVAIL;
+    break;
+  }
+
+  fclose(system_file_stream);
+  return ret;
 }
 
 //
@@ -211,18 +189,17 @@ enum nss_status _nss_cache_bsearch(struct nss_cache_args *args, int *errnop) {
 // _nss_cache_setpwent_path()
 // Helper function for testing
 
-extern char* _nss_cache_setpwent_path(const char *path) {
+extern char *_nss_cache_setpwent_path(const char *path) {
 
   DEBUG("%s %s\n", "Setting p_filename to", path);
   return strncpy(p_filename, path, NSS_CACHE_PATH_LENGTH - 1);
-
 }
 
 // _nss_cache_pwuid_wrap()
 // Internal wrapper for binary searches, using uid-specific calls.
 
 static enum nss_cache_match _nss_cache_pwuid_wrap(FILE *file,
-                                                 struct nss_cache_args *args) {
+                                                  struct nss_cache_args *args) {
   struct passwd *result = args->lookup_result;
   uid_t *uid = args->lookup_value;
 
@@ -246,7 +223,7 @@ static enum nss_cache_match _nss_cache_pwuid_wrap(FILE *file,
 // Internal wrapper for binary searches, using username-specific calls.
 
 static enum nss_cache_match _nss_cache_pwnam_wrap(FILE *file,
-                                                 struct nss_cache_args *args) {
+                                                  struct nss_cache_args *args) {
   struct passwd *result = args->lookup_result;
   char *name = args->lookup_value;
   int ret;
@@ -323,8 +300,8 @@ enum nss_status _nss_cache_endpwent(void) {
 // Called internally to return the next entry from the passwd file
 
 static enum nss_status _nss_cache_getpwent_r_locked(struct passwd *result,
-                                                   char *buffer, size_t buflen,
-                                                   int *errnop) {
+                                                    char *buffer, size_t buflen,
+                                                    int *errnop) {
   enum nss_status ret = NSS_STATUS_SUCCESS;
 
   if (p_file == NULL) {
@@ -336,6 +313,9 @@ static enum nss_status _nss_cache_getpwent_r_locked(struct passwd *result,
     if (fgetpwent_r(p_file, result, buffer, buflen, &result) == 0) {
       DEBUG("Returning user %d:%s\n", result->pw_uid, result->pw_name);
     } else {
+      if (errno == ENOENT) {
+        errno = 0;
+      }
       *errnop = errno;
       ret = _nss_cache_ent_bad_return_code(*errnop);
     }
@@ -347,9 +327,8 @@ static enum nss_status _nss_cache_getpwent_r_locked(struct passwd *result,
 // _nss_cache_getpwent_r()
 // Called by NSS to look up next entry in passwd file
 
-enum nss_status _nss_cache_getpwent_r(struct passwd *result,
-                                     char *buffer, size_t buflen,
-                                     int *errnop) {
+enum nss_status _nss_cache_getpwent_r(struct passwd *result, char *buffer,
+                                      size_t buflen, int *errnop) {
   enum nss_status ret;
   NSS_CACHE_LOCK();
   ret = _nss_cache_getpwent_r_locked(result, buffer, buflen, errnop);
@@ -361,8 +340,8 @@ enum nss_status _nss_cache_getpwent_r(struct passwd *result,
 // Find a user account by uid
 
 enum nss_status _nss_cache_getpwuid_r(uid_t uid, struct passwd *result,
-                                     char *buffer, size_t buflen,
-                                     int *errnop) {
+                                      char *buffer, size_t buflen,
+                                      int *errnop) {
   char filename[NSS_CACHE_PATH_LENGTH];
   struct nss_cache_args args;
   enum nss_status ret;
@@ -372,7 +351,7 @@ enum nss_status _nss_cache_getpwuid_r(uid_t uid, struct passwd *result,
     DEBUG("filename too long\n");
     return NSS_STATUS_UNAVAIL;
   }
-  strncat(filename, ".byuid", 6);
+  strncat(filename, ".ixuid", 6);
 
   args.sorted_filename = filename;
   args.system_filename = p_filename;
@@ -381,28 +360,22 @@ enum nss_status _nss_cache_getpwuid_r(uid_t uid, struct passwd *result,
   args.lookup_result = result;
   args.buffer = buffer;
   args.buflen = buflen;
+  char uid_text[11];
+  snprintf(uid_text, sizeof(uid_text), "%d", uid);
+  args.lookup_key = uid_text;
+  args.lookup_key_length = strlen(uid_text);
 
   DEBUG("Binary search for uid %d\n", uid);
   NSS_CACHE_LOCK();
-  ret = _nss_cache_bsearch(&args, errnop);
-
-  // TODO(vasilios): make this a runtime or compile-time option, as this slows
-  // down legitimate misses as the trade off for safety.
-  if (ret == NSS_STATUS_NOTFOUND) {
-    DEBUG("Binary search returned nothing.\n");
-    ret = NSS_STATUS_UNAVAIL;
-  }
+  ret = _nss_cache_bsearch2(&args, errnop);
 
   if (ret == NSS_STATUS_UNAVAIL) {
     DEBUG("Binary search failed, falling back to full linear search\n");
     ret = _nss_cache_setpwent_locked();
 
     if (ret == NSS_STATUS_SUCCESS) {
-      while ((ret = _nss_cache_getpwent_r_locked(result,
-                                                buffer,
-                                                buflen,
-                                                errnop))
-             == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getpwent_r_locked(
+                  result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) {
         if (result->pw_uid == uid)
           break;
       }
@@ -419,8 +392,8 @@ enum nss_status _nss_cache_getpwuid_r(uid_t uid, struct passwd *result,
 // Find a user account by name
 
 enum nss_status _nss_cache_getpwnam_r(const char *name, struct passwd *result,
-                                     char *buffer, size_t buflen,
-                                     int *errnop) {
+                                      char *buffer, size_t buflen,
+                                      int *errnop) {
   char *pw_name;
   char filename[NSS_CACHE_PATH_LENGTH];
   struct nss_cache_args args;
@@ -442,7 +415,7 @@ enum nss_status _nss_cache_getpwnam_r(const char *name, struct passwd *result,
     free(pw_name);
     return NSS_STATUS_UNAVAIL;
   }
-  strncat(filename, ".byname", 7);
+  strncat(filename, ".ixname", 7);
 
   args.sorted_filename = filename;
   args.system_filename = p_filename;
@@ -451,27 +424,19 @@ enum nss_status _nss_cache_getpwnam_r(const char *name, struct passwd *result,
   args.lookup_result = result;
   args.buffer = buffer;
   args.buflen = buflen;
+  args.lookup_key = pw_name;
+  args.lookup_key_length = strlen(pw_name);
 
   DEBUG("Binary search for user %s\n", pw_name);
-  ret = _nss_cache_bsearch(&args, errnop);
-
-  // TODO(vasilios): make this a runtime or compile-time option, as this slows
-  // down legitimate misses as the trade off for safety.
-  if (ret == NSS_STATUS_NOTFOUND) {
-    DEBUG("Binary search returned nothing.\n");
-    ret = NSS_STATUS_UNAVAIL;
-  }
+  ret = _nss_cache_bsearch2(&args, errnop);
 
   if (ret == NSS_STATUS_UNAVAIL) {
     DEBUG("Binary search failed, falling back to full linear search\n");
     ret = _nss_cache_setpwent_locked();
 
     if (ret == NSS_STATUS_SUCCESS) {
-      while ((ret = _nss_cache_getpwent_r_locked(result,
-                                                buffer,
-                                                buflen,
-                                                errnop))
-             == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getpwent_r_locked(
+                  result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) {
         if (!strcmp(result->pw_name, name))
           break;
       }
@@ -492,11 +457,10 @@ enum nss_status _nss_cache_getpwnam_r(const char *name, struct passwd *result,
 // _nss_cache_setgrent_path()
 // Helper function for testing
 
-extern char* _nss_cache_setgrent_path(const char *path) {
+extern char *_nss_cache_setgrent_path(const char *path) {
 
   DEBUG("%s %s\n", "Setting g_filename to", path);
   return strncpy(g_filename, path, NSS_CACHE_PATH_LENGTH - 1);
-
 }
 
 // _nss_cache_setgrent_locked()
@@ -518,7 +482,7 @@ static enum nss_status _nss_cache_setgrent_locked(void) {
 // Internal wrapper for binary searches, using gid-specific calls.
 
 static enum nss_cache_match _nss_cache_grgid_wrap(FILE *file,
-                                                 struct nss_cache_args *args) {
+                                                  struct nss_cache_args *args) {
   struct group *result = args->lookup_result;
   gid_t *gid = args->lookup_value;
 
@@ -542,7 +506,7 @@ static enum nss_cache_match _nss_cache_grgid_wrap(FILE *file,
 // Internal wrapper for binary searches, using groupname-specific calls.
 
 static enum nss_cache_match _nss_cache_grnam_wrap(FILE *file,
-                                                 struct nss_cache_args *args) {
+                                                  struct nss_cache_args *args) {
   struct group *result = args->lookup_result;
   char *name = args->lookup_value;
   int ret;
@@ -604,8 +568,8 @@ enum nss_status _nss_cache_endgrent(void) {
 // Called internally to return the next entry from the group file
 
 static enum nss_status _nss_cache_getgrent_r_locked(struct group *result,
-                                                   char *buffer, size_t buflen,
-                                                   int *errnop) {
+                                                    char *buffer, size_t buflen,
+                                                    int *errnop) {
   enum nss_status ret = NSS_STATUS_SUCCESS;
 
   if (g_file == NULL) {
@@ -627,7 +591,11 @@ static enum nss_status _nss_cache_getgrent_r_locked(struct group *result,
        * something similar in CONCAT(_nss_files_get,ENTNAME_r) (around
        * line 242 in glibc 2.4 sources).
        */
-      fsetpos(g_file, &position);
+      if (errno == ENOENT) {
+        errno = 0;
+      } else {
+        fsetpos(g_file, &position);
+      }
       *errnop = errno;
       ret = _nss_cache_ent_bad_return_code(*errnop);
     }
@@ -639,9 +607,8 @@ static enum nss_status _nss_cache_getgrent_r_locked(struct group *result,
 // _nss_cache_getgrent_r()
 // Called by NSS to look up next entry in group file
 
-enum nss_status _nss_cache_getgrent_r(struct group *result,
-                                     char *buffer, size_t buflen,
-                                     int *errnop) {
+enum nss_status _nss_cache_getgrent_r(struct group *result, char *buffer,
+                                      size_t buflen, int *errnop) {
   enum nss_status ret;
   NSS_CACHE_LOCK();
   ret = _nss_cache_getgrent_r_locked(result, buffer, buflen, errnop);
@@ -653,18 +620,26 @@ enum nss_status _nss_cache_getgrent_r(struct group *result,
 // Find a group by gid
 
 enum nss_status _nss_cache_getgrgid_r(gid_t gid, struct group *result,
-                                     char *buffer, size_t buflen,
-                                     int *errnop) {
+                                      char *buffer, size_t buflen,
+                                      int *errnop) {
   char filename[NSS_CACHE_PATH_LENGTH];
   struct nss_cache_args args;
   enum nss_status ret;
 
+  // Since we binary search over the groups using the user provided
+  // buffer, we do not start searching before we have a buffer that
+  // is big enough to have a high chance of succeeding.
+  if (buflen < (1 << 20)) {
+    *errnop = ERANGE;
+    return NSS_STATUS_TRYAGAIN;
+  }
+
   strncpy(filename, g_filename, NSS_CACHE_PATH_LENGTH - 1);
   if (strlen(filename) > NSS_CACHE_PATH_LENGTH - 7) {
     DEBUG("filename too long\n");
     return NSS_STATUS_UNAVAIL;
   }
-  strncat(filename, ".bygid", 6);
+  strncat(filename, ".ixgid", 6);
 
   args.sorted_filename = filename;
   args.system_filename = g_filename;
@@ -673,28 +648,22 @@ enum nss_status _nss_cache_getgrgid_r(gid_t gid, struct group *result,
   args.lookup_result = result;
   args.buffer = buffer;
   args.buflen = buflen;
+  char gid_text[11];
+  snprintf(gid_text, sizeof(gid_text), "%d", gid);
+  args.lookup_key = gid_text;
+  args.lookup_key_length = strlen(gid_text);
 
   DEBUG("Binary search for gid %d\n", gid);
   NSS_CACHE_LOCK();
-  ret = _nss_cache_bsearch(&args, errnop);
-
-  // TODO(vasilios): make this a runtime or compile-time option, as this slows
-  // down legitimate misses as the trade off for safety.
-  if (ret == NSS_STATUS_NOTFOUND) {
-    DEBUG("Binary search returned nothing.\n");
-    ret = NSS_STATUS_UNAVAIL;
-  }
+  ret = _nss_cache_bsearch2(&args, errnop);
 
   if (ret == NSS_STATUS_UNAVAIL) {
     DEBUG("Binary search failed, falling back to full linear search\n");
     ret = _nss_cache_setgrent_locked();
 
     if (ret == NSS_STATUS_SUCCESS) {
-      while ((ret = _nss_cache_getgrent_r_locked(result,
-                                                buffer,
-                                                buflen,
-                                                errnop))
-             == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getgrent_r_locked(
+                  result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) {
         if (result->gr_gid == gid)
           break;
       }
@@ -711,8 +680,8 @@ enum nss_status _nss_cache_getgrgid_r(gid_t gid, struct group *result,
 // Find a group by name
 
 enum nss_status _nss_cache_getgrnam_r(const char *name, struct group *result,
-                                     char *buffer, size_t buflen,
-                                     int *errnop) {
+                                      char *buffer, size_t buflen,
+                                      int *errnop) {
   char *gr_name;
   char filename[NSS_CACHE_PATH_LENGTH];
   struct nss_cache_args args;
@@ -734,7 +703,7 @@ enum nss_status _nss_cache_getgrnam_r(const char *name, struct group *result,
     free(gr_name);
     return NSS_STATUS_UNAVAIL;
   }
-  strncat(filename, ".byname", 7);
+  strncat(filename, ".ixname", 7);
 
   args.sorted_filename = filename;
   args.system_filename = g_filename;
@@ -743,27 +712,19 @@ enum nss_status _nss_cache_getgrnam_r(const char *name, struct group *result,
   args.lookup_result = result;
   args.buffer = buffer;
   args.buflen = buflen;
+  args.lookup_key = gr_name;
+  args.lookup_key_length = strlen(gr_name);
 
   DEBUG("Binary search for group %s\n", gr_name);
-  ret = _nss_cache_bsearch(&args, errnop);
-
-  // TODO(vasilios): make this a runtime or compile-time option, as this slows
-  // down legitimate misses as the trade off for safety.
-  if (ret == NSS_STATUS_NOTFOUND) {
-    DEBUG("Binary search returned nothing.\n");
-    ret = NSS_STATUS_UNAVAIL;
-  }
+  ret = _nss_cache_bsearch2(&args, errnop);
 
   if (ret == NSS_STATUS_UNAVAIL) {
     DEBUG("Binary search failed, falling back to full linear search\n");
     ret = _nss_cache_setgrent_locked();
 
     if (ret == NSS_STATUS_SUCCESS) {
-      while ((ret = _nss_cache_getgrent_r_locked(result,
-                                                buffer,
-                                                buflen,
-                                                errnop))
-             == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getgrent_r_locked(
+                  result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) {
         if (!strcmp(result->gr_name, name))
           break;
       }
@@ -780,15 +741,15 @@ enum nss_status _nss_cache_getgrnam_r(const char *name, struct group *result,
 //
 //  Routines for shadow map defined here.
 //
+#ifndef BSD
 
 // _nss_cache_setspent_path()
 // Helper function for testing
 
-extern char* _nss_cache_setspent_path(const char *path) {
+extern char *_nss_cache_setspent_path(const char *path) {
 
   DEBUG("%s %s\n", "Setting s_filename to", path);
   return strncpy(s_filename, path, NSS_CACHE_PATH_LENGTH - 1);
-
 }
 
 // _nss_cache_setspent_locked()
@@ -810,7 +771,7 @@ static enum nss_status _nss_cache_setspent_locked(void) {
 // Internal wrapper for binary searches, using shadow-specific calls.
 
 static enum nss_cache_match _nss_cache_spnam_wrap(FILE *file,
-                                                 struct nss_cache_args *args) {
+                                                  struct nss_cache_args *args) {
   struct spwd *result = args->lookup_result;
   char *name = args->lookup_value;
   int ret;
@@ -872,8 +833,8 @@ enum nss_status _nss_cache_endspent(void) {
 // Called internally to return the next entry from the shadow file
 
 static enum nss_status _nss_cache_getspent_r_locked(struct spwd *result,
-                                                   char *buffer, size_t buflen,
-                                                   int *errnop) {
+                                                    char *buffer, size_t buflen,
+                                                    int *errnop) {
 
   enum nss_status ret = NSS_STATUS_SUCCESS;
 
@@ -886,6 +847,9 @@ static enum nss_status _nss_cache_getspent_r_locked(struct spwd *result,
     if (fgetspent_r(s_file, result, buffer, buflen, &result) == 0) {
       DEBUG("Returning shadow entry %s\n", result->sp_namp);
     } else {
+      if (errno == ENOENT) {
+        errno = 0;
+      }
       *errnop = errno;
       ret = _nss_cache_ent_bad_return_code(*errnop);
     }
@@ -897,9 +861,8 @@ static enum nss_status _nss_cache_getspent_r_locked(struct spwd *result,
 // _nss_cache_getspent_r()
 // Called by NSS to look up next entry in the shadow file
 
-enum nss_status _nss_cache_getspent_r(struct spwd *result,
-                                     char *buffer, size_t buflen,
-                                     int *errnop) {
+enum nss_status _nss_cache_getspent_r(struct spwd *result, char *buffer,
+                                      size_t buflen, int *errnop) {
   enum nss_status ret;
   NSS_CACHE_LOCK();
   ret = _nss_cache_getspent_r_locked(result, buffer, buflen, errnop);
@@ -911,8 +874,8 @@ enum nss_status _nss_cache_getspent_r(struct spwd *result,
 // Find a user by name
 
 enum nss_status _nss_cache_getspnam_r(const char *name, struct spwd *result,
-                                     char *buffer, size_t buflen,
-                                     int *errnop) {
+                                      char *buffer, size_t buflen,
+                                      int *errnop) {
   char *sp_namp;
   char filename[NSS_CACHE_PATH_LENGTH];
   struct nss_cache_args args;
@@ -934,7 +897,7 @@ enum nss_status _nss_cache_getspnam_r(const char *name, struct spwd *result,
     free(sp_namp);
     return NSS_STATUS_UNAVAIL;
   }
-  strncat(filename, ".byname", 7);
+  strncat(filename, ".ixname", 7);
 
   args.sorted_filename = filename;
   args.system_filename = s_filename;
@@ -943,27 +906,19 @@ enum nss_status _nss_cache_getspnam_r(const char *name, struct spwd *result,
   args.lookup_result = result;
   args.buffer = buffer;
   args.buflen = buflen;
+  args.lookup_key = sp_namp;
+  args.lookup_key_length = strlen(sp_namp);
 
   DEBUG("Binary search for user %s\n", sp_namp);
-  ret = _nss_cache_bsearch(&args, errnop);
-
-  // TODO(vasilios): make this a runtime or compile-time option, as this slows
-  // down legitimate misses as the trade off for safety.
-  if (ret == NSS_STATUS_NOTFOUND) {
-    DEBUG("Binary search returned nothing.\n");
-    ret = NSS_STATUS_UNAVAIL;
-  }
+  ret = _nss_cache_bsearch2(&args, errnop);
 
   if (ret == NSS_STATUS_UNAVAIL) {
     DEBUG("Binary search failed, falling back to full linear search\n");
     ret = _nss_cache_setspent_locked();
 
     if (ret == NSS_STATUS_SUCCESS) {
-      while ((ret = _nss_cache_getspent_r_locked(result,
-                                                buffer,
-                                                buflen,
-                                                errnop))
-             == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getspent_r_locked(
+                  result, buffer, buflen, errnop)) == NSS_STATUS_SUCCESS) {
         if (!strcmp(result->sp_namp, name))
           break;
       }
@@ -976,3 +931,6 @@ enum nss_status _nss_cache_getspnam_r(const char *name, struct spwd *result,
 
   return ret;
 }
+#else
+#include "bsdnss.c"
+#endif // ifndef BSD
diff --git a/nss/nss_cache/nss_cache.h b/nss/nss_cache/nss_cache.h
index 8ce4b97..1780be6 100644
--- a/nss/nss_cache/nss_cache.h
+++ b/nss/nss_cache/nss_cache.h
@@ -21,29 +21,40 @@
 #include <nss.h>
 #include <stdlib.h>
 #include <pwd.h>
-#include <shadow.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/param.h>
 #include <time.h>
 #include <unistd.h>
 
+#ifndef BSD
+#include <shadow.h>
+#endif // ifndef BSD
+
 #ifndef NSS_CACHE_H
 #define NSS_CACHE_H
 
 #ifdef DEBUG
 #undef DEBUG
-#define DEBUG(fmt, args...)  do { fprintf(stderr, fmt, ##args); } while (0)
+#define DEBUG(fmt, args...)                                                    \
+  do {                                                                         \
+    fprintf(stderr, fmt, ##args);                                              \
+  } while (0)
 #else
-#define DEBUG(fmt, ...)      do { } while (0)
+#define DEBUG(fmt, ...)                                                        \
+  do {                                                                         \
+  } while (0)
 #endif /* DEBUG */
 
 #define NSS_CACHE_PATH_LENGTH 255
-extern char* _nss_cache_setpwent_path(const char *path);
-extern char* _nss_cache_setgrent_path(const char *path);
-extern char* _nss_cache_setspent_path(const char *path);
+extern char *_nss_cache_setpwent_path(const char *path);
+extern char *_nss_cache_setgrent_path(const char *path);
+#ifndef BSD
+extern char *_nss_cache_setspent_path(const char *path);
+#endif // ifndef BSD
 
 enum nss_cache_match {
   NSS_CACHE_EXACT = 0,
@@ -60,6 +71,8 @@ struct nss_cache_args {
   void *lookup_result;
   char *buffer;
   size_t buflen;
+  char *lookup_key;
+  size_t lookup_key_length;
 };
 
 #endif /* NSS_CACHE_H */

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=9be560e59e09c083947cdd3e7c3cf9fd3140f9b8

commit 9be560e59e09c083947cdd3e7c3cf9fd3140f9b8
Author: Stan Shebs <stanshebs@google.com>
Date:   Wed Mar 14 08:17:45 2018 -0700

    Update an include path

diff --git a/nss/nss_borg/borg-pwd.c b/nss/nss_borg/borg-pwd.c
index dc8206a..afa696c 100644
--- a/nss/nss_borg/borg-pwd.c
+++ b/nss/nss_borg/borg-pwd.c
@@ -16,7 +16,7 @@ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 #define NSSBORG_LOCK  pthread_mutex_lock(&mutex)
 #define NSSBORG_UNLOCK pthread_mutex_unlock(&mutex)
 #else
-#include <bits/libc-lock.h>
+#include <libc-lock.h>
 __libc_lock_define_initialized (static, lock)
 #define NSSBORG_LOCK  __libc_lock_lock (lock)
 #define NSSBORG_UNLOCK  __libc_lock_unlock (lock);

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=e4c46226339729ed9f72ce0c8c3f938bb216553c

commit e4c46226339729ed9f72ce0c8c3f938bb216553c
Author: Paul Pluzhnikov <ppluzhnikov@google.com>
Date:   Fri Feb 28 12:54:06 2014 -0800

    Integrate nss_{borg,cache} local changes from glibc-2.18 to 2.19

diff --git a/include/grp.h b/include/grp.h
index 871701a..16dd8bd 100644
--- a/include/grp.h
+++ b/include/grp.h
@@ -53,6 +53,7 @@ extern enum nss_status _nss_ ## service ##_initgroups_dyn		   \
 			long int *size, gid_t **groupsp, long int limit,   \
 			int *errnop);
 
+DECLARE_NSS_PROTOTYPES (cache)
 DECLARE_NSS_PROTOTYPES (compat)
 DECLARE_NSS_PROTOTYPES (files)
 DECLARE_NSS_PROTOTYPES (hesiod)
diff --git a/include/pwd.h b/include/pwd.h
index fc99506..de8272c 100644
--- a/include/pwd.h
+++ b/include/pwd.h
@@ -45,8 +45,10 @@ extern enum nss_status _nss_ ## service ##_getpwent_r			\
 		       (struct passwd *result, char *buffer,		\
 			size_t buflen, int *errnop);
 
+DECLARE_NSS_PROTOTYPES (cache)
 DECLARE_NSS_PROTOTYPES (compat)
 DECLARE_NSS_PROTOTYPES (files)
+DECLARE_NSS_PROTOTYPES (borg)
 DECLARE_NSS_PROTOTYPES (hesiod)
 DECLARE_NSS_PROTOTYPES (nis)
 DECLARE_NSS_PROTOTYPES (nisplus)
diff --git a/include/shadow.h b/include/shadow.h
index 366ea83..3b7bf77 100644
--- a/include/shadow.h
+++ b/include/shadow.h
@@ -41,6 +41,7 @@ extern enum nss_status _nss_ ## service ## _getspnam_r			\
 		       (const char *name, struct spwd *pwd,		\
 			char *buffer, size_t buflen, int *errnop);
 
+DECLARE_NSS_PROTOTYPES (cache)
 DECLARE_NSS_PROTOTYPES (compat)
 DECLARE_NSS_PROTOTYPES (files)
 DECLARE_NSS_PROTOTYPES (hesiod)
diff --git a/nss/Makefile b/nss/Makefile
index a5cd2aa..375f060 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -73,7 +73,7 @@ tests += tst-cancel-getpwuid_r
 endif
 
 # Specify rules for the nss_* modules.  We have some services.
-services		:= files db compat
+services		:= files db compat borg cache
 
 extra-libs		= $(services:%=libnss_%)
 # These libraries will be built in the `others' pass rather than
@@ -87,6 +87,8 @@ vpath %.c $(subdir-dirs) ../locale/programs ../intl
 
 libnss_files-routines	:= $(addprefix files-,$(databases)) \
 			   files-initgroups files-init
+libnss_borg-routines	:= borg-pwd
+libnss_cache-routines	:= nss_cache
 
 libnss_db-dbs		:= $(addprefix db-,\
 				       $(filter-out hosts network key alias,\
@@ -104,10 +106,14 @@ install-others		+= $(inst_vardbdir)/Makefile
 # Build static module into libc if requested
 libnss_files-inhibit-o	= $(filter-out .os,$(object-suffixes))
 libnss_db-inhibit-o	= $(filter-out .os,$(object-suffixes))
+libnss_borg-inhibit-o	= $(filter-out .os,$(object-suffixes))
+libnss_cache-inhibit-o	= $(filter-out .os,$(object-suffixes))
 libnss_compat-inhibit-o	= $(filter-out .os,$(object-suffixes))
 ifeq ($(build-static-nss),yes)
-routines                += $(libnss_files-routines)
-static-only-routines    += $(libnss_files-routines)
+routines                += $(libnss_files-routines) $(libnss_borg-routines) \
+                           $(libnss_cache-routines)
+static-only-routines    += $(libnss_files-routines) $(libnss_borg-routines) \
+                           $(libnss_cache-routines)
 tests-static		+= tst-nss-static
 endif
 extra-test-objs		+= nss_test1.os nss_test2.os
diff --git a/nss/Versions b/nss/Versions
index db8c887..f472d64 100644
--- a/nss/Versions
+++ b/nss/Versions
@@ -174,3 +174,32 @@ libnss_compat {
     _nss_compat_initgroups_dyn;
   }
 }
+
+libnss_borg {
+  GLIBC_PRIVATE {
+    _nss_borg_setpwent;
+    _nss_borg_endpwent;
+    _nss_borg_getpwent_r;
+    _nss_borg_getpwnam_r;
+    _nss_borg_getpwuid_r;
+  }
+}
+
+libnss_cache {
+  GLIBC_PRIVATE {
+    _nss_cache_setpwent;
+    _nss_cache_endpwent;
+    _nss_cache_getpwent_r;
+    _nss_cache_getpwuid_r;
+    _nss_cache_getpwnam_r;
+    _nss_cache_setgrent;
+    _nss_cache_endgrent;
+    _nss_cache_getgrent_r;
+    _nss_cache_getgrgid_r;
+    _nss_cache_getgrnam_r;
+    _nss_cache_setspent;
+    _nss_cache_endspent;
+    _nss_cache_getspent_r;
+    _nss_cache_getspnam_r;
+  }
+}
diff --git a/nss/function.def b/nss/function.def
index 1a38d5a..6464a92 100644
--- a/nss/function.def
+++ b/nss/function.def
@@ -31,6 +31,9 @@ DEFINE_ENT (files, ether)
 DEFINE_ENT (files, gr)
 DEFINE_GET (files, grgid)
 DEFINE_GET (files, grnam)
+DEFINE_ENT (cache, gr)
+DEFINE_GET (cache, grgid)
+DEFINE_GET (cache, grnam)
 
 /* hosts */
 DEFINE_ENT (files, host)
@@ -62,6 +65,11 @@ DEFINE_GETBY (files, proto, number)
 DEFINE_ENT (files, pw)
 DEFINE_GET (files, pwnam)
 DEFINE_GET (files, pwuid)
+DEFINE_GET (borg, pwnam)  /* /etc/passwd2 */
+DEFINE_GET (borg, pwuid)
+DEFINE_ENT (cache, pw)
+DEFINE_GET (cache, pwnam)
+DEFINE_GET (cache, pwuid)
 
 /* rpc */
 DEFINE_ENT (files, rpc)
@@ -76,3 +84,5 @@ DEFINE_GETBY (files, serv, port)
 /* shadow */
 DEFINE_ENT (files, sp)
 DEFINE_GET (files, spnam)
+DEFINE_ENT (cache, sp)
+DEFINE_GET (cache, spnam)
diff --git a/nss/nss_borg/borg-pwd.c b/nss/nss_borg/borg-pwd.c
new file mode 100644
index 0000000..dc8206a
--- /dev/null
+++ b/nss/nss_borg/borg-pwd.c
@@ -0,0 +1,171 @@
+// Copyright 2004 Google Inc.
+// Author: Paul Menage
+
+// An NSS module that extends local user account lookup to the file /etc/passwd.borg
+
+#include <stdio.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <nss.h>
+#include <errno.h>
+#include <string.h>
+
+#ifdef NSSBORG_STANDALONE
+#include <pthread.h>
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+#define NSSBORG_LOCK  pthread_mutex_lock(&mutex)
+#define NSSBORG_UNLOCK pthread_mutex_unlock(&mutex)
+#else
+#include <bits/libc-lock.h>
+__libc_lock_define_initialized (static, lock)
+#define NSSBORG_LOCK  __libc_lock_lock (lock)
+#define NSSBORG_UNLOCK  __libc_lock_unlock (lock);
+#endif
+
+static FILE *f;
+
+#define DEBUG(fmt, ...)
+
+// _nss_borg_setpwent_locked()
+// Internal setup routine
+
+static enum nss_status _nss_borg_setpwent_locked(void) {
+
+  DEBUG("Opening passwd.borg\n");
+  f = fopen("/etc/passwd.borg", "r");
+
+  if (f) {
+    return NSS_STATUS_SUCCESS;
+  } else {
+    return NSS_STATUS_UNAVAIL;
+  }
+}
+
+// _nss_borg_setpwent()
+// Called by NSS to open the passwd file
+// Oddly, NSS passes a boolean saying whether to keep the database file open; ignore it
+
+enum nss_status _nss_borg_setpwent(int stayopen) {
+  enum nss_status ret;
+  NSSBORG_LOCK;
+  ret = _nss_borg_setpwent_locked();
+  NSSBORG_UNLOCK;
+  return ret;
+}
+
+// _nss_borg_endpwent_locked()
+// Internal close routine
+
+static enum nss_status _nss_borg_endpwent_locked(void) {
+
+  DEBUG("Closing passwd.borg\n");
+  if (f) {
+    fclose(f);
+    f = NULL;
+  }
+  return NSS_STATUS_SUCCESS;
+}
+
+// _nss_borg_endpwent()
+// Called by NSS to close the passwd file
+
+enum nss_status _nss_borg_endpwent() {
+  enum nss_status ret;
+  NSSBORG_LOCK;
+  ret = _nss_borg_endpwent_locked();
+  NSSBORG_UNLOCK;
+  return ret;
+}
+
+// _nss_borg_getpwent_r_locked()
+// Called internally to return the next entry from the passwd file
+
+static enum nss_status _nss_borg_getpwent_r_locked(struct passwd *result,
+                                                   char *buffer, size_t buflen,
+                                                   int *errnop) {
+
+  enum nss_status ret;
+
+  if (fgetpwent_r(f, result, buffer, buflen, &result) == 0) {
+    DEBUG("Returning user %d:%s\n", result->pw_uid, result->pw_name);
+    ret = NSS_STATUS_SUCCESS;
+  } else {
+    *errnop = errno;
+    switch (*errnop) {
+      case ERANGE:
+        ret = NSS_STATUS_TRYAGAIN;
+        break;
+      case ENOENT:
+      default:
+        ret = NSS_STATUS_NOTFOUND;
+    }
+  }
+
+  return ret;
+}
+
+// _nss_borg_getpwent_r()
+// Called by NSS (I think) to look up next entry in passwd file
+enum nss_status _nss_borg_getpwent_r(struct passwd *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+  enum nss_status ret;
+  NSSBORG_LOCK;
+  ret = _nss_borg_getpwent_r_locked(result, buffer, buflen, errnop);
+  NSSBORG_UNLOCK;
+  return ret;
+}
+
+// _nss_borg_getpwuid_r()
+// Find a user account by uid
+
+enum nss_status _nss_borg_getpwuid_r(uid_t uid, struct passwd *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+
+  enum nss_status ret;
+
+  NSSBORG_LOCK;
+  ret = _nss_borg_setpwent_locked();
+  DEBUG("Looking for uid %d\n", uid);
+
+  if (ret == NSS_STATUS_SUCCESS) {
+    while ((ret = _nss_borg_getpwent_r_locked(result, buffer, buflen, errnop))
+           == NSS_STATUS_SUCCESS) {
+      if (result->pw_uid == uid)
+        break;
+    }
+  }
+
+  _nss_borg_endpwent_locked();
+  NSSBORG_UNLOCK;
+
+  return ret;
+}
+
+// _nss_borg_getpwnam_r()
+// Find a user account by name
+
+enum nss_status _nss_borg_getpwnam_r(const char *name, struct passwd *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+
+  enum nss_status ret;
+
+  NSSBORG_LOCK;
+  ret = _nss_borg_setpwent_locked();
+  DEBUG("Looking for user %s\n", name);
+
+  if (ret == NSS_STATUS_SUCCESS) {
+    while ((ret = _nss_borg_getpwent_r_locked(result, buffer, buflen, errnop))
+           == NSS_STATUS_SUCCESS) {
+      if (!strcmp(result->pw_name, name))
+        break;
+    }
+  }
+
+  _nss_borg_endpwent_locked();
+  NSSBORG_UNLOCK;
+
+  return ret;
+}
diff --git a/nss/nss_cache/nss_cache.c b/nss/nss_cache/nss_cache.c
new file mode 100644
index 0000000..6613dbb
--- /dev/null
+++ b/nss/nss_cache/nss_cache.c
@@ -0,0 +1,978 @@
+/* Copyright 2009 Google Inc.
+ *
+ * This 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 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA
+ */
+
+/* An NSS module which adds supports for file maps with a trailing .cache
+ * suffix (/etc/passwd.cache, /etc/group.cache, and /etc/shadow.cache)
+ */
+
+#include "nss_cache.h"
+
+// Locking implementation: use pthreads.
+#include <pthread.h>
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#pragma weak pthread_mutex_lock
+#define NSS_CACHE_LOCK()    do { \
+    if (&pthread_mutex_lock != NULL) pthread_mutex_lock(&mutex); \
+  } while (0)
+
+#pragma weak pthread_mutex_unlock
+#define NSS_CACHE_UNLOCK()  do { \
+    if (&pthread_mutex_unlock != NULL) pthread_mutex_unlock(&mutex); \
+  } while (0)
+
+static FILE *p_file = NULL;
+static FILE *g_file = NULL;
+static FILE *s_file = NULL;
+static char p_filename[NSS_CACHE_PATH_LENGTH] = "/etc/passwd.cache";
+static char g_filename[NSS_CACHE_PATH_LENGTH] = "/etc/group.cache";
+static char s_filename[NSS_CACHE_PATH_LENGTH] = "/etc/shadow.cache";
+
+/* Common return code routine for all *ent_r_locked functions.
+ * We need to return TRYAGAIN if the underlying files guy raises ERANGE,
+ * so that our caller knows to try again with a bigger buffer.
+ */
+
+static inline enum nss_status _nss_cache_ent_bad_return_code(int errnoval) {
+  enum nss_status ret;
+
+  switch (errnoval) {
+    case ERANGE:
+      DEBUG("ERANGE: Try again with a bigger buffer\n");
+      ret = NSS_STATUS_TRYAGAIN;
+      break;
+    case ENOENT:
+    default:
+      DEBUG("ENOENT or default case: Not found\n");
+      ret = NSS_STATUS_NOTFOUND;
+  };
+  return ret;
+}
+
+//
+// Binary search routines below here
+//
+
+// _nss_cache_bsearch_lookup()
+// Binary search through a sorted nss file for a single record.
+
+enum nss_status _nss_cache_bsearch_lookup(FILE *file,
+                                         struct nss_cache_args *args,
+                                         int *errnop) {
+  enum nss_cache_match (*lookup)(
+                                  FILE *,
+                                  struct nss_cache_args *
+                                 ) = args->lookup_function;
+  long min = 0;
+  long max;
+  long pos;
+
+  // get the size of the file
+  if (fseek(file, 0, SEEK_END) != 0) {
+    DEBUG("fseek fail\n");
+    return NSS_STATUS_UNAVAIL;
+  }
+  max = ftell(file);
+
+  // binary search until we are within 100 chars of the right line
+  while (min + 100 < max) {
+    pos = (min + max) / 2;
+
+    if (fseek(file, pos, SEEK_SET) != 0) {
+      DEBUG("fseek fail\n");
+      return NSS_STATUS_UNAVAIL;
+    }
+
+    // scan forward to the start of the next line.
+    for (;;) {
+      int c = getc_unlocked(file);
+      if (c == EOF) break;
+      ++pos;
+      if (c == '\n') break;
+    }
+
+    // break if we've stopped making progress in this loop (long lines)
+    if (pos <= min || pos >= max) {
+      break;
+    }
+
+    // see if this line matches
+    switch (lookup(file, args)) {
+      case NSS_CACHE_EXACT:
+        return NSS_STATUS_SUCCESS;  // done!
+      case NSS_CACHE_HIGH:
+        max = pos;
+        continue;  // search again
+      case NSS_CACHE_LOW:
+        min = pos;
+        continue;  // search again
+      case NSS_CACHE_ERROR:
+        if (errno == ERANGE) {
+          // let the caller retry
+          *errnop = errno;
+          return _nss_cache_ent_bad_return_code(*errnop);
+        }
+        DEBUG("expected error %s [errno=%d] from lookup function\n",
+              strerror(errno), errno);
+        return NSS_STATUS_UNAVAIL;
+    }
+  }
+
+  // fall back on a linear search in the remaining space
+  DEBUG("Switching to linear scan\n");
+  pos = min - 100;  // back 100, might be in the middle of the right line
+  if (fseek(file, pos, SEEK_SET) != 0) {
+    DEBUG("fseek fail\n");
+    return NSS_STATUS_UNAVAIL;
+  }
+
+  while (pos < max) {
+    switch (lookup(file, args)) {
+      case NSS_CACHE_EXACT:
+        return NSS_STATUS_SUCCESS;
+      case NSS_CACHE_HIGH:
+      case NSS_CACHE_LOW:
+        pos = ftell(file);
+        continue;
+      case NSS_CACHE_ERROR:
+        if (errno == ERANGE) {
+          // let the caller retry
+          *errnop = errno;
+          return _nss_cache_ent_bad_return_code(*errnop);
+        }
+        break;
+    }
+    break;
+  }
+
+  return NSS_STATUS_NOTFOUND;
+}
+
+// _nss_cache_bsearch()
+// If a sorted nss file is present, attempt a binary search on it.
+
+enum nss_status _nss_cache_bsearch(struct nss_cache_args *args, int *errnop) {
+  FILE *file = NULL;
+  struct stat system_file;
+  struct stat sorted_file;
+  enum nss_status ret;
+
+  file =  fopen(args->sorted_filename, "r");
+  if (file == NULL) {
+    DEBUG("error opening %s\n", args->sorted_filename);
+    return NSS_STATUS_UNAVAIL;
+  }
+
+  // if the sorted file is older than the system file, do not risk stale
+  // data and abort
+  // TODO(vasilios):  should be a compile or runtime option
+  if (stat(args->system_filename, &system_file) != 0) {
+    DEBUG("failed to stat %s\n", args->system_filename);
+    fclose(file);
+    return NSS_STATUS_UNAVAIL;
+  }
+  if (fstat(fileno(file), &sorted_file) != 0) {
+    DEBUG("failed to stat %s\n", args->sorted_filename);
+    fclose(file);
+    return NSS_STATUS_UNAVAIL;
+  }
+  if (difftime(system_file.st_mtime, sorted_file.st_mtime) > 0) {
+    DEBUG("%s may be stale, aborting lookup\n", args->sorted_filename);
+    fclose(file);
+    return NSS_STATUS_UNAVAIL;
+  }
+
+  ret = _nss_cache_bsearch_lookup(file, args, errnop);
+
+  fclose(file);
+  return ret;
+
+}
+
+//
+// Routines for passwd map defined below here
+//
+
+// _nss_cache_setpwent_path()
+// Helper function for testing
+
+extern char* _nss_cache_setpwent_path(const char *path) {
+
+  DEBUG("%s %s\n", "Setting p_filename to", path);
+  return strncpy(p_filename, path, NSS_CACHE_PATH_LENGTH - 1);
+
+}
+
+// _nss_cache_pwuid_wrap()
+// Internal wrapper for binary searches, using uid-specific calls.
+
+static enum nss_cache_match _nss_cache_pwuid_wrap(FILE *file,
+                                                 struct nss_cache_args *args) {
+  struct passwd *result = args->lookup_result;
+  uid_t *uid = args->lookup_value;
+
+  if (fgetpwent_r(file, result, args->buffer, args->buflen, &result) == 0) {
+    if (result->pw_uid == *uid) {
+      DEBUG("SUCCESS: found user %d:%s\n", result->pw_uid, result->pw_name);
+      return NSS_CACHE_EXACT;
+    }
+    DEBUG("Failed match at uid %d\n", result->pw_uid);
+    if (result->pw_uid > *uid) {
+      return NSS_CACHE_HIGH;
+    } else {
+      return NSS_CACHE_LOW;
+    }
+  }
+
+  return NSS_CACHE_ERROR;
+}
+
+// _nss_cache_pwnam_wrap()
+// Internal wrapper for binary searches, using username-specific calls.
+
+static enum nss_cache_match _nss_cache_pwnam_wrap(FILE *file,
+                                                 struct nss_cache_args *args) {
+  struct passwd *result = args->lookup_result;
+  char *name = args->lookup_value;
+  int ret;
+
+  if (fgetpwent_r(file, result, args->buffer, args->buflen, &result) == 0) {
+    ret = strcoll(result->pw_name, name);
+    if (ret == 0) {
+      DEBUG("SUCCESS: found user %s\n", result->pw_name);
+      return NSS_CACHE_EXACT;
+    }
+    DEBUG("Failed match at name %s\n", result->pw_name);
+    if (ret > 0) {
+      return NSS_CACHE_HIGH;
+    } else {
+      return NSS_CACHE_LOW;
+    }
+  }
+
+  return NSS_CACHE_ERROR;
+}
+
+// _nss_cache_setpwent_locked()
+// Internal setup routine
+
+static enum nss_status _nss_cache_setpwent_locked(void) {
+
+  DEBUG("%s %s\n", "Opening", p_filename);
+  p_file = fopen(p_filename, "r");
+
+  if (p_file) {
+    return NSS_STATUS_SUCCESS;
+  } else {
+    return NSS_STATUS_UNAVAIL;
+  }
+}
+
+// _nss_cache_setpwent()
+// Called by NSS to open the passwd file
+// 'stayopen' parameter is ignored.
+
+enum nss_status _nss_cache_setpwent(int stayopen) {
+  enum nss_status ret;
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_setpwent_locked();
+  NSS_CACHE_UNLOCK();
+  return ret;
+}
+
+// _nss_cache_endpwent_locked()
+// Internal close routine
+
+static enum nss_status _nss_cache_endpwent_locked(void) {
+
+  DEBUG("Closing passwd.cache\n");
+  if (p_file) {
+    fclose(p_file);
+    p_file = NULL;
+  }
+  return NSS_STATUS_SUCCESS;
+}
+
+// _nss_cache_endpwent()
+// Called by NSS to close the passwd file
+
+enum nss_status _nss_cache_endpwent(void) {
+  enum nss_status ret;
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_endpwent_locked();
+  NSS_CACHE_UNLOCK();
+  return ret;
+}
+
+// _nss_cache_getpwent_r_locked()
+// Called internally to return the next entry from the passwd file
+
+static enum nss_status _nss_cache_getpwent_r_locked(struct passwd *result,
+                                                   char *buffer, size_t buflen,
+                                                   int *errnop) {
+  enum nss_status ret = NSS_STATUS_SUCCESS;
+
+  if (p_file == NULL) {
+    DEBUG("p_file == NULL, going to setpwent\n");
+    ret = _nss_cache_setpwent_locked();
+  }
+
+  if (ret == NSS_STATUS_SUCCESS) {
+    if (fgetpwent_r(p_file, result, buffer, buflen, &result) == 0) {
+      DEBUG("Returning user %d:%s\n", result->pw_uid, result->pw_name);
+    } else {
+      *errnop = errno;
+      ret = _nss_cache_ent_bad_return_code(*errnop);
+    }
+  }
+
+  return ret;
+}
+
+// _nss_cache_getpwent_r()
+// Called by NSS to look up next entry in passwd file
+
+enum nss_status _nss_cache_getpwent_r(struct passwd *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+  enum nss_status ret;
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_getpwent_r_locked(result, buffer, buflen, errnop);
+  NSS_CACHE_UNLOCK();
+  return ret;
+}
+
+// _nss_cache_getpwuid_r()
+// Find a user account by uid
+
+enum nss_status _nss_cache_getpwuid_r(uid_t uid, struct passwd *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+  char filename[NSS_CACHE_PATH_LENGTH];
+  struct nss_cache_args args;
+  enum nss_status ret;
+
+  strncpy(filename, p_filename, NSS_CACHE_PATH_LENGTH - 1);
+  if (strlen(filename) > NSS_CACHE_PATH_LENGTH - 7) {
+    DEBUG("filename too long\n");
+    return NSS_STATUS_UNAVAIL;
+  }
+  strncat(filename, ".byuid", 6);
+
+  args.sorted_filename = filename;
+  args.system_filename = p_filename;
+  args.lookup_function = _nss_cache_pwuid_wrap;
+  args.lookup_value = &uid;
+  args.lookup_result = result;
+  args.buffer = buffer;
+  args.buflen = buflen;
+
+  DEBUG("Binary search for uid %d\n", uid);
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_bsearch(&args, errnop);
+
+  // TODO(vasilios): make this a runtime or compile-time option, as this slows
+  // down legitimate misses as the trade off for safety.
+  if (ret == NSS_STATUS_NOTFOUND) {
+    DEBUG("Binary search returned nothing.\n");
+    ret = NSS_STATUS_UNAVAIL;
+  }
+
+  if (ret == NSS_STATUS_UNAVAIL) {
+    DEBUG("Binary search failed, falling back to full linear search\n");
+    ret = _nss_cache_setpwent_locked();
+
+    if (ret == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getpwent_r_locked(result,
+                                                buffer,
+                                                buflen,
+                                                errnop))
+             == NSS_STATUS_SUCCESS) {
+        if (result->pw_uid == uid)
+          break;
+      }
+    }
+  }
+
+  _nss_cache_endpwent_locked();
+  NSS_CACHE_UNLOCK();
+
+  return ret;
+}
+
+// _nss_cache_getpwnam_r()
+// Find a user account by name
+
+enum nss_status _nss_cache_getpwnam_r(const char *name, struct passwd *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+  char *pw_name;
+  char filename[NSS_CACHE_PATH_LENGTH];
+  struct nss_cache_args args;
+  enum nss_status ret;
+
+  NSS_CACHE_LOCK();
+
+  // name is a const char, we need a non-const copy
+  pw_name = malloc(strlen(name) + 1);
+  if (pw_name == NULL) {
+    DEBUG("malloc error\n");
+    return NSS_STATUS_UNAVAIL;
+  }
+  strncpy(pw_name, name, strlen(name) + 1);
+
+  strncpy(filename, p_filename, NSS_CACHE_PATH_LENGTH - 1);
+  if (strlen(filename) > NSS_CACHE_PATH_LENGTH - 8) {
+    DEBUG("filename too long\n");
+    free(pw_name);
+    return NSS_STATUS_UNAVAIL;
+  }
+  strncat(filename, ".byname", 7);
+
+  args.sorted_filename = filename;
+  args.system_filename = p_filename;
+  args.lookup_function = _nss_cache_pwnam_wrap;
+  args.lookup_value = pw_name;
+  args.lookup_result = result;
+  args.buffer = buffer;
+  args.buflen = buflen;
+
+  DEBUG("Binary search for user %s\n", pw_name);
+  ret = _nss_cache_bsearch(&args, errnop);
+
+  // TODO(vasilios): make this a runtime or compile-time option, as this slows
+  // down legitimate misses as the trade off for safety.
+  if (ret == NSS_STATUS_NOTFOUND) {
+    DEBUG("Binary search returned nothing.\n");
+    ret = NSS_STATUS_UNAVAIL;
+  }
+
+  if (ret == NSS_STATUS_UNAVAIL) {
+    DEBUG("Binary search failed, falling back to full linear search\n");
+    ret = _nss_cache_setpwent_locked();
+
+    if (ret == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getpwent_r_locked(result,
+                                                buffer,
+                                                buflen,
+                                                errnop))
+             == NSS_STATUS_SUCCESS) {
+        if (!strcmp(result->pw_name, name))
+          break;
+      }
+    }
+  }
+
+  free(pw_name);
+  _nss_cache_endpwent_locked();
+  NSS_CACHE_UNLOCK();
+
+  return ret;
+}
+
+//
+//  Routines for group map defined here.
+//
+
+// _nss_cache_setgrent_path()
+// Helper function for testing
+
+extern char* _nss_cache_setgrent_path(const char *path) {
+
+  DEBUG("%s %s\n", "Setting g_filename to", path);
+  return strncpy(g_filename, path, NSS_CACHE_PATH_LENGTH - 1);
+
+}
+
+// _nss_cache_setgrent_locked()
+// Internal setup routine
+
+static enum nss_status _nss_cache_setgrent_locked(void) {
+
+  DEBUG("%s %s\n", "Opening", g_filename);
+  g_file = fopen(g_filename, "r");
+
+  if (g_file) {
+    return NSS_STATUS_SUCCESS;
+  } else {
+    return NSS_STATUS_UNAVAIL;
+  }
+}
+
+// _nss_cache_grgid_wrap()
+// Internal wrapper for binary searches, using gid-specific calls.
+
+static enum nss_cache_match _nss_cache_grgid_wrap(FILE *file,
+                                                 struct nss_cache_args *args) {
+  struct group *result = args->lookup_result;
+  gid_t *gid = args->lookup_value;
+
+  if (fgetgrent_r(file, result, args->buffer, args->buflen, &result) == 0) {
+    if (result->gr_gid == *gid) {
+      DEBUG("SUCCESS: found group %d:%s\n", result->gr_gid, result->gr_name);
+      return NSS_CACHE_EXACT;
+    }
+    DEBUG("Failed match at gid %d\n", result->gr_gid);
+    if (result->gr_gid > *gid) {
+      return NSS_CACHE_HIGH;
+    } else {
+      return NSS_CACHE_LOW;
+    }
+  }
+
+  return NSS_CACHE_ERROR;
+}
+
+// _nss_cache_grnam_wrap()
+// Internal wrapper for binary searches, using groupname-specific calls.
+
+static enum nss_cache_match _nss_cache_grnam_wrap(FILE *file,
+                                                 struct nss_cache_args *args) {
+  struct group *result = args->lookup_result;
+  char *name = args->lookup_value;
+  int ret;
+
+  if (fgetgrent_r(file, result, args->buffer, args->buflen, &result) == 0) {
+    ret = strcoll(result->gr_name, name);
+    if (ret == 0) {
+      DEBUG("SUCCESS: found group %s\n", result->gr_name);
+      return NSS_CACHE_EXACT;
+    }
+    DEBUG("Failed match at name %s\n", result->gr_name);
+    if (ret > 0) {
+      return NSS_CACHE_HIGH;
+    } else {
+      return NSS_CACHE_LOW;
+    }
+  }
+
+  return NSS_CACHE_ERROR;
+}
+
+// _nss_cache_setgrent()
+// Called by NSS to open the group file
+// 'stayopen' parameter is ignored.
+
+enum nss_status _nss_cache_setgrent(int stayopen) {
+  enum nss_status ret;
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_setgrent_locked();
+  NSS_CACHE_UNLOCK();
+  return ret;
+}
+
+// _nss_cache_endgrent_locked()
+// Internal close routine
+
+static enum nss_status _nss_cache_endgrent_locked(void) {
+
+  DEBUG("Closing group.cache\n");
+  if (g_file) {
+    fclose(g_file);
+    g_file = NULL;
+  }
+  return NSS_STATUS_SUCCESS;
+}
+
+// _nss_cache_endgrent()
+// Called by NSS to close the group file
+
+enum nss_status _nss_cache_endgrent(void) {
+  enum nss_status ret;
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_endgrent_locked();
+  NSS_CACHE_UNLOCK();
+  return ret;
+}
+
+// _nss_cache_getgrent_r_locked()
+// Called internally to return the next entry from the group file
+
+static enum nss_status _nss_cache_getgrent_r_locked(struct group *result,
+                                                   char *buffer, size_t buflen,
+                                                   int *errnop) {
+  enum nss_status ret = NSS_STATUS_SUCCESS;
+
+  if (g_file == NULL) {
+    DEBUG("g_file == NULL, going to setgrent\n");
+    ret = _nss_cache_setgrent_locked();
+  }
+
+  if (ret == NSS_STATUS_SUCCESS) {
+    fpos_t position;
+
+    fgetpos(g_file, &position);
+    if (fgetgrent_r(g_file, result, buffer, buflen, &result) == 0) {
+      DEBUG("Returning group %s (%d)\n", result->gr_name, result->gr_gid);
+    } else {
+      /* Rewind back to where we were just before, otherwise the data read
+       * into the buffer is probably going to be lost because there's no
+       * guarantee that the caller is going to have preserved the line we
+       * just read.  Note that glibc's nss/nss_files/files-XXX.c does
+       * something similar in CONCAT(_nss_files_get,ENTNAME_r) (around
+       * line 242 in glibc 2.4 sources).
+       */
+      fsetpos(g_file, &position);
+      *errnop = errno;
+      ret = _nss_cache_ent_bad_return_code(*errnop);
+    }
+  }
+
+  return ret;
+}
+
+// _nss_cache_getgrent_r()
+// Called by NSS to look up next entry in group file
+
+enum nss_status _nss_cache_getgrent_r(struct group *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+  enum nss_status ret;
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_getgrent_r_locked(result, buffer, buflen, errnop);
+  NSS_CACHE_UNLOCK();
+  return ret;
+}
+
+// _nss_cache_getgrgid_r()
+// Find a group by gid
+
+enum nss_status _nss_cache_getgrgid_r(gid_t gid, struct group *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+  char filename[NSS_CACHE_PATH_LENGTH];
+  struct nss_cache_args args;
+  enum nss_status ret;
+
+  strncpy(filename, g_filename, NSS_CACHE_PATH_LENGTH - 1);
+  if (strlen(filename) > NSS_CACHE_PATH_LENGTH - 7) {
+    DEBUG("filename too long\n");
+    return NSS_STATUS_UNAVAIL;
+  }
+  strncat(filename, ".bygid", 6);
+
+  args.sorted_filename = filename;
+  args.system_filename = g_filename;
+  args.lookup_function = _nss_cache_grgid_wrap;
+  args.lookup_value = &gid;
+  args.lookup_result = result;
+  args.buffer = buffer;
+  args.buflen = buflen;
+
+  DEBUG("Binary search for gid %d\n", gid);
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_bsearch(&args, errnop);
+
+  // TODO(vasilios): make this a runtime or compile-time option, as this slows
+  // down legitimate misses as the trade off for safety.
+  if (ret == NSS_STATUS_NOTFOUND) {
+    DEBUG("Binary search returned nothing.\n");
+    ret = NSS_STATUS_UNAVAIL;
+  }
+
+  if (ret == NSS_STATUS_UNAVAIL) {
+    DEBUG("Binary search failed, falling back to full linear search\n");
+    ret = _nss_cache_setgrent_locked();
+
+    if (ret == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getgrent_r_locked(result,
+                                                buffer,
+                                                buflen,
+                                                errnop))
+             == NSS_STATUS_SUCCESS) {
+        if (result->gr_gid == gid)
+          break;
+      }
+    }
+  }
+
+  _nss_cache_endgrent_locked();
+  NSS_CACHE_UNLOCK();
+
+  return ret;
+}
+
+// _nss_cache_getgrnam_r()
+// Find a group by name
+
+enum nss_status _nss_cache_getgrnam_r(const char *name, struct group *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+  char *gr_name;
+  char filename[NSS_CACHE_PATH_LENGTH];
+  struct nss_cache_args args;
+  enum nss_status ret;
+
+  NSS_CACHE_LOCK();
+
+  // name is a const char, we need a non-const copy
+  gr_name = malloc(strlen(name) + 1);
+  if (gr_name == NULL) {
+    DEBUG("malloc error\n");
+    return NSS_STATUS_UNAVAIL;
+  }
+  strncpy(gr_name, name, strlen(name) + 1);
+
+  strncpy(filename, g_filename, NSS_CACHE_PATH_LENGTH - 1);
+  if (strlen(filename) > NSS_CACHE_PATH_LENGTH - 8) {
+    DEBUG("filename too long\n");
+    free(gr_name);
+    return NSS_STATUS_UNAVAIL;
+  }
+  strncat(filename, ".byname", 7);
+
+  args.sorted_filename = filename;
+  args.system_filename = g_filename;
+  args.lookup_function = _nss_cache_grnam_wrap;
+  args.lookup_value = gr_name;
+  args.lookup_result = result;
+  args.buffer = buffer;
+  args.buflen = buflen;
+
+  DEBUG("Binary search for group %s\n", gr_name);
+  ret = _nss_cache_bsearch(&args, errnop);
+
+  // TODO(vasilios): make this a runtime or compile-time option, as this slows
+  // down legitimate misses as the trade off for safety.
+  if (ret == NSS_STATUS_NOTFOUND) {
+    DEBUG("Binary search returned nothing.\n");
+    ret = NSS_STATUS_UNAVAIL;
+  }
+
+  if (ret == NSS_STATUS_UNAVAIL) {
+    DEBUG("Binary search failed, falling back to full linear search\n");
+    ret = _nss_cache_setgrent_locked();
+
+    if (ret == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getgrent_r_locked(result,
+                                                buffer,
+                                                buflen,
+                                                errnop))
+             == NSS_STATUS_SUCCESS) {
+        if (!strcmp(result->gr_name, name))
+          break;
+      }
+    }
+  }
+
+  free(gr_name);
+  _nss_cache_endgrent_locked();
+  NSS_CACHE_UNLOCK();
+
+  return ret;
+}
+
+//
+//  Routines for shadow map defined here.
+//
+
+// _nss_cache_setspent_path()
+// Helper function for testing
+
+extern char* _nss_cache_setspent_path(const char *path) {
+
+  DEBUG("%s %s\n", "Setting s_filename to", path);
+  return strncpy(s_filename, path, NSS_CACHE_PATH_LENGTH - 1);
+
+}
+
+// _nss_cache_setspent_locked()
+// Internal setup routine
+
+static enum nss_status _nss_cache_setspent_locked(void) {
+
+  DEBUG("%s %s\n", "Opening", g_filename);
+  s_file = fopen(s_filename, "r");
+
+  if (s_file) {
+    return NSS_STATUS_SUCCESS;
+  } else {
+    return NSS_STATUS_UNAVAIL;
+  }
+}
+
+// _nss_cache_spnam_wrap()
+// Internal wrapper for binary searches, using shadow-specific calls.
+
+static enum nss_cache_match _nss_cache_spnam_wrap(FILE *file,
+                                                 struct nss_cache_args *args) {
+  struct spwd *result = args->lookup_result;
+  char *name = args->lookup_value;
+  int ret;
+
+  if (fgetspent_r(file, result, args->buffer, args->buflen, &result) == 0) {
+    ret = strcoll(result->sp_namp, name);
+    if (ret == 0) {
+      DEBUG("SUCCESS: found user %s\n", result->sp_namp);
+      return NSS_CACHE_EXACT;
+    }
+    DEBUG("Failed match at name %s\n", result->sp_namp);
+    if (ret > 0) {
+      return NSS_CACHE_HIGH;
+    } else {
+      return NSS_CACHE_LOW;
+    }
+  }
+
+  return NSS_CACHE_ERROR;
+}
+
+// _nss_cache_setspent()
+// Called by NSS to open the shadow file
+// 'stayopen' parameter is ignored.
+
+enum nss_status _nss_cache_setspent(int stayopen) {
+  enum nss_status ret;
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_setspent_locked();
+  NSS_CACHE_UNLOCK();
+  return ret;
+}
+
+// _nss_cache_endspent_locked()
+// Internal close routine
+
+static enum nss_status _nss_cache_endspent_locked(void) {
+
+  DEBUG("Closing shadow.cache\n");
+  if (s_file) {
+    fclose(s_file);
+    s_file = NULL;
+  }
+  return NSS_STATUS_SUCCESS;
+}
+
+// _nss_cache_endspent()
+// Called by NSS to close the shadow file
+
+enum nss_status _nss_cache_endspent(void) {
+  enum nss_status ret;
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_endspent_locked();
+  NSS_CACHE_UNLOCK();
+  return ret;
+}
+
+// _nss_cache_getspent_r_locked()
+// Called internally to return the next entry from the shadow file
+
+static enum nss_status _nss_cache_getspent_r_locked(struct spwd *result,
+                                                   char *buffer, size_t buflen,
+                                                   int *errnop) {
+
+  enum nss_status ret = NSS_STATUS_SUCCESS;
+
+  if (s_file == NULL) {
+    DEBUG("s_file == NULL, going to setspent\n");
+    ret = _nss_cache_setspent_locked();
+  }
+
+  if (ret == NSS_STATUS_SUCCESS) {
+    if (fgetspent_r(s_file, result, buffer, buflen, &result) == 0) {
+      DEBUG("Returning shadow entry %s\n", result->sp_namp);
+    } else {
+      *errnop = errno;
+      ret = _nss_cache_ent_bad_return_code(*errnop);
+    }
+  }
+
+  return ret;
+}
+
+// _nss_cache_getspent_r()
+// Called by NSS to look up next entry in the shadow file
+
+enum nss_status _nss_cache_getspent_r(struct spwd *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+  enum nss_status ret;
+  NSS_CACHE_LOCK();
+  ret = _nss_cache_getspent_r_locked(result, buffer, buflen, errnop);
+  NSS_CACHE_UNLOCK();
+  return ret;
+}
+
+// _nss_cache_getspnam_r()
+// Find a user by name
+
+enum nss_status _nss_cache_getspnam_r(const char *name, struct spwd *result,
+                                     char *buffer, size_t buflen,
+                                     int *errnop) {
+  char *sp_namp;
+  char filename[NSS_CACHE_PATH_LENGTH];
+  struct nss_cache_args args;
+  enum nss_status ret;
+
+  NSS_CACHE_LOCK();
+
+  // name is a const char, we need a non-const copy
+  sp_namp = malloc(strlen(name) + 1);
+  if (sp_namp == NULL) {
+    DEBUG("malloc error\n");
+    return NSS_STATUS_UNAVAIL;
+  }
+  strncpy(sp_namp, name, strlen(name) + 1);
+
+  strncpy(filename, s_filename, NSS_CACHE_PATH_LENGTH - 1);
+  if (strlen(filename) > NSS_CACHE_PATH_LENGTH - 8) {
+    DEBUG("filename too long\n");
+    free(sp_namp);
+    return NSS_STATUS_UNAVAIL;
+  }
+  strncat(filename, ".byname", 7);
+
+  args.sorted_filename = filename;
+  args.system_filename = s_filename;
+  args.lookup_function = _nss_cache_spnam_wrap;
+  args.lookup_value = sp_namp;
+  args.lookup_result = result;
+  args.buffer = buffer;
+  args.buflen = buflen;
+
+  DEBUG("Binary search for user %s\n", sp_namp);
+  ret = _nss_cache_bsearch(&args, errnop);
+
+  // TODO(vasilios): make this a runtime or compile-time option, as this slows
+  // down legitimate misses as the trade off for safety.
+  if (ret == NSS_STATUS_NOTFOUND) {
+    DEBUG("Binary search returned nothing.\n");
+    ret = NSS_STATUS_UNAVAIL;
+  }
+
+  if (ret == NSS_STATUS_UNAVAIL) {
+    DEBUG("Binary search failed, falling back to full linear search\n");
+    ret = _nss_cache_setspent_locked();
+
+    if (ret == NSS_STATUS_SUCCESS) {
+      while ((ret = _nss_cache_getspent_r_locked(result,
+                                                buffer,
+                                                buflen,
+                                                errnop))
+             == NSS_STATUS_SUCCESS) {
+        if (!strcmp(result->sp_namp, name))
+          break;
+      }
+    }
+  }
+
+  free(sp_namp);
+  _nss_cache_endspent_locked();
+  NSS_CACHE_UNLOCK();
+
+  return ret;
+}
diff --git a/nss/nss_cache/nss_cache.h b/nss/nss_cache/nss_cache.h
new file mode 100644
index 0000000..8ce4b97
--- /dev/null
+++ b/nss/nss_cache/nss_cache.h
@@ -0,0 +1,65 @@
+/* Copyright 2009 Google Inc.
+ *
+ * This 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 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA
+ */
+
+#include <errno.h>
+#include <grp.h>
+#include <nss.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef NSS_CACHE_H
+#define NSS_CACHE_H
+
+#ifdef DEBUG
+#undef DEBUG
+#define DEBUG(fmt, args...)  do { fprintf(stderr, fmt, ##args); } while (0)
+#else
+#define DEBUG(fmt, ...)      do { } while (0)
+#endif /* DEBUG */
+
+#define NSS_CACHE_PATH_LENGTH 255
+extern char* _nss_cache_setpwent_path(const char *path);
+extern char* _nss_cache_setgrent_path(const char *path);
+extern char* _nss_cache_setspent_path(const char *path);
+
+enum nss_cache_match {
+  NSS_CACHE_EXACT = 0,
+  NSS_CACHE_HIGH = 1,
+  NSS_CACHE_LOW = 2,
+  NSS_CACHE_ERROR = 3,
+};
+
+struct nss_cache_args {
+  char *system_filename;
+  char *sorted_filename;
+  void *lookup_function;
+  void *lookup_value;
+  void *lookup_result;
+  char *buffer;
+  size_t buflen;
+};
+
+#endif /* NSS_CACHE_H */
diff --git a/shlib-versions b/shlib-versions
index b9cb99d..749346e 100644
--- a/shlib-versions
+++ b/shlib-versions
@@ -48,6 +48,8 @@ libnss_nisplus=2
 libnss_ldap=2
 libnss_hesiod=2
 libnss_db=2
+libnss_borg=2
+libnss_cache=2
 
 # Tests for NSS.  They must have the same NSS_SHLIB_REVISION number as
 # the rest.

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=be2e30101dc7d4e5fcde03d9520509b506d50875

commit be2e30101dc7d4e5fcde03d9520509b506d50875
Author: Stan Shebs <stanshebs@google.com>
Date:   Tue Mar 13 11:53:48 2018 -0700

    Forward-port addition of _google_dl_debug_state_hook

diff --git a/elf/Versions b/elf/Versions
index 201c6d3..17fb15e 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -79,6 +79,7 @@ ld {
     # Set value of a tunable.
     __tunable_get_val;
 
+    _google_dl_debug_state_hook;
     __google_auxv;
   }
 }
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
index 14d1125..390ef99 100644
--- a/elf/dl-debug.c
+++ b/elf/dl-debug.c
@@ -64,6 +64,8 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
 }
 
 
+void (*_google_dl_debug_state_hook)(const struct r_debug *);
+
 /* This function exists solely to have a breakpoint set on it by the
    debugger.  The debugger is supposed to find this function's address by
    examining the r_brk member of struct r_debug, but GDB 4.15 in fact looks
@@ -71,5 +73,7 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
 void
 _dl_debug_state (void)
 {
+  if (_google_dl_debug_state_hook)
+    _google_dl_debug_state_hook(&_r_debug);
 }
 rtld_hidden_def (_dl_debug_state)

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=b6ae0cdf4009cd2175c16abf029a5a204efd9bc9

commit b6ae0cdf4009cd2175c16abf029a5a204efd9bc9
Author: Stan Shebs <stanshebs@google.com>
Date:   Tue Mar 13 11:37:02 2018 -0700

    As with gettimeofday, avoid vdso for clang-compiled time()

diff --git a/sysdeps/unix/sysv/linux/x86/time.c b/sysdeps/unix/sysv/linux/x86/time.c
index d19cccd..124bd96 100644
--- a/sysdeps/unix/sysv/linux/x86/time.c
+++ b/sysdeps/unix/sysv/linux/x86/time.c
@@ -18,7 +18,8 @@
 
 #include <time.h>
 
-#ifdef SHARED
+/* Clang ifunc support works, but differently enough that this code breaks.  */
+#if defined(SHARED) && !defined(__clang__)
 
 #include <dl-vdso.h>
 #include <errno.h>
@@ -57,3 +58,5 @@ time (time_t *t)
 }
 
 #endif
+
+libc_hidden_weak (time)

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=f649403e8a5d028e0e36735306189be3aa0ed3f5

commit f649403e8a5d028e0e36735306189be3aa0ed3f5
Author: Stan Shebs <stanshebs@google.com>
Date:   Mon Mar 12 14:59:51 2018 -0700

    Forward-port addition of __google_pthread_signal_safe_key_create

diff --git a/nptl/Versions b/nptl/Versions
index 0ae5def..eeca21b 100644
--- a/nptl/Versions
+++ b/nptl/Versions
@@ -266,6 +266,7 @@ libpthread {
   }
 
   GLIBC_PRIVATE {
+    __google_pthread_signal_safe_key_create;
     __pthread_initialize_minimal;
     __pthread_clock_gettime; __pthread_clock_settime;
     __pthread_unwind; __pthread_get_minstack;
diff --git a/nptl/pthread_key_create.c b/nptl/pthread_key_create.c
index 0c0e53c..4ff1e05 100644
--- a/nptl/pthread_key_create.c
+++ b/nptl/pthread_key_create.c
@@ -18,31 +18,52 @@
 
 #include <errno.h>
 #include "pthreadP.h"
+#include <assert.h>
 #include <atomic.h>
 
+static int
+claim_key (pthread_key_t *key, void (*destr) (void *), size_t cnt)
+{
+  uintptr_t seq = __pthread_keys[cnt].seq;
+  if (KEY_UNUSED (seq) && KEY_USABLE (seq)
+      /* We found an unused slot.  Try to allocate it.  */
+      && ! atomic_compare_and_exchange_bool_acq (&__pthread_keys[cnt].seq,
+                                                 seq + 1, seq))
+    {
+      /* Remember the destructor.  */
+      __pthread_keys[cnt].destr = destr;
+
+      /* Return the key to the caller.  */
+      *key = cnt;
+      return 0;
+    }
+  return -1;
+}
 
 int
 __pthread_key_create (pthread_key_t *key, void (*destr) (void *))
 {
   /* Find a slot in __pthread_keys which is unused.  */
-  for (size_t cnt = 0; cnt < PTHREAD_KEYS_MAX; ++cnt)
+  for (size_t cnt = PTHREAD_SIGNAL_SAFE_KEYS_MAX; cnt < PTHREAD_KEYS_MAX; ++cnt)
+    {
+      if (claim_key (key, destr, cnt) == 0)
+        return 0;
+    }
+
+  return EAGAIN;
+}
+
+int __google_pthread_signal_safe_key_create (
+    pthread_key_t *key, void (*destr) (void *))
+{
+  /* Our implementation makes signal safe keys easy: they just have to
+     reside in the first (inline) block. */
+  assert (PTHREAD_SIGNAL_SAFE_KEYS_MAX <= PTHREAD_KEY_1STLEVEL_SIZE);
+  /* Find a slot in __pthread_keys which is unused.  */
+  for (size_t cnt = 0; cnt < PTHREAD_SIGNAL_SAFE_KEYS_MAX; ++cnt)
     {
-      uintptr_t seq = __pthread_keys[cnt].seq;
-
-      if (KEY_UNUSED (seq) && KEY_USABLE (seq)
-	  /* We found an unused slot.  Try to allocate it.  */
-	  && ! atomic_compare_and_exchange_bool_acq (&__pthread_keys[cnt].seq,
-						     seq + 1, seq))
-	{
-	  /* Remember the destructor.  */
-	  __pthread_keys[cnt].destr = destr;
-
-	  /* Return the key to the caller.  */
-	  *key = cnt;
-
-	  /* The call succeeded.  */
-	  return 0;
-	}
+      if (claim_key (key, destr, cnt) == 0)
+        return 0;
     }
 
   return EAGAIN;
diff --git a/nptl/tst-key1.c b/nptl/tst-key1.c
index 8227f9e..3fc7487 100644
--- a/nptl/tst-key1.c
+++ b/nptl/tst-key1.c
@@ -33,7 +33,11 @@ do_test (void)
 {
   int max;
 #ifdef PTHREAD_KEYS_MAX
+#ifdef PTHREAD_SIGNAL_SAFE_KEYS_MAX
+  max = PTHREAD_KEYS_MAX - PTHREAD_SIGNAL_SAFE_KEYS_MAX;
+#else
   max = PTHREAD_KEYS_MAX;
+#endif
 #else
   max = _POSIX_THREAD_KEYS_MAX;
 #endif
diff --git a/nptl/tst-key4.c b/nptl/tst-key4.c
index 810f25b..aa795f4 100644
--- a/nptl/tst-key4.c
+++ b/nptl/tst-key4.c
@@ -24,7 +24,11 @@
 
 
 #ifdef PTHREAD_KEYS_MAX
+#ifdef PTHREAD_SIGNAL_SAFE_KEYS_MAX
+const int max = PTHREAD_KEYS_MAX - PTHREAD_SIGNAL_SAFE_KEYS_MAX;
+#else
 const int max = PTHREAD_KEYS_MAX;
+#endif
 #else
 const int max = _POSIX_THREAD_KEYS_MAX;
 #endif
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index df049ab..fa125e0 100644
--- a/sysdeps/nptl/pthread.h
+++ b/sysdeps/nptl/pthread.h
@@ -1113,6 +1113,20 @@ extern int pthread_key_create (pthread_key_t *__key,
 			       void (*__destr_function) (void *))
      __THROW __nonnull ((1));
 
+/* Exactly as pthread_key_create, but returns an async-signal-safe key:
+   getspecific, setspecific, and key_delete are async-signal-safe when
+   used with the returned key. The number of signal_safe keys is *extremely*
+   limited (PTHREAD_SIGNAL_SAFE_KEY_MAX); you are highly recommended to use
+   pthread_key_create unless it's absolutely necessary.  This function
+   is async-signal-safe. */
+#if !defined(IS_IN_libpthread)
+__attribute__((weak))
+#endif
+extern int __google_pthread_signal_safe_key_create (pthread_key_t *__key,
+						    void (*__destr_function) (void *))
+    __THROW  __nonnull ((1));
+
+
 /* Destroy KEY.  */
 extern int pthread_key_delete (pthread_key_t __key) __THROW;
 
diff --git a/sysdeps/unix/sysv/linux/aarch64/bits/local_lim.h b/sysdeps/unix/sysv/linux/aarch64/bits/local_lim.h
index 37aab9a..4328619 100644
--- a/sysdeps/unix/sysv/linux/aarch64/bits/local_lim.h
+++ b/sysdeps/unix/sysv/linux/aarch64/bits/local_lim.h
@@ -63,6 +63,7 @@
 #define _POSIX_THREAD_KEYS_MAX	128
 /* This is the value this implementation supports.  */
 #define PTHREAD_KEYS_MAX	1024
+#define PTHREAD_SIGNAL_SAFE_KEYS_MAX	4
 
 /* Controlling the iterations of destructors for thread-specific data.  */
 #define _POSIX_THREAD_DESTRUCTOR_ITERATIONS	4
diff --git a/sysdeps/unix/sysv/linux/alpha/bits/local_lim.h b/sysdeps/unix/sysv/linux/alpha/bits/local_lim.h
index 7805fa5..904bb5c 100644
--- a/sysdeps/unix/sysv/linux/alpha/bits/local_lim.h
+++ b/sysdeps/unix/sysv/linux/alpha/bits/local_lim.h
@@ -62,6 +62,7 @@
 #define _POSIX_THREAD_KEYS_MAX	128
 /* This is the value this implementation supports.  */
 #define PTHREAD_KEYS_MAX	1024
+#define PTHREAD_SIGNAL_SAFE_KEYS_MAX	4
 
 /* Controlling the iterations of destructors for thread-specific data.  */
 #define _POSIX_THREAD_DESTRUCTOR_ITERATIONS	4
diff --git a/sysdeps/unix/sysv/linux/bits/local_lim.h b/sysdeps/unix/sysv/linux/bits/local_lim.h
index 2d82ada..ae2d914 100644
--- a/sysdeps/unix/sysv/linux/bits/local_lim.h
+++ b/sysdeps/unix/sysv/linux/bits/local_lim.h
@@ -62,6 +62,7 @@
 #define _POSIX_THREAD_KEYS_MAX	128
 /* This is the value this implementation supports.  */
 #define PTHREAD_KEYS_MAX	1024
+#define PTHREAD_SIGNAL_SAFE_KEYS_MAX	4
 
 /* Controlling the iterations of destructors for thread-specific data.  */
 #define _POSIX_THREAD_DESTRUCTOR_ITERATIONS	4
diff --git a/sysdeps/unix/sysv/linux/powerpc/bits/local_lim.h b/sysdeps/unix/sysv/linux/powerpc/bits/local_lim.h
index 049094e..dfcf728 100644
--- a/sysdeps/unix/sysv/linux/powerpc/bits/local_lim.h
+++ b/sysdeps/unix/sysv/linux/powerpc/bits/local_lim.h
@@ -62,6 +62,7 @@
 #define _POSIX_THREAD_KEYS_MAX	128
 /* This is the value this implementation supports.  */
 #define PTHREAD_KEYS_MAX	1024
+#define PTHREAD_SIGNAL_SAFE_KEYS_MAX	4
 
 /* Controlling the iterations of destructors for thread-specific data.  */
 #define _POSIX_THREAD_DESTRUCTOR_ITERATIONS	4
diff --git a/sysdeps/unix/sysv/linux/sparc/bits/local_lim.h b/sysdeps/unix/sysv/linux/sparc/bits/local_lim.h
index a7ec88e..954650e 100644
--- a/sysdeps/unix/sysv/linux/sparc/bits/local_lim.h
+++ b/sysdeps/unix/sysv/linux/sparc/bits/local_lim.h
@@ -62,6 +62,7 @@
 #define _POSIX_THREAD_KEYS_MAX	128
 /* This is the value this implementation supports.  */
 #define PTHREAD_KEYS_MAX	1024
+#define PTHREAD_SIGNAL_SAFE_KEYS_MAX	4
 
 /* Controlling the iterations of destructors for thread-specific data.  */
 #define _POSIX_THREAD_DESTRUCTOR_ITERATIONS	4

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=cfa5a3225d0705d1fe9617e7ca6ceb6f52ad21c8

commit cfa5a3225d0705d1fe9617e7ca6ceb6f52ad21c8
Author: Stan Shebs <stanshebs@google.com>
Date:   Mon Mar 12 13:28:31 2018 -0700

    Forward-port __google_auxv addition

diff --git a/elf/Versions b/elf/Versions
index 3b09901..201c6d3 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -78,5 +78,7 @@ ld {
 
     # Set value of a tunable.
     __tunable_get_val;
+
+    __google_auxv;
   }
 }
diff --git a/elf/dl-init.c b/elf/dl-init.c
index 3e72fa3..2f12665 100644
--- a/elf/dl-init.c
+++ b/elf/dl-init.c
@@ -74,6 +74,8 @@ call_init (struct link_map *l, int argc, char **argv, char **env)
 }
 
 
+ElfW(auxv_t) *__google_auxv;
+
 void
 _dl_init (struct link_map *main_map, int argc, char **argv, char **env)
 {
@@ -81,6 +83,17 @@ _dl_init (struct link_map *main_map, int argc, char **argv, char **env)
   ElfW(Dyn) *preinit_array_size = main_map->l_info[DT_PREINIT_ARRAYSZ];
   unsigned int i;
 
+  /* _dl_init is called from _dl_start_user during loader startup.
+     It is also called from dl_open_worker.
+     Prevent multiple initialization of __google_auxv.  */
+  if (__google_auxv == NULL)
+    {
+      char **e;
+
+      for (e = env; *e; ++e) /* Skip.  */;
+      __google_auxv = (ElfW(auxv_t) *) ++e;
+    }
+
   if (__glibc_unlikely (GL(dl_initfirst) != NULL))
     {
       call_init (GL(dl_initfirst), argc, argv, env);
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 114f77a..12270f0 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -228,6 +228,9 @@ _dl_aux_init (ElfW(auxv_t) *av)
   uid_t uid = 0;
   gid_t gid = 0;
 
+  extern ElfW(auxv_t) *__google_auxv;
+  __google_auxv = av;
+
   _dl_auxv = av;
   for (; av->a_type != AT_NULL; ++av)
     switch (av->a_type)

-----------------------------------------------------------------------

Summary of changes:
 elf/Versions                                     |    3 +
 elf/dl-debug.c                                   |    4 +
 elf/dl-init.c                                    |   13 +
 elf/dl-support.c                                 |    3 +
 include/grp.h                                    |    1 +
 include/pwd.h                                    |    2 +
 include/shadow.h                                 |    1 +
 nptl/Versions                                    |    1 +
 nptl/pthread_key_create.c                        |   55 +-
 nptl/tst-key1.c                                  |    4 +
 nptl/tst-key4.c                                  |    4 +
 nss/Makefile                                     |   12 +-
 nss/Versions                                     |   29 +
 nss/function.def                                 |   10 +
 nss/nss_borg/borg-pwd.c                          |  180 +++++
 nss/nss_cache/nss_cache.c                        |  936 ++++++++++++++++++++++
 nss/nss_cache/nss_cache.h                        |   78 ++
 shlib-versions                                   |    2 +
 sysdeps/nptl/pthread.h                           |   14 +
 sysdeps/unix/sysv/linux/aarch64/bits/local_lim.h |    1 +
 sysdeps/unix/sysv/linux/alpha/bits/local_lim.h   |    1 +
 sysdeps/unix/sysv/linux/bits/local_lim.h         |    1 +
 sysdeps/unix/sysv/linux/powerpc/bits/local_lim.h |    1 +
 sysdeps/unix/sysv/linux/sparc/bits/local_lim.h   |    1 +
 sysdeps/unix/sysv/linux/x86/time.c               |    5 +-
 25 files changed, 1341 insertions(+), 21 deletions(-)
 create mode 100644 nss/nss_borg/borg-pwd.c
 create mode 100644 nss/nss_cache/nss_cache.c
 create mode 100644 nss/nss_cache/nss_cache.h


hooks/post-receive
-- 
GNU C Library master sources


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]