Merge "bionic: Implement getpwent and getgrent"
diff --git a/libc/bionic/grp_pwd.cpp b/libc/bionic/grp_pwd.cpp
index d75b94d..332b2b8 100644
--- a/libc/bionic/grp_pwd.cpp
+++ b/libc/bionic/grp_pwd.cpp
@@ -54,6 +54,8 @@
   group group_;
   char* group_members_[2];
   char group_name_buffer_[32];
+  // Must be last so init_group_state can run a simple memset for the above
+  ssize_t getgrent_idx;
 };
 
 struct passwd_state_t {
@@ -61,13 +63,14 @@
   char name_buffer_[32];
   char dir_buffer_[32];
   char sh_buffer_[32];
+  ssize_t getpwent_idx;
 };
 
 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));
+  memset(state, 0, sizeof(group_state_t) - sizeof(state->getgrent_idx));
   state->group_.gr_mem = state->group_members_;
 }
 
@@ -467,6 +470,60 @@
   return (pw != NULL) ? pw->pw_name : NULL;
 }
 
+void setpwent() {
+  passwd_state_t* state = g_passwd_tls_buffer.get();
+  if (state) {
+    state->getpwent_idx = 0;
+  }
+}
+
+void endpwent() {
+  setpwent();
+}
+
+passwd* getpwent() {
+  passwd_state_t* state = g_passwd_tls_buffer.get();
+  if (state == NULL) {
+    return NULL;
+  }
+  if (state->getpwent_idx < 0) {
+    return NULL;
+  }
+
+  size_t start = 0;
+  ssize_t end = android_id_count;
+  if (state->getpwent_idx < end) {
+    return android_iinfo_to_passwd(state, android_ids + state->getpwent_idx++);
+  }
+
+  start = end;
+  end += AID_OEM_RESERVED_END - AID_OEM_RESERVED_START + 1;
+
+  if (state->getpwent_idx < end) {
+    return oem_id_to_passwd(
+        state->getpwent_idx++ - start + AID_OEM_RESERVED_START, state);
+  }
+
+  start = end;
+  end += AID_OEM_RESERVED_2_END - AID_OEM_RESERVED_2_START + 1;
+
+  if (state->getpwent_idx < end) {
+    return oem_id_to_passwd(
+        state->getpwent_idx++ - start + AID_OEM_RESERVED_2_START, state);
+  }
+
+  start = end;
+  end += AID_USER - AID_APP; // Do not expose higher users
+
+  if (state->getpwent_idx < end) {
+    return app_id_to_passwd(state->getpwent_idx++ - start + AID_APP, state);
+  }
+
+  // We are not reporting u1_a* and higher or we will be here forever
+  state->getpwent_idx = -1;
+  return NULL;
+}
+
 static group* getgrgid_internal(gid_t gid, group_state_t* state) {
   group* grp = android_id_to_group(state, gid);
   if (grp != NULL) {
@@ -537,3 +594,61 @@
                struct group **result) {
   return getgroup_r(true, name, 0, grp, buf, buflen, result);
 }
+
+void setgrent() {
+  group_state_t* state = g_group_tls_buffer.get();
+  if (state) {
+    state->getgrent_idx = 0;
+  }
+}
+
+void endgrent() {
+  setgrent();
+}
+
+group* getgrent() {
+  group_state_t* state = g_group_tls_buffer.get();
+  if (state == NULL) {
+    return NULL;
+  }
+  if (state->getgrent_idx < 0) {
+    return NULL;
+  }
+
+  size_t start = 0;
+  ssize_t end = android_id_count;
+  if (state->getgrent_idx < end) {
+    init_group_state(state);
+    return android_iinfo_to_group(state, android_ids + state->getgrent_idx++);
+  }
+
+  start = end;
+  end += AID_OEM_RESERVED_END - AID_OEM_RESERVED_START + 1;
+
+  if (state->getgrent_idx < end) {
+    init_group_state(state);
+    return oem_id_to_group(
+        state->getgrent_idx++ - start + AID_OEM_RESERVED_START, state);
+  }
+
+  start = end;
+  end += AID_OEM_RESERVED_2_END - AID_OEM_RESERVED_2_START + 1;
+
+  if (state->getgrent_idx < end) {
+    init_group_state(state);
+    return oem_id_to_group(
+        state->getgrent_idx++ - start + AID_OEM_RESERVED_2_START, state);
+  }
+
+  start = end;
+  end += AID_USER - AID_APP; // Do not expose higher groups
+
+  if (state->getgrent_idx < end) {
+    init_group_state(state);
+    return app_id_to_group(state->getgrent_idx++ - start + AID_APP, state);
+  }
+
+  // We are not reporting u1_a* and higher or we will be here forever
+  state->getgrent_idx = -1;
+  return NULL;
+}
diff --git a/libc/bionic/ndk_cruft.cpp b/libc/bionic/ndk_cruft.cpp
index 95abc20..224cd41 100644
--- a/libc/bionic/ndk_cruft.cpp
+++ b/libc/bionic/ndk_cruft.cpp
@@ -366,10 +366,6 @@
   return __set_errno_internal(n);
 }
 
-// This was never implemented in bionic, only needed for ABI compatibility with the NDK.
-// In the M time frame, over 1000 apps have a reference to this!
-void endpwent() { }
-
 // Since dlmalloc_inspect_all and dlmalloc_trim are exported for systems
 // that use dlmalloc, be consistent and export them everywhere.
 void dlmalloc_inspect_all(void (*)(void*, void*, size_t, void*), void*) {
diff --git a/libc/include/grp.h b/libc/include/grp.h
index df7a613..3ae0d6e 100644
--- a/libc/include/grp.h
+++ b/libc/include/grp.h
@@ -1,9 +1,9 @@
-/*	$OpenBSD: grp.h,v 1.8 2005/12/13 00:35:22 millert Exp $	*/
-/*	$NetBSD: grp.h,v 1.7 1995/04/29 05:30:40 cgd Exp $	*/
+/* $OpenBSD: grp.h,v 1.8 2005/12/13 00:35:22 millert Exp $ */
+/* $NetBSD: grp.h,v 1.7 1995/04/29 05:30:40 cgd Exp $ */
 
 /*-
  * Copyright (c) 1989, 1993
- *	The Regents of the University of California.  All rights reserved.
+ *    The Regents of the University of California.  All rights reserved.
  * (c) UNIX System Laboratories, Inc.
  * All or some portions of this file are derived from material licensed
  * to the University of California by American Telephone and Telegraph
@@ -34,39 +34,37 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)grp.h	8.2 (Berkeley) 1/21/94
+ *    @(#)grp.h  8.2 (Berkeley) 1/21/94
  */
 
 #ifndef _GRP_H_
-#define	_GRP_H_
+#define _GRP_H_
 
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
 struct group {
-	char	*gr_name;		/* group name */
-	char	*gr_passwd;		/* group password */
-	gid_t	gr_gid;			/* group id */
-	char	**gr_mem;		/* group members */
+    char* gr_name; /* group name */
+    char* gr_passwd; /* group password */
+    gid_t gr_gid; /* group id */
+    char** gr_mem; /* group members */
 };
 
 __BEGIN_DECLS
-struct group	*getgrgid(gid_t);
-struct group	*getgrnam(const char *);
+struct group* getgrgid(gid_t);
+struct group* getgrnam(const char *);
 #if __POSIX_VISIBLE >= 200112 || __XPG_VISIBLE
-struct group	*getgrent(void)  __attribute__((deprecated("getgrent is meaningless on Android")));
-void setgrent(void) __attribute__((deprecated("setgrent is meaningless on Android")));
-void endgrent(void) __attribute__((deprecated("endgrent is meaningless on Android")));
-int		 getgrgid_r(gid_t, struct group *, char *,
-		    size_t, struct group **);
-int		 getgrnam_r(const char *, struct group *, char *,
-		    size_t, struct group **);
+/* Android has thousands and thousands of ids to iterate through */
+struct group* getgrent(void) __attribute__((warning("getgrent is inefficient on Android")));
+void setgrent(void);
+void endgrent(void);
+int getgrgid_r(gid_t, struct group *, char *, size_t, struct group **);
+int getgrnam_r(const char *, struct group *, char *, size_t, struct group **);
 #endif
 
-int   getgrouplist (const char *user, gid_t group,
-                  gid_t *groups, int *ngroups);
+int getgrouplist (const char *user, gid_t group, gid_t *groups, int *ngroups);
 
-int   initgroups (const char *user, gid_t group);
+int initgroups (const char *user, gid_t group);
 
 __END_DECLS
 
diff --git a/libc/include/pwd.h b/libc/include/pwd.h
index 905bc75..e32825e 100644
--- a/libc/include/pwd.h
+++ b/libc/include/pwd.h
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 1989, 1993
- *	The Regents of the University of California.  All rights reserved.
+ *    The Regents of the University of California.  All rights reserved.
  * (c) UNIX System Laboratories, Inc.
  * All or some portions of this file are derived from material licensed
  * to the University of California by American Telephone and Telegraph
@@ -31,7 +31,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	@(#)pwd.h	8.2 (Berkeley) 1/21/94
+ *    @(#)pwd.h  8.2 (Berkeley) 1/21/94
  */
 
 /*-
@@ -118,6 +118,10 @@
 
 struct passwd* getpwnam(const char*);
 struct passwd* getpwuid(uid_t);
+/* Android has thousands and thousands of ids to iterate through */
+struct passwd* getpwent(void) __attribute__((warning("getpwent is inefficient on Android")));
+void setpwent(void);
+void endpwent(void);
 
 int getpwnam_r(const char*, struct passwd*, char*, size_t, struct passwd**);
 int getpwuid_r(uid_t, struct passwd*, char*, size_t, struct passwd**);
diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map
index b0c8299..ae41430 100644
--- a/libc/libc.arm.brillo.map
+++ b/libc/libc.arm.brillo.map
@@ -1276,12 +1276,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 7a3b8bd..019a880 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1276,12 +1276,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
@@ -1480,7 +1486,6 @@
     dlmalloc_inspect_all; # arm x86 mips nobrillo
     dlmalloc_trim; # arm x86 mips nobrillo
     dlmalloc_usable_size; # arm x86 mips nobrillo
-    endpwent; # arm x86 mips nobrillo
     fdprintf; # arm x86 mips nobrillo
     free_malloc_leak_info;
     ftime; # arm x86 mips nobrillo
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index 217d95e..b1a50ae 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1198,12 +1198,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index c7f9058..8881485 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1301,12 +1301,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
@@ -1506,7 +1512,6 @@
     dlmalloc_inspect_all; # arm x86 mips nobrillo
     dlmalloc_trim; # arm x86 mips nobrillo
     dlmalloc_usable_size; # arm x86 mips nobrillo
-    endpwent; # arm x86 mips nobrillo
     fdprintf; # arm x86 mips nobrillo
     free_malloc_leak_info;
     ftime; # arm x86 mips nobrillo
diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map
index 2b92162..c281104 100644
--- a/libc/libc.mips.brillo.map
+++ b/libc/libc.mips.brillo.map
@@ -1260,12 +1260,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index 934b23b..47a1ba2 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1260,12 +1260,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
@@ -1322,7 +1328,6 @@
     dlmalloc_inspect_all; # arm x86 mips nobrillo
     dlmalloc_trim; # arm x86 mips nobrillo
     dlmalloc_usable_size; # arm x86 mips nobrillo
-    endpwent; # arm x86 mips nobrillo
     fdprintf; # arm x86 mips nobrillo
     free_malloc_leak_info;
     ftime; # arm x86 mips nobrillo
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index 217d95e..b1a50ae 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1198,12 +1198,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map
index d973ac1..30eaf94 100644
--- a/libc/libc.x86.brillo.map
+++ b/libc/libc.x86.brillo.map
@@ -1258,12 +1258,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index b3f9394..b98f865 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1258,12 +1258,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
@@ -1321,7 +1327,6 @@
     dlmalloc_inspect_all; # arm x86 mips nobrillo
     dlmalloc_trim; # arm x86 mips nobrillo
     dlmalloc_usable_size; # arm x86 mips nobrillo
-    endpwent; # arm x86 mips nobrillo
     fdprintf; # arm x86 mips nobrillo
     free_malloc_leak_info;
     ftime; # arm x86 mips nobrillo
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index 217d95e..b1a50ae 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1198,12 +1198,18 @@
     catclose;
     catgets;
     catopen;
+    endgrent;
+    endpwent;
     getdomainname;
+    getgrent;
+    getpwent;
     getsubopt;
     hasmntopt;
     pthread_getname_np;
     quotactl;
     setdomainname;
+    setgrent;
+    setpwent;
     sighold;
     sigignore;
     sigpause;
diff --git a/tests/grp_pwd_test.cpp b/tests/grp_pwd_test.cpp
index 29cd907..a684780 100644
--- a/tests/grp_pwd_test.cpp
+++ b/tests/grp_pwd_test.cpp
@@ -26,6 +26,10 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <bitset>
+
+#include <private/android_filesystem_config.h>
+
 enum uid_type_t {
   TYPE_SYSTEM,
   TYPE_APP
@@ -179,6 +183,46 @@
   check_get_passwd("u1_i0", 199000, TYPE_APP);
 }
 
+TEST(getpwent, iterate) {
+  passwd* pwd;
+  std::bitset<10000> exist;
+  bool application = false;
+
+  exist.reset();
+
+  setpwent();
+  while ((pwd = getpwent()) != NULL) {
+    ASSERT_TRUE(NULL != pwd->pw_name);
+    ASSERT_EQ(pwd->pw_gid, pwd->pw_uid);
+    ASSERT_EQ(NULL, pwd->pw_passwd);
+#ifdef __LP64__
+    ASSERT_TRUE(NULL == pwd->pw_gecos);
+#endif
+    ASSERT_TRUE(NULL != pwd->pw_shell);
+    if (pwd->pw_uid >= exist.size()) {
+      ASSERT_STREQ("/data", pwd->pw_dir);
+      application = true;
+    } else {
+      ASSERT_STREQ("/", pwd->pw_dir);
+      ASSERT_FALSE(exist[pwd->pw_uid]);
+      exist[pwd->pw_uid] = true;
+    }
+  }
+  endpwent();
+
+  // Required content
+  for (size_t n = 0; n < android_id_count; ++n) {
+    ASSERT_TRUE(exist[android_ids[n].aid]);
+  }
+  for (size_t n = 2900; n < 2999; ++n) {
+    ASSERT_TRUE(exist[n]);
+  }
+  for (size_t n = 5000; n < 5999; ++n) {
+    ASSERT_TRUE(exist[n]);
+  }
+  ASSERT_TRUE(application);
+}
+
 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);
@@ -373,3 +417,38 @@
   ASSERT_EQ(0, getgrnam_r("root", &grp_storage, buf, size, &grp));
   check_group(grp, "root", 0);
 }
+
+TEST(getgrent, iterate) {
+  group* grp;
+  std::bitset<10000> exist;
+  bool application = false;
+
+  exist.reset();
+
+  setgrent();
+  while ((grp = getgrent()) != NULL) {
+    ASSERT_TRUE(grp->gr_name != NULL);
+    ASSERT_TRUE(grp->gr_mem != NULL);
+    ASSERT_STREQ(grp->gr_name, grp->gr_mem[0]);
+    ASSERT_TRUE(grp->gr_mem[1] == NULL);
+    if (grp->gr_gid >= exist.size()) {
+      application = true;
+    } else {
+      ASSERT_FALSE(exist[grp->gr_gid]);
+      exist[grp->gr_gid] = true;
+    }
+  }
+  endgrent();
+
+  // Required content
+  for (size_t n = 0; n < android_id_count; ++n) {
+    ASSERT_TRUE(exist[android_ids[n].aid]);
+  }
+  for (size_t n = 2900; n < 2999; ++n) {
+    ASSERT_TRUE(exist[n]);
+  }
+  for (size_t n = 5000; n < 5999; ++n) {
+    ASSERT_TRUE(exist[n]);
+  }
+  ASSERT_TRUE(application);
+}