bionic: Implement getpwent and getgrent

Not efficient to iterate through given the large number of Android
ids (AID). Compile warning will result if you use these functions,
telling you as much. Not for general consumption, however for
example, some filesystem tests would like to see these to perform
all corners.

About 1/4 second for getpwent, and 1/8 second for getgrent to iterate
through all reserved Android aids.

Bug: 27999086
Change-Id: I7784273b7875c38e4954ae21d314f35e4bf8c2fc
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);
+}