Add bufgetgrgid()
diff --git a/lib/lib.c b/lib/lib.c
index 98597b2..66814a4 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -1102,3 +1102,31 @@
 
   return &list->pw;
 }
+
+// Return cached passwd entries.
+struct group *bufgetgrgid(gid_t gid)
+{
+  struct grgidbuf_list {
+    struct grgidbuf_list *next;
+    struct group gr;
+  } *list;
+  struct group *temp;
+  static struct grgidbuf_list *grgidbuf;
+
+  for (list = grgidbuf; list; list = list->next)
+    if (list->gr.gr_gid == gid) return &(list->gr);
+
+  list = xmalloc(512);
+  list->next = grgidbuf;
+
+  errno = getgrgid_r(gid, &list->gr, sizeof(*list)+(char *)list,
+    512-sizeof(*list), &temp);
+  if (!temp) {
+    free(list);
+
+    return 0;
+  }
+  grgidbuf = list;
+
+  return &list->gr;
+}
diff --git a/lib/lib.h b/lib/lib.h
index b1f4ca1..f545a79 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -206,6 +206,7 @@
 int dev_major(int dev);
 int dev_makedev(int major, int minor);
 struct passwd *bufgetpwuid(uid_t uid);
+struct group *bufgetgrgid(gid_t gid);
 
 #define HR_SPACE 1 // Space between number and units
 #define HR_B     2 // Use "B" for single byte units
diff --git a/toys/posix/find.c b/toys/posix/find.c
index 3b27225..189773c 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -303,9 +303,9 @@
       if (check) do_print(new, s[5] ? 0 : '\n');
 
     } else if (!strcmp(s, "nouser")) {
-      if (check) if (getpwuid(new->st.st_uid)) test = 0;
+      if (check) if (bufgetpwuid(new->st.st_uid)) test = 0;
     } else if (!strcmp(s, "nogroup")) {
-      if (check) if (getgrgid(new->st.st_gid)) test = 0;
+      if (check) if (bufgetgrgid(new->st.st_gid)) test = 0;
     } else if (!strcmp(s, "prune")) {
       if (check && S_ISDIR(new->st.st_mode) && !TT.depth) recurse = 0;
 
diff --git a/toys/posix/ls.c b/toys/posix/ls.c
index 2ebc062..3ad28da 100644
--- a/toys/posix/ls.c
+++ b/toys/posix/ls.c
@@ -123,7 +123,7 @@
 
 static char *getusername(uid_t uid)
 {
-  struct passwd *pw = getpwuid(uid);
+  struct passwd *pw = bufgetpwuid(uid);
 
   sprintf(TT.uid_buf, "%u", (unsigned)uid);
   return pw ? pw->pw_name : TT.uid_buf;
@@ -131,7 +131,7 @@
 
 static char *getgroupname(gid_t gid)
 {
-  struct group *gr = getgrgid(gid);
+  struct group *gr = bufgetgrgid(gid);
 
   sprintf(TT.gid_buf, "%u", (unsigned)gid);
   return gr ? gr->gr_name : TT.gid_buf;
@@ -334,12 +334,10 @@
     // Do preprocessing (Dirtree didn't populate, so callback wasn't called.)
     for (;dt; dt = dt->next) filter(dt);
     if (flags == (FLAG_1|FLAG_f)) return;
-  } else {
-    // Read directory contents. We dup() the fd because this will close it.
-    // This reads/saves contents to display later, except for in "ls -1f" mode.
-    dirtree_recurse(indir, filter, dup(dirfd),
+  // Read directory contents. We dup() the fd because this will close it.
+  // This reads/saves contents to display later, except for in "ls -1f" mode.
+  } else  dirtree_recurse(indir, filter, dup(dirfd),
       DIRTREE_SYMFOLLOW*!!(flags&FLAG_L));
-  }
 
   // Copy linked list to array and sort it. Directories go in array because
   // we visit them in sorted order too. (The nested loops let us measure and
diff --git a/toys/posix/ps.c b/toys/posix/ps.c
index 445228f..301c1f3 100644
--- a/toys/posix/ps.c
+++ b/toys/posix/ps.c
@@ -449,7 +449,7 @@
     sprintf(out, "%lld", ll);
     if (sl&64) {
       if (which > PS_RUSER) {
-        struct group *gr = getgrgid(ll);
+        struct group *gr = bufgetgrgid(ll);
 
         if (gr) out = gr->gr_name;
       } else {