Merge "Add getgrgid_r/getgrnam_r."
diff --git a/libc/bionic/stubs.cpp b/libc/bionic/stubs.cpp
index b57aeda..41c78bc 100644
--- a/libc/bionic/stubs.cpp
+++ b/libc/bionic/stubs.cpp
@@ -39,6 +39,7 @@
 #include <unistd.h>
 
 #include "private/android_filesystem_config.h"
+#include "private/bionic_macros.h"
 #include "private/ErrnoRestorer.h"
 #include "private/libc_logging.h"
 #include "private/ThreadLocalBuffer.h"
@@ -66,11 +67,15 @@
 static ThreadLocalBuffer<group_state_t> g_group_tls_buffer;
 static ThreadLocalBuffer<passwd_state_t> g_passwd_tls_buffer;
 
+static void init_group_state(group_state_t* state) {
+  memset(state, 0, sizeof(group_state_t));
+  state->group_.gr_mem = state->group_members_;
+}
+
 static group_state_t* __group_state() {
   group_state_t* result = g_group_tls_buffer.get();
   if (result != nullptr) {
-    memset(result, 0, sizeof(group_state_t));
-    result->group_.gr_mem = result->group_members_;
+    init_group_state(result);
   }
   return result;
 }
@@ -397,17 +402,28 @@
   return (pw != NULL) ? pw->pw_name : NULL;
 }
 
+static group* getgrgid_internal(gid_t gid, group_state_t* state) {
+  group* grp = android_id_to_group(state, gid);
+  if (grp != NULL) {
+    return grp;
+  }
+  return app_id_to_group(gid, state);
+}
+
 group* getgrgid(gid_t gid) { // NOLINT: implementing bad function.
   group_state_t* state = __group_state();
   if (state == NULL) {
     return NULL;
   }
+  return getgrgid_internal(gid, state);
+}
 
-  group* gr = android_id_to_group(state, gid);
-  if (gr != NULL) {
-    return gr;
+static group* getgrnam_internal(const char* name, group_state_t* state) {
+  group* grp = android_name_to_group(state, name);
+  if (grp != NULL) {
+    return grp;
   }
-  return app_id_to_group(gid, state);
+  return app_id_to_group(app_id_from_name(name, true), state);
 }
 
 group* getgrnam(const char* name) { // NOLINT: implementing bad function.
@@ -415,11 +431,36 @@
   if (state == NULL) {
     return NULL;
   }
+  return getgrnam_internal(name, state);
+}
 
-  if (android_name_to_group(state, name) != 0) {
-    return &state->group_;
+static int getgroup_r(bool by_name, const char* name, gid_t gid, struct group* grp, char* buf,
+                      size_t buflen, struct group** result) {
+  ErrnoRestorer errno_restorer;
+  *result = NULL;
+  char* p = reinterpret_cast<char*>(
+      BIONIC_ALIGN(reinterpret_cast<uintptr_t>(buf), sizeof(uintptr_t)));
+  if (p + sizeof(group_state_t) > buf + buflen) {
+    return ERANGE;
   }
-  return app_id_to_group(app_id_from_name(name, true), state);
+  group_state_t* state = reinterpret_cast<group_state_t*>(p);
+  init_group_state(state);
+  group* retval = (by_name ? getgrnam_internal(name, state) : getgrgid_internal(gid, state));
+  if (retval != NULL) {
+    *grp = *retval;
+    *result = grp;
+    return 0;
+  }
+  return errno;
+}
+
+int getgrgid_r(gid_t gid, struct group* grp, char* buf, size_t buflen, struct group** result) {
+  return getgroup_r(false, NULL, gid, grp, buf, buflen, result);
+}
+
+int getgrnam_r(const char* name, struct group* grp, char* buf, size_t buflen,
+               struct group **result) {
+  return getgroup_r(true, name, 0, grp, buf, buflen, result);
 }
 
 // We don't have an /etc/networks, so all inputs return NULL.
diff --git a/libc/libc.map b/libc/libc.map
index 47c52a4..ffbd29c 100644
--- a/libc/libc.map
+++ b/libc/libc.map
@@ -1332,6 +1332,12 @@
     *;
 };
 
+LIBC_N {
+  global:
+    getgrgid_r;
+    getgrnam_r;
+} LIBC;
+
 LIBC_PRIVATE {
   global:
     ___Unwind_Backtrace; # arm
@@ -1453,4 +1459,4 @@
     SHA1Init; # arm x86 mips
     SHA1Transform; # arm x86 mips
     SHA1Update; # arm x86 mips
-} LIBC;
+} LIBC_N;
diff --git a/tests/stubs_test.cpp b/tests/stubs_test.cpp
index 89d67cb..2d1bdee 100644
--- a/tests/stubs_test.cpp
+++ b/tests/stubs_test.cpp
@@ -163,8 +163,6 @@
   check_get_passwd("u1_i0", 199000, TYPE_APP);
 }
 
-#if defined(__BIONIC__)
-
 static void check_group(const group* grp, const char* group_name, gid_t gid) {
   ASSERT_TRUE(grp != NULL);
   ASSERT_STREQ(group_name, grp->gr_name);
@@ -174,6 +172,8 @@
   ASSERT_TRUE(grp->gr_mem[1] == NULL);
 }
 
+#if defined(__BIONIC__)
+
 static void check_getgrgid(const char* group_name, gid_t gid) {
   errno = 0;
   group* grp = getgrgid(gid);
@@ -190,17 +190,49 @@
   check_group(grp, group_name, gid);
 }
 
+static void check_getgrgid_r(const char* group_name, gid_t gid) {
+  group grp_storage;
+  char buf[512];
+  group* grp;
+
+  errno = 0;
+  int result = getgrgid_r(gid, &grp_storage, buf, sizeof(buf), &grp);
+  ASSERT_EQ(0, result);
+  ASSERT_EQ(0, errno);
+  SCOPED_TRACE("getgrgid_r");
+  check_group(grp, group_name, gid);
+}
+
+static void check_getgrnam_r(const char* group_name, gid_t gid) {
+  group grp_storage;
+  char buf[512];
+  group* grp;
+
+  errno = 0;
+  int result = getgrnam_r(group_name, &grp_storage, buf, sizeof(buf), &grp);
+  ASSERT_EQ(0, result);
+  ASSERT_EQ(0, errno);
+  SCOPED_TRACE("getgrnam_r");
+  check_group(grp, group_name, gid);
+}
+
 static void check_get_group(const char* group_name, gid_t gid) {
   check_getgrgid(group_name, gid);
   check_getgrnam(group_name, gid);
+  check_getgrgid_r(group_name, gid);
+  check_getgrnam_r(group_name, gid);
 }
 
 #else // !defined(__BIONIC__)
 
-static void check_get_group(const char* /* group_name */, gid_t /* gid */) {
+static void print_no_getgrnam_test_info() {
   GTEST_LOG_(INFO) << "This test is about gid/group_name translation for Android, which does nothing on libc other than bionic.\n";
 }
 
+static void check_get_group(const char*, gid_t) {
+  print_no_getgrnam_test_info();
+}
+
 #endif
 
 TEST(getgrnam, system_id_root) {
@@ -259,3 +291,53 @@
 TEST(getgrnam, app_id_u1_i0) {
   check_get_group("u1_i0", 199000);
 }
+
+TEST(getgrnam_r, reentrancy) {
+#if defined(__BIONIC__)
+  group grp_storage[2];
+  char buf[2][512];
+  group* grp[3];
+  int result = getgrnam_r("root", &grp_storage[0], buf[0], sizeof(buf[0]), &grp[0]);
+  ASSERT_EQ(0, result);
+  check_group(grp[0], "root", 0);
+  grp[1] = getgrnam("system");
+  check_group(grp[1], "system", 1000);
+  result = getgrnam_r("radio", &grp_storage[1], buf[1], sizeof(buf[1]), &grp[2]);
+  ASSERT_EQ(0, result);
+  check_group(grp[2], "radio", 1001);
+  check_group(grp[0], "root", 0);
+  check_group(grp[1], "system", 1000);
+#else
+  print_no_getgrnam_test_info();
+#endif
+}
+
+TEST(getgrgid_r, reentrancy) {
+#if defined(__BIONIC__)
+  group grp_storage[2];
+  char buf[2][512];
+  group* grp[3];
+  int result = getgrgid_r(0, &grp_storage[0], buf[0], sizeof(buf[0]), &grp[0]);
+  ASSERT_EQ(0, result);
+  check_group(grp[0], "root", 0);
+  grp[1] = getgrgid(1000);
+  check_group(grp[1], "system", 1000);
+  result = getgrgid_r(1001, &grp_storage[1], buf[1], sizeof(buf[1]), &grp[2]);
+  ASSERT_EQ(0, result);
+  check_group(grp[2], "radio", 1001);
+  check_group(grp[0], "root", 0);
+  check_group(grp[1], "system", 1000);
+#else
+  print_no_getgrnam_test_info();
+#endif
+}
+
+TEST(getgrnam_r, large_enough_suggested_buffer_size) {
+  long size = sysconf(_SC_GETGR_R_SIZE_MAX);
+  ASSERT_GT(size, 0);
+  char buf[size];
+  group grp_storage;
+  group* grp;
+  ASSERT_EQ(0, getgrnam_r("root", &grp_storage, buf, size, &grp));
+  check_group(grp, "root", 0);
+}